@cross-deck/web 1.11.1 → 1.12.1
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 +17 -0
- package/README.md +14 -1
- package/dist/crossdeck.umd.min.js +2 -2
- package/dist/crossdeck.umd.min.js.map +1 -1
- package/dist/error-codes.json +1 -1
- package/dist/index.cjs +147 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +147 -16
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +79 -5
- package/dist/react.cjs.map +1 -1
- package/dist/react.mjs +79 -5
- package/dist/react.mjs.map +1 -1
- package/dist/vue.cjs +79 -5
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.mjs +79 -5
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
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.
|
|
250
|
+
readonly sdkVersion: "1.12.0";
|
|
251
251
|
/** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
|
|
252
|
-
readonly bundledIn: "@cross-deck/web@1.
|
|
252
|
+
readonly bundledIn: "@cross-deck/web@1.12.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
|
|
@@ -969,7 +969,7 @@ declare class MemoryStorage implements KeyValueStorage {
|
|
|
969
969
|
*
|
|
970
970
|
* Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
|
|
971
971
|
*/
|
|
972
|
-
declare const SDK_VERSION = "1.
|
|
972
|
+
declare const SDK_VERSION = "1.12.1";
|
|
973
973
|
declare const SDK_NAME = "@cross-deck/web";
|
|
974
974
|
|
|
975
975
|
/**
|
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.
|
|
250
|
+
readonly sdkVersion: "1.12.0";
|
|
251
251
|
/** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
|
|
252
|
-
readonly bundledIn: "@cross-deck/web@1.
|
|
252
|
+
readonly bundledIn: "@cross-deck/web@1.12.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
|
|
@@ -969,7 +969,7 @@ declare class MemoryStorage implements KeyValueStorage {
|
|
|
969
969
|
*
|
|
970
970
|
* Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
|
|
971
971
|
*/
|
|
972
|
-
declare const SDK_VERSION = "1.
|
|
972
|
+
declare const SDK_VERSION = "1.12.1";
|
|
973
973
|
declare const SDK_NAME = "@cross-deck/web";
|
|
974
974
|
|
|
975
975
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -69,7 +69,7 @@ function typeMapForStatus(status) {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// src/_version.ts
|
|
72
|
-
var SDK_VERSION = "1.
|
|
72
|
+
var SDK_VERSION = "1.12.1";
|
|
73
73
|
var SDK_NAME = "@cross-deck/web";
|
|
74
74
|
|
|
75
75
|
// src/http.ts
|
|
@@ -1559,6 +1559,7 @@ var AutoTracker = class {
|
|
|
1559
1559
|
this.pageviewId = null;
|
|
1560
1560
|
this.storage = opts?.storage ?? null;
|
|
1561
1561
|
this.sessionKey = opts?.storageKey ?? SESSION_STORAGE_KEY;
|
|
1562
|
+
this.cookieStorage = opts?.cookieStorage ?? null;
|
|
1562
1563
|
}
|
|
1563
1564
|
install() {
|
|
1564
1565
|
if (!isBrowserSafe()) return;
|
|
@@ -1710,9 +1711,74 @@ var AutoTracker = class {
|
|
|
1710
1711
|
lastActivityAt: now,
|
|
1711
1712
|
hiddenAt: null,
|
|
1712
1713
|
endedSent: false,
|
|
1713
|
-
acquisition:
|
|
1714
|
+
acquisition: this.resolveAcquisition()
|
|
1714
1715
|
};
|
|
1715
1716
|
}
|
|
1717
|
+
/**
|
|
1718
|
+
* Session acquisition, with FIRST-TOUCH survival across sessions.
|
|
1719
|
+
*
|
|
1720
|
+
* WHY: `captureAcquisition()` reads the CURRENT page. That is correct for a
|
|
1721
|
+
* fresh arrival, but a session boundary (30-min idle, or a return visit next
|
|
1722
|
+
* week) starts a new session — and a visitor returning directly has no utm_*
|
|
1723
|
+
* and no click id, so the acquisition that actually WON them was silently
|
|
1724
|
+
* replaced with empty. Someone who arrives from LinkedIn on Monday, returns
|
|
1725
|
+
* direct on Tuesday and signs up on Wednesday converted with no source at all.
|
|
1726
|
+
*
|
|
1727
|
+
* That is exactly the question the product exists to answer — "where did this
|
|
1728
|
+
* PAYING CUSTOMER come from" — so origin has to outlive the session that
|
|
1729
|
+
* captured it. We persist the first touch that carried a real signal and,
|
|
1730
|
+
* whenever a later session arrives with nothing, fall back to it.
|
|
1731
|
+
*
|
|
1732
|
+
* Deliberately NOT last-touch-wins: a real new campaign click overwrites
|
|
1733
|
+
* nothing, it simply records a newer touch for that session while the stored
|
|
1734
|
+
* first touch stays put. Write-once by design.
|
|
1735
|
+
*/
|
|
1736
|
+
resolveAcquisition() {
|
|
1737
|
+
const current = captureAcquisition();
|
|
1738
|
+
if (!this.storage && !this.cookieStorage) return current;
|
|
1739
|
+
const key = `${this.sessionKey}_origin_first`;
|
|
1740
|
+
const readFirstTouch = () => {
|
|
1741
|
+
try {
|
|
1742
|
+
const v = this.storage?.getItem(key);
|
|
1743
|
+
if (v) return v;
|
|
1744
|
+
} catch {
|
|
1745
|
+
}
|
|
1746
|
+
try {
|
|
1747
|
+
return this.cookieStorage?.getItem(key) ?? null;
|
|
1748
|
+
} catch {
|
|
1749
|
+
return null;
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
const writeFirstTouch = (v) => {
|
|
1753
|
+
try {
|
|
1754
|
+
this.storage?.setItem(key, v);
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
try {
|
|
1758
|
+
this.cookieStorage?.setItem(key, v);
|
|
1759
|
+
} catch {
|
|
1760
|
+
}
|
|
1761
|
+
};
|
|
1762
|
+
const hasSignal = (a) => Boolean(a.utm_source || a.utm_medium || a.utm_campaign || a.utm_content || a.utm_term || a.gclid || a.fbclid || a.msclkid || a.ttclid || a.li_fat_id || a.twclid);
|
|
1763
|
+
try {
|
|
1764
|
+
if (hasSignal(current)) {
|
|
1765
|
+
if (readFirstTouch() === null) writeFirstTouch(JSON.stringify(current));
|
|
1766
|
+
return current;
|
|
1767
|
+
}
|
|
1768
|
+
const raw = readFirstTouch();
|
|
1769
|
+
if (raw) {
|
|
1770
|
+
const stored = JSON.parse(raw);
|
|
1771
|
+
return {
|
|
1772
|
+
...EMPTY_ACQUISITION,
|
|
1773
|
+
...stored,
|
|
1774
|
+
// Referrer always describes THIS visit, never the remembered one.
|
|
1775
|
+
referrer: current.referrer
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
} catch {
|
|
1779
|
+
}
|
|
1780
|
+
return current;
|
|
1781
|
+
}
|
|
1716
1782
|
/**
|
|
1717
1783
|
* Read the persisted session continuity record. Returns null on no
|
|
1718
1784
|
* storage, no record, malformed JSON, or a record missing its required
|
|
@@ -2708,7 +2774,7 @@ var BreadcrumbBuffer = class {
|
|
|
2708
2774
|
|
|
2709
2775
|
// src/_diagnostic-telemetry.ts
|
|
2710
2776
|
var DIAGNOSTIC_TELEMETRY_ENDPOINT = "https://api.cross-deck.com/v1/events";
|
|
2711
|
-
var DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY = "
|
|
2777
|
+
var DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY = "cd_pub_live_eea2f84dbb834a45b7";
|
|
2712
2778
|
function isDiagnosticTelemetryEnabled() {
|
|
2713
2779
|
return !DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY.startsWith(
|
|
2714
2780
|
"cd_pub_RELIABILITY_PLACEHOLDER"
|
|
@@ -4759,7 +4825,15 @@ var CrossdeckClient = class {
|
|
|
4759
4825
|
// a visit survives full-page navigations (multi-page sites
|
|
4760
4826
|
// re-install the SDK on every page) and honours the same consent
|
|
4761
4827
|
// posture — MemoryStorage when identity persistence is off.
|
|
4762
|
-
|
|
4828
|
+
// cookieStorage is the SAME registrable-domain cookie identity uses.
|
|
4829
|
+
// First-touch origin rides it so the campaign that won a visitor on the
|
|
4830
|
+
// marketing site is still known when they land on the app subdomain —
|
|
4831
|
+
// localStorage alone is per-origin and would lose it at exactly that hop.
|
|
4832
|
+
{
|
|
4833
|
+
storage: effectiveStorage,
|
|
4834
|
+
storageKey: opts.storagePrefix + "session",
|
|
4835
|
+
cookieStorage: cookieStore ?? void 0
|
|
4836
|
+
}
|
|
4763
4837
|
);
|
|
4764
4838
|
this.state.autoTracker = tracker;
|
|
4765
4839
|
tracker.install();
|
|
@@ -5605,7 +5679,7 @@ var CrossdeckClient = class {
|
|
|
5605
5679
|
}
|
|
5606
5680
|
}
|
|
5607
5681
|
this.state.autoTracker?.uninstall();
|
|
5608
|
-
this.state.identity.reset();
|
|
5682
|
+
if (this.state.developerUserId) this.state.identity.reset();
|
|
5609
5683
|
bridgeReadCost({ actor: void 0 });
|
|
5610
5684
|
this.state.entitlements.clearAll();
|
|
5611
5685
|
this.state.events.reset();
|
|
@@ -5839,8 +5913,8 @@ function installUnloadFlush(onUnload) {
|
|
|
5839
5913
|
}
|
|
5840
5914
|
|
|
5841
5915
|
// src/_contracts-bundled.ts
|
|
5842
|
-
var BUNDLED_IN = "@cross-deck/web@1.
|
|
5843
|
-
var SDK_VERSION2 = "1.
|
|
5916
|
+
var BUNDLED_IN = "@cross-deck/web@1.12.0";
|
|
5917
|
+
var SDK_VERSION2 = "1.12.0";
|
|
5844
5918
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
5845
5919
|
{
|
|
5846
5920
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -5962,7 +6036,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5962
6036
|
"legal/security/index.html#diagnostic",
|
|
5963
6037
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
5964
6038
|
],
|
|
5965
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6039
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
5966
6040
|
"runtimeVerified": true
|
|
5967
6041
|
},
|
|
5968
6042
|
{
|
|
@@ -6002,7 +6076,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6002
6076
|
],
|
|
6003
6077
|
"registeredAt": "2026-05-26",
|
|
6004
6078
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
6005
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6079
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6006
6080
|
"runtimeVerified": true
|
|
6007
6081
|
},
|
|
6008
6082
|
{
|
|
@@ -6048,7 +6122,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6048
6122
|
],
|
|
6049
6123
|
"registeredAt": "2026-05-26",
|
|
6050
6124
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
6051
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6125
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6052
6126
|
"runtimeVerified": true
|
|
6053
6127
|
},
|
|
6054
6128
|
{
|
|
@@ -6154,7 +6228,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6154
6228
|
],
|
|
6155
6229
|
"registeredAt": "2026-05-26",
|
|
6156
6230
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
6157
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6231
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6158
6232
|
"runtimeVerified": true
|
|
6159
6233
|
},
|
|
6160
6234
|
{
|
|
@@ -6182,7 +6256,64 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6182
6256
|
],
|
|
6183
6257
|
"registeredAt": "2026-05-26",
|
|
6184
6258
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
|
|
6185
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6259
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6260
|
+
"runtimeVerified": false
|
|
6261
|
+
},
|
|
6262
|
+
{
|
|
6263
|
+
"id": "invalid-input-rejected-natively",
|
|
6264
|
+
"pillar": "errors",
|
|
6265
|
+
"status": "enforced",
|
|
6266
|
+
"claim": "No public SDK API ever crashes the host app, and invalid input never reaches the wire. Invalid input (empty event name, empty userId, out-of-range config such as a non-positive breadcrumb capacity, NaN/Infinity/oversize/cyclic property values) is rejected at the call site WITHOUT a fatal trap. The signalling IDIOM is per-language and intentionally NOT uniform: Web, Node, and React Native THROW a typed CrossdeckError synchronously (code missing_event_name / missing_user_id / invalid_request_error) \u2014 a normal, catchable JavaScript convention where an uncaught throw logs and the app continues; Swift DROPS with a debug-log signal (track_dropped / identify_dropped) to match its non-throwing fire-and-forget surface, and exposes the throwing equivalent only via identifyAndWait(userId:). What is UNIFORM is the invariant, not the mechanism: every SDK rejects the same inputs, no public fire-and-forget API contains a fatalError / assertionFailure / precondition reachable from customer input, and no rejected input is enqueued or transmitted. Swift additionally proves this in BOTH debug and release configuration, because precondition fires under -O while assertionFailure does not. The bug class this contract closes was never the per-language difference \u2014 it was the UNDECLARED difference: each SDK's public API documentation must state its own semantics explicitly (TS docs: 'throws on empty name'; Swift docs: 'drops and logs').",
|
|
6267
|
+
"appliesTo": [
|
|
6268
|
+
"web",
|
|
6269
|
+
"node",
|
|
6270
|
+
"react-native",
|
|
6271
|
+
"swift"
|
|
6272
|
+
],
|
|
6273
|
+
"codeRef": [
|
|
6274
|
+
"sdks/web/src/crossdeck.ts",
|
|
6275
|
+
"sdks/node/src/crossdeck-server.ts",
|
|
6276
|
+
"sdks/react-native/src/crossdeck.ts",
|
|
6277
|
+
"sdks/swift/Sources/Crossdeck/Crossdeck.swift",
|
|
6278
|
+
"sdks/swift/Sources/Crossdeck/Breadcrumbs.swift"
|
|
6279
|
+
],
|
|
6280
|
+
"testRef": [
|
|
6281
|
+
{
|
|
6282
|
+
"file": "sdks/web/tests/crossdeck.test.ts",
|
|
6283
|
+
"name": "track with empty name throws synchronously"
|
|
6284
|
+
},
|
|
6285
|
+
{
|
|
6286
|
+
"file": "sdks/web/tests/crossdeck.test.ts",
|
|
6287
|
+
"name": "rejects empty userId"
|
|
6288
|
+
},
|
|
6289
|
+
{
|
|
6290
|
+
"file": "sdks/node/tests/crossdeck-server.test.ts",
|
|
6291
|
+
"name": "track() throws CrossdeckError with code 'missing_event_name' when event name is empty"
|
|
6292
|
+
},
|
|
6293
|
+
{
|
|
6294
|
+
"file": "sdks/react-native/tests/crossdeck.test.ts",
|
|
6295
|
+
"name": "track('') throws CrossdeckError(missing_event_name) synchronously"
|
|
6296
|
+
},
|
|
6297
|
+
{
|
|
6298
|
+
"file": "sdks/react-native/tests/crossdeck.test.ts",
|
|
6299
|
+
"name": "identify('') rejects with CrossdeckError(missing_user_id)"
|
|
6300
|
+
},
|
|
6301
|
+
{
|
|
6302
|
+
"file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
|
|
6303
|
+
"name": "test_track_dropsEmptyName"
|
|
6304
|
+
},
|
|
6305
|
+
{
|
|
6306
|
+
"file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
|
|
6307
|
+
"name": "test_identifyAndWait_rejectsEmptyId"
|
|
6308
|
+
},
|
|
6309
|
+
{
|
|
6310
|
+
"file": "sdks/swift/Tests/CrossdeckTests/PublicAPIInputSafetyTests.swift",
|
|
6311
|
+
"name": "test_start_withZeroBreadcrumbCapacity_doesNotTrap"
|
|
6312
|
+
}
|
|
6313
|
+
],
|
|
6314
|
+
"registeredAt": "2026-06-11",
|
|
6315
|
+
"firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
|
|
6316
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6186
6317
|
"runtimeVerified": false
|
|
6187
6318
|
},
|
|
6188
6319
|
{
|
|
@@ -6262,7 +6393,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6262
6393
|
],
|
|
6263
6394
|
"registeredAt": "2026-05-26",
|
|
6264
6395
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
|
|
6265
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6396
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6266
6397
|
"runtimeVerified": true
|
|
6267
6398
|
},
|
|
6268
6399
|
{
|
|
@@ -6308,7 +6439,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6308
6439
|
],
|
|
6309
6440
|
"registeredAt": "2026-05-26",
|
|
6310
6441
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
6311
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6442
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6312
6443
|
"runtimeVerified": true
|
|
6313
6444
|
},
|
|
6314
6445
|
{
|
|
@@ -6350,7 +6481,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6350
6481
|
],
|
|
6351
6482
|
"registeredAt": "2026-05-26",
|
|
6352
6483
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
|
|
6353
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6484
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6354
6485
|
"runtimeVerified": true
|
|
6355
6486
|
},
|
|
6356
6487
|
{
|
|
@@ -6384,7 +6515,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6384
6515
|
],
|
|
6385
6516
|
"registeredAt": "2026-05-26",
|
|
6386
6517
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
6387
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6518
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6388
6519
|
"runtimeVerified": false
|
|
6389
6520
|
}
|
|
6390
6521
|
]);
|