@getdevteam/analytics-web 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# @getdevteam/analytics-web
|
|
2
|
+
|
|
3
|
+
Browser SDK for the [DevTeam Analytics & Logging Platform](https://github.com/getdevteam-ai/analytics).
|
|
4
|
+
Drop it into any browser app - plain JS, Astro, Vue, Svelte, or React without the hooks - to send analytics events and logs.
|
|
5
|
+
Building with React and want a `useAnalytics()` hook? Pair this with [`@getdevteam/analytics-react`](https://www.npmjs.com/package/@getdevteam/analytics-react).
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @getdevteam/analytics-web
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Use
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createAnalytics } from "@getdevteam/analytics-web";
|
|
17
|
+
|
|
18
|
+
const analytics = createAnalytics({
|
|
19
|
+
key: "dtp_your_public_key",
|
|
20
|
+
host: "https://ingest.analytics.getdevteam.ai",
|
|
21
|
+
trackPageviews: true, // auto-send a pageview on every route change
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
analytics.identify("user_123", { plan: "pro" }); // who the user is
|
|
25
|
+
analytics.track("checkout_completed", { amount: 4200 }); // something that happened
|
|
26
|
+
analytics.log.error("payment gateway timed out", { gateway: "stripe" });
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Auto-captures uncaught errors and unhandled promise rejections (turn off with `autoCaptureErrors: false`), and flushes queued events when the page is hidden or closed so nothing is lost on navigation.
|
|
30
|
+
|
|
31
|
+
You need a public ingest key (`dtp_...`) and the ingest host URL for your environment - see [Getting a key](https://github.com/getdevteam-ai/analytics/tree/main/src/sdk#getting-a-key) in the full SDK docs.
|
|
32
|
+
|
|
33
|
+
Full client API, config options, and release process: [src/sdk README](https://github.com/getdevteam-ai/analytics/tree/main/src/sdk).
|
package/dist/index.cjs
CHANGED
|
@@ -161,7 +161,7 @@ function createBrowserStorage(resolve = resolveLocalStorage) {
|
|
|
161
161
|
|
|
162
162
|
// src/index.ts
|
|
163
163
|
var WEB_SDK_NAME = "@getdevteam/analytics-web";
|
|
164
|
-
var WEB_SDK_VERSION = "0.
|
|
164
|
+
var WEB_SDK_VERSION = "0.2.0";
|
|
165
165
|
function createAnalytics(config) {
|
|
166
166
|
let finalFlush = false;
|
|
167
167
|
const baseFetch = config.fetch ?? defaultBrowserFetch();
|
|
@@ -184,7 +184,7 @@ function createAnalytics(config) {
|
|
|
184
184
|
if (typeof window !== "undefined") {
|
|
185
185
|
const flushOnHide = () => {
|
|
186
186
|
finalFlush = true;
|
|
187
|
-
void client.flush().finally(() => {
|
|
187
|
+
void client.flush({ keepaliveConstrained: true }).finally(() => {
|
|
188
188
|
finalFlush = false;
|
|
189
189
|
});
|
|
190
190
|
};
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/pageviews.ts","../src/storage.ts"],"sourcesContent":["import {\n createClient,\n type AnalyticsClient,\n type ClientConfig,\n type EventContext,\n type FetchLike,\n type FetchRequestInit,\n type FetchResponseLike,\n} from \"@getdevteam/analytics-core\";\nimport { collectWebContext } from \"./context\";\nimport { installPageviewTracking } from \"./pageviews\";\nimport { createBrowserStorage } from \"./storage\";\n\nexport const WEB_SDK_NAME = \"@getdevteam/analytics-web\";\nexport const WEB_SDK_VERSION = \"0.1.0\";\n\nexport interface WebAnalyticsConfig extends ClientConfig {\n autoCaptureErrors?: boolean;\n trackPageviews?: boolean;\n context?: Partial<EventContext>;\n}\n\nexport function createAnalytics(config: WebAnalyticsConfig): AnalyticsClient {\n let finalFlush = false;\n const baseFetch = config.fetch ?? defaultBrowserFetch();\n\n const client = createClient({\n ...config,\n storage: config.storage ?? createBrowserStorage(),\n fetch: (url, init) => {\n if (finalFlush) return sendOnHide(url, init, config.key, baseFetch);\n if (baseFetch) return baseFetch(url, init);\n return Promise.reject(new Error(\"fetch is not available in this environment\"));\n },\n });\n\n client.setContext({\n sdk_name: WEB_SDK_NAME,\n sdk_version: WEB_SDK_VERSION,\n ...collectWebContext(),\n ...config.context,\n });\n\n const teardowns: Array<() => void> = [];\n\n if (typeof window !== \"undefined\") {\n const flushOnHide = (): void => {\n finalFlush = true;\n void client.flush().finally(() => {\n finalFlush = false;\n });\n };\n const onPageHide = (): void => flushOnHide();\n const onVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") flushOnHide();\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n teardowns.push(() => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n });\n\n if (config.autoCaptureErrors !== false) {\n const onError = (event: ErrorEvent): void => {\n client.captureError(event.error ?? event.message, { source: \"window.onerror\" });\n };\n const onRejection = (event: Event): void => {\n const reason = (event as { reason?: unknown }).reason;\n client.captureError(reason ?? \"unhandled promise rejection\", {\n source: \"unhandledrejection\",\n });\n };\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n teardowns.push(() => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n });\n }\n\n if (config.trackPageviews === true) {\n teardowns.push(installPageviewTracking(client));\n }\n }\n\n return {\n ...client,\n shutdown: async () => {\n for (const teardown of teardowns.splice(0)) {\n teardown();\n }\n await client.shutdown();\n },\n };\n}\n\nfunction defaultBrowserFetch(): FetchLike | undefined {\n if (typeof fetch !== \"function\") return undefined;\n return (url, init) => fetch(url, init);\n}\n\nfunction sendOnHide(\n url: string,\n init: FetchRequestInit,\n key: string,\n baseFetch: FetchLike | undefined,\n): Promise<FetchResponseLike> {\n if (baseFetch) {\n return baseFetch(url, { ...init, keepalive: true });\n }\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") {\n // sendBeacon cannot set headers, so the key rides as a query parameter as a last resort.\n const beaconUrl = `${url}${url.includes(\"?\") ? \"&\" : \"?\"}key=${encodeURIComponent(key)}`;\n const delivered = navigator.sendBeacon(\n beaconUrl,\n new Blob([init.body], { type: \"application/json\" }),\n );\n return Promise.resolve({ ok: delivered, status: delivered ? 202 : 503 });\n }\n return Promise.reject(new Error(\"no transport available for final flush\"));\n}\n\nexport { collectWebContext, browserFromUserAgent, osFromUserAgent, detectDeviceType } from \"./context\";\nexport { createBrowserStorage } from \"./storage\";\nexport { currentPageProperties, installPageviewTracking } from \"./pageviews\";\nexport type {\n AnalyticsClient,\n AnalyticsLogger,\n ClientConfig,\n EventContext,\n FetchLike,\n LogSeverity,\n Properties,\n StorageAdapter,\n} from \"@getdevteam/analytics-core\";\n","import type { EventContext } from \"@getdevteam/analytics-core\";\n\nexport function browserFromUserAgent(ua: string): string | undefined {\n if (/Edg(e|A|iOS)?\\//.test(ua)) return \"Edge\";\n if (/Firefox\\/|FxiOS\\//.test(ua)) return \"Firefox\";\n if (/CriOS\\/|Chrome\\//.test(ua)) return \"Chrome\";\n if (/Safari\\//.test(ua)) return \"Safari\";\n return undefined;\n}\n\nexport function osFromUserAgent(\n ua: string,\n platform?: string,\n): { os_name?: string; os_version?: string } {\n if (/Windows/.test(ua)) {\n const match = /Windows NT ([\\d.]+)/.exec(ua);\n return { os_name: \"Windows\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/iPhone|iPad|iPod/.test(ua)) {\n const match = /OS (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"iOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Android/.test(ua)) {\n const match = /Android ([\\d.]+)/.exec(ua);\n return { os_name: \"Android\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/Mac OS X|Macintosh/.test(ua) || (platform ?? \"\").startsWith(\"Mac\")) {\n const match = /Mac OS X (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"macOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Linux/.test(ua) || (platform ?? \"\").startsWith(\"Linux\")) {\n return { os_name: \"Linux\" };\n }\n return {};\n}\n\ninterface WindowLike {\n matchMedia?: (query: string) => { matches: boolean };\n innerWidth?: number;\n screen?: { width?: number };\n}\n\nexport function detectDeviceType(win: WindowLike): string {\n try {\n const coarse =\n typeof win.matchMedia === \"function\" && win.matchMedia(\"(pointer: coarse)\").matches;\n if (!coarse) return \"desktop\";\n const width = win.innerWidth || win.screen?.width || 0;\n return width >= 768 ? \"tablet\" : \"mobile\";\n } catch {\n return \"desktop\";\n }\n}\n\nexport function collectWebContext(): EventContext {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return { platform: \"web\" };\n }\n const ua = navigator.userAgent ?? \"\";\n const context: EventContext = { platform: \"web\" };\n const browser = browserFromUserAgent(ua);\n if (browser !== undefined) context.browser = browser;\n Object.assign(context, osFromUserAgent(ua, navigator.platform));\n context.device_type = detectDeviceType(window);\n if (navigator.language) context.locale = navigator.language;\n if (window.screen) {\n context.screen = { w: window.screen.width ?? 0, h: window.screen.height ?? 0 };\n }\n return context;\n}\n","import type { AnalyticsClient, Properties } from \"@getdevteam/analytics-core\";\n\nexport function currentPageProperties(): Properties {\n return {\n url: window.location.href,\n title: document.title,\n };\n}\n\nexport function installPageviewTracking(client: AnalyticsClient): () => void {\n const sendPageview = (): void => client.page(currentPageProperties());\n const history = window.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n sendPageview();\n };\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n sendPageview();\n };\n window.addEventListener(\"popstate\", sendPageview);\n sendPageview();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", sendPageview);\n };\n}\n","import { createMemoryStorage, type StorageAdapter } from \"@getdevteam/analytics-core\";\n\nexport function resolveLocalStorage(): Storage | undefined {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const probe = \"__devteam_analytics_probe__\";\n window.localStorage.setItem(probe, \"1\");\n window.localStorage.removeItem(probe);\n return window.localStorage;\n } catch {\n return undefined;\n }\n}\n\nexport function createBrowserStorage(\n resolve: () => Storage | undefined = resolveLocalStorage,\n): StorageAdapter {\n const localStorage = resolve();\n if (!localStorage) return createMemoryStorage();\n return {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // quota or private-mode failure; queue persistence degrades gracefully\n }\n },\n removeItem: (key) => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAAAA,yBAQO;;;ACNA,SAAS,qBAAqB,IAAgC;AACnE,MAAI,kBAAkB,KAAK,EAAE,EAAG,QAAO;AACvC,MAAI,oBAAoB,KAAK,EAAE,EAAG,QAAO;AACzC,MAAI,mBAAmB,KAAK,EAAE,EAAG,QAAO;AACxC,MAAI,WAAW,KAAK,EAAE,EAAG,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,gBACd,IACA,UAC2C;AAC3C,MAAI,UAAU,KAAK,EAAE,GAAG;AACtB,UAAM,QAAQ,sBAAsB,KAAK,EAAE;AAC3C,WAAO,EAAE,SAAS,WAAW,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,IAAI,CAAC,EAAG;AAAA,EAC/E;AACA,MAAI,mBAAmB,KAAK,EAAE,GAAG;AAC/B,UAAM,QAAQ,8BAA8B,KAAK,EAAE;AACnD,WAAO,EAAE,SAAS,OAAO,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,EAAG;AAAA,EAC9F;AACA,MAAI,UAAU,KAAK,EAAE,GAAG;AACtB,UAAM,QAAQ,mBAAmB,KAAK,EAAE;AACxC,WAAO,EAAE,SAAS,WAAW,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,IAAI,CAAC,EAAG;AAAA,EAC/E;AACA,MAAI,qBAAqB,KAAK,EAAE,MAAM,YAAY,IAAI,WAAW,KAAK,GAAG;AACvE,UAAM,QAAQ,oCAAoC,KAAK,EAAE;AACzD,WAAO,EAAE,SAAS,SAAS,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,EAAG;AAAA,EAChG;AACA,MAAI,QAAQ,KAAK,EAAE,MAAM,YAAY,IAAI,WAAW,OAAO,GAAG;AAC5D,WAAO,EAAE,SAAS,QAAQ;AAAA,EAC5B;AACA,SAAO,CAAC;AACV;AAQO,SAAS,iBAAiB,KAAyB;AACxD,MAAI;AACF,UAAM,SACJ,OAAO,IAAI,eAAe,cAAc,IAAI,WAAW,mBAAmB,EAAE;AAC9E,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,QAAQ,IAAI,cAAc,IAAI,QAAQ,SAAS;AACrD,WAAO,SAAS,MAAM,WAAW;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAkC;AAChD,MAAI,OAAO,WAAW,eAAe,OAAO,cAAc,aAAa;AACrE,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AACA,QAAM,KAAK,UAAU,aAAa;AAClC,QAAM,UAAwB,EAAE,UAAU,MAAM;AAChD,QAAM,UAAU,qBAAqB,EAAE;AACvC,MAAI,YAAY,OAAW,SAAQ,UAAU;AAC7C,SAAO,OAAO,SAAS,gBAAgB,IAAI,UAAU,QAAQ,CAAC;AAC9D,UAAQ,cAAc,iBAAiB,MAAM;AAC7C,MAAI,UAAU,SAAU,SAAQ,SAAS,UAAU;AACnD,MAAI,OAAO,QAAQ;AACjB,YAAQ,SAAS,EAAE,GAAG,OAAO,OAAO,SAAS,GAAG,GAAG,OAAO,OAAO,UAAU,EAAE;AAAA,EAC/E;AACA,SAAO;AACT;;;ACnEO,SAAS,wBAAoC;AAClD,SAAO;AAAA,IACL,KAAK,OAAO,SAAS;AAAA,IACrB,OAAO,SAAS;AAAA,EAClB;AACF;AAEO,SAAS,wBAAwB,QAAqC;AAC3E,QAAM,eAAe,MAAY,OAAO,KAAK,sBAAsB,CAAC;AACpE,QAAM,UAAU,OAAO;AACvB,QAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACxD,QAAM,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAE9D,UAAQ,YAAY,IAAI,SAA2C;AACjE,sBAAkB,GAAG,IAAI;AACzB,iBAAa;AAAA,EACf;AACA,UAAQ,eAAe,IAAI,SAA8C;AACvE,yBAAqB,GAAG,IAAI;AAC5B,iBAAa;AAAA,EACf;AACA,SAAO,iBAAiB,YAAY,YAAY;AAChD,eAAa;AAEb,SAAO,MAAM;AACX,YAAQ,YAAY;AACpB,YAAQ,eAAe;AACvB,WAAO,oBAAoB,YAAY,YAAY;AAAA,EACrD;AACF;;;AC/BA,4BAAyD;AAElD,SAAS,sBAA2C;AACzD,MAAI;AACF,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,aAAc,QAAO;AAClE,UAAM,QAAQ;AACd,WAAO,aAAa,QAAQ,OAAO,GAAG;AACtC,WAAO,aAAa,WAAW,KAAK;AACpC,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,qBACd,UAAqC,qBACrB;AAChB,QAAM,eAAe,QAAQ;AAC7B,MAAI,CAAC,aAAc,YAAO,2CAAoB;AAC9C,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ;AAChB,UAAI;AACF,eAAO,aAAa,QAAQ,GAAG;AAAA,MACjC,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,SAAS,CAAC,KAAK,UAAU;AACvB,UAAI;AACF,qBAAa,QAAQ,KAAK,KAAK;AAAA,MACjC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,YAAY,CAAC,QAAQ;AACnB,UAAI;AACF,qBAAa,WAAW,GAAG;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AH7BO,IAAM,eAAe;AACrB,IAAM,kBAAkB;AAQxB,SAAS,gBAAgB,QAA6C;AAC3E,MAAI,aAAa;AACjB,QAAM,YAAY,OAAO,SAAS,oBAAoB;AAEtD,QAAM,aAAS,qCAAa;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS,OAAO,WAAW,qBAAqB;AAAA,IAChD,OAAO,CAAC,KAAK,SAAS;AACpB,UAAI,WAAY,QAAO,WAAW,KAAK,MAAM,OAAO,KAAK,SAAS;AAClE,UAAI,UAAW,QAAO,UAAU,KAAK,IAAI;AACzC,aAAO,QAAQ,OAAO,IAAI,MAAM,4CAA4C,CAAC;AAAA,IAC/E;AAAA,EACF,CAAC;AAED,SAAO,WAAW;AAAA,IAChB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,GAAG,kBAAkB;AAAA,IACrB,GAAG,OAAO;AAAA,EACZ,CAAC;AAED,QAAM,YAA+B,CAAC;AAEtC,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,cAAc,MAAY;AAC9B,mBAAa;AACb,WAAK,OAAO,MAAM,EAAE,QAAQ,MAAM;AAChC,qBAAa;AAAA,MACf,CAAC;AAAA,IACH;AACA,UAAM,aAAa,MAAY,YAAY;AAC3C,UAAM,qBAAqB,MAAY;AACrC,UAAI,SAAS,oBAAoB,SAAU,aAAY;AAAA,IACzD;AACA,WAAO,iBAAiB,YAAY,UAAU;AAC9C,aAAS,iBAAiB,oBAAoB,kBAAkB;AAChE,cAAU,KAAK,MAAM;AACnB,aAAO,oBAAoB,YAAY,UAAU;AACjD,eAAS,oBAAoB,oBAAoB,kBAAkB;AAAA,IACrE,CAAC;AAED,QAAI,OAAO,sBAAsB,OAAO;AACtC,YAAM,UAAU,CAAC,UAA4B;AAC3C,eAAO,aAAa,MAAM,SAAS,MAAM,SAAS,EAAE,QAAQ,iBAAiB,CAAC;AAAA,MAChF;AACA,YAAM,cAAc,CAAC,UAAuB;AAC1C,cAAM,SAAU,MAA+B;AAC/C,eAAO,aAAa,UAAU,+BAA+B;AAAA,UAC3D,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,aAAO,iBAAiB,SAAS,OAAO;AACxC,aAAO,iBAAiB,sBAAsB,WAAW;AACzD,gBAAU,KAAK,MAAM;AACnB,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,oBAAoB,sBAAsB,WAAW;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,mBAAmB,MAAM;AAClC,gBAAU,KAAK,wBAAwB,MAAM,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,YAAY;AACpB,iBAAW,YAAY,UAAU,OAAO,CAAC,GAAG;AAC1C,iBAAS;AAAA,MACX;AACA,YAAM,OAAO,SAAS;AAAA,IACxB;AAAA,EACF;AACF;AAEA,SAAS,sBAA6C;AACpD,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,SAAO,CAAC,KAAK,SAAS,MAAM,KAAK,IAAI;AACvC;AAEA,SAAS,WACP,KACA,MACA,KACA,WAC4B;AAC5B,MAAI,WAAW;AACb,WAAO,UAAU,KAAK,EAAE,GAAG,MAAM,WAAW,KAAK,CAAC;AAAA,EACpD;AACA,MAAI,OAAO,cAAc,eAAe,OAAO,UAAU,eAAe,YAAY;AAElF,UAAM,YAAY,GAAG,GAAG,GAAG,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,OAAO,mBAAmB,GAAG,CAAC;AACtF,UAAM,YAAY,UAAU;AAAA,MAC1B;AAAA,MACA,IAAI,KAAK,CAAC,KAAK,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAAA,IACpD;AACA,WAAO,QAAQ,QAAQ,EAAE,IAAI,WAAW,QAAQ,YAAY,MAAM,IAAI,CAAC;AAAA,EACzE;AACA,SAAO,QAAQ,OAAO,IAAI,MAAM,wCAAwC,CAAC;AAC3E;","names":["import_analytics_core"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/pageviews.ts","../src/storage.ts"],"sourcesContent":["import {\n createClient,\n type AnalyticsClient,\n type ClientConfig,\n type EventContext,\n type FetchLike,\n type FetchRequestInit,\n type FetchResponseLike,\n} from \"@getdevteam/analytics-core\";\nimport { collectWebContext } from \"./context\";\nimport { installPageviewTracking } from \"./pageviews\";\nimport { createBrowserStorage } from \"./storage\";\n\nexport const WEB_SDK_NAME = \"@getdevteam/analytics-web\";\nexport const WEB_SDK_VERSION = \"0.2.0\";\n\nexport interface WebAnalyticsConfig extends ClientConfig {\n autoCaptureErrors?: boolean;\n trackPageviews?: boolean;\n context?: Partial<EventContext>;\n}\n\nexport function createAnalytics(config: WebAnalyticsConfig): AnalyticsClient {\n let finalFlush = false;\n const baseFetch = config.fetch ?? defaultBrowserFetch();\n\n const client = createClient({\n ...config,\n storage: config.storage ?? createBrowserStorage(),\n fetch: (url, init) => {\n if (finalFlush) return sendOnHide(url, init, config.key, baseFetch);\n if (baseFetch) return baseFetch(url, init);\n return Promise.reject(new Error(\"fetch is not available in this environment\"));\n },\n });\n\n client.setContext({\n sdk_name: WEB_SDK_NAME,\n sdk_version: WEB_SDK_VERSION,\n ...collectWebContext(),\n ...config.context,\n });\n\n const teardowns: Array<() => void> = [];\n\n if (typeof window !== \"undefined\") {\n const flushOnHide = (): void => {\n finalFlush = true;\n // sendOnHide delivers over `keepalive`, so constrain each batch under the browser's 64 KiB keepalive body\n // limit. The first sub-request always fits the quota; the rest are best-effort (they drain fully when the\n // tab is hidden but alive; on a real teardown the remainder stays persisted for the next visit).\n void client.flush({ keepaliveConstrained: true }).finally(() => {\n finalFlush = false;\n });\n };\n const onPageHide = (): void => flushOnHide();\n const onVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") flushOnHide();\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n teardowns.push(() => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n });\n\n if (config.autoCaptureErrors !== false) {\n const onError = (event: ErrorEvent): void => {\n client.captureError(event.error ?? event.message, { source: \"window.onerror\" });\n };\n const onRejection = (event: Event): void => {\n const reason = (event as { reason?: unknown }).reason;\n client.captureError(reason ?? \"unhandled promise rejection\", {\n source: \"unhandledrejection\",\n });\n };\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n teardowns.push(() => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n });\n }\n\n if (config.trackPageviews === true) {\n teardowns.push(installPageviewTracking(client));\n }\n }\n\n return {\n ...client,\n shutdown: async () => {\n for (const teardown of teardowns.splice(0)) {\n teardown();\n }\n await client.shutdown();\n },\n };\n}\n\nfunction defaultBrowserFetch(): FetchLike | undefined {\n if (typeof fetch !== \"function\") return undefined;\n return (url, init) => fetch(url, init);\n}\n\nfunction sendOnHide(\n url: string,\n init: FetchRequestInit,\n key: string,\n baseFetch: FetchLike | undefined,\n): Promise<FetchResponseLike> {\n if (baseFetch) {\n return baseFetch(url, { ...init, keepalive: true });\n }\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") {\n // sendBeacon cannot set headers, so the key rides as a query parameter as a last resort.\n const beaconUrl = `${url}${url.includes(\"?\") ? \"&\" : \"?\"}key=${encodeURIComponent(key)}`;\n const delivered = navigator.sendBeacon(\n beaconUrl,\n new Blob([init.body], { type: \"application/json\" }),\n );\n return Promise.resolve({ ok: delivered, status: delivered ? 202 : 503 });\n }\n return Promise.reject(new Error(\"no transport available for final flush\"));\n}\n\nexport { collectWebContext, browserFromUserAgent, osFromUserAgent, detectDeviceType } from \"./context\";\nexport { createBrowserStorage } from \"./storage\";\nexport { currentPageProperties, installPageviewTracking } from \"./pageviews\";\nexport type {\n AnalyticsClient,\n AnalyticsLogger,\n ClientConfig,\n EventContext,\n FetchLike,\n LogSeverity,\n Properties,\n StorageAdapter,\n} from \"@getdevteam/analytics-core\";\n","import type { EventContext } from \"@getdevteam/analytics-core\";\n\nexport function browserFromUserAgent(ua: string): string | undefined {\n if (/Edg(e|A|iOS)?\\//.test(ua)) return \"Edge\";\n if (/Firefox\\/|FxiOS\\//.test(ua)) return \"Firefox\";\n if (/CriOS\\/|Chrome\\//.test(ua)) return \"Chrome\";\n if (/Safari\\//.test(ua)) return \"Safari\";\n return undefined;\n}\n\nexport function osFromUserAgent(\n ua: string,\n platform?: string,\n): { os_name?: string; os_version?: string } {\n if (/Windows/.test(ua)) {\n const match = /Windows NT ([\\d.]+)/.exec(ua);\n return { os_name: \"Windows\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/iPhone|iPad|iPod/.test(ua)) {\n const match = /OS (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"iOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Android/.test(ua)) {\n const match = /Android ([\\d.]+)/.exec(ua);\n return { os_name: \"Android\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/Mac OS X|Macintosh/.test(ua) || (platform ?? \"\").startsWith(\"Mac\")) {\n const match = /Mac OS X (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"macOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Linux/.test(ua) || (platform ?? \"\").startsWith(\"Linux\")) {\n return { os_name: \"Linux\" };\n }\n return {};\n}\n\ninterface WindowLike {\n matchMedia?: (query: string) => { matches: boolean };\n innerWidth?: number;\n screen?: { width?: number };\n}\n\nexport function detectDeviceType(win: WindowLike): string {\n try {\n const coarse =\n typeof win.matchMedia === \"function\" && win.matchMedia(\"(pointer: coarse)\").matches;\n if (!coarse) return \"desktop\";\n const width = win.innerWidth || win.screen?.width || 0;\n return width >= 768 ? \"tablet\" : \"mobile\";\n } catch {\n return \"desktop\";\n }\n}\n\nexport function collectWebContext(): EventContext {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return { platform: \"web\" };\n }\n const ua = navigator.userAgent ?? \"\";\n const context: EventContext = { platform: \"web\" };\n const browser = browserFromUserAgent(ua);\n if (browser !== undefined) context.browser = browser;\n Object.assign(context, osFromUserAgent(ua, navigator.platform));\n context.device_type = detectDeviceType(window);\n if (navigator.language) context.locale = navigator.language;\n if (window.screen) {\n context.screen = { w: window.screen.width ?? 0, h: window.screen.height ?? 0 };\n }\n return context;\n}\n","import type { AnalyticsClient, Properties } from \"@getdevteam/analytics-core\";\n\nexport function currentPageProperties(): Properties {\n return {\n url: window.location.href,\n title: document.title,\n };\n}\n\nexport function installPageviewTracking(client: AnalyticsClient): () => void {\n const sendPageview = (): void => client.page(currentPageProperties());\n const history = window.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n sendPageview();\n };\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n sendPageview();\n };\n window.addEventListener(\"popstate\", sendPageview);\n sendPageview();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", sendPageview);\n };\n}\n","import { createMemoryStorage, type StorageAdapter } from \"@getdevteam/analytics-core\";\n\nexport function resolveLocalStorage(): Storage | undefined {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const probe = \"__devteam_analytics_probe__\";\n window.localStorage.setItem(probe, \"1\");\n window.localStorage.removeItem(probe);\n return window.localStorage;\n } catch {\n return undefined;\n }\n}\n\nexport function createBrowserStorage(\n resolve: () => Storage | undefined = resolveLocalStorage,\n): StorageAdapter {\n const localStorage = resolve();\n if (!localStorage) return createMemoryStorage();\n return {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // quota or private-mode failure; queue persistence degrades gracefully\n }\n },\n removeItem: (key) => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n"],"mappings":"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","names":["import_analytics_core"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -24,7 +24,7 @@ declare function currentPageProperties(): Properties;
|
|
|
24
24
|
declare function installPageviewTracking(client: AnalyticsClient): () => void;
|
|
25
25
|
|
|
26
26
|
declare const WEB_SDK_NAME = "@getdevteam/analytics-web";
|
|
27
|
-
declare const WEB_SDK_VERSION = "0.
|
|
27
|
+
declare const WEB_SDK_VERSION = "0.2.0";
|
|
28
28
|
interface WebAnalyticsConfig extends ClientConfig {
|
|
29
29
|
autoCaptureErrors?: boolean;
|
|
30
30
|
trackPageviews?: boolean;
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,7 @@ declare function currentPageProperties(): Properties;
|
|
|
24
24
|
declare function installPageviewTracking(client: AnalyticsClient): () => void;
|
|
25
25
|
|
|
26
26
|
declare const WEB_SDK_NAME = "@getdevteam/analytics-web";
|
|
27
|
-
declare const WEB_SDK_VERSION = "0.
|
|
27
|
+
declare const WEB_SDK_VERSION = "0.2.0";
|
|
28
28
|
interface WebAnalyticsConfig extends ClientConfig {
|
|
29
29
|
autoCaptureErrors?: boolean;
|
|
30
30
|
trackPageviews?: boolean;
|
package/dist/index.js
CHANGED
|
@@ -130,7 +130,7 @@ function createBrowserStorage(resolve = resolveLocalStorage) {
|
|
|
130
130
|
|
|
131
131
|
// src/index.ts
|
|
132
132
|
var WEB_SDK_NAME = "@getdevteam/analytics-web";
|
|
133
|
-
var WEB_SDK_VERSION = "0.
|
|
133
|
+
var WEB_SDK_VERSION = "0.2.0";
|
|
134
134
|
function createAnalytics(config) {
|
|
135
135
|
let finalFlush = false;
|
|
136
136
|
const baseFetch = config.fetch ?? defaultBrowserFetch();
|
|
@@ -153,7 +153,7 @@ function createAnalytics(config) {
|
|
|
153
153
|
if (typeof window !== "undefined") {
|
|
154
154
|
const flushOnHide = () => {
|
|
155
155
|
finalFlush = true;
|
|
156
|
-
void client.flush().finally(() => {
|
|
156
|
+
void client.flush({ keepaliveConstrained: true }).finally(() => {
|
|
157
157
|
finalFlush = false;
|
|
158
158
|
});
|
|
159
159
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/pageviews.ts","../src/storage.ts"],"sourcesContent":["import {\n createClient,\n type AnalyticsClient,\n type ClientConfig,\n type EventContext,\n type FetchLike,\n type FetchRequestInit,\n type FetchResponseLike,\n} from \"@getdevteam/analytics-core\";\nimport { collectWebContext } from \"./context\";\nimport { installPageviewTracking } from \"./pageviews\";\nimport { createBrowserStorage } from \"./storage\";\n\nexport const WEB_SDK_NAME = \"@getdevteam/analytics-web\";\nexport const WEB_SDK_VERSION = \"0.1.0\";\n\nexport interface WebAnalyticsConfig extends ClientConfig {\n autoCaptureErrors?: boolean;\n trackPageviews?: boolean;\n context?: Partial<EventContext>;\n}\n\nexport function createAnalytics(config: WebAnalyticsConfig): AnalyticsClient {\n let finalFlush = false;\n const baseFetch = config.fetch ?? defaultBrowserFetch();\n\n const client = createClient({\n ...config,\n storage: config.storage ?? createBrowserStorage(),\n fetch: (url, init) => {\n if (finalFlush) return sendOnHide(url, init, config.key, baseFetch);\n if (baseFetch) return baseFetch(url, init);\n return Promise.reject(new Error(\"fetch is not available in this environment\"));\n },\n });\n\n client.setContext({\n sdk_name: WEB_SDK_NAME,\n sdk_version: WEB_SDK_VERSION,\n ...collectWebContext(),\n ...config.context,\n });\n\n const teardowns: Array<() => void> = [];\n\n if (typeof window !== \"undefined\") {\n const flushOnHide = (): void => {\n finalFlush = true;\n void client.flush().finally(() => {\n finalFlush = false;\n });\n };\n const onPageHide = (): void => flushOnHide();\n const onVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") flushOnHide();\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n teardowns.push(() => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n });\n\n if (config.autoCaptureErrors !== false) {\n const onError = (event: ErrorEvent): void => {\n client.captureError(event.error ?? event.message, { source: \"window.onerror\" });\n };\n const onRejection = (event: Event): void => {\n const reason = (event as { reason?: unknown }).reason;\n client.captureError(reason ?? \"unhandled promise rejection\", {\n source: \"unhandledrejection\",\n });\n };\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n teardowns.push(() => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n });\n }\n\n if (config.trackPageviews === true) {\n teardowns.push(installPageviewTracking(client));\n }\n }\n\n return {\n ...client,\n shutdown: async () => {\n for (const teardown of teardowns.splice(0)) {\n teardown();\n }\n await client.shutdown();\n },\n };\n}\n\nfunction defaultBrowserFetch(): FetchLike | undefined {\n if (typeof fetch !== \"function\") return undefined;\n return (url, init) => fetch(url, init);\n}\n\nfunction sendOnHide(\n url: string,\n init: FetchRequestInit,\n key: string,\n baseFetch: FetchLike | undefined,\n): Promise<FetchResponseLike> {\n if (baseFetch) {\n return baseFetch(url, { ...init, keepalive: true });\n }\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") {\n // sendBeacon cannot set headers, so the key rides as a query parameter as a last resort.\n const beaconUrl = `${url}${url.includes(\"?\") ? \"&\" : \"?\"}key=${encodeURIComponent(key)}`;\n const delivered = navigator.sendBeacon(\n beaconUrl,\n new Blob([init.body], { type: \"application/json\" }),\n );\n return Promise.resolve({ ok: delivered, status: delivered ? 202 : 503 });\n }\n return Promise.reject(new Error(\"no transport available for final flush\"));\n}\n\nexport { collectWebContext, browserFromUserAgent, osFromUserAgent, detectDeviceType } from \"./context\";\nexport { createBrowserStorage } from \"./storage\";\nexport { currentPageProperties, installPageviewTracking } from \"./pageviews\";\nexport type {\n AnalyticsClient,\n AnalyticsLogger,\n ClientConfig,\n EventContext,\n FetchLike,\n LogSeverity,\n Properties,\n StorageAdapter,\n} from \"@getdevteam/analytics-core\";\n","import type { EventContext } from \"@getdevteam/analytics-core\";\n\nexport function browserFromUserAgent(ua: string): string | undefined {\n if (/Edg(e|A|iOS)?\\//.test(ua)) return \"Edge\";\n if (/Firefox\\/|FxiOS\\//.test(ua)) return \"Firefox\";\n if (/CriOS\\/|Chrome\\//.test(ua)) return \"Chrome\";\n if (/Safari\\//.test(ua)) return \"Safari\";\n return undefined;\n}\n\nexport function osFromUserAgent(\n ua: string,\n platform?: string,\n): { os_name?: string; os_version?: string } {\n if (/Windows/.test(ua)) {\n const match = /Windows NT ([\\d.]+)/.exec(ua);\n return { os_name: \"Windows\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/iPhone|iPad|iPod/.test(ua)) {\n const match = /OS (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"iOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Android/.test(ua)) {\n const match = /Android ([\\d.]+)/.exec(ua);\n return { os_name: \"Android\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/Mac OS X|Macintosh/.test(ua) || (platform ?? \"\").startsWith(\"Mac\")) {\n const match = /Mac OS X (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"macOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Linux/.test(ua) || (platform ?? \"\").startsWith(\"Linux\")) {\n return { os_name: \"Linux\" };\n }\n return {};\n}\n\ninterface WindowLike {\n matchMedia?: (query: string) => { matches: boolean };\n innerWidth?: number;\n screen?: { width?: number };\n}\n\nexport function detectDeviceType(win: WindowLike): string {\n try {\n const coarse =\n typeof win.matchMedia === \"function\" && win.matchMedia(\"(pointer: coarse)\").matches;\n if (!coarse) return \"desktop\";\n const width = win.innerWidth || win.screen?.width || 0;\n return width >= 768 ? \"tablet\" : \"mobile\";\n } catch {\n return \"desktop\";\n }\n}\n\nexport function collectWebContext(): EventContext {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return { platform: \"web\" };\n }\n const ua = navigator.userAgent ?? \"\";\n const context: EventContext = { platform: \"web\" };\n const browser = browserFromUserAgent(ua);\n if (browser !== undefined) context.browser = browser;\n Object.assign(context, osFromUserAgent(ua, navigator.platform));\n context.device_type = detectDeviceType(window);\n if (navigator.language) context.locale = navigator.language;\n if (window.screen) {\n context.screen = { w: window.screen.width ?? 0, h: window.screen.height ?? 0 };\n }\n return context;\n}\n","import type { AnalyticsClient, Properties } from \"@getdevteam/analytics-core\";\n\nexport function currentPageProperties(): Properties {\n return {\n url: window.location.href,\n title: document.title,\n };\n}\n\nexport function installPageviewTracking(client: AnalyticsClient): () => void {\n const sendPageview = (): void => client.page(currentPageProperties());\n const history = window.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n sendPageview();\n };\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n sendPageview();\n };\n window.addEventListener(\"popstate\", sendPageview);\n sendPageview();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", sendPageview);\n };\n}\n","import { createMemoryStorage, type StorageAdapter } from \"@getdevteam/analytics-core\";\n\nexport function resolveLocalStorage(): Storage | undefined {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const probe = \"__devteam_analytics_probe__\";\n window.localStorage.setItem(probe, \"1\");\n window.localStorage.removeItem(probe);\n return window.localStorage;\n } catch {\n return undefined;\n }\n}\n\nexport function createBrowserStorage(\n resolve: () => Storage | undefined = resolveLocalStorage,\n): StorageAdapter {\n const localStorage = resolve();\n if (!localStorage) return createMemoryStorage();\n return {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // quota or private-mode failure; queue persistence degrades gracefully\n }\n },\n removeItem: (key) => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/context.ts","../src/pageviews.ts","../src/storage.ts"],"sourcesContent":["import {\n createClient,\n type AnalyticsClient,\n type ClientConfig,\n type EventContext,\n type FetchLike,\n type FetchRequestInit,\n type FetchResponseLike,\n} from \"@getdevteam/analytics-core\";\nimport { collectWebContext } from \"./context\";\nimport { installPageviewTracking } from \"./pageviews\";\nimport { createBrowserStorage } from \"./storage\";\n\nexport const WEB_SDK_NAME = \"@getdevteam/analytics-web\";\nexport const WEB_SDK_VERSION = \"0.2.0\";\n\nexport interface WebAnalyticsConfig extends ClientConfig {\n autoCaptureErrors?: boolean;\n trackPageviews?: boolean;\n context?: Partial<EventContext>;\n}\n\nexport function createAnalytics(config: WebAnalyticsConfig): AnalyticsClient {\n let finalFlush = false;\n const baseFetch = config.fetch ?? defaultBrowserFetch();\n\n const client = createClient({\n ...config,\n storage: config.storage ?? createBrowserStorage(),\n fetch: (url, init) => {\n if (finalFlush) return sendOnHide(url, init, config.key, baseFetch);\n if (baseFetch) return baseFetch(url, init);\n return Promise.reject(new Error(\"fetch is not available in this environment\"));\n },\n });\n\n client.setContext({\n sdk_name: WEB_SDK_NAME,\n sdk_version: WEB_SDK_VERSION,\n ...collectWebContext(),\n ...config.context,\n });\n\n const teardowns: Array<() => void> = [];\n\n if (typeof window !== \"undefined\") {\n const flushOnHide = (): void => {\n finalFlush = true;\n // sendOnHide delivers over `keepalive`, so constrain each batch under the browser's 64 KiB keepalive body\n // limit. The first sub-request always fits the quota; the rest are best-effort (they drain fully when the\n // tab is hidden but alive; on a real teardown the remainder stays persisted for the next visit).\n void client.flush({ keepaliveConstrained: true }).finally(() => {\n finalFlush = false;\n });\n };\n const onPageHide = (): void => flushOnHide();\n const onVisibilityChange = (): void => {\n if (document.visibilityState === \"hidden\") flushOnHide();\n };\n window.addEventListener(\"pagehide\", onPageHide);\n document.addEventListener(\"visibilitychange\", onVisibilityChange);\n teardowns.push(() => {\n window.removeEventListener(\"pagehide\", onPageHide);\n document.removeEventListener(\"visibilitychange\", onVisibilityChange);\n });\n\n if (config.autoCaptureErrors !== false) {\n const onError = (event: ErrorEvent): void => {\n client.captureError(event.error ?? event.message, { source: \"window.onerror\" });\n };\n const onRejection = (event: Event): void => {\n const reason = (event as { reason?: unknown }).reason;\n client.captureError(reason ?? \"unhandled promise rejection\", {\n source: \"unhandledrejection\",\n });\n };\n window.addEventListener(\"error\", onError);\n window.addEventListener(\"unhandledrejection\", onRejection);\n teardowns.push(() => {\n window.removeEventListener(\"error\", onError);\n window.removeEventListener(\"unhandledrejection\", onRejection);\n });\n }\n\n if (config.trackPageviews === true) {\n teardowns.push(installPageviewTracking(client));\n }\n }\n\n return {\n ...client,\n shutdown: async () => {\n for (const teardown of teardowns.splice(0)) {\n teardown();\n }\n await client.shutdown();\n },\n };\n}\n\nfunction defaultBrowserFetch(): FetchLike | undefined {\n if (typeof fetch !== \"function\") return undefined;\n return (url, init) => fetch(url, init);\n}\n\nfunction sendOnHide(\n url: string,\n init: FetchRequestInit,\n key: string,\n baseFetch: FetchLike | undefined,\n): Promise<FetchResponseLike> {\n if (baseFetch) {\n return baseFetch(url, { ...init, keepalive: true });\n }\n if (typeof navigator !== \"undefined\" && typeof navigator.sendBeacon === \"function\") {\n // sendBeacon cannot set headers, so the key rides as a query parameter as a last resort.\n const beaconUrl = `${url}${url.includes(\"?\") ? \"&\" : \"?\"}key=${encodeURIComponent(key)}`;\n const delivered = navigator.sendBeacon(\n beaconUrl,\n new Blob([init.body], { type: \"application/json\" }),\n );\n return Promise.resolve({ ok: delivered, status: delivered ? 202 : 503 });\n }\n return Promise.reject(new Error(\"no transport available for final flush\"));\n}\n\nexport { collectWebContext, browserFromUserAgent, osFromUserAgent, detectDeviceType } from \"./context\";\nexport { createBrowserStorage } from \"./storage\";\nexport { currentPageProperties, installPageviewTracking } from \"./pageviews\";\nexport type {\n AnalyticsClient,\n AnalyticsLogger,\n ClientConfig,\n EventContext,\n FetchLike,\n LogSeverity,\n Properties,\n StorageAdapter,\n} from \"@getdevteam/analytics-core\";\n","import type { EventContext } from \"@getdevteam/analytics-core\";\n\nexport function browserFromUserAgent(ua: string): string | undefined {\n if (/Edg(e|A|iOS)?\\//.test(ua)) return \"Edge\";\n if (/Firefox\\/|FxiOS\\//.test(ua)) return \"Firefox\";\n if (/CriOS\\/|Chrome\\//.test(ua)) return \"Chrome\";\n if (/Safari\\//.test(ua)) return \"Safari\";\n return undefined;\n}\n\nexport function osFromUserAgent(\n ua: string,\n platform?: string,\n): { os_name?: string; os_version?: string } {\n if (/Windows/.test(ua)) {\n const match = /Windows NT ([\\d.]+)/.exec(ua);\n return { os_name: \"Windows\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/iPhone|iPad|iPod/.test(ua)) {\n const match = /OS (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"iOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Android/.test(ua)) {\n const match = /Android ([\\d.]+)/.exec(ua);\n return { os_name: \"Android\", ...(match?.[1] ? { os_version: match[1] } : {}) };\n }\n if (/Mac OS X|Macintosh/.test(ua) || (platform ?? \"\").startsWith(\"Mac\")) {\n const match = /Mac OS X (\\d+[_.]\\d+(?:[_.]\\d+)?)/.exec(ua);\n return { os_name: \"macOS\", ...(match?.[1] ? { os_version: match[1].replace(/_/g, \".\") } : {}) };\n }\n if (/Linux/.test(ua) || (platform ?? \"\").startsWith(\"Linux\")) {\n return { os_name: \"Linux\" };\n }\n return {};\n}\n\ninterface WindowLike {\n matchMedia?: (query: string) => { matches: boolean };\n innerWidth?: number;\n screen?: { width?: number };\n}\n\nexport function detectDeviceType(win: WindowLike): string {\n try {\n const coarse =\n typeof win.matchMedia === \"function\" && win.matchMedia(\"(pointer: coarse)\").matches;\n if (!coarse) return \"desktop\";\n const width = win.innerWidth || win.screen?.width || 0;\n return width >= 768 ? \"tablet\" : \"mobile\";\n } catch {\n return \"desktop\";\n }\n}\n\nexport function collectWebContext(): EventContext {\n if (typeof window === \"undefined\" || typeof navigator === \"undefined\") {\n return { platform: \"web\" };\n }\n const ua = navigator.userAgent ?? \"\";\n const context: EventContext = { platform: \"web\" };\n const browser = browserFromUserAgent(ua);\n if (browser !== undefined) context.browser = browser;\n Object.assign(context, osFromUserAgent(ua, navigator.platform));\n context.device_type = detectDeviceType(window);\n if (navigator.language) context.locale = navigator.language;\n if (window.screen) {\n context.screen = { w: window.screen.width ?? 0, h: window.screen.height ?? 0 };\n }\n return context;\n}\n","import type { AnalyticsClient, Properties } from \"@getdevteam/analytics-core\";\n\nexport function currentPageProperties(): Properties {\n return {\n url: window.location.href,\n title: document.title,\n };\n}\n\nexport function installPageviewTracking(client: AnalyticsClient): () => void {\n const sendPageview = (): void => client.page(currentPageProperties());\n const history = window.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n history.pushState = (...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n sendPageview();\n };\n history.replaceState = (...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n sendPageview();\n };\n window.addEventListener(\"popstate\", sendPageview);\n sendPageview();\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n window.removeEventListener(\"popstate\", sendPageview);\n };\n}\n","import { createMemoryStorage, type StorageAdapter } from \"@getdevteam/analytics-core\";\n\nexport function resolveLocalStorage(): Storage | undefined {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const probe = \"__devteam_analytics_probe__\";\n window.localStorage.setItem(probe, \"1\");\n window.localStorage.removeItem(probe);\n return window.localStorage;\n } catch {\n return undefined;\n }\n}\n\nexport function createBrowserStorage(\n resolve: () => Storage | undefined = resolveLocalStorage,\n): StorageAdapter {\n const localStorage = resolve();\n if (!localStorage) return createMemoryStorage();\n return {\n getItem: (key) => {\n try {\n return localStorage.getItem(key);\n } catch {\n return null;\n }\n },\n setItem: (key, value) => {\n try {\n localStorage.setItem(key, value);\n } catch {\n // quota or private-mode failure; queue persistence degrades gracefully\n }\n },\n removeItem: (key) => {\n try {\n localStorage.removeItem(key);\n } catch {\n // ignore\n }\n },\n };\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,OAOK;;;ACNA,SAAS,qBAAqB,IAAgC;AACnE,MAAI,kBAAkB,KAAK,EAAE,EAAG,QAAO;AACvC,MAAI,oBAAoB,KAAK,EAAE,EAAG,QAAO;AACzC,MAAI,mBAAmB,KAAK,EAAE,EAAG,QAAO;AACxC,MAAI,WAAW,KAAK,EAAE,EAAG,QAAO;AAChC,SAAO;AACT;AAEO,SAAS,gBACd,IACA,UAC2C;AAC3C,MAAI,UAAU,KAAK,EAAE,GAAG;AACtB,UAAM,QAAQ,sBAAsB,KAAK,EAAE;AAC3C,WAAO,EAAE,SAAS,WAAW,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,IAAI,CAAC,EAAG;AAAA,EAC/E;AACA,MAAI,mBAAmB,KAAK,EAAE,GAAG;AAC/B,UAAM,QAAQ,8BAA8B,KAAK,EAAE;AACnD,WAAO,EAAE,SAAS,OAAO,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,EAAG;AAAA,EAC9F;AACA,MAAI,UAAU,KAAK,EAAE,GAAG;AACtB,UAAM,QAAQ,mBAAmB,KAAK,EAAE;AACxC,WAAO,EAAE,SAAS,WAAW,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,IAAI,CAAC,EAAG;AAAA,EAC/E;AACA,MAAI,qBAAqB,KAAK,EAAE,MAAM,YAAY,IAAI,WAAW,KAAK,GAAG;AACvE,UAAM,QAAQ,oCAAoC,KAAK,EAAE;AACzD,WAAO,EAAE,SAAS,SAAS,GAAI,QAAQ,CAAC,IAAI,EAAE,YAAY,MAAM,CAAC,EAAE,QAAQ,MAAM,GAAG,EAAE,IAAI,CAAC,EAAG;AAAA,EAChG;AACA,MAAI,QAAQ,KAAK,EAAE,MAAM,YAAY,IAAI,WAAW,OAAO,GAAG;AAC5D,WAAO,EAAE,SAAS,QAAQ;AAAA,EAC5B;AACA,SAAO,CAAC;AACV;AAQO,SAAS,iBAAiB,KAAyB;AACxD,MAAI;AACF,UAAM,SACJ,OAAO,IAAI,eAAe,cAAc,IAAI,WAAW,mBAAmB,EAAE;AAC9E,QAAI,CAAC,OAAQ,QAAO;AACpB,UAAM,QAAQ,IAAI,cAAc,IAAI,QAAQ,SAAS;AACrD,WAAO,SAAS,MAAM,WAAW;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,oBAAkC;AAChD,MAAI,OAAO,WAAW,eAAe,OAAO,cAAc,aAAa;AACrE,WAAO,EAAE,UAAU,MAAM;AAAA,EAC3B;AACA,QAAM,KAAK,UAAU,aAAa;AAClC,QAAM,UAAwB,EAAE,UAAU,MAAM;AAChD,QAAM,UAAU,qBAAqB,EAAE;AACvC,MAAI,YAAY,OAAW,SAAQ,UAAU;AAC7C,SAAO,OAAO,SAAS,gBAAgB,IAAI,UAAU,QAAQ,CAAC;AAC9D,UAAQ,cAAc,iBAAiB,MAAM;AAC7C,MAAI,UAAU,SAAU,SAAQ,SAAS,UAAU;AACnD,MAAI,OAAO,QAAQ;AACjB,YAAQ,SAAS,EAAE,GAAG,OAAO,OAAO,SAAS,GAAG,GAAG,OAAO,OAAO,UAAU,EAAE;AAAA,EAC/E;AACA,SAAO;AACT;;;ACnEO,SAAS,wBAAoC;AAClD,SAAO;AAAA,IACL,KAAK,OAAO,SAAS;AAAA,IACrB,OAAO,SAAS;AAAA,EAClB;AACF;AAEO,SAAS,wBAAwB,QAAqC;AAC3E,QAAM,eAAe,MAAY,OAAO,KAAK,sBAAsB,CAAC;AACpE,QAAM,UAAU,OAAO;AACvB,QAAM,oBAAoB,QAAQ,UAAU,KAAK,OAAO;AACxD,QAAM,uBAAuB,QAAQ,aAAa,KAAK,OAAO;AAE9D,UAAQ,YAAY,IAAI,SAA2C;AACjE,sBAAkB,GAAG,IAAI;AACzB,iBAAa;AAAA,EACf;AACA,UAAQ,eAAe,IAAI,SAA8C;AACvE,yBAAqB,GAAG,IAAI;AAC5B,iBAAa;AAAA,EACf;AACA,SAAO,iBAAiB,YAAY,YAAY;AAChD,eAAa;AAEb,SAAO,MAAM;AACX,YAAQ,YAAY;AACpB,YAAQ,eAAe;AACvB,WAAO,oBAAoB,YAAY,YAAY;AAAA,EACrD;AACF;;;AC/BA,SAAS,2BAAgD;AAElD,SAAS,sBAA2C;AACzD,MAAI;AACF,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO,aAAc,QAAO;AAClE,UAAM,QAAQ;AACd,WAAO,aAAa,QAAQ,OAAO,GAAG;AACtC,WAAO,aAAa,WAAW,KAAK;AACpC,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,qBACd,UAAqC,qBACrB;AAChB,QAAM,eAAe,QAAQ;AAC7B,MAAI,CAAC,aAAc,QAAO,oBAAoB;AAC9C,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ;AAChB,UAAI;AACF,eAAO,aAAa,QAAQ,GAAG;AAAA,MACjC,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,SAAS,CAAC,KAAK,UAAU;AACvB,UAAI;AACF,qBAAa,QAAQ,KAAK,KAAK;AAAA,MACjC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,IACA,YAAY,CAAC,QAAQ;AACnB,UAAI;AACF,qBAAa,WAAW,GAAG;AAAA,MAC7B,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;;;AH7BO,IAAM,eAAe;AACrB,IAAM,kBAAkB;AAQxB,SAAS,gBAAgB,QAA6C;AAC3E,MAAI,aAAa;AACjB,QAAM,YAAY,OAAO,SAAS,oBAAoB;AAEtD,QAAM,SAAS,aAAa;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS,OAAO,WAAW,qBAAqB;AAAA,IAChD,OAAO,CAAC,KAAK,SAAS;AACpB,UAAI,WAAY,QAAO,WAAW,KAAK,MAAM,OAAO,KAAK,SAAS;AAClE,UAAI,UAAW,QAAO,UAAU,KAAK,IAAI;AACzC,aAAO,QAAQ,OAAO,IAAI,MAAM,4CAA4C,CAAC;AAAA,IAC/E;AAAA,EACF,CAAC;AAED,SAAO,WAAW;AAAA,IAChB,UAAU;AAAA,IACV,aAAa;AAAA,IACb,GAAG,kBAAkB;AAAA,IACrB,GAAG,OAAO;AAAA,EACZ,CAAC;AAED,QAAM,YAA+B,CAAC;AAEtC,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,cAAc,MAAY;AAC9B,mBAAa;AAIb,WAAK,OAAO,MAAM,EAAE,sBAAsB,KAAK,CAAC,EAAE,QAAQ,MAAM;AAC9D,qBAAa;AAAA,MACf,CAAC;AAAA,IACH;AACA,UAAM,aAAa,MAAY,YAAY;AAC3C,UAAM,qBAAqB,MAAY;AACrC,UAAI,SAAS,oBAAoB,SAAU,aAAY;AAAA,IACzD;AACA,WAAO,iBAAiB,YAAY,UAAU;AAC9C,aAAS,iBAAiB,oBAAoB,kBAAkB;AAChE,cAAU,KAAK,MAAM;AACnB,aAAO,oBAAoB,YAAY,UAAU;AACjD,eAAS,oBAAoB,oBAAoB,kBAAkB;AAAA,IACrE,CAAC;AAED,QAAI,OAAO,sBAAsB,OAAO;AACtC,YAAM,UAAU,CAAC,UAA4B;AAC3C,eAAO,aAAa,MAAM,SAAS,MAAM,SAAS,EAAE,QAAQ,iBAAiB,CAAC;AAAA,MAChF;AACA,YAAM,cAAc,CAAC,UAAuB;AAC1C,cAAM,SAAU,MAA+B;AAC/C,eAAO,aAAa,UAAU,+BAA+B;AAAA,UAC3D,QAAQ;AAAA,QACV,CAAC;AAAA,MACH;AACA,aAAO,iBAAiB,SAAS,OAAO;AACxC,aAAO,iBAAiB,sBAAsB,WAAW;AACzD,gBAAU,KAAK,MAAM;AACnB,eAAO,oBAAoB,SAAS,OAAO;AAC3C,eAAO,oBAAoB,sBAAsB,WAAW;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,mBAAmB,MAAM;AAClC,gBAAU,KAAK,wBAAwB,MAAM,CAAC;AAAA,IAChD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,UAAU,YAAY;AACpB,iBAAW,YAAY,UAAU,OAAO,CAAC,GAAG;AAC1C,iBAAS;AAAA,MACX;AACA,YAAM,OAAO,SAAS;AAAA,IACxB;AAAA,EACF;AACF;AAEA,SAAS,sBAA6C;AACpD,MAAI,OAAO,UAAU,WAAY,QAAO;AACxC,SAAO,CAAC,KAAK,SAAS,MAAM,KAAK,IAAI;AACvC;AAEA,SAAS,WACP,KACA,MACA,KACA,WAC4B;AAC5B,MAAI,WAAW;AACb,WAAO,UAAU,KAAK,EAAE,GAAG,MAAM,WAAW,KAAK,CAAC;AAAA,EACpD;AACA,MAAI,OAAO,cAAc,eAAe,OAAO,UAAU,eAAe,YAAY;AAElF,UAAM,YAAY,GAAG,GAAG,GAAG,IAAI,SAAS,GAAG,IAAI,MAAM,GAAG,OAAO,mBAAmB,GAAG,CAAC;AACtF,UAAM,YAAY,UAAU;AAAA,MAC1B;AAAA,MACA,IAAI,KAAK,CAAC,KAAK,IAAI,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAAA,IACpD;AACA,WAAO,QAAQ,QAAQ,EAAE,IAAI,WAAW,QAAQ,YAAY,MAAM,IAAI,CAAC;AAAA,EACzE;AACA,SAAO,QAAQ,OAAO,IAAI,MAAM,wCAAwC,CAAC;AAC3E;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getdevteam/analytics-web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Browser SDK for the DevTeam analytics and logging platform.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"typecheck": "tsc --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@getdevteam/analytics-core": "0.
|
|
38
|
+
"@getdevteam/analytics-core": "0.2.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"jsdom": "^26.1.0",
|