@interfere/react 9.0.1 → 10.0.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.
Files changed (187) hide show
  1. package/README.md +4 -4
  2. package/dist/api.d.mts +25 -0
  3. package/dist/api.d.mts.map +1 -0
  4. package/dist/api.mjs +68 -0
  5. package/dist/api.mjs.map +1 -0
  6. package/dist/error-boundary.d.mts +11 -4
  7. package/dist/error-boundary.d.mts.map +1 -1
  8. package/dist/error-boundary.mjs +6 -3
  9. package/dist/error-boundary.mjs.map +1 -1
  10. package/dist/internal/browser-context.d.mts +6 -0
  11. package/dist/internal/browser-context.d.mts.map +1 -0
  12. package/dist/internal/browser-context.mjs +59 -0
  13. package/dist/internal/browser-context.mjs.map +1 -0
  14. package/dist/internal/capture-boundary.d.mts +5 -1
  15. package/dist/internal/capture-boundary.d.mts.map +1 -1
  16. package/dist/internal/capture-boundary.mjs +9 -5
  17. package/dist/internal/capture-boundary.mjs.map +1 -1
  18. package/dist/internal/capture.d.mts +16 -5
  19. package/dist/internal/capture.d.mts.map +1 -1
  20. package/dist/internal/capture.mjs +20 -16
  21. package/dist/internal/capture.mjs.map +1 -1
  22. package/dist/internal/config.d.mts +20 -4
  23. package/dist/internal/config.d.mts.map +1 -1
  24. package/dist/internal/config.mjs +12 -12
  25. package/dist/internal/config.mjs.map +1 -1
  26. package/dist/internal/consent.d.mts.map +1 -1
  27. package/dist/internal/consent.mjs +3 -1
  28. package/dist/internal/consent.mjs.map +1 -1
  29. package/dist/internal/console-patch.d.mts +19 -0
  30. package/dist/internal/console-patch.d.mts.map +1 -0
  31. package/dist/internal/console-patch.mjs +62 -0
  32. package/dist/internal/console-patch.mjs.map +1 -0
  33. package/dist/internal/dom/actionable.d.mts +27 -0
  34. package/dist/internal/dom/actionable.d.mts.map +1 -0
  35. package/dist/internal/dom/actionable.mjs +62 -0
  36. package/dist/internal/dom/actionable.mjs.map +1 -0
  37. package/dist/internal/kernel-registry.d.mts +8 -0
  38. package/dist/internal/kernel-registry.d.mts.map +1 -0
  39. package/dist/internal/kernel-registry.mjs +31 -0
  40. package/dist/internal/kernel-registry.mjs.map +1 -0
  41. package/dist/internal/kernel.d.mts +267 -0
  42. package/dist/internal/kernel.d.mts.map +1 -0
  43. package/dist/internal/kernel.mjs +322 -0
  44. package/dist/internal/kernel.mjs.map +1 -0
  45. package/dist/internal/otel/exporter.d.mts +93 -0
  46. package/dist/internal/otel/exporter.d.mts.map +1 -0
  47. package/dist/internal/otel/exporter.mjs +212 -0
  48. package/dist/internal/otel/exporter.mjs.map +1 -0
  49. package/dist/internal/otel/index.d.mts +6 -0
  50. package/dist/internal/otel/index.mjs +6 -0
  51. package/dist/internal/otel/instrumentations.d.mts +42 -0
  52. package/dist/internal/otel/instrumentations.d.mts.map +1 -0
  53. package/dist/internal/otel/instrumentations.mjs +150 -0
  54. package/dist/internal/otel/instrumentations.mjs.map +1 -0
  55. package/dist/internal/otel/page-scope-context-manager.d.mts +32 -0
  56. package/dist/internal/otel/page-scope-context-manager.d.mts.map +1 -0
  57. package/dist/internal/otel/page-scope-context-manager.mjs +36 -0
  58. package/dist/internal/otel/page-scope-context-manager.mjs.map +1 -0
  59. package/dist/internal/otel/propagation.d.mts +21 -0
  60. package/dist/internal/otel/propagation.d.mts.map +1 -0
  61. package/dist/internal/otel/propagation.mjs +40 -0
  62. package/dist/internal/otel/propagation.mjs.map +1 -0
  63. package/dist/internal/otel/provider.d.mts +107 -0
  64. package/dist/internal/otel/provider.d.mts.map +1 -0
  65. package/dist/internal/otel/provider.mjs +151 -0
  66. package/dist/internal/otel/provider.mjs.map +1 -0
  67. package/dist/internal/otel/web-vitals.d.mts +35 -0
  68. package/dist/internal/otel/web-vitals.d.mts.map +1 -0
  69. package/dist/internal/otel/web-vitals.mjs +162 -0
  70. package/dist/internal/otel/web-vitals.mjs.map +1 -0
  71. package/dist/internal/page-lifecycle.d.mts +21 -0
  72. package/dist/internal/page-lifecycle.d.mts.map +1 -0
  73. package/dist/internal/page-lifecycle.mjs +33 -0
  74. package/dist/internal/page-lifecycle.mjs.map +1 -0
  75. package/dist/internal/plugin-runtime.d.mts +0 -2
  76. package/dist/internal/plugin-runtime.d.mts.map +1 -1
  77. package/dist/internal/plugin-runtime.mjs +1 -7
  78. package/dist/internal/plugin-runtime.mjs.map +1 -1
  79. package/dist/internal/react-context.d.mts +45 -0
  80. package/dist/internal/react-context.d.mts.map +1 -0
  81. package/dist/internal/react-context.mjs +34 -0
  82. package/dist/internal/react-context.mjs.map +1 -0
  83. package/dist/internal/sw.d.mts +22 -2
  84. package/dist/internal/sw.d.mts.map +1 -1
  85. package/dist/internal/sw.mjs +30 -3
  86. package/dist/internal/sw.mjs.map +1 -1
  87. package/dist/internal/version.d.mts +3 -1
  88. package/dist/internal/version.d.mts.map +1 -1
  89. package/dist/internal/version.mjs +4 -2
  90. package/dist/internal/version.mjs.map +1 -1
  91. package/dist/internal/wrapper-singleton.d.mts +47 -0
  92. package/dist/internal/wrapper-singleton.d.mts.map +1 -0
  93. package/dist/internal/wrapper-singleton.mjs +73 -0
  94. package/dist/internal/wrapper-singleton.mjs.map +1 -0
  95. package/dist/package.mjs +1 -1
  96. package/dist/plugins/errors.d.mts.map +1 -1
  97. package/dist/plugins/errors.mjs +18 -25
  98. package/dist/plugins/errors.mjs.map +1 -1
  99. package/dist/plugins/lib/loader.d.mts +1 -2
  100. package/dist/plugins/lib/loader.d.mts.map +1 -1
  101. package/dist/plugins/lib/loader.mjs +2 -11
  102. package/dist/plugins/lib/loader.mjs.map +1 -1
  103. package/dist/plugins/lib/types.d.mts +3 -2
  104. package/dist/plugins/lib/types.d.mts.map +1 -1
  105. package/dist/plugins/logs.d.mts +13 -0
  106. package/dist/plugins/logs.d.mts.map +1 -0
  107. package/dist/plugins/logs.mjs +53 -0
  108. package/dist/plugins/logs.mjs.map +1 -0
  109. package/dist/plugins/rage-clicks.d.mts.map +1 -1
  110. package/dist/plugins/rage-clicks.mjs +12 -10
  111. package/dist/plugins/rage-clicks.mjs.map +1 -1
  112. package/dist/plugins/replay.d.mts.map +1 -1
  113. package/dist/plugins/replay.mjs +58 -19
  114. package/dist/plugins/replay.mjs.map +1 -1
  115. package/dist/provider.d.mts +11 -20
  116. package/dist/provider.d.mts.map +1 -1
  117. package/dist/provider.mjs +13 -14
  118. package/dist/provider.mjs.map +1 -1
  119. package/dist/react-error-handler.d.mts +21 -5
  120. package/dist/react-error-handler.d.mts.map +1 -1
  121. package/dist/react-error-handler.mjs +15 -7
  122. package/dist/react-error-handler.mjs.map +1 -1
  123. package/dist/sw.d.mts +2 -0
  124. package/dist/sw.mjs +2 -0
  125. package/dist/tracking/api.d.mts +41 -15
  126. package/dist/tracking/api.d.mts.map +1 -1
  127. package/dist/tracking/api.mjs +122 -104
  128. package/dist/tracking/api.mjs.map +1 -1
  129. package/dist/tracking/device.d.mts +30 -7
  130. package/dist/tracking/device.d.mts.map +1 -1
  131. package/dist/tracking/device.mjs +70 -46
  132. package/dist/tracking/device.mjs.map +1 -1
  133. package/dist/tracking/geo.d.mts +11 -3
  134. package/dist/tracking/geo.d.mts.map +1 -1
  135. package/dist/tracking/geo.mjs +33 -29
  136. package/dist/tracking/geo.mjs.map +1 -1
  137. package/dist/tracking/session.d.mts +3 -1
  138. package/dist/tracking/session.d.mts.map +1 -1
  139. package/dist/tracking/session.mjs.map +1 -1
  140. package/dist/util/bot.d.mts +10 -0
  141. package/dist/util/bot.d.mts.map +1 -0
  142. package/dist/util/bot.mjs +14 -0
  143. package/dist/util/bot.mjs.map +1 -0
  144. package/dist/util/global.d.mts +10 -0
  145. package/dist/util/global.d.mts.map +1 -0
  146. package/dist/util/global.mjs +12 -0
  147. package/dist/util/global.mjs.map +1 -0
  148. package/dist/util/log.d.mts.map +1 -1
  149. package/dist/util/log.mjs +8 -1
  150. package/dist/util/log.mjs.map +1 -1
  151. package/dist/util/stringify.d.mts +9 -0
  152. package/dist/util/stringify.d.mts.map +1 -0
  153. package/dist/util/stringify.mjs +16 -0
  154. package/dist/util/stringify.mjs.map +1 -0
  155. package/package.json +73 -20
  156. package/dist/internal/client.d.mts +0 -48
  157. package/dist/internal/client.d.mts.map +0 -1
  158. package/dist/internal/client.mjs +0 -146
  159. package/dist/internal/client.mjs.map +0 -1
  160. package/dist/internal/context.d.mts +0 -6
  161. package/dist/internal/context.d.mts.map +0 -1
  162. package/dist/internal/context.mjs +0 -32
  163. package/dist/internal/context.mjs.map +0 -1
  164. package/dist/internal/envelope.d.mts +0 -15
  165. package/dist/internal/envelope.d.mts.map +0 -1
  166. package/dist/internal/envelope.mjs +0 -24
  167. package/dist/internal/envelope.mjs.map +0 -1
  168. package/dist/internal/errors.d.mts +0 -4
  169. package/dist/internal/errors.d.mts.map +0 -1
  170. package/dist/internal/errors.mjs +0 -4
  171. package/dist/internal/errors.mjs.map +0 -1
  172. package/dist/plugins/device.d.mts +0 -6
  173. package/dist/plugins/device.d.mts.map +0 -1
  174. package/dist/plugins/device.mjs +0 -13
  175. package/dist/plugins/device.mjs.map +0 -1
  176. package/dist/plugins/pages.d.mts +0 -6
  177. package/dist/plugins/pages.d.mts.map +0 -1
  178. package/dist/plugins/pages.mjs +0 -102
  179. package/dist/plugins/pages.mjs.map +0 -1
  180. package/dist/transport/http.d.mts +0 -21
  181. package/dist/transport/http.d.mts.map +0 -1
  182. package/dist/transport/http.mjs +0 -72
  183. package/dist/transport/http.mjs.map +0 -1
  184. package/dist/transport/queue.d.mts +0 -34
  185. package/dist/transport/queue.d.mts.map +0 -1
  186. package/dist/transport/queue.mjs +0 -95
  187. package/dist/transport/queue.mjs.map +0 -1
@@ -1,9 +1,6 @@
1
1
  import { cloudflareTraceSchema } from "@interfere/types/sdk/geo";
2
2
  //#region src/tracking/geo.ts
3
3
  const CF_TRACE_URL = "https://cloudflare.com/cdn-cgi/trace";
4
- let cached = null;
5
- let pending = null;
6
- let failed = false;
7
4
  function parseTrace(text) {
8
5
  const result = {};
9
6
  for (const line of text.split("\n")) {
@@ -12,33 +9,40 @@ function parseTrace(text) {
12
9
  }
13
10
  return result;
14
11
  }
15
- async function fetchCountryCode() {
16
- try {
17
- const res = await fetch(CF_TRACE_URL, { signal: AbortSignal.timeout(3e3) });
18
- if (!res.ok) return null;
19
- const raw = parseTrace(await res.text());
20
- const trace = cloudflareTraceSchema.safeParse(raw);
21
- if (!trace.success) return null;
22
- return trace.data.loc.toUpperCase();
23
- } catch {
24
- return null;
25
- }
26
- }
27
- function detectCountryCode() {
28
- if (cached) return Promise.resolve(cached);
29
- if (failed) return Promise.resolve(null);
30
- if (!pending) pending = fetchCountryCode().then((code) => {
31
- cached = code;
32
- if (!code) failed = true;
33
- pending = null;
34
- return code;
35
- });
36
- return pending;
37
- }
38
- function resetGeo() {
12
+ var GeoDetector = class {
39
13
  cached = null;
40
14
  pending = null;
41
15
  failed = false;
42
- }
16
+ fetcher;
17
+ constructor(fetcher) {
18
+ this.fetcher = fetcher ?? globalThis.fetch.bind(globalThis);
19
+ }
20
+ detect() {
21
+ if (this.cached) return Promise.resolve(this.cached);
22
+ if (this.failed) return Promise.resolve(null);
23
+ if (!this.pending) this.pending = this.fetchCountryCode().then((code) => {
24
+ this.cached = code;
25
+ if (!code) this.failed = true;
26
+ this.pending = null;
27
+ return code;
28
+ });
29
+ return this.pending;
30
+ }
31
+ getCountry() {
32
+ return this.cached;
33
+ }
34
+ async fetchCountryCode() {
35
+ try {
36
+ const res = await this.fetcher(CF_TRACE_URL, { signal: AbortSignal.timeout(3e3) });
37
+ if (!res.ok) return null;
38
+ const raw = parseTrace(await res.text());
39
+ const trace = cloudflareTraceSchema.safeParse(raw);
40
+ if (!trace.success) return null;
41
+ return trace.data.loc.toUpperCase();
42
+ } catch {
43
+ return null;
44
+ }
45
+ }
46
+ };
43
47
  //#endregion
44
- export { detectCountryCode, resetGeo };
48
+ export { GeoDetector };
@@ -1 +1 @@
1
- {"version":3,"file":"geo.mjs","names":[],"sources":["../../src/tracking/geo.ts"],"sourcesContent":["import { cloudflareTraceSchema } from \"@interfere/types/sdk/geo\";\n\nconst CF_TRACE_URL = \"https://cloudflare.com/cdn-cgi/trace\";\n\nlet cached: string | null = null;\nlet pending: Promise<string | null> | null = null;\nlet failed = false;\n\nfunction parseTrace(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const idx = line.indexOf(\"=\");\n if (idx > 0) {\n result[line.slice(0, idx)] = line.slice(idx + 1);\n }\n }\n return result;\n}\n\nasync function fetchCountryCode(): Promise<string | null> {\n try {\n const res = await fetch(CF_TRACE_URL, {\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n return null;\n }\n\n const raw = parseTrace(await res.text());\n const trace = cloudflareTraceSchema.safeParse(raw);\n if (!trace.success) {\n return null;\n }\n\n return trace.data.loc.toUpperCase();\n } catch {\n return null;\n }\n}\n\nexport function detectCountryCode(): Promise<string | null> {\n if (cached) {\n return Promise.resolve(cached);\n }\n\n if (failed) {\n return Promise.resolve(null);\n }\n\n if (!pending) {\n pending = fetchCountryCode().then((code) => {\n cached = code;\n if (!code) {\n failed = true;\n }\n pending = null;\n return code;\n });\n }\n\n return pending;\n}\n\nexport function resetGeo(): void {\n cached = null;\n pending = null;\n failed = false;\n}\n"],"mappings":";;AAEA,MAAM,eAAe;AAErB,IAAI,SAAwB;AAC5B,IAAI,UAAyC;AAC7C,IAAI,SAAS;AAEb,SAAS,WAAW,MAAsC;CACxD,MAAM,SAAiC,EAAE;AACzC,MAAK,MAAM,QAAQ,KAAK,MAAM,KAAK,EAAE;EACnC,MAAM,MAAM,KAAK,QAAQ,IAAI;AAC7B,MAAI,MAAM,EACR,QAAO,KAAK,MAAM,GAAG,IAAI,IAAI,KAAK,MAAM,MAAM,EAAE;;AAGpD,QAAO;;AAGT,eAAe,mBAA2C;AACxD,KAAI;EACF,MAAM,MAAM,MAAM,MAAM,cAAc,EACpC,QAAQ,YAAY,QAAQ,IAAK,EAClC,CAAC;AACF,MAAI,CAAC,IAAI,GACP,QAAO;EAGT,MAAM,MAAM,WAAW,MAAM,IAAI,MAAM,CAAC;EACxC,MAAM,QAAQ,sBAAsB,UAAU,IAAI;AAClD,MAAI,CAAC,MAAM,QACT,QAAO;AAGT,SAAO,MAAM,KAAK,IAAI,aAAa;SAC7B;AACN,SAAO;;;AAIX,SAAgB,oBAA4C;AAC1D,KAAI,OACF,QAAO,QAAQ,QAAQ,OAAO;AAGhC,KAAI,OACF,QAAO,QAAQ,QAAQ,KAAK;AAG9B,KAAI,CAAC,QACH,WAAU,kBAAkB,CAAC,MAAM,SAAS;AAC1C,WAAS;AACT,MAAI,CAAC,KACH,UAAS;AAEX,YAAU;AACV,SAAO;GACP;AAGJ,QAAO;;AAGT,SAAgB,WAAiB;AAC/B,UAAS;AACT,WAAU;AACV,UAAS"}
1
+ {"version":3,"file":"geo.mjs","names":[],"sources":["../../src/tracking/geo.ts"],"sourcesContent":["import { cloudflareTraceSchema } from \"@interfere/types/sdk/geo\";\n\nconst CF_TRACE_URL = \"https://cloudflare.com/cdn-cgi/trace\";\n\nfunction parseTrace(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n for (const line of text.split(\"\\n\")) {\n const idx = line.indexOf(\"=\");\n if (idx > 0) {\n result[line.slice(0, idx)] = line.slice(idx + 1);\n }\n }\n return result;\n}\n\nexport class GeoDetector {\n private cached: string | null = null;\n private pending: Promise<string | null> | null = null;\n private failed = false;\n private readonly fetcher: typeof globalThis.fetch;\n\n constructor(fetcher?: typeof globalThis.fetch) {\n this.fetcher = fetcher ?? globalThis.fetch.bind(globalThis);\n }\n\n detect(): Promise<string | null> {\n if (this.cached) {\n return Promise.resolve(this.cached);\n }\n\n if (this.failed) {\n return Promise.resolve(null);\n }\n\n if (!this.pending) {\n this.pending = this.fetchCountryCode().then((code) => {\n this.cached = code;\n if (!code) {\n this.failed = true;\n }\n this.pending = null;\n return code;\n });\n }\n\n return this.pending;\n }\n\n getCountry(): string | null {\n return this.cached;\n }\n\n private async fetchCountryCode(): Promise<string | null> {\n try {\n const res = await this.fetcher(CF_TRACE_URL, {\n signal: AbortSignal.timeout(3000),\n });\n if (!res.ok) {\n return null;\n }\n\n const raw = parseTrace(await res.text());\n const trace = cloudflareTraceSchema.safeParse(raw);\n if (!trace.success) {\n return null;\n }\n\n return trace.data.loc.toUpperCase();\n } catch {\n return null;\n }\n }\n}\n"],"mappings":";;AAEA,MAAM,eAAe;AAErB,SAAS,WAAW,MAAsC;CACxD,MAAM,SAAiC,EAAE;CACzC,KAAK,MAAM,QAAQ,KAAK,MAAM,KAAK,EAAE;EACnC,MAAM,MAAM,KAAK,QAAQ,IAAI;EAC7B,IAAI,MAAM,GACR,OAAO,KAAK,MAAM,GAAG,IAAI,IAAI,KAAK,MAAM,MAAM,EAAE;;CAGpD,OAAO;;AAGT,IAAa,cAAb,MAAyB;CACvB,SAAgC;CAChC,UAAiD;CACjD,SAAiB;CACjB;CAEA,YAAY,SAAmC;EAC7C,KAAK,UAAU,WAAW,WAAW,MAAM,KAAK,WAAW;;CAG7D,SAAiC;EAC/B,IAAI,KAAK,QACP,OAAO,QAAQ,QAAQ,KAAK,OAAO;EAGrC,IAAI,KAAK,QACP,OAAO,QAAQ,QAAQ,KAAK;EAG9B,IAAI,CAAC,KAAK,SACR,KAAK,UAAU,KAAK,kBAAkB,CAAC,MAAM,SAAS;GACpD,KAAK,SAAS;GACd,IAAI,CAAC,MACH,KAAK,SAAS;GAEhB,KAAK,UAAU;GACf,OAAO;IACP;EAGJ,OAAO,KAAK;;CAGd,aAA4B;EAC1B,OAAO,KAAK;;CAGd,MAAc,mBAA2C;EACvD,IAAI;GACF,MAAM,MAAM,MAAM,KAAK,QAAQ,cAAc,EAC3C,QAAQ,YAAY,QAAQ,IAAK,EAClC,CAAC;GACF,IAAI,CAAC,IAAI,IACP,OAAO;GAGT,MAAM,MAAM,WAAW,MAAM,IAAI,MAAM,CAAC;GACxC,MAAM,QAAQ,sBAAsB,UAAU,IAAI;GAClD,IAAI,CAAC,MAAM,SACT,OAAO;GAGT,OAAO,MAAM,KAAK,IAAI,aAAa;UAC7B;GACN,OAAO"}
@@ -1,3 +1,5 @@
1
+ import { SessionId } from "@interfere/types/data/session";
2
+
1
3
  //#region src/tracking/session.d.ts
2
4
  type OnRotate = (sessionId: string) => void;
3
5
  declare class SessionManager {
@@ -8,7 +10,7 @@ declare class SessionManager {
8
10
  private readonly onRotate;
9
11
  private lastActivityMs;
10
12
  constructor(onRotate?: OnRotate);
11
- getSessionId(): string;
13
+ getSessionId(): SessionId;
12
14
  getWindowId(): string;
13
15
  private restore;
14
16
  private rotate;
@@ -1 +1 @@
1
- {"version":3,"file":"session.d.mts","names":[],"sources":["../../src/tracking/session.ts"],"mappings":";KAmBY,QAAA,IAAY,SAAA;AAAA,cAEX,cAAA;EACX,SAAA;EACA,QAAA;EAAA,iBACiB,KAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,cAAA;cAEI,QAAA,GAAW,QAAA;EAOvB,YAAA,CAAA;EAQA,WAAA,CAAA;EAAA,QAgBQ,OAAA;EAAA,QAWA,MAAA;EAAA,QAQA,KAAA;EAAA,QAKA,SAAA;AAAA"}
1
+ {"version":3,"file":"session.d.mts","names":[],"sources":["../../src/tracking/session.ts"],"mappings":";;;KAqBY,QAAA,IAAY,SAAA;AAAA,cAEX,cAAA;EACX,SAAA;EACA,QAAA;EAAA,iBACiB,KAAA;EAAA,iBACA,OAAA;EAAA,iBACA,QAAA;EAAA,QACT,cAAA;cAEI,QAAA,GAAW,QAAA;EAOvB,YAAA,CAAA,GAAgB,SAAA;EAQhB,WAAA,CAAA;EAAA,QAgBQ,OAAA;EAAA,QAWA,MAAA;EAAA,QAQA,KAAA;EAAA,QAKA,SAAA;AAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"session.mjs","names":["uuidv7"],"sources":["../../src/tracking/session.ts"],"sourcesContent":["import { v7 as uuidv7 } from \"uuid\";\n\nconst SESSION_ID_KEY = \"interfere:session_id\";\nconst LAST_ACTIVITY_KEY = \"interfere:last_activity\";\nconst WINDOW_ID_KEY = \"interfere:window_id\";\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction tryStorage(type: \"localStorage\" | \"sessionStorage\"): Storage | null {\n try {\n const s = globalThis[type];\n const key = \"__interfere_probe__\";\n s.setItem(key, \"1\");\n s.removeItem(key);\n return s;\n } catch {\n return null;\n }\n}\n\nexport type OnRotate = (sessionId: string) => void;\n\nexport class SessionManager {\n sessionId: string | null = null;\n windowId: string | null = null;\n private readonly local: Storage | null;\n private readonly session: Storage | null;\n private readonly onRotate: OnRotate | null = null;\n private lastActivityMs = 0;\n\n constructor(onRotate?: OnRotate) {\n this.local = tryStorage(\"localStorage\");\n this.session = tryStorage(\"sessionStorage\");\n this.onRotate = onRotate ?? null;\n this.restore();\n }\n\n getSessionId(): string {\n if (this.sessionId && !this.isExpired()) {\n this.touch();\n return this.sessionId;\n }\n return this.rotate();\n }\n\n getWindowId(): string {\n if (this.windowId) {\n return this.windowId;\n }\n\n const stored = this.session?.getItem(WINDOW_ID_KEY);\n if (stored) {\n this.windowId = stored;\n return stored;\n }\n\n this.windowId = `win_${crypto.randomUUID()}`;\n this.session?.setItem(WINDOW_ID_KEY, this.windowId);\n return this.windowId;\n }\n\n private restore(): void {\n const stored = this.local?.getItem(SESSION_ID_KEY);\n\n if (stored && !this.isExpired()) {\n this.sessionId = stored;\n this.touch();\n } else {\n this.rotate();\n }\n }\n\n private rotate(): string {\n this.sessionId = uuidv7();\n this.local?.setItem(SESSION_ID_KEY, this.sessionId);\n this.touch();\n this.onRotate?.(this.sessionId);\n return this.sessionId;\n }\n\n private touch(): void {\n this.lastActivityMs = Date.now();\n this.local?.setItem(LAST_ACTIVITY_KEY, String(this.lastActivityMs));\n }\n\n private isExpired(): boolean {\n const raw = this.local?.getItem(LAST_ACTIVITY_KEY);\n const ts = raw ? Number(raw) : this.lastActivityMs;\n\n if (ts === 0) {\n return true;\n }\n return Date.now() - ts > SESSION_TIMEOUT_MS;\n }\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AACtB,MAAM,qBAAqB,OAAU;AAErC,SAAS,WAAW,MAAyD;AAC3E,KAAI;EACF,MAAM,IAAI,WAAW;EACrB,MAAM,MAAM;AACZ,IAAE,QAAQ,KAAK,IAAI;AACnB,IAAE,WAAW,IAAI;AACjB,SAAO;SACD;AACN,SAAO;;;AAMX,IAAa,iBAAb,MAA4B;CAC1B,YAA2B;CAC3B,WAA0B;CAC1B;CACA;CACA,WAA6C;CAC7C,iBAAyB;CAEzB,YAAY,UAAqB;AAC/B,OAAK,QAAQ,WAAW,eAAe;AACvC,OAAK,UAAU,WAAW,iBAAiB;AAC3C,OAAK,WAAW,YAAY;AAC5B,OAAK,SAAS;;CAGhB,eAAuB;AACrB,MAAI,KAAK,aAAa,CAAC,KAAK,WAAW,EAAE;AACvC,QAAK,OAAO;AACZ,UAAO,KAAK;;AAEd,SAAO,KAAK,QAAQ;;CAGtB,cAAsB;AACpB,MAAI,KAAK,SACP,QAAO,KAAK;EAGd,MAAM,SAAS,KAAK,SAAS,QAAQ,cAAc;AACnD,MAAI,QAAQ;AACV,QAAK,WAAW;AAChB,UAAO;;AAGT,OAAK,WAAW,OAAO,OAAO,YAAY;AAC1C,OAAK,SAAS,QAAQ,eAAe,KAAK,SAAS;AACnD,SAAO,KAAK;;CAGd,UAAwB;EACtB,MAAM,SAAS,KAAK,OAAO,QAAQ,eAAe;AAElD,MAAI,UAAU,CAAC,KAAK,WAAW,EAAE;AAC/B,QAAK,YAAY;AACjB,QAAK,OAAO;QAEZ,MAAK,QAAQ;;CAIjB,SAAyB;AACvB,OAAK,YAAYA,IAAQ;AACzB,OAAK,OAAO,QAAQ,gBAAgB,KAAK,UAAU;AACnD,OAAK,OAAO;AACZ,OAAK,WAAW,KAAK,UAAU;AAC/B,SAAO,KAAK;;CAGd,QAAsB;AACpB,OAAK,iBAAiB,KAAK,KAAK;AAChC,OAAK,OAAO,QAAQ,mBAAmB,OAAO,KAAK,eAAe,CAAC;;CAGrE,YAA6B;EAC3B,MAAM,MAAM,KAAK,OAAO,QAAQ,kBAAkB;EAClD,MAAM,KAAK,MAAM,OAAO,IAAI,GAAG,KAAK;AAEpC,MAAI,OAAO,EACT,QAAO;AAET,SAAO,KAAK,KAAK,GAAG,KAAK"}
1
+ {"version":3,"file":"session.mjs","names":["uuidv7"],"sources":["../../src/tracking/session.ts"],"sourcesContent":["import type { SessionId } from \"@interfere/types/data/session\";\n\nimport { v7 as uuidv7 } from \"uuid\";\n\nconst SESSION_ID_KEY = \"interfere:session_id\";\nconst LAST_ACTIVITY_KEY = \"interfere:last_activity\";\nconst WINDOW_ID_KEY = \"interfere:window_id\";\nconst SESSION_TIMEOUT_MS = 30 * 60 * 1000;\n\nfunction tryStorage(type: \"localStorage\" | \"sessionStorage\"): Storage | null {\n try {\n const s = globalThis[type];\n const key = \"__interfere_probe__\";\n s.setItem(key, \"1\");\n s.removeItem(key);\n return s;\n } catch {\n return null;\n }\n}\n\nexport type OnRotate = (sessionId: string) => void;\n\nexport class SessionManager {\n sessionId: string | null = null;\n windowId: string | null = null;\n private readonly local: Storage | null;\n private readonly session: Storage | null;\n private readonly onRotate: OnRotate | null = null;\n private lastActivityMs = 0;\n\n constructor(onRotate?: OnRotate) {\n this.local = tryStorage(\"localStorage\");\n this.session = tryStorage(\"sessionStorage\");\n this.onRotate = onRotate ?? null;\n this.restore();\n }\n\n getSessionId(): SessionId {\n if (this.sessionId && !this.isExpired()) {\n this.touch();\n return this.sessionId as SessionId;\n }\n return this.rotate() as SessionId;\n }\n\n getWindowId(): string {\n if (this.windowId) {\n return this.windowId;\n }\n\n const stored = this.session?.getItem(WINDOW_ID_KEY);\n if (stored) {\n this.windowId = stored;\n return stored;\n }\n\n this.windowId = `win_${crypto.randomUUID()}`;\n this.session?.setItem(WINDOW_ID_KEY, this.windowId);\n return this.windowId;\n }\n\n private restore(): void {\n const stored = this.local?.getItem(SESSION_ID_KEY);\n\n if (stored && !this.isExpired()) {\n this.sessionId = stored;\n this.touch();\n } else {\n this.rotate();\n }\n }\n\n private rotate(): SessionId {\n this.sessionId = uuidv7();\n this.local?.setItem(SESSION_ID_KEY, this.sessionId);\n this.touch();\n this.onRotate?.(this.sessionId);\n return this.sessionId as SessionId;\n }\n\n private touch(): void {\n this.lastActivityMs = Date.now();\n this.local?.setItem(LAST_ACTIVITY_KEY, String(this.lastActivityMs));\n }\n\n private isExpired(): boolean {\n const raw = this.local?.getItem(LAST_ACTIVITY_KEY);\n const ts = raw ? Number(raw) : this.lastActivityMs;\n\n if (ts === 0) {\n return true;\n }\n return Date.now() - ts > SESSION_TIMEOUT_MS;\n }\n}\n"],"mappings":";;AAIA,MAAM,iBAAiB;AACvB,MAAM,oBAAoB;AAC1B,MAAM,gBAAgB;AACtB,MAAM,qBAAqB,OAAU;AAErC,SAAS,WAAW,MAAyD;CAC3E,IAAI;EACF,MAAM,IAAI,WAAW;EACrB,MAAM,MAAM;EACZ,EAAE,QAAQ,KAAK,IAAI;EACnB,EAAE,WAAW,IAAI;EACjB,OAAO;SACD;EACN,OAAO;;;AAMX,IAAa,iBAAb,MAA4B;CAC1B,YAA2B;CAC3B,WAA0B;CAC1B;CACA;CACA,WAA6C;CAC7C,iBAAyB;CAEzB,YAAY,UAAqB;EAC/B,KAAK,QAAQ,WAAW,eAAe;EACvC,KAAK,UAAU,WAAW,iBAAiB;EAC3C,KAAK,WAAW,YAAY;EAC5B,KAAK,SAAS;;CAGhB,eAA0B;EACxB,IAAI,KAAK,aAAa,CAAC,KAAK,WAAW,EAAE;GACvC,KAAK,OAAO;GACZ,OAAO,KAAK;;EAEd,OAAO,KAAK,QAAQ;;CAGtB,cAAsB;EACpB,IAAI,KAAK,UACP,OAAO,KAAK;EAGd,MAAM,SAAS,KAAK,SAAS,QAAQ,cAAc;EACnD,IAAI,QAAQ;GACV,KAAK,WAAW;GAChB,OAAO;;EAGT,KAAK,WAAW,OAAO,OAAO,YAAY;EAC1C,KAAK,SAAS,QAAQ,eAAe,KAAK,SAAS;EACnD,OAAO,KAAK;;CAGd,UAAwB;EACtB,MAAM,SAAS,KAAK,OAAO,QAAQ,eAAe;EAElD,IAAI,UAAU,CAAC,KAAK,WAAW,EAAE;GAC/B,KAAK,YAAY;GACjB,KAAK,OAAO;SAEZ,KAAK,QAAQ;;CAIjB,SAA4B;EAC1B,KAAK,YAAYA,IAAQ;EACzB,KAAK,OAAO,QAAQ,gBAAgB,KAAK,UAAU;EACnD,KAAK,OAAO;EACZ,KAAK,WAAW,KAAK,UAAU;EAC/B,OAAO,KAAK;;CAGd,QAAsB;EACpB,KAAK,iBAAiB,KAAK,KAAK;EAChC,KAAK,OAAO,QAAQ,mBAAmB,OAAO,KAAK,eAAe,CAAC;;CAGrE,YAA6B;EAC3B,MAAM,MAAM,KAAK,OAAO,QAAQ,kBAAkB;EAClD,MAAM,KAAK,MAAM,OAAO,IAAI,GAAG,KAAK;EAEpC,IAAI,OAAO,GACT,OAAO;EAET,OAAO,KAAK,KAAK,GAAG,KAAK"}
@@ -0,0 +1,10 @@
1
+ //#region src/util/bot.d.ts
2
+ /**
3
+ * `isBot` covers the union of the Crawlers, Fetchers, CLIs, and Libraries
4
+ * extensions, so `isAICrawler` / `isAIAssistant` are strict subsets that
5
+ * don't need separate checks. Returns `false` outside the browser (no UA
6
+ * to inspect).
7
+ */
8
+ declare function isBotUserAgent(): boolean;
9
+ //#endregion
10
+ export { isBotUserAgent };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot.d.mts","names":[],"sources":["../../src/util/bot.ts"],"mappings":";;AAQA;;;;;iBAAgB,cAAA,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { isBot } from "@ua-parser-js/pro-enterprise/bot-detection";
2
+ //#region src/util/bot.ts
3
+ /**
4
+ * `isBot` covers the union of the Crawlers, Fetchers, CLIs, and Libraries
5
+ * extensions, so `isAICrawler` / `isAIAssistant` are strict subsets that
6
+ * don't need separate checks. Returns `false` outside the browser (no UA
7
+ * to inspect).
8
+ */
9
+ function isBotUserAgent() {
10
+ if (typeof navigator === "undefined") return false;
11
+ return isBot(navigator.userAgent);
12
+ }
13
+ //#endregion
14
+ export { isBotUserAgent };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot.mjs","names":[],"sources":["../../src/util/bot.ts"],"sourcesContent":["import { isBot } from \"@ua-parser-js/pro-enterprise/bot-detection\";\n\n/**\n * `isBot` covers the union of the Crawlers, Fetchers, CLIs, and Libraries\n * extensions, so `isAICrawler` / `isAIAssistant` are strict subsets that\n * don't need separate checks. Returns `false` outside the browser (no UA\n * to inspect).\n */\nexport function isBotUserAgent(): boolean {\n if (typeof navigator === \"undefined\") {\n return false;\n }\n return isBot(navigator.userAgent);\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,iBAA0B;CACxC,IAAI,OAAO,cAAc,aACvB,OAAO;CAET,OAAO,MAAM,UAAU,UAAU"}
@@ -0,0 +1,10 @@
1
+ //#region src/util/global.d.ts
2
+ /**
3
+ * Typed read of a build-time-injected global. The Vite plugin and
4
+ * `withInterfere()` stamp values like `__INTERFERE_RELEASE_SLUG__` onto
5
+ * `globalThis`; this is the single read site so the unsafe cast lives in
6
+ * one place.
7
+ */
8
+ declare function getGlobal<T>(key: string): T | undefined;
9
+ //#endregion
10
+ export { getGlobal };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global.d.mts","names":[],"sources":["../../src/util/global.ts"],"mappings":";;AAMA;;;;;iBAAgB,SAAA,GAAA,CAAa,GAAA,WAAc,CAAA"}
@@ -0,0 +1,12 @@
1
+ //#region src/util/global.ts
2
+ /**
3
+ * Typed read of a build-time-injected global. The Vite plugin and
4
+ * `withInterfere()` stamp values like `__INTERFERE_RELEASE_SLUG__` onto
5
+ * `globalThis`; this is the single read site so the unsafe cast lives in
6
+ * one place.
7
+ */
8
+ function getGlobal(key) {
9
+ return globalThis[key];
10
+ }
11
+ //#endregion
12
+ export { getGlobal };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"global.mjs","names":[],"sources":["../../src/util/global.ts"],"sourcesContent":["/**\n * Typed read of a build-time-injected global. The Vite plugin and\n * `withInterfere()` stamp values like `__INTERFERE_RELEASE_SLUG__` onto\n * `globalThis`; this is the single read site so the unsafe cast lives in\n * one place.\n */\nexport function getGlobal<T>(key: string): T | undefined {\n return (globalThis as Record<string, unknown>)[key] as T | undefined;\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,UAAa,KAA4B;CACvD,OAAQ,WAAuC"}
@@ -1 +1 @@
1
- {"version":3,"file":"log.d.mts","names":[],"sources":["../../src/util/log.ts"],"mappings":";KAAY,QAAA;AAAA,iBA0BI,WAAA,CAAY,KAAA,EAAO,QAAA;AAAA,iBAInB,WAAA,CAAA,GAAe,QAAA;AAAA,UAKd,MAAA;EACf,KAAA,IAAS,IAAA;EACT,KAAA,IAAS,IAAA;EACT,IAAA,IAAQ,IAAA;EACR,IAAA,IAAQ,IAAA;AAAA;AAAA,iBAeM,YAAA,CAAa,KAAA,WAAgB,MAAA"}
1
+ {"version":3,"file":"log.d.mts","names":[],"sources":["../../src/util/log.ts"],"mappings":";KAAY,QAAA;AAAA,iBA6BI,WAAA,CAAY,KAAA,EAAO,QAAA;AAAA,iBAInB,WAAA,CAAA,GAAe,QAAA;AAAA,UAKd,MAAA;EACf,KAAA,IAAS,IAAA;EACT,KAAA,IAAS,IAAA;EACT,IAAA,IAAQ,IAAA;EACR,IAAA,IAAQ,IAAA;AAAA;AAAA,iBAeM,YAAA,CAAa,KAAA,WAAgB,MAAA"}
package/dist/util/log.mjs CHANGED
@@ -12,7 +12,14 @@ const CONSOLE_FN = {
12
12
  warn: "warn",
13
13
  error: "error"
14
14
  };
15
- let threshold = typeof import.meta !== "undefined" && Boolean(import.meta.env?.VITEST) ? PRIORITY.none : PRIORITY.warn;
15
+ /**
16
+ * Customer apps see `warn` and above by default. Tests opt into silence by
17
+ * calling `setLogLevel("none")` in their setup file (we don't auto-detect
18
+ * the test runtime — verified empirically that vitest browser mode exposes
19
+ * neither `import.meta.env`, `process.env`, nor `__VITEST_*` globals to
20
+ * library code, so any "is this a test?" probe is unreliable).
21
+ */
22
+ let threshold = PRIORITY.warn;
16
23
  function setLogLevel(level) {
17
24
  threshold = PRIORITY[level];
18
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"log.mjs","names":[],"sources":["../../src/util/log.ts"],"sourcesContent":["export type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"none\";\n\nconst PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n none: 4,\n};\n\nconst CONSOLE_FN: Record<\n Exclude<LogLevel, \"none\">,\n \"debug\" | \"info\" | \"warn\" | \"error\"\n> = {\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n};\n\nconst isTestEnv =\n typeof import.meta !== \"undefined\" &&\n Boolean((import.meta as { env?: { VITEST?: unknown } }).env?.VITEST);\n\nlet threshold = isTestEnv ? PRIORITY.none : PRIORITY.warn;\n\nexport function setLogLevel(level: LogLevel): void {\n threshold = PRIORITY[level];\n}\n\nexport function getLogLevel(): LogLevel {\n const entry = Object.entries(PRIORITY).find(([, v]) => v === threshold);\n return (entry?.[0] ?? \"warn\") as LogLevel;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n}\n\nfunction emit(\n level: Exclude<LogLevel, \"none\">,\n prefix: string,\n args: unknown[]\n): void {\n if (PRIORITY[level] < threshold) {\n return;\n }\n const fn = CONSOLE_FN[level];\n globalThis.console[fn](prefix, ...args);\n}\n\nexport function createLogger(scope: string): Logger {\n const prefix = `[Interfere:${scope}]`;\n return {\n debug: (...args) => emit(\"debug\", prefix, args),\n info: (...args) => emit(\"info\", prefix, args),\n warn: (...args) => emit(\"warn\", prefix, args),\n error: (...args) => emit(\"error\", prefix, args),\n };\n}\n"],"mappings":";AAEA,MAAM,WAAqC;CACzC,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACP;AAED,MAAM,aAGF;CACF,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;AAMD,IAAI,YAHF,OAAO,OAAO,SAAS,eACvB,QAAS,OAAO,KAAwC,KAAK,OAAO,GAE1C,SAAS,OAAO,SAAS;AAErD,SAAgB,YAAY,OAAuB;AACjD,aAAY,SAAS;;AAGvB,SAAgB,cAAwB;AAEtC,QADc,OAAO,QAAQ,SAAS,CAAC,MAAM,GAAG,OAAO,MAAM,UAChD,GAAG,MAAM;;AAUxB,SAAS,KACP,OACA,QACA,MACM;AACN,KAAI,SAAS,SAAS,UACpB;CAEF,MAAM,KAAK,WAAW;AACtB,YAAW,QAAQ,IAAI,QAAQ,GAAG,KAAK;;AAGzC,SAAgB,aAAa,OAAuB;CAClD,MAAM,SAAS,cAAc,MAAM;AACnC,QAAO;EACL,QAAQ,GAAG,SAAS,KAAK,SAAS,QAAQ,KAAK;EAC/C,OAAO,GAAG,SAAS,KAAK,QAAQ,QAAQ,KAAK;EAC7C,OAAO,GAAG,SAAS,KAAK,QAAQ,QAAQ,KAAK;EAC7C,QAAQ,GAAG,SAAS,KAAK,SAAS,QAAQ,KAAK;EAChD"}
1
+ {"version":3,"file":"log.mjs","names":[],"sources":["../../src/util/log.ts"],"sourcesContent":["export type LogLevel = \"debug\" | \"info\" | \"warn\" | \"error\" | \"none\";\n\nconst PRIORITY: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n none: 4,\n};\n\nconst CONSOLE_FN: Record<\n Exclude<LogLevel, \"none\">,\n \"debug\" | \"info\" | \"warn\" | \"error\"\n> = {\n debug: \"debug\",\n info: \"info\",\n warn: \"warn\",\n error: \"error\",\n};\n\n/**\n * Customer apps see `warn` and above by default. Tests opt into silence by\n * calling `setLogLevel(\"none\")` in their setup file (we don't auto-detect\n * the test runtime — verified empirically that vitest browser mode exposes\n * neither `import.meta.env`, `process.env`, nor `__VITEST_*` globals to\n * library code, so any \"is this a test?\" probe is unreliable).\n */\nlet threshold = PRIORITY.warn;\n\nexport function setLogLevel(level: LogLevel): void {\n threshold = PRIORITY[level];\n}\n\nexport function getLogLevel(): LogLevel {\n const entry = Object.entries(PRIORITY).find(([, v]) => v === threshold);\n return (entry?.[0] ?? \"warn\") as LogLevel;\n}\n\nexport interface Logger {\n debug(...args: unknown[]): void;\n error(...args: unknown[]): void;\n info(...args: unknown[]): void;\n warn(...args: unknown[]): void;\n}\n\nfunction emit(\n level: Exclude<LogLevel, \"none\">,\n prefix: string,\n args: unknown[]\n): void {\n if (PRIORITY[level] < threshold) {\n return;\n }\n const fn = CONSOLE_FN[level];\n globalThis.console[fn](prefix, ...args);\n}\n\nexport function createLogger(scope: string): Logger {\n const prefix = `[Interfere:${scope}]`;\n return {\n debug: (...args) => emit(\"debug\", prefix, args),\n info: (...args) => emit(\"info\", prefix, args),\n warn: (...args) => emit(\"warn\", prefix, args),\n error: (...args) => emit(\"error\", prefix, args),\n };\n}\n"],"mappings":";AAEA,MAAM,WAAqC;CACzC,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACP;AAED,MAAM,aAGF;CACF,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;;;;;;;;AASD,IAAI,YAAY,SAAS;AAEzB,SAAgB,YAAY,OAAuB;CACjD,YAAY,SAAS;;AAGvB,SAAgB,cAAwB;CAEtC,OADc,OAAO,QAAQ,SAAS,CAAC,MAAM,GAAG,OAAO,MAAM,UAChD,GAAG,MAAM;;AAUxB,SAAS,KACP,OACA,QACA,MACM;CACN,IAAI,SAAS,SAAS,WACpB;CAEF,MAAM,KAAK,WAAW;CACtB,WAAW,QAAQ,IAAI,QAAQ,GAAG,KAAK;;AAGzC,SAAgB,aAAa,OAAuB;CAClD,MAAM,SAAS,cAAc,MAAM;CACnC,OAAO;EACL,QAAQ,GAAG,SAAS,KAAK,SAAS,QAAQ,KAAK;EAC/C,OAAO,GAAG,SAAS,KAAK,QAAQ,QAAQ,KAAK;EAC7C,OAAO,GAAG,SAAS,KAAK,QAAQ,QAAQ,KAAK;EAC7C,QAAQ,GAAG,SAAS,KAAK,SAAS,QAAQ,KAAK;EAChD"}
@@ -0,0 +1,9 @@
1
+ //#region src/util/stringify.d.ts
2
+ /**
3
+ * `JSON.stringify` with a `String(value)` fallback for circular refs and
4
+ * non-serialisable values (BigInt, …). Strings pass through unchanged so
5
+ * single-arg log calls don't get re-quoted.
6
+ */
7
+ declare function safeStringify(value: unknown): string;
8
+ //#endregion
9
+ export { safeStringify };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify.d.mts","names":[],"sources":["../../src/util/stringify.ts"],"mappings":";;AAKA;;;;iBAAgB,aAAA,CAAc,KAAA"}
@@ -0,0 +1,16 @@
1
+ //#region src/util/stringify.ts
2
+ /**
3
+ * `JSON.stringify` with a `String(value)` fallback for circular refs and
4
+ * non-serialisable values (BigInt, …). Strings pass through unchanged so
5
+ * single-arg log calls don't get re-quoted.
6
+ */
7
+ function safeStringify(value) {
8
+ if (typeof value === "string") return value;
9
+ try {
10
+ return JSON.stringify(value) ?? String(value);
11
+ } catch {
12
+ return String(value);
13
+ }
14
+ }
15
+ //#endregion
16
+ export { safeStringify };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stringify.mjs","names":[],"sources":["../../src/util/stringify.ts"],"sourcesContent":["/**\n * `JSON.stringify` with a `String(value)` fallback for circular refs and\n * non-serialisable values (BigInt, …). Strings pass through unchanged so\n * single-arg log calls don't get re-quoted.\n */\nexport function safeStringify(value: unknown): string {\n if (typeof value === \"string\") {\n return value;\n }\n try {\n return JSON.stringify(value) ?? String(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;;;;;AAKA,SAAgB,cAAc,OAAwB;CACpD,IAAI,OAAO,UAAU,UACnB,OAAO;CAET,IAAI;EACF,OAAO,KAAK,UAAU,MAAM,IAAI,OAAO,MAAM;SACvC;EACN,OAAO,OAAO,MAAM"}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@interfere/react",
3
- "version": "9.0.1",
3
+ "version": "10.0.0",
4
4
  "license": "MIT",
5
5
  "description": "Client-side React SDK for Interfere. Error tracking, session replay, and analytics.",
6
6
  "keywords": [
7
7
  "observability",
8
- "typescript",
8
+ "@typescript/native-preview",
9
9
  "react",
10
10
  "error-tracking",
11
11
  "session-replay"
@@ -40,37 +40,89 @@
40
40
  "types": "./dist/react-error-handler.d.mts",
41
41
  "default": "./dist/react-error-handler.mjs"
42
42
  },
43
- "./internal/client": {
44
- "@source": "./src/internal/client.ts",
45
- "types": "./dist/internal/client.d.mts",
46
- "default": "./dist/internal/client.mjs"
43
+ "./api": {
44
+ "@source": "./src/api.ts",
45
+ "types": "./dist/api.d.mts",
46
+ "default": "./dist/api.mjs"
47
+ },
48
+ "./internal/kernel": {
49
+ "@source": "./src/internal/kernel.ts",
50
+ "types": "./dist/internal/kernel.d.mts",
51
+ "default": "./dist/internal/kernel.mjs"
52
+ },
53
+ "./internal/kernel-registry": {
54
+ "@source": "./src/internal/kernel-registry.ts",
55
+ "types": "./dist/internal/kernel-registry.d.mts",
56
+ "default": "./dist/internal/kernel-registry.mjs"
47
57
  },
48
58
  "./internal/version": {
49
59
  "@source": "./src/internal/version.ts",
50
60
  "types": "./dist/internal/version.d.mts",
51
61
  "default": "./dist/internal/version.mjs"
62
+ },
63
+ "./internal/wrapper-singleton": {
64
+ "@source": "./src/internal/wrapper-singleton.ts",
65
+ "types": "./dist/internal/wrapper-singleton.d.mts",
66
+ "default": "./dist/internal/wrapper-singleton.mjs"
67
+ },
68
+ "./sw": {
69
+ "types": "./dist/sw.d.mts",
70
+ "default": "./dist/sw.mjs"
71
+ },
72
+ "./util/log": {
73
+ "@source": "./src/util/log.ts",
74
+ "types": "./dist/util/log.d.mts",
75
+ "default": "./dist/util/log.mjs"
52
76
  }
53
77
  },
54
78
  "sideEffects": false,
55
79
  "publishConfig": {
56
- "access": "public"
80
+ "access": "public",
81
+ "tag": "canary"
57
82
  },
58
83
  "scripts": {
59
84
  "build": "tsdown",
60
- "test": "vitest run --coverage",
61
- "typecheck": "tsc --noEmit --incremental"
85
+ "test": "vitest run",
86
+ "typecheck": "tsgo --noEmit --incremental",
87
+ "typegen": "bun run scripts/build-sw.ts"
62
88
  },
63
89
  "dependencies": {
64
90
  "@fingerprintjs/fingerprintjs": "^5.2.0",
65
- "@interfere/constants": "^9.0.0",
91
+ "@interfere/constants": "^9.0.1",
66
92
  "@interfere/types": "^9.0.0",
93
+ "@opentelemetry/api": "^1.9.1",
94
+ "@opentelemetry/api-logs": "^0.217.0",
95
+ "@opentelemetry/context-zone": "^2.7.1",
96
+ "@opentelemetry/core": "^2.7.0",
97
+ "@opentelemetry/exporter-logs-otlp-http": "^0.217.0",
98
+ "@opentelemetry/exporter-metrics-otlp-http": "^0.217.0",
99
+ "@opentelemetry/exporter-trace-otlp-http": "^0.217.0",
100
+ "@opentelemetry/instrumentation": "^0.217.0",
101
+ "@opentelemetry/instrumentation-browser-navigation": "^0.10.0",
102
+ "@opentelemetry/instrumentation-document-load": "^0.62.0",
103
+ "@opentelemetry/instrumentation-fetch": "^0.217.0",
104
+ "@opentelemetry/instrumentation-long-task": "^0.61.0",
105
+ "@opentelemetry/instrumentation-user-interaction": "^0.61.0",
106
+ "@opentelemetry/instrumentation-xml-http-request": "^0.217.0",
107
+ "@opentelemetry/opentelemetry-browser-detector": "^0.217.0",
108
+ "@opentelemetry/otlp-transformer": "^0.217.0",
109
+ "@opentelemetry/resources": "^2.7.0",
110
+ "@opentelemetry/sdk-logs": "^0.217.0",
111
+ "@opentelemetry/sdk-metrics": "^2.7.0",
112
+ "@opentelemetry/sdk-trace-base": "^2.7.0",
113
+ "@opentelemetry/sdk-trace-web": "^2.7.1",
114
+ "@opentelemetry/web-common": "^0.217.0",
67
115
  "@ua-parser-js/pro-enterprise": "^2.0.6",
68
116
  "rrweb": "2.0.0-alpha.4",
69
- "uuid": "^14.0.0"
117
+ "uuid": "^14.0.0",
118
+ "web-vitals": "^5.2.0",
119
+ "workbox-background-sync": "^7.4.0",
120
+ "workbox-routing": "^7.4.0",
121
+ "workbox-strategies": "^7.4.0"
70
122
  },
71
123
  "peerDependencies": {
72
- "react": "^19.0.0",
73
- "react-dom": "^19.0.0"
124
+ "react": "^19.2.6",
125
+ "react-dom": "^19.2.6"
74
126
  },
75
127
  "peerDependenciesMeta": {
76
128
  "react": {
@@ -87,13 +139,14 @@
87
139
  "@types/node": "^24.12.0",
88
140
  "@types/react": "19.2.14",
89
141
  "@types/react-dom": "19.2.3",
90
- "@vitest/browser": "4.1.5",
91
- "@vitest/browser-playwright": "4.1.5",
92
- "@vitest/coverage-v8": "^4.1.5",
93
- "playwright": "^1.59.0",
94
- "tsdown": "^0.21.10",
95
- "typescript": "6.0.3",
96
- "vitest": "^4.1.5",
142
+ "@typescript/native-preview": "7.0.0-dev.20260512.1",
143
+ "@vitest/browser": "4.1.6",
144
+ "@vitest/browser-playwright": "4.1.6",
145
+ "@vitest/coverage-v8": "^4.1.6",
146
+ "playwright": "^1.60.0",
147
+ "rolldown": "^1.0.0-rc.17",
148
+ "tsdown": "^0.22.0",
149
+ "vitest": "^4.1.6",
97
150
  "vitest-browser-react": "2.2.0"
98
151
  }
99
152
  }
@@ -1,48 +0,0 @@
1
- import { PluginOverrides } from "../plugins/lib/loader.mjs";
2
- import { QueueOptions } from "../transport/queue.mjs";
3
- import { ConsentState } from "@interfere/types/sdk/plugins/manifest";
4
- import { EnvelopePayload, EventType } from "@interfere/types/sdk/envelope";
5
-
6
- //#region src/internal/client.d.ts
7
- declare function buildSdkStack(wrapperVersions?: string[]): string[];
8
- interface ClientOptions {
9
- /** @internal Wrapper SDK versions (e.g. `@interfere/next@8.1.0`). */
10
- _wrapperVersions?: string[];
11
- batch?: Omit<Partial<QueueOptions>, "transport">;
12
- consent?: ConsentState;
13
- /**
14
- * Override the automatic dev-mode guard. When `undefined`, the SDK
15
- * auto-detects: it disables itself if `process.env["NODE_ENV"]` is not
16
- * `"production"` (Node / webpack / Next.js). In environments where
17
- * `process` does not exist (Vite, CRA, plain browser) the SDK
18
- * defaults to **enabled** — pass `false` to disable explicitly.
19
- */
20
- enabled?: boolean;
21
- plugins?: PluginOverrides;
22
- }
23
- declare class Client {
24
- private readonly metadata;
25
- private readonly queue;
26
- private readonly runtime;
27
- constructor(opts: ClientOptions, buildId: string, releaseId: string | null);
28
- private fetchRemoteConfig;
29
- capture<T extends EventType>(type: T, payload: EnvelopePayload<T>): void;
30
- flush(): void;
31
- dispose(): Promise<void>;
32
- getConsent(): ConsentState | null;
33
- setConsent(value?: ConsentState): void;
34
- resetConsent(): void;
35
- }
36
- declare function getClient(): Client;
37
- declare function init(opts?: ClientOptions): void;
38
- declare function close(): Promise<void>;
39
- declare const consent: {
40
- get(): ConsentState | null;
41
- set(value?: ConsentState): void;
42
- };
43
- declare function syncConsent(consentState: ConsentState | undefined): void;
44
- declare function flush(): void;
45
- /** @internal Test-only. Resets the module state so init() can be called again. */
46
- declare function _reset(): void;
47
- //#endregion
48
- export { ClientOptions, _reset, buildSdkStack, close, consent, flush, getClient, init, syncConsent };
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.d.mts","names":[],"sources":["../../src/internal/client.ts"],"mappings":";;;;;;iBAmBgB,aAAA,CAAc,eAAA;AAAA,UAIb,aAAA;EAJD;EAMd,gBAAA;EACA,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,YAAA;EACrB,OAAA,GAAU,YAAA;EAR4C;AAIxD;;;;;;EAYE,OAAA;EACA,OAAA,GAAU,eAAA;AAAA;AAAA,cAGN,MAAA;EAAA,iBACa,QAAA;EAAA,iBACA,KAAA;EAAA,iBACA,OAAA;cAEL,IAAA,EAAM,aAAA,EAAe,OAAA,UAAiB,SAAA;EAAA,QAuC1C,iBAAA;EA0BR,OAAA,WAAkB,SAAA,CAAA,CAAW,IAAA,EAAM,CAAA,EAAG,OAAA,EAAS,eAAA,CAAgB,CAAA;EAS/D,KAAA,CAAA;EAIM,OAAA,CAAA,GAAW,OAAA;EAMjB,UAAA,CAAA,GAAc,YAAA;EAId,UAAA,CAAW,KAAA,GAAQ,YAAA;EAInB,YAAA,CAAA;AAAA;AAAA,iBAOc,SAAA,CAAA,GAAa,MAAA;AAAA,iBA6Bb,IAAA,CAAK,IAAA,GAAM,aAAA;AAAA,iBAoCL,KAAA,CAAA,GAAS,OAAA;AAAA,cASlB,OAAA;SACJ,YAAA;cAIK,YAAA;AAAA;AAAA,iBAKE,WAAA,CAAY,YAAA,EAAc,YAAA;AAAA,iBAa1B,KAAA,CAAA;;iBAKA,MAAA,CAAA"}
@@ -1,146 +0,0 @@
1
- import { createLogger } from "../util/log.mjs";
2
- import { HttpTransport, buildHeaders } from "../transport/http.mjs";
3
- import { bootstrap, session, teardown } from "../tracking/api.mjs";
4
- import { BatchQueue } from "../transport/queue.mjs";
5
- import { resolveTargets } from "./config.mjs";
6
- import { collectContext } from "./context.mjs";
7
- import { PRODUCER_VERSION } from "./version.mjs";
8
- import { buildEnvelope } from "./envelope.mjs";
9
- import { PluginRuntime } from "./plugin-runtime.mjs";
10
- import { registerServiceWorker } from "./sw.mjs";
11
- import { inferRuntime, normalizeEnv } from "@interfere/types/sdk/runtime";
12
- //#region src/internal/client.ts
13
- const log = createLogger("client");
14
- function buildSdkStack(wrapperVersions) {
15
- return [...wrapperVersions ?? [], PRODUCER_VERSION];
16
- }
17
- var Client = class {
18
- metadata;
19
- queue;
20
- runtime;
21
- constructor(opts, buildId, releaseId) {
22
- const targets = resolveTargets();
23
- bootstrap(targets.session);
24
- log.info("target: %s", targets.ingest.url);
25
- this.metadata = {
26
- context: collectContext(),
27
- environment: normalizeEnv(typeof process === "undefined" ? void 0 : process.env["NODE_ENV"]),
28
- runtime: inferRuntime(),
29
- buildId,
30
- releaseId,
31
- ...opts._wrapperVersions ? { wrapperVersions: opts._wrapperVersions } : {}
32
- };
33
- registerServiceWorker();
34
- const transport = new HttpTransport(targets.ingest);
35
- this.queue = new BatchQueue({
36
- transport,
37
- ...opts.batch
38
- });
39
- this.queue.start();
40
- this.runtime = new PluginRuntime({
41
- capture: (type, payload) => this.capture(type, payload),
42
- getSessionId: () => session.getId() ?? ""
43
- }, opts.plugins, opts.consent);
44
- this.runtime.start();
45
- this.fetchRemoteConfig(targets.config);
46
- }
47
- fetchRemoteConfig(configTarget) {
48
- fetch(configTarget.url, {
49
- method: "GET",
50
- headers: buildHeaders(configTarget.headers),
51
- signal: AbortSignal.timeout(1e4)
52
- }).then((res) => {
53
- if (!res.ok) return;
54
- return res.json();
55
- }).then((config) => {
56
- if (config?.plugins) {
57
- this.runtime.applyRemoteConfig(config.plugins);
58
- log.debug("applied remote config");
59
- }
60
- }).catch(() => {
61
- log.warn("remote config fetch failed, using local defaults");
62
- });
63
- }
64
- capture(type, payload) {
65
- const sessionId = session.getId();
66
- if (!(sessionId && this.runtime.canCapture(type))) return;
67
- this.queue.enqueue(buildEnvelope(type, payload, sessionId, this.metadata));
68
- }
69
- flush() {
70
- this.queue.flush();
71
- }
72
- async dispose() {
73
- await this.runtime.dispose();
74
- teardown();
75
- this.queue.dispose();
76
- }
77
- getConsent() {
78
- return this.runtime.getConsent();
79
- }
80
- setConsent(value) {
81
- this.runtime.setConsent(value);
82
- }
83
- resetConsent() {
84
- this.runtime.resetConsent();
85
- }
86
- };
87
- let instance = null;
88
- function getClient() {
89
- if (!instance) throw new Error("Interfere SDK not initialized. Call init() from your instrumentation-client entrypoint.");
90
- return instance;
91
- }
92
- function isEnabledByEnvironment() {
93
- try {
94
- if (typeof process === "undefined" || !process.env) return true;
95
- if (process.env["NODE_ENV"] === "production") return true;
96
- if (process.env["NODE_ENV"] === void 0) return true;
97
- return !!(globalThis["__INTERFERE_FORCE_ENABLE__"] || process.env["INTERFERE_FORCE_ENABLE"]);
98
- } catch {
99
- return true;
100
- }
101
- }
102
- function init(opts = {}) {
103
- if (instance) return;
104
- if (!(opts.enabled ?? isEnabledByEnvironment())) {
105
- log.info("Disabled in non-production. Pass enabled: true to init() or set INTERFERE_FORCE_ENABLE=1.");
106
- return;
107
- }
108
- const buildId = globalThis["__INTERFERE_BUILD_ID__"];
109
- const releaseId = globalThis["__INTERFERE_RELEASE_ID__"];
110
- if (!buildId) {
111
- log.error("buildId not found — ensure withInterfere() is configured in next.config and instrumentation-client.ts exists in your project root.");
112
- return;
113
- }
114
- if (typeof window !== "undefined") window["__INTERFERE_SDK_STACK__"] = buildSdkStack(opts._wrapperVersions);
115
- instance = new Client(opts, buildId, releaseId ?? null);
116
- }
117
- async function close() {
118
- if (!instance) return;
119
- await instance.dispose();
120
- instance = null;
121
- }
122
- const consent = {
123
- get() {
124
- return instance?.getConsent() ?? null;
125
- },
126
- set(value) {
127
- instance?.setConsent(value);
128
- }
129
- };
130
- function syncConsent(consentState) {
131
- if (!instance) return;
132
- if (consentState) {
133
- instance.setConsent(consentState);
134
- return;
135
- }
136
- instance.resetConsent();
137
- }
138
- function flush() {
139
- instance?.flush();
140
- }
141
- /** @internal Test-only. Resets the module state so init() can be called again. */
142
- function _reset() {
143
- instance = null;
144
- }
145
- //#endregion
146
- export { _reset, buildSdkStack, close, consent, flush, getClient, init, syncConsent };