@cross-deck/web 1.7.0 → 1.9.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/CHANGELOG.md +40 -0
- package/README.md +12 -6
- package/dist/crossdeck.umd.min.js +2 -2
- package/dist/crossdeck.umd.min.js.map +1 -1
- package/dist/error-codes.json +8 -1
- package/dist/index.cjs +113 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +30 -5
- package/dist/index.d.ts +30 -5
- package/dist/index.mjs +113 -14
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +102 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +102 -3
- package/dist/react.mjs.map +1 -1
- package/dist/vue.cjs +102 -3
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.mjs +102 -3
- package/dist/vue.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/contracts.json +0 -546
package/dist/error-codes.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"generatedAt": "2026-06-
|
|
3
|
+
"generatedAt": "2026-06-24T10:42:10.345Z",
|
|
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.
|
|
107
|
+
var SDK_VERSION = "1.9.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") {
|
|
@@ -1350,6 +1405,16 @@ function hasDocument() {
|
|
|
1350
1405
|
return typeof globalThis.document !== "undefined";
|
|
1351
1406
|
}
|
|
1352
1407
|
|
|
1408
|
+
// src/read-cost-bridge.ts
|
|
1409
|
+
var BUCKETS_BRIDGE_KEY = "__crossdeckBucketsBridge__";
|
|
1410
|
+
function bridgeReadCost(ctx) {
|
|
1411
|
+
try {
|
|
1412
|
+
const setter = globalThis[BUCKETS_BRIDGE_KEY];
|
|
1413
|
+
if (typeof setter === "function") setter(ctx);
|
|
1414
|
+
} catch {
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1353
1418
|
// src/device-info.ts
|
|
1354
1419
|
function isBrowser() {
|
|
1355
1420
|
return typeof globalThis.window !== "undefined" && typeof globalThis.document !== "undefined" && typeof globalThis.navigator !== "undefined";
|
|
@@ -2905,6 +2970,13 @@ var CROSSDECK_ERROR_CODES = Object.freeze([
|
|
|
2905
2970
|
description: "A field is present but the value failed validation (wrong shape, wrong length, wrong enum value).",
|
|
2906
2971
|
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
2972
|
retryable: false
|
|
2973
|
+
},
|
|
2974
|
+
{
|
|
2975
|
+
code: "sdk_version_unsupported",
|
|
2976
|
+
type: "version_error",
|
|
2977
|
+
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.",
|
|
2978
|
+
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/.",
|
|
2979
|
+
retryable: false
|
|
2908
2980
|
}
|
|
2909
2981
|
]);
|
|
2910
2982
|
function getErrorCode(code) {
|
|
@@ -3450,7 +3522,8 @@ var BACKEND_WIRE_CODES = Object.freeze([
|
|
|
3450
3522
|
"google_not_supported",
|
|
3451
3523
|
"stripe_not_supported",
|
|
3452
3524
|
"missing_required_param",
|
|
3453
|
-
"invalid_param_value"
|
|
3525
|
+
"invalid_param_value",
|
|
3526
|
+
"sdk_version_unsupported"
|
|
3454
3527
|
]);
|
|
3455
3528
|
var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
|
|
3456
3529
|
contractId: "sdk-error-codes-catalogue",
|
|
@@ -4607,6 +4680,13 @@ var CrossdeckClient = class {
|
|
|
4607
4680
|
headline,
|
|
4608
4681
|
{ ...info }
|
|
4609
4682
|
);
|
|
4683
|
+
},
|
|
4684
|
+
onParked: (info) => {
|
|
4685
|
+
debug.emit(
|
|
4686
|
+
"sdk.parked",
|
|
4687
|
+
`[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.`,
|
|
4688
|
+
{ ...info }
|
|
4689
|
+
);
|
|
4610
4690
|
}
|
|
4611
4691
|
});
|
|
4612
4692
|
const deviceInfo = autoTrack.deviceInfo ? collectDeviceInfo({ appVersion: opts.appVersion ?? void 0 }) : opts.appVersion ? { appVersion: opts.appVersion } : {};
|
|
@@ -4824,6 +4904,7 @@ var CrossdeckClient = class {
|
|
|
4824
4904
|
message: "identify(userId) requires a non-empty userId."
|
|
4825
4905
|
});
|
|
4826
4906
|
}
|
|
4907
|
+
bridgeReadCost({ actor: userId });
|
|
4827
4908
|
if (!s.consent.analytics) {
|
|
4828
4909
|
s.debug.emit(
|
|
4829
4910
|
"sdk.consent_denied",
|
|
@@ -5476,6 +5557,7 @@ var CrossdeckClient = class {
|
|
|
5476
5557
|
}
|
|
5477
5558
|
this.state.autoTracker?.uninstall();
|
|
5478
5559
|
this.state.identity.reset();
|
|
5560
|
+
bridgeReadCost({ actor: void 0 });
|
|
5479
5561
|
this.state.entitlements.clearAll();
|
|
5480
5562
|
this.state.events.reset();
|
|
5481
5563
|
this.state.superProps.clear();
|
|
@@ -5566,6 +5648,23 @@ var CrossdeckClient = class {
|
|
|
5566
5648
|
const s = this.requireStarted();
|
|
5567
5649
|
return s.identity.crossdeckCustomerId ?? s.developerUserId ?? s.identity.anonymousId;
|
|
5568
5650
|
}
|
|
5651
|
+
/**
|
|
5652
|
+
* The device-scoped anonymous id the SDK minted on first boot and persists
|
|
5653
|
+
* across launches (stable until reset()). Public accessor so a server-to-
|
|
5654
|
+
* server flow or a block/suspension gate can pass the device identity to
|
|
5655
|
+
* POST /v1/resolve without reaching into private storage.
|
|
5656
|
+
*
|
|
5657
|
+
* Returns `null` BEFORE init() — there is no anon id yet, and a gate that
|
|
5658
|
+
* fires during early app boot should get a clean falsy, not a throw. (This is
|
|
5659
|
+
* deliberately softer than getCheckoutReference(), which requires init.)
|
|
5660
|
+
*
|
|
5661
|
+
* Note: /v1/resolve also accepts a VERIFIED identity (userId + idToken)
|
|
5662
|
+
* without an anonymousId, and that path is higher-trust — prefer it where the
|
|
5663
|
+
* user is authenticated.
|
|
5664
|
+
*/
|
|
5665
|
+
getAnonymousId() {
|
|
5666
|
+
return this.state ? this.state.identity.anonymousId : null;
|
|
5667
|
+
}
|
|
5569
5668
|
// ---------- private helpers ----------
|
|
5570
5669
|
requireStarted() {
|
|
5571
5670
|
if (!this.state) {
|
|
@@ -5683,8 +5782,8 @@ function installUnloadFlush(onUnload) {
|
|
|
5683
5782
|
}
|
|
5684
5783
|
|
|
5685
5784
|
// src/_contracts-bundled.ts
|
|
5686
|
-
var BUNDLED_IN = "@cross-deck/web@1.
|
|
5687
|
-
var SDK_VERSION2 = "1.
|
|
5785
|
+
var BUNDLED_IN = "@cross-deck/web@1.8.0";
|
|
5786
|
+
var SDK_VERSION2 = "1.8.0";
|
|
5688
5787
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
5689
5788
|
{
|
|
5690
5789
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -5806,7 +5905,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5806
5905
|
"legal/security/index.html#diagnostic",
|
|
5807
5906
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
5808
5907
|
],
|
|
5809
|
-
"bundledIn": "@cross-deck/web@1.
|
|
5908
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
5810
5909
|
"runtimeVerified": true
|
|
5811
5910
|
},
|
|
5812
5911
|
{
|
|
@@ -5846,7 +5945,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5846
5945
|
],
|
|
5847
5946
|
"registeredAt": "2026-05-26",
|
|
5848
5947
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
5849
|
-
"bundledIn": "@cross-deck/web@1.
|
|
5948
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
5850
5949
|
"runtimeVerified": true
|
|
5851
5950
|
},
|
|
5852
5951
|
{
|
|
@@ -5892,7 +5991,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5892
5991
|
],
|
|
5893
5992
|
"registeredAt": "2026-05-26",
|
|
5894
5993
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
5895
|
-
"bundledIn": "@cross-deck/web@1.
|
|
5994
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
5896
5995
|
"runtimeVerified": true
|
|
5897
5996
|
},
|
|
5898
5997
|
{
|
|
@@ -5998,7 +6097,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5998
6097
|
],
|
|
5999
6098
|
"registeredAt": "2026-05-26",
|
|
6000
6099
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
6001
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6100
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6002
6101
|
"runtimeVerified": true
|
|
6003
6102
|
},
|
|
6004
6103
|
{
|
|
@@ -6026,7 +6125,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6026
6125
|
],
|
|
6027
6126
|
"registeredAt": "2026-05-26",
|
|
6028
6127
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
|
|
6029
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6128
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6030
6129
|
"runtimeVerified": false
|
|
6031
6130
|
},
|
|
6032
6131
|
{
|
|
@@ -6106,7 +6205,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6106
6205
|
],
|
|
6107
6206
|
"registeredAt": "2026-05-26",
|
|
6108
6207
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
|
|
6109
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6208
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6110
6209
|
"runtimeVerified": true
|
|
6111
6210
|
},
|
|
6112
6211
|
{
|
|
@@ -6152,7 +6251,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6152
6251
|
],
|
|
6153
6252
|
"registeredAt": "2026-05-26",
|
|
6154
6253
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
6155
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6254
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6156
6255
|
"runtimeVerified": true
|
|
6157
6256
|
},
|
|
6158
6257
|
{
|
|
@@ -6194,7 +6293,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6194
6293
|
],
|
|
6195
6294
|
"registeredAt": "2026-05-26",
|
|
6196
6295
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
|
|
6197
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6296
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6198
6297
|
"runtimeVerified": true
|
|
6199
6298
|
},
|
|
6200
6299
|
{
|
|
@@ -6228,7 +6327,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6228
6327
|
],
|
|
6229
6328
|
"registeredAt": "2026-05-26",
|
|
6230
6329
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
6231
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6330
|
+
"bundledIn": "@cross-deck/web@1.8.0",
|
|
6232
6331
|
"runtimeVerified": false
|
|
6233
6332
|
}
|
|
6234
6333
|
]);
|