@tolinku/web-sdk 0.3.0 → 0.4.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.mjs CHANGED
@@ -167,7 +167,11 @@ var HttpClient = class {
167
167
  }
168
168
  headers() {
169
169
  return {
170
- "X-API-Key": this.apiKey
170
+ "X-API-Key": this.apiKey,
171
+ // Browsers default to "Accept: */*", which a server content-negotiating an
172
+ // error page reads as "HTML is fine". Saying so explicitly keeps an error
173
+ // parseable instead of arriving as a rendered page.
174
+ "Accept": "application/json"
171
175
  };
172
176
  }
173
177
  };
@@ -525,6 +529,7 @@ var Referrals = class {
525
529
  };
526
530
 
527
531
  // src/deferred.ts
532
+ var CLAIMED_KEY = "tolinku_deferred_claimed";
528
533
  var Deferred = class {
529
534
  constructor(client) {
530
535
  this.client = client;
@@ -538,10 +543,60 @@ var Deferred = class {
538
543
  return null;
539
544
  }
540
545
  }
546
+ /**
547
+ * Recover the link that led here, once.
548
+ *
549
+ * There is no Play Install Referrer on the web, so this is signal matching
550
+ * with the bookkeeping that makes calling it safe. That bookkeeping is the
551
+ * point: a claim is consumed the first time it succeeds, so an app calling
552
+ * claimBySignals on every page load asks again after the answer is already
553
+ * spent, and every one of those asks is recorded as a miss. The match rate on
554
+ * the dashboard then falls towards zero while the integration is working
555
+ * correctly, which is a hard thing to diagnose from the outside.
556
+ *
557
+ * Call it once on first run. Calling it again is free after the first.
558
+ *
559
+ * Named to match the Android, React Native and Flutter SDKs, where the same
560
+ * call also tries the install referrer before falling back to here.
561
+ */
562
+ async claimDeferredLink(options) {
563
+ if (!options.appspaceId || !options.appspaceId.trim()) {
564
+ throw new Error("Tolinku: appspaceId is required and must not be blank for claimDeferredLink.");
565
+ }
566
+ if (!options.force && this.alreadyAttempted()) return null;
567
+ const { link, settled } = await this.attemptSignals({ appspaceId: options.appspaceId });
568
+ if (settled) this.rememberAttempt();
569
+ return link;
570
+ }
571
+ alreadyAttempted() {
572
+ try {
573
+ return window.localStorage.getItem(CLAIMED_KEY) !== null;
574
+ } catch {
575
+ return false;
576
+ }
577
+ }
578
+ rememberAttempt() {
579
+ try {
580
+ window.localStorage.setItem(CLAIMED_KEY, (/* @__PURE__ */ new Date()).toISOString());
581
+ } catch {
582
+ }
583
+ }
541
584
  /** Claim a deferred deep link by device signal matching */
542
585
  async claimBySignals(options) {
586
+ return (await this.attemptSignals(options)).link;
587
+ }
588
+ /**
589
+ * The signal claim, with whether the server actually answered.
590
+ *
591
+ * `settled` separates "nothing is waiting for this device", which no amount
592
+ * of asking will change, from "the request never got there". Both surface as
593
+ * null to callers of claimBySignals, but claimDeferredLink has to tell them
594
+ * apart: recording an attempt that never reached the server would spend an
595
+ * install's one chance at attribution on a dropped connection.
596
+ */
597
+ async attemptSignals(options) {
543
598
  try {
544
- return await this.client.postPublic("/v1/api/deferred/claim-by-signals", {
599
+ const link = await this.client.postPublic("/v1/api/deferred/claim-by-signals", {
545
600
  appspace_id: options.appspaceId,
546
601
  timezone: options.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone,
547
602
  language: options.language || navigator.language,
@@ -550,10 +605,11 @@ var Deferred = class {
550
605
  // Separates devices reporting identical logical dimensions.
551
606
  device_pixel_ratio: options.devicePixelRatio || window.devicePixelRatio || 1
552
607
  });
608
+ return { link, settled: true };
553
609
  } catch (err) {
554
610
  const status = err?.statusCode ?? err?.status;
555
611
  if (status === 404) {
556
- return null;
612
+ return { link: null, settled: true };
557
613
  }
558
614
  if (status === 403) {
559
615
  console.warn(
@@ -562,10 +618,10 @@ var Deferred = class {
562
618
  "under Settings), not your subdomain or slug.",
563
619
  err
564
620
  );
565
- return null;
621
+ return { link: null, settled: false };
566
622
  }
567
623
  console.warn("[Tolinku] Failed to claim deferred link by signals:", err);
568
- return null;
624
+ return { link: null, settled: false };
569
625
  }
570
626
  }
571
627
  };