@volter/twin-stripe 2.0.38 → 3.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 (442) hide show
  1. package/README.md +82 -207
  2. package/dist/generated/pack-facts.json +94 -0
  3. package/dist/spec/SOURCE.md +14 -0
  4. package/dist/spec/fixtures3.json.gz +0 -0
  5. package/dist/spec/openapi.json.gz +0 -0
  6. package/dist/spec/patches.json +34 -0
  7. package/dist/spec/sources.json +378 -0
  8. package/dist/src/cli.js +12 -30
  9. package/dist/src/engine/after-payment.d.ts +9 -0
  10. package/dist/src/engine/after-payment.js +28 -0
  11. package/dist/src/engine/apps-secrets.d.ts +1 -0
  12. package/dist/src/engine/apps-secrets.js +3 -0
  13. package/dist/src/engine/balance.d.ts +5 -0
  14. package/dist/src/engine/balance.js +15 -0
  15. package/dist/src/engine/billing.d.ts +5 -0
  16. package/dist/src/engine/billing.js +6 -0
  17. package/dist/src/engine/charges.d.ts +6 -0
  18. package/dist/src/engine/charges.js +13 -0
  19. package/dist/src/engine/checkout.d.ts +24 -0
  20. package/dist/src/engine/checkout.js +30 -0
  21. package/dist/src/engine/common.d.ts +19 -0
  22. package/dist/src/engine/common.js +32 -0
  23. package/dist/src/{screens → engine}/connect-settings.d.ts +1 -7
  24. package/dist/src/engine/connect-settings.js +26 -0
  25. package/dist/src/engine/connect.d.ts +66 -0
  26. package/dist/src/engine/connect.js +189 -0
  27. package/dist/src/engine/coupons.d.ts +0 -0
  28. package/dist/src/engine/coupons.js +5 -0
  29. package/dist/src/engine/credit-notes.d.ts +1 -0
  30. package/dist/src/engine/credit-notes.js +5 -0
  31. package/dist/src/engine/customers.d.ts +12 -0
  32. package/dist/src/engine/customers.js +30 -0
  33. package/dist/src/engine/display.d.ts +14 -0
  34. package/dist/src/engine/display.js +16 -0
  35. package/dist/src/engine/disputes.d.ts +0 -0
  36. package/dist/src/engine/disputes.js +9 -0
  37. package/dist/src/engine/entitlements.d.ts +1 -0
  38. package/dist/src/engine/entitlements.js +4 -0
  39. package/dist/src/engine/ephemeral-keys.d.ts +0 -0
  40. package/dist/src/engine/ephemeral-keys.js +4 -0
  41. package/dist/src/engine/files.d.ts +18 -0
  42. package/dist/src/engine/files.js +33 -0
  43. package/dist/src/engine/front.d.ts +5 -0
  44. package/dist/src/engine/front.js +25 -0
  45. package/dist/src/engine/invoices.d.ts +24 -0
  46. package/dist/src/engine/invoices.js +57 -0
  47. package/dist/src/engine/issuing.d.ts +35 -0
  48. package/dist/src/engine/issuing.js +45 -0
  49. package/dist/src/engine/ledger.d.ts +9 -0
  50. package/dist/src/engine/ledger.js +9 -0
  51. package/dist/src/engine/oauth.d.ts +4 -0
  52. package/dist/src/engine/oauth.js +25 -0
  53. package/dist/src/engine/onboarding.d.ts +13 -0
  54. package/dist/src/engine/onboarding.js +42 -0
  55. package/dist/src/{stripe-params.d.ts → engine/params.d.ts} +20 -3
  56. package/dist/src/{stripe-params.js → engine/params.js} +7 -60
  57. package/dist/src/engine/payment-intents.d.ts +10 -0
  58. package/dist/src/engine/payment-intents.js +27 -0
  59. package/dist/src/engine/payment-links.d.ts +1 -0
  60. package/dist/src/engine/payment-links.js +5 -0
  61. package/dist/src/engine/payment-methods.d.ts +16 -0
  62. package/dist/src/engine/payment-methods.js +22 -0
  63. package/dist/src/engine/plans.d.ts +6 -0
  64. package/dist/src/engine/plans.js +26 -0
  65. package/dist/src/engine/platform.d.ts +39 -0
  66. package/dist/src/engine/platform.js +34 -0
  67. package/dist/src/engine/products.d.ts +0 -0
  68. package/dist/src/engine/products.js +4 -0
  69. package/dist/src/engine/radar.d.ts +2 -0
  70. package/dist/src/engine/radar.js +5 -0
  71. package/dist/src/engine/refunds.d.ts +1 -0
  72. package/dist/src/engine/refunds.js +28 -0
  73. package/dist/src/engine/renewals.d.ts +36 -0
  74. package/dist/src/engine/renewals.js +65 -0
  75. package/dist/src/engine/setup-intents.d.ts +1 -0
  76. package/dist/src/engine/setup-intents.js +4 -0
  77. package/dist/src/{stripe-twin.d.ts → engine/stripe.d.ts} +11 -39
  78. package/dist/src/{stripe-twin.js → engine/stripe.js} +25 -310
  79. package/dist/src/engine/subscription-schedules.d.ts +3 -0
  80. package/dist/src/engine/subscription-schedules.js +1 -0
  81. package/dist/src/engine/subscriptions.d.ts +12 -0
  82. package/dist/src/engine/subscriptions.js +31 -0
  83. package/dist/src/engine/tax.d.ts +7 -0
  84. package/dist/src/engine/tax.js +28 -0
  85. package/dist/src/engine/terminal.d.ts +4 -0
  86. package/dist/src/engine/terminal.js +20 -0
  87. package/dist/src/engine/test-clocks.d.ts +5 -0
  88. package/dist/src/engine/test-clocks.js +9 -0
  89. package/dist/src/engine/tokens.d.ts +7 -0
  90. package/dist/src/engine/tokens.js +20 -0
  91. package/dist/src/engine/transfers.d.ts +2 -0
  92. package/dist/src/engine/transfers.js +1 -0
  93. package/dist/src/engine/treasury.d.ts +23 -0
  94. package/dist/src/engine/treasury.js +45 -0
  95. package/dist/src/{stripe-version.js → engine/version.js} +4 -3
  96. package/dist/src/engine/webhook-endpoints.d.ts +1 -0
  97. package/dist/src/engine/webhook-endpoints.js +12 -0
  98. package/dist/src/engine/webhooks.d.ts +31 -0
  99. package/dist/src/engine/webhooks.js +68 -0
  100. package/dist/src/fetch.d.ts +2 -0
  101. package/dist/src/fetch.js +26 -0
  102. package/dist/src/generated/surface.gen.json +1 -1
  103. package/dist/src/generated/ui.gen.json +1 -1
  104. package/dist/src/index.d.ts +5 -14
  105. package/dist/src/index.js +8 -75
  106. package/dist/src/manifest.js +232 -758
  107. package/dist/src/screens/billing-portal.d.ts +3 -0
  108. package/dist/src/screens/{portal.js → billing-portal.js} +28 -54
  109. package/dist/src/screens/checkout.d.ts +2 -31
  110. package/dist/src/screens/checkout.js +32 -121
  111. package/dist/src/screens/connect-oauth.d.ts +3 -27
  112. package/dist/src/screens/connect-oauth.js +51 -132
  113. package/dist/src/screens/connect-onboarding.d.ts +3 -0
  114. package/dist/src/screens/{onboarding.js → connect-onboarding.js} +36 -102
  115. package/dist/src/screens/dashboard.d.ts +3 -0
  116. package/dist/src/screens/dashboard.js +117 -0
  117. package/dist/src/screens/shared.d.ts +27 -0
  118. package/dist/src/screens/shared.js +87 -0
  119. package/dist/src/screens/stripe-js.d.ts +3 -0
  120. package/dist/src/screens/stripe-js.js +135 -0
  121. package/dist/src/semantics/account_links.d.ts +2 -0
  122. package/dist/src/semantics/account_links.js +22 -0
  123. package/dist/src/semantics/accounts.d.ts +4 -0
  124. package/dist/src/semantics/accounts.js +38 -0
  125. package/dist/src/semantics/apps.d.ts +4 -0
  126. package/dist/src/semantics/{apps-secrets.js → apps.js} +14 -26
  127. package/dist/src/semantics/around.d.ts +2 -0
  128. package/dist/src/semantics/around.js +16 -0
  129. package/dist/src/semantics/balance.d.ts +2 -11
  130. package/dist/src/semantics/balance.js +3 -194
  131. package/dist/src/semantics/balance_transactions.d.ts +2 -0
  132. package/dist/src/semantics/balance_transactions.js +15 -0
  133. package/dist/src/semantics/billing_portal.d.ts +5 -0
  134. package/dist/src/semantics/billing_portal.js +42 -0
  135. package/dist/src/semantics/charges.d.ts +3 -28
  136. package/dist/src/semantics/charges.js +5 -202
  137. package/dist/src/semantics/checkout.d.ts +3 -20
  138. package/dist/src/semantics/checkout.js +8 -261
  139. package/dist/src/semantics/clock.d.ts +2 -0
  140. package/dist/src/semantics/clock.js +21 -0
  141. package/dist/src/semantics/coupons.d.ts +2 -6
  142. package/dist/src/semantics/coupons.js +2 -60
  143. package/dist/src/semantics/customers.d.ts +6 -6
  144. package/dist/src/semantics/customers.js +27 -387
  145. package/dist/src/semantics/disputes.d.ts +2 -2
  146. package/dist/src/semantics/disputes.js +3 -49
  147. package/dist/src/semantics/doors.d.ts +23 -0
  148. package/dist/src/semantics/doors.js +73 -0
  149. package/dist/src/semantics/entitlements.d.ts +2 -2
  150. package/dist/src/semantics/entitlements.js +5 -50
  151. package/dist/src/semantics/events.d.ts +12 -0
  152. package/dist/src/semantics/events.js +36 -0
  153. package/dist/src/semantics/index.d.ts +2 -0
  154. package/dist/src/semantics/index.js +107 -0
  155. package/dist/src/semantics/invoice_payments.d.ts +2 -0
  156. package/dist/src/semantics/invoice_payments.js +12 -0
  157. package/dist/src/semantics/invoiceitems.d.ts +3 -0
  158. package/dist/src/semantics/invoiceitems.js +59 -0
  159. package/dist/src/semantics/invoices.d.ts +16 -18
  160. package/dist/src/semantics/invoices.js +118 -444
  161. package/dist/src/semantics/payment_intents.d.ts +4 -0
  162. package/dist/src/semantics/payment_intents.js +79 -0
  163. package/dist/src/semantics/payment_methods.d.ts +5 -0
  164. package/dist/src/semantics/payment_methods.js +39 -0
  165. package/dist/src/semantics/payouts.d.ts +2 -0
  166. package/dist/src/semantics/payouts.js +21 -0
  167. package/dist/src/semantics/prices.d.ts +4 -0
  168. package/dist/src/semantics/prices.js +37 -0
  169. package/dist/src/semantics/products.d.ts +6 -2
  170. package/dist/src/semantics/products.js +15 -130
  171. package/dist/src/semantics/promotion_codes.d.ts +3 -0
  172. package/dist/src/semantics/promotion_codes.js +35 -0
  173. package/dist/src/semantics/radar.d.ts +2 -2
  174. package/dist/src/semantics/radar.js +5 -74
  175. package/dist/src/semantics/refunds.d.ts +3 -9
  176. package/dist/src/semantics/refunds.js +6 -192
  177. package/dist/src/semantics/setup_intents.d.ts +3 -0
  178. package/dist/src/semantics/{setup-intents.js → setup_intents.js} +10 -53
  179. package/dist/src/semantics/shared.d.ts +822 -47
  180. package/dist/src/semantics/shared.js +3044 -78
  181. package/dist/src/semantics/states.d.ts +531 -0
  182. package/dist/src/semantics/states.js +852 -0
  183. package/dist/src/semantics/subscription_items.d.ts +5 -0
  184. package/dist/src/semantics/subscription_items.js +55 -0
  185. package/dist/src/semantics/subscription_schedules.d.ts +4 -0
  186. package/dist/src/semantics/subscription_schedules.js +60 -0
  187. package/dist/src/semantics/subscriptions.d.ts +7 -11
  188. package/dist/src/semantics/subscriptions.js +44 -415
  189. package/dist/src/semantics/test_helpers.d.ts +6 -0
  190. package/dist/src/semantics/{test-clocks.js → test_helpers.js} +14 -40
  191. package/dist/src/semantics/transfers.d.ts +2 -2
  192. package/dist/src/semantics/transfers.js +6 -101
  193. package/dist/src/server.d.ts +7 -0
  194. package/dist/src/server.js +7 -0
  195. package/generated/pack-facts.json +94 -0
  196. package/package.json +10 -20
  197. package/spec/SOURCE.md +14 -0
  198. package/spec/fixtures3.json.gz +0 -0
  199. package/spec/openapi.json.gz +0 -0
  200. package/spec/patches.json +34 -0
  201. package/spec/sources.json +378 -0
  202. package/src/cli.ts +12 -26
  203. package/src/engine/after-payment.ts +31 -0
  204. package/src/engine/apps-secrets.ts +4 -0
  205. package/src/engine/balance.ts +33 -0
  206. package/src/engine/billing.ts +16 -0
  207. package/src/engine/charges.ts +19 -0
  208. package/src/engine/checkout.ts +40 -0
  209. package/src/engine/common.ts +47 -0
  210. package/src/engine/connect-settings.ts +23 -0
  211. package/src/engine/connect.ts +184 -0
  212. package/src/engine/coupons.ts +5 -0
  213. package/src/engine/credit-notes.ts +6 -0
  214. package/src/engine/customers.ts +35 -0
  215. package/src/engine/display.ts +19 -0
  216. package/src/engine/disputes.ts +9 -0
  217. package/src/engine/entitlements.ts +5 -0
  218. package/src/engine/ephemeral-keys.ts +3 -0
  219. package/src/engine/files.ts +41 -0
  220. package/src/engine/front.ts +31 -0
  221. package/src/engine/invoices.ts +71 -0
  222. package/src/engine/issuing.ts +61 -0
  223. package/src/engine/ledger.ts +50 -0
  224. package/src/engine/oauth.ts +19 -0
  225. package/src/engine/onboarding.ts +54 -0
  226. package/src/{stripe-params.ts → engine/params.ts} +8 -56
  227. package/src/engine/payment-intents.ts +33 -0
  228. package/src/engine/payment-links.ts +6 -0
  229. package/src/engine/payment-methods.ts +25 -0
  230. package/src/engine/plans.ts +42 -0
  231. package/src/engine/platform.ts +47 -0
  232. package/src/engine/products.ts +3 -0
  233. package/src/engine/radar.ts +6 -0
  234. package/src/engine/refunds.ts +29 -0
  235. package/src/engine/renewals.ts +73 -0
  236. package/src/engine/setup-intents.ts +5 -0
  237. package/src/{stripe-twin.ts → engine/stripe.ts} +28 -323
  238. package/src/engine/subscription-schedules.ts +10 -0
  239. package/src/engine/subscriptions.ts +40 -0
  240. package/src/engine/tax.ts +39 -0
  241. package/src/engine/terminal.ts +23 -0
  242. package/src/engine/test-clocks.ts +11 -0
  243. package/src/engine/tokens.ts +22 -0
  244. package/src/engine/transfers.ts +23 -0
  245. package/src/engine/treasury.ts +47 -0
  246. package/src/{stripe-version.ts → engine/version.ts} +4 -3
  247. package/src/engine/webhook-endpoints.ts +14 -0
  248. package/src/engine/webhooks.ts +72 -0
  249. package/src/fetch.ts +27 -0
  250. package/src/generated/surface.gen.json +1 -1
  251. package/src/generated/ui.gen.json +1 -1
  252. package/src/index.ts +8 -116
  253. package/src/manifest.ts +234 -786
  254. package/src/screens/{portal.tsx → billing-portal.tsx} +32 -37
  255. package/src/screens/checkout.tsx +33 -95
  256. package/src/screens/connect-oauth.tsx +64 -88
  257. package/src/screens/{onboarding.tsx → connect-onboarding.tsx} +38 -82
  258. package/src/screens/dashboard.tsx +198 -0
  259. package/src/screens/shared.tsx +84 -0
  260. package/src/screens/stripe-js.ts +170 -0
  261. package/src/semantics/account_links.ts +21 -0
  262. package/src/semantics/accounts.ts +43 -0
  263. package/src/semantics/{apps-secrets.ts → apps.ts} +17 -32
  264. package/src/semantics/around.ts +20 -0
  265. package/src/semantics/balance.ts +6 -207
  266. package/src/semantics/balance_transactions.ts +20 -0
  267. package/src/semantics/billing_portal.ts +51 -0
  268. package/src/semantics/charges.ts +12 -211
  269. package/src/semantics/checkout.ts +13 -268
  270. package/src/semantics/clock.ts +26 -0
  271. package/src/semantics/coupons.ts +5 -67
  272. package/src/semantics/customers.ts +29 -392
  273. package/src/semantics/disputes.ts +9 -59
  274. package/src/semantics/doors.ts +80 -0
  275. package/src/semantics/entitlements.ts +9 -56
  276. package/src/semantics/events.ts +44 -0
  277. package/src/semantics/index.ts +111 -0
  278. package/src/semantics/invoice_payments.ts +18 -0
  279. package/src/semantics/invoiceitems.ts +59 -0
  280. package/src/semantics/invoices.ts +109 -443
  281. package/src/semantics/payment_intents.ts +83 -0
  282. package/src/semantics/payment_methods.ts +43 -0
  283. package/src/semantics/payouts.ts +23 -0
  284. package/src/semantics/prices.ts +42 -0
  285. package/src/semantics/products.ts +18 -142
  286. package/src/semantics/promotion_codes.ts +39 -0
  287. package/src/semantics/radar.ts +8 -77
  288. package/src/semantics/refunds.ts +9 -212
  289. package/src/semantics/setup_intents.ts +45 -0
  290. package/src/semantics/shared.ts +3092 -106
  291. package/src/semantics/states.ts +883 -0
  292. package/src/semantics/subscription_items.ts +54 -0
  293. package/src/semantics/subscription_schedules.ts +66 -0
  294. package/src/semantics/subscriptions.ts +49 -432
  295. package/src/semantics/{test-clocks.ts → test_helpers.ts} +23 -47
  296. package/src/semantics/transfers.ts +11 -131
  297. package/src/server.ts +8 -0
  298. package/client/dashboard-api.ts +0 -286
  299. package/client/stripe-mirror.css +0 -277
  300. package/client/stripe-mirror.tsx +0 -1559
  301. package/dist/client/dashboard-api.d.ts +0 -107
  302. package/dist/client/dashboard-api.js +0 -238
  303. package/dist/client/dashboard-api.ts +0 -286
  304. package/dist/client/stripe-mirror.bundle.js +0 -236
  305. package/dist/client/stripe-mirror.css +0 -277
  306. package/dist/client/stripe-mirror.d.ts +0 -134
  307. package/dist/client/stripe-mirror.js +0 -859
  308. package/dist/client/stripe-mirror.tsx +0 -1559
  309. package/dist/src/screens/connect-settings.js +0 -103
  310. package/dist/src/screens/consent-skin.d.ts +0 -4
  311. package/dist/src/screens/consent-skin.js +0 -18
  312. package/dist/src/screens/financial-connections.d.ts +0 -5
  313. package/dist/src/screens/financial-connections.js +0 -90
  314. package/dist/src/screens/identity.d.ts +0 -5
  315. package/dist/src/screens/identity.js +0 -86
  316. package/dist/src/screens/onboarding.d.ts +0 -13
  317. package/dist/src/screens/portal.d.ts +0 -5
  318. package/dist/src/screens/public-details.d.ts +0 -5
  319. package/dist/src/screens/public-details.js +0 -90
  320. package/dist/src/semantics/after-payment.d.ts +0 -22
  321. package/dist/src/semantics/after-payment.js +0 -99
  322. package/dist/src/semantics/apps-secrets.d.ts +0 -2
  323. package/dist/src/semantics/billing.d.ts +0 -2
  324. package/dist/src/semantics/billing.js +0 -220
  325. package/dist/src/semantics/connect.d.ts +0 -5
  326. package/dist/src/semantics/connect.js +0 -493
  327. package/dist/src/semantics/credit-notes.d.ts +0 -2
  328. package/dist/src/semantics/credit-notes.js +0 -172
  329. package/dist/src/semantics/ephemeral-keys.d.ts +0 -2
  330. package/dist/src/semantics/ephemeral-keys.js +0 -34
  331. package/dist/src/semantics/files.d.ts +0 -2
  332. package/dist/src/semantics/files.js +0 -125
  333. package/dist/src/semantics/issuing.d.ts +0 -13
  334. package/dist/src/semantics/issuing.js +0 -575
  335. package/dist/src/semantics/ledger.d.ts +0 -59
  336. package/dist/src/semantics/ledger.js +0 -200
  337. package/dist/src/semantics/payment-intents.d.ts +0 -18
  338. package/dist/src/semantics/payment-intents.js +0 -452
  339. package/dist/src/semantics/payment-links.d.ts +0 -2
  340. package/dist/src/semantics/payment-links.js +0 -133
  341. package/dist/src/semantics/payment-methods.d.ts +0 -20
  342. package/dist/src/semantics/payment-methods.js +0 -140
  343. package/dist/src/semantics/plans.d.ts +0 -5
  344. package/dist/src/semantics/plans.js +0 -121
  345. package/dist/src/semantics/platform.d.ts +0 -9
  346. package/dist/src/semantics/platform.js +0 -206
  347. package/dist/src/semantics/renewals.d.ts +0 -47
  348. package/dist/src/semantics/renewals.js +0 -281
  349. package/dist/src/semantics/setup-intents.d.ts +0 -2
  350. package/dist/src/semantics/subscription-schedules.d.ts +0 -2
  351. package/dist/src/semantics/subscription-schedules.js +0 -119
  352. package/dist/src/semantics/tax.d.ts +0 -2
  353. package/dist/src/semantics/tax.js +0 -197
  354. package/dist/src/semantics/terminal.d.ts +0 -5
  355. package/dist/src/semantics/terminal.js +0 -182
  356. package/dist/src/semantics/test-clocks.d.ts +0 -6
  357. package/dist/src/semantics/tokens.d.ts +0 -4
  358. package/dist/src/semantics/tokens.js +0 -44
  359. package/dist/src/semantics/treasury.d.ts +0 -2
  360. package/dist/src/semantics/treasury.js +0 -377
  361. package/dist/src/semantics/webhook-endpoints.d.ts +0 -3
  362. package/dist/src/semantics/webhook-endpoints.js +0 -85
  363. package/dist/src/stripe-budget.d.ts +0 -55
  364. package/dist/src/stripe-budget.js +0 -155
  365. package/dist/src/stripe-capabilities.d.ts +0 -3
  366. package/dist/src/stripe-capabilities.js +0 -5836
  367. package/dist/src/stripe-conformance.d.ts +0 -43
  368. package/dist/src/stripe-conformance.js +0 -105
  369. package/dist/src/stripe-connector.d.ts +0 -161
  370. package/dist/src/stripe-connector.js +0 -414
  371. package/dist/src/stripe-emit.d.ts +0 -2
  372. package/dist/src/stripe-emit.js +0 -145
  373. package/dist/src/stripe-events.d.ts +0 -93
  374. package/dist/src/stripe-events.js +0 -392
  375. package/dist/src/stripe-js.d.ts +0 -4
  376. package/dist/src/stripe-js.js +0 -70
  377. package/dist/src/stripe-mirror-ui.d.ts +0 -15
  378. package/dist/src/stripe-mirror-ui.js +0 -87
  379. package/dist/src/stripe-perform-harness.d.ts +0 -9
  380. package/dist/src/stripe-perform-harness.js +0 -26
  381. package/dist/src/stripe-server.d.ts +0 -33
  382. package/dist/src/stripe-server.js +0 -429
  383. package/dist/src/stripe-shared.d.ts +0 -109
  384. package/dist/src/stripe-shared.js +0 -276
  385. package/dist/src/stripe-ui-conformance.d.ts +0 -5
  386. package/dist/src/stripe-ui-conformance.js +0 -79
  387. package/dist/src/stripe-ui-structure.d.ts +0 -3
  388. package/dist/src/stripe-ui-structure.js +0 -168
  389. package/dist/test-fixtures/stripe-known-deviations.json +0 -110
  390. package/dist/test-fixtures/stripe-openapi-operations.SOURCE.md +0 -14
  391. package/dist/test-fixtures/stripe-openapi-operations.json +0 -4717
  392. package/dist/test-fixtures/stripe-schemas.SOURCE.md +0 -35
  393. package/dist/test-fixtures/stripe-schemas.json +0 -3813
  394. package/src/screens/connect-settings.tsx +0 -121
  395. package/src/screens/consent-skin.ts +0 -20
  396. package/src/screens/financial-connections.tsx +0 -101
  397. package/src/screens/identity.tsx +0 -96
  398. package/src/screens/public-details.tsx +0 -105
  399. package/src/semantics/after-payment.ts +0 -118
  400. package/src/semantics/billing.ts +0 -216
  401. package/src/semantics/connect.ts +0 -487
  402. package/src/semantics/credit-notes.ts +0 -168
  403. package/src/semantics/ephemeral-keys.ts +0 -34
  404. package/src/semantics/files.ts +0 -143
  405. package/src/semantics/issuing.ts +0 -590
  406. package/src/semantics/ledger.ts +0 -253
  407. package/src/semantics/payment-intents.ts +0 -463
  408. package/src/semantics/payment-links.ts +0 -148
  409. package/src/semantics/payment-methods.ts +0 -145
  410. package/src/semantics/plans.ts +0 -131
  411. package/src/semantics/platform.ts +0 -220
  412. package/src/semantics/renewals.ts +0 -279
  413. package/src/semantics/setup-intents.ts +0 -87
  414. package/src/semantics/subscription-schedules.ts +0 -129
  415. package/src/semantics/tax.ts +0 -220
  416. package/src/semantics/terminal.ts +0 -195
  417. package/src/semantics/tokens.ts +0 -52
  418. package/src/semantics/treasury.ts +0 -383
  419. package/src/semantics/webhook-endpoints.ts +0 -87
  420. package/src/stripe-budget.ts +0 -181
  421. package/src/stripe-capabilities.ts +0 -5651
  422. package/src/stripe-conformance.ts +0 -124
  423. package/src/stripe-connector.ts +0 -462
  424. package/src/stripe-emit.ts +0 -157
  425. package/src/stripe-events.ts +0 -434
  426. package/src/stripe-js.ts +0 -70
  427. package/src/stripe-mirror-ui.ts +0 -90
  428. package/src/stripe-perform-harness.ts +0 -29
  429. package/src/stripe-server.ts +0 -393
  430. package/src/stripe-shared.ts +0 -297
  431. package/src/stripe-ui-conformance.ts +0 -88
  432. package/src/stripe-ui-structure.ts +0 -182
  433. package/test-fixtures/stripe-known-deviations.json +0 -110
  434. package/test-fixtures/stripe-openapi-operations.json +0 -4717
  435. package/test-fixtures/stripe-schemas.json +0 -3813
  436. /package/dist/src/{screens → engine}/industries.d.ts +0 -0
  437. /package/dist/src/{screens → engine}/industries.js +0 -0
  438. /package/dist/src/{semantics → engine}/test-cards.d.ts +0 -0
  439. /package/dist/src/{semantics → engine}/test-cards.js +0 -0
  440. /package/dist/src/{stripe-version.d.ts → engine/version.d.ts} +0 -0
  441. /package/src/{screens → engine}/industries.ts +0 -0
  442. /package/src/{semantics → engine}/test-cards.ts +0 -0
@@ -1,124 +0,0 @@
1
- // Stripe twin conformance (scorecard R2) — spec-conformance against Stripe's
2
- // PUBLISHED OpenAPI, offline (no API key). Stripe is the twin we CANNOT dual-run
3
- // against a live sandbox, so the published spec carries the most weight here.
4
- //
5
- // For every resource the twin emits, validate its REST body against the vendored
6
- // per-object schema (test-fixtures/stripe-schemas.json) via the shared
7
- // specConformance harness: declared TYPES, REQUIRED fields (omission), and ENUMS.
8
- // Polymorphic Stripe fields (anyOf/$ref — `customer` may be id|object|null) are
9
- // left untyped in the projection and so are not type-checked, avoiding false
10
- // positives. Fields the twin deliberately does not model are DECLARED in
11
- // stripe-known-deviations.json with reasons; an undeclared missing-required field,
12
- // a wrong type, an enum violation, or a fabricated field all fail CI.
13
- import { readFile } from 'node:fs/promises';
14
- import { twinResources } from '@volter/world-core';
15
- import { specConformance } from '@volter/world-tooling';
16
- import type { TwinResource } from '@volter/world-core';
17
- import { OBJECT_NAME } from './stripe-twin.ts';
18
- import { INCLUDABLE_FIELDS, render } from './stripe-version.ts';
19
-
20
- type JsonSchema = specConformance.JsonSchema;
21
- type KnownDeviation = specConformance.KnownDeviation;
22
- type SpecViolation = specConformance.SpecViolation;
23
-
24
- // twin resource type -> Stripe OpenAPI object name (the resources the twin serves)
25
- const TYPE_TO_OBJECT: Record<string, string> = {
26
- charge: 'charge', customer: 'customer', payment_intent: 'payment_intent', refund: 'refund',
27
- subscription: 'subscription', setup_intent: 'setup_intent', payment_method: 'payment_method',
28
- price: 'price', product: 'product', invoice: 'invoice', invoiceitem: 'invoiceitem',
29
- dispute: 'dispute', payout: 'payout', balance_transaction: 'balance_transaction', event: 'event',
30
- checkout_session: 'checkout.session', billing_portal_session: 'billing_portal.session',
31
- billing_portal_configuration: 'billing_portal.configuration',
32
- account: 'account', transfer: 'transfer',
33
- tax_rate: 'tax_rate', tax_calculation: 'tax.calculation', tax_registration: 'tax.registration',
34
- credit_note: 'credit_note', tax_id: 'tax_id', customer_balance_transaction: 'customer_balance_transaction',
35
- issuing_authorization: 'issuing.authorization', issuing_card: 'issuing.card',
36
- issuing_cardholder: 'issuing.cardholder', issuing_transaction: 'issuing.transaction',
37
- };
38
-
39
- export type StripeSchemas = Record<string, JsonSchema>;
40
- export type StripeViolation = SpecViolation & { object: string; id: string };
41
- export type StripeConformanceReport = {
42
- ok: boolean;
43
- resourcesChecked: number;
44
- fieldsChecked: number;
45
- violations: StripeViolation[];
46
- knownIgnored: number;
47
- /** fields checked per twin resource type: a type the check skipped (no schema) is absent */
48
- fieldsByType: Record<string, number>;
49
- };
50
-
51
- const isTwinExtra = (k: string): boolean => k.startsWith('_');
52
- const fixturePath = (name: string): string => new URL(`../test-fixtures/${name}`, import.meta.url).pathname;
53
-
54
- // The emitted REST body == the twin's view(): strip internal fields, add object + id.
55
- // Mirrors stripe-twin.ts view(): a vendor `type` field collides with the kernel
56
- // discriminator, so it is stored under `_stripe_type` and restored to `type` on emit.
57
- // ...and then rendered in the version a caller that pins none is served (stripe-version.ts render): since 24e757f60
58
- // the twin KEEPS objects in the 2024-06-20 shape its rules were written against and answers them in the served
59
- // version's (basil onward moved an invoice's subscription and charge, a charge's source, ...), so the stored row is
60
- // not what any client receives. The schemas are projected from a post-basil spec, so the answer is what is checked.
61
- function emitted(r: TwinResource): Record<string, unknown> {
62
- const { type, updatedAt, _stripe_type, ...rest } = r as TwinResource & { _stripe_type?: unknown };
63
- const out: Record<string, unknown> = { object: OBJECT_NAME[type] ?? type, ...rest, id: r.id };
64
- if (_stripe_type !== undefined) out.type = _stripe_type;
65
- // expanded: an includable field (a session's line_items, a charge's refunds) is answered only when a request asks, and
66
- // the check reads the answer that carries it so its shape is still checked
67
- return render(out, undefined, INCLUDABLE_FIELDS) as Record<string, unknown>;
68
- }
69
-
70
- /** Load the vendored per-object Stripe JSON Schemas (type+required+enum from OpenAPI). */
71
- export async function loadStripeSchemas(): Promise<StripeSchemas> {
72
- return JSON.parse(await readFile(fixturePath('stripe-schemas.json'), 'utf8')) as StripeSchemas;
73
- }
74
-
75
- /** Load the declared scope (Stripe fields the twin does not model, + reasons). */
76
- export async function loadStripeKnownDeviations(): Promise<KnownDeviation[]> {
77
- const doc = JSON.parse(await readFile(fixturePath('stripe-known-deviations.json'), 'utf8')) as { deviations: KnownDeviation[] };
78
- return doc.deviations;
79
- }
80
-
81
- /**
82
- * Validate every Stripe resource the twin serves (in `root`) against the published
83
- * schema. A wrong type, an undeclared missing-required field, an enum violation, or
84
- * a fabricated field fails the report.
85
- */
86
- export function checkStripeConformance(schemas: StripeSchemas, opts: { root?: string; known?: KnownDeviation[] } = {}): StripeConformanceReport {
87
- const resources = twinResources('stripe', opts.root);
88
- const violations: StripeViolation[] = [];
89
- let fieldsChecked = 0;
90
- let knownIgnored = 0;
91
- const fieldsByType: Record<string, number> = {};
92
- for (const r of resources) {
93
- const objectName = TYPE_TO_OBJECT[r.type];
94
- if (!objectName) continue;
95
- const schema = schemas[objectName];
96
- if (!schema) continue;
97
- const rep = specConformance.checkSpecConformance(emitted(r), schema, { exemptKey: isTwinExtra, known: opts.known ?? [] });
98
- fieldsChecked += rep.fieldsChecked;
99
- fieldsByType[r.type] = (fieldsByType[r.type] ?? 0) + rep.fieldsChecked;
100
- knownIgnored += rep.knownIgnored;
101
- for (const v of rep.violations) violations.push({ ...v, object: objectName, id: r.id });
102
- }
103
- return { ok: violations.length === 0, resourcesChecked: resources.length, fieldsChecked, violations, knownIgnored, fieldsByType };
104
- }
105
-
106
- export type StripeCoverageReport = specConformance.SpecCoverageReport & { object: string };
107
-
108
- /**
109
- * Per-object coverage: of the fields each Stripe object declares, which does the
110
- * twin emit? Computed over one representative resource of each type present in
111
- * `root` — the "what we emulate / what we're missing" report, from the spec.
112
- */
113
- export function stripeCoverage(schemas: StripeSchemas, opts: { root?: string } = {}): StripeCoverageReport[] {
114
- const byType = new Map<string, TwinResource>();
115
- for (const r of twinResources('stripe', opts.root)) if (!byType.has(r.type)) byType.set(r.type, r);
116
- const out: StripeCoverageReport[] = [];
117
- for (const [type, r] of byType) {
118
- const objectName = TYPE_TO_OBJECT[type];
119
- const schema = objectName ? schemas[objectName] : undefined;
120
- if (!objectName || !schema) continue;
121
- out.push({ object: objectName, ...specConformance.specCoverage(schema, emitted(r), { exemptKey: isTwinExtra }) });
122
- }
123
- return out;
124
- }
@@ -1,462 +0,0 @@
1
- // Stripe CONNECTOR — the live-vendor pull/push path that gives the Stripe twin the
2
- // full "git for SaaS" lifecycle (the piece every other connector pack already had,
3
- // and Stripe was missing entirely).
4
- //
5
- // PULL (real → twin): fetch real Stripe objects, map snake_case → SyncResource[],
6
- // fold into the tree through the kernel's observe (its own diff, so a
7
- // re-pull of identical state appends nothing).
8
- // PUSH (twin → real): for every PENDING local action, call the real Stripe REST API
9
- // and the head confirms on success — which records the confirmed
10
- // fields as an observed event and suppresses the local
11
- // projection (the change is counted exactly once).
12
- //
13
- // The vendor I/O is an INJECTED executor (the auth-boundary, hard-problem #6): the
14
- // kernel and this pack hold NO Stripe key and import NO network client.
15
- // - offline/tests pass a fake executor (deterministic, no network),
16
- // - live runs pass `liveStripeExecute(apiKey)` (the user's own secret key).
17
- // Same code path either way, so the connector is fully exercisable offline AND
18
- // runnable against a real account.
19
- import { assertBudgetGuardIntact, observeResources } from '@volter/world-core';
20
- import type { PerformContext, PushOutcome, RemoteExecute, SyncResource, TwinAction } from '@volter/world-core';
21
- import { StripeBudget, StripeBudgetError, stripeCallWeight, type StripeBudgetOptions } from './stripe-budget.ts';
22
-
23
- const SERVICE = 'stripe';
24
-
25
- // Subject types that are twin-internal and are NEVER pushed to real Stripe: the recorded
26
- // Stripe `event` envelopes (the local Events-API store) and idempotency bookkeeping.
27
- export const INTERNAL_SUBJECT_TYPES = new Set(['event', '_idempotency']);
28
-
29
- /**
30
- * The injected real-Stripe boundary. `request` issues ONE Stripe REST call:
31
- * method — 'GET' | 'POST' | 'DELETE'
32
- * path — e.g. '/v1/customers' or '/v1/customers/cus_123'
33
- * params — form params (POST) or query filters (GET); omitted for plain GETs
34
- * It returns the parsed JSON body (a Stripe object or `{ object: 'list', data }`) or
35
- * a Stripe `{ error }` envelope. A real client (the `stripe` SDK or a raw fetch
36
- * wrapper) is structurally assignable; tests pass a fake.
37
- */
38
- export type StripeExecute = (
39
- method: 'GET' | 'POST' | 'DELETE',
40
- path: string,
41
- params?: Record<string, unknown>,
42
- ) => Promise<{ object?: string; data?: any; error?: { message?: string; type?: string }; [k: string]: unknown }>;
43
-
44
- /** Construction options for the live executor. `budget` cannot be null and cannot be loosened. */
45
- export type LiveStripeOptions = {
46
- /** Injected `fetch`, so a test can COUNT the requests the guard did or did not let through. */
47
- fetchImpl?: typeof fetch;
48
- /** An existing budget to share across executors. Omit and one is constructed. Cannot be null. */
49
- budget?: StripeBudget;
50
- /** Construction options for the default budget (ledger path, clock). Cannot loosen it. */
51
- budgetOptions?: StripeBudgetOptions;
52
- };
53
-
54
- /**
55
- * A live executor against the real Stripe REST API (secret key = the user's own).
56
- * Form-encodes POST params; sends GET filters as a query string. Never imported by
57
- * the pack's own code path — only constructed by a caller that opts into real I/O.
58
- *
59
- * THIS IS THE ONE PLACE this pack issues a live `api.stripe.com` request, and therefore the one
60
- * place the rate budget has to be enforced — and Stripe is the pack where a runaway loop does not
61
- * merely annoy a vendor, it MOVES MONEY. EVERY call is guarded: the budget is charged BEFORE the
62
- * request goes out (`checkBudget`, which THROWS `StripeBudgetError` instead of returning when the
63
- * ceiling or a cooldown says stop) and the response is fed back (`recordCall`) so a `Retry-After` /
64
- * 429 signal becomes a persisted cooldown that makes every later call fail fast WITHOUT touching
65
- * Stripe. There is deliberately no OPTION to disable the guard, and no
66
- * value a caller can pass for `budget` that yields an unguarded client. That is NOT immunity from
67
- * a caller who WANTS one: a fresh `budgetOptions.path` per construction, or an injected clock,
68
- * restores the allowance, because the seam tests need cannot be denied to a determined caller in
69
- * the same process. The kernel header states that limit and this does not upgrade it. See `stripe-budget.ts` for why,
70
- * and for the limits of the guarantee.
71
- */
72
- export function liveStripeExecute(
73
- apiKey: string,
74
- base = 'https://api.stripe.com',
75
- opts: LiveStripeOptions = {},
76
- ): StripeExecute {
77
- // `null`/`undefined` (or omitting it) build the default budget. Anything else must be an
78
- // UNMODIFIED StripeBudget: a duck-typed stand-in, a SUBCLASS that overrides `checkBudget`, and a
79
- // Proxy that traps it are all refused, because all three are one-liners that would otherwise
80
- // hand back a client with no ceiling at all (§9 finding, 2026-07-26 — `instanceof` alone was
81
- // not a check). What this cannot stop is deliberate sabotage from inside the process (an
82
- // injected clock, a throwaway ledger path); the kernel's header says so rather than pretending
83
- // otherwise, and this guards the accident and the one-liner, which are the shapes that happen.
84
- const doFetch = opts.fetchImpl ?? fetch;
85
- // The default ledger is keyed by a hash of THIS key — Stripe's limits attach to the account
86
- // behind it, so a cwd-scoped ledger would hand it a fresh allowance per checkout/worktree/CI leg.
87
- // A live key and a test key hash differently, which matches Stripe's separate 100/s and 25/s.
88
- // ONE expression decides which budget is used, so there is no second, weaker test that could
89
- // disagree with the first. `null`/`undefined` (or omitting it) build the default; anything else
90
- // must be an UNMODIFIED StripeBudget — a duck-typed stand-in, a SUBCLASS overriding
91
- // `checkBudget`, and a Proxy trapping it are ALL refused, because each is a one-liner that
92
- // would otherwise hand back a client with no ceiling (§9 finding, 2026-07-26: `instanceof`
93
- // alone was not a check — a subclass satisfied it). What this cannot stop is deliberate
94
- // sabotage from inside the process (an injected clock, a throwaway ledger path); the kernel
95
- // header states that limit rather than pretending otherwise. This closes the accident and the
96
- // one-liner, which are the shapes that actually happen.
97
- const budget = opts.budget !== undefined && opts.budget !== null
98
- ? assertBudgetGuardIntact(opts.budget, StripeBudget, 'liveStripeExecute')
99
- : new StripeBudget({ token: apiKey, ...(opts.budgetOptions ?? {}) });
100
- return async (method, path, params) => {
101
- const headers: Record<string, string> = { Authorization: `Bearer ${apiKey}` };
102
- let url = `${base}${path}`;
103
- const form = encodeForm(params ?? {});
104
- const init: { method: string; headers: Record<string, string>; body?: string } = { method, headers };
105
- if (method === 'GET') {
106
- if (form) url += `?${form}`;
107
- } else if (form) {
108
- headers['Content-Type'] = 'application/x-www-form-urlencoded';
109
- init.body = form;
110
- }
111
- const weight = stripeCallWeight(method, path);
112
- // THROWS instead of calling. Nothing below this line runs when the budget refuses.
113
- const reservation = budget.checkBudget(weight);
114
- const res = await doFetch(url, init);
115
- const resHeaders: Record<string, string> = {};
116
- res.headers.forEach((v: string, k: string) => { resHeaders[k.toLowerCase()] = v; });
117
- const parsed = (await res.json()) as { object?: string; data?: any; error?: { message?: string } };
118
- // Settles the reservation and, on a back-off signal, arms the cooldown. May itself throw (a
119
- // `Retry-After` beyond the cap is not something to sleep off) — the cooldown is persisted
120
- // first either way, so the refusal survives the throw.
121
- // recordCall may THROW after arming the cooldown (a back-off beyond the cap). On a refused
122
- // call that louder refusal wins; an answer Stripe ACCEPTED is kept, so a write that landed is
123
- // never recorded as failed and performed again on retry.
124
- try {
125
- budget.recordCall(weight, resHeaders, { status: res.status, reservation });
126
- } catch (error) {
127
- if (!(error instanceof StripeBudgetError) || !res.ok) throw error;
128
- }
129
- return parsed;
130
- };
131
- }
132
-
133
- // Stripe's nested form encoding (a[b]=c). Only the shapes the connector pushes
134
- // (flat scalars + one level of object nesting, e.g. metadata) are handled.
135
- function encodeForm(params: Record<string, unknown>, prefix = ''): string {
136
- const parts: string[] = [];
137
- for (const [key, value] of Object.entries(params)) {
138
- if (value === undefined) continue;
139
- const name = prefix ? `${prefix}[${key}]` : key;
140
- if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
141
- const nested = encodeForm(value as Record<string, unknown>, name);
142
- if (nested) parts.push(nested);
143
- } else {
144
- parts.push(`${encodeURIComponent(name)}=${encodeURIComponent(value === null ? '' : String(value))}`);
145
- }
146
- }
147
- return parts.join('&');
148
- }
149
-
150
- function listOf(res: { data?: unknown }): any[] {
151
- return Array.isArray(res.data) ? res.data : [];
152
- }
153
- function throwIfError(res: { error?: { message?: string } }, ctx: string): void {
154
- if (res.error) throw new Error(`stripe ${ctx} failed: ${res.error.message ?? 'unknown error'}`);
155
- }
156
-
157
- // ── PULL ────────────────────────────────────────────────────────────────────
158
-
159
- /**
160
- * Map a real-Stripe customer (snake_case) → a twin sync resource. Only the stable
161
- * scalar fields the twin tracks are carried; absent fields become null so the diff
162
- * is faithful (an unset email reads as null, not missing).
163
- */
164
- export function mapCustomer(c: Record<string, unknown>): SyncResource {
165
- return {
166
- type: 'customer',
167
- id: String(c.id),
168
- fields: {
169
- email: (c.email as string) ?? null,
170
- name: (c.name as string) ?? null,
171
- description: (c.description as string) ?? null,
172
- phone: (c.phone as string) ?? null,
173
- currency: (c.currency as string) ?? null,
174
- delinquent: typeof c.delinquent === 'boolean' ? c.delinquent : null,
175
- created: typeof c.created === 'number' ? c.created : null,
176
- livemode: typeof c.livemode === 'boolean' ? c.livemode : null,
177
- },
178
- };
179
- }
180
-
181
- /**
182
- * Map a real-Stripe subscription (snake_case) → a twin sync resource. The single
183
- * subscribed price id is lifted out of items.data[0].price.id (Stripe's nested list
184
- * shape) so the twin tracks a flat, diffable `price`.
185
- */
186
- export function mapSubscription(s: Record<string, unknown>): SyncResource {
187
- const items = s.items as { data?: Array<{ price?: { id?: unknown } }> } | undefined;
188
- const firstPrice = items?.data?.[0]?.price?.id;
189
- return {
190
- type: 'subscription',
191
- id: String(s.id),
192
- fields: {
193
- customer: (s.customer as string) ?? null,
194
- status: (s.status as string) ?? null,
195
- currency: (s.currency as string) ?? null,
196
- cancel_at_period_end: typeof s.cancel_at_period_end === 'boolean' ? s.cancel_at_period_end : null,
197
- price: typeof firstPrice === 'string' ? firstPrice : null,
198
- created: typeof s.created === 'number' ? s.created : null,
199
- livemode: typeof s.livemode === 'boolean' ? s.livemode : null,
200
- },
201
- };
202
- }
203
-
204
- /**
205
- * A GENERIC scalar mapper for the collections that don't need special id-lifting like
206
- * subscriptions do. We carry the stable, diffable scalar fields a twin tracks (anything
207
- * that is a string/number/boolean), dropping nested objects/arrays (which the twin either
208
- * synthesizes or doesn't diff on). This keeps every collection's pull faithful without a
209
- * bespoke mapper per type — vendor-SPECIFIC lifting (subscription.price) stays explicit.
210
- */
211
- export function mapScalarResource(type: string, r: Record<string, unknown>): SyncResource {
212
- const fields: Record<string, unknown> = {};
213
- for (const [k, v] of Object.entries(r)) {
214
- if (k === 'id' || k === 'object') continue;
215
- if (v === null || typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') fields[k] = v ?? null;
216
- }
217
- return { type, id: String(r.id), fields };
218
- }
219
-
220
- // The full set of collections a full sync pulls, mapped to the REST list path + the twin
221
- // resource type. Customers + subscriptions keep their bespoke mappers (id-lifting); the
222
- // rest use the generic scalar mapper.
223
- const PULL_COLLECTIONS: Array<{ path: string; type: string; map?: (r: Record<string, unknown>) => SyncResource; params?: Record<string, unknown> }> = [
224
- { path: '/v1/customers', type: 'customer', map: mapCustomer },
225
- { path: '/v1/subscriptions', type: 'subscription', map: mapSubscription, params: { status: 'all' } },
226
- { path: '/v1/products', type: 'product' },
227
- { path: '/v1/prices', type: 'price' },
228
- { path: '/v1/charges', type: 'charge' },
229
- { path: '/v1/payment_intents', type: 'payment_intent' },
230
- { path: '/v1/invoices', type: 'invoice' },
231
- { path: '/v1/refunds', type: 'refund' },
232
- { path: '/v1/payouts', type: 'payout' },
233
- { path: '/v1/disputes', type: 'dispute' },
234
- { path: '/v1/coupons', type: 'coupon' },
235
- ];
236
-
237
- /** Pull real Stripe customers via the executor and map them to twin sync resources. */
238
- export async function pullStripeCustomers(execute: StripeExecute, opts: { limit?: number } = {}): Promise<SyncResource[]> {
239
- const res = await execute('GET', '/v1/customers', { limit: opts.limit ?? 100 });
240
- throwIfError(res, 'pull customers');
241
- return listOf(res).map(mapCustomer);
242
- }
243
-
244
- /** Pull real Stripe subscriptions via the executor and map them to twin sync resources. */
245
- export async function pullStripeSubscriptions(execute: StripeExecute, opts: { limit?: number } = {}): Promise<SyncResource[]> {
246
- const res = await execute('GET', '/v1/subscriptions', { status: 'all', limit: opts.limit ?? 100 });
247
- throwIfError(res, 'pull subscriptions');
248
- return listOf(res).map(mapSubscription);
249
- }
250
-
251
- /**
252
- * Pull from real Stripe (customers + subscriptions) and fold into the twin (mirror
253
- * seeding). The fold makes a re-pull of identical state a no-op.
254
- */
255
- /** One fold onto the head: protocol 2's observe, one batch, one instant. */
256
- function fold(resources: SyncResource[], opts: { root?: string; occurredAt: string }) {
257
- return observeResources(SERVICE, resources.map((r) => ({ type: r.type, id: r.id, fields: r.fields })), {
258
- ...(opts.root !== undefined ? { root: opts.root } : {}), at: opts.occurredAt, batch: `obs:${SERVICE}:${opts.occurredAt}`,
259
- });
260
- }
261
-
262
- export async function syncStripeFromReal(
263
- execute: StripeExecute,
264
- opts: { root?: string; occurredAt: string; limit?: number },
265
- ): Promise<{ observed: number; deltasAppended: number }> {
266
- const customers = await pullStripeCustomers(execute, { ...(opts.limit ? { limit: opts.limit } : {}) });
267
- const subscriptions = await pullStripeSubscriptions(execute, { ...(opts.limit ? { limit: opts.limit } : {}) });
268
- const resources = [...customers, ...subscriptions];
269
- const result = fold(resources, opts);
270
- return { observed: result.observed, deltasAppended: result.appended };
271
- }
272
-
273
- // ── PUSH ────────────────────────────────────────────────────────────────────
274
-
275
- // Stripe resource type → REST collection segment, for building create paths.
276
- // (Pluralization isn't uniform — e.g. `dispute`→`disputes` is fine but several types
277
- // the twin writes need an explicit mapping so the path matches real Stripe exactly.)
278
- const COLLECTION: Record<string, string> = {
279
- customer: 'customers',
280
- subscription: 'subscriptions',
281
- charge: 'charges',
282
- product: 'products',
283
- price: 'prices',
284
- invoice: 'invoices',
285
- payment_intent: 'payment_intents',
286
- setup_intent: 'setup_intents',
287
- payment_method: 'payment_methods',
288
- refund: 'refunds',
289
- dispute: 'disputes',
290
- payout: 'payouts',
291
- };
292
-
293
- // Verb-style twin operations that map to a real-Stripe SUB-ACTION endpoint
294
- // (POST /v1/<collection>/:id/<verb>) rather than a plain resource update. The twin
295
- // records these verbs verbatim as the operation's suffix (see stripe-twin.ts:
296
- // payment_intent.confirm, setup_intent.confirm, payment_method.detach,
297
- // invoice.finalize|pay|void, dispute.close, payout.cancel). The verb here IS the
298
- // real Stripe path segment, so this stays faithful as the twin grows.
299
- const SUBACTION_VERBS = new Set(['confirm', 'detach', 'finalize', 'pay', 'void', 'close']);
300
-
301
- // `cancel` is resource-specific in real Stripe: a subscription is canceled with
302
- // DELETE /v1/subscriptions/:id, but a payout is canceled with POST
303
- // /v1/payouts/:id/cancel. Anything else canceled via DELETE on the resource is a safe
304
- // default (no other type the twin emits a `.cancel` for uses a sub-action path).
305
- const CANCEL_VIA_SUBACTION = new Set(['payout']);
306
-
307
- /**
308
- * Resolve the REST (method, path) for ONE pending action — faithful to the real
309
- * Stripe REST surface for EVERY write operation the twin records. The action carries
310
- * the twin operation (`<type>.<verb>`) plus its subject:
311
- * - `<type>.create` → POST /v1/<collection>
312
- * - `subscription.cancel` → DELETE /v1/subscriptions/:id
313
- * - `payout.cancel` → POST /v1/payouts/:id/cancel (sub-action)
314
- * - `payment_intent.confirm` → POST /v1/payment_intents/:id/confirm
315
- * - `setup_intent.confirm` → POST /v1/setup_intents/:id/confirm
316
- * - `payment_method.detach` → POST /v1/payment_methods/:id/detach
317
- * - `invoice.finalize|pay|void` → POST /v1/invoices/:id/<verb>
318
- * - `dispute.close` → POST /v1/disputes/:id/close
319
- * - `<type>.update` (or any other verb) → POST /v1/<collection>/:id
320
- *
321
- * Unknown/unsupported operations must FAIL LOUDLY at push time (see pushStripeAction),
322
- * never be silently dropped — so this resolver always returns a concrete request and
323
- * the caller validates the operation is one the twin is allowed to push.
324
- */
325
- export function stripeRequestForAction(action: Pick<TwinAction, 'operation' | 'subject'>): { method: 'POST' | 'DELETE'; path: string } {
326
- const op = action.operation ?? `${action.subject.type}.update`;
327
- const verb = op.includes('.') ? op.slice(op.indexOf('.') + 1) : op;
328
- const collection = COLLECTION[action.subject.type] ?? `${action.subject.type}s`;
329
- const base = `/v1/${collection}`;
330
- if (verb === 'create') return { method: 'POST', path: base };
331
- if (verb === 'cancel') {
332
- return CANCEL_VIA_SUBACTION.has(action.subject.type)
333
- ? { method: 'POST', path: `${base}/${action.subject.id}/cancel` }
334
- : { method: 'DELETE', path: `${base}/${action.subject.id}` };
335
- }
336
- // confirm / detach / finalize / pay / void / close → /:id/<verb>
337
- if (SUBACTION_VERBS.has(verb)) return { method: 'POST', path: `${base}/${action.subject.id}/${verb}` };
338
- // update (and any other plain mutation) → POST the resource itself.
339
- return { method: 'POST', path: `${base}/${action.subject.id}` };
340
- }
341
-
342
- // The twin operations this connector knows how to push to real Stripe. Anything not
343
- // here (a new/unmodeled write op) must FAIL LOUDLY rather than be silently dropped —
344
- // pushing an unrecognized op risks hitting the wrong real endpoint or no-op'ing a
345
- // real change. New twin write ops must be deliberately added here.
346
- const PUSHABLE_VERBS = new Set(['create', 'update', 'cancel', ...SUBACTION_VERBS]);
347
-
348
- /** Throw if `op` is not a write operation this connector can faithfully push. */
349
- /** Whether this operation is one the connector can faithfully send to Stripe. */
350
- export function isPushable(op: string): boolean {
351
- const verb = op.includes('.') ? op.slice(op.indexOf('.') + 1) : op;
352
- return PUSHABLE_VERBS.has(verb);
353
- }
354
- function assertPushable(op: string): void {
355
- const verb = op.includes('.') ? op.slice(op.indexOf('.') + 1) : op;
356
- if (!PUSHABLE_VERBS.has(verb)) {
357
- throw new Error(`stripe push: unsupported operation '${op}' — refusing to silently drop a local write`);
358
- }
359
- }
360
-
361
- /**
362
- * Push ONE pending action to REAL Stripe via the injected executor. Returns the real
363
- * external id (the Stripe object id from the response — for a create that is a freshly
364
- * minted id; for an update it echoes the subject). WRITES TO THE REAL ACCOUNT.
365
- */
366
- export async function pushStripeAction(
367
- execute: StripeExecute,
368
- action: Pick<TwinAction, 'operation' | 'subject' | 'fields'>,
369
- ): Promise<{ externalId: string }> {
370
- assertPushable(action.operation ?? `${action.subject.type}.update`);
371
- const { method, path } = stripeRequestForAction(action);
372
- const res = await execute(method, path, method === 'DELETE' ? undefined : (action.fields ?? {}));
373
- throwIfError(res, `push ${action.subject.type}`);
374
- const id = (res as { id?: unknown }).id;
375
- return { externalId: typeof id === 'string' && id ? id : action.subject.id };
376
- }
377
-
378
- // ── PROTOCOL 2: the pack's half of the real state system ────────────────────────────────────
379
- /** The pack's executor over the kernel's: the same Stripe REST call, carried by the head. Stripe's API is
380
- * form-encoded, which is what `stripeExecute` already speaks — this only carries it. */
381
- export function stripeExecuteOver(execute: RemoteExecute): StripeExecute {
382
- return async (method, path, params) => {
383
- const body = params === undefined ? undefined : encodeForm(params);
384
- const res = await execute({
385
- method, path,
386
- headers: { accept: 'application/json', ...(body === undefined ? {} : { 'content-type': 'application/x-www-form-urlencoded' }) },
387
- ...(body === undefined ? {} : { body }),
388
- });
389
- if (res.body === '') return {};
390
- try { return JSON.parse(res.body) as Record<string, unknown>; } catch { return { error: { type: 'api_error', message: res.body.slice(0, 200) } }; }
391
- };
392
- }
393
- /** The refresh adapter: pull every modeled collection and the webhook endpoints through the executor. */
394
- export async function syncStripeFromRemote(execute: RemoteExecute, opts: { root?: string; origin?: string; occurredAt?: string } = {}): Promise<{ observed: number; deltasAppended: number }> {
395
- const at = opts.occurredAt ?? new Date().toISOString();
396
- const wire = stripeExecuteOver(execute);
397
- const resources = [
398
- ...await pullStripeAll(wire),
399
- ...await pullStripeWebhookEndpoints(wire),
400
- ];
401
- const report = fold(resources, { ...(opts.root !== undefined ? { root: opts.root } : {}), occurredAt: at });
402
- return { observed: report.observed, deltasAppended: report.appended };
403
- }
404
- /** The perform adapter: one entry crosses to Stripe, or settles with the reason it never could. The twin's
405
- * own subject types — the recorded `event` envelopes the write path persists for the Events API, and the
406
- * idempotency bookkeeping — are local state and must never be sent. */
407
- export async function performStripeAction(execute: RemoteExecute, action: TwinAction, _ctx: PerformContext): Promise<PushOutcome> {
408
- const op = action.operation ?? `${action.subject.type}.update`;
409
- if (INTERNAL_SUBJECT_TYPES.has(action.subject.type) || !isPushable(op)) {
410
- return { externalId: action.subject.id, data: { performed: false, reason: `${op} is the twin's own record — nothing at Stripe to write` } };
411
- }
412
- return pushStripeAction(stripeExecuteOver(execute), { operation: op, subject: action.subject, fields: action.fields ?? {} });
413
- }
414
-
415
- // ── FULL SYNC (all collections + webhooks, bi-directional) ───────────────────
416
-
417
- /** Pull a single REST list collection and map each row to a twin sync resource. */
418
- async function pullCollection(execute: StripeExecute, spec: { path: string; type: string; map?: (r: Record<string, unknown>) => SyncResource; params?: Record<string, unknown> }, limit: number): Promise<SyncResource[]> {
419
- const res = await execute('GET', spec.path, { ...(spec.params ?? {}), limit });
420
- throwIfError(res, `pull ${spec.type}`);
421
- const map = spec.map ?? ((r: Record<string, unknown>) => mapScalarResource(spec.type, r));
422
- return listOf(res).map(map);
423
- }
424
-
425
- /**
426
- * Pull EVERY tracked collection from real Stripe (not just customers + subscriptions) in
427
- * one pass and return the combined sync resources. This is the read half of a full sync.
428
- */
429
- export async function pullStripeAll(execute: StripeExecute, opts: { limit?: number } = {}): Promise<SyncResource[]> {
430
- const limit = opts.limit ?? 100;
431
- const all: SyncResource[] = [];
432
- for (const spec of PULL_COLLECTIONS) all.push(...await pullCollection(execute, spec, limit));
433
- return all;
434
- }
435
-
436
- /** Pull the account's registered webhook endpoints (so the twin mirrors them too). */
437
- export async function pullStripeWebhookEndpoints(execute: StripeExecute, opts: { limit?: number } = {}): Promise<SyncResource[]> {
438
- const res = await execute('GET', '/v1/webhook_endpoints', { limit: opts.limit ?? 100 });
439
- throwIfError(res, 'pull webhook_endpoints');
440
- return listOf(res).map((w: Record<string, unknown>) => mapScalarResource('webhook_endpoint', w));
441
- }
442
-
443
- /**
444
- * FULL bi-directional sync over the injected client: (1) PUSH every pending local action to
445
- * real Stripe and confirm it, then (2) PULL all collections + webhook endpoints back and
446
- * fold them into the event log. Pushing first means the pull observes the twin's own writes
447
- * as confirmed external state (no double-count). Returns per-direction counts. Same code
448
- * path offline (fake executor) and live (real key) — D4-faithful.
449
- */
450
- export async function fullSyncStripe(
451
- execute: StripeExecute,
452
- opts: { root?: string; occurredAt: string; limit?: number },
453
- ): Promise<{ observed: number; deltasAppended: number; collections: number }> {
454
- // protocol 2: this is the PULL half only. The push half is the head's — it performs each entry through
455
- // `performStripeAction` and confirms it — so a refresh no longer reconciles both directions at once.
456
- const resources = [
457
- ...await pullStripeAll(execute, { ...(opts.limit ? { limit: opts.limit } : {}) }),
458
- ...await pullStripeWebhookEndpoints(execute, { ...(opts.limit ? { limit: opts.limit } : {}) }),
459
- ];
460
- const pull = fold(resources, opts);
461
- return { observed: pull.observed, deltasAppended: pull.appended, collections: PULL_COLLECTIONS.length + 1 };
462
- }