@monetize.software/sdk 3.0.0-alpha.1 → 3.0.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/core.d.ts CHANGED
@@ -487,6 +487,8 @@ export declare class BillingClient {
487
487
  private balancesStorageUnwatch;
488
488
  private inflightBalances;
489
489
  private balanceListeners;
490
+ private readonly previewMode;
491
+ private previewVersionCounter;
490
492
  constructor(opts: BillingClientOptions);
491
493
  /**
492
494
  * Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
@@ -519,6 +521,24 @@ export declare class BillingClient {
519
521
  * unsubscribe.
520
522
  */
521
523
  onBootstrapChange(cb: (b: PaywallBootstrap) => void): () => void;
524
+ /**
525
+ * Заменить cachedBootstrap частичными или полными данными и эмитнуть всем
526
+ * подписчикам. Используется host'ом в preview-mode (редактор админки) для
527
+ * live-обновления открытой модалки без сетевого revalidate'а.
528
+ *
529
+ * Поведение:
530
+ * - Без `cachedBootstrap` ожидаются как минимум `settings` + `prices` —
531
+ * иначе PaywallRoot не сможет отрендерить тарифы и упадёт.
532
+ * - С существующим кешем партиал мёрджится поверх: `settings` глубокий мёрдж
533
+ * на 1 уровень (поля настроек), массивы `prices`/`offers` перезаписываются.
534
+ * - Каждый вызов бампит `version` ("preview:<n>"), чтобы applyBootstrap'овая
535
+ * проверка `versionChanged` всегда срабатывала и listener'ы дёргались.
536
+ * - Persist в storage НЕ делаем — preview не должен утекать в другие вкладки.
537
+ *
538
+ * В non-preview режиме метод доступен, но это редкий путь (например, для
539
+ * тестов host'а) — production-код должен полагаться на bootstrap() + revalidate.
540
+ */
541
+ setBootstrap(partial: Partial<PaywallBootstrap>): void;
522
542
  private fetchBootstrap;
523
543
  private revalidateBootstrap;
524
544
  private applyBootstrap;
@@ -790,6 +810,16 @@ export declare interface BillingClientOptions {
790
810
  * через `setIdentity`, Bearer не отправляется.
791
811
  */
792
812
  auth?: AuthClient;
813
+ /**
814
+ * Preview/editor-mode. Когда true:
815
+ * - `bootstrap()` НЕ ходит в сеть — отдаёт только `cachedBootstrap`, заданный
816
+ * через `setBootstrap()`. Без seed'а throw'ает (caller обязан засидить до open).
817
+ * - Storage.watch / persist отключены (preview редактора локален для текущей вкладки).
818
+ * - `setBootstrap(partial)` доступен как публичный setter — host'у разрешено
819
+ * мутировать кеш для live-обновления модалки в редакторе админки.
820
+ * Дефолт false — обычный production-режим.
821
+ */
822
+ preview?: boolean;
793
823
  }
794
824
 
795
825
  export declare interface CheckoutResult {
package/dist/core.js CHANGED
@@ -3,21 +3,21 @@ class r 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 R extends r {
6
+ class q extends r {
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 g = "3.0.0-alpha.0";
14
- class k {
13
+ const m = "3.0.0-alpha.0";
14
+ class O {
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(), i = this.opts.fetch ?? fetch, n = new Headers(e.headers);
20
- n.set("Accept", "application/json"), n.set("X-SDK-Version", g), 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 o = await this.opts.getAuthToken?.();
22
22
  o && n.set("Authorization", `Bearer ${o}`);
23
23
  const u = typeof FormData < "u" && e.body instanceof FormData;
@@ -40,12 +40,12 @@ class k {
40
40
  return f;
41
41
  }
42
42
  }
43
- const q = "https://appbox.space";
43
+ const $ = "https://appbox.space";
44
44
  class K {
45
45
  constructor(t) {
46
46
  if (!t.paywallId)
47
47
  throw new r("invalid_config", "paywallId is required");
48
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? q, 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(
48
+ 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(
49
49
  "[paywall] WARNING: ApiGatewayClient.userId set without auth in browser. Client can spoof userId. Use AuthClient + Bearer for trusted user.id."
50
50
  );
51
51
  }
@@ -56,7 +56,7 @@ class K {
56
56
  );
57
57
  s.searchParams.set("paywall_id", this.paywallId);
58
58
  const i = new Headers(t.headers);
59
- i.set("X-SDK-Version", g), i.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && i.set("X-SDK-Capabilities", this.capabilities.join(","));
59
+ i.set("X-SDK-Version", m), i.set("X-Paywall-Id", this.paywallId), this.capabilities?.length && i.set("X-SDK-Capabilities", this.capabilities.join(","));
60
60
  const n = await this.auth?.getAccessToken();
61
61
  n ? i.set("Authorization", `Bearer ${n}`) : this.userId && i.set("X-User-ID", this.userId);
62
62
  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";
@@ -73,15 +73,15 @@ class K {
73
73
  credentials: "omit"
74
74
  });
75
75
  } catch (p) {
76
- const P = p instanceof Error ? p.message : String(p);
77
- throw new r("network_error", `Network request failed: ${P}`, { cause: p });
76
+ const R = p instanceof Error ? p.message : String(p);
77
+ throw new r("network_error", `Network request failed: ${R}`, { cause: p });
78
78
  }
79
79
  if (c.status === 402) {
80
- const p = await $(c);
80
+ const p = await F(c);
81
81
  throw this.onQuotaExceeded?.(p), p;
82
82
  }
83
83
  if (!c.ok) {
84
- const p = await F(c.clone());
84
+ const p = await N(c.clone());
85
85
  throw new r(
86
86
  p ?? `http_${c.status}`,
87
87
  c.statusText || "Gateway request failed",
@@ -92,7 +92,7 @@ class K {
92
92
  return this.onChargeSuccess?.(w), c;
93
93
  }
94
94
  }
95
- async function $(a) {
95
+ async function F(a) {
96
96
  let t = {};
97
97
  try {
98
98
  t = await a.json();
@@ -104,13 +104,13 @@ async function $(a) {
104
104
  const i = e[0];
105
105
  Array.isArray(i) ? s = i : i && Array.isArray(i.balances) && (s = i.balances);
106
106
  }
107
- return new R({
107
+ return new q({
108
108
  balances: s,
109
109
  queryType: t.details?.queryType ?? "",
110
110
  currentBalance: t.details?.currentBalance ?? null
111
111
  });
112
112
  }
113
- async function F(a) {
113
+ async function N(a) {
114
114
  if (!(a.headers.get("content-type") ?? "").includes("application/json")) return null;
115
115
  try {
116
116
  const e = await a.json();
@@ -119,10 +119,10 @@ async function F(a) {
119
119
  return null;
120
120
  }
121
121
  }
122
- function N() {
122
+ function D() {
123
123
  return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
124
124
  }
125
- const D = {
125
+ const M = {
126
126
  getItem(a) {
127
127
  return new Promise((t) => {
128
128
  chrome.storage.local.get([a], (e) => {
@@ -180,19 +180,19 @@ const D = {
180
180
  };
181
181
  return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
182
182
  }
183
- }, m = /* @__PURE__ */ new Map(), M = {
183
+ }, b = /* @__PURE__ */ new Map(), J = {
184
184
  async getItem(a) {
185
- return m.get(a) ?? null;
185
+ return b.get(a) ?? null;
186
186
  },
187
187
  async setItem(a, t) {
188
- m.set(a, t);
188
+ b.set(a, t);
189
189
  },
190
190
  async removeItem(a) {
191
- m.delete(a);
191
+ b.delete(a);
192
192
  }
193
193
  };
194
- function O(a) {
195
- return a || (N() ? D : typeof window < "u" && "localStorage" in window ? x : M);
194
+ function C(a) {
195
+ return a || (D() ? M : typeof window < "u" && "localStorage" in window ? x : J);
196
196
  }
197
197
  const y = {
198
198
  visitorId: "pw-visitor-id",
@@ -224,7 +224,7 @@ const y = {
224
224
  // (оптимистично через `decrementBalanceLocal`).
225
225
  balances: (a, t) => `pw-${a}-${t}-balances-v1`
226
226
  };
227
- function C() {
227
+ function E() {
228
228
  const a = typeof globalThis < "u" ? globalThis.crypto : void 0;
229
229
  if (a && typeof a.randomUUID == "function") return a.randomUUID();
230
230
  const t = new Uint8Array(16);
@@ -236,48 +236,48 @@ function C() {
236
236
  const e = Array.from(t, (s) => s.toString(16).padStart(2, "0")).join("");
237
237
  return `${e.slice(0, 8)}-${e.slice(8, 12)}-${e.slice(12, 16)}-${e.slice(16, 20)}-${e.slice(20)}`;
238
238
  }
239
- async function I(a) {
239
+ async function S(a) {
240
240
  try {
241
241
  const e = await a.getItem(y.visitorId);
242
242
  if (e && typeof e == "string" && e.length >= 16) return e;
243
243
  } catch {
244
244
  }
245
- const t = C();
245
+ const t = E();
246
246
  try {
247
247
  await a.setItem(y.visitorId, t);
248
248
  } catch {
249
249
  }
250
250
  return t;
251
251
  }
252
- const J = 5e3, H = 30 * 6e4, v = 60 * 6e4, V = 5 * 6e4, _ = {
252
+ const H = 5e3, V = 30 * 6e4, I = 60 * 6e4, j = 5 * 6e4, B = {
253
253
  has_active_subscription: !1,
254
254
  purchases: [],
255
255
  trial: null
256
256
  };
257
- function B(a) {
257
+ function _(a) {
258
258
  return a && (a.email || a.userId || a.anonymousId) || "guest";
259
259
  }
260
- function j(a, t) {
260
+ function X(a, t) {
261
261
  return a === t ? !0 : !a || !t ? !1 : JSON.stringify(a) === JSON.stringify(t);
262
262
  }
263
- const X = 5e3, A = 5 * 6e4, z = 3e4;
264
- function G(a, t) {
263
+ const z = 5e3, A = 5 * 6e4, G = 3e4;
264
+ function Q(a, t) {
265
265
  if (a === t) return !0;
266
266
  if (!a || !t || a.length !== t.length) return !1;
267
267
  for (let e = 0; e < a.length; e++)
268
268
  if (a[e].type !== t[e].type || a[e].count !== t[e].count) return !1;
269
269
  return !0;
270
270
  }
271
- const Q = "https://appbox.space";
271
+ const W = "https://appbox.space";
272
272
  class ut {
273
273
  constructor(t) {
274
- 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)
274
+ 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)
275
275
  throw new r("invalid_config", "paywallId is required");
276
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? Q, this.capabilities = t.capabilities, this.auth = t.auth;
276
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? W, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
277
277
  const e = t.auth?.getCachedUser();
278
278
  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(
279
279
  "[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."
280
- ), this.storage = O(t.storage), this.api = new k({
280
+ ), this.storage = C(t.storage), this.api = new O({
281
281
  apiOrigin: this.apiOrigin,
282
282
  paywallId: t.paywallId,
283
283
  capabilities: t.capabilities,
@@ -288,8 +288,8 @@ class ut {
288
288
  getAuthToken: t.auth ? () => t.auth.getAccessToken() : void 0
289
289
  }), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s) => {
290
290
  const i = s ? T(s.user) : void 0;
291
- W(this.identity, i) || this.setIdentity(i);
292
- })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = I(this.storage).then((s) => (this.visitorId = s, s));
291
+ Y(this.identity, i) || this.setIdentity(i);
292
+ })), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = S(this.storage).then((s) => (this.visitorId = s, s));
293
293
  }
294
294
  /**
295
295
  * Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
@@ -297,7 +297,7 @@ class ut {
297
297
  * EventTracker'ом для атрибуции аналитики.
298
298
  */
299
299
  async getVisitorId() {
300
- return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = I(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
300
+ return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = S(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
301
301
  }
302
302
  /** Sync-доступ к visitor_id. null если ещё не зарезолвили (первые ms жизни). */
303
303
  getCachedVisitorId() {
@@ -325,8 +325,16 @@ class ut {
325
325
  return this.storage;
326
326
  }
327
327
  async bootstrap(t = !1) {
328
- const e = typeof t == "boolean" ? { force: t } : t, s = Date.now(), i = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < v;
329
- return !e.force && i ? (s - this.cachedBootstrapAt > V && this.revalidateBootstrap(e.signal).catch(() => {
328
+ const e = typeof t == "boolean" ? { force: t } : t;
329
+ if (this.previewMode) {
330
+ if (this.cachedBootstrap) return this.cachedBootstrap;
331
+ throw new r(
332
+ "invalid_config",
333
+ "BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
334
+ );
335
+ }
336
+ const s = Date.now(), i = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < I;
337
+ return !e.force && i ? (s - this.cachedBootstrapAt > j && this.revalidateBootstrap(e.signal).catch(() => {
330
338
  }), this.cachedBootstrap) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
331
339
  ifVersion: e.force ? void 0 : this.cachedBootstrap?.version,
332
340
  signal: e.signal
@@ -345,6 +353,44 @@ class ut {
345
353
  this.bootstrapListeners.delete(t);
346
354
  };
347
355
  }
356
+ /**
357
+ * Заменить cachedBootstrap частичными или полными данными и эмитнуть всем
358
+ * подписчикам. Используется host'ом в preview-mode (редактор админки) для
359
+ * live-обновления открытой модалки без сетевого revalidate'а.
360
+ *
361
+ * Поведение:
362
+ * - Без `cachedBootstrap` ожидаются как минимум `settings` + `prices` —
363
+ * иначе PaywallRoot не сможет отрендерить тарифы и упадёт.
364
+ * - С существующим кешем партиал мёрджится поверх: `settings` глубокий мёрдж
365
+ * на 1 уровень (поля настроек), массивы `prices`/`offers` перезаписываются.
366
+ * - Каждый вызов бампит `version` ("preview:<n>"), чтобы applyBootstrap'овая
367
+ * проверка `versionChanged` всегда срабатывала и listener'ы дёргались.
368
+ * - Persist в storage НЕ делаем — preview не должен утекать в другие вкладки.
369
+ *
370
+ * В non-preview режиме метод доступен, но это редкий путь (например, для
371
+ * тестов host'а) — production-код должен полагаться на bootstrap() + revalidate.
372
+ */
373
+ setBootstrap(t) {
374
+ const e = this.cachedBootstrap ?? {
375
+ settings: { id: this.paywallId, name: "" },
376
+ prices: [],
377
+ offers: []
378
+ }, s = {
379
+ ...e,
380
+ ...t,
381
+ settings: t.settings !== void 0 ? { ...e.settings, ...t.settings } : e.settings,
382
+ prices: t.prices !== void 0 ? t.prices : e.prices,
383
+ offers: t.offers !== void 0 ? t.offers : e.offers,
384
+ version: `preview:${++this.previewVersionCounter}`
385
+ };
386
+ s.layout || (s.layout = U(s.settings, s.prices)), g(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
387
+ for (const i of this.bootstrapListeners)
388
+ try {
389
+ i(s);
390
+ } catch (n) {
391
+ console.warn("[paywall] onBootstrapChange listener threw", n);
392
+ }
393
+ }
348
394
  // Network primitive — единая точка для force-запроса, revalidate'а и
349
395
  // первого холодного bootstrap'а. `ifVersion` шлёт server-side short-circuit:
350
396
  // если совпала — бэк отвечает `{unchanged: true, version, user}` и мы лишь
@@ -359,7 +405,7 @@ class ut {
359
405
  if ("unchanged" in i && i.unchanged)
360
406
  return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), i.user && this.applyUser(i.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
361
407
  const n = i;
362
- return n.layout || (n.layout = Y(n.settings, n.prices)), b(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
408
+ return n.layout || (n.layout = U(n.settings, n.prices)), g(n), this.applyBootstrap(n, { persist: !0 }), n.user && this.applyUser(n.user), n;
363
409
  }
364
410
  // Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
365
411
  // `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
@@ -392,8 +438,8 @@ class ut {
392
438
  const t = await this.storage.getItem(y.bootstrap(this.paywallId));
393
439
  if (!t) return;
394
440
  const e = JSON.parse(t);
395
- if (!e?.bootstrap || Date.now() - e.at > v || this.cachedBootstrap) return;
396
- b(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
441
+ if (!e?.bootstrap || Date.now() - e.at > I || this.cachedBootstrap) return;
442
+ g(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
397
443
  for (const s of this.bootstrapListeners)
398
444
  try {
399
445
  s(e.bootstrap);
@@ -429,7 +475,7 @@ class ut {
429
475
  this.cachedBootstrapAt = e.at;
430
476
  return;
431
477
  }
432
- b(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
478
+ g(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
433
479
  } catch {
434
480
  }
435
481
  }
@@ -476,7 +522,7 @@ class ut {
476
522
  * есть `navigator.language`.
477
523
  */
478
524
  getUserLanguage() {
479
- const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? E(this.cachedBootstrap) : null;
525
+ const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ? L(this.cachedBootstrap) : null;
480
526
  return { tag: s ?? t ?? e, applied: s, browserLanguage: t, countryLanguage: e };
481
527
  }
482
528
  /**
@@ -488,10 +534,10 @@ class ut {
488
534
  * - Без identity возвращает empty-state (сервер тоже так делает).
489
535
  */
490
536
  async getUser({ force: t = !1, signal: e } = {}) {
491
- return !t && this.cachedUser && Date.now() - this.cachedUserAt < J ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
537
+ return !t && this.cachedUser && Date.now() - this.cachedUserAt < H ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
492
538
  try {
493
539
  if (!this.identity?.email)
494
- return this.applyUser(_), _;
540
+ return this.applyUser(B), B;
495
541
  const s = await this.api.request(
496
542
  `/api/v1/paywall/${this.paywallId}/user-state`,
497
543
  { headers: { "X-User-Email": this.identity.email }, signal: e }
@@ -544,7 +590,7 @@ class ut {
544
590
  return this.cachedUser;
545
591
  }
546
592
  applyUser(t) {
547
- const e = !j(this.cachedUser, t);
593
+ const e = !X(this.cachedUser, t);
548
594
  if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
549
595
  this.persistUser(t);
550
596
  for (const s of this.userListeners)
@@ -556,7 +602,7 @@ class ut {
556
602
  }
557
603
  }
558
604
  storageKey() {
559
- return y.userState(this.paywallId, B(this.identity));
605
+ return y.userState(this.paywallId, _(this.identity));
560
606
  }
561
607
  async hydrateUserFromStorage() {
562
608
  if (!this.cachedUser)
@@ -564,7 +610,7 @@ class ut {
564
610
  const t = await this.storage.getItem(this.storageKey());
565
611
  if (!t) return;
566
612
  const e = JSON.parse(t);
567
- if (!e?.user || Date.now() - e.at > H || this.cachedUser) return;
613
+ if (!e?.user || Date.now() - e.at > V || this.cachedUser) return;
568
614
  this.applyUser(e.user);
569
615
  } catch {
570
616
  }
@@ -593,7 +639,7 @@ class ut {
593
639
  */
594
640
  async getBalances({ force: t = !1, signal: e } = {}) {
595
641
  const s = Date.now(), i = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
596
- return !t && this.cachedBalances && (i < X || i < z) ? this.cachedBalances : !t && this.cachedBalances && i < A ? (this.fetchBalances({ signal: e }).catch(() => {
642
+ return !t && this.cachedBalances && (i < z || i < G) ? this.cachedBalances : !t && this.cachedBalances && i < A ? (this.fetchBalances({ signal: e }).catch(() => {
597
643
  }), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
598
644
  }
599
645
  // Network primitive — единая точка для force/stale-revalidate/cold-start.
@@ -701,7 +747,7 @@ class ut {
701
747
  });
702
748
  }
703
749
  applyBalances(t, { persist: e = !0 } = {}) {
704
- const s = !G(this.cachedBalances, t);
750
+ const s = !Q(this.cachedBalances, t);
705
751
  if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
706
752
  for (const i of this.balanceListeners)
707
753
  try {
@@ -711,7 +757,7 @@ class ut {
711
757
  }
712
758
  }
713
759
  balancesStorageKey() {
714
- return y.balances(this.paywallId, B(this.identity));
760
+ return y.balances(this.paywallId, _(this.identity));
715
761
  }
716
762
  async hydrateBalancesFromStorage() {
717
763
  if (!this.cachedBalances)
@@ -765,7 +811,7 @@ class ut {
765
811
  const e = t.idempotencyKey ?? `auto:${t.priceId}`, s = this.inflightCheckouts.get(e);
766
812
  if (s) return s;
767
813
  const n = {
768
- "Idempotency-Key": t.idempotencyKey ?? C()
814
+ "Idempotency-Key": t.idempotencyKey ?? E()
769
815
  };
770
816
  this.apiKey && (n["X-Api-Key"] = this.apiKey);
771
817
  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`, {
@@ -912,10 +958,10 @@ class ut {
912
958
  function T(a) {
913
959
  return { email: a.email, userId: a.id };
914
960
  }
915
- function W(a, t) {
961
+ function Y(a, t) {
916
962
  return a === t ? !0 : !a || !t ? !1 : a.email === t.email && a.userId === t.userId && a.anonymousId === t.anonymousId;
917
963
  }
918
- function Y(a, t) {
964
+ function U(a, t) {
919
965
  return {
920
966
  type: "modal",
921
967
  blocks: [
@@ -925,7 +971,7 @@ function Y(a, t) {
925
971
  ]
926
972
  };
927
973
  }
928
- function E(a) {
974
+ function L(a) {
929
975
  const t = a.locales;
930
976
  if (!t) return null;
931
977
  const e = [];
@@ -940,8 +986,8 @@ function E(a) {
940
986
  if (i && Object.prototype.hasOwnProperty.call(t, i)) return i;
941
987
  return null;
942
988
  }
943
- function b(a) {
944
- const t = E(a);
989
+ function g(a) {
990
+ const t = L(a);
945
991
  if (!t) return;
946
992
  const e = a.locales?.[t];
947
993
  e && (e.layout && (a.layout = e.layout), e.prices && (a.prices = a.prices.map((s) => {
@@ -951,7 +997,7 @@ function b(a) {
951
997
  return "label" in i && (n.label = i.label ?? null), "description" in i && (n.description = i.description ?? null), n;
952
998
  })));
953
999
  }
954
- function L(a) {
1000
+ function P(a) {
955
1001
  const t = new Uint8Array(a), e = typeof globalThis < "u" ? globalThis.crypto : void 0;
956
1002
  if (e && typeof e.getRandomValues == "function")
957
1003
  e.getRandomValues(t);
@@ -959,30 +1005,30 @@ function L(a) {
959
1005
  for (let s = 0; s < a; s++) t[s] = Math.floor(Math.random() * 256);
960
1006
  return t;
961
1007
  }
962
- function S(a) {
1008
+ function v(a) {
963
1009
  let t = "";
964
1010
  for (let e = 0; e < a.length; e++) t += String.fromCharCode(a[e]);
965
1011
  return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
966
1012
  }
967
1013
  function Z() {
968
- return S(L(64));
1014
+ return v(P(64));
969
1015
  }
970
1016
  async function tt(a) {
971
1017
  const t = new TextEncoder().encode(a), e = globalThis.crypto;
972
1018
  if (!e?.subtle?.digest)
973
1019
  throw new Error("crypto.subtle is required for PKCE");
974
1020
  const s = await e.subtle.digest("SHA-256", t);
975
- return S(new Uint8Array(s));
1021
+ return v(new Uint8Array(s));
976
1022
  }
977
1023
  function et() {
978
- return S(L(16));
1024
+ return v(P(16));
979
1025
  }
980
1026
  const st = "https://appbox.space", it = 6e4, at = 600 * 1e3;
981
1027
  class dt {
982
1028
  constructor(t) {
983
1029
  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)
984
1030
  throw new r("invalid_config", "paywallId is required");
985
- this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? st, this.storage = O(t.storage), this.api = new k({
1031
+ this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin ?? st, this.storage = C(t.storage), this.api = new O({
986
1032
  apiOrigin: this.apiOrigin,
987
1033
  paywallId: t.paywallId,
988
1034
  fetch: t.fetch
@@ -1740,7 +1786,7 @@ function ot(a, t) {
1740
1786
  function ht(a, t) {
1741
1787
  return a === t ? !0 : !a || !t ? !1 : a.access_token === t.access_token && a.refresh_token === t.refresh_token && a.expires_at === t.expires_at && a.user.id === t.user.id && a.user.email === t.user.email;
1742
1788
  }
1743
- const ct = 1500, lt = 20, U = 200;
1789
+ const ct = 1500, lt = 20, k = 200;
1744
1790
  class ft {
1745
1791
  constructor(t) {
1746
1792
  this.buffer = [], this.flushTimer = null, this.destroyed = !1, this.unloadHandler = null, this.visibilityHandler = null, this.opts = t, this.isEnabled() && this.attachUnloadHandlers();
@@ -1756,7 +1802,7 @@ class ft {
1756
1802
  this.flush();
1757
1803
  return;
1758
1804
  }
1759
- this.buffer.length > U && (this.buffer = this.buffer.slice(-U)), this.scheduleFlush();
1805
+ this.buffer.length > k && (this.buffer = this.buffer.slice(-k)), this.scheduleFlush();
1760
1806
  }
1761
1807
  scheduleFlush() {
1762
1808
  if (this.flushTimer || this.destroyed) return;
@@ -1804,7 +1850,7 @@ class ft {
1804
1850
  // body-level дубликаты для beacon-flow, читаются сервером как fallback.
1805
1851
  visitor_id: e,
1806
1852
  user_id: s,
1807
- sdk_version: g,
1853
+ sdk_version: m,
1808
1854
  paywall_id: this.opts.paywallId,
1809
1855
  capabilities: this.opts.capabilities?.join(",") ?? ""
1810
1856
  }), n = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
@@ -1821,7 +1867,7 @@ class ft {
1821
1867
  buildHeaders(t, e) {
1822
1868
  const s = {
1823
1869
  "Content-Type": "application/json",
1824
- "X-SDK-Version": g,
1870
+ "X-SDK-Version": m,
1825
1871
  "X-Paywall-Id": this.opts.paywallId,
1826
1872
  "X-Visitor-Id": t
1827
1873
  };
@@ -1840,17 +1886,17 @@ class ft {
1840
1886
  }
1841
1887
  }
1842
1888
  export {
1843
- k as ApiClient,
1889
+ O as ApiClient,
1844
1890
  K as ApiGatewayClient,
1845
1891
  dt as AuthClient,
1846
1892
  ut as BillingClient,
1847
1893
  ft as EventTracker,
1848
1894
  r as PaywallError,
1849
- R as QuotaExceededError,
1850
- g as SDK_VERSION,
1895
+ q as QuotaExceededError,
1896
+ m as SDK_VERSION,
1851
1897
  y as STORAGE_KEYS,
1852
- O as createStorage,
1853
- I as ensureVisitorId,
1854
- C as generateVisitorId
1898
+ C as createStorage,
1899
+ S as ensureVisitorId,
1900
+ E as generateVisitorId
1855
1901
  };
1856
1902
  //# sourceMappingURL=core.js.map