@relume_io/partner-node 2.0.0 → 2.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/dist/index.d.cts 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
  }
@@ -451,8 +458,9 @@ declare class Relume<TRequest extends SessionRequest = SessionRequest> {
451
458
  express(): NodeHandler;
452
459
  /**
453
460
  * `POST /relume/calls`, framework-agnostic: verifies the signature and the
454
- * 5-minute window, routes by `type`, runs your callback and validates what
455
- * it returns. Pass the raw body exactly as received.
461
+ * 5-minute window, refuses a call for another environment than the secret
462
+ * key's (`400 environment_mismatch`), routes by `type`, runs your callback
463
+ * and validates what it returns. Pass the raw body exactly as received.
456
464
  */
457
465
  handleCall(input: {
458
466
  rawBody: string | Uint8Array;
@@ -522,4 +530,4 @@ declare function verifySignature(input: {
522
530
  nowSeconds?: number;
523
531
  }): Promise<VerifySignatureResult>;
524
532
 
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 };
533
+ 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 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, 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.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
  }
@@ -451,8 +458,9 @@ declare class Relume<TRequest extends SessionRequest = SessionRequest> {
451
458
  express(): NodeHandler;
452
459
  /**
453
460
  * `POST /relume/calls`, framework-agnostic: verifies the signature and the
454
- * 5-minute window, routes by `type`, runs your callback and validates what
455
- * it returns. Pass the raw body exactly as received.
461
+ * 5-minute window, refuses a call for another environment than the secret
462
+ * key's (`400 environment_mismatch`), routes by `type`, runs your callback
463
+ * and validates what it returns. Pass the raw body exactly as received.
456
464
  */
457
465
  handleCall(input: {
458
466
  rawBody: string | Uint8Array;
@@ -522,4 +530,4 @@ declare function verifySignature(input: {
522
530
  nowSeconds?: number;
523
531
  }): Promise<VerifySignatureResult>;
524
532
 
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 };
533
+ 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 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, 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
- absent,
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-HPVVBUR7.js";
29
+ } from "./chunk-6WSDAFUV.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.0";
231
+ var SDK_VERSION = "2.1.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";
@@ -723,6 +560,7 @@ var Relume = class _Relume {
723
560
  */
724
561
  fetchHandler;
725
562
  #secretKey;
563
+ #environment;
726
564
  #api;
727
565
  #callbacks = {};
728
566
  #onHandlerError;
@@ -742,6 +580,12 @@ var Relume = class _Relume {
742
580
  "Relume.init: that is a publishable key. Pass the secret key (sk_\u2026); the publishable key goes to the embed."
743
581
  );
744
582
  }
583
+ const environment = keyEnvironment(secretKey);
584
+ if (environment === null) {
585
+ throw new TypeError(
586
+ "Relume.init: secretKey must start with sk_test_ or sk_live_."
587
+ );
588
+ }
745
589
  const maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
746
590
  if (!Number.isSafeInteger(maxRetries) || maxRetries < 0) {
747
591
  throw new TypeError(
@@ -749,6 +593,7 @@ var Relume = class _Relume {
749
593
  );
750
594
  }
751
595
  this.#secretKey = secretKey;
596
+ this.#environment = environment;
752
597
  this.#now = options.now ?? Date.now;
753
598
  this.#api = {
754
599
  secretKey,
@@ -824,8 +669,9 @@ var Relume = class _Relume {
824
669
  }
825
670
  /**
826
671
  * `POST /relume/calls`, framework-agnostic: verifies the signature and the
827
- * 5-minute window, routes by `type`, runs your callback and validates what
828
- * it returns. Pass the raw body exactly as received.
672
+ * 5-minute window, refuses a call for another environment than the secret
673
+ * key's (`400 environment_mismatch`), routes by `type`, runs your callback
674
+ * and validates what it returns. Pass the raw body exactly as received.
829
675
  */
830
676
  async handleCall(input) {
831
677
  const requestId = input.requestId ?? null;
@@ -849,10 +695,14 @@ var Relume = class _Relume {
849
695
  "The body is not valid JSON."
850
696
  );
851
697
  }
852
- const parsed = parseCall(body);
698
+ const parsed = parseCall(body, this.#environment);
853
699
  if (!parsed.ok) return errorResponse(400, parsed.code, parsed.message);
854
700
  const { call } = parsed;
855
- const context = { type: call.type, requestId };
701
+ const context = {
702
+ type: call.type,
703
+ environment: this.#environment,
704
+ requestId
705
+ };
856
706
  const callbackName = CALLBACK_FOR[call.type];
857
707
  if (!this.#callbacks[callbackName]) {
858
708
  this.#onHandlerError(