@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/react.cjs CHANGED
@@ -93,7 +93,7 @@ function typeMapForStatus(status) {
93
93
  }
94
94
 
95
95
  // src/_version.ts
96
- var SDK_VERSION = "1.6.1";
96
+ var SDK_VERSION = "1.6.2";
97
97
  var SDK_NAME = "@cross-deck/web";
98
98
 
99
99
  // src/http.ts
@@ -1518,6 +1518,13 @@ var AutoTracker = class {
1518
1518
  markActivity() {
1519
1519
  if (!this.session) return;
1520
1520
  const now = Date.now();
1521
+ if (now - this.session.lastActivityAt >= SESSION_RESUME_THRESHOLD_MS) {
1522
+ this.pageviewId = null;
1523
+ this.session = this.startNewSession();
1524
+ this.persistSession();
1525
+ this.emitSessionStart();
1526
+ return;
1527
+ }
1521
1528
  this.session.lastActivityAt = now;
1522
1529
  if (now - this.lastPersistAt >= ACTIVITY_PERSIST_THROTTLE_MS) {
1523
1530
  this.persistSession();
@@ -1571,7 +1578,6 @@ var AutoTracker = class {
1571
1578
  } else if (doc2.visibilityState === "visible") {
1572
1579
  const idleFor = Date.now() - this.session.lastActivityAt;
1573
1580
  if (idleFor >= SESSION_RESUME_THRESHOLD_MS) {
1574
- this.emitSessionEnd();
1575
1581
  this.pageviewId = null;
1576
1582
  this.session = this.startNewSession();
1577
1583
  this.persistSession();
@@ -2633,6 +2639,210 @@ function sendDiagnosticTelemetry(payload) {
2633
2639
  }
2634
2640
  }
2635
2641
 
2642
+ // src/error-codes.ts
2643
+ var CROSSDECK_ERROR_CODES = Object.freeze([
2644
+ // ----- Configuration -----
2645
+ {
2646
+ code: "invalid_public_key",
2647
+ type: "configuration_error",
2648
+ description: "The publishable key passed to Crossdeck.init() doesn't start with cd_pub_.",
2649
+ resolution: "Copy the key from your Crossdeck dashboard \u2192 API keys page.",
2650
+ retryable: false
2651
+ },
2652
+ {
2653
+ code: "missing_app_id",
2654
+ type: "configuration_error",
2655
+ description: "Crossdeck.init() was called without an appId.",
2656
+ resolution: "Add appId to your init options \u2014 find it in the dashboard's Apps page.",
2657
+ retryable: false
2658
+ },
2659
+ {
2660
+ code: "invalid_environment",
2661
+ type: "configuration_error",
2662
+ description: "Crossdeck.init() requires environment: 'production' | 'sandbox'.",
2663
+ resolution: 'Pass the literal string "production" or "sandbox" \u2014 no other values are accepted.',
2664
+ retryable: false
2665
+ },
2666
+ {
2667
+ code: "environment_mismatch",
2668
+ type: "configuration_error",
2669
+ description: "The publishable key's env prefix doesn't match the declared environment option.",
2670
+ 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.",
2671
+ retryable: false
2672
+ },
2673
+ {
2674
+ code: "not_initialized",
2675
+ type: "configuration_error",
2676
+ description: "An SDK method was called before Crossdeck.init().",
2677
+ resolution: "Call Crossdeck.init({ appId, publicKey, environment }) once at app startup before any other method.",
2678
+ retryable: false
2679
+ },
2680
+ // ----- Identify / track / purchase argument validation -----
2681
+ {
2682
+ code: "missing_user_id",
2683
+ type: "invalid_request_error",
2684
+ description: "identify() was called with an empty userId.",
2685
+ resolution: "Pass a stable, non-empty user identifier from your auth layer \u2014 never a hardcoded placeholder.",
2686
+ retryable: false
2687
+ },
2688
+ {
2689
+ code: "missing_event_name",
2690
+ type: "invalid_request_error",
2691
+ description: "track() was called without an event name.",
2692
+ resolution: "Pass a non-empty string as the first argument.",
2693
+ retryable: false
2694
+ },
2695
+ {
2696
+ code: "missing_group_type",
2697
+ type: "invalid_request_error",
2698
+ description: "group() was called without a group type.",
2699
+ resolution: 'Pass a non-empty type (e.g. "org", "team") as the first argument.',
2700
+ retryable: false
2701
+ },
2702
+ {
2703
+ code: "missing_signed_transaction_info",
2704
+ type: "invalid_request_error",
2705
+ description: "syncPurchases() was called without StoreKit 2 signed transaction info.",
2706
+ resolution: "Pass the JWS string from Transaction.currentEntitlements / Transaction.updates.",
2707
+ retryable: false
2708
+ },
2709
+ // ----- Network / transport -----
2710
+ {
2711
+ code: "fetch_failed",
2712
+ type: "network_error",
2713
+ description: "The underlying fetch() call failed (typically a network outage or DNS issue).",
2714
+ resolution: "Check the user's network. The SDK will retry automatically with exponential backoff.",
2715
+ retryable: true
2716
+ },
2717
+ {
2718
+ code: "request_timeout",
2719
+ type: "network_error",
2720
+ description: "A request was aborted after the configured timeoutMs (default 15s).",
2721
+ resolution: "Check the user's connection. Increase timeoutMs in init options if the user is on a known-slow network.",
2722
+ retryable: true
2723
+ },
2724
+ {
2725
+ code: "invalid_json_response",
2726
+ type: "internal_error",
2727
+ description: "The server returned a 2xx with an unparseable body.",
2728
+ resolution: "Likely a transient backend bug. Retry; if it persists, contact support with the requestId.",
2729
+ retryable: true
2730
+ },
2731
+ // ----- Backend-emitted codes (v1.4.0 Phase 6.2 backfill) -----
2732
+ // Mirror of backend/src/api/v1-errors.ts ApiErrorCode. A developer
2733
+ // hitting any of these on the wire can look them up via
2734
+ // getErrorCode(code) for a canonical remediation step instead of
2735
+ // hunting through Slack history.
2736
+ {
2737
+ code: "missing_api_key",
2738
+ type: "authentication_error",
2739
+ description: "No Authorization header (or Crossdeck-Api-Key header) on the request.",
2740
+ 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.",
2741
+ retryable: false
2742
+ },
2743
+ {
2744
+ code: "invalid_api_key",
2745
+ type: "authentication_error",
2746
+ description: "The API key is malformed, unknown, or doesn't resolve to a project.",
2747
+ 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.",
2748
+ retryable: false
2749
+ },
2750
+ {
2751
+ code: "key_revoked",
2752
+ type: "authentication_error",
2753
+ description: "The API key was revoked in the Crossdeck dashboard.",
2754
+ 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.",
2755
+ retryable: false
2756
+ },
2757
+ {
2758
+ code: "identity_token_invalid",
2759
+ type: "authentication_error",
2760
+ description: "The Firebase / Apple / Google ID token supplied with the request didn't verify against the dashboard's configured signers.",
2761
+ 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.",
2762
+ retryable: true
2763
+ },
2764
+ {
2765
+ code: "origin_not_allowed",
2766
+ type: "permission_error",
2767
+ description: "The Origin header isn't in the project's Allowed origins list.",
2768
+ resolution: "Add the origin (e.g. https://app.example.com) under dashboard \u2192 Settings \u2192 Allowed origins. Wildcards like https://*.example.com are supported.",
2769
+ retryable: false
2770
+ },
2771
+ {
2772
+ code: "bundle_id_not_allowed",
2773
+ type: "permission_error",
2774
+ description: "The iOS bundle ID sent via X-Crossdeck-Bundle-Id isn't registered under this app's Apple identity lock.",
2775
+ resolution: "Add the bundle ID under dashboard \u2192 Apps \u2192 <your app> \u2192 iOS bundle IDs.",
2776
+ retryable: false
2777
+ },
2778
+ {
2779
+ code: "package_name_not_allowed",
2780
+ type: "permission_error",
2781
+ description: "The Android package name sent via X-Crossdeck-Package-Name isn't registered under this app's Android identity lock.",
2782
+ resolution: "Add the package name under dashboard \u2192 Apps \u2192 <your app> \u2192 Android package names.",
2783
+ retryable: false
2784
+ },
2785
+ {
2786
+ code: "env_mismatch",
2787
+ type: "permission_error",
2788
+ description: "The request env (inferred from key prefix) doesn't match the resolved app's configured env.",
2789
+ 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.",
2790
+ retryable: false
2791
+ },
2792
+ {
2793
+ code: "idempotency_key_in_use",
2794
+ type: "invalid_request_error",
2795
+ description: "An Idempotency-Key was reused for a request with a different body (Stripe-grade contract).",
2796
+ 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.",
2797
+ retryable: false
2798
+ },
2799
+ {
2800
+ code: "rate_limited",
2801
+ type: "rate_limit_error",
2802
+ description: "Request rate exceeded the project's per-second cap.",
2803
+ 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.",
2804
+ retryable: true
2805
+ },
2806
+ {
2807
+ code: "internal_error",
2808
+ type: "internal_error",
2809
+ description: "Server-side issue. Safe to retry with backoff.",
2810
+ resolution: "The SDK retries automatically. If your code paths through to this error, contact support with the requestId from the response envelope.",
2811
+ retryable: true
2812
+ },
2813
+ {
2814
+ code: "google_not_supported",
2815
+ type: "invalid_request_error",
2816
+ description: "POST /purchases/sync with rail=google is gated until the Play Developer API reconciliation worker ships.",
2817
+ resolution: "Until v1.5+, Google Play purchases verify via Real-time Developer Notifications. The SDK auto-track path handles this transparently for Android consumers.",
2818
+ retryable: false
2819
+ },
2820
+ {
2821
+ code: "stripe_not_supported",
2822
+ type: "invalid_request_error",
2823
+ description: "POST /purchases/sync with rail=stripe is unsupported \u2014 Stripe Checkout's redirect flow uses platform webhooks instead.",
2824
+ resolution: "Wire Stripe via the standard Checkout / Customer Portal flow; Crossdeck reconciles via the platform webhook automatically. No SDK call needed.",
2825
+ retryable: false
2826
+ },
2827
+ {
2828
+ code: "missing_required_param",
2829
+ type: "invalid_request_error",
2830
+ description: "A required field is absent from the request body.",
2831
+ 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.",
2832
+ retryable: false
2833
+ },
2834
+ {
2835
+ code: "invalid_param_value",
2836
+ type: "invalid_request_error",
2837
+ description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
2838
+ resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
2839
+ retryable: false
2840
+ }
2841
+ ]);
2842
+ function getErrorCode(code) {
2843
+ return CROSSDECK_ERROR_CODES.find((e) => e.code === code);
2844
+ }
2845
+
2636
2846
  // src/_contract-verifiers.ts
2637
2847
  function buildVerifierContext(opts) {
2638
2848
  return {
@@ -3157,13 +3367,64 @@ var VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK = {
3157
3367
  );
3158
3368
  }
3159
3369
  };
3370
+ var BACKEND_WIRE_CODES = Object.freeze([
3371
+ "missing_api_key",
3372
+ "invalid_api_key",
3373
+ "key_revoked",
3374
+ "identity_token_invalid",
3375
+ "origin_not_allowed",
3376
+ "bundle_id_not_allowed",
3377
+ "package_name_not_allowed",
3378
+ "env_mismatch",
3379
+ "idempotency_key_in_use",
3380
+ "rate_limited",
3381
+ "internal_error",
3382
+ "google_not_supported",
3383
+ "stripe_not_supported",
3384
+ "missing_required_param",
3385
+ "invalid_param_value"
3386
+ ]);
3387
+ var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3388
+ contractId: "sdk-error-codes-catalogue",
3389
+ bootTest() {
3390
+ const t0 = nowMs();
3391
+ try {
3392
+ const missing = [];
3393
+ for (const code of BACKEND_WIRE_CODES) {
3394
+ const entry = getErrorCode(code);
3395
+ if (!entry || !entry.description || entry.description.trim().length === 0 || !entry.resolution || entry.resolution.trim().length === 0) {
3396
+ missing.push(code);
3397
+ }
3398
+ }
3399
+ if (missing.length > 0) {
3400
+ return fail(
3401
+ "sdk-error-codes-catalogue",
3402
+ `catalogue missing description+resolution for backend code(s): ${missing.join(", ")}`,
3403
+ nowMs() - t0
3404
+ );
3405
+ }
3406
+ return pass(
3407
+ "sdk-error-codes-catalogue",
3408
+ `all ${BACKEND_WIRE_CODES.length} backend wire codes carry description + resolution`,
3409
+ nowMs() - t0
3410
+ );
3411
+ } catch (err) {
3412
+ return fail(
3413
+ "sdk-error-codes-catalogue",
3414
+ `boot test threw: ${err.message?.slice(0, 80) ?? "unknown error"}`,
3415
+ nowMs() - t0
3416
+ );
3417
+ }
3418
+ }
3419
+ };
3160
3420
  var STATIC_VERIFIERS = Object.freeze([
3161
3421
  VERIFIER_PER_USER_CACHE_ISOLATION,
3162
3422
  VERIFIER_IDEMPOTENCY_KEY_DETERMINISTIC,
3163
3423
  VERIFIER_ERROR_ENVELOPE_SHAPE,
3164
3424
  VERIFIER_FLUSH_INTERVAL_PARITY,
3165
3425
  VERIFIER_SUPER_PROPERTY_MERGE_PRECEDENCE,
3166
- VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK
3426
+ VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK,
3427
+ VERIFIER_SDK_ERROR_CODES_CATALOGUE
3167
3428
  ]);
3168
3429
  async function runBootSelfTest(verifiers, reporter, ctx) {
3169
3430
  if (ctx.disableContractAssertions) {