@volter/twin-stripe 0.1.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. package/README.md +64 -27
  2. package/client/dashboard-api.ts +286 -0
  3. package/client/stripe-mirror.css +272 -159
  4. package/client/stripe-mirror.tsx +1384 -541
  5. package/dist/client/dashboard-api.d.ts +107 -0
  6. package/dist/client/dashboard-api.js +238 -0
  7. package/dist/client/dashboard-api.ts +286 -0
  8. package/dist/client/stripe-mirror.bundle.js +236 -0
  9. package/dist/client/stripe-mirror.css +275 -0
  10. package/dist/client/stripe-mirror.d.ts +134 -0
  11. package/dist/client/stripe-mirror.js +823 -0
  12. package/dist/client/stripe-mirror.tsx +1534 -0
  13. package/dist/src/cli.d.ts +2 -0
  14. package/dist/src/cli.js +39 -0
  15. package/dist/src/generated/events.gen.json +1 -0
  16. package/dist/src/generated/surface.gen.json +1 -0
  17. package/dist/src/generated/ui.gen.json +1 -0
  18. package/dist/src/index.d.ts +14 -0
  19. package/dist/src/index.js +73 -0
  20. package/dist/src/manifest.d.ts +2 -0
  21. package/dist/src/manifest.js +1065 -0
  22. package/dist/src/screens/checkout.d.ts +31 -0
  23. package/dist/src/screens/checkout.js +241 -0
  24. package/dist/src/screens/consent-skin.d.ts +4 -0
  25. package/dist/src/screens/consent-skin.js +18 -0
  26. package/dist/src/screens/financial-connections.d.ts +5 -0
  27. package/dist/src/screens/financial-connections.js +90 -0
  28. package/dist/src/screens/identity.d.ts +5 -0
  29. package/dist/src/screens/identity.js +86 -0
  30. package/dist/src/screens/industries.d.ts +1 -0
  31. package/dist/src/screens/industries.js +267 -0
  32. package/dist/src/screens/onboarding.d.ts +13 -0
  33. package/dist/src/screens/onboarding.js +225 -0
  34. package/dist/src/screens/portal.d.ts +5 -0
  35. package/dist/src/screens/portal.js +214 -0
  36. package/dist/src/screens/public-details.d.ts +5 -0
  37. package/dist/src/screens/public-details.js +90 -0
  38. package/dist/src/semantics/after-payment.d.ts +22 -0
  39. package/dist/src/semantics/after-payment.js +93 -0
  40. package/dist/src/semantics/apps-secrets.d.ts +2 -0
  41. package/dist/src/semantics/apps-secrets.js +54 -0
  42. package/dist/src/semantics/balance.d.ts +11 -0
  43. package/dist/src/semantics/balance.js +195 -0
  44. package/dist/src/semantics/billing.d.ts +2 -0
  45. package/dist/src/semantics/billing.js +220 -0
  46. package/dist/src/semantics/charges.d.ts +28 -0
  47. package/dist/src/semantics/charges.js +201 -0
  48. package/dist/src/semantics/checkout.d.ts +15 -0
  49. package/dist/src/semantics/checkout.js +303 -0
  50. package/dist/src/semantics/connect.d.ts +5 -0
  51. package/dist/src/semantics/connect.js +476 -0
  52. package/dist/src/semantics/coupons.d.ts +6 -0
  53. package/dist/src/semantics/coupons.js +92 -0
  54. package/dist/src/semantics/credit-notes.d.ts +2 -0
  55. package/dist/src/semantics/credit-notes.js +172 -0
  56. package/dist/src/semantics/customers.d.ts +6 -0
  57. package/dist/src/semantics/customers.js +429 -0
  58. package/dist/src/semantics/disputes.d.ts +2 -0
  59. package/dist/src/semantics/disputes.js +51 -0
  60. package/dist/src/semantics/entitlements.d.ts +2 -0
  61. package/dist/src/semantics/entitlements.js +95 -0
  62. package/dist/src/semantics/ephemeral-keys.d.ts +2 -0
  63. package/dist/src/semantics/ephemeral-keys.js +34 -0
  64. package/dist/src/semantics/files.d.ts +2 -0
  65. package/dist/src/semantics/files.js +125 -0
  66. package/dist/src/semantics/invoices.d.ts +18 -0
  67. package/dist/src/semantics/invoices.js +541 -0
  68. package/dist/src/semantics/issuing.d.ts +13 -0
  69. package/dist/src/semantics/issuing.js +570 -0
  70. package/dist/src/semantics/ledger.d.ts +54 -0
  71. package/dist/src/semantics/ledger.js +181 -0
  72. package/dist/src/semantics/payment-intents.d.ts +18 -0
  73. package/dist/src/semantics/payment-intents.js +404 -0
  74. package/dist/src/semantics/payment-links.d.ts +2 -0
  75. package/dist/src/semantics/payment-links.js +133 -0
  76. package/dist/src/semantics/payment-methods.d.ts +20 -0
  77. package/dist/src/semantics/payment-methods.js +138 -0
  78. package/dist/src/semantics/plans.d.ts +5 -0
  79. package/dist/src/semantics/plans.js +121 -0
  80. package/dist/src/semantics/platform.d.ts +9 -0
  81. package/dist/src/semantics/platform.js +206 -0
  82. package/dist/src/semantics/products.d.ts +2 -0
  83. package/dist/src/semantics/products.js +140 -0
  84. package/dist/src/semantics/radar.d.ts +2 -0
  85. package/dist/src/semantics/radar.js +83 -0
  86. package/dist/src/semantics/refunds.d.ts +9 -0
  87. package/dist/src/semantics/refunds.js +195 -0
  88. package/dist/src/semantics/renewals.d.ts +47 -0
  89. package/dist/src/semantics/renewals.js +251 -0
  90. package/dist/src/semantics/setup-intents.d.ts +2 -0
  91. package/dist/src/semantics/setup-intents.js +84 -0
  92. package/dist/src/semantics/shared.d.ts +78 -0
  93. package/dist/src/semantics/shared.js +192 -0
  94. package/dist/src/semantics/subscription-schedules.d.ts +2 -0
  95. package/dist/src/semantics/subscription-schedules.js +119 -0
  96. package/dist/src/semantics/subscriptions.d.ts +11 -0
  97. package/dist/src/semantics/subscriptions.js +605 -0
  98. package/dist/src/semantics/tax.d.ts +2 -0
  99. package/dist/src/semantics/tax.js +197 -0
  100. package/dist/src/semantics/terminal.d.ts +5 -0
  101. package/dist/src/semantics/terminal.js +182 -0
  102. package/dist/src/semantics/test-clocks.d.ts +6 -0
  103. package/dist/src/semantics/test-clocks.js +73 -0
  104. package/dist/src/semantics/tokens.d.ts +4 -0
  105. package/dist/src/semantics/tokens.js +44 -0
  106. package/dist/src/semantics/transfers.d.ts +2 -0
  107. package/dist/src/semantics/transfers.js +154 -0
  108. package/dist/src/semantics/treasury.d.ts +2 -0
  109. package/dist/src/semantics/treasury.js +377 -0
  110. package/dist/src/semantics/webhook-endpoints.d.ts +3 -0
  111. package/dist/src/semantics/webhook-endpoints.js +85 -0
  112. package/dist/src/stripe-budget.d.ts +55 -0
  113. package/dist/src/stripe-budget.js +155 -0
  114. package/dist/src/stripe-capabilities.d.ts +3 -0
  115. package/dist/src/stripe-capabilities.js +5052 -0
  116. package/dist/src/stripe-conformance.d.ts +41 -0
  117. package/dist/src/stripe-conformance.js +96 -0
  118. package/dist/src/stripe-connector.d.ts +161 -0
  119. package/dist/src/stripe-connector.js +414 -0
  120. package/dist/src/stripe-emit.d.ts +2 -0
  121. package/dist/src/stripe-emit.js +145 -0
  122. package/dist/src/stripe-events.d.ts +93 -0
  123. package/dist/src/stripe-events.js +388 -0
  124. package/dist/src/stripe-js.d.ts +4 -0
  125. package/dist/src/stripe-js.js +70 -0
  126. package/dist/src/stripe-mirror-ui.d.ts +15 -0
  127. package/dist/src/stripe-mirror-ui.js +87 -0
  128. package/dist/src/stripe-params.d.ts +3 -0
  129. package/dist/src/stripe-params.js +43 -0
  130. package/dist/src/stripe-perform-harness.d.ts +9 -0
  131. package/dist/src/stripe-perform-harness.js +26 -0
  132. package/dist/src/stripe-server.d.ts +33 -0
  133. package/dist/src/stripe-server.js +326 -0
  134. package/dist/src/stripe-shared.d.ts +106 -0
  135. package/dist/src/stripe-shared.js +273 -0
  136. package/dist/src/stripe-twin.d.ts +155 -0
  137. package/dist/src/stripe-twin.js +1226 -0
  138. package/dist/src/stripe-ui-conformance.d.ts +5 -0
  139. package/dist/src/stripe-ui-conformance.js +79 -0
  140. package/dist/src/stripe-ui-structure.d.ts +3 -0
  141. package/dist/src/stripe-ui-structure.js +168 -0
  142. package/dist/src/stripe-version.d.ts +10 -0
  143. package/dist/src/stripe-version.js +285 -0
  144. package/dist/test-fixtures/stripe-known-deviations.json +105 -0
  145. package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +14 -0
  146. package/dist/test-fixtures/stripe-openapi-operations.json +4717 -0
  147. package/dist/test-fixtures/stripe-schemas.SOURCE.md +35 -0
  148. package/dist/test-fixtures/stripe-schemas.json +3740 -0
  149. package/package.json +18 -10
  150. package/src/cli.ts +7 -7
  151. package/src/generated/events.gen.json +1 -0
  152. package/src/generated/surface.gen.json +1 -0
  153. package/src/generated/ui.gen.json +1 -0
  154. package/src/index.ts +31 -9
  155. package/src/manifest.ts +1097 -0
  156. package/src/screens/checkout.tsx +252 -0
  157. package/src/screens/consent-skin.ts +20 -0
  158. package/src/screens/financial-connections.tsx +101 -0
  159. package/src/screens/identity.tsx +96 -0
  160. package/src/screens/industries.ts +267 -0
  161. package/src/screens/onboarding.tsx +243 -0
  162. package/src/screens/portal.tsx +218 -0
  163. package/src/screens/public-details.tsx +105 -0
  164. package/src/semantics/after-payment.ts +113 -0
  165. package/src/semantics/apps-secrets.ts +58 -0
  166. package/src/semantics/balance.ts +209 -0
  167. package/src/semantics/billing.ts +216 -0
  168. package/src/semantics/charges.ts +211 -0
  169. package/src/semantics/checkout.ts +297 -0
  170. package/src/semantics/connect.ts +471 -0
  171. package/src/semantics/coupons.ts +97 -0
  172. package/src/semantics/credit-notes.ts +168 -0
  173. package/src/semantics/customers.ts +432 -0
  174. package/src/semantics/disputes.ts +62 -0
  175. package/src/semantics/entitlements.ts +94 -0
  176. package/src/semantics/ephemeral-keys.ts +34 -0
  177. package/src/semantics/files.ts +143 -0
  178. package/src/semantics/invoices.ts +541 -0
  179. package/src/semantics/issuing.ts +585 -0
  180. package/src/semantics/ledger.ts +216 -0
  181. package/src/semantics/payment-intents.ts +420 -0
  182. package/src/semantics/payment-links.ts +148 -0
  183. package/src/semantics/payment-methods.ts +143 -0
  184. package/src/semantics/plans.ts +131 -0
  185. package/src/semantics/platform.ts +220 -0
  186. package/src/semantics/products.ts +154 -0
  187. package/src/semantics/radar.ts +85 -0
  188. package/src/semantics/refunds.ts +218 -0
  189. package/src/semantics/renewals.ts +274 -0
  190. package/src/semantics/setup-intents.ts +87 -0
  191. package/src/semantics/shared.ts +215 -0
  192. package/src/semantics/subscription-schedules.ts +129 -0
  193. package/src/semantics/subscriptions.ts +610 -0
  194. package/src/semantics/tax.ts +220 -0
  195. package/src/semantics/terminal.ts +195 -0
  196. package/src/semantics/test-clocks.ts +77 -0
  197. package/src/semantics/tokens.ts +52 -0
  198. package/src/semantics/transfers.ts +174 -0
  199. package/src/semantics/treasury.ts +383 -0
  200. package/src/semantics/webhook-endpoints.ts +87 -0
  201. package/src/stripe-budget.ts +4 -4
  202. package/src/stripe-capabilities.ts +1456 -222
  203. package/src/stripe-conformance.ts +6 -5
  204. package/src/stripe-connector.ts +68 -40
  205. package/src/stripe-emit.ts +14 -7
  206. package/src/stripe-events.ts +94 -36
  207. package/src/stripe-js.ts +70 -0
  208. package/src/stripe-mirror-ui.ts +28 -298
  209. package/src/stripe-params.ts +44 -0
  210. package/src/stripe-perform-harness.ts +29 -0
  211. package/src/stripe-server.ts +263 -38
  212. package/src/stripe-shared.ts +294 -0
  213. package/src/stripe-twin.ts +429 -5325
  214. package/src/stripe-ui-conformance.ts +70 -107
  215. package/src/stripe-ui-structure.ts +124 -348
  216. package/src/stripe-version.ts +278 -0
  217. package/test-fixtures/stripe-known-deviations.json +2 -7
  218. package/test-fixtures/stripe-openapi-operations.json +1188 -2855
  219. package/src/stripe-form.ts +0 -35
@@ -0,0 +1,55 @@
1
+ import { RateBudget, type RateBudgetDeclaration, type RateBudgetOptions, type RateBudgetReservation, type RateBudgetSnapshot } from '@volter/world-core';
2
+ /** Rolling window, in ms. Spend older than this is pruned. */
3
+ export declare const STRIPE_BUDGET_WINDOW_MS = 60000;
4
+ /**
5
+ * Weighted units allowed inside one window. 120/60s = 120 reads a minute (2/second) — 2% of
6
+ * Stripe's documented live-mode 100/second and 8% of the 25/second a sandbox key or any single
7
+ * endpoint gets.
8
+ */
9
+ export declare const STRIPE_BUDGET_CEILING = 120;
10
+ /** Seconds. A `Retry-After` above this means the key is throttled hard — fail loudly, don't sleep. */
11
+ export declare const STRIPE_BUDGET_MAX_RETRY_AFTER_S = 300;
12
+ /** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for what is documented vs. judged. */
13
+ export declare const STRIPE_CALL_WEIGHTS: {
14
+ /** `POST /v1/payouts` — documented at 15 creates/second, and the most irreversible object here. */
15
+ readonly payout: 5;
16
+ /** Any other `POST`/`DELETE` — creates charges, refunds, subscriptions, receipt e-mail. */
17
+ readonly write: 2;
18
+ /** Reads: list/retrieve. */
19
+ readonly other: 1;
20
+ };
21
+ /** THE PACK'S DECLARATION — pure data, the only Stripe-specific thing in the whole budget. */
22
+ export declare const STRIPE_RATE_BUDGET: RateBudgetDeclaration;
23
+ /**
24
+ * Price one call. The key is `"<METHOD> <path>"` — Stripe's executor carries filters as separate
25
+ * `params` rather than in the path, so there is no query string to split off here; a `?` is still
26
+ * handled defensively in case a caller inlines one. An unclassified endpoint costs `defaultWeight`.
27
+ */
28
+ export declare function stripeCallWeight(method: string, path: string): number;
29
+ /** Where Stripe's ledger lives. Key-keyed and cwd-independent by default (limits attach to the
30
+ * account behind the secret key, so a cwd-scoped ledger would hand the same key a fresh allowance
31
+ * in every checkout, worktree and CI matrix leg); pass `root` for world-scoped accounting.
32
+ *
33
+ * A live key and a test key are different strings, so they get different ledgers — which matches
34
+ * Stripe, whose live and sandbox limits are separate (100/s vs 25/s). */
35
+ export declare function stripeBudgetPath(opts?: {
36
+ root?: string;
37
+ token?: string;
38
+ } | string): string;
39
+ /** Construction options for Stripe's budget. The vendor is fixed; everything else may only TIGHTEN. */
40
+ export type StripeBudgetOptions = Omit<RateBudgetOptions, 'vendor'>;
41
+ /**
42
+ * Stripe's budget — the shared kernel guard bound to this vendor's declaration. A real subclass,
43
+ * not an alias, so `budget instanceof StripeBudget` in `liveStripeExecute` means "a budget that
44
+ * accounts against STRIPE's ledger under STRIPE's ceiling": another vendor's `RateBudget` (with its
45
+ * own, possibly larger, ceiling) is NOT assignable there.
46
+ */
47
+ export declare class StripeBudget extends RateBudget {
48
+ constructor(opts?: StripeBudgetOptions);
49
+ }
50
+ /** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
51
+ * which one refused, and `err.kind` says why. */
52
+ export { RateBudgetError as StripeBudgetError } from '@volter/world-core';
53
+ export type { RateBudgetErrorKind as StripeBudgetErrorKind } from '@volter/world-core';
54
+ export type StripeBudgetReservation = RateBudgetReservation;
55
+ export type StripeBudgetSnapshot = RateBudgetSnapshot;
@@ -0,0 +1,155 @@
1
+ // Stripe's CLIENT-SIDE RATE BUDGET — the pack's DECLARATION (the numbers) plus the thin typed
2
+ // bindings `liveStripeExecute` uses. The MECHANISM — the durable token-keyed ledger, the rolling
3
+ // window, reserve-under-lock, the `Retry-After`/429 cooldown, fail-CLOSED on a corrupt ledger —
4
+ // lives ONCE in the vendor-agnostic kernel (`@volter/world-core` → `rateBudget.ts`). Read that module's
5
+ // header for the full rationale AND for the honest list of what the guard does not guarantee (an
6
+ // injected clock or ledger path still defeats it — it guards carelessness, not malice).
7
+ //
8
+ // ── WHY THIS EXISTS ─────────────────────────────────────────────────────────────────────────
9
+ // A real ~4.5-DAY vendor lockout (Figma, 2026-07-25) happened because raw API calls were made
10
+ // outside the pack's connector — no cache, no batching, no ceiling. Discipline only binds the code
11
+ // that follows it; a BUDGET binds the code that does not. Stripe is the pack where a runaway loop
12
+ // is not merely rude: its writes MOVE MONEY.
13
+ //
14
+ // ── HOW THE CEILING WAS CHOSEN ──────────────────────────────────────────────────────────────
15
+ // Stripe DOES publish scalar limits, so this models the real thing rather than guessing. From
16
+ // https://docs.stripe.com/rate-limits (read 2026-07-26):
17
+ // • live mode: 100 requests/second
18
+ // • sandbox (test mode): 25 requests/second
19
+ // • an individual endpoint: 25 requests/second unless noted otherwise
20
+ // • Payouts, create: 15 requests/second
21
+ // • Subscriptions: 10 new invoices per subscription per minute
22
+ // • Payment Intents: 1,000 update requests per PaymentIntent per hour
23
+ // Over the limit: 429, with a `Stripe-Rate-Limited-Reason` header naming which limit was hit.
24
+ //
25
+ // The ceiling is 120 weighted units per 60s — 120 calls a minute, i.e. 2 requests/second at the
26
+ // default weight. That is 2% of the live-mode 100/s and 8% of the tightest general limit (25/s,
27
+ // which is what a sandbox key and any single endpoint get). It is more permissive than the kernel's
28
+ // undeclared fallback (30 calls/min) precisely because those limits are documented and are one to
29
+ // three ORDERS of magnitude higher; without them it would not be.
30
+ //
31
+ // It bounds the 60-second AVERAGE; it does NOT pace (the kernel refuses, it never sleeps — see its
32
+ // header). Stripe's own window is a SECOND, so a tight loop can legitimately fire all 120 inside
33
+ // one second here — 120 req/s, above the sandbox 25/s — and in that shape Stripe's 429 arrives
34
+ // before this ceiling does. The backstop is then the cooldown: the guard reads the 429 off the
35
+ // response and refuses every later call without touching Stripe. The honest claim is therefore
36
+ // "bounds the minute, and converts Stripe's first 429 into a hard stop", not "refuses before Stripe
37
+ // ever 429s". Pacing is the caller's job; this is the ceiling underneath it.
38
+ //
39
+ // ── HOW THE WEIGHTS WERE CHOSEN ─────────────────────────────────────────────────────────────
40
+ // • `POST` / `DELETE` cost 2. Stripe counts them the same as a read, so this is NOT a published
41
+ // ratio — it is a judgement call about blast radius: a write creates a charge, a refund, a
42
+ // subscription or a receipt e-mail, and unlike a read it cannot be taken back. Halving the rate
43
+ // at which a runaway loop can do that is worth the cost to a legitimate push, which is small
44
+ // (a push is a handful of writes).
45
+ // • `POST /v1/payouts` costs 5, because Stripe documents payout creation at 15/second — six times
46
+ // tighter than live mode's 100/s — and a payout is the single most irreversible object in the
47
+ // API. At weight 5 at most 24 land in a window: 0.4/s against a documented 15/s.
48
+ import { declareRateBudget, rateBudgetPath, rateBudgetWeight, RateBudget, } from '@volter/world-core';
49
+ const VENDOR = 'stripe';
50
+ /** Rolling window, in ms. Spend older than this is pruned. */
51
+ export const STRIPE_BUDGET_WINDOW_MS = 60_000;
52
+ /**
53
+ * Weighted units allowed inside one window. 120/60s = 120 reads a minute (2/second) — 2% of
54
+ * Stripe's documented live-mode 100/second and 8% of the 25/second a sandbox key or any single
55
+ * endpoint gets.
56
+ */
57
+ export const STRIPE_BUDGET_CEILING = 120;
58
+ /** Seconds. A `Retry-After` above this means the key is throttled hard — fail loudly, don't sleep. */
59
+ export const STRIPE_BUDGET_MAX_RETRY_AFTER_S = 300;
60
+ /** Per-call cost, keyed by `"<METHOD> <path>"`. See the header for what is documented vs. judged. */
61
+ export const STRIPE_CALL_WEIGHTS = {
62
+ /** `POST /v1/payouts` — documented at 15 creates/second, and the most irreversible object here. */
63
+ payout: 5,
64
+ /** Any other `POST`/`DELETE` — creates charges, refunds, subscriptions, receipt e-mail. */
65
+ write: 2,
66
+ /** Reads: list/retrieve. */
67
+ other: 1,
68
+ };
69
+ /** THE PACK'S DECLARATION — pure data, the only Stripe-specific thing in the whole budget. */
70
+ export const STRIPE_RATE_BUDGET = {
71
+ windowMs: STRIPE_BUDGET_WINDOW_MS,
72
+ ceiling: STRIPE_BUDGET_CEILING,
73
+ defaultWeight: STRIPE_CALL_WEIGHTS.other,
74
+ maxRetryAfterSeconds: STRIPE_BUDGET_MAX_RETRY_AFTER_S,
75
+ rules: [
76
+ { match: '^POST /v1/payouts$', weight: STRIPE_CALL_WEIGHTS.payout },
77
+ { match: '^(POST|DELETE) ', weight: STRIPE_CALL_WEIGHTS.write },
78
+ ],
79
+ reason: 'Stripe documents scalar limits (docs.stripe.com/rate-limits, read 2026-07-26): 100 requests/' +
80
+ 'second in live mode, 25/second in sandbox, 25/second for an individual endpoint unless noted, ' +
81
+ '15 payout creates/second, 10 new invoices per subscription per minute, 1,000 PaymentIntent ' +
82
+ 'updates per intent per hour; over it, 429 with a Stripe-Rate-Limited-Reason header. 120 ' +
83
+ 'weighted units / 60s is 120 reads a minute = 2 requests/second — 2% of live mode and 8% of the ' +
84
+ "tightest general limit. It is more permissive than the kernel's undeclared fallback (30 calls/" +
85
+ 'min) BECAUSE those documented limits are one to three orders of magnitude higher. POST/DELETE ' +
86
+ 'cost 2 — NOT a published ratio, a judgement call about blast radius: a write creates a charge, ' +
87
+ 'refund or receipt e-mail and cannot be taken back. POST /v1/payouts costs 5 because Stripe ' +
88
+ 'documents payout creation at 15/second, six times tighter than live mode. The window bounds the ' +
89
+ "60s AVERAGE and does not pace, and Stripe's own window is a SECOND, so an intra-second burst " +
90
+ "reaches Stripe's limiter first — the 429 cooldown is the backstop for that shape, not this " +
91
+ 'ceiling.',
92
+ };
93
+ // Declared at module load, so merely importing this module (which `stripe-connector.ts` does) is
94
+ // enough to arm the real ceiling. `RateBudget` reads its policy live precisely so this declaration
95
+ // takes effect the moment it lands, and constructing through the subclass below (which imports this
96
+ // module) is what makes the ordering a non-issue in practice.
97
+ declareRateBudget(VENDOR, STRIPE_RATE_BUDGET);
98
+ /**
99
+ * Price one call. The key is `"<METHOD> <path>"` — Stripe's executor carries filters as separate
100
+ * `params` rather than in the path, so there is no query string to split off here; a `?` is still
101
+ * handled defensively in case a caller inlines one. An unclassified endpoint costs `defaultWeight`.
102
+ */
103
+ export function stripeCallWeight(method, path) {
104
+ const { bare, query } = splitQuery(path);
105
+ // UPPER-CASE the method: `fetch` normalizes a known lowercase method before sending, so
106
+ // `execute('post', …)` really does issue a POST and must be priced as one.
107
+ return rateBudgetWeight(VENDOR, `${String(method).toUpperCase()} ${bare}`, query);
108
+ }
109
+ /**
110
+ * `/v1/x?a=1` -> `{ bare: '/v1/x', query: { a: '1' } }`. Rules match the path; `whenQuery*` the query.
111
+ *
112
+ * NORMALIZED, because the anchored rules are otherwise trivially evaded (§9 finding, 2026-07-26):
113
+ * `fetch` upper-cases a known method before sending, so `execute('post', …)` issues a real WRITE
114
+ * that a `^POST ` rule would price as a read; and a trailing slash makes a path miss a `$` anchor
115
+ * while most routers treat it as the same endpoint. Both are input variations, not attacks, and
116
+ * either one silently voids the "expensive endpoints are priced up" claim the ceiling rests on.
117
+ */
118
+ function splitQuery(path) {
119
+ const at = path.indexOf('?');
120
+ const query = {};
121
+ if (at !== -1)
122
+ for (const [k, v] of new URLSearchParams(path.slice(at + 1)))
123
+ query[k] = v;
124
+ const raw = at === -1 ? path : path.slice(0, at);
125
+ // Collapse a trailing slash, but never turn the root path into the empty string.
126
+ const bare = raw.length > 1 && raw.endsWith('/') ? raw.replace(/\/+$/, '') : raw;
127
+ return { bare, query };
128
+ }
129
+ /** Where Stripe's ledger lives. Key-keyed and cwd-independent by default (limits attach to the
130
+ * account behind the secret key, so a cwd-scoped ledger would hand the same key a fresh allowance
131
+ * in every checkout, worktree and CI matrix leg); pass `root` for world-scoped accounting.
132
+ *
133
+ * A live key and a test key are different strings, so they get different ledgers — which matches
134
+ * Stripe, whose live and sandbox limits are separate (100/s vs 25/s). */
135
+ export function stripeBudgetPath(opts = {}) {
136
+ const o = typeof opts === 'string' ? { root: opts } : opts;
137
+ // VENDOR spread LAST: a loosely-typed `{ vendor: 'other', … }` slipping through (TypeScript's
138
+ // excess-property check only catches object literals) must not redirect this pack's ledger to
139
+ // another vendor's file.
140
+ return rateBudgetPath({ ...o, vendor: VENDOR });
141
+ }
142
+ /**
143
+ * Stripe's budget — the shared kernel guard bound to this vendor's declaration. A real subclass,
144
+ * not an alias, so `budget instanceof StripeBudget` in `liveStripeExecute` means "a budget that
145
+ * accounts against STRIPE's ledger under STRIPE's ceiling": another vendor's `RateBudget` (with its
146
+ * own, possibly larger, ceiling) is NOT assignable there.
147
+ */
148
+ export class StripeBudget extends RateBudget {
149
+ constructor(opts = {}) {
150
+ super({ ...opts, vendor: VENDOR });
151
+ }
152
+ }
153
+ /** The typed refusal. One error class shared with every other vendor's budget; `err.vendor` says
154
+ * which one refused, and `err.kind` says why. */
155
+ export { RateBudgetError as StripeBudgetError } from '@volter/world-core';
@@ -0,0 +1,3 @@
1
+ import { type CapabilityReport, type CapabilitySpec } from '@volter/world-tooling';
2
+ export declare const STRIPE_CAPABILITIES: CapabilitySpec[];
3
+ export declare function stripeCapabilities(): Promise<CapabilityReport>;