@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
package/dist/react.d.mts
CHANGED
package/dist/react.d.ts
CHANGED
package/dist/react.mjs
CHANGED
|
@@ -67,7 +67,7 @@ function typeMapForStatus(status) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// src/_version.ts
|
|
70
|
-
var SDK_VERSION = "1.
|
|
70
|
+
var SDK_VERSION = "1.3.0";
|
|
71
71
|
var SDK_NAME = "@cross-deck/web";
|
|
72
72
|
|
|
73
73
|
// src/http.ts
|
|
@@ -317,258 +317,29 @@ function randomChars(count) {
|
|
|
317
317
|
return out.join("");
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
// src/hash.ts
|
|
321
|
-
var K = new Uint32Array([
|
|
322
|
-
1116352408,
|
|
323
|
-
1899447441,
|
|
324
|
-
3049323471,
|
|
325
|
-
3921009573,
|
|
326
|
-
961987163,
|
|
327
|
-
1508970993,
|
|
328
|
-
2453635748,
|
|
329
|
-
2870763221,
|
|
330
|
-
3624381080,
|
|
331
|
-
310598401,
|
|
332
|
-
607225278,
|
|
333
|
-
1426881987,
|
|
334
|
-
1925078388,
|
|
335
|
-
2162078206,
|
|
336
|
-
2614888103,
|
|
337
|
-
3248222580,
|
|
338
|
-
3835390401,
|
|
339
|
-
4022224774,
|
|
340
|
-
264347078,
|
|
341
|
-
604807628,
|
|
342
|
-
770255983,
|
|
343
|
-
1249150122,
|
|
344
|
-
1555081692,
|
|
345
|
-
1996064986,
|
|
346
|
-
2554220882,
|
|
347
|
-
2821834349,
|
|
348
|
-
2952996808,
|
|
349
|
-
3210313671,
|
|
350
|
-
3336571891,
|
|
351
|
-
3584528711,
|
|
352
|
-
113926993,
|
|
353
|
-
338241895,
|
|
354
|
-
666307205,
|
|
355
|
-
773529912,
|
|
356
|
-
1294757372,
|
|
357
|
-
1396182291,
|
|
358
|
-
1695183700,
|
|
359
|
-
1986661051,
|
|
360
|
-
2177026350,
|
|
361
|
-
2456956037,
|
|
362
|
-
2730485921,
|
|
363
|
-
2820302411,
|
|
364
|
-
3259730800,
|
|
365
|
-
3345764771,
|
|
366
|
-
3516065817,
|
|
367
|
-
3600352804,
|
|
368
|
-
4094571909,
|
|
369
|
-
275423344,
|
|
370
|
-
430227734,
|
|
371
|
-
506948616,
|
|
372
|
-
659060556,
|
|
373
|
-
883997877,
|
|
374
|
-
958139571,
|
|
375
|
-
1322822218,
|
|
376
|
-
1537002063,
|
|
377
|
-
1747873779,
|
|
378
|
-
1955562222,
|
|
379
|
-
2024104815,
|
|
380
|
-
2227730452,
|
|
381
|
-
2361852424,
|
|
382
|
-
2428436474,
|
|
383
|
-
2756734187,
|
|
384
|
-
3204031479,
|
|
385
|
-
3329325298
|
|
386
|
-
]);
|
|
387
|
-
function utf8Bytes(input) {
|
|
388
|
-
if (typeof TextEncoder !== "undefined") {
|
|
389
|
-
return new TextEncoder().encode(input);
|
|
390
|
-
}
|
|
391
|
-
const out = [];
|
|
392
|
-
for (let i = 0; i < input.length; i++) {
|
|
393
|
-
let codePoint = input.charCodeAt(i);
|
|
394
|
-
if (codePoint >= 55296 && codePoint <= 56319 && i + 1 < input.length) {
|
|
395
|
-
const next = input.charCodeAt(i + 1);
|
|
396
|
-
if (next >= 56320 && next <= 57343) {
|
|
397
|
-
codePoint = 65536 + (codePoint - 55296 << 10) + (next - 56320);
|
|
398
|
-
i++;
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
if (codePoint < 128) {
|
|
402
|
-
out.push(codePoint);
|
|
403
|
-
} else if (codePoint < 2048) {
|
|
404
|
-
out.push(192 | codePoint >> 6);
|
|
405
|
-
out.push(128 | codePoint & 63);
|
|
406
|
-
} else if (codePoint < 65536) {
|
|
407
|
-
out.push(224 | codePoint >> 12);
|
|
408
|
-
out.push(128 | codePoint >> 6 & 63);
|
|
409
|
-
out.push(128 | codePoint & 63);
|
|
410
|
-
} else {
|
|
411
|
-
out.push(240 | codePoint >> 18);
|
|
412
|
-
out.push(128 | codePoint >> 12 & 63);
|
|
413
|
-
out.push(128 | codePoint >> 6 & 63);
|
|
414
|
-
out.push(128 | codePoint & 63);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
return new Uint8Array(out);
|
|
418
|
-
}
|
|
419
|
-
function sha256Hex(input) {
|
|
420
|
-
const bytes = utf8Bytes(input);
|
|
421
|
-
const bitLength = bytes.length * 8;
|
|
422
|
-
const blockCount = Math.floor((bytes.length + 9 + 63) / 64);
|
|
423
|
-
const padded = new Uint8Array(blockCount * 64);
|
|
424
|
-
padded.set(bytes);
|
|
425
|
-
padded[bytes.length] = 128;
|
|
426
|
-
const high = Math.floor(bitLength / 4294967296);
|
|
427
|
-
const low = bitLength >>> 0;
|
|
428
|
-
const lenOffset = padded.length - 8;
|
|
429
|
-
padded[lenOffset + 0] = high >>> 24 & 255;
|
|
430
|
-
padded[lenOffset + 1] = high >>> 16 & 255;
|
|
431
|
-
padded[lenOffset + 2] = high >>> 8 & 255;
|
|
432
|
-
padded[lenOffset + 3] = high & 255;
|
|
433
|
-
padded[lenOffset + 4] = low >>> 24 & 255;
|
|
434
|
-
padded[lenOffset + 5] = low >>> 16 & 255;
|
|
435
|
-
padded[lenOffset + 6] = low >>> 8 & 255;
|
|
436
|
-
padded[lenOffset + 7] = low & 255;
|
|
437
|
-
const H = new Uint32Array([
|
|
438
|
-
1779033703,
|
|
439
|
-
3144134277,
|
|
440
|
-
1013904242,
|
|
441
|
-
2773480762,
|
|
442
|
-
1359893119,
|
|
443
|
-
2600822924,
|
|
444
|
-
528734635,
|
|
445
|
-
1541459225
|
|
446
|
-
]);
|
|
447
|
-
const W = new Uint32Array(64);
|
|
448
|
-
for (let block = 0; block < blockCount; block++) {
|
|
449
|
-
const offset = block * 64;
|
|
450
|
-
for (let t = 0; t < 16; t++) {
|
|
451
|
-
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;
|
|
452
|
-
}
|
|
453
|
-
for (let t = 16; t < 64; t++) {
|
|
454
|
-
const w15 = W[t - 15];
|
|
455
|
-
const w2 = W[t - 2];
|
|
456
|
-
const s0 = (w15 >>> 7 | w15 << 25) ^ (w15 >>> 18 | w15 << 14) ^ w15 >>> 3;
|
|
457
|
-
const s1 = (w2 >>> 17 | w2 << 15) ^ (w2 >>> 19 | w2 << 13) ^ w2 >>> 10;
|
|
458
|
-
W[t] = W[t - 16] + s0 + W[t - 7] + s1 >>> 0;
|
|
459
|
-
}
|
|
460
|
-
let a = H[0], b = H[1], c = H[2], d = H[3];
|
|
461
|
-
let e = H[4], f = H[5], g = H[6], h = H[7];
|
|
462
|
-
for (let t = 0; t < 64; t++) {
|
|
463
|
-
const S1 = (e >>> 6 | e << 26) ^ (e >>> 11 | e << 21) ^ (e >>> 25 | e << 7);
|
|
464
|
-
const ch = e & f ^ ~e & g;
|
|
465
|
-
const temp1 = h + S1 + ch + K[t] + W[t] >>> 0;
|
|
466
|
-
const S0 = (a >>> 2 | a << 30) ^ (a >>> 13 | a << 19) ^ (a >>> 22 | a << 10);
|
|
467
|
-
const maj = a & b ^ a & c ^ b & c;
|
|
468
|
-
const temp2 = S0 + maj >>> 0;
|
|
469
|
-
h = g;
|
|
470
|
-
g = f;
|
|
471
|
-
f = e;
|
|
472
|
-
e = d + temp1 >>> 0;
|
|
473
|
-
d = c;
|
|
474
|
-
c = b;
|
|
475
|
-
b = a;
|
|
476
|
-
a = temp1 + temp2 >>> 0;
|
|
477
|
-
}
|
|
478
|
-
H[0] = H[0] + a >>> 0;
|
|
479
|
-
H[1] = H[1] + b >>> 0;
|
|
480
|
-
H[2] = H[2] + c >>> 0;
|
|
481
|
-
H[3] = H[3] + d >>> 0;
|
|
482
|
-
H[4] = H[4] + e >>> 0;
|
|
483
|
-
H[5] = H[5] + f >>> 0;
|
|
484
|
-
H[6] = H[6] + g >>> 0;
|
|
485
|
-
H[7] = H[7] + h >>> 0;
|
|
486
|
-
}
|
|
487
|
-
let hex = "";
|
|
488
|
-
for (let i = 0; i < 8; i++) {
|
|
489
|
-
hex += H[i].toString(16).padStart(8, "0");
|
|
490
|
-
}
|
|
491
|
-
return hex;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
320
|
// src/entitlement-cache.ts
|
|
495
321
|
var DEFAULT_STALE_AFTER_MS = 24 * 60 * 60 * 1e3;
|
|
496
|
-
var
|
|
497
|
-
var INDEX_SUFFIX = "_index";
|
|
498
|
-
var EntitlementCache = class _EntitlementCache {
|
|
322
|
+
var EntitlementCache = class {
|
|
499
323
|
/**
|
|
500
|
-
* @param storage
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
* @param
|
|
505
|
-
*
|
|
506
|
-
*
|
|
507
|
-
* suffix is filled at identify() / reset()
|
|
508
|
-
* time — see [[setUserKey]].
|
|
509
|
-
* @param staleAfterMs Age past which last-known-good is flagged stale
|
|
510
|
-
* even without a failed refresh. Default 24h.
|
|
324
|
+
* @param storage Device storage adapter. When omitted (tests) or
|
|
325
|
+
* a MemoryStorage (strict-consent / no-persistence
|
|
326
|
+
* mode) the cache is session-only — durability is
|
|
327
|
+
* simply absent, never wrong.
|
|
328
|
+
* @param storageKey Full key the persisted blob lives under.
|
|
329
|
+
* @param staleAfterMs Age past which last-known-good is flagged stale
|
|
330
|
+
* even without a failed refresh. Default 24h.
|
|
511
331
|
*/
|
|
512
|
-
constructor(storage,
|
|
332
|
+
constructor(storage, storageKey = "crossdeck:entitlements", staleAfterMs = DEFAULT_STALE_AFTER_MS) {
|
|
513
333
|
this.all = [];
|
|
514
334
|
this.lastUpdated = 0;
|
|
515
335
|
this.lastRefreshFailedAt = 0;
|
|
516
336
|
this.listeners = /* @__PURE__ */ new Set();
|
|
517
337
|
this.listenerErrorCount = 0;
|
|
518
|
-
this.currentSuffix = ANON_SUFFIX;
|
|
519
338
|
this.storage = storage;
|
|
520
|
-
this.
|
|
339
|
+
this.storageKey = storageKey;
|
|
521
340
|
this.staleAfterMs = staleAfterMs;
|
|
522
341
|
this.hydrate();
|
|
523
342
|
}
|
|
524
|
-
/** The full storage key the current-user blob is persisted under. */
|
|
525
|
-
get storageKey() {
|
|
526
|
-
return `${this.storageKeyPrefix}:${this.currentSuffix}`;
|
|
527
|
-
}
|
|
528
|
-
/** Key of the index blob — a JSON array of every suffix we've
|
|
529
|
-
* written. Used by clearAll() to scope a logout-wipe. */
|
|
530
|
-
get indexKey() {
|
|
531
|
-
return `${this.storageKeyPrefix}:${INDEX_SUFFIX}`;
|
|
532
|
-
}
|
|
533
|
-
/** Derive a stable suffix for a developerUserId via SHA-256. The
|
|
534
|
-
* raw userId never lands in the storage key — protects against
|
|
535
|
-
* accidentally leaking email-style identifiers through DevTools
|
|
536
|
-
* inspection. Pass `null` to switch back to the anonymous slot. */
|
|
537
|
-
static suffixForUserId(userId) {
|
|
538
|
-
if (userId == null || userId === "") return ANON_SUFFIX;
|
|
539
|
-
return sha256Hex(userId);
|
|
540
|
-
}
|
|
541
|
-
/**
|
|
542
|
-
* Switch the cache to a different user's storage slot. Bank-grade
|
|
543
|
-
* three-layer isolation:
|
|
544
|
-
* (a) Physical key separation — `<prefix>:<sha256(userId)>` so
|
|
545
|
-
* a user-switch can't physically read prior user's data
|
|
546
|
-
* even if the in-memory clear was skipped.
|
|
547
|
-
* (b) Unconditional in-memory clear — invoked whenever the
|
|
548
|
-
* active suffix changes, even on same-id re-identify.
|
|
549
|
-
* (c) Re-hydrate from the new slot — a returning user observes
|
|
550
|
-
* their last-known-good cache from storage immediately.
|
|
551
|
-
*
|
|
552
|
-
* Caller (identify() / reset()) MUST invoke this BEFORE the next
|
|
553
|
-
* setFromList() so the write lands under the right key.
|
|
554
|
-
*/
|
|
555
|
-
setUserKey(userId) {
|
|
556
|
-
const nextSuffix = _EntitlementCache.suffixForUserId(userId);
|
|
557
|
-
if (nextSuffix === this.currentSuffix) {
|
|
558
|
-
this.all = [];
|
|
559
|
-
this.lastUpdated = 0;
|
|
560
|
-
this.lastRefreshFailedAt = 0;
|
|
561
|
-
this.notify();
|
|
562
|
-
this.hydrate();
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
this.currentSuffix = nextSuffix;
|
|
566
|
-
this.all = [];
|
|
567
|
-
this.lastUpdated = 0;
|
|
568
|
-
this.lastRefreshFailedAt = 0;
|
|
569
|
-
this.hydrate();
|
|
570
|
-
this.notify();
|
|
571
|
-
}
|
|
572
343
|
/**
|
|
573
344
|
* Sync read — true iff the entitlement is currently granting access.
|
|
574
345
|
*
|
|
@@ -638,13 +409,12 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
638
409
|
this.lastUpdated = Date.now();
|
|
639
410
|
this.lastRefreshFailedAt = 0;
|
|
640
411
|
this.persist();
|
|
641
|
-
this.recordSuffixInIndex(this.currentSuffix);
|
|
642
412
|
this.notify();
|
|
643
413
|
}
|
|
644
414
|
/**
|
|
645
|
-
* Wipe
|
|
646
|
-
*
|
|
647
|
-
*
|
|
415
|
+
* Wipe — used on reset() (logout) and on an identity switch. Clears
|
|
416
|
+
* BOTH memory and durable storage so a prior user's entitlements can
|
|
417
|
+
* never leak to the next person on this device.
|
|
648
418
|
*/
|
|
649
419
|
clear() {
|
|
650
420
|
this.all = [];
|
|
@@ -656,40 +426,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
656
426
|
} catch {
|
|
657
427
|
}
|
|
658
428
|
}
|
|
659
|
-
this.removeSuffixFromIndex(this.currentSuffix);
|
|
660
|
-
this.notify();
|
|
661
|
-
}
|
|
662
|
-
/**
|
|
663
|
-
* Logout-grade wipe — bank-grade contract: removes EVERY per-user
|
|
664
|
-
* entitlement slot the SDK has ever written on this device, then
|
|
665
|
-
* clears the index. Used by `Crossdeck.reset()` so a logout on a
|
|
666
|
-
* shared device can never leave another user's entitlements
|
|
667
|
-
* readable (layer (c) of the v1.4.0 isolation fix).
|
|
668
|
-
*
|
|
669
|
-
* After clearAll(), the cache is back to anonymous + empty.
|
|
670
|
-
*/
|
|
671
|
-
clearAll() {
|
|
672
|
-
this.all = [];
|
|
673
|
-
this.lastUpdated = 0;
|
|
674
|
-
this.lastRefreshFailedAt = 0;
|
|
675
|
-
this.currentSuffix = ANON_SUFFIX;
|
|
676
|
-
if (this.storage) {
|
|
677
|
-
const suffixes = this.readIndex();
|
|
678
|
-
for (const suffix of suffixes) {
|
|
679
|
-
try {
|
|
680
|
-
this.storage.removeItem(`${this.storageKeyPrefix}:${suffix}`);
|
|
681
|
-
} catch {
|
|
682
|
-
}
|
|
683
|
-
}
|
|
684
|
-
try {
|
|
685
|
-
this.storage.removeItem(`${this.storageKeyPrefix}:${ANON_SUFFIX}`);
|
|
686
|
-
} catch {
|
|
687
|
-
}
|
|
688
|
-
try {
|
|
689
|
-
this.storage.removeItem(this.indexKey);
|
|
690
|
-
} catch {
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
429
|
this.notify();
|
|
694
430
|
}
|
|
695
431
|
/**
|
|
@@ -739,47 +475,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
739
475
|
} catch {
|
|
740
476
|
}
|
|
741
477
|
}
|
|
742
|
-
/** Read the index of all per-user suffixes the SDK has written. */
|
|
743
|
-
readIndex() {
|
|
744
|
-
if (!this.storage) return [];
|
|
745
|
-
try {
|
|
746
|
-
const raw = this.storage.getItem(this.indexKey);
|
|
747
|
-
if (!raw) return [];
|
|
748
|
-
const parsed = JSON.parse(raw);
|
|
749
|
-
if (Array.isArray(parsed)) {
|
|
750
|
-
return parsed.filter((x) => typeof x === "string");
|
|
751
|
-
}
|
|
752
|
-
return [];
|
|
753
|
-
} catch {
|
|
754
|
-
return [];
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
/** Add a suffix to the persisted index. Idempotent. */
|
|
758
|
-
recordSuffixInIndex(suffix) {
|
|
759
|
-
if (!this.storage) return;
|
|
760
|
-
const existing = this.readIndex();
|
|
761
|
-
if (existing.includes(suffix)) return;
|
|
762
|
-
existing.push(suffix);
|
|
763
|
-
try {
|
|
764
|
-
this.storage.setItem(this.indexKey, JSON.stringify(existing));
|
|
765
|
-
} catch {
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
/** Remove a suffix from the persisted index. No-op if absent. */
|
|
769
|
-
removeSuffixFromIndex(suffix) {
|
|
770
|
-
if (!this.storage) return;
|
|
771
|
-
const existing = this.readIndex();
|
|
772
|
-
const next = existing.filter((s) => s !== suffix);
|
|
773
|
-
if (next.length === existing.length) return;
|
|
774
|
-
try {
|
|
775
|
-
if (next.length === 0) {
|
|
776
|
-
this.storage.removeItem(this.indexKey);
|
|
777
|
-
} else {
|
|
778
|
-
this.storage.setItem(this.indexKey, JSON.stringify(next));
|
|
779
|
-
}
|
|
780
|
-
} catch {
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
478
|
notify() {
|
|
784
479
|
if (this.listeners.size === 0) return;
|
|
785
480
|
const snapshot = this.all.slice();
|
|
@@ -794,34 +489,6 @@ var EntitlementCache = class _EntitlementCache {
|
|
|
794
489
|
}
|
|
795
490
|
};
|
|
796
491
|
|
|
797
|
-
// src/idempotency-key.ts
|
|
798
|
-
function formatAsUuid(hex) {
|
|
799
|
-
return [
|
|
800
|
-
hex.slice(0, 8),
|
|
801
|
-
hex.slice(8, 12),
|
|
802
|
-
hex.slice(12, 16),
|
|
803
|
-
hex.slice(16, 20),
|
|
804
|
-
hex.slice(20, 32)
|
|
805
|
-
].join("-");
|
|
806
|
-
}
|
|
807
|
-
function deriveIdempotencyKeyForPurchase(body) {
|
|
808
|
-
let identifier;
|
|
809
|
-
if (body.rail === "apple") {
|
|
810
|
-
identifier = body.signedTransactionInfo ?? "";
|
|
811
|
-
} else if (body.rail === "google") {
|
|
812
|
-
identifier = body.purchaseToken ?? "";
|
|
813
|
-
} else {
|
|
814
|
-
identifier = "";
|
|
815
|
-
}
|
|
816
|
-
if (!identifier) {
|
|
817
|
-
throw new Error(
|
|
818
|
-
`deriveIdempotencyKeyForPurchase: no stable identifier in body (rail=${body.rail}). Apple needs signedTransactionInfo; Google needs purchaseToken.`
|
|
819
|
-
);
|
|
820
|
-
}
|
|
821
|
-
const namespaced = `crossdeck:purchases/sync:${body.rail}:${identifier}`;
|
|
822
|
-
return formatAsUuid(sha256Hex(namespaced));
|
|
823
|
-
}
|
|
824
|
-
|
|
825
492
|
// src/retry-policy.ts
|
|
826
493
|
var DEFAULT_BASE = 1e3;
|
|
827
494
|
var DEFAULT_MAX = 6e4;
|
|
@@ -3084,10 +2751,6 @@ var CrossdeckClient = class {
|
|
|
3084
2751
|
this.state.errors?.uninstall();
|
|
3085
2752
|
} catch {
|
|
3086
2753
|
}
|
|
3087
|
-
try {
|
|
3088
|
-
void this.state.events.flush({ keepalive: true });
|
|
3089
|
-
} catch {
|
|
3090
|
-
}
|
|
3091
2754
|
}
|
|
3092
2755
|
if (!options.publicKey || !options.publicKey.startsWith("cd_pub_")) {
|
|
3093
2756
|
throw new CrossdeckError({
|
|
@@ -3135,12 +2798,7 @@ var CrossdeckClient = class {
|
|
|
3135
2798
|
// load still flushes if the user leaves quickly (the keepalive
|
|
3136
2799
|
// pagehide handler picks up anything that doesn't); long enough
|
|
3137
2800
|
// that bursts of clicks coalesce into one network round-trip.
|
|
3138
|
-
|
|
3139
|
-
// v1.4.0: Web/Node 1500ms, RN/Swift/Android 5000ms. All
|
|
3140
|
-
// converged on 2000ms (the Stripe-adjacent industry norm)
|
|
3141
|
-
// so cross-platform funnels show events landing at the
|
|
3142
|
-
// same cadence on every SDK. Per-instance override stays.
|
|
3143
|
-
eventFlushIntervalMs: options.eventFlushIntervalMs ?? 2e3,
|
|
2801
|
+
eventFlushIntervalMs: options.eventFlushIntervalMs ?? 1500,
|
|
3144
2802
|
sdkVersion: options.sdkVersion ?? SDK_VERSION,
|
|
3145
2803
|
autoTrack,
|
|
3146
2804
|
appVersion: options.appVersion ?? null
|
|
@@ -3368,10 +3026,14 @@ var CrossdeckClient = class {
|
|
|
3368
3026
|
};
|
|
3369
3027
|
if (options?.email) body.email = options.email;
|
|
3370
3028
|
if (traits) body.traits = traits;
|
|
3371
|
-
s.entitlements.setUserKey(userId);
|
|
3372
3029
|
const result = await s.http.request("POST", "/identity/alias", {
|
|
3373
3030
|
body
|
|
3374
3031
|
});
|
|
3032
|
+
const priorCdcust = s.identity.crossdeckCustomerId;
|
|
3033
|
+
const cacheHasEntries = s.entitlements.list().length > 0;
|
|
3034
|
+
if (priorCdcust && result.crossdeckCustomerId && priorCdcust !== result.crossdeckCustomerId || !priorCdcust && cacheHasEntries) {
|
|
3035
|
+
s.entitlements.clear();
|
|
3036
|
+
}
|
|
3375
3037
|
s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);
|
|
3376
3038
|
s.developerUserId = userId;
|
|
3377
3039
|
return result;
|
|
@@ -3676,37 +3338,6 @@ var CrossdeckClient = class {
|
|
|
3676
3338
|
* trip happens in the background. To flush before the page unloads,
|
|
3677
3339
|
* call flush().
|
|
3678
3340
|
*/
|
|
3679
|
-
/**
|
|
3680
|
-
* Emit `crossdeck.contract_failed` with the canonical property
|
|
3681
|
-
* shape (`contract_id`, `sdk_version`, `sdk_platform`,
|
|
3682
|
-
* `failure_reason`, `run_context`, `run_id`). Goes through the
|
|
3683
|
-
* standard track() pipeline — same consent gate, same queue,
|
|
3684
|
-
* same ingest, no new endpoint.
|
|
3685
|
-
*
|
|
3686
|
-
* Wire the call from a test hook, dogfood failure path, or
|
|
3687
|
-
* customer contract-verification harness; see
|
|
3688
|
-
* `contracts/README.md` for the per-test-framework hook recipes.
|
|
3689
|
-
*/
|
|
3690
|
-
reportContractFailure(input) {
|
|
3691
|
-
const props = {
|
|
3692
|
-
contract_id: input.contractId,
|
|
3693
|
-
sdk_version: SDK_VERSION,
|
|
3694
|
-
sdk_platform: "web",
|
|
3695
|
-
failure_reason: input.failureReason,
|
|
3696
|
-
run_context: input.runContext,
|
|
3697
|
-
run_id: input.runId
|
|
3698
|
-
};
|
|
3699
|
-
if (input.testRef) {
|
|
3700
|
-
props.test_file = input.testRef.file;
|
|
3701
|
-
props.test_name = input.testRef.name;
|
|
3702
|
-
}
|
|
3703
|
-
if (input.extra) {
|
|
3704
|
-
for (const [k, v] of Object.entries(input.extra)) {
|
|
3705
|
-
if (props[k] === void 0) props[k] = v;
|
|
3706
|
-
}
|
|
3707
|
-
}
|
|
3708
|
-
this.track("crossdeck.contract_failed", props);
|
|
3709
|
-
}
|
|
3710
3341
|
track(name, properties) {
|
|
3711
3342
|
const s = this.requireStarted();
|
|
3712
3343
|
if (!name) {
|
|
@@ -3843,24 +3474,11 @@ var CrossdeckClient = class {
|
|
|
3843
3474
|
});
|
|
3844
3475
|
}
|
|
3845
3476
|
const rail = input.rail ?? "apple";
|
|
3846
|
-
const body = { ...input, rail };
|
|
3847
|
-
const idempotencyKey = deriveIdempotencyKeyForPurchase(body);
|
|
3848
3477
|
const result = await s.http.request("POST", "/purchases/sync", {
|
|
3849
|
-
body,
|
|
3850
|
-
idempotencyKey
|
|
3478
|
+
body: { ...input, rail }
|
|
3851
3479
|
});
|
|
3852
3480
|
s.identity.setCrossdeckCustomerId(result.crossdeckCustomerId);
|
|
3853
3481
|
s.entitlements.setFromList(result.entitlements);
|
|
3854
|
-
try {
|
|
3855
|
-
const sourceProductId = result.entitlements[0]?.source.productId;
|
|
3856
|
-
const sourceSubscriptionId = result.entitlements[0]?.source.subscriptionId;
|
|
3857
|
-
const props = { rail };
|
|
3858
|
-
if (sourceProductId) props.productId = sourceProductId;
|
|
3859
|
-
if (sourceSubscriptionId) props.subscriptionId = sourceSubscriptionId;
|
|
3860
|
-
if (result.idempotent_replay) props.idempotent_replay = true;
|
|
3861
|
-
this.track("purchase.completed", props);
|
|
3862
|
-
} catch {
|
|
3863
|
-
}
|
|
3864
3482
|
s.debug.emit(
|
|
3865
3483
|
"sdk.purchase_evidence_sent",
|
|
3866
3484
|
"StoreKit transaction forwarded. Waiting for backend verification.",
|
|
@@ -3916,7 +3534,7 @@ var CrossdeckClient = class {
|
|
|
3916
3534
|
}
|
|
3917
3535
|
this.state.autoTracker?.uninstall();
|
|
3918
3536
|
this.state.identity.reset();
|
|
3919
|
-
this.state.entitlements.
|
|
3537
|
+
this.state.entitlements.clear();
|
|
3920
3538
|
this.state.events.reset();
|
|
3921
3539
|
this.state.superProps.clear();
|
|
3922
3540
|
this.state.breadcrumbs.clear();
|