@cross-deck/web 1.6.4 → 1.7.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
@@ -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.6.4";
250
+ readonly sdkVersion: "1.7.0";
251
251
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.6.4";
252
+ readonly bundledIn: "@cross-deck/web@1.7.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
@@ -462,7 +462,6 @@ declare class CrossdeckClient {
462
462
  private verifiers;
463
463
  private verifierReporter;
464
464
  private verifierCtx;
465
- private flushIntervalMs;
466
465
  /**
467
466
  * Boot the SDK. Idempotent — calling init twice with the same options
468
467
  * is a no-op; calling with different options replaces the previous
@@ -937,7 +936,7 @@ declare class MemoryStorage implements KeyValueStorage {
937
936
  *
938
937
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
939
938
  */
940
- declare const SDK_VERSION = "1.6.4";
939
+ declare const SDK_VERSION = "1.7.0";
941
940
  declare const SDK_NAME = "@cross-deck/web";
942
941
 
943
942
  /**
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.6.4";
250
+ readonly sdkVersion: "1.7.0";
251
251
  /** Fully-qualified bundle identifier — e.g. `@cross-deck/web@1.4.2`. */
252
- readonly bundledIn: "@cross-deck/web@1.6.4";
252
+ readonly bundledIn: "@cross-deck/web@1.7.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
@@ -462,7 +462,6 @@ declare class CrossdeckClient {
462
462
  private verifiers;
463
463
  private verifierReporter;
464
464
  private verifierCtx;
465
- private flushIntervalMs;
466
465
  /**
467
466
  * Boot the SDK. Idempotent — calling init twice with the same options
468
467
  * is a no-op; calling with different options replaces the previous
@@ -937,7 +936,7 @@ declare class MemoryStorage implements KeyValueStorage {
937
936
  *
938
937
  * Do NOT edit by hand — `node scripts/sync-sdk-versions.mjs`.
939
938
  */
940
- declare const SDK_VERSION = "1.6.4";
939
+ declare const SDK_VERSION = "1.7.0";
941
940
  declare const SDK_NAME = "@cross-deck/web";
942
941
 
943
942
  /**
package/dist/index.mjs CHANGED
@@ -64,7 +64,7 @@ function typeMapForStatus(status) {
64
64
  }
65
65
 
66
66
  // src/_version.ts
67
- var SDK_VERSION = "1.6.4";
67
+ var SDK_VERSION = "1.7.0";
68
68
  var SDK_NAME = "@cross-deck/web";
69
69
 
70
70
  // src/http.ts
@@ -980,9 +980,13 @@ var EventQueue = class {
980
980
  const env = this.cfg.envelope();
981
981
  const result = await this.cfg.http.request("POST", "/events", {
982
982
  body: {
983
- // NorthStar §13.1 batch envelope. The backend validates these
984
- // against the API-key-resolved app and rejects mismatches
985
- // loudly (env_mismatch).
983
+ // Event Envelope v1 batch envelope (backend/docs/
984
+ // event-envelope-spec-v1.md §1). `envelopeVersion` is the
985
+ // schema/wire version the server parses against ("can I parse
986
+ // this?") and is DISTINCT from `sdk.version` ("which build is in
987
+ // the wild?") — two questions, two fields, never conflated. The
988
+ // backend refuses payloads with a missing/unknown envelopeVersion.
989
+ envelopeVersion: 1,
986
990
  appId: env.appId,
987
991
  environment: env.environment,
988
992
  sdk: env.sdk,
@@ -1437,6 +1441,15 @@ var AutoTracker = class {
1437
1441
  this.cleanups = [];
1438
1442
  /** Last time we flushed lastActivityAt to storage (throttle gate). */
1439
1443
  this.lastPersistAt = 0;
1444
+ /**
1445
+ * Event Envelope v1 §3 — per-session monotonic sequence counter.
1446
+ * The single owner of the seq counter lives here, alongside all other
1447
+ * session state. Incremented atomically at track() time (via nextSeq()),
1448
+ * reset to 0 whenever a new session boundary is crossed (startNewSession
1449
+ * path + resetSession). Persists across background/foreground within a
1450
+ * session — only a real session boundary (not a tab hide/show) resets it.
1451
+ */
1452
+ this._sessionSeq = 0;
1440
1453
  /**
1441
1454
  * Stable per-page-view identifier. Minted at every `page.viewed`
1442
1455
  * emission and attached to every subsequent event until the next
@@ -1521,6 +1534,20 @@ var AutoTracker = class {
1521
1534
  get currentAcquisition() {
1522
1535
  return this.session?.acquisition ?? EMPTY_ACQUISITION;
1523
1536
  }
1537
+ /**
1538
+ * Event Envelope v1 §3 — return the next seq value for the current session
1539
+ * and advance the counter. Called SYNCHRONOUSLY at track() time, before any
1540
+ * async dispatch, so the seq assignment order is deterministic (call order,
1541
+ * not scheduler luck). The counter is reset to 0 at every session boundary;
1542
+ * it is NOT reset by tab hide/show (spec §3 clause 1: backgrounding does not
1543
+ * reset seq). When no session is active (Node, before init, after uninstall),
1544
+ * returns 0 and leaves the counter unchanged — fallback, not an error.
1545
+ */
1546
+ nextSeq() {
1547
+ const seq = this._sessionSeq;
1548
+ this._sessionSeq += 1;
1549
+ return seq;
1550
+ }
1524
1551
  // ---------- sessions ----------
1525
1552
  installSessionTracking() {
1526
1553
  const now = Date.now();
@@ -1572,6 +1599,7 @@ var AutoTracker = class {
1572
1599
  }
1573
1600
  startNewSession() {
1574
1601
  const now = Date.now();
1602
+ this._sessionSeq = 0;
1575
1603
  return {
1576
1604
  sessionId: mintSessionId(),
1577
1605
  startedAt: now,
@@ -2300,6 +2328,40 @@ function writeJson(storage, key, value) {
2300
2328
  }
2301
2329
  }
2302
2330
 
2331
+ // src/internal-opt-out.ts
2332
+ var INTERNAL_OPT_OUT_PROPERTY = "$crossdeck_internal";
2333
+ var STORAGE_KEY = "crossdeck.internalOptOut";
2334
+ function localStore() {
2335
+ try {
2336
+ return typeof localStorage !== "undefined" ? localStorage : null;
2337
+ } catch {
2338
+ return null;
2339
+ }
2340
+ }
2341
+ function processInternalOptOutUrl() {
2342
+ try {
2343
+ const search = typeof location !== "undefined" ? location.search : "";
2344
+ const params = new URLSearchParams(search || "");
2345
+ if (!params.has("crossdeck_internal")) return;
2346
+ const store = localStore();
2347
+ if (!store) return;
2348
+ const v = params.get("crossdeck_internal");
2349
+ if (v === "1" || v === "true") {
2350
+ store.setItem(STORAGE_KEY, "1");
2351
+ } else if (v === "0" || v === "false") {
2352
+ store.removeItem(STORAGE_KEY);
2353
+ }
2354
+ } catch {
2355
+ }
2356
+ }
2357
+ function isInternalOptOut() {
2358
+ try {
2359
+ return localStore()?.getItem(STORAGE_KEY) === "1";
2360
+ } catch {
2361
+ return false;
2362
+ }
2363
+ }
2364
+
2303
2365
  // src/web-vitals.ts
2304
2366
  var WebVitalsTracker = class {
2305
2367
  constructor(cfg, report) {
@@ -2830,15 +2892,6 @@ var VerifierReporter = class {
2830
2892
  constructor(ctx) {
2831
2893
  this.ctx = ctx;
2832
2894
  this.reentrancyDepth = 0;
2833
- /** The live verifier set — attached by the SDK right after init so the
2834
- * emit path can schema-lock its OWN outgoing telemetry against the real
2835
- * payload. Null until attached (boot-time reports still work without it). */
2836
- this.verifiers = null;
2837
- }
2838
- /** Hand the reporter the live verifier set so `reportFail` can run the
2839
- * `onReportContractFailure` hook on the exact payload it's about to emit. */
2840
- attachVerifiers(verifiers) {
2841
- this.verifiers = verifiers;
2842
2895
  }
2843
2896
  /**
2844
2897
  * Report a verifier result. Pass `operation` for hot-path results
@@ -2867,7 +2920,7 @@ var VerifierReporter = class {
2867
2920
  if (this.reentrancyDepth > 0) return;
2868
2921
  this.reentrancyDepth += 1;
2869
2922
  try {
2870
- const payload = {
2923
+ this.ctx.emitTelemetry({
2871
2924
  contract_id: result.contractId,
2872
2925
  sdk_version: this.ctx.sdkVersion,
2873
2926
  sdk_platform: "web",
@@ -2875,13 +2928,7 @@ var VerifierReporter = class {
2875
2928
  run_context: this.ctx.runContext,
2876
2929
  run_id: this.ctx.runId,
2877
2930
  verification_phase: phase
2878
- };
2879
- if (this.verifiers) {
2880
- runOnReportContractFailure(this.verifiers, this, this.ctx, {
2881
- outgoingPayload: payload
2882
- });
2883
- }
2884
- this.ctx.emitTelemetry(payload);
2931
+ });
2885
2932
  } finally {
2886
2933
  this.reentrancyDepth -= 1;
2887
2934
  }
@@ -3171,31 +3218,9 @@ var VERIFIER_FLUSH_INTERVAL_PARITY = {
3171
3218
  "eventFlushIntervalMs default = 2000ms (Web/Node/RN/Swift/Android parity)",
3172
3219
  nowMs() - t0
3173
3220
  );
3174
- },
3175
- // Hot-path hook — on every real track() we re-affirm the LIVE configured
3176
- // flush interval (carried on the observation), so the parity guarantee is
3177
- // verified against the running SDK in the customer flow, not just a
3178
- // canonical constant at boot. It can't change per-event, but observing it
3179
- // per-event is what makes the contract genuinely runtime-verified.
3180
- hooks: {
3181
- onTrack(obs) {
3182
- const t0 = nowMs();
3183
- const CANONICAL_DEFAULT_MS = 2e3;
3184
- const ms = obs.flushIntervalMs;
3185
- if (typeof ms !== "number" || ms < 100 || ms > 6e4) {
3186
- return fail(
3187
- "flush-interval-parity",
3188
- `live eventFlushIntervalMs=${ms} outside reasonable bounds [100, 60000]`,
3189
- nowMs() - t0
3190
- );
3191
- }
3192
- return pass(
3193
- "flush-interval-parity",
3194
- ms === CANONICAL_DEFAULT_MS ? "live eventFlushIntervalMs = 2000ms (canonical parity value)" : `live eventFlushIntervalMs = ${ms}ms (permitted override; canonical default 2000ms)`,
3195
- nowMs() - t0
3196
- );
3197
- }
3198
3221
  }
3222
+ // No hot-path hook — the flush interval is set once at start() and
3223
+ // never changes per-operation.
3199
3224
  };
3200
3225
  function buildFlushIntervalVerifier(configuredIntervalMs) {
3201
3226
  return {
@@ -3322,26 +3347,6 @@ var CONTRACT_FAILED_FORBIDDEN_FIELDS = [
3322
3347
  "referrer",
3323
3348
  "deviceId"
3324
3349
  ];
3325
- function contractFailedSchemaViolation(keys) {
3326
- const allowed = /* @__PURE__ */ new Set([
3327
- ...CONTRACT_FAILED_REQUIRED_FIELDS,
3328
- ...CONTRACT_FAILED_OPTIONAL_FIELDS
3329
- ]);
3330
- const forbidden = new Set(CONTRACT_FAILED_FORBIDDEN_FIELDS);
3331
- for (const required of CONTRACT_FAILED_REQUIRED_FIELDS) {
3332
- if (!keys.includes(required)) return `missing required field: ${required}`;
3333
- }
3334
- for (const k of keys) {
3335
- if (forbidden.has(k)) return `forbidden field on wire: ${k}`;
3336
- }
3337
- for (const k of keys) {
3338
- if (!allowed.has(k)) {
3339
- return `unrecognised field on wire: ${k} (not in required \u222A optional)`;
3340
- }
3341
- }
3342
- return null;
3343
- }
3344
- var CONTRACT_FAILED_OK_EVIDENCE = `fields \u2286 required(${CONTRACT_FAILED_REQUIRED_FIELDS.length}) \u222A optional(${CONTRACT_FAILED_OPTIONAL_FIELDS.length}); ${CONTRACT_FAILED_FORBIDDEN_FIELDS.length} forbidden absent`;
3345
3350
  var VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK = {
3346
3351
  contractId: "contract-failed-payload-schema-lock",
3347
3352
  bootTest() {
@@ -3356,35 +3361,43 @@ var VERIFIER_CONTRACT_FAILED_PAYLOAD_SCHEMA_LOCK = {
3356
3361
  verification_phase: "boot"
3357
3362
  };
3358
3363
  const keys = Object.keys(syntheticPayload);
3359
- const violation = contractFailedSchemaViolation(keys);
3360
- if (violation) {
3361
- return fail("contract-failed-payload-schema-lock", violation, nowMs() - t0);
3364
+ const allowed = /* @__PURE__ */ new Set([
3365
+ ...CONTRACT_FAILED_REQUIRED_FIELDS,
3366
+ ...CONTRACT_FAILED_OPTIONAL_FIELDS
3367
+ ]);
3368
+ const forbidden = new Set(CONTRACT_FAILED_FORBIDDEN_FIELDS);
3369
+ for (const required of CONTRACT_FAILED_REQUIRED_FIELDS) {
3370
+ if (!keys.includes(required)) {
3371
+ return fail(
3372
+ "contract-failed-payload-schema-lock",
3373
+ `missing required field: ${required}`,
3374
+ nowMs() - t0
3375
+ );
3376
+ }
3377
+ }
3378
+ for (const k of keys) {
3379
+ if (forbidden.has(k)) {
3380
+ return fail(
3381
+ "contract-failed-payload-schema-lock",
3382
+ `forbidden field on wire: ${k}`,
3383
+ nowMs() - t0
3384
+ );
3385
+ }
3386
+ }
3387
+ for (const k of keys) {
3388
+ if (!allowed.has(k)) {
3389
+ return fail(
3390
+ "contract-failed-payload-schema-lock",
3391
+ `unrecognised field on wire: ${k} (not in required \u222A optional)`,
3392
+ nowMs() - t0
3393
+ );
3394
+ }
3362
3395
  }
3363
3396
  return pass(
3364
3397
  "contract-failed-payload-schema-lock",
3365
- `${keys.length} ${CONTRACT_FAILED_OK_EVIDENCE}`,
3398
+ `${keys.length} fields \u2286 required(${CONTRACT_FAILED_REQUIRED_FIELDS.length}) \u222A optional(${CONTRACT_FAILED_OPTIONAL_FIELDS.length}); ${CONTRACT_FAILED_FORBIDDEN_FIELDS.length} forbidden absent`,
3366
3399
  nowMs() - t0
3367
3400
  );
3368
- },
3369
- // Hot-path hook — fires on the REAL reportFail emit (VerifierReporter,
3370
- // inside the re-entrancy guard) against the exact payload about to go on
3371
- // the wire. This is the assertion the guard at reportFail was built in
3372
- // anticipation of: every reliability-channel write is schema-locked in the
3373
- // field, against real data, not just the synthetic boot mirror.
3374
- hooks: {
3375
- onReportContractFailure(obs) {
3376
- const t0 = nowMs();
3377
- const keys = Object.keys(obs.outgoingPayload);
3378
- const violation = contractFailedSchemaViolation(keys);
3379
- if (violation) {
3380
- return fail("contract-failed-payload-schema-lock", violation, nowMs() - t0);
3381
- }
3382
- return pass(
3383
- "contract-failed-payload-schema-lock",
3384
- `${keys.length} ${CONTRACT_FAILED_OK_EVIDENCE}`,
3385
- nowMs() - t0
3386
- );
3387
- }
3388
3401
  }
3389
3402
  };
3390
3403
  var BACKEND_WIRE_CODES = Object.freeze([
@@ -3435,39 +3448,6 @@ var VERIFIER_SDK_ERROR_CODES_CATALOGUE = {
3435
3448
  nowMs() - t0
3436
3449
  );
3437
3450
  }
3438
- },
3439
- // Hot-path hook — when the SDK parses a real wire error, assert the code
3440
- // the customer actually hit carries usable remediation in the shipped
3441
- // catalogue. Scoped to BACKEND_WIRE_CODES (the contract's set); other
3442
- // codes are out of scope and pass. This turns the catalogue from a
3443
- // boot-only completeness claim into a per-error field assertion: a backend
3444
- // code that ships without remediation is caught the first time a customer
3445
- // hits it, not only if the off-by-default boot self-test ran.
3446
- hooks: {
3447
- onErrorParse(obs) {
3448
- const t0 = nowMs();
3449
- const code = obs.errorCode;
3450
- if (!code || !BACKEND_WIRE_CODES.includes(code)) {
3451
- return pass(
3452
- "sdk-error-codes-catalogue",
3453
- `wire code "${code || "(none)"}" is out of the backend-catalogue scope`,
3454
- nowMs() - t0
3455
- );
3456
- }
3457
- const entry = getErrorCode(code);
3458
- if (!entry || !entry.description || entry.description.trim().length === 0 || !entry.resolution || entry.resolution.trim().length === 0) {
3459
- return fail(
3460
- "sdk-error-codes-catalogue",
3461
- `backend wire code "${code}" hit in the field has no description+resolution in the shipped catalogue`,
3462
- nowMs() - t0
3463
- );
3464
- }
3465
- return pass(
3466
- "sdk-error-codes-catalogue",
3467
- `backend wire code "${code}" carries description + resolution`,
3468
- nowMs() - t0
3469
- );
3470
- }
3471
3451
  }
3472
3452
  };
3473
3453
  var STATIC_VERIFIERS = Object.freeze([
@@ -3591,24 +3571,6 @@ function runOnErrorParse(verifiers, reporter, ctx, obs) {
3591
3571
  reporter.report(result, "hot_path", "errorParse");
3592
3572
  }
3593
3573
  }
3594
- function runOnReportContractFailure(verifiers, reporter, ctx, obs) {
3595
- if (ctx.disableContractAssertions) return;
3596
- for (const verifier of verifiers) {
3597
- const hook = verifier.hooks?.onReportContractFailure;
3598
- if (!hook) continue;
3599
- let result;
3600
- try {
3601
- result = hook(obs);
3602
- } catch (err) {
3603
- result = fail(
3604
- verifier.contractId,
3605
- `hook threw: ${err.message?.slice(0, 80) ?? "unknown"}`,
3606
- 0
3607
- );
3608
- }
3609
- reporter.report(result, "hot_path", "reportContractFailure");
3610
- }
3611
- }
3612
3574
  function defaultDebugModeFlag() {
3613
3575
  try {
3614
3576
  if (typeof process !== "undefined" && process.env) {
@@ -3760,18 +3722,18 @@ function isInAppFrame(filename) {
3760
3722
  if (/\/crossdeck\.umd\.min\.js$/.test(filename)) return false;
3761
3723
  return true;
3762
3724
  }
3763
- function fingerprintError(message, frames, location) {
3725
+ function fingerprintError(message, frames, location2) {
3764
3726
  const inAppFrames = frames.filter((f) => f.in_app).slice(0, 3);
3765
3727
  const parts = [
3766
3728
  (message || "").slice(0, 200),
3767
3729
  ...inAppFrames.map((f) => `${f.function}@${f.filename}:${f.lineno}`)
3768
3730
  ];
3769
- if (inAppFrames.length === 0 && location) {
3731
+ if (inAppFrames.length === 0 && location2) {
3770
3732
  const loc = [
3771
- location.errorType ?? "",
3772
- location.filename ?? "",
3773
- location.lineno ?? "",
3774
- location.colno ?? ""
3733
+ location2.errorType ?? "",
3734
+ location2.filename ?? "",
3735
+ location2.lineno ?? "",
3736
+ location2.colno ?? ""
3775
3737
  ].join(":");
3776
3738
  if (loc !== ":::") parts.push(loc);
3777
3739
  }
@@ -4441,9 +4403,6 @@ var CrossdeckClient = class {
4441
4403
  this.verifiers = null;
4442
4404
  this.verifierReporter = null;
4443
4405
  this.verifierCtx = null;
4444
- // The live configured event-flush interval, surfaced to the track-path
4445
- // verifier so flush-interval-parity validates the real cadence per event.
4446
- this.flushIntervalMs = 2e3;
4447
4406
  }
4448
4407
  /**
4449
4408
  * Boot the SDK. Idempotent — calling init twice with the same options
@@ -4620,6 +4579,7 @@ var CrossdeckClient = class {
4620
4579
  persistIdentity ? effectiveStorage : new MemoryStorage(),
4621
4580
  opts.storagePrefix
4622
4581
  );
4582
+ processInternalOptOutUrl();
4623
4583
  const consent = new ConsentManager({ respectDnt: options.respectDnt === true });
4624
4584
  if (consent.isDntDenied) {
4625
4585
  debug.emit(
@@ -4723,8 +4683,6 @@ var CrossdeckClient = class {
4723
4683
  this.verifierReporter = new VerifierReporter(this.verifierCtx);
4724
4684
  const flushVerifier = buildFlushIntervalVerifier(opts.eventFlushIntervalMs);
4725
4685
  this.verifiers = Object.freeze([...STATIC_VERIFIERS, flushVerifier]);
4726
- this.flushIntervalMs = opts.eventFlushIntervalMs;
4727
- this.verifierReporter.attachVerifiers(this.verifiers);
4728
4686
  if (localDevMode) {
4729
4687
  const verifyAtBootLocal = options.verifyContractsAtBoot ?? debugDefault;
4730
4688
  if (verifyAtBootLocal && this.verifierReporter) {
@@ -5275,8 +5233,27 @@ var CrossdeckClient = class {
5275
5233
  );
5276
5234
  }
5277
5235
  }
5236
+ const seq = s.autoTracker?.nextSeq() ?? 0;
5237
+ const occurrenceTimestamp = Date.now();
5278
5238
  s.autoTracker?.markActivity();
5279
- const enriched = { ...s.deviceInfo };
5239
+ const wireContext = {};
5240
+ const di = s.deviceInfo;
5241
+ if (di.os) wireContext.os = di.os;
5242
+ if (di.osVersion) wireContext.osVersion = di.osVersion;
5243
+ const appVer = s.options.appVersion;
5244
+ if (appVer) wireContext.appVersion = appVer;
5245
+ wireContext.sdkName = SDK_NAME;
5246
+ wireContext.sdkVersion = s.options.sdkVersion;
5247
+ if (di.locale) wireContext.locale = di.locale;
5248
+ if (di.timezone) wireContext.timezone = di.timezone;
5249
+ if (di.browser) wireContext.browser = di.browser;
5250
+ if (di.browserVersion) wireContext.browserVersion = di.browserVersion;
5251
+ const enriched = {};
5252
+ if (di.screenWidth !== void 0) enriched.screenWidth = di.screenWidth;
5253
+ if (di.screenHeight !== void 0) enriched.screenHeight = di.screenHeight;
5254
+ if (di.viewportWidth !== void 0) enriched.viewportWidth = di.viewportWidth;
5255
+ if (di.viewportHeight !== void 0) enriched.viewportHeight = di.viewportHeight;
5256
+ if (di.devicePixelRatio !== void 0) enriched.devicePixelRatio = di.devicePixelRatio;
5280
5257
  const sessionId = s.autoTracker?.currentSessionId;
5281
5258
  if (sessionId) enriched.sessionId = sessionId;
5282
5259
  const pageviewId = s.autoTracker?.currentPageviewId;
@@ -5299,17 +5276,24 @@ var CrossdeckClient = class {
5299
5276
  }
5300
5277
  }
5301
5278
  const supers = s.superProps.getSuperProperties();
5302
- Object.assign(enriched, supers);
5279
+ for (const k of Object.keys(supers)) {
5280
+ if (!(k in enriched)) enriched[k] = supers[k];
5281
+ }
5303
5282
  const groupIds = s.superProps.getGroupIds();
5304
5283
  if (Object.keys(groupIds).length > 0) {
5305
5284
  enriched.$groups = groupIds;
5306
5285
  }
5307
5286
  Object.assign(enriched, validation.properties);
5287
+ if (isInternalOptOut()) {
5288
+ enriched[INTERNAL_OPT_OUT_PROPERTY] = true;
5289
+ }
5308
5290
  const finalProperties = s.scrubPii ? scrubPiiFromProperties(enriched) : enriched;
5309
5291
  const event = {
5310
5292
  eventId: this.mintEventId(),
5311
5293
  name,
5312
- timestamp: Date.now(),
5294
+ timestamp: occurrenceTimestamp,
5295
+ seq,
5296
+ context: wireContext,
5313
5297
  properties: finalProperties
5314
5298
  };
5315
5299
  Object.assign(event, this.identityHintForEvent());
@@ -5319,8 +5303,7 @@ var CrossdeckClient = class {
5319
5303
  callerProperties: validation.properties,
5320
5304
  superProperties: supers,
5321
5305
  deviceProperties: s.deviceInfo,
5322
- mergedProperties: enriched,
5323
- flushIntervalMs: this.flushIntervalMs
5306
+ mergedProperties: enriched
5324
5307
  });
5325
5308
  }
5326
5309
  if (!isError && !isWebVital) {
@@ -5665,8 +5648,8 @@ function installUnloadFlush(onUnload) {
5665
5648
  }
5666
5649
 
5667
5650
  // src/_contracts-bundled.ts
5668
- var BUNDLED_IN = "@cross-deck/web@1.6.4";
5669
- var SDK_VERSION2 = "1.6.4";
5651
+ var BUNDLED_IN = "@cross-deck/web@1.7.0";
5652
+ var SDK_VERSION2 = "1.7.0";
5670
5653
  var BUNDLED_CONTRACTS = Object.freeze([
5671
5654
  {
5672
5655
  "id": "contract-failed-payload-schema-lock",
@@ -5788,7 +5771,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5788
5771
  "legal/security/index.html#diagnostic",
5789
5772
  "legal/sdk-data/index.html#b-diagnostic"
5790
5773
  ],
5791
- "bundledIn": "@cross-deck/web@1.6.4",
5774
+ "bundledIn": "@cross-deck/web@1.7.0",
5792
5775
  "runtimeVerified": true
5793
5776
  },
5794
5777
  {
@@ -5828,7 +5811,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5828
5811
  ],
5829
5812
  "registeredAt": "2026-05-26",
5830
5813
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 8 (codifies existing contract)",
5831
- "bundledIn": "@cross-deck/web@1.6.4",
5814
+ "bundledIn": "@cross-deck/web@1.7.0",
5832
5815
  "runtimeVerified": true
5833
5816
  },
5834
5817
  {
@@ -5874,7 +5857,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5874
5857
  ],
5875
5858
  "registeredAt": "2026-05-26",
5876
5859
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.3",
5877
- "bundledIn": "@cross-deck/web@1.6.4",
5860
+ "bundledIn": "@cross-deck/web@1.7.0",
5878
5861
  "runtimeVerified": true
5879
5862
  },
5880
5863
  {
@@ -5980,7 +5963,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
5980
5963
  ],
5981
5964
  "registeredAt": "2026-05-26",
5982
5965
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 2.2.a + 2.2.b + 2.2.c",
5983
- "bundledIn": "@cross-deck/web@1.6.4",
5966
+ "bundledIn": "@cross-deck/web@1.7.0",
5984
5967
  "runtimeVerified": true
5985
5968
  },
5986
5969
  {
@@ -6008,7 +5991,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6008
5991
  ],
6009
5992
  "registeredAt": "2026-05-26",
6010
5993
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 5.5",
6011
- "bundledIn": "@cross-deck/web@1.6.4",
5994
+ "bundledIn": "@cross-deck/web@1.7.0",
6012
5995
  "runtimeVerified": false
6013
5996
  },
6014
5997
  {
@@ -6088,7 +6071,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6088
6071
  ],
6089
6072
  "registeredAt": "2026-05-26",
6090
6073
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 1.3 (web/RN) + dogfood-gap fix (swift + android)",
6091
- "bundledIn": "@cross-deck/web@1.6.4",
6074
+ "bundledIn": "@cross-deck/web@1.7.0",
6092
6075
  "runtimeVerified": true
6093
6076
  },
6094
6077
  {
@@ -6134,7 +6117,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6134
6117
  ],
6135
6118
  "registeredAt": "2026-05-26",
6136
6119
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 6.2",
6137
- "bundledIn": "@cross-deck/web@1.6.4",
6120
+ "bundledIn": "@cross-deck/web@1.7.0",
6138
6121
  "runtimeVerified": true
6139
6122
  },
6140
6123
  {
@@ -6176,7 +6159,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6176
6159
  ],
6177
6160
  "registeredAt": "2026-05-26",
6178
6161
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.2",
6179
- "bundledIn": "@cross-deck/web@1.6.4",
6162
+ "bundledIn": "@cross-deck/web@1.7.0",
6180
6163
  "runtimeVerified": true
6181
6164
  },
6182
6165
  {
@@ -6210,7 +6193,7 @@ var BUNDLED_CONTRACTS = Object.freeze([
6210
6193
  ],
6211
6194
  "registeredAt": "2026-05-26",
6212
6195
  "firstRegisteredIn": "bank-grade reconciliation v1.4.0 \u2014 phase 3.5",
6213
- "bundledIn": "@cross-deck/web@1.6.4",
6196
+ "bundledIn": "@cross-deck/web@1.7.0",
6214
6197
  "runtimeVerified": false
6215
6198
  }
6216
6199
  ]);