@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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "generatedAt": "2026-06-10T12:59:26.148Z",
3
+ "generatedAt": "2026-06-11T07:13:22.322Z",
4
4
  "sdk": "@cross-deck/web",
5
5
  "codes": [
6
6
  {
@@ -191,6 +191,13 @@
191
191
  "description": "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
192
192
  "resolution": "Read error.message for the specific field + reason. SDK-managed call sites should never emit this — file a bug if you do.",
193
193
  "retryable": false
194
+ },
195
+ {
196
+ "code": "sdk_version_unsupported",
197
+ "type": "version_error",
198
+ "description": "HTTP 426 — 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.",
199
+ "resolution": "Update @cross-deck/web to at least the version in error.minVersion and redeploy — the parked queue backfills automatically. See https://cross-deck.com/docs/sdk-event-durability/.",
200
+ "retryable": false
194
201
  }
195
202
  ]
196
203
  }
package/dist/index.cjs CHANGED
@@ -43,6 +43,8 @@ var CrossdeckError = class _CrossdeckError extends Error {
43
43
  this.requestId = payload.requestId;
44
44
  this.status = payload.status;
45
45
  this.retryAfterMs = payload.retryAfterMs;
46
+ this.minVersion = payload.minVersion;
47
+ this.surface = payload.surface;
46
48
  Object.setPrototypeOf(this, _CrossdeckError.prototype);
47
49
  }
48
50
  };
@@ -63,7 +65,10 @@ async function crossdeckErrorFromResponse(res) {
63
65
  message: envelope.message ?? `HTTP ${res.status}`,
64
66
  requestId: envelope.request_id ?? requestId,
65
67
  status: res.status,
66
- retryAfterMs
68
+ retryAfterMs,
69
+ // PARK metadata, present only on a 426 / sdk_version_unsupported body.
70
+ minVersion: typeof envelope.minVersion === "string" ? envelope.minVersion : void 0,
71
+ surface: typeof envelope.surface === "string" ? envelope.surface : void 0
67
72
  });
68
73
  }
69
74
  return new CrossdeckError({
@@ -99,7 +104,7 @@ function typeMapForStatus(status) {
99
104
  }
100
105
 
101
106
  // src/_version.ts
102
- var SDK_VERSION = "1.7.0";
107
+ var SDK_VERSION = "1.8.0";
103
108
  var SDK_NAME = "@cross-deck/web";
104
109
 
105
110
  // src/http.ts
@@ -939,6 +944,18 @@ var EventQueue = class {
939
944
  this.cancelTimer = null;
940
945
  this.firstFlushFired = false;
941
946
  this.nextRetryAt = null;
947
+ /**
948
+ * PARK state (HTTP 426 / `sdk_version_unsupported`). Once parked, the
949
+ * queue stops flushing — the server has told us our wire dialect is too
950
+ * old, and retrying the same payload only burns the user's battery and
951
+ * bandwidth and drips pointless rejects into the server logs until the
952
+ * app ships an upgraded SDK. The held events stay durable (persisted)
953
+ * and are delivered by the next boot's rehydrate, post-upgrade. Parked
954
+ * is per-instance: a fresh boot (the upgraded build) starts unparked.
955
+ */
956
+ this.parked = false;
957
+ /** One developer-facing console warning per instance — never per-event spam. */
958
+ this.parkWarned = false;
942
959
  this.retry = new RetryPolicy(cfg.retry ?? {});
943
960
  this.persistent = cfg.persistentStore ?? null;
944
961
  if (this.persistent) {
@@ -994,6 +1011,7 @@ var EventQueue = class {
994
1011
  * flushes (pagehide / visibilitychange→hidden / beforeunload).
995
1012
  */
996
1013
  async flush(options = {}) {
1014
+ if (this.parked) return null;
997
1015
  let batch;
998
1016
  let batchId;
999
1017
  if (this.pendingBatch !== null && this.pendingBatchId !== null) {
@@ -1045,6 +1063,29 @@ var EventQueue = class {
1045
1063
  } catch (err) {
1046
1064
  const message = err instanceof Error ? err.message : String(err);
1047
1065
  this.lastError = message;
1066
+ if (isVersionRejected(err)) {
1067
+ this.parked = true;
1068
+ this.buffer = [...batch, ...this.buffer];
1069
+ if (this.buffer.length > HARD_BUFFER_CAP) {
1070
+ const overflow = this.buffer.length - HARD_BUFFER_CAP;
1071
+ this.buffer.splice(0, overflow);
1072
+ this.dropped += overflow;
1073
+ }
1074
+ this.pendingBatch = null;
1075
+ this.pendingBatchId = null;
1076
+ this.inFlight -= batch.length;
1077
+ this.persistAll();
1078
+ this.cfg.onBufferChange?.(this.buffer.length);
1079
+ const minVersion = versionRejectionFloor(err);
1080
+ if (!this.parkWarned) {
1081
+ this.parkWarned = true;
1082
+ console.warn(
1083
+ `[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.`
1084
+ );
1085
+ }
1086
+ this.cfg.onParked?.({ minVersion, surface: versionRejectionSurface(err) });
1087
+ return null;
1088
+ }
1048
1089
  if (isPermanent4xx(err)) {
1049
1090
  const droppedCount = batch.length;
1050
1091
  this.pendingBatch = null;
@@ -1163,8 +1204,22 @@ function isPermanent4xx(err) {
1163
1204
  if (typeof status !== "number" || !Number.isFinite(status)) return false;
1164
1205
  if (status < 400 || status >= 500) return false;
1165
1206
  if (status === 408 || status === 429) return false;
1207
+ if (status === 426) return false;
1166
1208
  return true;
1167
1209
  }
1210
+ function isVersionRejected(err) {
1211
+ if (!err || typeof err !== "object") return false;
1212
+ if (err.status === 426) return true;
1213
+ return err.code === "sdk_version_unsupported";
1214
+ }
1215
+ function versionRejectionFloor(err) {
1216
+ const v = err?.minVersion;
1217
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1218
+ }
1219
+ function versionRejectionSurface(err) {
1220
+ const v = err?.surface;
1221
+ return typeof v === "string" && v.length > 0 ? v : void 0;
1222
+ }
1168
1223
  function defaultScheduler(fn, ms) {
1169
1224
  const id = setTimeout(fn, ms);
1170
1225
  if (typeof id.unref === "function") {
@@ -2905,6 +2960,13 @@ var CROSSDECK_ERROR_CODES = Object.freeze([
2905
2960
  description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
2906
2961
  resolution: "Read error.message for the specific field + reason. SDK-managed call sites should never emit this \u2014 file a bug if you do.",
2907
2962
  retryable: false
2963
+ },
2964
+ {
2965
+ code: "sdk_version_unsupported",
2966
+ type: "version_error",
2967
+ 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.",
2968
+ 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/.",
2969
+ retryable: false
2908
2970
  }
2909
2971
  ]);
2910
2972
  function getErrorCode(code) {
@@ -3450,7 +3512,8 @@ var BACKEND_WIRE_CODES = Object.freeze([
3450
3512
  "google_not_supported",
3451
3513
  "stripe_not_supported",
3452
3514
  "missing_required_param",
3453
- "invalid_param_value"
3515
+ "invalid_param_value",
3516
+ "sdk_version_unsupported"
3454
3517
  ]);
3455
3518
  var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3456
3519
  contractId: "sdk-error-codes-catalogue",
@@ -4607,6 +4670,13 @@ var CrossdeckClient = class {
4607
4670
  headline,
4608
4671
  { ...info }
4609
4672
  );
4673
+ },
4674
+ onParked: (info) => {
4675
+ debug.emit(
4676
+ "sdk.parked",
4677
+ `[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.`,
4678
+ { ...info }
4679
+ );
4610
4680
  }
4611
4681
  });
4612
4682
  const deviceInfo = autoTrack.deviceInfo ? collectDeviceInfo({ appVersion: opts.appVersion ?? void 0 }) : opts.appVersion ? { appVersion: opts.appVersion } : {};
@@ -5683,8 +5753,8 @@ function installUnloadFlush(onUnload) {
5683
5753
  }
5684
5754
 
5685
5755
  // src/_contracts-bundled.ts
5686
- var BUNDLED_IN = "@cross-deck/web@1.7.0";
5687
- var SDK_VERSION2 = "1.7.0";
5756
+ var BUNDLED_IN = "@cross-deck/web@1.8.0";
5757
+ var SDK_VERSION2 = "1.8.0";
5688
5758
  var BUNDLED_CONTRACTS = Object.freeze([
5689
5759
  {
5690
5760
  "id": "contract-failed-payload-schema-lock",
@@ -5806,7 +5876,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5806
5876
  "legal/security/index.html#diagnostic",
5807
5877
  "legal/sdk-data/index.html#b-diagnostic"
5808
5878
  ],
5809
- "bundledIn": "@cross-deck/web@1.7.0",
5879
+ "bundledIn": "@cross-deck/web@1.8.0",
5810
5880
  "runtimeVerified": true
5811
5881
  },
5812
5882
  {
@@ -5846,7 +5916,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5846
5916
  ],
5847
5917
  "registeredAt": "2026-05-26",
5848
5918
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
5849
- "bundledIn": "@cross-deck/web@1.7.0",
5919
+ "bundledIn": "@cross-deck/web@1.8.0",
5850
5920
  "runtimeVerified": true
5851
5921
  },
5852
5922
  {
@@ -5892,7 +5962,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5892
5962
  ],
5893
5963
  "registeredAt": "2026-05-26",
5894
5964
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
5895
- "bundledIn": "@cross-deck/web@1.7.0",
5965
+ "bundledIn": "@cross-deck/web@1.8.0",
5896
5966
  "runtimeVerified": true
5897
5967
  },
5898
5968
  {
@@ -5998,7 +6068,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5998
6068
  ],
5999
6069
  "registeredAt": "2026-05-26",
6000
6070
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
6001
- "bundledIn": "@cross-deck/web@1.7.0",
6071
+ "bundledIn": "@cross-deck/web@1.8.0",
6002
6072
  "runtimeVerified": true
6003
6073
  },
6004
6074
  {
@@ -6026,7 +6096,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6026
6096
  ],
6027
6097
  "registeredAt": "2026-05-26",
6028
6098
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
6029
- "bundledIn": "@cross-deck/web@1.7.0",
6099
+ "bundledIn": "@cross-deck/web@1.8.0",
6030
6100
  "runtimeVerified": false
6031
6101
  },
6032
6102
  {
@@ -6106,7 +6176,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6106
6176
  ],
6107
6177
  "registeredAt": "2026-05-26",
6108
6178
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
6109
- "bundledIn": "@cross-deck/web@1.7.0",
6179
+ "bundledIn": "@cross-deck/web@1.8.0",
6110
6180
  "runtimeVerified": true
6111
6181
  },
6112
6182
  {
@@ -6152,7 +6222,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6152
6222
  ],
6153
6223
  "registeredAt": "2026-05-26",
6154
6224
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
6155
- "bundledIn": "@cross-deck/web@1.7.0",
6225
+ "bundledIn": "@cross-deck/web@1.8.0",
6156
6226
  "runtimeVerified": true
6157
6227
  },
6158
6228
  {
@@ -6194,7 +6264,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6194
6264
  ],
6195
6265
  "registeredAt": "2026-05-26",
6196
6266
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6197
- "bundledIn": "@cross-deck/web@1.7.0",
6267
+ "bundledIn": "@cross-deck/web@1.8.0",
6198
6268
  "runtimeVerified": true
6199
6269
  },
6200
6270
  {
@@ -6228,7 +6298,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6228
6298
  ],
6229
6299
  "registeredAt": "2026-05-26",
6230
6300
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
6231
- "bundledIn": "@cross-deck/web@1.7.0",
6301
+ "bundledIn": "@cross-deck/web@1.8.0",
6232
6302
  "runtimeVerified": false
6233
6303
  }
6234
6304
  ]);