@formo/analytics 1.38.2 → 1.39.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 (38) hide show
  1. package/dist/cjs/src/FormoAnalytics.js +9 -4
  2. package/dist/cjs/src/FormoAnalyticsProvider.js +20 -0
  3. package/dist/cjs/src/solana/SolanaManager.d.ts +37 -9
  4. package/dist/cjs/src/solana/SolanaManager.js +135 -20
  5. package/dist/cjs/src/solana/SolanaStoreHandler.d.ts +6 -0
  6. package/dist/cjs/src/solana/SolanaStoreHandler.js +17 -8
  7. package/dist/cjs/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
  8. package/dist/cjs/src/solana/SolanaWalletStandardRegistry.js +447 -0
  9. package/dist/cjs/src/solana/index.d.ts +10 -5
  10. package/dist/cjs/src/solana/index.js +12 -6
  11. package/dist/cjs/src/solana/storeTypes.d.ts +20 -1
  12. package/dist/cjs/src/solana/types.d.ts +32 -9
  13. package/dist/cjs/src/solana/types.js +15 -0
  14. package/dist/cjs/src/solana/walletStandardTypes.d.ts +54 -0
  15. package/dist/cjs/src/solana/walletStandardTypes.js +21 -0
  16. package/dist/cjs/src/types/base.d.ts +15 -5
  17. package/dist/cjs/src/version.d.ts +1 -1
  18. package/dist/cjs/src/version.js +1 -1
  19. package/dist/esm/src/FormoAnalytics.js +9 -4
  20. package/dist/esm/src/FormoAnalyticsProvider.js +20 -0
  21. package/dist/esm/src/solana/SolanaManager.d.ts +37 -9
  22. package/dist/esm/src/solana/SolanaManager.js +135 -20
  23. package/dist/esm/src/solana/SolanaStoreHandler.d.ts +6 -0
  24. package/dist/esm/src/solana/SolanaStoreHandler.js +18 -9
  25. package/dist/esm/src/solana/SolanaWalletStandardRegistry.d.ts +163 -0
  26. package/dist/esm/src/solana/SolanaWalletStandardRegistry.js +444 -0
  27. package/dist/esm/src/solana/index.d.ts +10 -5
  28. package/dist/esm/src/solana/index.js +10 -5
  29. package/dist/esm/src/solana/storeTypes.d.ts +20 -1
  30. package/dist/esm/src/solana/types.d.ts +32 -9
  31. package/dist/esm/src/solana/types.js +14 -0
  32. package/dist/esm/src/solana/walletStandardTypes.d.ts +54 -0
  33. package/dist/esm/src/solana/walletStandardTypes.js +18 -0
  34. package/dist/esm/src/types/base.d.ts +15 -5
  35. package/dist/esm/src/version.d.ts +1 -1
  36. package/dist/esm/src/version.js +1 -1
  37. package/dist/index.umd.min.js +1 -1
  38. package/package.json +2 -2
@@ -245,9 +245,12 @@ var FormoAnalytics = /** @class */ (function () {
245
245
  this.evmEvents.trackEIP1193Provider(provider);
246
246
  }
247
247
  }
248
- // Initialize Solana manager if Solana options are provided
249
- if (options.solana) {
250
- this.solanaManager = new SolanaManager_1.SolanaManager(this, options.solana);
248
+ // Solana wallets are discovered through the Wallet Standard
249
+ // unconditionally, the way EVM wallets are through EIP-6963: an app
250
+ // that never configures Solana still gets its connects. `solana: false`
251
+ // is the opt-out; an object adds framework-kit's store or a cluster.
252
+ if (options.solana !== false) {
253
+ this.solanaManager = new SolanaManager_1.SolanaManager(this, typeof options.solana === "object" ? options.solana : undefined);
251
254
  }
252
255
  this._currentUrl = window.location.href;
253
256
  // Seed currentAddress/currentChainId from the persisted snapshot before
@@ -1271,7 +1274,9 @@ var FormoAnalytics = /** @class */ (function () {
1271
1274
  */
1272
1275
  get: function () {
1273
1276
  if (!this.solanaManager) {
1274
- this.solanaManager = new SolanaManager_1.SolanaManager(this);
1277
+ // Only reachable after `solana: false` (or after cleanup). The host
1278
+ // opted out of discovery, so this manager serves the store path only.
1279
+ this.solanaManager = new SolanaManager_1.SolanaManager(this, undefined, false);
1275
1280
  }
1276
1281
  return this.solanaManager;
1277
1282
  },
@@ -72,6 +72,18 @@ var defaultContext = {
72
72
  hasOptedOutTracking: function () { return false; },
73
73
  };
74
74
  exports.FormoAnalyticsContext = (0, react_1.createContext)(defaultContext);
75
+ var optionObjectIds = new WeakMap();
76
+ var nextOptionObjectId = 1;
77
+ var optionObjectId = function (value) {
78
+ if (!value)
79
+ return undefined;
80
+ var id = optionObjectIds.get(value);
81
+ if (id === undefined) {
82
+ id = nextOptionObjectId++;
83
+ optionObjectIds.set(value, id);
84
+ }
85
+ return id;
86
+ };
75
87
  /**
76
88
  * A stable key over the serializable parts of Options. The provider effect
77
89
  * re-initialises the SDK when this key changes; anything that alters SDK
@@ -95,6 +107,14 @@ var computeOptionsKey = function (options) {
95
107
  logger: options.logger,
96
108
  referral: options.referral,
97
109
  evm: options.evm,
110
+ // `solana` is a boolean or an options object. A store is a live event
111
+ // source, so replacing it must reinitialize the SDK even when both the
112
+ // old and new options contain a store.
113
+ solana: typeof options.solana === "object" ? undefined : options.solana,
114
+ solanaStoreId: typeof options.solana === "object"
115
+ ? optionObjectId(options.solana.store)
116
+ : undefined,
117
+ solanaCluster: typeof options.solana === "object" ? options.solana.cluster : undefined,
98
118
  // For complex objects, just track their presence, not their content
99
119
  hasProvider: !!options.provider,
100
120
  hasWagmi: !!options.wagmi,
@@ -1,14 +1,27 @@
1
1
  /**
2
2
  * SolanaManager
3
3
  *
4
- * Manages the lifecycle of the Solana store integration.
5
- * Subscribes to framework-kit's zustand store for automatic event capture
6
- * of wallet connect/disconnect and transaction lifecycle events.
4
+ * Owns the two ways the SDK learns about Solana wallets:
7
5
  *
8
- * For signMessage/signTransaction tracking (not captured by the store),
6
+ * 1. `SolanaWalletStandardRegistry`: discovers wallets through the Wallet
7
+ * Standard and reports detect / connect / disconnect. On by default, so
8
+ * compatible wallets registered by Solana Kit, wallet-adapter,
9
+ * framework-kit, or another host are covered with no configuration,
10
+ * exactly like EVM wallets through EIP-6963. `solana: false` turns it off.
11
+ * 2. `SolanaStoreHandler`: subscribes to framework-kit's zustand store for
12
+ * connect / disconnect / cluster changes AND the transaction lifecycle.
13
+ * Opt-in through `solana: { store }` or `formo.solana.setStore()`.
14
+ *
15
+ * Both observe the same Wallet Standard connection when a framework-kit app
16
+ * connects. A store supplied at initialization owns wallet events; a store
17
+ * attached later takes ownership when it observes its first connection and
18
+ * adopts any connect the registry already reported. One connect per
19
+ * connection, whichever path an app is on.
20
+ *
21
+ * For signMessage/signTransaction tracking (not captured by either path),
9
22
  * use formo.signature() directly with the address and chainId.
10
23
  *
11
- * For manual event tracking without the store, use the core API directly:
24
+ * For manual event tracking, use the core API directly:
12
25
  * formo.transaction(), formo.signature(), formo.connect(), formo.disconnect().
13
26
  */
14
27
  import { FormoAnalytics } from "../FormoAnalytics";
@@ -16,9 +29,19 @@ import { SolanaCluster, SolanaOptions } from "./types";
16
29
  import { SolanaClientStore } from "./storeTypes";
17
30
  export declare class SolanaManager {
18
31
  private formo;
32
+ private readonly enabled;
19
33
  private storeHandler?;
34
+ private registry?;
20
35
  private pendingCluster?;
21
- constructor(formo: FormoAnalytics, options?: SolanaOptions);
36
+ private storeOwnsWalletEvents;
37
+ /**
38
+ * @param formo - The SDK instance events are reported to.
39
+ * @param options - `options.solana` as passed to the SDK, if an object.
40
+ * @param enabled - Whether Solana tracking is enabled. False only when the
41
+ * host app passed `solana: false`; both discovery and stores then stay off.
42
+ */
43
+ constructor(formo: FormoAnalytics, options?: SolanaOptions, enabled?: boolean);
44
+ private attachStore;
22
45
  /**
23
46
  * Set the framework-kit zustand store for automatic event tracking.
24
47
  * This enables autocapture mode — connect/disconnect and transaction events
@@ -39,11 +62,16 @@ export declare class SolanaManager {
39
62
  cluster?: SolanaCluster;
40
63
  }): void;
41
64
  /**
42
- * Update the cluster/network. Only needed if the store endpoint doesn't
43
- * contain a recognizable cluster name (e.g. custom RPC URLs).
44
- * In most cases, the cluster is auto-detected from the store's endpoint.
65
+ * Update the cluster/network.
66
+ *
67
+ * With a framework-kit store, only needed if the store endpoint doesn't
68
+ * contain a recognizable cluster name (e.g. custom RPC URLs). Without one,
69
+ * this is how a non-mainnet app tells the SDK which cluster its Wallet
70
+ * Standard connections are on, since the standard itself cannot say.
45
71
  */
46
72
  setCluster(cluster: SolanaCluster): void;
73
+ /** Names of the Wallet Standard wallets discovered so far. */
74
+ get discoveredWallets(): string[];
47
75
  cleanup(): void;
48
76
  }
49
77
  //# sourceMappingURL=SolanaManager.d.ts.map
@@ -2,34 +2,126 @@
2
2
  /**
3
3
  * SolanaManager
4
4
  *
5
- * Manages the lifecycle of the Solana store integration.
6
- * Subscribes to framework-kit's zustand store for automatic event capture
7
- * of wallet connect/disconnect and transaction lifecycle events.
5
+ * Owns the two ways the SDK learns about Solana wallets:
8
6
  *
9
- * For signMessage/signTransaction tracking (not captured by the store),
7
+ * 1. `SolanaWalletStandardRegistry`: discovers wallets through the Wallet
8
+ * Standard and reports detect / connect / disconnect. On by default, so
9
+ * compatible wallets registered by Solana Kit, wallet-adapter,
10
+ * framework-kit, or another host are covered with no configuration,
11
+ * exactly like EVM wallets through EIP-6963. `solana: false` turns it off.
12
+ * 2. `SolanaStoreHandler`: subscribes to framework-kit's zustand store for
13
+ * connect / disconnect / cluster changes AND the transaction lifecycle.
14
+ * Opt-in through `solana: { store }` or `formo.solana.setStore()`.
15
+ *
16
+ * Both observe the same Wallet Standard connection when a framework-kit app
17
+ * connects. A store supplied at initialization owns wallet events; a store
18
+ * attached later takes ownership when it observes its first connection and
19
+ * adopts any connect the registry already reported. One connect per
20
+ * connection, whichever path an app is on.
21
+ *
22
+ * For signMessage/signTransaction tracking (not captured by either path),
10
23
  * use formo.signature() directly with the address and chainId.
11
24
  *
12
- * For manual event tracking without the store, use the core API directly:
25
+ * For manual event tracking, use the core API directly:
13
26
  * formo.transaction(), formo.signature(), formo.connect(), formo.disconnect().
14
27
  */
15
28
  Object.defineProperty(exports, "__esModule", { value: true });
16
29
  exports.SolanaManager = void 0;
17
30
  var logger_1 = require("../logger");
18
31
  var SolanaStoreHandler_1 = require("./SolanaStoreHandler");
32
+ var SolanaWalletStandardRegistry_1 = require("./SolanaWalletStandardRegistry");
33
+ var types_1 = require("./types");
19
34
  var SolanaManager = /** @class */ (function () {
20
- function SolanaManager(formo, options) {
35
+ /**
36
+ * @param formo - The SDK instance events are reported to.
37
+ * @param options - `options.solana` as passed to the SDK, if an object.
38
+ * @param enabled - Whether Solana tracking is enabled. False only when the
39
+ * host app passed `solana: false`; both discovery and stores then stay off.
40
+ */
41
+ function SolanaManager(formo, options, enabled) {
42
+ if (enabled === void 0) { enabled = true; }
43
+ var _this = this;
21
44
  this.formo = formo;
22
- if (options === null || options === void 0 ? void 0 : options.store) {
23
- logger_1.logger.info("SolanaManager: Initializing store-based Solana tracking");
24
- this.storeHandler = new SolanaStoreHandler_1.SolanaStoreHandler(formo, options.store, {
25
- cluster: options.cluster,
26
- });
27
- }
28
- else if (options === null || options === void 0 ? void 0 : options.cluster) {
45
+ this.enabled = enabled;
46
+ this.storeOwnsWalletEvents = false;
47
+ if (!enabled)
48
+ return;
49
+ if (options === null || options === void 0 ? void 0 : options.cluster) {
29
50
  // Store pending cluster for when setStore is called later
30
51
  this.pendingCluster = options.cluster;
31
52
  }
53
+ // A store supplied at initialization owns wallet events from the outset:
54
+ // unlike a store attached later, it has not missed any prior registry
55
+ // state and it knows the cluster more precisely.
56
+ this.storeOwnsWalletEvents = !!(options === null || options === void 0 ? void 0 : options.store);
57
+ this.registry = new SolanaWalletStandardRegistry_1.SolanaWalletStandardRegistry({
58
+ isAutocaptureEnabled: function (t) { return _this.formo.isAutocaptureEnabled(t); },
59
+ willTrackEvent: function (chainId) { return _this.formo.willTrackEvent(chainId); },
60
+ detect: function (params) { return _this.formo.detect(params); },
61
+ connect: function (params, properties) {
62
+ return _this.formo.connect(params, properties);
63
+ },
64
+ disconnect: function (params) { return _this.formo.disconnect(params); },
65
+ chain: function (params) { return _this.formo.chain(params); },
66
+ syncWalletState: function (params) { return _this.formo.syncWalletState(params); },
67
+ currentAddress: function () { return _this.formo.currentAddress; },
68
+ ownsWalletEvents: function () { return !_this.storeOwnsWalletEvents; },
69
+ }, { cluster: options === null || options === void 0 ? void 0 : options.cluster });
70
+ if (options === null || options === void 0 ? void 0 : options.store) {
71
+ logger_1.logger.info("SolanaManager: Initializing store-based Solana tracking");
72
+ this.attachStore(options.store, options.cluster);
73
+ }
32
74
  }
75
+ SolanaManager.prototype.attachStore = function (store, cluster) {
76
+ var _this = this;
77
+ var _a;
78
+ this.storeHandler = new SolanaStoreHandler_1.SolanaStoreHandler(this.formo, store, {
79
+ cluster: cluster,
80
+ beforeWalletConnect: function (connection) {
81
+ var _a, _b;
82
+ // The store's cluster is authoritative even when chain autocapture is
83
+ // disabled. Keep central attribution correct without manufacturing a
84
+ // chain event in that mode.
85
+ _this.formo.syncWalletState({
86
+ address: connection.address,
87
+ chainId: connection.chainId,
88
+ });
89
+ if (_this.storeOwnsWalletEvents)
90
+ return true;
91
+ var reported = (_a = _this.registry) === null || _a === void 0 ? void 0 : _a.takeReportedConnection(connection.address, connection.rdns);
92
+ _this.storeOwnsWalletEvents = true;
93
+ // If Wallet Standard got there first, the store adopts that live
94
+ // connection instead of emitting it again. Correct its cluster if
95
+ // the store has more precise information.
96
+ if (!reported) {
97
+ // Nothing to adopt. Either the registry never reported this
98
+ // connection, or it reported it under another identity, in which
99
+ // case the store is about to emit a second connect for the same
100
+ // live connection. Both paths derive the rdns from the wallet's
101
+ // own name, so a mismatch means the store's connector is labelled
102
+ // differently from the registered wallet.
103
+ var held = (_b = _this.registry) === null || _b === void 0 ? void 0 : _b.reportedConnectionRdns(connection.address);
104
+ if (held) {
105
+ logger_1.logger.warn("SolanaManager: Store connector does not match the discovered wallet; the connection is reported twice", { storeRdns: connection.rdns, walletRdns: held });
106
+ }
107
+ return true;
108
+ }
109
+ if (reported.chainId !== connection.chainId &&
110
+ _this.formo.isAutocaptureEnabled("chain")) {
111
+ _this.formo.chain(connection).catch(function (error) {
112
+ logger_1.logger.error("SolanaManager: Error correcting cluster during store handoff", error);
113
+ });
114
+ }
115
+ return false;
116
+ },
117
+ });
118
+ // Keep the registry's snapshot on the store's detected cluster. This is
119
+ // silent once the store owns events; during a late handoff it corrects a
120
+ // registry-reported connection before the store adopts it.
121
+ var detectedCluster = types_1.SOLANA_CLUSTERS_BY_ID[this.storeHandler.getChainId()];
122
+ if (detectedCluster)
123
+ (_a = this.registry) === null || _a === void 0 ? void 0 : _a.setCluster(detectedCluster);
124
+ };
33
125
  /**
34
126
  * Set the framework-kit zustand store for automatic event tracking.
35
127
  * This enables autocapture mode — connect/disconnect and transaction events
@@ -48,29 +140,52 @@ var SolanaManager = /** @class */ (function () {
48
140
  */
49
141
  SolanaManager.prototype.setStore = function (store, options) {
50
142
  var _a;
143
+ if (!this.enabled) {
144
+ logger_1.logger.warn("SolanaManager: Ignoring setStore. Solana tracking is off for this instance (solana: false, or the SDK was cleaned up)");
145
+ return;
146
+ }
51
147
  (_a = this.storeHandler) === null || _a === void 0 ? void 0 : _a.cleanup();
52
- this.storeHandler = new SolanaStoreHandler_1.SolanaStoreHandler(this.formo, store, {
53
- cluster: (options === null || options === void 0 ? void 0 : options.cluster) || this.pendingCluster,
54
- });
148
+ this.storeHandler = undefined;
149
+ this.storeOwnsWalletEvents = false;
150
+ this.attachStore(store, (options === null || options === void 0 ? void 0 : options.cluster) || this.pendingCluster);
55
151
  this.pendingCluster = undefined;
56
152
  };
57
153
  /**
58
- * Update the cluster/network. Only needed if the store endpoint doesn't
59
- * contain a recognizable cluster name (e.g. custom RPC URLs).
60
- * In most cases, the cluster is auto-detected from the store's endpoint.
154
+ * Update the cluster/network.
155
+ *
156
+ * With a framework-kit store, only needed if the store endpoint doesn't
157
+ * contain a recognizable cluster name (e.g. custom RPC URLs). Without one,
158
+ * this is how a non-mainnet app tells the SDK which cluster its Wallet
159
+ * Standard connections are on, since the standard itself cannot say.
61
160
  */
62
161
  SolanaManager.prototype.setCluster = function (cluster) {
162
+ var _a;
163
+ if (!this.enabled)
164
+ return;
63
165
  if (this.storeHandler) {
64
166
  this.storeHandler.setCluster(cluster);
65
167
  }
66
168
  else {
67
169
  this.pendingCluster = cluster;
68
170
  }
171
+ (_a = this.registry) === null || _a === void 0 ? void 0 : _a.setCluster(cluster);
69
172
  };
173
+ Object.defineProperty(SolanaManager.prototype, "discoveredWallets", {
174
+ /** Names of the Wallet Standard wallets discovered so far. */
175
+ get: function () {
176
+ var _a, _b;
177
+ return (_b = (_a = this.registry) === null || _a === void 0 ? void 0 : _a.walletNames) !== null && _b !== void 0 ? _b : [];
178
+ },
179
+ enumerable: false,
180
+ configurable: true
181
+ });
70
182
  SolanaManager.prototype.cleanup = function () {
71
- var _a;
183
+ var _a, _b;
72
184
  (_a = this.storeHandler) === null || _a === void 0 ? void 0 : _a.cleanup();
73
185
  this.storeHandler = undefined;
186
+ this.storeOwnsWalletEvents = false;
187
+ (_b = this.registry) === null || _b === void 0 ? void 0 : _b.cleanup();
188
+ this.registry = undefined;
74
189
  };
75
190
  return SolanaManager;
76
191
  }());
@@ -48,8 +48,14 @@ export declare class SolanaStoreHandler {
48
48
  * When true, auto-detection from the store endpoint is disabled.
49
49
  */
50
50
  private explicitCluster;
51
+ private beforeWalletConnect?;
51
52
  constructor(formoAnalytics: FormoAnalytics, store: SolanaClientStore, options?: {
52
53
  cluster?: SolanaCluster;
54
+ beforeWalletConnect?: (connection: {
55
+ address: string;
56
+ chainId: number;
57
+ rdns: string;
58
+ }) => boolean;
53
59
  });
54
60
  /**
55
61
  * Update the cluster/network.
@@ -69,6 +69,7 @@ var SolanaStoreHandler = /** @class */ (function () {
69
69
  this.formo = formoAnalytics;
70
70
  this.store = store;
71
71
  this.explicitCluster = !!(options === null || options === void 0 ? void 0 : options.cluster);
72
+ this.beforeWalletConnect = options === null || options === void 0 ? void 0 : options.beforeWalletConnect;
72
73
  this.cluster = (options === null || options === void 0 ? void 0 : options.cluster) || this.detectClusterFromStore(store) || "mainnet-beta";
73
74
  this.chainId = types_1.SOLANA_CHAIN_IDS[this.cluster];
74
75
  logger_1.logger.info("SolanaStoreHandler: Initializing framework-kit store integration", {
@@ -141,7 +142,7 @@ var SolanaStoreHandler = /** @class */ (function () {
141
142
  logger_1.logger.info("SolanaStoreHandler: Wallet subscription set up");
142
143
  };
143
144
  SolanaStoreHandler.prototype.checkInitialWalletState = function () {
144
- var _a;
145
+ var _a, _b, _c;
145
146
  var state = this.store.getState();
146
147
  var wallet = state.wallet;
147
148
  if (wallet.status === "connected") {
@@ -154,11 +155,17 @@ var SolanaStoreHandler = /** @class */ (function () {
154
155
  address: address,
155
156
  chainId: this.chainId,
156
157
  });
157
- if (this.formo.isAutocaptureEnabled("connect")) {
158
- var connectorName = ((_a = wallet.session.connector) === null || _a === void 0 ? void 0 : _a.name) || wallet.connectorId;
158
+ var connectorName = ((_a = wallet.session.connector) === null || _a === void 0 ? void 0 : _a.name) || wallet.connectorId;
159
+ var rdns = (0, types_1.solanaWalletRdns)(connectorName);
160
+ var shouldEmit = (_c = (_b = this.beforeWalletConnect) === null || _b === void 0 ? void 0 : _b.call(this, {
161
+ address: address,
162
+ chainId: this.chainId,
163
+ rdns: rdns,
164
+ })) !== null && _c !== void 0 ? _c : true;
165
+ if (shouldEmit && this.formo.isAutocaptureEnabled("connect")) {
159
166
  this.formo.connect({ chainId: this.chainId, address: address }, {
160
167
  providerName: connectorName,
161
- rdns: "sol.wallet.".concat(connectorName.toLowerCase().replace(/\s+/g, "")),
168
+ rdns: rdns,
162
169
  }).catch(function (error) {
163
170
  logger_1.logger.error("SolanaStoreHandler: Error emitting initial connect", error);
164
171
  });
@@ -190,7 +197,7 @@ var SolanaStoreHandler = /** @class */ (function () {
190
197
  this.lastWalletStatus = wallet.status;
191
198
  };
192
199
  SolanaStoreHandler.prototype.handleConnect = function (wallet) {
193
- var _a;
200
+ var _a, _b, _c;
194
201
  var address = wallet.session.account.address;
195
202
  if (!address || (0, address_1.isBlockedSolanaAddress)(address)) {
196
203
  return;
@@ -206,11 +213,13 @@ var SolanaStoreHandler = /** @class */ (function () {
206
213
  chainId: chainId,
207
214
  connector: wallet.connectorId,
208
215
  });
209
- if (this.formo.isAutocaptureEnabled("connect")) {
210
- var connectorName = ((_a = wallet.session.connector) === null || _a === void 0 ? void 0 : _a.name) || wallet.connectorId;
216
+ var connectorName = ((_a = wallet.session.connector) === null || _a === void 0 ? void 0 : _a.name) || wallet.connectorId;
217
+ var rdns = (0, types_1.solanaWalletRdns)(connectorName);
218
+ var shouldEmit = (_c = (_b = this.beforeWalletConnect) === null || _b === void 0 ? void 0 : _b.call(this, { address: address, chainId: chainId, rdns: rdns })) !== null && _c !== void 0 ? _c : true;
219
+ if (shouldEmit && this.formo.isAutocaptureEnabled("connect")) {
211
220
  this.formo.connect({ chainId: chainId, address: address }, {
212
221
  providerName: connectorName,
213
- rdns: "sol.wallet.".concat(connectorName.toLowerCase().replace(/\s+/g, "")),
222
+ rdns: rdns,
214
223
  }).catch(function (error) {
215
224
  logger_1.logger.error("SolanaStoreHandler: Error emitting connect", error);
216
225
  });
@@ -0,0 +1,163 @@
1
+ /**
2
+ * SolanaWalletStandardRegistry
3
+ *
4
+ * Which Solana wallets exist, and whether each is connected. The Solana
5
+ * analogue of `EvmProviderRegistry` + EIP-6963.
6
+ *
7
+ * Compatible wallets (Phantom, Solflare, Backpack, ...) announce themselves
8
+ * through the Wallet Standard's window-event handshake. Solana Kit,
9
+ * wallet-adapter, and framework-kit use these registered wallets. Observing
10
+ * the handshake covers those wallets without coupling Formo to the host
11
+ * library: the registry announces `wallet-standard:app-ready`, listens for
12
+ * `wallet-standard:register-wallet`, and subscribes to each wallet's
13
+ * `standard:events` `change` event.
14
+ *
15
+ * What it reports:
16
+ * - `detect` when a wallet registers (session-deduped by the SDK on rdns).
17
+ * - `connect` when a wallet's Solana accounts go from none to some, or the
18
+ * first account changes; `disconnect` when they go from some to none.
19
+ * - `chain` when the configured cluster changes while a wallet is connected.
20
+ *
21
+ * What it does not do: wrap any wallet method, issue any request to a
22
+ * wallet, or depend on `@wallet-standard/*` (the handshake is a few lines,
23
+ * and the SDK's dependency policy is two runtime dependencies, forever).
24
+ *
25
+ * Cluster: the Wallet Standard lists every cluster a wallet SUPPORTS, not
26
+ * the one the app is using, so the cluster is `options.solana.cluster` when
27
+ * given, else mainnet-beta when the wallet supports it, else the first
28
+ * Solana cluster it lists. A framework-kit app gets the cluster from its
29
+ * store instead, through `SolanaStoreHandler`.
30
+ *
31
+ * @see https://github.com/wallet-standard/wallet-standard
32
+ */
33
+ import type { AutocaptureEventType } from "../tracking/TrackingPolicy";
34
+ import { SolanaCluster, UnsubscribeFn } from "./types";
35
+ /** What the registry needs from the SDK that owns it. */
36
+ export interface SolanaWalletStandardRegistryDeps {
37
+ isAutocaptureEnabled(eventType: AutocaptureEventType): boolean;
38
+ willTrackEvent(chainId: number): boolean;
39
+ detect(params: {
40
+ providerName: string;
41
+ rdns: string;
42
+ }): Promise<void>;
43
+ connect(params: {
44
+ chainId: number;
45
+ address: string;
46
+ }, properties: {
47
+ providerName: string;
48
+ rdns: string;
49
+ }): Promise<void>;
50
+ disconnect(params: {
51
+ chainId: number;
52
+ address: string;
53
+ }): Promise<void>;
54
+ chain(params: {
55
+ chainId: number;
56
+ address: string;
57
+ }): Promise<void>;
58
+ /**
59
+ * Record wallet and chain state centrally WITHOUT emitting an event.
60
+ *
61
+ * Observing a chain is not the same as reporting one: the exclusion gate
62
+ * keys off the central chain, so a cluster switch has to land there even
63
+ * when `autocapture.chain` is off. The EVM tracker separates the two the
64
+ * same way.
65
+ * @see FormoAnalytics.syncWalletState
66
+ */
67
+ syncWalletState(params: {
68
+ chainId: number;
69
+ address: string;
70
+ }): void;
71
+ /** The wallet the SDK currently treats as active, across namespaces. */
72
+ currentAddress(): string | undefined;
73
+ /**
74
+ * Whether THIS registry reports connections.
75
+ *
76
+ * A framework-kit app connects through the very same Wallet Standard
77
+ * wallet, so its store and this registry both see every connection. The
78
+ * store is the better witness there (it knows the cluster and connector),
79
+ * so once it has observed or adopted a connection the registry keeps
80
+ * discovering wallets and emitting `detect`, but leaves connect and
81
+ * disconnect to the store. Exactly one connect per connection, either way.
82
+ */
83
+ ownsWalletEvents(): boolean;
84
+ }
85
+ export interface SolanaWalletStandardRegistryOptions {
86
+ /** The cluster the app uses. Overrides what is derived from the wallet. */
87
+ cluster?: SolanaCluster;
88
+ }
89
+ export declare class SolanaWalletStandardRegistry {
90
+ private readonly deps;
91
+ private wallets;
92
+ private cluster?;
93
+ private removeWindowListener?;
94
+ /** Set by cleanup(); a torn-down registry refuses late registrations. */
95
+ private isCleanedUp;
96
+ /**
97
+ * Handed to every wallet. Wallets keep it and may call `register` long
98
+ * after the handshake, which is why `register` checks `isCleanedUp`.
99
+ */
100
+ private readonly api;
101
+ constructor(deps: SolanaWalletStandardRegistryDeps, options?: SolanaWalletStandardRegistryOptions);
102
+ /**
103
+ * The Wallet Standard handshake, both directions.
104
+ *
105
+ * A wallet injected before us hears `app-ready` and registers at once; a
106
+ * wallet injected after us dispatches `register-wallet`, which we answer.
107
+ * Neither is optional: an extension's content script and the app's bundle
108
+ * race, and which one wins differs per page load.
109
+ */
110
+ private listen;
111
+ /**
112
+ * Add wallets once. Returns an unregister function, as the standard asks.
113
+ *
114
+ * Public so an integration that already holds a wallet object (its own
115
+ * `getWallets()` call, say) can hand it over without the window handshake.
116
+ */
117
+ register(...wallets: unknown[]): UnsubscribeFn;
118
+ private track;
119
+ private untrack;
120
+ private onChange;
121
+ /**
122
+ * Compare what the wallet now authorizes with what was last reported.
123
+ *
124
+ * none → some is a connect, some → none a disconnect, and a different
125
+ * first account is a disconnect followed by a connect, matching what the
126
+ * framework-kit store handler reports for the same transitions.
127
+ */
128
+ private reconcile;
129
+ private reportConnect;
130
+ private reportDisconnect;
131
+ /**
132
+ * The chain id to report for a wallet.
133
+ *
134
+ * The Wallet Standard cannot say which cluster the app is on, only which
135
+ * ones the wallet supports, so an explicit cluster wins, then mainnet-beta
136
+ * if supported (nearly every wallet lists every cluster, and production
137
+ * traffic is mainnet), then the first cluster listed.
138
+ */
139
+ private chainIdFor;
140
+ /**
141
+ * Set the cluster the app uses. A connected wallet's chain id follows,
142
+ * with a `chain` event, the same as the store handler on a cluster switch.
143
+ */
144
+ setCluster(cluster: SolanaCluster): void;
145
+ /** Names of every wallet discovered so far, for the debug helpers. */
146
+ get walletNames(): string[];
147
+ /**
148
+ * The rdns this registry reported a still-live connect for `address`
149
+ * under, if any. Lets a failed store adoption name both identities.
150
+ */
151
+ reportedConnectionRdns(address: string): string | undefined;
152
+ /**
153
+ * Transfer a connect already emitted by this registry to a store handler.
154
+ * The state remains connected, but the same transition must not be emitted
155
+ * a second time when framework-kit's store catches up.
156
+ */
157
+ takeReportedConnection(address: string, rdns: string): {
158
+ address: string;
159
+ chainId: number;
160
+ } | undefined;
161
+ cleanup(): void;
162
+ }
163
+ //# sourceMappingURL=SolanaWalletStandardRegistry.d.ts.map