@formo/analytics 1.35.2 → 1.37.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 (51) 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 +98 -0
  11. package/dist/cjs/src/evm/EvmRequestTracker.js +713 -0
  12. package/dist/cjs/src/evm/batch.d.ts +94 -0
  13. package/dist/cjs/src/evm/batch.js +130 -0
  14. package/dist/cjs/src/queue/EventQueue.d.ts +28 -0
  15. package/dist/cjs/src/queue/EventQueue.js +97 -19
  16. package/dist/cjs/src/queue/type.d.ts +1 -0
  17. package/dist/cjs/src/tracking/TrackingPolicy.d.ts +146 -0
  18. package/dist/cjs/src/tracking/TrackingPolicy.js +200 -0
  19. package/dist/cjs/src/validators/address.d.ts +1 -1
  20. package/dist/cjs/src/version.d.ts +1 -1
  21. package/dist/cjs/src/version.js +1 -1
  22. package/dist/cjs/src/wagmi/WagmiEventHandler.d.ts +43 -0
  23. package/dist/cjs/src/wagmi/WagmiEventHandler.js +253 -2
  24. package/dist/cjs/src/wallet/WalletStateStore.d.ts +223 -0
  25. package/dist/cjs/src/wallet/WalletStateStore.js +515 -0
  26. package/dist/esm/src/FormoAnalytics.d.ts +79 -363
  27. package/dist/esm/src/FormoAnalytics.js +369 -2235
  28. package/dist/esm/src/event/EventManager.d.ts +3 -1
  29. package/dist/esm/src/event/EventManager.js +5 -1
  30. package/dist/esm/src/event/type.d.ts +1 -0
  31. package/dist/esm/src/evm/EvmEventTracker.d.ts +175 -0
  32. package/dist/esm/src/evm/EvmEventTracker.js +1027 -0
  33. package/dist/esm/src/evm/EvmProviderRegistry.d.ts +132 -0
  34. package/dist/esm/src/evm/EvmProviderRegistry.js +345 -0
  35. package/dist/esm/src/evm/EvmRequestTracker.d.ts +98 -0
  36. package/dist/esm/src/evm/EvmRequestTracker.js +710 -0
  37. package/dist/esm/src/evm/batch.d.ts +94 -0
  38. package/dist/esm/src/evm/batch.js +123 -0
  39. package/dist/esm/src/queue/EventQueue.d.ts +28 -0
  40. package/dist/esm/src/queue/EventQueue.js +97 -19
  41. package/dist/esm/src/queue/type.d.ts +1 -0
  42. package/dist/esm/src/tracking/TrackingPolicy.d.ts +146 -0
  43. package/dist/esm/src/tracking/TrackingPolicy.js +197 -0
  44. package/dist/esm/src/version.d.ts +1 -1
  45. package/dist/esm/src/version.js +1 -1
  46. package/dist/esm/src/wagmi/WagmiEventHandler.d.ts +43 -0
  47. package/dist/esm/src/wagmi/WagmiEventHandler.js +253 -2
  48. package/dist/esm/src/wallet/WalletStateStore.d.ts +223 -0
  49. package/dist/esm/src/wallet/WalletStateStore.js +512 -0
  50. package/dist/index.umd.min.js +1 -1
  51. package/package.json +6 -4
@@ -0,0 +1,512 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ import { ACTIVE_WALLET_KEY, ACTIVE_WALLET_TTL_MS } from "../constants";
13
+ import { cookie } from "../storage";
14
+ import { getIdentityCookieDomain, getIdentityCookieSecurity, } from "../storage/cookiePolicy";
15
+ import { logger } from "../logger";
16
+ import { validateAddress, validateAndChecksumAddress } from "../utils/address";
17
+ import { isSolanaChainId } from "../solana";
18
+ /**
19
+ * Wallet identity and chain state, and the cookie that outlives the page.
20
+ *
21
+ * Split out of `FormoAnalytics` so there is one owner for "which wallet is
22
+ * active, on which chain". Before this, `_chainState`, the derived
23
+ * `currentAddress`/`currentChainId`, the active-wallet cookie and the session
24
+ * generation were manipulated from a dozen places across a 3400-line class,
25
+ * which is what made the ordering races in #341 possible to write.
26
+ */
27
+ var WalletStateStore = /** @class */ (function () {
28
+ function WalletStateStore(deps) {
29
+ this.deps = deps;
30
+ this.state = {
31
+ evm: {},
32
+ solana: {},
33
+ };
34
+ /**
35
+ * Ticket counter for wallet observations.
36
+ *
37
+ * Every signal from a wallet is handled asynchronously: resolving an
38
+ * address, emitting an event. Between the moment a handler decides what to
39
+ * do and the moment it commits, a newer signal can arrive and be fully
40
+ * processed. Whichever handler resumes last then writes its captured data
41
+ * over the newer state.
42
+ *
43
+ * Bespoke guards were added for each case as it was found - a per-provider
44
+ * disconnect count, a per-namespace session generation, a "currently
45
+ * processing" flag - and a fourth review round kept producing new ones,
46
+ * because each guard answers one question and none establishes an order.
47
+ *
48
+ * A ticket does. A handler takes one before its first await; anything it
49
+ * commits afterwards is refused if a newer observation has claimed the
50
+ * namespace since.
51
+ */
52
+ this.observationSeq = 0;
53
+ this.newestObservation = {
54
+ evm: 0,
55
+ solana: 0,
56
+ };
57
+ /**
58
+ * How many disconnects a namespace has begun.
59
+ *
60
+ * Separate from the observation ticket on purpose, because they answer
61
+ * different questions. A ticket asks "is the state I captured still the
62
+ * newest?", which is what a switch or a probe needs. This asks "did the
63
+ * wallet go away after I started?", which is what a connect handler needs.
64
+ *
65
+ * Conflating them loses connects: a connect observation superseded by a
66
+ * NEWER connect must still be reported by somebody, and the ticket cannot
67
+ * tell that apart from being superseded by a disconnect.
68
+ */
69
+ this.disconnectCount = {
70
+ evm: 0,
71
+ solana: 0,
72
+ };
73
+ }
74
+ Object.defineProperty(WalletStateStore.prototype, "activeNamespace", {
75
+ /** Which namespace currently owns the derived values, if any. */
76
+ get: function () {
77
+ return this._activeNamespace;
78
+ },
79
+ enumerable: false,
80
+ configurable: true
81
+ });
82
+ // ── reads ────────────────────────────────────────────────────────────────
83
+ /** Which namespace a chain id belongs to. */
84
+ WalletStateStore.prototype.namespaceOf = function (chainId) {
85
+ return isSolanaChainId(chainId) ? "solana" : "evm";
86
+ };
87
+ Object.defineProperty(WalletStateStore.prototype, "evmAddress", {
88
+ get: function () {
89
+ return this.state.evm.address;
90
+ },
91
+ enumerable: false,
92
+ configurable: true
93
+ });
94
+ Object.defineProperty(WalletStateStore.prototype, "evmChainId", {
95
+ get: function () {
96
+ return this.state.evm.chainId;
97
+ },
98
+ enumerable: false,
99
+ configurable: true
100
+ });
101
+ Object.defineProperty(WalletStateStore.prototype, "provider", {
102
+ get: function () {
103
+ return this.state.evm.provider;
104
+ },
105
+ // ── writes ───────────────────────────────────────────────────────────────
106
+ set: function (next) {
107
+ this.displaceProvider(this.state.evm.provider, next);
108
+ this.state.evm.provider = next;
109
+ },
110
+ enumerable: false,
111
+ configurable: true
112
+ });
113
+ /**
114
+ * Take a ticket for an observation about to be processed.
115
+ *
116
+ * MUST be called before the handler's first await, so the order recorded is
117
+ * the order the signals arrived in, not the order their async work happens
118
+ * to finish in.
119
+ */
120
+ WalletStateStore.prototype.observe = function (namespace) {
121
+ var id = ++this.observationSeq;
122
+ this.newestObservation[namespace] = id;
123
+ return { id: id, namespace: namespace };
124
+ };
125
+ /**
126
+ * Record that a namespace's wallet is being torn down.
127
+ *
128
+ * Called at EVERY teardown site before anything is awaited, and whether or
129
+ * not a disconnect event will be emitted: whether the app opted into
130
+ * disconnect autocapture has no bearing on ordering.
131
+ */
132
+ WalletStateStore.prototype.beginDisconnect = function (namespace) {
133
+ this.disconnectCount[namespace]++;
134
+ };
135
+ /** How many disconnects this namespace has begun so far. */
136
+ WalletStateStore.prototype.disconnectsSoFar = function (namespace) {
137
+ return this.disconnectCount[namespace];
138
+ };
139
+ /**
140
+ * The newest observation for a namespace, for a caller that must NOT
141
+ * supersede its own caller.
142
+ *
143
+ * `disconnect()` is reached both directly by a consumer and from a handler
144
+ * that already holds a ticket. Taking a fresh ticket there would invalidate
145
+ * the handler that called it. Reading the current value and checking it
146
+ * later asks the same question without changing the answer for anyone else.
147
+ */
148
+ WalletStateStore.prototype.snapshot = function (namespace) {
149
+ return this.newestObservation[namespace];
150
+ };
151
+ /** Whether nothing newer has claimed the namespace since `snapshot`. */
152
+ WalletStateStore.prototype.isUnchangedSince = function (namespace, snapshot) {
153
+ return this.newestObservation[namespace] === snapshot;
154
+ };
155
+ /**
156
+ * The current newest observation, as a ticket, WITHOUT taking a new one.
157
+ *
158
+ * For a handler that must respect the order but has nothing to claim: it
159
+ * still abandons its work if something newer arrives, but it does not
160
+ * supersede whatever is already in flight.
161
+ */
162
+ WalletStateStore.prototype.currentObservation = function (namespace) {
163
+ return { id: this.newestObservation[namespace], namespace: namespace };
164
+ };
165
+ /**
166
+ * Whether this observation is still the newest for its namespace.
167
+ *
168
+ * False means a newer signal arrived while this handler was suspended, and
169
+ * it must abandon whatever it captured rather than write it over the newer
170
+ * state. Superseding, not dropping: the newer handler is already running.
171
+ */
172
+ WalletStateStore.prototype.isCurrent = function (observation) {
173
+ return this.newestObservation[observation.namespace] === observation.id;
174
+ };
175
+ /**
176
+ * Update a namespace and re-derive `address`/`chainId`.
177
+ *
178
+ * Accepts a namespace or a chain id. A chain id is also stored as the
179
+ * namespace's chain unless the update names one explicitly.
180
+ */
181
+ WalletStateStore.prototype.set = function (namespaceOrChainId, update) {
182
+ var namespace = typeof namespaceOrChainId === "string"
183
+ ? namespaceOrChainId
184
+ : this.namespaceOf(namespaceOrChainId);
185
+ var ns = this.state[namespace];
186
+ // A plain write. Whether this transition is still the newest is decided
187
+ // by the observation ticket its caller holds, not by comparing addresses
188
+ // here: re-adopting the SAME wallet is a real transition, and no
189
+ // comparison of the values can tell that apart from nothing happening.
190
+ if ("address" in update)
191
+ ns.address = update.address;
192
+ if ("chainId" in update) {
193
+ ns.chainId = update.chainId;
194
+ }
195
+ else if (typeof namespaceOrChainId === "number") {
196
+ ns.chainId = namespaceOrChainId;
197
+ }
198
+ if (namespace === "evm" && "provider" in update) {
199
+ this.displaceProvider(ns.provider, update.provider);
200
+ ns.provider = update.provider;
201
+ }
202
+ this._activeNamespace = namespace;
203
+ this.syncDerived();
204
+ };
205
+ /** Wipe a namespace. Per-namespace so a Solana disconnect spares EVM. */
206
+ WalletStateStore.prototype.clear = function (namespaceOrChainId) {
207
+ var namespace = typeof namespaceOrChainId === "string"
208
+ ? namespaceOrChainId
209
+ : this.namespaceOf(namespaceOrChainId);
210
+ if (namespace === "evm") {
211
+ // Wiping the namespace drops the active provider with it.
212
+ this.displaceProvider(this.state.evm.provider, undefined);
213
+ this.state.evm = {};
214
+ }
215
+ else {
216
+ this.state.solana = {};
217
+ }
218
+ this.syncDerived();
219
+ };
220
+ /** Both namespaces, keeping the EVM provider so tracking can resume. */
221
+ WalletStateStore.prototype.reset = function () {
222
+ var evmProvider = this.state.evm.provider;
223
+ this.state = { evm: { provider: evmProvider }, solana: {} };
224
+ this._activeNamespace = undefined;
225
+ this.address = undefined;
226
+ this.chainId = undefined;
227
+ };
228
+ /** Drop the active provider without touching identity. */
229
+ WalletStateStore.prototype.clearProvider = function () {
230
+ this.provider = undefined;
231
+ };
232
+ /**
233
+ * Repoint the active identity at a wallet, as `identify(setActive)` does,
234
+ * without disturbing per-namespace chain state.
235
+ *
236
+ * Deliberately transient. The next wallet event for either namespace
237
+ * re-derives from namespace state and replaces this, which is the intended
238
+ * order: a wallet that actually connects outranks one merely named by
239
+ * `identify()`. The alternative, making an identify sticky until another
240
+ * identify, would silently mis-attribute every event after a connect.
241
+ */
242
+ WalletStateStore.prototype.setActiveAddress = function (address) {
243
+ this.address = address;
244
+ this.persist();
245
+ };
246
+ /** Repoint (or forget) the derived chain while keeping the wallet. */
247
+ WalletStateStore.prototype.setActiveChainId = function (chainId) {
248
+ this.chainId = chainId;
249
+ this.persist();
250
+ };
251
+ /** Forget the derived chain while keeping the wallet. */
252
+ WalletStateStore.prototype.clearActiveChainId = function () {
253
+ this.setActiveChainId(undefined);
254
+ };
255
+ // ── higher-level operations ──────────────────────────────────────────────
256
+ /**
257
+ * Record validated wallet/chain state WITHOUT emitting an event.
258
+ *
259
+ * Integrations (the wagmi handler among them) must call this on every
260
+ * connect / chain change / disconnect, even when the matching autocapture
261
+ * event is disabled. Otherwise `chainId` stays stale and the exclusion gate,
262
+ * which keys off it rather than the event payload, can be bypassed.
263
+ */
264
+ WalletStateStore.prototype.syncWalletState = function (params) {
265
+ var chainId = params.chainId, address = params.address;
266
+ // An integration adopting or dropping a wallet is a wallet signal like
267
+ // any other, so it takes its place in the order. Without this, a
268
+ // disconnect still in flight could not tell that the wagmi handler had
269
+ // already adopted a replacement.
270
+ this.observe(this.namespaceOf(chainId));
271
+ if (this.deps.isTrackingSuppressed()) {
272
+ // While suppressed we must never LEARN a new wallet, but we must still
273
+ // CLEAR a stale one. Otherwise a disconnect or switch observed on a
274
+ // suppressed route leaves the previous address in memory and in the
275
+ // cookie, ready to attach to later allowed-page events.
276
+ if (!address) {
277
+ this.clearForChain(chainId);
278
+ return;
279
+ }
280
+ if (chainId === null || chainId === undefined)
281
+ return;
282
+ var namespace = this.namespaceOf(chainId);
283
+ var known = this.state[namespace].address;
284
+ var incoming = validateAddress(address, chainId);
285
+ if (known && incoming && incoming !== known) {
286
+ // A switch away from the learned wallet invalidates it. Drop the
287
+ // stale one without learning the new address.
288
+ this.clear(chainId);
289
+ }
290
+ return;
291
+ }
292
+ if (!address) {
293
+ this.clearForChain(chainId);
294
+ return;
295
+ }
296
+ if (chainId === null || chainId === undefined)
297
+ return;
298
+ var valid = validateAddress(address, chainId);
299
+ if (!valid) {
300
+ logger.warn("syncWalletState: invalid address (\"".concat(address, "\") for chain ").concat(chainId));
301
+ return;
302
+ }
303
+ this.set(chainId, { address: valid });
304
+ };
305
+ /**
306
+ * Learn an EVM wallet observed on an autocaptured signature or transaction,
307
+ * when nothing is known yet.
308
+ *
309
+ * Deliberately conservative: it never overwrites a different wallet, and it
310
+ * corrects only a stale chain for the wallet already known.
311
+ */
312
+ WalletStateStore.prototype.backfill = function (address, chainId, provider) {
313
+ // Refuse a chain the provider has since moved off.
314
+ //
315
+ // A request captures its chain once and reuses that snapshot for every
316
+ // status it emits, which is right for the payload: a confirmation must
317
+ // not be relabelled mid-flight. Writing that captured value back into
318
+ // central state on a LATER status restored a chain the wallet had already
319
+ // left, and the unscoped events that fall back to it then bypassed an
320
+ // exclusion that should have caught them.
321
+ if (provider && chainId !== undefined && chainId !== 0) {
322
+ var live = this.deps.providerChainId(provider);
323
+ if (live !== undefined && live !== chainId)
324
+ chainId = live;
325
+ }
326
+ // `0` is kept deliberately. It means "could not resolve", and persisting
327
+ // it is what lets the exclusion gate refuse the unscoped events (page,
328
+ // track, identify) that carry no chain of their own. Erasing it to
329
+ // `undefined` looked tidier but removed the only marker distinguishing
330
+ // "unknown" from "no wallet yet".
331
+ // Never learn identity while suppressed. A signature observed on an
332
+ // excluded route must not populate the address for later allowed pages.
333
+ if (this.deps.isTrackingSuppressed())
334
+ return;
335
+ var known = this.evmAddress;
336
+ if (known) {
337
+ // Same wallet, newer chain: correct it rather than returning. A
338
+ // persisted wallet restores a chain from a previous session, and if the
339
+ // provider has since moved, the stale restored chain would stay in the
340
+ // derived value and carry the unscoped events past an exclusion.
341
+ // `0` means "could not resolve". Correcting a stale chain with a real
342
+ // one is the point of this branch; replacing a chain we already know
343
+ // with "unknown" only makes attribution worse, and would trip the
344
+ // exclusion gate's fail-closed rule against a wallet we can place.
345
+ var usable = chainId !== undefined && !(chainId === 0 && this.evmChainId);
346
+ if (usable &&
347
+ known.toLowerCase() === address.toLowerCase() &&
348
+ this.evmChainId !== chainId) {
349
+ this.set("evm", { address: known, chainId: chainId });
350
+ }
351
+ // A DIFFERENT address is another wallet's business; never overwrite.
352
+ return;
353
+ }
354
+ this.set("evm", { address: address, chainId: chainId });
355
+ };
356
+ /**
357
+ * Apply an EVM connect/switch seen while tracking is suppressed: never
358
+ * learn the wallet, but if it is a switch away from one already learned,
359
+ * drop the stale one so it cannot attach to a later allowed-page event.
360
+ */
361
+ WalletStateStore.prototype.clearStaleEvmWalletOnSwitchWhileSuppressed = function (address) {
362
+ var known = this.state.evm.address;
363
+ var incoming = validateAndChecksumAddress(address);
364
+ if (known && incoming && incoming !== known)
365
+ this.clear("evm");
366
+ };
367
+ // ── persistence ──────────────────────────────────────────────────────────
368
+ /**
369
+ * Persist (or clear) the wallet snapshot, so the next page load can
370
+ * repopulate identity before the wallet reconnects. Without it, every
371
+ * `track()` / `page()` between page-show and reconnection ships with no
372
+ * address.
373
+ */
374
+ WalletStateStore.prototype.persist = function () {
375
+ try {
376
+ // Visitor-level suppression is stable for the session, so purging is
377
+ // safe and prevents a stale snapshot outliving an opt-out.
378
+ if (this.deps.isPersistedIdentityPurgeRequired()) {
379
+ cookie().remove(ACTIVE_WALLET_KEY);
380
+ return;
381
+ }
382
+ if (this.address) {
383
+ // Page-level exclusion: do not write while on an excluded route, but
384
+ // leave any existing cookie alone. One written on an allowed page
385
+ // must survive a transient visit here.
386
+ if (this.deps.isPageExcluded())
387
+ return;
388
+ var value = JSON.stringify(__assign({ address: this.address }, (this.chainId !== undefined && { chainId: this.chainId })));
389
+ var domain = getIdentityCookieDomain(this.deps.crossSubdomainCookies());
390
+ cookie().set(ACTIVE_WALLET_KEY, value, __assign(__assign({ path: "/", expires: new Date(Date.now() + ACTIVE_WALLET_TTL_MS).toUTCString() }, getIdentityCookieSecurity()), (domain ? { domain: domain } : {})));
391
+ }
392
+ else {
393
+ // No active wallet: clear the snapshot. This runs even on an excluded
394
+ // route, so a disconnect observed while suppressed actively removes
395
+ // stale identity rather than leaving it for later.
396
+ cookie().remove(ACTIVE_WALLET_KEY);
397
+ }
398
+ }
399
+ catch (err) {
400
+ logger.warn("Failed to persist current wallet snapshot", err);
401
+ }
402
+ };
403
+ /** Seed identity from the persisted snapshot, once, during construction. */
404
+ WalletStateStore.prototype.load = function () {
405
+ try {
406
+ if (this.deps.isPersistedIdentityPurgeRequired()) {
407
+ cookie().remove(ACTIVE_WALLET_KEY);
408
+ return;
409
+ }
410
+ // Page-level exclusion: don't restore into memory here, but keep the
411
+ // cookie so a later allowed-page load can.
412
+ if (this.deps.isPageExcluded())
413
+ return;
414
+ var raw = cookie().get(ACTIVE_WALLET_KEY);
415
+ if (!raw)
416
+ return;
417
+ var parsed = JSON.parse(raw);
418
+ if (!(parsed === null || parsed === void 0 ? void 0 : parsed.address))
419
+ return;
420
+ // A cookie is attacker-writable and survives across SDK versions, so
421
+ // the chain has to be a real number before anything trusts it. A string
422
+ // "137" restored as-is would never match a numeric exclusion list, so
423
+ // an excluded chain would silently start reporting again.
424
+ //
425
+ // A present-but-unusable chain rejects the WHOLE snapshot rather than
426
+ // being downgraded to "chainless". Downgrading looks harmless and is
427
+ // not: the namespace is derived from the chain, so a Solana wallet with
428
+ // a corrupt chain would be filed under EVM, and a later Solana
429
+ // disconnect would fall through to that phantom entry and keep
430
+ // attributing events to a wallet that had gone.
431
+ var rawChain = parsed.chainId;
432
+ var chainMissing = rawChain === undefined || rawChain === null;
433
+ if (!chainMissing &&
434
+ (typeof rawChain !== "number" || !Number.isFinite(rawChain))) {
435
+ cookie().remove(ACTIVE_WALLET_KEY);
436
+ return;
437
+ }
438
+ var chainId = chainMissing ? undefined : rawChain;
439
+ // With no chain to go on, the address shape decides. Defaulting to EVM
440
+ // filed a chainless Solana wallet under EVM, where later EVM handlers
441
+ // read it as `evmAddress` and emitted EVM state for a Solana wallet.
442
+ var namespace = chainId === undefined
443
+ ? validateAndChecksumAddress(parsed.address)
444
+ ? "evm"
445
+ : "solana"
446
+ : this.namespaceOf(chainId);
447
+ var validated = validateAddress(parsed.address, chainId);
448
+ if (!validated) {
449
+ cookie().remove(ACTIVE_WALLET_KEY);
450
+ return;
451
+ }
452
+ var ns = this.state[namespace];
453
+ ns.address = validated;
454
+ if (chainId !== undefined)
455
+ ns.chainId = chainId;
456
+ this._activeNamespace = namespace;
457
+ this.address = validated;
458
+ this.chainId = chainId;
459
+ }
460
+ catch (err) {
461
+ logger.warn("Failed to restore persisted wallet snapshot", err);
462
+ cookie().remove(ACTIVE_WALLET_KEY);
463
+ }
464
+ };
465
+ // ── internals ────────────────────────────────────────────────────────────
466
+ WalletStateStore.prototype.clearForChain = function (chainId) {
467
+ if (chainId !== undefined && chainId !== null) {
468
+ this.clear(chainId);
469
+ }
470
+ else {
471
+ this.clear("evm");
472
+ this.clear("solana");
473
+ }
474
+ };
475
+ WalletStateStore.prototype.displaceProvider = function (previous, next) {
476
+ if (previous && previous !== next)
477
+ this.deps.onProviderDisplaced(previous);
478
+ };
479
+ /**
480
+ * Re-derive `address`/`chainId` from the active namespace.
481
+ *
482
+ * Last-connected wins, then fall through to the other namespace so a
483
+ * still-connected wallet keeps attribution when its neighbour disconnects.
484
+ */
485
+ WalletStateStore.prototype.syncDerived = function () {
486
+ var active = this._activeNamespace;
487
+ if (active) {
488
+ var state = this.state[active];
489
+ if (state.address || state.chainId) {
490
+ this.address = state.address;
491
+ this.chainId = state.chainId;
492
+ this.persist();
493
+ return;
494
+ }
495
+ }
496
+ var other = active === "evm" ? "solana" : "evm";
497
+ var otherState = this.state[other];
498
+ if (otherState.address || otherState.chainId) {
499
+ this.address = otherState.address;
500
+ this.chainId = otherState.chainId;
501
+ this._activeNamespace = other;
502
+ this.persist();
503
+ return;
504
+ }
505
+ this.address = undefined;
506
+ this.chainId = undefined;
507
+ this.persist();
508
+ };
509
+ return WalletStateStore;
510
+ }());
511
+ export { WalletStateStore };
512
+ //# sourceMappingURL=WalletStateStore.js.map