@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
@@ -0,0 +1,223 @@
1
+ import { Address, ChainID, ChainNamespace, EIP1193Provider } from "../types";
2
+ /**
3
+ * A ticket identifying one wallet observation, taken before any async work.
4
+ * See `WalletStateStore.observe`.
5
+ */
6
+ export interface Observation {
7
+ readonly id: number;
8
+ readonly namespace: ChainNamespace;
9
+ }
10
+ /** What the store needs from the SDK that owns it. */
11
+ export interface WalletStateStoreDeps {
12
+ /** Opt-out or an excluded timezone: purge persisted identity, don't keep it. */
13
+ isPersistedIdentityPurgeRequired(): boolean;
14
+ /** Excluded host or path: skip writing and restoring, but keep the cookie. */
15
+ isPageExcluded(): boolean;
16
+ /** Any visitor-level or page-level suppression: never LEARN a wallet. */
17
+ isTrackingSuppressed(): boolean;
18
+ /** Whether the identity cookie is shared across subdomains. */
19
+ crossSubdomainCookies(): boolean;
20
+ /**
21
+ * The last chain heard from a provider, or undefined if none.
22
+ *
23
+ * Used to refuse a captured chain the wallet has since left. That state
24
+ * belongs to provider tracking, so the store asks rather than owns it.
25
+ */
26
+ providerChainId(provider: EIP1193Provider): number | undefined;
27
+ /**
28
+ * A provider stopped being the active one.
29
+ *
30
+ * From the SDK's point of view its connection has ended, so whatever the
31
+ * owner records about it (a reported connect, for one) must stop counting.
32
+ * The active provider is reassigned from several paths, so the store
33
+ * reports it centrally rather than each caller remembering to.
34
+ */
35
+ onProviderDisplaced(previous: EIP1193Provider): void;
36
+ }
37
+ /**
38
+ * Wallet identity and chain state, and the cookie that outlives the page.
39
+ *
40
+ * Split out of `FormoAnalytics` so there is one owner for "which wallet is
41
+ * active, on which chain". Before this, `_chainState`, the derived
42
+ * `currentAddress`/`currentChainId`, the active-wallet cookie and the session
43
+ * generation were manipulated from a dozen places across a 3400-line class,
44
+ * which is what made the ordering races in #341 possible to write.
45
+ */
46
+ export declare class WalletStateStore {
47
+ private readonly deps;
48
+ private state;
49
+ /**
50
+ * Which namespace last claimed the wallet slot. Last-connected wins, so an
51
+ * EVM connect after a Solana one repoints the derived values, and vice
52
+ * versa, rather than one namespace permanently shadowing the other.
53
+ */
54
+ private _activeNamespace?;
55
+ /** Which namespace currently owns the derived values, if any. */
56
+ get activeNamespace(): ChainNamespace | undefined;
57
+ /**
58
+ * Ticket counter for wallet observations.
59
+ *
60
+ * Every signal from a wallet is handled asynchronously: resolving an
61
+ * address, emitting an event. Between the moment a handler decides what to
62
+ * do and the moment it commits, a newer signal can arrive and be fully
63
+ * processed. Whichever handler resumes last then writes its captured data
64
+ * over the newer state.
65
+ *
66
+ * Bespoke guards were added for each case as it was found - a per-provider
67
+ * disconnect count, a per-namespace session generation, a "currently
68
+ * processing" flag - and a fourth review round kept producing new ones,
69
+ * because each guard answers one question and none establishes an order.
70
+ *
71
+ * A ticket does. A handler takes one before its first await; anything it
72
+ * commits afterwards is refused if a newer observation has claimed the
73
+ * namespace since.
74
+ */
75
+ private observationSeq;
76
+ private newestObservation;
77
+ /**
78
+ * How many disconnects a namespace has begun.
79
+ *
80
+ * Separate from the observation ticket on purpose, because they answer
81
+ * different questions. A ticket asks "is the state I captured still the
82
+ * newest?", which is what a switch or a probe needs. This asks "did the
83
+ * wallet go away after I started?", which is what a connect handler needs.
84
+ *
85
+ * Conflating them loses connects: a connect observation superseded by a
86
+ * NEWER connect must still be reported by somebody, and the ticket cannot
87
+ * tell that apart from being superseded by a disconnect.
88
+ */
89
+ private disconnectCount;
90
+ /** Derived from the active namespace. Read by integrations and by events. */
91
+ address?: Address;
92
+ chainId?: ChainID;
93
+ constructor(deps: WalletStateStoreDeps);
94
+ /** Which namespace a chain id belongs to. */
95
+ namespaceOf(chainId?: ChainID): ChainNamespace;
96
+ get evmAddress(): Address | undefined;
97
+ get evmChainId(): ChainID | undefined;
98
+ get provider(): EIP1193Provider | undefined;
99
+ /**
100
+ * Take a ticket for an observation about to be processed.
101
+ *
102
+ * MUST be called before the handler's first await, so the order recorded is
103
+ * the order the signals arrived in, not the order their async work happens
104
+ * to finish in.
105
+ */
106
+ observe(namespace: ChainNamespace): Observation;
107
+ /**
108
+ * Record that a namespace's wallet is being torn down.
109
+ *
110
+ * Called at EVERY teardown site before anything is awaited, and whether or
111
+ * not a disconnect event will be emitted: whether the app opted into
112
+ * disconnect autocapture has no bearing on ordering.
113
+ */
114
+ beginDisconnect(namespace: ChainNamespace): void;
115
+ /** How many disconnects this namespace has begun so far. */
116
+ disconnectsSoFar(namespace: ChainNamespace): number;
117
+ /**
118
+ * The newest observation for a namespace, for a caller that must NOT
119
+ * supersede its own caller.
120
+ *
121
+ * `disconnect()` is reached both directly by a consumer and from a handler
122
+ * that already holds a ticket. Taking a fresh ticket there would invalidate
123
+ * the handler that called it. Reading the current value and checking it
124
+ * later asks the same question without changing the answer for anyone else.
125
+ */
126
+ snapshot(namespace: ChainNamespace): number;
127
+ /** Whether nothing newer has claimed the namespace since `snapshot`. */
128
+ isUnchangedSince(namespace: ChainNamespace, snapshot: number): boolean;
129
+ /**
130
+ * The current newest observation, as a ticket, WITHOUT taking a new one.
131
+ *
132
+ * For a handler that must respect the order but has nothing to claim: it
133
+ * still abandons its work if something newer arrives, but it does not
134
+ * supersede whatever is already in flight.
135
+ */
136
+ currentObservation(namespace: ChainNamespace): Observation;
137
+ /**
138
+ * Whether this observation is still the newest for its namespace.
139
+ *
140
+ * False means a newer signal arrived while this handler was suspended, and
141
+ * it must abandon whatever it captured rather than write it over the newer
142
+ * state. Superseding, not dropping: the newer handler is already running.
143
+ */
144
+ isCurrent(observation: Observation): boolean;
145
+ set provider(next: EIP1193Provider | undefined);
146
+ /**
147
+ * Update a namespace and re-derive `address`/`chainId`.
148
+ *
149
+ * Accepts a namespace or a chain id. A chain id is also stored as the
150
+ * namespace's chain unless the update names one explicitly.
151
+ */
152
+ set(namespaceOrChainId: ChainNamespace | ChainID | undefined, update: {
153
+ address?: Address;
154
+ chainId?: ChainID;
155
+ provider?: EIP1193Provider;
156
+ }): void;
157
+ /** Wipe a namespace. Per-namespace so a Solana disconnect spares EVM. */
158
+ clear(namespaceOrChainId: ChainNamespace | ChainID | undefined): void;
159
+ /** Both namespaces, keeping the EVM provider so tracking can resume. */
160
+ reset(): void;
161
+ /** Drop the active provider without touching identity. */
162
+ clearProvider(): void;
163
+ /**
164
+ * Repoint the active identity at a wallet, as `identify(setActive)` does,
165
+ * without disturbing per-namespace chain state.
166
+ *
167
+ * Deliberately transient. The next wallet event for either namespace
168
+ * re-derives from namespace state and replaces this, which is the intended
169
+ * order: a wallet that actually connects outranks one merely named by
170
+ * `identify()`. The alternative, making an identify sticky until another
171
+ * identify, would silently mis-attribute every event after a connect.
172
+ */
173
+ setActiveAddress(address: Address | undefined): void;
174
+ /** Repoint (or forget) the derived chain while keeping the wallet. */
175
+ setActiveChainId(chainId: ChainID | undefined): void;
176
+ /** Forget the derived chain while keeping the wallet. */
177
+ clearActiveChainId(): void;
178
+ /**
179
+ * Record validated wallet/chain state WITHOUT emitting an event.
180
+ *
181
+ * Integrations (the wagmi handler among them) must call this on every
182
+ * connect / chain change / disconnect, even when the matching autocapture
183
+ * event is disabled. Otherwise `chainId` stays stale and the exclusion gate,
184
+ * which keys off it rather than the event payload, can be bypassed.
185
+ */
186
+ syncWalletState(params: {
187
+ chainId?: ChainID;
188
+ address?: Address;
189
+ }): void;
190
+ /**
191
+ * Learn an EVM wallet observed on an autocaptured signature or transaction,
192
+ * when nothing is known yet.
193
+ *
194
+ * Deliberately conservative: it never overwrites a different wallet, and it
195
+ * corrects only a stale chain for the wallet already known.
196
+ */
197
+ backfill(address: Address, chainId?: ChainID, provider?: EIP1193Provider): void;
198
+ /**
199
+ * Apply an EVM connect/switch seen while tracking is suppressed: never
200
+ * learn the wallet, but if it is a switch away from one already learned,
201
+ * drop the stale one so it cannot attach to a later allowed-page event.
202
+ */
203
+ clearStaleEvmWalletOnSwitchWhileSuppressed(address: string): void;
204
+ /**
205
+ * Persist (or clear) the wallet snapshot, so the next page load can
206
+ * repopulate identity before the wallet reconnects. Without it, every
207
+ * `track()` / `page()` between page-show and reconnection ships with no
208
+ * address.
209
+ */
210
+ persist(): void;
211
+ /** Seed identity from the persisted snapshot, once, during construction. */
212
+ load(): void;
213
+ private clearForChain;
214
+ private displaceProvider;
215
+ /**
216
+ * Re-derive `address`/`chainId` from the active namespace.
217
+ *
218
+ * Last-connected wins, then fall through to the other namespace so a
219
+ * still-connected wallet keeps attribution when its neighbour disconnects.
220
+ */
221
+ private syncDerived;
222
+ }
223
+ //# sourceMappingURL=WalletStateStore.d.ts.map