@monetize.software/sdk-extension 3.0.0-alpha.3 → 3.0.0-alpha.5

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.
@@ -3,7 +3,7 @@ class o extends Error {
3
3
  super(e), this.name = "PaywallError", this.code = t, this.status = s.status, this.cause = s.cause;
4
4
  }
5
5
  }
6
- class N extends o {
6
+ class F extends o {
7
7
  constructor(t) {
8
8
  super("not_enough_queries", t.message ?? "Not enough queries", {
9
9
  status: 402
@@ -40,10 +40,10 @@ class P {
40
40
  return f;
41
41
  }
42
42
  }
43
- function D() {
43
+ function x() {
44
44
  return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
45
45
  }
46
- const x = {
46
+ const H = {
47
47
  getItem(i) {
48
48
  return new Promise((t) => {
49
49
  chrome.storage.local.get([i], (e) => {
@@ -101,19 +101,19 @@ const x = {
101
101
  };
102
102
  return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
103
103
  }
104
- }, b = /* @__PURE__ */ new Map(), H = {
104
+ }, S = /* @__PURE__ */ new Map(), V = {
105
105
  async getItem(i) {
106
- return b.get(i) ?? null;
106
+ return S.get(i) ?? null;
107
107
  },
108
108
  async setItem(i, t) {
109
- b.set(i, t);
109
+ S.set(i, t);
110
110
  },
111
111
  async removeItem(i) {
112
- b.delete(i);
112
+ S.delete(i);
113
113
  }
114
114
  };
115
115
  function R(i) {
116
- return i || (D() ? x : typeof window < "u" && "localStorage" in window ? J : H);
116
+ return i || (x() ? H : typeof window < "u" && "localStorage" in window ? J : V);
117
117
  }
118
118
  const y = {
119
119
  visitorId: "pw-visitor-id",
@@ -145,7 +145,7 @@ const y = {
145
145
  // (оптимистично через `decrementBalanceLocal`).
146
146
  balances: (i, t) => `pw-${i}-${t}-balances-v1`
147
147
  };
148
- function M() {
148
+ function D() {
149
149
  const i = typeof globalThis < "u" ? globalThis.crypto : void 0;
150
150
  if (i && typeof i.randomUUID == "function") return i.randomUUID();
151
151
  const t = new Uint8Array(16);
@@ -157,13 +157,13 @@ function M() {
157
157
  const e = Array.from(t, (s) => s.toString(16).padStart(2, "0")).join("");
158
158
  return `${e.slice(0, 8)}-${e.slice(8, 12)}-${e.slice(12, 16)}-${e.slice(16, 20)}-${e.slice(20)}`;
159
159
  }
160
- async function B(i) {
160
+ async function _(i) {
161
161
  try {
162
162
  const e = await i.getItem(y.visitorId);
163
163
  if (e && typeof e == "string" && e.length >= 16) return e;
164
164
  } catch {
165
165
  }
166
- const t = M();
166
+ const t = D();
167
167
  try {
168
168
  await i.setItem(y.visitorId, t);
169
169
  } catch {
@@ -183,10 +183,10 @@ function I(i) {
183
183
  for (let e = 0; e < i.length; e++) t += String.fromCharCode(i[e]);
184
184
  return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
185
185
  }
186
- function V() {
186
+ function j() {
187
187
  return I(q(64));
188
188
  }
189
- async function j(i) {
189
+ async function G(i) {
190
190
  const t = new TextEncoder().encode(i), e = globalThis.crypto;
191
191
  if (!e?.subtle?.digest)
192
192
  throw new Error("crypto.subtle is required for PKCE");
@@ -196,12 +196,17 @@ async function j(i) {
196
196
  function X() {
197
197
  return I(q(16));
198
198
  }
199
- const z = "https://appbox.space", G = 6e4, Q = 600 * 1e3;
200
- class mt {
199
+ const z = 6e4, Q = 600 * 1e3;
200
+ class wt {
201
201
  constructor(t) {
202
202
  if (this.session = null, this.inflightRefresh = null, this.inflightAnonSignin = null, this.listeners = /* @__PURE__ */ new Set(), this.storageUnwatch = null, this.destroyed = !1, this.oauthFlows = /* @__PURE__ */ new Map(), !t.paywallId)
203
203
  throw new o("invalid_config", "paywallId is required");
204
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? z, this.storage = R(t.storage), this.api = new P({
204
+ if (!t.apiOrigin)
205
+ throw new o(
206
+ "invalid_config",
207
+ "apiOrigin is required. Pass the paywall custom_domain configured in the platform."
208
+ );
209
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.storage = R(t.storage), this.api = new P({
205
210
  apiOrigin: this.apiOrigin,
206
211
  paywallId: t.paywallId,
207
212
  fetch: t.fetch
@@ -227,14 +232,15 @@ class mt {
227
232
  async applyExternalSession(t) {
228
233
  if (!this.destroyed && (await this.hydrated, !this.destroyed)) {
229
234
  if (t == null) {
230
- this.session && this.setSession(null, { skipPersist: !0 });
235
+ this.session && this.setSession(null, { skipPersist: !0, event: "SIGNED_OUT" });
231
236
  return;
232
237
  }
233
238
  try {
234
239
  const e = JSON.parse(t);
235
240
  if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
236
241
  return;
237
- this.setSession(e, { skipPersist: !0 });
242
+ const s = !this.session || this.session.user.id !== e.user.id ? "SIGNED_IN" : "TOKEN_REFRESHED";
243
+ this.setSession(e, { skipPersist: !0, event: s });
238
244
  } catch {
239
245
  }
240
246
  }
@@ -291,7 +297,7 @@ class mt {
291
297
  })
292
298
  }
293
299
  ), n = this.toSession(a, a.user);
294
- return this.setSession(n), n;
300
+ return this.setSession(n, { event: "SIGNED_IN" }), n;
295
301
  }
296
302
  /**
297
303
  * Signup. Если в Supabase включён email confirm — сервер возвращает
@@ -319,7 +325,7 @@ class mt {
319
325
  if (a.status === "confirmation_required")
320
326
  return { kind: "confirmation_required", user: a.user };
321
327
  const n = this.toSession(a, a.user);
322
- return this.setSession(n), { kind: "signed_in", session: n };
328
+ return this.setSession(n, { event: "SIGNED_IN" }), { kind: "signed_in", session: n };
323
329
  }
324
330
  /**
325
331
  * Повторная отправка confirmation-email после signUp с включённым
@@ -382,8 +388,8 @@ class mt {
382
388
  user_meta: t.userMeta
383
389
  })
384
390
  }
385
- ), a = this.toSession(s, s.user);
386
- return this.setSession(a), a;
391
+ ), a = this.toSession(s, s.user), n = t.type === "recovery" ? "PASSWORD_RECOVERY" : "SIGNED_IN";
392
+ return this.setSession(a, { event: n }), a;
387
393
  }
388
394
  /**
389
395
  * Запрос recovery email. Бэк всегда ok, чтобы не палить enumeration.
@@ -475,7 +481,7 @@ class mt {
475
481
  email: s.user.email ?? null,
476
482
  is_anonymous: !0
477
483
  }, n = this.toSession(s, a);
478
- return this.setSession(n), await this.writeAnonRefreshToken(n.refresh_token), n;
484
+ return this.setSession(n, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(n.refresh_token), n;
479
485
  })();
480
486
  try {
481
487
  return await this.inflightAnonSignin;
@@ -497,7 +503,7 @@ class mt {
497
503
  `/api/v1/paywall/${this.paywallId}/auth/refresh`,
498
504
  { method: "POST", body: JSON.stringify({ refresh_token: t }) }
499
505
  ), s = this.session?.user.is_anonymous === !0 ? this.session.user : { id: "", email: null, is_anonymous: !0 }, a = this.toSession(e, s);
500
- return this.setSession(a), await this.writeAnonRefreshToken(a.refresh_token), a;
506
+ return this.setSession(a, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(a.refresh_token), a;
501
507
  } catch (e) {
502
508
  if (e instanceof o && e.status === 401)
503
509
  return await this.clearAnonRefreshToken(), null;
@@ -560,7 +566,7 @@ class mt {
560
566
  email: a.user.email,
561
567
  is_anonymous: a.user.is_anonymous ?? !1
562
568
  }, l = { ...n, user: r };
563
- return this.setSession(l), await this.clearAnonRefreshToken(), { kind: "updated", session: l };
569
+ return this.setSession(l, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: l };
564
570
  }
565
571
  /**
566
572
  * OAuth signin через popup с PKCE. Жизненный цикл:
@@ -617,7 +623,7 @@ class mt {
617
623
  */
618
624
  async startOAuthFlow(t) {
619
625
  await this.hydrated, this.gcOAuthFlows();
620
- const e = V(), s = await j(e), a = X(), n = {}, r = await this.getAccessToken().catch(() => null);
626
+ const e = j(), s = await G(e), a = X(), n = {}, r = await this.getAccessToken().catch(() => null);
621
627
  r && (n.Authorization = `Bearer ${r}`);
622
628
  const { authorize_url: l } = await this.api.request(
623
629
  `/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
@@ -671,7 +677,7 @@ class mt {
671
677
  if (this.destroyed)
672
678
  throw new o("aborted", "AuthClient destroyed mid-flow");
673
679
  const n = this.toSession(a, a.user);
674
- return this.setSession(n), n;
680
+ return this.setSession(n, { event: "SIGNED_IN" }), n;
675
681
  }
676
682
  gcOAuthFlows() {
677
683
  const t = Date.now() - Q;
@@ -700,10 +706,10 @@ class mt {
700
706
  body: JSON.stringify({ refresh_token: t })
701
707
  }
702
708
  ), a = this.toSession(s, e);
703
- return this.setSession(a), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
709
+ return this.setSession(a, { event: "TOKEN_REFRESHED" }), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
704
710
  } catch (s) {
705
711
  if (s instanceof o && s.status === 401)
706
- return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null), null;
712
+ return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null, { event: "SIGNED_OUT" }), null;
707
713
  throw s;
708
714
  } finally {
709
715
  this.inflightRefresh = null;
@@ -736,7 +742,7 @@ class mt {
736
742
  method: "POST",
737
743
  headers: { Authorization: `Bearer ${t}` }
738
744
  }
739
- ), this.setSession(null);
745
+ ), this.setSession(null, { event: "SIGNED_OUT" });
740
746
  }
741
747
  /**
742
748
  * Signout: чистит локальную session СРАЗУ (UX — мгновенный logout без
@@ -756,7 +762,7 @@ class mt {
756
762
  async signOut(t = {}) {
757
763
  await this.hydrated;
758
764
  const e = this.session?.access_token, s = this.session?.user.is_anonymous === !0;
759
- if (this.setSession(null), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
765
+ if (this.setSession(null, { event: "SIGNED_OUT" }), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
760
766
  try {
761
767
  await this.api.request(
762
768
  `/api/v1/paywall/${this.paywallId}/auth/signout`,
@@ -770,22 +776,35 @@ class mt {
770
776
  }
771
777
  /**
772
778
  * Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
773
- * Колбек вызывается с текущим snapshot через microtask (если session есть)
774
- * + на каждое реальное изменение. Возвращает unsubscribe.
779
+ *
780
+ * Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
781
+ * `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
782
+ * (даже если session=null — listener получает explicit «нет сессии», а не
783
+ * молчание). Все последующие callback'и — реальные переходы с конкретным
784
+ * event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
785
+ * PASSWORD_RECOVERY).
786
+ *
787
+ * Это позволяет listener'у безопасно делать «only on real signin» побочные
788
+ * эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
789
+ * не путая их с восстановлением из storage.
790
+ *
791
+ * Возвращает unsubscribe.
775
792
  */
776
793
  onAuthChange(t) {
777
- if (this.listeners.add(t), this.session) {
794
+ return this.listeners.add(t), this.hydrated.then(() => {
795
+ if (this.destroyed || !this.listeners.has(t)) return;
778
796
  const e = this.session;
779
- queueMicrotask(() => {
780
- this.listeners.has(t) && t(e);
781
- });
782
- }
783
- return () => {
797
+ try {
798
+ t("INITIAL_SESSION", e);
799
+ } catch (s) {
800
+ console.warn("[paywall] onAuthChange INITIAL_SESSION threw", s);
801
+ }
802
+ }), () => {
784
803
  this.listeners.delete(t);
785
804
  };
786
805
  }
787
806
  isFresh(t) {
788
- return t.expires_at - Date.now() > G;
807
+ return t.expires_at - Date.now() > z;
789
808
  }
790
809
  toSession(t, e) {
791
810
  const s = t.expires_at != null ? t.expires_at * 1e3 : Date.now() + t.expires_in * 1e3;
@@ -796,17 +815,17 @@ class mt {
796
815
  user: e
797
816
  };
798
817
  }
799
- setSession(t, e = {}) {
818
+ setSession(t, e) {
800
819
  if (this.destroyed) return;
801
820
  const s = this.session;
802
- this.session = t, e.skipPersist || this.persist(), tt(s, t) || this.emit();
821
+ this.session = t, e.skipPersist || this.persist(), tt(s, t) || this.emit(e.event);
803
822
  }
804
- emit() {
805
- for (const t of this.listeners)
823
+ emit(t) {
824
+ for (const e of this.listeners)
806
825
  try {
807
- t(this.session);
808
- } catch (e) {
809
- console.warn("[paywall] onAuthChange listener threw", e);
826
+ e(t, this.session);
827
+ } catch (s) {
828
+ console.warn("[paywall] onAuthChange listener threw", s);
810
829
  }
811
830
  }
812
831
  storageKey() {
@@ -819,7 +838,7 @@ class mt {
819
838
  const e = JSON.parse(t);
820
839
  if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
821
840
  return;
822
- this.session = e, this.emit();
841
+ this.session = e;
823
842
  } catch {
824
843
  }
825
844
  }
@@ -833,7 +852,7 @@ class mt {
833
852
  const e = JSON.parse(t);
834
853
  if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
835
854
  return;
836
- this.setSession(e, { skipPersist: !0 });
855
+ this.setSession(e, { skipPersist: !0, event: "SIGNED_IN" });
837
856
  } catch {
838
857
  }
839
858
  }
@@ -959,12 +978,16 @@ function Z(i, t) {
959
978
  function tt(i, t) {
960
979
  return i === t ? !0 : !i || !t ? !1 : i.access_token === t.access_token && i.refresh_token === t.refresh_token && i.expires_at === t.expires_at && i.user.id === t.user.id && i.user.email === t.user.email;
961
980
  }
962
- const et = "https://appbox.space";
963
- class st {
981
+ class et {
964
982
  constructor(t) {
965
983
  if (!t.paywallId)
966
984
  throw new o("invalid_config", "paywallId is required");
967
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? et, this.auth = t.auth, this.userId = t.userId, this.capabilities = t.capabilities, this.customFetch = t.fetch, this.onChargeSuccess = t.onChargeSuccess, this.onQuotaExceeded = t.onQuotaExceeded, t.userId && !t.auth && typeof window < "u" && typeof window.document < "u" && console.warn(
985
+ if (!t.apiOrigin)
986
+ throw new o(
987
+ "invalid_config",
988
+ "apiOrigin is required. Pass the paywall custom_domain configured in the platform."
989
+ );
990
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.auth = t.auth, this.userId = t.userId, this.capabilities = t.capabilities, this.customFetch = t.fetch, this.onChargeSuccess = t.onChargeSuccess, this.onQuotaExceeded = t.onQuotaExceeded, t.userId && !t.auth && typeof window < "u" && typeof window.document < "u" && console.warn(
968
991
  "[paywall] WARNING: ApiGatewayClient.userId set without auth in browser. Client can spoof userId. Use AuthClient + Bearer for trusted user.id."
969
992
  );
970
993
  }
@@ -992,15 +1015,15 @@ class st {
992
1015
  credentials: "omit"
993
1016
  });
994
1017
  } catch (p) {
995
- const K = p instanceof Error ? p.message : String(p);
996
- throw new o("network_error", `Network request failed: ${K}`, { cause: p });
1018
+ const $ = p instanceof Error ? p.message : String(p);
1019
+ throw new o("network_error", `Network request failed: ${$}`, { cause: p });
997
1020
  }
998
1021
  if (h.status === 402) {
999
- const p = await it(h);
1022
+ const p = await st(h);
1000
1023
  throw this.onQuotaExceeded?.(p), p;
1001
1024
  }
1002
1025
  if (!h.ok) {
1003
- const p = await at(h.clone());
1026
+ const p = await it(h.clone());
1004
1027
  throw new o(
1005
1028
  p ?? `http_${h.status}`,
1006
1029
  h.statusText || "Gateway request failed",
@@ -1011,7 +1034,7 @@ class st {
1011
1034
  return this.onChargeSuccess?.(g), h;
1012
1035
  }
1013
1036
  }
1014
- async function it(i) {
1037
+ async function st(i) {
1015
1038
  let t = {};
1016
1039
  try {
1017
1040
  t = await i.json();
@@ -1023,13 +1046,13 @@ async function it(i) {
1023
1046
  const a = e[0];
1024
1047
  Array.isArray(a) ? s = a : a && Array.isArray(a.balances) && (s = a.balances);
1025
1048
  }
1026
- return new N({
1049
+ return new F({
1027
1050
  balances: s,
1028
1051
  queryType: t.details?.queryType ?? "",
1029
1052
  currentBalance: t.details?.currentBalance ?? null
1030
1053
  });
1031
1054
  }
1032
- async function at(i) {
1055
+ async function it(i) {
1033
1056
  if (!(i.headers.get("content-type") ?? "").includes("application/json")) return null;
1034
1057
  try {
1035
1058
  const e = await i.json();
@@ -1038,7 +1061,7 @@ async function at(i) {
1038
1061
  return null;
1039
1062
  }
1040
1063
  }
1041
- const nt = 5e3, rt = 30 * 6e4, _ = 60 * 6e4, ot = 5 * 6e4, k = {
1064
+ const at = 5e3, nt = 30 * 6e4, B = 60 * 6e4, rt = 5 * 6e4, k = {
1042
1065
  has_active_subscription: !1,
1043
1066
  purchases: [],
1044
1067
  trial: null
@@ -1046,25 +1069,29 @@ const nt = 5e3, rt = 30 * 6e4, _ = 60 * 6e4, ot = 5 * 6e4, k = {
1046
1069
  function A(i) {
1047
1070
  return i && (i.email || i.userId || i.anonymousId) || "guest";
1048
1071
  }
1049
- function ct(i, t) {
1072
+ function ot(i, t) {
1050
1073
  return i === t ? !0 : !i || !t ? !1 : JSON.stringify(i) === JSON.stringify(t);
1051
1074
  }
1052
- const ht = 5e3, T = 5 * 6e4, lt = 3e4;
1053
- function ut(i, t) {
1075
+ const ct = 5e3, T = 5 * 6e4, ht = 3e4;
1076
+ function lt(i, t) {
1054
1077
  if (i === t) return !0;
1055
1078
  if (!i || !t || i.length !== t.length) return !1;
1056
1079
  for (let e = 0; e < i.length; e++)
1057
1080
  if (i[e].type !== t[e].type || i[e].count !== t[e].count) return !1;
1058
1081
  return !0;
1059
1082
  }
1060
- const dt = "https://appbox.space";
1061
- class bt {
1083
+ class mt {
1062
1084
  constructor(t) {
1063
1085
  if (this.cachedBootstrap = null, this.cachedBootstrapAt = 0, this.inflightBootstrap = null, this.bootstrapListeners = /* @__PURE__ */ new Set(), this.bootstrapStorageUnwatch = null, this.authUnsubscribe = null, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.userListeners = /* @__PURE__ */ new Set(), this.visitorIdPromise = null, this.visitorId = null, this.inflightCheckouts = /* @__PURE__ */ new Map(), this.cachedBalances = null, this.cachedBalancesAt = 0, this.balancesStorageUnwatch = null, this.inflightBalances = null, this.balanceListeners = /* @__PURE__ */ new Set(), this.previewVersionCounter = 0, !t.paywallId)
1064
1086
  throw new o("invalid_config", "paywallId is required");
1065
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? dt, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
1087
+ if (!t.apiOrigin)
1088
+ throw new o(
1089
+ "invalid_config",
1090
+ 'apiOrigin is required. Pass the paywall custom_domain configured in the platform (e.g. "https://pay.your-domain.com"). The legacy "appbox.space" fallback is not used in SDK 3.0.'
1091
+ );
1092
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
1066
1093
  const e = t.auth?.getCachedUser();
1067
- this.identity = t.identity ?? (e ? U(e) : void 0), this.apiKey = t.apiKey, this.fetchImpl = t.fetch, t.apiKey && typeof window < "u" && typeof window.document < "u" && console.error(
1094
+ this.identity = t.identity ?? (e ? O(e) : void 0), this.apiKey = t.apiKey, this.fetchImpl = t.fetch, t.apiKey && typeof window < "u" && typeof window.document < "u" && console.error(
1068
1095
  "[paywall] SECURITY: BillingClient.apiKey detected in browser context. This is a server-SDK key and exposes your account. Remove apiKey or move BillingClient to a trusted backend."
1069
1096
  ), this.storage = R(t.storage), this.api = new P({
1070
1097
  apiOrigin: this.apiOrigin,
@@ -1075,10 +1102,10 @@ class bt {
1075
1102
  // делает lazy refresh, дедупит, на 401 возвращает null — тогда
1076
1103
  // Authorization-хедер просто не выставится.
1077
1104
  getAuthToken: t.auth ? () => t.auth.getAccessToken() : void 0
1078
- }), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s) => {
1079
- const a = s ? U(s.user) : void 0;
1080
- ft(this.identity, a) || this.setIdentity(a);
1081
- })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = B(this.storage).then((s) => (this.visitorId = s, s));
1105
+ }), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s, a) => {
1106
+ const n = a ? O(a.user) : void 0;
1107
+ ut(this.identity, n) || this.setIdentity(n);
1108
+ })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = _(this.storage).then((s) => (this.visitorId = s, s));
1082
1109
  }
1083
1110
  /**
1084
1111
  * Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
@@ -1086,7 +1113,7 @@ class bt {
1086
1113
  * EventTracker'ом для атрибуции аналитики.
1087
1114
  */
1088
1115
  async getVisitorId() {
1089
- return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = B(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
1116
+ return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = _(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
1090
1117
  }
1091
1118
  /** Sync-доступ к visitor_id. null если ещё не зарезолвили (первые ms жизни). */
1092
1119
  getCachedVisitorId() {
@@ -1122,9 +1149,9 @@ class bt {
1122
1149
  "BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
1123
1150
  );
1124
1151
  }
1125
- const s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < _;
1126
- return !e.force && a ? (s - this.cachedBootstrapAt > ot && this.revalidateBootstrap(e.signal).catch(() => {
1127
- }), this.cachedBootstrap) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
1152
+ const s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < B;
1153
+ return !e.force && a ? (s - this.cachedBootstrapAt > rt && this.revalidateBootstrap(e.signal).catch(() => {
1154
+ }), { ...this.cachedBootstrap, user: this.cachedUser ?? void 0 }) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
1128
1155
  ifVersion: e.force ? void 0 : this.cachedBootstrap?.version,
1129
1156
  signal: e.signal
1130
1157
  }).finally(() => {
@@ -1172,7 +1199,7 @@ class bt {
1172
1199
  offers: t.offers !== void 0 ? t.offers : e.offers,
1173
1200
  version: `preview:${++this.previewVersionCounter}`
1174
1201
  };
1175
- s.layout || (s.layout = O(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
1202
+ s.layout || (s.layout = E(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
1176
1203
  for (const a of this.bootstrapListeners)
1177
1204
  try {
1178
1205
  a(s);
@@ -1194,7 +1221,7 @@ class bt {
1194
1221
  if ("unchanged" in a && a.unchanged)
1195
1222
  return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), a.user && this.applyUser(a.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
1196
1223
  const n = a;
1197
- return n.layout || (n.layout = O(n.settings, n.prices)), w(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
1224
+ return dt(n.settings.custom_domain, this.apiOrigin), n.layout || (n.layout = E(n.settings, n.prices)), w(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
1198
1225
  }
1199
1226
  // Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
1200
1227
  // `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
@@ -1227,7 +1254,7 @@ class bt {
1227
1254
  const t = await this.storage.getItem(y.bootstrap(this.paywallId));
1228
1255
  if (!t) return;
1229
1256
  const e = JSON.parse(t);
1230
- if (!e?.bootstrap || Date.now() - e.at > _ || this.cachedBootstrap) return;
1257
+ if (!e?.bootstrap || Date.now() - e.at > B || this.cachedBootstrap) return;
1231
1258
  w(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
1232
1259
  for (const s of this.bootstrapListeners)
1233
1260
  try {
@@ -1311,7 +1338,7 @@ class bt {
1311
1338
  * есть `navigator.language`.
1312
1339
  */
1313
1340
  getUserLanguage() {
1314
- const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? $(this.cachedBootstrap) : null;
1341
+ const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? K(this.cachedBootstrap) : null;
1315
1342
  return { tag: s ?? t ?? e, applied: s, browserLanguage: t, countryLanguage: e };
1316
1343
  }
1317
1344
  /**
@@ -1323,7 +1350,7 @@ class bt {
1323
1350
  * - Без identity возвращает empty-state (сервер тоже так делает).
1324
1351
  */
1325
1352
  async getUser({ force: t = !1, signal: e } = {}) {
1326
- return !t && this.cachedUser && Date.now() - this.cachedUserAt < nt ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
1353
+ return !t && this.cachedUser && Date.now() - this.cachedUserAt < at ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
1327
1354
  try {
1328
1355
  if (!this.identity?.email)
1329
1356
  return this.applyUser(k), k;
@@ -1379,7 +1406,7 @@ class bt {
1379
1406
  return this.cachedUser;
1380
1407
  }
1381
1408
  applyUser(t) {
1382
- const e = !ct(this.cachedUser, t);
1409
+ const e = !ot(this.cachedUser, t);
1383
1410
  if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
1384
1411
  this.persistUser(t);
1385
1412
  for (const s of this.userListeners)
@@ -1399,7 +1426,7 @@ class bt {
1399
1426
  const t = await this.storage.getItem(this.storageKey());
1400
1427
  if (!t) return;
1401
1428
  const e = JSON.parse(t);
1402
- if (!e?.user || Date.now() - e.at > rt || this.cachedUser) return;
1429
+ if (!e?.user || Date.now() - e.at > nt || this.cachedUser) return;
1403
1430
  this.applyUser(e.user);
1404
1431
  } catch {
1405
1432
  }
@@ -1428,7 +1455,7 @@ class bt {
1428
1455
  */
1429
1456
  async getBalances({ force: t = !1, signal: e } = {}) {
1430
1457
  const s = Date.now(), a = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
1431
- return !t && this.cachedBalances && (a < ht || a < lt) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
1458
+ return !t && this.cachedBalances && (a < ct || a < ht) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
1432
1459
  }), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
1433
1460
  }
1434
1461
  // Network primitive — единая точка для force/stale-revalidate/cold-start.
@@ -1519,7 +1546,7 @@ class bt {
1519
1546
  */
1520
1547
  createApiGatewayClient(t = {}) {
1521
1548
  const e = t.onChargeSuccess, s = t.onQuotaExceeded;
1522
- return new st({
1549
+ return new et({
1523
1550
  paywallId: this.paywallId,
1524
1551
  apiOrigin: this.apiOrigin,
1525
1552
  auth: this.auth,
@@ -1536,7 +1563,7 @@ class bt {
1536
1563
  });
1537
1564
  }
1538
1565
  applyBalances(t, { persist: e = !0 } = {}) {
1539
- const s = !ut(this.cachedBalances, t);
1566
+ const s = !lt(this.cachedBalances, t);
1540
1567
  if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
1541
1568
  for (const a of this.balanceListeners)
1542
1569
  try {
@@ -1600,7 +1627,7 @@ class bt {
1600
1627
  const e = t.idempotencyKey ?? `auto:${t.priceId}`, s = this.inflightCheckouts.get(e);
1601
1628
  if (s) return s;
1602
1629
  const n = {
1603
- "Idempotency-Key": t.idempotencyKey ?? M()
1630
+ "Idempotency-Key": t.idempotencyKey ?? D()
1604
1631
  };
1605
1632
  this.apiKey && (n["X-Api-Key"] = this.apiKey);
1606
1633
  const r = this.cachedBootstrap?.settings, l = t.successUrl ?? r?.success_redirect_url ?? void 0, u = t.shopUrl ?? r?.checkout_shop_url ?? void 0, d = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
@@ -1744,23 +1771,43 @@ class bt {
1744
1771
  });
1745
1772
  }
1746
1773
  }
1747
- function U(i) {
1774
+ function O(i) {
1748
1775
  return { email: i.email, userId: i.id };
1749
1776
  }
1750
- function ft(i, t) {
1777
+ function ut(i, t) {
1751
1778
  return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
1752
1779
  }
1753
- function O(i, t) {
1780
+ function U(i) {
1781
+ if (!i) return null;
1782
+ const t = i.trim();
1783
+ if (!t) return null;
1784
+ try {
1785
+ return new URL(t.includes("://") ? t : `https://${t}`).origin;
1786
+ } catch {
1787
+ return null;
1788
+ }
1789
+ }
1790
+ function dt(i, t) {
1791
+ const e = U(i);
1792
+ if (!(!e || U(t) === e))
1793
+ throw new o(
1794
+ "invalid_config",
1795
+ `apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
1796
+ );
1797
+ }
1798
+ function E(i, t) {
1754
1799
  return {
1755
1800
  type: "modal",
1756
1801
  blocks: [
1757
1802
  { type: "heading", text: i.name || "Upgrade", level: 1 },
1758
1803
  { type: "price_grid", priceIds: t.map((e) => e.id) },
1759
- { type: "cta_button", label: "Continue", action: "checkout" }
1804
+ { type: "cta_button", label: "Continue", action: "checkout" },
1805
+ { type: "guarantee_badge" },
1806
+ { type: "current_session" }
1760
1807
  ]
1761
1808
  };
1762
1809
  }
1763
- function $(i) {
1810
+ function K(i) {
1764
1811
  const t = i.locales;
1765
1812
  if (!t) return null;
1766
1813
  const e = [];
@@ -1776,7 +1823,7 @@ function $(i) {
1776
1823
  return null;
1777
1824
  }
1778
1825
  function w(i) {
1779
- const t = $(i);
1826
+ const t = K(i);
1780
1827
  if (!t) return;
1781
1828
  const e = i.locales?.[t];
1782
1829
  e && (e.layout && (i.layout = e.layout), e.prices && (i.prices = i.prices.map((s) => {
@@ -1786,8 +1833,8 @@ function w(i) {
1786
1833
  return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
1787
1834
  })));
1788
1835
  }
1789
- const yt = 1500, pt = 20, C = 200;
1790
- class vt {
1836
+ const ft = 1500, yt = 20, C = 200;
1837
+ class St {
1791
1838
  constructor(t) {
1792
1839
  this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
1793
1840
  }
@@ -1797,7 +1844,7 @@ class vt {
1797
1844
  track(t, e) {
1798
1845
  if (this.destroyed || !this.isEnabled() || typeof t != "string" || t.length === 0) return;
1799
1846
  this.buffer.push({ type: t, ts: Date.now(), props: e });
1800
- const s = this.opts.maxBufferSize ?? pt;
1847
+ const s = this.opts.maxBufferSize ?? yt;
1801
1848
  if (this.buffer.length >= s) {
1802
1849
  this.flush();
1803
1850
  return;
@@ -1806,7 +1853,7 @@ class vt {
1806
1853
  }
1807
1854
  scheduleFlush() {
1808
1855
  if (this.flushTimer || this.destroyed) return;
1809
- const t = this.opts.flushIntervalMs ?? yt;
1856
+ const t = this.opts.flushIntervalMs ?? ft;
1810
1857
  this.flushTimer = setTimeout(() => {
1811
1858
  this.flushTimer = null, this.flush();
1812
1859
  }, t);
@@ -1885,14 +1932,14 @@ class vt {
1885
1932
  this.destroyed || (this.destroyed = !0, this.flushTimer && (clearTimeout(this.flushTimer), this.flushTimer = null), this.flush(), this.detachUnloadHandlers());
1886
1933
  }
1887
1934
  }
1888
- const E = 3600 * 1e3;
1935
+ const N = 3600 * 1e3;
1889
1936
  function v(i) {
1890
1937
  return `paywall-${i}-trial-time-first-open`;
1891
1938
  }
1892
- function S(i) {
1939
+ function b(i) {
1893
1940
  return `paywall-${i}-skip-times`;
1894
1941
  }
1895
- class F {
1942
+ class M {
1896
1943
  constructor(t, e, s) {
1897
1944
  this.storage = t, this.paywallId = e, this.config = s;
1898
1945
  }
@@ -1903,10 +1950,10 @@ class F {
1903
1950
  return this.config.mode === "time" ? this.recordTime() : this.recordOpens();
1904
1951
  }
1905
1952
  async reset() {
1906
- await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : S(this.paywallId));
1953
+ await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : b(this.paywallId));
1907
1954
  }
1908
1955
  async checkTime() {
1909
- const t = this.config.payload * E, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
1956
+ const t = this.config.payload * N, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
1910
1957
  if (!s || !Number.isFinite(s))
1911
1958
  return {
1912
1959
  mode: "time",
@@ -1927,7 +1974,7 @@ class F {
1927
1974
  };
1928
1975
  }
1929
1976
  async checkOpens() {
1930
- const t = this.config.payload, e = await this.storage.getItem(S(this.paywallId)), s = e ? Number(e) : 0, a = Number.isFinite(s) ? s : 0, n = a < t, r = Math.max(0, t - a);
1977
+ const t = this.config.payload, e = await this.storage.getItem(b(this.paywallId)), s = e ? Number(e) : 0, a = Number.isFinite(s) ? s : 0, n = a < t, r = Math.max(0, t - a);
1931
1978
  return {
1932
1979
  mode: "opens",
1933
1980
  blocked: n,
@@ -1936,7 +1983,7 @@ class F {
1936
1983
  };
1937
1984
  }
1938
1985
  async recordTime() {
1939
- const t = this.config.payload * E, e = v(this.paywallId), s = await this.storage.getItem(e);
1986
+ const t = this.config.payload * N, e = v(this.paywallId), s = await this.storage.getItem(e);
1940
1987
  let a = s ? Number(s) : null;
1941
1988
  (!a || !Number.isFinite(a)) && (a = Date.now(), await this.storage.setItem(e, String(a)));
1942
1989
  const n = a + t, r = Math.max(0, n - Date.now());
@@ -1950,7 +1997,7 @@ class F {
1950
1997
  };
1951
1998
  }
1952
1999
  async recordOpens() {
1953
- const t = this.config.payload, e = S(this.paywallId), s = await this.storage.getItem(e), a = s ? Number(s) : 0, n = Number.isFinite(a) ? a : 0, r = Math.min(t, n + 1);
2000
+ const t = this.config.payload, e = b(this.paywallId), s = await this.storage.getItem(e), a = s ? Number(s) : 0, n = Number.isFinite(a) ? a : 0, r = Math.min(t, n + 1);
1954
2001
  await this.storage.setItem(e, String(r));
1955
2002
  const l = Math.max(0, t - r);
1956
2003
  return {
@@ -1962,11 +2009,11 @@ class F {
1962
2009
  }
1963
2010
  }
1964
2011
  let L = !1;
1965
- class gt {
2012
+ class pt {
1966
2013
  constructor(t, e, s) {
1967
2014
  L || (L = !0, console.warn(
1968
2015
  '[paywall] trial.storage="server" is not implemented yet — falling back to client storage. State lives in localStorage; users can reset trial by clearing site data.'
1969
- )), this.fallback = new F(t, e, s);
2016
+ )), this.fallback = new M(t, e, s);
1970
2017
  }
1971
2018
  check() {
1972
2019
  return this.fallback.check();
@@ -1978,11 +2025,11 @@ class gt {
1978
2025
  return this.fallback.reset();
1979
2026
  }
1980
2027
  }
1981
- function St(i, t, e) {
1982
- return e.storage === "server" ? new gt(i, t, e) : new F(i, t, e);
2028
+ function vt(i, t, e) {
2029
+ return e.storage === "server" ? new pt(i, t, e) : new M(i, t, e);
1983
2030
  }
1984
- const It = 1;
1985
- function Bt(i) {
2031
+ const bt = 1;
2032
+ function It(i) {
1986
2033
  return i instanceof o ? {
1987
2034
  name: "PaywallError",
1988
2035
  code: i.code,
@@ -2008,7 +2055,7 @@ function _t(i) {
2008
2055
  const t = new Error(i.message);
2009
2056
  return t.name = i.name, i.stack && (t.stack = i.stack), t;
2010
2057
  }
2011
- function wt(i) {
2058
+ function gt(i) {
2012
2059
  let t = !1;
2013
2060
  const e = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = (r) => {
2014
2061
  for (const l of e) l(r);
@@ -2040,21 +2087,21 @@ function wt(i) {
2040
2087
  }
2041
2088
  };
2042
2089
  }
2043
- function kt(i) {
2090
+ function Bt(i) {
2044
2091
  const t = chrome.runtime.connect({ name: i });
2045
- return wt(t);
2092
+ return gt(t);
2046
2093
  }
2047
2094
  export {
2048
- mt as A,
2049
- bt as B,
2050
- vt as E,
2095
+ wt as A,
2096
+ mt as B,
2097
+ St as E,
2051
2098
  o as P,
2052
- It as a,
2053
- kt as b,
2054
- St as c,
2055
- wt as p,
2099
+ bt as a,
2100
+ Bt as b,
2101
+ vt as c,
2102
+ gt as p,
2056
2103
  _t as r,
2057
- Bt as s,
2104
+ It as s,
2058
2105
  Z as w
2059
2106
  };
2060
- //# sourceMappingURL=chrome-port-DPFUj1MP.js.map
2107
+ //# sourceMappingURL=chrome-port-rPi6zrjD.js.map