UNPKG

@clerk/shared

Version:

Internal package utils used by the Clerk SDKs

1 lines 5.53 kB
{"version":3,"file":"browser.mjs","names":[],"sources":["../src/browser.ts"],"sourcesContent":["/**\n * Checks if the window object is defined. You can also use this to check if something is happening on the client side.\n *\n * @returns\n */\nexport function inBrowser(): boolean {\n return typeof window !== 'undefined';\n}\n\nconst botAgents = [\n 'bot',\n 'spider',\n 'crawl',\n 'APIs-Google',\n 'AdsBot',\n 'Googlebot',\n 'mediapartners',\n 'Google Favicon',\n 'FeedFetcher',\n 'Google-Read-Aloud',\n 'DuplexWeb-Google',\n 'googleweblight',\n 'bing',\n 'yandex',\n 'baidu',\n 'duckduck',\n 'yahoo',\n 'ecosia',\n 'ia_archiver',\n 'facebook',\n 'instagram',\n 'pinterest',\n 'reddit',\n 'slack',\n 'twitter',\n 'whatsapp',\n 'youtube',\n 'semrush',\n];\nconst botAgentRegex = new RegExp(botAgents.join('|'), 'i');\n\n/**\n * Checks if the user agent is a bot.\n *\n * @param userAgent - Any user agent string\n * @returns\n */\nexport function userAgentIsRobot(userAgent: string): boolean {\n return !userAgent ? false : botAgentRegex.test(userAgent);\n}\n\n/**\n * Server-side runtimes with worker-like globals self-identify in `navigator.userAgent`\n * (`Cloudflare-Workers`, `Node.js/24`, `Deno/2.5.0`, `Bun/1.3.9`). Today workerd's `self`\n * does not satisfy `instanceof WorkerGlobalScope` (even though it exposes the constructor),\n * so the scope gate alone happens to exclude it, but that is an implementation detail of\n * workerd's prototype chain, not a guarantee. Excluding self-identified server runtimes by\n * user agent keeps these heuristics server-false even if such a runtime becomes fully\n * spec-compliant about its worker scope.\n */\nconst serverRuntimeUserAgentRegex = /^(Cloudflare-Workers|Node\\.js|Deno|Bun)\\b/i;\n\n/**\n * Resolves the `Navigator` object from either the DOM `window` (standard browsers)\n * or a Web/Service Worker global scope. An MV3 extension background service worker\n * has no `window`, but runs inside a `WorkerGlobalScope` that exposes a\n * `WorkerNavigator` as `self.navigator` with the `onLine`/`userAgent` properties\n * our heuristics rely on.\n *\n * We intentionally gate the worker fallback on a real `WorkerGlobalScope` rather than\n * accepting any global `navigator`. Modern Node exposes `globalThis.navigator`, so a\n * blanket global-navigator check would make Node SSR look like a browser; requiring a\n * `WorkerGlobalScope` keeps SSR returning `null`.\n *\n * @returns\n */\nfunction getNavigator(): Navigator | null {\n if (typeof window !== 'undefined' && window.navigator) {\n return window.navigator;\n }\n const workerScope = globalThis as unknown as {\n WorkerGlobalScope?: new (...args: never[]) => { navigator?: Navigator };\n self?: { navigator?: Navigator };\n };\n if (\n typeof workerScope.WorkerGlobalScope === 'function' &&\n workerScope.self instanceof workerScope.WorkerGlobalScope &&\n workerScope.self.navigator &&\n !serverRuntimeUserAgentRegex.test(workerScope.self.navigator.userAgent ?? '')\n ) {\n return workerScope.self.navigator;\n }\n return null;\n}\n\n/**\n * Checks if the current environment is a browser and the user agent is not a bot.\n *\n * @returns\n */\nexport function isValidBrowser(): boolean {\n const navigator = getNavigator();\n if (!navigator) {\n return false;\n }\n return !userAgentIsRobot(navigator?.userAgent) && !navigator?.webdriver;\n}\n\n/**\n * Checks if the current environment is a browser and if the navigator is online.\n *\n * @returns\n */\nexport function isBrowserOnline(): boolean {\n const navigator = getNavigator();\n if (!navigator) {\n return false;\n }\n\n // Some environments (e.g. React Native) define a Navigator object but do not\n // implement navigator.onLine as a boolean. Default to online in those cases.\n if (typeof navigator.onLine !== 'boolean') {\n return true;\n }\n\n // navigator.onLine is the standard API and is reliable for detecting\n // complete disconnection (airplane mode, WiFi off, etc.).\n // The experimental navigator.connection API (rtt/downlink) was previously\n // used as a secondary signal, but it reports zero values in headless browsers\n // and CI environments even when connected, causing false offline detection.\n return !!navigator.onLine;\n}\n\n/**\n * Runs `isBrowserOnline` and `isValidBrowser` to check if the current environment is a valid browser and if the navigator is online.\n *\n * @returns\n */\nexport function isValidBrowserOnline(): boolean {\n return isBrowserOnline() && isValidBrowser();\n}\n"],"mappings":";;;;;;AAKA,SAAgB,YAAqB;CACnC,OAAO,OAAO,WAAW;AAC3B;AAgCA,MAAM,gBAAgB,IAAI,OAAO;CA7B/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AAEuC,CAAC,CAAC,KAAK,GAAG,GAAG,GAAG;;;;;;;AAQzD,SAAgB,iBAAiB,WAA4B;CAC3D,OAAO,CAAC,YAAY,QAAQ,cAAc,KAAK,SAAS;AAC1D;;;;;;;;;;AAWA,MAAM,8BAA8B;;;;;;;;;;;;;;;AAgBpC,SAAS,eAAiC;CACxC,IAAI,OAAO,WAAW,eAAe,OAAO,WAC1C,OAAO,OAAO;CAEhB,MAAM,cAAc;CAIpB,IACE,OAAO,YAAY,sBAAsB,cACzC,YAAY,gBAAgB,YAAY,qBACxC,YAAY,KAAK,aACjB,CAAC,4BAA4B,KAAK,YAAY,KAAK,UAAU,aAAa,EAAE,GAE5E,OAAO,YAAY,KAAK;CAE1B,OAAO;AACT;;;;;;AAOA,SAAgB,iBAA0B;CACxC,MAAM,YAAY,aAAa;CAC/B,IAAI,CAAC,WACH,OAAO;CAET,OAAO,CAAC,iBAAiB,WAAW,SAAS,KAAK,CAAC,WAAW;AAChE;;;;;;AAOA,SAAgB,kBAA2B;CACzC,MAAM,YAAY,aAAa;CAC/B,IAAI,CAAC,WACH,OAAO;CAKT,IAAI,OAAO,UAAU,WAAW,WAC9B,OAAO;CAQT,OAAO,CAAC,CAAC,UAAU;AACrB;;;;;;AAOA,SAAgB,uBAAgC;CAC9C,OAAO,gBAAgB,KAAK,eAAe;AAC7C"}