create-ailk 0.24.0 → 0.26.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 (638) hide show
  1. package/README.md +53 -22
  2. package/component-catalog.json +3 -3
  3. package/component-catalog.md +3 -3
  4. package/dist/add-module.js +4 -2
  5. package/dist/add-route.d.ts +12 -1
  6. package/dist/add-route.js +31 -3
  7. package/dist/cli.js +12 -7
  8. package/dist/derive-architecture.d.ts +3 -4
  9. package/dist/derive-architecture.js +65 -29
  10. package/dist/lib/design-checks.d.ts +9 -4
  11. package/dist/lib/design-checks.js +13 -7
  12. package/dist/lib/extract-module-bundle.js +29 -2
  13. package/dist/lib/fetch-template-snapshot.d.ts +2 -1
  14. package/dist/lib/fetch-template-snapshot.js +8 -3
  15. package/dist/lib/import-graph.d.ts +1 -1
  16. package/dist/lib/import-graph.js +4 -4
  17. package/dist/lib/module-license-gate.d.ts +12 -1
  18. package/dist/lib/module-license-gate.js +36 -15
  19. package/dist/lib/module-tier.d.ts +2 -8
  20. package/dist/lib/module-tier.js +13 -12
  21. package/dist/module-architecture.d.ts +20 -3
  22. package/dist/module-architecture.js +129 -552
  23. package/dist/paid-sections.d.ts +25 -1
  24. package/dist/paid-sections.js +88 -3
  25. package/dist/starter-grid.d.ts +6 -6
  26. package/dist/starter-grid.js +12 -5
  27. package/dist/surfaces.js +39 -2
  28. package/dist/template-record.d.ts +21 -3
  29. package/dist/template-record.js +38 -7
  30. package/dist/topology.d.ts +16 -3
  31. package/dist/topology.js +54 -23
  32. package/dist/update.d.ts +10 -1
  33. package/dist/update.js +715 -21
  34. package/package.json +3 -3
  35. package/release-scope.json +2 -2
  36. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +8 -2
  37. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +2 -2
  38. package/site-templates/marketing-site/ailk-template.json +7 -1
  39. package/site-templates/marketing-site/provenance.json +7 -1
  40. package/site-templates/saas-product/ailk-template.json +2 -1
  41. package/site-templates/saas-product/provenance.json +1 -0
  42. package/templates/.claude/cloudparity-repo-hooks.json +5 -0
  43. package/templates/.claude/hooks/template-guard.sh +137 -0
  44. package/templates/.claude/rules/extending.md +106 -0
  45. package/templates/.claude/rules/loop-contract.md +2 -2
  46. package/templates/.claude/settings.json +11 -0
  47. package/templates/.env.example +20 -5
  48. package/templates/.github/workflows/ci.yml +7 -4
  49. package/templates/CLAUDE.md +11 -1
  50. package/templates/CONVENTIONS.md +8 -10
  51. package/templates/README.md +14 -13
  52. package/templates/apps/api/.env.example +23 -2
  53. package/templates/apps/api/CLAUDE.md +119 -83
  54. package/templates/apps/api/jest.config.cjs +15 -5
  55. package/templates/apps/api/package.json +16 -20
  56. package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
  57. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
  58. package/templates/apps/api/src/index.ts +1 -1
  59. package/templates/apps/api/src/lib/__mocks__/prisma-client.js +16 -0
  60. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -3
  61. package/templates/apps/api/src/modules/commerce.ts +10 -0
  62. package/templates/apps/api/src/modules/content-write.ts +10 -0
  63. package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
  64. package/templates/apps/api/src/modules/scheduling.ts +10 -0
  65. package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
  66. package/templates/apps/api/src/modules/workspace.ts +10 -0
  67. package/templates/apps/api/src/server.ts +117 -1882
  68. package/templates/apps/api/vercel.json +2 -2
  69. package/templates/apps/mcp/CLAUDE.md +90 -26
  70. package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
  71. package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
  72. package/templates/apps/mcp/jest.config.cjs +6 -1
  73. package/templates/apps/mcp/package.json +5 -13
  74. package/templates/apps/mcp/src/app.ts +8 -7
  75. package/templates/apps/mcp/src/index.ts +6 -34
  76. package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
  77. package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
  78. package/templates/apps/mcp/src/site.ts +12 -0
  79. package/templates/apps/web/.env.example +1 -1
  80. package/templates/apps/web/CLAUDE.md +31 -3
  81. package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
  82. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +43 -4
  83. package/templates/apps/web/app/[locale]/(auth)/legal-footnote.tsx +35 -0
  84. package/templates/apps/web/app/[locale]/(auth)/sign-in/__tests__/page.test.tsx +117 -8
  85. package/templates/apps/web/app/[locale]/(auth)/sign-in/page.tsx +14 -37
  86. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +6 -6
  87. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-text.ts +20 -35
  88. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/page.test.tsx +108 -0
  89. package/templates/apps/web/app/[locale]/(auth)/sign-up/__tests__/sign-up-card.test.tsx +111 -5
  90. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +78 -16
  91. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +132 -12
  92. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +53 -1
  93. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/billing.test.tsx +38 -0
  94. package/templates/apps/web/app/[locale]/(authed)/account/billing/page.tsx +4 -2
  95. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +47 -24
  96. package/templates/apps/web/app/[locale]/(authed)/signed-out-gate.generated.tsx +23 -4
  97. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
  98. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
  99. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +7 -3
  100. package/templates/apps/web/app/[locale]/layout.tsx +33 -13
  101. package/templates/apps/web/app/[locale]/meet/confirmed/BookingHandOff.tsx +128 -0
  102. package/templates/apps/web/app/[locale]/meet/confirmed/__tests__/BookingHandOff.test.tsx +126 -0
  103. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
  104. package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
  105. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
  106. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
  107. package/templates/apps/web/app/api/revalidate/route.ts +3 -3
  108. package/templates/apps/web/auth.ts +10 -6
  109. package/templates/apps/web/i18n/request.ts +49 -1
  110. package/templates/apps/web/jest.config.cjs +1 -0
  111. package/templates/apps/web/lib/__tests__/account-config.test.ts +7 -0
  112. package/templates/apps/web/lib/__tests__/auth-email.test.ts +276 -0
  113. package/templates/apps/web/lib/auth-email.ts +103 -21
  114. package/templates/apps/web/lib/hub-session/__tests__/sign-in-gate.test.tsx +72 -23
  115. package/templates/apps/web/lib/hub-session/sign-in-gate.tsx +94 -70
  116. package/templates/apps/web/lib/prisma-for-auth.ts +5 -2
  117. package/templates/apps/web/lib/relay.ts +2 -2
  118. package/templates/apps/web/lib/release-scope.ts +47 -0
  119. package/templates/apps/web/lib/server-api.ts +1 -1
  120. package/templates/apps/web/lib/sign-up-agreements.ts +291 -28
  121. package/templates/apps/web/lib/site-contributions.ts +18 -0
  122. package/templates/apps/web/messages/en.json +186 -20
  123. package/templates/apps/web/messages/site/en.json +1 -0
  124. package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
  125. package/templates/customizations.yaml +37 -0
  126. package/templates/database/prisma.config.ts +10 -14
  127. package/templates/database/seed-test.ts +2 -2
  128. package/templates/database/seed.ts +5 -5
  129. package/templates/package.json +12 -6
  130. package/templates/pnpm-workspace.yaml +0 -1
  131. package/templates/project.yaml +16 -8
  132. package/templates/tools/jest/reporters.cjs +65 -0
  133. package/templates/tools/lint-architecture.ts +553 -7
  134. package/templates/tsconfig.json +1 -2
  135. package/versions.json +49 -36
  136. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
  137. package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
  138. package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
  139. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
  140. package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
  141. package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
  142. package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
  143. package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
  144. package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
  145. package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
  146. package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
  147. package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
  148. package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
  149. package/templates/apps/api/src/bin/spokes.ts +0 -198
  150. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
  151. package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
  152. package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
  153. package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
  154. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
  155. package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
  156. package/templates/apps/api/src/config/index.ts +0 -596
  157. package/templates/apps/api/src/config/modules.ts +0 -83
  158. package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
  159. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
  160. package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
  161. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
  162. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
  163. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
  164. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
  165. package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
  166. package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
  167. package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
  168. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
  169. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
  170. package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
  171. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
  172. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
  173. package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
  174. package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
  175. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
  176. package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
  177. package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -308
  178. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
  179. package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
  180. package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
  181. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
  182. package/templates/apps/api/src/lib/agent-store.ts +0 -613
  183. package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
  184. package/templates/apps/api/src/lib/consent-store.ts +0 -363
  185. package/templates/apps/api/src/lib/content-adapter.ts +0 -112
  186. package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
  187. package/templates/apps/api/src/lib/error-serializer.ts +0 -164
  188. package/templates/apps/api/src/lib/errors.ts +0 -95
  189. package/templates/apps/api/src/lib/file-fallback.ts +0 -87
  190. package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
  191. package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
  192. package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
  193. package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
  194. package/templates/apps/api/src/lib/license-key-store.ts +0 -45
  195. package/templates/apps/api/src/lib/locales.ts +0 -201
  196. package/templates/apps/api/src/lib/outbound-http.ts +0 -127
  197. package/templates/apps/api/src/lib/prisma.ts +0 -21
  198. package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
  199. package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
  200. package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
  201. package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
  202. package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
  203. package/templates/apps/api/src/lib/site.ts +0 -257
  204. package/templates/apps/api/src/lib/slice-load.ts +0 -90
  205. package/templates/apps/api/src/lib/spoke-store.ts +0 -180
  206. package/templates/apps/api/src/lib/stripe.ts +0 -321
  207. package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
  208. package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
  209. package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
  210. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
  211. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
  212. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
  213. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
  214. package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
  215. package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
  216. package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
  217. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
  218. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
  219. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
  220. package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
  221. package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
  222. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
  223. package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
  224. package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
  225. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
  226. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
  227. package/templates/apps/api/src/middleware/auth.ts +0 -864
  228. package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
  229. package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
  230. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -391
  231. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
  232. package/templates/apps/api/src/openapi/index.ts +0 -18
  233. package/templates/apps/api/src/openapi/spec.ts +0 -2148
  234. package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
  235. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
  236. package/templates/apps/api/src/routes/aeo/index.ts +0 -107
  237. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
  238. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
  239. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
  240. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
  241. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
  242. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
  243. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
  244. package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
  245. package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
  246. package/templates/apps/api/src/routes/auth/user.ts +0 -305
  247. package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -332
  248. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
  249. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1426
  250. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
  251. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
  252. package/templates/apps/api/src/routes/checkout/index.ts +0 -34
  253. package/templates/apps/api/src/routes/checkout/session.ts +0 -171
  254. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
  255. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
  256. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
  257. package/templates/apps/api/src/routes/consent/account.ts +0 -268
  258. package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
  259. package/templates/apps/api/src/routes/consent/index.ts +0 -26
  260. package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
  261. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
  262. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
  263. package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
  264. package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
  265. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
  266. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
  267. package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
  268. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
  269. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
  270. package/templates/apps/api/src/routes/content/get-page.ts +0 -183
  271. package/templates/apps/api/src/routes/content/index.ts +0 -49
  272. package/templates/apps/api/src/routes/content/list-pages.ts +0 -74
  273. package/templates/apps/api/src/routes/content/list-types.ts +0 -19
  274. package/templates/apps/api/src/routes/content/offerings.ts +0 -184
  275. package/templates/apps/api/src/routes/content/pricing.ts +0 -176
  276. package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
  277. package/templates/apps/api/src/routes/content/search.ts +0 -100
  278. package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
  279. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
  280. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
  281. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
  282. package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
  283. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
  284. package/templates/apps/api/src/routes/leads/index.ts +0 -639
  285. package/templates/apps/api/src/routes/leads/list.ts +0 -104
  286. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
  287. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
  288. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
  289. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -491
  290. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -476
  291. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
  292. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
  293. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
  294. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
  295. package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
  296. package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
  297. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
  298. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
  299. package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
  300. package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
  301. package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
  302. package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
  303. package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
  304. package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
  305. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
  306. package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
  307. package/templates/apps/api/src/routes/requests/index.ts +0 -54
  308. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
  309. package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
  310. package/templates/apps/api/src/routes/webhooks/README.md +0 -398
  311. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
  312. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
  313. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -449
  314. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
  315. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
  316. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
  317. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
  318. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
  319. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
  320. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
  321. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
  322. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -809
  323. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
  324. package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
  325. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
  326. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2028
  327. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -645
  328. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
  329. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
  330. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
  331. package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
  332. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
  333. package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
  334. package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
  335. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -877
  336. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
  337. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
  338. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
  339. package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
  340. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
  341. package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
  342. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
  343. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
  344. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
  345. package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1204
  346. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -450
  347. package/templates/apps/api/src/services/__tests__/project-listings.counts.test.ts +0 -202
  348. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -151
  349. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
  350. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
  351. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
  352. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
  353. package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
  354. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
  355. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
  356. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
  357. package/templates/apps/api/src/services/abandon-email.ts +0 -336
  358. package/templates/apps/api/src/services/aeo-score.ts +0 -325
  359. package/templates/apps/api/src/services/answer-distributions.ts +0 -138
  360. package/templates/apps/api/src/services/audit-log.ts +0 -961
  361. package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
  362. package/templates/apps/api/src/services/consent.ts +0 -834
  363. package/templates/apps/api/src/services/default-tenant.ts +0 -416
  364. package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -750
  365. package/templates/apps/api/src/services/dunning-policy.ts +0 -294
  366. package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
  367. package/templates/apps/api/src/services/entitlements.ts +0 -338
  368. package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
  369. package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
  370. package/templates/apps/api/src/services/lead-promotion.ts +0 -160
  371. package/templates/apps/api/src/services/lead-routing.ts +0 -1372
  372. package/templates/apps/api/src/services/leads-inbox.ts +0 -312
  373. package/templates/apps/api/src/services/listing-config.ts +0 -178
  374. package/templates/apps/api/src/services/magnet-completion.ts +0 -242
  375. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -348
  376. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
  377. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -975
  378. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
  379. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -400
  380. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -685
  381. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
  382. package/templates/apps/api/src/services/offerings/catalog.ts +0 -318
  383. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -403
  384. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
  385. package/templates/apps/api/src/services/offerings/connection.ts +0 -1083
  386. package/templates/apps/api/src/services/offerings/index.ts +0 -614
  387. package/templates/apps/api/src/services/offerings/source.ts +0 -140
  388. package/templates/apps/api/src/services/offerings/store.ts +0 -150
  389. package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
  390. package/templates/apps/api/src/services/person.ts +0 -1211
  391. package/templates/apps/api/src/services/plan-offerings.ts +0 -125
  392. package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
  393. package/templates/apps/api/src/services/project-listings.ts +0 -2085
  394. package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
  395. package/templates/apps/api/src/services/request-confirm.ts +0 -698
  396. package/templates/apps/api/src/services/revalidation.ts +0 -235
  397. package/templates/apps/api/src/services/slack-notify.ts +0 -291
  398. package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
  399. package/templates/apps/api/src/services/usage-credits.ts +0 -340
  400. package/templates/apps/api/src/services/verified-email.ts +0 -71
  401. package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
  402. package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
  403. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
  404. package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
  405. package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
  406. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
  407. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
  408. package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
  409. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
  410. package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
  411. package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
  412. package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
  413. package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
  414. package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
  415. package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
  416. package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
  417. package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1232
  418. package/templates/apps/mcp/__tests__/index.test.ts +0 -195
  419. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
  420. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
  421. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -70
  422. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -162
  423. package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
  424. package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
  425. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -180
  426. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
  427. package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -268
  428. package/templates/apps/mcp/__tests__/locale.test.ts +0 -120
  429. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
  430. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
  431. package/templates/apps/mcp/__tests__/rate-limit-write.test.ts +0 -189
  432. package/templates/apps/mcp/__tests__/read_content.test.ts +0 -511
  433. package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -160
  434. package/templates/apps/mcp/__tests__/search_content.test.ts +0 -271
  435. package/templates/apps/mcp/__tests__/server.test.ts +0 -388
  436. package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
  437. package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -222
  438. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -152
  439. package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
  440. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
  441. package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
  442. package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
  443. package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
  444. package/templates/apps/mcp/src/abuse/index.ts +0 -349
  445. package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
  446. package/templates/apps/mcp/src/boot.ts +0 -167
  447. package/templates/apps/mcp/src/config/modules.ts +0 -43
  448. package/templates/apps/mcp/src/http-transport.ts +0 -564
  449. package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
  450. package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
  451. package/templates/apps/mcp/src/lib/content-adapter.ts +0 -116
  452. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -140
  453. package/templates/apps/mcp/src/lib/errors.ts +0 -61
  454. package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
  455. package/templates/apps/mcp/src/lib/locales.ts +0 -171
  456. package/templates/apps/mcp/src/lib/prisma.ts +0 -39
  457. package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
  458. package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
  459. package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
  460. package/templates/apps/mcp/src/lib/site-config.ts +0 -53
  461. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
  462. package/templates/apps/mcp/src/request-context.ts +0 -52
  463. package/templates/apps/mcp/src/server.ts +0 -343
  464. package/templates/apps/mcp/src/serverless-app.ts +0 -88
  465. package/templates/apps/mcp/src/tools/audit.ts +0 -127
  466. package/templates/apps/mcp/src/tools/capture_lead.ts +0 -66
  467. package/templates/apps/mcp/src/tools/create_project.ts +0 -134
  468. package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
  469. package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
  470. package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
  471. package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
  472. package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
  473. package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
  474. package/templates/apps/mcp/src/tools/index.ts +0 -138
  475. package/templates/apps/mcp/src/tools/list_pages.ts +0 -68
  476. package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
  477. package/templates/apps/mcp/src/tools/proposals.ts +0 -27
  478. package/templates/apps/mcp/src/tools/purchase.ts +0 -205
  479. package/templates/apps/mcp/src/tools/read_content.ts +0 -81
  480. package/templates/apps/mcp/src/tools/request_callback.ts +0 -52
  481. package/templates/apps/mcp/src/tools/search_content.ts +0 -63
  482. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -54
  483. package/templates/apps/mcp/src/workspace-server.ts +0 -224
  484. package/templates/database/CHANGELOG.md +0 -981
  485. package/templates/database/LICENSE +0 -202
  486. package/templates/database/NOTICE +0 -2
  487. package/templates/database/README.md +0 -243
  488. package/templates/database/__tests__/agents.db.test.ts +0 -320
  489. package/templates/database/__tests__/appointment.db.test.ts +0 -106
  490. package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
  491. package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
  492. package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
  493. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
  494. package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
  495. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
  496. package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
  497. package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
  498. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
  499. package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
  500. package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
  501. package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
  502. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
  503. package/templates/database/__tests__/flows-presets.test.ts +0 -173
  504. package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
  505. package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
  506. package/templates/database/__tests__/member-area.db.test.ts +0 -257
  507. package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
  508. package/templates/database/__tests__/member-contract.db.test.ts +0 -413
  509. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
  510. package/templates/database/__tests__/offerings-migration.test.ts +0 -197
  511. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
  512. package/templates/database/__tests__/offerings.db.test.ts +0 -303
  513. package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
  514. package/templates/database/__tests__/person-contact.db.test.ts +0 -563
  515. package/templates/database/__tests__/person-notes.db.test.ts +0 -249
  516. package/templates/database/__tests__/person.db.test.ts +0 -586
  517. package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
  518. package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
  519. package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
  520. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
  521. package/templates/database/__tests__/seed.test.ts +0 -272
  522. package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
  523. package/templates/database/__tests__/tenancy.test.ts +0 -141
  524. package/templates/database/__tests__/trial-magnet-migration.test.ts +0 -186
  525. package/templates/database/__tests__/user-cascade.test.ts +0 -140
  526. package/templates/database/_gitignore +0 -8
  527. package/templates/database/auth/.gitkeep +0 -0
  528. package/templates/database/auth/schema.prisma +0 -641
  529. package/templates/database/config/.gitkeep +0 -0
  530. package/templates/database/config/schema.prisma +0 -441
  531. package/templates/database/content/.gitkeep +0 -0
  532. package/templates/database/content/schema.prisma +0 -339
  533. package/templates/database/eslint.config.mjs +0 -69
  534. package/templates/database/flows/listing-diagnostic.ts +0 -182
  535. package/templates/database/flows/presets.ts +0 -315
  536. package/templates/database/inbox/.gitkeep +0 -0
  537. package/templates/database/inbox/README.md +0 -3
  538. package/templates/database/inbox/__tests__/schema.test.ts +0 -44
  539. package/templates/database/inbox/schema.prisma +0 -1447
  540. package/templates/database/jest.config.cjs +0 -36
  541. package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
  542. package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
  543. package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
  544. package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
  545. package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
  546. package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
  547. package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
  548. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
  549. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
  550. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
  551. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
  552. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
  553. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
  554. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
  555. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
  556. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
  557. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
  558. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
  559. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
  560. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
  561. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
  562. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
  563. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
  564. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
  565. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
  566. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
  567. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
  568. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
  569. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
  570. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
  571. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
  572. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
  573. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
  574. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
  575. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
  576. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
  577. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
  578. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
  579. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
  580. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
  581. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
  582. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
  583. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
  584. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
  585. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
  586. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
  587. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
  588. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
  589. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
  590. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
  591. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
  592. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
  593. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
  594. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
  595. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
  596. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
  597. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
  598. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
  599. package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
  600. package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
  601. package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
  602. package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
  603. package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
  604. package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
  605. package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
  606. package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
  607. package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
  608. package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
  609. package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
  610. package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
  611. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
  612. package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
  613. package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
  614. package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
  615. package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
  616. package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
  617. package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
  618. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
  619. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
  620. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
  621. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
  622. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
  623. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
  624. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
  625. package/templates/database/migrations/20261025120000_trial_magnet/migration.sql +0 -28
  626. package/templates/database/migrations/20261026120000_trial_magnet_proof_line/migration.sql +0 -23
  627. package/templates/database/migrations/migration_lock.toml +0 -3
  628. package/templates/database/ops/.gitkeep +0 -0
  629. package/templates/database/ops/schema.prisma +0 -43
  630. package/templates/database/package.json +0 -89
  631. package/templates/database/schema.prisma +0 -71
  632. package/templates/database/scripts/db-generate-locked.sh +0 -80
  633. package/templates/database/scripts/person-backfill.sql +0 -132
  634. package/templates/database/tenancy.ts +0 -183
  635. package/templates/database/tsconfig.build.json +0 -16
  636. package/templates/database/tsconfig.flows.json +0 -15
  637. package/templates/database/tsconfig.json +0 -16
  638. package/templates/database/tsconfig.tenancy.json +0 -14
@@ -1,164 +0,0 @@
1
- /**
2
- * Project-listings route group (#4998 — the founder's application row;
3
- * reshaped by #5036 — the application became a resumable draft).
4
- *
5
- * Routes:
6
- * POST /v1/project-listings — anonymous start (leadsScope)
7
- * PATCH /v1/project-listings/drafts/:id — anonymous save-as-you-go, token-gated
8
- * POST /v1/project-listings/drafts/:id/submit — anonymous submit, token-gated
9
- * POST /v1/project-listings/drafts/:id/verify-email — anonymous, token- AND
10
- * nonce-gated (#5407)
11
- * GET /v1/project-listings/public — anonymous page-render read (leadsScope)
12
- * GET /v1/project-listings/directory — anonymous landing-page directory (own scope, #7521)
13
- * GET /v1/project-listings/me — the founder's OWN listings (authScope)
14
- * GET /v1/project-listings — tenant-scoped dashboard list
15
- * GET /v1/project-listings/:id — tenant-scoped single read
16
- * PATCH /v1/project-listings/:id — tenant-scoped decision + featured flag
17
- * PATCH /v1/project-listings/:id/copy — tenant-scoped operator copy edit
18
- *
19
- * The anonymous set is registered in `server.ts`'s rate-limited `leadsScope`
20
- * alongside `/v1/leads/*`; the tenant-scoped set is registered inside
21
- * `tenantScope`, behind `tenantPreHandlerPlugin`, the same as
22
- * `waitlistExperimentRoutes`. The `/drafts/` path segment is what keeps the
23
- * anonymous token-gated PATCH and the operator's tenant-guarded PATCH apart
24
- * at the router — they differ in segment count, so neither shadows the other.
25
- *
26
- * `/me` (#5295) sits between the two scopes: `authScope` only, no tenant guard.
27
- * A founder is not a workspace member, so the guard's default-deny answers 403
28
- * for their own submission — and the fix is this ADDITIVE route, joined on the
29
- * owner link, rather than a relaxed `inbox-read` permission. Its static `/me`
30
- * segment takes router precedence over the parametric `/:id` below, exactly as
31
- * the already-shipped static `/public` does.
32
- *
33
- * API<->MCP parity: the anonymous set has no MCP twin — it mirrors the leads
34
- * routes' own identity-blind posture (an applying founder has no account, and
35
- * the resume token is a per-draft capability rather than an identity). Three
36
- * tenant-scoped routes have a twin on the signed-in workspace MCP surface,
37
- * `/mcp/workspace` (#3591 D13), each parsing with a
38
- * `@working-theory/validation` export (D10):
39
- * `GET /v1/project-listings` — `list_inbox_applications`,
40
- * `GET /v1/project-listings/:id` — `get_inbox_application`,
41
- * `PATCH /v1/project-listings/:id` — `decide_inbox_application`. The copy
42
- * edit (`PATCH /:id/copy`) keeps the no-twin posture of the sibling
43
- * `/v1/waitlist-experiments/*` routes. The directory read (#7521) takes the
44
- * same no-twin posture as `/public`: agents read a directory from its page's
45
- * JSON-LD, and a `list_directory` tool is a follow-up.
46
- */
47
- import type { FastifyPluginAsync } from "fastify";
48
-
49
- import { loadSlice } from "../../lib/slice-load.js";
50
-
51
- import { projectListingMeRoute as meRoute } from "./me.js";
52
- import { patchProjectListingDraftRoute } from "./patch-draft.js";
53
- import {
54
- projectListingDirectoryRoute as directoryRoute,
55
- publicProjectListingsRoute,
56
- } from "./public.js";
57
- import { startProjectListingRoute } from "./start.js";
58
- import { submitProjectListingRoute } from "./submit.js";
59
- import { verifyProjectListingDraftEmailRoute } from "./verify-email.js";
60
-
61
- export const projectListingStartRoute = startProjectListingRoute;
62
- export const projectListingDraftPatchRoute = patchProjectListingDraftRoute;
63
- export const projectListingSubmitRoute = submitProjectListingRoute;
64
- export const projectListingPublicRoute = publicProjectListingsRoute;
65
- /**
66
- * The landing-page directory read (#7521). Static, like `/public`; `server.ts`
67
- * registers it in its own rate-limited scope, not `leadsScope`.
68
- */
69
- export const projectListingDirectoryRoute = directoryRoute;
70
- /**
71
- * The resume link's verification write (#5407). Static, not a slice: it is
72
- * part of the free anonymous surface, exactly like the three routes above.
73
- */
74
- export const projectListingVerifyEmailRoute = verifyProjectListingDraftEmailRoute;
75
- /**
76
- * The founder's own listings read (#5295). Static, not a slice: it depends on
77
- * the owner link alone, so it is registered — and works — on a build with no
78
- * multi-tenant module, the same as `flowCheckoutRoutes`.
79
- */
80
- export const projectListingMeRoute = meRoute;
81
-
82
- /**
83
- * Tenant-scoped handlers, as { specifier, exportName }, reached at REGISTRATION
84
- * time rather than named as static imports (#5182).
85
- *
86
- * `get.ts`, `list.ts`, `patch.ts` and `patch-copy.ts` are multi-tenant SLICE
87
- * files the OSS strip removes; the other four route modules in this directory
88
- * are the free anonymous surface and stay static. This directory is mixed on
89
- * purpose, which is why the boundary is drawn here at the registration
90
- * call-site rather than by splitting the directory.
91
- *
92
- * The specifier is kept off the `import()` call site because `tsc` resolves a
93
- * string-literal `import("...")` EXPRESSION whether or not the branch runs, and
94
- * would report TS2307 on the stripped tree. It is held in a module-level
95
- * constant rather than read out of the table per iteration (#5304): the table
96
- * form was invisible to the deploy-time file tracer too, so these files were
97
- * compiled and never shipped, and the constant form is the one shape `tsc`
98
- * refuses to resolve and `@vercel/nft` still traces. `specifier` stays on each
99
- * row for the drift message and reads the same constant as `load`. The plugin body
100
- * only executes when `server.ts` registers it, which it does inside
101
- * `isModuleEnabled('multi-tenant')` — so nothing here runs on an OSS build.
102
- *
103
- * The export name is unchanged so `server.ts` needs no edit: it still reaches
104
- * `projectListingTenantRoutes` from this module.
105
- */
106
- const LIST_HANDLER = "./list.js";
107
- const GET_HANDLER = "./get.js";
108
- const PATCH_HANDLER = "./patch.js";
109
- const PATCH_COPY_HANDLER = "./patch-copy.js";
110
-
111
- const TENANT_HANDLERS: ReadonlyArray<{
112
- method: "get" | "patch";
113
- url: string;
114
- specifier: string;
115
- exportName: string;
116
- load: () => Promise<unknown>;
117
- }> = [
118
- {
119
- method: "get",
120
- url: "/v1/project-listings",
121
- specifier: LIST_HANDLER,
122
- exportName: "listProjectListingsHandler",
123
- load: () => import(LIST_HANDLER),
124
- },
125
- {
126
- method: "get",
127
- url: "/v1/project-listings/:id",
128
- specifier: GET_HANDLER,
129
- exportName: "getProjectListingHandler",
130
- load: () => import(GET_HANDLER),
131
- },
132
- {
133
- method: "patch",
134
- url: "/v1/project-listings/:id",
135
- specifier: PATCH_HANDLER,
136
- exportName: "patchProjectListingHandler",
137
- load: () => import(PATCH_HANDLER),
138
- },
139
- {
140
- method: "patch",
141
- url: "/v1/project-listings/:id/copy",
142
- specifier: PATCH_COPY_HANDLER,
143
- exportName: "patchProjectListingCopyHandler",
144
- load: () => import(PATCH_COPY_HANDLER),
145
- },
146
- ];
147
-
148
- export const projectListingTenantRoutes: FastifyPluginAsync = async (
149
- fastify,
150
- ) => {
151
- for (const { method, url, specifier, exportName, load } of TENANT_HANDLERS) {
152
- const mod = (await loadSlice(specifier, load)) as Record<
153
- string,
154
- Parameters<typeof fastify.get>[1] | undefined
155
- >;
156
- const handler = mod[exportName];
157
- if (!handler) {
158
- throw new Error(
159
- `multi-tenant slice drift: '${specifier}' does not export '${exportName}'`,
160
- );
161
- }
162
- fastify[method](url, handler);
163
- }
164
- };
@@ -1,87 +0,0 @@
1
- /**
2
- * NOT marked `oss: false` (D-48/#5366 escalation, mirrors the original
3
- * multi-tenant/#5088 card's own conclusion about this exact file, lines
4
- * 556-571 of module-architecture.ts): `./index.ts` in this directory is a
5
- * MIXED barrel that statically imports this file, and `server.ts` reaches
6
- * `./index.ts` via a plain (non-slice) literal `import()` for multi-tenant's
7
- * tenant-scoped routes — a specifier `tsc` always resolves, module selection
8
- * or not. Stripping this file leaves that barrel with a dangling import on
9
- * every tree. Registration stays gated (`isModuleEnabled("signal-collectors")`
10
- * in server.ts); only the source file itself must ship unconditionally.
11
- *
12
- * GET /v1/project-listings/me
13
- *
14
- * The FOUNDER'S OWN listings read (#5295) — the `/v1/flow-checkouts/me` and
15
- * `/v1/waitlist-signups/me` posture, for `ProjectListing`.
16
- *
17
- * Registered INSIDE `server.ts`'s `authScope` (auth only — NOT the inner
18
- * `tenantScope`): a founder is not a workspace member, and the tenant guard is
19
- * default-deny, so a founder reaching the tenant-scoped list route gets a 403
20
- * for their own submission. That is the problem this route exists to solve, and
21
- * it is solved by ADDING this route rather than by relaxing the tenant
22
- * routes' permissions — `GET|PATCH /v1/project-listings[/:id]` stay on the
23
- * permission table's `inbox-read` / `applications-decide` rows (#7636 D2),
24
- * which a founder, holding no Membership, never reaches.
25
- *
26
- * Identity is `request.userId` ONLY. The route declares no query schema and
27
- * reads no query or body key — an `email` parameter is never parsed and never
28
- * read — and the service joins on the owner link and nothing else
29
- * (`services/project-listings.ts`'s `listMyProjectListings`: `ownerUserId ===
30
- * userId`, never a `founderEmail` comparison at read time).
31
- *
32
- * Route precedence: this static path is registered alongside the parametric
33
- * `/v1/project-listings/:id` in the tenant scope. Fastify's router prefers a
34
- * static segment over a parametric one, so `/me` reaches this handler, exactly
35
- * as the already-shipped static `/v1/project-listings/public` coexists with the
36
- * same parametric route.
37
- *
38
- * Envelope per apps/api/CLAUDE.md: `{ ok: true, data }` /
39
- * `{ ok: false, error: { code, message } }`, real HTTP status codes:
40
- * 401 unauthorized · 500 internal_error (the 200 body failed its own
41
- * contract schema — a drift is never a leak). A signed-in caller who owns
42
- * NOTHING is a 200 with an empty list, never a 403: they are legitimately
43
- * signed in and simply own no listing yet.
44
- *
45
- * API↔MCP parity: identity-bound (the session IS the identity; the OSS MCP
46
- * surface is identity-blind) — no MCP twin (#3591 exception, the same shape as
47
- * `/v1/flow-checkouts/me` and `/v1/waitlist-signups/me`).
48
- */
49
- import { projectListingMeResponseSchema } from "@working-theory/validation";
50
- import type { FastifyPluginAsync, FastifyReply } from "fastify";
51
-
52
- import { listMyProjectListings } from "../../services/project-listings.js";
53
-
54
- import { sendValidated } from "./respond.js";
55
-
56
- function unauthorized(reply: FastifyReply) {
57
- return reply.status(401).send({
58
- ok: false,
59
- error: { code: "unauthorized", message: "Authentication required" },
60
- });
61
- }
62
-
63
- export const projectListingMeRoute: FastifyPluginAsync = async (fastify) => {
64
- fastify.get("/v1/project-listings/me", async (request, reply) => {
65
- const userId = request.userId;
66
- if (!userId) return unauthorized(reply);
67
-
68
- const listings = await listMyProjectListings(userId);
69
-
70
- // The omission is explicit at the send site rather than hidden in a schema
71
- // strip, the idiom `patch-draft.ts` uses: `projectListingDraftSchema` is
72
- // `.strict()`, so a key that stops being dropped here becomes a 500 instead
73
- // of quietly shipping an internal reference to the founder.
74
- const data = listings.map(
75
- ({
76
- siteId: _siteId,
77
- leadId: _leadId,
78
- decidedBy: _decidedBy,
79
- issueUrl: _issueUrl,
80
- experimentKey: _experimentKey,
81
- ...mine
82
- }) => mine,
83
- );
84
-
85
- return sendValidated(reply, projectListingMeResponseSchema, data, "me");
86
- });
87
- };
@@ -1,224 +0,0 @@
1
- // oss: false
2
- /**
3
- * PATCH /v1/project-listings/:id/copy
4
- *
5
- * The operator's copy edit — the THIRD review outcome, beside approve and
6
- * reject.
7
- *
8
- * Before this route a submitted listing's copy was frozen for everyone: the
9
- * legal transitions out of `requested` are `active` and `rejected` only, there
10
- * is no route back to `draft`, and `PATCH /v1/project-listings/drafts/:id` is
11
- * scoped to `status: "draft"` plus the applicant's own resume-token hash. A
12
- * listing whose headline needed a trim could therefore be published exactly as
13
- * written or rejected outright, and the founder could not revise it either.
14
- *
15
- * Deliberately NOT folded into `PATCH /v1/project-listings/:id`. That body is
16
- * `.strict()`, carries at most `status` and `featured`, and exists to express
17
- * the DECISION transition; entangling "edit the copy" with "decide the
18
- * listing" would muddy a contract that is currently clean. The lifecycle is
19
- * untouched by this route — it adds no status, no transition and no decision
20
- * target.
21
- *
22
- * Auth, tenant scoping and the error shapes are `patch.ts`'s, with the
23
- * `listings-edit` permission (owner, admin — #7636 D2) as the floor, the
24
- * same `siteId` scoping from `request.tenant`, and the same 401 / 403 / 400 /
25
- * 404 bodies. A foreign or missing id is `404`, so a
26
- * cross-tenant probe cannot tell "exists but not yours" from "does not exist".
27
- *
28
- * `questions` is replaced wholesale, and its `extras` array carries a
29
- * `.default([])` in the package schema, so a payload omitting that key would
30
- * parse to an empty array and silently delete the founder's extra questions.
31
- * A `questions` body without an explicit `extras` is therefore a `400`, which
32
- * makes clearing them something the caller has to say rather than something it
33
- * can do by forgetting.
34
- *
35
- * The status floor (`requested` or `active` only) lives in the service's WHERE
36
- * clause rather than in a read-then-write check, so a concurrent decision
37
- * cannot slip the row out of the editable set between the check and the write.
38
- * A `draft`, `rejected` or `closed` row is `404` and unchanged — `closed`
39
- * included, even though nothing stores it: the WHERE transcribes
40
- * `computeListingStatus`'s accepting branches rather than its stored string,
41
- * so an elapsed window refuses the edit exactly as it refuses on a read.
42
- */
43
- import { zProjectListingId } from "@working-theory/ids";
44
- import {
45
- buildProjectListingDraftPatchSchema,
46
- can,
47
- projectListingSchema,
48
- } from "@working-theory/validation";
49
- import type { FastifyReply, FastifyRequest } from "fastify";
50
- import { z } from "zod";
51
-
52
- import { listingConfigFor } from "../../services/listing-config.js";
53
- import { patchListingCopy } from "../../services/project-listings.js";
54
-
55
- import { sendValidated } from "./respond.js";
56
-
57
- const paramsSchema = z.object({ id: zProjectListingId });
58
-
59
- /**
60
- * The five PUBLISHED copy fields this route may rewrite.
61
- *
62
- * `questions` is in because its prompts and option labels render on the public
63
- * page, which is exactly where abusive language would land. `name` is out
64
- * because the slug is derived from it at submit and stored, so editing it would
65
- * desync the public URL from the name. `url`, `product`, `commitment`,
66
- * `teamComposition`, `address` and `founderCompany` are out because they are
67
- * facts rather than copy — a wrong fact is a question to the founder or a
68
- * reject, not an edit. `anythingElse` is out because it is a note to the
69
- * reviewer and never renders.
70
- */
71
- const COPY_FIELDS = ["headline", "oneLiner", "problem", "faqs", "questions"] as const;
72
-
73
- /**
74
- * The copy-edit body, DERIVED from the application's own schema at THIS SITE's
75
- * configured bounds rather than re-declared.
76
- *
77
- * Deriving is what carries the word caps, the exactly-three-FAQ rule and every
78
- * question refinement into this route for free, and what keeps them from
79
- * drifting: a bound the application gains is a bound the copy edit gains in the
80
- * same commit.
81
- *
82
- * Built from `buildProjectListingDraftPatchSchema` — the same builder the
83
- * applicant's own write path uses — rather than from the fixed
84
- * `projectListingApplicationFieldsSchema`, because since #5048 D5/D6 the word
85
- * caps and the collected field set are the SITE's configuration, not platform
86
- * constants. Against the fixed schema an operator could not re-save copy the
87
- * applicant was legitimately allowed to submit: a site configured to fifteen
88
- * words would accept a fourteen-word headline at submit and refuse the same
89
- * text here. Passing the intersection of the site's `collectedFields` with
90
- * `COPY_FIELDS` as the collected set is what narrows the builder's full roster
91
- * to this route's five, so a field the site does not collect cannot be authored
92
- * onto a founder's public page by the operator either — the same security
93
- * property the builder's own docblock records.
94
- *
95
- * Unlike `patch-draft.ts`, which must resolve the draft before it knows the
96
- * site and therefore validates inside the service, this route reads the site
97
- * from the caller's own session, so the schema is built here and the editable
98
- * set stays visible in this file.
99
- *
100
- * NOT lifted into `@working-theory/validation` as a sibling of
101
- * `projectListingPatchSchema`, tempting as the symmetry is. This file is
102
- * vendored byte-for-byte into a consuming repo that installs that package from
103
- * a PUBLISHED version, so a new export would leave the synced copy referencing
104
- * a symbol its installed version does not have — broken until a publish and a
105
- * version bump land. `buildProjectListingDraftPatchSchema` is already public in
106
- * the version that repo has (`services/project-listings.ts` imports it), so
107
- * this local derivation syncs cleanly.
108
- *
109
- * The builder supplies the at-least-one-key refine, so an empty body is a 400
110
- * rather than a silent no-op the caller would read as a successful save.
111
- */
112
- function copyPatchSchemaFor(
113
- wordCaps: Parameters<typeof buildProjectListingDraftPatchSchema>[0],
114
- collectedFields: readonly string[],
115
- ) {
116
- return buildProjectListingDraftPatchSchema(
117
- wordCaps,
118
- COPY_FIELDS.filter((field) => collectedFields.includes(field)),
119
- );
120
- }
121
-
122
- function unauthorized(reply: FastifyReply) {
123
- return reply.status(401).send({
124
- ok: false,
125
- error: { code: "unauthorized", message: "Authentication required" },
126
- });
127
- }
128
-
129
- function forbidden(reply: FastifyReply) {
130
- return reply.status(403).send({
131
- ok: false,
132
- error: { code: "forbidden", message: "Forbidden" },
133
- });
134
- }
135
-
136
- function invalidParams(reply: FastifyReply) {
137
- return reply.status(400).send({
138
- ok: false,
139
- error: { code: "validation_failed", message: "Invalid project listing id" },
140
- });
141
- }
142
-
143
- function invalidBody(reply: FastifyReply, message: string) {
144
- return reply.status(400).send({
145
- ok: false,
146
- error: { code: "invalid_input", message },
147
- });
148
- }
149
-
150
- function notFound(reply: FastifyReply) {
151
- return reply.status(404).send({
152
- ok: false,
153
- error: { code: "not_found", message: "Project listing not found" },
154
- });
155
- }
156
-
157
- export async function patchProjectListingCopyHandler(
158
- request: FastifyRequest,
159
- reply: FastifyReply,
160
- ): Promise<void> {
161
- const tenant = request.tenant;
162
- if (!tenant) return unauthorized(reply);
163
- if (!can(tenant.role, "listings-edit")) {
164
- request.log.warn(
165
- { method: request.method, path: request.url, status: 403 },
166
- "[project-listings] copy patch denied; insufficient role",
167
- );
168
- return forbidden(reply);
169
- }
170
-
171
- const parsedParams = paramsSchema.safeParse(request.params);
172
- if (!parsedParams.success) return invalidParams(reply);
173
-
174
- const listingConfig = await listingConfigFor(tenant.workspaceId);
175
- const editableFields = COPY_FIELDS.filter((field) =>
176
- listingConfig.collectedFields.includes(field),
177
- );
178
- if (editableFields.length === 0) {
179
- // A site whose application collects none of the five copy fields has no
180
- // copy to edit. Refused here rather than handed to the builder, whose
181
- // `pickCollected` throws on an empty mask — that throw guards the
182
- // applicant's surface and would be a 500 on this one.
183
- return invalidBody(reply, "this site collects none of the editable copy fields");
184
- }
185
-
186
- const parsedBody = copyPatchSchemaFor(
187
- listingConfig.wordCaps,
188
- listingConfig.collectedFields,
189
- ).safeParse(request.body);
190
- if (!parsedBody.success) {
191
- return invalidBody(reply, parsedBody.error.issues[0]?.message ?? "invalid input");
192
- }
193
-
194
- // `questions.extras` carries a `.default([])` in the package schema, so a
195
- // `questions` payload that OMITS the key parses to `extras: []` —
196
- // indistinguishable from a deliberate clear. `questions` is replaced
197
- // wholesale, so accepting the ambiguous body would silently delete the
198
- // founder's up-to-three extra questions with no error. Refuse it instead and
199
- // make the caller say which it meant. Checked against the RAW body because
200
- // the parsed value always carries the key once zod has applied the default.
201
- if (parsedBody.data.questions !== undefined) {
202
- const rawQuestions = (request.body as { questions?: unknown }).questions;
203
- if (
204
- typeof rawQuestions !== "object" ||
205
- rawQuestions === null ||
206
- !("extras" in rawQuestions)
207
- ) {
208
- return invalidBody(
209
- reply,
210
- "questions must carry an explicit 'extras' array; send [] to clear it",
211
- );
212
- }
213
- }
214
-
215
- const result = await patchListingCopy(
216
- tenant.workspaceId,
217
- parsedParams.data.id,
218
- parsedBody.data,
219
- tenant.userId,
220
- );
221
- if (!result.ok) return notFound(reply);
222
-
223
- return sendValidated(reply, projectListingSchema, result.value, "patch-copy");
224
- }
@@ -1,102 +0,0 @@
1
- /**
2
- * NOT marked `oss: false` — see `./me.ts`'s header comment (D-48/#5366
3
- * escalation: `./index.ts` is a mixed barrel multi-tenant's `server.ts`
4
- * reaches by a literal, always-resolved `import()`; this file ships
5
- * unconditionally, registration stays gated).
6
- *
7
- * PATCH /v1/project-listings/drafts/:id
8
- *
9
- * `patch` — the save-as-you-go write (#5036 D1). Anonymous, token-gated,
10
- * registered in the rate-limited `leadsScope`.
11
- *
12
- * It merges a partial application into a draft and ENFORCES NO COMPLETENESS.
13
- * A present field is still checked against its own bounds (length, enum
14
- * membership, and the SITE's own word caps — #5048 D6, which is why the body
15
- * is validated inside the service, where the draft's site is known) because
16
- * this is an anonymous write surface; what
17
- * it deliberately does not assert is that the application is finished. That is
18
- * `submit`'s job alone, and the split is the whole point of the lifecycle.
19
- *
20
- * The credential is the `Authorization: Bearer` resume token minted at
21
- * `start`, and it is bound to ONE listing: a valid token presented against a
22
- * different id resolves to nothing. A wrong token, a spent token, an unknown
23
- * id and an already-submitted row are all the same `404` — the anonymous
24
- * surface must not distinguish "exists but not yours" from "does not exist".
25
- *
26
- * The response is `projectListingDraftSchema`, NOT the tenant dashboard's
27
- * row shape: an anonymous caller addresses its site by an opaque public key
28
- * and must not be handed the real `siteId` back on every save (security
29
- * review on this change, finding 3).
30
- *
31
- * Distinct from the tenant-scoped `PATCH /v1/project-listings/:id`, which
32
- * carries the operator's decision and lives behind the tenant guard. The
33
- * `/drafts/` segment keeps the two apart at the router.
34
- */
35
- import { zProjectListingId } from "@working-theory/ids";
36
- import { projectListingDraftSchema } from "@working-theory/validation";
37
- import type { FastifyPluginAsync } from "fastify";
38
- import { z } from "zod";
39
-
40
- import { patchProjectListingDraft } from "../../services/project-listings.js";
41
-
42
- import { sendValidated } from "./respond.js";
43
- import { resumeTokenFrom } from "./resume-token.js";
44
-
45
- const paramsSchema = z.object({ id: zProjectListingId });
46
-
47
- export const patchProjectListingDraftRoute: FastifyPluginAsync = async (fastify) => {
48
- fastify.patch("/v1/project-listings/drafts/:id", async (request, reply) => {
49
- const parsedParams = paramsSchema.safeParse(request.params);
50
- if (!parsedParams.success) {
51
- return reply.status(400).send({
52
- ok: false,
53
- error: { code: "validation_failed", message: "Invalid project listing id" },
54
- });
55
- }
56
-
57
- const resumeToken = resumeTokenFrom(request);
58
- if (resumeToken === null) {
59
- return reply.status(401).send({
60
- ok: false,
61
- error: { code: "unauthorized", message: "A resume token is required" },
62
- });
63
- }
64
-
65
- // The body is validated INSIDE the service (#5048 D6), not here: the word
66
- // caps are the site's own configuration, and which site this draft belongs
67
- // to is not knowable until the draft is resolved. A caller holding no valid
68
- // token still never reaches body validation — the service answers
69
- // `not_found` first.
70
- const result = await patchProjectListingDraft(
71
- parsedParams.data.id,
72
- resumeToken,
73
- request.body,
74
- );
75
- if (!result.ok) {
76
- if (result.error.kind === "invalid_input") {
77
- return reply.status(400).send({
78
- ok: false,
79
- error: { code: "invalid_input", message: result.error.message },
80
- });
81
- }
82
- return reply.status(404).send({
83
- ok: false,
84
- error: { code: "not_found", message: "Draft not found" },
85
- });
86
- }
87
-
88
- // The omission is explicit at the send site rather than hidden in a
89
- // schema strip: `projectListingDraftSchema` is `.strict()`, so a key that
90
- // stops being dropped here becomes a 500 instead of quietly shipping.
91
- const {
92
- siteId: _siteId,
93
- leadId: _leadId,
94
- decidedBy: _decidedBy,
95
- issueUrl: _issueUrl,
96
- experimentKey: _experimentKey,
97
- ...draft
98
- } = result.value;
99
-
100
- return sendValidated(reply, projectListingDraftSchema, draft, "patch-draft");
101
- });
102
- };
@@ -1,98 +0,0 @@
1
- /**
2
- * NOT marked `oss: false` — see `./me.ts`'s header comment (D-48/#5366
3
- * escalation: `./index.ts` is a mixed barrel multi-tenant's `server.ts`
4
- * reaches by a literal, always-resolved `import()`; this file ships
5
- * unconditionally, registration stays gated).
6
- *
7
- * GET /v1/project-listings/public?siteKey=
8
- *
9
- * The anonymous page-render read (#4998 AC4, reshaped by #5033). Every
10
- * `active` row for the site, ordered: the three newest by `decidedAt` among
11
- * them first, then the rest by `waitlistCount` desc, then `decidedAt` desc.
12
- * A row whose window has elapsed comes back with a COMPUTED `closed` status —
13
- * no job runs, nothing is stored, and a consumer partitions its shelves on
14
- * what it is handed. `featured` surfaces only where the site's
15
- * `SiteListingConfig` enables it. `founderEmail` never leaves this route —
16
- * `projectListingPublicSchema` (`.omit({founderEmail})`) is what the response
17
- * is re-parsed against before send.
18
- *
19
- * GET /v1/project-listings/directory?siteKey=&landingPage=&locale= (#7521 D3)
20
- *
21
- * The generic directory read beside it: the live listicle naming
22
- * `landingPage`, its schema.org entry type and its accepted entries, each
23
- * with a public profile of the listicle's own Application answers — or
24
- * `data: null` when no live listicle names the page, which is almost every
25
- * page (a 404 per page view would be noise). Its own plugin, so `server.ts`
26
- * registers it in its own rate-limited scope rather than `leadsScope`: the
27
- * web server renders it from one egress address (SEC-7521-D1).
28
- */
29
- import {
30
- listingDirectoryQuerySchema,
31
- listingDirectoryResponseSchema,
32
- projectListingPublicListResponseSchema,
33
- publicSiteKeySchema,
34
- } from "@working-theory/validation";
35
- import type { FastifyPluginAsync } from "fastify";
36
- import { z } from "zod";
37
-
38
- import { siteIdForPublicKey } from "../../services/listing-config.js";
39
- import {
40
- listingDirectoryFor,
41
- publicProjectListings,
42
- } from "../../services/project-listings.js";
43
-
44
- import { sendValidated } from "./respond.js";
45
-
46
- const querySchema = z.object({ siteKey: publicSiteKeySchema });
47
-
48
- export const publicProjectListingsRoute: FastifyPluginAsync = async (fastify) => {
49
- fastify.get("/v1/project-listings/public", async (request, reply) => {
50
- const parsedQuery = querySchema.safeParse(request.query);
51
- if (!parsedQuery.success) {
52
- return reply.status(400).send({
53
- ok: false,
54
- error: { code: "validation_failed", message: "Invalid siteKey" },
55
- });
56
- }
57
-
58
- // #5048 D2 — one identifier for the whole mountable contract. The shelves
59
- // and the application form are configured with the same public key, so a
60
- // site never has to hold two different names for itself.
61
- const siteId = await siteIdForPublicKey(parsedQuery.data.siteKey);
62
- if (siteId === null) {
63
- return reply.status(400).send({
64
- ok: false,
65
- error: { code: "unknown_site_key", message: "Unknown site key" },
66
- });
67
- }
68
-
69
- const data = await publicProjectListings(siteId);
70
- return sendValidated(reply, projectListingPublicListResponseSchema, data, "public");
71
- });
72
- };
73
-
74
- export const projectListingDirectoryRoute: FastifyPluginAsync = async (fastify) => {
75
- fastify.get("/v1/project-listings/directory", async (request, reply) => {
76
- const parsedQuery = listingDirectoryQuerySchema.safeParse(request.query);
77
- if (!parsedQuery.success) {
78
- return reply.status(400).send({
79
- ok: false,
80
- error: { code: "validation_failed", message: "Invalid directory query" },
81
- });
82
- }
83
-
84
- // The same one-key resolution `/public` makes: a `SiteListingConfig` row
85
- // under multi-tenant, the env key under single-tenant (#5048 D2).
86
- const { siteKey, landingPage, locale } = parsedQuery.data;
87
- const siteId = await siteIdForPublicKey(siteKey);
88
- if (siteId === null) {
89
- return reply.status(400).send({
90
- ok: false,
91
- error: { code: "unknown_site_key", message: "Unknown site key" },
92
- });
93
- }
94
-
95
- const data = await listingDirectoryFor(siteId, landingPage, locale);
96
- return sendValidated(reply, listingDirectoryResponseSchema, data, "directory");
97
- });
98
- };