@formo/analytics 1.35.2 → 1.36.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 (43) hide show
  1. package/dist/cjs/src/FormoAnalytics.d.ts +79 -363
  2. package/dist/cjs/src/FormoAnalytics.js +368 -2234
  3. package/dist/cjs/src/event/EventManager.d.ts +3 -1
  4. package/dist/cjs/src/event/EventManager.js +5 -1
  5. package/dist/cjs/src/event/type.d.ts +1 -0
  6. package/dist/cjs/src/evm/EvmEventTracker.d.ts +175 -0
  7. package/dist/cjs/src/evm/EvmEventTracker.js +1030 -0
  8. package/dist/cjs/src/evm/EvmProviderRegistry.d.ts +132 -0
  9. package/dist/cjs/src/evm/EvmProviderRegistry.js +348 -0
  10. package/dist/cjs/src/evm/EvmRequestTracker.d.ts +120 -0
  11. package/dist/cjs/src/evm/EvmRequestTracker.js +756 -0
  12. package/dist/cjs/src/queue/EventQueue.d.ts +28 -0
  13. package/dist/cjs/src/queue/EventQueue.js +97 -19
  14. package/dist/cjs/src/queue/type.d.ts +1 -0
  15. package/dist/cjs/src/tracking/TrackingPolicy.d.ts +146 -0
  16. package/dist/cjs/src/tracking/TrackingPolicy.js +200 -0
  17. package/dist/cjs/src/validators/address.d.ts +1 -1
  18. package/dist/cjs/src/version.d.ts +1 -1
  19. package/dist/cjs/src/version.js +1 -1
  20. package/dist/cjs/src/wallet/WalletStateStore.d.ts +223 -0
  21. package/dist/cjs/src/wallet/WalletStateStore.js +515 -0
  22. package/dist/esm/src/FormoAnalytics.d.ts +79 -363
  23. package/dist/esm/src/FormoAnalytics.js +369 -2235
  24. package/dist/esm/src/event/EventManager.d.ts +3 -1
  25. package/dist/esm/src/event/EventManager.js +5 -1
  26. package/dist/esm/src/event/type.d.ts +1 -0
  27. package/dist/esm/src/evm/EvmEventTracker.d.ts +175 -0
  28. package/dist/esm/src/evm/EvmEventTracker.js +1027 -0
  29. package/dist/esm/src/evm/EvmProviderRegistry.d.ts +132 -0
  30. package/dist/esm/src/evm/EvmProviderRegistry.js +345 -0
  31. package/dist/esm/src/evm/EvmRequestTracker.d.ts +120 -0
  32. package/dist/esm/src/evm/EvmRequestTracker.js +753 -0
  33. package/dist/esm/src/queue/EventQueue.d.ts +28 -0
  34. package/dist/esm/src/queue/EventQueue.js +97 -19
  35. package/dist/esm/src/queue/type.d.ts +1 -0
  36. package/dist/esm/src/tracking/TrackingPolicy.d.ts +146 -0
  37. package/dist/esm/src/tracking/TrackingPolicy.js +197 -0
  38. package/dist/esm/src/version.d.ts +1 -1
  39. package/dist/esm/src/version.js +1 -1
  40. package/dist/esm/src/wallet/WalletStateStore.d.ts +223 -0
  41. package/dist/esm/src/wallet/WalletStateStore.js +512 -0
  42. package/dist/index.umd.min.js +1 -1
  43. package/package.json +5 -4
@@ -28,6 +28,8 @@ export declare class EventQueue implements IEventQueue {
28
28
  private pendingFlush;
29
29
  private payloadHashes;
30
30
  private canSend?;
31
+ private closed;
32
+ private disposePageLeave;
31
33
  constructor(writeKey: string, options: Options);
32
34
  private generateMessageId;
33
35
  /**
@@ -35,6 +37,26 @@ export declare class EventQueue implements IEventQueue {
35
37
  * withdrawal / SDK teardown so nothing buffered can be sent later.
36
38
  */
37
39
  clear(): void;
40
+ /**
41
+ * Terminal shutdown. Unlike clear(), which only empties the buffer and can
42
+ * be followed by more events (consent is re-granted, say), close() makes
43
+ * this queue permanently inert: every later enqueue() is a no-op and the
44
+ * page-leave listeners are removed. flush() needs no guard of its own,
45
+ * because a closed queue starts empty and can never be filled again.
46
+ *
47
+ * This is the guarantee a torn-down SDK instance needs. Emission decisions
48
+ * and the emission itself are separated by an await in many call paths
49
+ * (event creation is async), so guarding the *caller* cannot work: the
50
+ * instance may be destroyed while the continuation is in flight. Enforcing
51
+ * it here means no holder of a stale reference can ever send.
52
+ *
53
+ * What close() deliberately does NOT stop is a flush already in flight.
54
+ * Those events were accepted while the instance was alive, so they are
55
+ * real data; abandoning them would turn every unmount into silent loss.
56
+ */
57
+ close(): void;
58
+ /** True once close() has run. Exposed for teardown assertions. */
59
+ get isClosed(): boolean;
38
60
  enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
39
61
  flush(callback?: (...args: any) => void, drainAll?: boolean): Promise<void | IFormoEventFlushPayload[]>;
40
62
  /**
@@ -58,6 +80,12 @@ export declare class EventQueue implements IEventQueue {
58
80
  private sendBatches;
59
81
  private isErrorRetryable;
60
82
  private isDuplicate;
83
+ /**
84
+ * Installs the page-leave listeners and returns a disposer that removes
85
+ * every one of them. Without the disposer each SDK instance leaked five
86
+ * listeners (three on the global, two on the document) that kept the
87
+ * instance and its queue alive for the life of the page.
88
+ */
61
89
  private onPageLeave;
62
90
  }
63
91
  export {};
@@ -89,11 +89,23 @@ var EventQueue = /** @class */ (function () {
89
89
  this.queue = [];
90
90
  this.queueByteSize = 0; // running total of queued items' byteSize
91
91
  this.payloadHashes = new Set();
92
+ // Terminal shutdown flag. Once set, enqueue() and flush() are no-ops for
93
+ // the rest of this instance's life. See close().
94
+ this.closed = false;
95
+ // Undoes the page-leave listeners installed in the constructor.
96
+ this.disposePageLeave = null;
97
+ /**
98
+ * Installs the page-leave listeners and returns a disposer that removes
99
+ * every one of them. Without the disposer each SDK instance leaked five
100
+ * listeners (three on the global, two on the document) that kept the
101
+ * instance and its queue alive for the life of the page.
102
+ */
92
103
  this.onPageLeave = function (callback) {
93
104
  // To ensure the callback is only called once even if more than one events
94
105
  // are fired at once.
95
106
  var pageLeft = false;
96
107
  var isAccessible = false;
108
+ var resetTimer = null;
97
109
  function handleOnLeave() {
98
110
  if (pageLeft) {
99
111
  return;
@@ -103,33 +115,27 @@ var EventQueue = /** @class */ (function () {
103
115
  // Reset pageLeft on the next tick
104
116
  // to ensure callback executes for other listeners
105
117
  // when closing an inactive browser tab.
106
- setTimeout(function () {
118
+ resetTimer = setTimeout(function () {
119
+ resetTimer = null;
107
120
  pageLeft = false;
108
121
  }, 0);
109
122
  }
110
- // Catches the unloading of the page (e.g., closing the tab or navigating away).
111
- // Includes user actions like clicking a link, entering a new URL,
112
- // refreshing the page, or closing the browser tab
113
- // Note that 'pagehide' is not supported in IE.
114
- // So, this is a fallback.
115
- globalThis.addEventListener("beforeunload", function () {
123
+ var onBeforeUnload = function () {
116
124
  isAccessible = false;
117
125
  handleOnLeave();
118
- });
119
- globalThis.addEventListener("blur", function () {
126
+ };
127
+ var onBlur = function () {
120
128
  isAccessible = true;
121
129
  handleOnLeave();
122
- });
123
- globalThis.addEventListener("focus", function () {
130
+ };
131
+ var onFocus = function () {
124
132
  pageLeft = false;
125
- });
126
- // Catches the page being hidden, including scenarios like closing the tab.
127
- document.addEventListener("pagehide", function () {
133
+ };
134
+ var onPageHide = function () {
128
135
  isAccessible = document.visibilityState !== "hidden";
129
136
  handleOnLeave();
130
- });
131
- // Catches visibility changes, such as switching tabs or minimizing the browser.
132
- document.addEventListener("visibilitychange", function () {
137
+ };
138
+ var onVisibilityChange = function () {
133
139
  isAccessible = document.visibilityState !== "hidden";
134
140
  if (document.visibilityState === "hidden") {
135
141
  handleOnLeave();
@@ -137,7 +143,36 @@ var EventQueue = /** @class */ (function () {
137
143
  else {
138
144
  pageLeft = false;
139
145
  }
140
- });
146
+ };
147
+ // Captured at install time, not read again at removal time. Teardown can
148
+ // run after the host has swapped or removed these globals (a test harness
149
+ // replacing jsdom between specs, for one), and removing a listener from a
150
+ // different object than it was added to silently leaves it attached.
151
+ var globalTarget = globalThis;
152
+ var documentTarget = document;
153
+ // Catches the unloading of the page (e.g., closing the tab or navigating away).
154
+ // Includes user actions like clicking a link, entering a new URL,
155
+ // refreshing the page, or closing the browser tab
156
+ // Note that 'pagehide' is not supported in IE.
157
+ // So, this is a fallback.
158
+ globalTarget.addEventListener("beforeunload", onBeforeUnload);
159
+ globalTarget.addEventListener("blur", onBlur);
160
+ globalTarget.addEventListener("focus", onFocus);
161
+ // Catches the page being hidden, including scenarios like closing the tab.
162
+ documentTarget.addEventListener("pagehide", onPageHide);
163
+ // Catches visibility changes, such as switching tabs or minimizing the browser.
164
+ documentTarget.addEventListener("visibilitychange", onVisibilityChange);
165
+ return function () {
166
+ if (resetTimer) {
167
+ clearTimeout(resetTimer);
168
+ resetTimer = null;
169
+ }
170
+ globalTarget.removeEventListener("beforeunload", onBeforeUnload);
171
+ globalTarget.removeEventListener("blur", onBlur);
172
+ globalTarget.removeEventListener("focus", onFocus);
173
+ documentTarget.removeEventListener("pagehide", onPageHide);
174
+ documentTarget.removeEventListener("visibilitychange", onVisibilityChange);
175
+ };
141
176
  };
142
177
  options = options || {};
143
178
  this.queue = [];
@@ -157,7 +192,7 @@ var EventQueue = /** @class */ (function () {
157
192
  this.errorHandler = options.errorHandler;
158
193
  this.pendingFlush = null;
159
194
  this.timer = null;
160
- this.onPageLeave(function (isAccessible) { return __awaiter(_this, void 0, void 0, function () {
195
+ this.disposePageLeave = this.onPageLeave(function (isAccessible) { return __awaiter(_this, void 0, void 0, function () {
161
196
  return __generator(this, function (_a) {
162
197
  switch (_a.label) {
163
198
  case 0:
@@ -195,6 +230,39 @@ var EventQueue = /** @class */ (function () {
195
230
  this.queueByteSize = 0;
196
231
  this.payloadHashes.clear();
197
232
  };
233
+ /**
234
+ * Terminal shutdown. Unlike clear(), which only empties the buffer and can
235
+ * be followed by more events (consent is re-granted, say), close() makes
236
+ * this queue permanently inert: every later enqueue() is a no-op and the
237
+ * page-leave listeners are removed. flush() needs no guard of its own,
238
+ * because a closed queue starts empty and can never be filled again.
239
+ *
240
+ * This is the guarantee a torn-down SDK instance needs. Emission decisions
241
+ * and the emission itself are separated by an await in many call paths
242
+ * (event creation is async), so guarding the *caller* cannot work: the
243
+ * instance may be destroyed while the continuation is in flight. Enforcing
244
+ * it here means no holder of a stale reference can ever send.
245
+ *
246
+ * What close() deliberately does NOT stop is a flush already in flight.
247
+ * Those events were accepted while the instance was alive, so they are
248
+ * real data; abandoning them would turn every unmount into silent loss.
249
+ */
250
+ EventQueue.prototype.close = function () {
251
+ this.closed = true;
252
+ this.clear();
253
+ if (this.disposePageLeave) {
254
+ safeCall(this.disposePageLeave);
255
+ this.disposePageLeave = null;
256
+ }
257
+ };
258
+ Object.defineProperty(EventQueue.prototype, "isClosed", {
259
+ /** True once close() has run. Exposed for teardown assertions. */
260
+ get: function () {
261
+ return this.closed;
262
+ },
263
+ enumerable: false,
264
+ configurable: true
265
+ });
198
266
  EventQueue.prototype.enqueue = function (event, callback) {
199
267
  return __awaiter(this, void 0, void 0, function () {
200
268
  var message_id, queueItem, hasReachedFlushAt, hasReachedQueueSize;
@@ -202,6 +270,10 @@ var EventQueue = /** @class */ (function () {
202
270
  switch (_a.label) {
203
271
  case 0:
204
272
  callback = callback || noop;
273
+ // A torn-down instance must never buffer, however late the caller
274
+ // arrives. See close().
275
+ if (this.closed)
276
+ return [2 /*return*/];
205
277
  // Refuse to buffer anything once consent is withdrawn.
206
278
  if (this.canSend && !this.canSend()) {
207
279
  this.clear();
@@ -210,6 +282,12 @@ var EventQueue = /** @class */ (function () {
210
282
  return [4 /*yield*/, this.generateMessageId(event)];
211
283
  case 1:
212
284
  message_id = _a.sent();
285
+ // Re-check after the await. A caller that entered before close() is
286
+ // suspended here, and on a queue that has not flushed yet its event
287
+ // would push and flush immediately - the exact shape of the bug close()
288
+ // exists to stop.
289
+ if (this.closed)
290
+ return [2 /*return*/];
213
291
  // check if the message already exists
214
292
  if (this.isDuplicate(message_id)) {
215
293
  logger_1.logger.warn("Event already enqueued, try again after ".concat((0, utils_1.millisecondsToSecond)(this.flushIntervalMs), " seconds."));
@@ -3,5 +3,6 @@ export interface IEventQueue {
3
3
  enqueue(event: IFormoEvent, callback?: (...args: any) => void): Promise<void>;
4
4
  flush(callback?: (...args: any) => void): Promise<any>;
5
5
  clear(): void;
6
+ close(): void;
6
7
  }
7
8
  //# sourceMappingURL=type.d.ts.map
@@ -0,0 +1,146 @@
1
+ import { ChainID, Options } from "../types";
2
+ /** Wallet event kinds that `autocapture` can switch on or off individually. */
3
+ export type AutocaptureEventType = "connect" | "disconnect" | "signature" | "transaction" | "chain";
4
+ /**
5
+ * What the policy knows about the event being considered.
6
+ *
7
+ * An object rather than a bare chain id on purpose. The `excludeChains` fix
8
+ * had to thread an explicit chain through every gate because the policy had
9
+ * no identity of its own, and each further per-event input would widen those
10
+ * signatures again. New inputs extend this type instead.
11
+ */
12
+ export interface TrackingContext {
13
+ /**
14
+ * The event's own chain, when it has one. It wins over the SDK's central
15
+ * chain, because the two differ whenever the event did not come from the
16
+ * active provider: a second wallet signing through its own transport, or a
17
+ * wagmi mutation naming an explicit chain.
18
+ */
19
+ chainId?: ChainID;
20
+ }
21
+ /** What the policy needs from the SDK it advises. */
22
+ export interface TrackingPolicyDeps {
23
+ /**
24
+ * The CURRENT options.
25
+ *
26
+ * A getter, not a captured object: `FormoAnalytics.options` is public and
27
+ * mutable, so a consumer can replace it after init
28
+ * (`formo.options = { ...formo.options, tracking: false }`). The gates used
29
+ * to read `this.options` on every call, so that took effect immediately,
30
+ * and holding the constructor's object here would silently freeze
31
+ * configuration at init time.
32
+ */
33
+ options(): Options;
34
+ /** Consent state. Separate from options because the visitor can change it. */
35
+ hasOptedOut(): boolean;
36
+ /** The SDK's central chain, used when an event carries none of its own. */
37
+ currentChainId(): ChainID | undefined;
38
+ }
39
+ export interface ITrackingPolicy {
40
+ shouldTrack(context?: TrackingContext): boolean;
41
+ isTrackingSuppressed(): boolean;
42
+ isChainExcluded(context?: TrackingContext): boolean;
43
+ isPageExcluded(): boolean;
44
+ isPersistedIdentityPurgeRequired(): boolean;
45
+ isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
46
+ }
47
+ /**
48
+ * Every "may we track this?" decision, in one place.
49
+ *
50
+ * Split out of `FormoAnalytics` so the rules can be read and tested without
51
+ * an SDK instance, and so option parsing happens once rather than being
52
+ * re-derived at each gate.
53
+ */
54
+ export declare class TrackingPolicy implements ITrackingPolicy {
55
+ private readonly deps;
56
+ constructor(deps: TrackingPolicyDeps);
57
+ /**
58
+ * `tracking` as an options object, or null when it is absent, a boolean, or
59
+ * anything else. Every exclusion rule lives on the object form, so a null
60
+ * here means "no exclusions configured", not "excluded".
61
+ */
62
+ private trackingOptions;
63
+ /**
64
+ * Visitor-level tracking suppression.
65
+ *
66
+ * True when the SDK must not persist any identity/session/chain state or
67
+ * send any events for this visitor: an explicit opt-out, or a
68
+ * jurisdiction/timezone/host/path exclusion. Public entry points that write
69
+ * state before reaching `shouldTrack()` (identify / connect / detect) check
70
+ * this first, so a suppressed visitor leaves no cookies or session state.
71
+ */
72
+ isTrackingSuppressed(): boolean;
73
+ /**
74
+ * Whether the environment is excluded: the visitor's timezone, the current
75
+ * hostname, or the current pathname.
76
+ *
77
+ * Timezone is visitor-level and stable for the session. Host and path are
78
+ * current-page-level and transient, so a SPA navigating back to an allowed
79
+ * path resumes tracking for later actions.
80
+ */
81
+ private isEnvironmentExcluded;
82
+ /** Exact match against `tracking.excludeHosts`. */
83
+ private isHostExcluded;
84
+ /** Exact match against `tracking.excludePaths`. */
85
+ private isPathExcluded;
86
+ /**
87
+ * Case-insensitive match of the browser-resolved timezone against
88
+ * `tracking.excludeTimezones`. Client-side and best-effort; see the option's
89
+ * own documentation.
90
+ */
91
+ private isTimezoneExcluded;
92
+ /**
93
+ * Whether the CURRENT PAGE is excluded, as opposed to the visitor.
94
+ *
95
+ * Host and path exclusions are transient: identity written on an allowed
96
+ * page must survive a visit to an excluded route, so callers that persist
97
+ * or restore identity skip the work here rather than purging.
98
+ */
99
+ isPageExcluded(): boolean;
100
+ /**
101
+ * Whether a persisted identity cookie should be actively purged, not merely
102
+ * left unwritten.
103
+ *
104
+ * Host and path exclusions are deliberately absent: they are transient
105
+ * current-page states, so a cookie legitimately written on an allowed page
106
+ * must survive a visit to an excluded route.
107
+ */
108
+ isPersistedIdentityPurgeRequired(): boolean;
109
+ /**
110
+ * Whether the chain in play is in `tracking.excludeChains`.
111
+ *
112
+ * Separate from `shouldTrack()` so `identify()` can ask *before* mutating
113
+ * identity state. `trackEvent()` drops an excluded event silently, but
114
+ * `identify()` marks the wallet as identified first, so without this an
115
+ * identify on an excluded chain is dedup-marked and then discarded, and the
116
+ * wallet never re-emits for the rest of the session even after switching to
117
+ * an allowed chain. On the Privy path that loses a whole cluster at once.
118
+ */
119
+ isChainExcluded(context?: TrackingContext): boolean;
120
+ /**
121
+ * The shared chain rule, so `shouldTrack()` and `isChainExcluded()` cannot
122
+ * drift apart. Callers have already established that `excludeChains` is
123
+ * non-empty.
124
+ *
125
+ * Fails CLOSED on an unknown chain. `resolveChainIdForProvider` reports 0
126
+ * when it has never heard a chain from the signing wallet, and 0 is in no
127
+ * exclusion list, so treating it as "not excluded" would let through exactly
128
+ * the events an operator excluded: the wallet on the excluded chain is often
129
+ * the one we know least about. An explicit exclusion is a directive, so an
130
+ * unresolvable chain is refused.
131
+ *
132
+ * Keyed on 0 and deliberately NOT on `undefined`. 0 is the explicit "we
133
+ * asked and could not tell" marker. `undefined` means no chain state yet,
134
+ * which is a legitimate transient (the Privy path reconciles a Solana wallet
135
+ * through exactly that state), and refusing it would drop real events.
136
+ * `backfillActiveWallet()` never persists 0, so an unresolvable chain cannot
137
+ * leak into the central value and reach the unscoped events (page / track /
138
+ * identify) that fall back to it.
139
+ */
140
+ private isChainRefused;
141
+ /** Whether an event may be tracked at all, given consent and configuration. */
142
+ shouldTrack(context?: TrackingContext): boolean;
143
+ /** Whether a wallet event kind is enabled for autocapture. Defaults to on. */
144
+ isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
145
+ }
146
+ //# sourceMappingURL=TrackingPolicy.d.ts.map
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TrackingPolicy = void 0;
4
+ var timezone_1 = require("../utils/timezone");
5
+ var validators_1 = require("../validators");
6
+ /**
7
+ * Every "may we track this?" decision, in one place.
8
+ *
9
+ * Split out of `FormoAnalytics` so the rules can be read and tested without
10
+ * an SDK instance, and so option parsing happens once rather than being
11
+ * re-derived at each gate.
12
+ */
13
+ var TrackingPolicy = /** @class */ (function () {
14
+ function TrackingPolicy(deps) {
15
+ this.deps = deps;
16
+ }
17
+ /**
18
+ * `tracking` as an options object, or null when it is absent, a boolean, or
19
+ * anything else. Every exclusion rule lives on the object form, so a null
20
+ * here means "no exclusions configured", not "excluded".
21
+ */
22
+ TrackingPolicy.prototype.trackingOptions = function () {
23
+ var tracking = this.deps.options().tracking;
24
+ if (tracking === null ||
25
+ typeof tracking !== "object" ||
26
+ Array.isArray(tracking)) {
27
+ return null;
28
+ }
29
+ return tracking;
30
+ };
31
+ /**
32
+ * Visitor-level tracking suppression.
33
+ *
34
+ * True when the SDK must not persist any identity/session/chain state or
35
+ * send any events for this visitor: an explicit opt-out, or a
36
+ * jurisdiction/timezone/host/path exclusion. Public entry points that write
37
+ * state before reaching `shouldTrack()` (identify / connect / detect) check
38
+ * this first, so a suppressed visitor leaves no cookies or session state.
39
+ */
40
+ TrackingPolicy.prototype.isTrackingSuppressed = function () {
41
+ return this.deps.hasOptedOut() || this.isEnvironmentExcluded();
42
+ };
43
+ /**
44
+ * Whether the environment is excluded: the visitor's timezone, the current
45
+ * hostname, or the current pathname.
46
+ *
47
+ * Timezone is visitor-level and stable for the session. Host and path are
48
+ * current-page-level and transient, so a SPA navigating back to an allowed
49
+ * path resumes tracking for later actions.
50
+ */
51
+ TrackingPolicy.prototype.isEnvironmentExcluded = function () {
52
+ return (this.isTimezoneExcluded() ||
53
+ this.isHostExcluded() ||
54
+ this.isPathExcluded());
55
+ };
56
+ /** Exact match against `tracking.excludeHosts`. */
57
+ TrackingPolicy.prototype.isHostExcluded = function () {
58
+ var tracking = this.trackingOptions();
59
+ if (!tracking)
60
+ return false;
61
+ if (typeof window === "undefined")
62
+ return false;
63
+ var _a = tracking.excludeHosts, excludeHosts = _a === void 0 ? [] : _a;
64
+ return excludeHosts.includes(window.location.hostname);
65
+ };
66
+ /** Exact match against `tracking.excludePaths`. */
67
+ TrackingPolicy.prototype.isPathExcluded = function () {
68
+ var tracking = this.trackingOptions();
69
+ if (!tracking)
70
+ return false;
71
+ if (typeof window === "undefined")
72
+ return false;
73
+ var _a = tracking.excludePaths, excludePaths = _a === void 0 ? [] : _a;
74
+ return excludePaths.includes(window.location.pathname);
75
+ };
76
+ /**
77
+ * Case-insensitive match of the browser-resolved timezone against
78
+ * `tracking.excludeTimezones`. Client-side and best-effort; see the option's
79
+ * own documentation.
80
+ */
81
+ TrackingPolicy.prototype.isTimezoneExcluded = function () {
82
+ var tracking = this.trackingOptions();
83
+ if (!tracking)
84
+ return false;
85
+ var _a = tracking.excludeTimezones, excludeTimezones = _a === void 0 ? [] : _a;
86
+ if (excludeTimezones.length === 0)
87
+ return false;
88
+ var timezone = (0, timezone_1.getTimezone)();
89
+ if (!timezone)
90
+ return false;
91
+ var lowerTimezone = timezone.toLowerCase();
92
+ return excludeTimezones.some(function (tz) { return typeof tz === "string" && tz.toLowerCase() === lowerTimezone; });
93
+ };
94
+ /**
95
+ * Whether the CURRENT PAGE is excluded, as opposed to the visitor.
96
+ *
97
+ * Host and path exclusions are transient: identity written on an allowed
98
+ * page must survive a visit to an excluded route, so callers that persist
99
+ * or restore identity skip the work here rather than purging.
100
+ */
101
+ TrackingPolicy.prototype.isPageExcluded = function () {
102
+ return this.isHostExcluded() || this.isPathExcluded();
103
+ };
104
+ /**
105
+ * Whether a persisted identity cookie should be actively purged, not merely
106
+ * left unwritten.
107
+ *
108
+ * Host and path exclusions are deliberately absent: they are transient
109
+ * current-page states, so a cookie legitimately written on an allowed page
110
+ * must survive a visit to an excluded route.
111
+ */
112
+ TrackingPolicy.prototype.isPersistedIdentityPurgeRequired = function () {
113
+ return this.deps.hasOptedOut() || this.isTimezoneExcluded();
114
+ };
115
+ /**
116
+ * Whether the chain in play is in `tracking.excludeChains`.
117
+ *
118
+ * Separate from `shouldTrack()` so `identify()` can ask *before* mutating
119
+ * identity state. `trackEvent()` drops an excluded event silently, but
120
+ * `identify()` marks the wallet as identified first, so without this an
121
+ * identify on an excluded chain is dedup-marked and then discarded, and the
122
+ * wallet never re-emits for the rest of the session even after switching to
123
+ * an allowed chain. On the Privy path that loses a whole cluster at once.
124
+ */
125
+ TrackingPolicy.prototype.isChainExcluded = function (context) {
126
+ var tracking = this.trackingOptions();
127
+ if (!tracking)
128
+ return false;
129
+ var _a = tracking.excludeChains, excludeChains = _a === void 0 ? [] : _a;
130
+ if (excludeChains.length === 0)
131
+ return false;
132
+ return this.isChainRefused(excludeChains, context);
133
+ };
134
+ /**
135
+ * The shared chain rule, so `shouldTrack()` and `isChainExcluded()` cannot
136
+ * drift apart. Callers have already established that `excludeChains` is
137
+ * non-empty.
138
+ *
139
+ * Fails CLOSED on an unknown chain. `resolveChainIdForProvider` reports 0
140
+ * when it has never heard a chain from the signing wallet, and 0 is in no
141
+ * exclusion list, so treating it as "not excluded" would let through exactly
142
+ * the events an operator excluded: the wallet on the excluded chain is often
143
+ * the one we know least about. An explicit exclusion is a directive, so an
144
+ * unresolvable chain is refused.
145
+ *
146
+ * Keyed on 0 and deliberately NOT on `undefined`. 0 is the explicit "we
147
+ * asked and could not tell" marker. `undefined` means no chain state yet,
148
+ * which is a legitimate transient (the Privy path reconciles a Solana wallet
149
+ * through exactly that state), and refusing it would drop real events.
150
+ * `backfillActiveWallet()` never persists 0, so an unresolvable chain cannot
151
+ * leak into the central value and reach the unscoped events (page / track /
152
+ * identify) that fall back to it.
153
+ */
154
+ TrackingPolicy.prototype.isChainRefused = function (excludeChains, context) {
155
+ var _a;
156
+ var chainToCheck = (_a = context === null || context === void 0 ? void 0 : context.chainId) !== null && _a !== void 0 ? _a : this.deps.currentChainId();
157
+ if (chainToCheck === 0)
158
+ return true;
159
+ if (chainToCheck === undefined)
160
+ return false;
161
+ return excludeChains.includes(chainToCheck);
162
+ };
163
+ /** Whether an event may be tracked at all, given consent and configuration. */
164
+ TrackingPolicy.prototype.shouldTrack = function (context) {
165
+ if (this.deps.hasOptedOut())
166
+ return false;
167
+ // An explicit boolean is the whole answer; no exclusions apply to it.
168
+ var tracking = this.deps.options().tracking;
169
+ if (typeof tracking === "boolean")
170
+ return tracking;
171
+ var configured = this.trackingOptions();
172
+ if (configured) {
173
+ if (this.isEnvironmentExcluded())
174
+ return false;
175
+ var _a = configured.excludeChains, excludeChains = _a === void 0 ? [] : _a;
176
+ if (excludeChains.length > 0 && this.isChainRefused(excludeChains, context)) {
177
+ return false;
178
+ }
179
+ return true;
180
+ }
181
+ // Nothing configured: track everywhere except localhost.
182
+ return !(0, validators_1.isLocalhost)();
183
+ };
184
+ /** Whether a wallet event kind is enabled for autocapture. Defaults to on. */
185
+ TrackingPolicy.prototype.isAutocaptureEnabled = function (eventType) {
186
+ var autocapture = this.deps.options().autocapture;
187
+ if (autocapture === undefined)
188
+ return true;
189
+ if (typeof autocapture === "boolean")
190
+ return autocapture;
191
+ if (autocapture !== null && typeof autocapture === "object") {
192
+ // Only an explicit false disables a kind.
193
+ return autocapture[eventType] !== false;
194
+ }
195
+ return true;
196
+ };
197
+ return TrackingPolicy;
198
+ }());
199
+ exports.TrackingPolicy = TrackingPolicy;
200
+ //# sourceMappingURL=TrackingPolicy.js.map
@@ -1,5 +1,5 @@
1
1
  import { ValidInputTypes } from "../types";
2
2
  export declare const isAddress: (value: ValidInputTypes, checksum?: boolean) => boolean;
3
3
  export declare const checkAddressChecksum: (data: string) => boolean;
4
- export declare function ensureIfUint8Array<T = any>(data: T): Uint8Array<ArrayBuffer> | T;
4
+ export declare function ensureIfUint8Array<T = any>(data: T): T | Uint8Array<ArrayBuffer>;
5
5
  //# sourceMappingURL=address.d.ts.map
@@ -1,2 +1,2 @@
1
- export declare const version = "1.35.2";
1
+ export declare const version = "1.36.0";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.version = void 0;
4
4
  // This file is auto-generated by scripts/update-version.js during npm version
5
5
  // Do not edit manually - it will be overwritten
6
- exports.version = '1.35.2';
6
+ exports.version = '1.36.0';
7
7
  //# sourceMappingURL=version.js.map