diff --git a/.dev.vars.example b/.dev.vars.example index be2a8b9..293e851 100644 --- a/.dev.vars.example +++ b/.dev.vars.example @@ -3,6 +3,6 @@ MATOMO_SITE_ID=1 MATOMO_TIMEOUT_MS=5000 LOG_LEVEL=debug HTTP_METHOD_ALLOWLIST=GET -USER_AGENT_ALLOWLIST_REGEX=(?:ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM) +USER_AGENT_ALLOWLIST_REGEX=(?:ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM|Google-GeminiNotebook) URL_EXCLUDE_REGEX=^[^?]+\.(?:css|js|mjs|map|json|xml|webmanifest|manifest|png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?|woff2?|ttf|otf|eot|rss|atom|wasm|txt)(?:\?|$) DOCUMENT_REGEX=^[^?]+\.(?:pdf|docx?|xlsx?|pptx?|csv|json|txt|xml|epub|mobi|azw3|mp3|mp4|mpe?g|webm|mov|avi|ogg|wav|flac|zip|gz|gzip|tgz|tar|bz2|tbz|7z|rar|dmg|exe|msi|apk|jar|md5|sig)(?:\?|$) diff --git a/README.md b/README.md index b56cd38..3f96750 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Cloudflare Worker (TypeScript, Node 24 tooling) that sits inline on your zone, p Example: `^[^?]+\\.(?:pdf|zip|docx?)(?:\\?|$)` - `LOG_LEVEL` (optional, default `warn`): `silent|error|warn|info|debug`. -- `USER_AGENT_ALLOWLIST_REGEX` (optional): Case-insensitive regex to permit user agents; non-matching entries are skipped. Defaults to an allowlist for `ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM`. +- `USER_AGENT_ALLOWLIST_REGEX` (optional): Case-insensitive regex to permit user agents; non-matching entries are skipped. Defaults to an allowlist for `ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM|Google-GeminiNotebook`. - `URL_EXCLUDE_REGEX` (optional): Case-insensitive regex to skip tracking for matching URLs. This regex runs against the full URL (`protocol://host/path?query`) and defaults to excluding common static assets and non-page resources: - Frontend assets: `.css`, `.js`, `.mjs` - Source maps: `.map` diff --git a/src/config.ts b/src/config.ts index d990e72..54e7d7b 100644 --- a/src/config.ts +++ b/src/config.ts @@ -17,7 +17,8 @@ const defaultUserAgentPatterns = [ 'Gemini-Deep-Research', 'Claude-User', 'Perplexity-User', - 'Google-NotebookLM' + 'Google-NotebookLM', + 'Google-GeminiNotebook' ]; const defaultAllowlistPattern = `(?:${defaultUserAgentPatterns .map(escapeRegex) diff --git a/tests/config.test.ts b/tests/config.test.ts index 4087c27..a5c675c 100644 --- a/tests/config.test.ts +++ b/tests/config.test.ts @@ -17,7 +17,7 @@ describe('getConfig', () => { httpMethodAllowlist: ['GET'] }); expect(config.userAgentAllowlistRegex).toEqual( - /(?:ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM)/i + /(?:ChatGPT-User|MistralAI-User|Gemini-Deep-Research|Claude-User|Perplexity-User|Google-NotebookLM|Google-GeminiNotebook)/i ); expect(config.urlExcludeRegex).toEqual( /^[^?]+\.(?:css|js|mjs|map|json|xml|webmanifest|manifest|png|jpe?g|gif|webp|avif|svg|ico|bmp|tiff?|woff2?|ttf|otf|eot|rss|atom|wasm|txt)(?:\?|$)/i @@ -27,6 +27,20 @@ describe('getConfig', () => { ); }); + it('allows both NotebookLM user agent tokens by default', () => { + const { userAgentAllowlistRegex } = getConfig({ ...baseEnv }); + const userAgents = [ + // current desktop and mobile agents + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 (compatible; Google-GeminiNotebook; +https://developers.google.com/crawling/docs/crawlers-fetchers/google-gemininotebook)', + 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Mobile Safari/537.36 (compatible; Google-GeminiNotebook; +https://developers.google.com/crawling/docs/crawlers-fetchers/google-gemininotebook)', + // former agent, supported until August 2026 + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36 (compatible; Google-NotebookLM; +https://developers.google.com/crawling/docs/crawlers-fetchers/google-notebooklm)' + ]; + for (const userAgent of userAgents) { + expect(userAgentAllowlistRegex?.test(userAgent)).toBe(true); + } + }); + it('uses optional overrides', () => { const config = getConfig({ ...baseEnv,