create-ailk 0.1.1 → 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 (284) hide show
  1. package/component-catalog.md +210 -52
  2. package/dist/cli.js +0 -0
  3. package/dist/lib/apply-module-patches.d.ts +47 -0
  4. package/dist/lib/apply-module-patches.js +370 -0
  5. package/dist/lib/extract-module-bundle.d.ts +12 -3
  6. package/dist/lib/extract-module-bundle.js +236 -24
  7. package/dist/lib/fetch-module.d.ts +3 -38
  8. package/dist/lib/fetch-module.js +26 -8
  9. package/dist/lib/module-license-gate.d.ts +2 -0
  10. package/dist/lib/module-license-gate.js +1 -0
  11. package/dist/lib/paid-paths.d.ts +16 -0
  12. package/dist/lib/paid-paths.js +90 -0
  13. package/dist/module-architecture.d.ts +139 -23
  14. package/dist/module-architecture.js +668 -25
  15. package/dist/parse-args.d.ts +2 -2
  16. package/dist/parse-args.js +3 -1
  17. package/dist/programmatic.d.ts +2 -1
  18. package/dist/programmatic.js +15 -4
  19. package/dist/surfaces.d.ts +12 -36
  20. package/dist/surfaces.js +35 -5
  21. package/dist/sync-routes.js +31 -1
  22. package/package.json +14 -15
  23. package/templates/.claude/agents/web.md +2 -3
  24. package/templates/.claude/rules/architecture.md +5 -5
  25. package/templates/.claude/skills/README.md +2 -1
  26. package/templates/.env.example +21 -9
  27. package/templates/CONVENTIONS.md +6 -7
  28. package/templates/apps/api/.env.example +9 -0
  29. package/templates/apps/api/CLAUDE.md +35 -4
  30. package/templates/apps/api/package.json +0 -1
  31. package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
  32. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
  33. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
  34. package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
  35. package/templates/apps/api/src/config/index.ts +19 -0
  36. package/templates/apps/api/src/config/modules.ts +12 -14
  37. package/templates/apps/api/src/lib/__mocks__/prisma.ts +13 -0
  38. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
  39. package/templates/apps/api/src/lib/slice-load.ts +90 -0
  40. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
  41. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
  42. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
  43. package/templates/apps/api/src/openapi/index.ts +9 -3
  44. package/templates/apps/api/src/openapi/spec.ts +505 -65
  45. package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
  46. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
  47. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
  48. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
  49. package/templates/apps/api/src/routes/content/index.ts +38 -11
  50. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +28 -0
  51. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +144 -0
  52. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
  53. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +478 -0
  54. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
  55. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
  56. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +717 -0
  57. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +35 -0
  58. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +484 -0
  59. package/templates/apps/api/src/routes/project-listings/index.ts +52 -10
  60. package/templates/apps/api/src/routes/project-listings/me.ts +86 -0
  61. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
  62. package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
  63. package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
  64. package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
  65. package/templates/apps/api/src/routes/project-listings/start.ts +38 -0
  66. package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
  67. package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
  68. package/templates/apps/api/src/server.ts +471 -133
  69. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
  70. package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
  71. package/templates/apps/api/src/services/consent.ts +236 -0
  72. package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
  73. package/templates/apps/api/src/services/listing-config.ts +58 -0
  74. package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
  75. package/templates/apps/api/src/services/project-listings.ts +602 -29
  76. package/templates/apps/api/src/vercel-handler.ts +60 -26
  77. package/templates/apps/mcp/.env.example +8 -0
  78. package/templates/apps/mcp/CLAUDE.md +2 -2
  79. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
  80. package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
  81. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
  82. package/templates/apps/mcp/src/config/modules.ts +14 -13
  83. package/templates/apps/mcp/src/server.ts +8 -4
  84. package/templates/apps/mcp/src/tools/index.ts +7 -22
  85. package/templates/apps/web/.env.example +15 -0
  86. package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
  87. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
  88. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
  89. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
  90. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
  91. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
  92. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
  93. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
  94. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
  95. package/templates/apps/web/app/[locale]/layout.tsx +13 -1
  96. package/templates/apps/web/app/llms.txt/route.ts +12 -9
  97. package/templates/apps/web/jest.config.cjs +20 -13
  98. package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
  99. package/templates/apps/web/lib/site-brand.tsx +4 -1
  100. package/templates/apps/web/lib/site-theme.ts +74 -0
  101. package/templates/apps/web/next.config.mjs +1 -3
  102. package/templates/apps/web/package.json +1 -6
  103. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  104. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  105. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  106. package/templates/apps/web/public/favicon-16x16.png +0 -0
  107. package/templates/apps/web/public/favicon-32x32.png +0 -0
  108. package/templates/apps/web/public/favicon.ico +0 -0
  109. package/templates/apps/web/public/favicon.svg +6 -3
  110. package/templates/apps/web/public/lockup-horizontal.svg +4 -0
  111. package/templates/apps/web/public/logomark.svg +4 -0
  112. package/templates/apps/web/public/site.webmanifest +2 -2
  113. package/templates/apps/web/public/wordmark.svg +4 -0
  114. package/templates/content/_site.mdx +12 -0
  115. package/templates/database/CHANGELOG.md +94 -0
  116. package/templates/database/inbox/schema.prisma +165 -0
  117. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
  118. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
  119. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
  120. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
  121. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
  122. package/templates/database/package.json +1 -1
  123. package/templates/database/scripts/db-generate-locked.sh +0 -0
  124. package/templates/package.json +1 -1
  125. package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
  126. package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
  127. package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
  128. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
  129. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
  130. package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
  131. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
  132. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
  133. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
  134. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
  135. package/templates/apps/api/src/__tests__/server.test.ts +0 -253
  136. package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
  137. package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
  138. package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
  139. package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
  140. package/templates/apps/api/src/bin/seed-presets.ts +0 -58
  141. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
  142. package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
  143. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
  144. package/templates/apps/api/src/lib/stripe.ts +0 -52
  145. package/templates/apps/api/src/lib/tenant-db.ts +0 -225
  146. package/templates/apps/api/src/lib/ws-token.ts +0 -91
  147. package/templates/apps/api/src/middleware/tenant.ts +0 -99
  148. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
  149. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
  150. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
  151. package/templates/apps/api/src/routes/billing/index.ts +0 -36
  152. package/templates/apps/api/src/routes/billing/portal.ts +0 -182
  153. package/templates/apps/api/src/routes/billing/read.ts +0 -75
  154. package/templates/apps/api/src/routes/billing/usage.ts +0 -153
  155. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
  156. package/templates/apps/api/src/routes/checkout/index.ts +0 -13
  157. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
  158. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
  159. package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
  160. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
  161. package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
  162. package/templates/apps/api/src/routes/flows/README.md +0 -147
  163. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
  164. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
  165. package/templates/apps/api/src/routes/flows/index.ts +0 -202
  166. package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
  167. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
  168. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
  169. package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
  170. package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
  171. package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
  172. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
  173. package/templates/apps/api/src/routes/schedule/index.ts +0 -249
  174. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
  175. package/templates/apps/api/src/routes/slack/actions.ts +0 -177
  176. package/templates/apps/api/src/routes/slack/index.ts +0 -38
  177. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
  178. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
  179. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
  180. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
  181. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
  182. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
  183. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
  184. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
  185. package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
  186. package/templates/apps/api/src/routes/webhooks/README.md +0 -80
  187. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
  188. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
  189. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
  190. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
  191. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
  192. package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
  193. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
  194. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
  195. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
  196. package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
  197. package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
  198. package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
  199. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
  200. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
  201. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
  202. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
  203. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
  204. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
  205. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
  206. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
  207. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
  208. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
  209. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
  210. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
  211. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
  212. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
  213. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
  214. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
  215. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
  216. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
  217. package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
  218. package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
  219. package/templates/apps/api/src/services/flow-engine.ts +0 -1278
  220. package/templates/apps/api/src/services/lead-promotion.ts +0 -176
  221. package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
  222. package/templates/apps/api/src/services/playbook-compile.ts +0 -398
  223. package/templates/apps/api/src/services/playbook-render.ts +0 -263
  224. package/templates/apps/api/src/services/project-listing-decision.ts +0 -490
  225. package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
  226. package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
  227. package/templates/apps/api/src/services/recommender-capture.ts +0 -835
  228. package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
  229. package/templates/apps/api/src/services/scheduling/index.ts +0 -63
  230. package/templates/apps/api/src/services/scheduling/types.ts +0 -88
  231. package/templates/apps/api/src/services/tenant-context.ts +0 -451
  232. package/templates/apps/api/src/services/usage-metering.ts +0 -699
  233. package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
  234. package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
  235. package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
  236. package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
  237. package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
  238. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
  239. package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
  240. package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
  241. package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
  242. package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
  243. package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
  244. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
  245. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
  246. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
  247. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
  248. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
  249. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
  250. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
  251. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
  252. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
  253. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
  254. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
  255. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
  256. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
  257. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
  258. package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
  259. package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
  260. package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
  261. package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
  262. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
  263. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
  264. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
  265. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
  266. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
  267. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
  268. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
  269. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
  270. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
  271. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
  272. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
  273. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
  274. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
  275. package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
  276. package/templates/apps/web/content/en/blog/README.txt +0 -25
  277. package/templates/apps/web/content/en/docs/index.mdx +0 -28
  278. package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
  279. package/templates/apps/web/lib/blog-list-data.ts +0 -95
  280. package/templates/apps/web/lib/blog-post-data.ts +0 -79
  281. package/templates/apps/web/lib/source.ts +0 -34
  282. package/templates/apps/web/public/logomark-dark.svg +0 -4
  283. package/templates/apps/web/public/logomark-light.svg +0 -4
  284. 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
- }