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,82 +0,0 @@
1
- /**
2
- * Flow-checkouts respondent route (#4984 — the "my purchases" read behind
3
- * the respondent account surface).
4
- *
5
- * Routes:
6
- * GET /v1/flow-checkouts/me — the caller's OWN flow checkouts, newest first
7
- *
8
- * Registered INSIDE server.ts's `authScope` (auth only — NOT the inner
9
- * `tenantScope`: a respondent has no workspace membership, and the tenant
10
- * guard is default-deny) so `request.userId` is populated by
11
- * `authPreHandlerPlugin` before the handler runs. An unauthenticated caller
12
- * receives the preHandler's existing 401, untouched. The identity is
13
- * `request.userId` ONLY: the route declares no query schema and reads no
14
- * query or body key (an `email` parameter is never parsed, never read),
15
- * never enumerates, never reads across sites — see
16
- * `services/flow-checkouts.ts`, where the only join is the lead-promotion
17
- * link (`Lead.promotedUserId`), never an email match.
18
- *
19
- * Envelope per apps/api/CLAUDE.md: `{ ok: true, data }` /
20
- * `{ ok: false, error: { code, message } }`, real HTTP status codes:
21
- * 401 unauthorized · 403 email_unverified · 404 user_not_found ·
22
- * 500 internal_error (the 200 body failed its own contract schema — a
23
- * drift is never a leak). A verified user with nothing to show is a 200
24
- * with an empty list.
25
- *
26
- * API↔MCP parity: identity-bound (the session IS the identity; the OSS MCP
27
- * surface is identity-blind) — no MCP twin (#3591 exception, the same shape
28
- * as `/v1/billing/*`, `/v1/workspaces/*`, `/v1/waitlist-signups/me`).
29
- */
30
- import { logger } from "@working-theory/observability";
31
- import { flowCheckoutMeResponseSchema } from "@working-theory/validation";
32
- import type { FastifyPluginAsync, FastifyReply } from "fastify";
33
-
34
- import { readMyCheckouts } from "../../services/flow-checkouts.js";
35
-
36
- function unauthorized(reply: FastifyReply) {
37
- return reply.status(401).send({
38
- ok: false,
39
- error: { code: "unauthorized", message: "Authentication required" },
40
- });
41
- }
42
-
43
- const ERROR_STATUS = {
44
- not_found_user: 404,
45
- email_unverified: 403,
46
- } as const;
47
-
48
- const ERROR_CODE = {
49
- not_found_user: "user_not_found",
50
- email_unverified: "email_unverified",
51
- } as const;
52
-
53
- export const flowCheckoutRoutes: FastifyPluginAsync = async (fastify) => {
54
- fastify.get("/v1/flow-checkouts/me", async (request, reply) => {
55
- const userId = request.userId;
56
- if (!userId) return unauthorized(reply);
57
-
58
- const result = await readMyCheckouts(userId);
59
- if (!result.ok) {
60
- return reply.status(ERROR_STATUS[result.error.kind]).send({
61
- ok: false,
62
- error: { code: ERROR_CODE[result.error.kind], message: result.error.message },
63
- });
64
- }
65
-
66
- // D5 — re-validate the body against its own strict contract before it
67
- // leaves the process (the waitlist-signups idiom): a shape drift is an
68
- // internal error, never a leak. Log issues only — never the value.
69
- const parsed = flowCheckoutMeResponseSchema.safeParse(result.value);
70
- if (!parsed.success) {
71
- logger.error(
72
- { route: "flow-checkouts/me", issues: parsed.error.issues },
73
- "[flow-checkouts] response failed its own contract schema",
74
- );
75
- return reply.status(500).send({
76
- ok: false,
77
- error: { code: "internal_error", message: "Response failed contract validation" },
78
- });
79
- }
80
- return reply.status(200).send({ ok: true, data: parsed.data });
81
- });
82
- };
@@ -1,147 +0,0 @@
1
- # Flows — `apps/api/src/routes/flows/`
2
-
3
- ```
4
- GET /v1/flows/:slug — serve a validated flow config
5
- POST /v1/flows/:slug/steps — submit one step's answers; persists
6
- server-side on landing
7
- GET /v1/flows/:slug/aggregate — anonymous per-question response counts
8
- for the results reveal (#4771)
9
- POST /v1/flows/:slug/save — the recommender's "email me this plan"
10
- moment: FlowSession + Lead (+ T+0 grant)
11
- for the permalink's answers (#4841)
12
- POST /v1/flows/:slug/checkout — the purchase moment: the email moment,
13
- the FlowCheckout buyer row at a
14
- SERVER-recomputed price, then a Stripe
15
- TEST-mode Checkout Session (#4841)
16
- ```
17
-
18
- Every route is anonymous — no auth, no `siteId` on the wire (see the
19
- module docstring in `index.ts` for the single-site default). All sit
20
- behind the per-IP rate limiter (`middleware/rate-limit.ts`), registered
21
- once for the whole `flowsScope` in `server.ts`.
22
-
23
- ## The email moment (`recommender.ts`, #4841)
24
-
25
- A recommending flow captures no PII until the visitor chooses save or
26
- checkout (PRD L108). Both routes take the permalink's answer map + an
27
- email; `checkout` additionally takes `offerKey` / `period` (+ `seats`,
28
- `creditStop`) and the two redirect URLs — and **no amount of any name**:
29
- the body schema is `.strict()`, and the server recomputes `price()` from
30
- the register it served (PRD L104). Checkout refuses a register with
31
- `stripeConfigRef: null` or no `rateTable.currency` (409), refuses any key
32
- outside the `sk_test_` allow-list before any side effect (500), and only
33
- opens the Stripe session AFTER the session, lead and buyer row exist — a
34
- Stripe outage returns 502 and the record survives. The Stripe session's
35
- `metadata` carries `flowCheckoutId`, `flowSlug`, `offerKey`, `sessionId`,
36
- `leadId` and the `permalink` (chunked `permalink_2…` past 500 chars) — the
37
- join the webhook (#4838) stamps on.
38
-
39
- ## Checkout redirect origins (#4912)
40
-
41
- `successUrl` and `cancelUrl` must be on **this site's own origin**. The
42
- shared `flowRedirectUrlSchema` pins only "an `http(s)` URL with a host",
43
- so without this gate a caller posting straight at the API — bypassing the
44
- consumer's Server Action, which builds both URLs itself — could mint a
45
- real Checkout Session whose post-payment redirect lands on a host they
46
- control: an open redirect carrying the credibility of a completed
47
- payment, and `cancelUrl` is the same vector without the victim even
48
- paying.
49
-
50
- | Var | Default | Meaning |
51
- | -------------- | ----------------------- | -------------------------------------------------------------------------- |
52
- | `FRONTEND_URL` | `http://localhost:3000` | The site origin checkout redirects are locked to — the allow-list, of one. |
53
-
54
- **The allow-list source is `FRONTEND_URL`**, read per request. It is the
55
- same variable this API already uses as its CORS origin
56
- (`apps/api/src/server.ts`), for the origin check in
57
- `middleware/auth.ts`, and to compose the billing-portal return URL
58
- (`routes/billing/portal.ts`) — reused rather than inventing a second
59
- site-origin contract, exactly as the scaffolded commerce checkout route
60
- does (`packages/create-ailk/templates/.claude/skills/scaffold-commerce/
61
- templates/checkout-route.template.ts`). The default is byte-identical to
62
- the one `apps/api/src/config/index.ts` applies.
63
-
64
- The comparison is **exact origin equality** — scheme, host and port must
65
- all match. A prefix or suffix test would accept
66
- `https://<site-origin>.evil.example`. A relative path cannot arrive: the
67
- shared schema refuses it before this gate runs. A refused redirect is a
68
- `400 invalid_input` raised **before** the register read, so no session,
69
- no lead and no buyer row is written and Stripe is never called; the
70
- offending URL is never echoed back or logged.
71
-
72
- **Operator consequence.** In production, leave `FRONTEND_URL` unset and
73
- your own checkout redirects will 400 — the same fail-closed direction
74
- CORS already takes on this variable. Set it to the origin visitors reach
75
- the site on. A value that is set but not a parseable absolute URL is a
76
- `500 config-error`, never a silently widened allow-list.
77
-
78
- ## Rate limit — env var and default
79
-
80
- | Var | Default | Meaning |
81
- | ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
82
- | `AILK_RATE_LIMIT_PER_IP_PER_MINUTE` | `20` | Requests one visitor IP may make against a rate-limited anonymous scope (flows/leads/aeo/schedule) in a 60-second window. |
83
-
84
- **Why 20.** The default is sized to a full flow — one visitor completing
85
- the longest shipped flow in one sitting should never trip the limit. The
86
- longest shipped flow, `ailk-founder-diagnostic`, is **13 steps + 1 GET**
87
- (the initial config fetch) = 14 requests. 20 leaves headroom for an
88
- invisible `aeo_score` step's own submission (fired alongside a visible
89
- step, #4663 D7) and one legitimate resubmit (e.g. a transient failure the
90
- visitor retries).
91
-
92
- If a customer fork ships a longer flow, raise this env var to
93
- `<step count> + 1 (GET) + 1 (aeo_score, if the flow has one) + a small
94
- margin` rather than lowering the floor for every other flow.
95
-
96
- ## Visitor-IP forwarding (#4730)
97
-
98
- A consumer site's own Server Action (`apps/web/app/[locale]/flows/[slug]/
99
- flow.actions.ts`) calls these routes from ITS OWN server process — every
100
- visitor of that site shares one calling process. Without forwarding, the
101
- API would see that process's egress IP for every request, and every
102
- visitor would share ONE rate-limit bucket.
103
-
104
- The fix has two halves:
105
-
106
- 1. `apps/api/src/server.ts` sets `trustProxy: true` on the Fastify
107
- instance, so `req.ip` resolves the LEFTMOST address of a multi-hop
108
- `X-Forwarded-For` chain (Fastify's built-in `@fastify/proxy-addr`
109
- parsing) instead of a socket address that's meaningless once requests
110
- are bridged through `.inject()` (the Vercel serverless handler,
111
- `apps/api/api/index.ts`, never opens a real socket).
112
- 2. The Server Action reads ITS OWN incoming request's `x-forwarded-for`
113
- (falling back to `x-real-ip`) via Next's `headers()` and forwards it
114
- as an explicit `x-forwarded-for` header on the outbound
115
- `flows.submitStep` call. This APPENDS to, rather than replaces,
116
- whatever hop this API's own edge subsequently adds — the leftmost
117
- entry in the resulting chain is still the real visitor.
118
-
119
- **Trade-off, stated plainly.** A caller hitting these routes directly
120
- (bypassing a consumer's Server Action) can set `x-forwarded-for` to
121
- anything — there is no fixed, allowlistable upstream proxy to trust,
122
- because this API's actual "proxies" are arbitrary consumer web
123
- processes. Per-IP rate limiting on these anonymous surfaces is a
124
- best-effort abuse deterrent, not a hard security boundary; this fix
125
- trades a small amount of spoof-resistance for correctness on the
126
- overwhelmingly common path (Server-Action-forwarded traffic), which was
127
- previously broken for every visitor.
128
-
129
- ## GET config fetch is NOT IP-forwarded (yet)
130
-
131
- `apps/web/app/[locale]/flows/[slug]/page.tsx` (a Server Component) calls
132
- `flows.get(slug)` directly — it does not forward a visitor IP header.
133
- That GET therefore still buckets on whatever `req.ip` resolves to for the
134
- calling process (not the visitor), separately from the POST-step bucket
135
- above. This is a known, narrower gap than the one #4730 fixes (a
136
- single-visitor lockout across 13+ POST steps); extending `flows.get` +
137
- `page.tsx` the same way is a natural follow-up if it proves to matter in
138
- practice.
139
-
140
- **What this means concretely, so the gap isn't understated:** a site
141
- receiving more than the default 20 flow-page loads per minute from one
142
- calling process would 429 its own config GET — and that failure would
143
- surface **nowhere in the UI**, because `flows.get` (unlike
144
- `flows.submitStep`) has no 429-to-`rate_limited` mapping at all; the page
145
- would simply fail to render the flow. This is not a regression (the
146
- pre-#4730 shared bucket was smaller, at 10), and no acceptance criterion
147
- covers it — named here so the follow-up starts from the right severity.