@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/chunks/PaywallUI-BWU_1hsh.js +26 -0
- package/dist/chunks/PaywallUI-BWU_1hsh.js.map +1 -0
- package/dist/chunks/{PaywallUI-BHp9afFC.js → PaywallUI-C3W0eKDo.js} +273 -249
- package/dist/chunks/PaywallUI-C3W0eKDo.js.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts +30 -0
- package/dist/core.js +120 -74
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +51 -0
- package/dist/index.js +1 -1
- package/dist/ui.cjs +1 -1
- package/dist/ui.d.ts +51 -0
- package/dist/ui.js +1 -1
- package/package.json +1 -1
- package/dist/chunks/PaywallUI-BHp9afFC.js.map +0 -1
- package/dist/chunks/PaywallUI-Dr-6q-HL.js +0 -26
- package/dist/chunks/PaywallUI-Dr-6q-HL.js.map +0 -1
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
|
|
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
|
|
14
|
-
class
|
|
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",
|
|
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
|
|
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 ??
|
|
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",
|
|
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
|
|
77
|
-
throw new r("network_error", `Network request failed: ${
|
|
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
|
|
80
|
+
const p = await F(c);
|
|
81
81
|
throw this.onQuotaExceeded?.(p), p;
|
|
82
82
|
}
|
|
83
83
|
if (!c.ok) {
|
|
84
|
-
const p = await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
122
|
+
function D() {
|
|
123
123
|
return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
|
|
124
124
|
}
|
|
125
|
-
const
|
|
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
|
-
},
|
|
183
|
+
}, b = /* @__PURE__ */ new Map(), J = {
|
|
184
184
|
async getItem(a) {
|
|
185
|
-
return
|
|
185
|
+
return b.get(a) ?? null;
|
|
186
186
|
},
|
|
187
187
|
async setItem(a, t) {
|
|
188
|
-
|
|
188
|
+
b.set(a, t);
|
|
189
189
|
},
|
|
190
190
|
async removeItem(a) {
|
|
191
|
-
|
|
191
|
+
b.delete(a);
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
|
-
function
|
|
195
|
-
return a || (
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
257
|
+
function _(a) {
|
|
258
258
|
return a && (a.email || a.userId || a.anonymousId) || "guest";
|
|
259
259
|
}
|
|
260
|
-
function
|
|
260
|
+
function X(a, t) {
|
|
261
261
|
return a === t ? !0 : !a || !t ? !1 : JSON.stringify(a) === JSON.stringify(t);
|
|
262
262
|
}
|
|
263
|
-
const
|
|
264
|
-
function
|
|
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
|
|
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 ??
|
|
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 =
|
|
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
|
-
|
|
292
|
-
})), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise =
|
|
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 =
|
|
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
|
|
329
|
-
|
|
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 =
|
|
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 >
|
|
396
|
-
|
|
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
|
-
|
|
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 ?
|
|
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 <
|
|
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 = !
|
|
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,
|
|
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 >
|
|
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 <
|
|
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 = !
|
|
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,
|
|
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 ??
|
|
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
|
|
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
|
|
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
|
|
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
|
|
944
|
-
const t =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1021
|
+
return v(new Uint8Array(s));
|
|
976
1022
|
}
|
|
977
1023
|
function et() {
|
|
978
|
-
return
|
|
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 =
|
|
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,
|
|
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 >
|
|
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:
|
|
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":
|
|
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
|
-
|
|
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
|
-
|
|
1850
|
-
|
|
1895
|
+
q as QuotaExceededError,
|
|
1896
|
+
m as SDK_VERSION,
|
|
1851
1897
|
y as STORAGE_KEYS,
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1898
|
+
C as createStorage,
|
|
1899
|
+
S as ensureVisitorId,
|
|
1900
|
+
E as generateVisitorId
|
|
1855
1901
|
};
|
|
1856
1902
|
//# sourceMappingURL=core.js.map
|