@relume_io/partner-node 2.0.1 → 2.2.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 +152 -207
- package/dist/bin.cjs +44 -4
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +39 -5
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-HPVVBUR7.js → chunk-WUW4CIBP.js} +201 -1
- package/dist/chunk-WUW4CIBP.js.map +1 -0
- package/dist/index.cjs +51 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +34 -4
- package/dist/index.d.ts +34 -4
- package/dist/index.js +30 -178
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-HPVVBUR7.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
2
|
|
|
3
3
|
type CallType = 'user.get' | 'credits.get' | 'credits.charge' | 'credits.reverse';
|
|
4
|
+
/** A key's environment, and the `environment` every signed call carries. */
|
|
5
|
+
type Environment = 'test' | 'live';
|
|
4
6
|
/** Passed to every callback, for your logs. */
|
|
5
7
|
interface CallContext {
|
|
6
8
|
type: CallType;
|
|
9
|
+
/**
|
|
10
|
+
* Always your secret key's: a call for the other environment is refused
|
|
11
|
+
* before any callback runs.
|
|
12
|
+
*/
|
|
13
|
+
environment: Environment;
|
|
7
14
|
/** Relume-Request-Id: unique per attempt. Quote it to Relume support. */
|
|
8
15
|
requestId: string | null;
|
|
9
16
|
}
|
|
@@ -39,7 +46,13 @@ interface Charge {
|
|
|
39
46
|
action: string;
|
|
40
47
|
siteId?: string;
|
|
41
48
|
pageId?: string;
|
|
42
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* The pages one charge pays for, when it covers several (a `page.unlock`
|
|
51
|
+
* for each, with both prices multiplied by their count). Sent instead of
|
|
52
|
+
* `pageId`. Debit `partnerCredits` as for any charge.
|
|
53
|
+
*/
|
|
54
|
+
pageIds?: string[];
|
|
55
|
+
/** For the user's wallet statement, e.g. `Build 3 pages`. At most 200 characters. */
|
|
43
56
|
description: string;
|
|
44
57
|
}
|
|
45
58
|
interface ChargeResult {
|
|
@@ -245,6 +258,20 @@ interface Price {
|
|
|
245
258
|
partnerCredits: number | null;
|
|
246
259
|
relumeCredits: number;
|
|
247
260
|
}
|
|
261
|
+
/** What you call your credits: the word the builder shows beside amounts. */
|
|
262
|
+
interface CreditUnit {
|
|
263
|
+
/** e.g. `coin`. */
|
|
264
|
+
singular: string;
|
|
265
|
+
/** e.g. `coins`. */
|
|
266
|
+
plural: string;
|
|
267
|
+
/** The https icon the builder shows beside amounts; absent when not set. */
|
|
268
|
+
iconUrl?: string;
|
|
269
|
+
}
|
|
270
|
+
interface PriceList {
|
|
271
|
+
data: Price[];
|
|
272
|
+
/** What the builder calls your credits, as Relume staff set it for you. */
|
|
273
|
+
creditUnit: CreditUnit;
|
|
274
|
+
}
|
|
248
275
|
|
|
249
276
|
declare const DEFAULT_API_BASE_URL = "https://apis.relume.io/nova/partner/v1";
|
|
250
277
|
/**
|
|
@@ -356,6 +383,8 @@ interface PartnerApi {
|
|
|
356
383
|
prices: {
|
|
357
384
|
/** GET /prices: every billable action's price in this environment. */
|
|
358
385
|
list(): Promise<Price[]>;
|
|
386
|
+
/** GET /prices: the prices, and what the builder calls your credits. */
|
|
387
|
+
get(): Promise<PriceList>;
|
|
359
388
|
};
|
|
360
389
|
}
|
|
361
390
|
|
|
@@ -451,8 +480,9 @@ declare class Relume<TRequest extends SessionRequest = SessionRequest> {
|
|
|
451
480
|
express(): NodeHandler;
|
|
452
481
|
/**
|
|
453
482
|
* `POST /relume/calls`, framework-agnostic: verifies the signature and the
|
|
454
|
-
* 5-minute window,
|
|
455
|
-
*
|
|
483
|
+
* 5-minute window, refuses a call for another environment than the secret
|
|
484
|
+
* key's (`400 environment_mismatch`), routes by `type`, runs your callback
|
|
485
|
+
* and validates what it returns. Pass the raw body exactly as received.
|
|
456
486
|
*/
|
|
457
487
|
handleCall(input: {
|
|
458
488
|
rawBody: string | Uint8Array;
|
|
@@ -522,4 +552,4 @@ declare function verifySignature(input: {
|
|
|
522
552
|
nowSeconds?: number;
|
|
523
553
|
}): Promise<VerifySignatureResult>;
|
|
524
554
|
|
|
525
|
-
export { type CallContext, type CallType, type Charge, type ChargeCreditsCallback, type ChargeResult, type CreateSessionBody, type CreateSiteBody, type CreatedSite, type Credits, DEFAULT_API_BASE_URL, EMBED_HEADER, EMBED_HEADER_VALUE, 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 SessionRequest, type SignatureFailure, type Site, type UsageGroup, type UsageParams, type UsageRow, type UsageTotals, type User, type UserProfile, type UserStatus, type VerifySignatureResult, computeSignature, parseSignatureHeader, signCall, verifySignature };
|
|
555
|
+
export { type CallContext, type CallType, type Charge, type ChargeCreditsCallback, type ChargeResult, type CreateSessionBody, type CreateSiteBody, type CreatedSite, type CreditUnit, type Credits, DEFAULT_API_BASE_URL, EMBED_HEADER, EMBED_HEADER_VALUE, type Environment, 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, type PriceList, 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 SessionRequest, type SignatureFailure, type Site, type UsageGroup, type UsageParams, type UsageRow, type UsageTotals, type User, type UserProfile, type UserStatus, type VerifySignatureResult, computeSignature, parseSignatureHeader, signCall, verifySignature };
|
package/dist/index.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
EMBED_HEADER,
|
|
3
3
|
EMBED_HEADER_VALUE,
|
|
4
|
+
InvalidCallbackResultError,
|
|
4
5
|
MAX_BODY_BYTES,
|
|
5
6
|
MAX_SIGNATURES,
|
|
6
7
|
REQUEST_ID_HEADER,
|
|
7
8
|
SIGNATURE_HEADER,
|
|
8
9
|
SIGNATURE_TOLERANCE_SECONDS,
|
|
9
|
-
|
|
10
|
+
checkChargeResult,
|
|
11
|
+
checkCredits,
|
|
12
|
+
checkReversalResult,
|
|
13
|
+
checkUserProfile,
|
|
10
14
|
computeSignature,
|
|
11
15
|
errorResponse,
|
|
12
|
-
isEmail,
|
|
13
|
-
isHttpsUrl,
|
|
14
16
|
isRecord,
|
|
15
|
-
isString,
|
|
16
|
-
isWhole,
|
|
17
17
|
jsonResponse,
|
|
18
|
+
keyEnvironment,
|
|
18
19
|
methodNotAllowed,
|
|
19
20
|
mountedRouteOf,
|
|
21
|
+
parseCall,
|
|
20
22
|
parseSignatureHeader,
|
|
21
23
|
payloadTooLarge,
|
|
22
24
|
preflight,
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
routeOf,
|
|
25
27
|
signCall,
|
|
26
28
|
verifySignature
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-WUW4CIBP.js";
|
|
28
30
|
|
|
29
31
|
// src/adapters/express.ts
|
|
30
32
|
var PayloadTooLargeError = class extends Error {
|
|
@@ -226,7 +228,7 @@ function fetchHandler(client, maxBodyBytes) {
|
|
|
226
228
|
|
|
227
229
|
// src/api.ts
|
|
228
230
|
var DEFAULT_API_BASE_URL = "https://apis.relume.io/nova/partner/v1";
|
|
229
|
-
var SDK_VERSION = "2.0
|
|
231
|
+
var SDK_VERSION = "2.2.0";
|
|
230
232
|
var USER_AGENT = `relume-partner-node/${SDK_VERSION}`;
|
|
231
233
|
var DEFAULT_API_TIMEOUT_MS = 15e3;
|
|
232
234
|
var DEFAULT_MAX_RETRIES = 2;
|
|
@@ -377,171 +379,6 @@ function isSession(value) {
|
|
|
377
379
|
return isRecord(value) && typeof value.clientSecret === "string" && typeof value.expiresAt === "string";
|
|
378
380
|
}
|
|
379
381
|
|
|
380
|
-
// src/calls.ts
|
|
381
|
-
var CALL_TYPES = [
|
|
382
|
-
"user.get",
|
|
383
|
-
"credits.get",
|
|
384
|
-
"credits.charge",
|
|
385
|
-
"credits.reverse"
|
|
386
|
-
];
|
|
387
|
-
var InvalidCallbackResultError = class extends Error {
|
|
388
|
-
constructor(message) {
|
|
389
|
-
super(message);
|
|
390
|
-
this.name = "InvalidCallbackResultError";
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
function optionalString(value, max) {
|
|
394
|
-
if (value === void 0 || value === null) {
|
|
395
|
-
return { ok: true, value: void 0 };
|
|
396
|
-
}
|
|
397
|
-
return isString(value, 1, max) ? { ok: true, value } : { ok: false };
|
|
398
|
-
}
|
|
399
|
-
function isCallType(value) {
|
|
400
|
-
return CALL_TYPES.some((type) => type === value);
|
|
401
|
-
}
|
|
402
|
-
function parseCall(body) {
|
|
403
|
-
if (!isRecord(body)) {
|
|
404
|
-
return {
|
|
405
|
-
ok: false,
|
|
406
|
-
code: "invalid_request",
|
|
407
|
-
message: "The body must be a JSON object."
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
const { type } = body;
|
|
411
|
-
if (!isCallType(type)) {
|
|
412
|
-
return {
|
|
413
|
-
ok: false,
|
|
414
|
-
code: "unsupported_call_type",
|
|
415
|
-
message: `Unsupported call type: ${JSON.stringify(type ?? null).slice(0, 64)}.`
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
const invalid = (field) => ({
|
|
419
|
-
ok: false,
|
|
420
|
-
code: "invalid_request",
|
|
421
|
-
message: `Invalid ${field} for ${type}.`,
|
|
422
|
-
type
|
|
423
|
-
});
|
|
424
|
-
const { userId } = body;
|
|
425
|
-
if (!isString(userId, 1, 128)) return invalid("userId");
|
|
426
|
-
switch (type) {
|
|
427
|
-
case "user.get":
|
|
428
|
-
case "credits.get":
|
|
429
|
-
return { ok: true, call: { type, userId } };
|
|
430
|
-
case "credits.charge": {
|
|
431
|
-
const { chargeId, partnerCredits, relumeCredits, action, description } = body;
|
|
432
|
-
if (!isString(chargeId, 1, 64)) return invalid("chargeId");
|
|
433
|
-
if (!isWhole(partnerCredits, 1)) return invalid("partnerCredits");
|
|
434
|
-
if (!isWhole(relumeCredits, 0)) return invalid("relumeCredits");
|
|
435
|
-
if (!isString(action, 1, 200)) return invalid("action");
|
|
436
|
-
if (!isString(description, 0, 200)) return invalid("description");
|
|
437
|
-
const siteId = optionalString(body.siteId, 200);
|
|
438
|
-
if (!siteId.ok) return invalid("siteId");
|
|
439
|
-
const pageId = optionalString(body.pageId, 200);
|
|
440
|
-
if (!pageId.ok) return invalid("pageId");
|
|
441
|
-
const charge = {
|
|
442
|
-
userId,
|
|
443
|
-
chargeId,
|
|
444
|
-
partnerCredits,
|
|
445
|
-
relumeCredits,
|
|
446
|
-
action,
|
|
447
|
-
description
|
|
448
|
-
};
|
|
449
|
-
if (siteId.value !== void 0) charge.siteId = siteId.value;
|
|
450
|
-
if (pageId.value !== void 0) charge.pageId = pageId.value;
|
|
451
|
-
return { ok: true, call: { type, charge } };
|
|
452
|
-
}
|
|
453
|
-
case "credits.reverse": {
|
|
454
|
-
const { reversalId, partnerCredits, relumeCredits, reason } = body;
|
|
455
|
-
if (!isString(reversalId, 1, 64)) return invalid("reversalId");
|
|
456
|
-
if (!isWhole(partnerCredits, 1)) return invalid("partnerCredits");
|
|
457
|
-
if (!isWhole(relumeCredits, 0)) return invalid("relumeCredits");
|
|
458
|
-
if (!isString(reason, 1, 200)) return invalid("reason");
|
|
459
|
-
const chargeId = optionalString(body.chargeId, 64);
|
|
460
|
-
if (!chargeId.ok) return invalid("chargeId");
|
|
461
|
-
const reversal = {
|
|
462
|
-
userId,
|
|
463
|
-
reversalId,
|
|
464
|
-
partnerCredits,
|
|
465
|
-
relumeCredits,
|
|
466
|
-
reason
|
|
467
|
-
};
|
|
468
|
-
if (chargeId.value !== void 0) reversal.chargeId = chargeId.value;
|
|
469
|
-
return { ok: true, call: { type, reversal } };
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
function balanceOrThrow(value, callback) {
|
|
474
|
-
if (!isWhole(value, 0)) {
|
|
475
|
-
throw new InvalidCallbackResultError(
|
|
476
|
-
`${callback} must return a balance that is a whole number, zero or more; got ${JSON.stringify(value)}.`
|
|
477
|
-
);
|
|
478
|
-
}
|
|
479
|
-
return value;
|
|
480
|
-
}
|
|
481
|
-
function checkUserProfile(value) {
|
|
482
|
-
if (value === null || value === void 0) return null;
|
|
483
|
-
if (!isRecord(value)) {
|
|
484
|
-
throw new InvalidCallbackResultError(
|
|
485
|
-
"onGetUser must return { name?, email?, avatarUrl? } or null."
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
const profile = {};
|
|
489
|
-
const { name, email, avatarUrl } = value;
|
|
490
|
-
if (!absent(name)) {
|
|
491
|
-
if (!isString(name, 1, 200)) {
|
|
492
|
-
throw new InvalidCallbackResultError(
|
|
493
|
-
"onGetUser: name must be a string of at most 200 characters."
|
|
494
|
-
);
|
|
495
|
-
}
|
|
496
|
-
profile.name = name;
|
|
497
|
-
}
|
|
498
|
-
if (!absent(email)) {
|
|
499
|
-
if (!isEmail(email)) {
|
|
500
|
-
throw new InvalidCallbackResultError(
|
|
501
|
-
"onGetUser: email must be a valid email address."
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
profile.email = email;
|
|
505
|
-
}
|
|
506
|
-
if (!absent(avatarUrl)) {
|
|
507
|
-
if (!isHttpsUrl(avatarUrl)) {
|
|
508
|
-
throw new InvalidCallbackResultError(
|
|
509
|
-
"onGetUser: avatarUrl must be an https URL."
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
profile.avatarUrl = avatarUrl;
|
|
513
|
-
}
|
|
514
|
-
return profile;
|
|
515
|
-
}
|
|
516
|
-
function checkCredits(value) {
|
|
517
|
-
if (!isRecord(value)) {
|
|
518
|
-
throw new InvalidCallbackResultError(
|
|
519
|
-
"onGetCredits must return { balance }."
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
return { balance: balanceOrThrow(value.balance, "onGetCredits") };
|
|
523
|
-
}
|
|
524
|
-
function optionalBalance(value, callback) {
|
|
525
|
-
return absent(value.balance) ? {} : { balance: balanceOrThrow(value.balance, callback) };
|
|
526
|
-
}
|
|
527
|
-
function checkChargeResult(value) {
|
|
528
|
-
if (!isRecord(value) || typeof value.ok !== "boolean") {
|
|
529
|
-
throw new InvalidCallbackResultError(
|
|
530
|
-
"onChargeCredits must return { ok: boolean, balance? }."
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
return { ok: value.ok, ...optionalBalance(value, "onChargeCredits") };
|
|
534
|
-
}
|
|
535
|
-
function checkReversalResult(value) {
|
|
536
|
-
if (value === void 0 || value === null) return {};
|
|
537
|
-
if (!isRecord(value)) {
|
|
538
|
-
throw new InvalidCallbackResultError(
|
|
539
|
-
"onReverseCharge must return { balance? } or nothing."
|
|
540
|
-
);
|
|
541
|
-
}
|
|
542
|
-
return optionalBalance(value, "onReverseCharge");
|
|
543
|
-
}
|
|
544
|
-
|
|
545
382
|
// src/pages.ts
|
|
546
383
|
function isPage(value) {
|
|
547
384
|
return isRecord(value) && Array.isArray(value.data) && typeof value.nextCursor === "string" && typeof value.hasMore === "boolean";
|
|
@@ -606,8 +443,9 @@ var isLedgerEntryDetail = hasStrings(
|
|
|
606
443
|
"type",
|
|
607
444
|
"status"
|
|
608
445
|
);
|
|
446
|
+
var isCreditUnit = hasStrings("singular", "plural");
|
|
609
447
|
function isPriceList(value) {
|
|
610
|
-
return isRecord(value) && Array.isArray(value.data);
|
|
448
|
+
return isRecord(value) && Array.isArray(value.data) && isCreditUnit(value.creditUnit);
|
|
611
449
|
}
|
|
612
450
|
function segment(name, value) {
|
|
613
451
|
if (typeof value !== "string" || value === "") {
|
|
@@ -686,7 +524,8 @@ function partnerApi(config) {
|
|
|
686
524
|
})
|
|
687
525
|
},
|
|
688
526
|
prices: {
|
|
689
|
-
list: async () => (await get("/prices", isPriceList)).data
|
|
527
|
+
list: async () => (await get("/prices", isPriceList)).data,
|
|
528
|
+
get: () => get("/prices", isPriceList)
|
|
690
529
|
}
|
|
691
530
|
};
|
|
692
531
|
}
|
|
@@ -723,6 +562,7 @@ var Relume = class _Relume {
|
|
|
723
562
|
*/
|
|
724
563
|
fetchHandler;
|
|
725
564
|
#secretKey;
|
|
565
|
+
#environment;
|
|
726
566
|
#api;
|
|
727
567
|
#callbacks = {};
|
|
728
568
|
#onHandlerError;
|
|
@@ -742,6 +582,12 @@ var Relume = class _Relume {
|
|
|
742
582
|
"Relume.init: that is a publishable key. Pass the secret key (sk_\u2026); the publishable key goes to the embed."
|
|
743
583
|
);
|
|
744
584
|
}
|
|
585
|
+
const environment = keyEnvironment(secretKey);
|
|
586
|
+
if (environment === null) {
|
|
587
|
+
throw new TypeError(
|
|
588
|
+
"Relume.init: secretKey must start with sk_test_ or sk_live_."
|
|
589
|
+
);
|
|
590
|
+
}
|
|
745
591
|
const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
746
592
|
if (!Number.isSafeInteger(maxRetries) || maxRetries < 0) {
|
|
747
593
|
throw new TypeError(
|
|
@@ -749,6 +595,7 @@ var Relume = class _Relume {
|
|
|
749
595
|
);
|
|
750
596
|
}
|
|
751
597
|
this.#secretKey = secretKey;
|
|
598
|
+
this.#environment = environment;
|
|
752
599
|
this.#now = options.now ?? Date.now;
|
|
753
600
|
this.#api = {
|
|
754
601
|
secretKey,
|
|
@@ -824,8 +671,9 @@ var Relume = class _Relume {
|
|
|
824
671
|
}
|
|
825
672
|
/**
|
|
826
673
|
* `POST /relume/calls`, framework-agnostic: verifies the signature and the
|
|
827
|
-
* 5-minute window,
|
|
828
|
-
*
|
|
674
|
+
* 5-minute window, refuses a call for another environment than the secret
|
|
675
|
+
* key's (`400 environment_mismatch`), routes by `type`, runs your callback
|
|
676
|
+
* and validates what it returns. Pass the raw body exactly as received.
|
|
829
677
|
*/
|
|
830
678
|
async handleCall(input) {
|
|
831
679
|
const requestId = input.requestId ?? null;
|
|
@@ -849,10 +697,14 @@ var Relume = class _Relume {
|
|
|
849
697
|
"The body is not valid JSON."
|
|
850
698
|
);
|
|
851
699
|
}
|
|
852
|
-
const parsed = parseCall(body);
|
|
700
|
+
const parsed = parseCall(body, this.#environment);
|
|
853
701
|
if (!parsed.ok) return errorResponse(400, parsed.code, parsed.message);
|
|
854
702
|
const { call } = parsed;
|
|
855
|
-
const context = {
|
|
703
|
+
const context = {
|
|
704
|
+
type: call.type,
|
|
705
|
+
environment: this.#environment,
|
|
706
|
+
requestId
|
|
707
|
+
};
|
|
856
708
|
const callbackName = CALLBACK_FOR[call.type];
|
|
857
709
|
if (!this.#callbacks[callbackName]) {
|
|
858
710
|
this.#onHandlerError(
|