@cross-deck/web 1.6.1 → 1.6.2

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/dist/index.mjs CHANGED
@@ -64,7 +64,7 @@ function typeMapForStatus(status) {
64
64
  }
65
65
 
66
66
  // src/_version.ts
67
- var SDK_VERSION = "1.6.1";
67
+ var SDK_VERSION = "1.6.2";
68
68
  var SDK_NAME = "@cross-deck/web";
69
69
 
70
70
  // src/http.ts
@@ -1489,6 +1489,13 @@ var AutoTracker = class {
1489
1489
  markActivity() {
1490
1490
  if (!this.session) return;
1491
1491
  const now = Date.now();
1492
+ if (now - this.session.lastActivityAt >= SESSION_RESUME_THRESHOLD_MS) {
1493
+ this.pageviewId = null;
1494
+ this.session = this.startNewSession();
1495
+ this.persistSession();
1496
+ this.emitSessionStart();
1497
+ return;
1498
+ }
1492
1499
  this.session.lastActivityAt = now;
1493
1500
  if (now - this.lastPersistAt >= ACTIVITY_PERSIST_THROTTLE_MS) {
1494
1501
  this.persistSession();
@@ -1542,7 +1549,6 @@ var AutoTracker = class {
1542
1549
  } else if (doc2.visibilityState === "visible") {
1543
1550
  const idleFor = Date.now() - this.session.lastActivityAt;
1544
1551
  if (idleFor >= SESSION_RESUME_THRESHOLD_MS) {
1545
- this.emitSessionEnd();
1546
1552
  this.pageviewId = null;
1547
1553
  this.session = this.startNewSession();
1548
1554
  this.persistSession();
@@ -2604,6 +2610,210 @@ function sendDiagnosticTelemetry(payload) {
2604
2610
  }
2605
2611
  }
2606
2612
 
2613
+ // src/error-codes.ts
2614
+ var CROSSDECK_ERROR_CODES = Object.freeze([
2615
+ // ----- Configuration -----
2616
+ {
2617
+ code: "invalid_public_key",
2618
+ type: "configuration_error",
2619
+ description: "The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.",
2620
+ resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys page.",
2621
+ retryable: false
2622
+ },
2623
+ {
2624
+ code: "missing_app_id",
2625
+ type: "configuration_error",
2626
+ description: "Crossdeck.init() was called without an appId.",
2627
+ resolution: "Add appId to your init options \u2014 find it in the dashboard's Apps page.",
2628
+ retryable: false
2629
+ },
2630
+ {
2631
+ code: "invalid_environment",
2632
+ type: "configuration_error",
2633
+ description: "Crossdeck.init() requires environment: 'production' | 'sandbox'.",
2634
+ resolution: 'Pass the literal string "production" or "sandbox" \u2014 no other values are accepted.',
2635
+ retryable: false
2636
+ },
2637
+ {
2638
+ code: "environment_mismatch",
2639
+ type: "configuration_error",
2640
+ description: "The publishable key's env prefix doesn't match the declared environment option.",
2641
+ resolution: "Either change `environment` to match the key prefix (cd_pub_test_ \u2194 sandbox, cd_pub_live_ \u2194 production), or swap the key for one minted in the right env.",
2642
+ retryable: false
2643
+ },
2644
+ {
2645
+ code: "not_initialized",
2646
+ type: "configuration_error",
2647
+ description: "An SDK method was called before Crossdeck.init().",
2648
+ resolution: "Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.",
2649
+ retryable: false
2650
+ },
2651
+ // ----- Identify / track / purchase argument validation -----
2652
+ {
2653
+ code: "missing_user_id",
2654
+ type: "invalid_request_error",
2655
+ description: "identify() was called with an empty userId.",
2656
+ resolution: "Pass a stable, non-empty user identifier from your auth layer \u2014 never a hardcoded placeholder.",
2657
+ retryable: false
2658
+ },
2659
+ {
2660
+ code: "missing_event_name",
2661
+ type: "invalid_request_error",
2662
+ description: "track() was called without an event name.",
2663
+ resolution: "Pass a non-empty string as the first argument.",
2664
+ retryable: false
2665
+ },
2666
+ {
2667
+ code: "missing_group_type",
2668
+ type: "invalid_request_error",
2669
+ description: "group() was called without a group type.",
2670
+ resolution: 'Pass a non-empty type (e.g. "org", "team") as the first argument.',
2671
+ retryable: false
2672
+ },
2673
+ {
2674
+ code: "missing_signed_transaction_info",
2675
+ type: "invalid_request_error",
2676
+ description: "syncPurchases() was called without StoreKit 2 signed transaction info.",
2677
+ resolution: "Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.",
2678
+ retryable: false
2679
+ },
2680
+ // ----- Network / transport -----
2681
+ {
2682
+ code: "fetch_failed",
2683
+ type: "network_error",
2684
+ description: "The underlying fetch() call failed (typically a network outage or DNS issue).",
2685
+ resolution: "Check the user's network. The SDK will retry automatically with exponential backoff.",
2686
+ retryable: true
2687
+ },
2688
+ {
2689
+ code: "request_timeout",
2690
+ type: "network_error",
2691
+ description: "A request was aborted after the configured timeoutMs (default 15s).",
2692
+ resolution: "Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.",
2693
+ retryable: true
2694
+ },
2695
+ {
2696
+ code: "invalid_json_response",
2697
+ type: "internal_error",
2698
+ description: "The server returned a 2xx with an unparseable body.",
2699
+ resolution: "Likely a transient backend bug. Retry; if it persists, contact support with the requestId.",
2700
+ retryable: true
2701
+ },
2702
+ // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----
2703
+ // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer
2704
+ // hitting any of these on the wire can look them up via
2705
+ // getErrorCode(code) for a canonical remediation step instead of
2706
+ // hunting through Slack history.
2707
+ {
2708
+ code: "missing_api_key",
2709
+ type: "authentication_error",
2710
+ description: "No Authorization header (or Crossdeck-Api-Key header) on the request.",
2711
+ resolution: "Make sure Crossdeck.init({ publicKey }) was called with a cd_pub_\u2026 key before the request fired. Re-check your env-vars in CI / Docker if the key is empty at runtime.",
2712
+ retryable: false
2713
+ },
2714
+ {
2715
+ code: "invalid_api_key",
2716
+ type: "authentication_error",
2717
+ description: "The API key is malformed, unknown, or doesn't resolve to a project.",
2718
+ resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys. Confirm prefix is cd_pub_test_ / cd_pub_live_ for client SDKs and cd_sk_test_ / cd_sk_live_ for the Node server SDK.",
2719
+ retryable: false
2720
+ },
2721
+ {
2722
+ code: "key_revoked",
2723
+ type: "authentication_error",
2724
+ description: "The API key was revoked in the Crossdeck dashboard.",
2725
+ resolution: "Mint a fresh key in the dashboard \u2192 API keys \u2192 Create new, swap it in, and redeploy. The revoked key cannot be reactivated.",
2726
+ retryable: false
2727
+ },
2728
+ {
2729
+ code: "identity_token_invalid",
2730
+ type: "authentication_error",
2731
+ description: "The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.",
2732
+ resolution: "Refresh the token client-side (Firebase auth.currentUser.getIdToken(true)) and retry. If the failure persists, confirm the signer is registered under dashboard \u2192 Authentication \u2192 Identity sources.",
2733
+ retryable: true
2734
+ },
2735
+ {
2736
+ code: "origin_not_allowed",
2737
+ type: "permission_error",
2738
+ description: "The Origin header isn't in the project's Allowed origins list.",
2739
+ resolution: "Add the origin (e.g. https://app.example.com) under dashboard \u2192 Settings \u2192 Allowed origins. Wildcards like https://*.example.com are supported.",
2740
+ retryable: false
2741
+ },
2742
+ {
2743
+ code: "bundle_id_not_allowed",
2744
+ type: "permission_error",
2745
+ description: "The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.",
2746
+ resolution: "Add the bundle ID under dashboard \u2192 Apps \u2192 <your app> \u2192 iOS bundle IDs.",
2747
+ retryable: false
2748
+ },
2749
+ {
2750
+ code: "package_name_not_allowed",
2751
+ type: "permission_error",
2752
+ description: "The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.",
2753
+ resolution: "Add the package name under dashboard \u2192 Apps \u2192 <your app> \u2192 Android package names.",
2754
+ retryable: false
2755
+ },
2756
+ {
2757
+ code: "env_mismatch",
2758
+ type: "permission_error",
2759
+ description: "The request env (inferred from key prefix) doesn't match the resolved app's configured env.",
2760
+ resolution: "Use a cd_pub_live_ / cd_sk_live_ key with a production app, cd_pub_test_ / cd_sk_test_ with a sandbox app. The two cannot cross.",
2761
+ retryable: false
2762
+ },
2763
+ {
2764
+ code: "idempotency_key_in_use",
2765
+ type: "invalid_request_error",
2766
+ description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).",
2767
+ resolution: "Generate a fresh key for a different transaction, or reuse the key only with the EXACT same body. The v1.4.0 SDKs derive keys deterministically from the body so this should never fire on SDK-managed calls.",
2768
+ retryable: false
2769
+ },
2770
+ {
2771
+ code: "rate_limited",
2772
+ type: "rate_limit_error",
2773
+ description: "Request rate exceeded the project's per-second cap.",
2774
+ resolution: "Honour the Retry-After header \u2014 the SDK does this automatically on managed retries. If you're hitting it from a custom code path, throttle to <100 req/s/key.",
2775
+ retryable: true
2776
+ },
2777
+ {
2778
+ code: "internal_error",
2779
+ type: "internal_error",
2780
+ description: "Server-side issue. Safe to retry with backoff.",
2781
+ resolution: "The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.",
2782
+ retryable: true
2783
+ },
2784
+ {
2785
+ code: "google_not_supported",
2786
+ type: "invalid_request_error",
2787
+ description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.",
2788
+ resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.",
2789
+ retryable: false
2790
+ },
2791
+ {
2792
+ code: "stripe_not_supported",
2793
+ type: "invalid_request_error",
2794
+ description: "POST /purchases/sync with rail=stripe is unsupported \u2014 Stripe Checkout's redirect flow uses platform webhooks instead.",
2795
+ resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.",
2796
+ retryable: false
2797
+ },
2798
+ {
2799
+ code: "missing_required_param",
2800
+ type: "invalid_request_error",
2801
+ description: "A required field is absent from the request body.",
2802
+ resolution: "Inspect the error.message \u2014 the missing field name is included verbatim. Refer to the SDK's TypeScript types for the canonical request shape.",
2803
+ retryable: false
2804
+ },
2805
+ {
2806
+ code: "invalid_param_value",
2807
+ type: "invalid_request_error",
2808
+ description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
2809
+ resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
2810
+ retryable: false
2811
+ }
2812
+ ]);
2813
+ function getErrorCode(code) {
2814
+ return CROSSDECK_ERROR_CODES.find((e) => e.code === code);
2815
+ }
2816
+
2607
2817
  // src/_contract-verifiers.ts
2608
2818
  function buildVerifierContext(opts) {
2609
2819
  return {
@@ -3128,13 +3338,64 @@ var VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK = {
3128
3338
  );
3129
3339
  }
3130
3340
  };
3341
+ var BACKEND_WIRE_CODES = Object.freeze([
3342
+ "missing_api_key",
3343
+ "invalid_api_key",
3344
+ "key_revoked",
3345
+ "identity_token_invalid",
3346
+ "origin_not_allowed",
3347
+ "bundle_id_not_allowed",
3348
+ "package_name_not_allowed",
3349
+ "env_mismatch",
3350
+ "idempotency_key_in_use",
3351
+ "rate_limited",
3352
+ "internal_error",
3353
+ "google_not_supported",
3354
+ "stripe_not_supported",
3355
+ "missing_required_param",
3356
+ "invalid_param_value"
3357
+ ]);
3358
+ var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3359
+ contractId: "sdk-error-codes-catalogue",
3360
+ bootTest() {
3361
+ const t0 = nowMs();
3362
+ try {
3363
+ const missing = [];
3364
+ for (const code of BACKEND_WIRE_CODES) {
3365
+ const entry = getErrorCode(code);
3366
+ if (!entry || !entry.description || entry.description.trim().length === 0 || !entry.resolution || entry.resolution.trim().length === 0) {
3367
+ missing.push(code);
3368
+ }
3369
+ }
3370
+ if (missing.length > 0) {
3371
+ return fail(
3372
+ "sdk-error-codes-catalogue",
3373
+ `catalogue missing description+resolution for backend code(s): ${missing.join(", ")}`,
3374
+ nowMs() - t0
3375
+ );
3376
+ }
3377
+ return pass(
3378
+ "sdk-error-codes-catalogue",
3379
+ `all ${BACKEND_WIRE_CODES.length} backend wire codes carry description + resolution`,
3380
+ nowMs() - t0
3381
+ );
3382
+ } catch (err) {
3383
+ return fail(
3384
+ "sdk-error-codes-catalogue",
3385
+ `boot test threw: ${err.message?.slice(0, 80) ?? "unknown error"}`,
3386
+ nowMs() - t0
3387
+ );
3388
+ }
3389
+ }
3390
+ };
3131
3391
  var STATIC_VERIFIERS = Object.freeze([
3132
3392
  VERIFIER_PER_USER_CACHE_ISOLATION,
3133
3393
  VERIFIER_IDEMPOTENCY_KEY_DETERMINISTIC,
3134
3394
  VERIFIER_ERROR_ENVELOPE_SHAPE,
3135
3395
  VERIFIER_FLUSH_INTERVAL_PARITY,
3136
3396
  VERIFIER_SUPER_PROPERTY_MERGE_PRECEDENCE,
3137
- VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK
3397
+ VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK,
3398
+ VERIFIER_SDK_ERROR_CODES_CATALOGUE
3138
3399
  ]);
3139
3400
  async function runBootSelfTest(verifiers, reporter, ctx) {
3140
3401
  if (ctx.disableContractAssertions) {
@@ -5286,213 +5547,9 @@ function installUnloadFlush(onUnload) {
5286
5547
  };
5287
5548
  }
5288
5549
 
5289
- // src/error-codes.ts
5290
- var CROSSDECK_ERROR_CODES = Object.freeze([
5291
- // ----- Configuration -----
5292
- {
5293
- code: "invalid_public_key",
5294
- type: "configuration_error",
5295
- description: "The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.",
5296
- resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys page.",
5297
- retryable: false
5298
- },
5299
- {
5300
- code: "missing_app_id",
5301
- type: "configuration_error",
5302
- description: "Crossdeck.init() was called without an appId.",
5303
- resolution: "Add appId to your init options \u2014 find it in the dashboard's Apps page.",
5304
- retryable: false
5305
- },
5306
- {
5307
- code: "invalid_environment",
5308
- type: "configuration_error",
5309
- description: "Crossdeck.init() requires environment: 'production' | 'sandbox'.",
5310
- resolution: 'Pass the literal string "production" or "sandbox" \u2014 no other values are accepted.',
5311
- retryable: false
5312
- },
5313
- {
5314
- code: "environment_mismatch",
5315
- type: "configuration_error",
5316
- description: "The publishable key's env prefix doesn't match the declared environment option.",
5317
- resolution: "Either change `environment` to match the key prefix (cd_pub_test_ \u2194 sandbox, cd_pub_live_ \u2194 production), or swap the key for one minted in the right env.",
5318
- retryable: false
5319
- },
5320
- {
5321
- code: "not_initialized",
5322
- type: "configuration_error",
5323
- description: "An SDK method was called before Crossdeck.init().",
5324
- resolution: "Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.",
5325
- retryable: false
5326
- },
5327
- // ----- Identify / track / purchase argument validation -----
5328
- {
5329
- code: "missing_user_id",
5330
- type: "invalid_request_error",
5331
- description: "identify() was called with an empty userId.",
5332
- resolution: "Pass a stable, non-empty user identifier from your auth layer \u2014 never a hardcoded placeholder.",
5333
- retryable: false
5334
- },
5335
- {
5336
- code: "missing_event_name",
5337
- type: "invalid_request_error",
5338
- description: "track() was called without an event name.",
5339
- resolution: "Pass a non-empty string as the first argument.",
5340
- retryable: false
5341
- },
5342
- {
5343
- code: "missing_group_type",
5344
- type: "invalid_request_error",
5345
- description: "group() was called without a group type.",
5346
- resolution: 'Pass a non-empty type (e.g. "org", "team") as the first argument.',
5347
- retryable: false
5348
- },
5349
- {
5350
- code: "missing_signed_transaction_info",
5351
- type: "invalid_request_error",
5352
- description: "syncPurchases() was called without StoreKit 2 signed transaction info.",
5353
- resolution: "Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.",
5354
- retryable: false
5355
- },
5356
- // ----- Network / transport -----
5357
- {
5358
- code: "fetch_failed",
5359
- type: "network_error",
5360
- description: "The underlying fetch() call failed (typically a network outage or DNS issue).",
5361
- resolution: "Check the user's network. The SDK will retry automatically with exponential backoff.",
5362
- retryable: true
5363
- },
5364
- {
5365
- code: "request_timeout",
5366
- type: "network_error",
5367
- description: "A request was aborted after the configured timeoutMs (default 15s).",
5368
- resolution: "Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.",
5369
- retryable: true
5370
- },
5371
- {
5372
- code: "invalid_json_response",
5373
- type: "internal_error",
5374
- description: "The server returned a 2xx with an unparseable body.",
5375
- resolution: "Likely a transient backend bug. Retry; if it persists, contact support with the requestId.",
5376
- retryable: true
5377
- },
5378
- // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----
5379
- // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer
5380
- // hitting any of these on the wire can look them up via
5381
- // getErrorCode(code) for a canonical remediation step instead of
5382
- // hunting through Slack history.
5383
- {
5384
- code: "missing_api_key",
5385
- type: "authentication_error",
5386
- description: "No Authorization header (or Crossdeck-Api-Key header) on the request.",
5387
- resolution: "Make sure Crossdeck.init({ publicKey }) was called with a cd_pub_\u2026 key before the request fired. Re-check your env-vars in CI / Docker if the key is empty at runtime.",
5388
- retryable: false
5389
- },
5390
- {
5391
- code: "invalid_api_key",
5392
- type: "authentication_error",
5393
- description: "The API key is malformed, unknown, or doesn't resolve to a project.",
5394
- resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys. Confirm prefix is cd_pub_test_ / cd_pub_live_ for client SDKs and cd_sk_test_ / cd_sk_live_ for the Node server SDK.",
5395
- retryable: false
5396
- },
5397
- {
5398
- code: "key_revoked",
5399
- type: "authentication_error",
5400
- description: "The API key was revoked in the Crossdeck dashboard.",
5401
- resolution: "Mint a fresh key in the dashboard \u2192 API keys \u2192 Create new, swap it in, and redeploy. The revoked key cannot be reactivated.",
5402
- retryable: false
5403
- },
5404
- {
5405
- code: "identity_token_invalid",
5406
- type: "authentication_error",
5407
- description: "The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.",
5408
- resolution: "Refresh the token client-side (Firebase auth.currentUser.getIdToken(true)) and retry. If the failure persists, confirm the signer is registered under dashboard \u2192 Authentication \u2192 Identity sources.",
5409
- retryable: true
5410
- },
5411
- {
5412
- code: "origin_not_allowed",
5413
- type: "permission_error",
5414
- description: "The Origin header isn't in the project's Allowed origins list.",
5415
- resolution: "Add the origin (e.g. https://app.example.com) under dashboard \u2192 Settings \u2192 Allowed origins. Wildcards like https://*.example.com are supported.",
5416
- retryable: false
5417
- },
5418
- {
5419
- code: "bundle_id_not_allowed",
5420
- type: "permission_error",
5421
- description: "The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.",
5422
- resolution: "Add the bundle ID under dashboard \u2192 Apps \u2192 <your app> \u2192 iOS bundle IDs.",
5423
- retryable: false
5424
- },
5425
- {
5426
- code: "package_name_not_allowed",
5427
- type: "permission_error",
5428
- description: "The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.",
5429
- resolution: "Add the package name under dashboard \u2192 Apps \u2192 <your app> \u2192 Android package names.",
5430
- retryable: false
5431
- },
5432
- {
5433
- code: "env_mismatch",
5434
- type: "permission_error",
5435
- description: "The request env (inferred from key prefix) doesn't match the resolved app's configured env.",
5436
- resolution: "Use a cd_pub_live_ / cd_sk_live_ key with a production app, cd_pub_test_ / cd_sk_test_ with a sandbox app. The two cannot cross.",
5437
- retryable: false
5438
- },
5439
- {
5440
- code: "idempotency_key_in_use",
5441
- type: "invalid_request_error",
5442
- description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).",
5443
- resolution: "Generate a fresh key for a different transaction, or reuse the key only with the EXACT same body. The v1.4.0 SDKs derive keys deterministically from the body so this should never fire on SDK-managed calls.",
5444
- retryable: false
5445
- },
5446
- {
5447
- code: "rate_limited",
5448
- type: "rate_limit_error",
5449
- description: "Request rate exceeded the project's per-second cap.",
5450
- resolution: "Honour the Retry-After header \u2014 the SDK does this automatically on managed retries. If you're hitting it from a custom code path, throttle to <100 req/s/key.",
5451
- retryable: true
5452
- },
5453
- {
5454
- code: "internal_error",
5455
- type: "internal_error",
5456
- description: "Server-side issue. Safe to retry with backoff.",
5457
- resolution: "The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.",
5458
- retryable: true
5459
- },
5460
- {
5461
- code: "google_not_supported",
5462
- type: "invalid_request_error",
5463
- description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.",
5464
- resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.",
5465
- retryable: false
5466
- },
5467
- {
5468
- code: "stripe_not_supported",
5469
- type: "invalid_request_error",
5470
- description: "POST /purchases/sync with rail=stripe is unsupported \u2014 Stripe Checkout's redirect flow uses platform webhooks instead.",
5471
- resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.",
5472
- retryable: false
5473
- },
5474
- {
5475
- code: "missing_required_param",
5476
- type: "invalid_request_error",
5477
- description: "A required field is absent from the request body.",
5478
- resolution: "Inspect the error.message \u2014 the missing field name is included verbatim. Refer to the SDK's TypeScript types for the canonical request shape.",
5479
- retryable: false
5480
- },
5481
- {
5482
- code: "invalid_param_value",
5483
- type: "invalid_request_error",
5484
- description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
5485
- resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
5486
- retryable: false
5487
- }
5488
- ]);
5489
- function getErrorCode(code) {
5490
- return CROSSDECK_ERROR_CODES.find((e) => e.code === code);
5491
- }
5492
-
5493
5550
  // src/_contracts-bundled.ts
5494
- var BUNDLED_IN = "@cross-deck/web@1.6.1";
5495
- var SDK_VERSION2 = "1.6.1";
5551
+ var BUNDLED_IN = "@cross-deck/web@1.6.2";
5552
+ var SDK_VERSION2 = "1.6.2";
5496
5553
  var BUNDLED_CONTRACTS = Object.freeze([
5497
5554
  {
5498
5555
  "id": "contract-failed-payload-schema-lock",
@@ -5614,7 +5671,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5614
5671
  "legal/security/index.html#diagnostic",
5615
5672
  "legal/sdk-data/index.html#b-diagnostic"
5616
5673
  ],
5617
- "bundledIn": "@cross-deck/web@1.6.1"
5674
+ "bundledIn": "@cross-deck/web@1.6.2",
5675
+ "runtimeVerified": true
5618
5676
  },
5619
5677
  {
5620
5678
  "id": "error-envelope-shape",
@@ -5653,7 +5711,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5653
5711
  ],
5654
5712
  "registeredAt": "2026-05-26",
5655
5713
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
5656
- "bundledIn": "@cross-deck/web@1.6.1"
5714
+ "bundledIn": "@cross-deck/web@1.6.2",
5715
+ "runtimeVerified": true
5657
5716
  },
5658
5717
  {
5659
5718
  "id": "flush-interval-parity",
@@ -5698,7 +5757,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5698
5757
  ],
5699
5758
  "registeredAt": "2026-05-26",
5700
5759
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
5701
- "bundledIn": "@cross-deck/web@1.6.1"
5760
+ "bundledIn": "@cross-deck/web@1.6.2",
5761
+ "runtimeVerified": true
5702
5762
  },
5703
5763
  {
5704
5764
  "id": "idempotency-key-deterministic",
@@ -5803,7 +5863,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5803
5863
  ],
5804
5864
  "registeredAt": "2026-05-26",
5805
5865
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
5806
- "bundledIn": "@cross-deck/web@1.6.1"
5866
+ "bundledIn": "@cross-deck/web@1.6.2",
5867
+ "runtimeVerified": true
5807
5868
  },
5808
5869
  {
5809
5870
  "id": "init-reentry-drains-prior-queue",
@@ -5830,7 +5891,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5830
5891
  ],
5831
5892
  "registeredAt": "2026-05-26",
5832
5893
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
5833
- "bundledIn": "@cross-deck/web@1.6.1"
5894
+ "bundledIn": "@cross-deck/web@1.6.2",
5895
+ "runtimeVerified": false
5834
5896
  },
5835
5897
  {
5836
5898
  "id": "per-user-cache-isolation",
@@ -5909,7 +5971,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5909
5971
  ],
5910
5972
  "registeredAt": "2026-05-26",
5911
5973
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
5912
- "bundledIn": "@cross-deck/web@1.6.1"
5974
+ "bundledIn": "@cross-deck/web@1.6.2",
5975
+ "runtimeVerified": true
5913
5976
  },
5914
5977
  {
5915
5978
  "id": "sdk-error-codes-catalogue",
@@ -5923,9 +5986,14 @@ var BUNDLED_CONTRACTS = Object.freeze([
5923
5986
  "codeRef": [
5924
5987
  "sdks/web/src/error-codes.ts",
5925
5988
  "sdks/node/src/error-codes.ts",
5989
+ "sdks/web/src/_contract-verifiers.ts",
5926
5990
  "backend/src/api/v1-errors.ts"
5927
5991
  ],
5928
5992
  "testRef": [
5993
+ {
5994
+ "file": "sdks/web/tests/contract-verifiers.test.ts",
5995
+ "name": "sdk-error-codes-catalogue covers every backend wire code with remediation"
5996
+ },
5929
5997
  {
5930
5998
  "file": "sdks/web/tests/error-codes-backfill.test.ts",
5931
5999
  "name": "includes backend code"
@@ -5949,7 +6017,50 @@ var BUNDLED_CONTRACTS = Object.freeze([
5949
6017
  ],
5950
6018
  "registeredAt": "2026-05-26",
5951
6019
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
5952
- "bundledIn": "@cross-deck/web@1.6.1"
6020
+ "bundledIn": "@cross-deck/web@1.6.2",
6021
+ "runtimeVerified": true
6022
+ },
6023
+ {
6024
+ "id": "super-property-merge-precedence",
6025
+ "pillar": "analytics",
6026
+ "status": "enforced",
6027
+ "claim": "Every Crossdeck SDK merges event properties with the precedence device < super < caller (caller-supplied values win over registered super-properties, which win over auto-attached device info). Pre-v1.4.0 Swift had it INVERTED (super < device < caller \u2014 device clobbered super), so a `register('plan', 'pro')` super-property was silently overridden by auto-attached device fields whenever keys collided. Cross-SDK funnel queries on super-property keys returned different answers per platform.",
6028
+ "appliesTo": [
6029
+ "web",
6030
+ "swift"
6031
+ ],
6032
+ "codeRef": [
6033
+ "sdks/web/src/super-properties.ts",
6034
+ "sdks/web/src/_contract-verifiers.ts",
6035
+ "sdks/swift/Sources/Crossdeck/EventPropertyMerge.swift",
6036
+ "sdks/swift/Sources/Crossdeck/Crossdeck.swift"
6037
+ ],
6038
+ "testRef": [
6039
+ {
6040
+ "file": "sdks/web/tests/contract-verifiers.test.ts",
6041
+ "name": "super-property-merge-precedence verifies caller > super > device"
6042
+ },
6043
+ {
6044
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6045
+ "name": "test_super_overrides_device"
6046
+ },
6047
+ {
6048
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6049
+ "name": "test_caller_overrides_super"
6050
+ },
6051
+ {
6052
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6053
+ "name": "test_full_precedence_chain"
6054
+ },
6055
+ {
6056
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6057
+ "name": "test_matchesWebNodeRNPrecedence"
6058
+ }
6059
+ ],
6060
+ "registeredAt": "2026-05-26",
6061
+ "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6062
+ "bundledIn": "@cross-deck/web@1.6.2",
6063
+ "runtimeVerified": true
5953
6064
  },
5954
6065
  {
5955
6066
  "id": "sync-purchases-funnel-parity",
@@ -5982,7 +6093,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5982
6093
  ],
5983
6094
  "registeredAt": "2026-05-26",
5984
6095
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
5985
- "bundledIn": "@cross-deck/web@1.6.1"
6096
+ "bundledIn": "@cross-deck/web@1.6.2",
6097
+ "runtimeVerified": false
5986
6098
  }
5987
6099
  ]);
5988
6100