@cross-deck/web 1.7.0 → 1.8.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/dist/index.d.mts CHANGED
@@ -247,9 +247,9 @@ declare const CrossdeckContracts: {
247
247
  /** Filter by lifecycle status. */
248
248
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
249
249
  /** Semver of the SDK release these contracts were bundled with. */
250
- readonly sdkVersion: "1.7.0";
250
+ readonly sdkVersion: "1.8.0";
251
251
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.7.0";
252
+ readonly bundledIn: "@cross-deck/web@1.8.0";
253
253
  /**
254
254
  * Resolve a failing test back to the contract it exercises.
255
255
  * Used by test-framework hooks (Vitest `afterEach`, XCTest
@@ -843,7 +843,7 @@ declare const Crossdeck: CrossdeckClient;
843
843
  * }
844
844
  * }
845
845
  */
846
- type CrossdeckErrorType = "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
846
+ type CrossdeckErrorType = "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
847
847
  interface CrossdeckErrorPayload {
848
848
  type: CrossdeckErrorType;
849
849
  code: string;
@@ -860,6 +860,14 @@ interface CrossdeckErrorPayload {
860
860
  * server is telling you the safe rate.
861
861
  */
862
862
  retryAfterMs?: number;
863
+ /**
864
+ * Required SDK version floor — populated only on a `426 Upgrade Required`
865
+ * / `sdk_version_unsupported` response. The queue surfaces it in the
866
+ * "update to >= X" PARK message so the cure is exact.
867
+ */
868
+ minVersion?: string;
869
+ /** SDK surface the rejection applies to (web/node/swift/…), on a 426. */
870
+ surface?: string;
863
871
  }
864
872
  declare class CrossdeckError extends Error {
865
873
  readonly type: CrossdeckErrorType;
@@ -867,6 +875,8 @@ declare class CrossdeckError extends Error {
867
875
  readonly requestId?: string;
868
876
  readonly status?: number;
869
877
  readonly retryAfterMs?: number;
878
+ readonly minVersion?: string;
879
+ readonly surface?: string;
870
880
  constructor(payload: CrossdeckErrorPayload);
871
881
  }
872
882
 
@@ -936,7 +946,7 @@ declare class MemoryStorage implements KeyValueStorage {
936
946
  *
937
947
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
938
948
  */
939
- declare const SDK_VERSION = "1.7.0";
949
+ declare const SDK_VERSION = "1.8.0";
940
950
  declare const SDK_NAME = "@cross-deck/web";
941
951
 
942
952
  /**
@@ -974,7 +984,7 @@ interface ErrorCodeEntry {
974
984
  /** The string thrown as CrossdeckError.code. */
975
985
  code: string;
976
986
  /** CrossdeckError.type — broad category. */
977
- type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
987
+ type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
978
988
  /** One-sentence description. Surfaced verbatim in dashboards. */
979
989
  description: string;
980
990
  /** What the developer should do. Imperative phrasing. */
package/dist/index.d.ts CHANGED
@@ -247,9 +247,9 @@ declare const CrossdeckContracts: {
247
247
  /** Filter by lifecycle status. */
248
248
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
249
249
  /** Semver of the SDK release these contracts were bundled with. */
250
- readonly sdkVersion: "1.7.0";
250
+ readonly sdkVersion: "1.8.0";
251
251
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.7.0";
252
+ readonly bundledIn: "@cross-deck/web@1.8.0";
253
253
  /**
254
254
  * Resolve a failing test back to the contract it exercises.
255
255
  * Used by test-framework hooks (Vitest `afterEach`, XCTest
@@ -843,7 +843,7 @@ declare const Crossdeck: CrossdeckClient;
843
843
  * }
844
844
  * }
845
845
  */
846
- type CrossdeckErrorType = "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
846
+ type CrossdeckErrorType = "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
847
847
  interface CrossdeckErrorPayload {
848
848
  type: CrossdeckErrorType;
849
849
  code: string;
@@ -860,6 +860,14 @@ interface CrossdeckErrorPayload {
860
860
  * server is telling you the safe rate.
861
861
  */
862
862
  retryAfterMs?: number;
863
+ /**
864
+ * Required SDK version floor — populated only on a `426 Upgrade Required`
865
+ * / `sdk_version_unsupported` response. The queue surfaces it in the
866
+ * "update to >= X" PARK message so the cure is exact.
867
+ */
868
+ minVersion?: string;
869
+ /** SDK surface the rejection applies to (web/node/swift/…), on a 426. */
870
+ surface?: string;
863
871
  }
864
872
  declare class CrossdeckError extends Error {
865
873
  readonly type: CrossdeckErrorType;
@@ -867,6 +875,8 @@ declare class CrossdeckError extends Error {
867
875
  readonly requestId?: string;
868
876
  readonly status?: number;
869
877
  readonly retryAfterMs?: number;
878
+ readonly minVersion?: string;
879
+ readonly surface?: string;
870
880
  constructor(payload: CrossdeckErrorPayload);
871
881
  }
872
882
 
@@ -936,7 +946,7 @@ declare class MemoryStorage implements KeyValueStorage {
936
946
  *
937
947
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
938
948
  */
939
- declare const SDK_VERSION = "1.7.0";
949
+ declare const SDK_VERSION = "1.8.0";
940
950
  declare const SDK_NAME = "@cross-deck/web";
941
951
 
942
952
  /**
@@ -974,7 +984,7 @@ interface ErrorCodeEntry {
974
984
  /** The string thrown as CrossdeckError.code. */
975
985
  code: string;
976
986
  /** CrossdeckError.type — broad category. */
977
- type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "internal_error" | "network_error" | "configuration_error";
987
+ type: "authentication_error" | "permission_error" | "invalid_request_error" | "rate_limit_error" | "version_error" | "internal_error" | "network_error" | "configuration_error";
978
988
  /** One-sentence description. Surfaced verbatim in dashboards. */
979
989
  description: string;
980
990
  /** What the developer should do. Imperative phrasing. */
package/dist/index.mjs CHANGED
@@ -8,6 +8,8 @@ var CrossdeckError = class _CrossdeckError extends Error {
8
8
  this.requestId = payload.requestId;
9
9
  this.status = payload.status;
10
10
  this.retryAfterMs = payload.retryAfterMs;
11
+ this.minVersion = payload.minVersion;
12
+ this.surface = payload.surface;
11
13
  Object.setPrototypeOf(this, _CrossdeckError.prototype);
12
14
  }
13
15
  };
@@ -28,7 +30,10 @@ async function crossdeckErrorFromResponse(res) {
28
30
  message: envelope.message ?? `HTTP ${res.status}`,
29
31
  requestId: envelope.request_id ?? requestId,
30
32
  status: res.status,
31
- retryAfterMs
33
+ retryAfterMs,
34
+ // PARK metadata, present only on a 426 / sdk_version_unsupported body.
35
+ minVersion: typeof envelope.minVersion === "string" ? envelope.minVersion : void 0,
36
+ surface: typeof envelope.surface === "string" ? envelope.surface : void 0
32
37
  });
33
38
  }
34
39
  return new CrossdeckError({
@@ -64,7 +69,7 @@ function typeMapForStatus(status) {
64
69
  }
65
70
 
66
71
  // src/_version.ts
67
- var SDK_VERSION = "1.7.0";
72
+ var SDK_VERSION = "1.8.0";
68
73
  var SDK_NAME = "@cross-deck/web";
69
74
 
70
75
  // src/http.ts
@@ -904,6 +909,18 @@ var EventQueue = class {
904
909
  this.cancelTimer = null;
905
910
  this.firstFlushFired = false;
906
911
  this.nextRetryAt = null;
912
+ /**
913
+ * PARK state (HTTP 426 / `sdk_version_unsupported`). Once parked, the
914
+ * queue stops flushing — the server has told us our wire dialect is too
915
+ * old, and retrying the same payload only burns the user's battery and
916
+ * bandwidth and drips pointless rejects into the server logs until the
917
+ * app ships an upgraded SDK. The held events stay durable (persisted)
918
+ * and are delivered by the next boot's rehydrate, post-upgrade. Parked
919
+ * is per-instance: a fresh boot (the upgraded build) starts unparked.
920
+ */
921
+ this.parked = false;
922
+ /** One developer-facing console warning per instance — never per-event spam. */
923
+ this.parkWarned = false;
907
924
  this.retry = new RetryPolicy(cfg.retry ?? {});
908
925
  this.persistent = cfg.persistentStore ?? null;
909
926
  if (this.persistent) {
@@ -959,6 +976,7 @@ var EventQueue = class {
959
976
  * flushes (pagehide / visibilitychange→hidden / beforeunload).
960
977
  */
961
978
  async flush(options = {}) {
979
+ if (this.parked) return null;
962
980
  let batch;
963
981
  let batchId;
964
982
  if (this.pendingBatch !== null && this.pendingBatchId !== null) {
@@ -1010,6 +1028,29 @@ var EventQueue = class {
1010
1028
  } catch (err) {
1011
1029
  const message = err instanceof Error ? err.message : String(err);
1012
1030
  this.lastError = message;
1031
+ if (isVersionRejected(err)) {
1032
+ this.parked = true;
1033
+ this.buffer = [...batch, ...this.buffer];
1034
+ if (this.buffer.length > HARD_BUFFER_CAP) {
1035
+ const overflow = this.buffer.length - HARD_BUFFER_CAP;
1036
+ this.buffer.splice(0, overflow);
1037
+ this.dropped += overflow;
1038
+ }
1039
+ this.pendingBatch = null;
1040
+ this.pendingBatchId = null;
1041
+ this.inFlight -= batch.length;
1042
+ this.persistAll();
1043
+ this.cfg.onBufferChange?.(this.buffer.length);
1044
+ const minVersion = versionRejectionFloor(err);
1045
+ if (!this.parkWarned) {
1046
+ this.parkWarned = true;
1047
+ console.warn(
1048
+ `[Crossdeck] SDK outdated \u2014 the server is no longer accepting this version's event format. Your events are PARKED on-device (held, not lost) and will deliver automatically once you update @cross-deck/web${minVersion ? ` to >= ${minVersion}` : ""} and redeploy.`
1049
+ );
1050
+ }
1051
+ this.cfg.onParked?.({ minVersion, surface: versionRejectionSurface(err) });
1052
+ return null;
1053
+ }
1013
1054
  if (isPermanent4xx(err)) {
1014
1055
  const droppedCount = batch.length;
1015
1056
  this.pendingBatch = null;
@@ -1128,8 +1169,22 @@ function isPermanent4xx(err) {
1128
1169
  if (typeof status !== "number" || !Number.isFinite(status)) return false;
1129
1170
  if (status < 400 || status >= 500) return false;
1130
1171
  if (status === 408 || status === 429) return false;
1172
+ if (status === 426) return false;
1131
1173
  return true;
1132
1174
  }
1175
+ function isVersionRejected(err) {
1176
+ if (!err || typeof err !== "object") return false;
1177
+ if (err.status === 426) return true;
1178
+ return err.code === "sdk_version_unsupported";
1179
+ }
1180
+ function versionRejectionFloor(err) {
1181
+ const v = err?.minVersion;
1182
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1183
+ }
1184
+ function versionRejectionSurface(err) {
1185
+ const v = err?.surface;
1186
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1187
+ }
1133
1188
  function defaultScheduler(fn, ms) {
1134
1189
  const id = setTimeout(fn, ms);
1135
1190
  if (typeof id.unref === "function") {
@@ -2870,6 +2925,13 @@ var CROSSDECK_ERROR_CODES = Object.freeze([
2870
2925
  description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
2871
2926
  resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
2872
2927
  retryable: false
2928
+ },
2929
+ {
2930
+ code: "sdk_version_unsupported",
2931
+ type: "version_error",
2932
+ description: "HTTP 426 \u2014 your installed SDK sends an event format the server no longer accepts. The data is good; only the wire dialect is too old. The SDK PARKS automatically: events are held on-device (paused, not lost) and deliver on the next launch after you upgrade.",
2933
+ resolution: "Update @cross-deck/web to at least the version in error.minVersion and redeploy \u2014 the parked queue backfills automatically. See https://cross-deck.com/docs/sdk-event-durability/.",
2934
+ retryable: false
2873
2935
  }
2874
2936
  ]);
2875
2937
  function getErrorCode(code) {
@@ -3415,7 +3477,8 @@ var BACKEND_WIRE_CODES = Object.freeze([
3415
3477
  "google_not_supported",
3416
3478
  "stripe_not_supported",
3417
3479
  "missing_required_param",
3418
- "invalid_param_value"
3480
+ "invalid_param_value",
3481
+ "sdk_version_unsupported"
3419
3482
  ]);
3420
3483
  var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3421
3484
  contractId: "sdk-error-codes-catalogue",
@@ -4572,6 +4635,13 @@ var CrossdeckClient = class {
4572
4635
  headline,
4573
4636
  { ...info }
4574
4637
  );
4638
+ },
4639
+ onParked: (info) => {
4640
+ debug.emit(
4641
+ "sdk.parked",
4642
+ `[crossdeck] SDK parked \u2014 server no longer accepts this version's event format. Events held on-device (paused, not lost); update @cross-deck/web${info.minVersion ? ` to >= ${info.minVersion}` : ""} to resume.`,
4643
+ { ...info }
4644
+ );
4575
4645
  }
4576
4646
  });
4577
4647
  const deviceInfo = autoTrack.deviceInfo ? collectDeviceInfo({ appVersion: opts.appVersion ?? void 0 }) : opts.appVersion ? { appVersion: opts.appVersion } : {};
@@ -5648,8 +5718,8 @@ function installUnloadFlush(onUnload) {
5648
5718
  }
5649
5719
 
5650
5720
  // src/_contracts-bundled.ts
5651
- var BUNDLED_IN = "@cross-deck/web@1.7.0";
5652
- var SDK_VERSION2 = "1.7.0";
5721
+ var BUNDLED_IN = "@cross-deck/web@1.8.0";
5722
+ var SDK_VERSION2 = "1.8.0";
5653
5723
  var BUNDLED_CONTRACTS = Object.freeze([
5654
5724
  {
5655
5725
  "id": "contract-failed-payload-schema-lock",
@@ -5771,7 +5841,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5771
5841
  "legal/security/index.html#diagnostic",
5772
5842
  "legal/sdk-data/index.html#b-diagnostic"
5773
5843
  ],
5774
- "bundledIn": "@cross-deck/web@1.7.0",
5844
+ "bundledIn": "@cross-deck/web@1.8.0",
5775
5845
  "runtimeVerified": true
5776
5846
  },
5777
5847
  {
@@ -5811,7 +5881,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5811
5881
  ],
5812
5882
  "registeredAt": "2026-05-26",
5813
5883
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
5814
- "bundledIn": "@cross-deck/web@1.7.0",
5884
+ "bundledIn": "@cross-deck/web@1.8.0",
5815
5885
  "runtimeVerified": true
5816
5886
  },
5817
5887
  {
@@ -5857,7 +5927,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5857
5927
  ],
5858
5928
  "registeredAt": "2026-05-26",
5859
5929
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
5860
- "bundledIn": "@cross-deck/web@1.7.0",
5930
+ "bundledIn": "@cross-deck/web@1.8.0",
5861
5931
  "runtimeVerified": true
5862
5932
  },
5863
5933
  {
@@ -5963,7 +6033,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5963
6033
  ],
5964
6034
  "registeredAt": "2026-05-26",
5965
6035
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
5966
- "bundledIn": "@cross-deck/web@1.7.0",
6036
+ "bundledIn": "@cross-deck/web@1.8.0",
5967
6037
  "runtimeVerified": true
5968
6038
  },
5969
6039
  {
@@ -5991,7 +6061,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5991
6061
  ],
5992
6062
  "registeredAt": "2026-05-26",
5993
6063
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
5994
- "bundledIn": "@cross-deck/web@1.7.0",
6064
+ "bundledIn": "@cross-deck/web@1.8.0",
5995
6065
  "runtimeVerified": false
5996
6066
  },
5997
6067
  {
@@ -6071,7 +6141,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6071
6141
  ],
6072
6142
  "registeredAt": "2026-05-26",
6073
6143
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
6074
- "bundledIn": "@cross-deck/web@1.7.0",
6144
+ "bundledIn": "@cross-deck/web@1.8.0",
6075
6145
  "runtimeVerified": true
6076
6146
  },
6077
6147
  {
@@ -6117,7 +6187,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6117
6187
  ],
6118
6188
  "registeredAt": "2026-05-26",
6119
6189
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
6120
- "bundledIn": "@cross-deck/web@1.7.0",
6190
+ "bundledIn": "@cross-deck/web@1.8.0",
6121
6191
  "runtimeVerified": true
6122
6192
  },
6123
6193
  {
@@ -6159,7 +6229,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6159
6229
  ],
6160
6230
  "registeredAt": "2026-05-26",
6161
6231
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6162
- "bundledIn": "@cross-deck/web@1.7.0",
6232
+ "bundledIn": "@cross-deck/web@1.8.0",
6163
6233
  "runtimeVerified": true
6164
6234
  },
6165
6235
  {
@@ -6193,7 +6263,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6193
6263
  ],
6194
6264
  "registeredAt": "2026-05-26",
6195
6265
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
6196
- "bundledIn": "@cross-deck/web@1.7.0",
6266
+ "bundledIn": "@cross-deck/web@1.8.0",
6197
6267
  "runtimeVerified": false
6198
6268
  }
6199
6269
  ]);