@cross-deck/web 1.11.2 → 1.13.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
@@ -1,3 +1,5 @@
1
+ import { C as CrossdeckTrustNamespace } from './trust-C0RcpR5I.mjs';
2
+ export { M as MountTrustPanelOptions, T as TRUST_PANEL_ORIGIN, a as TrustPanelHandle, b as TrustPanelInput, c as TrustResult, d as TrustToken, e as TrustTokenStatus, f as TrustUnavailable, m as mountTrustPanel } from './trust-C0RcpR5I.mjs';
1
3
  import { P as PublicEntitlement, C as CrossdeckOptions, I as IdentifyOptions, A as AliasResult, G as GroupTraits, E as EventProperties, a as PurchaseResult, H as HeartbeatResponse, D as Diagnostics, K as KeyValueStorage } from './types-iqtsOTGy.mjs';
2
4
  export { b as AuditRail, c as AutoTrackOptions, d as EntitlementsListResponse, e as Environment, f as Platform } from './types-iqtsOTGy.mjs';
3
5
 
@@ -247,9 +249,9 @@ declare const CrossdeckContracts: {
247
249
  /** Filter by lifecycle status. */
248
250
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
249
251
  /** Semver of the SDK release these contracts were bundled with. */
250
- readonly sdkVersion: "1.8.0";
252
+ readonly sdkVersion: "1.12.0";
251
253
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.8.0";
254
+ readonly bundledIn: "@cross-deck/web@1.12.0";
253
255
  /**
254
256
  * Resolve a failing test back to the contract it exercises.
255
257
  * Used by test-framework hooks (Vitest `afterEach`, XCTest
@@ -821,6 +823,25 @@ declare class CrossdeckClient {
821
823
  * user is authenticated.
822
824
  */
823
825
  getAnonymousId(): string | null;
826
+ /**
827
+ * **Crossdeck Trust** — human-proof at your signup, native to the SDK.
828
+ *
829
+ * `Crossdeck.trust.panel({ target, onToken })` renders the branded, un-restylable
830
+ * Trust panel (the same cross-origin iframe on every install) and mints a
831
+ * single-use attestation. Hand the token to your server and verify it at the gate
832
+ * (`crossdeck.trust.gate(...)` in @cross-deck/node). The publishable key is taken
833
+ * from your `init()` — you don't pass it again.
834
+ *
835
+ * Fail-open by contract: if the panel can't mint (adblocker, offline, our outage),
836
+ * `ready` resolves with `{ token: null }` and your signup proceeds — the server
837
+ * scores the missing token. It never throws and never blocks your form.
838
+ *
839
+ * @example
840
+ * const { ready } = Crossdeck.trust.panel({ target: "#cd-trust" });
841
+ * const result = await ready; // { token, expiresAt } | { token: null }
842
+ * await createUser({ email, cdTrustToken: result.token });
843
+ */
844
+ get trust(): CrossdeckTrustNamespace;
824
845
  private requireStarted;
825
846
  /**
826
847
  * Build the identity query for /v1/entitlements. Priority:
@@ -969,7 +990,7 @@ declare class MemoryStorage implements KeyValueStorage {
969
990
  *
970
991
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
971
992
  */
972
- declare const SDK_VERSION = "1.11.2";
993
+ declare const SDK_VERSION = "1.13.0";
973
994
  declare const SDK_NAME = "@cross-deck/web";
974
995
 
975
996
  /**
@@ -1053,4 +1074,4 @@ interface DeviceInfo {
1053
1074
  appVersion?: string;
1054
1075
  }
1055
1076
 
1056
- export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
1077
+ export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, CrossdeckTrustNamespace, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { C as CrossdeckTrustNamespace } from './trust-C0RcpR5I.js';
2
+ export { M as MountTrustPanelOptions, T as TRUST_PANEL_ORIGIN, a as TrustPanelHandle, b as TrustPanelInput, c as TrustResult, d as TrustToken, e as TrustTokenStatus, f as TrustUnavailable, m as mountTrustPanel } from './trust-C0RcpR5I.js';
1
3
  import { P as PublicEntitlement, C as CrossdeckOptions, I as IdentifyOptions, A as AliasResult, G as GroupTraits, E as EventProperties, a as PurchaseResult, H as HeartbeatResponse, D as Diagnostics, K as KeyValueStorage } from './types-iqtsOTGy.js';
2
4
  export { b as AuditRail, c as AutoTrackOptions, d as EntitlementsListResponse, e as Environment, f as Platform } from './types-iqtsOTGy.js';
3
5
 
@@ -247,9 +249,9 @@ declare const CrossdeckContracts: {
247
249
  /** Filter by lifecycle status. */
248
250
  readonly withStatus: (status: ContractStatus) => readonly Contract[];
249
251
  /** Semver of the SDK release these contracts were bundled with. */
250
- readonly sdkVersion: "1.8.0";
252
+ readonly sdkVersion: "1.12.0";
251
253
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.8.0";
254
+ readonly bundledIn: "@cross-deck/web@1.12.0";
253
255
  /**
254
256
  * Resolve a failing test back to the contract it exercises.
255
257
  * Used by test-framework hooks (Vitest `afterEach`, XCTest
@@ -821,6 +823,25 @@ declare class CrossdeckClient {
821
823
  * user is authenticated.
822
824
  */
823
825
  getAnonymousId(): string | null;
826
+ /**
827
+ * **Crossdeck Trust** — human-proof at your signup, native to the SDK.
828
+ *
829
+ * `Crossdeck.trust.panel({ target, onToken })` renders the branded, un-restylable
830
+ * Trust panel (the same cross-origin iframe on every install) and mints a
831
+ * single-use attestation. Hand the token to your server and verify it at the gate
832
+ * (`crossdeck.trust.gate(...)` in @cross-deck/node). The publishable key is taken
833
+ * from your `init()` — you don't pass it again.
834
+ *
835
+ * Fail-open by contract: if the panel can't mint (adblocker, offline, our outage),
836
+ * `ready` resolves with `{ token: null }` and your signup proceeds — the server
837
+ * scores the missing token. It never throws and never blocks your form.
838
+ *
839
+ * @example
840
+ * const { ready } = Crossdeck.trust.panel({ target: "#cd-trust" });
841
+ * const result = await ready; // { token, expiresAt } | { token: null }
842
+ * await createUser({ email, cdTrustToken: result.token });
843
+ */
844
+ get trust(): CrossdeckTrustNamespace;
824
845
  private requireStarted;
825
846
  /**
826
847
  * Build the identity query for /v1/entitlements. Priority:
@@ -969,7 +990,7 @@ declare class MemoryStorage implements KeyValueStorage {
969
990
  *
970
991
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
971
992
  */
972
- declare const SDK_VERSION = "1.11.2";
993
+ declare const SDK_VERSION = "1.13.0";
973
994
  declare const SDK_NAME = "@cross-deck/web";
974
995
 
975
996
  /**
@@ -1053,4 +1074,4 @@ interface DeviceInfo {
1053
1074
  appVersion?: string;
1054
1075
  }
1055
1076
 
1056
- export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
1077
+ export { AliasResult, type Breadcrumb, type BreadcrumbCategory, type BreadcrumbLevel, CROSSDECK_ERROR_CODES, type CapturedError, type ConsentState, type Contract, type ContractAppliesTo, type ContractFailureInput, type ContractPillar, type ContractStatus, type ContractTestRef, Crossdeck, CrossdeckClient, CrossdeckContracts, CrossdeckError, type CrossdeckErrorPayload, type CrossdeckErrorType, CrossdeckOptions, CrossdeckTrustNamespace, DEFAULT_BASE_URL, type DeviceInfo, Diagnostics, type ErrorCodeEntry, type ErrorLevel, EventProperties, GroupTraits, HeartbeatResponse, IdentifyOptions, KeyValueStorage, MemoryStorage, PublicEntitlement, PurchaseResult, SDK_NAME, SDK_VERSION, type StackFrame, getErrorCode };
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.11.2";
72
+ var SDK_VERSION = "1.13.0";
73
73
  var SDK_NAME = "@cross-deck/web";
74
74
 
75
75
  // src/http.ts
@@ -326,6 +326,125 @@ function randomChars(count) {
326
326
  return out.join("");
327
327
  }
328
328
 
329
+ // src/trust.ts
330
+ var TRUST_PANEL_ORIGIN = "https://trust.cross-deck.com";
331
+ var MAX_WIDTH_PX = 360;
332
+ var INITIAL_HEIGHT_PX = 132;
333
+ var MIN_HEIGHT_PX = 96;
334
+ var MAX_HEIGHT_PX = 220;
335
+ var DEFAULT_TIMEOUT_MS2 = 15e3;
336
+ function mountTrustPanel(opts) {
337
+ const origin = normalizeOrigin(opts.origin) || TRUST_PANEL_ORIGIN;
338
+ const timeoutMs = clampInt(opts.timeoutMs, DEFAULT_TIMEOUT_MS2, 1e3, 12e4);
339
+ let settled = false;
340
+ let onMessage = null;
341
+ let timer = null;
342
+ let frame = null;
343
+ let resolveReady;
344
+ const ready = new Promise((res) => {
345
+ resolveReady = res;
346
+ });
347
+ function settle(result, reason) {
348
+ if (settled) return;
349
+ settled = true;
350
+ if (timer) safe(() => clearTimeout(timer));
351
+ if (onMessage) safe(() => window.removeEventListener("message", onMessage));
352
+ if (result) {
353
+ safe(() => opts.onToken?.(result));
354
+ resolveReady(result);
355
+ } else {
356
+ const why = reason || "unavailable";
357
+ safe(() => opts.onUnavailable?.(why));
358
+ resolveReady({ token: null, expiresAt: null, reason: why });
359
+ }
360
+ }
361
+ try {
362
+ if (typeof window === "undefined" || typeof document === "undefined") {
363
+ queueMicrotask(() => settle(null, "no_dom"));
364
+ return makeHandle();
365
+ }
366
+ const host = resolveTarget(opts.target);
367
+ frame = createFrame(origin, opts.publicKey);
368
+ if (!host) {
369
+ queueMicrotask(() => settle(null, "no_mount_target"));
370
+ return makeHandle();
371
+ }
372
+ onMessage = (ev) => {
373
+ if (ev.origin !== origin) return;
374
+ if (!ev.data || !frame || ev.source !== frame.contentWindow) return;
375
+ const d = ev.data;
376
+ if (d.type === "cd-trust-size" && typeof d.height === "number") {
377
+ safe(() => {
378
+ if (frame) frame.style.height = clampInt(d.height, INITIAL_HEIGHT_PX, MIN_HEIGHT_PX, MAX_HEIGHT_PX) + "px";
379
+ });
380
+ } else if (d.type === "cd-trust-token" && typeof d.token === "string") {
381
+ settle({ token: d.token, expiresAt: typeof d.expMs === "number" ? d.expMs : null });
382
+ }
383
+ };
384
+ window.addEventListener("message", onMessage);
385
+ host.appendChild(frame);
386
+ timer = setTimeout(() => settle(null, "timeout"), timeoutMs);
387
+ } catch (err) {
388
+ queueMicrotask(() => settle(null, "mount_error:" + safeMessage(err)));
389
+ }
390
+ return makeHandle();
391
+ function makeHandle() {
392
+ return {
393
+ frame,
394
+ ready,
395
+ destroy() {
396
+ if (onMessage) safe(() => window.removeEventListener("message", onMessage));
397
+ if (timer) safe(() => clearTimeout(timer));
398
+ safe(() => frame?.parentNode?.removeChild(frame));
399
+ settle(null, "destroyed");
400
+ }
401
+ };
402
+ }
403
+ }
404
+ function createFrame(origin, publicKey) {
405
+ const frame = document.createElement("iframe");
406
+ frame.title = "Protected by Crossdeck Trust";
407
+ frame.setAttribute("aria-label", "Protected by Crossdeck Trust");
408
+ frame.setAttribute("scrolling", "no");
409
+ frame.setAttribute("allowtransparency", "true");
410
+ frame.style.cssText = "border:0;display:block;width:100%;max-width:" + MAX_WIDTH_PX + "px;height:" + INITIAL_HEIGHT_PX + "px;margin:0;background:transparent;color-scheme:light;";
411
+ frame.src = origin + "/panel?k=" + encodeURIComponent(publicKey || "");
412
+ return frame;
413
+ }
414
+ function resolveTarget(target) {
415
+ try {
416
+ if (typeof target === "string") return document.querySelector(target);
417
+ return target || null;
418
+ } catch {
419
+ return null;
420
+ }
421
+ }
422
+ function normalizeOrigin(o) {
423
+ if (!o || typeof o !== "string") return null;
424
+ try {
425
+ return new URL(o).origin;
426
+ } catch {
427
+ return null;
428
+ }
429
+ }
430
+ function clampInt(v, fallback, min, max) {
431
+ const n = typeof v === "number" && isFinite(v) ? v : fallback;
432
+ return Math.min(Math.max(n, min), max);
433
+ }
434
+ function safe(fn) {
435
+ try {
436
+ fn();
437
+ } catch {
438
+ }
439
+ }
440
+ function safeMessage(err) {
441
+ try {
442
+ return err instanceof Error ? err.message : String(err);
443
+ } catch {
444
+ return "unknown";
445
+ }
446
+ }
447
+
329
448
  // src/hash.ts
330
449
  var K = new Uint32Array([
331
450
  1116352408,
@@ -1559,6 +1678,7 @@ var AutoTracker = class {
1559
1678
  this.pageviewId = null;
1560
1679
  this.storage = opts?.storage ?? null;
1561
1680
  this.sessionKey = opts?.storageKey ?? SESSION_STORAGE_KEY;
1681
+ this.cookieStorage = opts?.cookieStorage ?? null;
1562
1682
  }
1563
1683
  install() {
1564
1684
  if (!isBrowserSafe()) return;
@@ -1710,8 +1830,73 @@ var AutoTracker = class {
1710
1830
  lastActivityAt: now,
1711
1831
  hiddenAt: null,
1712
1832
  endedSent: false,
1713
- acquisition: captureAcquisition()
1833
+ acquisition: this.resolveAcquisition()
1834
+ };
1835
+ }
1836
+ /**
1837
+ * Session acquisition, with FIRST-TOUCH survival across sessions.
1838
+ *
1839
+ * WHY: `captureAcquisition()` reads the CURRENT page. That is correct for a
1840
+ * fresh arrival, but a session boundary (30-min idle, or a return visit next
1841
+ * week) starts a new session — and a visitor returning directly has no utm_*
1842
+ * and no click id, so the acquisition that actually WON them was silently
1843
+ * replaced with empty. Someone who arrives from LinkedIn on Monday, returns
1844
+ * direct on Tuesday and signs up on Wednesday converted with no source at all.
1845
+ *
1846
+ * That is exactly the question the product exists to answer — "where did this
1847
+ * PAYING CUSTOMER come from" — so origin has to outlive the session that
1848
+ * captured it. We persist the first touch that carried a real signal and,
1849
+ * whenever a later session arrives with nothing, fall back to it.
1850
+ *
1851
+ * Deliberately NOT last-touch-wins: a real new campaign click overwrites
1852
+ * nothing, it simply records a newer touch for that session while the stored
1853
+ * first touch stays put. Write-once by design.
1854
+ */
1855
+ resolveAcquisition() {
1856
+ const current = captureAcquisition();
1857
+ if (!this.storage && !this.cookieStorage) return current;
1858
+ const key = `${this.sessionKey}_origin_first`;
1859
+ const readFirstTouch = () => {
1860
+ try {
1861
+ const v = this.storage?.getItem(key);
1862
+ if (v) return v;
1863
+ } catch {
1864
+ }
1865
+ try {
1866
+ return this.cookieStorage?.getItem(key) ?? null;
1867
+ } catch {
1868
+ return null;
1869
+ }
1870
+ };
1871
+ const writeFirstTouch = (v) => {
1872
+ try {
1873
+ this.storage?.setItem(key, v);
1874
+ } catch {
1875
+ }
1876
+ try {
1877
+ this.cookieStorage?.setItem(key, v);
1878
+ } catch {
1879
+ }
1714
1880
  };
1881
+ 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);
1882
+ try {
1883
+ if (hasSignal(current)) {
1884
+ if (readFirstTouch() === null) writeFirstTouch(JSON.stringify(current));
1885
+ return current;
1886
+ }
1887
+ const raw = readFirstTouch();
1888
+ if (raw) {
1889
+ const stored = JSON.parse(raw);
1890
+ return {
1891
+ ...EMPTY_ACQUISITION,
1892
+ ...stored,
1893
+ // Referrer always describes THIS visit, never the remembered one.
1894
+ referrer: current.referrer
1895
+ };
1896
+ }
1897
+ } catch {
1898
+ }
1899
+ return current;
1715
1900
  }
1716
1901
  /**
1717
1902
  * Read the persisted session continuity record. Returns null on no
@@ -4759,7 +4944,15 @@ var CrossdeckClient = class {
4759
4944
  // a visit survives full-page navigations (multi-page sites
4760
4945
  // re-install the SDK on every page) and honours the same consent
4761
4946
  // posture — MemoryStorage when identity persistence is off.
4762
- { storage: effectiveStorage, storageKey: opts.storagePrefix + "session" }
4947
+ // cookieStorage is the SAME registrable-domain cookie identity uses.
4948
+ // First-touch origin rides it so the campaign that won a visitor on the
4949
+ // marketing site is still known when they land on the app subdomain —
4950
+ // localStorage alone is per-origin and would lose it at exactly that hop.
4951
+ {
4952
+ storage: effectiveStorage,
4953
+ storageKey: opts.storagePrefix + "session",
4954
+ cookieStorage: cookieStore ?? void 0
4955
+ }
4763
4956
  );
4764
4957
  this.state.autoTracker = tracker;
4765
4958
  tracker.install();
@@ -5714,6 +5907,30 @@ var CrossdeckClient = class {
5714
5907
  getAnonymousId() {
5715
5908
  return this.state ? this.state.identity.anonymousId : null;
5716
5909
  }
5910
+ /**
5911
+ * **Crossdeck Trust** — human-proof at your signup, native to the SDK.
5912
+ *
5913
+ * `Crossdeck.trust.panel({ target, onToken })` renders the branded, un-restylable
5914
+ * Trust panel (the same cross-origin iframe on every install) and mints a
5915
+ * single-use attestation. Hand the token to your server and verify it at the gate
5916
+ * (`crossdeck.trust.gate(...)` in @cross-deck/node). The publishable key is taken
5917
+ * from your `init()` — you don't pass it again.
5918
+ *
5919
+ * Fail-open by contract: if the panel can't mint (adblocker, offline, our outage),
5920
+ * `ready` resolves with `{ token: null }` and your signup proceeds — the server
5921
+ * scores the missing token. It never throws and never blocks your form.
5922
+ *
5923
+ * @example
5924
+ * const { ready } = Crossdeck.trust.panel({ target: "#cd-trust" });
5925
+ * const result = await ready; // { token, expiresAt } | { token: null }
5926
+ * await createUser({ email, cdTrustToken: result.token });
5927
+ */
5928
+ get trust() {
5929
+ const publicKey = this.state ? this.state.options.publicKey : "";
5930
+ return {
5931
+ panel: (input) => mountTrustPanel({ ...input, publicKey })
5932
+ };
5933
+ }
5717
5934
  // ---------- private helpers ----------
5718
5935
  requireStarted() {
5719
5936
  if (!this.state) {
@@ -5839,8 +6056,8 @@ function installUnloadFlush(onUnload) {
5839
6056
  }
5840
6057
 
5841
6058
  // src/_contracts-bundled.ts
5842
- var BUNDLED_IN = "@cross-deck/web@1.8.0";
5843
- var SDK_VERSION2 = "1.8.0";
6059
+ var BUNDLED_IN = "@cross-deck/web@1.12.0";
6060
+ var SDK_VERSION2 = "1.12.0";
5844
6061
  var BUNDLED_CONTRACTS = Object.freeze([
5845
6062
  {
5846
6063
  "id": "contract-failed-payload-schema-lock",
@@ -5962,7 +6179,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5962
6179
  "legal/security/index.html#diagnostic",
5963
6180
  "legal/sdk-data/index.html#b-diagnostic"
5964
6181
  ],
5965
- "bundledIn": "@cross-deck/web@1.8.0",
6182
+ "bundledIn": "@cross-deck/web@1.12.0",
5966
6183
  "runtimeVerified": true
5967
6184
  },
5968
6185
  {
@@ -6002,7 +6219,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6002
6219
  ],
6003
6220
  "registeredAt": "2026-05-26",
6004
6221
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
6005
- "bundledIn": "@cross-deck/web@1.8.0",
6222
+ "bundledIn": "@cross-deck/web@1.12.0",
6006
6223
  "runtimeVerified": true
6007
6224
  },
6008
6225
  {
@@ -6048,7 +6265,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6048
6265
  ],
6049
6266
  "registeredAt": "2026-05-26",
6050
6267
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
6051
- "bundledIn": "@cross-deck/web@1.8.0",
6268
+ "bundledIn": "@cross-deck/web@1.12.0",
6052
6269
  "runtimeVerified": true
6053
6270
  },
6054
6271
  {
@@ -6154,7 +6371,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6154
6371
  ],
6155
6372
  "registeredAt": "2026-05-26",
6156
6373
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
6157
- "bundledIn": "@cross-deck/web@1.8.0",
6374
+ "bundledIn": "@cross-deck/web@1.12.0",
6158
6375
  "runtimeVerified": true
6159
6376
  },
6160
6377
  {
@@ -6182,7 +6399,64 @@ var BUNDLED_CONTRACTS = Object.freeze([
6182
6399
  ],
6183
6400
  "registeredAt": "2026-05-26",
6184
6401
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
6185
- "bundledIn": "@cross-deck/web@1.8.0",
6402
+ "bundledIn": "@cross-deck/web@1.12.0",
6403
+ "runtimeVerified": false
6404
+ },
6405
+ {
6406
+ "id": "invalid-input-rejected-natively",
6407
+ "pillar": "errors",
6408
+ "status": "enforced",
6409
+ "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').",
6410
+ "appliesTo": [
6411
+ "web",
6412
+ "node",
6413
+ "react-native",
6414
+ "swift"
6415
+ ],
6416
+ "codeRef": [
6417
+ "sdks/web/src/crossdeck.ts",
6418
+ "sdks/node/src/crossdeck-server.ts",
6419
+ "sdks/react-native/src/crossdeck.ts",
6420
+ "sdks/swift/Sources/Crossdeck/Crossdeck.swift",
6421
+ "sdks/swift/Sources/Crossdeck/Breadcrumbs.swift"
6422
+ ],
6423
+ "testRef": [
6424
+ {
6425
+ "file": "sdks/web/tests/crossdeck.test.ts",
6426
+ "name": "track with empty name throws synchronously"
6427
+ },
6428
+ {
6429
+ "file": "sdks/web/tests/crossdeck.test.ts",
6430
+ "name": "rejects empty userId"
6431
+ },
6432
+ {
6433
+ "file": "sdks/node/tests/crossdeck-server.test.ts",
6434
+ "name": "track() throws CrossdeckError with code 'missing_event_name' when event name is empty"
6435
+ },
6436
+ {
6437
+ "file": "sdks/react-native/tests/crossdeck.test.ts",
6438
+ "name": "track('') throws CrossdeckError(missing_event_name) synchronously"
6439
+ },
6440
+ {
6441
+ "file": "sdks/react-native/tests/crossdeck.test.ts",
6442
+ "name": "identify('') rejects with CrossdeckError(missing_user_id)"
6443
+ },
6444
+ {
6445
+ "file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
6446
+ "name": "test_track_dropsEmptyName"
6447
+ },
6448
+ {
6449
+ "file": "sdks/swift/Tests/CrossdeckTests/CrossdeckPublicAPITests.swift",
6450
+ "name": "test_identifyAndWait_rejectsEmptyId"
6451
+ },
6452
+ {
6453
+ "file": "sdks/swift/Tests/CrossdeckTests/PublicAPIInputSafetyTests.swift",
6454
+ "name": "test_start_withZeroBreadcrumbCapacity_doesNotTrap"
6455
+ }
6456
+ ],
6457
+ "registeredAt": "2026-06-11",
6458
+ "firstRegisteredIn": "swift trap-on-input class fix \u2014 first machine-tested Swift release",
6459
+ "bundledIn": "@cross-deck/web@1.12.0",
6186
6460
  "runtimeVerified": false
6187
6461
  },
6188
6462
  {
@@ -6262,7 +6536,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6262
6536
  ],
6263
6537
  "registeredAt": "2026-05-26",
6264
6538
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
6265
- "bundledIn": "@cross-deck/web@1.8.0",
6539
+ "bundledIn": "@cross-deck/web@1.12.0",
6266
6540
  "runtimeVerified": true
6267
6541
  },
6268
6542
  {
@@ -6308,7 +6582,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6308
6582
  ],
6309
6583
  "registeredAt": "2026-05-26",
6310
6584
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
6311
- "bundledIn": "@cross-deck/web@1.8.0",
6585
+ "bundledIn": "@cross-deck/web@1.12.0",
6312
6586
  "runtimeVerified": true
6313
6587
  },
6314
6588
  {
@@ -6350,7 +6624,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6350
6624
  ],
6351
6625
  "registeredAt": "2026-05-26",
6352
6626
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6353
- "bundledIn": "@cross-deck/web@1.8.0",
6627
+ "bundledIn": "@cross-deck/web@1.12.0",
6354
6628
  "runtimeVerified": true
6355
6629
  },
6356
6630
  {
@@ -6384,7 +6658,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6384
6658
  ],
6385
6659
  "registeredAt": "2026-05-26",
6386
6660
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
6387
- "bundledIn": "@cross-deck/web@1.8.0",
6661
+ "bundledIn": "@cross-deck/web@1.12.0",
6388
6662
  "runtimeVerified": false
6389
6663
  }
6390
6664
  ]);
@@ -6448,6 +6722,8 @@ export {
6448
6722
  MemoryStorage,
6449
6723
  SDK_NAME,
6450
6724
  SDK_VERSION,
6451
- getErrorCode
6725
+ TRUST_PANEL_ORIGIN,
6726
+ getErrorCode,
6727
+ mountTrustPanel
6452
6728
  };
6453
6729
  //# sourceMappingURL=index.mjs.map