create-ailk 0.2.0 → 0.3.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 (273) hide show
  1. package/component-catalog.md +50 -53
  2. package/dist/lib/apply-module-patches.d.ts +47 -0
  3. package/dist/lib/apply-module-patches.js +370 -0
  4. package/dist/lib/extract-module-bundle.d.ts +12 -3
  5. package/dist/lib/extract-module-bundle.js +236 -24
  6. package/dist/lib/fetch-module.d.ts +3 -38
  7. package/dist/lib/fetch-module.js +26 -8
  8. package/dist/lib/module-license-gate.d.ts +2 -0
  9. package/dist/lib/module-license-gate.js +1 -0
  10. package/dist/lib/paid-paths.d.ts +16 -0
  11. package/dist/lib/paid-paths.js +90 -0
  12. package/dist/module-architecture.d.ts +139 -23
  13. package/dist/module-architecture.js +668 -25
  14. package/dist/parse-args.d.ts +2 -2
  15. package/dist/parse-args.js +3 -1
  16. package/dist/programmatic.d.ts +2 -1
  17. package/dist/programmatic.js +15 -4
  18. package/dist/surfaces.d.ts +12 -36
  19. package/dist/surfaces.js +35 -5
  20. package/dist/sync-routes.js +31 -1
  21. package/package.json +2 -2
  22. package/templates/.claude/agents/web.md +2 -3
  23. package/templates/.claude/rules/architecture.md +5 -5
  24. package/templates/.claude/skills/README.md +2 -1
  25. package/templates/.env.example +21 -9
  26. package/templates/CONVENTIONS.md +6 -7
  27. package/templates/apps/api/.env.example +9 -0
  28. package/templates/apps/api/CLAUDE.md +33 -5
  29. package/templates/apps/api/package.json +0 -1
  30. package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
  31. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
  32. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
  33. package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
  34. package/templates/apps/api/src/config/index.ts +19 -0
  35. package/templates/apps/api/src/config/modules.ts +12 -14
  36. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  37. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
  38. package/templates/apps/api/src/lib/slice-load.ts +90 -0
  39. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
  40. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
  41. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
  42. package/templates/apps/api/src/openapi/index.ts +9 -3
  43. package/templates/apps/api/src/openapi/spec.ts +505 -65
  44. package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
  45. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
  46. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
  47. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
  48. package/templates/apps/api/src/routes/content/index.ts +38 -11
  49. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +17 -0
  50. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +17 -0
  51. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
  52. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +10 -0
  53. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
  54. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
  55. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +10 -0
  56. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +24 -0
  57. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +17 -0
  58. package/templates/apps/api/src/routes/project-listings/index.ts +37 -10
  59. package/templates/apps/api/src/routes/project-listings/me.ts +10 -0
  60. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
  61. package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
  62. package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
  63. package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
  64. package/templates/apps/api/src/routes/project-listings/start.ts +30 -0
  65. package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
  66. package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
  67. package/templates/apps/api/src/server.ts +459 -135
  68. package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
  69. package/templates/apps/api/src/services/listing-config.ts +58 -0
  70. package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
  71. package/templates/apps/api/src/services/project-listings.ts +229 -4
  72. package/templates/apps/api/src/vercel-handler.ts +60 -26
  73. package/templates/apps/mcp/.env.example +8 -0
  74. package/templates/apps/mcp/CLAUDE.md +2 -2
  75. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
  76. package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
  77. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
  78. package/templates/apps/mcp/src/config/modules.ts +14 -13
  79. package/templates/apps/mcp/src/server.ts +8 -4
  80. package/templates/apps/mcp/src/tools/index.ts +7 -22
  81. package/templates/apps/web/.env.example +15 -0
  82. package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
  83. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
  84. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
  85. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
  86. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
  87. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
  88. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
  89. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
  90. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
  91. package/templates/apps/web/app/llms.txt/route.ts +12 -9
  92. package/templates/apps/web/jest.config.cjs +14 -13
  93. package/templates/apps/web/next.config.mjs +1 -3
  94. package/templates/apps/web/package.json +0 -6
  95. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  96. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  97. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  98. package/templates/apps/web/public/favicon-16x16.png +0 -0
  99. package/templates/apps/web/public/favicon-32x32.png +0 -0
  100. package/templates/apps/web/public/favicon.ico +0 -0
  101. package/templates/apps/web/public/favicon.svg +6 -3
  102. package/templates/apps/web/public/lockup-horizontal.svg +4 -0
  103. package/templates/apps/web/public/logomark.svg +4 -0
  104. package/templates/apps/web/public/site.webmanifest +2 -2
  105. package/templates/apps/web/public/wordmark.svg +4 -0
  106. package/templates/database/CHANGELOG.md +33 -0
  107. package/templates/database/inbox/schema.prisma +67 -0
  108. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
  109. package/templates/database/package.json +1 -1
  110. package/templates/package.json +1 -1
  111. package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
  112. package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
  113. package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
  114. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
  115. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
  116. package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
  117. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
  118. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
  119. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
  120. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
  121. package/templates/apps/api/src/__tests__/server.test.ts +0 -253
  122. package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
  123. package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
  124. package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
  125. package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
  126. package/templates/apps/api/src/bin/seed-presets.ts +0 -58
  127. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
  128. package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
  129. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
  130. package/templates/apps/api/src/lib/stripe.ts +0 -52
  131. package/templates/apps/api/src/lib/tenant-db.ts +0 -225
  132. package/templates/apps/api/src/lib/ws-token.ts +0 -91
  133. package/templates/apps/api/src/middleware/tenant.ts +0 -99
  134. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
  135. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
  136. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
  137. package/templates/apps/api/src/routes/billing/index.ts +0 -36
  138. package/templates/apps/api/src/routes/billing/portal.ts +0 -182
  139. package/templates/apps/api/src/routes/billing/read.ts +0 -75
  140. package/templates/apps/api/src/routes/billing/usage.ts +0 -153
  141. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
  142. package/templates/apps/api/src/routes/checkout/index.ts +0 -13
  143. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
  144. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
  145. package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
  146. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
  147. package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
  148. package/templates/apps/api/src/routes/flows/README.md +0 -147
  149. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
  150. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
  151. package/templates/apps/api/src/routes/flows/index.ts +0 -202
  152. package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
  153. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
  154. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +0 -221
  155. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
  156. package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
  157. package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
  158. package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
  159. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
  160. package/templates/apps/api/src/routes/schedule/index.ts +0 -249
  161. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
  162. package/templates/apps/api/src/routes/slack/actions.ts +0 -177
  163. package/templates/apps/api/src/routes/slack/index.ts +0 -38
  164. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
  165. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
  166. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
  167. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
  168. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
  169. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
  170. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
  171. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
  172. package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
  173. package/templates/apps/api/src/routes/webhooks/README.md +0 -80
  174. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
  175. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
  176. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
  177. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
  178. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
  179. package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
  180. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
  181. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
  182. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
  183. package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
  184. package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
  185. package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
  186. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
  187. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
  188. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
  189. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
  190. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +0 -684
  191. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
  192. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
  193. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
  194. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
  195. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
  196. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
  197. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
  198. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
  199. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
  200. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
  201. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
  202. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
  203. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
  204. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
  205. package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
  206. package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
  207. package/templates/apps/api/src/services/flow-engine.ts +0 -1296
  208. package/templates/apps/api/src/services/lead-promotion.ts +0 -176
  209. package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
  210. package/templates/apps/api/src/services/listing-promotion.ts +0 -342
  211. package/templates/apps/api/src/services/playbook-compile.ts +0 -398
  212. package/templates/apps/api/src/services/playbook-render.ts +0 -263
  213. package/templates/apps/api/src/services/project-listing-decision.ts +0 -510
  214. package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
  215. package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
  216. package/templates/apps/api/src/services/recommender-capture.ts +0 -835
  217. package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
  218. package/templates/apps/api/src/services/scheduling/index.ts +0 -63
  219. package/templates/apps/api/src/services/scheduling/types.ts +0 -88
  220. package/templates/apps/api/src/services/tenant-context.ts +0 -451
  221. package/templates/apps/api/src/services/usage-metering.ts +0 -699
  222. package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
  223. package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
  224. package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
  225. package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
  226. package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
  227. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
  228. package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
  229. package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
  230. package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
  231. package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
  232. package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
  233. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
  234. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
  235. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
  236. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
  237. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
  238. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
  239. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
  240. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
  241. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
  242. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
  243. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
  244. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
  245. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
  246. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
  247. package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
  248. package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
  249. package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
  250. package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
  251. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
  252. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
  253. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
  254. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
  255. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
  256. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
  257. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
  258. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
  259. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
  260. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
  261. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
  262. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
  263. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
  264. package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
  265. package/templates/apps/web/content/en/blog/README.txt +0 -25
  266. package/templates/apps/web/content/en/docs/index.mdx +0 -28
  267. package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
  268. package/templates/apps/web/lib/blog-list-data.ts +0 -95
  269. package/templates/apps/web/lib/blog-post-data.ts +0 -79
  270. package/templates/apps/web/lib/source.ts +0 -34
  271. package/templates/apps/web/public/logomark-dark.svg +0 -4
  272. package/templates/apps/web/public/logomark-light.svg +0 -4
  273. package/templates/apps/web/source.config.ts +0 -78
@@ -1,144 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; mirrors abandon-sweep.ts's coverage-ignore
3
- rationale — the underlying list/resend logic is unit-tested at the service
4
- layer (services/__tests__/deliverable-resend.test.ts), through the same
5
- mocked-prisma double the fulfillment service tests already use. This file
6
- is a thin entry: parse args, dispatch to list or resend, print, set exit
7
- code. */
8
- /**
9
- * Deliverable-grant resend CLI (#3998 D2/D4).
10
- *
11
- * Dev — list mode (read-only, default):
12
- * `pnpm --filter @working-theory/api deliverable:resend -- --site <siteId>
13
- * [--since <iso>] [--limit <n>]`
14
- * Dev — resend mode (one grant):
15
- * `pnpm --filter @working-theory/api deliverable:resend -- --grant <grantId>`
16
- * Production: `node dist/bin/deliverable-resend.js <same flags>`.
17
- *
18
- * No HTTP resend route exists (spec §2.2/D2) — this CLI is the sole recovery
19
- * path for a grant whose post-mint send never completed, run by the operator
20
- * on their own machine/CI. List mode is the read-only default and can never
21
- * send anything. `--grant` is always a deliberate, named, single-grant act
22
- * (D4) — there is no bulk/`--all` mode; the operator loops over the list
23
- * output if several need resending.
24
- */
25
- import {
26
- listSendlessGrants,
27
- resendDeliverableGrant,
28
- } from "../services/deliverable-fulfillment.js";
29
-
30
- type ParsedArgs = {
31
- site?: string | undefined;
32
- since?: string | undefined;
33
- limit?: string | undefined;
34
- grant?: string | undefined;
35
- };
36
-
37
- /** Minimal `--flag value` parser — no external dependency for a 4-flag CLI. */
38
- function parseArgs(argv: string[]): ParsedArgs {
39
- const out: ParsedArgs = {};
40
- for (let i = 0; i < argv.length; i += 1) {
41
- const flag = argv[i];
42
- const value = argv[i + 1];
43
- if (flag === "--site") out.site = value;
44
- else if (flag === "--since") out.since = value;
45
- else if (flag === "--limit") out.limit = value;
46
- else if (flag === "--grant") out.grant = value;
47
- else continue;
48
- i += 1;
49
- }
50
- return out;
51
- }
52
-
53
- async function runResend(grantId: string): Promise<void> {
54
- const result = await resendDeliverableGrant(grantId);
55
-
56
- if (!result.ok) {
57
- console.error(
58
- `[deliverable-resend] ${result.error.kind}: ${result.error.message}`,
59
- );
60
- process.exitCode = 1;
61
- return;
62
- }
63
-
64
- console.log(
65
- `[deliverable-resend] grant="${grantId}" outcome="${result.outcome}"`,
66
- );
67
- if (result.outcome !== "sent") {
68
- process.exitCode = 1;
69
- }
70
- }
71
-
72
- async function runList(args: ParsedArgs): Promise<void> {
73
- if (!args.site) {
74
- console.error(
75
- "[deliverable-resend] usage: --site <siteId> [--since <iso>] [--limit <n>]",
76
- );
77
- process.exitCode = 1;
78
- return;
79
- }
80
-
81
- let since: Date | undefined;
82
- if (args.since !== undefined) {
83
- since = new Date(args.since);
84
- if (Number.isNaN(since.getTime())) {
85
- console.error(
86
- `[deliverable-resend] invalid --since value: "${args.since}"`,
87
- );
88
- process.exitCode = 1;
89
- return;
90
- }
91
- } else {
92
- // D4: every historical (pre-migration) grant reads sendStatus IS NULL,
93
- // so an unfiltered first run on an established site lists every grant
94
- // ever minted — most of which were, in fact, delivered via the prior
95
- // log-only path. The caveat is printed, not silently assumed away.
96
- console.log(
97
- "[deliverable-resend] no --since given — listing EVERY sendless grant " +
98
- "for this site, including pre-migration rows that predate this " +
99
- "column and may already have been delivered.",
100
- );
101
- }
102
-
103
- let limit: number | undefined;
104
- if (args.limit !== undefined) {
105
- limit = Number.parseInt(args.limit, 10);
106
- if (!Number.isInteger(limit) || limit <= 0) {
107
- console.error(
108
- `[deliverable-resend] invalid --limit value: "${args.limit}"`,
109
- );
110
- process.exitCode = 1;
111
- return;
112
- }
113
- }
114
-
115
- const rows = await listSendlessGrants({
116
- siteId: args.site,
117
- ...(since !== undefined && { since }),
118
- ...(limit !== undefined && { limit }),
119
- });
120
-
121
- for (const row of rows) {
122
- console.log(JSON.stringify(row));
123
- }
124
- console.log(`[deliverable-resend] ${rows.length} sendless grant(s)`);
125
- }
126
-
127
- async function main(): Promise<void> {
128
- const args = parseArgs(process.argv.slice(2));
129
-
130
- if (args.grant) {
131
- await runResend(args.grant);
132
- } else {
133
- await runList(args);
134
- }
135
- }
136
-
137
- main().catch((err: unknown) => {
138
- // Belt-and-suspenders (mirrors abandon-sweep.ts) — an unexpected throw
139
- // (a bad DB connection, etc.) still exits non-zero with a structured
140
- // message rather than an unhandled-rejection crash.
141
- const message = err instanceof Error ? err.message : "Unknown error";
142
- console.error(`[deliverable-resend] unexpected_error: ${message}`);
143
- process.exitCode = 1;
144
- });
@@ -1,114 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; mirrors abandon-sweep.ts's coverage-ignore
3
- rationale — real entry-point semantics run the compiled dist directly, and
4
- the DB write it performs (prisma.deliverableAsset.upsert) is exercised
5
- through the same mocked-prisma double the service-layer tests already use
6
- (apps/api/src/lib/__mocks__/prisma.ts § deliverableAsset). This file is a
7
- thin entry: parse args, read the file, refuse the size cap, upsert. */
8
- /**
9
- * Deliverable-asset upload CLI (#3961 D6).
10
- *
11
- * Dev: `pnpm --filter @working-theory/api deliverable:upload -- --site <siteId>
12
- * --key <key> --file <path> [--content-type <ct>] [--filename <name>]`.
13
- * Production: `node dist/bin/deliverable-upload.js <same flags>`.
14
- *
15
- * No HTTP upload route exists (spec §2.2) — this CLI is the sole write path
16
- * for `DeliverableAsset` rows, run by the operator on their own machine/CI,
17
- * never by a customer or agent request. Re-upload (same siteId + key)
18
- * REPLACES the stored bytes (upsert) — the config's `assetKey` reference
19
- * never changes, so a content update is a re-run of this command.
20
- *
21
- * The 20 MB cap is a CLI guard, not a schema constraint (Postgres `bytea`
22
- * has no practical limit at launch volumes) — it keeps the DB-storage
23
- * choice (D1) honest until the blob-storage upgrade path is taken.
24
- */
25
- import { readFileSync, statSync } from "node:fs";
26
- import { basename } from "node:path";
27
-
28
- import { prisma } from "../lib/prisma.js";
29
-
30
- const MAX_BYTES = 20 * 1024 * 1024; // 20 MB (D6)
31
- const DEFAULT_CONTENT_TYPE = "application/octet-stream";
32
-
33
- type ParsedArgs = {
34
- siteId?: string | undefined;
35
- key?: string | undefined;
36
- file?: string | undefined;
37
- contentType?: string | undefined;
38
- filename?: string | undefined;
39
- };
40
-
41
- /** Minimal `--flag value` parser — no external dependency for a 5-flag CLI. */
42
- function parseArgs(argv: string[]): ParsedArgs {
43
- const out: ParsedArgs = {};
44
- for (let i = 0; i < argv.length; i += 1) {
45
- const flag = argv[i];
46
- const value = argv[i + 1];
47
- if (flag === "--site") out.siteId = value;
48
- else if (flag === "--key") out.key = value;
49
- else if (flag === "--file") out.file = value;
50
- else if (flag === "--content-type") out.contentType = value;
51
- else if (flag === "--filename") out.filename = value;
52
- else continue;
53
- i += 1;
54
- }
55
- return out;
56
- }
57
-
58
- async function main(): Promise<void> {
59
- const args = parseArgs(process.argv.slice(2));
60
-
61
- if (!args.siteId || !args.key || !args.file) {
62
- console.error(
63
- "[deliverable-upload] usage: --site <siteId> --key <key> --file <path> " +
64
- "[--content-type <ct>] [--filename <name>]",
65
- );
66
- process.exitCode = 1;
67
- return;
68
- }
69
-
70
- const stat = statSync(args.file);
71
- if (stat.size > MAX_BYTES) {
72
- console.error(
73
- `[deliverable-upload] refused: ${args.file} is ${stat.size} bytes, ` +
74
- `exceeds the ${MAX_BYTES}-byte (20 MB) cap`,
75
- );
76
- process.exitCode = 1;
77
- return;
78
- }
79
-
80
- const bytes = readFileSync(args.file);
81
- const filename = args.filename ?? basename(args.file);
82
- const contentType = args.contentType ?? DEFAULT_CONTENT_TYPE;
83
-
84
- const row = await prisma.deliverableAsset.upsert({
85
- where: { siteId_key: { siteId: args.siteId, key: args.key } },
86
- create: {
87
- siteId: args.siteId,
88
- key: args.key,
89
- filename,
90
- contentType,
91
- bytes,
92
- size: stat.size,
93
- },
94
- update: {
95
- filename,
96
- contentType,
97
- bytes,
98
- size: stat.size,
99
- },
100
- select: { key: true },
101
- });
102
-
103
- console.log(`[deliverable-upload] uploaded key="${row.key}"`);
104
- }
105
-
106
- main().catch((err: unknown) => {
107
- // Belt-and-suspenders (mirrors abandon-sweep.ts) — main() has no
108
- // internal catch of its own, so an unexpected throw (a bad DB
109
- // connection, a filesystem error mid-read) still exits non-zero with a
110
- // structured message rather than an unhandled-rejection crash.
111
- const message = err instanceof Error ? err.message : "Unknown error";
112
- console.error(`[deliverable-upload] unexpected_error: ${message}`);
113
- process.exitCode = 1;
114
- });
@@ -1,44 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; the underlying
3
- runWaitlistFollowUpSweep logic is unit-tested in
4
- services/__tests__/waitlist-followup.test.ts. Mirrors abandon-sweep.ts's
5
- coverage-ignore rationale — real entry-point semantics run the compiled
6
- dist directly, on the operator's own cron/scheduler cadence (D1: no
7
- queue/timer infrastructure in this repo — an external scheduler runs this
8
- command on whatever cadence the deployment chooses). */
9
- /**
10
- * Waitlist follow-up sweep CLI entry (#4732 D1).
11
- *
12
- * Dev: `pnpm --filter @working-theory/api followup:sweep` (tsx, this file).
13
- * Production: `node dist/bin/followup-sweep.js` (the compiled entry, run by
14
- * any external scheduler — Vercel Cron, GitHub Actions cron, plain crontab).
15
- * The sweep is stateless and idempotent (the `waitlist_followup_sends`
16
- * `@@unique([leadId, followUpKey])` guarantee), so overlap or a missed tick
17
- * is harmless — the offset window is data, not a timer.
18
- */
19
- import { runWaitlistFollowUpSweep } from "../services/waitlist-followup.js";
20
-
21
- async function main(): Promise<void> {
22
- const result = await runWaitlistFollowUpSweep();
23
-
24
- if (!result.ok) {
25
- console.error(
26
- `[followup-sweep] ${result.error.kind}: ${result.error.message}`,
27
- );
28
- process.exitCode = 1;
29
- return;
30
- }
31
-
32
- console.log(`[followup-sweep] ${JSON.stringify(result.summary)}`);
33
- }
34
-
35
- main().catch((err: unknown) => {
36
- // Belt-and-suspenders (mirrors abandon-sweep.ts): runWaitlistFollowUpSweep
37
- // already catches internally and returns a structured `unexpected_error`
38
- // result, so this path is unreachable in practice — it exists only to
39
- // prevent a bare unhandled-rejection crash if `main` itself ever throws
40
- // before/around that call.
41
- const message = err instanceof Error ? err.message : "Unknown error";
42
- console.error(`[followup-sweep] unexpected_error: ${message}`);
43
- process.exitCode = 1;
44
- });
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; the underlying runListingCsvSweep
3
- logic is unit-tested in services/__tests__/listing-csv-sweep.test.ts.
4
- Mirrors followup-sweep.ts's coverage-ignore rationale — real entry-point
5
- semantics run the compiled dist directly, on the operator's own
6
- cron/scheduler cadence (D1: no queue/timer infrastructure in this repo —
7
- an external scheduler runs this command on whatever cadence the deployment
8
- chooses). */
9
- /**
10
- * Weekly founder-CSV sweep CLI entry (#5001).
11
- *
12
- * Dev: `pnpm --filter @working-theory/api listing-csv:sweep` (tsx, this file).
13
- * Production: `node dist/bin/listing-csv-sweep.js` (the compiled entry, run by
14
- * any external scheduler — Vercel Cron, GitHub Actions cron, plain crontab).
15
- * The consumer schedules this WEEKLY; this repo adds no cron of its own.
16
- *
17
- * The sweep is stateless and idempotent: a listing with no `WaitlistScore`
18
- * newer than its last `ListingCsvSend` sends nothing, so an extra tick is a
19
- * no-op and a missed tick simply widens the next CSV's window.
20
- */
21
- import { runListingCsvSweep } from "../services/listing-csv-sweep.js";
22
-
23
- async function main(): Promise<void> {
24
- const result = await runListingCsvSweep();
25
-
26
- if (!result.ok) {
27
- console.error(
28
- `[listing-csv-sweep] ${result.error.kind}: ${result.error.message}`,
29
- );
30
- process.exitCode = 1;
31
- return;
32
- }
33
-
34
- console.log(`[listing-csv-sweep] ${JSON.stringify(result.summary)}`);
35
- }
36
-
37
- main().catch((err: unknown) => {
38
- // Belt-and-suspenders (mirrors followup-sweep.ts): runListingCsvSweep
39
- // already catches internally and returns a structured `unexpected_error`
40
- // result, so this path is unreachable in practice — it exists only to
41
- // prevent a bare unhandled-rejection crash if `main` itself ever throws
42
- // before/around that call.
43
- const message = err instanceof Error ? err.message : "Unknown error";
44
- console.error(`[listing-csv-sweep] unexpected_error: ${message}`);
45
- process.exitCode = 1;
46
- });
@@ -1,58 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; the underlying seedFlowPresets logic
3
- is unit-tested in services/__tests__/flow-engine.test.ts. Mirrors
4
- bin/abandon-sweep.ts's coverage-ignore rationale — real entry-point
5
- semantics run the compiled dist directly. */
6
- /**
7
- * Flow-preset seed CLI entry (#3958 §3.3).
8
- *
9
- * Writes the three static capture presets (form, waitlist, lead-magnet —
10
- * `@working-theory/database/flows/presets`) through the existing validated
11
- * `upsertFlowDefinition` path, for the current site (`currentSiteId()`,
12
- * `lib/site.ts`) — the same site id the `/v1/flows/*` routes resolve against,
13
- * so a preset is never written somewhere the routes can't find it.
14
- *
15
- * Dev: `pnpm --filter @working-theory/api seed:presets` (tsx, this file).
16
- * Production: `node dist/bin/seed-presets.js` (the compiled entry, run once
17
- * per site by the operator — idempotent: `upsertFlowDefinition` upserts on
18
- * `[siteId, slug]`, so a re-run overwrites cleanly rather than duplicating).
19
- *
20
- * This entry point is the placement chosen for #3958's operator-facing
21
- * invocation (spec §3.3 / §7 item 1 — the implementer's choice, both
22
- * placements bounded and reversible): `apps/api/src/bin/` already hosts
23
- * exactly this shape of one-off operator command (`abandon-sweep.ts`), so it
24
- * is the closer fit to "the existing apps/api CLI surface" than adding
25
- * argv-verb parsing to `cli.ts`, which today does nothing but bootstrap the
26
- * long-running server.
27
- */
28
- import { FLOW_PRESETS } from "@working-theory/database/flows/presets";
29
-
30
- import { currentSiteId } from "../lib/site.js";
31
- import { seedFlowPresets } from "../services/flow-engine.js";
32
-
33
- async function main(): Promise<void> {
34
- const siteId = currentSiteId();
35
- const result = await seedFlowPresets(siteId, FLOW_PRESETS);
36
-
37
- if (!result.ok) {
38
- console.error(
39
- `[seed-presets] ${result.error.kind}: ${result.error.message} (seeded before failure: ${result.seeded.join(", ") || "none"})`,
40
- );
41
- process.exitCode = 1;
42
- return;
43
- }
44
-
45
- console.log(
46
- `[seed-presets] seeded for site "${siteId}": ${result.seeded.join(", ")}`,
47
- );
48
- }
49
-
50
- main().catch((err: unknown) => {
51
- // Belt-and-suspenders, mirroring abandon-sweep.ts: seedFlowPresets already
52
- // returns a structured failure rather than throwing, so this path exists
53
- // only to prevent a bare unhandled-rejection crash if `main` itself ever
54
- // throws before/around that call.
55
- const message = err instanceof Error ? err.message : "Unknown error";
56
- console.error(`[seed-presets] unexpected_error: ${message}`);
57
- process.exitCode = 1;
58
- });
@@ -1,104 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; the underlying seedWaitlistExperiments
3
- logic is unit-tested in services/__tests__/waitlist-experiments.test.ts.
4
- Mirrors bin/seed-presets.ts's coverage-ignore rationale. */
5
- /**
6
- * Waitlist-experiment seed CLI entry (#4661 D5).
7
- *
8
- * Reads one or more experiment-config JSON paths from argv, resolves any
9
- * `questionSetRef` / `scoringRef` / `sourceTrackingRef` path (relative to the
10
- * config file) into the embedded objects, and writes them through the
11
- * existing validated `seedWaitlistExperiments` path, for the current site
12
- * (`currentSiteId()`, `lib/site.ts`) — the same bootstrap shape as
13
- * `seed-presets.ts`.
14
- *
15
- * `seedWaitlistExperiments` does MORE than write `waitlist_experiments`
16
- * (#4726): for each config it also compiles + upserts one `flow_definitions`
17
- * row per `waitlists[]` entry (`materializeWaitlistFlows`,
18
- * `waitlist-experiments.ts`) — the step `GET /v1/flows/:flowSlug`
19
- * (`serveFlow`, `flow-engine.ts`) actually reads. A single run of this bin
20
- * is therefore sufficient to make every declared waitlist servable; a
21
- * compile failure (`unknown_waitlist` / `unsupported_by_engine`) or a
22
- * `flow_definitions` write failure exits this process non-zero, naming the
23
- * failing waitlist key, and writes no `flow_definitions` row for it.
24
- *
25
- * Dev: `pnpm --filter @working-theory/api tsx src/bin/seed-waitlist-experiments.ts <config.json>...`
26
- * Production: `node dist/bin/seed-waitlist-experiments.js <config.json>...` —
27
- * idempotent (`upsertWaitlistExperiment` upserts on `[siteId, key]`;
28
- * `upsertFlowDefinition` upserts on `[siteId, slug]`).
29
- */
30
- import { readFileSync } from "node:fs";
31
- import { dirname, resolve } from "node:path";
32
-
33
- import { currentSiteId } from "../lib/site.js";
34
- import { seedWaitlistExperiments } from "../services/waitlist-experiments.js";
35
-
36
- /**
37
- * A `*Ref` field on the config is a path RELATIVE to the config file (D5),
38
- * mapped to the embedded field name `waitlistExperimentConfigSchema`
39
- * actually declares. An EXPLICIT map, not a `Ref$` suffix strip —
40
- * `sourceTrackingRef` embeds as `tracking` (not `sourceTracking`), which a
41
- * generic strip gets wrong (code-review finding, #4661).
42
- */
43
- const REF_FIELD_MAP: Record<string, string> = {
44
- questionSetRef: "questionSet",
45
- scoringRef: "scoring",
46
- sourceTrackingRef: "tracking",
47
- };
48
-
49
- function readJson(path: string): unknown {
50
- return JSON.parse(readFileSync(path, "utf8"));
51
- }
52
-
53
- /**
54
- * Resolve every `*Ref` path on a raw config object relative to `configPath`
55
- * into the embedded object it names, mirroring the site-plan projection's
56
- * config-time `*Ref` convention (D5/D7) — the runtime never reads these
57
- * paths; this bin resolves them once, at seed time.
58
- */
59
- function resolveConfigRefs(raw: unknown, configPath: string): unknown {
60
- if (typeof raw !== "object" || raw === null) return raw;
61
- const resolved: Record<string, unknown> = { ...(raw as Record<string, unknown>) };
62
- const base = dirname(configPath);
63
- for (const [field, embeddedField] of Object.entries(REF_FIELD_MAP)) {
64
- const value = resolved[field];
65
- if (typeof value === "string") {
66
- resolved[embeddedField] = readJson(resolve(base, value));
67
- delete resolved[field];
68
- }
69
- }
70
- return resolved;
71
- }
72
-
73
- async function main(): Promise<void> {
74
- const configPaths = process.argv.slice(2);
75
- if (configPaths.length === 0) {
76
- console.error("[seed-waitlist-experiments] usage: <config.json>...");
77
- process.exitCode = 1;
78
- return;
79
- }
80
-
81
- const siteId = currentSiteId();
82
- const configs = configPaths.map((path) =>
83
- resolveConfigRefs(readJson(path), path),
84
- );
85
- const result = await seedWaitlistExperiments(siteId, configs);
86
-
87
- if (!result.ok) {
88
- console.error(
89
- `[seed-waitlist-experiments] ${result.error.kind}: ${result.error.message} (seeded before failure: ${result.seeded.join(", ") || "none"})`,
90
- );
91
- process.exitCode = 1;
92
- return;
93
- }
94
-
95
- console.log(
96
- `[seed-waitlist-experiments] seeded for site "${siteId}": ${result.seeded.join(", ")}`,
97
- );
98
- }
99
-
100
- main().catch((err: unknown) => {
101
- const message = err instanceof Error ? err.message : "Unknown error";
102
- console.error(`[seed-waitlist-experiments] unexpected_error: ${message}`);
103
- process.exitCode = 1;
104
- });
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; the underlying cleanupAbandonedSessions
3
- logic is unit-tested in services/__tests__/flow-engine.test.ts. Mirrors
4
- abandon-sweep.ts's coverage-ignore rationale — real entry-point semantics
5
- run the compiled dist directly, on the operator's own cron/scheduler
6
- cadence (no queue/timer infrastructure in this repo — an external
7
- scheduler runs this command on whatever cadence the deployment
8
- chooses). */
9
- /**
10
- * FlowSession retention/cleanup sweep CLI entry (#3974 AC3).
11
- *
12
- * Dev: `pnpm --filter @working-theory/api retention:sweep` (tsx, this file).
13
- * Production: `node dist/bin/session-retention-sweep.js` (the compiled
14
- * entry, run by any external scheduler — Vercel Cron, GitHub Actions cron,
15
- * plain crontab). Deletes abandoned FlowSession rows past the retention
16
- * window (default 90 days) — see `cleanupAbandonedSessions`'s own doc
17
- * comment in ../services/flow-engine.ts for the full policy + its #3962
18
- * abandon-email coordination.
19
- */
20
- import { cleanupAbandonedSessions } from "../services/flow-engine.js";
21
-
22
- async function main(): Promise<void> {
23
- const result = await cleanupAbandonedSessions();
24
-
25
- if (!result.ok) {
26
- console.error(
27
- `[session-retention-sweep] ${result.error.kind}: ${result.error.message}`,
28
- );
29
- process.exitCode = 1;
30
- return;
31
- }
32
-
33
- console.log(`[session-retention-sweep] deleted=${result.deleted}`);
34
- }
35
-
36
- main().catch((err: unknown) => {
37
- // cleanupAbandonedSessions only returns a structured error for the
38
- // DB-not-configured case (requireDb's guard) — a genuine prisma/DB
39
- // exception propagates as a rejected promise, exactly like every other
40
- // flow-engine.ts function (none of them wrap unexpected DB errors
41
- // internally). This is that failure's landing spot, so it degrades to a
42
- // clean process-exit rather than a bare unhandled-rejection crash.
43
- const message = err instanceof Error ? err.message : "Unknown error";
44
- console.error(`[session-retention-sweep] unexpected_error: ${message}`);
45
- process.exitCode = 1;
46
- });
@@ -1,74 +0,0 @@
1
- /**
2
- * apps/api stripe shim — surface-drift smoke tests.
3
- *
4
- * Ensures apps/api/src/lib/stripe.ts re-exports exactly what
5
- * @working-theory/stripe-client exports, with no surface drift.
6
- */
7
-
8
- jest.mock("@working-theory/stripe-client", () => ({
9
- getStripeClient: jest.fn(),
10
- assertSandboxKey: jest.fn(),
11
- verifyWebhookSignature: jest.fn(),
12
- createCheckoutSession: jest.fn(),
13
- STRIPE_API_VERSION: "2023-10-16",
14
- WEBHOOK_TOLERANCE_SECONDS: 300,
15
- StripeConfigError: class StripeConfigError extends Error {},
16
- StripeWebhookSignatureError: class StripeWebhookSignatureError extends Error {},
17
- UnknownProductCodeError: class UnknownProductCodeError extends Error {},
18
- }));
19
-
20
- // Import the shim (not the wrapper directly)
21
- import * as stripeShim from "../stripe.js";
22
- import * as stripeClient from "@working-theory/stripe-client";
23
-
24
- describe("apps/api/src/lib/stripe — surface re-exports", () => {
25
- it("exports getStripeClient as a function", () => {
26
- expect(typeof stripeShim.getStripeClient).toBe("function");
27
- });
28
-
29
- it("exports assertSandboxKey as a function", () => {
30
- expect(typeof stripeShim.assertSandboxKey).toBe("function");
31
- });
32
-
33
- it("exports verifyWebhookSignature as a function", () => {
34
- expect(typeof stripeShim.verifyWebhookSignature).toBe("function");
35
- });
36
-
37
- it("exports createCheckoutSession as a function", () => {
38
- expect(typeof stripeShim.createCheckoutSession).toBe("function");
39
- });
40
-
41
- it("exports STRIPE_API_VERSION", () => {
42
- expect(stripeShim.STRIPE_API_VERSION).toBe("2023-10-16");
43
- });
44
-
45
- it("exports WEBHOOK_TOLERANCE_SECONDS", () => {
46
- expect(stripeShim.WEBHOOK_TOLERANCE_SECONDS).toBe(300);
47
- });
48
-
49
- it("exports StripeConfigError as a constructor producing Error instances", () => {
50
- expect(typeof stripeShim.StripeConfigError).toBe("function");
51
- expect(new stripeShim.StripeConfigError()).toBeInstanceOf(Error);
52
- });
53
-
54
- it("exports StripeWebhookSignatureError as a constructor producing Error instances", () => {
55
- expect(typeof stripeShim.StripeWebhookSignatureError).toBe("function");
56
- expect(new stripeShim.StripeWebhookSignatureError()).toBeInstanceOf(Error);
57
- });
58
-
59
- it("exports UnknownProductCodeError as a constructor producing Error instances", () => {
60
- expect(typeof stripeShim.UnknownProductCodeError).toBe("function");
61
- expect(new stripeShim.UnknownProductCodeError()).toBeInstanceOf(Error);
62
- });
63
-
64
- it("shim functions are the same references as @working-theory/stripe-client exports", () => {
65
- expect(stripeShim.getStripeClient).toBe(stripeClient.getStripeClient);
66
- expect(stripeShim.assertSandboxKey).toBe(stripeClient.assertSandboxKey);
67
- expect(stripeShim.verifyWebhookSignature).toBe(
68
- stripeClient.verifyWebhookSignature,
69
- );
70
- expect(stripeShim.createCheckoutSession).toBe(
71
- stripeClient.createCheckoutSession,
72
- );
73
- });
74
- });
@@ -1,52 +0,0 @@
1
- /**
2
- * apps/api Stripe shim.
3
- *
4
- * Re-exports @working-theory/stripe-client's public surface for use by routes and
5
- * services under apps/api. This is the SOLE Stripe-touching file in apps/api.
6
- *
7
- * Future routes import from here (`from '../lib/stripe'`), not directly from
8
- * '@working-theory/stripe-client' — single chokepoint for AILK-internal ergonomics
9
- * (logger context, request-id propagation) without touching every consumer.
10
- */
11
- import { getStripeClient } from "@working-theory/stripe-client";
12
-
13
- export {
14
- assertSandboxKey,
15
- createBillingPortalSession,
16
- createCheckoutSession,
17
- createQuotedCheckoutSession,
18
- getStripeClient,
19
- STRIPE_API_VERSION,
20
- StripeConfigError,
21
- StripeWebhookSignatureError,
22
- UnknownProductCodeError,
23
- verifyWebhookSignature,
24
- WEBHOOK_TOLERANCE_SECONDS,
25
- } from "@working-theory/stripe-client";
26
-
27
- export type {
28
- CreateBillingPortalSessionInput,
29
- CreateBillingPortalSessionOutput,
30
- CreateCheckoutSessionInput,
31
- CreateCheckoutSessionOutput,
32
- CreateCheckoutSessionParams,
33
- CreateQuotedCheckoutSessionInput,
34
- CreateQuotedCheckoutSessionOutput,
35
- Stripe,
36
- StripeCheckoutSession,
37
- StripeWebhookEvent,
38
- } from "@working-theory/stripe-client";
39
-
40
- /**
41
- * Expire a Checkout Session (#4912 FR7/D7).
42
- *
43
- * The compensation the recommender's attach-miss branch owes: the Stripe
44
- * session already exists, its id never reached the `FlowCheckout` row, and
45
- * nothing will ever join the two — so the session is closed rather than left
46
- * live and payable (#4863). Built on the already-re-exported
47
- * `getStripeClient`, whose construction asserts the `sk_test_` allow-list, so
48
- * this call inherits the test-mode gate like every other Stripe call here.
49
- */
50
- export async function expireCheckoutSession(stripeSessionId: string): Promise<void> {
51
- await getStripeClient().checkout.sessions.expire(stripeSessionId);
52
- }