@relume_io/partner-node 0.0.0-stage → 1.0.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/README.md +332 -2
- package/dist/bin.cjs +712 -0
- package/dist/bin.cjs.map +1 -0
- package/dist/bin.d.cts +1 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +632 -0
- package/dist/bin.js.map +1 -0
- package/dist/chunk-LOMLIKT2.js +157 -0
- package/dist/chunk-LOMLIKT2.js.map +1 -0
- package/dist/index.cjs +1189 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +503 -0
- package/dist/index.d.ts +503 -0
- package/dist/index.js +1043 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -4
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
|
|
3
|
+
type CallType = 'user.get' | 'credits.get' | 'credits.charge' | 'credits.reverse';
|
|
4
|
+
/** Passed to every callback, for your logs. */
|
|
5
|
+
interface CallContext {
|
|
6
|
+
type: CallType;
|
|
7
|
+
/** Relume-Request-Id: unique per attempt. Quote it to Relume support. */
|
|
8
|
+
requestId: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** What `onGetUser` returns. Every field is optional; null or '' counts as unset. */
|
|
11
|
+
interface UserProfile {
|
|
12
|
+
/** At most 200 characters. */
|
|
13
|
+
name?: string | null;
|
|
14
|
+
/** A valid email address. */
|
|
15
|
+
email?: string | null;
|
|
16
|
+
/** An https URL. */
|
|
17
|
+
avatarUrl?: string | null;
|
|
18
|
+
}
|
|
19
|
+
interface Credits {
|
|
20
|
+
/** The user's balance in your own credits (the unit of partnerCredits). Whole number, zero or more. */
|
|
21
|
+
balance: number;
|
|
22
|
+
}
|
|
23
|
+
interface Charge {
|
|
24
|
+
/** Your own id for the user. */
|
|
25
|
+
userId: string;
|
|
26
|
+
/**
|
|
27
|
+
* Idempotency key. Debit at most once per chargeId, and answer a repeat
|
|
28
|
+
* with the same result as the first time.
|
|
29
|
+
*/
|
|
30
|
+
chargeId: string;
|
|
31
|
+
/** What to debit from the user's wallet. At least 1. */
|
|
32
|
+
partnerCredits: number;
|
|
33
|
+
/**
|
|
34
|
+
* What Relume charges you for this action, for your records only.
|
|
35
|
+
* Never debit it from the wallet.
|
|
36
|
+
*/
|
|
37
|
+
relumeCredits: number;
|
|
38
|
+
/** The billable action, e.g. `page.unlock`. New actions may appear. */
|
|
39
|
+
action: string;
|
|
40
|
+
siteId?: string;
|
|
41
|
+
pageId?: string;
|
|
42
|
+
/** For the user's wallet statement. At most 200 characters. */
|
|
43
|
+
description: string;
|
|
44
|
+
}
|
|
45
|
+
interface ChargeResult {
|
|
46
|
+
/** True if the wallet was debited; false if the user can't afford it (sent as 402). */
|
|
47
|
+
ok: boolean;
|
|
48
|
+
/** The balance after the charge. Saves Relume a credits.get call. */
|
|
49
|
+
balance?: number | null;
|
|
50
|
+
}
|
|
51
|
+
/** `work_failed`, `charge_unknown` or `staff_refund`. New reasons may appear. */
|
|
52
|
+
type ReversalReason = 'work_failed' | 'charge_unknown' | 'staff_refund' | (string & {});
|
|
53
|
+
interface Reversal {
|
|
54
|
+
userId: string;
|
|
55
|
+
/** Idempotency key. Credit at most once per reversalId. */
|
|
56
|
+
reversalId: string;
|
|
57
|
+
/** What to credit back to the wallet. At least 1. */
|
|
58
|
+
partnerCredits: number;
|
|
59
|
+
/** The relumeCredits being given back, for your records only. Never credit it to the wallet. */
|
|
60
|
+
relumeCredits: number;
|
|
61
|
+
/**
|
|
62
|
+
* The charge being reversed. Absent for a staff refund. If you never debited
|
|
63
|
+
* this charge (it timed out before reaching your wallet), credit nothing,
|
|
64
|
+
* and record it as declined: if the charge itself arrives later, it must
|
|
65
|
+
* not debit.
|
|
66
|
+
*/
|
|
67
|
+
chargeId?: string;
|
|
68
|
+
reason: ReversalReason;
|
|
69
|
+
}
|
|
70
|
+
interface ReversalResult {
|
|
71
|
+
balance?: number | null;
|
|
72
|
+
}
|
|
73
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
74
|
+
/** Return null when you don't know the user: Relume refuses the session. */
|
|
75
|
+
type GetUserCallback = (userId: string, context: CallContext) => MaybePromise<UserProfile | null | undefined>;
|
|
76
|
+
type GetCreditsCallback = (userId: string, context: CallContext) => MaybePromise<Credits>;
|
|
77
|
+
type ChargeCreditsCallback = (charge: Charge, context: CallContext) => MaybePromise<ChargeResult>;
|
|
78
|
+
type ReverseChargeCallback = (reversal: Reversal, context: CallContext) => MaybePromise<ReversalResult | void>;
|
|
79
|
+
/**
|
|
80
|
+
* Who is signed in, from the browser's GET /relume/session request: the
|
|
81
|
+
* Express/Node request for `relume.express()`, the `Request` for
|
|
82
|
+
* `relume.fetchHandler`. Return null when nobody is signed in. Never read the
|
|
83
|
+
* user id from the query string or body.
|
|
84
|
+
*/
|
|
85
|
+
type GetUserIdFromRequest<TRequest> = (request: TRequest) => MaybePromise<string | number | null | undefined>;
|
|
86
|
+
/** A response from the framework-agnostic handlers, always JSON. */
|
|
87
|
+
interface RelumeResponse {
|
|
88
|
+
status: number;
|
|
89
|
+
headers: Record<string, string>;
|
|
90
|
+
body: unknown;
|
|
91
|
+
}
|
|
92
|
+
/** Where the handlers report errors your callbacks threw or returned. */
|
|
93
|
+
interface HandlerErrorInfo {
|
|
94
|
+
route: 'session' | 'calls';
|
|
95
|
+
type?: CallType;
|
|
96
|
+
requestId?: string | null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** An Express request, or a plain node:http one. */
|
|
100
|
+
type NodeRequest = IncomingMessage & {
|
|
101
|
+
body?: unknown;
|
|
102
|
+
/** Set by Express to the mount path, e.g. '/relume'. */
|
|
103
|
+
baseUrl?: string;
|
|
104
|
+
};
|
|
105
|
+
type NodeHandler = (request: NodeRequest, response: ServerResponse, next?: (error?: unknown) => void) => void;
|
|
106
|
+
|
|
107
|
+
/** Every `error.code` the partner API sends (protocol section 9). */
|
|
108
|
+
type PartnerApiErrorCode = 'invalid_request' | 'invalid_cursor' | 'missing_api_key' | 'invalid_api_key' | 'partner_suspended' | 'user_deactivated' | 'user_suspended' | 'not_found' | 'user_not_found' | 'site_not_found' | 'export_not_found' | 'ledger_entry_not_found' | 'partner_user_unknown' | 'rate_limited' | 'internal_error' | 'not_implemented' | 'partner_endpoint_error';
|
|
109
|
+
/** One page of a list endpoint. */
|
|
110
|
+
interface Page<T> {
|
|
111
|
+
data: T[];
|
|
112
|
+
/** Always present. Pass it as `after` to continue. */
|
|
113
|
+
nextCursor: string;
|
|
114
|
+
hasMore: boolean;
|
|
115
|
+
}
|
|
116
|
+
/** `?after=&limit=` on every list endpoint. */
|
|
117
|
+
interface PageQuery {
|
|
118
|
+
/** A `nextCursor` from the same list. Omit to start from the beginning. */
|
|
119
|
+
after?: string;
|
|
120
|
+
/** 1 to 100. Default 50. */
|
|
121
|
+
limit?: number;
|
|
122
|
+
}
|
|
123
|
+
interface CreateSessionBody {
|
|
124
|
+
/** Your own id for the user, 1 to 128 characters. */
|
|
125
|
+
userId: string;
|
|
126
|
+
}
|
|
127
|
+
interface Session {
|
|
128
|
+
/** Single use, valid for 5 minutes, bound to this environment's publishable key. */
|
|
129
|
+
clientSecret: string;
|
|
130
|
+
expiresAt: string;
|
|
131
|
+
}
|
|
132
|
+
type UserStatus = 'active' | 'deactivated' | 'suspended';
|
|
133
|
+
interface User {
|
|
134
|
+
userId: string;
|
|
135
|
+
/** `suspended` users can only be reactivated by Relume staff. */
|
|
136
|
+
status: UserStatus;
|
|
137
|
+
createdAt: string;
|
|
138
|
+
/** When the user last opened the builder; null before their first session. */
|
|
139
|
+
lastActiveAt: string | null;
|
|
140
|
+
/** Sites they own, deleted sites excluded. */
|
|
141
|
+
siteCount: number;
|
|
142
|
+
}
|
|
143
|
+
/** New statuses only come with notice (protocol section 12). */
|
|
144
|
+
type ExportStatus = 'queued' | 'running' | 'ready' | 'failed' | 'expired';
|
|
145
|
+
interface Export {
|
|
146
|
+
exportId: string;
|
|
147
|
+
siteId: string;
|
|
148
|
+
status: ExportStatus;
|
|
149
|
+
/** A signed zip URL; null unless `status` is `ready`. */
|
|
150
|
+
downloadUrl: string | null;
|
|
151
|
+
createdAt: string;
|
|
152
|
+
/** When `downloadUrl` stops working; null unless `status` is `ready`. */
|
|
153
|
+
expiresAt: string | null;
|
|
154
|
+
}
|
|
155
|
+
interface Site {
|
|
156
|
+
/** The id `relume.open({ site })` takes. */
|
|
157
|
+
siteId: string;
|
|
158
|
+
/** Your own id for the owner. */
|
|
159
|
+
userId: string;
|
|
160
|
+
name: string;
|
|
161
|
+
createdAt: string;
|
|
162
|
+
/** Includes changes Relume makes itself, such as a new screenshot. */
|
|
163
|
+
updatedAt: string;
|
|
164
|
+
/** Signed, and stops working within 7 days: fetch the site again rather than store it. */
|
|
165
|
+
thumbnailUrl: string | null;
|
|
166
|
+
/** The most recent export. Always null until exports ship. */
|
|
167
|
+
lastExport: Export | null;
|
|
168
|
+
}
|
|
169
|
+
interface CreateSiteBody {
|
|
170
|
+
/** At most 200 characters. Default "Untitled site". */
|
|
171
|
+
name?: string;
|
|
172
|
+
}
|
|
173
|
+
interface CreatedSite {
|
|
174
|
+
siteId: string;
|
|
175
|
+
}
|
|
176
|
+
type LedgerEntryType = 'charge' | 'reversal';
|
|
177
|
+
/**
|
|
178
|
+
* A charge is `pending`, `charged`, `declined`, `unknown`, `void` or
|
|
179
|
+
* `reversed`; a reversal is `pending` or `reversed`. New statuses only come
|
|
180
|
+
* with notice (protocol section 12).
|
|
181
|
+
*/
|
|
182
|
+
type LedgerEntryStatus = 'pending' | 'charged' | 'declined' | 'unknown' | 'void' | 'reversed';
|
|
183
|
+
interface LedgerEntry {
|
|
184
|
+
/** The `chargeId` or `reversalId` sent in the call: upsert on it. */
|
|
185
|
+
id: string;
|
|
186
|
+
type: LedgerEntryType;
|
|
187
|
+
status: LedgerEntryStatus;
|
|
188
|
+
userId: string;
|
|
189
|
+
partnerCredits: number;
|
|
190
|
+
relumeCredits: number;
|
|
191
|
+
/** Null when it doesn't apply, e.g. on a staff refund. New actions may appear. */
|
|
192
|
+
action: string | null;
|
|
193
|
+
siteId: string | null;
|
|
194
|
+
pageId: string | null;
|
|
195
|
+
/** On a reversal tied to a charge; otherwise null. */
|
|
196
|
+
chargeId: string | null;
|
|
197
|
+
/** On a reversal (`work_failed`, `charge_unknown`, `staff_refund`, …); otherwise null. */
|
|
198
|
+
reason: string | null;
|
|
199
|
+
createdAt: string;
|
|
200
|
+
/** When the entry reached a final status; otherwise null. */
|
|
201
|
+
resolvedAt: string | null;
|
|
202
|
+
}
|
|
203
|
+
interface PartnerResponse {
|
|
204
|
+
/** From the last attempt; null after a timeout or network error. */
|
|
205
|
+
httpStatus: number | null;
|
|
206
|
+
body: {
|
|
207
|
+
balance: number;
|
|
208
|
+
} | Record<string, never> | null;
|
|
209
|
+
attempts: number;
|
|
210
|
+
}
|
|
211
|
+
/** `GET /ledger/{id}` adds what your endpoint last answered. */
|
|
212
|
+
interface LedgerEntryDetail extends LedgerEntry {
|
|
213
|
+
/** Null when Relume resolved the entry without calling you. */
|
|
214
|
+
partnerResponse: PartnerResponse | null;
|
|
215
|
+
}
|
|
216
|
+
type UsageGroup = 'day' | 'user' | 'action';
|
|
217
|
+
/** The query string of `GET /usage`, as sent. */
|
|
218
|
+
interface UsageQuery extends PageQuery {
|
|
219
|
+
/** `YYYY-MM-DD` (UTC), inclusive. */
|
|
220
|
+
from: string;
|
|
221
|
+
/** `YYYY-MM-DD` (UTC), exclusive. At most 366 days after `from`. */
|
|
222
|
+
to: string;
|
|
223
|
+
/** Comma-separated subset of `day`, `user`, `action`. */
|
|
224
|
+
groupBy?: string;
|
|
225
|
+
}
|
|
226
|
+
interface UsageTotals {
|
|
227
|
+
charged: number;
|
|
228
|
+
reversed: number;
|
|
229
|
+
/** `charged - reversed`. Relume invoices on `relumeCredits.net`. */
|
|
230
|
+
net: number;
|
|
231
|
+
}
|
|
232
|
+
interface UsageRow {
|
|
233
|
+
/** `YYYY-MM-DD`; only when grouped by day. */
|
|
234
|
+
day?: string;
|
|
235
|
+
/** Only when grouped by user. */
|
|
236
|
+
userId?: string;
|
|
237
|
+
/** Only when grouped by action; null for a staff refund. */
|
|
238
|
+
action?: string | null;
|
|
239
|
+
partnerCredits: UsageTotals;
|
|
240
|
+
relumeCredits: UsageTotals;
|
|
241
|
+
}
|
|
242
|
+
interface Price {
|
|
243
|
+
action: string;
|
|
244
|
+
/** Null until Relume staff set one; Relume refuses the action until then. */
|
|
245
|
+
partnerCredits: number | null;
|
|
246
|
+
relumeCredits: number;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
declare const DEFAULT_API_BASE_URL = "https://apis.relume.io/nova/partner/v1";
|
|
250
|
+
/**
|
|
251
|
+
* A partner API `error.code`, or one the SDK sets itself: `timeout` and
|
|
252
|
+
* `network_error` when no response arrived, `unexpected_response` when one
|
|
253
|
+
* arrived without the expected body. New API codes may appear.
|
|
254
|
+
*/
|
|
255
|
+
type RelumeErrorCode = PartnerApiErrorCode | 'timeout' | 'network_error' | 'unexpected_response' | (string & {});
|
|
256
|
+
/** An error answer from the partner API, or a request that never got one. */
|
|
257
|
+
declare class RelumeError extends Error {
|
|
258
|
+
/** The HTTP status, or 0 when no response arrived. */
|
|
259
|
+
readonly status: number;
|
|
260
|
+
/** The API's `error.code`, safe to switch on. */
|
|
261
|
+
readonly code: RelumeErrorCode;
|
|
262
|
+
readonly requestId: string | null;
|
|
263
|
+
constructor(input: {
|
|
264
|
+
status: number;
|
|
265
|
+
code: RelumeErrorCode;
|
|
266
|
+
message: string;
|
|
267
|
+
requestId?: string | null;
|
|
268
|
+
cause?: unknown;
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* A list endpoint, fetched a page at a time as you iterate:
|
|
274
|
+
*
|
|
275
|
+
* for await (const entry of relume.ledger.list({ after })) { … }
|
|
276
|
+
*
|
|
277
|
+
* Nothing is requested until iteration starts, and each iteration starts
|
|
278
|
+
* again from the `after` the list was made with. Iterate `pages()` instead to
|
|
279
|
+
* handle (and checkpoint) a page at a time. Iterate a list once at a time:
|
|
280
|
+
* every iteration moves the same `nextCursor`, so two at once would leave it
|
|
281
|
+
* wherever the last one got to.
|
|
282
|
+
*/
|
|
283
|
+
declare class PagedList<T> implements AsyncIterable<T> {
|
|
284
|
+
#private;
|
|
285
|
+
constructor(fetchPage: (after: string | undefined) => Promise<Page<T>>, after: string | undefined);
|
|
286
|
+
/**
|
|
287
|
+
* Where to resume: the `nextCursor` of the last page whose items have all
|
|
288
|
+
* been handed out, or the starting `after` (null when there was none)
|
|
289
|
+
* before that. Once iteration ends, store it and pass it as `after` later
|
|
290
|
+
* to get only what is new (on the ledger; site cursors don't poll).
|
|
291
|
+
*/
|
|
292
|
+
get nextCursor(): string | null;
|
|
293
|
+
/** Every page in turn. The cursor advances when you ask for the next one. */
|
|
294
|
+
pages(): AsyncGenerator<Page<T>, void, undefined>;
|
|
295
|
+
[Symbol.asyncIterator](): AsyncGenerator<T, void, undefined>;
|
|
296
|
+
/** Every item in one array. Mind the size of what you're listing. */
|
|
297
|
+
toArray(): Promise<T[]>;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
interface UsageParams extends Omit<UsageQuery, 'groupBy'> {
|
|
301
|
+
/** One row per combination of these. Omitted: one row for the whole range. */
|
|
302
|
+
groupBy?: readonly UsageGroup[];
|
|
303
|
+
}
|
|
304
|
+
interface PartnerApi {
|
|
305
|
+
sessions: {
|
|
306
|
+
/**
|
|
307
|
+
* POST /sessions: a single-use client secret for this user's embed. The
|
|
308
|
+
* first call for a userId creates their Relume account with user.get.
|
|
309
|
+
*/
|
|
310
|
+
create(input: CreateSessionBody): Promise<Session>;
|
|
311
|
+
};
|
|
312
|
+
users: {
|
|
313
|
+
/** GET /users/{userId}. `user_not_found` until their first session. */
|
|
314
|
+
get(userId: string): Promise<User>;
|
|
315
|
+
/**
|
|
316
|
+
* POST /users/{userId}/deactivate: cuts off their sessions and export
|
|
317
|
+
* links; data is kept. Idempotent, and retried on a 5xx.
|
|
318
|
+
*/
|
|
319
|
+
deactivate(userId: string): Promise<User>;
|
|
320
|
+
/** POST /users/{userId}/reactivate. `user_suspended` if Relume staff suspended them. */
|
|
321
|
+
reactivate(userId: string): Promise<User>;
|
|
322
|
+
};
|
|
323
|
+
sites: {
|
|
324
|
+
/**
|
|
325
|
+
* GET /users/{userId}/sites, most recently updated first. Fetch from the
|
|
326
|
+
* beginning each time: new and changed sites sort to the front, so a
|
|
327
|
+
* stored cursor never shows them.
|
|
328
|
+
*/
|
|
329
|
+
list(userId: string, params?: PageQuery): PagedList<Site>;
|
|
330
|
+
/** POST /users/{userId}/sites: an empty site. Not idempotent, so a 5xx isn't retried. */
|
|
331
|
+
create(userId: string, input?: CreateSiteBody): Promise<CreatedSite>;
|
|
332
|
+
/** GET /sites/{siteId}. */
|
|
333
|
+
get(siteId: string): Promise<Site>;
|
|
334
|
+
};
|
|
335
|
+
exports: {
|
|
336
|
+
/**
|
|
337
|
+
* GET /sites/{siteId}/exports/{exportId}. Not available yet: answers
|
|
338
|
+
* `not_implemented` (501) until exports ship.
|
|
339
|
+
*/
|
|
340
|
+
get(siteId: string, exportId: string): Promise<Export>;
|
|
341
|
+
};
|
|
342
|
+
ledger: {
|
|
343
|
+
/**
|
|
344
|
+
* GET /ledger, in change order: an entry appears again each time its
|
|
345
|
+
* status changes, so upsert on `id`. Store the list's `nextCursor` when
|
|
346
|
+
* you're done and pass it as `after` to get only what changed since.
|
|
347
|
+
*/
|
|
348
|
+
list(params?: PageQuery): PagedList<LedgerEntry>;
|
|
349
|
+
/** GET /ledger/{id}: one entry by chargeId or reversalId, with partnerResponse. */
|
|
350
|
+
get(id: string): Promise<LedgerEntryDetail>;
|
|
351
|
+
};
|
|
352
|
+
usage: {
|
|
353
|
+
/** GET /usage: credits totalled over [from, to), ordered by day, user, action. */
|
|
354
|
+
get(params: UsageParams): PagedList<UsageRow>;
|
|
355
|
+
};
|
|
356
|
+
prices: {
|
|
357
|
+
/** GET /prices: every billable action's price in this environment. */
|
|
358
|
+
list(): Promise<Price[]>;
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
interface RelumeOptions {
|
|
363
|
+
/** `sk_test_…` or `sk_live_…`. Server only: never send it to a browser. */
|
|
364
|
+
secretKey: string;
|
|
365
|
+
/** Defaults to production. Relume gives you another URL for staging. */
|
|
366
|
+
apiBaseUrl?: string;
|
|
367
|
+
/** Per partner API request attempt. Default 15 seconds. */
|
|
368
|
+
apiTimeoutMs?: number;
|
|
369
|
+
/**
|
|
370
|
+
* Retries after the first attempt. Default 2. A 429 is retried after its
|
|
371
|
+
* Retry-After; a 5xx or a failed connection with backoff, except on
|
|
372
|
+
* `sites.create`, which isn't idempotent. 0 turns retries off.
|
|
373
|
+
*/
|
|
374
|
+
maxRetries?: number;
|
|
375
|
+
/** Replace `fetch` for partner API requests, e.g. to add a proxy agent. */
|
|
376
|
+
fetch?: typeof fetch;
|
|
377
|
+
/**
|
|
378
|
+
* Called when a callback throws or returns something the protocol doesn't
|
|
379
|
+
* allow, or the session route fails. Defaults to console.error. The caller
|
|
380
|
+
* always gets a 5xx; Relume never sees the error's message.
|
|
381
|
+
*/
|
|
382
|
+
onHandlerError?: (error: unknown, info: HandlerErrorInfo) => void;
|
|
383
|
+
/** The clock, in milliseconds. For tests. */
|
|
384
|
+
now?: () => number;
|
|
385
|
+
/** Waits between retries. For tests. */
|
|
386
|
+
sleep?: (ms: number) => Promise<void>;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* A Relume partner client. Create one with `Relume.init`; there is no global
|
|
390
|
+
* state, so test and live clients can sit side by side.
|
|
391
|
+
*
|
|
392
|
+
* `TRequest` is the request your `getUserIdFromRequest` receives: the
|
|
393
|
+
* Express/Node request for `express()`, the `Request` for `fetchHandler`.
|
|
394
|
+
*/
|
|
395
|
+
declare class Relume<TRequest = unknown> {
|
|
396
|
+
#private;
|
|
397
|
+
readonly sessions: PartnerApi['sessions'];
|
|
398
|
+
readonly users: PartnerApi['users'];
|
|
399
|
+
readonly sites: PartnerApi['sites'];
|
|
400
|
+
readonly exports: PartnerApi['exports'];
|
|
401
|
+
readonly ledger: PartnerApi['ledger'];
|
|
402
|
+
readonly usage: PartnerApi['usage'];
|
|
403
|
+
readonly prices: PartnerApi['prices'];
|
|
404
|
+
/**
|
|
405
|
+
* A fetch-style handler for Next.js route handlers, Hono, Cloudflare
|
|
406
|
+
* Workers, Bun and Deno. Answers `GET …/session` and `POST …/calls`.
|
|
407
|
+
*/
|
|
408
|
+
readonly fetchHandler: (request: Request) => Promise<Response>;
|
|
409
|
+
static init<TRequest = unknown>(options: RelumeOptions): Relume<TRequest>;
|
|
410
|
+
constructor(options: RelumeOptions);
|
|
411
|
+
/** Who is signed in, for the browser's `GET /relume/session`. */
|
|
412
|
+
getUserIdFromRequest(callback: GetUserIdFromRequest<TRequest>): this;
|
|
413
|
+
/** Answers `user.get`: the user's profile, or null if you don't know them. */
|
|
414
|
+
onGetUser(callback: GetUserCallback): this;
|
|
415
|
+
/** Answers `credits.get`: `{ balance }` in your own credits. */
|
|
416
|
+
onGetCredits(callback: GetCreditsCallback): this;
|
|
417
|
+
/**
|
|
418
|
+
* Answers `credits.charge`. Debit `partnerCredits` (never `relumeCredits`),
|
|
419
|
+
* at most once per `chargeId`, and return `{ ok, balance }`.
|
|
420
|
+
*/
|
|
421
|
+
onChargeCredits(callback: ChargeCreditsCallback): this;
|
|
422
|
+
/**
|
|
423
|
+
* Answers `credits.reverse`. Credit back `partnerCredits` (never
|
|
424
|
+
* `relumeCredits`), at most once per `reversalId`. If `chargeId` names a
|
|
425
|
+
* debit you never made, change nothing and return normally.
|
|
426
|
+
*/
|
|
427
|
+
onReverseCharge(callback: ReverseChargeCallback): this;
|
|
428
|
+
/**
|
|
429
|
+
* Express (or Connect, or plain `node:http`) middleware. Mount it before
|
|
430
|
+
* any body parser: `app.use('/relume', relume.express())`.
|
|
431
|
+
*/
|
|
432
|
+
express(): NodeHandler;
|
|
433
|
+
/**
|
|
434
|
+
* `POST /relume/calls`, framework-agnostic: verifies the signature and the
|
|
435
|
+
* 5-minute window, routes by `type`, runs your callback and validates what
|
|
436
|
+
* it returns. Pass the raw body exactly as received.
|
|
437
|
+
*/
|
|
438
|
+
handleCall(input: {
|
|
439
|
+
rawBody: string | Uint8Array;
|
|
440
|
+
signatureHeader: string | null | undefined;
|
|
441
|
+
requestId?: string | null;
|
|
442
|
+
}): Promise<RelumeResponse>;
|
|
443
|
+
/**
|
|
444
|
+
* `GET /relume/session`, framework-agnostic: works out the signed-in user
|
|
445
|
+
* with `getUserIdFromRequest`, calls `POST /sessions` and returns the
|
|
446
|
+
* client secret, or passes the API's error through unchanged.
|
|
447
|
+
*/
|
|
448
|
+
handleSession(request: TRequest): Promise<RelumeResponse>;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/** Thrown when a callback returns something the protocol doesn't allow. */
|
|
452
|
+
declare class InvalidCallbackResultError extends Error {
|
|
453
|
+
constructor(message: string);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
declare const SIGNATURE_HEADER = "Relume-Signature";
|
|
457
|
+
declare const REQUEST_ID_HEADER = "Relume-Request-Id";
|
|
458
|
+
/** How far a call's timestamp may be from this server's clock, either way. */
|
|
459
|
+
declare const SIGNATURE_TOLERANCE_SECONDS: number;
|
|
460
|
+
/** The current key plus the previous one during a rotation overlap. */
|
|
461
|
+
declare const MAX_SIGNATURES = 2;
|
|
462
|
+
type SignatureFailure = 'malformed_header' | 'signature_mismatch' | 'timestamp_out_of_tolerance';
|
|
463
|
+
type VerifySignatureResult = {
|
|
464
|
+
valid: true;
|
|
465
|
+
timestamp: number;
|
|
466
|
+
} | {
|
|
467
|
+
valid: false;
|
|
468
|
+
reason: SignatureFailure;
|
|
469
|
+
};
|
|
470
|
+
interface ParsedSignatureHeader {
|
|
471
|
+
timestamp: number;
|
|
472
|
+
/** Every v1= value in header order, including ones that aren't hex. */
|
|
473
|
+
signatures: string[];
|
|
474
|
+
}
|
|
475
|
+
/** Lowercase hex HMAC-SHA256 of `t + "." + rawBody`, keyed by the secret key. */
|
|
476
|
+
declare function computeSignature(secret: string, timestamp: number, rawBody: string | Uint8Array): Promise<string>;
|
|
477
|
+
/**
|
|
478
|
+
* Builds a Relume-Signature header the way Relume does: newest secret first,
|
|
479
|
+
* a second one only during a rotation. For a partner's own tests; a partner's
|
|
480
|
+
* server never needs to sign anything.
|
|
481
|
+
*/
|
|
482
|
+
declare function signCall(secrets: string | readonly string[], timestamp: number, rawBody: string | Uint8Array): Promise<string>;
|
|
483
|
+
/**
|
|
484
|
+
* Null when the header has no `t`, more than one `t`, a `t` that isn't
|
|
485
|
+
* canonical unix seconds, no v1= value, or more than two v1= values. Entries
|
|
486
|
+
* with another name (a future `v2=`) are ignored.
|
|
487
|
+
*/
|
|
488
|
+
declare function parseSignatureHeader(header: string): ParsedSignatureHeader | null;
|
|
489
|
+
/**
|
|
490
|
+
* Verifies a call: the header parses, any v1= value matches the HMAC under
|
|
491
|
+
* `secret` (compared in constant time by Web Crypto), and `t` is within 300
|
|
492
|
+
* seconds of `nowSeconds`. The signature is checked before the timestamp, so
|
|
493
|
+
* a stale result is only reported for a call Relume really signed.
|
|
494
|
+
*/
|
|
495
|
+
declare function verifySignature(input: {
|
|
496
|
+
secret: string;
|
|
497
|
+
header: string | null | undefined;
|
|
498
|
+
rawBody: string | Uint8Array;
|
|
499
|
+
/** Defaults to the current time. */
|
|
500
|
+
nowSeconds?: number;
|
|
501
|
+
}): Promise<VerifySignatureResult>;
|
|
502
|
+
|
|
503
|
+
export { type CallContext, type CallType, type Charge, type ChargeCreditsCallback, type ChargeResult, type CreateSessionBody, type CreateSiteBody, type CreatedSite, type Credits, DEFAULT_API_BASE_URL, type Export, type ExportStatus, type GetCreditsCallback, type GetUserCallback, type GetUserIdFromRequest, type HandlerErrorInfo, InvalidCallbackResultError, type LedgerEntry, type LedgerEntryDetail, type LedgerEntryStatus, type LedgerEntryType, MAX_SIGNATURES, type NodeHandler, type NodeRequest, type Page, type PageQuery, PagedList, type ParsedSignatureHeader, type PartnerApi, type PartnerApiErrorCode, type PartnerResponse, type Price, REQUEST_ID_HEADER, Relume, RelumeError, type RelumeErrorCode, type RelumeOptions, type RelumeResponse, type Reversal, type ReversalReason, type ReversalResult, type ReverseChargeCallback, SIGNATURE_HEADER, SIGNATURE_TOLERANCE_SECONDS, type Session, type SignatureFailure, type Site, type UsageGroup, type UsageParams, type UsageRow, type UsageTotals, type User, type UserProfile, type UserStatus, type VerifySignatureResult, computeSignature, parseSignatureHeader, signCall, verifySignature };
|