@cross-deck/web 1.6.3 → 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/react.cjs CHANGED
@@ -93,7 +93,7 @@ function typeMapForStatus(status) {
93
93
  }
94
94
 
95
95
  // src/_version.ts
96
- var SDK_VERSION = "1.6.3";
96
+ var SDK_VERSION = "1.7.0";
97
97
  var SDK_NAME = "@cross-deck/web";
98
98
 
99
99
  // src/http.ts
@@ -1009,9 +1009,13 @@ var EventQueue = class {
1009
1009
  const env = this.cfg.envelope();
1010
1010
  const result = await this.cfg.http.request("POST", "/events", {
1011
1011
  body: {
1012
- // NorthStar §13.1 batch envelope. The backend validates these
1013
- // against the API-key-resolved app and rejects mismatches
1014
- // loudly (env_mismatch).
1012
+ // Event Envelope v1 batch envelope (backend/docs/
1013
+ // event-envelope-spec-v1.md §1). `envelopeVersion` is the
1014
+ // schema/wire version the server parses against ("can I parse
1015
+ // this?") and is DISTINCT from `sdk.version` ("which build is in
1016
+ // the wild?") — two questions, two fields, never conflated. The
1017
+ // backend refuses payloads with a missing/unknown envelopeVersion.
1018
+ envelopeVersion: 1,
1015
1019
  appId: env.appId,
1016
1020
  environment: env.environment,
1017
1021
  sdk: env.sdk,
@@ -1466,6 +1470,15 @@ var AutoTracker = class {
1466
1470
  this.cleanups = [];
1467
1471
  /** Last time we flushed lastActivityAt to storage (throttle gate). */
1468
1472
  this.lastPersistAt = 0;
1473
+ /**
1474
+ * Event Envelope v1 §3 — per-session monotonic sequence counter.
1475
+ * The single owner of the seq counter lives here, alongside all other
1476
+ * session state. Incremented atomically at track() time (via nextSeq()),
1477
+ * reset to 0 whenever a new session boundary is crossed (startNewSession
1478
+ * path + resetSession). Persists across background/foreground within a
1479
+ * session — only a real session boundary (not a tab hide/show) resets it.
1480
+ */
1481
+ this._sessionSeq = 0;
1469
1482
  /**
1470
1483
  * Stable per-page-view identifier. Minted at every `page.viewed`
1471
1484
  * emission and attached to every subsequent event until the next
@@ -1550,6 +1563,20 @@ var AutoTracker = class {
1550
1563
  get currentAcquisition() {
1551
1564
  return this.session?.acquisition ?? EMPTY_ACQUISITION;
1552
1565
  }
1566
+ /**
1567
+ * Event Envelope v1 §3 — return the next seq value for the current session
1568
+ * and advance the counter. Called SYNCHRONOUSLY at track() time, before any
1569
+ * async dispatch, so the seq assignment order is deterministic (call order,
1570
+ * not scheduler luck). The counter is reset to 0 at every session boundary;
1571
+ * it is NOT reset by tab hide/show (spec §3 clause 1: backgrounding does not
1572
+ * reset seq). When no session is active (Node, before init, after uninstall),
1573
+ * returns 0 and leaves the counter unchanged — fallback, not an error.
1574
+ */
1575
+ nextSeq() {
1576
+ const seq = this._sessionSeq;
1577
+ this._sessionSeq += 1;
1578
+ return seq;
1579
+ }
1553
1580
  // ---------- sessions ----------
1554
1581
  installSessionTracking() {
1555
1582
  const now = Date.now();
@@ -1601,6 +1628,7 @@ var AutoTracker = class {
1601
1628
  }
1602
1629
  startNewSession() {
1603
1630
  const now = Date.now();
1631
+ this._sessionSeq = 0;
1604
1632
  return {
1605
1633
  sessionId: mintSessionId(),
1606
1634
  startedAt: now,
@@ -2329,6 +2357,40 @@ function writeJson(storage, key, value) {
2329
2357
  }
2330
2358
  }
2331
2359
 
2360
+ // src/internal-opt-out.ts
2361
+ var INTERNAL_OPT_OUT_PROPERTY = "$crossdeck_internal";
2362
+ var STORAGE_KEY = "crossdeck.internalOptOut";
2363
+ function localStore() {
2364
+ try {
2365
+ return typeof localStorage !== "undefined" ? localStorage : null;
2366
+ } catch {
2367
+ return null;
2368
+ }
2369
+ }
2370
+ function processInternalOptOutUrl() {
2371
+ try {
2372
+ const search = typeof location !== "undefined" ? location.search : "";
2373
+ const params = new URLSearchParams(search || "");
2374
+ if (!params.has("crossdeck_internal")) return;
2375
+ const store = localStore();
2376
+ if (!store) return;
2377
+ const v = params.get("crossdeck_internal");
2378
+ if (v === "1" || v === "true") {
2379
+ store.setItem(STORAGE_KEY, "1");
2380
+ } else if (v === "0" || v === "false") {
2381
+ store.removeItem(STORAGE_KEY);
2382
+ }
2383
+ } catch {
2384
+ }
2385
+ }
2386
+ function isInternalOptOut() {
2387
+ try {
2388
+ return localStore()?.getItem(STORAGE_KEY) === "1";
2389
+ } catch {
2390
+ return false;
2391
+ }
2392
+ }
2393
+
2332
2394
  // src/web-vitals.ts
2333
2395
  var WebVitalsTracker = class {
2334
2396
  constructor(cfg, report) {
@@ -3689,18 +3751,18 @@ function isInAppFrame(filename) {
3689
3751
  if (/\/crossdeck\.umd\.min\.js$/.test(filename)) return false;
3690
3752
  return true;
3691
3753
  }
3692
- function fingerprintError(message, frames, location) {
3754
+ function fingerprintError(message, frames, location2) {
3693
3755
  const inAppFrames = frames.filter((f) => f.in_app).slice(0, 3);
3694
3756
  const parts = [
3695
3757
  (message || "").slice(0, 200),
3696
3758
  ...inAppFrames.map((f) => `${f.function}@${f.filename}:${f.lineno}`)
3697
3759
  ];
3698
- if (inAppFrames.length === 0 && location) {
3760
+ if (inAppFrames.length === 0 && location2) {
3699
3761
  const loc = [
3700
- location.errorType ?? "",
3701
- location.filename ?? "",
3702
- location.lineno ?? "",
3703
- location.colno ?? ""
3762
+ location2.errorType ?? "",
3763
+ location2.filename ?? "",
3764
+ location2.lineno ?? "",
3765
+ location2.colno ?? ""
3704
3766
  ].join(":");
3705
3767
  if (loc !== ":::") parts.push(loc);
3706
3768
  }
@@ -4546,6 +4608,7 @@ var CrossdeckClient = class {
4546
4608
  persistIdentity ? effectiveStorage : new MemoryStorage(),
4547
4609
  opts.storagePrefix
4548
4610
  );
4611
+ processInternalOptOutUrl();
4549
4612
  const consent = new ConsentManager({ respectDnt: options.respectDnt === true });
4550
4613
  if (consent.isDntDenied) {
4551
4614
  debug.emit(
@@ -5199,8 +5262,27 @@ var CrossdeckClient = class {
5199
5262
  );
5200
5263
  }
5201
5264
  }
5265
+ const seq = s.autoTracker?.nextSeq() ?? 0;
5266
+ const occurrenceTimestamp = Date.now();
5202
5267
  s.autoTracker?.markActivity();
5203
- const enriched = { ...s.deviceInfo };
5268
+ const wireContext = {};
5269
+ const di = s.deviceInfo;
5270
+ if (di.os) wireContext.os = di.os;
5271
+ if (di.osVersion) wireContext.osVersion = di.osVersion;
5272
+ const appVer = s.options.appVersion;
5273
+ if (appVer) wireContext.appVersion = appVer;
5274
+ wireContext.sdkName = SDK_NAME;
5275
+ wireContext.sdkVersion = s.options.sdkVersion;
5276
+ if (di.locale) wireContext.locale = di.locale;
5277
+ if (di.timezone) wireContext.timezone = di.timezone;
5278
+ if (di.browser) wireContext.browser = di.browser;
5279
+ if (di.browserVersion) wireContext.browserVersion = di.browserVersion;
5280
+ const enriched = {};
5281
+ if (di.screenWidth !== void 0) enriched.screenWidth = di.screenWidth;
5282
+ if (di.screenHeight !== void 0) enriched.screenHeight = di.screenHeight;
5283
+ if (di.viewportWidth !== void 0) enriched.viewportWidth = di.viewportWidth;
5284
+ if (di.viewportHeight !== void 0) enriched.viewportHeight = di.viewportHeight;
5285
+ if (di.devicePixelRatio !== void 0) enriched.devicePixelRatio = di.devicePixelRatio;
5204
5286
  const sessionId = s.autoTracker?.currentSessionId;
5205
5287
  if (sessionId) enriched.sessionId = sessionId;
5206
5288
  const pageviewId = s.autoTracker?.currentPageviewId;
@@ -5231,11 +5313,16 @@ var CrossdeckClient = class {
5231
5313
  enriched.$groups = groupIds;
5232
5314
  }
5233
5315
  Object.assign(enriched, validation.properties);
5316
+ if (isInternalOptOut()) {
5317
+ enriched[INTERNAL_OPT_OUT_PROPERTY] = true;
5318
+ }
5234
5319
  const finalProperties = s.scrubPii ? scrubPiiFromProperties(enriched) : enriched;
5235
5320
  const event = {
5236
5321
  eventId: this.mintEventId(),
5237
5322
  name,
5238
- timestamp: Date.now(),
5323
+ timestamp: occurrenceTimestamp,
5324
+ seq,
5325
+ context: wireContext,
5239
5326
  properties: finalProperties
5240
5327
  };
5241
5328
  Object.assign(event, this.identityHintForEvent());