@mesub/node 0.1.5 → 0.1.6

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.
Files changed (58) hide show
  1. package/README.md +18 -5
  2. package/dist/{chunk-43Q4PNF7.cjs → chunk-4M3ACBWX.cjs} +28 -2
  3. package/dist/chunk-4M3ACBWX.cjs.map +1 -0
  4. package/dist/{chunk-D3MM42ZL.cjs → chunk-5LDAYKUA.cjs} +7 -7
  5. package/dist/{chunk-D3MM42ZL.cjs.map → chunk-5LDAYKUA.cjs.map} +1 -1
  6. package/dist/{chunk-SFK52JDW.cjs → chunk-5THDILDD.cjs} +16 -3
  7. package/dist/chunk-5THDILDD.cjs.map +1 -0
  8. package/dist/{chunk-ELS6KPJQ.js → chunk-ANBU7BJU.js} +2 -2
  9. package/dist/{chunk-PH5LR5WA.js → chunk-EGGKTDHO.js} +2 -2
  10. package/dist/{chunk-IHGJN3QZ.js → chunk-QDBKSJ56.js} +16 -3
  11. package/dist/chunk-QDBKSJ56.js.map +1 -0
  12. package/dist/{chunk-JJWHMB2X.js → chunk-R4H46TSQ.js} +28 -2
  13. package/dist/chunk-R4H46TSQ.js.map +1 -0
  14. package/dist/{chunk-6XJWKRCN.cjs → chunk-TDRUSUBV.cjs} +4 -4
  15. package/dist/{chunk-6XJWKRCN.cjs.map → chunk-TDRUSUBV.cjs.map} +1 -1
  16. package/dist/{client-BZB6KIGd.d.cts → client-B2DttW-V.d.cts} +18 -2
  17. package/dist/{client-Q5QhXphp.d.ts → client-BHrc_yO3.d.ts} +18 -2
  18. package/dist/{errors-BN4ZWUfM.d.ts → errors-CTFJfJoF.d.ts} +1 -1
  19. package/dist/{errors-DcLfunS-.d.cts → errors-xk1hGCOf.d.cts} +1 -1
  20. package/dist/express.cjs +21 -21
  21. package/dist/express.d.cts +7 -7
  22. package/dist/express.d.ts +7 -7
  23. package/dist/express.js +3 -3
  24. package/dist/{guard-D4AB5HtN.d.cts → guard-CDC9mDWY.d.cts} +1 -1
  25. package/dist/{guard-CaXiHMVF.d.ts → guard-CrGTKKXV.d.ts} +1 -1
  26. package/dist/index.cjs +3 -3
  27. package/dist/index.d.cts +5 -5
  28. package/dist/index.d.ts +5 -5
  29. package/dist/index.js +2 -2
  30. package/dist/nest.cjs +13 -13
  31. package/dist/nest.d.cts +5 -5
  32. package/dist/nest.d.ts +5 -5
  33. package/dist/nest.js +2 -2
  34. package/dist/next.cjs +20 -20
  35. package/dist/next.d.cts +7 -7
  36. package/dist/next.d.ts +7 -7
  37. package/dist/next.js +3 -3
  38. package/dist/{routes-DT8EFWSl.d.cts → routes-BpOV9UAR.d.cts} +2 -2
  39. package/dist/{routes-KoaD1Xsk.d.ts → routes-DWhspa6s.d.ts} +2 -2
  40. package/dist/situations.cjs +2 -2
  41. package/dist/situations.d.cts +41 -1
  42. package/dist/situations.d.ts +41 -1
  43. package/dist/situations.js +1 -1
  44. package/dist/{subscriptions-B6Lin6i_.d.ts → subscriptions-Dy_BNyPE.d.cts} +6 -3
  45. package/dist/{subscriptions-B6Lin6i_.d.cts → subscriptions-Dy_BNyPE.d.ts} +6 -3
  46. package/dist/testing.cjs +19 -15
  47. package/dist/testing.cjs.map +1 -1
  48. package/dist/testing.d.cts +9 -6
  49. package/dist/testing.d.ts +9 -6
  50. package/dist/testing.js +5 -1
  51. package/dist/testing.js.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/chunk-43Q4PNF7.cjs.map +0 -1
  54. package/dist/chunk-IHGJN3QZ.js.map +0 -1
  55. package/dist/chunk-JJWHMB2X.js.map +0 -1
  56. package/dist/chunk-SFK52JDW.cjs.map +0 -1
  57. /package/dist/{chunk-ELS6KPJQ.js.map → chunk-ANBU7BJU.js.map} +0 -0
  58. /package/dist/{chunk-PH5LR5WA.js.map → chunk-EGGKTDHO.js.map} +0 -0
package/README.md CHANGED
@@ -148,8 +148,15 @@ A charge can also fail for a reason that is not your customer's. The answer
148
148
  then carries `blocked_since` and `blocked_reason`, both null otherwise:
149
149
  `mesub` for a cause on Mesub's side, or your plan's own setup by name,
150
150
  `puller_not_allowed`, `receiver_not_allowed`, `receiver_account_missing`,
151
- `receiver_account_frozen` or `mint_gone`. `status` and `payment_status` do not
152
- move for it and Mesub keeps trying; `access` ends at `access_until` if nothing
151
+ `receiver_account_frozen`, `receiver_account_needs_memo` (the receiving token
152
+ account requires a memo on every payment in, which Mesub's charges do not
153
+ carry: turn it off or choose another receiving wallet),
154
+ `receiver_account_confidential_only` (it takes confidential transfers only:
155
+ allow plain ones or choose another receiving wallet) or `mint_gone`. Two are
156
+ the token issuer's to lift, not yours: `mint_paused` (the issuer paused the
157
+ token) and `mint_transfer_hook` (the issuer made its transfers run a program
158
+ Mesub does not support yet; otherwise, create a plan with another token).
159
+ `status` and `payment_status` do not move for it and Mesub keeps trying; `access` ends at `access_until` if nothing
153
160
  is collected by then, and comes back with the first charge that goes through,
154
161
  nothing to sign. On `mint_gone` no charge can go through again, so access does
155
162
  not come back.
@@ -322,8 +329,9 @@ stays as `{amount}` and is listed in `missing`. Dates are written as Mesub
322
329
  serves them, in ISO 8601, unless you pass `formatDate`. A status or a reason
323
330
  newer than this release gives `key: 'unknown'` and the access line, never an
324
331
  error. A blocked subscription has its own keys, `blocked_mesub` and one per
325
- cause of your setup (`blocked_puller_not_allowed`, ...): the subscriber reads
326
- that nothing is theirs to do, you read what to fix.
332
+ cause (`blocked_puller_not_allowed`, ..., `blocked_mint_paused`): the
333
+ subscriber reads that nothing is theirs to do, you read what to fix, or who
334
+ lifts it when the token's issuer does.
327
335
 
328
336
  `close` is offered only while Mesub says the subscription is `closable`, and
329
337
  the sentence names `closable_from` when it can be closed later. A
@@ -493,7 +501,9 @@ A parked seat keeps access to the end of its paid period; its
493
501
  unpause when access had ended.
494
502
 
495
503
  A `subscription.renewed` can come late, on
496
- a subscription that is no longer `active`. The events, their bodies and how to
504
+ a subscription that is no longer `active`. Its `detail.amount_received` is what
505
+ the receiver got, `amount` less the fee a Token-2022 mint withholds: book
506
+ revenue from it. Null when Mesub does not know it. The events, their bodies and how to
497
507
  test: [Webhooks](https://docs.mesub.io/docs/webhooks).
498
508
 
499
509
  `subscription.renewal_upcoming` comes once per period, in its last quarter and
@@ -704,6 +714,9 @@ the option: a `baseUrl` that is not https (plain http only to `localhost` or
704
714
  number of milliseconds, or `maxRetries` or `maxStaleMs` below 0. On an edge runtime
705
715
  without `process.env` (Cloudflare Workers), pass `apiKey` yourself.
706
716
 
717
+ `await mesub.project()` answers `{ id }`, the project the key belongs to: the
718
+ quickest way to check that a key works. A wrong key throws `invalid_api_key`.
719
+
707
720
  Behind a proxy, `baseUrl` may carry a path: every call goes under it
708
721
  (`https://proxy.example.com/mesub/v1/access`). Add whatever the proxy asks
709
722
  for, such as a Cloudflare Access service token:
@@ -48,6 +48,7 @@ var UPGRADE = {
48
48
  };
49
49
  var CANCEL_PAID = { ...CANCEL, confirm: "confirm_cancel_paid" };
50
50
  var BLOCKED_BY_SETUP = "Mesub cannot take your payments for {plan_name} for now, because of how the plan is set up, not because of anything you did: there is nothing for you to do or sign, and Mesub keeps trying.[ You keep access until {access_until} if no payment goes through by then.] Access comes back on its own with the first payment that goes through.";
51
+ var BLOCKED_BY_TOKEN = "Mesub cannot take your payments for {plan_name} for now, because of the token it is paid in, not because of anything you did: there is nothing for you to do or sign, and Mesub keeps trying.[ You keep access until {access_until} if no payment goes through by then.] Access comes back on its own with the first payment that goes through.";
51
52
  var BLOCKED_ACCESS = "[ Access ends on {access_until} if nothing is collected by then.] Access comes back with the first payment that goes through, with nothing for the subscriber to sign.";
52
53
  var NEVER_BACK = "No payment can go through on this plan again, so once access ends it does not come back.";
53
54
  var CANCEL_BEHIND = { ...CANCEL, confirm: "confirm_cancel_behind" };
@@ -148,6 +149,27 @@ var SITUATIONS = {
148
149
  merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments is frozen, so it cannot take payments. Choose another receiving wallet, or ask the token's issuer to unfreeze that account; Mesub keeps trying.${BLOCKED_ACCESS}`,
149
150
  actions: [CANCEL]
150
151
  },
152
+ blocked_receiver_account_needs_memo: {
153
+ subscriber: BLOCKED_BY_SETUP,
154
+ merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments requires a memo on every payment in, which Mesub's charges do not carry. Turn that requirement off for the account, or choose another receiving wallet; Mesub keeps trying.${BLOCKED_ACCESS}`,
155
+ actions: [CANCEL]
156
+ },
157
+ blocked_receiver_account_confidential_only: {
158
+ subscriber: BLOCKED_BY_SETUP,
159
+ merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments takes confidential transfers only, and Mesub's charges are plain ones. Allow plain transfers into the account, or choose another receiving wallet; Mesub keeps trying.${BLOCKED_ACCESS}`,
160
+ actions: [CANCEL]
161
+ },
162
+ // The token issuer's to lift, not the merchant's (Mesub-io/backend#396).
163
+ blocked_mint_paused: {
164
+ subscriber: BLOCKED_BY_TOKEN,
165
+ merchant: `Payments blocked[ since {blocked_since}] by the plan's token: its issuer paused it, so no payment in it can move. Only the issuer can lift the pause, and Mesub charges again once they do.${BLOCKED_ACCESS}`,
166
+ actions: [CANCEL]
167
+ },
168
+ blocked_mint_transfer_hook: {
169
+ subscriber: BLOCKED_BY_TOKEN,
170
+ merchant: `Payments blocked[ since {blocked_since}] by the plan's token: its issuer made every transfer of it run their own program, which Mesub does not support yet. Mesub charges again if the issuer removes it; otherwise, create a new plan with another token.${BLOCKED_ACCESS}`,
171
+ actions: [CANCEL]
172
+ },
151
173
  blocked_mint_gone: {
152
174
  subscriber: `Mesub can no longer take your payments for {plan_name}: the token it is paid in no longer exists. This is not because of anything you did, and there is nothing for you to do or sign.[ You keep access until {access_until}.] ${NEVER_BACK}`,
153
175
  merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the plan's token no longer exists on chain, so nothing can be collected on it. Create a new plan with another token.[ Access ends on {access_until}.] ${NEVER_BACK}`,
@@ -325,7 +347,11 @@ var BLOCKED_REASONS = {
325
347
  receiver_not_allowed: "blocked_receiver_not_allowed",
326
348
  receiver_account_missing: "blocked_receiver_account_missing",
327
349
  receiver_account_frozen: "blocked_receiver_account_frozen",
328
- mint_gone: "blocked_mint_gone"
350
+ receiver_account_needs_memo: "blocked_receiver_account_needs_memo",
351
+ receiver_account_confidential_only: "blocked_receiver_account_confidential_only",
352
+ mint_gone: "blocked_mint_gone",
353
+ mint_paused: "blocked_mint_paused",
354
+ mint_transfer_hook: "blocked_mint_transfer_hook"
329
355
  };
330
356
  var BY_STATUS = /* @__PURE__ */ new Set(["none", "pending", "expired", "failed", "superseded"]);
331
357
  function isFree(input) {
@@ -477,4 +503,4 @@ function render(template, values, missing) {
477
503
 
478
504
 
479
505
  exports.SITUATIONS = SITUATIONS; exports.CONFIRMATIONS = CONFIRMATIONS; exports.ACCESS_LINE = ACCESS_LINE; exports.situationOf = situationOf; exports.explain = explain;
480
- //# sourceMappingURL=chunk-43Q4PNF7.cjs.map
506
+ //# sourceMappingURL=chunk-4M3ACBWX.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/node-sdk/node-sdk/dist/chunk-4M3ACBWX.cjs","../src/situations.ts"],"names":[],"mappings":"AAAA;AC4EA,IAAM,UAAA,EAAY;AAAA,EACd,EAAA,EAAI,YAAA;AAAA,EACJ,MAAA,EAAQ,WAAA;AAAA,EACR,GAAA,EAAK,sBAAA;AAAA,EACL,KAAA,EAAO;AACX,CAAA;AACA,IAAM,OAAA,EAAS;AAAA,EACX,EAAA,EAAI,YAAA;AAAA,EACJ,MAAA,EAAQ,QAAA;AAAA,EACR,GAAA,EAAK,sBAAA;AAAA,EACL,KAAA,EAAO;AACX,CAAA;AACA,IAAM,OAAA,EAAS;AAAA,EACX,EAAA,EAAI,YAAA;AAAA,EACJ,MAAA,EAAQ,QAAA;AAAA,EACR,GAAA,EAAK,sBAAA;AAAA,EACL,KAAA,EAAO,mCAAA;AAAA,EACP,KAAA,EAAO;AACX,CAAA;AACA,IAAM,MAAA,EAAQ;AAAA,EACV,EAAA,EAAI,YAAA;AAAA,EACJ,MAAA,EAAQ,OAAA;AAAA,EACR,GAAA,EAAK,qBAAA;AAAA,EACL,KAAA,EAAO,kCAAA;AAAA,EACP,OAAA,EAAS,eAAA;AAAA,EACT,IAAA,EAAM;AACV,CAAA;AAEA,IAAM,WAAA,EAAa,EAAE,GAAG,KAAA,EAAO,OAAA,EAAS,qBAAqB,CAAA;AAC7D,IAAM,UAAA,EAAY,EAAE,EAAA,EAAI,YAAA,EAAc,MAAA,EAAQ,YAAY,CAAA;AAE1D,IAAM,MAAA,EAAQ;AAAA,EACV,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ,OAAA;AAAA,EACR,KAAA,EAAO,wCAAA;AAAA,EACP,SAAA,EAAW;AACf,CAAA;AAMA,IAAM,QAAA,EAAU;AAAA,EACZ,EAAA,EAAI,YAAA;AAAA,EACJ,MAAA,EAAQ,OAAA;AAAA,EACR,GAAA,EAAK,qBAAA;AAAA,EACL,KAAA,EAAO;AACX,CAAA;AACA,IAAM,QAAA,EAAU;AAAA,EACZ,EAAA,EAAI,UAAA;AAAA,EACJ,MAAA,EAAQ,SAAA;AAAA,EACR,KAAA,EAAO,4BAAA;AAAA,EACP,SAAA,EAAW;AACf,CAAA;AACA,IAAM,YAAA,EAAc,EAAE,GAAG,MAAA,EAAQ,OAAA,EAAS,sBAAsB,CAAA;AAMhE,IAAM,iBAAA,EACF,gVAAA;AAEJ,IAAM,iBAAA,EACF,iVAAA;AAEJ,IAAM,eAAA,EACF,wKAAA;AAKJ,IAAM,WAAA,EACF,0FAAA;AACJ,IAAM,cAAA,EAAgB,EAAE,GAAG,MAAA,EAAQ,OAAA,EAAS,wBAAwB,CAAA;AAO7D,IAAM,WAAA,EAAa;AAAA;AAAA,EAEtB,IAAA,EAAM;AAAA,IACF,UAAA,EAAY,wCAAA;AAAA,IACZ,QAAA,EAAU,mDAAA;AAAA,IACV,OAAA,EAAS,CAAC,SAAS;AAAA,EACvB,CAAA;AAAA,EACA,MAAA,EAAQ;AAAA,IACJ,UAAA,EAAY,+DAAA;AAAA,IACZ,QAAA,EAAU,mEAAA;AAAA,IACV,OAAA,EAAS,CAAC,WAAW;AAAA,EACzB,CAAA;AAAA;AAAA,EAEA,kBAAA,EAAoB;AAAA,IAChB,UAAA,EACI,2FAAA;AAAA,IACJ,QAAA,EACI,4HAAA;AAAA,IACJ,OAAA,EAAS,CAAC,WAAW;AAAA,EACzB,CAAA;AAAA,EACA,eAAA,EAAiB;AAAA,IACb,UAAA,EAAY,+DAAA;AAAA,IACZ,QAAA,EACI,8IAAA;AAAA,IACJ,OAAA,EAAS,CAAC;AAAA,EACd,CAAA;AAAA;AAAA;AAAA,EAIA,kBAAA,EAAoB;AAAA,IAChB,UAAA,EACI,yIAAA;AAAA,IACJ,QAAA,EACI,uJAAA;AAAA,IACJ,OAAA,EAAS,CAAC,OAAA,EAAS,aAAA,EAAe,KAAK;AAAA,EAC3C,CAAA;AAAA,EACA,mCAAA,EAAqC;AAAA,IACjC,UAAA,EACI,wRAAA;AAAA,IACJ,QAAA,EACI,iPAAA;AAAA,IACJ,OAAA,EAAS,CAAC,SAAA,EAAW,OAAA,EAAS,aAAA,EAAe,KAAK;AAAA,EACtD,CAAA;AAAA,EACA,gCAAA,EAAkC;AAAA,IAC9B,UAAA,EACI,uPAAA;AAAA,IACJ,QAAA,EACI,+OAAA;AAAA,IACJ,OAAA,EAAS;AAAA,MACL,SAAA;AAAA,MACA,EAAE,GAAG,OAAA,EAAS,KAAA,EAAO,iBAAiB,CAAA;AAAA,MACtC,aAAA;AAAA,MACA,EAAE,GAAG,KAAA,EAAO,KAAA,EAAO,iBAAiB;AAAA,IACxC;AAAA,EACJ,CAAA;AAAA,EACA,uBAAA,EAAyB;AAAA,IACrB,UAAA,EACI,sQAAA;AAAA,IACJ,QAAA,EAAU;AAAA,MACN,OAAA,EACI,sOAAA;AAAA,MACJ,IAAA,EAAM;AAAA,IACV,CAAA;AAAA;AAAA;AAAA,IAGA,OAAA,EAAS,CAAC,aAAa;AAAA,EAC3B,CAAA;AAAA,EACA,uBAAA,EAAyB;AAAA,IACrB,UAAA,EACI,+NAAA;AAAA,IACJ,QAAA,EAAU;AAAA,MACN,OAAA,EACI,mMAAA;AAAA,MACJ,IAAA,EAAM;AAAA,IACV,CAAA;AAAA;AAAA,IAEA,OAAA,EAAS,CAAC,aAAa;AAAA,EAC3B,CAAA;AAAA,EACA,cAAA,EAAgB;AAAA,IACZ,UAAA,EACI,uKAAA;AAAA,IACJ,QAAA,EACI,uJAAA;AAAA,IACJ,OAAA,EAAS,CAAC,OAAA,EAAS,aAAA,EAAe,KAAK;AAAA,EAC3C,CAAA;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,EAAe;AAAA,IACX,UAAA,EACI,kTAAA;AAAA,IACJ,QAAA,EAAU,CAAA,6JAAA,EAAgK,cAAc,CAAA,CAAA;AACxK,IAAA;AACpB,EAAA;AAC4B,EAAA;AACZ,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AAC8B,EAAA;AACd,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AACkC,EAAA;AAClB,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AACiC,EAAA;AACjB,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AACqC,EAAA;AACrB,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AAC4C,EAAA;AAC5B,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AAAA;AAEqB,EAAA;AACL,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AAC4B,EAAA;AACZ,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AACmB,EAAA;AACH,IAAA;AACF,IAAA;AACM,IAAA;AACpB,EAAA;AAAA;AAGuB,EAAA;AAEf,IAAA;AAEA,IAAA;AAC8C,IAAA;AACtD,EAAA;AACqB,EAAA;AAEb,IAAA;AAEA,IAAA;AAAA;AAEW,IAAA;AACnB,EAAA;AACiB,EAAA;AAET,IAAA;AAEA,IAAA;AACW,IAAA;AACnB,EAAA;AAAA;AAGS,EAAA;AAED,IAAA;AAAA;AAGA,IAAA;AAAA;AAAA;AAAA;AAAA;AAKqC,IAAA;AAC7C,EAAA;AAAA;AAAA;AAAA;AAI0B,EAAA;AAElB,IAAA;AAEA,IAAA;AACW,IAAA;AACnB,EAAA;AAAA;AAGQ,EAAA;AAEA,IAAA;AAEA,IAAA;AAC0B,IAAA;AAClC,EAAA;AACoB,EAAA;AAEZ,IAAA;AAEA,IAAA;AACqB,IAAA;AAC7B,EAAA;AACkB,EAAA;AAEV,IAAA;AACM,IAAA;AAC+C,IAAA;AAC7D,EAAA;AAAA;AAGc,EAAA;AAEN,IAAA;AAEA,IAAA;AACe,IAAA;AACvB,EAAA;AACwB,EAAA;AAEhB,IAAA;AAEA,IAAA;AACe,IAAA;AACvB,EAAA;AACoB,EAAA;AAEZ,IAAA;AAAA;AAGA,IAAA;AACgB,IAAA;AACxB,EAAA;AACqB,EAAA;AAEb,IAAA;AAEA,IAAA;AACgB,IAAA;AACxB,EAAA;AACkB,EAAA;AAAA;AAGV,IAAA;AACM,IAAA;AAAA;AAAA;AAGU,IAAA;AACxB,EAAA;AACe,EAAA;AACC,IAAA;AACF,IAAA;AACA,IAAA;AACd,EAAA;AAAA;AAGS,EAAA;AAED,IAAA;AACM,IAAA;AAAA;AAED,IAAA;AACL,MAAA;AACQ,QAAA;AACI,QAAA;AACH,QAAA;AACE,QAAA;AACX,MAAA;AACA,MAAA;AACQ,QAAA;AACI,QAAA;AACH,QAAA;AACE,QAAA;AACX,MAAA;AACJ,IAAA;AACJ,EAAA;AACS,EAAA;AAED,IAAA;AACM,IAAA;AACS,IAAA;AACvB,EAAA;AACQ,EAAA;AAEA,IAAA;AAEA,IAAA;AACK,IAAA;AACL,MAAA;AACQ,QAAA;AACI,QAAA;AACH,QAAA;AACE,QAAA;AACX,MAAA;AACA,MAAA;AACJ,IAAA;AACJ,EAAA;AACY,EAAA;AAAA;AAEI,IAAA;AAAA;AAGR,IAAA;AACK,IAAA;AACL,MAAA;AACQ,QAAA;AACI,QAAA;AACH,QAAA;AACE,QAAA;AACX,MAAA;AACJ,IAAA;AACJ,EAAA;AACJ;AAG6B;AAErB,EAAA;AAEA,EAAA;AAEA,EAAA;AAEA,EAAA;AAEA,EAAA;AAEA,EAAA;AACR;AAGyF;AAmEzB;AACjD,EAAA;AACH,EAAA;AACU,EAAA;AACA,EAAA;AACJ,EAAA;AACC,EAAA;AACH,EAAA;AAChB;AAEmG;AAC7E,EAAA;AACA,EAAA;AACtB;AAM6E;AACzD,EAAA;AACO,EAAA;AAC3B;AAEwE;AAC7D,EAAA;AACa,EAAA;AACE,EAAA;AACI,EAAA;AACD,EAAA;AACI,EAAA;AACO,EAAA;AACzB,EAAA;AACE,EAAA;AACO,EAAA;AACxB;AAGgF;AAGnC;AACV,EAAA;AACnC;AAG+C;AACX,EAAA;AACpC;AAY8D;AAC7B,EAAA;AACG,EAAA;AAEE,EAAA;AAEoD,EAAA;AAC/C,IAAA;AACF,IAAA;AACrC,EAAA;AAIuD,EAAA;AACe,EAAA;AAG5D,IAAA;AACV,EAAA;AAEgB,EAAA;AACE,IAAA;AACwC,MAAA;AACtB,MAAA;AAGtB,MAAA;AACV,IAAA;AACK,IAAA;AACwB,MAAA;AAEoC,MAAA;AAClD,IAAA;AACwC,MAAA;AACP,MAAA;AACc,MAAA;AAEY,MAAA;AACxC,QAAA;AACa,QAAA;AAC3C,MAAA;AAC4B,MAAA;AACW,MAAA;AAG7B,QAAA;AACV,MAAA;AACuD,MAAA;AAC3D,IAAA;AACK,IAAA;AAC4B,MAAA;AACH,MAAA;AAGpB,MAAA;AACM,IAAA;AACuC,MAAA;AACT,MAAA;AAEiC,MAAA;AAC/E,IAAA;AACA,IAAA;AACW,MAAA;AACf,EAAA;AACJ;AAcuF;AACtD,EAAA;AACS,EAAA;AACN,EAAA;AAC2C,EAAA;AACI,EAAA;AAExD,EAAA;AACZ,IAAA;AACH,MAAA;AACY,MAAA;AACF,MAAA;AACF,MAAA;AACE,MAAA;AACU,MAAA;AACxB,IAAA;AACJ,EAAA;AAE2C,EAAA;AACL,EAAA;AACoD,EAAA;AAC1C,EAAA;AACZ,EAAA;AAEM,EAAA;AACW,IAAA;AACP,IAAA;AAGW,IAAA;AAE5B,IAAA;AACI,MAAA;AACN,MAAA;AACe,QAAA;AACf,QAAA;AACnB,MAAA;AACJ,IAAA;AACuE,IAAA;AACpD,IAAA;AACvB,EAAA;AAEO,EAAA;AACH,IAAA;AAC2C,IAAA;AACJ,IAAA;AAC/B,IAAA;AACR,IAAA;AACoB,IAAA;AACxB,EAAA;AACJ;AAGmG;AAC1C,EAAA;AACiC,EAAA;AACF,EAAA;AACpD,EAAA;AAEzB,EAAA;AACoC,IAAA;AAClB,IAAA;AACP,IAAA;AACD,IAAA;AAC0B,IAAA;AACI,IAAA;AACF,IAAA;AACE,IAAA;AACF,IAAA;AAAA;AAE+B,IAAA;AAAA;AAEiB,IAAA;AACN,IAAA;AAC1C,IAAA;AACA,IAAA;AAAA;AAE3B,IAAA;AACD,IAAA;AACjB,EAAA;AACJ;AAG4E;AAGlE,EAAA;AACV;AAEoB;AAUV;AACoB,EAAA;AAAQ,IAAA;AAGxB,IAAA;AACV,EAAA;AAE8D,EAAA;AACjC,IAAA;AACO,IAAA;AAChB,IAAA;AACT,IAAA;AACV,EAAA;AACL;ADtT6L;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/node-sdk/node-sdk/dist/chunk-4M3ACBWX.cjs","sourcesContent":[null,"/**\n * One sentence per subscription situation, for the subscriber and for the\n * merchant, and what each of them can do about it: the canonical wording of\n * 2026-10-05, agreed with the backend. Show these instead of writing your\n * own: hand-written ones drift, and some are false (\"add funds\" on a removed\n * approval, \"nothing more is charged\" on a stopped subscription).\n *\n * Browser-safe: this module imports types only, so `@mesub/node/situations`\n * loads in a page, a widget or a docs site with nothing of Node.\n *\n * Templates carry `{placeholders}`. A `[segment]` holds a placeholder `/v1`\n * does not always serve: it is shown when every placeholder in it has a\n * value, and left out otherwise, so the sentence stays true without it.\n */\nimport type { AccessAnswer, BlockedReason, EndReason, LateReason, StopReason } from './answer.js';\nimport type { ServerSubscription } from './subscriptions.js';\n\n/** Who acts. */\nexport type SituationActor = 'subscriber' | 'merchant';\n\n/**\n * What can be done. `add_funds` has no route (the wallet does it).\n * `transaction` hands a pending checkout a fresh transaction to sign\n * (`subscriptions.transaction`, Mesub-io/backend#427). `retry` is\n * the subscriber's \"Pay now\" with the API key (`subscriptions.retry`,\n * Mesub-io/backend#354), and the merchant's on the Mesub dashboard; `upgrade`\n * exists on the dashboard only.\n */\nexport type SituationActionId =\n | 'subscribe'\n | 'transaction'\n | 'submit'\n | 'cancel'\n | 'resume'\n | 'close'\n | 'add_funds'\n | 'retry'\n | 'upgrade'\n | 'retrieve'\n | 'list';\n\n/** One action of a situation, as the table lists it. */\nexport interface SituationActionTemplate {\n readonly by: SituationActor;\n readonly action: SituationActionId;\n /** The method of `@mesub/node`, when there is one: `subscriptions.cancel`, … */\n readonly sdk?: string;\n /** The API route behind it. */\n readonly route?: string;\n /** On the Mesub dashboard only: an API key cannot call it. */\n readonly dashboard?: true;\n /** The sentence to show before the subscriber signs it, a key of `CONFIRMATIONS`. */\n readonly confirm?: ConfirmationKey;\n /** Only while the date in this field of the subscription is ahead. */\n readonly until?: 'access_until' | 'retry_deadline';\n /**\n * Only while the subscription is `closable` (Mesub-io/backend#402): left\n * out where Mesub says `closable: false`, its `closable_from` in the\n * sentence when a date is known.\n */\n readonly when?: 'closable';\n}\n\n/**\n * A sentence, or one per tier where the canonical sentence names both:\n * `retries` for Dev and Business (Mesub retries on its own), `free` for Free\n * (hand retries until `retry_deadline`).\n */\nexport type SituationText = string | { readonly retries: string; readonly free: string };\n\nexport interface Situation {\n readonly subscriber: SituationText;\n readonly merchant: SituationText;\n readonly actions: readonly SituationActionTemplate[];\n}\n\nconst SUBSCRIBE = {\n by: 'subscriber',\n action: 'subscribe',\n sdk: 'subscriptions.create',\n route: 'POST /v1/subscriptions',\n} as const;\nconst CANCEL = {\n by: 'subscriber',\n action: 'cancel',\n sdk: 'subscriptions.cancel',\n route: 'POST /v1/subscriptions/:id/cancel',\n} as const;\nconst RESUME = {\n by: 'subscriber',\n action: 'resume',\n sdk: 'subscriptions.resume',\n route: 'POST /v1/subscriptions/:id/resume',\n until: 'access_until',\n} as const;\nconst CLOSE = {\n by: 'subscriber',\n action: 'close',\n sdk: 'subscriptions.close',\n route: 'POST /v1/subscriptions/:id/close',\n confirm: 'confirm_close',\n when: 'closable',\n} as const;\n/** A close on a plan that is gone or over: nothing to subscribe to again after it. */\nconst CLOSE_OVER = { ...CLOSE, confirm: 'confirm_close_over' } as const;\nconst ADD_FUNDS = { by: 'subscriber', action: 'add_funds' } as const;\n/** Does not count as a retry, and doubles the wait before the next one. */\nconst RETRY = {\n by: 'merchant',\n action: 'retry',\n route: 'POST /merchant/subscriptions/:id/retry',\n dashboard: true,\n} as const;\n/**\n * The subscriber's own, shown as \"Pay now\" (Mesub-io/backend#354): the same\n * rules as the merchant's, nothing to sign, and refused first when it cannot\n * succeed (a short wallet, an approval gone), so no retry is spent for nothing.\n */\nconst PAY_NOW = {\n by: 'subscriber',\n action: 'retry',\n sdk: 'subscriptions.retry',\n route: 'POST /v1/subscriptions/:id/retry',\n} as const;\nconst UPGRADE = {\n by: 'merchant',\n action: 'upgrade',\n route: 'POST /projects/:id/upgrade',\n dashboard: true,\n} as const;\nconst CANCEL_PAID = { ...CANCEL, confirm: 'confirm_cancel_paid' } as const;\n\n/**\n * A block of the merchant's setup, as the subscriber reads it: one sentence for\n * all but a gone token, which no payment can lift.\n */\nconst BLOCKED_BY_SETUP =\n 'Mesub cannot take your payments for {plan_name} for now, because of how the plan is set up, not because of anything you did: there is nothing for you to do or sign, and Mesub keeps trying.[ You keep access until {access_until} if no payment goes through by then.] Access comes back on its own with the first payment that goes through.';\n/** The subscriber's sentence on a token its issuer paused or gave a hook: not the merchant's either. */\nconst BLOCKED_BY_TOKEN =\n 'Mesub cannot take your payments for {plan_name} for now, because of the token it is paid in, not because of anything you did: there is nothing for you to do or sign, and Mesub keeps trying.[ You keep access until {access_until} if no payment goes through by then.] Access comes back on its own with the first payment that goes through.';\n/** How a merchant's sentence on a block ends, whatever the cause but a gone token. */\nconst BLOCKED_ACCESS =\n '[ Access ends on {access_until} if nothing is collected by then.] Access comes back with the first payment that goes through, with nothing for the subscriber to sign.';\n/**\n * How both sentences on a gone token end: Mesub still tries, and no payment\n * can land on a token that no longer exists.\n */\nconst NEVER_BACK =\n 'No payment can go through on this plan again, so once access ends it does not come back.';\nconst CANCEL_BEHIND = { ...CANCEL, confirm: 'confirm_cancel_behind' } as const;\n\n/**\n * The canonical table, by situation key. The renewal checks\n * (`active_renewal_*`) are left out: Mesub serves them on its dashboard\n * only, never on `/v1`.\n */\nexport const SITUATIONS = {\n // Running\n none: {\n subscriber: 'You are not subscribed to {plan_name}.',\n merchant: 'This customer has no subscription to {plan_name}.',\n actions: [SUBSCRIBE],\n },\n active: {\n subscriber: 'Active. Your next payment of {amount} is on {next_charge_at}.',\n merchant: 'Active and paid up. Next payment of {amount} on {next_charge_at}.',\n actions: [CANCEL_PAID],\n },\n // The plan ends before another charge: the last period, paid in full.\n active_last_period: {\n subscriber:\n 'Active until {access_until}: {plan_name} is ending, and no further payment will be taken.',\n merchant:\n 'Active and paid up, in its last period: the plan is ending, so no further payment is taken. Access ends on {access_until}.',\n actions: [CANCEL_PAID],\n },\n active_unbilled: {\n subscriber: 'Active until {access_until}. No further payment is scheduled.',\n merchant:\n \"Not billed: this subscription's terms were never signed through Mesub, so Mesub never takes a payment for it. Access ends on {access_until}.\",\n actions: [],\n },\n\n // Late (`unpaid`)\n // The plan ends before the next retry: none is scheduled, whatever the reason.\n unpaid_last_period: {\n subscriber:\n 'Your payment of {amount} is late, and Mesub will not try again on its own: {plan_name} is ending. You keep access until {access_until}.',\n merchant:\n 'Payment late, with no retry ahead: the plan ends before the next one. Access continues until {access_until}, and the subscription ends with the plan.',\n actions: [PAY_NOW, CANCEL_BEHIND, RETRY],\n },\n unpaid_insufficient_balance_retries: {\n subscriber:\n 'Your payment of {amount} is late because your wallet held too little, and nothing was taken. Add {amount} to your wallet, then pay it now, with nothing to sign; otherwise Mesub tries again on {next_retry_at}. Your subscription stops if no payment goes through by {access_until}.',\n merchant:\n \"Payment late: the wallet was short. Retry[ {next_retry_number} of {retries_allowed}] on {next_retry_at}, or sooner by hand, yours or the subscriber's. Access continues until {access_until}; the subscription stops then if every retry fails.\",\n actions: [ADD_FUNDS, PAY_NOW, CANCEL_BEHIND, RETRY],\n },\n unpaid_insufficient_balance_free: {\n subscriber:\n 'Your payment of {amount} is late because your wallet held too little, so your access is off. Add {amount} to your wallet, then pay it now, with nothing to sign, before {retry_deadline}; if no payment goes through by then, the subscription stops.',\n merchant:\n 'Payment late: the wallet was short. On Free, Mesub does not retry on its own: you or the subscriber can retry by hand, up to 3 times between you, 10 minutes apart, until {retry_deadline}. Access is off meanwhile, and it stops after that.',\n actions: [\n ADD_FUNDS,\n { ...PAY_NOW, until: 'retry_deadline' },\n CANCEL_BEHIND,\n { ...RETRY, until: 'retry_deadline' },\n ],\n },\n unpaid_approval_revoked: {\n subscriber:\n \"Mesub could not take your payment of {amount}: your wallet no longer approves it (the approval was removed, or replaced by another app's). Adding funds will not fix this, and removing the approval did not cancel the subscription: Cancel it if you want to stop.\",\n merchant: {\n retries:\n \"Payment late: the subscriber's wallet no longer approves Mesub (removed in the wallet, or replaced by another app's approval). Every retry fails until it is back, and no Mesub route restores it today. It stops on {access_until}.\",\n free: \"Payment late: the subscriber's wallet no longer approves Mesub (removed in the wallet, or replaced by another app's approval). Every retry fails until it is back, and no Mesub route restores it today. It stops at {retry_deadline}.\",\n },\n // A retry by hand is accepted by the dashboard but fails, and the\n // subscriber's is refused (`retry_cannot_succeed`): neither is offered.\n actions: [CANCEL_BEHIND],\n },\n unpaid_authority_closed: {\n subscriber:\n \"Mesub can no longer take payments for this subscription: your wallet's approval for {token} was closed for good, so adding funds will not help. Cancel it, then close it once its period ends if you want to subscribe again.\",\n merchant: {\n retries:\n \"Payment late, for good: the subscriber's subscription authority for {token} was closed (or re-created), so no retry can succeed. It stops on {access_until}, unless the subscriber cancels first.\",\n free: \"Payment late, for good: the subscriber's subscription authority for {token} was closed (or re-created), so no retry can succeed. It stops at {retry_deadline}, unless the subscriber cancels first.\",\n },\n // Close, then subscribe, come once it is cancelled and its period ended.\n actions: [CANCEL_BEHIND],\n },\n unpaid_unknown: {\n subscriber:\n 'Your wallet refused the payment of {amount}, so nothing was taken. Check that it still holds {amount} in {token} and can send it, then pay it now, or Cancel to stop.',\n merchant:\n \"Payment late: the network refused the payment for a reason Mesub does not name (for example a frozen {token} account). See the last attempt's reason.\",\n actions: [PAY_NOW, CANCEL_BEHIND, RETRY],\n },\n\n // Blocked: every charge fails for a reason that is not the subscriber's\n // (Mesub-io/backend#398). The status does not move, access has an end.\n // Worded here from the API's own comments, after the table of 2026-10-05.\n blocked_mesub: {\n subscriber:\n \"Mesub cannot take your payments for {plan_name} for now, because of a problem on Mesub's side: there is nothing for you to do or sign, and Mesub keeps trying.[ You keep access until {access_until} if no payment goes through by then.] Access comes back on its own with the first payment that goes through.\",\n merchant: `Payments blocked[ since {blocked_since}] by a problem on Mesub's side: nothing to fix on yours, and none of it is the subscriber's doing. Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_puller_not_allowed: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: Mesub's puller was taken off the plan on chain, so Mesub can take no payment for it. Add Mesub back to the plan's pullers, with the wallet that owns the plan; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_receiver_not_allowed: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the wallet that receives its payments is not among the plan's destinations on chain. Choose one that is; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_receiver_account_missing: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the wallet that receives its payments has no token account for the plan's token, and nobody else on the plan can be paid. Open that account; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_receiver_account_frozen: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments is frozen, so it cannot take payments. Choose another receiving wallet, or ask the token's issuer to unfreeze that account; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_receiver_account_needs_memo: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments requires a memo on every payment in, which Mesub's charges do not carry. Turn that requirement off for the account, or choose another receiving wallet; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_receiver_account_confidential_only: {\n subscriber: BLOCKED_BY_SETUP,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the token account of the wallet that receives its payments takes confidential transfers only, and Mesub's charges are plain ones. Allow plain transfers into the account, or choose another receiving wallet; Mesub keeps trying.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n // The token issuer's to lift, not the merchant's (Mesub-io/backend#396).\n blocked_mint_paused: {\n subscriber: BLOCKED_BY_TOKEN,\n merchant: `Payments blocked[ since {blocked_since}] by the plan's token: its issuer paused it, so no payment in it can move. Only the issuer can lift the pause, and Mesub charges again once they do.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_mint_transfer_hook: {\n subscriber: BLOCKED_BY_TOKEN,\n merchant: `Payments blocked[ since {blocked_since}] by the plan's token: its issuer made every transfer of it run their own program, which Mesub does not support yet. Mesub charges again if the issuer removes it; otherwise, create a new plan with another token.${BLOCKED_ACCESS}`,\n actions: [CANCEL],\n },\n blocked_mint_gone: {\n subscriber: `Mesub can no longer take your payments for {plan_name}: the token it is paid in no longer exists. This is not because of anything you did, and there is nothing for you to do or sign.[ You keep access until {access_until}.] ${NEVER_BACK}`,\n merchant: `Payments blocked[ since {blocked_since}] by your plan's setup: the plan's token no longer exists on chain, so nothing can be collected on it. Create a new plan with another token.[ Access ends on {access_until}.] ${NEVER_BACK}`,\n actions: [CANCEL],\n },\n\n // Cancelled\n cancelled_with_access: {\n subscriber:\n 'Cancelled: nothing more will be taken, and you keep access until {access_until}. Resume before then to keep it going.',\n merchant:\n 'Cancelled[ on {cancelled_at}]: access runs until {access_until}, and nothing more will be taken.',\n actions: [{ ...RESUME, confirm: 'confirm_resume' }],\n },\n cancelled_no_access: {\n subscriber:\n 'Cancelled. Your last payment was missed, so you have no access, and Mesub will not collect it; you can close the subscription[ from {closable_from}] to get your deposit back.',\n merchant:\n 'Cancelled while behind[ on {cancelled_at}]: no access, and Mesub will not collect the missed payment. The subscriber can close it[ from {closable_from}], and must do so before subscribing again.',\n // Not resume (refused) and not subscribe (refused until it is closed).\n actions: [CLOSE],\n },\n cancelled_ended: {\n subscriber:\n 'This subscription has ended. Close it to get your deposit back; you need to close it before subscribing to {plan_name} again.',\n merchant:\n \"Ended by the subscriber's cancellation. Its subscription account stays open until the subscriber closes it, which they must do before subscribing again.\",\n actions: [CLOSE],\n },\n\n // Stopped\n stopped: {\n subscriber:\n \"Your subscription stopped after missed payments, and your access has ended; Mesub will not take payments for it again. Its subscription account stays open on your wallet, so the plan's owner could still charge it: Cancel if you are not coming back.\",\n // `{stop_cause}` from `stop_reason` (`STOP_CAUSES`), left out while it is null.\n merchant:\n \"Stopped[: {stop_cause}]. Mesub takes nothing more and access is off. The subscription account stays open on chain, and you, as the plan's owner, could still pull from it, until the subscriber cancels and closes it. The subscriber can come back over it.\",\n // Subscribing again restarts it, while it is not cancelled on chain,\n // the plan's terms are unchanged and the project has a free seat. A\n // close only once Mesub says it is `closable`: cancelled on chain\n // without Mesub, and past its end.\n actions: [CANCEL_BEHIND, SUBSCRIBE, CLOSE],\n },\n // The wallet closed its subscription authority for good (Mesub-io/backend#394,\n // #402): nobody can charge it again, and it cannot be restarted. Worded\n // from the stopped emails' `authority-closed` variants.\n stopped_authority_closed: {\n subscriber:\n \"Your wallet's approval for {token} was closed for good, so no payment can go through for this subscription any more: it has stopped, your access is over, and it cannot be restarted. Close it to get your deposit back; you can then subscribe to {plan_name} again.\",\n merchant:\n \"Stopped for good: the subscriber's wallet approval for {token} was closed, so nobody can take payments from this subscription any more, you as the plan's owner included, and the subscriber cannot come back over it. They can close it at once to get their deposit back.\",\n actions: [CLOSE],\n },\n\n // Paused\n paused: {\n subscriber:\n 'Paused by {merchant_name}: nothing will be taken, and you keep access until {access_until}.',\n merchant:\n \"Paused: your project is over its tier's subscriber limit, so Mesub takes nothing from this subscriber. Access runs to the end of the paid period ({access_until}). Move back up a tier to restart payments.\",\n actions: [CANCEL_PAID, UPGRADE],\n },\n paused_access_over: {\n subscriber:\n 'Paused by {merchant_name}: nothing is taken, and your access has ended. If {merchant_name} unpauses it, a payment is due at once; Cancel to stop for good.',\n merchant:\n 'Paused over your tier limit, and its paid period is over, so access is off. Moving back up a tier makes a payment due at once.',\n actions: [CANCEL, UPGRADE],\n },\n paused_cancelled: {\n subscriber:\n 'Cancelled: nothing more will be taken, and you keep access until {access_until}. You can resume before then, but it stays paused, with no payment, until {merchant_name} unpauses it.',\n merchant: 'Cancelled while paused over your tier limit; access until {access_until}.',\n actions: [{ ...RESUME, confirm: 'confirm_resume_paused' }],\n },\n\n // Ended\n ended_closed: {\n subscriber:\n 'You closed this subscription, and its deposit is back in your wallet. You can subscribe to {plan_name} again at any time.',\n merchant:\n 'Ended: the subscriber closed it and got the deposit back. They can subscribe again.',\n actions: [SUBSCRIBE],\n },\n ended_authority_closed: {\n subscriber:\n \"This subscription's account was closed outside Mesub, so it has ended and nothing more can be taken. You can subscribe to {plan_name} again.\",\n merchant:\n 'Ended: its subscription account was found closed on chain, without Mesub. The subscriber can subscribe again.',\n actions: [SUBSCRIBE],\n },\n ended_plan_removed: {\n subscriber:\n '{merchant_name} removed {plan_name}, so your subscription has ended and nothing more will be taken.',\n // Closed through Mesub since Mesub-io/backend#344.\n merchant:\n \"Ended: the plan account was deleted. The subscriber's subscription account may still hold their deposit, which they get back by closing it.\",\n actions: [CLOSE_OVER],\n },\n ended_plan_replaced: {\n subscriber:\n '{merchant_name} replaced {plan_name} with another plan, so this subscription has ended and nothing more will be taken.',\n merchant:\n \"Ended: another plan now stands at this plan's address, and this subscription was never signed for it.\",\n actions: [CLOSE_OVER],\n },\n ended_plan_ended: {\n // `{plan_ends_at}` is the plan's `ends_at`, not on a subscription.\n subscriber:\n '{plan_name} reached its end date[ ({plan_ends_at})], so your subscription has ended and nothing more will be taken.',\n merchant: 'Ended: the plan passed its end date.',\n // Also read off a running row in the minutes before Mesub ends it,\n // which is not `closable` yet: the close shows once it is.\n actions: [CLOSE_OVER],\n },\n ended_unknown: {\n subscriber: 'This subscription has ended, and nothing more will be taken.',\n merchant: 'Ended (before Mesub recorded reasons).',\n actions: [],\n },\n\n // Checkout and history\n pending: {\n subscriber:\n 'Not signed yet. Approve it in your wallet to pay the first {amount} and start the subscription.',\n merchant: 'Checkout started, not signed yet: nothing is on chain and nothing was charged.',\n // A fresh transaction when the wallet is ready, or after `transaction_expired`.\n actions: [\n {\n by: 'merchant',\n action: 'transaction',\n sdk: 'subscriptions.transaction',\n route: 'POST /v1/subscriptions/:id/transaction',\n },\n {\n by: 'merchant',\n action: 'submit',\n sdk: 'subscriptions.submit',\n route: 'POST /v1/subscriptions/:id/submit',\n },\n ],\n },\n expired: {\n subscriber:\n 'This checkout was never signed, so nothing was charged. Subscribe again to start.',\n merchant: 'Checkout expired unsigned. Nothing on chain.',\n actions: [SUBSCRIBE],\n },\n failed: {\n subscriber:\n 'This subscription did not start: what reached the network was not the transaction prepared for it. If money left your wallet for it, contact {merchant_name}.',\n merchant:\n \"Failed: what landed on chain is not what Mesub built (see the submit reason). Check the wallet's transactions before creating a new one.\",\n actions: [\n {\n by: 'merchant',\n action: 'retrieve',\n sdk: 'subscriptions.retrieve',\n route: 'GET /v1/subscriptions/:id',\n },\n SUBSCRIBE,\n ],\n },\n superseded: {\n // Hide it from subscribers: the newer row is the one in force.\n subscriber: 'Replaced by your newer subscription to {plan_name}.',\n // `{replaced_at}`, when the newer row was created, is not on this row.\n merchant:\n 'An earlier subscription of this wallet, replaced when it subscribed again[ on {replaced_at}]. Read the current one.',\n actions: [\n {\n by: 'merchant',\n action: 'list',\n sdk: 'subscriptions.list',\n route: 'GET /v1/subscriptions',\n },\n ],\n },\n} as const satisfies Record<string, Situation>;\n\n/** The sentences to show before the subscriber signs an action. */\nexport const CONFIRMATIONS = {\n confirm_cancel_paid:\n 'Cancel {plan_name}? Nothing more will be taken, and you keep access until {access_until}.',\n confirm_cancel_behind:\n 'Cancel {plan_name}? Your access ends now and Mesub will not collect the missed payment; you can close it once the current period ends.',\n confirm_resume:\n 'Resume {plan_name}? Nothing is taken now; your next payment of {amount} is on {access_until}.',\n confirm_resume_paused:\n 'Resume {plan_name}? Nothing is taken now, and nothing will be until {merchant_name} unpauses it.',\n confirm_close:\n 'Close {plan_name}? This deletes its subscription account and returns its deposit to your wallet; you can then subscribe again.',\n confirm_close_over:\n 'Close {plan_name}? This deletes its subscription account and returns its deposit to your wallet.',\n} as const;\n\n/** Shown beside every sentence, which therefore need not repeat it. */\nexport const ACCESS_LINE = { granted: 'Access until {access_until}', denied: 'No access' } as const;\n\nexport type ConfirmationKey = keyof typeof CONFIRMATIONS;\n\n/**\n * A situation of the table, or `unknown`: a status or a reason newer than\n * this release, whose sentences are the access line, true of any situation.\n */\nexport type SituationKey = keyof typeof SITUATIONS | 'unknown';\n\nexport interface ExplainOptions {\n /** What \"now\" is, for actions open until a date. Defaults to the clock. */\n now?: Date;\n /** What neither `/v1/access` nor a subscription carries, as you show it. */\n names?: {\n /** `{plan_name}`. Defaults to the plan's slug. */\n plan?: string;\n /** `{merchant_name}`: your name, as subscribers know it. */\n merchant?: string;\n /** `{amount}`: the plan's amount with its symbol, such as `9.99 USDC`. */\n amount?: string;\n /** `{token}`: the plan's token symbol, such as `USDC`. */\n token?: string;\n };\n /**\n * How a date is written. Defaults to the ISO 8601 string as Mesub serves\n * it, the same in every locale: pass your own to show local time.\n */\n formatDate?: (iso: string) => string;\n}\n\nexport interface ExplainedAction {\n by: SituationActor;\n action: SituationActionId;\n sdk?: string;\n route?: string;\n dashboard?: true;\n /** Until when it can be done, as served (ISO 8601). */\n until?: string;\n /** The sentence to show before the subscriber signs it, filled. */\n confirm?: string;\n}\n\nexport interface Explanation {\n key: SituationKey;\n /** For the subscriber, filled. */\n subscriber: string;\n /** For the merchant, filled. */\n merchant: string;\n /** \"Access until …\" or \"No access\", to show beside either sentence. */\n access: string;\n /** What can be done now, by whom. */\n actions: ExplainedAction[];\n /**\n * Placeholders left as `{name}` in a sentence, since nothing gave them a\n * value: pass them in `options.names`, or check the answer's fields.\n */\n missing: string[];\n}\n\n/** What `explain` reads: an answer of `/v1/access` or a subscription. */\nexport type Explainable = AccessAnswer | ServerSubscription;\n\n/**\n * `closed_elsewhere` keeps the situation `authority_closed` had before\n * Mesub-io/backend#476 renamed it, id included: both read as one.\n */\nconst END_REASONS: Record<EndReason, keyof typeof SITUATIONS> = {\n cancelled: 'cancelled_ended',\n closed: 'ended_closed',\n closed_elsewhere: 'ended_authority_closed',\n authority_closed: 'ended_authority_closed',\n plan_removed: 'ended_plan_removed',\n plan_replaced: 'ended_plan_replaced',\n plan_ended: 'ended_plan_ended',\n};\n\nconst LATE_REASONS: Record<Exclude<LateReason, 'insufficient_balance'>, keyof typeof SITUATIONS> = {\n approval_revoked: 'unpaid_approval_revoked',\n authority_closed: 'unpaid_authority_closed',\n};\n\n/**\n * What `{stop_cause}` reads for each reason that leaves the subscription to\n * come back over. `authority_closed` has a situation of its own.\n */\nconst STOP_CAUSES: Record<Exclude<StopReason, 'authority_closed'>, string> = {\n out_of_retries: 'every retry of the missed payment failed',\n retry_deadline_passed: 'the last day to pay the missed payment passed',\n};\n\nconst BLOCKED_REASONS: Record<BlockedReason, keyof typeof SITUATIONS> = {\n mesub: 'blocked_mesub',\n puller_not_allowed: 'blocked_puller_not_allowed',\n receiver_not_allowed: 'blocked_receiver_not_allowed',\n receiver_account_missing: 'blocked_receiver_account_missing',\n receiver_account_frozen: 'blocked_receiver_account_frozen',\n receiver_account_needs_memo: 'blocked_receiver_account_needs_memo',\n receiver_account_confidential_only: 'blocked_receiver_account_confidential_only',\n mint_gone: 'blocked_mint_gone',\n mint_paused: 'blocked_mint_paused',\n mint_transfer_hook: 'blocked_mint_transfer_hook',\n};\n\n/** A key that is its status, whatever else the answer says. */\nconst BY_STATUS = new Set(['none', 'pending', 'expired', 'failed', 'superseded']);\n\n/** Free is read as a `retry_deadline` set: `/v1` does not serve the tier. */\nfunction isFree(input: Explainable): boolean {\n return input.retry_deadline != null;\n}\n\n/** Billed and running: neither parked nor one Mesub never pulls, which `payment_status` says. */\nfunction isBilled(input: Explainable): boolean {\n return input.payment_status !== 'none';\n}\n\n/**\n * Which situation an answer is in, in the canonical order: status first,\n * then paused, then blocked, then the end or late reason. Never throws: a\n * status or a reason this release does not know is `unknown`.\n *\n * A plan's end is read from what it leaves on the answer, which carries no\n * `ends_at`: access with no charge or retry ahead is the last period, and a\n * running status without access is the minutes between the end and the job\n * that ends the row. That one reads as ended already.\n */\nexport function situationOf(input: Explainable): SituationKey {\n const status: string = input.status;\n const paused = input.paused === true;\n\n if (BY_STATUS.has(status)) return status as SituationKey;\n\n if (paused && (status === 'active' || status === 'unpaid' || status === 'cancelled')) {\n if (status === 'cancelled') return 'paused_cancelled';\n return input.access ? 'paused' : 'paused_access_over';\n }\n\n // Before the plan's end is read off a running row without access: a\n // blocked one loses its access too, and has not ended.\n const blocked: string | null = input.blocked_reason ?? null;\n if (blocked !== null && (status === 'active' || status === 'unpaid')) {\n return Object.hasOwn(BLOCKED_REASONS, blocked)\n ? BLOCKED_REASONS[blocked as BlockedReason]\n : 'unknown';\n }\n\n switch (status) {\n case 'ended': {\n const reason: string | null = input.end_reason ?? null;\n if (reason === null) return 'ended_unknown';\n return Object.hasOwn(END_REASONS, reason)\n ? END_REASONS[reason as EndReason]\n : 'unknown';\n }\n case 'cancelled':\n if (input.access) return 'cancelled_with_access';\n // Paid up and cut short by the plan's end: it ends as cancelled.\n return input.payment_status === 'paid' ? 'cancelled_ended' : 'cancelled_no_access';\n case 'unpaid': {\n const reason: string | null = input.late_reason ?? null;\n const named = reason !== null && reason !== 'insufficient_balance';\n if (named && !Object.hasOwn(LATE_REASONS, reason)) return 'unknown';\n // On Free a late one has no access and no retry of Mesub's, end or not.\n if (!isFree(input) && isBilled(input) && input.next_retry_at == null) {\n if (!input.access) return 'ended_plan_ended';\n if (input.access_until != null) return 'unpaid_last_period';\n }\n if (reason === null) return 'unpaid_unknown';\n if (reason === 'insufficient_balance') {\n return isFree(input)\n ? 'unpaid_insufficient_balance_free'\n : 'unpaid_insufficient_balance_retries';\n }\n return LATE_REASONS[reason as keyof typeof LATE_REASONS];\n }\n case 'active':\n if (!isBilled(input)) return 'active_unbilled';\n if (!input.access) return 'ended_plan_ended';\n return input.access_until != null && input.next_charge_at == null\n ? 'active_last_period'\n : 'active';\n case 'stopped': {\n const reason: string | null = input.stop_reason ?? null;\n if (reason === 'authority_closed') return 'stopped_authority_closed';\n // A reason newer than this release may not leave it to come back over.\n return reason === null || Object.hasOwn(STOP_CAUSES, reason) ? 'stopped' : 'unknown';\n }\n default:\n return 'unknown';\n }\n}\n\n/**\n * The situation of a subscription, in words: one sentence for the\n * subscriber, one for the merchant, the access line, and what each can do\n * now. Takes an answer of `/v1/access` or a subscription as `@mesub/node`\n * returns them. Never throws.\n *\n * ```ts\n * const { subscriber, access, actions } = explain(answer, {\n * names: { plan: 'Pro', merchant: 'Acme', amount: '9.99 USDC', token: 'USDC' },\n * });\n * ```\n */\nexport function explain(input: Explainable, options: ExplainOptions = {}): Explanation {\n const key = situationOf(input);\n const values = valuesOf(input, options);\n const missing = new Set<string>();\n const fill = (template: string): string => render(template, values, missing);\n const accessLine = fill(input.access ? ACCESS_LINE.granted : ACCESS_LINE.denied);\n\n if (key === 'unknown') {\n return {\n key,\n subscriber: accessLine,\n merchant: accessLine,\n access: accessLine,\n actions: [],\n missing: [...missing],\n };\n }\n\n const situation: Situation = SITUATIONS[key];\n const tier = isFree(input) ? 'free' : 'retries';\n const pick = (text: SituationText): string => (typeof text === 'string' ? text : text[tier]);\n const now = (options.now ?? new Date()).getTime();\n const actions: ExplainedAction[] = [];\n\n for (const template of situation.actions) {\n const { until: field, confirm, when, ...rest } = template;\n const action: ExplainedAction = { ...rest };\n\n // Undefined only on an answer cached by an older SDK: offered, as the table says.\n if (when === 'closable' && input.closable === false) continue;\n\n if (field !== undefined) {\n const until = input[field];\n if (until != null) {\n if (Date.parse(until) <= now) continue;\n action.until = until;\n }\n }\n if (confirm !== undefined) action.confirm = fill(CONFIRMATIONS[confirm]);\n actions.push(action);\n }\n\n return {\n key,\n subscriber: fill(pick(situation.subscriber)),\n merchant: fill(pick(situation.merchant)),\n access: accessLine,\n actions,\n missing: [...missing],\n };\n}\n\n/** Every value a placeholder can take from this answer and these options. */\nfunction valuesOf(input: Explainable, options: ExplainOptions): Record<string, string | undefined> {\n const date = options.formatDate ?? ((iso: string) => iso);\n const asDate = (iso: string | null | undefined) => (iso == null ? undefined : date(iso));\n const asNumber = (n: number | null | undefined) => (n == null ? undefined : String(n));\n const names = options.names ?? {};\n\n return {\n plan_name: names.plan ?? input.plan ?? undefined,\n merchant_name: names.merchant,\n amount: names.amount,\n token: names.token,\n access_until: asDate(input.access_until),\n next_charge_at: asDate(input.next_charge_at),\n next_retry_at: asDate(input.next_retry_at),\n retry_deadline: asDate(input.retry_deadline),\n blocked_since: asDate(input.blocked_since),\n // `/v1/access` only.\n cancelled_at: asDate('cancelled_at' in input ? input.cancelled_at : null),\n // On a subscription only.\n next_retry_number: asNumber('next_retry_number' in input ? input.next_retry_number : null),\n retries_allowed: asNumber('retries_allowed' in input ? input.retries_allowed : null),\n closable_from: asDate(input.closable_from),\n stop_cause: stopCauseOf(input.stop_reason),\n // Not served on `/v1` (yet): their segments are left out.\n plan_ends_at: undefined,\n replaced_at: undefined,\n };\n}\n\n/** The words of a stop reason this release names, undefined for any other. */\nfunction stopCauseOf(reason: string | null | undefined): string | undefined {\n return reason != null && Object.hasOwn(STOP_CAUSES, reason)\n ? STOP_CAUSES[reason as keyof typeof STOP_CAUSES]\n : undefined;\n}\n\nconst PLACEHOLDER = /\\{([a-z_]+)\\}/g;\n\n/**\n * Fills a template: a `[segment]` with a placeholder lacking a value is left\n * out, and a placeholder outside one is kept as `{name}` and noted missing.\n */\nfunction render(\n template: string,\n values: Record<string, string | undefined>,\n missing: Set<string>,\n): string {\n const segments = template.replace(/\\[([^[\\]]*)\\]/g, (_, segment: string) =>\n [...segment.matchAll(PLACEHOLDER)].every(([, name]) => values[name!] !== undefined)\n ? segment\n : '',\n );\n\n return segments.replace(PLACEHOLDER, (whole, name: string) => {\n const value = values[name];\n if (value !== undefined) return value;\n missing.add(name);\n return whole;\n });\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
3
 
4
- var _chunkSFK52JDWcjs = require('./chunk-SFK52JDW.cjs');
4
+ var _chunk5THDILDDcjs = require('./chunk-5THDILDD.cjs');
5
5
 
6
6
  // src/routes.ts
7
7
  var MAX_BODY_BYTES = 64 * 1024;
@@ -79,7 +79,7 @@ async function upcomingOf(client, subscription, read) {
79
79
  try {
80
80
  price = await planOf(client, plan, read);
81
81
  } catch (error) {
82
- if (!(error instanceof _chunkSFK52JDWcjs.MesubError) || brokenIntegration(error)) throw error;
82
+ if (!(error instanceof _chunk5THDILDDcjs.MesubError) || brokenIntegration(error)) throw error;
83
83
  }
84
84
  }
85
85
  const retry = next.kind === "retry";
@@ -134,7 +134,7 @@ async function paymentsOf(client, subscription, read) {
134
134
  const page = await client.subscriptions.attempts(subscription.id, {}, read);
135
135
  return { payments: page.data.map(payment), paid: page.paid, payments_error: null };
136
136
  } catch (error) {
137
- if (!(error instanceof _chunkSFK52JDWcjs.MesubError) || brokenIntegration(error)) throw error;
137
+ if (!(error instanceof _chunk5THDILDDcjs.MesubError) || brokenIntegration(error)) throw error;
138
138
  if (error.status === 404 && error.apiCode === null) {
139
139
  return olderPaymentsOf(client, subscription, read);
140
140
  }
@@ -165,7 +165,7 @@ async function olderPaymentsOf(client, subscription, read) {
165
165
  (_nullishCoalesce(answer.attempts, () => ( []))).map(olderPayment).sort((a, b) => Date.parse(b.attempted_at) - Date.parse(a.attempted_at))
166
166
  );
167
167
  } catch (error) {
168
- if (!(error instanceof _chunkSFK52JDWcjs.MesubError) || brokenIntegration(error)) throw error;
168
+ if (!(error instanceof _chunk5THDILDDcjs.MesubError) || brokenIntegration(error)) throw error;
169
169
  return unlisted(_nullishCoalesce(error.apiCode, () => ( error.code)), said(error));
170
170
  }
171
171
  }
@@ -295,7 +295,7 @@ async function handleWidget(client, request, asked, options = {}) {
295
295
  body: { ...confirmed, subscription: shown(confirmed.subscription) }
296
296
  };
297
297
  } catch (error) {
298
- if (!(error instanceof _chunkSFK52JDWcjs.MesubError) || brokenIntegration(error)) throw error;
298
+ if (!(error instanceof _chunk5THDILDDcjs.MesubError) || brokenIntegration(error)) throw error;
299
299
  if (error.apiCode === "subscription_not_found") return NO_SUBSCRIPTION();
300
300
  return fromMesub(error);
301
301
  }
@@ -312,7 +312,7 @@ function customerParam(asked) {
312
312
  async function widgetCustomer(request, customer) {
313
313
  const named = await customer(request);
314
314
  if (named === null || named === void 0) return null;
315
- const asked = _chunkSFK52JDWcjs.customerOf.call(void 0, named);
315
+ const asked = _chunk5THDILDDcjs.customerOf.call(void 0, named);
316
316
  if (asked.value === "") {
317
317
  throw new TypeError(
318
318
  "`customer` returned an empty value: return null when nobody is signed in."
@@ -337,4 +337,4 @@ function checkWidgetOptions(options) {
337
337
 
338
338
 
339
339
  exports.MAX_BODY_BYTES = MAX_BODY_BYTES; exports.handleWidget = handleWidget; exports.widgetCustomer = widgetCustomer; exports.checkWidgetOptions = checkWidgetOptions;
340
- //# sourceMappingURL=chunk-D3MM42ZL.cjs.map
340
+ //# sourceMappingURL=chunk-5LDAYKUA.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/node-sdk/node-sdk/dist/chunk-D3MM42ZL.cjs","../src/routes.ts"],"names":["id","subscription","action"],"mappings":"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","file":"/home/runner/work/node-sdk/node-sdk/dist/chunk-D3MM42ZL.cjs","sourcesContent":[null,"import type { Customer, ServedAttempt } from './answer.js';\nimport type { Mesub } from './client.js';\nimport { type Asked, customerOf } from './customer.js';\nimport { MesubError } from './errors.js';\nimport type { Plan } from './plans.js';\nimport type { ServerSubscription, SubscriptionAttempt } from './subscriptions.js';\nimport type { CallOptions } from './transport.js';\n\n/**\n * The routes `@mesub/react` calls on your own server, so the browser never\n * talks to Mesub nor holds a key: a plan to show, the customer's\n * subscriptions, one of them with its payments, and each step of\n * subscribing (a fresh transaction to sign included), cancelling, resuming,\n * closing and paying a late payment now. One function, `handleWidget`; the\n * Express and Next entries only carry a request to it.\n *\n * Who is asking comes from `customer`, your own verified auth, as on the\n * guards. A subscription that is not that customer's is answered 404, the\n * same as one that does not exist.\n */\n\n/** Who is asking, from your own auth. Null when nobody is signed in, a 401. */\nexport type WidgetCustomer<Req> = (\n request: Req,\n) => Customer | string | null | undefined | Promise<Customer | string | null | undefined>;\n\nexport interface WidgetRoutesOptions<Req> {\n /** Defaults to one client built from MESUB_API_KEY. */\n client?: Mesub;\n /**\n * Who is asking, from a session you verified, never from the request\n * itself: `(req) => ({ external_id: req.user.id })`, a wallet or an email.\n * Every subscription created is tied to it, and only its own are reached.\n */\n customer: WidgetCustomer<Req>;\n /**\n * Where that customer's notices go, when `customer` is not an email:\n * `(req) => req.user.email`. Not verified by Mesub.\n */\n email?: (request: Req) => string | null | undefined | Promise<string | null | undefined>;\n /**\n * The plans the widget may subscribe to. Any plan of your project by\n * default; name them to keep one off your site.\n */\n plans?: readonly string[];\n}\n\n/** A request as the core reads it, whatever the framework. */\nexport interface WidgetRequest {\n method: string;\n /** After the mount point: `/subscriptions/sub_1/cancel`. */\n path: string;\n /** Parsed JSON, or undefined when there was none. */\n body: unknown;\n /** The request's `Content-Type`, to refuse a POST a plain form could send. */\n contentType: string | null;\n}\n\n/** What the core answers: a status and a JSON body. */\nexport interface WidgetResponse {\n status: number;\n body: unknown;\n headers?: Record<string, string>;\n}\n\n/** The most a widget request's body may weigh: a signed transaction is under 2 kB. */\nexport const MAX_BODY_BYTES = 64 * 1024;\n\nconst ACTIONS = ['cancel', 'resume', 'close'] as const;\ntype Action = (typeof ACTIONS)[number];\n\n/** An id as Mesub writes them: nothing that could rewrite the path. */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n/** A plan slug as Mesub writes them: with at least one letter, never a number. */\nconst SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * How long one widget request gives Mesub for all its reads, in milliseconds:\n * a browser is waiting, and a detail or a list makes several calls.\n */\nconst WIDGET_READ_TIME = 10_000;\n\n/**\n * How every read of one request is made, as a guard's: cut at one deadline,\n * and a 429 handed on at once, never waited out. The client's methods pass\n * their options to the transport whole, which is how these reach it.\n */\nfunction bounded(): CallOptions {\n return { deadline: Date.now() + WIDGET_READ_TIME, retryRateLimited: false };\n}\n\nfunction refusal(status: number, code: string, message: string): WidgetResponse {\n return { status, body: { error: { code, message } } };\n}\n\nconst NOT_FOUND = () => refusal(404, 'not_found', 'Nothing here.');\nconst NO_SUBSCRIPTION = () => refusal(404, 'subscription_not_found', 'No such subscription.');\n\n/** Said to the browser for an error Mesub did not word itself. */\nconst NO_ANSWER = 'Mesub could not answer this request.';\n\n/** Mesub's own words only: the transport's may name `baseUrl` or a network error. */\nfunction said(error: MesubError): string {\n return error.apiCode === null ? NO_ANSWER : error.message;\n}\n\n/**\n * Whether Mesub refused your server, not what the customer did: any 401,\n * which Mesub answers only to an API key missing, never issued or rotated\n * since (`missing_api_key`, `invalid_api_key`), or a 403 that names none of\n * Mesub's own codes, from something in front of Mesub turning your server\n * away. Handed on, a 401 reads to `@mesub/react` as \"nobody is signed in\",\n * and every visitor gets the sign-in screen: thrown instead, a broken\n * integration, for the framework to log and answer 500.\n */\nfunction brokenIntegration(error: MesubError): boolean {\n if (error.status === 401) return true;\n\n return error.status === 403 && (error.apiCode === null || error.apiCode === 'forbidden');\n}\n\n/** A Mesub refusal, handed on with its own status and code, and nothing of the key. */\nfunction fromMesub(error: MesubError): WidgetResponse {\n // No answer from Mesub, or a 2xx this SDK cannot read: a 502 of yours.\n const status = error.status === null || error.status < 400 ? 502 : error.status;\n const retry =\n error.retryAfter === null\n ? undefined\n : { 'Retry-After': String(Math.ceil(error.retryAfter / 1000)) };\n\n return {\n status,\n body: { error: { code: error.apiCode ?? error.code, message: said(error) } },\n ...(retry && { headers: retry }),\n };\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\nfunction fieldsOf(body: unknown): Record<string, unknown> | null {\n return typeof body === 'object' && body !== null && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null;\n}\n\n/** Whether that subscription is the customer's: by the same identifier it was asked with. */\nfunction isTheirs(subscription: ServerSubscription, asked: Asked): boolean {\n if (asked.kind === 'wallet') return subscription.wallet === asked.value;\n if (asked.kind === 'external_id') return subscription.external_id === asked.value;\n\n return subscription.email === asked.value;\n}\n\n/** What a browser gets of a subscription: the merchant's own id and email stay on the server. */\nfunction shown(subscription: ServerSubscription) {\n const { email: _email, external_id: _externalId, ...rest } = subscription;\n\n return rest;\n}\n\n/** `GET /subscriptions` reads pages of 100, Mesub's largest, and 5 at most. */\nconst LIST_PAGE = 100;\nconst LIST_MAX_PAGES = 5;\n\n/** What `GET /subscriptions` answers. */\nexport interface WidgetSubscriptionList {\n /** Newest first, expired checkouts included: the 500 newest at most. */\n subscriptions: Array<Omit<ServerSubscription, 'email' | 'external_id'>>;\n /** True when the customer has more than those, which are not read. */\n has_more: boolean;\n}\n\n/** One pull attempt as the browser gets it: the fields Mesub serves, and no other. */\nexport interface WidgetPayment {\n attempted_at: string;\n /** `paid`, `skipped`, `rejected` or `blocked` today; a newer one is handed on as is. */\n outcome: string;\n /** What was asked for, in the mint's smallest unit; on a paid one, what was paid. */\n amount: string;\n /**\n * That amount in dollars when it was attempted, a decimal string. Null\n * until Mesub priced it, and from a Mesub that does not say.\n */\n amount_usd: string | null;\n /**\n * Mesub's short reason, null on a paid one, as on `ServedAttempt`: snake\n * case, an open string with the `program_${number}` and\n * `solana_${number}` families.\n */\n reason: string | null;\n /** The transaction that paid. Null on every outcome but `paid`. */\n signature: string | null;\n /** A retry of a missed charge. Null from a Mesub that does not say. */\n retry: boolean | null;\n /**\n * Which retry and out of how many, as Mesub recorded them when it ran:\n * 2 and 3 for \"retry 2 / 3\". Null when it is no retry of the plan's, or\n * Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n /** The start of the period it was for. Null when Mesub recorded none or does not say. */\n period_start: string | null;\n}\n\n/** What `GET /subscriptions/:id` answers. */\nexport interface WidgetSubscriptionDetail {\n subscription: Omit<ServerSubscription, 'email' | 'external_id'>;\n /** What Mesub pulls next, soonest first: one entry at most, none when nothing is due. */\n upcoming: WidgetUpcoming[];\n /**\n * This subscription's own pull attempts, newest first: the first page,\n * twenty at most, so not always its full history. Null when they could\n * not be read: `payments_error` says why.\n */\n payments: WidgetPayment[] | null;\n /**\n * What it paid since it began, counted by Mesub over all its attempts,\n * not over `payments`: how many were paid, and their sum in the mint's\n * smallest unit. Null when the read failed, and from a Mesub that does\n * not serve it: never summed here.\n */\n paid: { count: number; amount: string } | null;\n /** Why `payments` is null; null otherwise. */\n payments_error: { code: string; message: string } | null;\n}\n\n/** The one pull Mesub announces next for a subscription. */\nexport interface WidgetUpcoming {\n /** `charge` at the due date of a running one, `retry` of a missed one. */\n kind: 'charge' | 'retry';\n due_at: string;\n /**\n * The plan's price in the mint's smallest unit, and as a person counts\n * it. Null when the plan could not be read, never guessed.\n */\n amount: string | null;\n amount_display: string | null;\n /**\n * The plan's amount in dollars at the latest price Mesub knows, a decimal\n * string such as `\"9.99\"`: an estimate to show beside the amount, never\n * what is charged. Null when Mesub knows no price, and when the plan could\n * not be read.\n */\n amount_usd: string | null;\n /**\n * On a `retry`, which one it will be and out of how many: 2 and 3 for\n * \"next try 2 / 3\". Null on a `charge`, and when Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n}\n\n/** How long the plan list read for the widget is kept. */\nconst PLAN_TTL_MS = 60_000;\n\ninterface PlanList {\n /** Shared while the request is out. */\n plans: Promise<Plan[]>;\n /** Infinity until Mesub answered. */\n until: number;\n}\n\n// Per client, so two projects never share a plan.\nconst planLists = new WeakMap<Mesub, PlanList>();\n\n/**\n * A plan for the widget routes, from the project's list: public reads must\n * not spend the API key's rate limit, so one call per TTL whatever the slugs,\n * and null with no call for a slug that is not in it. A failed read is\n * forgotten at once.\n */\nasync function planOf(client: Mesub, slug: string, read: CallOptions): Promise<Plan | null> {\n let list = planLists.get(client);\n\n if (!list || list.until <= Date.now()) {\n // Shared while in flight: bound by the first asker's deadline, as a guard's flight.\n const fresh: PlanList = { plans: client.plans.list(read), until: Infinity };\n\n list = fresh;\n planLists.set(client, fresh);\n fresh.plans.then(\n () => {\n fresh.until = Date.now() + PLAN_TTL_MS;\n },\n () => {\n if (planLists.get(client) === fresh) planLists.delete(client);\n },\n );\n }\n\n return (await list.plans).find((plan) => plan.slug === slug) ?? null;\n}\n\n/**\n * What Mesub will pull next: the next charge of a running subscription, the\n * next retry of a late one, and nothing for any other, a parked seat\n * included. One event at most: later periods are not served, so not computed.\n * On Free a late one has no retry of Mesub's, only `retry_deadline`.\n */\nasync function upcomingOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<WidgetUpcoming[]> {\n const { status, plan } = subscription;\n const next =\n status === 'active'\n ? { kind: 'charge' as const, due_at: subscription.next_charge_at }\n : status === 'unpaid'\n ? { kind: 'retry' as const, due_at: subscription.next_retry_at }\n : null;\n\n // A date on any other status is no pull Mesub will run.\n if (!next || next.due_at === null || subscription.paused) return [];\n\n let price: Pick<Plan, 'amount' | 'amount_display' | 'amount_usd'> | null = null;\n\n if (plan !== null && SLUG.test(plan)) {\n try {\n price = await planOf(client, plan, read);\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n }\n }\n\n const retry = next.kind === 'retry';\n\n return [\n {\n kind: next.kind,\n due_at: next.due_at,\n amount: price?.amount ?? null,\n amount_display: price?.amount_display ?? null,\n amount_usd: price?.amount_usd ?? null,\n // Mesub's own, never counted here; `?? null` for a subscription cached by an older SDK.\n retry_number: retry ? (subscription.next_retry_number ?? null) : null,\n retries_allowed: retry ? (subscription.retries_allowed ?? null) : null,\n },\n ];\n}\n\n/** The statuses nothing is ever pulled in: a checkout that never started. */\nconst NEVER_STARTED = ['pending', 'expired', 'failed'];\n\nfunction payment(attempt: SubscriptionAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n // `?? null` for an attempt handed by a client that predates the field.\n amount_usd: attempt.amount_usd ?? null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: attempt.retry,\n retry_number: attempt.retry_number,\n retries_allowed: attempt.retries_allowed,\n period_start: attempt.period_start,\n };\n}\n\n/** An attempt of `/v1/access`, which says nothing of retries nor periods. */\nfunction olderPayment(attempt: ServedAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n amount_usd: null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: null,\n retry_number: null,\n retries_allowed: null,\n period_start: null,\n };\n}\n\ntype Payments = Omit<WidgetSubscriptionDetail, 'subscription' | 'upcoming'>;\n\nfunction unlisted(code: string, message: string): Payments {\n return { payments: null, paid: null, payments_error: { code, message } };\n}\n\n/**\n * A subscription's own pull attempts and what it paid, from\n * `GET /v1/subscriptions/:id/attempts`. A Mesub that predates the route\n * (Mesub-io/backend#289) answers a 404 that names no code for it: its payments are then\n * read the older way, without a total.\n */\nasync function paymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n try {\n const page = await client.subscriptions.attempts(subscription.id, {}, read);\n\n return { payments: page.data.map(payment), paid: page.paid, payments_error: null };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // No such route, not no such subscription: that one names its code.\n if (error.status === 404 && error.apiCode === null) {\n return olderPaymentsOf(client, subscription, read);\n }\n\n // The subscription was read: it is answered without them, so the dialog still opens.\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * The last pull attempts as a Mesub without the attempts route serves them:\n * through `/v1/access` only, five at most, for the subscription in force on\n * that wallet and plan, naming no id. They are this one's only when the\n * answer started when this one did. No total: five attempts are not one.\n */\nasync function olderPaymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n const { plan, wallet, confirmed_at: confirmedAt } = subscription;\n const listed = (payments: WidgetPayment[]): Payments => ({\n payments,\n paid: null,\n payments_error: null,\n });\n\n // Never started, so never pulled: nothing to ask.\n if (confirmedAt === null && NEVER_STARTED.includes(subscription.status)) return listed([]);\n if (plan === null || !SLUG.test(plan)) {\n return unlisted('plan_without_slug', 'Its plan has no slug to read payments by.');\n }\n\n try {\n const answer = await client.access({ wallet }, plan, { attempts: true, ...read });\n const same =\n confirmedAt !== null &&\n answer.subscribed_since !== null &&\n Date.parse(answer.subscribed_since) === Date.parse(confirmedAt);\n\n if (!same) {\n return unlisted(\n 'not_the_current_subscription',\n 'This Mesub serves payments for the current subscription of a wallet on a plan only.',\n );\n }\n\n return listed(\n (answer.attempts ?? [])\n .map(olderPayment)\n .sort((a, b) => Date.parse(b.attempted_at) - Date.parse(a.attempted_at)),\n );\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * Answers one request of the widget. `asked` is who your auth says is asking,\n * already normalised, or null for nobody; `email` their notices' address.\n *\n * Never throws for what the caller sent or what Mesub refused of the\n * customer's request: both are answered. An integration error is thrown to\n * the framework, which logs it and answers 500: Mesub refusing your API key\n * (its 401, never handed on as the customer's own) or something in front of\n * Mesub refusing your server (a 403 without Mesub's code), and anything that\n * is not a MesubError. The 401 answered here means nobody is signed in, only.\n */\nexport async function handleWidget(\n client: Mesub,\n request: WidgetRequest,\n asked: Asked | null,\n options: { email?: string | null | undefined; plans?: readonly string[] } = {},\n): Promise<WidgetResponse> {\n const segments = request.path.split('?')[0]!.split('/').filter(Boolean);\n const method = request.method.toUpperCase();\n const read = bounded();\n\n try {\n // A plan is public: the page shows its price before anybody signs in.\n if (method === 'GET' && segments.length === 2 && segments[0] === 'plans') {\n const slug = segments[1]!;\n\n if (!SLUG.test(slug) || (options.plans && !options.plans.includes(slug))) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n const plan = await planOf(client, slug, read);\n\n return plan\n ? { status: 200, body: plan }\n : refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n if (segments[0] !== 'subscriptions') return NOT_FOUND();\n if (method !== 'GET' && method !== 'POST') {\n return refusal(405, 'method_not_allowed', 'GET or POST.');\n }\n if (!asked) return refusal(401, 'unauthenticated', 'Sign in first.');\n\n if (method === 'GET' && segments.length === 2) {\n const id = segments[1]!;\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n // Theirs first: another customer's is not read any further.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n const [upcoming, payments] = await Promise.all([\n upcomingOf(client, subscription, read),\n paymentsOf(client, subscription, read),\n ]);\n const detail: WidgetSubscriptionDetail = {\n subscription: shown(subscription),\n upcoming,\n ...payments,\n };\n\n return { status: 200, body: detail };\n }\n\n if (method === 'GET') {\n if (segments.length !== 1) return NOT_FOUND();\n\n const params = { ...customerParam(asked), limit: LIST_PAGE };\n let page = await client.subscriptions.list(params, read);\n const subscriptions = [...page.data];\n\n const more = () => page.has_more && page.data.length > 0;\n\n // One browser request is LIST_MAX_PAGES calls to Mesub at most.\n for (let pages = 1; more() && pages < LIST_MAX_PAGES; pages++) {\n page = await client.subscriptions.list(\n { ...params, starting_after: page.data.at(-1)!.id },\n read,\n );\n subscriptions.push(...page.data);\n }\n\n const list: WidgetSubscriptionList = {\n subscriptions: subscriptions.map(shown),\n has_more: more(),\n };\n\n return { status: 200, body: list };\n }\n\n // A JSON body cannot be sent by a plain form from another site.\n if (!request.contentType?.toLowerCase().startsWith('application/json')) {\n return refusal(415, 'unsupported_media_type', 'Send JSON.');\n }\n\n const body = fieldsOf(request.body) ?? {};\n\n if (segments.length === 1) {\n const { plan, wallet } = body;\n\n if (!isString(plan) || !SLUG.test(plan) || !isString(wallet)) {\n return refusal(400, 'invalid_request', 'A plan and a wallet are needed.');\n }\n if (options.plans && !options.plans.includes(plan)) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n // Named by wallet, the customer is that wallet: no other may be subscribed for them.\n if (asked.kind === 'wallet' && asked.value !== wallet) {\n return refusal(403, 'wallet_mismatch', 'Connect the wallet you signed in with.');\n }\n\n const email = asked.kind === 'email' ? asked.value : options.email;\n const created = await client.subscriptions.create({\n plan,\n wallet,\n ...(asked.kind === 'external_id' && { external_id: asked.value }),\n ...(isString(email) && { email }),\n });\n\n return { status: 201, body: created };\n }\n\n const id = segments[1]!;\n const step = segments.slice(2).join('/');\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n const known = [\n 'submit',\n 'transaction',\n 'retry',\n ...ACTIONS,\n ...ACTIONS.map((action) => `${action}/confirm`),\n ];\n if (!known.includes(step)) return NOT_FOUND();\n\n // Before anything is built or confirmed: is it theirs at all.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n if (step === 'submit') {\n const { transaction, terms_signature: termsSignature } = body;\n\n if (!isString(transaction) || !isString(termsSignature)) {\n return refusal(\n 400,\n 'invalid_request',\n 'The signed transaction and the terms signature are needed.',\n );\n }\n\n const settled = await client.subscriptions.submit(id, {\n transaction,\n terms_signature: termsSignature,\n });\n\n return { status: 201, body: { ...settled, subscription: shown(settled.subscription) } };\n }\n\n // A fresh transaction for a pending one (Mesub-io/backend#427): answered as subscribing is.\n if (step === 'transaction') {\n return { status: 201, body: await client.subscriptions.transaction(id) };\n }\n\n // Pay now (Mesub-io/backend#354): nothing to sign, the outcome comes with the pull.\n if (step === 'retry') {\n const retried = await client.subscriptions.retry(id);\n\n return { status: 202, body: { subscription: shown(retried) } };\n }\n\n const [action, confirm] = step.split('/') as [Action, 'confirm' | undefined];\n\n if (!confirm) {\n return { status: 201, body: await client.subscriptions[action](id) };\n }\n\n const { signature } = body;\n if (!isString(signature)) {\n return refusal(400, 'invalid_request', 'The signature of the transaction is needed.');\n }\n\n const confirmed = await client.subscriptions[CONFIRM[action]](id, { signature });\n\n return {\n status: 201,\n body: { ...confirmed, subscription: shown(confirmed.subscription) },\n };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // An unknown id is Mesub's 404, answered as ours: nothing tells the two apart.\n if (error.apiCode === 'subscription_not_found') return NO_SUBSCRIPTION();\n\n return fromMesub(error);\n }\n}\n\nconst CONFIRM = {\n cancel: 'confirmCancel',\n resume: 'confirmResume',\n close: 'confirmClose',\n} as const satisfies Record<Action, string>;\n\nfunction customerParam(asked: Asked): Customer {\n if (asked.kind === 'wallet') return { wallet: asked.value };\n\n return asked.kind === 'email' ? { email: asked.value } : { external_id: asked.value };\n}\n\n/**\n * Who your auth says is asking, normalised as Mesub reads it. Null for\n * nobody; a value Mesub cannot be asked about is a broken integration, thrown.\n */\nexport async function widgetCustomer<Req>(\n request: Req,\n customer: WidgetCustomer<Req>,\n): Promise<Asked | null> {\n const named = await customer(request);\n\n if (named === null || named === undefined) return null;\n\n const asked = customerOf(named);\n\n if (asked.value === '') {\n throw new TypeError(\n '`customer` returned an empty value: return null when nobody is signed in.',\n );\n }\n\n return asked;\n}\n\n/** Checked when the routes are built: without `customer` they would serve anybody. */\nexport function checkWidgetOptions<Req>(options: WidgetRoutesOptions<Req>): void {\n if (typeof options?.customer !== 'function') {\n throw new TypeError(\n 'The widget routes need `customer`: a function of the request returning who is signed in.',\n );\n }\n if (options.plans !== undefined && !options.plans.every((plan) => SLUG.test(plan))) {\n throw new TypeError('`plans` is a list of plan slugs.');\n }\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/node-sdk/node-sdk/dist/chunk-5LDAYKUA.cjs","../src/routes.ts"],"names":["id","subscription","action"],"mappings":"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","file":"/home/runner/work/node-sdk/node-sdk/dist/chunk-5LDAYKUA.cjs","sourcesContent":[null,"import type { Customer, ServedAttempt } from './answer.js';\nimport type { Mesub } from './client.js';\nimport { type Asked, customerOf } from './customer.js';\nimport { MesubError } from './errors.js';\nimport type { Plan } from './plans.js';\nimport type { ServerSubscription, SubscriptionAttempt } from './subscriptions.js';\nimport type { CallOptions } from './transport.js';\n\n/**\n * The routes `@mesub/react` calls on your own server, so the browser never\n * talks to Mesub nor holds a key: a plan to show, the customer's\n * subscriptions, one of them with its payments, and each step of\n * subscribing (a fresh transaction to sign included), cancelling, resuming,\n * closing and paying a late payment now. One function, `handleWidget`; the\n * Express and Next entries only carry a request to it.\n *\n * Who is asking comes from `customer`, your own verified auth, as on the\n * guards. A subscription that is not that customer's is answered 404, the\n * same as one that does not exist.\n */\n\n/** Who is asking, from your own auth. Null when nobody is signed in, a 401. */\nexport type WidgetCustomer<Req> = (\n request: Req,\n) => Customer | string | null | undefined | Promise<Customer | string | null | undefined>;\n\nexport interface WidgetRoutesOptions<Req> {\n /** Defaults to one client built from MESUB_API_KEY. */\n client?: Mesub;\n /**\n * Who is asking, from a session you verified, never from the request\n * itself: `(req) => ({ external_id: req.user.id })`, a wallet or an email.\n * Every subscription created is tied to it, and only its own are reached.\n */\n customer: WidgetCustomer<Req>;\n /**\n * Where that customer's notices go, when `customer` is not an email:\n * `(req) => req.user.email`. Not verified by Mesub.\n */\n email?: (request: Req) => string | null | undefined | Promise<string | null | undefined>;\n /**\n * The plans the widget may subscribe to. Any plan of your project by\n * default; name them to keep one off your site.\n */\n plans?: readonly string[];\n}\n\n/** A request as the core reads it, whatever the framework. */\nexport interface WidgetRequest {\n method: string;\n /** After the mount point: `/subscriptions/sub_1/cancel`. */\n path: string;\n /** Parsed JSON, or undefined when there was none. */\n body: unknown;\n /** The request's `Content-Type`, to refuse a POST a plain form could send. */\n contentType: string | null;\n}\n\n/** What the core answers: a status and a JSON body. */\nexport interface WidgetResponse {\n status: number;\n body: unknown;\n headers?: Record<string, string>;\n}\n\n/** The most a widget request's body may weigh: a signed transaction is under 2 kB. */\nexport const MAX_BODY_BYTES = 64 * 1024;\n\nconst ACTIONS = ['cancel', 'resume', 'close'] as const;\ntype Action = (typeof ACTIONS)[number];\n\n/** An id as Mesub writes them: nothing that could rewrite the path. */\nconst ID = /^[A-Za-z0-9_-]{1,64}$/;\n/** A plan slug as Mesub writes them: with at least one letter, never a number. */\nconst SLUG = /^(?=.*[a-z])[a-z0-9]+(?:-[a-z0-9]+)*$/;\n\n/**\n * How long one widget request gives Mesub for all its reads, in milliseconds:\n * a browser is waiting, and a detail or a list makes several calls.\n */\nconst WIDGET_READ_TIME = 10_000;\n\n/**\n * How every read of one request is made, as a guard's: cut at one deadline,\n * and a 429 handed on at once, never waited out. The client's methods pass\n * their options to the transport whole, which is how these reach it.\n */\nfunction bounded(): CallOptions {\n return { deadline: Date.now() + WIDGET_READ_TIME, retryRateLimited: false };\n}\n\nfunction refusal(status: number, code: string, message: string): WidgetResponse {\n return { status, body: { error: { code, message } } };\n}\n\nconst NOT_FOUND = () => refusal(404, 'not_found', 'Nothing here.');\nconst NO_SUBSCRIPTION = () => refusal(404, 'subscription_not_found', 'No such subscription.');\n\n/** Said to the browser for an error Mesub did not word itself. */\nconst NO_ANSWER = 'Mesub could not answer this request.';\n\n/** Mesub's own words only: the transport's may name `baseUrl` or a network error. */\nfunction said(error: MesubError): string {\n return error.apiCode === null ? NO_ANSWER : error.message;\n}\n\n/**\n * Whether Mesub refused your server, not what the customer did: any 401,\n * which Mesub answers only to an API key missing, never issued or rotated\n * since (`missing_api_key`, `invalid_api_key`), or a 403 that names none of\n * Mesub's own codes, from something in front of Mesub turning your server\n * away. Handed on, a 401 reads to `@mesub/react` as \"nobody is signed in\",\n * and every visitor gets the sign-in screen: thrown instead, a broken\n * integration, for the framework to log and answer 500.\n */\nfunction brokenIntegration(error: MesubError): boolean {\n if (error.status === 401) return true;\n\n return error.status === 403 && (error.apiCode === null || error.apiCode === 'forbidden');\n}\n\n/** A Mesub refusal, handed on with its own status and code, and nothing of the key. */\nfunction fromMesub(error: MesubError): WidgetResponse {\n // No answer from Mesub, or a 2xx this SDK cannot read: a 502 of yours.\n const status = error.status === null || error.status < 400 ? 502 : error.status;\n const retry =\n error.retryAfter === null\n ? undefined\n : { 'Retry-After': String(Math.ceil(error.retryAfter / 1000)) };\n\n return {\n status,\n body: { error: { code: error.apiCode ?? error.code, message: said(error) } },\n ...(retry && { headers: retry }),\n };\n}\n\nfunction isString(value: unknown): value is string {\n return typeof value === 'string' && value !== '';\n}\n\nfunction fieldsOf(body: unknown): Record<string, unknown> | null {\n return typeof body === 'object' && body !== null && !Array.isArray(body)\n ? (body as Record<string, unknown>)\n : null;\n}\n\n/** Whether that subscription is the customer's: by the same identifier it was asked with. */\nfunction isTheirs(subscription: ServerSubscription, asked: Asked): boolean {\n if (asked.kind === 'wallet') return subscription.wallet === asked.value;\n if (asked.kind === 'external_id') return subscription.external_id === asked.value;\n\n return subscription.email === asked.value;\n}\n\n/** What a browser gets of a subscription: the merchant's own id and email stay on the server. */\nfunction shown(subscription: ServerSubscription) {\n const { email: _email, external_id: _externalId, ...rest } = subscription;\n\n return rest;\n}\n\n/** `GET /subscriptions` reads pages of 100, Mesub's largest, and 5 at most. */\nconst LIST_PAGE = 100;\nconst LIST_MAX_PAGES = 5;\n\n/** What `GET /subscriptions` answers. */\nexport interface WidgetSubscriptionList {\n /** Newest first, expired checkouts included: the 500 newest at most. */\n subscriptions: Array<Omit<ServerSubscription, 'email' | 'external_id'>>;\n /** True when the customer has more than those, which are not read. */\n has_more: boolean;\n}\n\n/** One pull attempt as the browser gets it: the fields Mesub serves, and no other. */\nexport interface WidgetPayment {\n attempted_at: string;\n /** `paid`, `skipped`, `rejected` or `blocked` today; a newer one is handed on as is. */\n outcome: string;\n /** What was asked for, in the mint's smallest unit; on a paid one, what was paid. */\n amount: string;\n /**\n * That amount in dollars when it was attempted, a decimal string. Null\n * until Mesub priced it, and from a Mesub that does not say.\n */\n amount_usd: string | null;\n /**\n * Mesub's short reason, null on a paid one, as on `ServedAttempt`: snake\n * case, an open string with the `program_${number}` and\n * `solana_${number}` families.\n */\n reason: string | null;\n /** The transaction that paid. Null on every outcome but `paid`. */\n signature: string | null;\n /** A retry of a missed charge. Null from a Mesub that does not say. */\n retry: boolean | null;\n /**\n * Which retry and out of how many, as Mesub recorded them when it ran:\n * 2 and 3 for \"retry 2 / 3\". Null when it is no retry of the plan's, or\n * Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n /** The start of the period it was for. Null when Mesub recorded none or does not say. */\n period_start: string | null;\n}\n\n/** What `GET /subscriptions/:id` answers. */\nexport interface WidgetSubscriptionDetail {\n subscription: Omit<ServerSubscription, 'email' | 'external_id'>;\n /** What Mesub pulls next, soonest first: one entry at most, none when nothing is due. */\n upcoming: WidgetUpcoming[];\n /**\n * This subscription's own pull attempts, newest first: the first page,\n * twenty at most, so not always its full history. Null when they could\n * not be read: `payments_error` says why.\n */\n payments: WidgetPayment[] | null;\n /**\n * What it paid since it began, counted by Mesub over all its attempts,\n * not over `payments`: how many were paid, and their sum in the mint's\n * smallest unit. Null when the read failed, and from a Mesub that does\n * not serve it: never summed here.\n */\n paid: { count: number; amount: string } | null;\n /** Why `payments` is null; null otherwise. */\n payments_error: { code: string; message: string } | null;\n}\n\n/** The one pull Mesub announces next for a subscription. */\nexport interface WidgetUpcoming {\n /** `charge` at the due date of a running one, `retry` of a missed one. */\n kind: 'charge' | 'retry';\n due_at: string;\n /**\n * The plan's price in the mint's smallest unit, and as a person counts\n * it. Null when the plan could not be read, never guessed.\n */\n amount: string | null;\n amount_display: string | null;\n /**\n * The plan's amount in dollars at the latest price Mesub knows, a decimal\n * string such as `\"9.99\"`: an estimate to show beside the amount, never\n * what is charged. Null when Mesub knows no price, and when the plan could\n * not be read.\n */\n amount_usd: string | null;\n /**\n * On a `retry`, which one it will be and out of how many: 2 and 3 for\n * \"next try 2 / 3\". Null on a `charge`, and when Mesub does not say.\n */\n retry_number: number | null;\n retries_allowed: number | null;\n}\n\n/** How long the plan list read for the widget is kept. */\nconst PLAN_TTL_MS = 60_000;\n\ninterface PlanList {\n /** Shared while the request is out. */\n plans: Promise<Plan[]>;\n /** Infinity until Mesub answered. */\n until: number;\n}\n\n// Per client, so two projects never share a plan.\nconst planLists = new WeakMap<Mesub, PlanList>();\n\n/**\n * A plan for the widget routes, from the project's list: public reads must\n * not spend the API key's rate limit, so one call per TTL whatever the slugs,\n * and null with no call for a slug that is not in it. A failed read is\n * forgotten at once.\n */\nasync function planOf(client: Mesub, slug: string, read: CallOptions): Promise<Plan | null> {\n let list = planLists.get(client);\n\n if (!list || list.until <= Date.now()) {\n // Shared while in flight: bound by the first asker's deadline, as a guard's flight.\n const fresh: PlanList = { plans: client.plans.list(read), until: Infinity };\n\n list = fresh;\n planLists.set(client, fresh);\n fresh.plans.then(\n () => {\n fresh.until = Date.now() + PLAN_TTL_MS;\n },\n () => {\n if (planLists.get(client) === fresh) planLists.delete(client);\n },\n );\n }\n\n return (await list.plans).find((plan) => plan.slug === slug) ?? null;\n}\n\n/**\n * What Mesub will pull next: the next charge of a running subscription, the\n * next retry of a late one, and nothing for any other, a parked seat\n * included. One event at most: later periods are not served, so not computed.\n * On Free a late one has no retry of Mesub's, only `retry_deadline`.\n */\nasync function upcomingOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<WidgetUpcoming[]> {\n const { status, plan } = subscription;\n const next =\n status === 'active'\n ? { kind: 'charge' as const, due_at: subscription.next_charge_at }\n : status === 'unpaid'\n ? { kind: 'retry' as const, due_at: subscription.next_retry_at }\n : null;\n\n // A date on any other status is no pull Mesub will run.\n if (!next || next.due_at === null || subscription.paused) return [];\n\n let price: Pick<Plan, 'amount' | 'amount_display' | 'amount_usd'> | null = null;\n\n if (plan !== null && SLUG.test(plan)) {\n try {\n price = await planOf(client, plan, read);\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n }\n }\n\n const retry = next.kind === 'retry';\n\n return [\n {\n kind: next.kind,\n due_at: next.due_at,\n amount: price?.amount ?? null,\n amount_display: price?.amount_display ?? null,\n amount_usd: price?.amount_usd ?? null,\n // Mesub's own, never counted here; `?? null` for a subscription cached by an older SDK.\n retry_number: retry ? (subscription.next_retry_number ?? null) : null,\n retries_allowed: retry ? (subscription.retries_allowed ?? null) : null,\n },\n ];\n}\n\n/** The statuses nothing is ever pulled in: a checkout that never started. */\nconst NEVER_STARTED = ['pending', 'expired', 'failed'];\n\nfunction payment(attempt: SubscriptionAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n // `?? null` for an attempt handed by a client that predates the field.\n amount_usd: attempt.amount_usd ?? null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: attempt.retry,\n retry_number: attempt.retry_number,\n retries_allowed: attempt.retries_allowed,\n period_start: attempt.period_start,\n };\n}\n\n/** An attempt of `/v1/access`, which says nothing of retries nor periods. */\nfunction olderPayment(attempt: ServedAttempt): WidgetPayment {\n return {\n attempted_at: attempt.attempted_at,\n outcome: attempt.outcome,\n amount: attempt.amount,\n amount_usd: null,\n reason: attempt.reason,\n signature: attempt.signature,\n retry: null,\n retry_number: null,\n retries_allowed: null,\n period_start: null,\n };\n}\n\ntype Payments = Omit<WidgetSubscriptionDetail, 'subscription' | 'upcoming'>;\n\nfunction unlisted(code: string, message: string): Payments {\n return { payments: null, paid: null, payments_error: { code, message } };\n}\n\n/**\n * A subscription's own pull attempts and what it paid, from\n * `GET /v1/subscriptions/:id/attempts`. A Mesub that predates the route\n * (Mesub-io/backend#289) answers a 404 that names no code for it: its payments are then\n * read the older way, without a total.\n */\nasync function paymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n try {\n const page = await client.subscriptions.attempts(subscription.id, {}, read);\n\n return { payments: page.data.map(payment), paid: page.paid, payments_error: null };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // No such route, not no such subscription: that one names its code.\n if (error.status === 404 && error.apiCode === null) {\n return olderPaymentsOf(client, subscription, read);\n }\n\n // The subscription was read: it is answered without them, so the dialog still opens.\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * The last pull attempts as a Mesub without the attempts route serves them:\n * through `/v1/access` only, five at most, for the subscription in force on\n * that wallet and plan, naming no id. They are this one's only when the\n * answer started when this one did. No total: five attempts are not one.\n */\nasync function olderPaymentsOf(\n client: Mesub,\n subscription: ServerSubscription,\n read: CallOptions,\n): Promise<Payments> {\n const { plan, wallet, confirmed_at: confirmedAt } = subscription;\n const listed = (payments: WidgetPayment[]): Payments => ({\n payments,\n paid: null,\n payments_error: null,\n });\n\n // Never started, so never pulled: nothing to ask.\n if (confirmedAt === null && NEVER_STARTED.includes(subscription.status)) return listed([]);\n if (plan === null || !SLUG.test(plan)) {\n return unlisted('plan_without_slug', 'Its plan has no slug to read payments by.');\n }\n\n try {\n const answer = await client.access({ wallet }, plan, { attempts: true, ...read });\n const same =\n confirmedAt !== null &&\n answer.subscribed_since !== null &&\n Date.parse(answer.subscribed_since) === Date.parse(confirmedAt);\n\n if (!same) {\n return unlisted(\n 'not_the_current_subscription',\n 'This Mesub serves payments for the current subscription of a wallet on a plan only.',\n );\n }\n\n return listed(\n (answer.attempts ?? [])\n .map(olderPayment)\n .sort((a, b) => Date.parse(b.attempted_at) - Date.parse(a.attempted_at)),\n );\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n\n return unlisted(error.apiCode ?? error.code, said(error));\n }\n}\n\n/**\n * Answers one request of the widget. `asked` is who your auth says is asking,\n * already normalised, or null for nobody; `email` their notices' address.\n *\n * Never throws for what the caller sent or what Mesub refused of the\n * customer's request: both are answered. An integration error is thrown to\n * the framework, which logs it and answers 500: Mesub refusing your API key\n * (its 401, never handed on as the customer's own) or something in front of\n * Mesub refusing your server (a 403 without Mesub's code), and anything that\n * is not a MesubError. The 401 answered here means nobody is signed in, only.\n */\nexport async function handleWidget(\n client: Mesub,\n request: WidgetRequest,\n asked: Asked | null,\n options: { email?: string | null | undefined; plans?: readonly string[] } = {},\n): Promise<WidgetResponse> {\n const segments = request.path.split('?')[0]!.split('/').filter(Boolean);\n const method = request.method.toUpperCase();\n const read = bounded();\n\n try {\n // A plan is public: the page shows its price before anybody signs in.\n if (method === 'GET' && segments.length === 2 && segments[0] === 'plans') {\n const slug = segments[1]!;\n\n if (!SLUG.test(slug) || (options.plans && !options.plans.includes(slug))) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n const plan = await planOf(client, slug, read);\n\n return plan\n ? { status: 200, body: plan }\n : refusal(404, 'plan_not_found', 'No such plan.');\n }\n\n if (segments[0] !== 'subscriptions') return NOT_FOUND();\n if (method !== 'GET' && method !== 'POST') {\n return refusal(405, 'method_not_allowed', 'GET or POST.');\n }\n if (!asked) return refusal(401, 'unauthenticated', 'Sign in first.');\n\n if (method === 'GET' && segments.length === 2) {\n const id = segments[1]!;\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n // Theirs first: another customer's is not read any further.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n const [upcoming, payments] = await Promise.all([\n upcomingOf(client, subscription, read),\n paymentsOf(client, subscription, read),\n ]);\n const detail: WidgetSubscriptionDetail = {\n subscription: shown(subscription),\n upcoming,\n ...payments,\n };\n\n return { status: 200, body: detail };\n }\n\n if (method === 'GET') {\n if (segments.length !== 1) return NOT_FOUND();\n\n const params = { ...customerParam(asked), limit: LIST_PAGE };\n let page = await client.subscriptions.list(params, read);\n const subscriptions = [...page.data];\n\n const more = () => page.has_more && page.data.length > 0;\n\n // One browser request is LIST_MAX_PAGES calls to Mesub at most.\n for (let pages = 1; more() && pages < LIST_MAX_PAGES; pages++) {\n page = await client.subscriptions.list(\n { ...params, starting_after: page.data.at(-1)!.id },\n read,\n );\n subscriptions.push(...page.data);\n }\n\n const list: WidgetSubscriptionList = {\n subscriptions: subscriptions.map(shown),\n has_more: more(),\n };\n\n return { status: 200, body: list };\n }\n\n // A JSON body cannot be sent by a plain form from another site.\n if (!request.contentType?.toLowerCase().startsWith('application/json')) {\n return refusal(415, 'unsupported_media_type', 'Send JSON.');\n }\n\n const body = fieldsOf(request.body) ?? {};\n\n if (segments.length === 1) {\n const { plan, wallet } = body;\n\n if (!isString(plan) || !SLUG.test(plan) || !isString(wallet)) {\n return refusal(400, 'invalid_request', 'A plan and a wallet are needed.');\n }\n if (options.plans && !options.plans.includes(plan)) {\n return refusal(404, 'plan_not_found', 'No such plan.');\n }\n // Named by wallet, the customer is that wallet: no other may be subscribed for them.\n if (asked.kind === 'wallet' && asked.value !== wallet) {\n return refusal(403, 'wallet_mismatch', 'Connect the wallet you signed in with.');\n }\n\n const email = asked.kind === 'email' ? asked.value : options.email;\n const created = await client.subscriptions.create({\n plan,\n wallet,\n ...(asked.kind === 'external_id' && { external_id: asked.value }),\n ...(isString(email) && { email }),\n });\n\n return { status: 201, body: created };\n }\n\n const id = segments[1]!;\n const step = segments.slice(2).join('/');\n\n if (!ID.test(id)) return NO_SUBSCRIPTION();\n\n const known = [\n 'submit',\n 'transaction',\n 'retry',\n ...ACTIONS,\n ...ACTIONS.map((action) => `${action}/confirm`),\n ];\n if (!known.includes(step)) return NOT_FOUND();\n\n // Before anything is built or confirmed: is it theirs at all.\n const subscription = await client.subscriptions.retrieve(id, read);\n if (!isTheirs(subscription, asked)) return NO_SUBSCRIPTION();\n\n if (step === 'submit') {\n const { transaction, terms_signature: termsSignature } = body;\n\n if (!isString(transaction) || !isString(termsSignature)) {\n return refusal(\n 400,\n 'invalid_request',\n 'The signed transaction and the terms signature are needed.',\n );\n }\n\n const settled = await client.subscriptions.submit(id, {\n transaction,\n terms_signature: termsSignature,\n });\n\n return { status: 201, body: { ...settled, subscription: shown(settled.subscription) } };\n }\n\n // A fresh transaction for a pending one (Mesub-io/backend#427): answered as subscribing is.\n if (step === 'transaction') {\n return { status: 201, body: await client.subscriptions.transaction(id) };\n }\n\n // Pay now (Mesub-io/backend#354): nothing to sign, the outcome comes with the pull.\n if (step === 'retry') {\n const retried = await client.subscriptions.retry(id);\n\n return { status: 202, body: { subscription: shown(retried) } };\n }\n\n const [action, confirm] = step.split('/') as [Action, 'confirm' | undefined];\n\n if (!confirm) {\n return { status: 201, body: await client.subscriptions[action](id) };\n }\n\n const { signature } = body;\n if (!isString(signature)) {\n return refusal(400, 'invalid_request', 'The signature of the transaction is needed.');\n }\n\n const confirmed = await client.subscriptions[CONFIRM[action]](id, { signature });\n\n return {\n status: 201,\n body: { ...confirmed, subscription: shown(confirmed.subscription) },\n };\n } catch (error) {\n if (!(error instanceof MesubError) || brokenIntegration(error)) throw error;\n // An unknown id is Mesub's 404, answered as ours: nothing tells the two apart.\n if (error.apiCode === 'subscription_not_found') return NO_SUBSCRIPTION();\n\n return fromMesub(error);\n }\n}\n\nconst CONFIRM = {\n cancel: 'confirmCancel',\n resume: 'confirmResume',\n close: 'confirmClose',\n} as const satisfies Record<Action, string>;\n\nfunction customerParam(asked: Asked): Customer {\n if (asked.kind === 'wallet') return { wallet: asked.value };\n\n return asked.kind === 'email' ? { email: asked.value } : { external_id: asked.value };\n}\n\n/**\n * Who your auth says is asking, normalised as Mesub reads it. Null for\n * nobody; a value Mesub cannot be asked about is a broken integration, thrown.\n */\nexport async function widgetCustomer<Req>(\n request: Req,\n customer: WidgetCustomer<Req>,\n): Promise<Asked | null> {\n const named = await customer(request);\n\n if (named === null || named === undefined) return null;\n\n const asked = customerOf(named);\n\n if (asked.value === '') {\n throw new TypeError(\n '`customer` returned an empty value: return null when nobody is signed in.',\n );\n }\n\n return asked;\n}\n\n/** Checked when the routes are built: without `customer` they would serve anybody. */\nexport function checkWidgetOptions<Req>(options: WidgetRoutesOptions<Req>): void {\n if (typeof options?.customer !== 'function') {\n throw new TypeError(\n 'The widget routes need `customer`: a function of the request returning who is signed in.',\n );\n }\n if (options.plans !== undefined && !options.plans.every((plan) => SLUG.test(plan))) {\n throw new TypeError('`plans` is a list of plan slugs.');\n }\n}\n"]}
@@ -86,7 +86,7 @@ var AccessCache = class _AccessCache {
86
86
  };
87
87
 
88
88
  // src/version.ts
89
- var VERSION = "0.1.5";
89
+ var VERSION = "0.1.6";
90
90
  var API_VERSION = "2026-10-02";
91
91
  var API_VERSION_HEADER = "Mesub-Version";
92
92
 
@@ -538,6 +538,11 @@ function planProblem(body, label) {
538
538
  label === void 0 ? "token" : `${label}.token`
539
539
  )));
540
540
  }
541
+ function projectFrom(body) {
542
+ const problem = problemWith(body, { id: STRING });
543
+ if (problem !== null) throw unreadable(problem, body, "GET /v1/project");
544
+ return body;
545
+ }
541
546
  function planFrom(body) {
542
547
  const problem = planProblem(body);
543
548
  if (problem !== null) throw unreadable(problem, body, "GET /v1/plans/:slug");
@@ -655,7 +660,8 @@ var DETAILS = {
655
660
  mint: STRING,
656
661
  period_start: DATE,
657
662
  period_end: DATE,
658
- signature: STRING
663
+ signature: STRING,
664
+ amount_received: { ...STRING_OR_NULL, optional: true }
659
665
  },
660
666
  "subscription.payment_failed": {
661
667
  reason: STRING,
@@ -1785,6 +1791,13 @@ var Mesub = (_class3 = class {
1785
1791
  let cacheSecret;
1786
1792
  this.cacheSecret = () => cacheSecret ??= importSecret(apiKey);
1787
1793
  }
1794
+ /**
1795
+ * The project your API key belongs to: the quickest way to check that a
1796
+ * key works. A wrong key throws `invalid_api_key`. Never cached.
1797
+ */
1798
+ async project(options = {}) {
1799
+ return projectFrom(await this.transport.get("/v1/project", {}, options));
1800
+ }
1788
1801
  /**
1789
1802
  * Everything Mesub knows about that customer on that plan: whether they
1790
1803
  * have access, their status, their dates. Served from the cache while fresh.
@@ -2063,4 +2076,4 @@ function stillGrants(answer, now = Date.now()) {
2063
2076
 
2064
2077
 
2065
2078
  exports.DEFAULT_MAX_STALE_MS = DEFAULT_MAX_STALE_MS; exports.AccessCache = AccessCache; exports.VERSION = VERSION; exports.API_VERSION = API_VERSION; exports.API_VERSION_HEADER = API_VERSION_HEADER; exports.DEFAULT_MAX_ENTRIES = DEFAULT_MAX_ENTRIES; exports.MemoryStore = MemoryStore; exports.customerOf = customerOf; exports.MesubError = MesubError; exports.MesubSubmitError = MesubSubmitError; exports.verifyWebhook = verifyWebhook; exports.signedHeaders = signedHeaders; exports.Mesub = Mesub;
2066
- //# sourceMappingURL=chunk-SFK52JDW.cjs.map
2079
+ //# sourceMappingURL=chunk-5THDILDD.cjs.map