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