@circle-fin/app-kit 1.10.0 → 1.11.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.
package/earn.cjs CHANGED
@@ -18,6 +18,17 @@
18
18
 
19
19
  'use strict';
20
20
 
21
+ // Buffer polyfill setup - executes before any other code
22
+ // Ensures globalThis.Buffer is available for Solana libraries
23
+ const { Buffer } = require('buffer');
24
+ if (typeof globalThis !== 'undefined' && typeof globalThis.Buffer === 'undefined') {
25
+ globalThis.Buffer = Buffer;
26
+ }
27
+ if (typeof window !== 'undefined' && typeof window.Buffer === 'undefined') {
28
+ window.Buffer = Buffer;
29
+ }
30
+
31
+
21
32
  var zod = require('zod');
22
33
  require('pino');
23
34
  require('@ethersproject/bytes');
@@ -28,8 +39,8 @@ require('bn.js');
28
39
  require('@coral-xyz/anchor');
29
40
  require('bs58');
30
41
  require('@noble/curves/ed25519');
31
- var keccak256 = require('@ethersproject/keccak256');
32
42
  var units = require('@ethersproject/units');
43
+ var keccak256 = require('@ethersproject/keccak256');
33
44
 
34
45
  // Import global type declarations
35
46
  /**
@@ -46,6 +57,51 @@ var units = require('@ethersproject/units');
46
57
  * }
47
58
  * ```
48
59
  */ const isNodeEnvironment = ()=>typeof process !== 'undefined' && typeof process.versions === 'object' && typeof process.versions.node === 'string';
60
+ /**
61
+ * Check whether the current runtime exposes a browser DOM.
62
+ *
63
+ * @remarks
64
+ * This intentionally does not treat every non-Node runtime as a browser.
65
+ * Server-side edge runtimes such as Cloudflare Workers, Deno, and Supabase
66
+ * Edge Functions do not expose Node globals but can safely use server
67
+ * credentials. A Node.js runtime remains server-side even when a test or SSR
68
+ * environment provides a DOM shim.
69
+ *
70
+ * @returns `true` when running in a browser window, `false` otherwise.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * import { isBrowserEnvironment } from '@core/utils'
75
+ *
76
+ * if (isBrowserEnvironment()) {
77
+ * throw new Error('Server-only secrets must not be used in the browser')
78
+ * }
79
+ * ```
80
+ */ const isBrowserEnvironment = ()=>{
81
+ const browserWindow = globalThis.window;
82
+ return !isNodeEnvironment() && browserWindow?.document !== undefined;
83
+ };
84
+ /**
85
+ * Return the SDK User-Agent request header only when running in Node.js.
86
+ *
87
+ * Browsers forbid manually setting `User-Agent`, and a custom fallback header
88
+ * can trigger CORS preflight. Non-Node server runtimes also omit this optional
89
+ * attribution header because they cannot set it reliably.
90
+ *
91
+ * @returns A User-Agent header in Node.js, or an empty object otherwise.
92
+ *
93
+ * @example
94
+ * ```typescript
95
+ * import { getNodeUserAgentHeader } from '@core/utils'
96
+ *
97
+ * const headers = {
98
+ * 'Content-Type': 'application/json',
99
+ * ...getNodeUserAgentHeader(),
100
+ * }
101
+ * ```
102
+ */ const getNodeUserAgentHeader = ()=>isNodeEnvironment() ? {
103
+ 'User-Agent': getUserAgent()
104
+ } : {};
49
105
  /**
50
106
  * Detect the runtime environment and return a shortened identifier.
51
107
  *
@@ -6394,6 +6450,39 @@ const swapTokenEnumSchema = zod.z.enum([
6394
6450
  throw new Error(`Invalid chain identifier type: ${typeof chainIdentifier}. Expected ChainDefinition object, Blockchain enum, or string literal.`);
6395
6451
  }
6396
6452
 
6453
+ /**
6454
+ * Resolve a chain identifier to a plain chain-name string.
6455
+ *
6456
+ * Accept a string literal (`'Ethereum'`), a `ChainDefinition`-like
6457
+ * object (`{ chain: 'Ethereum' }`), or `null`/`undefined` and return
6458
+ * the chain name as a string. Return `undefined` when the value
6459
+ * cannot be resolved.
6460
+ *
6461
+ * @remarks
6462
+ * Unlike `resolveChainIdentifier` (which returns a full `ChainDefinition`
6463
+ * and throws on invalid input), this helper is intentionally lenient and
6464
+ * never throws — it is safe to call in error-handling and telemetry paths.
6465
+ *
6466
+ * @param value - A string, chain-definition object, or nullish value.
6467
+ * @returns The chain name string, or `undefined`.
6468
+ *
6469
+ * @example
6470
+ * ```typescript
6471
+ * import { resolveChainName } from '@core/chains'
6472
+ *
6473
+ * resolveChainName('Ethereum') // 'Ethereum'
6474
+ * resolveChainName({ chain: 'Ethereum' }) // 'Ethereum'
6475
+ * resolveChainName(undefined) // undefined
6476
+ * ```
6477
+ */ function resolveChainName(value) {
6478
+ if (value == null) return undefined;
6479
+ if (typeof value === 'string') return value;
6480
+ if (typeof value === 'object' && 'chain' in value && typeof value.chain === 'string') {
6481
+ return value.chain;
6482
+ }
6483
+ return undefined;
6484
+ }
6485
+
6397
6486
  /**
6398
6487
  * Extracts chain information including name, display name, and expected address format.
6399
6488
  *
@@ -6778,13 +6867,12 @@ const swapTokenEnumSchema = zod.z.enum([
6778
6867
  headers: {
6779
6868
  ...DEFAULT_CONFIG$1.headers,
6780
6869
  ...config.headers ?? {},
6781
- // In browser environments, directly setting the 'User-Agent' or similar headers is restricted and may be ignored or cause errors.
6782
- // This is why we use the 'X-User-Agent' header instead.
6783
- ...typeof window === 'undefined' ? {
6784
- 'User-Agent': getUserAgent()
6785
- } : {
6786
- 'X-User-Agent': getUserAgent()
6787
- }
6870
+ // Browsers forbid setting a user-agent request header, and the custom
6871
+ // fallback header the SDK used instead trips CORS preflight against the
6872
+ // Circle APIs (it isn't in their `Access-Control-Allow-Headers`),
6873
+ // blocking the request. So send the SDK user agent only in Node;
6874
+ // browsers omit it entirely.
6875
+ ...getNodeUserAgentHeader()
6788
6876
  }
6789
6877
  };
6790
6878
  let lastError;
@@ -8050,6 +8138,216 @@ const swapTokenEnumSchema = zod.z.enum([
8050
8138
  * This prefix is right-padded to 24 bytes in the final hookData.
8051
8139
  */ const CCTP_FORWARD_MAGIC_PREFIX = 'cctp-forward';
8052
8140
 
8141
+ /**
8142
+ * Project an arbitrary payload onto the exact set of fields the telemetry
8143
+ * endpoint accepts.
8144
+ *
8145
+ * @remarks
8146
+ * Defense-in-depth before the last network hop: rather than
8147
+ * `JSON.stringify`-ing the caller's object verbatim, only the
8148
+ * allowlisted {@link ClientLogPayload} fields (and the allowlisted
8149
+ * sub-fields of `errorDetails` / `clientContext`) are copied across.
8150
+ * A regressing upstream mapper — or a plain-JS caller that bypasses the
8151
+ * type — therefore cannot exfiltrate stray properties (secrets, PII,
8152
+ * raw error stacks) through the analytics channel. Optional fields are
8153
+ * only included when present so the serialised shape matches the
8154
+ * server's strict schema.
8155
+ *
8156
+ * @internal
8157
+ */ function toSafePayload(payload) {
8158
+ const clientContext = {
8159
+ platform: payload.clientContext.platform,
8160
+ os: payload.clientContext.os,
8161
+ runtimeName: payload.clientContext.runtimeName
8162
+ };
8163
+ const safe = {
8164
+ sdkName: payload.sdkName,
8165
+ sdkVersion: payload.sdkVersion,
8166
+ eventType: payload.eventType,
8167
+ timestamp: payload.timestamp,
8168
+ clientContext
8169
+ };
8170
+ if (payload.sourceChain !== undefined) safe['sourceChain'] = payload.sourceChain;
8171
+ if (payload.destinationChain !== undefined) safe['destinationChain'] = payload.destinationChain;
8172
+ if (payload.tokenIn !== undefined) safe['tokenIn'] = payload.tokenIn;
8173
+ if (payload.tokenOut !== undefined) safe['tokenOut'] = payload.tokenOut;
8174
+ if (payload.txHash !== undefined) safe['txHash'] = payload.txHash;
8175
+ if (payload.correlationId !== undefined) safe['correlationId'] = payload.correlationId;
8176
+ if (payload.errorDetails !== undefined) {
8177
+ const errorDetails = {
8178
+ ...payload.errorDetails.errorCode !== undefined && {
8179
+ errorCode: payload.errorDetails.errorCode
8180
+ },
8181
+ ...payload.errorDetails.errorType !== undefined && {
8182
+ errorType: payload.errorDetails.errorType
8183
+ }
8184
+ };
8185
+ safe['errorDetails'] = errorDetails;
8186
+ }
8187
+ return safe;
8188
+ }
8189
+ /**
8190
+ * Default telemetry endpoint.
8191
+ *
8192
+ * Override via the `STABLECOIN_KITS_TELEMETRY_URL` environment variable
8193
+ * (e.g. for staging or local development).
8194
+ *
8195
+ * @internal
8196
+ */ const DEFAULT_LOGS_URL = 'https://api.circle.com/v1/stablecoinKits/logs';
8197
+ /**
8198
+ * Resolve the telemetry endpoint URL.
8199
+ *
8200
+ * @internal
8201
+ */ function getLogsUrl() {
8202
+ if (isNodeEnvironment() && typeof process.env['STABLECOIN_KITS_TELEMETRY_URL'] === 'string' && process.env['STABLECOIN_KITS_TELEMETRY_URL'].length > 0) {
8203
+ return process.env['STABLECOIN_KITS_TELEMETRY_URL'];
8204
+ }
8205
+ return DEFAULT_LOGS_URL;
8206
+ }
8207
+ /**
8208
+ * Send a telemetry event to the proxy service.
8209
+ *
8210
+ * @remarks
8211
+ * Fire-and-forget: the returned promise is intentionally not awaited
8212
+ * by the caller. A fetch failure (network error, non-2xx, timeout)
8213
+ * is silently swallowed so telemetry never blocks or fails user
8214
+ * operations.
8215
+ *
8216
+ * @param payload - The structured log payload matching the server schema.
8217
+ *
8218
+ * @example
8219
+ * ```typescript
8220
+ * import { emitAnalyticsLog } from '@core/utils'
8221
+ *
8222
+ * // Fire-and-forget — do not await
8223
+ * void emitAnalyticsLog(payload)
8224
+ * ```
8225
+ */ async function emitAnalyticsLog(payload) {
8226
+ // Hand-rolled timeout via `AbortController` + `setTimeout` rather than
8227
+ // `AbortSignal.timeout(...)` so we can `clearTimeout` the handle in a
8228
+ // `finally`. `AbortSignal.timeout` registers a timer that stays on the
8229
+ // event loop until it fires even if the fetch already settled, which
8230
+ // manifests as spurious `TimeoutError` unhandled rejections during
8231
+ // process teardown (notably between e2e test fork lifecycles). See
8232
+ // nodejs/node#48298 for the underlying issue.
8233
+ const controller = new AbortController();
8234
+ const timeoutHandle = setTimeout(()=>{
8235
+ controller.abort(new DOMException('Telemetry request timed out', 'TimeoutError'));
8236
+ }, 5_000);
8237
+ // Don't let the timer keep the Node event loop alive in short-lived
8238
+ // CLIs / test processes; telemetry is best-effort and must never
8239
+ // block clean process exit. `unref` only exists on Node's `Timeout`
8240
+ // object, not on the `number` returned by the browser's `setTimeout`,
8241
+ // so we feature-detect rather than call unconditionally.
8242
+ if (typeof timeoutHandle.unref === 'function') {
8243
+ timeoutHandle.unref();
8244
+ }
8245
+ try {
8246
+ await fetch(getLogsUrl(), {
8247
+ method: 'POST',
8248
+ headers: {
8249
+ 'Content-Type': 'application/json',
8250
+ // Browsers forbid setting a user-agent request header, and the custom
8251
+ // fallback header the SDK used instead trips CORS preflight (it isn't
8252
+ // in the telemetry endpoint's `Access-Control-Allow-Headers`), so send
8253
+ // it only in Node; browsers omit it entirely.
8254
+ ...getNodeUserAgentHeader()
8255
+ },
8256
+ body: JSON.stringify(toSafePayload(payload)),
8257
+ signal: controller.signal
8258
+ });
8259
+ } catch {
8260
+ // Silently swallow — telemetry must never break user operations.
8261
+ } finally{
8262
+ clearTimeout(timeoutHandle);
8263
+ }
8264
+ }
8265
+
8266
+ /**
8267
+ * Build the `clientContext` object for telemetry payloads.
8268
+ *
8269
+ * @remarks
8270
+ * Use the exported `getRuntime()` and `isNodeEnvironment()` from
8271
+ * `@core/utils` to detect the runtime environment. The returned
8272
+ * string is parsed into the structured `ClientContext` fields
8273
+ * expected by the server schema.
8274
+ *
8275
+ * @returns A {@link ClientContext} with platform, OS, and runtime name
8276
+ * populated from the current environment.
8277
+ *
8278
+ * @example
8279
+ * ```typescript
8280
+ * import { buildClientContext } from '@core/utils'
8281
+ *
8282
+ * const ctx = buildClientContext()
8283
+ * // Node: { platform: 'node', os: 'darwin', runtimeName: null }
8284
+ * // Browser: { platform: 'browser', os: null, runtimeName: 'chrome' }
8285
+ * ```
8286
+ */ function buildClientContext() {
8287
+ const runtime = getRuntime();
8288
+ if (runtime.startsWith('browser/')) {
8289
+ return {
8290
+ platform: 'browser',
8291
+ os: null,
8292
+ runtimeName: runtime.slice('browser/'.length).toLowerCase()
8293
+ };
8294
+ }
8295
+ if (runtime.startsWith('node/')) {
8296
+ return {
8297
+ platform: 'node',
8298
+ os: isNodeEnvironment() ? process.platform : null,
8299
+ runtimeName: null
8300
+ };
8301
+ }
8302
+ return {
8303
+ platform: 'node',
8304
+ os: null,
8305
+ runtimeName: null
8306
+ };
8307
+ }
8308
+
8309
+ /**
8310
+ * Extract structured error details from an unknown error value.
8311
+ *
8312
+ * @remarks
8313
+ * Handle three cases:
8314
+ * - `KitError` — extract `code` and `name`.
8315
+ * - `Error` — extract `name`.
8316
+ * - Anything else — return empty details.
8317
+ *
8318
+ * Only structured, bounded fields (`errorCode`, `errorType`) are
8319
+ * included. Free-text fields (`message`, `stack`) are intentionally
8320
+ * omitted to avoid leaking secrets or PII through vendor telemetry.
8321
+ *
8322
+ * @param error - The thrown value to extract details from.
8323
+ * @returns A {@link ErrorDetails} object suitable for telemetry payloads.
8324
+ *
8325
+ * @example
8326
+ * ```typescript
8327
+ * import { extractErrorDetails } from '@core/utils'
8328
+ *
8329
+ * try {
8330
+ * await riskyOperation()
8331
+ * } catch (error) {
8332
+ * const details = extractErrorDetails(error)
8333
+ * // { errorCode: '1001', errorType: 'INPUT_NETWORK_MISMATCH' }
8334
+ * }
8335
+ * ```
8336
+ */ function extractErrorDetails(error) {
8337
+ if (error instanceof KitError) {
8338
+ return {
8339
+ errorCode: String(error.code),
8340
+ errorType: error.name
8341
+ };
8342
+ }
8343
+ if (error instanceof Error) {
8344
+ return {
8345
+ errorType: error.name
8346
+ };
8347
+ }
8348
+ return {};
8349
+ }
8350
+
8053
8351
  /**
8054
8352
  * Strip the `@circle-fin/` scope from a kit package name to produce the
8055
8353
  * short SDK name used in telemetry payloads.
@@ -8068,8 +8366,154 @@ const swapTokenEnumSchema = zod.z.enum([
8068
8366
  return pkgName.replace('@circle-fin/', '');
8069
8367
  }
8070
8368
 
8369
+ /**
8370
+ * Soft signal for the case where building or emitting a telemetry payload
8371
+ * threw — for example, a buggy `TelemetryContextResolver`, a regression in
8372
+ * `extractErrorDetails`, or a synchronous failure inside `emitAnalyticsLog`
8373
+ * before it could swallow the error itself. Logged with a stable prefix so
8374
+ * consumers can grep for it. We deliberately do not re-throw: the caller's
8375
+ * original operation error must always win.
8376
+ *
8377
+ * @internal
8378
+ */ function warnTelemetryDrop(eventType, cause) {
8379
+ try {
8380
+ // Pass `cause` as the second console.warn argument rather than
8381
+ // string-coercing it. `String(err)` (and `err.message` alone)
8382
+ // discards the stack trace, nested `cause`, and any custom Error
8383
+ // properties — exactly the context an on-call needs when a
8384
+ // resolver-closure regression triggers this path.
8385
+ console.warn(`[stablecoin-kits telemetry] dropped event '${eventType}':`, cause);
8386
+ } catch {
8387
+ // console.warn itself throwing is the user's environment; nothing more we
8388
+ // can do without risking the original operation error.
8389
+ }
8390
+ }
8391
+ /**
8392
+ * Build a telemetry payload from common fields.
8393
+ *
8394
+ * @internal
8395
+ */ function buildPayload(config, eventType, errorDetails, context) {
8396
+ return {
8397
+ sdkName: config.sdkName,
8398
+ sdkVersion: config.sdkVersion,
8399
+ eventType,
8400
+ timestamp: new Date().toISOString(),
8401
+ ...errorDetails !== undefined && {
8402
+ errorDetails
8403
+ },
8404
+ clientContext: buildClientContext(),
8405
+ ...context?.sourceChain != null && {
8406
+ sourceChain: context.sourceChain
8407
+ },
8408
+ ...context?.destinationChain != null && {
8409
+ destinationChain: context.destinationChain
8410
+ },
8411
+ ...context?.tokenIn != null && {
8412
+ tokenIn: context.tokenIn
8413
+ },
8414
+ ...context?.tokenOut != null && {
8415
+ tokenOut: context.tokenOut
8416
+ },
8417
+ ...context?.txHash != null && {
8418
+ txHash: context.txHash
8419
+ },
8420
+ ...context?.correlationId != null && {
8421
+ correlationId: context.correlationId
8422
+ }
8423
+ };
8424
+ }
8425
+ /**
8426
+ * Emit telemetry for a completed operation without affecting its caller.
8427
+ *
8428
+ * No-ops when `config.disabled` is `true`. Like {@link withErrorTelemetry},
8429
+ * failures while constructing or submitting the telemetry payload are reported
8430
+ * as a soft warning and never change a completed operation's result.
8431
+ *
8432
+ * @param eventType - The telemetry event type for the completed operation.
8433
+ * @param config - Per-kit SDK identity and disabled flag.
8434
+ * @param context - Optional chain, token, and transaction context.
8435
+ * @returns Nothing.
8436
+ * @throws Never — telemetry failures are reported as warnings.
8437
+ *
8438
+ * @example
8439
+ * ```typescript
8440
+ * import { emitSuccessTelemetry } from '@core/utils'
8441
+ *
8442
+ * emitSuccessTelemetry(
8443
+ * 'bridge_bridge',
8444
+ * { sdkName: 'bridge-kit', sdkVersion: '1.0.0', disabled: false },
8445
+ * { sourceChain: 'Ethereum', destinationChain: 'Base', tokenIn: 'USDC' },
8446
+ * )
8447
+ * ```
8448
+ */ function emitSuccessTelemetry(eventType, config, context) {
8449
+ if (config.disabled) {
8450
+ return;
8451
+ }
8452
+ try {
8453
+ void emitAnalyticsLog(buildPayload(config, eventType, undefined, context));
8454
+ } catch (telemetryError) {
8455
+ warnTelemetryDrop(eventType, telemetryError);
8456
+ }
8457
+ }
8458
+ /**
8459
+ * Wrap an async operation with error telemetry.
8460
+ *
8461
+ * Execute `fn` and, if it throws, emit an error telemetry payload
8462
+ * before re-throwing. No-ops when `config.disabled` is `true`.
8463
+ *
8464
+ * `context` may be a static {@link TelemetryContext} or a
8465
+ * {@link TelemetryContextResolver}. The resolver is invoked in the
8466
+ * catch branch, so it can read state — most importantly `txHash` —
8467
+ * that the wrapped operation set after a successful broadcast. The
8468
+ * resolver must close over per-call locals only; passing instance
8469
+ * state would break isolation between concurrent invocations.
8470
+ *
8471
+ * @param fn - The async operation to execute.
8472
+ * @param eventType - The telemetry event type for this operation.
8473
+ * @param config - Per-kit SDK identity and disabled flag.
8474
+ * @param context - Optional context, static or lazily resolved.
8475
+ * @returns The result of the operation.
8476
+ * @throws Re-throws any error after emitting telemetry.
8477
+ *
8478
+ * @example
8479
+ * ```typescript
8480
+ * import { withErrorTelemetry } from '@core/utils'
8481
+ *
8482
+ * let txHash: string | undefined
8483
+ * const result = await withErrorTelemetry(
8484
+ * () => provider.swap(params, h => { txHash = h }),
8485
+ * 'swap_swap',
8486
+ * { sdkName: 'swap-kit', sdkVersion: '1.0.0', disabled: false },
8487
+ * () => ({
8488
+ * sourceChain: 'Ethereum',
8489
+ * tokenIn: 'USDC',
8490
+ * tokenOut: 'EURC',
8491
+ * ...(txHash != null && { txHash }),
8492
+ * }),
8493
+ * )
8494
+ * ```
8495
+ */ async function withErrorTelemetry(fn, eventType, config, context) {
8496
+ try {
8497
+ return await fn();
8498
+ } catch (error) {
8499
+ if (!config.disabled) {
8500
+ try {
8501
+ const resolved = typeof context === 'function' ? context() : context;
8502
+ void emitAnalyticsLog(buildPayload(config, eventType, extractErrorDetails(error), resolved));
8503
+ } catch (telemetryError) {
8504
+ // Never let telemetry emission mask the original operation error.
8505
+ // But surface a soft signal so silent telemetry drops are
8506
+ // discoverable (e.g. a regression in a resolver closure or in
8507
+ // `extractErrorDetails`) instead of vanishing without any trace.
8508
+ warnTelemetryDrop(eventType, telemetryError);
8509
+ }
8510
+ }
8511
+ throw error;
8512
+ }
8513
+ }
8514
+
8071
8515
  var name$3 = "@circle-fin/bridge-kit";
8072
- var version$3 = "1.12.1";
8516
+ var version$3 = "1.12.2";
8073
8517
  var pkg$3 = {
8074
8518
  name: name$3,
8075
8519
  version: version$3};
@@ -8897,7 +9341,13 @@ var TransferSpeed;
8897
9341
  /**
8898
9342
  * The ASCII "cctp-forward" magic, hex-encoded (no `0x`), that a forward-friendly
8899
9343
  * hookData must start with.
8900
- */ Buffer.from(CCTP_FORWARD_MAGIC_PREFIX, 'ascii').toString('hex');
9344
+ *
9345
+ * Encoded with `TextEncoder` (a browser-safe Web API) rather than `Buffer.from`
9346
+ * so this module-level constant does not reference the Node `Buffer` global at
9347
+ * import time. App Kit inlines this provider into its bundle without a Buffer polyfill, and a
9348
+ * bare `Buffer` here crashes browser bundles (e.g. Vite) on load — even for apps
9349
+ * that never touch the prepaid FORWARD path. Mirrors `buildForwardingHookData`.
9350
+ */ Array.from(new TextEncoder().encode(CCTP_FORWARD_MAGIC_PREFIX)).map((byte)=>byte.toString(16).padStart(2, '0')).join('');
8901
9351
 
8902
9352
  /**
8903
9353
  * The minimum finality threshold for CCTPv2 transfers.
@@ -8931,7 +9381,7 @@ var TransferSpeed;
8931
9381
  registerKit(`${pkg$3.name}/${pkg$3.version}`);
8932
9382
 
8933
9383
  var name$2 = "@circle-fin/swap-kit";
8934
- var version$2 = "1.4.0";
9384
+ var version$2 = "1.5.0";
8935
9385
  var pkg$2 = {
8936
9386
  name: name$2,
8937
9387
  version: version$2};
@@ -9672,6 +10122,16 @@ getQuoteRequestBaseSchema.superRefine(requireCrossChainQuoteToAddress);
9672
10122
  required_error: 'estimatedAmount is required',
9673
10123
  invalid_type_error: 'estimatedAmount must be a string'
9674
10124
  }).min(1, 'estimatedAmount must be a non-empty string'),
10125
+ // Per-swap join key echoed back verbatim on success telemetry. Optional so a
10126
+ // not-yet-upgraded service (no field) still validates during rollout. A
10127
+ // malformed/non-UUID value is coerced to `undefined` (no telemetry id) rather
10128
+ // than throwing: this is a telemetry-only field (stripped from the developer
10129
+ // result, never used for control flow), so it must not be able to abort the
10130
+ // swap via `parseCreateSwapResponse().parse()`. Mirrors the best-effort,
10131
+ // never-throw contract of the rest of the telemetry stack. Implemented with
10132
+ // `preprocess` rather than Zod's `.catch()` because static analysis misreads
10133
+ // `.catch` on the schema chain as an unhandled Promise (S7785).
10134
+ correlationId: zod.z.preprocess((value)=>zod.z.string().uuid().safeParse(value).success ? value : undefined, zod.z.string().optional()),
9675
10135
  config: createSwapRequestBaseSchema.shape.config.optional(),
9676
10136
  fees: createSwapFeesSchema.optional(),
9677
10137
  transaction: createSwapTransactionSchema
@@ -12230,7 +12690,7 @@ new Set(Object.values(Blockchain));
12230
12690
  registerKit(`${pkg$2.name}/${pkg$2.version}`);
12231
12691
 
12232
12692
  var name$1 = "@circle-fin/earn-kit";
12233
- var version$1 = "1.3.0";
12693
+ var version$1 = "1.4.0";
12234
12694
  var pkg$1 = {
12235
12695
  name: name$1,
12236
12696
  version: version$1};
@@ -14229,7 +14689,7 @@ const bridgeDepositPrepareReviewSchema = zod.z.object({
14229
14689
  }
14230
14690
 
14231
14691
  var name = "@circle-fin/provider-earn-service";
14232
- var version = "1.3.0";
14692
+ var version = "1.3.1";
14233
14693
  var pkg = {
14234
14694
  name: name,
14235
14695
  version: version};
@@ -14291,15 +14751,25 @@ var pkg = {
14291
14751
  *
14292
14752
  * @internal
14293
14753
  */ function buildConfig(serviceConfig) {
14754
+ // The kit key is a server-only secret. Reject it in the browser so it cannot
14755
+ // leak into a client bundle (no-op in Node.js). Keyless usage stays allowed.
14756
+ if (serviceConfig?.kitKey !== undefined && isBrowserEnvironment()) {
14757
+ throw createValidationFailedError('kitKey', '[redacted]', 'kitKey must not be provided in a browser environment — it is a server-only secret. Run EarnKit operations that use a kit key on your server, or omit kitKey to use the permissionless (keyless) client path');
14758
+ }
14294
14759
  const baseUrl = serviceConfig?.baseUrl ?? EARN_SERVICE_BASE_URL;
14295
- const sdkVersion = resolveSdkVersionHeader();
14760
+ // The API CORS policy does not allow this custom header. Keep the existing
14761
+ // per-request version attribution for Node callers, but omit it in browsers
14762
+ // so public EarnKit endpoints do not fail at CORS preflight.
14763
+ const sdkVersionHeader = isNodeEnvironment() ? {
14764
+ [SDK_VERSION_HEADER]: resolveSdkVersionHeader()
14765
+ } : {};
14296
14766
  if (serviceConfig?.kitKey === undefined) {
14297
14767
  return {
14298
14768
  pollingConfig: {
14299
14769
  ...DEFAULT_CONFIG,
14300
14770
  headers: {
14301
14771
  ...DEFAULT_CONFIG.headers,
14302
- [SDK_VERSION_HEADER]: sdkVersion
14772
+ ...sdkVersionHeader
14303
14773
  }
14304
14774
  },
14305
14775
  baseUrl
@@ -14317,7 +14787,7 @@ var pkg = {
14317
14787
  ...DEFAULT_CONFIG,
14318
14788
  headers: {
14319
14789
  ...DEFAULT_CONFIG.headers,
14320
- [SDK_VERSION_HEADER]: sdkVersion,
14790
+ ...sdkVersionHeader,
14321
14791
  Authorization: `Bearer ${serviceConfig.kitKey}`
14322
14792
  }
14323
14793
  },
@@ -16421,6 +16891,12 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
16421
16891
  if (config.providers !== undefined && !Array.isArray(config.providers)) {
16422
16892
  throw createValidationFailedError('config.providers', config.providers, 'providers must be an array of earn providers when provided');
16423
16893
  }
16894
+ if (config.disableAnalytics !== undefined && typeof config.disableAnalytics !== 'boolean') {
16895
+ throw createValidationFailedError('config.disableAnalytics', config.disableAnalytics, 'disableAnalytics must be a boolean when provided');
16896
+ }
16897
+ if (config.disableErrorReporting !== undefined && typeof config.disableErrorReporting !== 'boolean') {
16898
+ throw createValidationFailedError('config.disableErrorReporting', config.disableErrorReporting, 'disableErrorReporting must be a boolean when provided');
16899
+ }
16424
16900
  const defaultProviders = getDefaultProviders();
16425
16901
  const providers = [
16426
16902
  ...config.providers ?? [],
@@ -16432,6 +16908,31 @@ function createUnsupportedCrossChainDepositError(chain, reason) {
16432
16908
  return context;
16433
16909
  }
16434
16910
 
16911
+ /**
16912
+ * Register Earn Kit telemetry event type strings with the shared registry so
16913
+ * error telemetry helpers remain compile-time checked.
16914
+ *
16915
+ * @internal
16916
+ */ /**
16917
+ * Telemetry event type identifiers for Earn Kit operations.
16918
+ *
16919
+ * @internal
16920
+ */ const EARN_EVENT_TYPES = {
16921
+ GET_VAULTS: 'earn_get_vaults',
16922
+ EXPLORE_VAULTS: 'earn_explore_vaults',
16923
+ GET_POSITION: 'earn_get_position',
16924
+ GET_CROSS_CHAIN_DEPOSIT_STATUS: 'earn_get_cross_chain_deposit_status',
16925
+ WAIT_FOR_CROSS_CHAIN_DEPOSIT: 'earn_wait_for_cross_chain_deposit',
16926
+ DEPOSIT: 'earn_deposit',
16927
+ CROSS_CHAIN_DEPOSIT: 'earn_cross_chain_deposit',
16928
+ WITHDRAW: 'earn_withdraw',
16929
+ CLAIM_REWARDS: 'earn_claim_rewards',
16930
+ GET_DEPOSIT_QUOTE: 'earn_get_deposit_quote',
16931
+ GET_WITHDRAWAL_QUOTE: 'earn_get_withdrawal_quote',
16932
+ GET_CLAIM_REWARDS_QUOTE: 'earn_get_claim_rewards_quote',
16933
+ RETRY: 'earn_retry'
16934
+ };
16935
+
16435
16936
  /**
16436
16937
  * Format a provider amount object as a human-readable decimal string.
16437
16938
  *
@@ -17947,6 +18448,14 @@ function hasCrossChainDestination(params) {
17947
18448
  return formatClaimRewardsQuoteInfo(result);
17948
18449
  }
17949
18450
 
18451
+ /** SDK name used in telemetry payloads. */ const SDK_NAME = resolveKitSdkName(pkg$1.name);
18452
+ /**
18453
+ * Determine whether deposit parameters target a destination chain.
18454
+ *
18455
+ * @internal
18456
+ */ function isCrossChainDeposit(params) {
18457
+ return 'to' in params && params.to !== undefined;
18458
+ }
17950
18459
  function formatRetryResult(operation, result) {
17951
18460
  switch(operation){
17952
18461
  case 'deposit':
@@ -17961,6 +18470,70 @@ function formatRetryResult(operation, result) {
17961
18470
  }
17962
18471
  }
17963
18472
  }
18473
+ /**
18474
+ * Emit the success event corresponding to a completed retry.
18475
+ *
18476
+ * @internal
18477
+ */ function emitRetrySuccessTelemetry(trace, result, config) {
18478
+ switch(trace.operation){
18479
+ case 'deposit':
18480
+ {
18481
+ const sourceChain = resolveChainName(trace.params.from.chain);
18482
+ if ('to' in trace.params && trace.params.to !== undefined) {
18483
+ const destinationChain = resolveChainName(trace.params.to.chain);
18484
+ emitSuccessTelemetry(EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT, config, {
18485
+ ...sourceChain != null && {
18486
+ sourceChain
18487
+ },
18488
+ ...destinationChain != null && {
18489
+ destinationChain
18490
+ }
18491
+ });
18492
+ return;
18493
+ }
18494
+ emitSuccessTelemetry(EARN_EVENT_TYPES.DEPOSIT, config, {
18495
+ ...sourceChain != null && {
18496
+ sourceChain
18497
+ },
18498
+ ...'txHash' in result && {
18499
+ txHash: result.txHash
18500
+ }
18501
+ });
18502
+ return;
18503
+ }
18504
+ case 'withdraw':
18505
+ {
18506
+ const sourceChain = resolveChainName(trace.params.from.chain);
18507
+ emitSuccessTelemetry(EARN_EVENT_TYPES.WITHDRAW, config, {
18508
+ ...sourceChain != null && {
18509
+ sourceChain
18510
+ },
18511
+ ...'txHash' in result && {
18512
+ txHash: result.txHash
18513
+ }
18514
+ });
18515
+ return;
18516
+ }
18517
+ case 'claimRewards':
18518
+ {
18519
+ if ('rewards' in result && result.status === 'claimed') {
18520
+ const sourceChain = resolveChainName(trace.params.from.chain);
18521
+ emitSuccessTelemetry(EARN_EVENT_TYPES.CLAIM_REWARDS, config, {
18522
+ ...sourceChain != null && {
18523
+ sourceChain
18524
+ },
18525
+ txHash: result.txHash
18526
+ });
18527
+ }
18528
+ return;
18529
+ }
18530
+ default:
18531
+ {
18532
+ const exhaustive = trace;
18533
+ throw createValidationFailedError('error.cause.trace', exhaustive, 'EarnKit.retry() does not support this earn operation');
18534
+ }
18535
+ }
18536
+ }
17964
18537
  /**
17965
18538
  * A high-level class-based interface for DeFi lending vault operations.
17966
18539
  *
@@ -18019,6 +18592,8 @@ function formatRetryResult(operation, result) {
18019
18592
  * ```
18020
18593
  */ class EarnKit {
18021
18594
  context;
18595
+ /** Per-kit identity and opt-out state for error telemetry. */ telemetryConfig;
18596
+ /** Per-kit identity and opt-out state for success telemetry. */ analyticsTelemetryConfig;
18022
18597
  /**
18023
18598
  * Event dispatcher for step-level events emitted during multi-phase earn
18024
18599
  * operations. Prefer {@link EarnKit.on} / {@link EarnKit.off} over using
@@ -18043,6 +18618,16 @@ function formatRetryResult(operation, result) {
18043
18618
  */ constructor(config = {}){
18044
18619
  this.context = createEarnKitContext(config);
18045
18620
  this.actionDispatcher = new Actionable();
18621
+ this.telemetryConfig = {
18622
+ sdkName: SDK_NAME,
18623
+ sdkVersion: pkg$1.version,
18624
+ disabled: config.disableErrorReporting === true
18625
+ };
18626
+ this.analyticsTelemetryConfig = {
18627
+ sdkName: SDK_NAME,
18628
+ sdkVersion: pkg$1.version,
18629
+ disabled: config.disableAnalytics === true
18630
+ };
18046
18631
  for (const provider of this.context.providers){
18047
18632
  provider.registerDispatcher(this.actionDispatcher);
18048
18633
  }
@@ -18103,29 +18688,36 @@ function formatRetryResult(operation, result) {
18103
18688
  * }
18104
18689
  * ```
18105
18690
  */ async retry(error) {
18106
- if (!isKitError(error)) {
18107
- throw createValidationFailedError('error', error, 'EarnKit.retry() requires a KitError thrown by a previous earn operation');
18108
- }
18109
- if (!isRetryableError$1(error)) {
18110
- throw createValidationFailedError('error.recoverability', error.recoverability, 'EarnKit.retry() requires a retryable or resumable error — check isRetryableError(error) first');
18111
- }
18112
- const trace = error.cause?.trace;
18113
- if (!isEarnErrorTrace(trace)) {
18114
- throw createValidationFailedError('error.cause.trace', trace, 'EarnKit.retry() requires a KitError carrying earn retry context (operation, steps, provider, params)');
18115
- }
18116
- const provider = this.context.providers.find((candidate)=>candidate.name === trace.provider);
18117
- if (provider === undefined) {
18118
- throw createValidationFailedError('error.cause.trace.provider', trace.provider, `No earn provider named "${trace.provider}" is registered with this kit`);
18119
- }
18120
- const result = await provider.retry(error);
18121
- // `provider.retry` returns a flat result union with no compile-time link to
18122
- // `trace.operation`, so narrow the operation here to select the matching
18123
- // overload. The result cast in each branch is sound: the provider always
18124
- // returns the result type corresponding to the resumed operation.
18125
- if (trace.operation === 'claimRewards') {
18691
+ const result = await withErrorTelemetry(async ()=>{
18692
+ if (!isKitError(error)) {
18693
+ throw createValidationFailedError('error', error, 'EarnKit.retry() requires a KitError thrown by a previous earn operation');
18694
+ }
18695
+ if (!isRetryableError$1(error)) {
18696
+ throw createValidationFailedError('error.recoverability', error.recoverability, 'EarnKit.retry() requires a retryable or resumable error — check isRetryableError(error) first');
18697
+ }
18698
+ const trace = error.cause?.trace;
18699
+ if (!isEarnErrorTrace(trace)) {
18700
+ throw createValidationFailedError('error.cause.trace', trace, 'EarnKit.retry() requires a KitError carrying earn retry context (operation, steps, provider, params)');
18701
+ }
18702
+ const provider = this.context.providers.find((candidate)=>candidate.name === trace.provider);
18703
+ if (provider === undefined) {
18704
+ throw createValidationFailedError('error.cause.trace.provider', trace.provider, `No earn provider named "${trace.provider}" is registered with this kit`);
18705
+ }
18706
+ const result = await provider.retry(error);
18707
+ // `provider.retry` returns a flat result union with no compile-time link to
18708
+ // `trace.operation`, so narrow the operation here to select the matching
18709
+ // overload. The result cast in each branch is sound: the provider always
18710
+ // returns the result type corresponding to the resumed operation.
18711
+ if (trace.operation === 'claimRewards') {
18712
+ return formatRetryResult(trace.operation, result);
18713
+ }
18126
18714
  return formatRetryResult(trace.operation, result);
18715
+ }, EARN_EVENT_TYPES.RETRY, this.telemetryConfig);
18716
+ const trace = isKitError(error) ? error.cause?.trace : undefined;
18717
+ if (isEarnErrorTrace(trace)) {
18718
+ emitRetrySuccessTelemetry(trace, result, this.analyticsTelemetryConfig);
18127
18719
  }
18128
- return formatRetryResult(trace.operation, result);
18720
+ return result;
18129
18721
  }
18130
18722
  /**
18131
18723
  * Return the chains supported by configured earn providers.
@@ -18159,7 +18751,9 @@ function formatRetryResult(operation, result) {
18159
18751
  * result.vaults.forEach(v => console.log(`${v.name}: ${(v.currentApy * 100).toFixed(2)}% APY`))
18160
18752
  * ```
18161
18753
  */ async getVaults(params) {
18162
- return getVaults$1(this.context, params);
18754
+ const result = await withErrorTelemetry(async ()=>getVaults$1(this.context, params), EARN_EVENT_TYPES.GET_VAULTS, this.telemetryConfig);
18755
+ emitSuccessTelemetry(EARN_EVENT_TYPES.GET_VAULTS, this.analyticsTelemetryConfig, {});
18756
+ return result;
18163
18757
  }
18164
18758
  /**
18165
18759
  * Discover vaults available on a chain.
@@ -18184,7 +18778,12 @@ function formatRetryResult(operation, result) {
18184
18778
  * const guarded = result.vaults.filter(v => v.circleGuarded) // Circle-guarded vaults only
18185
18779
  * ```
18186
18780
  */ async exploreVaults(params) {
18187
- return exploreVaults$1(this.context, params);
18781
+ const context = {
18782
+ sourceChain: resolveChainName(params.chain)
18783
+ };
18784
+ const result = await withErrorTelemetry(async ()=>exploreVaults$1(this.context, params), EARN_EVENT_TYPES.EXPLORE_VAULTS, this.telemetryConfig, context);
18785
+ emitSuccessTelemetry(EARN_EVENT_TYPES.EXPLORE_VAULTS, this.analyticsTelemetryConfig, context);
18786
+ return result;
18188
18787
  }
18189
18788
  /**
18190
18789
  * Lazily iterate every vault available on a chain.
@@ -18231,7 +18830,9 @@ function formatRetryResult(operation, result) {
18231
18830
  * }
18232
18831
  * ```
18233
18832
  */ async getPosition(params) {
18234
- return getPosition$1(this.context, params);
18833
+ return withErrorTelemetry(async ()=>getPosition$1(this.context, params), EARN_EVENT_TYPES.GET_POSITION, this.telemetryConfig, {
18834
+ sourceChain: resolveChainName(params.from.chain)
18835
+ });
18235
18836
  }
18236
18837
  /**
18237
18838
  * Fetch the current status of a cross-chain deposit by execution ID.
@@ -18256,7 +18857,7 @@ function formatRetryResult(operation, result) {
18256
18857
  * console.log(`Bridge ${status.execId} is ${status.status}`)
18257
18858
  * ```
18258
18859
  */ async getCrossChainDepositStatus(params) {
18259
- return getCrossChainDepositStatus$1(this.context, params);
18860
+ return withErrorTelemetry(async ()=>getCrossChainDepositStatus$1(this.context, params), EARN_EVENT_TYPES.GET_CROSS_CHAIN_DEPOSIT_STATUS, this.telemetryConfig);
18260
18861
  }
18261
18862
  /**
18262
18863
  * Poll a cross-chain deposit until it reaches a terminal bridge state.
@@ -18283,10 +18884,30 @@ function formatRetryResult(operation, result) {
18283
18884
  * console.log(`Bridge ended as ${result.outcome}`)
18284
18885
  * ```
18285
18886
  */ async waitForCrossChainDeposit(params) {
18286
- return waitForCrossChainDeposit$1(this.context, params);
18887
+ return withErrorTelemetry(async ()=>waitForCrossChainDeposit$1(this.context, params), EARN_EVENT_TYPES.WAIT_FOR_CROSS_CHAIN_DEPOSIT, this.telemetryConfig);
18287
18888
  }
18288
18889
  async deposit(params) {
18289
- return deposit$1(this.context, params);
18890
+ const isCrossChain = isCrossChainDeposit(params);
18891
+ const context = {
18892
+ sourceChain: resolveChainName(params.from.chain),
18893
+ ...isCrossChain && {
18894
+ destinationChain: resolveChainName(params.to.chain)
18895
+ }
18896
+ };
18897
+ const eventType = isCrossChain ? EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT : EARN_EVENT_TYPES.DEPOSIT;
18898
+ const result = await withErrorTelemetry(async ()=>deposit$1(this.context, params), eventType, this.telemetryConfig, context);
18899
+ if (result.kind === 'cross-chain') {
18900
+ emitSuccessTelemetry(EARN_EVENT_TYPES.CROSS_CHAIN_DEPOSIT, this.analyticsTelemetryConfig, {
18901
+ sourceChain: resolveChainName(result.sourceChain),
18902
+ destinationChain: resolveChainName(result.destinationChain)
18903
+ });
18904
+ } else {
18905
+ emitSuccessTelemetry(EARN_EVENT_TYPES.DEPOSIT, this.analyticsTelemetryConfig, {
18906
+ ...context,
18907
+ txHash: result.txHash
18908
+ });
18909
+ }
18910
+ return result;
18290
18911
  }
18291
18912
  /**
18292
18913
  * Execute a withdrawal from a DeFi lending vault.
@@ -18312,7 +18933,15 @@ function formatRetryResult(operation, result) {
18312
18933
  * console.log(`Withdrew ${result.amount} from ${result.vaultAddress}, tx: ${result.txHash}`)
18313
18934
  * ```
18314
18935
  */ async withdraw(params) {
18315
- return withdraw$1(this.context, params);
18936
+ const context = {
18937
+ sourceChain: resolveChainName(params.from.chain)
18938
+ };
18939
+ const result = await withErrorTelemetry(async ()=>withdraw$1(this.context, params), EARN_EVENT_TYPES.WITHDRAW, this.telemetryConfig, context);
18940
+ emitSuccessTelemetry(EARN_EVENT_TYPES.WITHDRAW, this.analyticsTelemetryConfig, {
18941
+ ...context,
18942
+ txHash: result.txHash
18943
+ });
18944
+ return result;
18316
18945
  }
18317
18946
  /**
18318
18947
  * Claim rewards from earn vaults.
@@ -18339,7 +18968,17 @@ function formatRetryResult(operation, result) {
18339
18968
  *
18340
18969
  * @internal
18341
18970
  */ async claimRewards(params) {
18342
- return claimRewards$1(this.context, params);
18971
+ const context = {
18972
+ sourceChain: resolveChainName(params.from.chain)
18973
+ };
18974
+ const result = await withErrorTelemetry(async ()=>claimRewards$1(this.context, params), EARN_EVENT_TYPES.CLAIM_REWARDS, this.telemetryConfig, context);
18975
+ if (result.status === 'claimed') {
18976
+ emitSuccessTelemetry(EARN_EVENT_TYPES.CLAIM_REWARDS, this.analyticsTelemetryConfig, {
18977
+ ...context,
18978
+ txHash: result.txHash
18979
+ });
18980
+ }
18981
+ return result;
18343
18982
  }
18344
18983
  /**
18345
18984
  * Get an informational quote for a deposit into a vault.
@@ -18361,7 +19000,9 @@ function formatRetryResult(operation, result) {
18361
19000
  * console.log(`Expected shares: ${quote.expectedShares.amount}`)
18362
19001
  * ```
18363
19002
  */ async getDepositQuote(params) {
18364
- return getDepositQuote$1(this.context, params);
19003
+ return withErrorTelemetry(async ()=>getDepositQuote$1(this.context, params), EARN_EVENT_TYPES.GET_DEPOSIT_QUOTE, this.telemetryConfig, {
19004
+ sourceChain: resolveChainName(params.from.chain)
19005
+ });
18365
19006
  }
18366
19007
  /**
18367
19008
  * Get an informational quote for a withdrawal from a vault.
@@ -18383,7 +19024,9 @@ function formatRetryResult(operation, result) {
18383
19024
  * console.log(`Shares to redeem: ${quote.sharesToRedeem.amount}`)
18384
19025
  * ```
18385
19026
  */ async getWithdrawalQuote(params) {
18386
- return getWithdrawalQuote$1(this.context, params);
19027
+ return withErrorTelemetry(async ()=>getWithdrawalQuote$1(this.context, params), EARN_EVENT_TYPES.GET_WITHDRAWAL_QUOTE, this.telemetryConfig, {
19028
+ sourceChain: resolveChainName(params.from.chain)
19029
+ });
18387
19030
  }
18388
19031
  /**
18389
19032
  * Get an informational quote for claiming rewards.
@@ -18405,7 +19048,9 @@ function formatRetryResult(operation, result) {
18405
19048
  *
18406
19049
  * @internal
18407
19050
  */ async getClaimRewardsQuote(params) {
18408
- return getClaimRewardsQuote$1(this.context, params);
19051
+ return withErrorTelemetry(async ()=>getClaimRewardsQuote$1(this.context, params), EARN_EVENT_TYPES.GET_CLAIM_REWARDS_QUOTE, this.telemetryConfig, {
19052
+ sourceChain: resolveChainName(params.from.chain)
19053
+ });
18409
19054
  }
18410
19055
  }
18411
19056
 
@@ -18494,7 +19139,14 @@ registerKit(`${pkg$1.name}/${pkg$1.version}`);
18494
19139
  * const earnKit = createEarnKit(context)
18495
19140
  * ```
18496
19141
  */ const createEarnKit = (context)=>{
18497
- const kit = new EarnKit();
19142
+ const kit = new EarnKit({
19143
+ ...context.disableErrorReporting != null && {
19144
+ disableErrorReporting: context.disableErrorReporting
19145
+ },
19146
+ ...context.disableAnalytics != null && {
19147
+ disableAnalytics: context.disableAnalytics
19148
+ }
19149
+ });
18498
19150
  registerActionHandlers(kit, context.actions.earn, 'earn');
18499
19151
  return kit;
18500
19152
  };