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