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

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-BXHR4SOG.js → chrome-port-CB1NEZOC.js} +367 -216
  8. package/dist/chunks/chrome-port-CB1NEZOC.js.map +1 -0
  9. package/dist/chunks/chrome-port-DptrBLez.js +2 -0
  10. package/dist/chunks/chrome-port-DptrBLez.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 +29 -0
  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 +2106 -1040
  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
  }
@@ -1277,10 +1383,10 @@ class bt {
1277
1383
  * - Без identity возвращает empty-state (сервер тоже так делает).
1278
1384
  */
1279
1385
  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 () => {
1386
+ return !t && this.cachedUser && Date.now() - this.cachedUserAt < nt ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
1281
1387
  try {
1282
1388
  if (!this.identity?.email)
1283
- return this.applyUser(k), k;
1389
+ return this.applyUser(S), S;
1284
1390
  const s = await this.api.request(
1285
1391
  `/api/v1/paywall/${this.paywallId}/user-state`,
1286
1392
  { headers: { "X-User-Email": this.identity.email }, signal: e }
@@ -1333,7 +1439,7 @@ class bt {
1333
1439
  return this.cachedUser;
1334
1440
  }
1335
1441
  applyUser(t) {
1336
- const e = !ot(this.cachedUser, t);
1442
+ const e = !ct(this.cachedUser, t);
1337
1443
  if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
1338
1444
  this.persistUser(t);
1339
1445
  for (const s of this.userListeners)
@@ -1345,7 +1451,7 @@ class bt {
1345
1451
  }
1346
1452
  }
1347
1453
  storageKey() {
1348
- return y.userState(this.paywallId, A(this.identity));
1454
+ return u.userState(this.paywallId, A(this.identity));
1349
1455
  }
1350
1456
  async hydrateUserFromStorage() {
1351
1457
  if (!this.cachedUser)
@@ -1353,7 +1459,7 @@ class bt {
1353
1459
  const t = await this.storage.getItem(this.storageKey());
1354
1460
  if (!t) return;
1355
1461
  const e = JSON.parse(t);
1356
- if (!e?.user || Date.now() - e.at > nt || this.cachedUser) return;
1462
+ if (!e?.user || Date.now() - e.at > rt || this.cachedUser) return;
1357
1463
  this.applyUser(e.user);
1358
1464
  } catch {
1359
1465
  }
@@ -1382,7 +1488,7 @@ class bt {
1382
1488
  */
1383
1489
  async getBalances({ force: t = !1, signal: e } = {}) {
1384
1490
  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(() => {
1491
+ return !t && this.cachedBalances && (a < ht || a < lt) ? this.cachedBalances : !t && this.cachedBalances && a < T ? (this.fetchBalances({ signal: e }).catch(() => {
1386
1492
  }), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
1387
1493
  }
1388
1494
  // Network primitive — единая точка для force/stale-revalidate/cold-start.
@@ -1473,7 +1579,7 @@ class bt {
1473
1579
  */
1474
1580
  createApiGatewayClient(t = {}) {
1475
1581
  const e = t.onChargeSuccess, s = t.onQuotaExceeded;
1476
- return new et({
1582
+ return new st({
1477
1583
  paywallId: this.paywallId,
1478
1584
  apiOrigin: this.apiOrigin,
1479
1585
  auth: this.auth,
@@ -1490,7 +1596,7 @@ class bt {
1490
1596
  });
1491
1597
  }
1492
1598
  applyBalances(t, { persist: e = !0 } = {}) {
1493
- const s = !lt(this.cachedBalances, t);
1599
+ const s = !ut(this.cachedBalances, t);
1494
1600
  if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
1495
1601
  for (const a of this.balanceListeners)
1496
1602
  try {
@@ -1500,7 +1606,7 @@ class bt {
1500
1606
  }
1501
1607
  }
1502
1608
  balancesStorageKey() {
1503
- return y.balances(this.paywallId, A(this.identity));
1609
+ return u.balances(this.paywallId, A(this.identity));
1504
1610
  }
1505
1611
  async hydrateBalancesFromStorage() {
1506
1612
  if (!this.cachedBalances)
@@ -1557,16 +1663,16 @@ class bt {
1557
1663
  "Idempotency-Key": t.idempotencyKey ?? R()
1558
1664
  };
1559
1665
  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`, {
1666
+ 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
1667
  method: "POST",
1562
1668
  headers: n,
1563
1669
  signal: t.signal,
1564
1670
  body: JSON.stringify({
1565
1671
  email: this.identity.email,
1566
1672
  priceId: Number(t.priceId),
1567
- successUrl: l,
1673
+ successUrl: h,
1568
1674
  errorUrl: t.errorUrl,
1569
- shopUrl: u,
1675
+ shopUrl: d,
1570
1676
  productName: r?.checkout_product_name ?? void 0,
1571
1677
  trial_days: t.trialDays,
1572
1678
  ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
@@ -1579,10 +1685,10 @@ class bt {
1579
1685
  { status: 409, cause: c.cause }
1580
1686
  ) : c;
1581
1687
  });
1582
- return this.inflightCheckouts.set(e, d), d.finally(() => {
1583
- this.inflightCheckouts.get(e) === d && this.inflightCheckouts.delete(e);
1688
+ return this.inflightCheckouts.set(e, f), f.finally(() => {
1689
+ this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
1584
1690
  }).catch(() => {
1585
- }), d;
1691
+ }), f;
1586
1692
  }
1587
1693
  /**
1588
1694
  * URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
@@ -1629,43 +1735,65 @@ class bt {
1629
1735
  * `/api/v1/paywall/[id]/user` без unstable_cache, потому что list для UI
1630
1736
  * должен быть свежим после cancel-а.
1631
1737
  *
1632
- * Auth: Bearer обязателен (через AuthClient). Без Bearer — 401 от бэка,
1633
- * пробрасываем как PaywallError('http_401'). Гость пустой список.
1738
+ * Auth (два пути):
1739
+ * - Bearer (через AuthClient) — user.id резолвится из сессии, identity
1740
+ * в query игнорируется.
1741
+ * - `apiKey` + `identity.email`/`identity.userId` — server-SDK путь для
1742
+ * интеграций со своей авторизацией. Бэк проверяет, что identity линкована
1743
+ * к этому пейволу (защита от cross-paywall lookup).
1744
+ * Без auth и без apiKey+identity — `identity_required`.
1634
1745
  */
1635
1746
  async listPurchases(t = {}) {
1636
- if (!this.auth)
1747
+ const e = !!(this.identity?.email || this.identity?.userId);
1748
+ if (!this.auth && !(this.apiKey && e))
1637
1749
  throw new o(
1638
- "auth_required",
1639
- "listPurchases requires AuthClient (Bearer auth)"
1750
+ "identity_required",
1751
+ "listPurchases requires AuthClient (Bearer) or apiKey + identity.email/userId"
1640
1752
  );
1641
- return (await this.api.request(`/api/v1/paywall/${this.paywallId}/user`, {
1753
+ const s = {};
1754
+ this.apiKey && (s["X-Api-Key"] = this.apiKey);
1755
+ const a = new URLSearchParams();
1756
+ this.apiKey && this.identity?.email && a.set("email", this.identity.email), this.apiKey && this.identity?.userId && a.set("user_id", this.identity.userId);
1757
+ const n = a.toString(), r = n ? `/api/v1/paywall/${this.paywallId}/user?${n}` : `/api/v1/paywall/${this.paywallId}/user`;
1758
+ return (await this.api.request(r, {
1642
1759
  method: "GET",
1760
+ headers: s,
1643
1761
  signal: t.signal
1644
1762
  })).purchases ?? [];
1645
1763
  }
1646
1764
  /**
1647
- * Отменить подписку. Бэк проверит что subscription принадлежит auth-юзеру
1648
- * и сделает cancel у acquiring'а (Stripe/Paddle/Chargebee). По умолчанию
1649
- * cancel в конце текущего периода — юзер сохраняет access до renewal date'ы.
1765
+ * Отменить подписку. Бэк проверит, что subscription принадлежит юзеру
1766
+ * (Bearer-путь из сессии; apiKey-путь — из identity), и сделает cancel у
1767
+ * acquiring'а (Stripe/Paddle/Chargebee/Overpay). По умолчанию cancel в
1768
+ * конце текущего периода — юзер сохраняет access до renewal date'ы.
1650
1769
  *
1651
- * `reason` обязательна (валидация на бэке). Удобно собрать через select
1652
- * причин в host-UI, как в legacy customer portal'е.
1770
+ * `reason` обязательна (валидация на бэке).
1653
1771
  *
1654
- * Auth: Bearer обязателен.
1772
+ * Auth (два пути):
1773
+ * - Bearer (через AuthClient) — стандартный путь для UI customer-portal'a.
1774
+ * - `apiKey` + `identity.email`/`identity.userId` — для self-service UI на
1775
+ * бэке клиента со своей авторизацией. Бэк дополнительно фильтрует
1776
+ * subscription по paywall_id, чтобы owner пейвола A не отменил подписку
1777
+ * пейвола B.
1655
1778
  */
1656
1779
  async cancelSubscription(t) {
1657
- if (!this.auth)
1780
+ const e = !!(this.identity?.email || this.identity?.userId);
1781
+ if (!this.auth && !(this.apiKey && e))
1658
1782
  throw new o(
1659
- "auth_required",
1660
- "cancelSubscription requires AuthClient (Bearer auth)"
1783
+ "identity_required",
1784
+ "cancelSubscription requires AuthClient (Bearer) or apiKey + identity.email/userId"
1661
1785
  );
1662
- return this.api.request("/api/paywall/cancel-subscription", {
1786
+ const s = {};
1787
+ this.apiKey && (s["X-Api-Key"] = this.apiKey);
1788
+ const a = {
1789
+ subscriptionId: t.subscriptionId,
1790
+ paywallId: this.paywallId,
1791
+ cancellationReason: t.reason
1792
+ };
1793
+ 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
1794
  method: "POST",
1664
- body: JSON.stringify({
1665
- subscriptionId: t.subscriptionId,
1666
- paywallId: this.paywallId,
1667
- cancellationReason: t.reason
1668
- }),
1795
+ headers: s,
1796
+ body: JSON.stringify(a),
1669
1797
  signal: t.signal
1670
1798
  });
1671
1799
  }
@@ -1698,19 +1826,42 @@ class bt {
1698
1826
  });
1699
1827
  }
1700
1828
  }
1701
- function U(i) {
1829
+ function O(i) {
1702
1830
  return { email: i.email, userId: i.id };
1703
1831
  }
1704
1832
  function dt(i, t) {
1705
1833
  return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
1706
1834
  }
1835
+ function U(i) {
1836
+ if (!i) return null;
1837
+ const t = i.trim();
1838
+ if (!t) return null;
1839
+ try {
1840
+ return new URL(t.includes("://") ? t : `https://${t}`).origin;
1841
+ } catch {
1842
+ return null;
1843
+ }
1844
+ }
1707
1845
  function ft(i, t) {
1846
+ const e = U(i);
1847
+ if (!(!e || U(t) === e))
1848
+ throw new o(
1849
+ "invalid_config",
1850
+ `apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
1851
+ );
1852
+ }
1853
+ function E(i, t) {
1708
1854
  return {
1709
1855
  type: "modal",
1710
1856
  blocks: [
1857
+ // offer_banner НЕ в default layout — PaywallRoot рендерит его как
1858
+ // top-tab над dialog'ом (rounded-top, negative margin), за пределами
1859
+ // scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
1711
1860
  { type: "heading", text: i.name || "Upgrade", level: 1 },
1712
1861
  { type: "price_grid", priceIds: t.map((e) => e.id) },
1713
- { type: "cta_button", label: "Continue", action: "checkout" }
1862
+ { type: "cta_button", action: "checkout" },
1863
+ { type: "guarantee_badge" },
1864
+ { type: "current_session" }
1714
1865
  ]
1715
1866
  };
1716
1867
  }
@@ -1729,7 +1880,7 @@ function M(i) {
1729
1880
  if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
1730
1881
  return null;
1731
1882
  }
1732
- function b(i) {
1883
+ function w(i) {
1733
1884
  const t = M(i);
1734
1885
  if (!t) return;
1735
1886
  const e = i.locales?.[t];
@@ -1740,7 +1891,7 @@ function b(i) {
1740
1891
  return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
1741
1892
  })));
1742
1893
  }
1743
- const yt = 1500, pt = 20, O = 200;
1894
+ const yt = 1500, pt = 20, L = 200;
1744
1895
  class St {
1745
1896
  constructor(t) {
1746
1897
  this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
@@ -1756,7 +1907,7 @@ class St {
1756
1907
  this.flush();
1757
1908
  return;
1758
1909
  }
1759
- this.buffer.length > O && (this.buffer = this.buffer.slice(-O)), this.scheduleFlush();
1910
+ this.buffer.length > L && (this.buffer = this.buffer.slice(-L)), this.scheduleFlush();
1760
1911
  }
1761
1912
  scheduleFlush() {
1762
1913
  if (this.flushTimer || this.destroyed) return;
@@ -1804,7 +1955,7 @@ class St {
1804
1955
  // body-level дубликаты для beacon-flow, читаются сервером как fallback.
1805
1956
  visitor_id: e,
1806
1957
  user_id: s,
1807
- sdk_version: w,
1958
+ sdk_version: m,
1808
1959
  paywall_id: this.opts.paywallId,
1809
1960
  capabilities: this.opts.capabilities?.join(",") ?? ""
1810
1961
  }), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
@@ -1821,7 +1972,7 @@ class St {
1821
1972
  buildHeaders(t, e) {
1822
1973
  const s = {
1823
1974
  "Content-Type": "application/json",
1824
- "X-SDK-Version": w,
1975
+ "X-SDK-Version": m,
1825
1976
  "X-Paywall-Id": this.opts.paywallId,
1826
1977
  "X-Visitor-Id": t
1827
1978
  };
@@ -1840,13 +1991,13 @@ class St {
1840
1991
  }
1841
1992
  }
1842
1993
  const C = 3600 * 1e3;
1843
- function S(i) {
1994
+ function v(i) {
1844
1995
  return `paywall-${i}-trial-time-first-open`;
1845
1996
  }
1846
- function I(i) {
1997
+ function b(i) {
1847
1998
  return `paywall-${i}-skip-times`;
1848
1999
  }
1849
- class F {
2000
+ class q {
1850
2001
  constructor(t, e, s) {
1851
2002
  this.storage = t, this.paywallId = e, this.config = s;
1852
2003
  }
@@ -1857,10 +2008,10 @@ class F {
1857
2008
  return this.config.mode === "time" ? this.recordTime() : this.recordOpens();
1858
2009
  }
1859
2010
  async reset() {
1860
- await this.storage.removeItem(this.config.mode === "time" ? S(this.paywallId) : I(this.paywallId));
2011
+ await this.storage.removeItem(this.config.mode === "time" ? v(this.paywallId) : b(this.paywallId));
1861
2012
  }
1862
2013
  async checkTime() {
1863
- const t = this.config.payload * C, e = await this.storage.getItem(S(this.paywallId)), s = e ? Number(e) : null;
2014
+ const t = this.config.payload * C, e = await this.storage.getItem(v(this.paywallId)), s = e ? Number(e) : null;
1864
2015
  if (!s || !Number.isFinite(s))
1865
2016
  return {
1866
2017
  mode: "time",
@@ -1881,7 +2032,7 @@ class F {
1881
2032
  };
1882
2033
  }
1883
2034
  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);
2035
+ 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
2036
  return {
1886
2037
  mode: "opens",
1887
2038
  blocked: n,
@@ -1890,7 +2041,7 @@ class F {
1890
2041
  };
1891
2042
  }
1892
2043
  async recordTime() {
1893
- const t = this.config.payload * C, e = S(this.paywallId), s = await this.storage.getItem(e);
2044
+ const t = this.config.payload * C, e = v(this.paywallId), s = await this.storage.getItem(e);
1894
2045
  let a = s ? Number(s) : null;
1895
2046
  (!a || !Number.isFinite(a)) && (a = Date.now(), await this.storage.setItem(e, String(a)));
1896
2047
  const n = a + t, r = Math.max(0, n - Date.now());
@@ -1904,23 +2055,23 @@ class F {
1904
2055
  };
1905
2056
  }
1906
2057
  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);
2058
+ 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
2059
  await this.storage.setItem(e, String(r));
1909
- const l = Math.max(0, t - r);
2060
+ const h = Math.max(0, t - r);
1910
2061
  return {
1911
2062
  mode: "opens",
1912
2063
  blocked: r < t,
1913
- remainingActions: l,
2064
+ remainingActions: h,
1914
2065
  totalActions: t
1915
2066
  };
1916
2067
  }
1917
2068
  }
1918
- let E = !1;
2069
+ let N = !1;
1919
2070
  class gt {
1920
2071
  constructor(t, e, s) {
1921
- E || (E = !0, console.warn(
2072
+ N || (N = !0, console.warn(
1922
2073
  '[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);
2074
+ )), this.fallback = new q(t, e, s);
1924
2075
  }
1925
2076
  check() {
1926
2077
  return this.fallback.check();
@@ -1932,10 +2083,10 @@ class gt {
1932
2083
  return this.fallback.reset();
1933
2084
  }
1934
2085
  }
1935
- function It(i, t, e) {
1936
- return e.storage === "server" ? new gt(i, t, e) : new F(i, t, e);
2086
+ function vt(i, t, e) {
2087
+ return e.storage === "server" ? new gt(i, t, e) : new q(i, t, e);
1937
2088
  }
1938
- const vt = 1;
2089
+ const bt = 1;
1939
2090
  function _t(i) {
1940
2091
  return i instanceof o ? {
1941
2092
  name: "PaywallError",
@@ -1965,7 +2116,7 @@ function Bt(i) {
1965
2116
  function wt(i) {
1966
2117
  let t = !1;
1967
2118
  const e = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), a = (r) => {
1968
- for (const l of e) l(r);
2119
+ for (const h of e) h(r);
1969
2120
  }, n = () => {
1970
2121
  if (!t) {
1971
2122
  t = !0;
@@ -1978,8 +2129,8 @@ function wt(i) {
1978
2129
  if (!t)
1979
2130
  try {
1980
2131
  i.postMessage(r);
1981
- } catch (l) {
1982
- throw n(), l;
2132
+ } catch (h) {
2133
+ throw n(), h;
1983
2134
  }
1984
2135
  },
1985
2136
  onMessage(r) {
@@ -2000,15 +2151,15 @@ function kt(i) {
2000
2151
  }
2001
2152
  export {
2002
2153
  mt as A,
2003
- bt as B,
2154
+ It as B,
2004
2155
  St as E,
2005
2156
  o as P,
2006
- vt as a,
2157
+ bt as a,
2007
2158
  kt as b,
2008
- It as c,
2159
+ vt as c,
2009
2160
  wt as p,
2010
2161
  Bt as r,
2011
2162
  _t as s,
2012
- Y as w
2163
+ Z as w
2013
2164
  };
2014
- //# sourceMappingURL=chrome-port-BXHR4SOG.js.map
2165
+ //# sourceMappingURL=chrome-port-CB1NEZOC.js.map