@monetize.software/sdk 3.0.0-alpha.4 → 3.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/dist/chunks/PaywallUI-BD5hRY2P.js +26 -0
  2. package/dist/chunks/PaywallUI-BD5hRY2P.js.map +1 -0
  3. package/dist/chunks/PaywallUI-D7lp-bC5.js +3206 -0
  4. package/dist/chunks/PaywallUI-D7lp-bC5.js.map +1 -0
  5. package/dist/chunks/ar-BCHXVoE2.js +114 -0
  6. package/dist/chunks/ar-BCHXVoE2.js.map +1 -0
  7. package/dist/chunks/ar-CsJNZJSr.js +2 -0
  8. package/dist/chunks/ar-CsJNZJSr.js.map +1 -0
  9. package/dist/chunks/cs-B5NqpTW_.js +110 -0
  10. package/dist/chunks/cs-B5NqpTW_.js.map +1 -0
  11. package/dist/chunks/cs-BydWUC0e.js +2 -0
  12. package/dist/chunks/cs-BydWUC0e.js.map +1 -0
  13. package/dist/chunks/da-BJrGZ3LD.js +110 -0
  14. package/dist/chunks/da-BJrGZ3LD.js.map +1 -0
  15. package/dist/chunks/da-DNhiAQnh.js +2 -0
  16. package/dist/chunks/da-DNhiAQnh.js.map +1 -0
  17. package/dist/chunks/de-H8ztFOie.js +2 -0
  18. package/dist/chunks/de-H8ztFOie.js.map +1 -0
  19. package/dist/chunks/de-aepBKwsb.js +130 -0
  20. package/dist/chunks/de-aepBKwsb.js.map +1 -0
  21. package/dist/chunks/el-DRfoadtI.js +2 -0
  22. package/dist/chunks/el-DRfoadtI.js.map +1 -0
  23. package/dist/chunks/el-DTLQoX2D.js +114 -0
  24. package/dist/chunks/el-DTLQoX2D.js.map +1 -0
  25. package/dist/chunks/es-CLutF-D_.js +130 -0
  26. package/dist/chunks/es-CLutF-D_.js.map +1 -0
  27. package/dist/chunks/es-GlaYesNR.js +2 -0
  28. package/dist/chunks/es-GlaYesNR.js.map +1 -0
  29. package/dist/chunks/fi-BIHFyScH.js +2 -0
  30. package/dist/chunks/fi-BIHFyScH.js.map +1 -0
  31. package/dist/chunks/fi-DZ4csxqk.js +110 -0
  32. package/dist/chunks/fi-DZ4csxqk.js.map +1 -0
  33. package/dist/chunks/fr-BtZILUNZ.js +2 -0
  34. package/dist/chunks/fr-BtZILUNZ.js.map +1 -0
  35. package/dist/chunks/fr-jJU1SSpj.js +130 -0
  36. package/dist/chunks/fr-jJU1SSpj.js.map +1 -0
  37. package/dist/chunks/he-D9obGPNj.js +114 -0
  38. package/dist/chunks/he-D9obGPNj.js.map +1 -0
  39. package/dist/chunks/he-vSDRE4Nn.js +2 -0
  40. package/dist/chunks/he-vSDRE4Nn.js.map +1 -0
  41. package/dist/chunks/hi-B90FsnP6.js +2 -0
  42. package/dist/chunks/hi-B90FsnP6.js.map +1 -0
  43. package/dist/chunks/hi-pM8SQwZ3.js +114 -0
  44. package/dist/chunks/hi-pM8SQwZ3.js.map +1 -0
  45. package/dist/chunks/hu-DWVFODsS.js +2 -0
  46. package/dist/chunks/hu-DWVFODsS.js.map +1 -0
  47. package/dist/chunks/hu-E0m9WgbD.js +110 -0
  48. package/dist/chunks/hu-E0m9WgbD.js.map +1 -0
  49. package/dist/chunks/id-C6poPvby.js +110 -0
  50. package/dist/chunks/id-C6poPvby.js.map +1 -0
  51. package/dist/chunks/id-Ce2gzMVT.js +2 -0
  52. package/dist/chunks/id-Ce2gzMVT.js.map +1 -0
  53. package/dist/chunks/it-B2RSFyVd.js +130 -0
  54. package/dist/chunks/it-B2RSFyVd.js.map +1 -0
  55. package/dist/chunks/it-u-Gu44bl.js +2 -0
  56. package/dist/chunks/it-u-Gu44bl.js.map +1 -0
  57. package/dist/chunks/ja-CM-VgVG6.js +134 -0
  58. package/dist/chunks/ja-CM-VgVG6.js.map +1 -0
  59. package/dist/chunks/ja-CQy8RaRa.js +2 -0
  60. package/dist/chunks/ja-CQy8RaRa.js.map +1 -0
  61. package/dist/chunks/ko-BRnb7vJ7.js +2 -0
  62. package/dist/chunks/ko-BRnb7vJ7.js.map +1 -0
  63. package/dist/chunks/ko-C451fA21.js +134 -0
  64. package/dist/chunks/ko-C451fA21.js.map +1 -0
  65. package/dist/chunks/nl-CJelco6J.js +2 -0
  66. package/dist/chunks/nl-CJelco6J.js.map +1 -0
  67. package/dist/chunks/nl-DzQfJPo2.js +130 -0
  68. package/dist/chunks/nl-DzQfJPo2.js.map +1 -0
  69. package/dist/chunks/no-B51be8KT.js +110 -0
  70. package/dist/chunks/no-B51be8KT.js.map +1 -0
  71. package/dist/chunks/no-BwTjSZ4K.js +2 -0
  72. package/dist/chunks/no-BwTjSZ4K.js.map +1 -0
  73. package/dist/chunks/pl-5rTEkvfY.js +110 -0
  74. package/dist/chunks/pl-5rTEkvfY.js.map +1 -0
  75. package/dist/chunks/pl-kO82vcjb.js +2 -0
  76. package/dist/chunks/pl-kO82vcjb.js.map +1 -0
  77. package/dist/chunks/pt-CsJzaSjg.js +2 -0
  78. package/dist/chunks/pt-CsJzaSjg.js.map +1 -0
  79. package/dist/chunks/pt-JwqffZ9u.js +130 -0
  80. package/dist/chunks/pt-JwqffZ9u.js.map +1 -0
  81. package/dist/chunks/ro-BE_wJ1td.js +110 -0
  82. package/dist/chunks/ro-BE_wJ1td.js.map +1 -0
  83. package/dist/chunks/ro-ue15Ina4.js +2 -0
  84. package/dist/chunks/ro-ue15Ina4.js.map +1 -0
  85. package/dist/chunks/ru-B1iMOhX0.js +2 -0
  86. package/dist/chunks/ru-B1iMOhX0.js.map +1 -0
  87. package/dist/chunks/ru-BviATvLb.js +124 -0
  88. package/dist/chunks/ru-BviATvLb.js.map +1 -0
  89. package/dist/chunks/sv-CkNYpUVy.js +2 -0
  90. package/dist/chunks/sv-CkNYpUVy.js.map +1 -0
  91. package/dist/chunks/sv-DabGF9WL.js +110 -0
  92. package/dist/chunks/sv-DabGF9WL.js.map +1 -0
  93. package/dist/chunks/th-BiF-bNo0.js +114 -0
  94. package/dist/chunks/th-BiF-bNo0.js.map +1 -0
  95. package/dist/chunks/th-Cu80HK4y.js +2 -0
  96. package/dist/chunks/th-Cu80HK4y.js.map +1 -0
  97. package/dist/chunks/tr-B7c0afXV.js +2 -0
  98. package/dist/chunks/tr-B7c0afXV.js.map +1 -0
  99. package/dist/chunks/tr-xZuly8X8.js +110 -0
  100. package/dist/chunks/tr-xZuly8X8.js.map +1 -0
  101. package/dist/chunks/uk-BO106B0H.js +2 -0
  102. package/dist/chunks/uk-BO106B0H.js.map +1 -0
  103. package/dist/chunks/uk-KlkAaHuy.js +124 -0
  104. package/dist/chunks/uk-KlkAaHuy.js.map +1 -0
  105. package/dist/chunks/vi-BVCeumNE.js +110 -0
  106. package/dist/chunks/vi-BVCeumNE.js.map +1 -0
  107. package/dist/chunks/vi-CZ6ow40D.js +2 -0
  108. package/dist/chunks/vi-CZ6ow40D.js.map +1 -0
  109. package/dist/chunks/zh-BhP80WI1.js +2 -0
  110. package/dist/chunks/zh-BhP80WI1.js.map +1 -0
  111. package/dist/chunks/zh-C_ghwmqi.js +134 -0
  112. package/dist/chunks/zh-C_ghwmqi.js.map +1 -0
  113. package/dist/core.cjs +1 -1
  114. package/dist/core.cjs.map +1 -1
  115. package/dist/core.d.ts +81 -5
  116. package/dist/core.js +209 -171
  117. package/dist/core.js.map +1 -1
  118. package/dist/index.cjs +1 -1
  119. package/dist/index.d.ts +81 -5
  120. package/dist/index.js +1 -1
  121. package/dist/ui.cjs +1 -1
  122. package/dist/ui.d.ts +81 -5
  123. package/dist/ui.js +1 -1
  124. package/package.json +1 -1
  125. package/dist/chunks/PaywallUI-2bwf2scV.js +0 -2277
  126. package/dist/chunks/PaywallUI-2bwf2scV.js.map +0 -1
  127. package/dist/chunks/PaywallUI-Bu51__PT.js +0 -26
  128. package/dist/chunks/PaywallUI-Bu51__PT.js.map +0 -1
package/dist/core.js CHANGED
@@ -1,9 +1,9 @@
1
- class r extends Error {
1
+ class n extends Error {
2
2
  constructor(t, e, s = {}) {
3
3
  super(e), this.name = "PaywallError", this.code = t, this.status = s.status, this.cause = s.cause;
4
4
  }
5
5
  }
6
- class D extends r {
6
+ class D extends n {
7
7
  constructor(t) {
8
8
  super("not_enough_queries", t.message ?? "Not enough queries", {
9
9
  status: 402
@@ -16,36 +16,36 @@ class E {
16
16
  this.opts = t;
17
17
  }
18
18
  async request(t, e = {}) {
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", m), n.set("X-Paywall-Id", this.opts.paywallId), this.opts.capabilities?.length && n.set("X-SDK-Capabilities", this.opts.capabilities.join(","));
19
+ const s = new URL(t, this.opts.apiOrigin).toString(), a = this.opts.fetch ?? fetch, r = new Headers(e.headers);
20
+ r.set("Accept", "application/json"), r.set("X-SDK-Version", m), r.set("X-Paywall-Id", this.opts.paywallId), this.opts.capabilities?.length && r.set("X-SDK-Capabilities", this.opts.capabilities.join(","));
21
21
  const o = await this.opts.getAuthToken?.();
22
- o && n.set("Authorization", `Bearer ${o}`);
23
- const u = typeof FormData < "u" && e.body instanceof FormData;
24
- e.body && !n.has("Content-Type") && !u && n.set("Content-Type", "application/json");
25
- let l;
22
+ o && r.set("Authorization", `Bearer ${o}`);
23
+ const d = typeof FormData < "u" && e.body instanceof FormData;
24
+ e.body && !r.has("Content-Type") && !d && r.set("Content-Type", "application/json");
25
+ let u;
26
26
  try {
27
- l = await a(s, {
27
+ u = await a(s, {
28
28
  ...e,
29
- headers: n,
29
+ headers: r,
30
30
  credentials: "omit"
31
31
  });
32
32
  } catch (c) {
33
- throw (c && typeof c == "object" && "name" in c ? c.name : void 0) === "AbortError" ? new r("aborted", "Request aborted", { cause: c }) : new r("network_error", "Network request failed", { cause: c });
33
+ throw (c && typeof c == "object" && "name" in c ? c.name : void 0) === "AbortError" ? new n("aborted", "Request aborted", { cause: c }) : new n("network_error", "Network request failed", { cause: c });
34
34
  }
35
- const f = (l.headers.get("content-type") ?? "").includes("application/json") ? await l.json().catch(() => null) : null;
36
- if (!l.ok) {
37
- const c = f && typeof f == "object" && "code" in f && String(f.code) || `http_${l.status}`, w = f && typeof f == "object" && "message" in f && String(f.message) || l.statusText || "Request failed";
38
- throw new r(c, w, { status: l.status, cause: f });
35
+ const y = (u.headers.get("content-type") ?? "").includes("application/json") ? await u.json().catch(() => null) : null;
36
+ if (!u.ok) {
37
+ const c = y && typeof y == "object" && "code" in y && String(y.code) || `http_${u.status}`, g = y && typeof y == "object" && "message" in y && String(y.message) || u.statusText || "Request failed";
38
+ throw new n(c, g, { status: u.status, cause: y });
39
39
  }
40
- return f;
40
+ return y;
41
41
  }
42
42
  }
43
43
  class q {
44
44
  constructor(t) {
45
45
  if (!t.paywallId)
46
- throw new r("invalid_config", "paywallId is required");
46
+ throw new n("invalid_config", "paywallId is required");
47
47
  if (!t.apiOrigin)
48
- throw new r(
48
+ throw new n(
49
49
  "invalid_config",
50
50
  "apiOrigin is required. Pass the paywall custom_domain configured in the platform."
51
51
  );
@@ -61,15 +61,15 @@ class q {
61
61
  s.searchParams.set("paywall_id", this.paywallId);
62
62
  const a = new Headers(t.headers);
63
63
  a.set("X-SDK-Version", m), a.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && a.set("X-SDK-Capabilities", this.capabilities.join(","));
64
- const n = await this.auth?.getAccessToken();
65
- n ? a.set("Authorization", `Bearer ${n}`) : this.userId && a.set("X-User-ID", this.userId);
66
- const o = typeof FormData < "u" && t.body instanceof FormData, u = typeof Blob < "u" && t.body instanceof Blob, l = typeof ReadableStream < "u" && t.body instanceof ReadableStream, d = typeof t.body == "string";
64
+ const r = await this.auth?.getAccessToken();
65
+ r ? a.set("Authorization", `Bearer ${r}`) : this.userId && a.set("X-User-ID", this.userId);
66
+ const o = typeof FormData < "u" && t.body instanceof FormData, d = typeof Blob < "u" && t.body instanceof Blob, u = typeof ReadableStream < "u" && t.body instanceof ReadableStream, f = typeof t.body == "string";
67
67
  let h;
68
- t.body === void 0 || t.body === null ? h = void 0 : o || u || l || d ? h = t.body : (h = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
69
- const f = this.customFetch ?? fetch;
68
+ t.body === void 0 || t.body === null ? h = void 0 : o || d || u || f ? h = t.body : (h = JSON.stringify(t.body), a.has("Content-Type") || a.set("Content-Type", "application/json"));
69
+ const y = this.customFetch ?? fetch;
70
70
  let c;
71
71
  try {
72
- c = await f(s.toString(), {
72
+ c = await y(s.toString(), {
73
73
  method: t.method ?? "POST",
74
74
  headers: a,
75
75
  body: h,
@@ -78,7 +78,7 @@ class q {
78
78
  });
79
79
  } catch (p) {
80
80
  const R = p instanceof Error ? p.message : String(p);
81
- throw new r("network_error", `Network request failed: ${R}`, { cause: p });
81
+ throw new n("network_error", `Network request failed: ${R}`, { cause: p });
82
82
  }
83
83
  if (c.status === 402) {
84
84
  const p = await K(c);
@@ -86,14 +86,14 @@ class q {
86
86
  }
87
87
  if (!c.ok) {
88
88
  const p = await $(c.clone());
89
- throw new r(
89
+ throw new n(
90
90
  p ?? `http_${c.status}`,
91
91
  c.statusText || "Gateway request failed",
92
92
  { status: c.status }
93
93
  );
94
94
  }
95
- const w = c.headers.get("X-Query-Type") ?? void 0;
96
- return this.onChargeSuccess?.(w), c;
95
+ const g = c.headers.get("X-Query-Type") ?? void 0;
96
+ return this.onChargeSuccess?.(g), c;
97
97
  }
98
98
  }
99
99
  async function K(i) {
@@ -149,8 +149,8 @@ const M = {
149
149
  const e = chrome?.storage?.onChanged;
150
150
  if (!e) return () => {
151
151
  };
152
- const s = (a, n) => {
153
- if (n !== "local") return;
152
+ const s = (a, r) => {
153
+ if (r !== "local") return;
154
154
  const o = a[i];
155
155
  o && t(typeof o.newValue == "string" ? o.newValue : null);
156
156
  };
@@ -195,10 +195,10 @@ const M = {
195
195
  v.delete(i);
196
196
  }
197
197
  };
198
- function C(i) {
198
+ function L(i) {
199
199
  return i || (F() ? M : typeof window < "u" && "localStorage" in window ? x : J);
200
200
  }
201
- const y = {
201
+ const l = {
202
202
  visitorId: "pw-visitor-id",
203
203
  lastLoginMethod: (i) => `pw-${i}-last-login-method`,
204
204
  lastLoginEmail: (i) => `pw-${i}-last-login-email`,
@@ -228,7 +228,7 @@ const y = {
228
228
  // (оптимистично через `decrementBalanceLocal`).
229
229
  balances: (i, t) => `pw-${i}-${t}-balances-v1`
230
230
  };
231
- function N() {
231
+ function C() {
232
232
  const i = typeof globalThis < "u" ? globalThis.crypto : void 0;
233
233
  if (i && typeof i.randomUUID == "function") return i.randomUUID();
234
234
  const t = new Uint8Array(16);
@@ -242,13 +242,13 @@ function N() {
242
242
  }
243
243
  async function I(i) {
244
244
  try {
245
- const e = await i.getItem(y.visitorId);
245
+ const e = await i.getItem(l.visitorId);
246
246
  if (e && typeof e == "string" && e.length >= 16) return e;
247
247
  } catch {
248
248
  }
249
- const t = N();
249
+ const t = C();
250
250
  try {
251
- await i.setItem(y.visitorId, t);
251
+ await i.setItem(l.visitorId, t);
252
252
  } catch {
253
253
  }
254
254
  return t;
@@ -256,7 +256,8 @@ async function I(i) {
256
256
  const H = 5e3, V = 30 * 6e4, b = 60 * 6e4, j = 5 * 6e4, _ = {
257
257
  has_active_subscription: !1,
258
258
  purchases: [],
259
- trial: null
259
+ trial: null,
260
+ had_previous_trial: !1
260
261
  };
261
262
  function B(i) {
262
263
  return i && (i.email || i.userId || i.anonymousId) || "guest";
@@ -272,12 +273,12 @@ function Q(i, t) {
272
273
  if (i[e].type !== t[e].type || i[e].count !== t[e].count) return !1;
273
274
  return !0;
274
275
  }
275
- class lt {
276
+ class ut {
276
277
  constructor(t) {
277
278
  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)
278
- throw new r("invalid_config", "paywallId is required");
279
+ throw new n("invalid_config", "paywallId is required");
279
280
  if (!t.apiOrigin)
280
- throw new r(
281
+ throw new n(
281
282
  "invalid_config",
282
283
  '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.'
283
284
  );
@@ -285,7 +286,7 @@ class lt {
285
286
  const e = t.auth?.getCachedUser();
286
287
  this.identity = t.identity ?? (e ? T(e) : void 0), this.apiKey = t.apiKey, this.fetchImpl = t.fetch, t.apiKey && typeof window < "u" && typeof window.document < "u" && console.error(
287
288
  "[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."
288
- ), this.storage = C(t.storage), this.api = new E({
289
+ ), this.storage = L(t.storage), this.api = new E({
289
290
  apiOrigin: this.apiOrigin,
290
291
  paywallId: t.paywallId,
291
292
  capabilities: t.capabilities,
@@ -295,8 +296,8 @@ class lt {
295
296
  // Authorization-хедер просто не выставится.
296
297
  getAuthToken: t.auth ? () => t.auth.getAccessToken() : void 0
297
298
  }), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s, a) => {
298
- const n = a ? T(a.user) : void 0;
299
- W(this.identity, n) || this.setIdentity(n);
299
+ const r = a ? T(a.user) : void 0;
300
+ W(this.identity, r) || this.setIdentity(r);
300
301
  })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = I(this.storage).then((s) => (this.visitorId = s, s));
301
302
  }
302
303
  /**
@@ -336,7 +337,7 @@ class lt {
336
337
  const e = typeof t == "boolean" ? { force: t } : t;
337
338
  if (this.previewMode) {
338
339
  if (this.cachedBootstrap) return this.cachedBootstrap;
339
- throw new r(
340
+ throw new n(
340
341
  "invalid_config",
341
342
  "BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
342
343
  );
@@ -391,12 +392,12 @@ class lt {
391
392
  offers: t.offers !== void 0 ? t.offers : e.offers,
392
393
  version: `preview:${++this.previewVersionCounter}`
393
394
  };
394
- s.layout || (s.layout = U(s.settings, s.prices)), g(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
395
+ s.layout || (s.layout = U(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
395
396
  for (const a of this.bootstrapListeners)
396
397
  try {
397
398
  a(s);
398
- } catch (n) {
399
- console.warn("[paywall] onBootstrapChange listener threw", n);
399
+ } catch (r) {
400
+ console.warn("[paywall] onBootstrapChange listener threw", r);
400
401
  }
401
402
  }
402
403
  // Network primitive — единая точка для force-запроса, revalidate'а и
@@ -412,8 +413,8 @@ class lt {
412
413
  });
413
414
  if ("unchanged" in a && a.unchanged)
414
415
  return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), a.user && this.applyUser(a.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
415
- const n = a;
416
- return Y(n.settings.custom_domain, this.apiOrigin), n.layout || (n.layout = U(n.settings, n.prices)), g(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
416
+ const r = a;
417
+ return Y(r.settings.custom_domain, this.apiOrigin), r.layout || (r.layout = U(r.settings, r.prices)), w(r), this.applyBootstrap(r, { persist: !0 }), r.user && this.applyUser(r.user), r;
417
418
  }
418
419
  // Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
419
420
  // `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
@@ -436,18 +437,18 @@ class lt {
436
437
  for (const a of this.bootstrapListeners)
437
438
  try {
438
439
  a(t);
439
- } catch (n) {
440
- console.warn("[paywall] onBootstrapChange listener threw", n);
440
+ } catch (r) {
441
+ console.warn("[paywall] onBootstrapChange listener threw", r);
441
442
  }
442
443
  }
443
444
  async hydrateBootstrapFromStorage() {
444
445
  if (!this.cachedBootstrap)
445
446
  try {
446
- const t = await this.storage.getItem(y.bootstrap(this.paywallId));
447
+ const t = await this.storage.getItem(l.bootstrap(this.paywallId));
447
448
  if (!t) return;
448
449
  const e = JSON.parse(t);
449
450
  if (!e?.bootstrap || Date.now() - e.at > b || this.cachedBootstrap) return;
450
- g(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
451
+ w(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
451
452
  for (const s of this.bootstrapListeners)
452
453
  try {
453
454
  s(e.bootstrap);
@@ -462,7 +463,7 @@ class lt {
462
463
  try {
463
464
  const { user: e, ...s } = t;
464
465
  await this.storage.setItem(
465
- y.bootstrap(this.paywallId),
466
+ l.bootstrap(this.paywallId),
466
467
  JSON.stringify({ at: Date.now(), bootstrap: s })
467
468
  );
468
469
  } catch {
@@ -473,7 +474,7 @@ class lt {
473
474
  // no-op, всё работает как раньше через сеть.
474
475
  subscribeBootstrapStorage() {
475
476
  typeof this.storage.watch == "function" && (this.bootstrapStorageUnwatch = this.storage.watch(
476
- y.bootstrap(this.paywallId),
477
+ l.bootstrap(this.paywallId),
477
478
  (t) => {
478
479
  if (t)
479
480
  try {
@@ -483,7 +484,7 @@ class lt {
483
484
  this.cachedBootstrapAt = e.at;
484
485
  return;
485
486
  }
486
- g(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
487
+ w(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
487
488
  } catch {
488
489
  }
489
490
  }
@@ -530,7 +531,7 @@ class lt {
530
531
  * есть `navigator.language`.
531
532
  */
532
533
  getUserLanguage() {
533
- const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? L(this.cachedBootstrap) : null;
534
+ const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? N(this.cachedBootstrap) : null;
534
535
  return { tag: s ?? t ?? e, applied: s, browserLanguage: t, countryLanguage: e };
535
536
  }
536
537
  /**
@@ -581,8 +582,8 @@ class lt {
581
582
  if (s === "sync")
582
583
  try {
583
584
  t(a);
584
- } catch (n) {
585
- console.warn("[paywall] onUserChange initial sync threw", n);
585
+ } catch (r) {
586
+ console.warn("[paywall] onUserChange initial sync threw", r);
586
587
  }
587
588
  else
588
589
  queueMicrotask(() => {
@@ -610,7 +611,7 @@ class lt {
610
611
  }
611
612
  }
612
613
  storageKey() {
613
- return y.userState(this.paywallId, B(this.identity));
614
+ return l.userState(this.paywallId, B(this.identity));
614
615
  }
615
616
  async hydrateUserFromStorage() {
616
617
  if (!this.cachedUser)
@@ -682,8 +683,8 @@ class lt {
682
683
  if (s === "sync")
683
684
  try {
684
685
  t(a);
685
- } catch (n) {
686
- console.warn("[paywall] onBalanceChange initial sync threw", n);
686
+ } catch (r) {
687
+ console.warn("[paywall] onBalanceChange initial sync threw", r);
687
688
  }
688
689
  else
689
690
  queueMicrotask(() => {
@@ -714,10 +715,10 @@ class lt {
714
715
  return;
715
716
  }
716
717
  if (!this.cachedBalances) return;
717
- const e = this.cachedBalances.findIndex((n) => n.type === t);
718
+ const e = this.cachedBalances.findIndex((r) => r.type === t);
718
719
  if (e < 0 || this.cachedBalances[e].count <= 0) return;
719
720
  const a = this.cachedBalances.map(
720
- (n, o) => o === e ? { ...n, count: n.count - 1 } : n
721
+ (r, o) => o === e ? { ...r, count: r.count - 1 } : r
721
722
  );
722
723
  this.applyBalances(a);
723
724
  }
@@ -760,12 +761,12 @@ class lt {
760
761
  for (const a of this.balanceListeners)
761
762
  try {
762
763
  a(t);
763
- } catch (n) {
764
- console.warn("[paywall] onBalanceChange listener threw", n);
764
+ } catch (r) {
765
+ console.warn("[paywall] onBalanceChange listener threw", r);
765
766
  }
766
767
  }
767
768
  balancesStorageKey() {
768
- return y.balances(this.paywallId, B(this.identity));
769
+ return l.balances(this.paywallId, B(this.identity));
769
770
  }
770
771
  async hydrateBalancesFromStorage() {
771
772
  if (!this.cachedBalances)
@@ -812,42 +813,42 @@ class lt {
812
813
  }
813
814
  async createCheckout(t) {
814
815
  if (!this.identity?.email)
815
- throw new r(
816
+ throw new n(
816
817
  "identity_required",
817
818
  "createCheckout requires identity with email"
818
819
  );
819
820
  const e = t.idempotencyKey ?? `auto:${t.priceId}`, s = this.inflightCheckouts.get(e);
820
821
  if (s) return s;
821
- const n = {
822
- "Idempotency-Key": t.idempotencyKey ?? N()
822
+ const r = {
823
+ "Idempotency-Key": t.idempotencyKey ?? C()
823
824
  };
824
- this.apiKey && (n["X-Api-Key"] = this.apiKey);
825
- const o = this.cachedBootstrap?.settings, u = t.successUrl ?? o?.success_redirect_url ?? void 0, l = t.shopUrl ?? o?.checkout_shop_url ?? void 0, d = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
825
+ this.apiKey && (r["X-Api-Key"] = this.apiKey);
826
+ const o = this.cachedBootstrap?.settings, d = t.successUrl ?? o?.success_redirect_url ?? void 0, u = t.shopUrl ?? o?.checkout_shop_url ?? void 0, f = this.api.request(`/api/v1/paywall/${this.paywallId}/start-checkout`, {
826
827
  method: "POST",
827
- headers: n,
828
+ headers: r,
828
829
  signal: t.signal,
829
830
  body: JSON.stringify({
830
831
  email: this.identity.email,
831
832
  priceId: Number(t.priceId),
832
- successUrl: u,
833
+ successUrl: d,
833
834
  errorUrl: t.errorUrl,
834
- shopUrl: l,
835
+ shopUrl: u,
835
836
  productName: o?.checkout_product_name ?? void 0,
836
837
  trial_days: t.trialDays,
837
838
  ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
838
839
  userMeta: this.identity.userId ? { userId: this.identity.userId } : void 0
839
840
  })
840
841
  }).then((h) => ({ url: h.checkoutUrl, acquiring: h.acquiring })).catch((h) => {
841
- throw h instanceof r && h.status === 409 && h.cause && typeof h.cause == "object" && h.cause.hasActivePurchase === !0 ? new r(
842
+ throw h instanceof n && h.status === 409 && h.cause && typeof h.cause == "object" && h.cause.hasActivePurchase === !0 ? new n(
842
843
  "already_purchased",
843
844
  "You already have an active subscription",
844
845
  { status: 409, cause: h.cause }
845
846
  ) : h;
846
847
  });
847
- return this.inflightCheckouts.set(e, d), d.finally(() => {
848
- this.inflightCheckouts.get(e) === d && this.inflightCheckouts.delete(e);
848
+ return this.inflightCheckouts.set(e, f), f.finally(() => {
849
+ this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
849
850
  }).catch(() => {
850
- }), d;
851
+ }), f;
851
852
  }
852
853
  /**
853
854
  * URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
@@ -867,7 +868,7 @@ class lt {
867
868
  */
868
869
  async getCustomerPortalUrl(t = {}) {
869
870
  if (!this.auth && !this.apiKey && !this.identity?.email)
870
- throw new r(
871
+ throw new n(
871
872
  "identity_required",
872
873
  "getCustomerPortalUrl requires auth, apiKey, or identity.email"
873
874
  );
@@ -899,7 +900,7 @@ class lt {
899
900
  */
900
901
  async listPurchases(t = {}) {
901
902
  if (!this.auth)
902
- throw new r(
903
+ throw new n(
903
904
  "auth_required",
904
905
  "listPurchases requires AuthClient (Bearer auth)"
905
906
  );
@@ -920,7 +921,7 @@ class lt {
920
921
  */
921
922
  async cancelSubscription(t) {
922
923
  if (!this.auth)
923
- throw new r(
924
+ throw new n(
924
925
  "auth_required",
925
926
  "cancelSubscription requires AuthClient (Bearer auth)"
926
927
  );
@@ -945,12 +946,12 @@ class lt {
945
946
  async createSupportTicket(t) {
946
947
  const e = t.email ?? this.identity?.email ?? null, s = `/api/v1/paywall/${this.paywallId}/support/ticket`;
947
948
  if (!!t.files && t.files.length > 0) {
948
- const n = new FormData();
949
- n.set("subject", t.subject), n.set("content", t.content), e && n.set("customer_email", e);
950
- for (const o of t.files) n.append("files", o);
949
+ const r = new FormData();
950
+ r.set("subject", t.subject), r.set("content", t.content), e && r.set("customer_email", e);
951
+ for (const o of t.files) r.append("files", o);
951
952
  return this.api.request(s, {
952
953
  method: "POST",
953
- body: n
954
+ body: r
954
955
  });
955
956
  }
956
957
  return this.api.request(s, {
@@ -982,7 +983,7 @@ function O(i) {
982
983
  function Y(i, t) {
983
984
  const e = O(i);
984
985
  if (!(!e || O(t) === e))
985
- throw new r(
986
+ throw new n(
986
987
  "invalid_config",
987
988
  `apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
988
989
  );
@@ -991,15 +992,18 @@ function U(i, t) {
991
992
  return {
992
993
  type: "modal",
993
994
  blocks: [
995
+ // offer_banner НЕ в default layout — PaywallRoot рендерит его как
996
+ // top-tab над dialog'ом (rounded-top, negative margin), за пределами
997
+ // scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
994
998
  { type: "heading", text: i.name || "Upgrade", level: 1 },
995
999
  { type: "price_grid", priceIds: t.map((e) => e.id) },
996
- { type: "cta_button", label: "Continue", action: "checkout" },
1000
+ { type: "cta_button", action: "checkout" },
997
1001
  { type: "guarantee_badge" },
998
1002
  { type: "current_session" }
999
1003
  ]
1000
1004
  };
1001
1005
  }
1002
- function L(i) {
1006
+ function N(i) {
1003
1007
  const t = i.locales;
1004
1008
  if (!t) return null;
1005
1009
  const e = [];
@@ -1014,15 +1018,15 @@ function L(i) {
1014
1018
  if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
1015
1019
  return null;
1016
1020
  }
1017
- function g(i) {
1018
- const t = L(i);
1021
+ function w(i) {
1022
+ const t = N(i);
1019
1023
  if (!t) return;
1020
1024
  const e = i.locales?.[t];
1021
1025
  e && (e.layout && (i.layout = e.layout), e.prices && (i.prices = i.prices.map((s) => {
1022
1026
  const a = e.prices?.[s.id];
1023
1027
  if (!a) return s;
1024
- const n = { ...s };
1025
- return "label" in a && (n.label = a.label ?? null), "description" in a && (n.description = a.description ?? null), n;
1028
+ const r = { ...s };
1029
+ return "label" in a && (r.label = a.label ?? null), "description" in a && (r.description = a.description ?? null), r;
1026
1030
  })));
1027
1031
  }
1028
1032
  function P(i) {
@@ -1052,16 +1056,16 @@ function et() {
1052
1056
  return S(P(16));
1053
1057
  }
1054
1058
  const st = 6e4, it = 600 * 1e3;
1055
- class ut {
1059
+ class dt {
1056
1060
  constructor(t) {
1057
1061
  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)
1058
- throw new r("invalid_config", "paywallId is required");
1062
+ throw new n("invalid_config", "paywallId is required");
1059
1063
  if (!t.apiOrigin)
1060
- throw new r(
1064
+ throw new n(
1061
1065
  "invalid_config",
1062
1066
  "apiOrigin is required. Pass the paywall custom_domain configured in the platform."
1063
1067
  );
1064
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.storage = C(t.storage), this.api = new E({
1068
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.storage = L(t.storage), this.api = new E({
1065
1069
  apiOrigin: this.apiOrigin,
1066
1070
  paywallId: t.paywallId,
1067
1071
  fetch: t.fetch
@@ -1151,8 +1155,8 @@ class ut {
1151
1155
  user_meta: t.userMeta
1152
1156
  })
1153
1157
  }
1154
- ), n = this.toSession(a, a.user);
1155
- return this.setSession(n, { event: "SIGNED_IN" }), n;
1158
+ ), r = this.toSession(a, a.user);
1159
+ return this.setSession(r, { event: "SIGNED_IN" }), this.recordLastLogin("email", t.email), r;
1156
1160
  }
1157
1161
  /**
1158
1162
  * Signup. Если в Supabase включён email confirm — сервер возвращает
@@ -1178,9 +1182,9 @@ class ut {
1178
1182
  }
1179
1183
  );
1180
1184
  if (a.status === "confirmation_required")
1181
- return { kind: "confirmation_required", user: a.user };
1182
- const n = this.toSession(a, a.user);
1183
- return this.setSession(n, { event: "SIGNED_IN" }), { kind: "signed_in", session: n };
1185
+ return this.recordLastLogin("email", t.email), { kind: "confirmation_required", user: a.user };
1186
+ const r = this.toSession(a, a.user);
1187
+ return this.setSession(r, { event: "SIGNED_IN" }), this.recordLastLogin("email", t.email), { kind: "signed_in", session: r };
1184
1188
  }
1185
1189
  /**
1186
1190
  * Повторная отправка confirmation-email после signUp с включённым
@@ -1243,8 +1247,8 @@ class ut {
1243
1247
  user_meta: t.userMeta
1244
1248
  })
1245
1249
  }
1246
- ), a = this.toSession(s, s.user), n = t.type === "recovery" ? "PASSWORD_RECOVERY" : "SIGNED_IN";
1247
- return this.setSession(a, { event: n }), a;
1250
+ ), a = this.toSession(s, s.user), r = t.type === "recovery" ? "PASSWORD_RECOVERY" : "SIGNED_IN";
1251
+ return this.setSession(a, { event: r }), a;
1248
1252
  }
1249
1253
  /**
1250
1254
  * Запрос recovery email. Бэк всегда ok, чтобы не палить enumeration.
@@ -1270,7 +1274,7 @@ class ut {
1270
1274
  await this.hydrated;
1271
1275
  const e = await this.getAccessToken();
1272
1276
  if (!e)
1273
- throw new r("not_authenticated", "no active session");
1277
+ throw new n("not_authenticated", "no active session");
1274
1278
  await this.api.request(
1275
1279
  `/api/v1/paywall/${this.paywallId}/auth/password/update`,
1276
1280
  {
@@ -1335,8 +1339,8 @@ class ut {
1335
1339
  ...s.user,
1336
1340
  email: s.user.email ?? null,
1337
1341
  is_anonymous: !0
1338
- }, n = this.toSession(s, a);
1339
- return this.setSession(n, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(n.refresh_token), n;
1342
+ }, r = this.toSession(s, a);
1343
+ return this.setSession(r, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(r.refresh_token), r;
1340
1344
  })();
1341
1345
  try {
1342
1346
  return await this.inflightAnonSignin;
@@ -1360,7 +1364,7 @@ class ut {
1360
1364
  ), s = this.session?.user.is_anonymous === !0 ? this.session.user : { id: "", email: null, is_anonymous: !0 }, a = this.toSession(e, s);
1361
1365
  return this.setSession(a, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(a.refresh_token), a;
1362
1366
  } catch (e) {
1363
- if (e instanceof r && e.status === 401)
1367
+ if (e instanceof n && e.status === 401)
1364
1368
  return await this.clearAnonRefreshToken(), null;
1365
1369
  throw e;
1366
1370
  }
@@ -1390,7 +1394,7 @@ class ut {
1390
1394
  await this.hydrated;
1391
1395
  const e = await this.getAccessToken();
1392
1396
  if (!e)
1393
- throw new r("not_authenticated", "no active session");
1397
+ throw new n("not_authenticated", "no active session");
1394
1398
  const s = {
1395
1399
  Authorization: `Bearer ${e}`
1396
1400
  };
@@ -1409,19 +1413,19 @@ class ut {
1409
1413
  );
1410
1414
  if (a.status === "confirmation_required")
1411
1415
  return { kind: "confirmation_required", email: a.email };
1412
- const n = this.session;
1413
- if (!n)
1414
- throw new r(
1416
+ const r = this.session;
1417
+ if (!r)
1418
+ throw new n(
1415
1419
  "not_authenticated",
1416
1420
  "session disappeared during upgrade"
1417
1421
  );
1418
1422
  const o = {
1419
- ...n.user,
1423
+ ...r.user,
1420
1424
  id: a.user.id,
1421
1425
  email: a.user.email,
1422
1426
  is_anonymous: a.user.is_anonymous ?? !1
1423
- }, u = { ...n, user: o };
1424
- return this.setSession(u, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: u };
1427
+ }, d = { ...r, user: o };
1428
+ return this.setSession(d, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: d };
1425
1429
  }
1426
1430
  /**
1427
1431
  * OAuth signin через popup с PKCE. Жизненный цикл:
@@ -1445,22 +1449,22 @@ class ut {
1445
1449
  */
1446
1450
  async signInWithOAuth(t) {
1447
1451
  if (typeof window > "u")
1448
- throw new r("oauth_unavailable", "window is required for OAuth");
1452
+ throw new n("oauth_unavailable", "window is required for OAuth");
1449
1453
  const { authorize_url: e, state: s } = await this.startOAuthFlow({
1450
1454
  provider: t.provider,
1451
1455
  scopes: t.scopes,
1452
1456
  userMeta: t.userMeta
1453
1457
  }), a = this.openPopup(e, `pw-oauth-${s}`);
1454
1458
  if (!a)
1455
- throw this.oauthFlows.delete(s), new r(
1459
+ throw this.oauthFlows.delete(s), new n(
1456
1460
  "popup_blocked",
1457
1461
  "browser blocked auth popup — call from a user gesture"
1458
1462
  );
1459
1463
  t.onPopupOpened?.();
1460
- const n = await rt(a, s);
1464
+ const r = await nt(a, s);
1461
1465
  if (this.destroyed)
1462
- throw this.oauthFlows.delete(s), new r("aborted", "AuthClient destroyed mid-flow");
1463
- return this.completeOAuthFlow({ state: s, code: n });
1466
+ throw this.oauthFlows.delete(s), new n("aborted", "AuthClient destroyed mid-flow");
1467
+ return this.completeOAuthFlow({ state: s, code: r });
1464
1468
  }
1465
1469
  /**
1466
1470
  * Шаг 1 OAuth split-API: инициирует flow на бэке, генерит PKCE verifier
@@ -1478,13 +1482,13 @@ class ut {
1478
1482
  */
1479
1483
  async startOAuthFlow(t) {
1480
1484
  await this.hydrated, this.gcOAuthFlows();
1481
- const e = Z(), s = await tt(e), a = et(), n = {}, o = await this.getAccessToken().catch(() => null);
1482
- o && (n.Authorization = `Bearer ${o}`);
1483
- const { authorize_url: u } = await this.api.request(
1485
+ const e = Z(), s = await tt(e), a = et(), r = {}, o = await this.getAccessToken().catch(() => null);
1486
+ o && (r.Authorization = `Bearer ${o}`);
1487
+ const { authorize_url: d } = await this.api.request(
1484
1488
  `/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
1485
1489
  {
1486
1490
  method: "POST",
1487
- headers: Object.keys(n).length ? n : void 0,
1491
+ headers: Object.keys(r).length ? r : void 0,
1488
1492
  body: JSON.stringify({
1489
1493
  provider: t.provider,
1490
1494
  code_challenge: s,
@@ -1496,8 +1500,9 @@ class ut {
1496
1500
  return this.oauthFlows.set(a, {
1497
1501
  verifier: e,
1498
1502
  userMeta: t.userMeta,
1503
+ provider: t.provider,
1499
1504
  startedAt: Date.now()
1500
- }), { authorize_url: u, state: a };
1505
+ }), this.recordLastLoginMethod(t.provider), { authorize_url: d, state: a };
1501
1506
  }
1502
1507
  /**
1503
1508
  * Шаг 2 OAuth split-API: обменивает code (полученный из popup) на session,
@@ -1512,7 +1517,7 @@ class ut {
1512
1517
  await this.hydrated;
1513
1518
  const e = this.oauthFlows.get(t.state);
1514
1519
  if (!e)
1515
- throw new r(
1520
+ throw new n(
1516
1521
  "oauth_invalid_state",
1517
1522
  "OAuth flow not found — start with startOAuthFlow first or check TTL"
1518
1523
  );
@@ -1530,9 +1535,9 @@ class ut {
1530
1535
  }
1531
1536
  );
1532
1537
  if (this.destroyed)
1533
- throw new r("aborted", "AuthClient destroyed mid-flow");
1534
- const n = this.toSession(a, a.user);
1535
- return this.setSession(n, { event: "SIGNED_IN" }), n;
1538
+ throw new n("aborted", "AuthClient destroyed mid-flow");
1539
+ const r = this.toSession(a, a.user);
1540
+ return this.setSession(r, { event: "SIGNED_IN" }), r.user.email && this.recordLastLoginEmail(r.user.email), r;
1536
1541
  }
1537
1542
  gcOAuthFlows() {
1538
1543
  const t = Date.now() - it;
@@ -1563,7 +1568,7 @@ class ut {
1563
1568
  ), a = this.toSession(s, e);
1564
1569
  return this.setSession(a, { event: "TOKEN_REFRESHED" }), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
1565
1570
  } catch (s) {
1566
- if (s instanceof r && s.status === 401)
1571
+ if (s instanceof n && s.status === 401)
1567
1572
  return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null, { event: "SIGNED_OUT" }), null;
1568
1573
  throw s;
1569
1574
  } finally {
@@ -1590,7 +1595,7 @@ class ut {
1590
1595
  await this.hydrated;
1591
1596
  const t = this.session?.access_token;
1592
1597
  if (!t)
1593
- throw new r("not_authenticated", "no active session");
1598
+ throw new n("not_authenticated", "no active session");
1594
1599
  await this.api.request(
1595
1600
  `/api/v1/paywall/${this.paywallId}/auth/revoke-all`,
1596
1601
  {
@@ -1673,7 +1678,7 @@ class ut {
1673
1678
  setSession(t, e) {
1674
1679
  if (this.destroyed) return;
1675
1680
  const s = this.session;
1676
- this.session = t, e.skipPersist || this.persist(), ot(s, t) || this.emit(e.event);
1681
+ this.session = t, e.skipPersist || this.persist(), ht(s, t) || this.emit(e.event);
1677
1682
  }
1678
1683
  emit(t) {
1679
1684
  for (const e of this.listeners)
@@ -1684,7 +1689,7 @@ class ut {
1684
1689
  }
1685
1690
  }
1686
1691
  storageKey() {
1687
- return y.authSession(this.paywallId);
1692
+ return l.authSession(this.paywallId);
1688
1693
  }
1689
1694
  async hydrate() {
1690
1695
  try {
@@ -1738,7 +1743,7 @@ class ut {
1738
1743
  }
1739
1744
  async readAnonRefreshToken() {
1740
1745
  try {
1741
- const t = await this.storage.getItem(y.anonRefreshToken(this.paywallId));
1746
+ const t = await this.storage.getItem(l.anonRefreshToken(this.paywallId));
1742
1747
  return typeof t == "string" && t.length > 0 ? t : null;
1743
1748
  } catch {
1744
1749
  return null;
@@ -1747,7 +1752,7 @@ class ut {
1747
1752
  async writeAnonRefreshToken(t) {
1748
1753
  try {
1749
1754
  await this.storage.setItem(
1750
- y.anonRefreshToken(this.paywallId),
1755
+ l.anonRefreshToken(this.paywallId),
1751
1756
  t
1752
1757
  );
1753
1758
  } catch {
@@ -1756,11 +1761,41 @@ class ut {
1756
1761
  async clearAnonRefreshToken() {
1757
1762
  try {
1758
1763
  await this.storage.removeItem(
1759
- y.anonRefreshToken(this.paywallId)
1764
+ l.anonRefreshToken(this.paywallId)
1760
1765
  );
1761
1766
  } catch {
1762
1767
  }
1763
1768
  }
1769
+ /**
1770
+ * Last-used auth method + email — для UI бейджа "Last used" и pre-fill'а
1771
+ * email-инпута. Storage paywall-scoped, поэтому переключение между
1772
+ * пейволами на одном host'е не пересекает данные. Чтение всегда возвращает
1773
+ * объект — отсутствующие поля = null. */
1774
+ async getLastLogin() {
1775
+ try {
1776
+ const [t, e] = await Promise.all([
1777
+ this.storage.getItem(l.lastLoginMethod(this.paywallId)),
1778
+ this.storage.getItem(l.lastLoginEmail(this.paywallId))
1779
+ ]);
1780
+ return !t || !ot(t) ? null : { method: t, email: typeof e == "string" && e ? e : null };
1781
+ } catch {
1782
+ return null;
1783
+ }
1784
+ }
1785
+ /** Запись method и email атомарно (для email/password flows — оба известны
1786
+ * на момент signin/signup'а). OAuth-flows используют раздельные
1787
+ * recordLastLoginMethod (до popup) и recordLastLoginEmail (после exchange). */
1788
+ recordLastLogin(t, e) {
1789
+ this.recordLastLoginMethod(t), e && this.recordLastLoginEmail(e);
1790
+ }
1791
+ recordLastLoginMethod(t) {
1792
+ this.storage.setItem(l.lastLoginMethod(this.paywallId), t).catch(() => {
1793
+ });
1794
+ }
1795
+ recordLastLoginEmail(t) {
1796
+ this.storage.setItem(l.lastLoginEmail(this.paywallId), t).catch(() => {
1797
+ });
1798
+ }
1764
1799
  /**
1765
1800
  * Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
1766
1801
  * AuthClient может быть инстанцирован раньше BillingClient, а синтетический
@@ -1770,71 +1805,74 @@ class ut {
1770
1805
  */
1771
1806
  async readVisitorId() {
1772
1807
  try {
1773
- const t = await this.storage.getItem(y.visitorId);
1808
+ const t = await this.storage.getItem(l.visitorId);
1774
1809
  return typeof t == "string" && t.length >= 16 ? t : void 0;
1775
1810
  } catch {
1776
1811
  return;
1777
1812
  }
1778
1813
  }
1779
1814
  }
1780
- const at = 5 * 6e4, nt = 500;
1781
- function rt(i, t) {
1815
+ const at = 5 * 6e4, rt = 500;
1816
+ function nt(i, t) {
1782
1817
  return new Promise((e, s) => {
1783
1818
  let a = !1;
1784
- const n = () => {
1785
- a = !0, window.removeEventListener("message", o), clearInterval(u), clearTimeout(l);
1786
- }, o = (d) => {
1819
+ const r = () => {
1820
+ a = !0, window.removeEventListener("message", o), clearInterval(d), clearTimeout(u);
1821
+ }, o = (f) => {
1787
1822
  if (a) return;
1788
- const h = d.data;
1823
+ const h = f.data;
1789
1824
  if (!(!h || h.type !== "pw-oauth") && h.messageId === t) {
1790
1825
  if (h.status === "success" && h.code) {
1791
- n();
1826
+ r();
1792
1827
  try {
1793
1828
  i.close();
1794
1829
  } catch {
1795
1830
  }
1796
1831
  e(h.code);
1797
1832
  } else if (h.status === "error") {
1798
- n();
1833
+ r();
1799
1834
  try {
1800
1835
  i.close();
1801
1836
  } catch {
1802
1837
  }
1803
1838
  s(
1804
- new r(
1839
+ new n(
1805
1840
  "oauth_failed",
1806
1841
  h.description || h.error || "OAuth provider returned error"
1807
1842
  )
1808
1843
  );
1809
1844
  }
1810
1845
  }
1811
- }, u = setInterval(() => {
1846
+ }, d = setInterval(() => {
1812
1847
  if (a) return;
1813
- let d;
1848
+ let f;
1814
1849
  try {
1815
- d = i.closed;
1850
+ f = i.closed;
1816
1851
  } catch {
1817
1852
  return;
1818
1853
  }
1819
- d && (n(), s(new r("oauth_cancelled", "auth popup was closed")));
1820
- }, nt), l = setTimeout(() => {
1854
+ f && (r(), s(new n("oauth_cancelled", "auth popup was closed")));
1855
+ }, rt), u = setTimeout(() => {
1821
1856
  if (!a) {
1822
- n();
1857
+ r();
1823
1858
  try {
1824
1859
  i.close();
1825
1860
  } catch {
1826
1861
  }
1827
- s(new r("oauth_timeout", "OAuth flow timed out"));
1862
+ s(new n("oauth_timeout", "OAuth flow timed out"));
1828
1863
  }
1829
1864
  }, at);
1830
1865
  window.addEventListener("message", o);
1831
1866
  });
1832
1867
  }
1833
- function ot(i, t) {
1868
+ function ot(i) {
1869
+ return i === "google" || i === "apple" || i === "github" || i === "facebook" || i === "email";
1870
+ }
1871
+ function ht(i, t) {
1834
1872
  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;
1835
1873
  }
1836
- const ht = 1500, ct = 20, k = 200;
1837
- class dt {
1874
+ const ct = 1500, lt = 20, k = 200;
1875
+ class ft {
1838
1876
  constructor(t) {
1839
1877
  this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
1840
1878
  }
@@ -1844,7 +1882,7 @@ class dt {
1844
1882
  track(t, e) {
1845
1883
  if (this.destroyed || !this.isEnabled() || typeof t != "string" || t.length === 0) return;
1846
1884
  this.buffer.push({ type: t, ts: Date.now(), props: e });
1847
- const s = this.opts.maxBufferSize ?? ct;
1885
+ const s = this.opts.maxBufferSize ?? lt;
1848
1886
  if (this.buffer.length >= s) {
1849
1887
  this.flush();
1850
1888
  return;
@@ -1853,7 +1891,7 @@ class dt {
1853
1891
  }
1854
1892
  scheduleFlush() {
1855
1893
  if (this.flushTimer || this.destroyed) return;
1856
- const t = this.opts.flushIntervalMs ?? ht;
1894
+ const t = this.opts.flushIntervalMs ?? ct;
1857
1895
  this.flushTimer = setTimeout(() => {
1858
1896
  this.flushTimer = null, this.flush();
1859
1897
  }, t);
@@ -1864,9 +1902,9 @@ class dt {
1864
1902
  const t = this.buffer;
1865
1903
  this.buffer = [];
1866
1904
  try {
1867
- const e = await this.opts.getVisitorId(), s = this.opts.getUserId?.() ?? null, a = JSON.stringify({ events: t }), n = this.opts.fetch ?? (typeof fetch < "u" ? fetch : void 0);
1868
- if (!n) return;
1869
- await n(this.opts.endpoint, {
1905
+ const e = await this.opts.getVisitorId(), s = this.opts.getUserId?.() ?? null, a = JSON.stringify({ events: t }), r = this.opts.fetch ?? (typeof fetch < "u" ? fetch : void 0);
1906
+ if (!r) return;
1907
+ await r(this.opts.endpoint, {
1870
1908
  method: "POST",
1871
1909
  credentials: "omit",
1872
1910
  keepalive: !0,
@@ -1900,13 +1938,13 @@ class dt {
1900
1938
  sdk_version: m,
1901
1939
  paywall_id: this.opts.paywallId,
1902
1940
  capabilities: this.opts.capabilities?.join(",") ?? ""
1903
- }), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
1904
- if (!n) {
1941
+ }), r = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
1942
+ if (!r) {
1905
1943
  this.buffer.unshift(...t), this.flush();
1906
1944
  return;
1907
1945
  }
1908
1946
  try {
1909
- n(this.opts.endpoint, a) || (this.buffer.unshift(...t), this.flush());
1947
+ r(this.opts.endpoint, a) || (this.buffer.unshift(...t), this.flush());
1910
1948
  } catch {
1911
1949
  this.buffer.unshift(...t), this.flush();
1912
1950
  }
@@ -1935,15 +1973,15 @@ class dt {
1935
1973
  export {
1936
1974
  E as ApiClient,
1937
1975
  q as ApiGatewayClient,
1938
- ut as AuthClient,
1939
- lt as BillingClient,
1940
- dt as EventTracker,
1941
- r as PaywallError,
1976
+ dt as AuthClient,
1977
+ ut as BillingClient,
1978
+ ft as EventTracker,
1979
+ n as PaywallError,
1942
1980
  D as QuotaExceededError,
1943
1981
  m as SDK_VERSION,
1944
- y as STORAGE_KEYS,
1945
- C as createStorage,
1982
+ l as STORAGE_KEYS,
1983
+ L as createStorage,
1946
1984
  I as ensureVisitorId,
1947
- N as generateVisitorId
1985
+ C as generateVisitorId
1948
1986
  };
1949
1987
  //# sourceMappingURL=core.js.map