@getdevteam/analytics-web 0.1.0-dev.2 → 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/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.1.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
  };
@@ -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.1.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.1.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.1.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":";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;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":[]}
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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getdevteam/analytics-web",
3
- "version": "0.1.0-dev.2",
3
+ "version": "0.2.0",
4
4
  "description": "Browser SDK for the DevTeam analytics and logging platform.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,9 +24,7 @@
24
24
  }
25
25
  }
26
26
  },
27
- "files": [
28
- "dist"
29
- ],
27
+ "files": ["dist"],
30
28
  "sideEffects": false,
31
29
  "publishConfig": {
32
30
  "access": "public"
@@ -37,7 +35,7 @@
37
35
  "typecheck": "tsc --noEmit"
38
36
  },
39
37
  "dependencies": {
40
- "@getdevteam/analytics-core": "0.1.0-dev.2"
38
+ "@getdevteam/analytics-core": "0.2.0"
41
39
  },
42
40
  "devDependencies": {
43
41
  "jsdom": "^26.1.0",