@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "generatedAt": "2026-05-30T11:19:57.320Z",
3
+ "generatedAt": "2026-06-01T05:25:25.597Z",
4
4
  "sdk": "@cross-deck/web",
5
5
  "codes": [
6
6
  {
package/dist/index.cjs CHANGED
@@ -99,7 +99,7 @@ function typeMapForStatus(status) {
99
99
  }
100
100
 
101
101
  // src/_version.ts
102
- var SDK_VERSION = "1.6.1";
102
+ var SDK_VERSION = "1.6.2";
103
103
  var SDK_NAME = "@cross-deck/web";
104
104
 
105
105
  // src/http.ts
@@ -1524,6 +1524,13 @@ var AutoTracker = class {
1524
1524
  markActivity() {
1525
1525
  if (!this.session) return;
1526
1526
  const now = Date.now();
1527
+ if (now - this.session.lastActivityAt >= SESSION_RESUME_THRESHOLD_MS) {
1528
+ this.pageviewId = null;
1529
+ this.session = this.startNewSession();
1530
+ this.persistSession();
1531
+ this.emitSessionStart();
1532
+ return;
1533
+ }
1527
1534
  this.session.lastActivityAt = now;
1528
1535
  if (now - this.lastPersistAt >= ACTIVITY_PERSIST_THROTTLE_MS) {
1529
1536
  this.persistSession();
@@ -1577,7 +1584,6 @@ var AutoTracker = class {
1577
1584
  } else if (doc2.visibilityState === "visible") {
1578
1585
  const idleFor = Date.now() - this.session.lastActivityAt;
1579
1586
  if (idleFor >= SESSION_RESUME_THRESHOLD_MS) {
1580
- this.emitSessionEnd();
1581
1587
  this.pageviewId = null;
1582
1588
  this.session = this.startNewSession();
1583
1589
  this.persistSession();
@@ -2639,6 +2645,210 @@ function sendDiagnosticTelemetry(payload) {
2639
2645
  }
2640
2646
  }
2641
2647
 
2648
+ // src/error-codes.ts
2649
+ var CROSSDECK_ERROR_CODES = Object.freeze([
2650
+ // ----- Configuration -----
2651
+ {
2652
+ code: "invalid_public_key",
2653
+ type: "configuration_error",
2654
+ description: "The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.",
2655
+ resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys page.",
2656
+ retryable: false
2657
+ },
2658
+ {
2659
+ code: "missing_app_id",
2660
+ type: "configuration_error",
2661
+ description: "Crossdeck.init() was called without an appId.",
2662
+ resolution: "Add appId to your init options \u2014 find it in the dashboard's Apps page.",
2663
+ retryable: false
2664
+ },
2665
+ {
2666
+ code: "invalid_environment",
2667
+ type: "configuration_error",
2668
+ description: "Crossdeck.init() requires environment: 'production' | 'sandbox'.",
2669
+ resolution: 'Pass the literal string "production" or "sandbox" \u2014 no other values are accepted.',
2670
+ retryable: false
2671
+ },
2672
+ {
2673
+ code: "environment_mismatch",
2674
+ type: "configuration_error",
2675
+ description: "The publishable key's env prefix doesn't match the declared environment option.",
2676
+ 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.",
2677
+ retryable: false
2678
+ },
2679
+ {
2680
+ code: "not_initialized",
2681
+ type: "configuration_error",
2682
+ description: "An SDK method was called before Crossdeck.init().",
2683
+ resolution: "Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.",
2684
+ retryable: false
2685
+ },
2686
+ // ----- Identify / track / purchase argument validation -----
2687
+ {
2688
+ code: "missing_user_id",
2689
+ type: "invalid_request_error",
2690
+ description: "identify() was called with an empty userId.",
2691
+ resolution: "Pass a stable, non-empty user identifier from your auth layer \u2014 never a hardcoded placeholder.",
2692
+ retryable: false
2693
+ },
2694
+ {
2695
+ code: "missing_event_name",
2696
+ type: "invalid_request_error",
2697
+ description: "track() was called without an event name.",
2698
+ resolution: "Pass a non-empty string as the first argument.",
2699
+ retryable: false
2700
+ },
2701
+ {
2702
+ code: "missing_group_type",
2703
+ type: "invalid_request_error",
2704
+ description: "group() was called without a group type.",
2705
+ resolution: 'Pass a non-empty type (e.g. "org", "team") as the first argument.',
2706
+ retryable: false
2707
+ },
2708
+ {
2709
+ code: "missing_signed_transaction_info",
2710
+ type: "invalid_request_error",
2711
+ description: "syncPurchases() was called without StoreKit 2 signed transaction info.",
2712
+ resolution: "Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.",
2713
+ retryable: false
2714
+ },
2715
+ // ----- Network / transport -----
2716
+ {
2717
+ code: "fetch_failed",
2718
+ type: "network_error",
2719
+ description: "The underlying fetch() call failed (typically a network outage or DNS issue).",
2720
+ resolution: "Check the user's network. The SDK will retry automatically with exponential backoff.",
2721
+ retryable: true
2722
+ },
2723
+ {
2724
+ code: "request_timeout",
2725
+ type: "network_error",
2726
+ description: "A request was aborted after the configured timeoutMs (default 15s).",
2727
+ resolution: "Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.",
2728
+ retryable: true
2729
+ },
2730
+ {
2731
+ code: "invalid_json_response",
2732
+ type: "internal_error",
2733
+ description: "The server returned a 2xx with an unparseable body.",
2734
+ resolution: "Likely a transient backend bug. Retry; if it persists, contact support with the requestId.",
2735
+ retryable: true
2736
+ },
2737
+ // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----
2738
+ // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer
2739
+ // hitting any of these on the wire can look them up via
2740
+ // getErrorCode(code) for a canonical remediation step instead of
2741
+ // hunting through Slack history.
2742
+ {
2743
+ code: "missing_api_key",
2744
+ type: "authentication_error",
2745
+ description: "No Authorization header (or Crossdeck-Api-Key header) on the request.",
2746
+ 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.",
2747
+ retryable: false
2748
+ },
2749
+ {
2750
+ code: "invalid_api_key",
2751
+ type: "authentication_error",
2752
+ description: "The API key is malformed, unknown, or doesn't resolve to a project.",
2753
+ 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.",
2754
+ retryable: false
2755
+ },
2756
+ {
2757
+ code: "key_revoked",
2758
+ type: "authentication_error",
2759
+ description: "The API key was revoked in the Crossdeck dashboard.",
2760
+ 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.",
2761
+ retryable: false
2762
+ },
2763
+ {
2764
+ code: "identity_token_invalid",
2765
+ type: "authentication_error",
2766
+ description: "The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.",
2767
+ 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.",
2768
+ retryable: true
2769
+ },
2770
+ {
2771
+ code: "origin_not_allowed",
2772
+ type: "permission_error",
2773
+ description: "The Origin header isn't in the project's Allowed origins list.",
2774
+ resolution: "Add the origin (e.g. https://app.example.com) under dashboard \u2192 Settings \u2192 Allowed origins. Wildcards like https://*.example.com are supported.",
2775
+ retryable: false
2776
+ },
2777
+ {
2778
+ code: "bundle_id_not_allowed",
2779
+ type: "permission_error",
2780
+ description: "The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.",
2781
+ resolution: "Add the bundle ID under dashboard \u2192 Apps \u2192 <your app> \u2192 iOS bundle IDs.",
2782
+ retryable: false
2783
+ },
2784
+ {
2785
+ code: "package_name_not_allowed",
2786
+ type: "permission_error",
2787
+ description: "The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.",
2788
+ resolution: "Add the package name under dashboard \u2192 Apps \u2192 <your app> \u2192 Android package names.",
2789
+ retryable: false
2790
+ },
2791
+ {
2792
+ code: "env_mismatch",
2793
+ type: "permission_error",
2794
+ description: "The request env (inferred from key prefix) doesn't match the resolved app's configured env.",
2795
+ 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.",
2796
+ retryable: false
2797
+ },
2798
+ {
2799
+ code: "idempotency_key_in_use",
2800
+ type: "invalid_request_error",
2801
+ description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).",
2802
+ 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.",
2803
+ retryable: false
2804
+ },
2805
+ {
2806
+ code: "rate_limited",
2807
+ type: "rate_limit_error",
2808
+ description: "Request rate exceeded the project's per-second cap.",
2809
+ 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.",
2810
+ retryable: true
2811
+ },
2812
+ {
2813
+ code: "internal_error",
2814
+ type: "internal_error",
2815
+ description: "Server-side issue. Safe to retry with backoff.",
2816
+ resolution: "The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.",
2817
+ retryable: true
2818
+ },
2819
+ {
2820
+ code: "google_not_supported",
2821
+ type: "invalid_request_error",
2822
+ description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.",
2823
+ resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.",
2824
+ retryable: false
2825
+ },
2826
+ {
2827
+ code: "stripe_not_supported",
2828
+ type: "invalid_request_error",
2829
+ description: "POST /purchases/sync with rail=stripe is unsupported \u2014 Stripe Checkout's redirect flow uses platform webhooks instead.",
2830
+ resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.",
2831
+ retryable: false
2832
+ },
2833
+ {
2834
+ code: "missing_required_param",
2835
+ type: "invalid_request_error",
2836
+ description: "A required field is absent from the request body.",
2837
+ 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.",
2838
+ retryable: false
2839
+ },
2840
+ {
2841
+ code: "invalid_param_value",
2842
+ type: "invalid_request_error",
2843
+ description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
2844
+ resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
2845
+ retryable: false
2846
+ }
2847
+ ]);
2848
+ function getErrorCode(code) {
2849
+ return CROSSDECK_ERROR_CODES.find((e) => e.code === code);
2850
+ }
2851
+
2642
2852
  // src/_contract-verifiers.ts
2643
2853
  function buildVerifierContext(opts) {
2644
2854
  return {
@@ -3163,13 +3373,64 @@ var VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK = {
3163
3373
  );
3164
3374
  }
3165
3375
  };
3376
+ var BACKEND_WIRE_CODES = Object.freeze([
3377
+ "missing_api_key",
3378
+ "invalid_api_key",
3379
+ "key_revoked",
3380
+ "identity_token_invalid",
3381
+ "origin_not_allowed",
3382
+ "bundle_id_not_allowed",
3383
+ "package_name_not_allowed",
3384
+ "env_mismatch",
3385
+ "idempotency_key_in_use",
3386
+ "rate_limited",
3387
+ "internal_error",
3388
+ "google_not_supported",
3389
+ "stripe_not_supported",
3390
+ "missing_required_param",
3391
+ "invalid_param_value"
3392
+ ]);
3393
+ var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3394
+ contractId: "sdk-error-codes-catalogue",
3395
+ bootTest() {
3396
+ const t0 = nowMs();
3397
+ try {
3398
+ const missing = [];
3399
+ for (const code of BACKEND_WIRE_CODES) {
3400
+ const entry = getErrorCode(code);
3401
+ if (!entry || !entry.description || entry.description.trim().length === 0 || !entry.resolution || entry.resolution.trim().length === 0) {
3402
+ missing.push(code);
3403
+ }
3404
+ }
3405
+ if (missing.length > 0) {
3406
+ return fail(
3407
+ "sdk-error-codes-catalogue",
3408
+ `catalogue missing description+resolution for backend code(s): ${missing.join(", ")}`,
3409
+ nowMs() - t0
3410
+ );
3411
+ }
3412
+ return pass(
3413
+ "sdk-error-codes-catalogue",
3414
+ `all ${BACKEND_WIRE_CODES.length} backend wire codes carry description + resolution`,
3415
+ nowMs() - t0
3416
+ );
3417
+ } catch (err) {
3418
+ return fail(
3419
+ "sdk-error-codes-catalogue",
3420
+ `boot test threw: ${err.message?.slice(0, 80) ?? "unknown error"}`,
3421
+ nowMs() - t0
3422
+ );
3423
+ }
3424
+ }
3425
+ };
3166
3426
  var STATIC_VERIFIERS = Object.freeze([
3167
3427
  VERIFIER_PER_USER_CACHE_ISOLATION,
3168
3428
  VERIFIER_IDEMPOTENCY_KEY_DETERMINISTIC,
3169
3429
  VERIFIER_ERROR_ENVELOPE_SHAPE,
3170
3430
  VERIFIER_FLUSH_INTERVAL_PARITY,
3171
3431
  VERIFIER_SUPER_PROPERTY_MERGE_PRECEDENCE,
3172
- VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK
3432
+ VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK,
3433
+ VERIFIER_SDK_ERROR_CODES_CATALOGUE
3173
3434
  ]);
3174
3435
  async function runBootSelfTest(verifiers, reporter, ctx) {
3175
3436
  if (ctx.disableContractAssertions) {
@@ -5321,213 +5582,9 @@ function installUnloadFlush(onUnload) {
5321
5582
  };
5322
5583
  }
5323
5584
 
5324
- // src/error-codes.ts
5325
- var CROSSDECK_ERROR_CODES = Object.freeze([
5326
- // ----- Configuration -----
5327
- {
5328
- code: "invalid_public_key",
5329
- type: "configuration_error",
5330
- description: "The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.",
5331
- resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys page.",
5332
- retryable: false
5333
- },
5334
- {
5335
- code: "missing_app_id",
5336
- type: "configuration_error",
5337
- description: "Crossdeck.init() was called without an appId.",
5338
- resolution: "Add appId to your init options \u2014 find it in the dashboard's Apps page.",
5339
- retryable: false
5340
- },
5341
- {
5342
- code: "invalid_environment",
5343
- type: "configuration_error",
5344
- description: "Crossdeck.init() requires environment: 'production' | 'sandbox'.",
5345
- resolution: 'Pass the literal string "production" or "sandbox" \u2014 no other values are accepted.',
5346
- retryable: false
5347
- },
5348
- {
5349
- code: "environment_mismatch",
5350
- type: "configuration_error",
5351
- description: "The publishable key's env prefix doesn't match the declared environment option.",
5352
- 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.",
5353
- retryable: false
5354
- },
5355
- {
5356
- code: "not_initialized",
5357
- type: "configuration_error",
5358
- description: "An SDK method was called before Crossdeck.init().",
5359
- resolution: "Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.",
5360
- retryable: false
5361
- },
5362
- // ----- Identify / track / purchase argument validation -----
5363
- {
5364
- code: "missing_user_id",
5365
- type: "invalid_request_error",
5366
- description: "identify() was called with an empty userId.",
5367
- resolution: "Pass a stable, non-empty user identifier from your auth layer \u2014 never a hardcoded placeholder.",
5368
- retryable: false
5369
- },
5370
- {
5371
- code: "missing_event_name",
5372
- type: "invalid_request_error",
5373
- description: "track() was called without an event name.",
5374
- resolution: "Pass a non-empty string as the first argument.",
5375
- retryable: false
5376
- },
5377
- {
5378
- code: "missing_group_type",
5379
- type: "invalid_request_error",
5380
- description: "group() was called without a group type.",
5381
- resolution: 'Pass a non-empty type (e.g. "org", "team") as the first argument.',
5382
- retryable: false
5383
- },
5384
- {
5385
- code: "missing_signed_transaction_info",
5386
- type: "invalid_request_error",
5387
- description: "syncPurchases() was called without StoreKit 2 signed transaction info.",
5388
- resolution: "Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.",
5389
- retryable: false
5390
- },
5391
- // ----- Network / transport -----
5392
- {
5393
- code: "fetch_failed",
5394
- type: "network_error",
5395
- description: "The underlying fetch() call failed (typically a network outage or DNS issue).",
5396
- resolution: "Check the user's network. The SDK will retry automatically with exponential backoff.",
5397
- retryable: true
5398
- },
5399
- {
5400
- code: "request_timeout",
5401
- type: "network_error",
5402
- description: "A request was aborted after the configured timeoutMs (default 15s).",
5403
- resolution: "Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.",
5404
- retryable: true
5405
- },
5406
- {
5407
- code: "invalid_json_response",
5408
- type: "internal_error",
5409
- description: "The server returned a 2xx with an unparseable body.",
5410
- resolution: "Likely a transient backend bug. Retry; if it persists, contact support with the requestId.",
5411
- retryable: true
5412
- },
5413
- // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----
5414
- // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer
5415
- // hitting any of these on the wire can look them up via
5416
- // getErrorCode(code) for a canonical remediation step instead of
5417
- // hunting through Slack history.
5418
- {
5419
- code: "missing_api_key",
5420
- type: "authentication_error",
5421
- description: "No Authorization header (or Crossdeck-Api-Key header) on the request.",
5422
- 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.",
5423
- retryable: false
5424
- },
5425
- {
5426
- code: "invalid_api_key",
5427
- type: "authentication_error",
5428
- description: "The API key is malformed, unknown, or doesn't resolve to a project.",
5429
- 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.",
5430
- retryable: false
5431
- },
5432
- {
5433
- code: "key_revoked",
5434
- type: "authentication_error",
5435
- description: "The API key was revoked in the Crossdeck dashboard.",
5436
- 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.",
5437
- retryable: false
5438
- },
5439
- {
5440
- code: "identity_token_invalid",
5441
- type: "authentication_error",
5442
- description: "The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.",
5443
- 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.",
5444
- retryable: true
5445
- },
5446
- {
5447
- code: "origin_not_allowed",
5448
- type: "permission_error",
5449
- description: "The Origin header isn't in the project's Allowed origins list.",
5450
- resolution: "Add the origin (e.g. https://app.example.com) under dashboard \u2192 Settings \u2192 Allowed origins. Wildcards like https://*.example.com are supported.",
5451
- retryable: false
5452
- },
5453
- {
5454
- code: "bundle_id_not_allowed",
5455
- type: "permission_error",
5456
- description: "The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.",
5457
- resolution: "Add the bundle ID under dashboard \u2192 Apps \u2192 <your app> \u2192 iOS bundle IDs.",
5458
- retryable: false
5459
- },
5460
- {
5461
- code: "package_name_not_allowed",
5462
- type: "permission_error",
5463
- description: "The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.",
5464
- resolution: "Add the package name under dashboard \u2192 Apps \u2192 <your app> \u2192 Android package names.",
5465
- retryable: false
5466
- },
5467
- {
5468
- code: "env_mismatch",
5469
- type: "permission_error",
5470
- description: "The request env (inferred from key prefix) doesn't match the resolved app's configured env.",
5471
- 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.",
5472
- retryable: false
5473
- },
5474
- {
5475
- code: "idempotency_key_in_use",
5476
- type: "invalid_request_error",
5477
- description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).",
5478
- 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.",
5479
- retryable: false
5480
- },
5481
- {
5482
- code: "rate_limited",
5483
- type: "rate_limit_error",
5484
- description: "Request rate exceeded the project's per-second cap.",
5485
- 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.",
5486
- retryable: true
5487
- },
5488
- {
5489
- code: "internal_error",
5490
- type: "internal_error",
5491
- description: "Server-side issue. Safe to retry with backoff.",
5492
- resolution: "The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.",
5493
- retryable: true
5494
- },
5495
- {
5496
- code: "google_not_supported",
5497
- type: "invalid_request_error",
5498
- description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.",
5499
- resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.",
5500
- retryable: false
5501
- },
5502
- {
5503
- code: "stripe_not_supported",
5504
- type: "invalid_request_error",
5505
- description: "POST /purchases/sync with rail=stripe is unsupported \u2014 Stripe Checkout's redirect flow uses platform webhooks instead.",
5506
- resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.",
5507
- retryable: false
5508
- },
5509
- {
5510
- code: "missing_required_param",
5511
- type: "invalid_request_error",
5512
- description: "A required field is absent from the request body.",
5513
- 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.",
5514
- retryable: false
5515
- },
5516
- {
5517
- code: "invalid_param_value",
5518
- type: "invalid_request_error",
5519
- description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
5520
- resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
5521
- retryable: false
5522
- }
5523
- ]);
5524
- function getErrorCode(code) {
5525
- return CROSSDECK_ERROR_CODES.find((e) => e.code === code);
5526
- }
5527
-
5528
5585
  // src/_contracts-bundled.ts
5529
- var BUNDLED_IN = "@cross-deck/web@1.6.1";
5530
- var SDK_VERSION2 = "1.6.1";
5586
+ var BUNDLED_IN = "@cross-deck/web@1.6.2";
5587
+ var SDK_VERSION2 = "1.6.2";
5531
5588
  var BUNDLED_CONTRACTS = Object.freeze([
5532
5589
  {
5533
5590
  "id": "contract-failed-payload-schema-lock",
@@ -5649,7 +5706,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5649
5706
  "legal/security/index.html#diagnostic",
5650
5707
  "legal/sdk-data/index.html#b-diagnostic"
5651
5708
  ],
5652
- "bundledIn": "@cross-deck/web@1.6.1"
5709
+ "bundledIn": "@cross-deck/web@1.6.2",
5710
+ "runtimeVerified": true
5653
5711
  },
5654
5712
  {
5655
5713
  "id": "error-envelope-shape",
@@ -5688,7 +5746,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5688
5746
  ],
5689
5747
  "registeredAt": "2026-05-26",
5690
5748
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
5691
- "bundledIn": "@cross-deck/web@1.6.1"
5749
+ "bundledIn": "@cross-deck/web@1.6.2",
5750
+ "runtimeVerified": true
5692
5751
  },
5693
5752
  {
5694
5753
  "id": "flush-interval-parity",
@@ -5733,7 +5792,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5733
5792
  ],
5734
5793
  "registeredAt": "2026-05-26",
5735
5794
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
5736
- "bundledIn": "@cross-deck/web@1.6.1"
5795
+ "bundledIn": "@cross-deck/web@1.6.2",
5796
+ "runtimeVerified": true
5737
5797
  },
5738
5798
  {
5739
5799
  "id": "idempotency-key-deterministic",
@@ -5838,7 +5898,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5838
5898
  ],
5839
5899
  "registeredAt": "2026-05-26",
5840
5900
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
5841
- "bundledIn": "@cross-deck/web@1.6.1"
5901
+ "bundledIn": "@cross-deck/web@1.6.2",
5902
+ "runtimeVerified": true
5842
5903
  },
5843
5904
  {
5844
5905
  "id": "init-reentry-drains-prior-queue",
@@ -5865,7 +5926,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5865
5926
  ],
5866
5927
  "registeredAt": "2026-05-26",
5867
5928
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
5868
- "bundledIn": "@cross-deck/web@1.6.1"
5929
+ "bundledIn": "@cross-deck/web@1.6.2",
5930
+ "runtimeVerified": false
5869
5931
  },
5870
5932
  {
5871
5933
  "id": "per-user-cache-isolation",
@@ -5944,7 +6006,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
5944
6006
  ],
5945
6007
  "registeredAt": "2026-05-26",
5946
6008
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
5947
- "bundledIn": "@cross-deck/web@1.6.1"
6009
+ "bundledIn": "@cross-deck/web@1.6.2",
6010
+ "runtimeVerified": true
5948
6011
  },
5949
6012
  {
5950
6013
  "id": "sdk-error-codes-catalogue",
@@ -5958,9 +6021,14 @@ var BUNDLED_CONTRACTS = Object.freeze([
5958
6021
  "codeRef": [
5959
6022
  "sdks/web/src/error-codes.ts",
5960
6023
  "sdks/node/src/error-codes.ts",
6024
+ "sdks/web/src/_contract-verifiers.ts",
5961
6025
  "backend/src/api/v1-errors.ts"
5962
6026
  ],
5963
6027
  "testRef": [
6028
+ {
6029
+ "file": "sdks/web/tests/contract-verifiers.test.ts",
6030
+ "name": "sdk-error-codes-catalogue covers every backend wire code with remediation"
6031
+ },
5964
6032
  {
5965
6033
  "file": "sdks/web/tests/error-codes-backfill.test.ts",
5966
6034
  "name": "includes backend code"
@@ -5984,7 +6052,50 @@ var BUNDLED_CONTRACTS = Object.freeze([
5984
6052
  ],
5985
6053
  "registeredAt": "2026-05-26",
5986
6054
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
5987
- "bundledIn": "@cross-deck/web@1.6.1"
6055
+ "bundledIn": "@cross-deck/web@1.6.2",
6056
+ "runtimeVerified": true
6057
+ },
6058
+ {
6059
+ "id": "super-property-merge-precedence",
6060
+ "pillar": "analytics",
6061
+ "status": "enforced",
6062
+ "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.",
6063
+ "appliesTo": [
6064
+ "web",
6065
+ "swift"
6066
+ ],
6067
+ "codeRef": [
6068
+ "sdks/web/src/super-properties.ts",
6069
+ "sdks/web/src/_contract-verifiers.ts",
6070
+ "sdks/swift/Sources/Crossdeck/EventPropertyMerge.swift",
6071
+ "sdks/swift/Sources/Crossdeck/Crossdeck.swift"
6072
+ ],
6073
+ "testRef": [
6074
+ {
6075
+ "file": "sdks/web/tests/contract-verifiers.test.ts",
6076
+ "name": "super-property-merge-precedence verifies caller > super > device"
6077
+ },
6078
+ {
6079
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6080
+ "name": "test_super_overrides_device"
6081
+ },
6082
+ {
6083
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6084
+ "name": "test_caller_overrides_super"
6085
+ },
6086
+ {
6087
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6088
+ "name": "test_full_precedence_chain"
6089
+ },
6090
+ {
6091
+ "file": "sdks/swift/Tests/CrossdeckTests/EventPropertyMergeTests.swift",
6092
+ "name": "test_matchesWebNodeRNPrecedence"
6093
+ }
6094
+ ],
6095
+ "registeredAt": "2026-05-26",
6096
+ "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6097
+ "bundledIn": "@cross-deck/web@1.6.2",
6098
+ "runtimeVerified": true
5988
6099
  },
5989
6100
  {
5990
6101
  "id": "sync-purchases-funnel-parity",
@@ -6017,7 +6128,8 @@ var BUNDLED_CONTRACTS = Object.freeze([
6017
6128
  ],
6018
6129
  "registeredAt": "2026-05-26",
6019
6130
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
6020
- "bundledIn": "@cross-deck/web@1.6.1"
6131
+ "bundledIn": "@cross-deck/web@1.6.2",
6132
+ "runtimeVerified": false
6021
6133
  }
6022
6134
  ]);
6023
6135