@mesub/node 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/README.md +835 -2
- package/dist/chunk-43Q4PNF7.cjs +480 -0
- package/dist/chunk-43Q4PNF7.cjs.map +1 -0
- package/dist/chunk-ELK7IBLO.js +340 -0
- package/dist/chunk-ELK7IBLO.js.map +1 -0
- package/dist/chunk-JJWHMB2X.js +480 -0
- package/dist/chunk-JJWHMB2X.js.map +1 -0
- package/dist/chunk-KMPBFNJE.js +2038 -0
- package/dist/chunk-KMPBFNJE.js.map +1 -0
- package/dist/chunk-PCDZI7B4.cjs +340 -0
- package/dist/chunk-PCDZI7B4.cjs.map +1 -0
- package/dist/chunk-SGSYQJYT.cjs +2038 -0
- package/dist/chunk-SGSYQJYT.cjs.map +1 -0
- package/dist/chunk-VMKI4VVA.cjs +120 -0
- package/dist/chunk-VMKI4VVA.cjs.map +1 -0
- package/dist/chunk-XDLLXY7Y.js +120 -0
- package/dist/chunk-XDLLXY7Y.js.map +1 -0
- package/dist/client-BQuRYSdB.d.ts +759 -0
- package/dist/client-Dm1jamju.d.cts +759 -0
- package/dist/errors-CtPucyOh.d.cts +112 -0
- package/dist/errors-DqW3DMBS.d.ts +112 -0
- package/dist/express.cjs +129 -0
- package/dist/express.cjs.map +1 -0
- package/dist/express.d.cts +57 -0
- package/dist/express.d.ts +57 -0
- package/dist/express.js +129 -0
- package/dist/express.js.map +1 -0
- package/dist/guard-DKb6iU1z.d.ts +51 -0
- package/dist/guard-Dhy4fI6n.d.cts +51 -0
- package/dist/index.cjs +39 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.js +39 -0
- package/dist/index.js.map +1 -0
- package/dist/nest.cjs +62 -0
- package/dist/nest.cjs.map +1 -0
- package/dist/nest.d.cts +57 -0
- package/dist/nest.d.ts +57 -0
- package/dist/nest.js +62 -0
- package/dist/nest.js.map +1 -0
- package/dist/next.cjs +113 -0
- package/dist/next.cjs.map +1 -0
- package/dist/next.d.cts +63 -0
- package/dist/next.d.ts +63 -0
- package/dist/next.js +113 -0
- package/dist/next.js.map +1 -0
- package/dist/routes-CLtZnUzl.d.ts +130 -0
- package/dist/routes-DWkRFOP0.d.cts +130 -0
- package/dist/situations.cjs +15 -0
- package/dist/situations.cjs.map +1 -0
- package/dist/situations.d.cts +607 -0
- package/dist/situations.d.ts +607 -0
- package/dist/situations.js +15 -0
- package/dist/situations.js.map +1 -0
- package/dist/subscriptions-D1hE7MUc.d.cts +768 -0
- package/dist/subscriptions-D1hE7MUc.d.ts +768 -0
- package/dist/testing.cjs +1486 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +331 -0
- package/dist/testing.d.ts +331 -0
- package/dist/testing.js +1486 -0
- package/dist/testing.js.map +1 -0
- package/package.json +158 -4
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
var _chunkSGSYQJYTcjs = require('./chunk-SGSYQJYT.cjs');
|
|
5
|
+
|
|
6
|
+
// src/routes.ts
|
|
7
|
+
var MAX_BODY_BYTES = 64 * 1024;
|
|
8
|
+
var ACTIONS = ["cancel", "resume", "close"];
|
|
9
|
+
var ID = /^[A-Za-z0-9_-]{1,64}$/;
|
|
10
|
+
var SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
11
|
+
var WIDGET_READ_TIME = 1e4;
|
|
12
|
+
function bounded() {
|
|
13
|
+
return { deadline: Date.now() + WIDGET_READ_TIME, retryRateLimited: false };
|
|
14
|
+
}
|
|
15
|
+
function refusal(status, code, message) {
|
|
16
|
+
return { status, body: { error: { code, message } } };
|
|
17
|
+
}
|
|
18
|
+
var NOT_FOUND = () => refusal(404, "not_found", "Nothing here.");
|
|
19
|
+
var NO_SUBSCRIPTION = () => refusal(404, "subscription_not_found", "No such subscription.");
|
|
20
|
+
var NO_ANSWER = "Mesub could not answer this request.";
|
|
21
|
+
function said(error) {
|
|
22
|
+
return error.apiCode === null ? NO_ANSWER : error.message;
|
|
23
|
+
}
|
|
24
|
+
function brokenIntegration(error) {
|
|
25
|
+
if (error.status === 401) return true;
|
|
26
|
+
return error.status === 403 && (error.apiCode === null || error.apiCode === "forbidden");
|
|
27
|
+
}
|
|
28
|
+
function fromMesub(error) {
|
|
29
|
+
const status = error.status === null || error.status < 400 ? 502 : error.status;
|
|
30
|
+
const retry = error.retryAfter === null ? void 0 : { "Retry-After": String(Math.ceil(error.retryAfter / 1e3)) };
|
|
31
|
+
return {
|
|
32
|
+
status,
|
|
33
|
+
body: { error: { code: _nullishCoalesce(error.apiCode, () => ( error.code)), message: said(error) } },
|
|
34
|
+
...retry && { headers: retry }
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function isString(value) {
|
|
38
|
+
return typeof value === "string" && value !== "";
|
|
39
|
+
}
|
|
40
|
+
function fieldsOf(body) {
|
|
41
|
+
return typeof body === "object" && body !== null && !Array.isArray(body) ? body : null;
|
|
42
|
+
}
|
|
43
|
+
function isTheirs(subscription, asked) {
|
|
44
|
+
if (asked.kind === "wallet") return subscription.wallet === asked.value;
|
|
45
|
+
if (asked.kind === "external_id") return subscription.external_id === asked.value;
|
|
46
|
+
return subscription.email === asked.value;
|
|
47
|
+
}
|
|
48
|
+
function shown(subscription) {
|
|
49
|
+
const { email: _email, external_id: _externalId, ...rest } = subscription;
|
|
50
|
+
return rest;
|
|
51
|
+
}
|
|
52
|
+
var LIST_PAGE = 100;
|
|
53
|
+
var LIST_MAX_PAGES = 5;
|
|
54
|
+
var PLAN_TTL_MS = 6e4;
|
|
55
|
+
var planLists = /* @__PURE__ */ new WeakMap();
|
|
56
|
+
async function planOf(client, slug, read) {
|
|
57
|
+
let list = planLists.get(client);
|
|
58
|
+
if (!list || list.until <= Date.now()) {
|
|
59
|
+
const fresh = { plans: client.plans.list(read), until: Infinity };
|
|
60
|
+
list = fresh;
|
|
61
|
+
planLists.set(client, fresh);
|
|
62
|
+
fresh.plans.then(
|
|
63
|
+
() => {
|
|
64
|
+
fresh.until = Date.now() + PLAN_TTL_MS;
|
|
65
|
+
},
|
|
66
|
+
() => {
|
|
67
|
+
if (planLists.get(client) === fresh) planLists.delete(client);
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return await _asyncNullishCoalesce((await list.plans).find((plan) => plan.slug === slug), async () => ( null));
|
|
72
|
+
}
|
|
73
|
+
async function upcomingOf(client, subscription, read) {
|
|
74
|
+
const { status, plan } = subscription;
|
|
75
|
+
const next = status === "active" ? { kind: "charge", due_at: subscription.next_charge_at } : status === "unpaid" ? { kind: "retry", due_at: subscription.next_retry_at } : null;
|
|
76
|
+
if (!next || next.due_at === null || subscription.paused) return [];
|
|
77
|
+
let price = null;
|
|
78
|
+
if (plan !== null && SLUG.test(plan)) {
|
|
79
|
+
try {
|
|
80
|
+
price = await planOf(client, plan, read);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (!(error instanceof _chunkSGSYQJYTcjs.MesubError) || brokenIntegration(error)) throw error;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const retry = next.kind === "retry";
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
kind: next.kind,
|
|
89
|
+
due_at: next.due_at,
|
|
90
|
+
amount: _nullishCoalesce(_optionalChain([price, 'optionalAccess', _ => _.amount]), () => ( null)),
|
|
91
|
+
amount_display: _nullishCoalesce(_optionalChain([price, 'optionalAccess', _2 => _2.amount_display]), () => ( null)),
|
|
92
|
+
amount_usd: _nullishCoalesce(_optionalChain([price, 'optionalAccess', _3 => _3.amount_usd]), () => ( null)),
|
|
93
|
+
// Mesub's own, never counted here; `?? null` for a subscription cached by an older SDK.
|
|
94
|
+
retry_number: retry ? _nullishCoalesce(subscription.next_retry_number, () => ( null)) : null,
|
|
95
|
+
retries_allowed: retry ? _nullishCoalesce(subscription.retries_allowed, () => ( null)) : null
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
}
|
|
99
|
+
var NEVER_STARTED = ["pending", "expired", "failed"];
|
|
100
|
+
function payment(attempt) {
|
|
101
|
+
return {
|
|
102
|
+
attempted_at: attempt.attempted_at,
|
|
103
|
+
outcome: attempt.outcome,
|
|
104
|
+
amount: attempt.amount,
|
|
105
|
+
// `?? null` for an attempt handed by a client that predates the field.
|
|
106
|
+
amount_usd: _nullishCoalesce(attempt.amount_usd, () => ( null)),
|
|
107
|
+
reason: attempt.reason,
|
|
108
|
+
signature: attempt.signature,
|
|
109
|
+
retry: attempt.retry,
|
|
110
|
+
retry_number: attempt.retry_number,
|
|
111
|
+
retries_allowed: attempt.retries_allowed,
|
|
112
|
+
period_start: attempt.period_start
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function olderPayment(attempt) {
|
|
116
|
+
return {
|
|
117
|
+
attempted_at: attempt.attempted_at,
|
|
118
|
+
outcome: attempt.outcome,
|
|
119
|
+
amount: attempt.amount,
|
|
120
|
+
amount_usd: null,
|
|
121
|
+
reason: attempt.reason,
|
|
122
|
+
signature: attempt.signature,
|
|
123
|
+
retry: null,
|
|
124
|
+
retry_number: null,
|
|
125
|
+
retries_allowed: null,
|
|
126
|
+
period_start: null
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function unlisted(code, message) {
|
|
130
|
+
return { payments: null, paid: null, payments_error: { code, message } };
|
|
131
|
+
}
|
|
132
|
+
async function paymentsOf(client, subscription, read) {
|
|
133
|
+
try {
|
|
134
|
+
const page = await client.subscriptions.attempts(subscription.id, {}, read);
|
|
135
|
+
return { payments: page.data.map(payment), paid: page.paid, payments_error: null };
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (!(error instanceof _chunkSGSYQJYTcjs.MesubError) || brokenIntegration(error)) throw error;
|
|
138
|
+
if (error.status === 404 && error.apiCode === null) {
|
|
139
|
+
return olderPaymentsOf(client, subscription, read);
|
|
140
|
+
}
|
|
141
|
+
return unlisted(_nullishCoalesce(error.apiCode, () => ( error.code)), said(error));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
async function olderPaymentsOf(client, subscription, read) {
|
|
145
|
+
const { plan, wallet, confirmed_at: confirmedAt } = subscription;
|
|
146
|
+
const listed = (payments) => ({
|
|
147
|
+
payments,
|
|
148
|
+
paid: null,
|
|
149
|
+
payments_error: null
|
|
150
|
+
});
|
|
151
|
+
if (confirmedAt === null && NEVER_STARTED.includes(subscription.status)) return listed([]);
|
|
152
|
+
if (plan === null || !SLUG.test(plan)) {
|
|
153
|
+
return unlisted("plan_without_slug", "Its plan has no slug to read payments by.");
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const answer = await client.access({ wallet }, plan, { attempts: true, ...read });
|
|
157
|
+
const same = confirmedAt !== null && answer.subscribed_since !== null && Date.parse(answer.subscribed_since) === Date.parse(confirmedAt);
|
|
158
|
+
if (!same) {
|
|
159
|
+
return unlisted(
|
|
160
|
+
"not_the_current_subscription",
|
|
161
|
+
"This Mesub serves payments for the current subscription of a wallet on a plan only."
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
return listed(
|
|
165
|
+
(_nullishCoalesce(answer.attempts, () => ( []))).map(olderPayment).sort((a, b) => Date.parse(b.attempted_at) - Date.parse(a.attempted_at))
|
|
166
|
+
);
|
|
167
|
+
} catch (error) {
|
|
168
|
+
if (!(error instanceof _chunkSGSYQJYTcjs.MesubError) || brokenIntegration(error)) throw error;
|
|
169
|
+
return unlisted(_nullishCoalesce(error.apiCode, () => ( error.code)), said(error));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function handleWidget(client, request, asked, options = {}) {
|
|
173
|
+
const segments = request.path.split("?")[0].split("/").filter(Boolean);
|
|
174
|
+
const method = request.method.toUpperCase();
|
|
175
|
+
const read = bounded();
|
|
176
|
+
try {
|
|
177
|
+
if (method === "GET" && segments.length === 2 && segments[0] === "plans") {
|
|
178
|
+
const slug = segments[1];
|
|
179
|
+
if (!SLUG.test(slug) || options.plans && !options.plans.includes(slug)) {
|
|
180
|
+
return refusal(404, "plan_not_found", "No such plan.");
|
|
181
|
+
}
|
|
182
|
+
const plan = await planOf(client, slug, read);
|
|
183
|
+
return plan ? { status: 200, body: plan } : refusal(404, "plan_not_found", "No such plan.");
|
|
184
|
+
}
|
|
185
|
+
if (segments[0] !== "subscriptions") return NOT_FOUND();
|
|
186
|
+
if (method !== "GET" && method !== "POST") {
|
|
187
|
+
return refusal(405, "method_not_allowed", "GET or POST.");
|
|
188
|
+
}
|
|
189
|
+
if (!asked) return refusal(401, "unauthenticated", "Sign in first.");
|
|
190
|
+
if (method === "GET" && segments.length === 2) {
|
|
191
|
+
const id2 = segments[1];
|
|
192
|
+
if (!ID.test(id2)) return NO_SUBSCRIPTION();
|
|
193
|
+
const subscription2 = await client.subscriptions.retrieve(id2, read);
|
|
194
|
+
if (!isTheirs(subscription2, asked)) return NO_SUBSCRIPTION();
|
|
195
|
+
const [upcoming, payments] = await Promise.all([
|
|
196
|
+
upcomingOf(client, subscription2, read),
|
|
197
|
+
paymentsOf(client, subscription2, read)
|
|
198
|
+
]);
|
|
199
|
+
const detail = {
|
|
200
|
+
subscription: shown(subscription2),
|
|
201
|
+
upcoming,
|
|
202
|
+
...payments
|
|
203
|
+
};
|
|
204
|
+
return { status: 200, body: detail };
|
|
205
|
+
}
|
|
206
|
+
if (method === "GET") {
|
|
207
|
+
if (segments.length !== 1) return NOT_FOUND();
|
|
208
|
+
const params = { ...customerParam(asked), limit: LIST_PAGE };
|
|
209
|
+
let page = await client.subscriptions.list(params, read);
|
|
210
|
+
const subscriptions = [...page.data];
|
|
211
|
+
const more = () => page.has_more && page.data.length > 0;
|
|
212
|
+
for (let pages = 1; more() && pages < LIST_MAX_PAGES; pages++) {
|
|
213
|
+
page = await client.subscriptions.list(
|
|
214
|
+
{ ...params, starting_after: page.data.at(-1).id },
|
|
215
|
+
read
|
|
216
|
+
);
|
|
217
|
+
subscriptions.push(...page.data);
|
|
218
|
+
}
|
|
219
|
+
const list = {
|
|
220
|
+
subscriptions: subscriptions.map(shown),
|
|
221
|
+
has_more: more()
|
|
222
|
+
};
|
|
223
|
+
return { status: 200, body: list };
|
|
224
|
+
}
|
|
225
|
+
if (!_optionalChain([request, 'access', _4 => _4.contentType, 'optionalAccess', _5 => _5.toLowerCase, 'call', _6 => _6(), 'access', _7 => _7.startsWith, 'call', _8 => _8("application/json")])) {
|
|
226
|
+
return refusal(415, "unsupported_media_type", "Send JSON.");
|
|
227
|
+
}
|
|
228
|
+
const body = _nullishCoalesce(fieldsOf(request.body), () => ( {}));
|
|
229
|
+
if (segments.length === 1) {
|
|
230
|
+
const { plan, wallet } = body;
|
|
231
|
+
if (!isString(plan) || !SLUG.test(plan) || !isString(wallet)) {
|
|
232
|
+
return refusal(400, "invalid_request", "A plan and a wallet are needed.");
|
|
233
|
+
}
|
|
234
|
+
if (options.plans && !options.plans.includes(plan)) {
|
|
235
|
+
return refusal(404, "plan_not_found", "No such plan.");
|
|
236
|
+
}
|
|
237
|
+
if (asked.kind === "wallet" && asked.value !== wallet) {
|
|
238
|
+
return refusal(403, "wallet_mismatch", "Connect the wallet you signed in with.");
|
|
239
|
+
}
|
|
240
|
+
const email = asked.kind === "email" ? asked.value : options.email;
|
|
241
|
+
const created = await client.subscriptions.create({
|
|
242
|
+
plan,
|
|
243
|
+
wallet,
|
|
244
|
+
...asked.kind === "external_id" && { external_id: asked.value },
|
|
245
|
+
...isString(email) && { email }
|
|
246
|
+
});
|
|
247
|
+
return { status: 201, body: created };
|
|
248
|
+
}
|
|
249
|
+
const id = segments[1];
|
|
250
|
+
const step = segments.slice(2).join("/");
|
|
251
|
+
if (!ID.test(id)) return NO_SUBSCRIPTION();
|
|
252
|
+
const known = [
|
|
253
|
+
"submit",
|
|
254
|
+
"transaction",
|
|
255
|
+
"retry",
|
|
256
|
+
...ACTIONS,
|
|
257
|
+
...ACTIONS.map((action2) => `${action2}/confirm`)
|
|
258
|
+
];
|
|
259
|
+
if (!known.includes(step)) return NOT_FOUND();
|
|
260
|
+
const subscription = await client.subscriptions.retrieve(id, read);
|
|
261
|
+
if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();
|
|
262
|
+
if (step === "submit") {
|
|
263
|
+
const { transaction, terms_signature: termsSignature } = body;
|
|
264
|
+
if (!isString(transaction) || !isString(termsSignature)) {
|
|
265
|
+
return refusal(
|
|
266
|
+
400,
|
|
267
|
+
"invalid_request",
|
|
268
|
+
"The signed transaction and the terms signature are needed."
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
const settled = await client.subscriptions.submit(id, {
|
|
272
|
+
transaction,
|
|
273
|
+
terms_signature: termsSignature
|
|
274
|
+
});
|
|
275
|
+
return { status: 201, body: { ...settled, subscription: shown(settled.subscription) } };
|
|
276
|
+
}
|
|
277
|
+
if (step === "transaction") {
|
|
278
|
+
return { status: 201, body: await client.subscriptions.transaction(id) };
|
|
279
|
+
}
|
|
280
|
+
if (step === "retry") {
|
|
281
|
+
const retried = await client.subscriptions.retry(id);
|
|
282
|
+
return { status: 202, body: { subscription: shown(retried) } };
|
|
283
|
+
}
|
|
284
|
+
const [action, confirm] = step.split("/");
|
|
285
|
+
if (!confirm) {
|
|
286
|
+
return { status: 201, body: await client.subscriptions[action](id) };
|
|
287
|
+
}
|
|
288
|
+
const { signature } = body;
|
|
289
|
+
if (!isString(signature)) {
|
|
290
|
+
return refusal(400, "invalid_request", "The signature of the transaction is needed.");
|
|
291
|
+
}
|
|
292
|
+
const confirmed = await client.subscriptions[CONFIRM[action]](id, { signature });
|
|
293
|
+
return {
|
|
294
|
+
status: 201,
|
|
295
|
+
body: { ...confirmed, subscription: shown(confirmed.subscription) }
|
|
296
|
+
};
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (!(error instanceof _chunkSGSYQJYTcjs.MesubError) || brokenIntegration(error)) throw error;
|
|
299
|
+
if (error.apiCode === "subscription_not_found") return NO_SUBSCRIPTION();
|
|
300
|
+
return fromMesub(error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
var CONFIRM = {
|
|
304
|
+
cancel: "confirmCancel",
|
|
305
|
+
resume: "confirmResume",
|
|
306
|
+
close: "confirmClose"
|
|
307
|
+
};
|
|
308
|
+
function customerParam(asked) {
|
|
309
|
+
if (asked.kind === "wallet") return { wallet: asked.value };
|
|
310
|
+
return asked.kind === "email" ? { email: asked.value } : { external_id: asked.value };
|
|
311
|
+
}
|
|
312
|
+
async function widgetCustomer(request, customer) {
|
|
313
|
+
const named = await customer(request);
|
|
314
|
+
if (named === null || named === void 0) return null;
|
|
315
|
+
const asked = _chunkSGSYQJYTcjs.customerOf.call(void 0, named);
|
|
316
|
+
if (asked.value === "") {
|
|
317
|
+
throw new TypeError(
|
|
318
|
+
"`customer` returned an empty value: return null when nobody is signed in."
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
return asked;
|
|
322
|
+
}
|
|
323
|
+
function checkWidgetOptions(options) {
|
|
324
|
+
if (typeof _optionalChain([options, 'optionalAccess', _9 => _9.customer]) !== "function") {
|
|
325
|
+
throw new TypeError(
|
|
326
|
+
"The widget routes need `customer`: a function of the request returning who is signed in."
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (options.plans !== void 0 && !options.plans.every((plan) => SLUG.test(plan))) {
|
|
330
|
+
throw new TypeError("`plans` is a list of plan slugs.");
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
exports.MAX_BODY_BYTES = MAX_BODY_BYTES; exports.handleWidget = handleWidget; exports.widgetCustomer = widgetCustomer; exports.checkWidgetOptions = checkWidgetOptions;
|
|
340
|
+
//# sourceMappingURL=chunk-PCDZI7B4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/node-sdk/node-sdk/dist/chunk-PCDZI7B4.cjs","../src/routes.ts"],"names":["id","subscription","action"],"mappings":"AAAA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;AC6DO,IAAM,eAAA,EAAiB,GAAA,EAAK,IAAA;AAEnC,IAAM,QAAA,EAAU,CAAC,QAAA,EAAU,QAAA,EAAU,OAAO,CAAA;AAI5C,IAAM,GAAA,EAAK,uBAAA;AAEX,IAAM,KAAA,EAAO,uCAAA;AAMb,IAAM,iBAAA,EAAmB,GAAA;AAOzB,SAAS,OAAA,CAAA,EAAuB;AAC5B,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,gBAAA,EAAkB,gBAAA,EAAkB,MAAM,CAAA;AAC9E;AAEA,SAAS,OAAA,CAAQ,MAAA,EAAgB,IAAA,EAAc,OAAA,EAAiC;AAC5E,EAAA,OAAO,EAAE,MAAA,EAAQ,IAAA,EAAM,EAAE,KAAA,EAAO,EAAE,IAAA,EAAM,QAAQ,EAAE,EAAE,CAAA;AACxD;AAEA,IAAM,UAAA,EAAY,CAAA,EAAA,GAAM,OAAA,CAAQ,GAAA,EAAK,WAAA,EAAa,eAAe,CAAA;AACjE,IAAM,gBAAA,EAAkB,CAAA,EAAA,GAAM,OAAA,CAAQ,GAAA,EAAK,wBAAA,EAA0B,uBAAuB,CAAA;AAG5F,IAAM,UAAA,EAAY,sCAAA;AAGlB,SAAS,IAAA,CAAK,KAAA,EAA2B;AACrC,EAAA,OAAO,KAAA,CAAM,QAAA,IAAY,KAAA,EAAO,UAAA,EAAY,KAAA,CAAM,OAAA;AACtD;AAWA,SAAS,iBAAA,CAAkB,KAAA,EAA4B;AACnD,EAAA,GAAA,CAAI,KAAA,CAAM,OAAA,IAAW,GAAA,EAAK,OAAO,IAAA;AAEjC,EAAA,OAAO,KAAA,CAAM,OAAA,IAAW,IAAA,GAAA,CAAQ,KAAA,CAAM,QAAA,IAAY,KAAA,GAAQ,KAAA,CAAM,QAAA,IAAY,WAAA,CAAA;AAChF;AAGA,SAAS,SAAA,CAAU,KAAA,EAAmC;AAElD,EAAA,MAAM,OAAA,EAAS,KAAA,CAAM,OAAA,IAAW,KAAA,GAAQ,KAAA,CAAM,OAAA,EAAS,IAAA,EAAM,IAAA,EAAM,KAAA,CAAM,MAAA;AACzE,EAAA,MAAM,MAAA,EACF,KAAA,CAAM,WAAA,IAAe,KAAA,EACf,KAAA,EAAA,EACA,EAAE,aAAA,EAAe,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,KAAA,CAAM,WAAA,EAAa,GAAI,CAAC,EAAE,CAAA;AAEtE,EAAA,OAAO;AAAA,IACH,MAAA;AAAA,IACA,IAAA,EAAM,EAAE,KAAA,EAAO,EAAE,IAAA,mBAAM,KAAA,CAAM,OAAA,UAAW,KAAA,CAAM,MAAA,EAAM,OAAA,EAAS,IAAA,CAAK,KAAK,EAAE,EAAE,CAAA;AAAA,IAC3E,GAAI,MAAA,GAAS,EAAE,OAAA,EAAS,MAAM;AAAA,EAClC,CAAA;AACJ;AAEA,SAAS,QAAA,CAAS,KAAA,EAAiC;AAC/C,EAAA,OAAO,OAAO,MAAA,IAAU,SAAA,GAAY,MAAA,IAAU,EAAA;AAClD;AAEA,SAAS,QAAA,CAAS,IAAA,EAA+C;AAC7D,EAAA,OAAO,OAAO,KAAA,IAAS,SAAA,GAAY,KAAA,IAAS,KAAA,GAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,EAAA,EAChE,KAAA,EACD,IAAA;AACV;AAGA,SAAS,QAAA,CAAS,YAAA,EAAkC,KAAA,EAAuB;AACvE,EAAA,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,QAAA,EAAU,OAAO,YAAA,CAAa,OAAA,IAAW,KAAA,CAAM,KAAA;AAClE,EAAA,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,aAAA,EAAe,OAAO,YAAA,CAAa,YAAA,IAAgB,KAAA,CAAM,KAAA;AAE5E,EAAA,OAAO,YAAA,CAAa,MAAA,IAAU,KAAA,CAAM,KAAA;AACxC;AAGA,SAAS,KAAA,CAAM,YAAA,EAAkC;AAC7C,EAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,WAAA,EAAa,WAAA,EAAa,GAAG,KAAK,EAAA,EAAI,YAAA;AAE7D,EAAA,OAAO,IAAA;AACX;AAGA,IAAM,UAAA,EAAY,GAAA;AAClB,IAAM,eAAA,EAAiB,CAAA;AA4FvB,IAAM,YAAA,EAAc,GAAA;AAUpB,IAAM,UAAA,kBAAY,IAAI,OAAA,CAAyB,CAAA;AAQ/C,MAAA,SAAe,MAAA,CAAO,MAAA,EAAe,IAAA,EAAc,IAAA,EAAyC;AACxF,EAAA,IAAI,KAAA,EAAO,SAAA,CAAU,GAAA,CAAI,MAAM,CAAA;AAE/B,EAAA,GAAA,CAAI,CAAC,KAAA,GAAQ,IAAA,CAAK,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG;AAEnC,IAAA,MAAM,MAAA,EAAkB,EAAE,KAAA,EAAO,MAAA,CAAO,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA,EAAG,KAAA,EAAO,SAAS,CAAA;AAE1E,IAAA,KAAA,EAAO,KAAA;AACP,IAAA,SAAA,CAAU,GAAA,CAAI,MAAA,EAAQ,KAAK,CAAA;AAC3B,IAAA,KAAA,CAAM,KAAA,CAAM,IAAA;AAAA,MACR,CAAA,EAAA,GAAM;AACF,QAAA,KAAA,CAAM,MAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,WAAA;AAAA,MAC/B,CAAA;AAAA,MACA,CAAA,EAAA,GAAM;AACF,QAAA,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,MAAM,EAAA,IAAM,KAAA,EAAO,SAAA,CAAU,MAAA,CAAO,MAAM,CAAA;AAAA,MAChE;AAAA,IACJ,CAAA;AAAA,EACJ;AAEA,EAAA,mCAAA,CAAQ,MAAM,IAAA,CAAK,KAAA,CAAA,CAAO,IAAA,CAAK,CAAC,IAAA,EAAA,GAAS,IAAA,CAAK,KAAA,IAAS,IAAI,CAAA,gBAAK,MAAA;AACpE;AAQA,MAAA,SAAe,UAAA,CACX,MAAA,EACA,YAAA,EACA,IAAA,EACyB;AACzB,EAAA,MAAM,EAAE,MAAA,EAAQ,KAAK,EAAA,EAAI,YAAA;AACzB,EAAA,MAAM,KAAA,EACF,OAAA,IAAW,SAAA,EACL,EAAE,IAAA,EAAM,QAAA,EAAmB,MAAA,EAAQ,YAAA,CAAa,eAAe,EAAA,EAC/D,OAAA,IAAW,SAAA,EACT,EAAE,IAAA,EAAM,OAAA,EAAkB,MAAA,EAAQ,YAAA,CAAa,cAAc,EAAA,EAC7D,IAAA;AAGZ,EAAA,GAAA,CAAI,CAAC,KAAA,GAAQ,IAAA,CAAK,OAAA,IAAW,KAAA,GAAQ,YAAA,CAAa,MAAA,EAAQ,OAAO,CAAC,CAAA;AAElE,EAAA,IAAI,MAAA,EAAuE,IAAA;AAE3E,EAAA,GAAA,CAAI,KAAA,IAAS,KAAA,GAAQ,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA,EAAG;AAClC,IAAA,IAAI;AACA,MAAA,MAAA,EAAQ,MAAM,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,IAC3C,EAAA,MAAA,CAAS,KAAA,EAAO;AACZ,MAAA,GAAA,CAAI,CAAA,CAAE,MAAA,WAAiB,4BAAA,EAAA,GAAe,iBAAA,CAAkB,KAAK,CAAA,EAAG,MAAM,KAAA;AAAA,IAC1E;AAAA,EACJ;AAEA,EAAA,MAAM,MAAA,EAAQ,IAAA,CAAK,KAAA,IAAS,OAAA;AAE5B,EAAA,OAAO;AAAA,IACH;AAAA,MACI,IAAA,EAAM,IAAA,CAAK,IAAA;AAAA,MACX,MAAA,EAAQ,IAAA,CAAK,MAAA;AAAA,MACb,MAAA,mCAAQ,KAAA,2BAAO,QAAA,UAAU,MAAA;AAAA,MACzB,cAAA,mCAAgB,KAAA,6BAAO,gBAAA,UAAkB,MAAA;AAAA,MACzC,UAAA,mCAAY,KAAA,6BAAO,YAAA,UAAc,MAAA;AAAA;AAAA,MAEjC,YAAA,EAAc,MAAA,mBAAS,YAAA,CAAa,iBAAA,UAAqB,OAAA,EAAQ,IAAA;AAAA,MACjE,eAAA,EAAiB,MAAA,mBAAS,YAAA,CAAa,eAAA,UAAmB,OAAA,EAAQ;AAAA,IACtE;AAAA,EACJ,CAAA;AACJ;AAGA,IAAM,cAAA,EAAgB,CAAC,SAAA,EAAW,SAAA,EAAW,QAAQ,CAAA;AAErD,SAAS,OAAA,CAAQ,OAAA,EAA6C;AAC1D,EAAA,OAAO;AAAA,IACH,YAAA,EAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,OAAA,EAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA;AAAA,IAEhB,UAAA,mBAAY,OAAA,CAAQ,UAAA,UAAc,MAAA;AAAA,IAClC,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,SAAA,EAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,KAAA,EAAO,OAAA,CAAQ,KAAA;AAAA,IACf,YAAA,EAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,eAAA,EAAiB,OAAA,CAAQ,eAAA;AAAA,IACzB,YAAA,EAAc,OAAA,CAAQ;AAAA,EAC1B,CAAA;AACJ;AAGA,SAAS,YAAA,CAAa,OAAA,EAAuC;AACzD,EAAA,OAAO;AAAA,IACH,YAAA,EAAc,OAAA,CAAQ,YAAA;AAAA,IACtB,OAAA,EAAS,OAAA,CAAQ,OAAA;AAAA,IACjB,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,UAAA,EAAY,IAAA;AAAA,IACZ,MAAA,EAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,SAAA,EAAW,OAAA,CAAQ,SAAA;AAAA,IACnB,KAAA,EAAO,IAAA;AAAA,IACP,YAAA,EAAc,IAAA;AAAA,IACd,eAAA,EAAiB,IAAA;AAAA,IACjB,YAAA,EAAc;AAAA,EAClB,CAAA;AACJ;AAIA,SAAS,QAAA,CAAS,IAAA,EAAc,OAAA,EAA2B;AACvD,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,cAAA,EAAgB,EAAE,IAAA,EAAM,QAAQ,EAAE,CAAA;AAC3E;AAQA,MAAA,SAAe,UAAA,CACX,MAAA,EACA,YAAA,EACA,IAAA,EACiB;AACjB,EAAA,IAAI;AACA,IAAA,MAAM,KAAA,EAAO,MAAM,MAAA,CAAO,aAAA,CAAc,QAAA,CAAS,YAAA,CAAa,EAAA,EAAI,CAAC,CAAA,EAAG,IAAI,CAAA;AAE1E,IAAA,OAAO,EAAE,QAAA,EAAU,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,OAAO,CAAA,EAAG,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,cAAA,EAAgB,KAAK,CAAA;AAAA,EACrF,EAAA,MAAA,CAAS,KAAA,EAAO;AACZ,IAAA,GAAA,CAAI,CAAA,CAAE,MAAA,WAAiB,4BAAA,EAAA,GAAe,iBAAA,CAAkB,KAAK,CAAA,EAAG,MAAM,KAAA;AAEtE,IAAA,GAAA,CAAI,KAAA,CAAM,OAAA,IAAW,IAAA,GAAO,KAAA,CAAM,QAAA,IAAY,IAAA,EAAM;AAChD,MAAA,OAAO,eAAA,CAAgB,MAAA,EAAQ,YAAA,EAAc,IAAI,CAAA;AAAA,IACrD;AAGA,IAAA,OAAO,QAAA,kBAAS,KAAA,CAAM,OAAA,UAAW,KAAA,CAAM,MAAA,EAAM,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,EAC5D;AACJ;AAQA,MAAA,SAAe,eAAA,CACX,MAAA,EACA,YAAA,EACA,IAAA,EACiB;AACjB,EAAA,MAAM,EAAE,IAAA,EAAM,MAAA,EAAQ,YAAA,EAAc,YAAY,EAAA,EAAI,YAAA;AACpD,EAAA,MAAM,OAAA,EAAS,CAAC,QAAA,EAAA,GAAA,CAAyC;AAAA,IACrD,QAAA;AAAA,IACA,IAAA,EAAM,IAAA;AAAA,IACN,cAAA,EAAgB;AAAA,EACpB,CAAA,CAAA;AAGA,EAAA,GAAA,CAAI,YAAA,IAAgB,KAAA,GAAQ,aAAA,CAAc,QAAA,CAAS,YAAA,CAAa,MAAM,CAAA,EAAG,OAAO,MAAA,CAAO,CAAC,CAAC,CAAA;AACzF,EAAA,GAAA,CAAI,KAAA,IAAS,KAAA,GAAQ,CAAC,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA,EAAG;AACnC,IAAA,OAAO,QAAA,CAAS,mBAAA,EAAqB,2CAA2C,CAAA;AAAA,EACpF;AAEA,EAAA,IAAI;AACA,IAAA,MAAM,OAAA,EAAS,MAAM,MAAA,CAAO,MAAA,CAAO,EAAE,OAAO,CAAA,EAAG,IAAA,EAAM,EAAE,QAAA,EAAU,IAAA,EAAM,GAAG,KAAK,CAAC,CAAA;AAChF,IAAA,MAAM,KAAA,EACF,YAAA,IAAgB,KAAA,GAChB,MAAA,CAAO,iBAAA,IAAqB,KAAA,GAC5B,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,gBAAgB,EAAA,IAAM,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA;AAElE,IAAA,GAAA,CAAI,CAAC,IAAA,EAAM;AACP,MAAA,OAAO,QAAA;AAAA,QACH,8BAAA;AAAA,QACA;AAAA,MACJ,CAAA;AAAA,IACJ;AAEA,IAAA,OAAO,MAAA;AAAA,MAAA,kBACF,MAAA,CAAO,QAAA,UAAY,CAAC,GAAA,CAAA,CAChB,GAAA,CAAI,YAAY,CAAA,CAChB,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,EAAA,GAAM,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,YAAY,EAAA,EAAI,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,YAAY,CAAC;AAAA,IAC/E,CAAA;AAAA,EACJ,EAAA,MAAA,CAAS,KAAA,EAAO;AACZ,IAAA,GAAA,CAAI,CAAA,CAAE,MAAA,WAAiB,4BAAA,EAAA,GAAe,iBAAA,CAAkB,KAAK,CAAA,EAAG,MAAM,KAAA;AAEtE,IAAA,OAAO,QAAA,kBAAS,KAAA,CAAM,OAAA,UAAW,KAAA,CAAM,MAAA,EAAM,IAAA,CAAK,KAAK,CAAC,CAAA;AAAA,EAC5D;AACJ;AAaA,MAAA,SAAsB,YAAA,CAClB,MAAA,EACA,OAAA,EACA,KAAA,EACA,QAAA,EAA4E,CAAC,CAAA,EACtD;AACvB,EAAA,MAAM,SAAA,EAAW,OAAA,CAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,CAAA,CAAG,KAAA,CAAM,GAAG,CAAA,CAAE,MAAA,CAAO,OAAO,CAAA;AACtE,EAAA,MAAM,OAAA,EAAS,OAAA,CAAQ,MAAA,CAAO,WAAA,CAAY,CAAA;AAC1C,EAAA,MAAM,KAAA,EAAO,OAAA,CAAQ,CAAA;AAErB,EAAA,IAAI;AAEA,IAAA,GAAA,CAAI,OAAA,IAAW,MAAA,GAAS,QAAA,CAAS,OAAA,IAAW,EAAA,GAAK,QAAA,CAAS,CAAC,EAAA,IAAM,OAAA,EAAS;AACtE,MAAA,MAAM,KAAA,EAAO,QAAA,CAAS,CAAC,CAAA;AAEvB,MAAA,GAAA,CAAI,CAAC,IAAA,CAAK,IAAA,CAAK,IAAI,EAAA,GAAM,OAAA,CAAQ,MAAA,GAAS,CAAC,OAAA,CAAQ,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,EAAI;AACtE,QAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,gBAAA,EAAkB,eAAe,CAAA;AAAA,MACzD;AAEA,MAAA,MAAM,KAAA,EAAO,MAAM,MAAA,CAAO,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAE5C,MAAA,OAAO,KAAA,EACD,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,KAAK,EAAA,EAC1B,OAAA,CAAQ,GAAA,EAAK,gBAAA,EAAkB,eAAe,CAAA;AAAA,IACxD;AAEA,IAAA,GAAA,CAAI,QAAA,CAAS,CAAC,EAAA,IAAM,eAAA,EAAiB,OAAO,SAAA,CAAU,CAAA;AACtD,IAAA,GAAA,CAAI,OAAA,IAAW,MAAA,GAAS,OAAA,IAAW,MAAA,EAAQ;AACvC,MAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,oBAAA,EAAsB,cAAc,CAAA;AAAA,IAC5D;AACA,IAAA,GAAA,CAAI,CAAC,KAAA,EAAO,OAAO,OAAA,CAAQ,GAAA,EAAK,iBAAA,EAAmB,gBAAgB,CAAA;AAEnE,IAAA,GAAA,CAAI,OAAA,IAAW,MAAA,GAAS,QAAA,CAAS,OAAA,IAAW,CAAA,EAAG;AAC3C,MAAA,MAAMA,IAAAA,EAAK,QAAA,CAAS,CAAC,CAAA;AAErB,MAAA,GAAA,CAAI,CAAC,EAAA,CAAG,IAAA,CAAKA,GAAE,CAAA,EAAG,OAAO,eAAA,CAAgB,CAAA;AAGzC,MAAA,MAAMC,cAAAA,EAAe,MAAM,MAAA,CAAO,aAAA,CAAc,QAAA,CAASD,GAAAA,EAAI,IAAI,CAAA;AACjE,MAAA,GAAA,CAAI,CAAC,QAAA,CAASC,aAAAA,EAAc,KAAK,CAAA,EAAG,OAAO,eAAA,CAAgB,CAAA;AAE3D,MAAA,MAAM,CAAC,QAAA,EAAU,QAAQ,EAAA,EAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,QAC3C,UAAA,CAAW,MAAA,EAAQA,aAAAA,EAAc,IAAI,CAAA;AAAA,QACrC,UAAA,CAAW,MAAA,EAAQA,aAAAA,EAAc,IAAI;AAAA,MACzC,CAAC,CAAA;AACD,MAAA,MAAM,OAAA,EAAmC;AAAA,QACrC,YAAA,EAAc,KAAA,CAAMA,aAAY,CAAA;AAAA,QAChC,QAAA;AAAA,QACA,GAAG;AAAA,MACP,CAAA;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,OAAO,CAAA;AAAA,IACvC;AAEA,IAAA,GAAA,CAAI,OAAA,IAAW,KAAA,EAAO;AAClB,MAAA,GAAA,CAAI,QAAA,CAAS,OAAA,IAAW,CAAA,EAAG,OAAO,SAAA,CAAU,CAAA;AAE5C,MAAA,MAAM,OAAA,EAAS,EAAE,GAAG,aAAA,CAAc,KAAK,CAAA,EAAG,KAAA,EAAO,UAAU,CAAA;AAC3D,MAAA,IAAI,KAAA,EAAO,MAAM,MAAA,CAAO,aAAA,CAAc,IAAA,CAAK,MAAA,EAAQ,IAAI,CAAA;AACvD,MAAA,MAAM,cAAA,EAAgB,CAAC,GAAG,IAAA,CAAK,IAAI,CAAA;AAEnC,MAAA,MAAM,KAAA,EAAO,CAAA,EAAA,GAAM,IAAA,CAAK,SAAA,GAAY,IAAA,CAAK,IAAA,CAAK,OAAA,EAAS,CAAA;AAGvD,MAAA,IAAA,CAAA,IAAS,MAAA,EAAQ,CAAA,EAAG,IAAA,CAAK,EAAA,GAAK,MAAA,EAAQ,cAAA,EAAgB,KAAA,EAAA,EAAS;AAC3D,QAAA,KAAA,EAAO,MAAM,MAAA,CAAO,aAAA,CAAc,IAAA;AAAA,UAC9B,EAAE,GAAG,MAAA,EAAQ,cAAA,EAAgB,IAAA,CAAK,IAAA,CAAK,EAAA,CAAG,CAAA,CAAE,CAAA,CAAG,GAAG,CAAA;AAAA,UAClD;AAAA,QACJ,CAAA;AACA,QAAA,aAAA,CAAc,IAAA,CAAK,GAAG,IAAA,CAAK,IAAI,CAAA;AAAA,MACnC;AAEA,MAAA,MAAM,KAAA,EAA+B;AAAA,QACjC,aAAA,EAAe,aAAA,CAAc,GAAA,CAAI,KAAK,CAAA;AAAA,QACtC,QAAA,EAAU,IAAA,CAAK;AAAA,MACnB,CAAA;AAEA,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,KAAK,CAAA;AAAA,IACrC;AAGA,IAAA,GAAA,CAAI,iBAAC,OAAA,qBAAQ,WAAA,6BAAa,WAAA,mBAAY,CAAA,qBAAE,UAAA,mBAAW,kBAAkB,GAAA,EAAG;AACpE,MAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,wBAAA,EAA0B,YAAY,CAAA;AAAA,IAC9D;AAEA,IAAA,MAAM,KAAA,mBAAO,QAAA,CAAS,OAAA,CAAQ,IAAI,CAAA,UAAK,CAAC,GAAA;AAExC,IAAA,GAAA,CAAI,QAAA,CAAS,OAAA,IAAW,CAAA,EAAG;AACvB,MAAA,MAAM,EAAE,IAAA,EAAM,OAAO,EAAA,EAAI,IAAA;AAEzB,MAAA,GAAA,CAAI,CAAC,QAAA,CAAS,IAAI,EAAA,GAAK,CAAC,IAAA,CAAK,IAAA,CAAK,IAAI,EAAA,GAAK,CAAC,QAAA,CAAS,MAAM,CAAA,EAAG;AAC1D,QAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,iBAAA,EAAmB,iCAAiC,CAAA;AAAA,MAC5E;AACA,MAAA,GAAA,CAAI,OAAA,CAAQ,MAAA,GAAS,CAAC,OAAA,CAAQ,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,EAAG;AAChD,QAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,gBAAA,EAAkB,eAAe,CAAA;AAAA,MACzD;AAEA,MAAA,GAAA,CAAI,KAAA,CAAM,KAAA,IAAS,SAAA,GAAY,KAAA,CAAM,MAAA,IAAU,MAAA,EAAQ;AACnD,QAAA,OAAO,OAAA,CAAQ,GAAA,EAAK,iBAAA,EAAmB,wCAAwC,CAAA;AAAA,MACnF;AAEA,MAAA,MAAM,MAAA,EAAQ,KAAA,CAAM,KAAA,IAAS,QAAA,EAAU,KAAA,CAAM,MAAA,EAAQ,OAAA,CAAQ,KAAA;AAC7D,MAAA,MAAM,QAAA,EAAU,MAAM,MAAA,CAAO,aAAA,CAAc,MAAA,CAAO;AAAA,QAC9C,IAAA;AAAA,QACA,MAAA;AAAA,QACA,GAAI,KAAA,CAAM,KAAA,IAAS,cAAA,GAAiB,EAAE,WAAA,EAAa,KAAA,CAAM,MAAM,CAAA;AAAA,QAC/D,GAAI,QAAA,CAAS,KAAK,EAAA,GAAK,EAAE,MAAM;AAAA,MACnC,CAAC,CAAA;AAED,MAAA,OAAO,EAAE,MAAA,EAAQ,GAAA,EAAK,IAAA,EAAM,QAAQ,CAAA;AAAA,IACxC;AAEA,IAAA,MAAM,GAAA,EAAK,QAAA,CAAS,CAAC,CAAA;AACrB,IAAA,MAAM,KAAA,EAAO,QAAA,CAAS,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAEvC,IAAA,GAAA,CAAI,CAAC,EAAA,CAAG,IAAA,CAAK,EAAE,CAAA,EAAG,OAAO,eAAA,CAAgB,CAAA;AAEzC,IAAA,MAAM,MAAA,EAAQ;AAAA,MACV,QAAA;AAAA,MACA,aAAA;AAAA,MACA,OAAA;AAAA,MACA,GAAG,OAAA;AAAA,MACH,GAAG,OAAA,CAAQ,GAAA,CAAI,CAACC,OAAAA,EAAAA,GAAW,CAAA,EAAA;AAC/B,IAAA;AAC2B,IAAA;AAGA,IAAA;AACC,IAAA;AAEL,IAAA;AACE,MAAA;AAEI,MAAA;AACd,QAAA;AACH,UAAA;AACA,UAAA;AACA,UAAA;AACJ,QAAA;AACJ,MAAA;AAE6B,MAAA;AACzB,QAAA;AACiB,QAAA;AACpB,MAAA;AAE2B,MAAA;AAChC,IAAA;AAG4B,IAAA;AACI,MAAA;AAChC,IAAA;AAGsB,IAAA;AACW,MAAA;AAED,MAAA;AAChC,IAAA;AAE+B,IAAA;AAEjB,IAAA;AACkB,MAAA;AAChC,IAAA;AAEsB,IAAA;AACI,IAAA;AACF,MAAA;AACxB,IAAA;AAE+B,IAAA;AAExB,IAAA;AACK,MAAA;AACc,MAAA;AAC1B,IAAA;AACY,EAAA;AACW,IAAA;AAED,IAAA;AAEA,IAAA;AAC1B,EAAA;AACJ;AAEgB;AACJ,EAAA;AACA,EAAA;AACD,EAAA;AACX;AAE+C;AACd,EAAA;AAEG,EAAA;AACpC;AAOI;AAG6B,EAAA;AAEG,EAAA;AAEF,EAAA;AAEN,EAAA;AACV,IAAA;AACN,MAAA;AACJ,IAAA;AACJ,EAAA;AAEO,EAAA;AACX;AAGwC;AACH,EAAA;AACnB,IAAA;AACN,MAAA;AACJ,IAAA;AACJ,EAAA;AACoC,EAAA;AACZ,IAAA;AACxB,EAAA;AACJ;ADrXmC;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/node-sdk/node-sdk/dist/chunk-PCDZI7B4.cjs","sourcesContent":[null,"import type { Customer, ServedAttempt } from './answer.js';\nimport type { Mesub } from './client.js';\nimport { type Asked, customerOf } from './customer.js';\nimport { MesubError } from './errors.js';\nimport type { Plan } from './plans.js';\nimport type { ServerSubscription, SubscriptionAttempt } from './subscriptions.js';\nimport type { CallOptions } from './transport.js';\n\n/**\n * The routes `@mesub/react` calls on your own server, so the browser never\n * talks to Mesub nor holds a key: a plan to show, the customer's\n * subscriptions, one of them with its payments, and each step of\n * subscribing (a fresh transaction to sign included), cancelling, resuming,\n * closing and paying a late payment now. One function, `handleWidget`; the\n * Express and Next entries only carry a request to it.\n *\n * Who is asking comes from `customer`, your own verified auth, as on the\n * guards. A subscription that is not that customer's is answered 404, the\n * same as one that does not exist.\n */\n\n/** Who is asking, from your own auth. Null when nobody is signed in, a 401. */\nexport type WidgetCustomer<Req> = (\n request: Req,\n) => Customer | string | null | undefined | Promise<Customer | string | null | undefined>;\n\nexport interface WidgetRoutesOptions<Req> {\n /** Defaults to one client built from MESUB_API_KEY. */\n client?: Mesub;\n /**\n * Who is asking, from a session you verified, never from the request\n * itself: `(req) => ({ external_id: req.user.id })`, a wallet or an email.\n * Every subscription created is tied to it, and only its own are reached.\n */\n customer: WidgetCustomer<Req>;\n /**\n * Where that customer's notices go, when `customer` is not an email:\n * `(req) => req.user.email`. Not verified by Mesub.\n */\n email?: (request: Req) => string | null | undefined | Promise<string | null | undefined>;\n /**\n * The plans the widget may subscribe to. Any plan of your project by\n * default; name them to keep one off your site.\n */\n plans?: readonly string[];\n}\n\n/** A request as the core reads it, whatever the framework. */\nexport interface WidgetRequest {\n method: string;\n /** After the mount point: `/subscriptions/sub_1/cancel`. */\n path: string;\n /** Parsed JSON, or undefined when there was none. */\n body: unknown;\n /** The request's `Content-Type`, to refuse a POST a plain form could send. */\n contentType: string | null;\n}\n\n/** What the core answers: a status and a JSON body. */\nexport interface WidgetResponse {\n status: number;\n body: unknown;\n headers?: Record<string, string>;\n}\n\n/** The most a widget request's body may weigh: a signed transaction is under 2 kB. */\nexport const MAX_BODY_BYTES = 64 * 1024;\n\nconst ACTIONS = ['cancel', 'resume', 'close'] as const;\ntype Action = (typeof ACTIONS)[number];\n\n/** An id as Mesub writes them: nothing that could rewrite the path. */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n/** A plan slug as Mesub writes them: with at least one letter, never a number. */\nconst SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * How long one widget request gives Mesub for all its reads, in milliseconds:\n * a browser is waiting, and a detail or a list makes several calls.\n */\nconst WIDGET_READ_TIME = 10_000;\n\n/**\n * How every read of one request is made, as a guard's: cut at one deadline,\n * and a 429 handed on at once, never waited out. The client's methods pass\n * their options to the transport whole, which is how these reach it.\n */\nfunction bounded(): CallOptions {\n return { deadline: Date.now() + WIDGET_READ_TIME, retryRateLimited: false };\n}\n\nfunction refusal(status: number, code: string, message: string): WidgetResponse {\n return { status, body: { error: { code, message } } };\n}\n\nconst NOT_FOUND = () => refusal(404, 'not_found', 'Nothing here.');\nconst NO_SUBSCRIPTION = () => refusal(404, 'subscription_not_found', 'No such subscription.');\n\n/** Said to the browser for an error Mesub did not word itself. */\nconst NO_ANSWER = 'Mesub could not answer this request.';\n\n/** Mesub's own words only: the transport's may name `baseUrl` or a network error. */\nfunction said(error: MesubError): string {\n return error.apiCode === null ? NO_ANSWER : error.message;\n}\n\n/**\n * Whether Mesub refused your server, not what the customer did: any 401,\n * which Mesub answers only to an API key missing, never issued or rotated\n * since (`missing_api_key`, `invalid_api_key`), or a 403 that names none of\n * Mesub's own codes, from something in front of Mesub turning your server\n * away. Handed on, a 401 reads to `@mesub/react` as \"nobody is signed in\",\n * and every visitor gets the sign-in screen: thrown instead, a broken\n * integration, for the framework to log and answer 500.\n */\nfunction brokenIntegration(error: MesubError): boolean {\n if (error.status === 401) return true;\n\n return error.status === 403 && (error.apiCode === null || error.apiCode === 'forbidden');\n}\n\n/** A Mesub refusal, handed on with its own status and code, and nothing of the key. */\nfunction fromMesub(error: MesubError): WidgetResponse {\n // No answer from Mesub, or a 2xx this SDK cannot read: a 502 of yours.\n const status = error.status === null || error.status < 400 ? 502 : error.status;\n const retry =\n error.retryAfter === null\n ? undefined\n : { 'Retry-After': String(Math.ceil(error.retryAfter / 1000)) };\n\n return {\n status,\n body: { error: { code: error.apiCode ?? error.code, message: said(error) } },\n ...(retry && { headers: retry }),\n };\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\nfunction fieldsOf(body: unknown): Record<string, unknown> | null {\n return typeof body === 'object' && body !== null && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null;\n}\n\n/** Whether that subscription is the customer's: by the same identifier it was asked with. */\nfunction isTheirs(subscription: ServerSubscription, asked: Asked): boolean {\n if (asked.kind === 'wallet') return subscription.wallet === asked.value;\n if (asked.kind === 'external_id') return subscription.external_id === asked.value;\n\n return subscription.email === asked.value;\n}\n\n/** What a browser gets of a subscription: the merchant's own id and email stay on the server. */\nfunction shown(subscription: ServerSubscription) {\n const { email: _email, external_id: _externalId, ...rest } = subscription;\n\n return rest;\n}\n\n/** `GET /subscriptions` reads pages of 100, Mesub's largest, and 5 at most. */\nconst LIST_PAGE = 100;\nconst LIST_MAX_PAGES = 5;\n\n/** What `GET /subscriptions` answers. */\nexport interface WidgetSubscriptionList {\n /** Newest first, expired checkouts included: the 500 newest at most. */\n subscriptions: Array<Omit<ServerSubscription, 'email' | 'external_id'>>;\n /** True when the customer has more than those, which are not read. */\n has_more: boolean;\n}\n\n/** One pull attempt as the browser gets it: the fields Mesub serves, and no other. */\nexport interface WidgetPayment {\n attempted_at: string;\n /** `paid`, `skipped`, `rejected` or `blocked` today; a newer one is handed on as is. */\n outcome: string;\n /** What was asked for, in the mint's smallest unit; on a paid one, what was paid. */\n amount: string;\n /**\n * That amount in dollars when it was attempted, a decimal string. Null\n * until Mesub priced it, and from a Mesub that does not say.\n */\n amount_usd: string | null;\n /**\n * Mesub's short reason, null on a paid one, as on `ServedAttempt`: snake\n * case, an open string with the `program_${number}` and\n * `solana_${number}` families.\n */\n reason: string | null;\n /** The transaction that paid. Null on every outcome but `paid`. */\n signature: string | null;\n /** A retry of a missed charge. Null from a Mesub that does not say. */\n retry: boolean | null;\n /**\n * Which retry and out of how many, as Mesub recorded them when it ran:\n * 2 and 3 for \"retry 2 / 3\". Null when it is no retry of the plan's, or\n * Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n /** The start of the period it was for. Null when Mesub recorded none or does not say. */\n period_start: string | null;\n}\n\n/** What `GET /subscriptions/:id` answers. */\nexport interface WidgetSubscriptionDetail {\n subscription: Omit<ServerSubscription, 'email' | 'external_id'>;\n /** What Mesub pulls next, soonest first: one entry at most, none when nothing is due. */\n upcoming: WidgetUpcoming[];\n /**\n * This subscription's own pull attempts, newest first: the first page,\n * twenty at most, so not always its full history. Null when they could\n * not be read: `payments_error` says why.\n */\n payments: WidgetPayment[] | null;\n /**\n * What it paid since it began, counted by Mesub over all its attempts,\n * not over `payments`: how many were paid, and their sum in the mint's\n * smallest unit. Null when the read failed, and from a Mesub that does\n * not serve it: never summed here.\n */\n paid: { count: number; amount: string } | null;\n /** Why `payments` is null; null otherwise. */\n payments_error: { code: string; message: string } | null;\n}\n\n/** The one pull Mesub announces next for a subscription. */\nexport interface WidgetUpcoming {\n /** `charge` at the due date of a running one, `retry` of a missed one. */\n kind: 'charge' | 'retry';\n due_at: string;\n /**\n * The plan's price in the mint's smallest unit, and as a person counts\n * it. Null when the plan could not be read, never guessed.\n */\n amount: string | null;\n amount_display: string | null;\n /**\n * The plan's amount in dollars at the latest price Mesub knows, a decimal\n * string such as `\"9.99\"`: an estimate to show beside the amount, never\n * what is charged. Null when Mesub knows no price, and when the plan could\n * not be read.\n */\n amount_usd: string | null;\n /**\n * On a `retry`, which one it will be and out of how many: 2 and 3 for\n * \"next try 2 / 3\". Null on a `charge`, and when Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n}\n\n/** How long the plan list read for the widget is kept. */\nconst PLAN_TTL_MS = 60_000;\n\ninterface PlanList {\n /** Shared while the request is out. */\n plans: Promise<Plan[]>;\n /** Infinity until Mesub answered. */\n until: number;\n}\n\n// Per client, so two projects never share a plan.\nconst planLists = new WeakMap<Mesub, PlanList>();\n\n/**\n * A plan for the widget routes, from the project's list: public reads must\n * not spend the API key's rate limit, so one call per TTL whatever the slugs,\n * and null with no call for a slug that is not in it. A failed read is\n * forgotten at once.\n */\nasync function planOf(client: Mesub, slug: string, read: CallOptions): Promise<Plan | null> {\n let list = planLists.get(client);\n\n if (!list || list.until <= Date.now()) {\n // Shared while in flight: bound by the first asker's deadline, as a guard's flight.\n const fresh: PlanList = { plans: client.plans.list(read), until: Infinity };\n\n list = fresh;\n planLists.set(client, fresh);\n fresh.plans.then(\n () => {\n fresh.until = Date.now() + PLAN_TTL_MS;\n },\n () => {\n if (planLists.get(client) === fresh) planLists.delete(client);\n },\n );\n }\n\n return (await list.plans).find((plan) => plan.slug === slug) ?? null;\n}\n\n/**\n * What Mesub will pull next: the next charge of a running subscription, the\n * next retry of a late one, and nothing for any other, a parked seat\n * included. One event at most: later periods are not served, so not computed.\n * On Free a late one has no retry of Mesub's, only `retry_deadline`.\n */\nasync function upcomingOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<WidgetUpcoming[]> {\n const { status, plan } = subscription;\n const next =\n status === 'active'\n ? { kind: 'charge' as const, due_at: subscription.next_charge_at }\n : status === 'unpaid'\n ? { kind: 'retry' as const, due_at: subscription.next_retry_at }\n : null;\n\n // A date on any other status is no pull Mesub will run.\n if (!next || next.due_at === null || subscription.paused) return [];\n\n let price: Pick<Plan, 'amount' | 'amount_display' | 'amount_usd'> | null = null;\n\n if (plan !== null && SLUG.test(plan)) {\n try {\n price = await planOf(client, plan, read);\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n }\n }\n\n const retry = next.kind === 'retry';\n\n return [\n {\n kind: next.kind,\n due_at: next.due_at,\n amount: price?.amount ?? null,\n amount_display: price?.amount_display ?? null,\n amount_usd: price?.amount_usd ?? null,\n // Mesub's own, never counted here; `?? null` for a subscription cached by an older SDK.\n retry_number: retry ? (subscription.next_retry_number ?? null) : null,\n retries_allowed: retry ? (subscription.retries_allowed ?? null) : null,\n },\n ];\n}\n\n/** The statuses nothing is ever pulled in: a checkout that never started. */\nconst NEVER_STARTED = ['pending', 'expired', 'failed'];\n\nfunction payment(attempt: SubscriptionAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n // `?? null` for an attempt handed by a client that predates the field.\n amount_usd: attempt.amount_usd ?? null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: attempt.retry,\n retry_number: attempt.retry_number,\n retries_allowed: attempt.retries_allowed,\n period_start: attempt.period_start,\n };\n}\n\n/** An attempt of `/v1/access`, which says nothing of retries nor periods. */\nfunction olderPayment(attempt: ServedAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n amount_usd: null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: null,\n retry_number: null,\n retries_allowed: null,\n period_start: null,\n };\n}\n\ntype Payments = Omit<WidgetSubscriptionDetail, 'subscription' | 'upcoming'>;\n\nfunction unlisted(code: string, message: string): Payments {\n return { payments: null, paid: null, payments_error: { code, message } };\n}\n\n/**\n * A subscription's own pull attempts and what it paid, from\n * `GET /v1/subscriptions/:id/attempts`. A Mesub that predates the route\n * (Mesub-io/backend#289) answers a 404 that names no code for it: its payments are then\n * read the older way, without a total.\n */\nasync function paymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n try {\n const page = await client.subscriptions.attempts(subscription.id, {}, read);\n\n return { payments: page.data.map(payment), paid: page.paid, payments_error: null };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // No such route, not no such subscription: that one names its code.\n if (error.status === 404 && error.apiCode === null) {\n return olderPaymentsOf(client, subscription, read);\n }\n\n // The subscription was read: it is answered without them, so the dialog still opens.\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * The last pull attempts as a Mesub without the attempts route serves them:\n * through `/v1/access` only, five at most, for the subscription in force on\n * that wallet and plan, naming no id. They are this one's only when the\n * answer started when this one did. No total: five attempts are not one.\n */\nasync function olderPaymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n const { plan, wallet, confirmed_at: confirmedAt } = subscription;\n const listed = (payments: WidgetPayment[]): Payments => ({\n payments,\n paid: null,\n payments_error: null,\n });\n\n // Never started, so never pulled: nothing to ask.\n if (confirmedAt === null && NEVER_STARTED.includes(subscription.status)) return listed([]);\n if (plan === null || !SLUG.test(plan)) {\n return unlisted('plan_without_slug', 'Its plan has no slug to read payments by.');\n }\n\n try {\n const answer = await client.access({ wallet }, plan, { attempts: true, ...read });\n const same =\n confirmedAt !== null &&\n answer.subscribed_since !== null &&\n Date.parse(answer.subscribed_since) === Date.parse(confirmedAt);\n\n if (!same) {\n return unlisted(\n 'not_the_current_subscription',\n 'This Mesub serves payments for the current subscription of a wallet on a plan only.',\n );\n }\n\n return listed(\n (answer.attempts ?? [])\n .map(olderPayment)\n .sort((a, b) => Date.parse(b.attempted_at) - Date.parse(a.attempted_at)),\n );\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * Answers one request of the widget. `asked` is who your auth says is asking,\n * already normalised, or null for nobody; `email` their notices' address.\n *\n * Never throws for what the caller sent or what Mesub refused of the\n * customer's request: both are answered. An integration error is thrown to\n * the framework, which logs it and answers 500: Mesub refusing your API key\n * (its 401, never handed on as the customer's own) or something in front of\n * Mesub refusing your server (a 403 without Mesub's code), and anything that\n * is not a MesubError. The 401 answered here means nobody is signed in, only.\n */\nexport async function handleWidget(\n client: Mesub,\n request: WidgetRequest,\n asked: Asked | null,\n options: { email?: string | null | undefined; plans?: readonly string[] } = {},\n): Promise<WidgetResponse> {\n const segments = request.path.split('?')[0]!.split('/').filter(Boolean);\n const method = request.method.toUpperCase();\n const read = bounded();\n\n try {\n // A plan is public: the page shows its price before anybody signs in.\n if (method === 'GET' && segments.length === 2 && segments[0] === 'plans') {\n const slug = segments[1]!;\n\n if (!SLUG.test(slug) || (options.plans && !options.plans.includes(slug))) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n const plan = await planOf(client, slug, read);\n\n return plan\n ? { status: 200, body: plan }\n : refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n if (segments[0] !== 'subscriptions') return NOT_FOUND();\n if (method !== 'GET' && method !== 'POST') {\n return refusal(405, 'method_not_allowed', 'GET or POST.');\n }\n if (!asked) return refusal(401, 'unauthenticated', 'Sign in first.');\n\n if (method === 'GET' && segments.length === 2) {\n const id = segments[1]!;\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n // Theirs first: another customer's is not read any further.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n const [upcoming, payments] = await Promise.all([\n upcomingOf(client, subscription, read),\n paymentsOf(client, subscription, read),\n ]);\n const detail: WidgetSubscriptionDetail = {\n subscription: shown(subscription),\n upcoming,\n ...payments,\n };\n\n return { status: 200, body: detail };\n }\n\n if (method === 'GET') {\n if (segments.length !== 1) return NOT_FOUND();\n\n const params = { ...customerParam(asked), limit: LIST_PAGE };\n let page = await client.subscriptions.list(params, read);\n const subscriptions = [...page.data];\n\n const more = () => page.has_more && page.data.length > 0;\n\n // One browser request is LIST_MAX_PAGES calls to Mesub at most.\n for (let pages = 1; more() && pages < LIST_MAX_PAGES; pages++) {\n page = await client.subscriptions.list(\n { ...params, starting_after: page.data.at(-1)!.id },\n read,\n );\n subscriptions.push(...page.data);\n }\n\n const list: WidgetSubscriptionList = {\n subscriptions: subscriptions.map(shown),\n has_more: more(),\n };\n\n return { status: 200, body: list };\n }\n\n // A JSON body cannot be sent by a plain form from another site.\n if (!request.contentType?.toLowerCase().startsWith('application/json')) {\n return refusal(415, 'unsupported_media_type', 'Send JSON.');\n }\n\n const body = fieldsOf(request.body) ?? {};\n\n if (segments.length === 1) {\n const { plan, wallet } = body;\n\n if (!isString(plan) || !SLUG.test(plan) || !isString(wallet)) {\n return refusal(400, 'invalid_request', 'A plan and a wallet are needed.');\n }\n if (options.plans && !options.plans.includes(plan)) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n // Named by wallet, the customer is that wallet: no other may be subscribed for them.\n if (asked.kind === 'wallet' && asked.value !== wallet) {\n return refusal(403, 'wallet_mismatch', 'Connect the wallet you signed in with.');\n }\n\n const email = asked.kind === 'email' ? asked.value : options.email;\n const created = await client.subscriptions.create({\n plan,\n wallet,\n ...(asked.kind === 'external_id' && { external_id: asked.value }),\n ...(isString(email) && { email }),\n });\n\n return { status: 201, body: created };\n }\n\n const id = segments[1]!;\n const step = segments.slice(2).join('/');\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n const known = [\n 'submit',\n 'transaction',\n 'retry',\n ...ACTIONS,\n ...ACTIONS.map((action) => `${action}/confirm`),\n ];\n if (!known.includes(step)) return NOT_FOUND();\n\n // Before anything is built or confirmed: is it theirs at all.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n if (step === 'submit') {\n const { transaction, terms_signature: termsSignature } = body;\n\n if (!isString(transaction) || !isString(termsSignature)) {\n return refusal(\n 400,\n 'invalid_request',\n 'The signed transaction and the terms signature are needed.',\n );\n }\n\n const settled = await client.subscriptions.submit(id, {\n transaction,\n terms_signature: termsSignature,\n });\n\n return { status: 201, body: { ...settled, subscription: shown(settled.subscription) } };\n }\n\n // A fresh transaction for a pending one (Mesub-io/backend#427): answered as subscribing is.\n if (step === 'transaction') {\n return { status: 201, body: await client.subscriptions.transaction(id) };\n }\n\n // Pay now (Mesub-io/backend#354): nothing to sign, the outcome comes with the pull.\n if (step === 'retry') {\n const retried = await client.subscriptions.retry(id);\n\n return { status: 202, body: { subscription: shown(retried) } };\n }\n\n const [action, confirm] = step.split('/') as [Action, 'confirm' | undefined];\n\n if (!confirm) {\n return { status: 201, body: await client.subscriptions[action](id) };\n }\n\n const { signature } = body;\n if (!isString(signature)) {\n return refusal(400, 'invalid_request', 'The signature of the transaction is needed.');\n }\n\n const confirmed = await client.subscriptions[CONFIRM[action]](id, { signature });\n\n return {\n status: 201,\n body: { ...confirmed, subscription: shown(confirmed.subscription) },\n };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // An unknown id is Mesub's 404, answered as ours: nothing tells the two apart.\n if (error.apiCode === 'subscription_not_found') return NO_SUBSCRIPTION();\n\n return fromMesub(error);\n }\n}\n\nconst CONFIRM = {\n cancel: 'confirmCancel',\n resume: 'confirmResume',\n close: 'confirmClose',\n} as const satisfies Record<Action, string>;\n\nfunction customerParam(asked: Asked): Customer {\n if (asked.kind === 'wallet') return { wallet: asked.value };\n\n return asked.kind === 'email' ? { email: asked.value } : { external_id: asked.value };\n}\n\n/**\n * Who your auth says is asking, normalised as Mesub reads it. Null for\n * nobody; a value Mesub cannot be asked about is a broken integration, thrown.\n */\nexport async function widgetCustomer<Req>(\n request: Req,\n customer: WidgetCustomer<Req>,\n): Promise<Asked | null> {\n const named = await customer(request);\n\n if (named === null || named === undefined) return null;\n\n const asked = customerOf(named);\n\n if (asked.value === '') {\n throw new TypeError(\n '`customer` returned an empty value: return null when nobody is signed in.',\n );\n }\n\n return asked;\n}\n\n/** Checked when the routes are built: without `customer` they would serve anybody. */\nexport function checkWidgetOptions<Req>(options: WidgetRoutesOptions<Req>): void {\n if (typeof options?.customer !== 'function') {\n throw new TypeError(\n 'The widget routes need `customer`: a function of the request returning who is signed in.',\n );\n }\n if (options.plans !== undefined && !options.plans.every((plan) => SLUG.test(plan))) {\n throw new TypeError('`plans` is a list of plan slugs.');\n }\n}\n"]}
|