@monetize.software/sdk 3.0.0-alpha.3 → 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.
- package/dist/chunks/PaywallUI-BD5hRY2P.js +26 -0
- package/dist/chunks/PaywallUI-BD5hRY2P.js.map +1 -0
- package/dist/chunks/PaywallUI-D7lp-bC5.js +3206 -0
- package/dist/chunks/PaywallUI-D7lp-bC5.js.map +1 -0
- package/dist/chunks/ar-BCHXVoE2.js +114 -0
- package/dist/chunks/ar-BCHXVoE2.js.map +1 -0
- package/dist/chunks/ar-CsJNZJSr.js +2 -0
- package/dist/chunks/ar-CsJNZJSr.js.map +1 -0
- package/dist/chunks/cs-B5NqpTW_.js +110 -0
- package/dist/chunks/cs-B5NqpTW_.js.map +1 -0
- package/dist/chunks/cs-BydWUC0e.js +2 -0
- package/dist/chunks/cs-BydWUC0e.js.map +1 -0
- package/dist/chunks/da-BJrGZ3LD.js +110 -0
- package/dist/chunks/da-BJrGZ3LD.js.map +1 -0
- package/dist/chunks/da-DNhiAQnh.js +2 -0
- package/dist/chunks/da-DNhiAQnh.js.map +1 -0
- package/dist/chunks/de-H8ztFOie.js +2 -0
- package/dist/chunks/de-H8ztFOie.js.map +1 -0
- package/dist/chunks/de-aepBKwsb.js +130 -0
- package/dist/chunks/de-aepBKwsb.js.map +1 -0
- package/dist/chunks/el-DRfoadtI.js +2 -0
- package/dist/chunks/el-DRfoadtI.js.map +1 -0
- package/dist/chunks/el-DTLQoX2D.js +114 -0
- package/dist/chunks/el-DTLQoX2D.js.map +1 -0
- package/dist/chunks/es-CLutF-D_.js +130 -0
- package/dist/chunks/es-CLutF-D_.js.map +1 -0
- package/dist/chunks/es-GlaYesNR.js +2 -0
- package/dist/chunks/es-GlaYesNR.js.map +1 -0
- package/dist/chunks/fi-BIHFyScH.js +2 -0
- package/dist/chunks/fi-BIHFyScH.js.map +1 -0
- package/dist/chunks/fi-DZ4csxqk.js +110 -0
- package/dist/chunks/fi-DZ4csxqk.js.map +1 -0
- package/dist/chunks/fr-BtZILUNZ.js +2 -0
- package/dist/chunks/fr-BtZILUNZ.js.map +1 -0
- package/dist/chunks/fr-jJU1SSpj.js +130 -0
- package/dist/chunks/fr-jJU1SSpj.js.map +1 -0
- package/dist/chunks/he-D9obGPNj.js +114 -0
- package/dist/chunks/he-D9obGPNj.js.map +1 -0
- package/dist/chunks/he-vSDRE4Nn.js +2 -0
- package/dist/chunks/he-vSDRE4Nn.js.map +1 -0
- package/dist/chunks/hi-B90FsnP6.js +2 -0
- package/dist/chunks/hi-B90FsnP6.js.map +1 -0
- package/dist/chunks/hi-pM8SQwZ3.js +114 -0
- package/dist/chunks/hi-pM8SQwZ3.js.map +1 -0
- package/dist/chunks/hu-DWVFODsS.js +2 -0
- package/dist/chunks/hu-DWVFODsS.js.map +1 -0
- package/dist/chunks/hu-E0m9WgbD.js +110 -0
- package/dist/chunks/hu-E0m9WgbD.js.map +1 -0
- package/dist/chunks/id-C6poPvby.js +110 -0
- package/dist/chunks/id-C6poPvby.js.map +1 -0
- package/dist/chunks/id-Ce2gzMVT.js +2 -0
- package/dist/chunks/id-Ce2gzMVT.js.map +1 -0
- package/dist/chunks/it-B2RSFyVd.js +130 -0
- package/dist/chunks/it-B2RSFyVd.js.map +1 -0
- package/dist/chunks/it-u-Gu44bl.js +2 -0
- package/dist/chunks/it-u-Gu44bl.js.map +1 -0
- package/dist/chunks/ja-CM-VgVG6.js +134 -0
- package/dist/chunks/ja-CM-VgVG6.js.map +1 -0
- package/dist/chunks/ja-CQy8RaRa.js +2 -0
- package/dist/chunks/ja-CQy8RaRa.js.map +1 -0
- package/dist/chunks/ko-BRnb7vJ7.js +2 -0
- package/dist/chunks/ko-BRnb7vJ7.js.map +1 -0
- package/dist/chunks/ko-C451fA21.js +134 -0
- package/dist/chunks/ko-C451fA21.js.map +1 -0
- package/dist/chunks/nl-CJelco6J.js +2 -0
- package/dist/chunks/nl-CJelco6J.js.map +1 -0
- package/dist/chunks/nl-DzQfJPo2.js +130 -0
- package/dist/chunks/nl-DzQfJPo2.js.map +1 -0
- package/dist/chunks/no-B51be8KT.js +110 -0
- package/dist/chunks/no-B51be8KT.js.map +1 -0
- package/dist/chunks/no-BwTjSZ4K.js +2 -0
- package/dist/chunks/no-BwTjSZ4K.js.map +1 -0
- package/dist/chunks/pl-5rTEkvfY.js +110 -0
- package/dist/chunks/pl-5rTEkvfY.js.map +1 -0
- package/dist/chunks/pl-kO82vcjb.js +2 -0
- package/dist/chunks/pl-kO82vcjb.js.map +1 -0
- package/dist/chunks/pt-CsJzaSjg.js +2 -0
- package/dist/chunks/pt-CsJzaSjg.js.map +1 -0
- package/dist/chunks/pt-JwqffZ9u.js +130 -0
- package/dist/chunks/pt-JwqffZ9u.js.map +1 -0
- package/dist/chunks/ro-BE_wJ1td.js +110 -0
- package/dist/chunks/ro-BE_wJ1td.js.map +1 -0
- package/dist/chunks/ro-ue15Ina4.js +2 -0
- package/dist/chunks/ro-ue15Ina4.js.map +1 -0
- package/dist/chunks/ru-B1iMOhX0.js +2 -0
- package/dist/chunks/ru-B1iMOhX0.js.map +1 -0
- package/dist/chunks/ru-BviATvLb.js +124 -0
- package/dist/chunks/ru-BviATvLb.js.map +1 -0
- package/dist/chunks/sv-CkNYpUVy.js +2 -0
- package/dist/chunks/sv-CkNYpUVy.js.map +1 -0
- package/dist/chunks/sv-DabGF9WL.js +110 -0
- package/dist/chunks/sv-DabGF9WL.js.map +1 -0
- package/dist/chunks/th-BiF-bNo0.js +114 -0
- package/dist/chunks/th-BiF-bNo0.js.map +1 -0
- package/dist/chunks/th-Cu80HK4y.js +2 -0
- package/dist/chunks/th-Cu80HK4y.js.map +1 -0
- package/dist/chunks/tr-B7c0afXV.js +2 -0
- package/dist/chunks/tr-B7c0afXV.js.map +1 -0
- package/dist/chunks/tr-xZuly8X8.js +110 -0
- package/dist/chunks/tr-xZuly8X8.js.map +1 -0
- package/dist/chunks/uk-BO106B0H.js +2 -0
- package/dist/chunks/uk-BO106B0H.js.map +1 -0
- package/dist/chunks/uk-KlkAaHuy.js +124 -0
- package/dist/chunks/uk-KlkAaHuy.js.map +1 -0
- package/dist/chunks/vi-BVCeumNE.js +110 -0
- package/dist/chunks/vi-BVCeumNE.js.map +1 -0
- package/dist/chunks/vi-CZ6ow40D.js +2 -0
- package/dist/chunks/vi-CZ6ow40D.js.map +1 -0
- package/dist/chunks/zh-BhP80WI1.js +2 -0
- package/dist/chunks/zh-BhP80WI1.js.map +1 -0
- package/dist/chunks/zh-C_ghwmqi.js +134 -0
- package/dist/chunks/zh-C_ghwmqi.js.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.ts +152 -11
- package/dist/core.js +433 -348
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +166 -22
- package/dist/index.js +1 -1
- package/dist/ui.cjs +1 -1
- package/dist/ui.d.ts +166 -22
- package/dist/ui.js +1 -1
- package/package.json +32 -31
- package/dist/chunks/PaywallUI-BWU_1hsh.js +0 -26
- package/dist/chunks/PaywallUI-BWU_1hsh.js.map +0 -1
- package/dist/chunks/PaywallUI-C3W0eKDo.js +0 -2233
- package/dist/chunks/PaywallUI-C3W0eKDo.js.map +0 -1
package/dist/core.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
class
|
|
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
|
|
6
|
+
class D extends n {
|
|
7
7
|
constructor(t) {
|
|
8
8
|
super("not_enough_queries", t.message ?? "Not enough queries", {
|
|
9
9
|
status: 402
|
|
@@ -11,41 +11,45 @@ class q extends r {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
const m = "3.0.0-alpha.0";
|
|
14
|
-
class
|
|
14
|
+
class E {
|
|
15
15
|
constructor(t) {
|
|
16
16
|
this.opts = t;
|
|
17
17
|
}
|
|
18
18
|
async request(t, e = {}) {
|
|
19
|
-
const s = new URL(t, this.opts.apiOrigin).toString(),
|
|
20
|
-
|
|
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 &&
|
|
23
|
-
const
|
|
24
|
-
e.body && !
|
|
25
|
-
let
|
|
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
|
-
|
|
27
|
+
u = await a(s, {
|
|
28
28
|
...e,
|
|
29
|
-
headers:
|
|
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
|
|
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
|
|
36
|
-
if (!
|
|
37
|
-
const c =
|
|
38
|
-
throw new
|
|
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
|
|
40
|
+
return y;
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
|
|
44
|
-
class K {
|
|
43
|
+
class q {
|
|
45
44
|
constructor(t) {
|
|
46
45
|
if (!t.paywallId)
|
|
47
|
-
throw new
|
|
48
|
-
|
|
46
|
+
throw new n("invalid_config", "paywallId is required");
|
|
47
|
+
if (!t.apiOrigin)
|
|
48
|
+
throw new n(
|
|
49
|
+
"invalid_config",
|
|
50
|
+
"apiOrigin is required. Pass the paywall custom_domain configured in the platform."
|
|
51
|
+
);
|
|
52
|
+
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
53
|
"[paywall] WARNING: ApiGatewayClient.userId set without auth in browser. Client can spoof userId. Use AuthClient + Bearer for trusted user.id."
|
|
50
54
|
);
|
|
51
55
|
}
|
|
@@ -55,229 +59,234 @@ class K {
|
|
|
55
59
|
this.apiOrigin
|
|
56
60
|
);
|
|
57
61
|
s.searchParams.set("paywall_id", this.paywallId);
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const o = typeof FormData < "u" && t.body instanceof FormData,
|
|
62
|
+
const a = new Headers(t.headers);
|
|
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 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";
|
|
63
67
|
let h;
|
|
64
|
-
t.body === void 0 || t.body === null ? h = void 0 : o ||
|
|
65
|
-
const
|
|
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;
|
|
66
70
|
let c;
|
|
67
71
|
try {
|
|
68
|
-
c = await
|
|
72
|
+
c = await y(s.toString(), {
|
|
69
73
|
method: t.method ?? "POST",
|
|
70
|
-
headers:
|
|
74
|
+
headers: a,
|
|
71
75
|
body: h,
|
|
72
76
|
signal: t.signal,
|
|
73
77
|
credentials: "omit"
|
|
74
78
|
});
|
|
75
79
|
} catch (p) {
|
|
76
80
|
const R = p instanceof Error ? p.message : String(p);
|
|
77
|
-
throw new
|
|
81
|
+
throw new n("network_error", `Network request failed: ${R}`, { cause: p });
|
|
78
82
|
}
|
|
79
83
|
if (c.status === 402) {
|
|
80
|
-
const p = await
|
|
84
|
+
const p = await K(c);
|
|
81
85
|
throw this.onQuotaExceeded?.(p), p;
|
|
82
86
|
}
|
|
83
87
|
if (!c.ok) {
|
|
84
|
-
const p = await
|
|
85
|
-
throw new
|
|
88
|
+
const p = await $(c.clone());
|
|
89
|
+
throw new n(
|
|
86
90
|
p ?? `http_${c.status}`,
|
|
87
91
|
c.statusText || "Gateway request failed",
|
|
88
92
|
{ status: c.status }
|
|
89
93
|
);
|
|
90
94
|
}
|
|
91
|
-
const
|
|
92
|
-
return this.onChargeSuccess?.(
|
|
95
|
+
const g = c.headers.get("X-Query-Type") ?? void 0;
|
|
96
|
+
return this.onChargeSuccess?.(g), c;
|
|
93
97
|
}
|
|
94
98
|
}
|
|
95
|
-
async function
|
|
99
|
+
async function K(i) {
|
|
96
100
|
let t = {};
|
|
97
101
|
try {
|
|
98
|
-
t = await
|
|
102
|
+
t = await i.json();
|
|
99
103
|
} catch {
|
|
100
104
|
}
|
|
101
105
|
const e = t.details?.balances;
|
|
102
106
|
let s = [];
|
|
103
107
|
if (Array.isArray(e)) {
|
|
104
|
-
const
|
|
105
|
-
Array.isArray(
|
|
108
|
+
const a = e[0];
|
|
109
|
+
Array.isArray(a) ? s = a : a && Array.isArray(a.balances) && (s = a.balances);
|
|
106
110
|
}
|
|
107
|
-
return new
|
|
111
|
+
return new D({
|
|
108
112
|
balances: s,
|
|
109
113
|
queryType: t.details?.queryType ?? "",
|
|
110
114
|
currentBalance: t.details?.currentBalance ?? null
|
|
111
115
|
});
|
|
112
116
|
}
|
|
113
|
-
async function
|
|
114
|
-
if (!(
|
|
117
|
+
async function $(i) {
|
|
118
|
+
if (!(i.headers.get("content-type") ?? "").includes("application/json")) return null;
|
|
115
119
|
try {
|
|
116
|
-
const e = await
|
|
120
|
+
const e = await i.json();
|
|
117
121
|
return e.code || e.error || null;
|
|
118
122
|
} catch {
|
|
119
123
|
return null;
|
|
120
124
|
}
|
|
121
125
|
}
|
|
122
|
-
function
|
|
126
|
+
function F() {
|
|
123
127
|
return typeof chrome < "u" && !!chrome?.storage?.local && !!chrome?.runtime?.id;
|
|
124
128
|
}
|
|
125
129
|
const M = {
|
|
126
|
-
getItem(
|
|
130
|
+
getItem(i) {
|
|
127
131
|
return new Promise((t) => {
|
|
128
|
-
chrome.storage.local.get([
|
|
129
|
-
const s = e[
|
|
132
|
+
chrome.storage.local.get([i], (e) => {
|
|
133
|
+
const s = e[i];
|
|
130
134
|
t(typeof s == "string" ? s : null);
|
|
131
135
|
});
|
|
132
136
|
});
|
|
133
137
|
},
|
|
134
|
-
setItem(
|
|
138
|
+
setItem(i, t) {
|
|
135
139
|
return new Promise((e) => {
|
|
136
|
-
chrome.storage.local.set({ [
|
|
140
|
+
chrome.storage.local.set({ [i]: t }, () => e());
|
|
137
141
|
});
|
|
138
142
|
},
|
|
139
|
-
removeItem(
|
|
143
|
+
removeItem(i) {
|
|
140
144
|
return new Promise((t) => {
|
|
141
|
-
chrome.storage.local.remove([
|
|
145
|
+
chrome.storage.local.remove([i], () => t());
|
|
142
146
|
});
|
|
143
147
|
},
|
|
144
|
-
watch(
|
|
148
|
+
watch(i, t) {
|
|
145
149
|
const e = chrome?.storage?.onChanged;
|
|
146
150
|
if (!e) return () => {
|
|
147
151
|
};
|
|
148
|
-
const s = (
|
|
149
|
-
if (
|
|
150
|
-
const o = i
|
|
152
|
+
const s = (a, r) => {
|
|
153
|
+
if (r !== "local") return;
|
|
154
|
+
const o = a[i];
|
|
151
155
|
o && t(typeof o.newValue == "string" ? o.newValue : null);
|
|
152
156
|
};
|
|
153
157
|
return e.addListener(s), () => e.removeListener(s);
|
|
154
158
|
}
|
|
155
159
|
}, x = {
|
|
156
|
-
async getItem(
|
|
160
|
+
async getItem(i) {
|
|
157
161
|
try {
|
|
158
|
-
return window.localStorage.getItem(
|
|
162
|
+
return window.localStorage.getItem(i);
|
|
159
163
|
} catch {
|
|
160
164
|
return null;
|
|
161
165
|
}
|
|
162
166
|
},
|
|
163
|
-
async setItem(
|
|
167
|
+
async setItem(i, t) {
|
|
164
168
|
try {
|
|
165
|
-
window.localStorage.setItem(
|
|
169
|
+
window.localStorage.setItem(i, t);
|
|
166
170
|
} catch {
|
|
167
171
|
}
|
|
168
172
|
},
|
|
169
|
-
async removeItem(
|
|
173
|
+
async removeItem(i) {
|
|
170
174
|
try {
|
|
171
|
-
window.localStorage.removeItem(
|
|
175
|
+
window.localStorage.removeItem(i);
|
|
172
176
|
} catch {
|
|
173
177
|
}
|
|
174
178
|
},
|
|
175
|
-
watch(
|
|
179
|
+
watch(i, t) {
|
|
176
180
|
if (typeof window > "u") return () => {
|
|
177
181
|
};
|
|
178
182
|
const e = (s) => {
|
|
179
|
-
s.storageArea === window.localStorage && s.key ===
|
|
183
|
+
s.storageArea === window.localStorage && s.key === i && t(s.newValue);
|
|
180
184
|
};
|
|
181
185
|
return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
|
|
182
186
|
}
|
|
183
|
-
},
|
|
184
|
-
async getItem(
|
|
185
|
-
return
|
|
187
|
+
}, v = /* @__PURE__ */ new Map(), J = {
|
|
188
|
+
async getItem(i) {
|
|
189
|
+
return v.get(i) ?? null;
|
|
186
190
|
},
|
|
187
|
-
async setItem(
|
|
188
|
-
|
|
191
|
+
async setItem(i, t) {
|
|
192
|
+
v.set(i, t);
|
|
189
193
|
},
|
|
190
|
-
async removeItem(
|
|
191
|
-
|
|
194
|
+
async removeItem(i) {
|
|
195
|
+
v.delete(i);
|
|
192
196
|
}
|
|
193
197
|
};
|
|
194
|
-
function
|
|
195
|
-
return
|
|
198
|
+
function L(i) {
|
|
199
|
+
return i || (F() ? M : typeof window < "u" && "localStorage" in window ? x : J);
|
|
196
200
|
}
|
|
197
|
-
const
|
|
201
|
+
const l = {
|
|
198
202
|
visitorId: "pw-visitor-id",
|
|
199
|
-
lastLoginMethod: (
|
|
200
|
-
lastLoginEmail: (
|
|
203
|
+
lastLoginMethod: (i) => `pw-${i}-last-login-method`,
|
|
204
|
+
lastLoginEmail: (i) => `pw-${i}-last-login-email`,
|
|
201
205
|
// last-known PaywallUser. Используется как offline-fallback на старте, пока
|
|
202
206
|
// первый getUser() не вернётся. Ключ зависит от paywallId+identity hash —
|
|
203
207
|
// переключение identity не должно отдавать чужой user.
|
|
204
|
-
userState: (
|
|
208
|
+
userState: (i, t) => `pw-${i}-${t}-user-v1`,
|
|
205
209
|
// Persisted auth bundle (access_token, refresh_token, expires_at, user) для
|
|
206
210
|
// одного пейвола. Ключ привязан к paywallId — мульти-пейвольное приложение
|
|
207
211
|
// не пересекает сессии. Bump '-v1' на breaking shape change.
|
|
208
|
-
authSession: (
|
|
212
|
+
authSession: (i) => `pw-${i}-auth-v1`,
|
|
209
213
|
// Refresh-token последнего анонимного юзера. Хранится отдельно от authSession,
|
|
210
214
|
// потому что должен пережить signOut: после signOut() юзер может опять
|
|
211
215
|
// зайти как тот же аноним — без капчи, через этот токен. signIn другим
|
|
212
216
|
// методом (email/oauth) тоже его не трогает. Чистится только явным
|
|
213
217
|
// signOut({forgetAnonymous: true}) или 401 от refresh-эндпоинта (значит
|
|
214
218
|
// токен отозван, дальше держать бессмысленно).
|
|
215
|
-
anonRefreshToken: (
|
|
219
|
+
anonRefreshToken: (i) => `pw-${i}-anon-rt-v1`,
|
|
216
220
|
// Persisted bootstrap (settings/prices/offers/layout/locales/version) для
|
|
217
221
|
// stale-while-revalidate. Не зависит от identity — layout одинаков для всех
|
|
218
222
|
// юзеров одного пейвола; user-state живёт отдельно под `userState(...)`.
|
|
219
223
|
// Bump '-v1' на breaking shape change.
|
|
220
|
-
bootstrap: (
|
|
224
|
+
bootstrap: (i) => `pw-${i}-bootstrap-v1`,
|
|
221
225
|
// Persisted balances (AI-провайдеры × tokenization_queries). Identity-bound,
|
|
222
226
|
// т.к. balance считается per-Bearer-юзеру; при re-login ключ меняется и
|
|
223
227
|
// чужие balances не видны. Меняются после оплаты (бэк) и API-вызовов
|
|
224
228
|
// (оптимистично через `decrementBalanceLocal`).
|
|
225
|
-
balances: (
|
|
229
|
+
balances: (i, t) => `pw-${i}-${t}-balances-v1`
|
|
226
230
|
};
|
|
227
|
-
function
|
|
228
|
-
const
|
|
229
|
-
if (
|
|
231
|
+
function C() {
|
|
232
|
+
const i = typeof globalThis < "u" ? globalThis.crypto : void 0;
|
|
233
|
+
if (i && typeof i.randomUUID == "function") return i.randomUUID();
|
|
230
234
|
const t = new Uint8Array(16);
|
|
231
|
-
if (
|
|
232
|
-
|
|
235
|
+
if (i && typeof i.getRandomValues == "function")
|
|
236
|
+
i.getRandomValues(t);
|
|
233
237
|
else
|
|
234
238
|
for (let s = 0; s < 16; s++) t[s] = Math.floor(Math.random() * 256);
|
|
235
239
|
t[6] = t[6] & 15 | 64, t[8] = t[8] & 63 | 128;
|
|
236
240
|
const e = Array.from(t, (s) => s.toString(16).padStart(2, "0")).join("");
|
|
237
241
|
return `${e.slice(0, 8)}-${e.slice(8, 12)}-${e.slice(12, 16)}-${e.slice(16, 20)}-${e.slice(20)}`;
|
|
238
242
|
}
|
|
239
|
-
async function
|
|
243
|
+
async function I(i) {
|
|
240
244
|
try {
|
|
241
|
-
const e = await
|
|
245
|
+
const e = await i.getItem(l.visitorId);
|
|
242
246
|
if (e && typeof e == "string" && e.length >= 16) return e;
|
|
243
247
|
} catch {
|
|
244
248
|
}
|
|
245
|
-
const t =
|
|
249
|
+
const t = C();
|
|
246
250
|
try {
|
|
247
|
-
await
|
|
251
|
+
await i.setItem(l.visitorId, t);
|
|
248
252
|
} catch {
|
|
249
253
|
}
|
|
250
254
|
return t;
|
|
251
255
|
}
|
|
252
|
-
const H = 5e3, V = 30 * 6e4,
|
|
256
|
+
const H = 5e3, V = 30 * 6e4, b = 60 * 6e4, j = 5 * 6e4, _ = {
|
|
253
257
|
has_active_subscription: !1,
|
|
254
258
|
purchases: [],
|
|
255
|
-
trial: null
|
|
259
|
+
trial: null,
|
|
260
|
+
had_previous_trial: !1
|
|
256
261
|
};
|
|
257
|
-
function
|
|
258
|
-
return
|
|
262
|
+
function B(i) {
|
|
263
|
+
return i && (i.email || i.userId || i.anonymousId) || "guest";
|
|
259
264
|
}
|
|
260
|
-
function
|
|
261
|
-
return
|
|
265
|
+
function G(i, t) {
|
|
266
|
+
return i === t ? !0 : !i || !t ? !1 : JSON.stringify(i) === JSON.stringify(t);
|
|
262
267
|
}
|
|
263
|
-
const
|
|
264
|
-
function Q(
|
|
265
|
-
if (
|
|
266
|
-
if (!
|
|
267
|
-
for (let e = 0; e <
|
|
268
|
-
if (
|
|
268
|
+
const X = 5e3, A = 5 * 6e4, z = 3e4;
|
|
269
|
+
function Q(i, t) {
|
|
270
|
+
if (i === t) return !0;
|
|
271
|
+
if (!i || !t || i.length !== t.length) return !1;
|
|
272
|
+
for (let e = 0; e < i.length; e++)
|
|
273
|
+
if (i[e].type !== t[e].type || i[e].count !== t[e].count) return !1;
|
|
269
274
|
return !0;
|
|
270
275
|
}
|
|
271
|
-
const W = "https://appbox.space";
|
|
272
276
|
class ut {
|
|
273
277
|
constructor(t) {
|
|
274
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)
|
|
275
|
-
throw new
|
|
276
|
-
|
|
279
|
+
throw new n("invalid_config", "paywallId is required");
|
|
280
|
+
if (!t.apiOrigin)
|
|
281
|
+
throw new n(
|
|
282
|
+
"invalid_config",
|
|
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.'
|
|
284
|
+
);
|
|
285
|
+
this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.capabilities = t.capabilities, this.auth = t.auth, this.previewMode = t.preview === !0;
|
|
277
286
|
const e = t.auth?.getCachedUser();
|
|
278
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(
|
|
279
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."
|
|
280
|
-
), this.storage =
|
|
289
|
+
), this.storage = L(t.storage), this.api = new E({
|
|
281
290
|
apiOrigin: this.apiOrigin,
|
|
282
291
|
paywallId: t.paywallId,
|
|
283
292
|
capabilities: t.capabilities,
|
|
@@ -286,10 +295,10 @@ class ut {
|
|
|
286
295
|
// делает lazy refresh, дедупит, на 401 возвращает null — тогда
|
|
287
296
|
// Authorization-хедер просто не выставится.
|
|
288
297
|
getAuthToken: t.auth ? () => t.auth.getAccessToken() : void 0
|
|
289
|
-
}), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s) => {
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
})), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise =
|
|
298
|
+
}), t.auth && (this.authUnsubscribe = t.auth.onAuthChange((s, a) => {
|
|
299
|
+
const r = a ? T(a.user) : void 0;
|
|
300
|
+
W(this.identity, r) || this.setIdentity(r);
|
|
301
|
+
})), this.hydrateUserFromStorage(), this.hydrateBootstrapFromStorage(), this.subscribeBootstrapStorage(), this.hydrateBalancesFromStorage(), this.subscribeBalancesStorage(), this.visitorIdPromise = I(this.storage).then((s) => (this.visitorId = s, s));
|
|
293
302
|
}
|
|
294
303
|
/**
|
|
295
304
|
* Stable visitor_id (UUID v4). Первый вызов awaitит первичный резолв из
|
|
@@ -297,7 +306,7 @@ class ut {
|
|
|
297
306
|
* EventTracker'ом для атрибуции аналитики.
|
|
298
307
|
*/
|
|
299
308
|
async getVisitorId() {
|
|
300
|
-
return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise =
|
|
309
|
+
return this.visitorId ? this.visitorId : (this.visitorIdPromise || (this.visitorIdPromise = I(this.storage).then((t) => (this.visitorId = t, t))), this.visitorIdPromise);
|
|
301
310
|
}
|
|
302
311
|
/** Sync-доступ к visitor_id. null если ещё не зарезолвили (первые ms жизни). */
|
|
303
312
|
getCachedVisitorId() {
|
|
@@ -328,14 +337,14 @@ class ut {
|
|
|
328
337
|
const e = typeof t == "boolean" ? { force: t } : t;
|
|
329
338
|
if (this.previewMode) {
|
|
330
339
|
if (this.cachedBootstrap) return this.cachedBootstrap;
|
|
331
|
-
throw new
|
|
340
|
+
throw new n(
|
|
332
341
|
"invalid_config",
|
|
333
342
|
"BillingClient in preview mode but cachedBootstrap is not seeded. Call setBootstrap(bootstrap) before open()."
|
|
334
343
|
);
|
|
335
344
|
}
|
|
336
|
-
const s = Date.now(),
|
|
337
|
-
return !e.force &&
|
|
338
|
-
}), this.cachedBootstrap) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
|
|
345
|
+
const s = Date.now(), a = this.cachedBootstrap && this.cachedBootstrapAt > 0 && s - this.cachedBootstrapAt < b;
|
|
346
|
+
return !e.force && a ? (s - this.cachedBootstrapAt > j && this.revalidateBootstrap(e.signal).catch(() => {
|
|
347
|
+
}), { ...this.cachedBootstrap, user: this.cachedUser ?? void 0 }) : this.inflightBootstrap ? this.inflightBootstrap : (this.inflightBootstrap = this.fetchBootstrap({
|
|
339
348
|
ifVersion: e.force ? void 0 : this.cachedBootstrap?.version,
|
|
340
349
|
signal: e.signal
|
|
341
350
|
}).finally(() => {
|
|
@@ -383,12 +392,12 @@ class ut {
|
|
|
383
392
|
offers: t.offers !== void 0 ? t.offers : e.offers,
|
|
384
393
|
version: `preview:${++this.previewVersionCounter}`
|
|
385
394
|
};
|
|
386
|
-
s.layout || (s.layout = U(s.settings, s.prices)),
|
|
387
|
-
for (const
|
|
395
|
+
s.layout || (s.layout = U(s.settings, s.prices)), w(s), this.cachedBootstrap = s, this.cachedBootstrapAt = Date.now();
|
|
396
|
+
for (const a of this.bootstrapListeners)
|
|
388
397
|
try {
|
|
389
|
-
|
|
390
|
-
} catch (
|
|
391
|
-
console.warn("[paywall] onBootstrapChange listener threw",
|
|
398
|
+
a(s);
|
|
399
|
+
} catch (r) {
|
|
400
|
+
console.warn("[paywall] onBootstrapChange listener threw", r);
|
|
392
401
|
}
|
|
393
402
|
}
|
|
394
403
|
// Network primitive — единая точка для force-запроса, revalidate'а и
|
|
@@ -398,14 +407,14 @@ class ut {
|
|
|
398
407
|
async fetchBootstrap(t) {
|
|
399
408
|
const e = {};
|
|
400
409
|
this.identity?.email && (e["X-User-Email"] = this.identity.email);
|
|
401
|
-
const s = t.ifVersion ? `/api/v1/paywall/${this.paywallId}/bootstrap?if_version=${encodeURIComponent(t.ifVersion)}` : `/api/v1/paywall/${this.paywallId}/bootstrap`,
|
|
410
|
+
const s = t.ifVersion ? `/api/v1/paywall/${this.paywallId}/bootstrap?if_version=${encodeURIComponent(t.ifVersion)}` : `/api/v1/paywall/${this.paywallId}/bootstrap`, a = await this.api.request(s, {
|
|
402
411
|
...Object.keys(e).length ? { headers: e } : {},
|
|
403
412
|
signal: t.signal
|
|
404
413
|
});
|
|
405
|
-
if ("unchanged" in
|
|
406
|
-
return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(),
|
|
407
|
-
const
|
|
408
|
-
return
|
|
414
|
+
if ("unchanged" in a && a.unchanged)
|
|
415
|
+
return this.cachedBootstrap ? (this.cachedBootstrapAt = Date.now(), a.user && this.applyUser(a.user), this.cachedBootstrap) : this.fetchBootstrap({ signal: t.signal });
|
|
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;
|
|
409
418
|
}
|
|
410
419
|
// Фоновый revalidate из stale-while-revalidate ветки. Дедуплицируется через
|
|
411
420
|
// `inflightBootstrap`, чтобы параллельные revalidate'ы не пересекались.
|
|
@@ -425,26 +434,26 @@ class ut {
|
|
|
425
434
|
applyBootstrap(t, { persist: e }) {
|
|
426
435
|
const s = !this.cachedBootstrap || this.cachedBootstrap.version !== t.version;
|
|
427
436
|
if (this.cachedBootstrap = t, this.cachedBootstrapAt = Date.now(), e && this.persistBootstrap(t), s)
|
|
428
|
-
for (const
|
|
437
|
+
for (const a of this.bootstrapListeners)
|
|
429
438
|
try {
|
|
430
|
-
|
|
431
|
-
} catch (
|
|
432
|
-
console.warn("[paywall] onBootstrapChange listener threw",
|
|
439
|
+
a(t);
|
|
440
|
+
} catch (r) {
|
|
441
|
+
console.warn("[paywall] onBootstrapChange listener threw", r);
|
|
433
442
|
}
|
|
434
443
|
}
|
|
435
444
|
async hydrateBootstrapFromStorage() {
|
|
436
445
|
if (!this.cachedBootstrap)
|
|
437
446
|
try {
|
|
438
|
-
const t = await this.storage.getItem(
|
|
447
|
+
const t = await this.storage.getItem(l.bootstrap(this.paywallId));
|
|
439
448
|
if (!t) return;
|
|
440
449
|
const e = JSON.parse(t);
|
|
441
|
-
if (!e?.bootstrap || Date.now() - e.at >
|
|
442
|
-
|
|
450
|
+
if (!e?.bootstrap || Date.now() - e.at > b || this.cachedBootstrap) return;
|
|
451
|
+
w(e.bootstrap), this.cachedBootstrap = e.bootstrap, this.cachedBootstrapAt = e.at;
|
|
443
452
|
for (const s of this.bootstrapListeners)
|
|
444
453
|
try {
|
|
445
454
|
s(e.bootstrap);
|
|
446
|
-
} catch (
|
|
447
|
-
console.warn("[paywall] onBootstrapChange listener threw",
|
|
455
|
+
} catch (a) {
|
|
456
|
+
console.warn("[paywall] onBootstrapChange listener threw", a);
|
|
448
457
|
}
|
|
449
458
|
} catch {
|
|
450
459
|
}
|
|
@@ -454,7 +463,7 @@ class ut {
|
|
|
454
463
|
try {
|
|
455
464
|
const { user: e, ...s } = t;
|
|
456
465
|
await this.storage.setItem(
|
|
457
|
-
|
|
466
|
+
l.bootstrap(this.paywallId),
|
|
458
467
|
JSON.stringify({ at: Date.now(), bootstrap: s })
|
|
459
468
|
);
|
|
460
469
|
} catch {
|
|
@@ -465,7 +474,7 @@ class ut {
|
|
|
465
474
|
// no-op, всё работает как раньше через сеть.
|
|
466
475
|
subscribeBootstrapStorage() {
|
|
467
476
|
typeof this.storage.watch == "function" && (this.bootstrapStorageUnwatch = this.storage.watch(
|
|
468
|
-
|
|
477
|
+
l.bootstrap(this.paywallId),
|
|
469
478
|
(t) => {
|
|
470
479
|
if (t)
|
|
471
480
|
try {
|
|
@@ -475,7 +484,7 @@ class ut {
|
|
|
475
484
|
this.cachedBootstrapAt = e.at;
|
|
476
485
|
return;
|
|
477
486
|
}
|
|
478
|
-
|
|
487
|
+
w(e.bootstrap), this.applyBootstrap(e.bootstrap, { persist: !1 });
|
|
479
488
|
} catch {
|
|
480
489
|
}
|
|
481
490
|
}
|
|
@@ -522,7 +531,7 @@ class ut {
|
|
|
522
531
|
* есть `navigator.language`.
|
|
523
532
|
*/
|
|
524
533
|
getUserLanguage() {
|
|
525
|
-
const t = typeof navigator < "u" && navigator.language ? navigator.language : null, e = this.cachedBootstrap?.settings.locale_default ?? null, s = this.cachedBootstrap ?
|
|
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;
|
|
526
535
|
return { tag: s ?? t ?? e, applied: s, browserLanguage: t, countryLanguage: e };
|
|
527
536
|
}
|
|
528
537
|
/**
|
|
@@ -537,7 +546,7 @@ class ut {
|
|
|
537
546
|
return !t && this.cachedUser && Date.now() - this.cachedUserAt < H ? this.cachedUser : this.inflightUser ? this.inflightUser : (this.inflightUser = (async () => {
|
|
538
547
|
try {
|
|
539
548
|
if (!this.identity?.email)
|
|
540
|
-
return this.applyUser(
|
|
549
|
+
return this.applyUser(_), _;
|
|
541
550
|
const s = await this.api.request(
|
|
542
551
|
`/api/v1/paywall/${this.paywallId}/user-state`,
|
|
543
552
|
{ headers: { "X-User-Email": this.identity.email }, signal: e }
|
|
@@ -569,16 +578,16 @@ class ut {
|
|
|
569
578
|
this.userListeners.add(t);
|
|
570
579
|
const s = e.immediate ?? "microtask";
|
|
571
580
|
if (this.cachedUser && s !== "none") {
|
|
572
|
-
const
|
|
581
|
+
const a = this.cachedUser;
|
|
573
582
|
if (s === "sync")
|
|
574
583
|
try {
|
|
575
|
-
t(
|
|
576
|
-
} catch (
|
|
577
|
-
console.warn("[paywall] onUserChange initial sync threw",
|
|
584
|
+
t(a);
|
|
585
|
+
} catch (r) {
|
|
586
|
+
console.warn("[paywall] onUserChange initial sync threw", r);
|
|
578
587
|
}
|
|
579
588
|
else
|
|
580
589
|
queueMicrotask(() => {
|
|
581
|
-
this.userListeners.has(t) && t(
|
|
590
|
+
this.userListeners.has(t) && t(a);
|
|
582
591
|
});
|
|
583
592
|
}
|
|
584
593
|
return () => {
|
|
@@ -590,19 +599,19 @@ class ut {
|
|
|
590
599
|
return this.cachedUser;
|
|
591
600
|
}
|
|
592
601
|
applyUser(t) {
|
|
593
|
-
const e = !
|
|
602
|
+
const e = !G(this.cachedUser, t);
|
|
594
603
|
if (this.cachedUser = t, this.cachedUserAt = Date.now(), e) {
|
|
595
604
|
this.persistUser(t);
|
|
596
605
|
for (const s of this.userListeners)
|
|
597
606
|
try {
|
|
598
607
|
s(t);
|
|
599
|
-
} catch (
|
|
600
|
-
console.warn("[paywall] onUserChange listener threw",
|
|
608
|
+
} catch (a) {
|
|
609
|
+
console.warn("[paywall] onUserChange listener threw", a);
|
|
601
610
|
}
|
|
602
611
|
}
|
|
603
612
|
}
|
|
604
613
|
storageKey() {
|
|
605
|
-
return
|
|
614
|
+
return l.userState(this.paywallId, B(this.identity));
|
|
606
615
|
}
|
|
607
616
|
async hydrateUserFromStorage() {
|
|
608
617
|
if (!this.cachedUser)
|
|
@@ -638,8 +647,8 @@ class ut {
|
|
|
638
647
|
* по `currentBalance` в QuotaExceededError или `balances.length`.
|
|
639
648
|
*/
|
|
640
649
|
async getBalances({ force: t = !1, signal: e } = {}) {
|
|
641
|
-
const s = Date.now(),
|
|
642
|
-
return !t && this.cachedBalances && (
|
|
650
|
+
const s = Date.now(), a = this.cachedBalances ? s - this.cachedBalancesAt : 1 / 0;
|
|
651
|
+
return !t && this.cachedBalances && (a < X || a < z) ? this.cachedBalances : !t && this.cachedBalances && a < A ? (this.fetchBalances({ signal: e }).catch(() => {
|
|
643
652
|
}), this.cachedBalances) : this.inflightBalances ? this.inflightBalances : this.fetchBalances({ signal: e });
|
|
644
653
|
}
|
|
645
654
|
// Network primitive — единая точка для force/stale-revalidate/cold-start.
|
|
@@ -670,16 +679,16 @@ class ut {
|
|
|
670
679
|
this.balanceListeners.add(t);
|
|
671
680
|
const s = e.immediate ?? "microtask";
|
|
672
681
|
if (this.cachedBalances && s !== "none") {
|
|
673
|
-
const
|
|
682
|
+
const a = this.cachedBalances;
|
|
674
683
|
if (s === "sync")
|
|
675
684
|
try {
|
|
676
|
-
t(
|
|
677
|
-
} catch (
|
|
678
|
-
console.warn("[paywall] onBalanceChange initial sync threw",
|
|
685
|
+
t(a);
|
|
686
|
+
} catch (r) {
|
|
687
|
+
console.warn("[paywall] onBalanceChange initial sync threw", r);
|
|
679
688
|
}
|
|
680
689
|
else
|
|
681
690
|
queueMicrotask(() => {
|
|
682
|
-
this.balanceListeners.has(t) && t(
|
|
691
|
+
this.balanceListeners.has(t) && t(a);
|
|
683
692
|
});
|
|
684
693
|
}
|
|
685
694
|
return () => {
|
|
@@ -706,12 +715,12 @@ class ut {
|
|
|
706
715
|
return;
|
|
707
716
|
}
|
|
708
717
|
if (!this.cachedBalances) return;
|
|
709
|
-
const e = this.cachedBalances.findIndex((
|
|
718
|
+
const e = this.cachedBalances.findIndex((r) => r.type === t);
|
|
710
719
|
if (e < 0 || this.cachedBalances[e].count <= 0) return;
|
|
711
|
-
const
|
|
712
|
-
(
|
|
720
|
+
const a = this.cachedBalances.map(
|
|
721
|
+
(r, o) => o === e ? { ...r, count: r.count - 1 } : r
|
|
713
722
|
);
|
|
714
|
-
this.applyBalances(
|
|
723
|
+
this.applyBalances(a);
|
|
715
724
|
}
|
|
716
725
|
/** Принудительный re-fetch — типичный вызов после QuotaExceededError, чтобы
|
|
717
726
|
* UI получил актуальный balance=0 и нарисовал upgrade-prompt. */
|
|
@@ -730,7 +739,7 @@ class ut {
|
|
|
730
739
|
*/
|
|
731
740
|
createApiGatewayClient(t = {}) {
|
|
732
741
|
const e = t.onChargeSuccess, s = t.onQuotaExceeded;
|
|
733
|
-
return new
|
|
742
|
+
return new q({
|
|
734
743
|
paywallId: this.paywallId,
|
|
735
744
|
apiOrigin: this.apiOrigin,
|
|
736
745
|
auth: this.auth,
|
|
@@ -738,26 +747,26 @@ class ut {
|
|
|
738
747
|
capabilities: this.capabilities,
|
|
739
748
|
fetch: this.fetchImpl,
|
|
740
749
|
...t,
|
|
741
|
-
onChargeSuccess: (
|
|
742
|
-
this.decrementBalanceLocal(
|
|
750
|
+
onChargeSuccess: (a) => {
|
|
751
|
+
this.decrementBalanceLocal(a), e?.(a);
|
|
743
752
|
},
|
|
744
|
-
onQuotaExceeded: (
|
|
745
|
-
this.refreshBalances(), s?.(
|
|
753
|
+
onQuotaExceeded: (a) => {
|
|
754
|
+
this.refreshBalances(), s?.(a);
|
|
746
755
|
}
|
|
747
756
|
});
|
|
748
757
|
}
|
|
749
758
|
applyBalances(t, { persist: e = !0 } = {}) {
|
|
750
759
|
const s = !Q(this.cachedBalances, t);
|
|
751
760
|
if (this.cachedBalances = t, this.cachedBalancesAt = Date.now(), e && this.persistBalances(t), s)
|
|
752
|
-
for (const
|
|
761
|
+
for (const a of this.balanceListeners)
|
|
753
762
|
try {
|
|
754
|
-
|
|
755
|
-
} catch (
|
|
756
|
-
console.warn("[paywall] onBalanceChange listener threw",
|
|
763
|
+
a(t);
|
|
764
|
+
} catch (r) {
|
|
765
|
+
console.warn("[paywall] onBalanceChange listener threw", r);
|
|
757
766
|
}
|
|
758
767
|
}
|
|
759
768
|
balancesStorageKey() {
|
|
760
|
-
return
|
|
769
|
+
return l.balances(this.paywallId, B(this.identity));
|
|
761
770
|
}
|
|
762
771
|
async hydrateBalancesFromStorage() {
|
|
763
772
|
if (!this.cachedBalances)
|
|
@@ -770,8 +779,8 @@ class ut {
|
|
|
770
779
|
for (const s of this.balanceListeners)
|
|
771
780
|
try {
|
|
772
781
|
s(e.balances);
|
|
773
|
-
} catch (
|
|
774
|
-
console.warn("[paywall] onBalanceChange listener threw",
|
|
782
|
+
} catch (a) {
|
|
783
|
+
console.warn("[paywall] onBalanceChange listener threw", a);
|
|
775
784
|
}
|
|
776
785
|
} catch {
|
|
777
786
|
}
|
|
@@ -804,42 +813,42 @@ class ut {
|
|
|
804
813
|
}
|
|
805
814
|
async createCheckout(t) {
|
|
806
815
|
if (!this.identity?.email)
|
|
807
|
-
throw new
|
|
816
|
+
throw new n(
|
|
808
817
|
"identity_required",
|
|
809
818
|
"createCheckout requires identity with email"
|
|
810
819
|
);
|
|
811
820
|
const e = t.idempotencyKey ?? `auto:${t.priceId}`, s = this.inflightCheckouts.get(e);
|
|
812
821
|
if (s) return s;
|
|
813
|
-
const
|
|
814
|
-
"Idempotency-Key": t.idempotencyKey ??
|
|
822
|
+
const r = {
|
|
823
|
+
"Idempotency-Key": t.idempotencyKey ?? C()
|
|
815
824
|
};
|
|
816
|
-
this.apiKey && (
|
|
817
|
-
const o = this.cachedBootstrap?.settings,
|
|
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`, {
|
|
818
827
|
method: "POST",
|
|
819
|
-
headers:
|
|
828
|
+
headers: r,
|
|
820
829
|
signal: t.signal,
|
|
821
830
|
body: JSON.stringify({
|
|
822
831
|
email: this.identity.email,
|
|
823
832
|
priceId: Number(t.priceId),
|
|
824
|
-
successUrl:
|
|
833
|
+
successUrl: d,
|
|
825
834
|
errorUrl: t.errorUrl,
|
|
826
|
-
shopUrl:
|
|
835
|
+
shopUrl: u,
|
|
827
836
|
productName: o?.checkout_product_name ?? void 0,
|
|
828
837
|
trial_days: t.trialDays,
|
|
829
838
|
ignoreActivePurchase: t.ignoreActivePurchase ? !0 : void 0,
|
|
830
839
|
userMeta: this.identity.userId ? { userId: this.identity.userId } : void 0
|
|
831
840
|
})
|
|
832
841
|
}).then((h) => ({ url: h.checkoutUrl, acquiring: h.acquiring })).catch((h) => {
|
|
833
|
-
throw h instanceof
|
|
842
|
+
throw h instanceof n && h.status === 409 && h.cause && typeof h.cause == "object" && h.cause.hasActivePurchase === !0 ? new n(
|
|
834
843
|
"already_purchased",
|
|
835
844
|
"You already have an active subscription",
|
|
836
845
|
{ status: 409, cause: h.cause }
|
|
837
846
|
) : h;
|
|
838
847
|
});
|
|
839
|
-
return this.inflightCheckouts.set(e,
|
|
840
|
-
this.inflightCheckouts.get(e) ===
|
|
848
|
+
return this.inflightCheckouts.set(e, f), f.finally(() => {
|
|
849
|
+
this.inflightCheckouts.get(e) === f && this.inflightCheckouts.delete(e);
|
|
841
850
|
}).catch(() => {
|
|
842
|
-
}),
|
|
851
|
+
}), f;
|
|
843
852
|
}
|
|
844
853
|
/**
|
|
845
854
|
* URL Stripe/Paddle/Chargebee customer portal — место, где залогиненный
|
|
@@ -859,7 +868,7 @@ class ut {
|
|
|
859
868
|
*/
|
|
860
869
|
async getCustomerPortalUrl(t = {}) {
|
|
861
870
|
if (!this.auth && !this.apiKey && !this.identity?.email)
|
|
862
|
-
throw new
|
|
871
|
+
throw new n(
|
|
863
872
|
"identity_required",
|
|
864
873
|
"getCustomerPortalUrl requires auth, apiKey, or identity.email"
|
|
865
874
|
);
|
|
@@ -891,7 +900,7 @@ class ut {
|
|
|
891
900
|
*/
|
|
892
901
|
async listPurchases(t = {}) {
|
|
893
902
|
if (!this.auth)
|
|
894
|
-
throw new
|
|
903
|
+
throw new n(
|
|
895
904
|
"auth_required",
|
|
896
905
|
"listPurchases requires AuthClient (Bearer auth)"
|
|
897
906
|
);
|
|
@@ -912,7 +921,7 @@ class ut {
|
|
|
912
921
|
*/
|
|
913
922
|
async cancelSubscription(t) {
|
|
914
923
|
if (!this.auth)
|
|
915
|
-
throw new
|
|
924
|
+
throw new n(
|
|
916
925
|
"auth_required",
|
|
917
926
|
"cancelSubscription requires AuthClient (Bearer auth)"
|
|
918
927
|
);
|
|
@@ -937,12 +946,12 @@ class ut {
|
|
|
937
946
|
async createSupportTicket(t) {
|
|
938
947
|
const e = t.email ?? this.identity?.email ?? null, s = `/api/v1/paywall/${this.paywallId}/support/ticket`;
|
|
939
948
|
if (!!t.files && t.files.length > 0) {
|
|
940
|
-
const
|
|
941
|
-
|
|
942
|
-
for (const o of t.files)
|
|
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);
|
|
943
952
|
return this.api.request(s, {
|
|
944
953
|
method: "POST",
|
|
945
|
-
body:
|
|
954
|
+
body: r
|
|
946
955
|
});
|
|
947
956
|
}
|
|
948
957
|
return this.api.request(s, {
|
|
@@ -955,80 +964,108 @@ class ut {
|
|
|
955
964
|
});
|
|
956
965
|
}
|
|
957
966
|
}
|
|
958
|
-
function T(
|
|
959
|
-
return { email:
|
|
967
|
+
function T(i) {
|
|
968
|
+
return { email: i.email, userId: i.id };
|
|
969
|
+
}
|
|
970
|
+
function W(i, t) {
|
|
971
|
+
return i === t ? !0 : !i || !t ? !1 : i.email === t.email && i.userId === t.userId && i.anonymousId === t.anonymousId;
|
|
960
972
|
}
|
|
961
|
-
function
|
|
962
|
-
|
|
973
|
+
function O(i) {
|
|
974
|
+
if (!i) return null;
|
|
975
|
+
const t = i.trim();
|
|
976
|
+
if (!t) return null;
|
|
977
|
+
try {
|
|
978
|
+
return new URL(t.includes("://") ? t : `https://${t}`).origin;
|
|
979
|
+
} catch {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
963
982
|
}
|
|
964
|
-
function
|
|
983
|
+
function Y(i, t) {
|
|
984
|
+
const e = O(i);
|
|
985
|
+
if (!(!e || O(t) === e))
|
|
986
|
+
throw new n(
|
|
987
|
+
"invalid_config",
|
|
988
|
+
`apiOrigin mismatch: SDK initialized with "${t}" but paywall is configured with custom_domain "${i}". Use the custom_domain from the platform paywall settings.`
|
|
989
|
+
);
|
|
990
|
+
}
|
|
991
|
+
function U(i, t) {
|
|
965
992
|
return {
|
|
966
993
|
type: "modal",
|
|
967
994
|
blocks: [
|
|
968
|
-
|
|
995
|
+
// offer_banner НЕ в default layout — PaywallRoot рендерит его как
|
|
996
|
+
// top-tab над dialog'ом (rounded-top, negative margin), за пределами
|
|
997
|
+
// scrollable area. Блок остаётся в registry для opt-in inline-вариантa.
|
|
998
|
+
{ type: "heading", text: i.name || "Upgrade", level: 1 },
|
|
969
999
|
{ type: "price_grid", priceIds: t.map((e) => e.id) },
|
|
970
|
-
{ type: "cta_button",
|
|
1000
|
+
{ type: "cta_button", action: "checkout" },
|
|
1001
|
+
{ type: "guarantee_badge" },
|
|
1002
|
+
{ type: "current_session" }
|
|
971
1003
|
]
|
|
972
1004
|
};
|
|
973
1005
|
}
|
|
974
|
-
function
|
|
975
|
-
const t =
|
|
1006
|
+
function N(i) {
|
|
1007
|
+
const t = i.locales;
|
|
976
1008
|
if (!t) return null;
|
|
977
1009
|
const e = [];
|
|
978
1010
|
if (typeof navigator < "u") {
|
|
979
1011
|
navigator.language && e.push(navigator.language);
|
|
980
|
-
const
|
|
981
|
-
|
|
1012
|
+
const a = navigator.language?.split("-")[0];
|
|
1013
|
+
a && a !== navigator.language && e.push(a);
|
|
982
1014
|
}
|
|
983
|
-
const s =
|
|
1015
|
+
const s = i.settings.locale_default;
|
|
984
1016
|
s && e.push(s);
|
|
985
|
-
for (const
|
|
986
|
-
if (
|
|
1017
|
+
for (const a of e)
|
|
1018
|
+
if (a && Object.prototype.hasOwnProperty.call(t, a)) return a;
|
|
987
1019
|
return null;
|
|
988
1020
|
}
|
|
989
|
-
function
|
|
990
|
-
const t =
|
|
1021
|
+
function w(i) {
|
|
1022
|
+
const t = N(i);
|
|
991
1023
|
if (!t) return;
|
|
992
|
-
const e =
|
|
993
|
-
e && (e.layout && (
|
|
994
|
-
const
|
|
995
|
-
if (!
|
|
996
|
-
const
|
|
997
|
-
return "label" in
|
|
1024
|
+
const e = i.locales?.[t];
|
|
1025
|
+
e && (e.layout && (i.layout = e.layout), e.prices && (i.prices = i.prices.map((s) => {
|
|
1026
|
+
const a = e.prices?.[s.id];
|
|
1027
|
+
if (!a) return s;
|
|
1028
|
+
const r = { ...s };
|
|
1029
|
+
return "label" in a && (r.label = a.label ?? null), "description" in a && (r.description = a.description ?? null), r;
|
|
998
1030
|
})));
|
|
999
1031
|
}
|
|
1000
|
-
function P(
|
|
1001
|
-
const t = new Uint8Array(
|
|
1032
|
+
function P(i) {
|
|
1033
|
+
const t = new Uint8Array(i), e = typeof globalThis < "u" ? globalThis.crypto : void 0;
|
|
1002
1034
|
if (e && typeof e.getRandomValues == "function")
|
|
1003
1035
|
e.getRandomValues(t);
|
|
1004
1036
|
else
|
|
1005
|
-
for (let s = 0; s <
|
|
1037
|
+
for (let s = 0; s < i; s++) t[s] = Math.floor(Math.random() * 256);
|
|
1006
1038
|
return t;
|
|
1007
1039
|
}
|
|
1008
|
-
function
|
|
1040
|
+
function S(i) {
|
|
1009
1041
|
let t = "";
|
|
1010
|
-
for (let e = 0; e <
|
|
1042
|
+
for (let e = 0; e < i.length; e++) t += String.fromCharCode(i[e]);
|
|
1011
1043
|
return btoa(t).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1012
1044
|
}
|
|
1013
1045
|
function Z() {
|
|
1014
|
-
return
|
|
1046
|
+
return S(P(64));
|
|
1015
1047
|
}
|
|
1016
|
-
async function tt(
|
|
1017
|
-
const t = new TextEncoder().encode(
|
|
1048
|
+
async function tt(i) {
|
|
1049
|
+
const t = new TextEncoder().encode(i), e = globalThis.crypto;
|
|
1018
1050
|
if (!e?.subtle?.digest)
|
|
1019
1051
|
throw new Error("crypto.subtle is required for PKCE");
|
|
1020
1052
|
const s = await e.subtle.digest("SHA-256", t);
|
|
1021
|
-
return
|
|
1053
|
+
return S(new Uint8Array(s));
|
|
1022
1054
|
}
|
|
1023
1055
|
function et() {
|
|
1024
|
-
return
|
|
1056
|
+
return S(P(16));
|
|
1025
1057
|
}
|
|
1026
|
-
const st =
|
|
1058
|
+
const st = 6e4, it = 600 * 1e3;
|
|
1027
1059
|
class dt {
|
|
1028
1060
|
constructor(t) {
|
|
1029
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)
|
|
1030
|
-
throw new
|
|
1031
|
-
|
|
1062
|
+
throw new n("invalid_config", "paywallId is required");
|
|
1063
|
+
if (!t.apiOrigin)
|
|
1064
|
+
throw new n(
|
|
1065
|
+
"invalid_config",
|
|
1066
|
+
"apiOrigin is required. Pass the paywall custom_domain configured in the platform."
|
|
1067
|
+
);
|
|
1068
|
+
this.paywallId = t.paywallId, this.apiOrigin = t.apiOrigin, this.storage = L(t.storage), this.api = new E({
|
|
1032
1069
|
apiOrigin: this.apiOrigin,
|
|
1033
1070
|
paywallId: t.paywallId,
|
|
1034
1071
|
fetch: t.fetch
|
|
@@ -1054,14 +1091,15 @@ class dt {
|
|
|
1054
1091
|
async applyExternalSession(t) {
|
|
1055
1092
|
if (!this.destroyed && (await this.hydrated, !this.destroyed)) {
|
|
1056
1093
|
if (t == null) {
|
|
1057
|
-
this.session && this.setSession(null, { skipPersist: !0 });
|
|
1094
|
+
this.session && this.setSession(null, { skipPersist: !0, event: "SIGNED_OUT" });
|
|
1058
1095
|
return;
|
|
1059
1096
|
}
|
|
1060
1097
|
try {
|
|
1061
1098
|
const e = JSON.parse(t);
|
|
1062
1099
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
1063
1100
|
return;
|
|
1064
|
-
this.
|
|
1101
|
+
const s = !this.session || this.session.user.id !== e.user.id ? "SIGNED_IN" : "TOKEN_REFRESHED";
|
|
1102
|
+
this.setSession(e, { skipPersist: !0, event: s });
|
|
1065
1103
|
} catch {
|
|
1066
1104
|
}
|
|
1067
1105
|
}
|
|
@@ -1105,7 +1143,7 @@ class dt {
|
|
|
1105
1143
|
await this.hydrated;
|
|
1106
1144
|
const e = await this.readVisitorId(), s = {};
|
|
1107
1145
|
t.idempotencyKey && (s["Idempotency-Key"] = t.idempotencyKey);
|
|
1108
|
-
const
|
|
1146
|
+
const a = await this.api.request(
|
|
1109
1147
|
`/api/v1/paywall/${this.paywallId}/auth/email/signin`,
|
|
1110
1148
|
{
|
|
1111
1149
|
method: "POST",
|
|
@@ -1117,8 +1155,8 @@ class dt {
|
|
|
1117
1155
|
user_meta: t.userMeta
|
|
1118
1156
|
})
|
|
1119
1157
|
}
|
|
1120
|
-
),
|
|
1121
|
-
return this.setSession(
|
|
1158
|
+
), r = this.toSession(a, a.user);
|
|
1159
|
+
return this.setSession(r, { event: "SIGNED_IN" }), this.recordLastLogin("email", t.email), r;
|
|
1122
1160
|
}
|
|
1123
1161
|
/**
|
|
1124
1162
|
* Signup. Если в Supabase включён email confirm — сервер возвращает
|
|
@@ -1130,7 +1168,7 @@ class dt {
|
|
|
1130
1168
|
await this.hydrated;
|
|
1131
1169
|
const e = await this.readVisitorId(), s = {};
|
|
1132
1170
|
t.idempotencyKey && (s["Idempotency-Key"] = t.idempotencyKey);
|
|
1133
|
-
const
|
|
1171
|
+
const a = await this.api.request(
|
|
1134
1172
|
`/api/v1/paywall/${this.paywallId}/auth/email/signup`,
|
|
1135
1173
|
{
|
|
1136
1174
|
method: "POST",
|
|
@@ -1143,10 +1181,10 @@ class dt {
|
|
|
1143
1181
|
})
|
|
1144
1182
|
}
|
|
1145
1183
|
);
|
|
1146
|
-
if (
|
|
1147
|
-
return { kind: "confirmation_required", user:
|
|
1148
|
-
const
|
|
1149
|
-
return this.setSession(
|
|
1184
|
+
if (a.status === "confirmation_required")
|
|
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 };
|
|
1150
1188
|
}
|
|
1151
1189
|
/**
|
|
1152
1190
|
* Повторная отправка confirmation-email после signUp с включённым
|
|
@@ -1209,8 +1247,8 @@ class dt {
|
|
|
1209
1247
|
user_meta: t.userMeta
|
|
1210
1248
|
})
|
|
1211
1249
|
}
|
|
1212
|
-
),
|
|
1213
|
-
return this.setSession(
|
|
1250
|
+
), a = this.toSession(s, s.user), r = t.type === "recovery" ? "PASSWORD_RECOVERY" : "SIGNED_IN";
|
|
1251
|
+
return this.setSession(a, { event: r }), a;
|
|
1214
1252
|
}
|
|
1215
1253
|
/**
|
|
1216
1254
|
* Запрос recovery email. Бэк всегда ok, чтобы не палить enumeration.
|
|
@@ -1236,7 +1274,7 @@ class dt {
|
|
|
1236
1274
|
await this.hydrated;
|
|
1237
1275
|
const e = await this.getAccessToken();
|
|
1238
1276
|
if (!e)
|
|
1239
|
-
throw new
|
|
1277
|
+
throw new n("not_authenticated", "no active session");
|
|
1240
1278
|
await this.api.request(
|
|
1241
1279
|
`/api/v1/paywall/${this.paywallId}/auth/password/update`,
|
|
1242
1280
|
{
|
|
@@ -1297,12 +1335,12 @@ class dt {
|
|
|
1297
1335
|
user_meta: t.userMeta
|
|
1298
1336
|
})
|
|
1299
1337
|
}
|
|
1300
|
-
),
|
|
1338
|
+
), a = {
|
|
1301
1339
|
...s.user,
|
|
1302
1340
|
email: s.user.email ?? null,
|
|
1303
1341
|
is_anonymous: !0
|
|
1304
|
-
},
|
|
1305
|
-
return this.setSession(
|
|
1342
|
+
}, r = this.toSession(s, a);
|
|
1343
|
+
return this.setSession(r, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(r.refresh_token), r;
|
|
1306
1344
|
})();
|
|
1307
1345
|
try {
|
|
1308
1346
|
return await this.inflightAnonSignin;
|
|
@@ -1323,10 +1361,10 @@ class dt {
|
|
|
1323
1361
|
const e = await this.api.request(
|
|
1324
1362
|
`/api/v1/paywall/${this.paywallId}/auth/refresh`,
|
|
1325
1363
|
{ method: "POST", body: JSON.stringify({ refresh_token: t }) }
|
|
1326
|
-
), s = this.session?.user.is_anonymous === !0 ? this.session.user : { id: "", email: null, is_anonymous: !0 },
|
|
1327
|
-
return this.setSession(
|
|
1364
|
+
), s = this.session?.user.is_anonymous === !0 ? this.session.user : { id: "", email: null, is_anonymous: !0 }, a = this.toSession(e, s);
|
|
1365
|
+
return this.setSession(a, { event: "SIGNED_IN" }), await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
1328
1366
|
} catch (e) {
|
|
1329
|
-
if (e instanceof
|
|
1367
|
+
if (e instanceof n && e.status === 401)
|
|
1330
1368
|
return await this.clearAnonRefreshToken(), null;
|
|
1331
1369
|
throw e;
|
|
1332
1370
|
}
|
|
@@ -1356,12 +1394,12 @@ class dt {
|
|
|
1356
1394
|
await this.hydrated;
|
|
1357
1395
|
const e = await this.getAccessToken();
|
|
1358
1396
|
if (!e)
|
|
1359
|
-
throw new
|
|
1397
|
+
throw new n("not_authenticated", "no active session");
|
|
1360
1398
|
const s = {
|
|
1361
1399
|
Authorization: `Bearer ${e}`
|
|
1362
1400
|
};
|
|
1363
1401
|
t.idempotencyKey && (s["Idempotency-Key"] = t.idempotencyKey);
|
|
1364
|
-
const
|
|
1402
|
+
const a = await this.api.request(
|
|
1365
1403
|
`/api/v1/paywall/${this.paywallId}/auth/anonymous/upgrade`,
|
|
1366
1404
|
{
|
|
1367
1405
|
method: "POST",
|
|
@@ -1373,21 +1411,21 @@ class dt {
|
|
|
1373
1411
|
})
|
|
1374
1412
|
}
|
|
1375
1413
|
);
|
|
1376
|
-
if (
|
|
1377
|
-
return { kind: "confirmation_required", email:
|
|
1378
|
-
const
|
|
1379
|
-
if (!
|
|
1380
|
-
throw new
|
|
1414
|
+
if (a.status === "confirmation_required")
|
|
1415
|
+
return { kind: "confirmation_required", email: a.email };
|
|
1416
|
+
const r = this.session;
|
|
1417
|
+
if (!r)
|
|
1418
|
+
throw new n(
|
|
1381
1419
|
"not_authenticated",
|
|
1382
1420
|
"session disappeared during upgrade"
|
|
1383
1421
|
);
|
|
1384
1422
|
const o = {
|
|
1385
|
-
...
|
|
1386
|
-
id:
|
|
1387
|
-
email:
|
|
1388
|
-
is_anonymous:
|
|
1389
|
-
},
|
|
1390
|
-
return this.setSession(
|
|
1423
|
+
...r.user,
|
|
1424
|
+
id: a.user.id,
|
|
1425
|
+
email: a.user.email,
|
|
1426
|
+
is_anonymous: a.user.is_anonymous ?? !1
|
|
1427
|
+
}, d = { ...r, user: o };
|
|
1428
|
+
return this.setSession(d, { event: "USER_UPDATED" }), await this.clearAnonRefreshToken(), { kind: "updated", session: d };
|
|
1391
1429
|
}
|
|
1392
1430
|
/**
|
|
1393
1431
|
* OAuth signin через popup с PKCE. Жизненный цикл:
|
|
@@ -1411,22 +1449,22 @@ class dt {
|
|
|
1411
1449
|
*/
|
|
1412
1450
|
async signInWithOAuth(t) {
|
|
1413
1451
|
if (typeof window > "u")
|
|
1414
|
-
throw new
|
|
1452
|
+
throw new n("oauth_unavailable", "window is required for OAuth");
|
|
1415
1453
|
const { authorize_url: e, state: s } = await this.startOAuthFlow({
|
|
1416
1454
|
provider: t.provider,
|
|
1417
1455
|
scopes: t.scopes,
|
|
1418
1456
|
userMeta: t.userMeta
|
|
1419
|
-
}),
|
|
1420
|
-
if (!
|
|
1421
|
-
throw this.oauthFlows.delete(s), new
|
|
1457
|
+
}), a = this.openPopup(e, `pw-oauth-${s}`);
|
|
1458
|
+
if (!a)
|
|
1459
|
+
throw this.oauthFlows.delete(s), new n(
|
|
1422
1460
|
"popup_blocked",
|
|
1423
1461
|
"browser blocked auth popup — call from a user gesture"
|
|
1424
1462
|
);
|
|
1425
1463
|
t.onPopupOpened?.();
|
|
1426
|
-
const
|
|
1464
|
+
const r = await nt(a, s);
|
|
1427
1465
|
if (this.destroyed)
|
|
1428
|
-
throw this.oauthFlows.delete(s), new
|
|
1429
|
-
return this.completeOAuthFlow({ state: s, code:
|
|
1466
|
+
throw this.oauthFlows.delete(s), new n("aborted", "AuthClient destroyed mid-flow");
|
|
1467
|
+
return this.completeOAuthFlow({ state: s, code: r });
|
|
1430
1468
|
}
|
|
1431
1469
|
/**
|
|
1432
1470
|
* Шаг 1 OAuth split-API: инициирует flow на бэке, генерит PKCE verifier
|
|
@@ -1444,13 +1482,13 @@ class dt {
|
|
|
1444
1482
|
*/
|
|
1445
1483
|
async startOAuthFlow(t) {
|
|
1446
1484
|
await this.hydrated, this.gcOAuthFlows();
|
|
1447
|
-
const e = Z(), s = await tt(e),
|
|
1448
|
-
o && (
|
|
1449
|
-
const { authorize_url:
|
|
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(
|
|
1450
1488
|
`/api/v1/paywall/${this.paywallId}/auth/oauth/init`,
|
|
1451
1489
|
{
|
|
1452
1490
|
method: "POST",
|
|
1453
|
-
headers: Object.keys(
|
|
1491
|
+
headers: Object.keys(r).length ? r : void 0,
|
|
1454
1492
|
body: JSON.stringify({
|
|
1455
1493
|
provider: t.provider,
|
|
1456
1494
|
code_challenge: s,
|
|
@@ -1459,11 +1497,12 @@ class dt {
|
|
|
1459
1497
|
})
|
|
1460
1498
|
}
|
|
1461
1499
|
);
|
|
1462
|
-
return this.oauthFlows.set(
|
|
1500
|
+
return this.oauthFlows.set(a, {
|
|
1463
1501
|
verifier: e,
|
|
1464
1502
|
userMeta: t.userMeta,
|
|
1503
|
+
provider: t.provider,
|
|
1465
1504
|
startedAt: Date.now()
|
|
1466
|
-
}), { authorize_url:
|
|
1505
|
+
}), this.recordLastLoginMethod(t.provider), { authorize_url: d, state: a };
|
|
1467
1506
|
}
|
|
1468
1507
|
/**
|
|
1469
1508
|
* Шаг 2 OAuth split-API: обменивает code (полученный из popup) на session,
|
|
@@ -1478,12 +1517,12 @@ class dt {
|
|
|
1478
1517
|
await this.hydrated;
|
|
1479
1518
|
const e = this.oauthFlows.get(t.state);
|
|
1480
1519
|
if (!e)
|
|
1481
|
-
throw new
|
|
1520
|
+
throw new n(
|
|
1482
1521
|
"oauth_invalid_state",
|
|
1483
1522
|
"OAuth flow not found — start with startOAuthFlow first or check TTL"
|
|
1484
1523
|
);
|
|
1485
1524
|
this.oauthFlows.delete(t.state);
|
|
1486
|
-
const s = await this.readVisitorId(),
|
|
1525
|
+
const s = await this.readVisitorId(), a = await this.api.request(
|
|
1487
1526
|
`/api/v1/paywall/${this.paywallId}/auth/oauth/exchange`,
|
|
1488
1527
|
{
|
|
1489
1528
|
method: "POST",
|
|
@@ -1496,12 +1535,12 @@ class dt {
|
|
|
1496
1535
|
}
|
|
1497
1536
|
);
|
|
1498
1537
|
if (this.destroyed)
|
|
1499
|
-
throw new
|
|
1500
|
-
const
|
|
1501
|
-
return this.setSession(
|
|
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;
|
|
1502
1541
|
}
|
|
1503
1542
|
gcOAuthFlows() {
|
|
1504
|
-
const t = Date.now() -
|
|
1543
|
+
const t = Date.now() - it;
|
|
1505
1544
|
for (const [e, s] of this.oauthFlows)
|
|
1506
1545
|
s.startedAt < t && this.oauthFlows.delete(e);
|
|
1507
1546
|
}
|
|
@@ -1526,11 +1565,11 @@ class dt {
|
|
|
1526
1565
|
method: "POST",
|
|
1527
1566
|
body: JSON.stringify({ refresh_token: t })
|
|
1528
1567
|
}
|
|
1529
|
-
),
|
|
1530
|
-
return this.setSession(
|
|
1568
|
+
), a = this.toSession(s, e);
|
|
1569
|
+
return this.setSession(a, { event: "TOKEN_REFRESHED" }), e.is_anonymous === !0 && await this.writeAnonRefreshToken(a.refresh_token), a;
|
|
1531
1570
|
} catch (s) {
|
|
1532
|
-
if (s instanceof
|
|
1533
|
-
return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null), null;
|
|
1571
|
+
if (s instanceof n && s.status === 401)
|
|
1572
|
+
return e.is_anonymous === !0 && await this.clearAnonRefreshToken(), this.setSession(null, { event: "SIGNED_OUT" }), null;
|
|
1534
1573
|
throw s;
|
|
1535
1574
|
} finally {
|
|
1536
1575
|
this.inflightRefresh = null;
|
|
@@ -1556,14 +1595,14 @@ class dt {
|
|
|
1556
1595
|
await this.hydrated;
|
|
1557
1596
|
const t = this.session?.access_token;
|
|
1558
1597
|
if (!t)
|
|
1559
|
-
throw new
|
|
1598
|
+
throw new n("not_authenticated", "no active session");
|
|
1560
1599
|
await this.api.request(
|
|
1561
1600
|
`/api/v1/paywall/${this.paywallId}/auth/revoke-all`,
|
|
1562
1601
|
{
|
|
1563
1602
|
method: "POST",
|
|
1564
1603
|
headers: { Authorization: `Bearer ${t}` }
|
|
1565
1604
|
}
|
|
1566
|
-
), this.setSession(null);
|
|
1605
|
+
), this.setSession(null, { event: "SIGNED_OUT" });
|
|
1567
1606
|
}
|
|
1568
1607
|
/**
|
|
1569
1608
|
* Signout: чистит локальную session СРАЗУ (UX — мгновенный logout без
|
|
@@ -1583,7 +1622,7 @@ class dt {
|
|
|
1583
1622
|
async signOut(t = {}) {
|
|
1584
1623
|
await this.hydrated;
|
|
1585
1624
|
const e = this.session?.access_token, s = this.session?.user.is_anonymous === !0;
|
|
1586
|
-
if (this.setSession(null), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
|
|
1625
|
+
if (this.setSession(null, { event: "SIGNED_OUT" }), t.forgetAnonymous && await this.clearAnonRefreshToken(), !!e && !(s && !t.forgetAnonymous))
|
|
1587
1626
|
try {
|
|
1588
1627
|
await this.api.request(
|
|
1589
1628
|
`/api/v1/paywall/${this.paywallId}/auth/signout`,
|
|
@@ -1597,22 +1636,35 @@ class dt {
|
|
|
1597
1636
|
}
|
|
1598
1637
|
/**
|
|
1599
1638
|
* Подписка на изменения session: signin/signup/refresh/signOut/expired-401.
|
|
1600
|
-
*
|
|
1601
|
-
*
|
|
1639
|
+
*
|
|
1640
|
+
* Гарантированный контракт: ПЕРВЫЙ callback каждому subscriber'у — всегда
|
|
1641
|
+
* `event = 'INITIAL_SESSION'`, дёргается асинхронно после resolve hydrate'а
|
|
1642
|
+
* (даже если session=null — listener получает explicit «нет сессии», а не
|
|
1643
|
+
* молчание). Все последующие callback'и — реальные переходы с конкретным
|
|
1644
|
+
* event'ом (SIGNED_IN / SIGNED_OUT / TOKEN_REFRESHED / USER_UPDATED /
|
|
1645
|
+
* PASSWORD_RECOVERY).
|
|
1646
|
+
*
|
|
1647
|
+
* Это позволяет listener'у безопасно делать «only on real signin» побочные
|
|
1648
|
+
* эффекты (force refetch balances и т.п.) через `event === 'SIGNED_IN'`,
|
|
1649
|
+
* не путая их с восстановлением из storage.
|
|
1650
|
+
*
|
|
1651
|
+
* Возвращает unsubscribe.
|
|
1602
1652
|
*/
|
|
1603
1653
|
onAuthChange(t) {
|
|
1604
|
-
|
|
1654
|
+
return this.listeners.add(t), this.hydrated.then(() => {
|
|
1655
|
+
if (this.destroyed || !this.listeners.has(t)) return;
|
|
1605
1656
|
const e = this.session;
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
})
|
|
1609
|
-
|
|
1610
|
-
|
|
1657
|
+
try {
|
|
1658
|
+
t("INITIAL_SESSION", e);
|
|
1659
|
+
} catch (s) {
|
|
1660
|
+
console.warn("[paywall] onAuthChange INITIAL_SESSION threw", s);
|
|
1661
|
+
}
|
|
1662
|
+
}), () => {
|
|
1611
1663
|
this.listeners.delete(t);
|
|
1612
1664
|
};
|
|
1613
1665
|
}
|
|
1614
1666
|
isFresh(t) {
|
|
1615
|
-
return t.expires_at - Date.now() >
|
|
1667
|
+
return t.expires_at - Date.now() > st;
|
|
1616
1668
|
}
|
|
1617
1669
|
toSession(t, e) {
|
|
1618
1670
|
const s = t.expires_at != null ? t.expires_at * 1e3 : Date.now() + t.expires_in * 1e3;
|
|
@@ -1623,21 +1675,21 @@ class dt {
|
|
|
1623
1675
|
user: e
|
|
1624
1676
|
};
|
|
1625
1677
|
}
|
|
1626
|
-
setSession(t, e
|
|
1678
|
+
setSession(t, e) {
|
|
1627
1679
|
if (this.destroyed) return;
|
|
1628
1680
|
const s = this.session;
|
|
1629
|
-
this.session = t, e.skipPersist || this.persist(), ht(s, t) || this.emit();
|
|
1681
|
+
this.session = t, e.skipPersist || this.persist(), ht(s, t) || this.emit(e.event);
|
|
1630
1682
|
}
|
|
1631
|
-
emit() {
|
|
1632
|
-
for (const
|
|
1683
|
+
emit(t) {
|
|
1684
|
+
for (const e of this.listeners)
|
|
1633
1685
|
try {
|
|
1634
|
-
t
|
|
1635
|
-
} catch (
|
|
1636
|
-
console.warn("[paywall] onAuthChange listener threw",
|
|
1686
|
+
e(t, this.session);
|
|
1687
|
+
} catch (s) {
|
|
1688
|
+
console.warn("[paywall] onAuthChange listener threw", s);
|
|
1637
1689
|
}
|
|
1638
1690
|
}
|
|
1639
1691
|
storageKey() {
|
|
1640
|
-
return
|
|
1692
|
+
return l.authSession(this.paywallId);
|
|
1641
1693
|
}
|
|
1642
1694
|
async hydrate() {
|
|
1643
1695
|
try {
|
|
@@ -1646,7 +1698,7 @@ class dt {
|
|
|
1646
1698
|
const e = JSON.parse(t);
|
|
1647
1699
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
1648
1700
|
return;
|
|
1649
|
-
this.session = e
|
|
1701
|
+
this.session = e;
|
|
1650
1702
|
} catch {
|
|
1651
1703
|
}
|
|
1652
1704
|
}
|
|
@@ -1660,7 +1712,7 @@ class dt {
|
|
|
1660
1712
|
const e = JSON.parse(t);
|
|
1661
1713
|
if (!e || typeof e.access_token != "string" || typeof e.refresh_token != "string" || typeof e.expires_at != "number" || !e.user)
|
|
1662
1714
|
return;
|
|
1663
|
-
this.setSession(e, { skipPersist: !0 });
|
|
1715
|
+
this.setSession(e, { skipPersist: !0, event: "SIGNED_IN" });
|
|
1664
1716
|
} catch {
|
|
1665
1717
|
}
|
|
1666
1718
|
}
|
|
@@ -1691,7 +1743,7 @@ class dt {
|
|
|
1691
1743
|
}
|
|
1692
1744
|
async readAnonRefreshToken() {
|
|
1693
1745
|
try {
|
|
1694
|
-
const t = await this.storage.getItem(
|
|
1746
|
+
const t = await this.storage.getItem(l.anonRefreshToken(this.paywallId));
|
|
1695
1747
|
return typeof t == "string" && t.length > 0 ? t : null;
|
|
1696
1748
|
} catch {
|
|
1697
1749
|
return null;
|
|
@@ -1700,7 +1752,7 @@ class dt {
|
|
|
1700
1752
|
async writeAnonRefreshToken(t) {
|
|
1701
1753
|
try {
|
|
1702
1754
|
await this.storage.setItem(
|
|
1703
|
-
|
|
1755
|
+
l.anonRefreshToken(this.paywallId),
|
|
1704
1756
|
t
|
|
1705
1757
|
);
|
|
1706
1758
|
} catch {
|
|
@@ -1709,11 +1761,41 @@ class dt {
|
|
|
1709
1761
|
async clearAnonRefreshToken() {
|
|
1710
1762
|
try {
|
|
1711
1763
|
await this.storage.removeItem(
|
|
1712
|
-
|
|
1764
|
+
l.anonRefreshToken(this.paywallId)
|
|
1713
1765
|
);
|
|
1714
1766
|
} catch {
|
|
1715
1767
|
}
|
|
1716
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
|
+
}
|
|
1717
1799
|
/**
|
|
1718
1800
|
* Читает stable visitor_id из storage если он там уже есть. НЕ генерит:
|
|
1719
1801
|
* AuthClient может быть инстанцирован раньше BillingClient, а синтетический
|
|
@@ -1723,68 +1805,71 @@ class dt {
|
|
|
1723
1805
|
*/
|
|
1724
1806
|
async readVisitorId() {
|
|
1725
1807
|
try {
|
|
1726
|
-
const t = await this.storage.getItem(
|
|
1808
|
+
const t = await this.storage.getItem(l.visitorId);
|
|
1727
1809
|
return typeof t == "string" && t.length >= 16 ? t : void 0;
|
|
1728
1810
|
} catch {
|
|
1729
1811
|
return;
|
|
1730
1812
|
}
|
|
1731
1813
|
}
|
|
1732
1814
|
}
|
|
1733
|
-
const
|
|
1734
|
-
function
|
|
1815
|
+
const at = 5 * 6e4, rt = 500;
|
|
1816
|
+
function nt(i, t) {
|
|
1735
1817
|
return new Promise((e, s) => {
|
|
1736
|
-
let
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1739
|
-
}, o = (
|
|
1740
|
-
if (
|
|
1741
|
-
const h =
|
|
1818
|
+
let a = !1;
|
|
1819
|
+
const r = () => {
|
|
1820
|
+
a = !0, window.removeEventListener("message", o), clearInterval(d), clearTimeout(u);
|
|
1821
|
+
}, o = (f) => {
|
|
1822
|
+
if (a) return;
|
|
1823
|
+
const h = f.data;
|
|
1742
1824
|
if (!(!h || h.type !== "pw-oauth") && h.messageId === t) {
|
|
1743
1825
|
if (h.status === "success" && h.code) {
|
|
1744
|
-
|
|
1826
|
+
r();
|
|
1745
1827
|
try {
|
|
1746
|
-
|
|
1828
|
+
i.close();
|
|
1747
1829
|
} catch {
|
|
1748
1830
|
}
|
|
1749
1831
|
e(h.code);
|
|
1750
1832
|
} else if (h.status === "error") {
|
|
1751
|
-
|
|
1833
|
+
r();
|
|
1752
1834
|
try {
|
|
1753
|
-
|
|
1835
|
+
i.close();
|
|
1754
1836
|
} catch {
|
|
1755
1837
|
}
|
|
1756
1838
|
s(
|
|
1757
|
-
new
|
|
1839
|
+
new n(
|
|
1758
1840
|
"oauth_failed",
|
|
1759
1841
|
h.description || h.error || "OAuth provider returned error"
|
|
1760
1842
|
)
|
|
1761
1843
|
);
|
|
1762
1844
|
}
|
|
1763
1845
|
}
|
|
1764
|
-
},
|
|
1765
|
-
if (
|
|
1766
|
-
let
|
|
1846
|
+
}, d = setInterval(() => {
|
|
1847
|
+
if (a) return;
|
|
1848
|
+
let f;
|
|
1767
1849
|
try {
|
|
1768
|
-
|
|
1850
|
+
f = i.closed;
|
|
1769
1851
|
} catch {
|
|
1770
1852
|
return;
|
|
1771
1853
|
}
|
|
1772
|
-
|
|
1773
|
-
}, rt),
|
|
1774
|
-
if (!
|
|
1775
|
-
|
|
1854
|
+
f && (r(), s(new n("oauth_cancelled", "auth popup was closed")));
|
|
1855
|
+
}, rt), u = setTimeout(() => {
|
|
1856
|
+
if (!a) {
|
|
1857
|
+
r();
|
|
1776
1858
|
try {
|
|
1777
|
-
|
|
1859
|
+
i.close();
|
|
1778
1860
|
} catch {
|
|
1779
1861
|
}
|
|
1780
|
-
s(new
|
|
1862
|
+
s(new n("oauth_timeout", "OAuth flow timed out"));
|
|
1781
1863
|
}
|
|
1782
|
-
},
|
|
1864
|
+
}, at);
|
|
1783
1865
|
window.addEventListener("message", o);
|
|
1784
1866
|
});
|
|
1785
1867
|
}
|
|
1786
|
-
function
|
|
1787
|
-
return
|
|
1868
|
+
function ot(i) {
|
|
1869
|
+
return i === "google" || i === "apple" || i === "github" || i === "facebook" || i === "email";
|
|
1870
|
+
}
|
|
1871
|
+
function ht(i, t) {
|
|
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;
|
|
1788
1873
|
}
|
|
1789
1874
|
const ct = 1500, lt = 20, k = 200;
|
|
1790
1875
|
class ft {
|
|
@@ -1817,15 +1902,15 @@ class ft {
|
|
|
1817
1902
|
const t = this.buffer;
|
|
1818
1903
|
this.buffer = [];
|
|
1819
1904
|
try {
|
|
1820
|
-
const e = await this.opts.getVisitorId(), s = this.opts.getUserId?.() ?? null,
|
|
1821
|
-
if (!
|
|
1822
|
-
await
|
|
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, {
|
|
1823
1908
|
method: "POST",
|
|
1824
1909
|
credentials: "omit",
|
|
1825
1910
|
keepalive: !0,
|
|
1826
1911
|
// если страница закроется в этот момент — браузер всё равно дотянет
|
|
1827
1912
|
headers: this.buildHeaders(e, s),
|
|
1828
|
-
body:
|
|
1913
|
+
body: a
|
|
1829
1914
|
});
|
|
1830
1915
|
} catch {
|
|
1831
1916
|
}
|
|
@@ -1845,7 +1930,7 @@ class ft {
|
|
|
1845
1930
|
this.buffer.unshift(...t), this.flush();
|
|
1846
1931
|
return;
|
|
1847
1932
|
}
|
|
1848
|
-
const
|
|
1933
|
+
const a = JSON.stringify({
|
|
1849
1934
|
events: t,
|
|
1850
1935
|
// body-level дубликаты для beacon-flow, читаются сервером как fallback.
|
|
1851
1936
|
visitor_id: e,
|
|
@@ -1853,13 +1938,13 @@ class ft {
|
|
|
1853
1938
|
sdk_version: m,
|
|
1854
1939
|
paywall_id: this.opts.paywallId,
|
|
1855
1940
|
capabilities: this.opts.capabilities?.join(",") ?? ""
|
|
1856
|
-
}),
|
|
1857
|
-
if (!
|
|
1941
|
+
}), r = this.opts.sendBeacon ?? (typeof navigator < "u" && typeof navigator.sendBeacon == "function" ? navigator.sendBeacon.bind(navigator) : null);
|
|
1942
|
+
if (!r) {
|
|
1858
1943
|
this.buffer.unshift(...t), this.flush();
|
|
1859
1944
|
return;
|
|
1860
1945
|
}
|
|
1861
1946
|
try {
|
|
1862
|
-
|
|
1947
|
+
r(this.opts.endpoint, a) || (this.buffer.unshift(...t), this.flush());
|
|
1863
1948
|
} catch {
|
|
1864
1949
|
this.buffer.unshift(...t), this.flush();
|
|
1865
1950
|
}
|
|
@@ -1886,17 +1971,17 @@ class ft {
|
|
|
1886
1971
|
}
|
|
1887
1972
|
}
|
|
1888
1973
|
export {
|
|
1889
|
-
|
|
1890
|
-
|
|
1974
|
+
E as ApiClient,
|
|
1975
|
+
q as ApiGatewayClient,
|
|
1891
1976
|
dt as AuthClient,
|
|
1892
1977
|
ut as BillingClient,
|
|
1893
1978
|
ft as EventTracker,
|
|
1894
|
-
|
|
1895
|
-
|
|
1979
|
+
n as PaywallError,
|
|
1980
|
+
D as QuotaExceededError,
|
|
1896
1981
|
m as SDK_VERSION,
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1982
|
+
l as STORAGE_KEYS,
|
|
1983
|
+
L as createStorage,
|
|
1984
|
+
I as ensureVisitorId,
|
|
1985
|
+
C as generateVisitorId
|
|
1901
1986
|
};
|
|
1902
1987
|
//# sourceMappingURL=core.js.map
|