@cross-deck/web 1.5.1 → 1.5.2
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/crossdeck.umd.min.js +2 -2
- package/dist/crossdeck.umd.min.js.map +1 -1
- package/dist/error-codes.json +1 -106
- package/dist/index.cjs +22 -940
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +4 -177
- package/dist/index.d.ts +4 -177
- package/dist/index.mjs +22 -939
- package/dist/index.mjs.map +1 -1
- package/dist/react.cjs +22 -404
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.mjs +22 -404
- package/dist/react.mjs.map +1 -1
- package/dist/{types-Bu3jbmdq.d.mts → types-BzoKor4z.d.mts} +0 -4
- package/dist/{types-Bu3jbmdq.d.ts → types-BzoKor4z.d.ts} +0 -4
- package/dist/vue.cjs +22 -404
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.mjs +22 -404
- package/dist/vue.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/contracts 2.json +0 -378
- package/dist/contracts.json +0 -378
- package/dist/crossdeck.umd.min 2.js +0 -3
- package/dist/crossdeck.umd.min.js 2.map +0 -1
- package/dist/error-codes 2.json +0 -196
- package/dist/index 2.cjs +0 -4778
- package/dist/index 2.mjs +0 -4742
- package/dist/index.cjs 2.map +0 -1
- package/dist/index.d 2.mts +0 -938
- package/dist/index.d 2.ts +0 -938
- package/dist/index.mjs 2.map +0 -1
- package/dist/react 2.cjs +0 -4220
- package/dist/react 2.mjs +0 -4193
- package/dist/react.cjs 2.map +0 -1
- package/dist/react.d 2.mts +0 -91
- package/dist/react.d 2.ts +0 -91
- package/dist/react.mjs 2.map +0 -1
- package/dist/types-Bu3jbmdq.d 2.mts +0 -314
- package/dist/types-Bu3jbmdq.d 2.ts +0 -314
- package/dist/vue 2.cjs +0 -4185
- package/dist/vue 2.mjs +0 -4159
- package/dist/vue.cjs 2.map +0 -1
- package/dist/vue.d 2.mts +0 -37
- package/dist/vue.d 2.ts +0 -37
- package/dist/vue.mjs 2.map +0 -1
|
@@ -42,10 +42,6 @@ interface PurchaseResult {
|
|
|
42
42
|
crossdeckCustomerId: string;
|
|
43
43
|
env: Environment;
|
|
44
44
|
entitlements: PublicEntitlement[];
|
|
45
|
-
/** True when the response came from the backend's idempotency
|
|
46
|
-
* cache instead of fresh processing. Backend also returns
|
|
47
|
-
* `Idempotent-Replayed: true` as a response header (v1.4.0). */
|
|
48
|
-
idempotent_replay?: boolean;
|
|
49
45
|
}
|
|
50
46
|
interface HeartbeatResponse {
|
|
51
47
|
object: "heartbeat";
|
|
@@ -42,10 +42,6 @@ interface PurchaseResult {
|
|
|
42
42
|
crossdeckCustomerId: string;
|
|
43
43
|
env: Environment;
|
|
44
44
|
entitlements: PublicEntitlement[];
|
|
45
|
-
/** True when the response came from the backend's idempotency
|
|
46
|
-
* cache instead of fresh processing. Backend also returns
|
|
47
|
-
* `Idempotent-Replayed: true` as a response header (v1.4.0). */
|
|
48
|
-
idempotent_replay?: boolean;
|
|
49
45
|
}
|
|
50
46
|
interface HeartbeatResponse {
|
|
51
47
|
object: "heartbeat";
|
package/dist/vue.cjs
CHANGED
|
@@ -92,7 +92,7 @@ function typeMapForStatus(status) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
// src/_version.ts
|
|
95
|
-
var SDK_VERSION = "1.
|
|
95
|
+
var SDK_VERSION = "1.3.0";
|
|
96
96
|
var SDK_NAME = "@cross-deck/web";
|
|
97
97
|
|
|
98
98
|
// src/http.ts
|
|
@@ -342,258 +342,29 @@ function randomChars(count) {
|
|
|
342
342
|
return out.join("");
|
|
343
343
|
}
|
|
344
344
|
|
|
345
|
-
// src/hash.ts
|
|
346
|
-
var K = new Uint32Array([
|
|
347
|
-
1116352408,
|
|
348
|
-
1899447441,
|
|
349
|
-
3049323471,
|
|
350
|
-
3921009573,
|
|
351
|
-
961987163,
|
|
352
|
-
1508970993,
|
|
353
|
-
2453635748,
|
|
354
|
-
2870763221,
|
|
355
|
-
3624381080,
|
|
356
|
-
310598401,
|
|
357
|
-
607225278,
|
|
358
|
-
1426881987,
|
|
359
|
-
1925078388,
|
|
360
|
-
2162078206,
|
|
361
|
-
2614888103,
|
|
362
|
-
3248222580,
|
|
363
|
-
3835390401,
|
|
364
|
-
4022224774,
|
|
365
|
-
264347078,
|
|
366
|
-
604807628,
|
|
367
|
-
770255983,
|
|
368
|
-
1249150122,
|
|
369
|
-
1555081692,
|
|
370
|
-
1996064986,
|
|
371
|
-
2554220882,
|
|
372
|
-
2821834349,
|
|
373
|
-
2952996808,
|
|
374
|
-
3210313671,
|
|
375
|
-
3336571891,
|
|
376
|
-
3584528711,
|
|
377
|
-
113926993,
|
|
378
|
-
338241895,
|
|
379
|
-
666307205,
|
|
380
|
-
773529912,
|
|
381
|
-
1294757372,
|
|
382
|
-
1396182291,
|
|
383
|
-
1695183700,
|
|
384
|
-
1986661051,
|
|
385
|
-
2177026350,
|
|
386
|
-
2456956037,
|
|
387
|
-
2730485921,
|
|
388
|
-
2820302411,
|
|
389
|
-
3259730800,
|
|
390
|
-
3345764771,
|
|
391
|
-
3516065817,
|
|
392
|
-
3600352804,
|
|
393
|
-
4094571909,
|
|
394
|
-
275423344,
|
|
395
|
-
430227734,
|
|
396
|
-
506948616,
|
|
397
|
-
659060556,
|
|
398
|
-
883997877,
|
|
399
|
-
958139571,
|
|
400
|
-
1322822218,
|
|
401
|
-
1537002063,
|
|
402
|
-
1747873779,
|
|
403
|
-
1955562222,
|
|
404
|
-
2024104815,
|
|
405
|
-
2227730452,
|
|
406
|
-
2361852424,
|
|
407
|
-
2428436474,
|
|
408
|
-
2756734187,
|
|
409
|
-
3204031479,
|
|
410
|
-
3329325298
|
|
411
|
-
]);
|
|
412
|
-
function utf8Bytes(input) {
|
|
413
|
-
if (typeof TextEncoder !== "undefined") {
|
|
414
|
-
return new TextEncoder().encode(input);
|
|
415
|
-
}
|
|
416
|
-
const out = [];
|
|
417
|
-
for (let i = 0; i < input.length; i++) {
|
|
418
|
-
let codePoint = input.charCodeAt(i);
|
|
419
|
-
if (codePoint >= 55296 && codePoint <= 56319 && i + 1 < input.length) {
|
|
420
|
-
const next = input.charCodeAt(i + 1);
|
|
421
|
-
if (next >= 56320 && next <= 57343) {
|
|
422
|
-
codePoint = 65536 + (codePoint - 55296 << 10) + (next - 56320);
|
|
423
|
-
i++;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (codePoint < 128) {
|
|
427
|
-
out.push(codePoint);
|
|
428
|
-
} else if (codePoint < 2048) {
|
|
429
|
-
out.push(192 | codePoint >> 6);
|
|
430
|
-
out.push(128 | codePoint & 63);
|
|
431
|
-
} else if (codePoint < 65536) {
|
|
432
|
-
out.push(224 | codePoint >> 12);
|
|
433
|
-
out.push(128 | codePoint >> 6 & 63);
|
|
434
|
-
out.push(128 | codePoint & 63);
|
|
435
|
-
} else {
|
|
436
|
-
out.push(240 | codePoint >> 18);
|
|
437
|
-
out.push(128 | codePoint >> 12 & 63);
|
|
438
|
-
out.push(128 | codePoint >> 6 & 63);
|
|
439
|
-
out.push(128 | codePoint & 63);
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
return new Uint8Array(out);
|
|
443
|
-
}
|
|
444
|
-
function sha256Hex(input) {
|
|
445
|
-
const bytes = utf8Bytes(input);
|
|
446
|
-
const bitLength = bytes.length * 8;
|
|
447
|
-
const blockCount = Math.floor((bytes.length + 9 + 63) / 64);
|
|
448
|
-
const padded = new Uint8Array(blockCount * 64);
|
|
449
|
-
padded.set(bytes);
|
|
450
|
-
padded[bytes.length] = 128;
|
|
451
|
-
const high = Math.floor(bitLength / 4294967296);
|
|
452
|
-
const low = bitLength >>> 0;
|
|
453
|
-
const lenOffset = padded.length - 8;
|
|
454
|
-
padded[lenOffset + 0] = high >>> 24 & 255;
|
|
455
|
-
padded[lenOffset + 1] = high >>> 16 & 255;
|
|
456
|
-
padded[lenOffset + 2] = high >>> 8 & 255;
|
|
457
|
-
padded[lenOffset + 3] = high & 255;
|
|
458
|
-
padded[lenOffset + 4] = low >>> 24 & 255;
|
|
459
|
-
padded[lenOffset + 5] = low >>> 16 & 255;
|
|
460
|
-
padded[lenOffset + 6] = low >>> 8 & 255;
|
|
461
|
-
padded[lenOffset + 7] = low & 255;
|
|
462
|
-
const H = new Uint32Array([
|
|
463
|
-
1779033703,
|
|
464
|
-
3144134277,
|
|
465
|
-
1013904242,
|
|
466
|
-
2773480762,
|
|
467
|
-
1359893119,
|
|
468
|
-
2600822924,
|
|
469
|
-
528734635,
|
|
470
|
-
1541459225
|
|
471
|
-
]);
|
|
472
|
-
const W = new Uint32Array(64);
|
|
473
|
-
for (let block = 0; block < blockCount; block++) {
|
|
474
|
-
const offset = block * 64;
|
|
475
|
-
for (let t = 0; t < 16; t++) {
|
|
476
|
-
W[t] = (padded[offset + t * 4] << 24 | padded[offset + t * 4 + 1] << 16 | padded[offset + t * 4 + 2] << 8 | padded[offset + t * 4 + 3]) >>> 0;
|
|
477
|
-
}
|
|
478
|
-
for (let t = 16; t < 64; t++) {
|
|
479
|
-
const w15 = W[t - 15];
|
|
480
|
-
const w2 = W[t - 2];
|
|
481
|
-
const s0 = (w15 >>> 7 | w15 << 25) ^ (w15 >>> 18 | w15 << 14) ^ w15 >>> 3;
|
|
482
|
-
const s1 = (w2 >>> 17 | w2 << 15) ^ (w2 >>> 19 | w2 << 13) ^ w2 >>> 10;
|
|
483
|
-
W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
|
|
484
|
-
}
|
|
485
|
-
let a = H[0], b = H[1], c = H[2], d = H[3];
|
|
486
|
-
let e = H[4], f = H[5], g = H[6], h = H[7];
|
|
487
|
-
for (let t = 0; t < 64; t++) {
|
|
488
|
-
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
489
|
-
const ch = e & f ^ ~e & g;
|
|
490
|
-
const temp1 = h + S1 + ch + K[t] + W[t] >>> 0;
|
|
491
|
-
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
492
|
-
const maj = a & b ^ a & c ^ b & c;
|
|
493
|
-
const temp2 = S0 + maj >>> 0;
|
|
494
|
-
h = g;
|
|
495
|
-
g = f;
|
|
496
|
-
f = e;
|
|
497
|
-
e = d + temp1 >>> 0;
|
|
498
|
-
d = c;
|
|
499
|
-
c = b;
|
|
500
|
-
b = a;
|
|
501
|
-
a = temp1 + temp2 >>> 0;
|
|
502
|
-
}
|
|
503
|
-
H[0] = H[0] + a >>> 0;
|
|
504
|
-
H[1] = H[1] + b >>> 0;
|
|
505
|
-
H[2] = H[2] + c >>> 0;
|
|
506
|
-
H[3] = H[3] + d >>> 0;
|
|
507
|
-
H[4] = H[4] + e >>> 0;
|
|
508
|
-
H[5] = H[5] + f >>> 0;
|
|
509
|
-
H[6] = H[6] + g >>> 0;
|
|
510
|
-
H[7] = H[7] + h >>> 0;
|
|
511
|
-
}
|
|
512
|
-
let hex = "";
|
|
513
|
-
for (let i = 0; i < 8; i++) {
|
|
514
|
-
hex += H[i].toString(16).padStart(8, "0");
|
|
515
|
-
}
|
|
516
|
-
return hex;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
345
|
// src/entitlement-cache.ts
|
|
520
346
|
var DEFAULT_STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
521
|
-
var
|
|
522
|
-
var INDEX_SUFFIX = "_index";
|
|
523
|
-
var EntitlementCache = class _EntitlementCache {
|
|
347
|
+
var EntitlementCache = class {
|
|
524
348
|
/**
|
|
525
|
-
* @param storage
|
|
526
|
-
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
529
|
-
* @param
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
* suffix is filled at identify() / reset()
|
|
533
|
-
* time — see [[setUserKey]].
|
|
534
|
-
* @param staleAfterMs Age past which last-known-good is flagged stale
|
|
535
|
-
* even without a failed refresh. Default 24h.
|
|
349
|
+
* @param storage Device storage adapter. When omitted (tests) or
|
|
350
|
+
* a MemoryStorage (strict-consent / no-persistence
|
|
351
|
+
* mode) the cache is session-only — durability is
|
|
352
|
+
* simply absent, never wrong.
|
|
353
|
+
* @param storageKey Full key the persisted blob lives under.
|
|
354
|
+
* @param staleAfterMs Age past which last-known-good is flagged stale
|
|
355
|
+
* even without a failed refresh. Default 24h.
|
|
536
356
|
*/
|
|
537
|
-
constructor(storage,
|
|
357
|
+
constructor(storage, storageKey = "crossdeck:entitlements", staleAfterMs = DEFAULT_STALE_AFTER_MS) {
|
|
538
358
|
this.all = [];
|
|
539
359
|
this.lastUpdated = 0;
|
|
540
360
|
this.lastRefreshFailedAt = 0;
|
|
541
361
|
this.listeners = /* @__PURE__ */ new Set();
|
|
542
362
|
this.listenerErrorCount = 0;
|
|
543
|
-
this.currentSuffix = ANON_SUFFIX;
|
|
544
363
|
this.storage = storage;
|
|
545
|
-
this.
|
|
364
|
+
this.storageKey = storageKey;
|
|
546
365
|
this.staleAfterMs = staleAfterMs;
|
|
547
366
|
this.hydrate();
|
|
548
367
|
}
|
|
549
|
-
/** The full storage key the current-user blob is persisted under. */
|
|
550
|
-
get storageKey() {
|
|
551
|
-
return `${this.storageKeyPrefix}:${this.currentSuffix}`;
|
|
552
|
-
}
|
|
553
|
-
/** Key of the index blob — a JSON array of every suffix we've
|
|
554
|
-
* written. Used by clearAll() to scope a logout-wipe. */
|
|
555
|
-
get indexKey() {
|
|
556
|
-
return `${this.storageKeyPrefix}:${INDEX_SUFFIX}`;
|
|
557
|
-
}
|
|
558
|
-
/** Derive a stable suffix for a developerUserId via SHA-256. The
|
|
559
|
-
* raw userId never lands in the storage key — protects against
|
|
560
|
-
* accidentally leaking email-style identifiers through DevTools
|
|
561
|
-
* inspection. Pass `null` to switch back to the anonymous slot. */
|
|
562
|
-
static suffixForUserId(userId) {
|
|
563
|
-
if (userId == null || userId === "") return ANON_SUFFIX;
|
|
564
|
-
return sha256Hex(userId);
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Switch the cache to a different user's storage slot. Bank-grade
|
|
568
|
-
* three-layer isolation:
|
|
569
|
-
* (a) Physical key separation — `<prefix>:<sha256(userId)>` so
|
|
570
|
-
* a user-switch can't physically read prior user's data
|
|
571
|
-
* even if the in-memory clear was skipped.
|
|
572
|
-
* (b) Unconditional in-memory clear — invoked whenever the
|
|
573
|
-
* active suffix changes, even on same-id re-identify.
|
|
574
|
-
* (c) Re-hydrate from the new slot — a returning user observes
|
|
575
|
-
* their last-known-good cache from storage immediately.
|
|
576
|
-
*
|
|
577
|
-
* Caller (identify() / reset()) MUST invoke this BEFORE the next
|
|
578
|
-
* setFromList() so the write lands under the right key.
|
|
579
|
-
*/
|
|
580
|
-
setUserKey(userId) {
|
|
581
|
-
const nextSuffix = _EntitlementCache.suffixForUserId(userId);
|
|
582
|
-
if (nextSuffix === this.currentSuffix) {
|
|
583
|
-
this.all = [];
|
|
584
|
-
this.lastUpdated = 0;
|
|
585
|
-
this.lastRefreshFailedAt = 0;
|
|
586
|
-
this.notify();
|
|
587
|
-
this.hydrate();
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
this.currentSuffix = nextSuffix;
|
|
591
|
-
this.all = [];
|
|
592
|
-
this.lastUpdated = 0;
|
|
593
|
-
this.lastRefreshFailedAt = 0;
|
|
594
|
-
this.hydrate();
|
|
595
|
-
this.notify();
|
|
596
|
-
}
|
|
597
368
|
/**
|
|
598
369
|
* Sync read — true iff the entitlement is currently granting access.
|
|
599
370
|
*
|
|
@@ -663,13 +434,12 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
663
434
|
this.lastUpdated = Date.now();
|
|
664
435
|
this.lastRefreshFailedAt = 0;
|
|
665
436
|
this.persist();
|
|
666
|
-
this.recordSuffixInIndex(this.currentSuffix);
|
|
667
437
|
this.notify();
|
|
668
438
|
}
|
|
669
439
|
/**
|
|
670
|
-
* Wipe
|
|
671
|
-
*
|
|
672
|
-
*
|
|
440
|
+
* Wipe — used on reset() (logout) and on an identity switch. Clears
|
|
441
|
+
* BOTH memory and durable storage so a prior user's entitlements can
|
|
442
|
+
* never leak to the next person on this device.
|
|
673
443
|
*/
|
|
674
444
|
clear() {
|
|
675
445
|
this.all = [];
|
|
@@ -681,40 +451,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
681
451
|
} catch {
|
|
682
452
|
}
|
|
683
453
|
}
|
|
684
|
-
this.removeSuffixFromIndex(this.currentSuffix);
|
|
685
|
-
this.notify();
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Logout-grade wipe — bank-grade contract: removes EVERY per-user
|
|
689
|
-
* entitlement slot the SDK has ever written on this device, then
|
|
690
|
-
* clears the index. Used by `Crossdeck.reset()` so a logout on a
|
|
691
|
-
* shared device can never leave another user's entitlements
|
|
692
|
-
* readable (layer (c) of the v1.4.0 isolation fix).
|
|
693
|
-
*
|
|
694
|
-
* After clearAll(), the cache is back to anonymous + empty.
|
|
695
|
-
*/
|
|
696
|
-
clearAll() {
|
|
697
|
-
this.all = [];
|
|
698
|
-
this.lastUpdated = 0;
|
|
699
|
-
this.lastRefreshFailedAt = 0;
|
|
700
|
-
this.currentSuffix = ANON_SUFFIX;
|
|
701
|
-
if (this.storage) {
|
|
702
|
-
const suffixes = this.readIndex();
|
|
703
|
-
for (const suffix of suffixes) {
|
|
704
|
-
try {
|
|
705
|
-
this.storage.removeItem(`${this.storageKeyPrefix}:${suffix}`);
|
|
706
|
-
} catch {
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
try {
|
|
710
|
-
this.storage.removeItem(`${this.storageKeyPrefix}:${ANON_SUFFIX}`);
|
|
711
|
-
} catch {
|
|
712
|
-
}
|
|
713
|
-
try {
|
|
714
|
-
this.storage.removeItem(this.indexKey);
|
|
715
|
-
} catch {
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
454
|
this.notify();
|
|
719
455
|
}
|
|
720
456
|
/**
|
|
@@ -764,47 +500,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
764
500
|
} catch {
|
|
765
501
|
}
|
|
766
502
|
}
|
|
767
|
-
/** Read the index of all per-user suffixes the SDK has written. */
|
|
768
|
-
readIndex() {
|
|
769
|
-
if (!this.storage) return [];
|
|
770
|
-
try {
|
|
771
|
-
const raw = this.storage.getItem(this.indexKey);
|
|
772
|
-
if (!raw) return [];
|
|
773
|
-
const parsed = JSON.parse(raw);
|
|
774
|
-
if (Array.isArray(parsed)) {
|
|
775
|
-
return parsed.filter((x) => typeof x === "string");
|
|
776
|
-
}
|
|
777
|
-
return [];
|
|
778
|
-
} catch {
|
|
779
|
-
return [];
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
/** Add a suffix to the persisted index. Idempotent. */
|
|
783
|
-
recordSuffixInIndex(suffix) {
|
|
784
|
-
if (!this.storage) return;
|
|
785
|
-
const existing = this.readIndex();
|
|
786
|
-
if (existing.includes(suffix)) return;
|
|
787
|
-
existing.push(suffix);
|
|
788
|
-
try {
|
|
789
|
-
this.storage.setItem(this.indexKey, JSON.stringify(existing));
|
|
790
|
-
} catch {
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
/** Remove a suffix from the persisted index. No-op if absent. */
|
|
794
|
-
removeSuffixFromIndex(suffix) {
|
|
795
|
-
if (!this.storage) return;
|
|
796
|
-
const existing = this.readIndex();
|
|
797
|
-
const next = existing.filter((s) => s !== suffix);
|
|
798
|
-
if (next.length === existing.length) return;
|
|
799
|
-
try {
|
|
800
|
-
if (next.length === 0) {
|
|
801
|
-
this.storage.removeItem(this.indexKey);
|
|
802
|
-
} else {
|
|
803
|
-
this.storage.setItem(this.indexKey, JSON.stringify(next));
|
|
804
|
-
}
|
|
805
|
-
} catch {
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
503
|
notify() {
|
|
809
504
|
if (this.listeners.size === 0) return;
|
|
810
505
|
const snapshot = this.all.slice();
|
|
@@ -819,34 +514,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
819
514
|
}
|
|
820
515
|
};
|
|
821
516
|
|
|
822
|
-
// src/idempotency-key.ts
|
|
823
|
-
function formatAsUuid(hex) {
|
|
824
|
-
return [
|
|
825
|
-
hex.slice(0, 8),
|
|
826
|
-
hex.slice(8, 12),
|
|
827
|
-
hex.slice(12, 16),
|
|
828
|
-
hex.slice(16, 20),
|
|
829
|
-
hex.slice(20, 32)
|
|
830
|
-
].join("-");
|
|
831
|
-
}
|
|
832
|
-
function deriveIdempotencyKeyForPurchase(body) {
|
|
833
|
-
let identifier;
|
|
834
|
-
if (body.rail === "apple") {
|
|
835
|
-
identifier = body.signedTransactionInfo ?? "";
|
|
836
|
-
} else if (body.rail === "google") {
|
|
837
|
-
identifier = body.purchaseToken ?? "";
|
|
838
|
-
} else {
|
|
839
|
-
identifier = "";
|
|
840
|
-
}
|
|
841
|
-
if (!identifier) {
|
|
842
|
-
throw new Error(
|
|
843
|
-
`deriveIdempotencyKeyForPurchase: no stable identifier in body (rail=${body.rail}). Apple needs signedTransactionInfo; Google needs purchaseToken.`
|
|
844
|
-
);
|
|
845
|
-
}
|
|
846
|
-
const namespaced = `crossdeck:purchases/sync:${body.rail}:${identifier}`;
|
|
847
|
-
return formatAsUuid(sha256Hex(namespaced));
|
|
848
|
-
}
|
|
849
|
-
|
|
850
517
|
// src/retry-policy.ts
|
|
851
518
|
var DEFAULT_BASE = 1e3;
|
|
852
519
|
var DEFAULT_MAX = 6e4;
|
|
@@ -3109,10 +2776,6 @@ var CrossdeckClient = class {
|
|
|
3109
2776
|
this.state.errors?.uninstall();
|
|
3110
2777
|
} catch {
|
|
3111
2778
|
}
|
|
3112
|
-
try {
|
|
3113
|
-
void this.state.events.flush({ keepalive: true });
|
|
3114
|
-
} catch {
|
|
3115
|
-
}
|
|
3116
2779
|
}
|
|
3117
2780
|
if (!options.publicKey || !options.publicKey.startsWith("cd_pub_")) {
|
|
3118
2781
|
throw new CrossdeckError({
|
|
@@ -3160,12 +2823,7 @@ var CrossdeckClient = class {
|
|
|
3160
2823
|
// load still flushes if the user leaves quickly (the keepalive
|
|
3161
2824
|
// pagehide handler picks up anything that doesn't); long enough
|
|
3162
2825
|
// that bursts of clicks coalesce into one network round-trip.
|
|
3163
|
-
|
|
3164
|
-
// v1.4.0: Web/Node 1500ms, RN/Swift/Android 5000ms. All
|
|
3165
|
-
// converged on 2000ms (the Stripe-adjacent industry norm)
|
|
3166
|
-
// so cross-platform funnels show events landing at the
|
|
3167
|
-
// same cadence on every SDK. Per-instance override stays.
|
|
3168
|
-
eventFlushIntervalMs: options.eventFlushIntervalMs ?? 2e3,
|
|
2826
|
+
eventFlushIntervalMs: options.eventFlushIntervalMs ?? 1500,
|
|
3169
2827
|
sdkVersion: options.sdkVersion ?? SDK_VERSION,
|
|
3170
2828
|
autoTrack,
|
|
3171
2829
|
appVersion: options.appVersion ?? null
|
|
@@ -3393,10 +3051,14 @@ var CrossdeckClient = class {
|
|
|
3393
3051
|
};
|
|
3394
3052
|
if (options?.email) body.email = options.email;
|
|
3395
3053
|
if (traits) body.traits = traits;
|
|
3396
|
-
s.entitlements.setUserKey(userId);
|
|
3397
3054
|
const result = await s.http.request("POST", "/identity/alias", {
|
|
3398
3055
|
body
|
|
3399
3056
|
});
|
|
3057
|
+
const priorCdcust = s.identity.crossdeckCustomerId;
|
|
3058
|
+
const cacheHasEntries = s.entitlements.list().length > 0;
|
|
3059
|
+
if (priorCdcust && result.crossdeckCustomerId && priorCdcust !== result.crossdeckCustomerId || !priorCdcust && cacheHasEntries) {
|
|
3060
|
+
s.entitlements.clear();
|
|
3061
|
+
}
|
|
3400
3062
|
s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);
|
|
3401
3063
|
s.developerUserId = userId;
|
|
3402
3064
|
return result;
|
|
@@ -3701,37 +3363,6 @@ var CrossdeckClient = class {
|
|
|
3701
3363
|
* trip happens in the background. To flush before the page unloads,
|
|
3702
3364
|
* call flush().
|
|
3703
3365
|
*/
|
|
3704
|
-
/**
|
|
3705
|
-
* Emit `crossdeck.contract_failed` with the canonical property
|
|
3706
|
-
* shape (`contract_id`, `sdk_version`, `sdk_platform`,
|
|
3707
|
-
* `failure_reason`, `run_context`, `run_id`). Goes through the
|
|
3708
|
-
* standard track() pipeline — same consent gate, same queue,
|
|
3709
|
-
* same ingest, no new endpoint.
|
|
3710
|
-
*
|
|
3711
|
-
* Wire the call from a test hook, dogfood failure path, or
|
|
3712
|
-
* customer contract-verification harness; see
|
|
3713
|
-
* `contracts/README.md` for the per-test-framework hook recipes.
|
|
3714
|
-
*/
|
|
3715
|
-
reportContractFailure(input) {
|
|
3716
|
-
const props = {
|
|
3717
|
-
contract_id: input.contractId,
|
|
3718
|
-
sdk_version: SDK_VERSION,
|
|
3719
|
-
sdk_platform: "web",
|
|
3720
|
-
failure_reason: input.failureReason,
|
|
3721
|
-
run_context: input.runContext,
|
|
3722
|
-
run_id: input.runId
|
|
3723
|
-
};
|
|
3724
|
-
if (input.testRef) {
|
|
3725
|
-
props.test_file = input.testRef.file;
|
|
3726
|
-
props.test_name = input.testRef.name;
|
|
3727
|
-
}
|
|
3728
|
-
if (input.extra) {
|
|
3729
|
-
for (const [k, v] of Object.entries(input.extra)) {
|
|
3730
|
-
if (props[k] === void 0) props[k] = v;
|
|
3731
|
-
}
|
|
3732
|
-
}
|
|
3733
|
-
this.track("crossdeck.contract_failed", props);
|
|
3734
|
-
}
|
|
3735
3366
|
track(name, properties) {
|
|
3736
3367
|
const s = this.requireStarted();
|
|
3737
3368
|
if (!name) {
|
|
@@ -3868,24 +3499,11 @@ var CrossdeckClient = class {
|
|
|
3868
3499
|
});
|
|
3869
3500
|
}
|
|
3870
3501
|
const rail = input.rail ?? "apple";
|
|
3871
|
-
const body = { ...input, rail };
|
|
3872
|
-
const idempotencyKey = deriveIdempotencyKeyForPurchase(body);
|
|
3873
3502
|
const result = await s.http.request("POST", "/purchases/sync", {
|
|
3874
|
-
body,
|
|
3875
|
-
idempotencyKey
|
|
3503
|
+
body: { ...input, rail }
|
|
3876
3504
|
});
|
|
3877
3505
|
s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);
|
|
3878
3506
|
s.entitlements.setFromList(result.entitlements);
|
|
3879
|
-
try {
|
|
3880
|
-
const sourceProductId = result.entitlements[0]?.source.productId;
|
|
3881
|
-
const sourceSubscriptionId = result.entitlements[0]?.source.subscriptionId;
|
|
3882
|
-
const props = { rail };
|
|
3883
|
-
if (sourceProductId) props.productId = sourceProductId;
|
|
3884
|
-
if (sourceSubscriptionId) props.subscriptionId = sourceSubscriptionId;
|
|
3885
|
-
if (result.idempotent_replay) props.idempotent_replay = true;
|
|
3886
|
-
this.track("purchase.completed", props);
|
|
3887
|
-
} catch {
|
|
3888
|
-
}
|
|
3889
3507
|
s.debug.emit(
|
|
3890
3508
|
"sdk.purchase_evidence_sent",
|
|
3891
3509
|
"StoreKit transaction forwarded. Waiting for backend verification.",
|
|
@@ -3941,7 +3559,7 @@ var CrossdeckClient = class {
|
|
|
3941
3559
|
}
|
|
3942
3560
|
this.state.autoTracker?.uninstall();
|
|
3943
3561
|
this.state.identity.reset();
|
|
3944
|
-
this.state.entitlements.
|
|
3562
|
+
this.state.entitlements.clear();
|
|
3945
3563
|
this.state.events.reset();
|
|
3946
3564
|
this.state.superProps.clear();
|
|
3947
3565
|
this.state.breadcrumbs.clear();
|