@dynamic-labs-sdk/client 1.13.0 → 1.15.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 (93) hide show
  1. package/dist/{InvalidParamError-C7c-OLz1.cjs → InvalidParamError-BPY2yug_.cjs} +842 -852
  2. package/dist/InvalidParamError-BPY2yug_.cjs.map +1 -0
  3. package/dist/{InvalidParamError-BeCPhfVz.esm.js → InvalidParamError-CXCYtY0p.esm.js} +1236 -1246
  4. package/dist/InvalidParamError-CXCYtY0p.esm.js.map +1 -0
  5. package/dist/{InvalidParamError-CGiSxym5.native.esm.js → InvalidParamError-DSXDW6g4.native.esm.js} +1059 -1069
  6. package/dist/InvalidParamError-DSXDW6g4.native.esm.js.map +1 -0
  7. package/dist/core.cjs +6 -6
  8. package/dist/core.esm.js +6 -6
  9. package/dist/core.native.esm.js +7 -7
  10. package/dist/{getNetworkProviderFromNetworkId-D8cpx0xz.cjs → getNetworkProviderFromNetworkId-DCwFIoM3.cjs} +4 -4
  11. package/dist/{getNetworkProviderFromNetworkId-D8cpx0xz.cjs.map → getNetworkProviderFromNetworkId-DCwFIoM3.cjs.map} +1 -1
  12. package/dist/{getNetworkProviderFromNetworkId-CP9gl1B1.esm.js → getNetworkProviderFromNetworkId-_pvqm2fB.esm.js} +3 -3
  13. package/dist/{getNetworkProviderFromNetworkId-CP9gl1B1.esm.js.map → getNetworkProviderFromNetworkId-_pvqm2fB.esm.js.map} +1 -1
  14. package/dist/{getNetworkProviderFromNetworkId-Be_UK4_7.native.esm.js → getNetworkProviderFromNetworkId-b-H_ta9Q.native.esm.js} +5 -5
  15. package/dist/{getNetworkProviderFromNetworkId-Be_UK4_7.native.esm.js.map → getNetworkProviderFromNetworkId-b-H_ta9Q.native.esm.js.map} +1 -1
  16. package/dist/{getSignedSessionId-D9w3Wbhk.native.esm.js → getSignedSessionId-Co8y0azF.native.esm.js} +3 -3
  17. package/dist/{getSignedSessionId-D9w3Wbhk.native.esm.js.map → getSignedSessionId-Co8y0azF.native.esm.js.map} +1 -1
  18. package/dist/{getSignedSessionId-Bq7xBsA3.esm.js → getSignedSessionId-D7Q9q-oy.esm.js} +3 -3
  19. package/dist/{getSignedSessionId-Bq7xBsA3.esm.js.map → getSignedSessionId-D7Q9q-oy.esm.js.map} +1 -1
  20. package/dist/{getSignedSessionId-BeK0vWep.cjs → getSignedSessionId-NZR0A-EZ.cjs} +3 -3
  21. package/dist/{getSignedSessionId-BeK0vWep.cjs.map → getSignedSessionId-NZR0A-EZ.cjs.map} +1 -1
  22. package/dist/{getWaasWalletProviderFromWalletAccount-CfqTF_82.cjs → getWaasWalletProviderFromWalletAccount-CUgNeKaa.cjs} +3 -3
  23. package/dist/{getWaasWalletProviderFromWalletAccount-CfqTF_82.cjs.map → getWaasWalletProviderFromWalletAccount-CUgNeKaa.cjs.map} +1 -1
  24. package/dist/{getWaasWalletProviderFromWalletAccount-BzpLoZTm.native.esm.js → getWaasWalletProviderFromWalletAccount-DLq_TjmC.native.esm.js} +3 -3
  25. package/dist/{getWaasWalletProviderFromWalletAccount-BzpLoZTm.native.esm.js.map → getWaasWalletProviderFromWalletAccount-DLq_TjmC.native.esm.js.map} +1 -1
  26. package/dist/{getWaasWalletProviderFromWalletAccount-BKHO68FO.esm.js → getWaasWalletProviderFromWalletAccount-DNBPKg4j.esm.js} +3 -3
  27. package/dist/{getWaasWalletProviderFromWalletAccount-BKHO68FO.esm.js.map → getWaasWalletProviderFromWalletAccount-DNBPKg4j.esm.js.map} +1 -1
  28. package/dist/{getWalletProviderByKey-BMzoL_69.esm.js → getWalletProviderByKey-C4KTyeYY.esm.js} +2 -2
  29. package/dist/{getWalletProviderByKey-BMzoL_69.esm.js.map → getWalletProviderByKey-C4KTyeYY.esm.js.map} +1 -1
  30. package/dist/{getWalletProviderByKey-DCKYAida.cjs → getWalletProviderByKey-DZrpcmPE.cjs} +3 -3
  31. package/dist/{getWalletProviderByKey-DCKYAida.cjs.map → getWalletProviderByKey-DZrpcmPE.cjs.map} +1 -1
  32. package/dist/{getWalletProviderByKey-DDDxBsdv.native.esm.js → getWalletProviderByKey-Dk_K8Lg8.native.esm.js} +2 -2
  33. package/dist/{getWalletProviderByKey-DDDxBsdv.native.esm.js.map → getWalletProviderByKey-Dk_K8Lg8.native.esm.js.map} +1 -1
  34. package/dist/index.cjs +53 -14
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.esm.js +53 -14
  37. package/dist/index.esm.js.map +1 -1
  38. package/dist/index.native.esm.js +53 -14
  39. package/dist/index.native.esm.js.map +1 -1
  40. package/dist/{isDynamicWaasEnabled-BMQq5uNX.esm.js → isDynamicWaasEnabled-C9aVoXek.esm.js} +2 -2
  41. package/dist/{isDynamicWaasEnabled-BMQq5uNX.esm.js.map → isDynamicWaasEnabled-C9aVoXek.esm.js.map} +1 -1
  42. package/dist/{isDynamicWaasEnabled-ChdeBFbN.native.esm.js → isDynamicWaasEnabled-DNtxO0Bc.native.esm.js} +2 -2
  43. package/dist/{isDynamicWaasEnabled-ChdeBFbN.native.esm.js.map → isDynamicWaasEnabled-DNtxO0Bc.native.esm.js.map} +1 -1
  44. package/dist/{isDynamicWaasEnabled-BtbzDbdz.cjs → isDynamicWaasEnabled-DQ1mtWZv.cjs} +3 -3
  45. package/dist/{isDynamicWaasEnabled-BtbzDbdz.cjs.map → isDynamicWaasEnabled-DQ1mtWZv.cjs.map} +1 -1
  46. package/dist/{isMfaRequiredForAction-dAtK8Pl6.native.esm.js → isMfaRequiredForAction-BXnpnD2X.native.esm.js} +2 -2
  47. package/dist/{isMfaRequiredForAction-dAtK8Pl6.native.esm.js.map → isMfaRequiredForAction-BXnpnD2X.native.esm.js.map} +1 -1
  48. package/dist/{isMfaRequiredForAction-Diwsf9GB.esm.js → isMfaRequiredForAction-BhLg6M8J.esm.js} +2 -2
  49. package/dist/{isMfaRequiredForAction-Diwsf9GB.esm.js.map → isMfaRequiredForAction-BhLg6M8J.esm.js.map} +1 -1
  50. package/dist/{isMfaRequiredForAction-CgWBU6kM.cjs → isMfaRequiredForAction-DsOwHl4C.cjs} +2 -2
  51. package/dist/{isMfaRequiredForAction-CgWBU6kM.cjs.map → isMfaRequiredForAction-DsOwHl4C.cjs.map} +1 -1
  52. package/dist/{isUserOnboardingComplete-DO2_gSAp.native.esm.js → isUserOnboardingComplete-BRfyMejK.native.esm.js} +3 -3
  53. package/dist/{isUserOnboardingComplete-DO2_gSAp.native.esm.js.map → isUserOnboardingComplete-BRfyMejK.native.esm.js.map} +1 -1
  54. package/dist/{isUserOnboardingComplete-OP4uyUlE.cjs → isUserOnboardingComplete-DrgtJIFi.cjs} +4 -4
  55. package/dist/{isUserOnboardingComplete-OP4uyUlE.cjs.map → isUserOnboardingComplete-DrgtJIFi.cjs.map} +1 -1
  56. package/dist/{isUserOnboardingComplete-D4Y3sdr3.esm.js → isUserOnboardingComplete-KFfpvN4R.esm.js} +3 -3
  57. package/dist/{isUserOnboardingComplete-D4Y3sdr3.esm.js.map → isUserOnboardingComplete-KFfpvN4R.esm.js.map} +1 -1
  58. package/dist/modules/auth/getElevatedAccessToken/getElevatedAccessToken.d.ts +1 -2
  59. package/dist/modules/auth/getElevatedAccessToken/getElevatedAccessToken.d.ts.map +1 -1
  60. package/dist/modules/flow/attachFlowSource/attachFlowSource.d.ts +12 -1
  61. package/dist/modules/flow/attachFlowSource/attachFlowSource.d.ts.map +1 -1
  62. package/dist/modules/funding/moonpay/getMoonPayUrl/getMoonPayUrl.d.ts +4 -0
  63. package/dist/modules/funding/moonpay/getMoonPayUrl/getMoonPayUrl.d.ts.map +1 -1
  64. package/dist/modules/mfa/registerTotpMfaDevice/registerTotpMfaDevice.d.ts +4 -1
  65. package/dist/modules/mfa/registerTotpMfaDevice/registerTotpMfaDevice.d.ts.map +1 -1
  66. package/dist/modules/wallets/verifyWalletAccountForSignInOrTransfer/verifyWalletAccountForSignInOrTransfer.d.ts.map +1 -1
  67. package/dist/modules/wallets/walletProvider/walletProvider.types.d.ts +8 -0
  68. package/dist/modules/wallets/walletProvider/walletProvider.types.d.ts.map +1 -1
  69. package/dist/{refreshAuth-BTPctQtq.esm.js → refreshAuth-BnkCuIdw.esm.js} +3 -3
  70. package/dist/{refreshAuth-BTPctQtq.esm.js.map → refreshAuth-BnkCuIdw.esm.js.map} +1 -1
  71. package/dist/{refreshAuth-C3issn5b.native.esm.js → refreshAuth-CfUYFXh_.native.esm.js} +3 -3
  72. package/dist/{refreshAuth-C3issn5b.native.esm.js.map → refreshAuth-CfUYFXh_.native.esm.js.map} +1 -1
  73. package/dist/{refreshAuth-ByIVzawo.cjs → refreshAuth-DvHy0g8j.cjs} +3 -3
  74. package/dist/{refreshAuth-ByIVzawo.cjs.map → refreshAuth-DvHy0g8j.cjs.map} +1 -1
  75. package/dist/services/instrumentation/constants.d.ts.map +1 -1
  76. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  77. package/dist/utils/retryOnFetchError/index.d.ts +2 -0
  78. package/dist/utils/retryOnFetchError/index.d.ts.map +1 -0
  79. package/dist/utils/retryOnFetchError/retryOnFetchError.d.ts +36 -0
  80. package/dist/utils/retryOnFetchError/retryOnFetchError.d.ts.map +1 -0
  81. package/dist/waas.cjs +6 -6
  82. package/dist/waas.esm.js +5 -5
  83. package/dist/waas.native.esm.js +5 -5
  84. package/dist/waasCore.cjs +10 -10
  85. package/dist/waasCore.cjs.map +1 -1
  86. package/dist/waasCore.esm.js +10 -10
  87. package/dist/waasCore.esm.js.map +1 -1
  88. package/dist/waasCore.native.esm.js +11 -11
  89. package/dist/waasCore.native.esm.js.map +1 -1
  90. package/package.json +2 -2
  91. package/dist/InvalidParamError-BeCPhfVz.esm.js.map +0 -1
  92. package/dist/InvalidParamError-C7c-OLz1.cjs.map +0 -1
  93. package/dist/InvalidParamError-CGiSxym5.native.esm.js.map +0 -1
@@ -2,7 +2,7 @@ import { AuthStorageEnum, Configuration, JwtVerifiedCredentialFormatEnum, SDKApi
2
2
 
3
3
  //#region package.json
4
4
  var name = "@dynamic-labs-sdk/client";
5
- var version = "1.13.0";
5
+ var version = "1.15.0";
6
6
  var dependencies = {
7
7
  "@dynamic-labs-sdk/assert-package-version": "workspace:*",
8
8
  "@dynamic-labs-wallet/browser-wallet-client": "1.0.39",
@@ -151,1474 +151,1464 @@ const randomString = ({ chars = DEFAULT_CHARS, length }) => {
151
151
  };
152
152
 
153
153
  //#endregion
154
- //#region src/modules/auth/extractSessionId/extractSessionId.ts
154
+ //#region src/modules/auth/getElevatedAccessToken/getElevatedAccessToken.ts
155
155
  /**
156
- * Extracts the session ID (`sid` claim) from a JWT token string.
156
+ * Gets an elevated access token by scope.
157
157
  *
158
- * Decodes the base64url-encoded payload segment of the token and reads the
159
- * `sid` field. This is used to correlate instrumentation events with the
160
- * authenticated session, without ever forwarding the full token.
158
+ * This function retrieves an elevated access token that contains the specified scope.
159
+ * Expired tokens are automatically filtered out.
161
160
  *
162
- * Returns `null` when no token is present, when the token is malformed, or
163
- * when the payload does not contain a `sid` claim.
161
+ * By default, if the token has `singleUse: true`, it will be automatically
162
+ * consumed (removed from state) after retrieval. Pass `consume: false` to
163
+ * check for token existence without consuming it.
164
+ *
165
+ * @param params - The parameters object.
166
+ * @param params.scope - The scope to match (e.g., 'wallet:export').
167
+ * @param params.consume - Whether to consume single-use tokens (default: true).
168
+ * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
169
+ * @returns The elevated access token if found and not expired, or undefined if not found or expired.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * // Retrieve and consume (default)
174
+ * const token = getElevatedAccessToken({ scope: 'wallet:export' });
175
+ *
176
+ * // Check existence without consuming
177
+ * const token = getElevatedAccessToken({ scope: 'credential:unlink', consume: false });
178
+ * ```
164
179
  * @notInstrumented
165
180
  */
166
- const extractSessionId = (token) => {
167
- try {
168
- const payload = token.split(".")[1];
169
- return JSON.parse(atob(payload)).sid ?? null;
170
- } catch {
171
- return null;
181
+ const getElevatedAccessToken = ({ consume = true, scope }, client = getDefaultClient()) => {
182
+ const core = getCore(client);
183
+ const now = /* @__PURE__ */ new Date();
184
+ const elevatedAccessTokens = core.state.get().elevatedAccessTokens || [];
185
+ const validTokens = elevatedAccessTokens.filter((token$1) => !token$1.expiresAt || token$1.expiresAt > now);
186
+ if (validTokens.length !== elevatedAccessTokens.length) core.state.set({ elevatedAccessTokens: validTokens });
187
+ const token = validTokens.find((token$1) => token$1.scopes.includes(scope));
188
+ if (!token) return;
189
+ if (consume && token.singleUse) {
190
+ const updatedTokens = validTokens.filter((t) => t !== token);
191
+ core.state.set({ elevatedAccessTokens: updatedTokens });
172
192
  }
193
+ return token.token;
173
194
  };
174
195
 
175
196
  //#endregion
176
- //#region src/utils/getOperatingSystem/getOperatingSystem.ts
177
- /**
178
- * Detects the operating system from `navigator.userAgent` for web environments.
179
- *
180
- * The React Native bundler (Metro) resolves `getOperatingSystem.native.ts`
181
- * instead when building for React Native, which uses `Platform.OS` directly.
182
- *
183
- * Falls back to `'unknown'` when `navigator` is unavailable (SSR, service
184
- * workers) or when the user-agent string doesn't match a known OS pattern.
185
- * @notInstrumented
186
- */
187
- const getOperatingSystem = () => {
188
- try {
189
- const ua = navigator.userAgent;
190
- if (/iPhone|iPad|iPod/.test(ua)) return "ios";
191
- if (/Android/.test(ua)) return "android";
192
- if (/Windows/.test(ua)) return "windows";
193
- if (/Macintosh|Mac OS X/.test(ua)) return "macos";
194
- if (/Linux/.test(ua)) return "linux";
195
- return "unknown";
196
- } catch {
197
- return "unknown";
197
+ //#region src/errors/ValueMustBeDefinedError.ts
198
+ var ValueMustBeDefinedError = class extends BaseError {
199
+ constructor(message) {
200
+ super({
201
+ cause: null,
202
+ code: "value_must_be_defined_error",
203
+ docsUrl: null,
204
+ name: "ValueMustBeDefined",
205
+ shortMessage: message
206
+ });
198
207
  }
199
208
  };
200
209
 
201
210
  //#endregion
202
- //#region src/utils/getPlatform/getPlatform.ts
211
+ //#region src/utils/assertDefined/assertDefined.ts
203
212
  /**
204
- * Returns the platform the SDK is running on.
213
+ * Asserts that a value is not null or undefined, throwing an error if it is.
214
+ * This function acts as a type guard, narrowing the type to exclude null and undefined.
205
215
  *
206
- * This is the web implementation the React Native bundler resolves
207
- * `getPlatform.native.ts` instead when building for React Native, which
208
- * returns `'react-native'`.
209
- * @notInstrumented
216
+ * @template T - The type of the value being checked
217
+ * @param value - The value to check for null or undefined
218
+ * @param message - The error message to throw if the value is null or undefined
219
+ * @throws Throws an error with the provided message if value is null or undefined
220
+ * @example
221
+ * ```typescript
222
+ * const maybeString: string | null = getValue();
223
+ * assertDefined(maybeString, 'String value is required');
224
+ * // maybeString is now typed as string (null is excluded)
225
+ * ```
210
226
  */
211
- const getPlatform = () => "web";
227
+ function assertDefined(value, message) {
228
+ if (value === null || value === void 0) throw new ValueMustBeDefinedError(message);
229
+ }
212
230
 
213
231
  //#endregion
214
- //#region src/utils/getUserAgent/getUserAgent.ts
232
+ //#region src/modules/projectSettings/isCookieEnabled/isCookieEnabled.ts
215
233
  /**
216
- * Returns the browser's `navigator.userAgent` string for inclusion in
217
- * instrumentation events.
218
- *
219
- * Falls back to an empty string in environments where `navigator` is not
220
- * available (e.g., Node.js SSR, service workers) so that callers never need
221
- * to guard against undefined.
234
+ * Returns true if the client is using Dynamic cookies or a BYO JWT cookie.
222
235
  * @notInstrumented
223
236
  */
224
- const getUserAgent = () => {
225
- try {
226
- return navigator.userAgent;
227
- } catch {
228
- return "";
229
- }
237
+ const isCookieEnabled = (client) => {
238
+ assertDefined(client.projectSettings, "Project settings are not defined");
239
+ const securitySettings = client.projectSettings.security;
240
+ if (!securitySettings) return false;
241
+ const dynamicCookiesEnabled = (securitySettings.auth?.storage || []).includes(AuthStorageEnum.Cookie);
242
+ const byoJwtCookieEnabled = Boolean(securitySettings.externalAuth?.cookieName);
243
+ return dynamicCookiesEnabled || byoJwtCookieEnabled;
230
244
  };
231
245
 
232
246
  //#endregion
233
- //#region src/services/instrumentation/constants.ts
234
- /**
235
- * Default key-based PII scrubber list, lowercased and matched against the
236
- * lowercased object key.
237
- *
238
- * Two complementary mechanisms cover credential leakage:
239
- *
240
- * 1. Explicit keys listed here — matched exactly (case-insensitive). Use this
241
- * for keys that don't end in a generic credential suffix (e.g. `mnemonic`,
242
- * `seed`, `verificationCode`, `keyShare`).
243
- * 2. Suffix matching in {@link scrubParameters} for the credential suffixes
244
- * enumerated by {@link PII_KEY_SUFFIXES} — catches any field whose name
245
- * ends in `token` or `jwt` (e.g. `mfaToken`, `minifiedJwt`, `accessToken`,
246
- * `idToken`, `captchaToken`, …) without needing to enumerate them here.
247
- */
248
- const DEFAULT_PII_FIELDS = [
249
- "password",
250
- "token",
251
- "secret",
252
- "privateKey",
253
- "privateJwk",
254
- "mnemonic",
255
- "seed",
256
- "keyShare",
257
- "keyShares",
258
- "email",
259
- "phone",
260
- "ssn",
261
- "address",
262
- "authorization",
263
- "verificationCode",
264
- "recoveryCodes",
265
- "codeVerifier",
266
- "pkce",
267
- "bearer",
268
- "apiKey"
269
- ];
270
- /**
271
- * Lowercased suffixes used by {@link scrubParameters} to match credential-like
272
- * keys without enumerating every variant (e.g. `mfaToken`, `accessToken`,
273
- * `refreshToken`, `legacyToken`, `minifiedJwt`). Only suffixes that are
274
- * unambiguously credentials belong here — `key` (matches `publicKey`) and
275
- * `code` (matches `countryCode`) are too broad and must not be added.
276
- */
277
- const PII_KEY_SUFFIXES = ["token", "jwt"];
278
- const REDACTED_VALUE = "[REDACTED]";
279
- const MAX_STRING_LENGTH = 500;
280
- const TRUNCATED_SUFFIX = "...[truncated]";
281
- /**
282
- * Label prefix for binary data placeholders. The full placeholder includes the
283
- * byte length, e.g. `[Binary:16 bytes]`, so consumers can gauge data size
284
- * without the actual bytes appearing in logs.
285
- */
286
- const BINARY_LABEL = "Binary";
287
- /**
288
- * Placeholder emitted when a circular reference is detected in the value tree.
289
- * Using a named constant makes it easy to search for or filter in downstream
290
- * log tooling.
291
- */
292
- const CIRCULAR_VALUE = "[Circular]";
247
+ //#region src/modules/sessionKeys/getSessionKeys/getSessionKeys.ts
248
+ /** @notInstrumented */
249
+ const getSessionKeys = (client) => {
250
+ const publicKey = getCore(client).state.get().sessionKeys;
251
+ if (!publicKey) return;
252
+ return { publicKey };
253
+ };
293
254
 
294
255
  //#endregion
295
- //#region src/services/instrumentation/scrubParameters/scrubParameters.ts
296
- /**
297
- * Returns true when `key` should be redacted based on the configured PII
298
- * field list. A key matches when its lowercased form either equals one of
299
- * `piiFieldsLower` exactly, or ends in one of the curated credential
300
- * suffixes in {@link PII_KEY_SUFFIXES} (currently `token` and `jwt`). The
301
- * suffix check is what catches `mfaToken`, `minifiedJwt`, `accessToken`,
302
- * etc. without needing every variant enumerated explicitly.
303
- */
304
- const keyMatchesPiiField = ({ keyLower, piiFieldsLower }) => {
305
- if (piiFieldsLower.includes(keyLower)) return true;
306
- return PII_KEY_SUFFIXES.some((suffix) => keyLower.endsWith(suffix));
256
+ //#region src/errors/APIError/APIError.ts
257
+ var APIError = class APIError extends BaseError {
258
+ status;
259
+ constructor(message, code, status) {
260
+ super({
261
+ cause: null,
262
+ code,
263
+ docsUrl: null,
264
+ name: "APIError",
265
+ shortMessage: message
266
+ });
267
+ this.status = status;
268
+ }
269
+ static async fromResponse(response) {
270
+ try {
271
+ const errorBody = await response.clone().json();
272
+ if (errorBody && "error" in errorBody && typeof errorBody.error === "string") {
273
+ const errorCode = "code" in errorBody && typeof errorBody.code === "string" ? errorBody.code : "unknown_error";
274
+ return new APIError(errorBody.error, errorCode, response.status);
275
+ }
276
+ return null;
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
307
281
  };
308
- const scrubString = ({ key, piiFieldsLower, redactAll, value }) => {
309
- if (keyMatchesPiiField({
310
- keyLower: key.toLowerCase(),
311
- piiFieldsLower
312
- })) return REDACTED_VALUE;
313
- if (redactAll) return REDACTED_VALUE;
314
- if (value.length > MAX_STRING_LENGTH) return value.slice(0, MAX_STRING_LENGTH) + TRUNCATED_SUFFIX;
315
- return value;
282
+
283
+ //#endregion
284
+ //#region src/errors/InvalidExternalAuthError.ts
285
+ var InvalidExternalAuthError = class extends BaseError {
286
+ constructor({ cause }) {
287
+ super({
288
+ cause,
289
+ code: "invalid_external_auth_error",
290
+ docsUrl: "https://www.dynamic.xyz/docs/external-auth/third-party-auth-overview",
291
+ name: "InvalidExternalAuthError",
292
+ shortMessage: "Error authenticating with external JWT"
293
+ });
294
+ }
316
295
  };
317
- const scrubArray = ({ context, value }) => {
318
- if (context.visited.has(value)) return [CIRCULAR_VALUE];
319
- context.visited.add(value);
320
- const result = value.map((item, index) => scrubValue({
321
- context,
322
- key: String(index),
323
- value: item
324
- }));
325
- context.visited.delete(value);
326
- return result;
327
- };
328
- const scrubObject = ({ context, value }) => {
329
- if (context.visited.has(value)) return { [CIRCULAR_VALUE]: true };
330
- context.visited.add(value);
331
- const result = Object.create(null);
332
- for (const key of Object.keys(value)) result[key] = scrubValue({
333
- context,
334
- key,
335
- value: value[key]
336
- });
337
- context.visited.delete(value);
338
- return result;
296
+
297
+ //#endregion
298
+ //#region src/errors/LinkCredentialError.ts
299
+ var LinkCredentialError = class extends BaseError {
300
+ constructor({ cause }) {
301
+ super({
302
+ cause,
303
+ code: "link_credential_error",
304
+ docsUrl: null,
305
+ name: "LinkCredentialError",
306
+ shortMessage: "The credential you are trying to link is associated with another account and cannot be reassigned."
307
+ });
308
+ }
339
309
  };
340
- const scrubValue = ({ context, key, value }) => {
341
- if (value === null || value === void 0) return value;
342
- if (value instanceof Uint8Array) return `[${BINARY_LABEL}:${value.byteLength} bytes]`;
343
- if (keyMatchesPiiField({
344
- keyLower: key.toLowerCase(),
345
- piiFieldsLower: context.piiFieldsLower
346
- })) return REDACTED_VALUE;
347
- if (typeof value === "string") return scrubString({
348
- key,
349
- piiFieldsLower: context.piiFieldsLower,
350
- redactAll: context.redactAll,
351
- value
352
- });
353
- if (Array.isArray(value)) return scrubArray({
354
- context,
355
- value
356
- });
357
- if (value instanceof Set) {
358
- if (context.visited.has(value)) return [CIRCULAR_VALUE];
359
- context.visited.add(value);
360
- const setResult = scrubArray({
361
- context,
362
- value: [...value]
310
+
311
+ //#endregion
312
+ //#region src/errors/MfaInvalidOtpError.ts
313
+ var MfaInvalidOtpError = class extends BaseError {
314
+ constructor({ cause }) {
315
+ super({
316
+ cause,
317
+ code: "mfa_invalid_otp_error",
318
+ docsUrl: null,
319
+ name: "MfaInvalidOtpError",
320
+ shortMessage: "Invalid OTP"
363
321
  });
364
- context.visited.delete(value);
365
- return setResult;
366
322
  }
367
- if (value instanceof Map) {
368
- if (context.visited.has(value)) return { [CIRCULAR_VALUE]: true };
369
- context.visited.add(value);
370
- const asObject = Object.create(null);
371
- for (const [k, v] of value.entries()) asObject[String(k)] = v;
372
- const mapResult = scrubObject({
373
- context,
374
- value: asObject
323
+ };
324
+
325
+ //#endregion
326
+ //#region src/errors/MfaRateLimitedError.ts
327
+ var MfaRateLimitedError = class extends BaseError {
328
+ constructor({ cause }) {
329
+ super({
330
+ cause,
331
+ code: "mfa_rate_limited_error",
332
+ docsUrl: null,
333
+ name: "MfaRateLimitedError",
334
+ shortMessage: "Rate limited"
375
335
  });
376
- context.visited.delete(value);
377
- return mapResult;
378
336
  }
379
- if (value instanceof WeakMap) return "[WeakMap]";
380
- if (value instanceof WeakSet) return "[WeakSet]";
381
- if (value instanceof Date) return value.toISOString();
382
- if (value instanceof Error) return `[Error: ${value.message}]`;
383
- if (typeof value === "object") return scrubObject({
384
- context,
385
- value
386
- });
387
- return value;
388
337
  };
389
- /** @notInstrumented */
390
- const scrubParameters = ({ piiFields, redactAll, value }) => {
391
- return scrubObject({
392
- context: {
393
- piiFieldsLower: piiFields.map((field) => field.toLowerCase()),
394
- redactAll,
395
- visited: /* @__PURE__ */ new WeakSet()
396
- },
397
- value
398
- });
338
+
339
+ //#endregion
340
+ //#region src/errors/SandboxMaximumThresholdReachedError.ts
341
+ var SandboxMaximumThresholdReachedError = class extends BaseError {
342
+ constructor({ cause }) {
343
+ super({
344
+ cause,
345
+ code: "sandbox_maximum_threshold_reached_error",
346
+ docsUrl: "https://www.dynamic.xyz/docs/developer-dashboard/sandbox-vs-live#sandbox-vs-live",
347
+ name: "SandboxMaximumThresholdReachedError",
348
+ shortMessage: "Your sandbox environment has reached the maximum MAU. Please use a live environment for production traffic."
349
+ });
350
+ }
399
351
  };
400
352
 
401
353
  //#endregion
402
- //#region src/services/instrumentation/instrumentFunction/extractParams/extractParams.ts
354
+ //#region src/modules/apiClient/utils/clientErrorMapper/clientErrorMapper.ts
403
355
  /**
404
- * Normalises the raw positional arguments of an instrumented function call
405
- * into a flat `Record<string, unknown>` suitable for structured logging.
406
- *
407
- * ## Why this is needed
356
+ * Default error mapper for the client that handles common API error codes.
408
357
  *
409
- * Instrumented functions are wrapped with `(...args: unknown[])`, so their
410
- * parameters arrive as an array. Logging a raw array loses the semantic names
411
- * of each argument. This function recovers a named representation:
358
+ * This mapper transforms specific API error codes into more specific error types:
359
+ * - `mfa_invalid_code` `MfaInvalidOtpError`
360
+ * - `mfa_rate_limited` `MfaRateLimitedError`
412
361
  *
413
- * - **No arguments** `{}`
414
- * - **Single plain-object argument** that object is returned as-is, because
415
- * SDK functions follow the single-object-parameter convention, so the object
416
- * already carries named keys (`{ amount, to }`, etc.).
417
- * - **Everything else** (multiple args, a single primitive, a single array) →
418
- * each value is indexed as `arg0`, `arg1`, …
362
+ * @param error - The error to be mapped
363
+ * @returns A transformed error if the error code matches a known pattern, or null if no transformation is needed
419
364
  *
420
- * ## Why arrays are NOT treated as plain objects
365
+ * @example
366
+ * ```typescript
367
+ * // This will be automatically applied to all API errors
368
+ * const apiClient = createApiClient({}, client);
421
369
  *
422
- * `typeof [] === 'object'` and `[] !== null`, so without the `Array.isArray`
423
- * guard a single-array argument would be returned as-is. That would produce
424
- * an object with numeric string keys (`{ '0': ..., '1': ... }`), which is
425
- * misleading and inconsistent with how multi-arg calls are handled. Instead,
426
- * arrays fall through to the indexed `arg0` path.
370
+ * // The clientErrorMapper will automatically convert mfa_invalid_code errors
371
+ * // to MfaInvalidOtpError instances
372
+ * ```
427
373
  * @notInstrumented
428
374
  */
429
- const extractParams = (args) => {
430
- if (args.length === 0) return {};
431
- if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0])) return args[0];
432
- const result = {};
433
- for (let i = 0; i < args.length; i++) result[`arg${i}`] = args[i];
434
- return result;
375
+ const clientErrorMapper = (error) => {
376
+ if (error instanceof APIError) {
377
+ if (error.code === "mfa_invalid_code") return new MfaInvalidOtpError({ cause: error });
378
+ if (error.code === "mfa_rate_limited") return new MfaRateLimitedError({ cause: error });
379
+ if (error.code === "invalid_external_auth") return new InvalidExternalAuthError({ cause: error });
380
+ if (error.code === "sandbox_maximum_threshold_reached") return new SandboxMaximumThresholdReachedError({ cause: error });
381
+ if (error.code === "merge_accounts_invalid" || error.code === "reassign_wallet_error") return new LinkCredentialError({ cause: error });
382
+ }
383
+ return null;
435
384
  };
436
385
 
437
386
  //#endregion
438
- //#region src/services/instrumentation/instrumentFunction/scrubResolvedValue/scrubResolvedValue.ts
387
+ //#region src/modules/apiClient/utils/convertToApiErrorMiddleware/convertToApiErrorMiddleware.ts
439
388
  /**
440
- * Scrubs a resolved return value of any type before logging it.
441
- * Routes through `scrubParameters` by wrapping the value so that strings,
442
- * arrays, Dates, Errors, and all other types are handled identically to
443
- * nested object fields.
389
+ * Creates middleware that converts HTTP error responses to APIError instances
390
+ * and optionally applies custom error mappers to transform them into specific error types.
391
+ *
392
+ * @param options.errorMappers - Array of error mappers to apply to API errors
393
+ * @returns A middleware function that handles error conversion and mapping
444
394
  * @notInstrumented
445
395
  */
446
- const scrubResolvedValue = ({ piiFields, redactAll, value }) => scrubParameters({
447
- piiFields,
448
- redactAll,
449
- value: { result: value }
450
- }).result;
396
+ const createConvertToApiErrorMiddleware = ({ errorMappers = [] }) => ({ post: async (context) => {
397
+ if (context.response.status >= 400) {
398
+ const apiError = await APIError.fromResponse(context.response);
399
+ if (apiError) {
400
+ let errorToThrow = apiError;
401
+ for (const mapper of errorMappers) {
402
+ const newError = mapper(apiError);
403
+ if (newError) {
404
+ errorToThrow = newError;
405
+ break;
406
+ }
407
+ }
408
+ throw errorToThrow;
409
+ }
410
+ }
411
+ return context.response;
412
+ } });
451
413
 
452
414
  //#endregion
453
- //#region src/services/instrumentation/instrumentFunction/instrumentFunction.ts
454
- const serializeError = (error) => {
455
- if (error instanceof Error) return {
456
- message: error.message,
457
- name: error.name,
458
- stack: error.stack
459
- };
415
+ //#region src/utils/getNonce/getNonce.ts
416
+ /**
417
+ * Returns a nonce for wallet ownership verification.
418
+ *
419
+ * Pops the first nonce from the prefetched pool. When the pool is running low
420
+ * (≤1 remaining after pop) a background refetch is triggered. If the pool is
421
+ * empty, nonces are fetched on-demand before returning.
422
+ * @notInstrumented
423
+ */
424
+ const getNonce = async (client) => {
425
+ const core = getCore(client);
426
+ const pool = core.state.get().prefetchedNonces;
427
+ if (pool.length > 0) {
428
+ const [nonce, ...remaining] = pool;
429
+ core.state.set({ prefetchedNonces: remaining });
430
+ if (remaining.length <= 1) fetchAndStoreNonces(client);
431
+ return nonce;
432
+ }
433
+ await fetchAndStoreNonces(client);
434
+ return getNonce(client);
435
+ };
436
+
437
+ //#endregion
438
+ //#region src/modules/deviceRegistration/getDeviceSigner/getDeviceSigner.ts
439
+ /** @notInstrumented */
440
+ const getDeviceSigner = async (client) => {
441
+ const { deviceSigner, keychain } = getCore(client);
442
+ /**
443
+ * If the device signer is available, it should handle the device signing.
444
+ * This is used for mobile devices with secure enclave.
445
+ */
446
+ if (deviceSigner) return deviceSigner;
447
+ const keyName = "device";
448
+ if (!await keychain.getPublicKey(keyName)) await keychain.generateKey(keyName);
460
449
  return {
461
- message: String(error),
462
- name: "UnknownError"
450
+ getPublicKey: () => keychain.getPublicKey(keyName),
451
+ sign: (payload) => keychain.sign(keyName, payload)
463
452
  };
464
453
  };
465
- /**
466
- * Wraps a function so that each invocation emits a structured
467
- * `FunctionInstrumentationEvent` at three lifecycle points:
468
- *
469
- * - **started** immediately before the function body executes, with the
470
- * PII-scrubbed call parameters.
471
- * - **resolved** after the function (or its returned Promise) settles
472
- * successfully, with the PII-scrubbed return value.
473
- * - **rejected** if the function throws synchronously or its returned
474
- * Promise rejects.
475
- *
476
- * The original return value (or thrown error) is always forwarded to the
477
- * caller unchanged — instrumentation is purely observational.
478
- *
479
- * ## Usage
480
- *
481
- * ```ts
482
- * const transfer = instrumentFunction({
483
- * fn: rawTransfer,
484
- * functionName: 'transfer',
485
- * getCore: () => core,
486
- * });
487
- *
488
- * // When called, three events are emitted automatically:
489
- * await transfer({ amount: 1, to: '0xabc' });
490
- * ```
491
- *
492
- * ## How functions are wrapped
493
- *
494
- * `instrumentFunction` is not called manually in application code. Instead,
495
- * the esbuild instrumentation plugin (`tools/instrumentation-plugin`) injects
496
- * it at build time: any exported function annotated with `@instrumented` in its
497
- * JSDoc is automatically wrapped by the plugin during the build step.
498
- * The plugin strips the original `export`, renames the function, and re-exports
499
- * it under the original name via `instrumentFunction(...)` — so the public API
500
- * is unchanged and no call sites need to be updated.
501
- *
502
- * ## Opting out
503
- *
504
- * Instrumentation is skipped entirely when:
505
- * - `getCore()` returns `undefined` (SDK not initialised), or
506
- * - `core.instrumentation.config.enabled` is `false`.
507
- *
508
- * In both cases the original function is called directly with no overhead.
509
- *
510
- * ## PII scrubbing
511
- *
512
- * All string values whose key appears in `piiFields` are replaced with
513
- * `"[redacted]"` before events are emitted. Pass `redactAll: true` to redact
514
- * every field, regardless of key name.
515
- * @notInstrumented
516
- */
517
- const instrumentFunction = ({ fn, functionName, getCore: getCore$1, redactAll }) => {
518
- const wrapped = (...args) => {
519
- const core = getCore$1();
520
- if (!core) return fn(...args);
521
- if (!core.instrumentation.config.enabled) return fn(...args);
522
- const instrumentation = core.instrumentation;
523
- const state = core.state.get();
524
- const baseEvent = {
525
- environmentId: core.environmentId,
526
- functionName,
527
- operatingSystem: getOperatingSystem(),
528
- parameters: scrubParameters({
529
- piiFields: instrumentation.config.piiFields,
530
- redactAll,
531
- value: extractParams(args)
532
- }),
533
- platform: getPlatform(),
534
- registeredExtensionKeys: Array.from(core.extensions),
535
- sdkSessionId: core.sdkSessionId,
536
- sdkVersion: core.version,
537
- status: "info",
538
- tokenSessionId: state.token ? extractSessionId(state.token) : null,
539
- userAgent: getUserAgent(),
540
- userId: state.user?.id ?? null
541
- };
542
- instrumentation.logFunction({
543
- ...baseEvent,
544
- phase: "started",
545
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
546
- });
547
- try {
548
- const result = fn(...args);
549
- if (result instanceof Promise) return result.then((value) => {
550
- instrumentation.logFunction({
551
- ...baseEvent,
552
- phase: "resolved",
553
- resolvedValue: scrubResolvedValue({
554
- piiFields: instrumentation.config.piiFields,
555
- redactAll,
556
- value
557
- }),
558
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
559
- });
560
- return value;
561
- }).catch((error) => {
562
- instrumentation.logFunction({
563
- ...baseEvent,
564
- phase: "rejected",
565
- rejectedError: serializeError(error),
566
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
567
- });
568
- throw error;
569
- });
570
- instrumentation.logFunction({
571
- ...baseEvent,
572
- phase: "resolved",
573
- resolvedValue: scrubResolvedValue({
574
- piiFields: instrumentation.config.piiFields,
575
- redactAll,
576
- value: result
577
- }),
578
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
579
- });
580
- return result;
581
- } catch (error) {
582
- instrumentation.logFunction({
583
- ...baseEvent,
584
- phase: "rejected",
585
- rejectedError: serializeError(error),
586
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
587
- });
588
- throw error;
589
- }
454
+
455
+ //#endregion
456
+ //#region src/modules/deviceRegistration/getHeadersForNonceSignedByDeviceSigners/getHeadersForNonceSignedByDeviceSigners.ts
457
+ /** @notInstrumented */
458
+ const getHeadersForNonceSignedByDeviceSigners = async (client) => {
459
+ const { projectSettings } = client;
460
+ assertDefined(projectSettings, "Project settings not found");
461
+ /**
462
+ * For cookie based environments, the device registration is handled
463
+ * by settings the cookie in the browser.
464
+ * Then we dont need to provide the headers
465
+ */
466
+ if (isCookieEnabled(client)) return {};
467
+ const deviceSigner = await getDeviceSigner(client);
468
+ const nonce = await getNonce(client);
469
+ const signedNonce = await deviceSigner.sign(nonce);
470
+ const publicKey = await deviceSigner.getPublicKey();
471
+ return {
472
+ "x-dynamic-device-nonce": nonce,
473
+ "x-dynamic-device-publickey": publicKey,
474
+ "x-dynamic-device-signed-nonce": signedNonce
590
475
  };
591
- return wrapped;
592
476
  };
593
477
 
594
478
  //#endregion
595
- //#region src/modules/auth/getElevatedAccessToken/getElevatedAccessToken.ts
596
- /**
597
- * Gets an elevated access token by scope.
598
- *
599
- * This function retrieves an elevated access token that contains the specified scope.
600
- * Expired tokens are automatically filtered out.
601
- *
602
- * By default, if the token has `singleUse: true`, it will be automatically
603
- * consumed (removed from state) after retrieval. Pass `consume: false` to
604
- * check for token existence without consuming it.
605
- *
606
- * @param params - The parameters object.
607
- * @param params.scope - The scope to match (e.g., 'wallet:export').
608
- * @param params.consume - Whether to consume single-use tokens (default: true).
609
- * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
610
- * @returns The elevated access token if found and not expired, or undefined if not found or expired.
611
- *
612
- * @example
613
- * ```typescript
614
- * // Retrieve and consume (default)
615
- * const token = getElevatedAccessToken({ scope: 'wallet:export' });
616
- *
617
- * // Check existence without consuming
618
- * const token = getElevatedAccessToken({ scope: 'credential:unlink', consume: false });
619
- * ```
620
- * @notInstrumented
621
- */
622
- const getElevatedAccessToken = ({ consume = true, scope }, client = getDefaultClient()) => {
623
- const core = getCore(client);
624
- const now = /* @__PURE__ */ new Date();
625
- const elevatedAccessTokens = core.state.get().elevatedAccessTokens || [];
626
- const validTokens = elevatedAccessTokens.filter((token$1) => !token$1.expiresAt || token$1.expiresAt > now);
627
- if (validTokens.length !== elevatedAccessTokens.length) core.state.set({ elevatedAccessTokens: validTokens });
628
- const token = validTokens.find((token$1) => token$1.scopes.includes(scope));
629
- if (!token) return;
630
- if (consume && token.singleUse) {
631
- const updatedTokens = validTokens.filter((t) => t !== token);
632
- core.state.set({ elevatedAccessTokens: updatedTokens });
633
- }
634
- return token.token;
635
- };
636
- const __getElevatedAccessToken_impl = getElevatedAccessToken;
637
- const __getElevatedAccessToken_wrapped = instrumentFunction({
638
- fn: __getElevatedAccessToken_impl,
639
- functionName: "getElevatedAccessToken",
640
- getCore: () => {
641
- try {
642
- return getCore(getDefaultClient());
643
- } catch {
644
- return;
479
+ //#region src/modules/apiClient/utils/deviceSignatureHeadersMiddleware/createDeviceSignatureHeadersMiddleware.ts
480
+ /** @notInstrumented */
481
+ const createDeviceSignatureHeadersMiddleware = (client) => {
482
+ return { pre: async (context) => {
483
+ /**
484
+ * The signed nonce is not required for cookie based environments.
485
+ */
486
+ if (isDeviceNonceSignatureRequiredForUrl(context.url) && !isCookieEnabled(client)) {
487
+ const deviceSignerHeaders = await getHeadersForNonceSignedByDeviceSigners(client);
488
+ return {
489
+ init: {
490
+ ...context.init,
491
+ headers: {
492
+ ...context.init.headers,
493
+ ...deviceSignerHeaders
494
+ }
495
+ },
496
+ url: context.url
497
+ };
645
498
  }
646
- }
647
- });
499
+ } };
500
+ };
648
501
 
649
502
  //#endregion
650
- //#region src/errors/ValueMustBeDefinedError.ts
651
- var ValueMustBeDefinedError = class extends BaseError {
652
- constructor(message) {
503
+ //#region src/errors/UnauthorizedError.ts
504
+ var UnauthorizedError = class extends BaseError {
505
+ constructor({ cause }) {
653
506
  super({
654
- cause: null,
655
- code: "value_must_be_defined_error",
507
+ cause,
508
+ code: "unauthorized_error",
656
509
  docsUrl: null,
657
- name: "ValueMustBeDefined",
658
- shortMessage: message
510
+ name: "UnauthorizedError",
511
+ shortMessage: "Unauthorized"
659
512
  });
660
513
  }
661
514
  };
662
515
 
663
516
  //#endregion
664
- //#region src/utils/assertDefined/assertDefined.ts
665
- /**
666
- * Asserts that a value is not null or undefined, throwing an error if it is.
667
- * This function acts as a type guard, narrowing the type to exclude null and undefined.
668
- *
669
- * @template T - The type of the value being checked
670
- * @param value - The value to check for null or undefined
671
- * @param message - The error message to throw if the value is null or undefined
672
- * @throws Throws an error with the provided message if value is null or undefined
673
- * @example
674
- * ```typescript
675
- * const maybeString: string | null = getValue();
676
- * assertDefined(maybeString, 'String value is required');
677
- * // maybeString is now typed as string (null is excluded)
678
- * ```
679
- */
680
- function assertDefined(value, message) {
681
- if (value === null || value === void 0) throw new ValueMustBeDefinedError(message);
682
- }
517
+ //#region src/modules/apiClient/utils/unauthorizedMiddleware/createUnauthorizedMiddleware.ts
518
+ /** @notInstrumented */
519
+ const createUnauthorizedMiddleware = () => ({ post: async (context) => {
520
+ if (context.response.status === 401) {
521
+ let cause = null;
522
+ try {
523
+ const errorBody = await context.response.clone().json();
524
+ if (errorBody && "error" in errorBody && typeof errorBody.error === "string") cause = new Error(errorBody.error);
525
+ } catch {}
526
+ throw new UnauthorizedError({ cause });
527
+ }
528
+ return context.response;
529
+ } });
683
530
 
684
531
  //#endregion
685
- //#region src/modules/projectSettings/isCookieEnabled/isCookieEnabled.ts
532
+ //#region src/modules/apiClient/createApiClient.ts
686
533
  /**
687
- * Returns true if the client is using Dynamic cookies or a BYO JWT cookie.
534
+ * Returns a new instance of the SDK API client.
535
+ *
536
+ * This is not meant for storing, as it is very light we can create it whenever needed.
688
537
  * @notInstrumented
689
538
  */
690
- const isCookieEnabled = (client) => {
691
- assertDefined(client.projectSettings, "Project settings are not defined");
692
- const securitySettings = client.projectSettings.security;
693
- if (!securitySettings) return false;
694
- const dynamicCookiesEnabled = (securitySettings.auth?.storage || []).includes(AuthStorageEnum.Cookie);
695
- const byoJwtCookieEnabled = Boolean(securitySettings.externalAuth?.cookieName);
696
- return dynamicCookiesEnabled || byoJwtCookieEnabled;
697
- };
698
-
699
- //#endregion
700
- //#region src/modules/sessionKeys/getSessionKeys/getSessionKeys.ts
701
- /** @notInstrumented */
702
- const getSessionKeys = (client) => {
703
- const publicKey = getCore(client).state.get().sessionKeys;
704
- if (!publicKey) return;
705
- return { publicKey };
706
- };
707
-
708
- //#endregion
709
- //#region src/errors/APIError/APIError.ts
710
- var APIError = class APIError extends BaseError {
711
- status;
712
- constructor(message, code, status) {
713
- super({
714
- cause: null,
715
- code,
716
- docsUrl: null,
717
- name: "APIError",
718
- shortMessage: message
719
- });
720
- this.status = status;
721
- }
722
- static async fromResponse(response) {
723
- try {
724
- const errorBody = await response.clone().json();
725
- if (errorBody && "error" in errorBody && typeof errorBody.error === "string") {
726
- const errorCode = "code" in errorBody && typeof errorBody.code === "string" ? errorBody.code : "unknown_error";
727
- return new APIError(errorBody.error, errorCode, response.status);
728
- }
729
- return null;
730
- } catch {
731
- return null;
539
+ const createApiClient = (options = {}, client) => {
540
+ const core = getCore(client);
541
+ const coreState = core.state.get();
542
+ const settings = {
543
+ basePath: core.apiBaseUrl,
544
+ headers: {
545
+ "Content-Type": "application/json",
546
+ [DYNAMIC_API_VERSION_HEADER]: `API/${DYNAMIC_SDK_API_VERSION}`,
547
+ [DYNAMIC_REQUEST_ID_HEADER]: randomString({ length: 50 }),
548
+ [DYNAMIC_SDK_SESSION_ID_HEADER]: core.sdkSessionId,
549
+ [DYNAMIC_SDK_VERSION_HEADER]: `${CLIENT_SDK_NAME}/${version}`,
550
+ ...core.getApiHeaders(),
551
+ ...options.headers
732
552
  }
553
+ };
554
+ if (core.metadata?.universalLink) settings.headers["Origin"] = core.metadata.universalLink;
555
+ if (client.token) settings.headers.Authorization = `Bearer ${client.token}`;
556
+ if (client.projectSettings && isCookieEnabled(client)) settings.credentials = "include";
557
+ if (options.includeMfaToken && coreState.mfaToken) settings.headers[MFA_TOKEN_HEADER] = coreState.mfaToken;
558
+ if (options.elevatedAccessTokenScope) {
559
+ const elevatedToken = getElevatedAccessToken({ scope: options.elevatedAccessTokenScope }, client);
560
+ if (elevatedToken) settings.headers[ELEVATED_ACCESS_TOKEN_HEADER] = elevatedToken;
733
561
  }
562
+ const sessionPublicKey = getSessionKeys(client)?.publicKey;
563
+ const isSessionPublicKeyHeaderPresent = settings.headers[SESSION_PUBLIC_KEY_HEADER] !== void 0;
564
+ if (sessionPublicKey && !isSessionPublicKeyHeaderPresent) settings.headers[SESSION_PUBLIC_KEY_HEADER] = sessionPublicKey;
565
+ return new SDKApi(new Configuration({
566
+ ...settings,
567
+ fetchApi: core.fetch,
568
+ middleware: [
569
+ createDeviceSignatureHeadersMiddleware(client),
570
+ createConvertToApiErrorMiddleware({ errorMappers: [...options.errorMappers || [], clientErrorMapper] }),
571
+ createUnauthorizedMiddleware()
572
+ ]
573
+ }));
734
574
  };
735
575
 
736
576
  //#endregion
737
- //#region src/errors/InvalidExternalAuthError.ts
738
- var InvalidExternalAuthError = class extends BaseError {
739
- constructor({ cause }) {
740
- super({
741
- cause,
742
- code: "invalid_external_auth_error",
743
- docsUrl: "https://www.dynamic.xyz/docs/external-auth/third-party-auth-overview",
744
- name: "InvalidExternalAuthError",
745
- shortMessage: "Error authenticating with external JWT"
746
- });
747
- }
748
- };
749
-
750
- //#endregion
751
- //#region src/errors/LinkCredentialError.ts
752
- var LinkCredentialError = class extends BaseError {
753
- constructor({ cause }) {
754
- super({
755
- cause,
756
- code: "link_credential_error",
757
- docsUrl: null,
758
- name: "LinkCredentialError",
759
- shortMessage: "The credential you are trying to link is associated with another account and cannot be reassigned."
760
- });
761
- }
762
- };
763
-
764
- //#endregion
765
- //#region src/errors/MfaInvalidOtpError.ts
766
- var MfaInvalidOtpError = class extends BaseError {
767
- constructor({ cause }) {
768
- super({
769
- cause,
770
- code: "mfa_invalid_otp_error",
771
- docsUrl: null,
772
- name: "MfaInvalidOtpError",
773
- shortMessage: "Invalid OTP"
577
+ //#region src/utils/getNonce/fetchAndStoreNonces/fetchAndStoreNonces.ts
578
+ /**
579
+ * Fetches a batch of nonces from the API.
580
+ *
581
+ * Prefetching is critical for iOS deep link wallet providers (e.g. Phantom
582
+ * redirect). On iOS, a network request (like fetching a nonce) between a user
583
+ * action and a deeplink call will break the gesture chain, causing iOS to
584
+ * silently ignore the deeplink. By prefetching nonces ahead of time,
585
+ * `getNonce` can return synchronously from cache and the deeplink fires
586
+ * without an interrupting network round-trip.
587
+ *
588
+ * New nonces are **appended** to the existing pool so that unconsumed entries
589
+ * are not discarded by a background refetch.
590
+ *
591
+ * Concurrent refills are coalesced behind a single in-flight promise so that
592
+ * simultaneous callers share one `GET /nonces` round-trip instead of each
593
+ * issuing their own (which would inflate the pool and hammer the backend).
594
+ * @notInstrumented
595
+ */
596
+ const fetchAndStoreNonces = async (client) => {
597
+ const core = getCore(client);
598
+ const inFlight = core.state.get().nonceFetchInFlight;
599
+ if (inFlight) return inFlight;
600
+ const { environmentId } = core;
601
+ const apiClient = createApiClient({}, client);
602
+ const fetchPromise = (async () => {
603
+ const { nonces } = await apiClient.getNonces({
604
+ count: NONCE_POOL_SIZE,
605
+ environmentId
774
606
  });
775
- }
776
- };
777
-
778
- //#endregion
779
- //#region src/errors/MfaRateLimitedError.ts
780
- var MfaRateLimitedError = class extends BaseError {
781
- constructor({ cause }) {
782
- super({
783
- cause,
784
- code: "mfa_rate_limited_error",
785
- docsUrl: null,
786
- name: "MfaRateLimitedError",
787
- shortMessage: "Rate limited"
607
+ const existing = core.state.get().prefetchedNonces;
608
+ core.state.set({
609
+ prefetchedNonces: [...existing, ...nonces],
610
+ prefetchedNoncesExpiration: Date.now() + NONCE_POOL_EXPIRATION_TIME
788
611
  });
789
- }
612
+ })().finally(() => {
613
+ core.state.set({ nonceFetchInFlight: null });
614
+ });
615
+ core.state.set({ nonceFetchInFlight: fetchPromise });
616
+ return fetchPromise;
790
617
  };
791
618
 
792
619
  //#endregion
793
- //#region src/errors/SandboxMaximumThresholdReachedError.ts
794
- var SandboxMaximumThresholdReachedError = class extends BaseError {
795
- constructor({ cause }) {
796
- super({
797
- cause,
798
- code: "sandbox_maximum_threshold_reached_error",
799
- docsUrl: "https://www.dynamic.xyz/docs/developer-dashboard/sandbox-vs-live#sandbox-vs-live",
800
- name: "SandboxMaximumThresholdReachedError",
801
- shortMessage: "Your sandbox environment has reached the maximum MAU. Please use a live environment for production traffic."
802
- });
803
- }
620
+ //#region src/utils/setCookie/setCookie.ts
621
+ /**
622
+ * Sefelly sets the cookie in the browser.
623
+ * @notInstrumented
624
+ */
625
+ const setCookie = (cookie) => {
626
+ document.cookie = cookie;
804
627
  };
805
628
 
806
629
  //#endregion
807
- //#region src/modules/apiClient/utils/clientErrorMapper/clientErrorMapper.ts
630
+ //#region src/modules/clientEvents/clientEvents.ts
808
631
  /**
809
- * Default error mapper for the client that handles common API error codes.
632
+ * Adds an event listener for Dynamic client events.
810
633
  *
811
- * This mapper transforms specific API error codes into more specific error types:
812
- * - `mfa_invalid_code` `MfaInvalidOtpError`
813
- * - `mfa_rate_limited` → `MfaRateLimitedError`
634
+ * This function allows you to listen for various events emitted by the Dynamic client,
635
+ * such as authentication state changes, wallet connections, and more.
814
636
  *
815
- * @param error - The error to be mapped
816
- * @returns A transformed error if the error code matches a known pattern, or null if no transformation is needed
637
+ * @param params.event - The event name to listen for.
638
+ * @param params.listener - The callback function to execute when the event is fired.
639
+ * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
640
+ * @returns A function that can be called to remove the listener.
641
+ * @notInstrumented
642
+ * @hook Event listeners are error-prone to wire up by hand with useEffect.
643
+ */
644
+ const onEvent = ({ event, listener }, client = getDefaultClient()) => {
645
+ const { eventEmitter } = getCore(client);
646
+ eventEmitter.on(event, listener);
647
+ return () => {
648
+ eventEmitter.off(event, listener);
649
+ };
650
+ };
651
+ /**
652
+ * Removes an event listener from Dynamic client events.
817
653
  *
818
- * @example
819
- * ```typescript
820
- * // This will be automatically applied to all API errors
821
- * const apiClient = createApiClient({}, client);
654
+ * This function unsubscribes a previously registered event listener
655
+ * from the specified Dynamic client event.
822
656
  *
823
- * // The clientErrorMapper will automatically convert mfa_invalid_code errors
824
- * // to MfaInvalidOtpError instances
825
- * ```
657
+ * @param params.event - The event name to remove the listener from.
658
+ * @param params.listener - The callback function to remove.
659
+ * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
826
660
  * @notInstrumented
827
661
  */
828
- const clientErrorMapper = (error) => {
829
- if (error instanceof APIError) {
830
- if (error.code === "mfa_invalid_code") return new MfaInvalidOtpError({ cause: error });
831
- if (error.code === "mfa_rate_limited") return new MfaRateLimitedError({ cause: error });
832
- if (error.code === "invalid_external_auth") return new InvalidExternalAuthError({ cause: error });
833
- if (error.code === "sandbox_maximum_threshold_reached") return new SandboxMaximumThresholdReachedError({ cause: error });
834
- if (error.code === "merge_accounts_invalid" || error.code === "reassign_wallet_error") return new LinkCredentialError({ cause: error });
835
- }
836
- return null;
662
+ const offEvent = ({ event, listener }, client = getDefaultClient()) => {
663
+ const { eventEmitter } = getCore(client);
664
+ eventEmitter.off(event, listener);
837
665
  };
838
-
839
- //#endregion
840
- //#region src/modules/apiClient/utils/convertToApiErrorMiddleware/convertToApiErrorMiddleware.ts
841
666
  /**
842
- * Creates middleware that converts HTTP error responses to APIError instances
843
- * and optionally applies custom error mappers to transform them into specific error types.
667
+ * Adds a one-time event listener for Dynamic client events.
844
668
  *
845
- * @param options.errorMappers - Array of error mappers to apply to API errors
846
- * @returns A middleware function that handles error conversion and mapping
669
+ * This function listens for an event that will automatically remove itself
670
+ * after being triggered once.
671
+ *
672
+ * @param params.event - The event name to listen for.
673
+ * @param params.listener - The callback function to execute when the event is fired.
674
+ * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
675
+ * @returns A function that can be called to remove the listener before it fires.
847
676
  * @notInstrumented
677
+ * @hook Event listeners are error-prone to wire up by hand with useEffect.
848
678
  */
849
- const createConvertToApiErrorMiddleware = ({ errorMappers = [] }) => ({ post: async (context) => {
850
- if (context.response.status >= 400) {
851
- const apiError = await APIError.fromResponse(context.response);
852
- if (apiError) {
853
- let errorToThrow = apiError;
854
- for (const mapper of errorMappers) {
855
- const newError = mapper(apiError);
856
- if (newError) {
857
- errorToThrow = newError;
858
- break;
859
- }
860
- }
861
- throw errorToThrow;
862
- }
863
- }
864
- return context.response;
865
- } });
866
-
867
- //#endregion
868
- //#region src/utils/getNonce/getNonce.ts
679
+ const onceEvent = ({ event, listener }, client = getDefaultClient()) => {
680
+ const { eventEmitter } = getCore(client);
681
+ eventEmitter.once(event, listener);
682
+ return () => {
683
+ eventEmitter.off(event, listener);
684
+ };
685
+ };
869
686
  /**
870
- * Returns a nonce for wallet ownership verification.
687
+ * Emits a Dynamic client event.
871
688
  *
872
- * Pops the first nonce from the prefetched pool. When the pool is running low
873
- * (≤1 remaining after pop) a background refetch is triggered. If the pool is
874
- * empty, nonces are fetched on-demand before returning.
689
+ * This function triggers an event that will be received by all registered
690
+ * listeners for the specified event type.
691
+ *
692
+ * @param params.event - The event name to emit.
693
+ * @param params.args - The arguments to pass to event listeners.
694
+ * @param client - The Dynamic client instance.
875
695
  * @notInstrumented
876
696
  */
877
- const getNonce = async (client) => {
878
- const core = getCore(client);
879
- const pool = core.state.get().prefetchedNonces;
880
- if (pool.length > 0) {
881
- const [nonce, ...remaining] = pool;
882
- core.state.set({ prefetchedNonces: remaining });
883
- if (remaining.length <= 1) fetchAndStoreNonces(client);
884
- return nonce;
885
- }
886
- await fetchAndStoreNonces(client);
887
- return getNonce(client);
697
+ const emitEvent = ({ event, args }, client) => {
698
+ const { eventEmitter } = getCore(client);
699
+ eventEmitter.emit(event, args);
888
700
  };
889
701
 
890
702
  //#endregion
891
- //#region src/modules/deviceRegistration/getDeviceSigner/getDeviceSigner.ts
892
- /** @notInstrumented */
893
- const getDeviceSigner = async (client) => {
894
- const { deviceSigner, keychain } = getCore(client);
895
- /**
896
- * If the device signer is available, it should handle the device signing.
897
- * This is used for mobile devices with secure enclave.
898
- */
899
- if (deviceSigner) return deviceSigner;
900
- const keyName = "device";
901
- if (!await keychain.getPublicKey(keyName)) await keychain.generateKey(keyName);
902
- return {
903
- getPublicKey: () => keychain.getPublicKey(keyName),
904
- sign: (payload) => keychain.sign(keyName, payload)
905
- };
703
+ //#region src/errors/InvalidWalletProviderKeyError.ts
704
+ var InvalidWalletProviderKeyError = class extends BaseError {
705
+ constructor(value) {
706
+ super({
707
+ cause: null,
708
+ code: "invalid_wallet_provider_key",
709
+ docsUrl: null,
710
+ name: "InvalidWalletProviderKeyError",
711
+ shortMessage: `Invalid wallet provider key: ${value}. Key must be in the format of <normalizedWalletNameWithChain>:<walletProviderType>[:<suffix>]`
712
+ });
713
+ }
906
714
  };
907
715
 
908
716
  //#endregion
909
- //#region src/modules/deviceRegistration/getHeadersForNonceSignedByDeviceSigners/getHeadersForNonceSignedByDeviceSigners.ts
717
+ //#region src/modules/wallets/walletProvider/splitWalletProviderKey/splitWalletProviderKey.ts
910
718
  /** @notInstrumented */
911
- const getHeadersForNonceSignedByDeviceSigners = async (client) => {
912
- const { projectSettings } = client;
913
- assertDefined(projectSettings, "Project settings not found");
914
- /**
915
- * For cookie based environments, the device registration is handled
916
- * by settings the cookie in the browser.
917
- * Then we dont need to provide the headers
918
- */
919
- if (isCookieEnabled(client)) return {};
920
- const deviceSigner = await getDeviceSigner(client);
921
- const nonce = await getNonce(client);
922
- const signedNonce = await deviceSigner.sign(nonce);
923
- const publicKey = await deviceSigner.getPublicKey();
719
+ const splitWalletProviderKey = (walletProviderKey) => {
720
+ const [normalizedWalletNameWithChain, walletProviderType, suffix, ...rest] = walletProviderKey.split(":");
721
+ if (!normalizedWalletNameWithChain || !Object.values(WalletProviderEnum).includes(walletProviderType) || rest.length > 0) throw new InvalidWalletProviderKeyError(walletProviderKey);
924
722
  return {
925
- "x-dynamic-device-nonce": nonce,
926
- "x-dynamic-device-publickey": publicKey,
927
- "x-dynamic-device-signed-nonce": signedNonce
723
+ normalizedWalletNameWithChain,
724
+ suffix,
725
+ walletProviderType
928
726
  };
929
727
  };
930
728
 
931
729
  //#endregion
932
- //#region src/modules/apiClient/utils/deviceSignatureHeadersMiddleware/createDeviceSignatureHeadersMiddleware.ts
730
+ //#region src/modules/wallets/utils/normalizeAddress/normalizeAddress.ts
933
731
  /** @notInstrumented */
934
- const createDeviceSignatureHeadersMiddleware = (client) => {
935
- return { pre: async (context) => {
936
- /**
937
- * The signed nonce is not required for cookie based environments.
938
- */
939
- if (isDeviceNonceSignatureRequiredForUrl(context.url) && !isCookieEnabled(client)) {
940
- const deviceSignerHeaders = await getHeadersForNonceSignedByDeviceSigners(client);
941
- return {
942
- init: {
943
- ...context.init,
944
- headers: {
945
- ...context.init.headers,
946
- ...deviceSignerHeaders
947
- }
948
- },
949
- url: context.url
950
- };
951
- }
952
- } };
732
+ const normalizeAddress = ({ address, chain }) => {
733
+ let normalizedAddress = address;
734
+ if (normalizedAddress?.startsWith("0x")) normalizedAddress = normalizedAddress.slice(2);
735
+ normalizedAddress = ["EVM", "FLOW"].includes(chain) ? normalizedAddress?.toLowerCase() : normalizedAddress;
736
+ return normalizedAddress;
953
737
  };
954
738
 
955
739
  //#endregion
956
- //#region src/errors/UnauthorizedError.ts
957
- var UnauthorizedError = class extends BaseError {
958
- constructor({ cause }) {
959
- super({
960
- cause,
961
- code: "unauthorized_error",
962
- docsUrl: null,
963
- name: "UnauthorizedError",
964
- shortMessage: "Unauthorized"
965
- });
966
- }
740
+ //#region src/modules/wallets/utils/formatWalletAccountId/formatWalletAccountId.ts
741
+ /** @notInstrumented */
742
+ const formatWalletAccountId = ({ address, chain, walletProviderKey }) => {
743
+ const { normalizedWalletNameWithChain } = splitWalletProviderKey(walletProviderKey);
744
+ return `${normalizedWalletNameWithChain}:${normalizeAddress({
745
+ address,
746
+ chain
747
+ })}`;
967
748
  };
968
749
 
969
750
  //#endregion
970
- //#region src/modules/apiClient/utils/unauthorizedMiddleware/createUnauthorizedMiddleware.ts
751
+ //#region src/modules/wallets/utils/convertUnverifiedWalletAccountToWalletAccount/convertUnverifiedWalletAccountToWalletAccount.ts
971
752
  /** @notInstrumented */
972
- const createUnauthorizedMiddleware = () => ({ post: async (context) => {
973
- if (context.response.status === 401) {
974
- let cause = null;
975
- try {
976
- const errorBody = await context.response.clone().json();
977
- if (errorBody && "error" in errorBody && typeof errorBody.error === "string") cause = new Error(errorBody.error);
978
- } catch {}
979
- throw new UnauthorizedError({ cause });
753
+ const convertUnverifiedWalletAccountToWalletAccount = ({ unverifiedWalletAccount }) => ({
754
+ address: unverifiedWalletAccount.address,
755
+ addressesWithTypes: unverifiedWalletAccount.addressesWithTypes,
756
+ chain: unverifiedWalletAccount.chain,
757
+ id: formatWalletAccountId({
758
+ address: unverifiedWalletAccount.address,
759
+ chain: unverifiedWalletAccount.chain,
760
+ walletProviderKey: unverifiedWalletAccount.walletProviderKey
761
+ }),
762
+ lastSelectedAt: unverifiedWalletAccount.lastSelectedAt,
763
+ verifiedCredentialId: null,
764
+ walletProviderKey: unverifiedWalletAccount.walletProviderKey
765
+ });
766
+
767
+ //#endregion
768
+ //#region src/modules/wallets/constants.ts
769
+ const CHAINS_INFO_MAP = {
770
+ ALEO: {
771
+ apiChainName: "aleo",
772
+ blockchainName: "Aleo",
773
+ verifiedCredentialChainName: "aleo"
774
+ },
775
+ ALGO: {
776
+ apiChainName: "algo",
777
+ blockchainName: "Algorand",
778
+ verifiedCredentialChainName: "algorand"
779
+ },
780
+ APTOS: {
781
+ apiChainName: "aptos",
782
+ blockchainName: "Aptos",
783
+ verifiedCredentialChainName: "aptos"
784
+ },
785
+ BTC: {
786
+ apiChainName: "bitcoin",
787
+ blockchainName: "Bitcoin",
788
+ legacyWalletBookAliases: ["btc"],
789
+ verifiedCredentialChainName: "bip122"
790
+ },
791
+ COSMOS: {
792
+ apiChainName: "cosmos",
793
+ blockchainName: "Cosmos",
794
+ verifiedCredentialChainName: "cosmos"
795
+ },
796
+ ECLIPSE: {
797
+ apiChainName: "eclipse",
798
+ blockchainName: "Eclipse",
799
+ verifiedCredentialChainName: "eclipse"
800
+ },
801
+ EVM: {
802
+ apiChainName: "evm",
803
+ blockchainName: "Ethereum",
804
+ verifiedCredentialChainName: "eip155"
805
+ },
806
+ FLOW: {
807
+ apiChainName: "flow",
808
+ blockchainName: "Flow",
809
+ verifiedCredentialChainName: "flow"
810
+ },
811
+ MIDNIGHT: {
812
+ apiChainName: "midnight",
813
+ blockchainName: "Midnight",
814
+ verifiedCredentialChainName: "midnight"
815
+ },
816
+ SOL: {
817
+ apiChainName: "solana",
818
+ blockchainName: "Solana",
819
+ legacyWalletBookAliases: ["sol"],
820
+ verifiedCredentialChainName: "solana",
821
+ waasChainNameOverride: "SVM"
822
+ },
823
+ SPARK: {
824
+ apiChainName: "spark",
825
+ blockchainName: "Spark",
826
+ verifiedCredentialChainName: "spark"
827
+ },
828
+ STARK: {
829
+ apiChainName: "starknet",
830
+ blockchainName: "Starknet",
831
+ legacyWalletBookAliases: ["stark"],
832
+ verifiedCredentialChainName: "starknet"
833
+ },
834
+ STELLAR: {
835
+ apiChainName: "stellar",
836
+ blockchainName: "Stellar",
837
+ verifiedCredentialChainName: "stellar"
838
+ },
839
+ SUI: {
840
+ apiChainName: "sui",
841
+ blockchainName: "Sui",
842
+ verifiedCredentialChainName: "sui"
843
+ },
844
+ TEMPO: {
845
+ apiChainName: "tempo",
846
+ blockchainName: "Tempo",
847
+ verifiedCredentialChainName: "tempo"
848
+ },
849
+ TON: {
850
+ apiChainName: "ton",
851
+ blockchainName: "TON",
852
+ verifiedCredentialChainName: "ton"
853
+ },
854
+ TRON: {
855
+ apiChainName: "tron",
856
+ blockchainName: "Tron",
857
+ verifiedCredentialChainName: "tron"
980
858
  }
981
- return context.response;
982
- } });
859
+ };
983
860
 
984
861
  //#endregion
985
- //#region src/modules/apiClient/createApiClient.ts
986
- /**
987
- * Returns a new instance of the SDK API client.
988
- *
989
- * This is not meant for storing, as it is very light we can create it whenever needed.
862
+ //#region src/utils/getChainFromVerifiedCredentialChain/getChainFromVerifiedCredentialChain.ts
863
+ /** @notInstrumented */
864
+ const getChainFromVerifiedCredentialChain = (verifiedCredentialChain) => {
865
+ const chain = Object.keys(CHAINS_INFO_MAP).find((chain$1) => CHAINS_INFO_MAP[chain$1].verifiedCredentialChainName === verifiedCredentialChain);
866
+ assertDefined(chain, `Unknown chain: ${verifiedCredentialChain}`);
867
+ return chain;
868
+ };
869
+
870
+ //#endregion
871
+ //#region src/modules/wallets/utils/normalizeWalletNameWithChain/normalizeWalletNameWithChain.ts
872
+ /**
873
+ * Format the raw wallet name and chain to get the value we can use for
874
+ * verified credentials' `walletName` field.
990
875
  * @notInstrumented
991
876
  */
992
- const createApiClient = (options = {}, client) => {
993
- const core = getCore(client);
994
- const coreState = core.state.get();
995
- const settings = {
996
- basePath: core.apiBaseUrl,
997
- headers: {
998
- "Content-Type": "application/json",
999
- [DYNAMIC_API_VERSION_HEADER]: `API/${DYNAMIC_SDK_API_VERSION}`,
1000
- [DYNAMIC_REQUEST_ID_HEADER]: randomString({ length: 50 }),
1001
- [DYNAMIC_SDK_SESSION_ID_HEADER]: core.sdkSessionId,
1002
- [DYNAMIC_SDK_VERSION_HEADER]: `${CLIENT_SDK_NAME}/${version}`,
1003
- ...core.getApiHeaders(),
1004
- ...options.headers
1005
- }
1006
- };
1007
- if (core.metadata?.universalLink) settings.headers["Origin"] = core.metadata.universalLink;
1008
- if (client.token) settings.headers.Authorization = `Bearer ${client.token}`;
1009
- if (client.projectSettings && isCookieEnabled(client)) settings.credentials = "include";
1010
- if (options.includeMfaToken && coreState.mfaToken) settings.headers[MFA_TOKEN_HEADER] = coreState.mfaToken;
1011
- if (options.elevatedAccessTokenScope) {
1012
- const elevatedToken = __getElevatedAccessToken_wrapped({ scope: options.elevatedAccessTokenScope }, client);
1013
- if (elevatedToken) settings.headers[ELEVATED_ACCESS_TOKEN_HEADER] = elevatedToken;
1014
- }
1015
- const sessionPublicKey = getSessionKeys(client)?.publicKey;
1016
- const isSessionPublicKeyHeaderPresent = settings.headers[SESSION_PUBLIC_KEY_HEADER] !== void 0;
1017
- if (sessionPublicKey && !isSessionPublicKeyHeaderPresent) settings.headers[SESSION_PUBLIC_KEY_HEADER] = sessionPublicKey;
1018
- return new SDKApi(new Configuration({
1019
- ...settings,
1020
- fetchApi: core.fetch,
1021
- middleware: [
1022
- createDeviceSignatureHeadersMiddleware(client),
1023
- createConvertToApiErrorMiddleware({ errorMappers: [...options.errorMappers || [], clientErrorMapper] }),
1024
- createUnauthorizedMiddleware()
1025
- ]
1026
- }));
877
+ const normalizeWalletNameWithChain = ({ displayName, chain }) => {
878
+ const sanitizedWalletName = displayName.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
879
+ const chainLowered = chain.toLocaleLowerCase();
880
+ if (sanitizedWalletName.endsWith(chainLowered)) return sanitizedWalletName;
881
+ return `${sanitizedWalletName}${chainLowered}`;
1027
882
  };
1028
883
 
1029
884
  //#endregion
1030
- //#region src/utils/getNonce/fetchAndStoreNonces/fetchAndStoreNonces.ts
885
+ //#region src/modules/wallets/utils/formatWalletProviderKey/formatWalletProviderKey.ts
1031
886
  /**
1032
- * Fetches a batch of nonces from the API.
1033
- *
1034
- * Prefetching is critical for iOS deep link wallet providers (e.g. Phantom
1035
- * redirect). On iOS, a network request (like fetching a nonce) between a user
1036
- * action and a deeplink call will break the gesture chain, causing iOS to
1037
- * silently ignore the deeplink. By prefetching nonces ahead of time,
1038
- * `getNonce` can return synchronously from cache and the deeplink fires
1039
- * without an interrupting network round-trip.
887
+ * Assembles the wallet provider key from the wallet name, chain, and wallet provider type.
1040
888
  *
1041
- * New nonces are **appended** to the existing pool so that unconsumed entries
1042
- * are not discarded by a background refetch.
889
+ * The suffix is optional and can be used to add a uniquely identifying string to the key, which
890
+ * might be necessary for some wallet providers (like Wallet Connect).
1043
891
  *
1044
- * Concurrent refills are coalesced behind a single in-flight promise so that
1045
- * simultaneous callers share one `GET /nonces` round-trip instead of each
1046
- * issuing their own (which would inflate the pool and hammer the backend).
892
+ * IMPORTANT: Do NOT add a suffix unless absolutely necessary, as it will cause the wallet account
893
+ * to not be able to find its wallet provider when connecting to a new device (it won't be possible
894
+ * to determine the full key just from the Verified Credential data).
1047
895
  * @notInstrumented
1048
896
  */
1049
- const fetchAndStoreNonces = async (client) => {
1050
- const core = getCore(client);
1051
- const inFlight = core.state.get().nonceFetchInFlight;
1052
- if (inFlight) return inFlight;
1053
- const { environmentId } = core;
1054
- const apiClient = createApiClient({}, client);
1055
- const fetchPromise = (async () => {
1056
- const { nonces } = await apiClient.getNonces({
1057
- count: NONCE_POOL_SIZE,
1058
- environmentId
1059
- });
1060
- const existing = core.state.get().prefetchedNonces;
1061
- core.state.set({
1062
- prefetchedNonces: [...existing, ...nonces],
1063
- prefetchedNoncesExpiration: Date.now() + NONCE_POOL_EXPIRATION_TIME
1064
- });
1065
- })().finally(() => {
1066
- core.state.set({ nonceFetchInFlight: null });
897
+ const formatWalletProviderKey = ({ suffix, chain, displayName, walletProviderType }) => {
898
+ return `${normalizeWalletNameWithChain({
899
+ chain,
900
+ displayName
901
+ })}:${walletProviderType}${suffix ? `:${suffix}` : ""}`;
902
+ };
903
+
904
+ //#endregion
905
+ //#region src/modules/wallets/walletProvider/walletProviderKeyMap/getWalletProviderKeyFromVerifiedCredential/getWalletProviderKeyFromVerifiedCredential.ts
906
+ /** @notInstrumented */
907
+ const getWalletProviderKeyFromVerifiedCredential = ({ verifiedCredential }, client) => {
908
+ const { walletProviderKeyMap } = getCore(client).state.get();
909
+ const storedWalletProviderKey = walletProviderKeyMap[verifiedCredential.id];
910
+ if (storedWalletProviderKey) return { walletProviderKey: storedWalletProviderKey };
911
+ /**
912
+ * We fallback to comprising the wallet provider key from walletName and walletProvider.
913
+ *
914
+ * Some wallet provider types (like Wallet Connect) also use a special suffix for their wallet provider
915
+ * keys, so this won't be enough for them.
916
+ * Therefore, for those specific wallet providers, the wallet account will remain without a wallet provider
917
+ * and will require reconnection.
918
+ * Read walletProvider.types.ts for more info.
919
+ */
920
+ assertDefined(verifiedCredential.walletName, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing walletName`);
921
+ assertDefined(verifiedCredential.walletProvider, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing walletProvider`);
922
+ assertDefined(verifiedCredential.chain, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing chain`);
923
+ return { walletProviderKey: formatWalletProviderKey({
924
+ chain: getChainFromVerifiedCredentialChain(verifiedCredential.chain),
925
+ displayName: verifiedCredential.walletName,
926
+ walletProviderType: verifiedCredential.walletProvider
927
+ }) };
928
+ };
929
+
930
+ //#endregion
931
+ //#region src/modules/wallets/utils/convertVerifiedCredentialToWalletAccount/convertVerifiedCredentialToWalletAccount.ts
932
+ /** @notInstrumented */
933
+ const convertVerifiedCredentialToWalletAccount = ({ verifiedCredential }, client) => {
934
+ assertDefined(verifiedCredential.address, "Missing address in verified credential");
935
+ assertDefined(verifiedCredential.chain, "Missing chain in verified credential");
936
+ const chain = getChainFromVerifiedCredentialChain(verifiedCredential.chain);
937
+ const { walletProviderKey } = getWalletProviderKeyFromVerifiedCredential({ verifiedCredential }, client);
938
+ const walletAccountId = formatWalletAccountId({
939
+ address: verifiedCredential.address,
940
+ chain,
941
+ walletProviderKey
1067
942
  });
1068
- core.state.set({ nonceFetchInFlight: fetchPromise });
1069
- return fetchPromise;
943
+ return {
944
+ address: verifiedCredential.address,
945
+ addressesWithTypes: verifiedCredential.walletAdditionalAddresses,
946
+ chain,
947
+ hardwareWalletVendor: verifiedCredential.walletProperties?.hardwareWallet,
948
+ id: walletAccountId,
949
+ lastSelectedAt: verifiedCredential.lastSelectedAt ?? null,
950
+ verifiedCredentialId: verifiedCredential.id,
951
+ walletProviderKey
952
+ };
1070
953
  };
1071
954
 
1072
955
  //#endregion
1073
- //#region src/utils/setCookie/setCookie.ts
1074
- /**
1075
- * Sefelly sets the cookie in the browser.
1076
- * @notInstrumented
1077
- */
1078
- const setCookie = (cookie) => {
1079
- document.cookie = cookie;
956
+ //#region src/modules/wallets/getWalletAccounts/getWalletAccountsFromState/getWalletAccountsFromState.ts
957
+ /** @notInstrumented */
958
+ const getWalletAccountsFromState = ({ unverifiedWalletAccounts, user }, client) => {
959
+ const walletAccountsMap = /* @__PURE__ */ new Map();
960
+ /**
961
+ * Handle the unverified wallet accounts before the user verified credentials
962
+ * so the later verified wallet accounts can override the unverified wallet accounts
963
+ */
964
+ unverifiedWalletAccounts.forEach((unverifiedWalletAccount) => {
965
+ const walletAccount = convertUnverifiedWalletAccountToWalletAccount({ unverifiedWalletAccount });
966
+ walletAccountsMap.set(walletAccount.id, walletAccount);
967
+ });
968
+ (user?.verifiedCredentials ?? []).filter((verified) => verified.format === JwtVerifiedCredentialFormatEnum.Blockchain).forEach((verifiedWalletAccount) => {
969
+ try {
970
+ const walletAccount = convertVerifiedCredentialToWalletAccount({ verifiedCredential: verifiedWalletAccount }, client);
971
+ walletAccountsMap.set(walletAccount.id, walletAccount);
972
+ } catch (error) {
973
+ /**
974
+ * A verified credential can be missing the data required to resolve its
975
+ * wallet provider (e.g. a legacy credential without a walletName). Skip
976
+ * it instead of throwing so a single unconvertible credential does not
977
+ * abort the whole mapping — which, during client initialization, would
978
+ * otherwise fail init and leave the app stuck loading. The wallet stays
979
+ * absent until the user reconnects it.
980
+ */
981
+ getCore(client).logger.debug(`Skipping verified credential ${verifiedWalletAccount.id}: unable to convert to wallet account`, error);
982
+ }
983
+ });
984
+ return Array.from(walletAccountsMap.values());
1080
985
  };
1081
986
 
1082
987
  //#endregion
1083
- //#region src/modules/clientEvents/clientEvents.ts
988
+ //#region src/modules/wallets/getWalletAccounts/getWalletAccounts.ts
1084
989
  /**
1085
- * Adds an event listener for Dynamic client events.
990
+ * Retrieves all wallet accounts associated with the current session.
1086
991
  *
1087
- * This function allows you to listen for various events emitted by the Dynamic client,
1088
- * such as authentication state changes, wallet connections, and more.
992
+ * This function returns both verified and unverified wallet accounts,
993
+ * combining data from user credentials and local unverified accounts.
994
+ * You can differentiate between verified and unverified wallet accounts by
995
+ * checking the `verifiedCredentialId` property.
1089
996
  *
1090
- * @param params.event - The event name to listen for.
1091
- * @param params.listener - The callback function to execute when the event is fired.
1092
997
  * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
1093
- * @returns A function that can be called to remove the listener.
998
+ * @returns An array of wallet accounts associated with the session.
1094
999
  * @notInstrumented
1095
- * @hook Event listeners are error-prone to wire up by hand with useEffect.
1000
+ * @hook Exposed as a hook so consumers get the reactive list of wallet accounts.
1096
1001
  */
1097
- const onEvent = ({ event, listener }, client = getDefaultClient()) => {
1098
- const { eventEmitter } = getCore(client);
1099
- eventEmitter.on(event, listener);
1100
- return () => {
1101
- eventEmitter.off(event, listener);
1102
- };
1002
+ const getWalletAccounts = (client = getDefaultClient()) => {
1003
+ const { unverifiedWalletAccounts, user } = getCore(client).state.get();
1004
+ return getWalletAccountsFromState({
1005
+ unverifiedWalletAccounts,
1006
+ user
1007
+ }, client);
1008
+ };
1009
+
1010
+ //#endregion
1011
+ //#region src/errors/NoWalletProviderFoundError.ts
1012
+ var NoWalletProviderFoundError = class extends BaseError {
1013
+ constructor({ walletProviderKey }) {
1014
+ super({
1015
+ cause: null,
1016
+ code: "no_wallet_provider_found_error",
1017
+ docsUrl: null,
1018
+ name: "NoWalletProviderFoundError",
1019
+ shortMessage: `No wallet provider found with key: ${walletProviderKey}`
1020
+ });
1021
+ }
1103
1022
  };
1023
+
1024
+ //#endregion
1025
+ //#region src/services/runtimeServices/createRuntimeServiceAccessKey/createRuntimeServiceAccessKey.ts
1104
1026
  /**
1105
- * Removes an event listener from Dynamic client events.
1106
- *
1107
- * This function unsubscribes a previously registered event listener
1108
- * from the specified Dynamic client event.
1027
+ * Creates a service accessor function that manages service instantiation and caching.
1028
+ * The returned function will either retrieve an existing service from the registry or
1029
+ * create a new one using the provided builder function.
1109
1030
  *
1110
- * @param params.event - The event name to remove the listener from.
1111
- * @param params.listener - The callback function to remove.
1112
- * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
1031
+ * @template - The type of service to be created/accessed
1032
+ * @param key - Unique identifier for the service in the registry
1033
+ * @param builder - Function that creates the service instance when called with a DynamicClient
1113
1034
  * @notInstrumented
1114
1035
  */
1115
- const offEvent = ({ event, listener }, client = getDefaultClient()) => {
1116
- const { eventEmitter } = getCore(client);
1117
- eventEmitter.off(event, listener);
1036
+ const createRuntimeServiceAccessKey = (key, builder) => (client) => {
1037
+ const { runtimeServices } = getCore(client);
1038
+ const currentService = runtimeServices.getByKey(key);
1039
+ if (currentService) return currentService;
1040
+ const service = builder(client);
1041
+ runtimeServices.register(key, service);
1042
+ return service;
1118
1043
  };
1119
- /**
1120
- * Adds a one-time event listener for Dynamic client events.
1044
+
1045
+ //#endregion
1046
+ //#region src/modules/wallets/walletProviderRegistry/createWalletProviderRegistry/createWalletProviderRegistry.ts
1047
+ /**
1048
+ * Creates a new wallet provider registry that manages wallet providers with priority-based registration.
1121
1049
  *
1122
- * This function listens for an event that will automatically remove itself
1123
- * after being triggered once.
1050
+ * @returns The wallet provider registry instance
1124
1051
  *
1125
- * @param params.event - The event name to listen for.
1126
- * @param params.listener - The callback function to execute when the event is fired.
1127
- * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
1128
- * @returns A function that can be called to remove the listener before it fires.
1052
+ * @example
1053
+ * ```typescript
1054
+ * const registry = createWalletProviderRegistry();
1055
+ *
1056
+ * registry.register({
1057
+ * priority: WalletProviderPriority.WALLET_SDK,
1058
+ * walletProvider: myWalletProvider
1059
+ * });
1060
+ *
1061
+ * const provider = registry.getByKey('my-wallet-key');
1062
+ * const providers = registry.listProviders();
1063
+ * ```
1129
1064
  * @notInstrumented
1130
- * @hook Event listeners are error-prone to wire up by hand with useEffect.
1131
1065
  */
1132
- const onceEvent = ({ event, listener }, client = getDefaultClient()) => {
1133
- const { eventEmitter } = getCore(client);
1134
- eventEmitter.once(event, listener);
1135
- return () => {
1136
- eventEmitter.off(event, listener);
1066
+ const createWalletProviderRegistry = (client) => {
1067
+ const registry = /* @__PURE__ */ new Map();
1068
+ return {
1069
+ getByKey: (key) => registry.get(key)?.walletProvider,
1070
+ listProviderEntries: () => Array.from(registry.values()),
1071
+ listProviders: () => Array.from(registry.values()).map((v) => v.walletProvider),
1072
+ register: (args) => {
1073
+ const existingEntry = registry.get(args.walletProvider.key);
1074
+ if (existingEntry) {
1075
+ if (existingEntry.priority < args.priority) {
1076
+ registry.set(args.walletProvider.key, args);
1077
+ emitEvent({
1078
+ args: { walletProviderKey: args.walletProvider.key },
1079
+ event: "walletProviderChanged"
1080
+ }, client);
1081
+ }
1082
+ } else {
1083
+ registry.set(args.walletProvider.key, args);
1084
+ emitEvent({
1085
+ args: { walletProvider: args.walletProvider },
1086
+ event: "walletProviderRegistered"
1087
+ }, client);
1088
+ emitEvent({
1089
+ args: { walletProviderKey: args.walletProvider.key },
1090
+ event: "walletProviderChanged"
1091
+ }, client);
1092
+ }
1093
+ },
1094
+ unregister: (key) => {
1095
+ registry.delete(key);
1096
+ emitEvent({
1097
+ args: { walletProviderKey: key },
1098
+ event: "walletProviderUnregistered"
1099
+ }, client);
1100
+ }
1137
1101
  };
1138
1102
  };
1103
+
1104
+ //#endregion
1105
+ //#region src/modules/wallets/walletProviderRegistry/getWalletProviderRegistry/getWalletProviderRegistry.ts
1139
1106
  /**
1140
- * Emits a Dynamic client event.
1107
+ * This function provides access to a shared instance of the wallet provider registry.
1141
1108
  *
1142
- * This function triggers an event that will be received by all registered
1143
- * listeners for the specified event type.
1109
+ * It ensures that the same registry instance is used throughout the client to maintaining
1110
+ * consistency of registered wallet providers across different parts of the codebase.
1144
1111
  *
1145
- * @param params.event - The event name to emit.
1146
- * @param params.args - The arguments to pass to event listeners.
1147
- * @param client - The Dynamic client instance.
1148
- * @notInstrumented
1112
+ * @returns The wallet provider registry instance
1113
+ *
1114
+ * @example
1115
+ * ```typescript
1116
+ * // Get the registry instance
1117
+ * const registry = getWalletProviderRegistry();
1118
+ *
1119
+ * // Register a wallet provider
1120
+ * registry.register({
1121
+ * priority: WalletProviderPriority.WALLET_SDK,
1122
+ * walletProvider: myWalletProvider
1123
+ * });
1124
+ *
1125
+ * // Retrieve a specific provider
1126
+ * const provider = registry.getByKey('metamaskevm');
1127
+ * ```
1149
1128
  */
1150
- const emitEvent = ({ event, args }, client) => {
1151
- const { eventEmitter } = getCore(client);
1152
- eventEmitter.emit(event, args);
1153
- };
1129
+ const getWalletProviderRegistry = createRuntimeServiceAccessKey("walletProviderRegistry", (client) => createWalletProviderRegistry(client));
1154
1130
 
1155
1131
  //#endregion
1156
- //#region src/errors/InvalidWalletProviderKeyError.ts
1157
- var InvalidWalletProviderKeyError = class extends BaseError {
1158
- constructor(value) {
1159
- super({
1160
- cause: null,
1161
- code: "invalid_wallet_provider_key",
1162
- docsUrl: null,
1163
- name: "InvalidWalletProviderKeyError",
1164
- shortMessage: `Invalid wallet provider key: ${value}. Key must be in the format of <normalizedWalletNameWithChain>:<walletProviderType>[:<suffix>]`
1165
- });
1166
- }
1167
- };
1132
+ //#region src/modules/wallets/walletProviderRegistry/walletProviderRegistry.types.ts
1133
+ let WalletProviderPriority = /* @__PURE__ */ function(WalletProviderPriority$1) {
1134
+ /**
1135
+ * Highest priority should be used by wallet providers that implement
1136
+ * the most reliable wallet integration.
1137
+ * example: The SDK provided by the wallet provider.
1138
+ */
1139
+ WalletProviderPriority$1[WalletProviderPriority$1["WALLET_SDK"] = 100] = "WALLET_SDK";
1140
+ /**
1141
+ * Medium priority should be used by wallet providers that implement
1142
+ * a wallet integration via some reliable standard.
1143
+ * example: A wallet provider that uses EIP6963 announcement.
1144
+ */
1145
+ WalletProviderPriority$1[WalletProviderPriority$1["WALLET_SELF_ANNOUNCEMENT_STANDARD"] = 50] = "WALLET_SELF_ANNOUNCEMENT_STANDARD";
1146
+ /**
1147
+ * Low priority should be used by wallet providers that implement
1148
+ * a wallet integration on a less reliable standard.
1149
+ * example: A wallet provider that uses window.ethereum, where the
1150
+ * window key can be overridden by other extensions.
1151
+ */
1152
+ WalletProviderPriority$1[WalletProviderPriority$1["WINDOW_INJECT"] = 20] = "WINDOW_INJECT";
1153
+ return WalletProviderPriority$1;
1154
+ }({});
1168
1155
 
1169
1156
  //#endregion
1170
- //#region src/modules/wallets/walletProvider/splitWalletProviderKey/splitWalletProviderKey.ts
1157
+ //#region src/modules/wallets/utils/getWalletProviderFromWalletAccount/getWalletProviderFromWalletAccount.ts
1171
1158
  /** @notInstrumented */
1172
- const splitWalletProviderKey = (walletProviderKey) => {
1173
- const [normalizedWalletNameWithChain, walletProviderType, suffix, ...rest] = walletProviderKey.split(":");
1174
- if (!normalizedWalletNameWithChain || !Object.values(WalletProviderEnum).includes(walletProviderType) || rest.length > 0) throw new InvalidWalletProviderKeyError(walletProviderKey);
1175
- return {
1176
- normalizedWalletNameWithChain,
1177
- suffix,
1178
- walletProviderType
1179
- };
1159
+ const getWalletProviderFromWalletAccount = ({ walletAccount }, client) => {
1160
+ const walletProvider = getWalletProviderRegistry(client).getByKey(walletAccount.walletProviderKey);
1161
+ if (!walletProvider) throw new NoWalletProviderFoundError({ walletProviderKey: walletAccount.walletProviderKey });
1162
+ return walletProvider;
1180
1163
  };
1181
1164
 
1182
1165
  //#endregion
1183
- //#region src/modules/wallets/utils/normalizeAddress/normalizeAddress.ts
1184
- /** @notInstrumented */
1185
- const normalizeAddress = ({ address, chain }) => {
1186
- let normalizedAddress = address;
1187
- if (normalizedAddress?.startsWith("0x")) normalizedAddress = normalizedAddress.slice(2);
1188
- normalizedAddress = ["EVM", "FLOW"].includes(chain) ? normalizedAddress?.toLowerCase() : normalizedAddress;
1189
- return normalizedAddress;
1166
+ //#region src/modules/auth/consts.ts
1167
+ const DYNAMIC_AUTH_COOKIE_NAME = "DYNAMIC_JWT_TOKEN";
1168
+
1169
+ //#endregion
1170
+ //#region src/modules/auth/extractSessionId/extractSessionId.ts
1171
+ /**
1172
+ * Extracts the session ID (`sid` claim) from a JWT token string.
1173
+ *
1174
+ * Decodes the base64url-encoded payload segment of the token and reads the
1175
+ * `sid` field. This is used to correlate instrumentation events with the
1176
+ * authenticated session, without ever forwarding the full token.
1177
+ *
1178
+ * Returns `null` when no token is present, when the token is malformed, or
1179
+ * when the payload does not contain a `sid` claim.
1180
+ * @notInstrumented
1181
+ */
1182
+ const extractSessionId = (token) => {
1183
+ try {
1184
+ const payload = token.split(".")[1];
1185
+ return JSON.parse(atob(payload)).sid ?? null;
1186
+ } catch {
1187
+ return null;
1188
+ }
1190
1189
  };
1191
1190
 
1192
1191
  //#endregion
1193
- //#region src/modules/wallets/utils/formatWalletAccountId/formatWalletAccountId.ts
1194
- /** @notInstrumented */
1195
- const formatWalletAccountId = ({ address, chain, walletProviderKey }) => {
1196
- const { normalizedWalletNameWithChain } = splitWalletProviderKey(walletProviderKey);
1197
- return `${normalizedWalletNameWithChain}:${normalizeAddress({
1198
- address,
1199
- chain
1200
- })}`;
1192
+ //#region src/utils/getOperatingSystem/getOperatingSystem.ts
1193
+ /**
1194
+ * Detects the operating system from `navigator.userAgent` for web environments.
1195
+ *
1196
+ * The React Native bundler (Metro) resolves `getOperatingSystem.native.ts`
1197
+ * instead when building for React Native, which uses `Platform.OS` directly.
1198
+ *
1199
+ * Falls back to `'unknown'` when `navigator` is unavailable (SSR, service
1200
+ * workers) or when the user-agent string doesn't match a known OS pattern.
1201
+ * @notInstrumented
1202
+ */
1203
+ const getOperatingSystem = () => {
1204
+ try {
1205
+ const ua = navigator.userAgent;
1206
+ if (/iPhone|iPad|iPod/.test(ua)) return "ios";
1207
+ if (/Android/.test(ua)) return "android";
1208
+ if (/Windows/.test(ua)) return "windows";
1209
+ if (/Macintosh|Mac OS X/.test(ua)) return "macos";
1210
+ if (/Linux/.test(ua)) return "linux";
1211
+ return "unknown";
1212
+ } catch {
1213
+ return "unknown";
1214
+ }
1201
1215
  };
1202
1216
 
1203
1217
  //#endregion
1204
- //#region src/modules/wallets/utils/convertUnverifiedWalletAccountToWalletAccount/convertUnverifiedWalletAccountToWalletAccount.ts
1205
- /** @notInstrumented */
1206
- const convertUnverifiedWalletAccountToWalletAccount = ({ unverifiedWalletAccount }) => ({
1207
- address: unverifiedWalletAccount.address,
1208
- addressesWithTypes: unverifiedWalletAccount.addressesWithTypes,
1209
- chain: unverifiedWalletAccount.chain,
1210
- id: formatWalletAccountId({
1211
- address: unverifiedWalletAccount.address,
1212
- chain: unverifiedWalletAccount.chain,
1213
- walletProviderKey: unverifiedWalletAccount.walletProviderKey
1214
- }),
1215
- lastSelectedAt: unverifiedWalletAccount.lastSelectedAt,
1216
- verifiedCredentialId: null,
1217
- walletProviderKey: unverifiedWalletAccount.walletProviderKey
1218
- });
1218
+ //#region src/utils/getPlatform/getPlatform.ts
1219
+ /**
1220
+ * Returns the platform the SDK is running on.
1221
+ *
1222
+ * This is the web implementation — the React Native bundler resolves
1223
+ * `getPlatform.native.ts` instead when building for React Native, which
1224
+ * returns `'react-native'`.
1225
+ * @notInstrumented
1226
+ */
1227
+ const getPlatform = () => "web";
1219
1228
 
1220
1229
  //#endregion
1221
- //#region src/modules/wallets/constants.ts
1222
- const CHAINS_INFO_MAP = {
1223
- ALEO: {
1224
- apiChainName: "aleo",
1225
- blockchainName: "Aleo",
1226
- verifiedCredentialChainName: "aleo"
1227
- },
1228
- ALGO: {
1229
- apiChainName: "algo",
1230
- blockchainName: "Algorand",
1231
- verifiedCredentialChainName: "algorand"
1232
- },
1233
- APTOS: {
1234
- apiChainName: "aptos",
1235
- blockchainName: "Aptos",
1236
- verifiedCredentialChainName: "aptos"
1237
- },
1238
- BTC: {
1239
- apiChainName: "bitcoin",
1240
- blockchainName: "Bitcoin",
1241
- legacyWalletBookAliases: ["btc"],
1242
- verifiedCredentialChainName: "bip122"
1243
- },
1244
- COSMOS: {
1245
- apiChainName: "cosmos",
1246
- blockchainName: "Cosmos",
1247
- verifiedCredentialChainName: "cosmos"
1248
- },
1249
- ECLIPSE: {
1250
- apiChainName: "eclipse",
1251
- blockchainName: "Eclipse",
1252
- verifiedCredentialChainName: "eclipse"
1253
- },
1254
- EVM: {
1255
- apiChainName: "evm",
1256
- blockchainName: "Ethereum",
1257
- verifiedCredentialChainName: "eip155"
1258
- },
1259
- FLOW: {
1260
- apiChainName: "flow",
1261
- blockchainName: "Flow",
1262
- verifiedCredentialChainName: "flow"
1263
- },
1264
- MIDNIGHT: {
1265
- apiChainName: "midnight",
1266
- blockchainName: "Midnight",
1267
- verifiedCredentialChainName: "midnight"
1268
- },
1269
- SOL: {
1270
- apiChainName: "solana",
1271
- blockchainName: "Solana",
1272
- legacyWalletBookAliases: ["sol"],
1273
- verifiedCredentialChainName: "solana",
1274
- waasChainNameOverride: "SVM"
1275
- },
1276
- SPARK: {
1277
- apiChainName: "spark",
1278
- blockchainName: "Spark",
1279
- verifiedCredentialChainName: "spark"
1280
- },
1281
- STARK: {
1282
- apiChainName: "starknet",
1283
- blockchainName: "Starknet",
1284
- legacyWalletBookAliases: ["stark"],
1285
- verifiedCredentialChainName: "starknet"
1286
- },
1287
- STELLAR: {
1288
- apiChainName: "stellar",
1289
- blockchainName: "Stellar",
1290
- verifiedCredentialChainName: "stellar"
1291
- },
1292
- SUI: {
1293
- apiChainName: "sui",
1294
- blockchainName: "Sui",
1295
- verifiedCredentialChainName: "sui"
1296
- },
1297
- TEMPO: {
1298
- apiChainName: "tempo",
1299
- blockchainName: "Tempo",
1300
- verifiedCredentialChainName: "tempo"
1301
- },
1302
- TON: {
1303
- apiChainName: "ton",
1304
- blockchainName: "TON",
1305
- verifiedCredentialChainName: "ton"
1306
- },
1307
- TRON: {
1308
- apiChainName: "tron",
1309
- blockchainName: "Tron",
1310
- verifiedCredentialChainName: "tron"
1311
- }
1312
- };
1313
-
1314
- //#endregion
1315
- //#region src/utils/getChainFromVerifiedCredentialChain/getChainFromVerifiedCredentialChain.ts
1316
- /** @notInstrumented */
1317
- const getChainFromVerifiedCredentialChain = (verifiedCredentialChain) => {
1318
- const chain = Object.keys(CHAINS_INFO_MAP).find((chain$1) => CHAINS_INFO_MAP[chain$1].verifiedCredentialChainName === verifiedCredentialChain);
1319
- assertDefined(chain, `Unknown chain: ${verifiedCredentialChain}`);
1320
- return chain;
1321
- };
1322
-
1323
- //#endregion
1324
- //#region src/modules/wallets/utils/normalizeWalletNameWithChain/normalizeWalletNameWithChain.ts
1230
+ //#region src/utils/getUserAgent/getUserAgent.ts
1325
1231
  /**
1326
- * Format the raw wallet name and chain to get the value we can use for
1327
- * verified credentials' `walletName` field.
1232
+ * Returns the browser's `navigator.userAgent` string for inclusion in
1233
+ * instrumentation events.
1234
+ *
1235
+ * Falls back to an empty string in environments where `navigator` is not
1236
+ * available (e.g., Node.js SSR, service workers) so that callers never need
1237
+ * to guard against undefined.
1328
1238
  * @notInstrumented
1329
1239
  */
1330
- const normalizeWalletNameWithChain = ({ displayName, chain }) => {
1331
- const sanitizedWalletName = displayName.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
1332
- const chainLowered = chain.toLocaleLowerCase();
1333
- if (sanitizedWalletName.endsWith(chainLowered)) return sanitizedWalletName;
1334
- return `${sanitizedWalletName}${chainLowered}`;
1240
+ const getUserAgent = () => {
1241
+ try {
1242
+ return navigator.userAgent;
1243
+ } catch {
1244
+ return "";
1245
+ }
1335
1246
  };
1336
1247
 
1337
1248
  //#endregion
1338
- //#region src/modules/wallets/utils/formatWalletProviderKey/formatWalletProviderKey.ts
1249
+ //#region src/services/instrumentation/constants.ts
1339
1250
  /**
1340
- * Assembles the wallet provider key from the wallet name, chain, and wallet provider type.
1251
+ * Default key-based PII scrubber list, lowercased and matched against the
1252
+ * lowercased object key.
1341
1253
  *
1342
- * The suffix is optional and can be used to add a uniquely identifying string to the key, which
1343
- * might be necessary for some wallet providers (like Wallet Connect).
1254
+ * Two complementary mechanisms cover credential leakage:
1344
1255
  *
1345
- * IMPORTANT: Do NOT add a suffix unless absolutely necessary, as it will cause the wallet account
1346
- * to not be able to find its wallet provider when connecting to a new device (it won't be possible
1347
- * to determine the full key just from the Verified Credential data).
1348
- * @notInstrumented
1256
+ * 1. Explicit keys listed here matched exactly (case-insensitive). Use this
1257
+ * for keys that don't end in a generic credential suffix (e.g. `mnemonic`,
1258
+ * `seed`, `verificationCode`, `keyShare`).
1259
+ * 2. Suffix matching in {@link scrubParameters} for the credential suffixes
1260
+ * enumerated by {@link PII_KEY_SUFFIXES} — catches any field whose name
1261
+ * ends in `token` or `jwt` (e.g. `mfaToken`, `minifiedJwt`, `accessToken`,
1262
+ * `idToken`, `captchaToken`, …) without needing to enumerate them here.
1349
1263
  */
1350
- const formatWalletProviderKey = ({ suffix, chain, displayName, walletProviderType }) => {
1351
- return `${normalizeWalletNameWithChain({
1352
- chain,
1353
- displayName
1354
- })}:${walletProviderType}${suffix ? `:${suffix}` : ""}`;
1355
- };
1264
+ const DEFAULT_PII_FIELDS = [
1265
+ "password",
1266
+ "token",
1267
+ "secret",
1268
+ "privateKey",
1269
+ "privateJwk",
1270
+ "mnemonic",
1271
+ "seed",
1272
+ "keyShare",
1273
+ "keyShares",
1274
+ "email",
1275
+ "phone",
1276
+ "ssn",
1277
+ "address",
1278
+ "authorization",
1279
+ "verificationCode",
1280
+ "recoveryCodes",
1281
+ "codeVerifier",
1282
+ "pkce",
1283
+ "bearer",
1284
+ "apiKey",
1285
+ "alias",
1286
+ "publicIdentifier"
1287
+ ];
1288
+ /**
1289
+ * Lowercased suffixes used by {@link scrubParameters} to match credential-like
1290
+ * keys without enumerating every variant (e.g. `mfaToken`, `accessToken`,
1291
+ * `refreshToken`, `legacyToken`, `minifiedJwt`). Only suffixes that are
1292
+ * unambiguously credentials belong here — `key` (matches `publicKey`) and
1293
+ * `code` (matches `countryCode`) are too broad and must not be added.
1294
+ */
1295
+ const PII_KEY_SUFFIXES = ["token", "jwt"];
1296
+ const REDACTED_VALUE = "[REDACTED]";
1297
+ const MAX_STRING_LENGTH = 500;
1298
+ const TRUNCATED_SUFFIX = "...[truncated]";
1299
+ /**
1300
+ * Label prefix for binary data placeholders. The full placeholder includes the
1301
+ * byte length, e.g. `[Binary:16 bytes]`, so consumers can gauge data size
1302
+ * without the actual bytes appearing in logs.
1303
+ */
1304
+ const BINARY_LABEL = "Binary";
1305
+ /**
1306
+ * Placeholder emitted when a circular reference is detected in the value tree.
1307
+ * Using a named constant makes it easy to search for or filter in downstream
1308
+ * log tooling.
1309
+ */
1310
+ const CIRCULAR_VALUE = "[Circular]";
1356
1311
 
1357
1312
  //#endregion
1358
- //#region src/modules/wallets/walletProvider/walletProviderKeyMap/getWalletProviderKeyFromVerifiedCredential/getWalletProviderKeyFromVerifiedCredential.ts
1359
- /** @notInstrumented */
1360
- const getWalletProviderKeyFromVerifiedCredential = ({ verifiedCredential }, client) => {
1361
- const { walletProviderKeyMap } = getCore(client).state.get();
1362
- const storedWalletProviderKey = walletProviderKeyMap[verifiedCredential.id];
1363
- if (storedWalletProviderKey) return { walletProviderKey: storedWalletProviderKey };
1364
- /**
1365
- * We fallback to comprising the wallet provider key from walletName and walletProvider.
1366
- *
1367
- * Some wallet provider types (like Wallet Connect) also use a special suffix for their wallet provider
1368
- * keys, so this won't be enough for them.
1369
- * Therefore, for those specific wallet providers, the wallet account will remain without a wallet provider
1370
- * and will require reconnection.
1371
- * Read walletProvider.types.ts for more info.
1372
- */
1373
- assertDefined(verifiedCredential.walletName, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing walletName`);
1374
- assertDefined(verifiedCredential.walletProvider, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing walletProvider`);
1375
- assertDefined(verifiedCredential.chain, `Failed to get wallet provider for verified credential with ID ${verifiedCredential.id}: missing chain`);
1376
- return { walletProviderKey: formatWalletProviderKey({
1377
- chain: getChainFromVerifiedCredentialChain(verifiedCredential.chain),
1378
- displayName: verifiedCredential.walletName,
1379
- walletProviderType: verifiedCredential.walletProvider
1380
- }) };
1313
+ //#region src/services/instrumentation/scrubParameters/scrubParameters.ts
1314
+ /**
1315
+ * Returns true when `key` should be redacted based on the configured PII
1316
+ * field list. A key matches when its lowercased form either equals one of
1317
+ * `piiFieldsLower` exactly, or ends in one of the curated credential
1318
+ * suffixes in {@link PII_KEY_SUFFIXES} (currently `token` and `jwt`). The
1319
+ * suffix check is what catches `mfaToken`, `minifiedJwt`, `accessToken`,
1320
+ * etc. without needing every variant enumerated explicitly.
1321
+ */
1322
+ const keyMatchesPiiField = ({ keyLower, piiFieldsLower }) => {
1323
+ if (piiFieldsLower.includes(keyLower)) return true;
1324
+ return PII_KEY_SUFFIXES.some((suffix) => keyLower.endsWith(suffix));
1381
1325
  };
1382
-
1383
- //#endregion
1384
- //#region src/modules/wallets/utils/convertVerifiedCredentialToWalletAccount/convertVerifiedCredentialToWalletAccount.ts
1385
- /** @notInstrumented */
1386
- const convertVerifiedCredentialToWalletAccount = ({ verifiedCredential }, client) => {
1387
- assertDefined(verifiedCredential.address, "Missing address in verified credential");
1388
- assertDefined(verifiedCredential.chain, "Missing chain in verified credential");
1389
- const chain = getChainFromVerifiedCredentialChain(verifiedCredential.chain);
1390
- const { walletProviderKey } = getWalletProviderKeyFromVerifiedCredential({ verifiedCredential }, client);
1391
- const walletAccountId = formatWalletAccountId({
1392
- address: verifiedCredential.address,
1393
- chain,
1394
- walletProviderKey
1326
+ const scrubString = ({ key, piiFieldsLower, redactAll, value }) => {
1327
+ if (keyMatchesPiiField({
1328
+ keyLower: key.toLowerCase(),
1329
+ piiFieldsLower
1330
+ })) return REDACTED_VALUE;
1331
+ if (redactAll) return REDACTED_VALUE;
1332
+ if (value.length > MAX_STRING_LENGTH) return value.slice(0, MAX_STRING_LENGTH) + TRUNCATED_SUFFIX;
1333
+ return value;
1334
+ };
1335
+ const scrubArray = ({ context, value }) => {
1336
+ if (context.visited.has(value)) return [CIRCULAR_VALUE];
1337
+ context.visited.add(value);
1338
+ const result = value.map((item, index) => scrubValue({
1339
+ context,
1340
+ key: String(index),
1341
+ value: item
1342
+ }));
1343
+ context.visited.delete(value);
1344
+ return result;
1345
+ };
1346
+ const scrubObject = ({ context, value }) => {
1347
+ if (context.visited.has(value)) return { [CIRCULAR_VALUE]: true };
1348
+ context.visited.add(value);
1349
+ const result = Object.create(null);
1350
+ for (const key of Object.keys(value)) result[key] = scrubValue({
1351
+ context,
1352
+ key,
1353
+ value: value[key]
1395
1354
  });
1396
- return {
1397
- address: verifiedCredential.address,
1398
- addressesWithTypes: verifiedCredential.walletAdditionalAddresses,
1399
- chain,
1400
- hardwareWalletVendor: verifiedCredential.walletProperties?.hardwareWallet,
1401
- id: walletAccountId,
1402
- lastSelectedAt: verifiedCredential.lastSelectedAt ?? null,
1403
- verifiedCredentialId: verifiedCredential.id,
1404
- walletProviderKey
1405
- };
1355
+ context.visited.delete(value);
1356
+ return result;
1406
1357
  };
1407
-
1408
- //#endregion
1409
- //#region src/modules/wallets/getWalletAccounts/getWalletAccountsFromState/getWalletAccountsFromState.ts
1410
- /** @notInstrumented */
1411
- const getWalletAccountsFromState = ({ unverifiedWalletAccounts, user }, client) => {
1412
- const walletAccountsMap = /* @__PURE__ */ new Map();
1413
- /**
1414
- * Handle the unverified wallet accounts before the user verified credentials
1415
- * so the later verified wallet accounts can override the unverified wallet accounts
1416
- */
1417
- unverifiedWalletAccounts.forEach((unverifiedWalletAccount) => {
1418
- const walletAccount = convertUnverifiedWalletAccountToWalletAccount({ unverifiedWalletAccount });
1419
- walletAccountsMap.set(walletAccount.id, walletAccount);
1358
+ const scrubValue = ({ context, key, value }) => {
1359
+ if (value === null || value === void 0) return value;
1360
+ if (value instanceof Uint8Array) return `[${BINARY_LABEL}:${value.byteLength} bytes]`;
1361
+ if (keyMatchesPiiField({
1362
+ keyLower: key.toLowerCase(),
1363
+ piiFieldsLower: context.piiFieldsLower
1364
+ })) return REDACTED_VALUE;
1365
+ if (typeof value === "string") return scrubString({
1366
+ key,
1367
+ piiFieldsLower: context.piiFieldsLower,
1368
+ redactAll: context.redactAll,
1369
+ value
1420
1370
  });
1421
- (user?.verifiedCredentials ?? []).filter((verified) => verified.format === JwtVerifiedCredentialFormatEnum.Blockchain).forEach((verifiedWalletAccount) => {
1422
- try {
1423
- const walletAccount = convertVerifiedCredentialToWalletAccount({ verifiedCredential: verifiedWalletAccount }, client);
1424
- walletAccountsMap.set(walletAccount.id, walletAccount);
1425
- } catch (error) {
1426
- /**
1427
- * A verified credential can be missing the data required to resolve its
1428
- * wallet provider (e.g. a legacy credential without a walletName). Skip
1429
- * it instead of throwing so a single unconvertible credential does not
1430
- * abort the whole mapping — which, during client initialization, would
1431
- * otherwise fail init and leave the app stuck loading. The wallet stays
1432
- * absent until the user reconnects it.
1433
- */
1434
- getCore(client).logger.debug(`Skipping verified credential ${verifiedWalletAccount.id}: unable to convert to wallet account`, error);
1435
- }
1371
+ if (Array.isArray(value)) return scrubArray({
1372
+ context,
1373
+ value
1436
1374
  });
1437
- return Array.from(walletAccountsMap.values());
1438
- };
1439
-
1440
- //#endregion
1441
- //#region src/modules/wallets/getWalletAccounts/getWalletAccounts.ts
1442
- /**
1443
- * Retrieves all wallet accounts associated with the current session.
1444
- *
1445
- * This function returns both verified and unverified wallet accounts,
1446
- * combining data from user credentials and local unverified accounts.
1447
- * You can differentiate between verified and unverified wallet accounts by
1448
- * checking the `verifiedCredentialId` property.
1449
- *
1450
- * @param [client] - The Dynamic client instance. Only required when using multiple Dynamic clients.
1451
- * @returns An array of wallet accounts associated with the session.
1452
- * @notInstrumented
1453
- * @hook Exposed as a hook so consumers get the reactive list of wallet accounts.
1454
- */
1455
- const getWalletAccounts = (client = getDefaultClient()) => {
1456
- const { unverifiedWalletAccounts, user } = getCore(client).state.get();
1457
- return getWalletAccountsFromState({
1458
- unverifiedWalletAccounts,
1459
- user
1460
- }, client);
1461
- };
1462
-
1463
- //#endregion
1464
- //#region src/errors/NoWalletProviderFoundError.ts
1465
- var NoWalletProviderFoundError = class extends BaseError {
1466
- constructor({ walletProviderKey }) {
1467
- super({
1468
- cause: null,
1469
- code: "no_wallet_provider_found_error",
1470
- docsUrl: null,
1471
- name: "NoWalletProviderFoundError",
1472
- shortMessage: `No wallet provider found with key: ${walletProviderKey}`
1375
+ if (value instanceof Set) {
1376
+ if (context.visited.has(value)) return [CIRCULAR_VALUE];
1377
+ context.visited.add(value);
1378
+ const setResult = scrubArray({
1379
+ context,
1380
+ value: [...value]
1381
+ });
1382
+ context.visited.delete(value);
1383
+ return setResult;
1384
+ }
1385
+ if (value instanceof Map) {
1386
+ if (context.visited.has(value)) return { [CIRCULAR_VALUE]: true };
1387
+ context.visited.add(value);
1388
+ const asObject = Object.create(null);
1389
+ for (const [k, v] of value.entries()) asObject[String(k)] = v;
1390
+ const mapResult = scrubObject({
1391
+ context,
1392
+ value: asObject
1473
1393
  });
1394
+ context.visited.delete(value);
1395
+ return mapResult;
1474
1396
  }
1397
+ if (value instanceof WeakMap) return "[WeakMap]";
1398
+ if (value instanceof WeakSet) return "[WeakSet]";
1399
+ if (value instanceof Date) return value.toISOString();
1400
+ if (value instanceof Error) return `[Error: ${value.message}]`;
1401
+ if (typeof value === "object") return scrubObject({
1402
+ context,
1403
+ value
1404
+ });
1405
+ return value;
1406
+ };
1407
+ /** @notInstrumented */
1408
+ const scrubParameters = ({ piiFields, redactAll, value }) => {
1409
+ return scrubObject({
1410
+ context: {
1411
+ piiFieldsLower: piiFields.map((field) => field.toLowerCase()),
1412
+ redactAll,
1413
+ visited: /* @__PURE__ */ new WeakSet()
1414
+ },
1415
+ value
1416
+ });
1475
1417
  };
1476
1418
 
1477
1419
  //#endregion
1478
- //#region src/services/runtimeServices/createRuntimeServiceAccessKey/createRuntimeServiceAccessKey.ts
1420
+ //#region src/services/instrumentation/instrumentFunction/extractParams/extractParams.ts
1479
1421
  /**
1480
- * Creates a service accessor function that manages service instantiation and caching.
1481
- * The returned function will either retrieve an existing service from the registry or
1482
- * create a new one using the provided builder function.
1422
+ * Normalises the raw positional arguments of an instrumented function call
1423
+ * into a flat `Record<string, unknown>` suitable for structured logging.
1483
1424
  *
1484
- * @template - The type of service to be created/accessed
1485
- * @param key - Unique identifier for the service in the registry
1486
- * @param builder - Function that creates the service instance when called with a DynamicClient
1487
- * @notInstrumented
1488
- */
1489
- const createRuntimeServiceAccessKey = (key, builder) => (client) => {
1490
- const { runtimeServices } = getCore(client);
1491
- const currentService = runtimeServices.getByKey(key);
1492
- if (currentService) return currentService;
1493
- const service = builder(client);
1494
- runtimeServices.register(key, service);
1495
- return service;
1425
+ * ## Why this is needed
1426
+ *
1427
+ * Instrumented functions are wrapped with `(...args: unknown[])`, so their
1428
+ * parameters arrive as an array. Logging a raw array loses the semantic names
1429
+ * of each argument. This function recovers a named representation:
1430
+ *
1431
+ * - **No arguments** `{}`
1432
+ * - **Single plain-object argument** → that object is returned as-is, because
1433
+ * SDK functions follow the single-object-parameter convention, so the object
1434
+ * already carries named keys (`{ amount, to }`, etc.).
1435
+ * - **Everything else** (multiple args, a single primitive, a single array)
1436
+ * each value is indexed as `arg0`, `arg1`, …
1437
+ *
1438
+ * ## Why arrays are NOT treated as plain objects
1439
+ *
1440
+ * `typeof [] === 'object'` and `[] !== null`, so without the `Array.isArray`
1441
+ * guard a single-array argument would be returned as-is. That would produce
1442
+ * an object with numeric string keys (`{ '0': ..., '1': ... }`), which is
1443
+ * misleading and inconsistent with how multi-arg calls are handled. Instead,
1444
+ * arrays fall through to the indexed `arg0` path.
1445
+ * @notInstrumented
1446
+ */
1447
+ const extractParams = (args) => {
1448
+ if (args.length === 0) return {};
1449
+ if (args.length === 1 && typeof args[0] === "object" && args[0] !== null && !Array.isArray(args[0])) return args[0];
1450
+ const result = {};
1451
+ for (let i = 0; i < args.length; i++) result[`arg${i}`] = args[i];
1452
+ return result;
1496
1453
  };
1497
1454
 
1498
1455
  //#endregion
1499
- //#region src/modules/wallets/walletProviderRegistry/createWalletProviderRegistry/createWalletProviderRegistry.ts
1456
+ //#region src/services/instrumentation/instrumentFunction/scrubResolvedValue/scrubResolvedValue.ts
1500
1457
  /**
1501
- * Creates a new wallet provider registry that manages wallet providers with priority-based registration.
1502
- *
1503
- * @returns The wallet provider registry instance
1504
- *
1505
- * @example
1506
- * ```typescript
1507
- * const registry = createWalletProviderRegistry();
1508
- *
1509
- * registry.register({
1510
- * priority: WalletProviderPriority.WALLET_SDK,
1511
- * walletProvider: myWalletProvider
1512
- * });
1513
- *
1514
- * const provider = registry.getByKey('my-wallet-key');
1515
- * const providers = registry.listProviders();
1516
- * ```
1458
+ * Scrubs a resolved return value of any type before logging it.
1459
+ * Routes through `scrubParameters` by wrapping the value so that strings,
1460
+ * arrays, Dates, Errors, and all other types are handled identically to
1461
+ * nested object fields.
1517
1462
  * @notInstrumented
1518
1463
  */
1519
- const createWalletProviderRegistry = (client) => {
1520
- const registry = /* @__PURE__ */ new Map();
1464
+ const scrubResolvedValue = ({ piiFields, redactAll, value }) => scrubParameters({
1465
+ piiFields,
1466
+ redactAll,
1467
+ value: { result: value }
1468
+ }).result;
1469
+
1470
+ //#endregion
1471
+ //#region src/services/instrumentation/instrumentFunction/instrumentFunction.ts
1472
+ const serializeError = (error) => {
1473
+ if (error instanceof Error) return {
1474
+ message: error.message,
1475
+ name: error.name,
1476
+ stack: error.stack
1477
+ };
1521
1478
  return {
1522
- getByKey: (key) => registry.get(key)?.walletProvider,
1523
- listProviderEntries: () => Array.from(registry.values()),
1524
- listProviders: () => Array.from(registry.values()).map((v) => v.walletProvider),
1525
- register: (args) => {
1526
- const existingEntry = registry.get(args.walletProvider.key);
1527
- if (existingEntry) {
1528
- if (existingEntry.priority < args.priority) {
1529
- registry.set(args.walletProvider.key, args);
1530
- emitEvent({
1531
- args: { walletProviderKey: args.walletProvider.key },
1532
- event: "walletProviderChanged"
1533
- }, client);
1534
- }
1535
- } else {
1536
- registry.set(args.walletProvider.key, args);
1537
- emitEvent({
1538
- args: { walletProvider: args.walletProvider },
1539
- event: "walletProviderRegistered"
1540
- }, client);
1541
- emitEvent({
1542
- args: { walletProviderKey: args.walletProvider.key },
1543
- event: "walletProviderChanged"
1544
- }, client);
1545
- }
1546
- },
1547
- unregister: (key) => {
1548
- registry.delete(key);
1549
- emitEvent({
1550
- args: { walletProviderKey: key },
1551
- event: "walletProviderUnregistered"
1552
- }, client);
1553
- }
1479
+ message: String(error),
1480
+ name: "UnknownError"
1554
1481
  };
1555
1482
  };
1556
-
1557
- //#endregion
1558
- //#region src/modules/wallets/walletProviderRegistry/getWalletProviderRegistry/getWalletProviderRegistry.ts
1559
1483
  /**
1560
- * This function provides access to a shared instance of the wallet provider registry.
1484
+ * Wraps a function so that each invocation emits a structured
1485
+ * `FunctionInstrumentationEvent` at three lifecycle points:
1561
1486
  *
1562
- * It ensures that the same registry instance is used throughout the client to maintaining
1563
- * consistency of registered wallet providers across different parts of the codebase.
1487
+ * - **started** immediately before the function body executes, with the
1488
+ * PII-scrubbed call parameters.
1489
+ * - **resolved** — after the function (or its returned Promise) settles
1490
+ * successfully, with the PII-scrubbed return value.
1491
+ * - **rejected** — if the function throws synchronously or its returned
1492
+ * Promise rejects.
1564
1493
  *
1565
- * @returns The wallet provider registry instance
1494
+ * The original return value (or thrown error) is always forwarded to the
1495
+ * caller unchanged — instrumentation is purely observational.
1566
1496
  *
1567
- * @example
1568
- * ```typescript
1569
- * // Get the registry instance
1570
- * const registry = getWalletProviderRegistry();
1497
+ * ## Usage
1571
1498
  *
1572
- * // Register a wallet provider
1573
- * registry.register({
1574
- * priority: WalletProviderPriority.WALLET_SDK,
1575
- * walletProvider: myWalletProvider
1499
+ * ```ts
1500
+ * const transfer = instrumentFunction({
1501
+ * fn: rawTransfer,
1502
+ * functionName: 'transfer',
1503
+ * getCore: () => core,
1576
1504
  * });
1577
1505
  *
1578
- * // Retrieve a specific provider
1579
- * const provider = registry.getByKey('metamaskevm');
1506
+ * // When called, three events are emitted automatically:
1507
+ * await transfer({ amount: 1, to: '0xabc' });
1580
1508
  * ```
1509
+ *
1510
+ * ## How functions are wrapped
1511
+ *
1512
+ * `instrumentFunction` is not called manually in application code. Instead,
1513
+ * the esbuild instrumentation plugin (`tools/instrumentation-plugin`) injects
1514
+ * it at build time: any exported function annotated with `@instrumented` in its
1515
+ * JSDoc is automatically wrapped by the plugin during the build step.
1516
+ * The plugin strips the original `export`, renames the function, and re-exports
1517
+ * it under the original name via `instrumentFunction(...)` — so the public API
1518
+ * is unchanged and no call sites need to be updated.
1519
+ *
1520
+ * ## Opting out
1521
+ *
1522
+ * Instrumentation is skipped entirely when:
1523
+ * - `getCore()` returns `undefined` (SDK not initialised), or
1524
+ * - `core.instrumentation.config.enabled` is `false`.
1525
+ *
1526
+ * In both cases the original function is called directly with no overhead.
1527
+ *
1528
+ * ## PII scrubbing
1529
+ *
1530
+ * All string values whose key appears in `piiFields` are replaced with
1531
+ * `"[redacted]"` before events are emitted. Pass `redactAll: true` to redact
1532
+ * every field, regardless of key name.
1533
+ * @notInstrumented
1581
1534
  */
1582
- const getWalletProviderRegistry = createRuntimeServiceAccessKey("walletProviderRegistry", (client) => createWalletProviderRegistry(client));
1583
-
1584
- //#endregion
1585
- //#region src/modules/wallets/walletProviderRegistry/walletProviderRegistry.types.ts
1586
- let WalletProviderPriority = /* @__PURE__ */ function(WalletProviderPriority$1) {
1587
- /**
1588
- * Highest priority should be used by wallet providers that implement
1589
- * the most reliable wallet integration.
1590
- * example: The SDK provided by the wallet provider.
1591
- */
1592
- WalletProviderPriority$1[WalletProviderPriority$1["WALLET_SDK"] = 100] = "WALLET_SDK";
1593
- /**
1594
- * Medium priority should be used by wallet providers that implement
1595
- * a wallet integration via some reliable standard.
1596
- * example: A wallet provider that uses EIP6963 announcement.
1597
- */
1598
- WalletProviderPriority$1[WalletProviderPriority$1["WALLET_SELF_ANNOUNCEMENT_STANDARD"] = 50] = "WALLET_SELF_ANNOUNCEMENT_STANDARD";
1599
- /**
1600
- * Low priority should be used by wallet providers that implement
1601
- * a wallet integration on a less reliable standard.
1602
- * example: A wallet provider that uses window.ethereum, where the
1603
- * window key can be overridden by other extensions.
1604
- */
1605
- WalletProviderPriority$1[WalletProviderPriority$1["WINDOW_INJECT"] = 20] = "WINDOW_INJECT";
1606
- return WalletProviderPriority$1;
1607
- }({});
1608
-
1609
- //#endregion
1610
- //#region src/modules/wallets/utils/getWalletProviderFromWalletAccount/getWalletProviderFromWalletAccount.ts
1611
- /** @notInstrumented */
1612
- const getWalletProviderFromWalletAccount = ({ walletAccount }, client) => {
1613
- const walletProvider = getWalletProviderRegistry(client).getByKey(walletAccount.walletProviderKey);
1614
- if (!walletProvider) throw new NoWalletProviderFoundError({ walletProviderKey: walletAccount.walletProviderKey });
1615
- return walletProvider;
1535
+ const instrumentFunction = ({ fn, functionName, getCore: getCore$1, redactAll }) => {
1536
+ const wrapped = (...args) => {
1537
+ const core = getCore$1();
1538
+ if (!core) return fn(...args);
1539
+ if (!core.instrumentation.config.enabled) return fn(...args);
1540
+ const instrumentation = core.instrumentation;
1541
+ const state = core.state.get();
1542
+ const baseEvent = {
1543
+ environmentId: core.environmentId,
1544
+ functionName,
1545
+ operatingSystem: getOperatingSystem(),
1546
+ parameters: scrubParameters({
1547
+ piiFields: instrumentation.config.piiFields,
1548
+ redactAll,
1549
+ value: extractParams(args)
1550
+ }),
1551
+ platform: getPlatform(),
1552
+ registeredExtensionKeys: Array.from(core.extensions),
1553
+ sdkSessionId: core.sdkSessionId,
1554
+ sdkVersion: core.version,
1555
+ status: "info",
1556
+ tokenSessionId: state.token ? extractSessionId(state.token) : null,
1557
+ userAgent: getUserAgent(),
1558
+ userId: state.user?.id ?? null
1559
+ };
1560
+ instrumentation.logFunction({
1561
+ ...baseEvent,
1562
+ phase: "started",
1563
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1564
+ });
1565
+ try {
1566
+ const result = fn(...args);
1567
+ if (result instanceof Promise) return result.then((value) => {
1568
+ instrumentation.logFunction({
1569
+ ...baseEvent,
1570
+ phase: "resolved",
1571
+ resolvedValue: scrubResolvedValue({
1572
+ piiFields: instrumentation.config.piiFields,
1573
+ redactAll,
1574
+ value
1575
+ }),
1576
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1577
+ });
1578
+ return value;
1579
+ }).catch((error) => {
1580
+ instrumentation.logFunction({
1581
+ ...baseEvent,
1582
+ phase: "rejected",
1583
+ rejectedError: serializeError(error),
1584
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1585
+ });
1586
+ throw error;
1587
+ });
1588
+ instrumentation.logFunction({
1589
+ ...baseEvent,
1590
+ phase: "resolved",
1591
+ resolvedValue: scrubResolvedValue({
1592
+ piiFields: instrumentation.config.piiFields,
1593
+ redactAll,
1594
+ value: result
1595
+ }),
1596
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1597
+ });
1598
+ return result;
1599
+ } catch (error) {
1600
+ instrumentation.logFunction({
1601
+ ...baseEvent,
1602
+ phase: "rejected",
1603
+ rejectedError: serializeError(error),
1604
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1605
+ });
1606
+ throw error;
1607
+ }
1608
+ };
1609
+ return wrapped;
1616
1610
  };
1617
1611
 
1618
- //#endregion
1619
- //#region src/modules/auth/consts.ts
1620
- const DYNAMIC_AUTH_COOKIE_NAME = "DYNAMIC_JWT_TOKEN";
1621
-
1622
1612
  //#endregion
1623
1613
  //#region src/constants.ts
1624
1614
  const SDK_API_CORE_VERSION = dependencies["@dynamic-labs/sdk-api-core"];
@@ -1674,5 +1664,5 @@ var InvalidParamError = class extends BaseError {
1674
1664
  };
1675
1665
 
1676
1666
  //#endregion
1677
- export { getOperatingSystem as $, fetchAndStoreNonces as A, LinkCredentialError as B, splitWalletProviderKey as C, onEvent as D, offEvent as E, getDeviceSigner as F, assertDefined as G, APIError as H, getNonce as I, instrumentFunction as J, ValueMustBeDefinedError as K, SandboxMaximumThresholdReachedError as L, UnauthorizedError as M, createDeviceSignatureHeadersMiddleware as N, onceEvent as O, getHeadersForNonceSignedByDeviceSigners as P, getPlatform as Q, MfaRateLimitedError as R, normalizeAddress as S, emitEvent as T, getSessionKeys as U, InvalidExternalAuthError as V, isCookieEnabled as W, DEFAULT_PII_FIELDS as X, scrubParameters as Y, getUserAgent as Z, formatWalletProviderKey as _, DYNAMIC_WAAS_METADATA as a, getDefaultClient as at, CHAINS_INFO_MAP as b, SDK_API_CORE_VERSION as c, BaseError as ct, WalletProviderPriority as d, version as dt, extractSessionId as et, getWalletProviderRegistry as f, getWalletAccountsFromState as g, getWalletAccounts as h, DEFAULT_WAAS_BASE_MPC_RELAY_API_URL as i, NONCE_POOL_SIZE as it, createApiClient as j, setCookie as k, DYNAMIC_AUTH_COOKIE_NAME as l, getCore as lt, NoWalletProviderFoundError as m, isWaasWalletProvider as n, CLIENT_SDK_NAME as nt, isUserMissingMfaAuth as o, setDefaultClient as ot, createRuntimeServiceAccessKey as p, __getElevatedAccessToken_wrapped as q, DEFAULT_WAAS_BASE_API_URL as r, DYNAMIC_SDK_API_VERSION as rt, DYNAMIC_ICONIC_SPRITE_URL as s, ClientNotFoundError as st, InvalidParamError as t, randomString as tt, getWalletProviderFromWalletAccount as u, name as ut, normalizeWalletNameWithChain as v, InvalidWalletProviderKeyError as w, formatWalletAccountId as x, getChainFromVerifiedCredentialChain as y, MfaInvalidOtpError as z };
1678
- //# sourceMappingURL=InvalidParamError-BeCPhfVz.esm.js.map
1667
+ export { ValueMustBeDefinedError as $, splitWalletProviderKey as A, createDeviceSignatureHeadersMiddleware as B, getWalletAccountsFromState as C, CHAINS_INFO_MAP as D, getChainFromVerifiedCredentialChain as E, onceEvent as F, MfaRateLimitedError as G, getDeviceSigner as H, setCookie as I, InvalidExternalAuthError as J, MfaInvalidOtpError as K, fetchAndStoreNonces as L, emitEvent as M, offEvent as N, formatWalletAccountId as O, onEvent as P, assertDefined as Q, createApiClient as R, getWalletAccounts as S, normalizeWalletNameWithChain as T, getNonce as U, getHeadersForNonceSignedByDeviceSigners as V, SandboxMaximumThresholdReachedError as W, getSessionKeys as X, APIError as Y, isCookieEnabled as Z, getWalletProviderFromWalletAccount as _, DYNAMIC_WAAS_METADATA as a, getDefaultClient as at, createRuntimeServiceAccessKey as b, SDK_API_CORE_VERSION as c, BaseError as ct, DEFAULT_PII_FIELDS as d, version as dt, getElevatedAccessToken as et, getUserAgent as f, DYNAMIC_AUTH_COOKIE_NAME as g, extractSessionId as h, DEFAULT_WAAS_BASE_MPC_RELAY_API_URL as i, NONCE_POOL_SIZE as it, InvalidWalletProviderKeyError as j, normalizeAddress as k, instrumentFunction as l, getCore as lt, getOperatingSystem as m, isWaasWalletProvider as n, CLIENT_SDK_NAME as nt, isUserMissingMfaAuth as o, setDefaultClient as ot, getPlatform as p, LinkCredentialError as q, DEFAULT_WAAS_BASE_API_URL as r, DYNAMIC_SDK_API_VERSION as rt, DYNAMIC_ICONIC_SPRITE_URL as s, ClientNotFoundError as st, InvalidParamError as t, randomString as tt, scrubParameters as u, name as ut, WalletProviderPriority as v, formatWalletProviderKey as w, NoWalletProviderFoundError as x, getWalletProviderRegistry as y, UnauthorizedError as z };
1668
+ //# sourceMappingURL=InvalidParamError-CXCYtY0p.esm.js.map