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,225 +0,0 @@
1
- // oss: false
2
- /**
3
- * Tenant-scoped data-access boundary (the multi-tenant module's scope layer —
4
- * FEAT-042, design decision (a)).
5
- *
6
- * Every domain-content query (agent_sessions, conversation_events,
7
- * session_read_receipts, artifacts, artifact_versions) goes through
8
- * `tenantDb(tenant)`, which injects the resolved `TenantContext.workspaceId`
9
- * into EVERY where/data clause. There is deliberately no method that accepts
10
- * a caller-supplied workspaceId, and lookups by id are `findFirst({ id,
11
- * workspaceId })` — never `findUnique({ id })` — so a foreign-tenant row is
12
- * indistinguishable from a missing one.
13
- *
14
- * Raw `prisma.<domainModel>` access outside this file and
15
- * `../services/tenant-context.ts` is forbidden by the tenant-scope
16
- * `forbidden_pattern` in architecture.yaml (CI: `pnpm architecture`).
17
- */
18
- import type {
19
- AgentSession,
20
- Artifact,
21
- ConversationEvent,
22
- Prisma,
23
- SessionReadReceipt,
24
- WorkspaceSEO,
25
- WorkspaceSiteIdentity,
26
- WorkspaceTheme,
27
- } from "@working-theory/database";
28
- import type { Id } from "@working-theory/ids";
29
-
30
- import type { TenantContext } from "../services/tenant-context.js";
31
-
32
- import { prisma } from "./prisma.js";
33
-
34
- /** Explicit surface (declaration-emit portability + a reviewable contract). */
35
- export interface TenantDb {
36
- agentSession: {
37
- list(limit: number): Promise<AgentSession[]>;
38
- byId(id: Id<"agentSession">): Promise<AgentSession | null>;
39
- create(data: { title?: string | null }): Promise<AgentSession>;
40
- updateDraft(
41
- id: Id<"agentSession">,
42
- draftMessage: string | null,
43
- ): Promise<{ count: number }>;
44
- };
45
- conversationEvent: {
46
- listSince(
47
- sessionId: Id<"agentSession">,
48
- sinceSeq: number,
49
- limit: number,
50
- ): Promise<ConversationEvent[]>;
51
- };
52
- readReceipt: {
53
- listForUser(): Promise<Array<Pick<SessionReadReceipt, "sessionId" | "lastReadSeq">>>;
54
- upsert(
55
- sessionId: Id<"agentSession">,
56
- lastReadSeq: number,
57
- ): Promise<SessionReadReceipt>;
58
- };
59
- artifact: {
60
- listBySession(sessionId: Id<"agentSession">): Promise<Artifact[]>;
61
- byId(id: Id<"artifact">): Promise<Artifact | null>;
62
- };
63
- artifactVersion: {
64
- listByArtifact(
65
- artifactId: Id<"artifact">,
66
- ): Promise<
67
- Array<{ id: Id<"artifactVersion">; savedAt: Date; isCurrent: boolean }>
68
- >;
69
- };
70
- // Per-workspace site config (#3634). Each model is `workspaceId @unique`, so
71
- // the unique key IS the tenant scope — `findUnique`/`upsert` on
72
- // `{ workspaceId }` never accepts a caller-supplied scope.
73
- theme: {
74
- get(): Promise<WorkspaceTheme | null>;
75
- upsert(data: unknown): Promise<WorkspaceTheme>;
76
- };
77
- siteIdentity: {
78
- get(): Promise<WorkspaceSiteIdentity | null>;
79
- upsert(data: unknown): Promise<WorkspaceSiteIdentity>;
80
- };
81
- seo: {
82
- get(): Promise<WorkspaceSEO | null>;
83
- upsert(data: unknown): Promise<WorkspaceSEO>;
84
- };
85
- }
86
-
87
- export function tenantDb(tenant: TenantContext): TenantDb {
88
- const { workspaceId, userId } = tenant;
89
-
90
- return {
91
- agentSession: {
92
- list(limit: number) {
93
- return prisma.agentSession.findMany({
94
- where: { workspaceId },
95
- orderBy: [
96
- { lastMessageAt: { sort: "desc", nulls: "last" } },
97
- { createdAt: "desc" },
98
- ],
99
- take: limit,
100
- });
101
- },
102
- byId(id: string) {
103
- return prisma.agentSession.findFirst({
104
- where: { id, workspaceId },
105
- });
106
- },
107
- create(data: { title?: string | null; agentId?: string }) {
108
- return prisma.agentSession.create({
109
- data: { title: data.title ?? null, workspaceId },
110
- });
111
- },
112
- updateDraft(id: string, draftMessage: string | null) {
113
- return prisma.agentSession.updateMany({
114
- where: { id, workspaceId },
115
- data: { draftMessage },
116
- });
117
- },
118
- },
119
-
120
- conversationEvent: {
121
- listSince(sessionId: string, sinceSeq: number, limit: number) {
122
- return prisma.conversationEvent.findMany({
123
- where: { sessionId, workspaceId, seq: { gt: sinceSeq } },
124
- orderBy: { seq: "asc" },
125
- take: limit,
126
- });
127
- },
128
- },
129
-
130
- readReceipt: {
131
- /** Read positions for the CALLING user across this workspace's sessions. */
132
- listForUser() {
133
- return prisma.sessionReadReceipt.findMany({
134
- where: { workspaceId, userId },
135
- select: { sessionId: true, lastReadSeq: true },
136
- });
137
- },
138
- upsert(sessionId: string, lastReadSeq: number) {
139
- return prisma.sessionReadReceipt.upsert({
140
- where: { sessionId_userId: { sessionId, userId } },
141
- create: { sessionId, workspaceId, userId, lastReadSeq },
142
- update: { lastReadSeq },
143
- });
144
- },
145
- },
146
-
147
- artifact: {
148
- listBySession(sessionId: string) {
149
- return prisma.artifact.findMany({
150
- where: { sessionId, workspaceId },
151
- orderBy: { createdAt: "asc" },
152
- });
153
- },
154
- byId(id: string) {
155
- return prisma.artifact.findFirst({ where: { id, workspaceId } });
156
- },
157
- },
158
-
159
- artifactVersion: {
160
- listByArtifact(artifactId: string) {
161
- // Brand assertion at the Prisma read edge (the DB CHECK constraints
162
- // guarantee the id shape).
163
- return prisma.artifactVersion.findMany({
164
- where: { artifactId, workspaceId },
165
- orderBy: { savedAt: "desc" },
166
- select: {
167
- id: true,
168
- savedAt: true,
169
- isCurrent: true,
170
- },
171
- }) as Promise<
172
- Array<{ id: Id<"artifactVersion">; savedAt: Date; isCurrent: boolean }>
173
- >;
174
- },
175
- },
176
-
177
- // Site config — scoped by the `@unique` workspaceId (the unique key IS the
178
- // scope). The validated blob is stored in the `data` Json column; validation
179
- // happens at the route boundary against the shared @working-theory/site-config
180
- // schema, so the DB layer stores an already-validated object.
181
- theme: {
182
- get() {
183
- return prisma.workspaceTheme.findUnique({ where: { workspaceId } });
184
- },
185
- upsert(data: unknown) {
186
- const json = data as Prisma.InputJsonValue;
187
- return prisma.workspaceTheme.upsert({
188
- where: { workspaceId },
189
- create: { workspaceId, data: json },
190
- update: { data: json },
191
- });
192
- },
193
- },
194
-
195
- siteIdentity: {
196
- get() {
197
- return prisma.workspaceSiteIdentity.findUnique({
198
- where: { workspaceId },
199
- });
200
- },
201
- upsert(data: unknown) {
202
- const json = data as Prisma.InputJsonValue;
203
- return prisma.workspaceSiteIdentity.upsert({
204
- where: { workspaceId },
205
- create: { workspaceId, data: json },
206
- update: { data: json },
207
- });
208
- },
209
- },
210
-
211
- seo: {
212
- get() {
213
- return prisma.workspaceSEO.findUnique({ where: { workspaceId } });
214
- },
215
- upsert(data: unknown) {
216
- const json = data as Prisma.InputJsonValue;
217
- return prisma.workspaceSEO.upsert({
218
- where: { workspaceId },
219
- create: { workspaceId, data: json },
220
- update: { data: json },
221
- });
222
- },
223
- },
224
- };
225
- }
@@ -1,91 +0,0 @@
1
- // oss: false
2
- /**
3
- * Short-lived stream tokens (BACKEND-REQUIREMENTS R-T3): the push channel is
4
- * authenticated by a per-session token minted over REST, not the ambient
5
- * cookie. Compact HMAC-SHA256 JWT via node:crypto — no new dependency; the
6
- * Phase-6 realtime server verifies with `verifyWsToken`.
7
- *
8
- * Secret resolution mirrors apps/web's Better Auth pattern: AILK_WS_TOKEN_SECRET
9
- * wins, BETTER_AUTH_SECRET is the shared fallback, and a dev-only default
10
- * keeps the OSS zero-config boot working. Production deployments MUST set one
11
- * of the two env vars.
12
- */
13
- import { createHmac, timingSafeEqual } from "node:crypto";
14
-
15
- import { getClock } from "@working-theory/common";
16
- import type { Id } from "@working-theory/ids";
17
-
18
- export const WS_TOKEN_TTL_SECONDS = 60;
19
-
20
- export interface WsTokenClaims {
21
- /** Caller user id. */
22
- sub: Id<"user">;
23
- /** Agent-session id the stream is scoped to. */
24
- sid: Id<"agentSession">;
25
- /** Workspace id the session belongs to (tenant scope). */
26
- wid: Id<"workspace">;
27
- iat: number;
28
- exp: number;
29
- }
30
-
31
- function resolveSecret(): string {
32
- const secret =
33
- process.env.AILK_WS_TOKEN_SECRET ?? process.env.BETTER_AUTH_SECRET;
34
- if (secret) return secret;
35
- // Fail CLOSED in production (security review M2): a token-minting path
36
- // must never run on a guessable default secret.
37
- if (process.env.NODE_ENV === "production") {
38
- throw new Error(
39
- "ws-token: set AILK_WS_TOKEN_SECRET or BETTER_AUTH_SECRET in production",
40
- );
41
- }
42
- return "ailk-dev-secret-do-not-use-in-production";
43
- }
44
-
45
- function b64url(input: Buffer | string): string {
46
- return Buffer.from(input).toString("base64url");
47
- }
48
-
49
- function sign(payload: string): string {
50
- return createHmac("sha256", resolveSecret()).update(payload).digest("base64url");
51
- }
52
-
53
- /** Mint a short-lived stream token for (user, session, workspace). */
54
- export function mintWsToken(params: {
55
- userId: Id<"user">;
56
- sessionId: Id<"agentSession">;
57
- workspaceId: Id<"workspace">;
58
- }): { token: string; expiresAt: string } {
59
- const nowSec = Math.floor(getClock().now() / 1000);
60
- const claims: WsTokenClaims = {
61
- sub: params.userId,
62
- sid: params.sessionId,
63
- wid: params.workspaceId,
64
- iat: nowSec,
65
- exp: nowSec + WS_TOKEN_TTL_SECONDS,
66
- };
67
- const head = b64url(JSON.stringify({ alg: "HS256", typ: "JWT" }));
68
- const body = b64url(JSON.stringify(claims));
69
- const token = `${head}.${body}.${sign(`${head}.${body}`)}`;
70
- return { token, expiresAt: new Date(claims.exp * 1000).toISOString() };
71
- }
72
-
73
- /** Verify a stream token; returns claims or null (invalid / expired). */
74
- export function verifyWsToken(token: string): WsTokenClaims | null {
75
- const [head, body, sig] = token.split(".");
76
- if (!head || !body || !sig) return null;
77
- const expected = sign(`${head}.${body}`);
78
- const a = Buffer.from(sig);
79
- const b = Buffer.from(expected);
80
- if (a.length !== b.length || !timingSafeEqual(a, b)) return null;
81
- try {
82
- const claims = JSON.parse(
83
- Buffer.from(body, "base64url").toString("utf-8"),
84
- ) as WsTokenClaims;
85
- if (typeof claims.exp !== "number") return null;
86
- if (claims.exp * 1000 <= getClock().now()) return null;
87
- return claims;
88
- } catch {
89
- return null;
90
- }
91
- }
@@ -1,99 +0,0 @@
1
- // oss: false
2
- /**
3
- * Tenant guard preHandler (multi-tenant module — FEAT-042).
4
- *
5
- * Runs AFTER the dual-mode auth preHandler inside the workspace route scope.
6
- * Default-deny: resolves the caller's active tenant via
7
- * `services/tenant-context.ts#resolveActiveTenant` and rejects everything
8
- * that is not an explicit grant:
9
- *
10
- * - bearer-authenticated requests (no workspace binding yet — #3591) → 403
11
- * - cookie sessions with no / stale active workspace → 403
12
- * - revoked membership → 403
13
- * - URL workspaceId ≠ the session's active workspace → 403
14
- * (compared BEFORE any lookup of the target — no tenant enumeration)
15
- *
16
- * On grant it decorates `request.tenant` (a `TenantContext`); route handlers
17
- * pass that to `tenantDb(tenant)` so every domain query is workspace-scoped.
18
- * Routes whose target is a session/artifact id (no workspaceId param) rely on
19
- * the scope layer's `findFirst({ id, workspaceId })` — a foreign target is a
20
- * 404, indistinguishable from a missing one.
21
- */
22
- import type {
23
- FastifyPluginCallback,
24
- FastifyReply,
25
- FastifyRequest,
26
- } from "fastify";
27
-
28
- import {
29
- resolveActiveTenant,
30
- type TenantContext,
31
- } from "../services/tenant-context.js";
32
-
33
- declare module "fastify" {
34
- interface FastifyRequest {
35
- tenant?: TenantContext;
36
- }
37
- }
38
-
39
- const FORBIDDEN = {
40
- ok: false,
41
- error: { code: "forbidden", message: "Forbidden" },
42
- } as const;
43
-
44
- /**
45
- * Resolve + attach the active tenant, or reply 403. Registered as a hook in
46
- * the workspace route scope (after auth), so no tenant-scoped handler runs
47
- * without a granted TenantContext.
48
- */
49
- export async function tenantPreHandler(
50
- request: FastifyRequest,
51
- reply: FastifyReply,
52
- ): Promise<void> {
53
- const userId = request.userId;
54
- if (!userId) {
55
- reply.code(401).send({
56
- ok: false,
57
- error: { code: "unauthorized", message: "Authentication required" },
58
- });
59
- return;
60
- }
61
-
62
- const resolved = await resolveActiveTenant({
63
- userId,
64
- sessionTenant: request.sessionTenant,
65
- });
66
- if (!resolved.ok) {
67
- reply.code(403).send(FORBIDDEN);
68
- return;
69
- }
70
-
71
- // URL-scoped routes: the target workspace MUST be the session's active
72
- // workspace. Compared before any target lookup — a foreign (or nonexistent)
73
- // workspaceId gets the same 403.
74
- const params = request.params as { workspaceId?: string } | undefined;
75
- if (params?.workspaceId && params.workspaceId !== resolved.value.workspaceId) {
76
- reply.code(403).send(FORBIDDEN);
77
- return;
78
- }
79
-
80
- request.tenant = resolved.value;
81
- }
82
-
83
- /**
84
- * Plugin form, mirroring `authPreHandlerPlugin`: skip-override so the hook
85
- * propagates to sibling route plugins registered in the same scope.
86
- */
87
- export const tenantPreHandlerPlugin: FastifyPluginCallback = (
88
- fastify,
89
- _opts,
90
- done,
91
- ) => {
92
- fastify.decorateRequest("tenant", undefined);
93
- fastify.addHook("preHandler", tenantPreHandler);
94
- done();
95
- };
96
-
97
- (tenantPreHandlerPlugin as FastifyPluginCallback & { [key: symbol]: boolean })[
98
- Symbol.for("skip-override")
99
- ] = true;