@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/error-codes.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -104,7 +104,7 @@ function typeMapForStatus(status) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
// src/_version.ts
|
|
107
|
-
var SDK_VERSION = "1.
|
|
107
|
+
var SDK_VERSION = "1.12.1";
|
|
108
108
|
var SDK_NAME = "@cross-deck/web";
|
|
109
109
|
|
|
110
110
|
// src/http.ts
|
|
@@ -1594,6 +1594,7 @@ var AutoTracker = class {
|
|
|
1594
1594
|
this.pageviewId = null;
|
|
1595
1595
|
this.storage = opts?.storage ?? null;
|
|
1596
1596
|
this.sessionKey = opts?.storageKey ?? SESSION_STORAGE_KEY;
|
|
1597
|
+
this.cookieStorage = opts?.cookieStorage ?? null;
|
|
1597
1598
|
}
|
|
1598
1599
|
install() {
|
|
1599
1600
|
if (!isBrowserSafe()) return;
|
|
@@ -1745,9 +1746,74 @@ var AutoTracker = class {
|
|
|
1745
1746
|
lastActivityAt: now,
|
|
1746
1747
|
hiddenAt: null,
|
|
1747
1748
|
endedSent: false,
|
|
1748
|
-
acquisition:
|
|
1749
|
+
acquisition: this.resolveAcquisition()
|
|
1749
1750
|
};
|
|
1750
1751
|
}
|
|
1752
|
+
/**
|
|
1753
|
+
* Session acquisition, with FIRST-TOUCH survival across sessions.
|
|
1754
|
+
*
|
|
1755
|
+
* WHY: `captureAcquisition()` reads the CURRENT page. That is correct for a
|
|
1756
|
+
* fresh arrival, but a session boundary (30-min idle, or a return visit next
|
|
1757
|
+
* week) starts a new session — and a visitor returning directly has no utm_*
|
|
1758
|
+
* and no click id, so the acquisition that actually WON them was silently
|
|
1759
|
+
* replaced with empty. Someone who arrives from LinkedIn on Monday, returns
|
|
1760
|
+
* direct on Tuesday and signs up on Wednesday converted with no source at all.
|
|
1761
|
+
*
|
|
1762
|
+
* That is exactly the question the product exists to answer — "where did this
|
|
1763
|
+
* PAYING CUSTOMER come from" — so origin has to outlive the session that
|
|
1764
|
+
* captured it. We persist the first touch that carried a real signal and,
|
|
1765
|
+
* whenever a later session arrives with nothing, fall back to it.
|
|
1766
|
+
*
|
|
1767
|
+
* Deliberately NOT last-touch-wins: a real new campaign click overwrites
|
|
1768
|
+
* nothing, it simply records a newer touch for that session while the stored
|
|
1769
|
+
* first touch stays put. Write-once by design.
|
|
1770
|
+
*/
|
|
1771
|
+
resolveAcquisition() {
|
|
1772
|
+
const current = captureAcquisition();
|
|
1773
|
+
if (!this.storage && !this.cookieStorage) return current;
|
|
1774
|
+
const key = `${this.sessionKey}_origin_first`;
|
|
1775
|
+
const readFirstTouch = () => {
|
|
1776
|
+
try {
|
|
1777
|
+
const v = this.storage?.getItem(key);
|
|
1778
|
+
if (v) return v;
|
|
1779
|
+
} catch {
|
|
1780
|
+
}
|
|
1781
|
+
try {
|
|
1782
|
+
return this.cookieStorage?.getItem(key) ?? null;
|
|
1783
|
+
} catch {
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
const writeFirstTouch = (v) => {
|
|
1788
|
+
try {
|
|
1789
|
+
this.storage?.setItem(key, v);
|
|
1790
|
+
} catch {
|
|
1791
|
+
}
|
|
1792
|
+
try {
|
|
1793
|
+
this.cookieStorage?.setItem(key, v);
|
|
1794
|
+
} catch {
|
|
1795
|
+
}
|
|
1796
|
+
};
|
|
1797
|
+
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);
|
|
1798
|
+
try {
|
|
1799
|
+
if (hasSignal(current)) {
|
|
1800
|
+
if (readFirstTouch() === null) writeFirstTouch(JSON.stringify(current));
|
|
1801
|
+
return current;
|
|
1802
|
+
}
|
|
1803
|
+
const raw = readFirstTouch();
|
|
1804
|
+
if (raw) {
|
|
1805
|
+
const stored = JSON.parse(raw);
|
|
1806
|
+
return {
|
|
1807
|
+
...EMPTY_ACQUISITION,
|
|
1808
|
+
...stored,
|
|
1809
|
+
// Referrer always describes THIS visit, never the remembered one.
|
|
1810
|
+
referrer: current.referrer
|
|
1811
|
+
};
|
|
1812
|
+
}
|
|
1813
|
+
} catch {
|
|
1814
|
+
}
|
|
1815
|
+
return current;
|
|
1816
|
+
}
|
|
1751
1817
|
/**
|
|
1752
1818
|
* Read the persisted session continuity record. Returns null on no
|
|
1753
1819
|
* storage, no record, malformed JSON, or a record missing its required
|
|
@@ -2743,7 +2809,7 @@ var BreadcrumbBuffer = class {
|
|
|
2743
2809
|
|
|
2744
2810
|
// src/_diagnostic-telemetry.ts
|
|
2745
2811
|
var DIAGNOSTIC_TELEMETRY_ENDPOINT = "https://api.cross-deck.com/v1/events";
|
|
2746
|
-
var DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY = "
|
|
2812
|
+
var DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY = "cd_pub_live_eea2f84dbb834a45b7";
|
|
2747
2813
|
function isDiagnosticTelemetryEnabled() {
|
|
2748
2814
|
return !DIAGNOSTIC_TELEMETRY_PUBLISHABLE_KEY.startsWith(
|
|
2749
2815
|
"cd_pub_RELIABILITY_PLACEHOLDER"
|
|
@@ -4794,7 +4860,15 @@ var CrossdeckClient = class {
|
|
|
4794
4860
|
// a visit survives full-page navigations (multi-page sites
|
|
4795
4861
|
// re-install the SDK on every page) and honours the same consent
|
|
4796
4862
|
// posture — MemoryStorage when identity persistence is off.
|
|
4797
|
-
|
|
4863
|
+
// cookieStorage is the SAME registrable-domain cookie identity uses.
|
|
4864
|
+
// First-touch origin rides it so the campaign that won a visitor on the
|
|
4865
|
+
// marketing site is still known when they land on the app subdomain —
|
|
4866
|
+
// localStorage alone is per-origin and would lose it at exactly that hop.
|
|
4867
|
+
{
|
|
4868
|
+
storage: effectiveStorage,
|
|
4869
|
+
storageKey: opts.storagePrefix + "session",
|
|
4870
|
+
cookieStorage: cookieStore ?? void 0
|
|
4871
|
+
}
|
|
4798
4872
|
);
|
|
4799
4873
|
this.state.autoTracker = tracker;
|
|
4800
4874
|
tracker.install();
|
|
@@ -5640,7 +5714,7 @@ var CrossdeckClient = class {
|
|
|
5640
5714
|
}
|
|
5641
5715
|
}
|
|
5642
5716
|
this.state.autoTracker?.uninstall();
|
|
5643
|
-
this.state.identity.reset();
|
|
5717
|
+
if (this.state.developerUserId) this.state.identity.reset();
|
|
5644
5718
|
bridgeReadCost({ actor: void 0 });
|
|
5645
5719
|
this.state.entitlements.clearAll();
|
|
5646
5720
|
this.state.events.reset();
|
|
@@ -5874,8 +5948,8 @@ function installUnloadFlush(onUnload) {
|
|
|
5874
5948
|
}
|
|
5875
5949
|
|
|
5876
5950
|
// src/_contracts-bundled.ts
|
|
5877
|
-
var BUNDLED_IN = "@cross-deck/web@1.
|
|
5878
|
-
var SDK_VERSION2 = "1.
|
|
5951
|
+
var BUNDLED_IN = "@cross-deck/web@1.12.0";
|
|
5952
|
+
var SDK_VERSION2 = "1.12.0";
|
|
5879
5953
|
var BUNDLED_CONTRACTS = Object.freeze([
|
|
5880
5954
|
{
|
|
5881
5955
|
"id": "contract-failed-payload-schema-lock",
|
|
@@ -5997,7 +6071,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
5997
6071
|
"legal/security/index.html#diagnostic",
|
|
5998
6072
|
"legal/sdk-data/index.html#b-diagnostic"
|
|
5999
6073
|
],
|
|
6000
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6074
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6001
6075
|
"runtimeVerified": true
|
|
6002
6076
|
},
|
|
6003
6077
|
{
|
|
@@ -6037,7 +6111,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6037
6111
|
],
|
|
6038
6112
|
"registeredAt": "2026-05-26",
|
|
6039
6113
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
|
|
6040
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6114
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6041
6115
|
"runtimeVerified": true
|
|
6042
6116
|
},
|
|
6043
6117
|
{
|
|
@@ -6083,7 +6157,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6083
6157
|
],
|
|
6084
6158
|
"registeredAt": "2026-05-26",
|
|
6085
6159
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
|
|
6086
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6160
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6087
6161
|
"runtimeVerified": true
|
|
6088
6162
|
},
|
|
6089
6163
|
{
|
|
@@ -6189,7 +6263,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6189
6263
|
],
|
|
6190
6264
|
"registeredAt": "2026-05-26",
|
|
6191
6265
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
|
|
6192
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6266
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6193
6267
|
"runtimeVerified": true
|
|
6194
6268
|
},
|
|
6195
6269
|
{
|
|
@@ -6217,7 +6291,64 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6217
6291
|
],
|
|
6218
6292
|
"registeredAt": "2026-05-26",
|
|
6219
6293
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
|
|
6220
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6294
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6295
|
+
"runtimeVerified": false
|
|
6296
|
+
},
|
|
6297
|
+
{
|
|
6298
|
+
"id": "invalid-input-rejected-natively",
|
|
6299
|
+
"pillar": "errors",
|
|
6300
|
+
"status": "enforced",
|
|
6301
|
+
"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').",
|
|
6302
|
+
"appliesTo": [
|
|
6303
|
+
"web",
|
|
6304
|
+
"node",
|
|
6305
|
+
"react-native",
|
|
6306
|
+
"swift"
|
|
6307
|
+
],
|
|
6308
|
+
"codeRef": [
|
|
6309
|
+
"sdks/web/src/crossdeck.ts",
|
|
6310
|
+
"sdks/node/src/crossdeck-server.ts",
|
|
6311
|
+
"sdks/react-native/src/crossdeck.ts",
|
|
6312
|
+
"sdks/swift/Sources/Crossdeck/Crossdeck.swift",
|
|
6313
|
+
"sdks/swift/Sources/Crossdeck/Breadcrumbs.swift"
|
|
6314
|
+
],
|
|
6315
|
+
"testRef": [
|
|
6316
|
+
{
|
|
6317
|
+
"file": "sdks/web/tests/crossdeck.test.ts",
|
|
6318
|
+
"name": "track with empty name throws synchronously"
|
|
6319
|
+
},
|
|
6320
|
+
{
|
|
6321
|
+
"file": "sdks/web/tests/crossdeck.test.ts",
|
|
6322
|
+
"name": "rejects empty userId"
|
|
6323
|
+
},
|
|
6324
|
+
{
|
|
6325
|
+
"file": "sdks/node/tests/crossdeck-server.test.ts",
|
|
6326
|
+
"name": "track() throws CrossdeckError with code 'missing_event_name' when event name is empty"
|
|
6327
|
+
},
|
|
6328
|
+
{
|
|
6329
|
+
"file": "sdks/react-native/tests/crossdeck.test.ts",
|
|
6330
|
+
"name": "track('') throws CrossdeckError(missing_event_name) synchronously"
|
|
6331
|
+
},
|
|
6332
|
+
{
|
|
6333
|
+
"file": "sdks/react-native/tests/crossdeck.test.ts",
|
|
6334
|
+
"name": "identify('') rejects with CrossdeckError(missing_user_id)"
|
|
6335
|
+
},
|
|
6336
|
+
{
|
|
6337
|
+
"file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
|
|
6338
|
+
"name": "test_track_dropsEmptyName"
|
|
6339
|
+
},
|
|
6340
|
+
{
|
|
6341
|
+
"file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
|
|
6342
|
+
"name": "test_identifyAndWait_rejectsEmptyId"
|
|
6343
|
+
},
|
|
6344
|
+
{
|
|
6345
|
+
"file": "sdks/swift/Tests/CrossdeckTests/PublicAPIInputSafetyTests.swift",
|
|
6346
|
+
"name": "test_start_withZeroBreadcrumbCapacity_doesNotTrap"
|
|
6347
|
+
}
|
|
6348
|
+
],
|
|
6349
|
+
"registeredAt": "2026-06-11",
|
|
6350
|
+
"firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
|
|
6351
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6221
6352
|
"runtimeVerified": false
|
|
6222
6353
|
},
|
|
6223
6354
|
{
|
|
@@ -6297,7 +6428,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6297
6428
|
],
|
|
6298
6429
|
"registeredAt": "2026-05-26",
|
|
6299
6430
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
|
|
6300
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6431
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6301
6432
|
"runtimeVerified": true
|
|
6302
6433
|
},
|
|
6303
6434
|
{
|
|
@@ -6343,7 +6474,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6343
6474
|
],
|
|
6344
6475
|
"registeredAt": "2026-05-26",
|
|
6345
6476
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
|
|
6346
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6477
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6347
6478
|
"runtimeVerified": true
|
|
6348
6479
|
},
|
|
6349
6480
|
{
|
|
@@ -6385,7 +6516,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6385
6516
|
],
|
|
6386
6517
|
"registeredAt": "2026-05-26",
|
|
6387
6518
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
|
|
6388
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6519
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6389
6520
|
"runtimeVerified": true
|
|
6390
6521
|
},
|
|
6391
6522
|
{
|
|
@@ -6419,7 +6550,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
|
|
|
6419
6550
|
],
|
|
6420
6551
|
"registeredAt": "2026-05-26",
|
|
6421
6552
|
"firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
|
|
6422
|
-
"bundledIn": "@cross-deck/web@1.
|
|
6553
|
+
"bundledIn": "@cross-deck/web@1.12.0",
|
|
6423
6554
|
"runtimeVerified": false
|
|
6424
6555
|
}
|
|
6425
6556
|
]);
|