@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,759 @@
|
|
|
1
|
+
import { j as ServerSubscription, B as BlockedReason, T as Transport, R as RequestOptions, w as Subscriptions, A as AccessAnswer, a as AccessList, C as Customer, b as AccessOptions } from './subscriptions-D1hE7MUc.js';
|
|
2
|
+
|
|
3
|
+
/** One cached answer, with the two moments that decide what it is still good for. */
|
|
4
|
+
interface CacheEntry<T> {
|
|
5
|
+
value: T;
|
|
6
|
+
/** Epoch ms. Before it, the answer is served without calling Mesub. */
|
|
7
|
+
freshUntil: number;
|
|
8
|
+
/** Epoch ms. Until then a stale answer is kept for the outage fallback, then dropped. */
|
|
9
|
+
keepUntil: number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Where answers are kept. The memory store is the default; a merchant plugs
|
|
13
|
+
* Redis or anything else by implementing `get` and `set`, and `delete` if it
|
|
14
|
+
* can. Each may be sync or async.
|
|
15
|
+
*
|
|
16
|
+
* A store that throws or rejects costs a cached answer, never the request. One
|
|
17
|
+
* that hangs without failing, as a saturated Redis, costs no more under a
|
|
18
|
+
* guard (#132): its `get` is given until the guard's `guardTimeout` runs out,
|
|
19
|
+
* then read as a miss, and an answer never waits for its `set`.
|
|
20
|
+
*/
|
|
21
|
+
interface CacheStore<T = unknown> {
|
|
22
|
+
/**
|
|
23
|
+
* Under a guard, read as a miss when it has not answered within
|
|
24
|
+
* `guardTimeout`: the guard then has no time left to ask Mesub, and
|
|
25
|
+
* answers from nothing, a 503.
|
|
26
|
+
*/
|
|
27
|
+
get(key: string): CacheEntry<T> | undefined | Promise<CacheEntry<T> | undefined>;
|
|
28
|
+
/**
|
|
29
|
+
* `ttlMs` is how long until `keepUntil`, for a store that expires keys
|
|
30
|
+
* itself. Not awaited: the answer is handed back while it runs, so a read
|
|
31
|
+
* right after may not find it yet.
|
|
32
|
+
*/
|
|
33
|
+
set(key: string, entry: CacheEntry<T>, ttlMs: number): void | Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Drops a key, when a subscription that grants access lands and the
|
|
36
|
+
* answer cached says no. Optional: without it, that answer is rewritten
|
|
37
|
+
* as stale instead, which also makes the next call ask Mesub.
|
|
38
|
+
*/
|
|
39
|
+
delete?(key: string): void | Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** How long a stale answer is kept for the outage fallback, then dropped. */
|
|
43
|
+
declare const DEFAULT_MAX_STALE_MS: number;
|
|
44
|
+
/** The one field the cache reads from an answer: seconds it stays true. */
|
|
45
|
+
interface Revalidating {
|
|
46
|
+
revalidate_after: number;
|
|
47
|
+
}
|
|
48
|
+
interface AccessCacheOptions {
|
|
49
|
+
maxStaleMs?: number;
|
|
50
|
+
now?: () => number;
|
|
51
|
+
}
|
|
52
|
+
/** What a read found: the answer, and whether it can be served without calling Mesub. */
|
|
53
|
+
interface CachedAnswer<T> {
|
|
54
|
+
value: T;
|
|
55
|
+
fresh: boolean;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The `/v1/access` answers, by customer and plan, over any store.
|
|
59
|
+
*
|
|
60
|
+
* A store that fails never breaks a request: a read that throws is a miss,
|
|
61
|
+
* a write that throws is dropped. A merchant whose Redis is down still gets
|
|
62
|
+
* answers, straight from Mesub. Nor does one that hangs hold a guard: given a
|
|
63
|
+
* deadline, a read not answered by then is a miss too (#132).
|
|
64
|
+
*/
|
|
65
|
+
declare class AccessCache<T extends Revalidating> {
|
|
66
|
+
private readonly store;
|
|
67
|
+
private readonly maxStaleMs;
|
|
68
|
+
private readonly now;
|
|
69
|
+
constructor(store: CacheStore<T>, options?: AccessCacheOptions);
|
|
70
|
+
/**
|
|
71
|
+
* `mesub:access:<scope>:<plan>:<who>`, prefixed so it can share a Redis
|
|
72
|
+
* with anything. The scope (the client passes a hash of its API key) keeps
|
|
73
|
+
* two projects sharing one store apart; without one, `mesub:access:<plan>:<who>`.
|
|
74
|
+
*
|
|
75
|
+
* `who` is whatever names the customer, the client's being
|
|
76
|
+
* `<kind>:<wallet or hash>`. A null plan is the list of every plan they
|
|
77
|
+
* have, under its own prefix, `mesub:access-list:`: no plan slug can reach it.
|
|
78
|
+
*/
|
|
79
|
+
static key(who: string, plan: string | null, scope?: string): string;
|
|
80
|
+
/** With a `deadline` (epoch ms), a store that has not answered by then is a miss. */
|
|
81
|
+
read(who: string, plan: string | null, scope?: string, deadline?: number): Promise<CachedAnswer<T> | undefined>;
|
|
82
|
+
/** What `pending` gives by `deadline`, or undefined, as a miss, when it has not answered. */
|
|
83
|
+
private within;
|
|
84
|
+
write(who: string, plan: string | null, value: T, scope?: string): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Drops the cached answer when `outdated` says it no longer holds, so the
|
|
87
|
+
* next call asks Mesub. A store without `delete` gets it back as stale
|
|
88
|
+
* instead: never served as is again, only as the outage fallback.
|
|
89
|
+
*/
|
|
90
|
+
forget(who: string, plan: string | null, scope: string | undefined, outdated: (value: T) => boolean): Promise<void>;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Webhooks, as Mesub-io/backend#144 sends them: Standard Webhooks
|
|
95
|
+
* (https://www.standardwebhooks.com), a POST whose body is signed with the
|
|
96
|
+
* endpoint's secret. Since Mesub-io/backend#482 the body is the event, as
|
|
97
|
+
* `GET /v1/events` serves it. The types copy the back's `event-body.ts`,
|
|
98
|
+
* `webhook-payload.ts`, `webhook-events.ts` and `pull-events.ts`, which stay
|
|
99
|
+
* the source of truth. Snake case, as the API serves it.
|
|
100
|
+
*/
|
|
101
|
+
/** A Fetch `Headers` (Next, route handlers) or Node's plain object (Express). */
|
|
102
|
+
type HeaderSource = Headers | Record<string, string | string[] | undefined>;
|
|
103
|
+
/** The v1 events an endpoint subscribes to, and `test`, sent from the dashboard on request. */
|
|
104
|
+
type WebhookEventType = WebhookEvent['type'];
|
|
105
|
+
/** The v1 events `GET /v1/events` serves: every type but `test`. */
|
|
106
|
+
type EventType = MesubEvent['type'];
|
|
107
|
+
/** The Solana network a subscription is on, as `/v1` spells it. */
|
|
108
|
+
type EventNetwork = 'devnet' | 'mainnet-beta';
|
|
109
|
+
/**
|
|
110
|
+
* One event, as `GET /v1/events` serves it and its webhooks carry it
|
|
111
|
+
* (Mesub-io/backend#482): written once, the same bytes for every endpoint,
|
|
112
|
+
* every retry and every read.
|
|
113
|
+
*/
|
|
114
|
+
interface EventOf<Type extends string, Detail> {
|
|
115
|
+
/**
|
|
116
|
+
* The event's own id, the same for every endpoint and every delivery:
|
|
117
|
+
* what `events.retrieve` takes.
|
|
118
|
+
*/
|
|
119
|
+
id: string;
|
|
120
|
+
object: 'event';
|
|
121
|
+
type: Type;
|
|
122
|
+
/** When the event happened (ISO 8601), not when it was sent. */
|
|
123
|
+
created_at: string;
|
|
124
|
+
/** The Solana network the subscription is on. */
|
|
125
|
+
network: EventNetwork;
|
|
126
|
+
/** The `Mesub-Version` `data` and `detail` are written in, kept with the body. */
|
|
127
|
+
api_version: string;
|
|
128
|
+
/**
|
|
129
|
+
* Its place among this subscription's events (Mesub-io/backend#502): 1,
|
|
130
|
+
* 2, 3... in the order they happened, two events of one change included.
|
|
131
|
+
* Deliveries arrive in any order: apply one subscription's events by
|
|
132
|
+
* `sequence`, and skip one lower than the last you applied for it.
|
|
133
|
+
* Absent on a body written before Mesub numbered events, and on one it
|
|
134
|
+
* could not number: such an event cannot be placed, so read the
|
|
135
|
+
* subscription now (`subscriptions.retrieve` or `access`) rather than
|
|
136
|
+
* applying its `data`. Always 1 on a test delivery.
|
|
137
|
+
*/
|
|
138
|
+
sequence?: number;
|
|
139
|
+
/**
|
|
140
|
+
* The subscription as it stood right after the change that raised the
|
|
141
|
+
* event, as `GET /v1/subscriptions/:id` would have answered it then,
|
|
142
|
+
* however late the delivery. Events raised by one change share it:
|
|
143
|
+
* `detail` says what each is about.
|
|
144
|
+
*/
|
|
145
|
+
data: ServerSubscription;
|
|
146
|
+
/** What the event is about, beyond its type. */
|
|
147
|
+
detail: Detail;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* One event as `verify` hands it back: the body Mesub signed, and
|
|
151
|
+
* `delivery_id`, its `webhook-id` header.
|
|
152
|
+
*
|
|
153
|
+
* A body written before the event shape (before `available_since`, see
|
|
154
|
+
* `events.list`) keeps the shape it was sent in, `{ type, created_at, data }`
|
|
155
|
+
* with the detail inside `data`. It is handed back in this shape all the
|
|
156
|
+
* same: `detail` moved to the top, and `id`, `network` and `api_version`
|
|
157
|
+
* null, since it carried none. Its `data` is the subscription as it stood at
|
|
158
|
+
* the delivery's first attempt.
|
|
159
|
+
*/
|
|
160
|
+
interface WebhookEventOf<Type extends string, Detail> extends Omit<EventOf<Type, Detail>, 'id' | 'network' | 'api_version'> {
|
|
161
|
+
/** The event's id, as `EventOf`'s; null on a body written in the older shape. */
|
|
162
|
+
id: string | null;
|
|
163
|
+
/**
|
|
164
|
+
* The delivery's id, from the `webhook-id` header: one per event and per
|
|
165
|
+
* endpoint, the same on every retry and resend. Drop a delivery you
|
|
166
|
+
* already handled by it; across endpoints, by `id`.
|
|
167
|
+
*/
|
|
168
|
+
delivery_id: string;
|
|
169
|
+
/** Null on a body written in the older shape. */
|
|
170
|
+
network: EventNetwork | null;
|
|
171
|
+
/** Null on a body written in the older shape. */
|
|
172
|
+
api_version: string | null;
|
|
173
|
+
/**
|
|
174
|
+
* True on a body sent by hand from the dashboard: the `test` event, or
|
|
175
|
+
* a sample of a real type on a made-up subscription (`sub_test`,
|
|
176
|
+
* `user_123`), with the id `evt_test`. Absent on a real event. Change
|
|
177
|
+
* nothing for it.
|
|
178
|
+
*/
|
|
179
|
+
test?: true;
|
|
180
|
+
}
|
|
181
|
+
/** No detail: the subscription says it all. */
|
|
182
|
+
type NoDetail = Record<string, never>;
|
|
183
|
+
/** `subscription.created`: the first payment landed. */
|
|
184
|
+
interface CreatedDetail {
|
|
185
|
+
/** On a wallet coming back after a stop, the subscription it replaces. */
|
|
186
|
+
previous_id?: string;
|
|
187
|
+
/**
|
|
188
|
+
* What the first payment paid (Mesub-io/backend#509). Null when Mesub
|
|
189
|
+
* recorded none at the confirmation (a period paid only in part, which
|
|
190
|
+
* the next charge collects); absent on an event written before it.
|
|
191
|
+
*/
|
|
192
|
+
first_payment?: FirstPayment | null;
|
|
193
|
+
}
|
|
194
|
+
/** The first payment of a subscription: the transfer its own transaction carried. */
|
|
195
|
+
interface FirstPayment {
|
|
196
|
+
/** In the token's smallest unit, as a string. */
|
|
197
|
+
amount: string;
|
|
198
|
+
mint: string | null;
|
|
199
|
+
/** The transaction that paid it. */
|
|
200
|
+
signature: string | null;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* `subscription.ended`: `changed_at` only on a cancellation that reached its
|
|
204
|
+
* end (Mesub-io/backend#509), its expiry to the millisecond, the moment
|
|
205
|
+
* `data` describes. Absent on any other end.
|
|
206
|
+
*/
|
|
207
|
+
interface EndedDetail {
|
|
208
|
+
changed_at?: string;
|
|
209
|
+
/**
|
|
210
|
+
* `cancelled` on a cancellation that reached its end, as `data.end_reason`
|
|
211
|
+
* says it (Mesub-io/backend#511); absent otherwise, and on an older event.
|
|
212
|
+
*/
|
|
213
|
+
end_reason?: 'cancelled';
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* `subscription.renewed`: a period paid. Amounts in the mint's base units, as
|
|
217
|
+
* strings. It can come late: a transfer whose send failed may land later, and
|
|
218
|
+
* Mesub sends `renewed` then. By that time the subscription may no longer be
|
|
219
|
+
* `active`. Ask `hasAccess` before granting access.
|
|
220
|
+
*/
|
|
221
|
+
interface RenewedDetail {
|
|
222
|
+
amount: string;
|
|
223
|
+
mint: string;
|
|
224
|
+
period_start: string;
|
|
225
|
+
period_end: string;
|
|
226
|
+
/** The transaction that paid it. */
|
|
227
|
+
signature: string;
|
|
228
|
+
}
|
|
229
|
+
/** `subscription.payment_failed`: a pull missed, and what comes next. */
|
|
230
|
+
interface PaymentFailedDetail {
|
|
231
|
+
/** Why, e.g. `insufficient_balance`: an attempt's reason (`ServedAttempt`). */
|
|
232
|
+
reason: string;
|
|
233
|
+
amount: string;
|
|
234
|
+
mint: string;
|
|
235
|
+
/** The period it was for; null when unknown. */
|
|
236
|
+
period_start: string | null;
|
|
237
|
+
period_end: string | null;
|
|
238
|
+
/**
|
|
239
|
+
* When Mesub retries; null when it will not (the plan ends before the
|
|
240
|
+
* retry, among others), or on Free, where retries are by hand.
|
|
241
|
+
*/
|
|
242
|
+
next_retry_at: string | null;
|
|
243
|
+
/** Free only: when hand retries close, the plan's end when that comes first. */
|
|
244
|
+
retry_deadline: string | null;
|
|
245
|
+
/**
|
|
246
|
+
* Pulls still to come before the subscription stops, the next one
|
|
247
|
+
* included; 0 with `next_retry_at` null when the plan ends before any.
|
|
248
|
+
*/
|
|
249
|
+
retries_left: number;
|
|
250
|
+
/** Who retries: Mesub on its schedule, or you by hand (Free). Null once none is left. */
|
|
251
|
+
retry_mode: 'scheduled' | 'manual' | null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* `subscription.stopped`: no more pulls. `data.stop_reason` says why it
|
|
255
|
+
* stopped, the reading a program branches on.
|
|
256
|
+
*/
|
|
257
|
+
interface StoppedDetail {
|
|
258
|
+
/** Why, e.g. `insufficient_balance` or, on Free, `retry_deadline_passed`. */
|
|
259
|
+
reason: string;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Why the coming charge would not pay: the wallet holds too little or has no
|
|
263
|
+
* account of the token (`balance`), or Mesub's approval can no longer move
|
|
264
|
+
* the tokens (`authority`). One newer than this release is handed back as it is.
|
|
265
|
+
*/
|
|
266
|
+
type RenewalIssue = 'balance' | 'authority';
|
|
267
|
+
/**
|
|
268
|
+
* `subscription.renewal_upcoming`: the charge due next, and whether the wallet
|
|
269
|
+
* can pay it as it stood at the event's `created_at`. A reading, not a promise.
|
|
270
|
+
*/
|
|
271
|
+
interface RenewalUpcomingDetail {
|
|
272
|
+
can_pay: boolean;
|
|
273
|
+
/** Why not; null when it can. */
|
|
274
|
+
renewal_issue: RenewalIssue | null;
|
|
275
|
+
amount: string;
|
|
276
|
+
mint: string;
|
|
277
|
+
/** When the charge is due. */
|
|
278
|
+
due_at: string;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* `subscription.access_granted` and `subscription.access_revoked`: when
|
|
282
|
+
* access actually changed. A date that passed with nothing else happening
|
|
283
|
+
* (the end of a paid period, of a cancellation, of the plan) is that date to
|
|
284
|
+
* the millisecond, even when Mesub wrote the event later; `created_at` is
|
|
285
|
+
* when it was written.
|
|
286
|
+
*/
|
|
287
|
+
interface AccessChangedDetail {
|
|
288
|
+
changed_at: string;
|
|
289
|
+
/**
|
|
290
|
+
* Why the subscription's charges were held when access changed, in the
|
|
291
|
+
* words of `data.blocked_reason`; null when they were not. Absent on an
|
|
292
|
+
* event written before Mesub-io/backend#511. Keep a default branch: Mesub
|
|
293
|
+
* may add a reason.
|
|
294
|
+
*/
|
|
295
|
+
blocked_reason?: BlockedReason | null;
|
|
296
|
+
/**
|
|
297
|
+
* On `access_revoked`, the date access ended: the end of a paid period,
|
|
298
|
+
* of a cancellation, of the plan, or of the access a held subscription
|
|
299
|
+
* keeps; never after `changed_at`, and null when access ended with no
|
|
300
|
+
* date of its own (a stop). On `access_granted`, on a held subscription,
|
|
301
|
+
* the date access ends if nothing is collected by then; null otherwise.
|
|
302
|
+
* Absent on an event written before Mesub-io/backend#511.
|
|
303
|
+
*/
|
|
304
|
+
access_until?: string | null;
|
|
305
|
+
}
|
|
306
|
+
/** Each event type and its `detail`. */
|
|
307
|
+
interface EventDetails {
|
|
308
|
+
'subscription.created': CreatedDetail;
|
|
309
|
+
'subscription.renewal_upcoming': RenewalUpcomingDetail;
|
|
310
|
+
'subscription.renewed': RenewedDetail;
|
|
311
|
+
'subscription.payment_failed': PaymentFailedDetail;
|
|
312
|
+
'subscription.stopped': StoppedDetail;
|
|
313
|
+
'subscription.cancelled': NoDetail;
|
|
314
|
+
'subscription.resumed': NoDetail;
|
|
315
|
+
'subscription.ended': EndedDetail;
|
|
316
|
+
'subscription.expired': NoDetail;
|
|
317
|
+
'subscription.access_granted': AccessChangedDetail;
|
|
318
|
+
'subscription.access_revoked': AccessChangedDetail;
|
|
319
|
+
'subscription.paused': NoDetail;
|
|
320
|
+
'subscription.unpaused': NoDetail;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Every event `GET /v1/events` serves. Narrow it with `switch (event.type)`,
|
|
324
|
+
* and keep a default branch: a type newer than this release is handed back
|
|
325
|
+
* too, its subscription checked.
|
|
326
|
+
*/
|
|
327
|
+
type MesubEvent = {
|
|
328
|
+
[Type in keyof EventDetails]: EventOf<Type, EventDetails[Type]>;
|
|
329
|
+
}[keyof EventDetails];
|
|
330
|
+
type SubscriptionCreatedEvent = WebhookEventOf<'subscription.created', CreatedDetail>;
|
|
331
|
+
/** Once per period, in its last quarter and three days before the charge at most. */
|
|
332
|
+
type SubscriptionRenewalUpcomingEvent = WebhookEventOf<'subscription.renewal_upcoming', RenewalUpcomingDetail>;
|
|
333
|
+
type SubscriptionRenewedEvent = WebhookEventOf<'subscription.renewed', RenewedDetail>;
|
|
334
|
+
type SubscriptionPaymentFailedEvent = WebhookEventOf<'subscription.payment_failed', PaymentFailedDetail>;
|
|
335
|
+
type SubscriptionStoppedEvent = WebhookEventOf<'subscription.stopped', StoppedDetail>;
|
|
336
|
+
type SubscriptionCancelledEvent = WebhookEventOf<'subscription.cancelled', NoDetail>;
|
|
337
|
+
type SubscriptionResumedEvent = WebhookEventOf<'subscription.resumed', NoDetail>;
|
|
338
|
+
/**
|
|
339
|
+
* Sent when the plan ends, is removed or replaced, and when a subscription is
|
|
340
|
+
* closed. Once Mesub turns the access events on, also when a cancellation
|
|
341
|
+
* reaches its end (Mesub-io/backend#509): `end_reason` `cancelled`,
|
|
342
|
+
* `detail.changed_at` its expiry, usually within half an hour of it, after
|
|
343
|
+
* that expiry's `subscription.access_revoked`. Closing it later sends it
|
|
344
|
+
* again, `end_reason` `closed`: one subscription can end twice.
|
|
345
|
+
*/
|
|
346
|
+
type SubscriptionEndedEvent = WebhookEventOf<'subscription.ended', EndedDetail>;
|
|
347
|
+
/** A checkout nobody signed in time. */
|
|
348
|
+
type SubscriptionExpiredEvent = WebhookEventOf<'subscription.expired', NoDetail>;
|
|
349
|
+
/**
|
|
350
|
+
* Access started (Mesub-io/backend#507): the subscription grants access now
|
|
351
|
+
* and did not just before. Its first access, a payment that gives access
|
|
352
|
+
* back, a resume after a cancellation ended. A renewal paid on time, or a
|
|
353
|
+
* resume while the period runs, sends none. `data.access` is true.
|
|
354
|
+
*
|
|
355
|
+
* With `subscription.access_revoked`, the one pair to drive access from:
|
|
356
|
+
* sent exactly when access changes, and every change sends one. Apply them
|
|
357
|
+
* by `sequence`; the last one applied says whether the customer has access.
|
|
358
|
+
* The other events say why. Off until Mesub turns them on (`ACCESS_EVENTS`).
|
|
359
|
+
*/
|
|
360
|
+
type SubscriptionAccessGrantedEvent = WebhookEventOf<'subscription.access_granted', AccessChangedDetail>;
|
|
361
|
+
/**
|
|
362
|
+
* Access ended (Mesub-io/backend#507): the subscription grants no access now
|
|
363
|
+
* and did just before. It stopped or ended, a cancellation or the plan
|
|
364
|
+
* reached its end, or a blocked collection reached its `access_until`. For a
|
|
365
|
+
* date that passed, sent a few seconds after it. `data.access` is false.
|
|
366
|
+
* Off until Mesub turns it on.
|
|
367
|
+
*/
|
|
368
|
+
type SubscriptionAccessRevokedEvent = WebhookEventOf<'subscription.access_revoked', AccessChangedDetail>;
|
|
369
|
+
/**
|
|
370
|
+
* A seat parked (Mesub-io/backend#509): the project holds more subscribers
|
|
371
|
+
* than its tier allows, and Mesub stopped charging the newest. Not a pause
|
|
372
|
+
* the subscriber asked for; the status stays. `data.paused` is true,
|
|
373
|
+
* `data.next_charge_at` null; access runs to the end of the paid period,
|
|
374
|
+
* and its `subscription.access_revoked` comes then. Off until Mesub turns
|
|
375
|
+
* the access events on.
|
|
376
|
+
*/
|
|
377
|
+
type SubscriptionPausedEvent = WebhookEventOf<'subscription.paused', NoDetail>;
|
|
378
|
+
/**
|
|
379
|
+
* A parked seat back (Mesub-io/backend#509): the tier has room again, and
|
|
380
|
+
* Mesub charges it again. `data.paused` is false; a
|
|
381
|
+
* `subscription.access_granted` follows when its access had ended. Off until
|
|
382
|
+
* Mesub turns the access events on.
|
|
383
|
+
*/
|
|
384
|
+
type SubscriptionUnpausedEvent = WebhookEventOf<'subscription.unpaused', NoDetail>;
|
|
385
|
+
/**
|
|
386
|
+
* Sent from the dashboard on request, with a made-up subscription (`sub_test`).
|
|
387
|
+
* The dashboard also sends samples of the other types, with `test: true`.
|
|
388
|
+
*/
|
|
389
|
+
type TestEvent = WebhookEventOf<'test', NoDetail>;
|
|
390
|
+
/**
|
|
391
|
+
* Every event Mesub sends. Narrow it with `switch (event.type)`, and keep a
|
|
392
|
+
* default branch: an event type newer than this release is handed back too,
|
|
393
|
+
* its subscription checked, typed as one of these.
|
|
394
|
+
*/
|
|
395
|
+
type WebhookEvent = SubscriptionCreatedEvent | SubscriptionRenewalUpcomingEvent | SubscriptionRenewedEvent | SubscriptionPaymentFailedEvent | SubscriptionStoppedEvent | SubscriptionCancelledEvent | SubscriptionResumedEvent | SubscriptionEndedEvent | SubscriptionExpiredEvent | SubscriptionAccessGrantedEvent | SubscriptionAccessRevokedEvent | SubscriptionPausedEvent | SubscriptionUnpausedEvent | TestEvent;
|
|
396
|
+
/** The body as received, never parsed: a string, a Buffer or any Uint8Array, an ArrayBuffer. */
|
|
397
|
+
type WebhookBody = string | Uint8Array | ArrayBuffer;
|
|
398
|
+
interface VerifyWebhookOptions {
|
|
399
|
+
/**
|
|
400
|
+
* The endpoint's signing secret, `whsec_...`. Defaults to the client's
|
|
401
|
+
* `webhookSecret`, then to `process.env.MESUB_WEBHOOK_SECRET`.
|
|
402
|
+
*/
|
|
403
|
+
secret?: string;
|
|
404
|
+
/**
|
|
405
|
+
* How far `webhook-timestamp` may be from now, either way, in
|
|
406
|
+
* milliseconds, above 0: older is refused as a replay. Defaults to 5 minutes.
|
|
407
|
+
*/
|
|
408
|
+
tolerance?: number;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Checks a webhook Mesub sent and hands back its event: the signature over
|
|
412
|
+
* the raw body, against the endpoint's secret, then the timestamp, then the
|
|
413
|
+
* body's shape.
|
|
414
|
+
*
|
|
415
|
+
* Throws a MesubError `invalid_webhook` when it is not Mesub's, or not now:
|
|
416
|
+
* a header missing, no signature matching, a timestamp outside `tolerance`.
|
|
417
|
+
* Answer it with a 400.
|
|
418
|
+
*
|
|
419
|
+
* Throws `unexpected` for a body Mesub signed that this release cannot read.
|
|
420
|
+
* Never answer it with a 400. Log it, update `@mesub/node`, and answer a
|
|
421
|
+
* 5xx: Mesub retries the same body for 3 days, so the event gets through
|
|
422
|
+
* once the update is out. Past 3 days of failures, Mesub disables the
|
|
423
|
+
* endpoint.
|
|
424
|
+
*
|
|
425
|
+
* A TypeError is an integration error: a body already parsed, no secret,
|
|
426
|
+
* one that is not `whsec_...`.
|
|
427
|
+
*/
|
|
428
|
+
declare function verifyWebhook(body: WebhookBody, headers: HeaderSource, options?: VerifyWebhookOptions): Promise<WebhookEvent>;
|
|
429
|
+
/** `mesub.webhooks`: `verifyWebhook` with the client's own secret. */
|
|
430
|
+
declare class Webhooks {
|
|
431
|
+
#private;
|
|
432
|
+
/** @internal */
|
|
433
|
+
constructor(secret: string | undefined, received: (event: WebhookEvent) => Promise<void>);
|
|
434
|
+
/**
|
|
435
|
+
* `verifyWebhook`, with `webhookSecret` unless `options.secret` says
|
|
436
|
+
* otherwise. A verified event also drops every access answer cached for
|
|
437
|
+
* its customer, the yes and the no, so the next check asks Mesub.
|
|
438
|
+
* Three drop nothing: `subscription.renewal_upcoming`, which moves no
|
|
439
|
+
* access, the `test` event, and any body with `test: true`, whose
|
|
440
|
+
* subscription is made up.
|
|
441
|
+
*/
|
|
442
|
+
verify(body: WebhookBody, headers: HeaderSource, options?: VerifyWebhookOptions): Promise<WebhookEvent>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Your project's subscription events (Mesub-io/backend#482): `GET /v1/events`
|
|
447
|
+
* and `GET /v1/events/:id`, each event the very body its webhooks carry. The
|
|
448
|
+
* types copy the back's `server-events.service.ts`, which stays the source
|
|
449
|
+
* of truth. Snake case, as the API serves it.
|
|
450
|
+
*/
|
|
451
|
+
/** One page of events, newest first. */
|
|
452
|
+
interface EventList {
|
|
453
|
+
data: MesubEvent[];
|
|
454
|
+
/** Ask for the next page with `starting_after` set to the last id of this one. */
|
|
455
|
+
has_more: boolean;
|
|
456
|
+
/**
|
|
457
|
+
* When Mesub began keeping events in this shape: events before it are
|
|
458
|
+
* never served, whatever the window. Their webhooks carried the older
|
|
459
|
+
* shape, with no event id.
|
|
460
|
+
*/
|
|
461
|
+
available_since: string;
|
|
462
|
+
}
|
|
463
|
+
/** Which events, and which page of them. */
|
|
464
|
+
interface EventListParams {
|
|
465
|
+
/** Only events of this type. */
|
|
466
|
+
type?: EventType;
|
|
467
|
+
/** Only events about this subscription, by its id. */
|
|
468
|
+
subscription?: string;
|
|
469
|
+
/** 1 to 100, 25 by default. */
|
|
470
|
+
limit?: number;
|
|
471
|
+
/** The id of the last event of the previous page. */
|
|
472
|
+
starting_after?: string;
|
|
473
|
+
}
|
|
474
|
+
declare class Events {
|
|
475
|
+
#private;
|
|
476
|
+
/** @internal */
|
|
477
|
+
constructor(transport: Transport);
|
|
478
|
+
/**
|
|
479
|
+
* Your project's events of the last 30 days, newest first, one page at a
|
|
480
|
+
* time: each the very body its webhooks carried, the same `id`, `data`
|
|
481
|
+
* and `detail`. Read it to catch up on what an endpoint missed while it
|
|
482
|
+
* was down or turned off. Events before `available_since` are never
|
|
483
|
+
* served, whatever the window.
|
|
484
|
+
*
|
|
485
|
+
* An event is listed a moment after its `created_at`, so one can appear
|
|
486
|
+
* below one already read: when polling for what is new, page on until
|
|
487
|
+
* `created_at` is a minute older than the newest you held, and skip the
|
|
488
|
+
* ids you have seen.
|
|
489
|
+
*
|
|
490
|
+
* Needs a Mesub that serves the route (Mesub-io/backend#482): from an
|
|
491
|
+
* older one it throws a `not_found` whose `apiCode` is null.
|
|
492
|
+
*/
|
|
493
|
+
list(params?: EventListParams, options?: RequestOptions): Promise<EventList>;
|
|
494
|
+
/**
|
|
495
|
+
* One event, by the `id` its webhook body carries, as `list` serves it.
|
|
496
|
+
* A `not_found` whose `apiCode` is `event_not_found` for an id older than
|
|
497
|
+
* 30 days or than `available_since`, or of another project. A delivery's
|
|
498
|
+
* `webhook-id` is not an event id: read `event.id`, not `delivery_id`.
|
|
499
|
+
*/
|
|
500
|
+
retrieve(id: string, options?: RequestOptions): Promise<MesubEvent>;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
/** A plan a subscriber can hold: taking new ones (`active`), or ending (`sunset`). */
|
|
504
|
+
type PlanStatus = 'active' | 'sunset';
|
|
505
|
+
/**
|
|
506
|
+
* What Mesub knows of a plan's token, resolved by its mint: never from the
|
|
507
|
+
* merchant nor the client. A symbol is free text on chain and a fake "USDC"
|
|
508
|
+
* is a known scam: show an unverified token as unverified, with its mint.
|
|
509
|
+
*/
|
|
510
|
+
interface PlanToken {
|
|
511
|
+
/** Null when Mesub found none, and on an unverified token named like a vouched stablecoin. */
|
|
512
|
+
symbol: string | null;
|
|
513
|
+
name: string | null;
|
|
514
|
+
logo_url: string | null;
|
|
515
|
+
/** True for a mint Mesub vouches for, or one Jupiter marks verified. */
|
|
516
|
+
verified: boolean;
|
|
517
|
+
}
|
|
518
|
+
/** A plan of your project, as a pricing page needs it. */
|
|
519
|
+
interface Plan {
|
|
520
|
+
/** What `access`, the guards and `subscriptions.create` name it by. */
|
|
521
|
+
slug: string;
|
|
522
|
+
/** The plan's name, or its slug when none was given. */
|
|
523
|
+
name: string;
|
|
524
|
+
description: string | null;
|
|
525
|
+
project_name: string;
|
|
526
|
+
logo_url: string | null;
|
|
527
|
+
/** The price in the token's base units, a string: a u64 does not survive a float. */
|
|
528
|
+
amount: string;
|
|
529
|
+
/** The price as a person counts it: `"9.99"`. */
|
|
530
|
+
amount_display: string;
|
|
531
|
+
/**
|
|
532
|
+
* The plan's amount in dollars at the latest price Mesub knows, a decimal
|
|
533
|
+
* string such as `"9.99"`: an estimate to show beside the amount, never
|
|
534
|
+
* what is charged. Null when Mesub knows no price, and from a Mesub that
|
|
535
|
+
* predates it.
|
|
536
|
+
*/
|
|
537
|
+
amount_usd: string | null;
|
|
538
|
+
decimals: number;
|
|
539
|
+
/** Set only for a mint Mesub vouches for (USDC, USDT, PYUSD), null for any other. */
|
|
540
|
+
symbol: string | null;
|
|
541
|
+
/**
|
|
542
|
+
* The token's symbol, name and logo for any mint, and whether it is
|
|
543
|
+
* verified: when it is not, say so and keep `mint` visible. All null and
|
|
544
|
+
* unverified from a Mesub that predates it.
|
|
545
|
+
*/
|
|
546
|
+
token: PlanToken;
|
|
547
|
+
mint: string;
|
|
548
|
+
period_hours: number;
|
|
549
|
+
network: 'devnet' | 'mainnet-beta';
|
|
550
|
+
status: PlanStatus;
|
|
551
|
+
/** Whether it takes new subscribers now: show "Subscribe" only when true. */
|
|
552
|
+
available: boolean;
|
|
553
|
+
/**
|
|
554
|
+
* When the plan ends, null when it has no end: nobody has access past
|
|
555
|
+
* it, and the last period before it is charged in full.
|
|
556
|
+
*/
|
|
557
|
+
ends_at: string | null;
|
|
558
|
+
}
|
|
559
|
+
/** `mesub.plans`: the plans of your project, for a pricing page. */
|
|
560
|
+
declare class Plans {
|
|
561
|
+
#private;
|
|
562
|
+
/** @internal */
|
|
563
|
+
constructor(transport: Transport);
|
|
564
|
+
/**
|
|
565
|
+
* Every plan of your project a subscriber can hold, sorted by slug: the
|
|
566
|
+
* active ones, and the ones ending (`sunset`, `available: false`). Never
|
|
567
|
+
* cached: call it where you render, and cache it your way.
|
|
568
|
+
*/
|
|
569
|
+
list(options?: RequestOptions): Promise<Plan[]>;
|
|
570
|
+
/** One plan by its slug. A slug your project lacks throws `plan_not_found`. */
|
|
571
|
+
retrieve(slug: string, options?: RequestOptions): Promise<Plan>;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/** What a guard decided, and on which answer. */
|
|
575
|
+
interface Decision {
|
|
576
|
+
access: boolean;
|
|
577
|
+
answer: AccessAnswer | null;
|
|
578
|
+
/** The answer came from the outage fallback, not from Mesub just now. */
|
|
579
|
+
stale: boolean;
|
|
580
|
+
/**
|
|
581
|
+
* Mesub did not answer (an outage, a rate limit, or the guard's time
|
|
582
|
+
* budget running out) and no answer was cached for that customer: nobody
|
|
583
|
+
* knows yet, so the guards answer 503, not 402.
|
|
584
|
+
*/
|
|
585
|
+
unavailable?: boolean;
|
|
586
|
+
/**
|
|
587
|
+
* With `unavailable`, the milliseconds Mesub's `Retry-After` asked for,
|
|
588
|
+
* when it sent one: a guard's 503 hands it on (#132).
|
|
589
|
+
*/
|
|
590
|
+
retryAfter?: number;
|
|
591
|
+
}
|
|
592
|
+
interface MesubOptions {
|
|
593
|
+
/** API key. Defaults to `process.env.MESUB_API_KEY`. */
|
|
594
|
+
apiKey?: string;
|
|
595
|
+
/**
|
|
596
|
+
* Defaults to `https://api.mesub.io`. May carry a path, e.g. behind a
|
|
597
|
+
* proxy at `https://proxy.example.com/mesub`: every call is made under it.
|
|
598
|
+
*/
|
|
599
|
+
baseUrl?: string;
|
|
600
|
+
/**
|
|
601
|
+
* Extra headers sent with every call to Mesub: e.g. a Cloudflare Access
|
|
602
|
+
* service token for the proxy in front. Cannot set Authorization,
|
|
603
|
+
* User-Agent, Accept, Content-Type nor Mesub-Version.
|
|
604
|
+
*/
|
|
605
|
+
headers?: Record<string, string>;
|
|
606
|
+
/** A custom `fetch`, e.g. one bound to your own agent. Defaults to the global one. */
|
|
607
|
+
fetch?: typeof fetch;
|
|
608
|
+
/** Per attempt, in milliseconds. Defaults to 5000. */
|
|
609
|
+
timeout?: number;
|
|
610
|
+
/**
|
|
611
|
+
* How many times a failed call to Mesub is sent again, after the first
|
|
612
|
+
* attempt. Defaults to 2. HTTP retries only: nothing to do with a plan's
|
|
613
|
+
* pull retries, which Mesub runs on its side (and a Free plan has none).
|
|
614
|
+
*/
|
|
615
|
+
maxRetries?: number;
|
|
616
|
+
/**
|
|
617
|
+
* Where `/v1/access` answers are kept. Defaults to a memory store of
|
|
618
|
+
* 10,000 entries, emptied on restart; a Redis store keeps the outage
|
|
619
|
+
* fallback across restarts and servers.
|
|
620
|
+
*/
|
|
621
|
+
cache?: CacheStore<AccessAnswer | AccessList>;
|
|
622
|
+
/**
|
|
623
|
+
* How long an answer is kept once stale, for the outage fallback of
|
|
624
|
+
* `hasAccess` and the guards, in milliseconds. Defaults to 24 hours. 0
|
|
625
|
+
* turns the fallback off: an outage then keeps everyone out.
|
|
626
|
+
*/
|
|
627
|
+
maxStaleMs?: number;
|
|
628
|
+
/**
|
|
629
|
+
* How long the guards (`requirePlan`, `withMesub`, `RequirePlan`) give
|
|
630
|
+
* Mesub to answer, retries and waits included, in milliseconds. Defaults
|
|
631
|
+
* to 2000. Once it runs out, or Mesub fails, the guard answers from the
|
|
632
|
+
* cache, even stale, or 503 with Retry-After for a wallet it never saw. `access` and
|
|
633
|
+
* `hasAccess`, called directly, are not bound by it.
|
|
634
|
+
*/
|
|
635
|
+
guardTimeout?: number;
|
|
636
|
+
/**
|
|
637
|
+
* The webhook endpoint's signing secret, `whsec_...`, for
|
|
638
|
+
* `webhooks.verify`. Defaults to `process.env.MESUB_WEBHOOK_SECRET`; none
|
|
639
|
+
* is needed until a webhook is verified.
|
|
640
|
+
*/
|
|
641
|
+
webhookSecret?: string;
|
|
642
|
+
}
|
|
643
|
+
declare class Mesub {
|
|
644
|
+
/** Subscribing from your server: create, submit, retrieve and list. */
|
|
645
|
+
readonly subscriptions: Subscriptions;
|
|
646
|
+
/** The plans of your project, for a pricing page: `list` and `retrieve`. */
|
|
647
|
+
readonly plans: Plans;
|
|
648
|
+
/** Checking the webhooks Mesub sends: `verify`. */
|
|
649
|
+
readonly webhooks: Webhooks;
|
|
650
|
+
/** The events of the last 30 days, as their webhooks carried them: `list` and `retrieve`. */
|
|
651
|
+
readonly events: Events;
|
|
652
|
+
/** @internal */
|
|
653
|
+
protected readonly transport: Transport;
|
|
654
|
+
/** @internal */
|
|
655
|
+
protected readonly cache: AccessCache<AccessAnswer>;
|
|
656
|
+
/** The lists of `accessList`, in the same store, under their own keys. */
|
|
657
|
+
private readonly lists;
|
|
658
|
+
/** What the cache keys are scoped by: a hash of the API key, computed once. */
|
|
659
|
+
private readonly cacheScope;
|
|
660
|
+
/** What hashes emails and external ids in the cache keys: the API key, imported once. */
|
|
661
|
+
private readonly cacheSecret;
|
|
662
|
+
private readonly guardTimeout;
|
|
663
|
+
/**
|
|
664
|
+
* The `/v1/access` requests in flight, by cache key: a call for the same
|
|
665
|
+
* answer waits for that one rather than send its own (#34).
|
|
666
|
+
*/
|
|
667
|
+
private readonly flights;
|
|
668
|
+
constructor(options?: MesubOptions);
|
|
669
|
+
/**
|
|
670
|
+
* Everything Mesub knows about that customer on that plan: whether they
|
|
671
|
+
* have access, their status, their dates. Served from the cache while fresh.
|
|
672
|
+
*
|
|
673
|
+
* The customer is `{ wallet }`, `{ external_id }` or `{ email }`, or a
|
|
674
|
+
* wallet as a string. Asked by external id or email, the answer is about
|
|
675
|
+
* the wallet that grants access, if any: `wallet` is null when they have
|
|
676
|
+
* nothing on that plan.
|
|
677
|
+
*
|
|
678
|
+
* Throws a `MesubError` whenever Mesub cannot answer: it is for screens.
|
|
679
|
+
* A guard calls `hasAccess`, which falls back instead.
|
|
680
|
+
*/
|
|
681
|
+
access(customer: Customer | string, plan: string, options?: AccessOptions): Promise<AccessAnswer>;
|
|
682
|
+
/**
|
|
683
|
+
* Every plan of the project that customer has anything on, each answered
|
|
684
|
+
* like `access`, for a page listing their entitlements in one call. Cached
|
|
685
|
+
* apart from the answers by plan, for its own `revalidate_after`.
|
|
686
|
+
*
|
|
687
|
+
* Throws a `MesubError` whenever Mesub cannot answer, like `access`.
|
|
688
|
+
*/
|
|
689
|
+
accessList(customer: Customer | string, options?: AccessOptions): Promise<AccessList>;
|
|
690
|
+
/**
|
|
691
|
+
* `access` from Mesub, cached in the slot its caller computed once and
|
|
692
|
+
* already read, or without the cache at all (`null`) for an answer with
|
|
693
|
+
* its attempts.
|
|
694
|
+
*/
|
|
695
|
+
private ask;
|
|
696
|
+
/**
|
|
697
|
+
* One request in flight per key (#34): fifty calls for a customer not in
|
|
698
|
+
* the cache made fifty requests to Mesub, and as many 429s to retry. The
|
|
699
|
+
* first sends it, the others wait for its answer or its error. Its answer
|
|
700
|
+
* is cached once, unless a subscription that landed dropped the flight
|
|
701
|
+
* meanwhile: then it is answered, not cached.
|
|
702
|
+
*/
|
|
703
|
+
private shared;
|
|
704
|
+
/**
|
|
705
|
+
* Where a customer's answers are cached: the project's scope, and who
|
|
706
|
+
* they are as the key reads it. A wallet is public on chain and stays
|
|
707
|
+
* readable; an email or an external id is never written in clear, only
|
|
708
|
+
* as an HMAC under the API key, which a leaked Redis alone cannot reverse
|
|
709
|
+
* by guessing.
|
|
710
|
+
*/
|
|
711
|
+
private slotOf;
|
|
712
|
+
/**
|
|
713
|
+
* A subscription `submit`, `retrieve` or `list` answered (#33): once it
|
|
714
|
+
* grants access (`active`, or `cancelled` before its end), the answers
|
|
715
|
+
* cached for its customer that say no are dropped, by wallet, external
|
|
716
|
+
* id and email, for its plan and in the list of every plan. Without it, a
|
|
717
|
+
* `hasAccess` right after a submit kept answering the cached no until
|
|
718
|
+
* `revalidate_after` ran out. A store that fails costs nothing more than
|
|
719
|
+
* that: it never throws.
|
|
720
|
+
*
|
|
721
|
+
* With `moved`, one a confirm just cancelled, resumed or closed, or one a
|
|
722
|
+
* verified webhook carried: every answer cached for its customer is
|
|
723
|
+
* dropped, the yes too, since its status and dates changed whatever its
|
|
724
|
+
* access. A `stopped` or `ended` event must not leave a yes behind.
|
|
725
|
+
*/
|
|
726
|
+
private landed;
|
|
727
|
+
/**
|
|
728
|
+
* Whether that customer has access to that plan, for a guard. The
|
|
729
|
+
* customer is the same as for `access`, a wallet string included.
|
|
730
|
+
*
|
|
731
|
+
* When Mesub is unavailable after the retries (`unavailable` or
|
|
732
|
+
* `rate_limited`), it answers the last answer it knew, even stale, and
|
|
733
|
+
* `false` for a customer it never saw. Any other error is thrown, never
|
|
734
|
+
* turned into `false`: a bad key or an unknown plan is a broken
|
|
735
|
+
* integration, not a denial.
|
|
736
|
+
*
|
|
737
|
+
* No time budget: it waits out the client's timeout and retries, and a
|
|
738
|
+
* 429's `Retry-After`. A guard answers within `guardTimeout` instead.
|
|
739
|
+
*/
|
|
740
|
+
hasAccess(customer: Customer | string, plan: string): Promise<boolean>;
|
|
741
|
+
/**
|
|
742
|
+
* `hasAccess`, with the answer it decided on, within `guardTimeout`: what
|
|
743
|
+
* the middlewares hand the route. `answer` is null only for a customer
|
|
744
|
+
* never seen during an outage, which is then `unavailable`; `stale` is
|
|
745
|
+
* true when the answer came from the fallback.
|
|
746
|
+
*
|
|
747
|
+
* @internal
|
|
748
|
+
*/
|
|
749
|
+
decide(customer: Customer | string, plan: string): Promise<Decision>;
|
|
750
|
+
/**
|
|
751
|
+
* The decision itself. With a deadline (the guards), every call to Mesub
|
|
752
|
+
* fits before it: attempts are cut at it and a Retry-After that would
|
|
753
|
+
* outlast it is not waited. Without one (`hasAccess` called directly),
|
|
754
|
+
* the client's timeout and retries, as configured.
|
|
755
|
+
*/
|
|
756
|
+
private decideBy;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
export { AccessCache as A, type SubscriptionPausedEvent as B, type CacheStore as C, DEFAULT_MAX_STALE_MS as D, type EndedDetail as E, type FirstPayment as F, type SubscriptionPaymentFailedEvent as G, type HeaderSource as H, type SubscriptionRenewalUpcomingEvent as I, type SubscriptionRenewedEvent as J, type SubscriptionResumedEvent as K, type SubscriptionStoppedEvent as L, Mesub as M, type NoDetail as N, type SubscriptionUnpausedEvent as O, type PaymentFailedDetail as P, type WebhookEvent as Q, type RenewalIssue as R, type StoppedDetail as S, type TestEvent as T, type WebhookEventType as U, type VerifyWebhookOptions as V, type WebhookBody as W, Webhooks as X, verifyWebhook as Y, type CacheEntry as a, type AccessCacheOptions as b, type AccessChangedDetail as c, type CachedAnswer as d, type CreatedDetail as e, type Decision as f, type EventList as g, type EventListParams as h, type EventNetwork as i, type EventType as j, Events as k, type MesubEvent as l, type MesubOptions as m, type Plan as n, type PlanStatus as o, type PlanToken as p, Plans as q, type RenewalUpcomingDetail as r, type RenewedDetail as s, type Revalidating as t, type SubscriptionAccessGrantedEvent as u, type SubscriptionAccessRevokedEvent as v, type SubscriptionCancelledEvent as w, type SubscriptionCreatedEvent as x, type SubscriptionEndedEvent as y, type SubscriptionExpiredEvent as z };
|