@monetize.software/sdk-extension 3.0.0-alpha.1 → 3.0.0-alpha.11

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.
Files changed (153) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -20
  3. package/dist/chunks/ar-7cgIM-Vl.js +2 -0
  4. package/dist/chunks/ar-7cgIM-Vl.js.map +1 -0
  5. package/dist/chunks/ar-B2Wg_IrC.js +126 -0
  6. package/dist/chunks/ar-B2Wg_IrC.js.map +1 -0
  7. package/dist/chunks/chrome-port-BXK5myXz.js +2 -0
  8. package/dist/chunks/chrome-port-BXK5myXz.js.map +1 -0
  9. package/dist/chunks/{chrome-port-BXHR4SOG.js → chrome-port-KYVwww_u.js} +376 -216
  10. package/dist/chunks/chrome-port-KYVwww_u.js.map +1 -0
  11. package/dist/chunks/cs-BNo9Dx0Q.js +122 -0
  12. package/dist/chunks/cs-BNo9Dx0Q.js.map +1 -0
  13. package/dist/chunks/cs-S05PC5AC.js +2 -0
  14. package/dist/chunks/cs-S05PC5AC.js.map +1 -0
  15. package/dist/chunks/da-Bi4zBG14.js +2 -0
  16. package/dist/chunks/da-Bi4zBG14.js.map +1 -0
  17. package/dist/chunks/da-Do9Lq6En.js +122 -0
  18. package/dist/chunks/da-Do9Lq6En.js.map +1 -0
  19. package/dist/chunks/de-C8pDZNvx.js +141 -0
  20. package/dist/chunks/de-C8pDZNvx.js.map +1 -0
  21. package/dist/chunks/de-nCDB6D2W.js +2 -0
  22. package/dist/chunks/de-nCDB6D2W.js.map +1 -0
  23. package/dist/chunks/el-BrKaa978.js +2 -0
  24. package/dist/chunks/el-BrKaa978.js.map +1 -0
  25. package/dist/chunks/el-DzMNX-_P.js +126 -0
  26. package/dist/chunks/el-DzMNX-_P.js.map +1 -0
  27. package/dist/chunks/es-B-Wtyzrl.js +2 -0
  28. package/dist/chunks/es-B-Wtyzrl.js.map +1 -0
  29. package/dist/chunks/es-YrKt-q4w.js +141 -0
  30. package/dist/chunks/es-YrKt-q4w.js.map +1 -0
  31. package/dist/chunks/fi-Bh44pwZ4.js +122 -0
  32. package/dist/chunks/fi-Bh44pwZ4.js.map +1 -0
  33. package/dist/chunks/fi-D1SGXjnO.js +2 -0
  34. package/dist/chunks/fi-D1SGXjnO.js.map +1 -0
  35. package/dist/chunks/fr-Bc0pw4ws.js +141 -0
  36. package/dist/chunks/fr-Bc0pw4ws.js.map +1 -0
  37. package/dist/chunks/fr-BhYf-iKk.js +2 -0
  38. package/dist/chunks/fr-BhYf-iKk.js.map +1 -0
  39. package/dist/chunks/he-BXAaFv6Y.js +2 -0
  40. package/dist/chunks/he-BXAaFv6Y.js.map +1 -0
  41. package/dist/chunks/he-Bfm-bhe3.js +126 -0
  42. package/dist/chunks/he-Bfm-bhe3.js.map +1 -0
  43. package/dist/chunks/hi-D-O-B9Dn.js +126 -0
  44. package/dist/chunks/hi-D-O-B9Dn.js.map +1 -0
  45. package/dist/chunks/hi-xblDO0O7.js +2 -0
  46. package/dist/chunks/hi-xblDO0O7.js.map +1 -0
  47. package/dist/chunks/hu-CmIuAbLL.js +122 -0
  48. package/dist/chunks/hu-CmIuAbLL.js.map +1 -0
  49. package/dist/chunks/hu-Wa46p0y4.js +2 -0
  50. package/dist/chunks/hu-Wa46p0y4.js.map +1 -0
  51. package/dist/chunks/id-CQEo5X94.js +2 -0
  52. package/dist/chunks/id-CQEo5X94.js.map +1 -0
  53. package/dist/chunks/id-DN7IES-A.js +122 -0
  54. package/dist/chunks/id-DN7IES-A.js.map +1 -0
  55. package/dist/chunks/it-8AYCm0xz.js +2 -0
  56. package/dist/chunks/it-8AYCm0xz.js.map +1 -0
  57. package/dist/chunks/it-Cz5Nmqx5.js +141 -0
  58. package/dist/chunks/it-Cz5Nmqx5.js.map +1 -0
  59. package/dist/chunks/ja-BH9BlBh2.js +145 -0
  60. package/dist/chunks/ja-BH9BlBh2.js.map +1 -0
  61. package/dist/chunks/ja-q-COVayn.js +2 -0
  62. package/dist/chunks/ja-q-COVayn.js.map +1 -0
  63. package/dist/chunks/ko-B6HRCscZ.js +2 -0
  64. package/dist/chunks/ko-B6HRCscZ.js.map +1 -0
  65. package/dist/chunks/ko-CYV9QuYs.js +145 -0
  66. package/dist/chunks/ko-CYV9QuYs.js.map +1 -0
  67. package/dist/chunks/nl-BvkB900D.js +141 -0
  68. package/dist/chunks/nl-BvkB900D.js.map +1 -0
  69. package/dist/chunks/nl-CAd6_xlm.js +2 -0
  70. package/dist/chunks/nl-CAd6_xlm.js.map +1 -0
  71. package/dist/chunks/no-3s9_ormb.js +122 -0
  72. package/dist/chunks/no-3s9_ormb.js.map +1 -0
  73. package/dist/chunks/no-CAmz6bz6.js +2 -0
  74. package/dist/chunks/no-CAmz6bz6.js.map +1 -0
  75. package/dist/chunks/pl-C9WTGQtb.js +122 -0
  76. package/dist/chunks/pl-C9WTGQtb.js.map +1 -0
  77. package/dist/chunks/pl-DqUSTCaF.js +2 -0
  78. package/dist/chunks/pl-DqUSTCaF.js.map +1 -0
  79. package/dist/chunks/port-name-CF4WQQ3-.js +2 -0
  80. package/dist/chunks/port-name-CF4WQQ3-.js.map +1 -0
  81. package/dist/chunks/port-name-ervLBWAQ.js +6 -0
  82. package/dist/chunks/port-name-ervLBWAQ.js.map +1 -0
  83. package/dist/chunks/pt-8ARZnH0_.js +2 -0
  84. package/dist/chunks/pt-8ARZnH0_.js.map +1 -0
  85. package/dist/chunks/pt-uFVUv_Op.js +141 -0
  86. package/dist/chunks/pt-uFVUv_Op.js.map +1 -0
  87. package/dist/chunks/ro-BrqQ8Au-.js +122 -0
  88. package/dist/chunks/ro-BrqQ8Au-.js.map +1 -0
  89. package/dist/chunks/ro-D-NMbp2F.js +2 -0
  90. package/dist/chunks/ro-D-NMbp2F.js.map +1 -0
  91. package/dist/chunks/ru-8gbHPh0g.js +2 -0
  92. package/dist/chunks/ru-8gbHPh0g.js.map +1 -0
  93. package/dist/chunks/ru-DK594dA8.js +144 -0
  94. package/dist/chunks/ru-DK594dA8.js.map +1 -0
  95. package/dist/chunks/sv-CHNH8-mq.js +122 -0
  96. package/dist/chunks/sv-CHNH8-mq.js.map +1 -0
  97. package/dist/chunks/sv-D8a8hmx9.js +2 -0
  98. package/dist/chunks/sv-D8a8hmx9.js.map +1 -0
  99. package/dist/chunks/th-DfjUK0Y7.js +2 -0
  100. package/dist/chunks/th-DfjUK0Y7.js.map +1 -0
  101. package/dist/chunks/th-l24Pm5q-.js +126 -0
  102. package/dist/chunks/th-l24Pm5q-.js.map +1 -0
  103. package/dist/chunks/tr-ADpigSY5.js +122 -0
  104. package/dist/chunks/tr-ADpigSY5.js.map +1 -0
  105. package/dist/chunks/tr-BdBpz4tL.js +2 -0
  106. package/dist/chunks/tr-BdBpz4tL.js.map +1 -0
  107. package/dist/chunks/uk-CGqo4jek.js +144 -0
  108. package/dist/chunks/uk-CGqo4jek.js.map +1 -0
  109. package/dist/chunks/uk-Cx1zv1ao.js +2 -0
  110. package/dist/chunks/uk-Cx1zv1ao.js.map +1 -0
  111. package/dist/chunks/vi-Dk9bTu6f.js +122 -0
  112. package/dist/chunks/vi-Dk9bTu6f.js.map +1 -0
  113. package/dist/chunks/vi-oe2dW21I.js +2 -0
  114. package/dist/chunks/vi-oe2dW21I.js.map +1 -0
  115. package/dist/chunks/zh-CwczPMPp.js +2 -0
  116. package/dist/chunks/zh-CwczPMPp.js.map +1 -0
  117. package/dist/chunks/zh-LDkEV2D9.js +145 -0
  118. package/dist/chunks/zh-LDkEV2D9.js.map +1 -0
  119. package/dist/content/RemoteAuthClient.d.ts +8 -4
  120. package/dist/content/RemoteAuthClient.d.ts.map +1 -1
  121. package/dist/content/RemoteAuthClient.test-d.d.ts +2 -0
  122. package/dist/content/RemoteAuthClient.test-d.d.ts.map +1 -0
  123. package/dist/content/RemoteBillingClient.d.ts +34 -1
  124. package/dist/content/RemoteBillingClient.d.ts.map +1 -1
  125. package/dist/content/RemoteBillingClient.test-d.d.ts +2 -0
  126. package/dist/content/RemoteBillingClient.test-d.d.ts.map +1 -0
  127. package/dist/content.cjs +3 -3
  128. package/dist/content.cjs.map +1 -1
  129. package/dist/content.js +2356 -1221
  130. package/dist/content.js.map +1 -1
  131. package/dist/offscreen/server.d.ts.map +1 -1
  132. package/dist/offscreen.cjs +1 -1
  133. package/dist/offscreen.cjs.map +1 -1
  134. package/dist/offscreen.js +18 -15
  135. package/dist/offscreen.js.map +1 -1
  136. package/dist/shared/messages.d.ts +27 -4
  137. package/dist/shared/messages.d.ts.map +1 -1
  138. package/dist/shared/port-name.d.ts +1 -0
  139. package/dist/shared/port-name.d.ts.map +1 -1
  140. package/dist/shared/protocol.d.ts +1 -1
  141. package/dist/shared/protocol.d.ts.map +1 -1
  142. package/dist/sw.cjs +1 -1
  143. package/dist/sw.cjs.map +1 -1
  144. package/dist/sw.js +14 -14
  145. package/dist/sw.js.map +1 -1
  146. package/package.json +39 -21
  147. package/dist/chunks/chrome-port-BXHR4SOG.js.map +0 -1
  148. package/dist/chunks/chrome-port-EtYqHf3p.js +0 -2
  149. package/dist/chunks/chrome-port-EtYqHf3p.js.map +0 -1
  150. package/dist/chunks/port-name-BPfQKtdb.js +0 -5
  151. package/dist/chunks/port-name-BPfQKtdb.js.map +0 -1
  152. package/dist/chunks/port-name-qwB109u9.js +0 -2
  153. package/dist/chunks/port-name-qwB109u9.js.map +0 -1
@@ -3,47 +3,47 @@ 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
10
10
  }), this.name = "QuotaExceededError", this.balances = t.balances, this.queryType = t.queryType, this.currentBalance = t.currentBalance;
11
11
  }
12
12
  }
13
- const w = "3.0.0-alpha.0";
14
- class L {
13
+ const m = "3.0.0-alpha.0";
14
+ class K {
15
15
  constructor(t) {
16
16
  this.opts = t;
17
17
  }
18
18
  async request(t, e = {}) {
19
19
  const s = new URL(t, this.opts.apiOrigin).toString(), a = this.opts.fetch ?? fetch, n = new Headers(e.headers);
20
- n.set("Accept", "application/json"), n.set("X-SDK-Version", w), n.set("X-Paywall-Id", this.opts.paywallId), this.opts.capabilities?.length && n.set("X-SDK-Capabilities", this.opts.capabilities.join(","));
20
+ n.set("Accept", "application/json"), n.set("X-SDK-Version", m), n.set("X-Paywall-Id", this.opts.paywallId), this.opts.capabilities?.length && n.set("X-SDK-Capabilities", this.opts.capabilities.join(","));
21
21
  const r = await this.opts.getAuthToken?.();
22
22
  r && n.set("Authorization", `Bearer ${r}`);
23
- const l = typeof FormData < "u" && e.body instanceof FormData;
24
- e.body && !n.has("Content-Type") && !l && n.set("Content-Type", "application/json");
25
- let u;
23
+ const h = typeof FormData < "u" && e.body instanceof FormData;
24
+ e.body && !n.has("Content-Type") && !h && n.set("Content-Type", "application/json");
25
+ let d;
26
26
  try {
27
- u = await a(s, {
27
+ d = await a(s, {
28
28
  ...e,
29
29
  headers: n,
30
30
  credentials: "omit"
31
31
  });
32
- } catch (h) {
33
- throw (h && typeof h == "object" && "name" in h ? h.name : void 0) === "AbortError" ? new o("aborted", "Request aborted", { cause: h }) : new o("network_error", "Network request failed", { cause: h });
32
+ } catch (l) {
33
+ throw (l && typeof l == "object" && "name" in l ? l.name : void 0) === "AbortError" ? new o("aborted", "Request aborted", { cause: l }) : new o("network_error", "Network request failed", { cause: l });
34
34
  }
35
- const f = (u.headers.get("content-type") ?? "").includes("application/json") ? await u.json().catch(() => null) : null;
36
- if (!u.ok) {
37
- const h = f && typeof f == "object" && "code" in f && String(f.code) || `http_${u.status}`, g = f && typeof f == "object" && "message" in f && String(f.message) || u.statusText || "Request failed";
38
- throw new o(h, g, { status: u.status, cause: f });
35
+ const y = (d.headers.get("content-type") ?? "").includes("application/json") ? await d.json().catch(() => null) : null;
36
+ if (!d.ok) {
37
+ const l = y && typeof y == "object" && "code" in y && String(y.code) || `http_${d.status}`, g = y && typeof y == "object" && "message" in y && String(y.message) || d.statusText || "Request failed";
38
+ throw new o(l, g, { status: d.status, cause: y });
39
39
  }
40
- return f;
40
+ return y;
41
41
  }
42
42
  }
43
- function $() {
43
+ function x() {
44
44
  return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
45
45
  }
46
- const D = {
46
+ const H = {
47
47
  getItem(i) {
48
48
  return new Promise((t) => {
49
49
  chrome.storage.local.get([i], (e) => {
@@ -73,7 +73,7 @@ const D = {
73
73
  };
74
74
  return e.addListener(s), () => e.removeListener(s);
75
75
  }
76
- }, x = {
76
+ }, J = {
77
77
  async getItem(i) {
78
78
  try {
79
79
  return window.localStorage.getItem(i);
@@ -101,21 +101,21 @@ const D = {
101
101
  };
102
102
  return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
103
103
  }
104
- }, m = /* @__PURE__ */ new Map(), J = {
104
+ }, I = /* @__PURE__ */ new Map(), V = {
105
105
  async getItem(i) {
106
- return m.get(i) ?? null;
106
+ return I.get(i) ?? null;
107
107
  },
108
108
  async setItem(i, t) {
109
- m.set(i, t);
109
+ I.set(i, t);
110
110
  },
111
111
  async removeItem(i) {
112
- m.delete(i);
112
+ I.delete(i);
113
113
  }
114
114
  };
115
115
  function P(i) {
116
- return i || ($() ? D : typeof window < "u" && "localStorage" in window ? x : J);
116
+ return i || (x() ? H : typeof window < "u" && "localStorage" in window ? J : V);
117
117
  }
118
- const y = {
118
+ const u = {
119
119
  visitorId: "pw-visitor-id",
120
120
  lastLoginMethod: (i) => `pw-${i}-last-login-method`,
121
121
  lastLoginEmail: (i) => `pw-${i}-last-login-email`,
@@ -157,20 +157,20 @@ function R() {
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 _(i) {
160
+ async function B(i) {
161
161
  try {
162
- const e = await i.getItem(y.visitorId);
162
+ const e = await i.getItem(u.visitorId);
163
163
  if (e && typeof e == "string" && e.length >= 16) return e;
164
164
  } catch {
165
165
  }
166
166
  const t = R();
167
167
  try {
168
- await i.setItem(y.visitorId, t);
168
+ await i.setItem(u.visitorId, t);
169
169
  } catch {
170
170
  }
171
171
  return t;
172
172
  }
173
- function q(i) {
173
+ function D(i) {
174
174
  const t = new Uint8Array(i), e = typeof globalThis < "u" ? globalThis.crypto : void 0;
175
175
  if (e && typeof e.getRandomValues == "function")
176
176
  e.getRandomValues(t);
@@ -178,30 +178,35 @@ function q(i) {
178
178
  for (let s = 0; s < i; s++) t[s] = Math.floor(Math.random() * 256);
179
179
  return t;
180
180
  }
181
- function v(i) {
181
+ function _(i) {
182
182
  let t = "";
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 H() {
187
- return v(q(64));
186
+ function j() {
187
+ return _(D(64));
188
188
  }
189
- async function V(i) {
189
+ async function X(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");
193
193
  const s = await e.subtle.digest("SHA-256", t);
194
- return v(new Uint8Array(s));
194
+ return _(new Uint8Array(s));
195
195
  }
196
- function j() {
197
- return v(q(16));
196
+ function G() {
197
+ return _(D(16));
198
198
  }
199
- const X = "https://appbox.space", z = 6e4, G = 600 * 1e3;
199
+ const z = 6e4, Q = 600 * 1e3;
200
200
  class mt {
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 ?? X, this.storage = P(t.storage), this.api = new L({
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 = P(t.storage), this.api = new K({
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" }), this.recordLastLogin("email", t.email), n;
295
301
  }
296
302
  /**
297
303
  * Signup. Если в Supabase включён email confirm — сервер возвращает
@@ -317,9 +323,9 @@ class mt {
317
323
  }
318
324
  );
319
325
  if (a.status === "confirmation_required")
320
- return { kind: "confirmation_required", user: a.user };
326
+ return this.recordLastLogin("email", t.email), { 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" }), this.recordLastLogin("email", t.email), { 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.
@@ -442,10 +448,8 @@ class mt {
442
448
  * когда сервер начнёт возвращать challenge_required в риск-сценариях,
443
449
  * SDK сможет передать proof-of-something обратно без breaking change.
444
450
  *
445
- * `forceCaptcha: true` пропускает шаги 1-2 и сразу делает /signin (создаёт
446
- * нового anon-юзера). Используется в switch-account flow. Имя поля исторически
447
- * остаётся `forceCaptcha`, хотя капчи там больше нет — менять имя ломает
448
- * host-сигнатуру; смысл «принудительно новая anon-сессия» сохранён.
451
+ * `forceNewAnon: true` пропускает шаги 1-2 и сразу делает /signin (создаёт
452
+ * нового anon-юзера). Используется в switch-account flow.
449
453
  *
450
454
  * Параллельные вызовы дедуплицируются через `inflightAnonSignin` — два
451
455
  * click'а на «Войти как гость» не создадут двух anon-юзеров (два /signup =
@@ -454,9 +458,9 @@ class mt {
454
458
  async signInAnonymously(t = {}) {
455
459
  if (this.inflightAnonSignin) return this.inflightAnonSignin;
456
460
  this.inflightAnonSignin = (async () => {
457
- if (await this.hydrated, !t.forceCaptcha && this.session?.user.is_anonymous === !0)
461
+ if (await this.hydrated, !t.forceNewAnon && this.session?.user.is_anonymous === !0)
458
462
  return this.session;
459
- if (!t.forceCaptcha) {
463
+ if (!t.forceNewAnon) {
460
464
  const r = await this.resumeAnonymous();
461
465
  if (r) return r;
462
466
  }
@@ -475,7 +479,7 @@ class mt {
475
479
  email: s.user.email ?? null,
476
480
  is_anonymous: !0
477
481
  }, n = this.toSession(s, a);
478
- return this.setSession(n), await this.writeAnonRefreshToken(n.refresh_token), n;
482
+ return this.setSession(n, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(n.refresh_token), n;
479
483
  })();
480
484
  try {
481
485
  return await this.inflightAnonSignin;
@@ -497,7 +501,7 @@ class mt {
497
501
  `/api/v1/paywall/${this.paywallId}/auth/refresh`,
498
502
  { method: "POST", body: JSON.stringify({ refresh_token: t }) }
499
503
  ), 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;
504
+ return this.setSession(a, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(a.refresh_token), a;
501
505
  } catch (e) {
502
506
  if (e instanceof o && e.status === 401)
503
507
  return await this.clearAnonRefreshToken(), null;
@@ -559,8 +563,8 @@ class mt {
559
563
  id: a.user.id,
560
564
  email: a.user.email,
561
565
  is_anonymous: a.user.is_anonymous ?? !1
562
- }, l = { ...n, user: r };
563
- return this.setSession(l), await this.clearAnonRefreshToken(), { kind: "updated", session: l };
566
+ }, h = { ...n, user: r };
567
+ return this.setSession(h, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: h };
564
568
  }
565
569
  /**
566
570
  * OAuth signin через popup с PKCE. Жизненный цикл:
@@ -596,7 +600,7 @@ class mt {
596
600
  "browser blocked auth popup — call from a user gesture"
597
601
  );
598
602
  t.onPopupOpened?.();
599
- const n = await Y(a, s);
603
+ const n = await Z(a, s);
600
604
  if (this.destroyed)
601
605
  throw this.oauthFlows.delete(s), new o("aborted", "AuthClient destroyed mid-flow");
602
606
  return this.completeOAuthFlow({ state: s, code: n });
@@ -617,9 +621,9 @@ class mt {
617
621
  */
618
622
  async startOAuthFlow(t) {
619
623
  await this.hydrated, this.gcOAuthFlows();
620
- const e = H(), s = await V(e), a = j(), n = {}, r = await this.getAccessToken().catch(() => null);
624
+ const e = j(), s = await X(e), a = G(), n = {}, r = await this.getAccessToken().catch(() => null);
621
625
  r && (n.Authorization = `Bearer ${r}`);
622
- const { authorize_url: l } = await this.api.request(
626
+ const { authorize_url: h } = await this.api.request(
623
627
  `/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
624
628
  {
625
629
  method: "POST",
@@ -635,8 +639,9 @@ class mt {
635
639
  return this.oauthFlows.set(a, {
636
640
  verifier: e,
637
641
  userMeta: t.userMeta,
642
+ provider: t.provider,
638
643
  startedAt: Date.now()
639
- }), { authorize_url: l, state: a };
644
+ }), this.recordLastLoginMethod(t.provider), { authorize_url: h, state: a };
640
645
  }
641
646
  /**
642
647
  * Шаг 2 OAuth split-API: обменивает code (полученный из popup) на session,
@@ -671,10 +676,10 @@ class mt {
671
676
  if (this.destroyed)
672
677
  throw new o("aborted", "AuthClient destroyed mid-flow");
673
678
  const n = this.toSession(a, a.user);
674
- return this.setSession(n), n;
679
+ return this.setSession(n, { event: "SIGNED_IN" }), n.user.email && this.recordLastLoginEmail(n.user.email), n;
675
680
  }
676
681
  gcOAuthFlows() {
677
- const t = Date.now() - G;
682
+ const t = Date.now() - Q;
678
683
  for (const [e, s] of this.oauthFlows)
679
684
  s.startedAt < t && this.oauthFlows.delete(e);
680
685
  }
@@ -700,10 +705,10 @@ class mt {
700
705
  body: JSON.stringify({ refresh_token: t })
701
706
  }
702
707
  ), a = this.toSession(s, e);
703
- return this.setSession(a), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
708
+ return this.setSession(a, { event: "TOKEN_REFRESHED" }), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
704
709
  } catch (s) {
705
710
  if (s instanceof o && s.status === 401)
706
- return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null), null;
711
+ return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null, { event: "SIGNED_OUT" }), null;
707
712
  throw s;
708
713
  } finally {
709
714
  this.inflightRefresh = null;
@@ -736,7 +741,7 @@ class mt {
736
741
  method: "POST",
737
742
  headers: { Authorization: `Bearer ${t}` }
738
743
  }
739
- ), this.setSession(null);
744
+ ), this.setSession(null, { event: "SIGNED_OUT" });
740
745
  }
741
746
  /**
742
747
  * Signout: чистит локальную session СРАЗУ (UX — мгновенный logout без
@@ -756,7 +761,7 @@ class mt {
756
761
  async signOut(t = {}) {
757
762
  await this.hydrated;
758
763
  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))
764
+ if (this.setSession(null, { event: "SIGNED_OUT" }), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
760
765
  try {
761
766
  await this.api.request(
762
767
  `/api/v1/paywall/${this.paywallId}/auth/signout`,
@@ -770,17 +775,30 @@ class mt {
770
775
  }
771
776
  /**
772
777
  * Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
773
- * Колбек вызывается с текущим snapshot через microtask (если session есть)
774
- * + на каждое реальное изменение. Возвращает unsubscribe.
778
+ *
779
+ * Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
780
+ * `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
781
+ * (даже если session=null — listener получает explicit «нет сессии», а не
782
+ * молчание). Все последующие callback'и — реальные переходы с конкретным
783
+ * event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
784
+ * PASSWORD_RECOVERY).
785
+ *
786
+ * Это позволяет listener'у безопасно делать «only on real signin» побочные
787
+ * эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
788
+ * не путая их с восстановлением из storage.
789
+ *
790
+ * Возвращает unsubscribe.
775
791
  */
776
792
  onAuthChange(t) {
777
- if (this.listeners.add(t), this.session) {
793
+ return this.listeners.add(t), this.hydrated.then(() => {
794
+ if (this.destroyed || !this.listeners.has(t)) return;
778
795
  const e = this.session;
779
- queueMicrotask(() => {
780
- this.listeners.has(t) && t(e);
781
- });
782
- }
783
- return () => {
796
+ try {
797
+ t("INITIAL_SESSION", e);
798
+ } catch (s) {
799
+ console.warn("[paywall] onAuthChange INITIAL_SESSION threw", s);
800
+ }
801
+ }), () => {
784
802
  this.listeners.delete(t);
785
803
  };
786
804
  }
@@ -796,21 +814,21 @@ class mt {
796
814
  user: e
797
815
  };
798
816
  }
799
- setSession(t, e = {}) {
817
+ setSession(t, e) {
800
818
  if (this.destroyed) return;
801
819
  const s = this.session;
802
- this.session = t, e.skipPersist || this.persist(), Z(s, t) || this.emit();
820
+ this.session = t, e.skipPersist || this.persist(), et(s, t) || this.emit(e.event);
803
821
  }
804
- emit() {
805
- for (const t of this.listeners)
822
+ emit(t) {
823
+ for (const e of this.listeners)
806
824
  try {
807
- t(this.session);
808
- } catch (e) {
809
- console.warn("[paywall] onAuthChange listener threw", e);
825
+ e(t, this.session);
826
+ } catch (s) {
827
+ console.warn("[paywall] onAuthChange listener threw", s);
810
828
  }
811
829
  }
812
830
  storageKey() {
813
- return y.authSession(this.paywallId);
831
+ return u.authSession(this.paywallId);
814
832
  }
815
833
  async hydrate() {
816
834
  try {
@@ -819,7 +837,7 @@ class mt {
819
837
  const e = JSON.parse(t);
820
838
  if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
821
839
  return;
822
- this.session = e, this.emit();
840
+ this.session = e;
823
841
  } catch {
824
842
  }
825
843
  }
@@ -833,7 +851,7 @@ class mt {
833
851
  const e = JSON.parse(t);
834
852
  if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
835
853
  return;
836
- this.setSession(e, { skipPersist: !0 });
854
+ this.setSession(e, { skipPersist: !0, event: "SIGNED_IN" });
837
855
  } catch {
838
856
  }
839
857
  }
@@ -864,7 +882,7 @@ class mt {
864
882
  }
865
883
  async readAnonRefreshToken() {
866
884
  try {
867
- const t = await this.storage.getItem(y.anonRefreshToken(this.paywallId));
885
+ const t = await this.storage.getItem(u.anonRefreshToken(this.paywallId));
868
886
  return typeof t == "string" && t.length > 0 ? t : null;
869
887
  } catch {
870
888
  return null;
@@ -873,7 +891,7 @@ class mt {
873
891
  async writeAnonRefreshToken(t) {
874
892
  try {
875
893
  await this.storage.setItem(
876
- y.anonRefreshToken(this.paywallId),
894
+ u.anonRefreshToken(this.paywallId),
877
895
  t
878
896
  );
879
897
  } catch {
@@ -882,11 +900,41 @@ class mt {
882
900
  async clearAnonRefreshToken() {
883
901
  try {
884
902
  await this.storage.removeItem(
885
- y.anonRefreshToken(this.paywallId)
903
+ u.anonRefreshToken(this.paywallId)
886
904
  );
887
905
  } catch {
888
906
  }
889
907
  }
908
+ /**
909
+ * Last-used auth method + email — для UI бейджа "Last used" и pre-fill'а
910
+ * email-инпута. Storage paywall-scoped, поэтому переключение между
911
+ * пейволами на одном host'е не пересекает данные. Чтение всегда возвращает
912
+ * объект — отсутствующие поля = null. */
913
+ async getLastLogin() {
914
+ try {
915
+ const [t, e] = await Promise.all([
916
+ this.storage.getItem(u.lastLoginMethod(this.paywallId)),
917
+ this.storage.getItem(u.lastLoginEmail(this.paywallId))
918
+ ]);
919
+ return !t || !tt(t) ? null : { method: t, email: typeof e == "string" && e ? e : null };
920
+ } catch {
921
+ return null;
922
+ }
923
+ }
924
+ /** Запись method и email атомарно (для email/password flows — оба известны
925
+ * на момент signin/signup'а). OAuth-flows используют раздельные
926
+ * recordLastLoginMethod (до popup) и recordLastLoginEmail (после exchange). */
927
+ recordLastLogin(t, e) {
928
+ this.recordLastLoginMethod(t), e && this.recordLastLoginEmail(e);
929
+ }
930
+ recordLastLoginMethod(t) {
931
+ this.storage.setItem(u.lastLoginMethod(this.paywallId), t).catch(() => {
932
+ });
933
+ }
934
+ recordLastLoginEmail(t) {
935
+ this.storage.setItem(u.lastLoginEmail(this.paywallId), t).catch(() => {
936
+ });
937
+ }
890
938
  /**
891
939
  * Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
892
940
  * AuthClient может быть инстанцирован раньше BillingClient, а синтетический
@@ -896,22 +944,22 @@ class mt {
896
944
  */
897
945
  async readVisitorId() {
898
946
  try {
899
- const t = await this.storage.getItem(y.visitorId);
947
+ const t = await this.storage.getItem(u.visitorId);
900
948
  return typeof t == "string" && t.length >= 16 ? t : void 0;
901
949
  } catch {
902
950
  return;
903
951
  }
904
952
  }
905
953
  }
906
- const Q = 5 * 6e4, W = 500;
907
- function Y(i, t) {
954
+ const W = 5 * 6e4, Y = 500;
955
+ function Z(i, t) {
908
956
  return new Promise((e, s) => {
909
957
  let a = !1;
910
958
  const n = () => {
911
- a = !0, window.removeEventListener("message", r), clearInterval(l), clearTimeout(u);
912
- }, r = (d) => {
959
+ a = !0, window.removeEventListener("message", r), clearInterval(h), clearTimeout(d);
960
+ }, r = (f) => {
913
961
  if (a) return;
914
- const c = d.data;
962
+ const c = f.data;
915
963
  if (!(!c || c.type !== "pw-oauth") && c.messageId === t) {
916
964
  if (c.status === "success" && c.code) {
917
965
  n();
@@ -934,16 +982,16 @@ function Y(i, t) {
934
982
  );
935
983
  }
936
984
  }
937
- }, l = setInterval(() => {
985
+ }, h = setInterval(() => {
938
986
  if (a) return;
939
- let d;
987
+ let f;
940
988
  try {
941
- d = i.closed;
989
+ f = i.closed;
942
990
  } catch {
943
991
  return;
944
992
  }
945
- d && (n(), s(new o("oauth_cancelled", "auth popup was closed")));
946
- }, W), u = setTimeout(() => {
993
+ f && (n(), s(new o("oauth_cancelled", "auth popup was closed")));
994
+ }, Y), d = setTimeout(() => {
947
995
  if (!a) {
948
996
  n();
949
997
  try {
@@ -952,19 +1000,26 @@ function Y(i, t) {
952
1000
  }
953
1001
  s(new o("oauth_timeout", "OAuth flow timed out"));
954
1002
  }
955
- }, Q);
1003
+ }, W);
956
1004
  window.addEventListener("message", r);
957
1005
  });
958
1006
  }
959
- function Z(i, t) {
1007
+ function tt(i) {
1008
+ return i === "google" || i === "apple" || i === "github" || i === "facebook" || i === "email";
1009
+ }
1010
+ function et(i, t) {
960
1011
  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
1012
  }
962
- const tt = "https://appbox.space";
963
- class et {
1013
+ class st {
964
1014
  constructor(t) {
965
1015
  if (!t.paywallId)
966
1016
  throw new o("invalid_config", "paywallId is required");
967
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? tt, 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(
1017
+ if (!t.apiOrigin)
1018
+ throw new o(
1019
+ "invalid_config",
1020
+ "apiOrigin is required. Pass the paywall custom_domain configured in the platform."
1021
+ );
1022
+ 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
1023
  "[paywall] WARNING: ApiGatewayClient.userId set without auth in browser. Client can spoof userId. Use AuthClient + Bearer for trusted user.id."
969
1024
  );
970
1025
  }
@@ -975,16 +1030,16 @@ class et {
975
1030
  );
976
1031
  s.searchParams.set("paywall_id", this.paywallId);
977
1032
  const a = new Headers(t.headers);
978
- a.set("X-SDK-Version", w), a.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && a.set("X-SDK-Capabilities", this.capabilities.join(","));
1033
+ a.set("X-SDK-Version", m), a.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && a.set("X-SDK-Capabilities", this.capabilities.join(","));
979
1034
  const n = await this.auth?.getAccessToken();
980
1035
  n ? a.set("Authorization", `Bearer ${n}`) : this.userId && a.set("X-User-ID", this.userId);
981
- const r = typeof FormData < "u" && t.body instanceof FormData, l = typeof Blob < "u" && t.body instanceof Blob, u = typeof ReadableStream < "u" && t.body instanceof ReadableStream, d = typeof t.body == "string";
1036
+ const r = typeof FormData < "u" && t.body instanceof FormData, h = typeof Blob < "u" && t.body instanceof Blob, d = typeof ReadableStream < "u" && t.body instanceof ReadableStream, f = typeof t.body == "string";
982
1037
  let c;
983
- t.body === void 0 || t.body === null ? c = void 0 : r || l || u || d ? c = t.body : (c = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
984
- const f = this.customFetch ?? fetch;
985
- let h;
1038
+ t.body === void 0 || t.body === null ? c = void 0 : r || h || d || f ? c = t.body : (c = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
1039
+ const y = this.customFetch ?? fetch;
1040
+ let l;
986
1041
  try {
987
- h = await f(s.toString(), {
1042
+ l = await y(s.toString(), {
988
1043
  method: t.method ?? "POST",
989
1044
  headers: a,
990
1045
  body: c,
@@ -992,26 +1047,26 @@ class et {
992
1047
  credentials: "omit"
993
1048
  });
994
1049
  } catch (p) {
995
- const K = p instanceof Error ? p.message : String(p);
996
- throw new o("network_error", `Network request failed: ${K}`, { cause: p });
1050
+ const $ = p instanceof Error ? p.message : String(p);
1051
+ throw new o("network_error", `Network request failed: ${$}`, { cause: p });
997
1052
  }
998
- if (h.status === 402) {
999
- const p = await st(h);
1053
+ if (l.status === 402) {
1054
+ const p = await it(l);
1000
1055
  throw this.onQuotaExceeded?.(p), p;
1001
1056
  }
1002
- if (!h.ok) {
1003
- const p = await it(h.clone());
1057
+ if (!l.ok) {
1058
+ const p = await at(l.clone());
1004
1059
  throw new o(
1005
- p ?? `http_${h.status}`,
1006
- h.statusText || "Gateway request failed",
1007
- { status: h.status }
1060
+ p ?? `http_${l.status}`,
1061
+ l.statusText || "Gateway request failed",
1062
+ { status: l.status }
1008
1063
  );
1009
1064
  }
1010
- const g = h.headers.get("X-Query-Type") ?? void 0;
1011
- return this.onChargeSuccess?.(g), h;
1065
+ const g = l.headers.get("X-Query-Type") ?? void 0;
1066
+ return this.onChargeSuccess?.(g), l;
1012
1067
  }
1013
1068
  }
1014
- async function st(i) {
1069
+ async function it(i) {
1015
1070
  let t = {};
1016
1071
  try {
1017
1072
  t = await i.json();
@@ -1023,13 +1078,13 @@ async function st(i) {
1023
1078
  const a = e[0];
1024
1079
  Array.isArray(a) ? s = a : a && Array.isArray(a.balances) && (s = a.balances);
1025
1080
  }
1026
- return new N({
1081
+ return new F({
1027
1082
  balances: s,
1028
1083
  queryType: t.details?.queryType ?? "",
1029
1084
  currentBalance: t.details?.currentBalance ?? null
1030
1085
  });
1031
1086
  }
1032
- async function it(i) {
1087
+ async function at(i) {
1033
1088
  if (!(i.headers.get("content-type") ?? "").includes("application/json")) return null;
1034
1089
  try {
1035
1090
  const e = await i.json();
@@ -1038,35 +1093,40 @@ async function it(i) {
1038
1093
  return null;
1039
1094
  }
1040
1095
  }
1041
- const at = 5e3, nt = 30 * 6e4, B = 60 * 6e4, rt = 5 * 6e4, k = {
1096
+ const nt = 5e3, rt = 30 * 6e4, k = 60 * 6e4, ot = 5 * 6e4, S = {
1042
1097
  has_active_subscription: !1,
1043
1098
  purchases: [],
1044
- trial: null
1099
+ trial: null,
1100
+ had_previous_trial: !1
1045
1101
  };
1046
1102
  function A(i) {
1047
1103
  return i && (i.email || i.userId || i.anonymousId) || "guest";
1048
1104
  }
1049
- function ot(i, t) {
1105
+ function ct(i, t) {
1050
1106
  return i === t ? !0 : !i || !t ? !1 : JSON.stringify(i) === JSON.stringify(t);
1051
1107
  }
1052
- const ct = 5e3, T = 5 * 6e4, ht = 3e4;
1053
- function lt(i, t) {
1108
+ const ht = 5e3, T = 5 * 6e4, lt = 3e4;
1109
+ function ut(i, t) {
1054
1110
  if (i === t) return !0;
1055
1111
  if (!i || !t || i.length !== t.length) return !1;
1056
1112
  for (let e = 0; e < i.length; e++)
1057
1113
  if (i[e].type !== t[e].type || i[e].count !== t[e].count) return !1;
1058
1114
  return !0;
1059
1115
  }
1060
- const ut = "https://appbox.space";
1061
- class bt {
1116
+ class It {
1062
1117
  constructor(t) {
1063
- 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(), !t.paywallId)
1118
+ 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
1119
  throw new o("invalid_config", "paywallId is required");
1065
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? ut, this.capabilities = t.capabilities, this.auth = t.auth;
1120
+ if (!t.apiOrigin)
1121
+ throw new o(
1122
+ "invalid_config",
1123
+ '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.'
1124
+ );
1125
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
1066
1126
  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(
1127
+ 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
1128
  "[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
- ), this.storage = P(t.storage), this.api = new L({
1129
+ ), this.storage = P(t.storage), this.api = new K({
1070
1130
  apiOrigin: this.apiOrigin,
1071
1131
  paywallId: t.paywallId,
1072
1132
  capabilities: t.capabilities,
@@ -1075,10 +1135,10 @@ class bt {
1075
1135
  // делает lazy refresh, дедупит, на 401 возвращает null — тогда
1076
1136
  // Authorization-хедер просто не выставится.
1077
1137
  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
- dt(this.identity, a) || this.setIdentity(a);
1081
- })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = _(this.storage).then((s) => (this.visitorId = s, s));
1138
+ }), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s, a) => {
1139
+ const n = a ? O(a.user) : void 0;
1140
+ dt(this.identity, n) || this.setIdentity(n);
1141
+ })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = B(this.storage).then((s) => (this.visitorId = s, s));
1082
1142
  }
1083
1143
  /**
1084
1144
  * Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
@@ -1086,15 +1146,15 @@ class bt {
1086
1146
  * EventTracker'ом для атрибуции аналитики.
1087
1147
  */
1088
1148
  async getVisitorId() {
1089
- return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = _(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
1149
+ return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = B(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
1090
1150
  }
1091
1151
  /** Sync-доступ к visitor_id. null если ещё не зарезолвили (первые ms жизни). */
1092
1152
  getCachedVisitorId() {
1093
1153
  return this.visitorId;
1094
1154
  }
1095
1155
  setIdentity(t) {
1096
- this.identity = t, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.cachedBalances = null, this.cachedBalancesAt = 0, this.inflightBalances = null, this.balancesStorageUnwatch && (this.balancesStorageUnwatch(), this.balancesStorageUnwatch = null), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.hydrateUserFromStorage(), t && this.getUser({ force: !0 }).catch(() => {
1097
- });
1156
+ this.identity = t, this.cachedUser = null, this.cachedUserAt = 0, this.inflightUser = null, this.cachedBalances = null, this.cachedBalancesAt = 0, this.inflightBalances = null, this.balancesStorageUnwatch && (this.balancesStorageUnwatch(), this.balancesStorageUnwatch = null), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.hydrateUserFromStorage(), t ? this.getUser({ force: !0 }).catch(() => {
1157
+ }) : (this.applyUser(S), this.applyBalances([]));
1098
1158
  }
1099
1159
  /**
1100
1160
  * Отписаться от auth-event'ов и сбросить listener'ы. Вызывать когда
@@ -1114,9 +1174,17 @@ class bt {
1114
1174
  return this.storage;
1115
1175
  }
1116
1176
  async bootstrap(t = !1) {
1117
- const e = typeof t == "boolean" ? { force: t } : t, s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < B;
1118
- return !e.force && a ? (s - this.cachedBootstrapAt > rt && this.revalidateBootstrap(e.signal).catch(() => {
1119
- }), this.cachedBootstrap) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
1177
+ const e = typeof t == "boolean" ? { force: t } : t;
1178
+ if (this.previewMode) {
1179
+ if (this.cachedBootstrap) return this.cachedBootstrap;
1180
+ throw new o(
1181
+ "invalid_config",
1182
+ "BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
1183
+ );
1184
+ }
1185
+ const s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < k;
1186
+ return !e.force && a ? (s - this.cachedBootstrapAt > ot && this.revalidateBootstrap(e.signal).catch(() => {
1187
+ }), { ...this.cachedBootstrap, user: this.cachedUser ?? void 0 }) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
1120
1188
  ifVersion: e.force ? void 0 : this.cachedBootstrap?.version,
1121
1189
  signal: e.signal
1122
1190
  }).finally(() => {
@@ -1134,6 +1202,44 @@ class bt {
1134
1202
  this.bootstrapListeners.delete(t);
1135
1203
  };
1136
1204
  }
1205
+ /**
1206
+ * Заменить cachedBootstrap частичными или полными данными и эмитнуть всем
1207
+ * подписчикам. Используется host'ом в preview-mode (редактор админки) для
1208
+ * live-обновления открытой модалки без сетевого revalidate'а.
1209
+ *
1210
+ * Поведение:
1211
+ * - Без `cachedBootstrap` ожидаются как минимум `settings` + `prices` —
1212
+ * иначе PaywallRoot не сможет отрендерить тарифы и упадёт.
1213
+ * - С существующим кешем партиал мёрджится поверх: `settings` глубокий мёрдж
1214
+ * на 1 уровень (поля настроек), массивы `prices`/`offers` перезаписываются.
1215
+ * - Каждый вызов бампит `version` ("preview:<n>"), чтобы applyBootstrap'овая
1216
+ * проверка `versionChanged` всегда срабатывала и listener'ы дёргались.
1217
+ * - Persist в storage НЕ делаем — preview не должен утекать в другие вкладки.
1218
+ *
1219
+ * В non-preview режиме метод доступен, но это редкий путь (например, для
1220
+ * тестов host'а) — production-код должен полагаться на bootstrap() + revalidate.
1221
+ */
1222
+ setBootstrap(t) {
1223
+ const e = this.cachedBootstrap ?? {
1224
+ settings: { id: this.paywallId, name: "" },
1225
+ prices: [],
1226
+ offers: []
1227
+ }, s = {
1228
+ ...e,
1229
+ ...t,
1230
+ settings: t.settings !== void 0 ? { ...e.settings, ...t.settings } : e.settings,
1231
+ prices: t.prices !== void 0 ? t.prices : e.prices,
1232
+ offers: t.offers !== void 0 ? t.offers : e.offers,
1233
+ version: `preview:${++this.previewVersionCounter}`
1234
+ };
1235
+ s.layout || (s.layout = E(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
1236
+ for (const a of this.bootstrapListeners)
1237
+ try {
1238
+ a(s);
1239
+ } catch (n) {
1240
+ console.warn("[paywall] onBootstrapChange listener threw", n);
1241
+ }
1242
+ }
1137
1243
  // Network primitive — единая точка для force-запроса, revalidate'а и
1138
1244
  // первого холодного bootstrap'а. `ifVersion` шлёт server-side short-circuit:
1139
1245
  // если совпала — бэк отвечает `{unchanged: true, version, user}` и мы лишь
@@ -1148,7 +1254,7 @@ class bt {
1148
1254
  if ("unchanged" in a && a.unchanged)
1149
1255
  return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), a.user && this.applyUser(a.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
1150
1256
  const n = a;
1151
- return n.layout || (n.layout = ft(n.settings, n.prices)), b(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
1257
+ return ft(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;
1152
1258
  }
1153
1259
  // Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
1154
1260
  // `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
@@ -1178,11 +1284,11 @@ class bt {
1178
1284
  async hydrateBootstrapFromStorage() {
1179
1285
  if (!this.cachedBootstrap)
1180
1286
  try {
1181
- const t = await this.storage.getItem(y.bootstrap(this.paywallId));
1287
+ const t = await this.storage.getItem(u.bootstrap(this.paywallId));
1182
1288
  if (!t) return;
1183
1289
  const e = JSON.parse(t);
1184
- if (!e?.bootstrap || Date.now() - e.at > B || this.cachedBootstrap) return;
1185
- b(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
1290
+ if (!e?.bootstrap || Date.now() - e.at > k || this.cachedBootstrap) return;
1291
+ w(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
1186
1292
  for (const s of this.bootstrapListeners)
1187
1293
  try {
1188
1294
  s(e.bootstrap);
@@ -1197,7 +1303,7 @@ class bt {
1197
1303
  try {
1198
1304
  const { user: e, ...s } = t;
1199
1305
  await this.storage.setItem(
1200
- y.bootstrap(this.paywallId),
1306
+ u.bootstrap(this.paywallId),
1201
1307
  JSON.stringify({ at: Date.now(), bootstrap: s })
1202
1308
  );
1203
1309
  } catch {
@@ -1208,7 +1314,7 @@ class bt {
1208
1314
  // no-op, всё работает как раньше через сеть.
1209
1315
  subscribeBootstrapStorage() {
1210
1316
  typeof this.storage.watch == "function" && (this.bootstrapStorageUnwatch = this.storage.watch(
1211
- y.bootstrap(this.paywallId),
1317
+ u.bootstrap(this.paywallId),
1212
1318
  (t) => {
1213
1319
  if (t)
1214
1320
  try {
@@ -1218,7 +1324,7 @@ class bt {
1218
1324
  this.cachedBootstrapAt = e.at;
1219
1325
  return;
1220
1326
  }
1221
- b(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
1327
+ w(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
1222
1328
  } catch {
1223
1329
  }
1224
1330
  }
@@ -1246,6 +1352,15 @@ class bt {
1246
1352
  getCachedPrices() {
1247
1353
  return this.cachedBootstrap?.prices ?? null;
1248
1354
  }
1355
+ /** Sync-снимок офферов из последнего bootstrap'а. null = bootstrap ещё не
1356
+ * загружали, пустой массив = бэк отдал пейвол без офферов. Бэк уже
1357
+ * применил серверный таргетинг (target_countries / target_emails /
1358
+ * targeting_mode из offer_settings) — наружу выезжает только то, что
1359
+ * применимо к текущему юзеру. Клиентская сторона остаётся ответственной
1360
+ * за price_id-matching и countdown (см. core/offer.ts → resolveOffer). */
1361
+ getCachedOffers() {
1362
+ return this.cachedBootstrap?.offers ?? null;
1363
+ }
1249
1364
  /**
1250
1365
  * Снимок того, какой язык SDK сейчас считает «языком юзера». Полезно для
1251
1366
  * синхронизации i18n хоста с тем, что фактически показывает пейвол — чтобы
@@ -1277,10 +1392,10 @@ class bt {
1277
1392
  * - Без identity возвращает empty-state (сервер тоже так делает).
1278
1393
  */
1279
1394
  async getUser({ force: t = !1, signal: e } = {}) {
1280
- return !t && this.cachedUser && Date.now() - this.cachedUserAt < at ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
1395
+ return !t && this.cachedUser && Date.now() - this.cachedUserAt < nt ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
1281
1396
  try {
1282
1397
  if (!this.identity?.email)
1283
- return this.applyUser(k), k;
1398
+ return this.applyUser(S), S;
1284
1399
  const s = await this.api.request(
1285
1400
  `/api/v1/paywall/${this.paywallId}/user-state`,
1286
1401
  { headers: { "X-User-Email": this.identity.email }, signal: e }
@@ -1333,7 +1448,7 @@ class bt {
1333
1448
  return this.cachedUser;
1334
1449
  }
1335
1450
  applyUser(t) {
1336
- const e = !ot(this.cachedUser, t);
1451
+ const e = !ct(this.cachedUser, t);
1337
1452
  if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
1338
1453
  this.persistUser(t);
1339
1454
  for (const s of this.userListeners)
@@ -1345,7 +1460,7 @@ class bt {
1345
1460
  }
1346
1461
  }
1347
1462
  storageKey() {
1348
- return y.userState(this.paywallId, A(this.identity));
1463
+ return u.userState(this.paywallId, A(this.identity));
1349
1464
  }
1350
1465
  async hydrateUserFromStorage() {
1351
1466
  if (!this.cachedUser)
@@ -1353,7 +1468,7 @@ class bt {
1353
1468
  const t = await this.storage.getItem(this.storageKey());
1354
1469
  if (!t) return;
1355
1470
  const e = JSON.parse(t);
1356
- if (!e?.user || Date.now() - e.at > nt || this.cachedUser) return;
1471
+ if (!e?.user || Date.now() - e.at > rt || this.cachedUser) return;
1357
1472
  this.applyUser(e.user);
1358
1473
  } catch {
1359
1474
  }
@@ -1382,7 +1497,7 @@ class bt {
1382
1497
  */
1383
1498
  async getBalances({ force: t = !1, signal: e } = {}) {
1384
1499
  const s = Date.now(), a = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
1385
- return !t && this.cachedBalances && (a < ct || a < ht) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
1500
+ return !t && this.cachedBalances && (a < ht || a < lt) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
1386
1501
  }), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
1387
1502
  }
1388
1503
  // Network primitive — единая точка для force/stale-revalidate/cold-start.
@@ -1473,7 +1588,7 @@ class bt {
1473
1588
  */
1474
1589
  createApiGatewayClient(t = {}) {
1475
1590
  const e = t.onChargeSuccess, s = t.onQuotaExceeded;
1476
- return new et({
1591
+ return new st({
1477
1592
  paywallId: this.paywallId,
1478
1593
  apiOrigin: this.apiOrigin,
1479
1594
  auth: this.auth,
@@ -1490,7 +1605,7 @@ class bt {
1490
1605
  });
1491
1606
  }
1492
1607
  applyBalances(t, { persist: e = !0 } = {}) {
1493
- const s = !lt(this.cachedBalances, t);
1608
+ const s = !ut(this.cachedBalances, t);
1494
1609
  if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
1495
1610
  for (const a of this.balanceListeners)
1496
1611
  try {
@@ -1500,7 +1615,7 @@ class bt {
1500
1615
  }
1501
1616
  }
1502
1617
  balancesStorageKey() {
1503
- return y.balances(this.paywallId, A(this.identity));
1618
+ return u.balances(this.paywallId, A(this.identity));
1504
1619
  }
1505
1620
  async hydrateBalancesFromStorage() {
1506
1621
  if (!this.cachedBalances)
@@ -1557,16 +1672,16 @@ class bt {
1557
1672
  "Idempotency-Key": t.idempotencyKey ?? R()
1558
1673
  };
1559
1674
  this.apiKey && (n["X-Api-Key"] = this.apiKey);
1560
- 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`, {
1675
+ const r = this.cachedBootstrap?.settings, h = t.successUrl ?? r?.success_redirect_url ?? void 0, d = t.shopUrl ?? r?.checkout_shop_url ?? void 0, f = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
1561
1676
  method: "POST",
1562
1677
  headers: n,
1563
1678
  signal: t.signal,
1564
1679
  body: JSON.stringify({
1565
1680
  email: this.identity.email,
1566
1681
  priceId: Number(t.priceId),
1567
- successUrl: l,
1682
+ successUrl: h,
1568
1683
  errorUrl: t.errorUrl,
1569
- shopUrl: u,
1684
+ shopUrl: d,
1570
1685
  productName: r?.checkout_product_name ?? void 0,
1571
1686
  trial_days: t.trialDays,
1572
1687
  ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
@@ -1579,10 +1694,10 @@ class bt {
1579
1694
  { status: 409, cause: c.cause }
1580
1695
  ) : c;
1581
1696
  });
1582
- return this.inflightCheckouts.set(e, d), d.finally(() => {
1583
- this.inflightCheckouts.get(e) === d && this.inflightCheckouts.delete(e);
1697
+ return this.inflightCheckouts.set(e, f), f.finally(() => {
1698
+ this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
1584
1699
  }).catch(() => {
1585
- }), d;
1700
+ }), f;
1586
1701
  }
1587
1702
  /**
1588
1703
  * URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
@@ -1629,43 +1744,65 @@ class bt {
1629
1744
  * `/api/v1/paywall/[id]/user` без unstable_cache, потому что list для UI
1630
1745
  * должен быть свежим после cancel-а.
1631
1746
  *
1632
- * Auth: Bearer обязателен (через AuthClient). Без Bearer — 401 от бэка,
1633
- * пробрасываем как PaywallError('http_401'). Гость пустой список.
1747
+ * Auth (два пути):
1748
+ * - Bearer (через AuthClient) — user.id резолвится из сессии, identity
1749
+ * в query игнорируется.
1750
+ * - `apiKey` + `identity.email`/`identity.userId` — server-SDK путь для
1751
+ * интеграций со своей авторизацией. Бэк проверяет, что identity линкована
1752
+ * к этому пейволу (защита от cross-paywall lookup).
1753
+ * Без auth и без apiKey+identity — `identity_required`.
1634
1754
  */
1635
1755
  async listPurchases(t = {}) {
1636
- if (!this.auth)
1756
+ const e = !!(this.identity?.email || this.identity?.userId);
1757
+ if (!this.auth && !(this.apiKey && e))
1637
1758
  throw new o(
1638
- "auth_required",
1639
- "listPurchases requires AuthClient (Bearer auth)"
1759
+ "identity_required",
1760
+ "listPurchases requires AuthClient (Bearer) or apiKey + identity.email/userId"
1640
1761
  );
1641
- return (await this.api.request(`/api/v1/paywall/${this.paywallId}/user`, {
1762
+ const s = {};
1763
+ this.apiKey && (s["X-Api-Key"] = this.apiKey);
1764
+ const a = new URLSearchParams();
1765
+ this.apiKey && this.identity?.email && a.set("email", this.identity.email), this.apiKey && this.identity?.userId && a.set("user_id", this.identity.userId);
1766
+ const n = a.toString(), r = n ? `/api/v1/paywall/${this.paywallId}/user?${n}` : `/api/v1/paywall/${this.paywallId}/user`;
1767
+ return (await this.api.request(r, {
1642
1768
  method: "GET",
1769
+ headers: s,
1643
1770
  signal: t.signal
1644
1771
  })).purchases ?? [];
1645
1772
  }
1646
1773
  /**
1647
- * Отменить подписку. Бэк проверит что subscription принадлежит auth-юзеру
1648
- * и сделает cancel у acquiring'а (Stripe/Paddle/Chargebee). По умолчанию
1649
- * cancel в конце текущего периода — юзер сохраняет access до renewal date'ы.
1774
+ * Отменить подписку. Бэк проверит, что subscription принадлежит юзеру
1775
+ * (Bearer-путь из сессии; apiKey-путь — из identity), и сделает cancel у
1776
+ * acquiring'а (Stripe/Paddle/Chargebee/Overpay). По умолчанию cancel в
1777
+ * конце текущего периода — юзер сохраняет access до renewal date'ы.
1650
1778
  *
1651
- * `reason` обязательна (валидация на бэке). Удобно собрать через select
1652
- * причин в host-UI, как в legacy customer portal'е.
1779
+ * `reason` обязательна (валидация на бэке).
1653
1780
  *
1654
- * Auth: Bearer обязателен.
1781
+ * Auth (два пути):
1782
+ * - Bearer (через AuthClient) — стандартный путь для UI customer-portal'a.
1783
+ * - `apiKey` + `identity.email`/`identity.userId` — для self-service UI на
1784
+ * бэке клиента со своей авторизацией. Бэк дополнительно фильтрует
1785
+ * subscription по paywall_id, чтобы owner пейвола A не отменил подписку
1786
+ * пейвола B.
1655
1787
  */
1656
1788
  async cancelSubscription(t) {
1657
- if (!this.auth)
1789
+ const e = !!(this.identity?.email || this.identity?.userId);
1790
+ if (!this.auth && !(this.apiKey && e))
1658
1791
  throw new o(
1659
- "auth_required",
1660
- "cancelSubscription requires AuthClient (Bearer auth)"
1792
+ "identity_required",
1793
+ "cancelSubscription requires AuthClient (Bearer) or apiKey + identity.email/userId"
1661
1794
  );
1662
- return this.api.request("/api/paywall/cancel-subscription", {
1795
+ const s = {};
1796
+ this.apiKey && (s["X-Api-Key"] = this.apiKey);
1797
+ const a = {
1798
+ subscriptionId: t.subscriptionId,
1799
+ paywallId: this.paywallId,
1800
+ cancellationReason: t.reason
1801
+ };
1802
+ return this.apiKey && this.identity?.email && (a.email = this.identity.email), this.apiKey && this.identity?.userId && (a.userId = this.identity.userId), this.api.request("/api/paywall/cancel-subscription", {
1663
1803
  method: "POST",
1664
- body: JSON.stringify({
1665
- subscriptionId: t.subscriptionId,
1666
- paywallId: this.paywallId,
1667
- cancellationReason: t.reason
1668
- }),
1804
+ headers: s,
1805
+ body: JSON.stringify(a),
1669
1806
  signal: t.signal
1670
1807
  });
1671
1808
  }
@@ -1698,19 +1835,42 @@ class bt {
1698
1835
  });
1699
1836
  }
1700
1837
  }
1701
- function U(i) {
1838
+ function O(i) {
1702
1839
  return { email: i.email, userId: i.id };
1703
1840
  }
1704
1841
  function dt(i, t) {
1705
1842
  return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
1706
1843
  }
1844
+ function U(i) {
1845
+ if (!i) return null;
1846
+ const t = i.trim();
1847
+ if (!t) return null;
1848
+ try {
1849
+ return new URL(t.includes("://") ? t : `https://${t}`).origin;
1850
+ } catch {
1851
+ return null;
1852
+ }
1853
+ }
1707
1854
  function ft(i, t) {
1855
+ const e = U(i);
1856
+ if (!(!e || U(t) === e))
1857
+ throw new o(
1858
+ "invalid_config",
1859
+ `apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
1860
+ );
1861
+ }
1862
+ function E(i, t) {
1708
1863
  return {
1709
1864
  type: "modal",
1710
1865
  blocks: [
1866
+ // offer_banner НЕ в default layout — PaywallRoot рендерит его как
1867
+ // top-tab над dialog'ом (rounded-top, negative margin), за пределами
1868
+ // scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
1711
1869
  { type: "heading", text: i.name || "Upgrade", level: 1 },
1712
1870
  { type: "price_grid", priceIds: t.map((e) => e.id) },
1713
- { type: "cta_button", label: "Continue", action: "checkout" }
1871
+ { type: "cta_button", action: "checkout" },
1872
+ { type: "guarantee_badge" },
1873
+ { type: "current_session" }
1714
1874
  ]
1715
1875
  };
1716
1876
  }
@@ -1729,7 +1889,7 @@ function M(i) {
1729
1889
  if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
1730
1890
  return null;
1731
1891
  }
1732
- function b(i) {
1892
+ function w(i) {
1733
1893
  const t = M(i);
1734
1894
  if (!t) return;
1735
1895
  const e = i.locales?.[t];
@@ -1740,7 +1900,7 @@ function b(i) {
1740
1900
  return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
1741
1901
  })));
1742
1902
  }
1743
- const yt = 1500, pt = 20, O = 200;
1903
+ const yt = 1500, pt = 20, L = 200;
1744
1904
  class St {
1745
1905
  constructor(t) {
1746
1906
  this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
@@ -1756,7 +1916,7 @@ class St {
1756
1916
  this.flush();
1757
1917
  return;
1758
1918
  }
1759
- this.buffer.length > O && (this.buffer = this.buffer.slice(-O)), this.scheduleFlush();
1919
+ this.buffer.length > L && (this.buffer = this.buffer.slice(-L)), this.scheduleFlush();
1760
1920
  }
1761
1921
  scheduleFlush() {
1762
1922
  if (this.flushTimer || this.destroyed) return;
@@ -1804,7 +1964,7 @@ class St {
1804
1964
  // body-level дубликаты для beacon-flow, читаются сервером как fallback.
1805
1965
  visitor_id: e,
1806
1966
  user_id: s,
1807
- sdk_version: w,
1967
+ sdk_version: m,
1808
1968
  paywall_id: this.opts.paywallId,
1809
1969
  capabilities: this.opts.capabilities?.join(",") ?? ""
1810
1970
  }), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
@@ -1821,7 +1981,7 @@ class St {
1821
1981
  buildHeaders(t, e) {
1822
1982
  const s = {
1823
1983
  "Content-Type": "application/json",
1824
- "X-SDK-Version": w,
1984
+ "X-SDK-Version": m,
1825
1985
  "X-Paywall-Id": this.opts.paywallId,
1826
1986
  "X-Visitor-Id": t
1827
1987
  };
@@ -1840,13 +2000,13 @@ class St {
1840
2000
  }
1841
2001
  }
1842
2002
  const C = 3600 * 1e3;
1843
- function S(i) {
2003
+ function v(i) {
1844
2004
  return `paywall-${i}-trial-time-first-open`;
1845
2005
  }
1846
- function I(i) {
2006
+ function b(i) {
1847
2007
  return `paywall-${i}-skip-times`;
1848
2008
  }
1849
- class F {
2009
+ class q {
1850
2010
  constructor(t, e, s) {
1851
2011
  this.storage = t, this.paywallId = e, this.config = s;
1852
2012
  }
@@ -1857,10 +2017,10 @@ class F {
1857
2017
  return this.config.mode === "time" ? this.recordTime() : this.recordOpens();
1858
2018
  }
1859
2019
  async reset() {
1860
- await this.storage.removeItem(this.config.mode === "time" ? S(this.paywallId) : I(this.paywallId));
2020
+ await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : b(this.paywallId));
1861
2021
  }
1862
2022
  async checkTime() {
1863
- const t = this.config.payload * C, e = await this.storage.getItem(S(this.paywallId)), s = e ? Number(e) : null;
2023
+ const t = this.config.payload * C, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
1864
2024
  if (!s || !Number.isFinite(s))
1865
2025
  return {
1866
2026
  mode: "time",
@@ -1881,7 +2041,7 @@ class F {
1881
2041
  };
1882
2042
  }
1883
2043
  async checkOpens() {
1884
- const t = this.config.payload, e = await this.storage.getItem(I(this.paywallId)), s = e ? Number(e) : 0, a = Number.isFinite(s) ? s : 0, n = a < t, r = Math.max(0, t - a);
2044
+ 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);
1885
2045
  return {
1886
2046
  mode: "opens",
1887
2047
  blocked: n,
@@ -1890,7 +2050,7 @@ class F {
1890
2050
  };
1891
2051
  }
1892
2052
  async recordTime() {
1893
- const t = this.config.payload * C, e = S(this.paywallId), s = await this.storage.getItem(e);
2053
+ const t = this.config.payload * C, e = v(this.paywallId), s = await this.storage.getItem(e);
1894
2054
  let a = s ? Number(s) : null;
1895
2055
  (!a || !Number.isFinite(a)) && (a = Date.now(), await this.storage.setItem(e, String(a)));
1896
2056
  const n = a + t, r = Math.max(0, n - Date.now());
@@ -1904,23 +2064,23 @@ class F {
1904
2064
  };
1905
2065
  }
1906
2066
  async recordOpens() {
1907
- const t = this.config.payload, e = I(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);
2067
+ 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);
1908
2068
  await this.storage.setItem(e, String(r));
1909
- const l = Math.max(0, t - r);
2069
+ const h = Math.max(0, t - r);
1910
2070
  return {
1911
2071
  mode: "opens",
1912
2072
  blocked: r < t,
1913
- remainingActions: l,
2073
+ remainingActions: h,
1914
2074
  totalActions: t
1915
2075
  };
1916
2076
  }
1917
2077
  }
1918
- let E = !1;
2078
+ let N = !1;
1919
2079
  class gt {
1920
2080
  constructor(t, e, s) {
1921
- E || (E = !0, console.warn(
2081
+ N || (N = !0, console.warn(
1922
2082
  '[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.'
1923
- )), this.fallback = new F(t, e, s);
2083
+ )), this.fallback = new q(t, e, s);
1924
2084
  }
1925
2085
  check() {
1926
2086
  return this.fallback.check();
@@ -1932,10 +2092,10 @@ class gt {
1932
2092
  return this.fallback.reset();
1933
2093
  }
1934
2094
  }
1935
- function It(i, t, e) {
1936
- return e.storage === "server" ? new gt(i, t, e) : new F(i, t, e);
2095
+ function vt(i, t, e) {
2096
+ return e.storage === "server" ? new gt(i, t, e) : new q(i, t, e);
1937
2097
  }
1938
- const vt = 1;
2098
+ const bt = 1;
1939
2099
  function _t(i) {
1940
2100
  return i instanceof o ? {
1941
2101
  name: "PaywallError",
@@ -1965,7 +2125,7 @@ function Bt(i) {
1965
2125
  function wt(i) {
1966
2126
  let t = !1;
1967
2127
  const e = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = (r) => {
1968
- for (const l of e) l(r);
2128
+ for (const h of e) h(r);
1969
2129
  }, n = () => {
1970
2130
  if (!t) {
1971
2131
  t = !0;
@@ -1978,8 +2138,8 @@ function wt(i) {
1978
2138
  if (!t)
1979
2139
  try {
1980
2140
  i.postMessage(r);
1981
- } catch (l) {
1982
- throw n(), l;
2141
+ } catch (h) {
2142
+ throw n(), h;
1983
2143
  }
1984
2144
  },
1985
2145
  onMessage(r) {
@@ -2000,15 +2160,15 @@ function kt(i) {
2000
2160
  }
2001
2161
  export {
2002
2162
  mt as A,
2003
- bt as B,
2163
+ It as B,
2004
2164
  St as E,
2005
2165
  o as P,
2006
- vt as a,
2166
+ bt as a,
2007
2167
  kt as b,
2008
- It as c,
2168
+ vt as c,
2009
2169
  wt as p,
2010
2170
  Bt as r,
2011
2171
  _t as s,
2012
- Y as w
2172
+ Z as w
2013
2173
  };
2014
- //# sourceMappingURL=chrome-port-BXHR4SOG.js.map
2174
+ //# sourceMappingURL=chrome-port-KYVwww_u.js.map