create-ailk 0.23.0 → 0.25.1

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 (604) hide show
  1. package/README.md +68 -33
  2. package/component-catalog.json +3 -3
  3. package/component-catalog.md +3 -3
  4. package/dist/add-module.js +10 -3
  5. package/dist/add-route.d.ts +12 -1
  6. package/dist/add-route.js +38 -4
  7. package/dist/cli.js +12 -7
  8. package/dist/derive-architecture.d.ts +3 -4
  9. package/dist/derive-architecture.js +75 -22
  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 +1 -1
  14. package/dist/lib/fetch-template-snapshot.js +7 -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 +34 -12
  19. package/dist/lib/scaffold-render-sweep.d.ts +33 -0
  20. package/dist/lib/scaffold-render-sweep.js +45 -0
  21. package/dist/module-architecture.d.ts +20 -3
  22. package/dist/module-architecture.js +122 -538
  23. package/dist/paid-sections.d.ts +25 -1
  24. package/dist/paid-sections.js +88 -3
  25. package/dist/surfaces.js +10 -2
  26. package/dist/sync-routes.d.ts +30 -4
  27. package/dist/sync-routes.js +271 -18
  28. package/dist/template-record.d.ts +14 -4
  29. package/dist/template-record.js +23 -5
  30. package/dist/topology.d.ts +16 -3
  31. package/dist/topology.js +31 -19
  32. package/dist/update.d.ts +10 -1
  33. package/dist/update.js +540 -16
  34. package/package.json +3 -3
  35. package/release-scope.json +1 -1
  36. package/templates/.claude/cloudparity-repo-hooks.json +5 -0
  37. package/templates/.claude/hooks/template-guard.sh +137 -0
  38. package/templates/.claude/rules/extending.md +106 -0
  39. package/templates/.claude/rules/loop-contract.md +2 -2
  40. package/templates/.claude/settings.json +11 -0
  41. package/templates/.env.example +9 -5
  42. package/templates/.github/workflows/ci.yml +7 -4
  43. package/templates/CLAUDE.md +15 -3
  44. package/templates/CONVENTIONS.md +8 -10
  45. package/templates/README.md +14 -13
  46. package/templates/apps/api/.env.example +9 -2
  47. package/templates/apps/api/CLAUDE.md +122 -82
  48. package/templates/apps/api/jest.config.cjs +15 -5
  49. package/templates/apps/api/package.json +16 -20
  50. package/templates/apps/api/src/__tests__/trusted-origins.server.test.ts +2 -2
  51. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +4 -4
  52. package/templates/apps/api/src/index.ts +1 -1
  53. package/templates/apps/api/src/lib/__mocks__/prisma.ts +7 -4
  54. package/templates/apps/api/src/modules/commerce.ts +10 -0
  55. package/templates/apps/api/src/modules/content-write.ts +10 -0
  56. package/templates/apps/api/src/modules/multi-tenant.ts +10 -0
  57. package/templates/apps/api/src/modules/scheduling.ts +10 -0
  58. package/templates/apps/api/src/modules/signal-collectors.ts +10 -0
  59. package/templates/apps/api/src/modules/workspace.ts +10 -0
  60. package/templates/apps/api/src/server.ts +117 -1857
  61. package/templates/apps/api/vercel.json +2 -2
  62. package/templates/apps/mcp/CLAUDE.md +77 -24
  63. package/templates/apps/mcp/__tests__/app-entry.test.ts +4 -254
  64. package/templates/apps/mcp/__tests__/boot-file.test.ts +92 -0
  65. package/templates/apps/mcp/jest.config.cjs +6 -1
  66. package/templates/apps/mcp/package.json +5 -12
  67. package/templates/apps/mcp/src/app.ts +8 -7
  68. package/templates/apps/mcp/src/index.ts +6 -34
  69. package/templates/apps/mcp/src/lib/paid-tools.generated.ts +10 -0
  70. package/templates/apps/mcp/src/lib/topology.generated.ts +2 -2
  71. package/templates/apps/mcp/src/site.ts +12 -0
  72. package/templates/apps/web/.env.example +1 -1
  73. package/templates/apps/web/CLAUDE.md +21 -5
  74. package/templates/apps/web/app/.well-known/mcp.json/route.ts +9 -3
  75. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +1 -1
  76. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +14 -4
  77. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +1 -1
  78. package/templates/apps/web/app/[locale]/contact/intake-script.ts +1 -1
  79. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/magnet-page.test.tsx +22 -1
  80. package/templates/apps/web/app/[locale]/flows/[slug]/magnet-page.tsx +28 -7
  81. package/templates/apps/web/app/[locale]/pricing/__tests__/unconnected.test.tsx +43 -0
  82. package/templates/apps/web/app/[locale]/pricing/page.tsx +7 -3
  83. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/__tests__/route.test.ts +222 -0
  84. package/templates/apps/web/app/api/offerings/checkout/[lookupKey]/route.ts +10 -3
  85. package/templates/apps/web/app/api/revalidate/route.ts +3 -3
  86. package/templates/apps/web/auth.ts +2 -2
  87. package/templates/apps/web/jest.config.cjs +1 -0
  88. package/templates/apps/web/lib/__tests__/magnet-routes.test.ts +7 -0
  89. package/templates/apps/web/lib/auth-email.ts +1 -1
  90. package/templates/apps/web/lib/magnet-routes.ts +3 -1
  91. package/templates/apps/web/lib/newsletter.actions.ts +9 -6
  92. package/templates/apps/web/lib/relay.ts +2 -2
  93. package/templates/apps/web/lib/release-scope.ts +47 -0
  94. package/templates/apps/web/lib/server-api.ts +1 -1
  95. package/templates/apps/web/src/lib/commerce-claim.ts +1 -1
  96. package/templates/customizations.yaml +37 -0
  97. package/templates/database/prisma.config.ts +10 -14
  98. package/templates/database/seed-test.ts +2 -2
  99. package/templates/database/seed.ts +32 -5
  100. package/templates/package.json +12 -6
  101. package/templates/pnpm-workspace.yaml +0 -1
  102. package/templates/project.yaml +16 -8
  103. package/templates/tools/jest/reporters.cjs +65 -0
  104. package/templates/tools/lint-architecture.ts +757 -7
  105. package/templates/tsconfig.json +1 -2
  106. package/versions.json +48 -35
  107. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +0 -409
  108. package/templates/apps/api/src/__tests__/factories/agent-caller.ts +0 -102
  109. package/templates/apps/api/src/__tests__/factories/content-fixture.ts +0 -138
  110. package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +0 -441
  111. package/templates/apps/api/src/__tests__/factories/index.ts +0 -70
  112. package/templates/apps/api/src/__tests__/factories/lead-fixture.ts +0 -82
  113. package/templates/apps/api/src/__tests__/leads-forwarded-rate-limit.test.ts +0 -135
  114. package/templates/apps/api/src/__tests__/services/enrichment-shim.test.ts +0 -126
  115. package/templates/apps/api/src/__tests__/stripe-boot.test.ts +0 -132
  116. package/templates/apps/api/src/__tests__/support/hub-sign-in-fake-db.ts +0 -362
  117. package/templates/apps/api/src/bin/__tests__/rate-limit-probe.test.ts +0 -184
  118. package/templates/apps/api/src/bin/abandon-sweep.ts +0 -42
  119. package/templates/apps/api/src/bin/rate-limit-probe.ts +0 -53
  120. package/templates/apps/api/src/bin/spokes.ts +0 -198
  121. package/templates/apps/api/src/bin/stripe-connections-rekey.ts +0 -45
  122. package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +0 -196
  123. package/templates/apps/api/src/config/__tests__/index.test.ts +0 -233
  124. package/templates/apps/api/src/config/__tests__/modules.test.ts +0 -76
  125. package/templates/apps/api/src/config/__tests__/stripe-connection-key.test.ts +0 -185
  126. package/templates/apps/api/src/config/__tests__/trusted-origins.test.ts +0 -238
  127. package/templates/apps/api/src/config/index.ts +0 -596
  128. package/templates/apps/api/src/config/modules.ts +0 -83
  129. package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +0 -581
  130. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +0 -143
  131. package/templates/apps/api/src/lib/__tests__/consent-ledger-double.ts +0 -237
  132. package/templates/apps/api/src/lib/__tests__/consent-store.test.ts +0 -353
  133. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +0 -201
  134. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +0 -224
  135. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +0 -120
  136. package/templates/apps/api/src/lib/__tests__/errors.test.ts +0 -94
  137. package/templates/apps/api/src/lib/__tests__/file-fallback.test.ts +0 -144
  138. package/templates/apps/api/src/lib/__tests__/fixtures/HomePage/home.mdx +0 -8
  139. package/templates/apps/api/src/lib/__tests__/flow-completion-store.test.ts +0 -149
  140. package/templates/apps/api/src/lib/__tests__/flow-rename-race.db.test.ts +0 -116
  141. package/templates/apps/api/src/lib/__tests__/hub-sign-in-store.test.ts +0 -209
  142. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +0 -257
  143. package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +0 -96
  144. package/templates/apps/api/src/lib/__tests__/outbound-http.test.ts +0 -125
  145. package/templates/apps/api/src/lib/__tests__/prisma.test.ts +0 -165
  146. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +0 -317
  147. package/templates/apps/api/src/lib/__tests__/require-paid-tier.test.ts +0 -129
  148. package/templates/apps/api/src/lib/__tests__/site.test.ts +0 -292
  149. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +0 -89
  150. package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +0 -202
  151. package/templates/apps/api/src/lib/__tests__/stripe-admission.test.ts +0 -217
  152. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -82
  153. package/templates/apps/api/src/lib/agent-store.ts +0 -613
  154. package/templates/apps/api/src/lib/auth-email-budget.ts +0 -161
  155. package/templates/apps/api/src/lib/consent-store.ts +0 -363
  156. package/templates/apps/api/src/lib/content-adapter.ts +0 -112
  157. package/templates/apps/api/src/lib/deliverable-asset-store.ts +0 -96
  158. package/templates/apps/api/src/lib/error-serializer.ts +0 -164
  159. package/templates/apps/api/src/lib/errors.ts +0 -95
  160. package/templates/apps/api/src/lib/file-fallback.ts +0 -87
  161. package/templates/apps/api/src/lib/flow-completion-store.ts +0 -136
  162. package/templates/apps/api/src/lib/hub-sign-in-store.ts +0 -455
  163. package/templates/apps/api/src/lib/lead-capture-store.ts +0 -122
  164. package/templates/apps/api/src/lib/lead-delivery.ts +0 -52
  165. package/templates/apps/api/src/lib/license-key-store.ts +0 -45
  166. package/templates/apps/api/src/lib/locales.ts +0 -201
  167. package/templates/apps/api/src/lib/outbound-http.ts +0 -127
  168. package/templates/apps/api/src/lib/prisma.ts +0 -21
  169. package/templates/apps/api/src/lib/proposal-paid-store.ts +0 -66
  170. package/templates/apps/api/src/lib/rate-limit-probe.ts +0 -165
  171. package/templates/apps/api/src/lib/rate-limit-store.ts +0 -142
  172. package/templates/apps/api/src/lib/require-commerce-claim.ts +0 -250
  173. package/templates/apps/api/src/lib/require-paid-tier.ts +0 -53
  174. package/templates/apps/api/src/lib/site.ts +0 -254
  175. package/templates/apps/api/src/lib/slice-load.ts +0 -90
  176. package/templates/apps/api/src/lib/spoke-store.ts +0 -180
  177. package/templates/apps/api/src/lib/stripe.ts +0 -318
  178. package/templates/apps/api/src/lib/webhook-idempotency.ts +0 -156
  179. package/templates/apps/api/src/middleware/__tests__/adversarial/bearer-token-format.test.ts +0 -111
  180. package/templates/apps/api/src/middleware/__tests__/adversarial/cookie-or-bearer-precedence.test.ts +0 -172
  181. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-bearer-skip.test.ts +0 -119
  182. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-cookie-path.test.ts +0 -138
  183. package/templates/apps/api/src/middleware/__tests__/adversarial/csrf-trusted-origins.test.ts +0 -142
  184. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +0 -155
  185. package/templates/apps/api/src/middleware/__tests__/adversarial/session-timeout.test.ts +0 -139
  186. package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +0 -562
  187. package/templates/apps/api/src/middleware/__tests__/auth.both-present-precedence.test.ts +0 -76
  188. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +0 -220
  189. package/templates/apps/api/src/middleware/__tests__/auth.database-not-configured.test.ts +0 -97
  190. package/templates/apps/api/src/middleware/__tests__/auth.hub-session-path.test.ts +0 -557
  191. package/templates/apps/api/src/middleware/__tests__/auth.neither-present.test.ts +0 -72
  192. package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +0 -140
  193. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +0 -721
  194. package/templates/apps/api/src/middleware/__tests__/rate-limit-cross-isolate.db.test.ts +0 -220
  195. package/templates/apps/api/src/middleware/__tests__/rate-limit-store.test.ts +0 -430
  196. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +0 -1409
  197. package/templates/apps/api/src/middleware/__tests__/write-budget.test.ts +0 -174
  198. package/templates/apps/api/src/middleware/auth.ts +0 -864
  199. package/templates/apps/api/src/middleware/default-tenant.ts +0 -166
  200. package/templates/apps/api/src/middleware/rate-limit.ts +0 -983
  201. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +0 -264
  202. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +0 -182
  203. package/templates/apps/api/src/openapi/index.ts +0 -18
  204. package/templates/apps/api/src/openapi/spec.ts +0 -1815
  205. package/templates/apps/api/src/openapi/surface-drift.ts +0 -195
  206. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +0 -175
  207. package/templates/apps/api/src/routes/aeo/index.ts +0 -107
  208. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +0 -196
  209. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +0 -249
  210. package/templates/apps/api/src/routes/auth/__tests__/hub-sign-in.test.ts +0 -532
  211. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +0 -313
  212. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +0 -413
  213. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +0 -609
  214. package/templates/apps/api/src/routes/auth/hub-sign-in.ts +0 -242
  215. package/templates/apps/api/src/routes/auth/tokens.ts +0 -211
  216. package/templates/apps/api/src/routes/auth/transactional-email.ts +0 -301
  217. package/templates/apps/api/src/routes/auth/user.ts +0 -305
  218. package/templates/apps/api/src/routes/billing/plan-checkout.ts +0 -325
  219. package/templates/apps/api/src/routes/checkout/__tests__/live-key.test.ts +0 -178
  220. package/templates/apps/api/src/routes/checkout/__tests__/offerings-authorization.test.ts +0 -1300
  221. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +0 -448
  222. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -1742
  223. package/templates/apps/api/src/routes/checkout/index.ts +0 -34
  224. package/templates/apps/api/src/routes/checkout/session.ts +0 -171
  225. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -540
  226. package/templates/apps/api/src/routes/consent/__tests__/account.test.ts +0 -386
  227. package/templates/apps/api/src/routes/consent/__tests__/sign-up-rate-limit.test.ts +0 -160
  228. package/templates/apps/api/src/routes/consent/account.ts +0 -268
  229. package/templates/apps/api/src/routes/consent/cookie.ts +0 -90
  230. package/templates/apps/api/src/routes/consent/index.ts +0 -26
  231. package/templates/apps/api/src/routes/consent/unsubscribe.ts +0 -127
  232. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +0 -853
  233. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +0 -230
  234. package/templates/apps/api/src/routes/content/__tests__/list-types.test.ts +0 -44
  235. package/templates/apps/api/src/routes/content/__tests__/locale.test.ts +0 -118
  236. package/templates/apps/api/src/routes/content/__tests__/offerings.test.ts +0 -731
  237. package/templates/apps/api/src/routes/content/__tests__/pricing.test.ts +0 -753
  238. package/templates/apps/api/src/routes/content/__tests__/rate-limit.test.ts +0 -51
  239. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +0 -450
  240. package/templates/apps/api/src/routes/content/__tests__/shown-offerings.test.ts +0 -762
  241. package/templates/apps/api/src/routes/content/get-page.ts +0 -268
  242. package/templates/apps/api/src/routes/content/index.ts +0 -49
  243. package/templates/apps/api/src/routes/content/list-pages.ts +0 -95
  244. package/templates/apps/api/src/routes/content/list-types.ts +0 -19
  245. package/templates/apps/api/src/routes/content/offerings.ts +0 -184
  246. package/templates/apps/api/src/routes/content/pricing.ts +0 -176
  247. package/templates/apps/api/src/routes/content/rate-limit.ts +0 -22
  248. package/templates/apps/api/src/routes/content/search.ts +0 -129
  249. package/templates/apps/api/src/routes/content/shown-offerings.ts +0 -229
  250. package/templates/apps/api/src/routes/content/slug.ts +0 -9
  251. package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +0 -88
  252. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +0 -171
  253. package/templates/apps/api/src/routes/leads/__tests__/magnet-completion.test.ts +0 -515
  254. package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +0 -392
  255. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +0 -165
  256. package/templates/apps/api/src/routes/leads/index.ts +0 -573
  257. package/templates/apps/api/src/routes/leads/list.ts +0 -104
  258. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +0 -626
  259. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +0 -1268
  260. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +0 -847
  261. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +0 -480
  262. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +0 -465
  263. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +0 -872
  264. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +0 -1022
  265. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +0 -484
  266. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +0 -535
  267. package/templates/apps/api/src/routes/project-listings/index.ts +0 -164
  268. package/templates/apps/api/src/routes/project-listings/me.ts +0 -87
  269. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +0 -224
  270. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +0 -102
  271. package/templates/apps/api/src/routes/project-listings/public.ts +0 -98
  272. package/templates/apps/api/src/routes/project-listings/respond.ts +0 -48
  273. package/templates/apps/api/src/routes/project-listings/resume-token.ts +0 -27
  274. package/templates/apps/api/src/routes/project-listings/start.ts +0 -140
  275. package/templates/apps/api/src/routes/project-listings/submit.ts +0 -130
  276. package/templates/apps/api/src/routes/project-listings/verify-email.ts +0 -150
  277. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +0 -686
  278. package/templates/apps/api/src/routes/requests/confirm.ts +0 -601
  279. package/templates/apps/api/src/routes/requests/index.ts +0 -54
  280. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +0 -306
  281. package/templates/apps/api/src/routes/test/auth/session.ts +0 -143
  282. package/templates/apps/api/src/routes/webhooks/README.md +0 -398
  283. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-checkout-consent.test.ts +0 -649
  284. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +0 -749
  285. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -448
  286. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -264
  287. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode-live.test.ts +0 -159
  288. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-livemode.test.ts +0 -158
  289. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-offerings.test.ts +0 -814
  290. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -301
  291. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-proposal-paid.test.ts +0 -369
  292. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -215
  293. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +0 -301
  294. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +0 -808
  295. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -1155
  296. package/templates/apps/api/src/routes/webhooks/index.ts +0 -141
  297. package/templates/apps/api/src/routes/webhooks/stripe-offerings.ts +0 -384
  298. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -2026
  299. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +0 -528
  300. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +0 -132
  301. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +0 -1028
  302. package/templates/apps/api/src/services/__tests__/checkout-plan.test.ts +0 -313
  303. package/templates/apps/api/src/services/__tests__/consent-ledger.test.ts +0 -503
  304. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +0 -65
  305. package/templates/apps/api/src/services/__tests__/consent.test.ts +0 -299
  306. package/templates/apps/api/src/services/__tests__/default-tenant.test.ts +0 -147
  307. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +0 -867
  308. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +0 -379
  309. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +0 -368
  310. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +0 -142
  311. package/templates/apps/api/src/services/__tests__/hub-sign-in.test.ts +0 -854
  312. package/templates/apps/api/src/services/__tests__/keyless-workspace-predicate.test.ts +0 -234
  313. package/templates/apps/api/src/services/__tests__/lead-accept-user.test.ts +0 -251
  314. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +0 -2660
  315. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +0 -166
  316. package/templates/apps/api/src/services/__tests__/magnet-completion.test.ts +0 -389
  317. package/templates/apps/api/src/services/__tests__/person.test.ts +0 -1176
  318. package/templates/apps/api/src/services/__tests__/plan-offerings.test.ts +0 -444
  319. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +0 -154
  320. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +0 -137
  321. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +0 -531
  322. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +0 -372
  323. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +0 -348
  324. package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +0 -660
  325. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +0 -941
  326. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +0 -588
  327. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +0 -78
  328. package/templates/apps/api/src/services/abandon-email.ts +0 -313
  329. package/templates/apps/api/src/services/aeo-score.ts +0 -325
  330. package/templates/apps/api/src/services/answer-distributions.ts +0 -138
  331. package/templates/apps/api/src/services/audit-log.ts +0 -961
  332. package/templates/apps/api/src/services/checkout-buyer.ts +0 -168
  333. package/templates/apps/api/src/services/consent.ts +0 -834
  334. package/templates/apps/api/src/services/default-tenant.ts +0 -416
  335. package/templates/apps/api/src/services/deliverable-fulfillment.ts +0 -755
  336. package/templates/apps/api/src/services/dunning-policy.ts +0 -294
  337. package/templates/apps/api/src/services/enrichment-shim.ts +0 -53
  338. package/templates/apps/api/src/services/entitlements.ts +0 -338
  339. package/templates/apps/api/src/services/hub-sign-in.ts +0 -472
  340. package/templates/apps/api/src/services/lead-accept-user.ts +0 -126
  341. package/templates/apps/api/src/services/lead-promotion.ts +0 -160
  342. package/templates/apps/api/src/services/lead-routing.ts +0 -1372
  343. package/templates/apps/api/src/services/leads-inbox.ts +0 -312
  344. package/templates/apps/api/src/services/listing-config.ts +0 -178
  345. package/templates/apps/api/src/services/magnet-completion.ts +0 -242
  346. package/templates/apps/api/src/services/offerings/__tests__/catalog.test.ts +0 -295
  347. package/templates/apps/api/src/services/offerings/__tests__/checkout-session-record.test.ts +0 -268
  348. package/templates/apps/api/src/services/offerings/__tests__/connection.test.ts +0 -868
  349. package/templates/apps/api/src/services/offerings/__tests__/fixtures.ts +0 -416
  350. package/templates/apps/api/src/services/offerings/__tests__/org-products.test.ts +0 -396
  351. package/templates/apps/api/src/services/offerings/__tests__/readers.test.ts +0 -681
  352. package/templates/apps/api/src/services/offerings/__tests__/source.test.ts +0 -231
  353. package/templates/apps/api/src/services/offerings/catalog.ts +0 -308
  354. package/templates/apps/api/src/services/offerings/checkout-authorization.ts +0 -385
  355. package/templates/apps/api/src/services/offerings/checkout-session-record.ts +0 -192
  356. package/templates/apps/api/src/services/offerings/connection.ts +0 -1003
  357. package/templates/apps/api/src/services/offerings/index.ts +0 -614
  358. package/templates/apps/api/src/services/offerings/source.ts +0 -140
  359. package/templates/apps/api/src/services/offerings/store.ts +0 -150
  360. package/templates/apps/api/src/services/owner-workspaces.ts +0 -132
  361. package/templates/apps/api/src/services/person.ts +0 -1188
  362. package/templates/apps/api/src/services/plan-offerings.ts +0 -125
  363. package/templates/apps/api/src/services/project-listing-resume-email.ts +0 -326
  364. package/templates/apps/api/src/services/project-listings.ts +0 -2073
  365. package/templates/apps/api/src/services/public-site-resolver.ts +0 -396
  366. package/templates/apps/api/src/services/request-confirm.ts +0 -698
  367. package/templates/apps/api/src/services/revalidation.ts +0 -235
  368. package/templates/apps/api/src/services/slack-notify.ts +0 -291
  369. package/templates/apps/api/src/services/usage-aggregation.ts +0 -516
  370. package/templates/apps/api/src/services/usage-credits.ts +0 -340
  371. package/templates/apps/api/src/services/verified-email.ts +0 -71
  372. package/templates/apps/mcp/__tests__/abuse/daily-cap.test.ts +0 -52
  373. package/templates/apps/mcp/__tests__/abuse/email-validator.test.ts +0 -46
  374. package/templates/apps/mcp/__tests__/abuse/guard.test.ts +0 -198
  375. package/templates/apps/mcp/__tests__/abuse/honeypot.test.ts +0 -36
  376. package/templates/apps/mcp/__tests__/abuse/user-agent-deny.test.ts +0 -54
  377. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +0 -116
  378. package/templates/apps/mcp/__tests__/audit-single-page.test.ts +0 -299
  379. package/templates/apps/mcp/__tests__/boot.test.ts +0 -280
  380. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +0 -359
  381. package/templates/apps/mcp/__tests__/config/modules.test.ts +0 -78
  382. package/templates/apps/mcp/__tests__/get_business_hours.test.ts +0 -82
  383. package/templates/apps/mcp/__tests__/get_faq.test.ts +0 -80
  384. package/templates/apps/mcp/__tests__/get_locations.test.ts +0 -65
  385. package/templates/apps/mcp/__tests__/get_offerings.test.ts +0 -557
  386. package/templates/apps/mcp/__tests__/get_pricing.test.ts +0 -524
  387. package/templates/apps/mcp/__tests__/get_site_info.test.ts +0 -81
  388. package/templates/apps/mcp/__tests__/http-transport.test.ts +0 -1151
  389. package/templates/apps/mcp/__tests__/index.test.ts +0 -195
  390. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +0 -191
  391. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +0 -531
  392. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +0 -65
  393. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +0 -187
  394. package/templates/apps/mcp/__tests__/lib/content-root.test.ts +0 -93
  395. package/templates/apps/mcp/__tests__/lib/errors.test.ts +0 -75
  396. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +0 -165
  397. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +0 -124
  398. package/templates/apps/mcp/__tests__/list_pages.test.ts +0 -228
  399. package/templates/apps/mcp/__tests__/locale.test.ts +0 -135
  400. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +0 -175
  401. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +0 -101
  402. package/templates/apps/mcp/__tests__/read_content.test.ts +0 -474
  403. package/templates/apps/mcp/__tests__/request_callback.test.ts +0 -225
  404. package/templates/apps/mcp/__tests__/search_content.test.ts +0 -256
  405. package/templates/apps/mcp/__tests__/server.test.ts +0 -388
  406. package/templates/apps/mcp/__tests__/start_purchase.test.ts +0 -448
  407. package/templates/apps/mcp/__tests__/submit_lead.test.ts +0 -307
  408. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +0 -223
  409. package/templates/apps/mcp/__tests__/tools/create_project.test.ts +0 -243
  410. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +0 -451
  411. package/templates/apps/mcp/src/abuse/daily-cap.ts +0 -47
  412. package/templates/apps/mcp/src/abuse/email-validator.ts +0 -25
  413. package/templates/apps/mcp/src/abuse/honeypot.ts +0 -21
  414. package/templates/apps/mcp/src/abuse/index.ts +0 -342
  415. package/templates/apps/mcp/src/abuse/user-agent-deny.ts +0 -58
  416. package/templates/apps/mcp/src/boot.ts +0 -167
  417. package/templates/apps/mcp/src/config/modules.ts +0 -43
  418. package/templates/apps/mcp/src/http-transport.ts +0 -526
  419. package/templates/apps/mcp/src/lib/audit-single-page.ts +0 -308
  420. package/templates/apps/mcp/src/lib/commerce-claim.ts +0 -263
  421. package/templates/apps/mcp/src/lib/content-adapter.ts +0 -142
  422. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +0 -139
  423. package/templates/apps/mcp/src/lib/errors.ts +0 -61
  424. package/templates/apps/mcp/src/lib/license-key-store.ts +0 -45
  425. package/templates/apps/mcp/src/lib/locales.ts +0 -171
  426. package/templates/apps/mcp/src/lib/prisma.ts +0 -39
  427. package/templates/apps/mcp/src/lib/public-workspace.ts +0 -133
  428. package/templates/apps/mcp/src/lib/resolve-locale.ts +0 -30
  429. package/templates/apps/mcp/src/lib/shown-offerings.ts +0 -105
  430. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +0 -59
  431. package/templates/apps/mcp/src/request-context.ts +0 -52
  432. package/templates/apps/mcp/src/server.ts +0 -343
  433. package/templates/apps/mcp/src/serverless-app.ts +0 -88
  434. package/templates/apps/mcp/src/tools/audit.ts +0 -127
  435. package/templates/apps/mcp/src/tools/capture_lead.ts +0 -64
  436. package/templates/apps/mcp/src/tools/create_project.ts +0 -134
  437. package/templates/apps/mcp/src/tools/get_business_hours.ts +0 -68
  438. package/templates/apps/mcp/src/tools/get_faq.ts +0 -71
  439. package/templates/apps/mcp/src/tools/get_locations.ts +0 -88
  440. package/templates/apps/mcp/src/tools/get_offerings.ts +0 -96
  441. package/templates/apps/mcp/src/tools/get_pricing.ts +0 -94
  442. package/templates/apps/mcp/src/tools/get_site_info.ts +0 -86
  443. package/templates/apps/mcp/src/tools/index.ts +0 -137
  444. package/templates/apps/mcp/src/tools/list_pages.ts +0 -74
  445. package/templates/apps/mcp/src/tools/person_notes.ts +0 -25
  446. package/templates/apps/mcp/src/tools/proposals.ts +0 -27
  447. package/templates/apps/mcp/src/tools/purchase.ts +0 -205
  448. package/templates/apps/mcp/src/tools/read_content.ts +0 -75
  449. package/templates/apps/mcp/src/tools/request_callback.ts +0 -59
  450. package/templates/apps/mcp/src/tools/search_content.ts +0 -67
  451. package/templates/apps/mcp/src/tools/subscribe_newsletter.ts +0 -57
  452. package/templates/apps/mcp/src/workspace-server.ts +0 -224
  453. package/templates/database/CHANGELOG.md +0 -961
  454. package/templates/database/LICENSE +0 -202
  455. package/templates/database/NOTICE +0 -2
  456. package/templates/database/README.md +0 -243
  457. package/templates/database/__tests__/agents.db.test.ts +0 -320
  458. package/templates/database/__tests__/appointment.db.test.ts +0 -106
  459. package/templates/database/__tests__/artifact-content-store.db.test.ts +0 -316
  460. package/templates/database/__tests__/composite-fence.db.test.ts +0 -206
  461. package/templates/database/__tests__/consent-ledger.db.test.ts +0 -345
  462. package/templates/database/__tests__/consent-source-checkout-migration.test.ts +0 -79
  463. package/templates/database/__tests__/fence-fan-out.db.test.ts +0 -715
  464. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +0 -79
  465. package/templates/database/__tests__/fixtures/person-backfill.json +0 -264
  466. package/templates/database/__tests__/fixtures/person-contact-backfill.json +0 -182
  467. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +0 -100
  468. package/templates/database/__tests__/flow-completion-migration.test.ts +0 -115
  469. package/templates/database/__tests__/flow-completion.db.test.ts +0 -136
  470. package/templates/database/__tests__/flow-workspace-key.db.test.ts +0 -263
  471. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +0 -392
  472. package/templates/database/__tests__/flows-presets.test.ts +0 -173
  473. package/templates/database/__tests__/in-page-magnets-migration.test.ts +0 -161
  474. package/templates/database/__tests__/lead-idempotency-key.db.test.ts +0 -92
  475. package/templates/database/__tests__/member-area.db.test.ts +0 -257
  476. package/templates/database/__tests__/member-contract-migration.test.ts +0 -85
  477. package/templates/database/__tests__/member-contract.db.test.ts +0 -413
  478. package/templates/database/__tests__/offerings-checkout-session.db.test.ts +0 -270
  479. package/templates/database/__tests__/offerings-migration.test.ts +0 -197
  480. package/templates/database/__tests__/offerings-shown-price-ids-migration.test.ts +0 -86
  481. package/templates/database/__tests__/offerings.db.test.ts +0 -303
  482. package/templates/database/__tests__/outbound-message-action.db.test.ts +0 -80
  483. package/templates/database/__tests__/person-contact.db.test.ts +0 -563
  484. package/templates/database/__tests__/person-notes.db.test.ts +0 -249
  485. package/templates/database/__tests__/person.db.test.ts +0 -586
  486. package/templates/database/__tests__/piece-kind-migration.test.ts +0 -67
  487. package/templates/database/__tests__/piece-kind.db.test.ts +0 -208
  488. package/templates/database/__tests__/prefixed-ids.db.test.ts +0 -222
  489. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +0 -111
  490. package/templates/database/__tests__/seed.test.ts +0 -240
  491. package/templates/database/__tests__/spoke-registry.db.test.ts +0 -191
  492. package/templates/database/__tests__/tenancy.test.ts +0 -141
  493. package/templates/database/__tests__/user-cascade.test.ts +0 -140
  494. package/templates/database/_gitignore +0 -8
  495. package/templates/database/auth/.gitkeep +0 -0
  496. package/templates/database/auth/schema.prisma +0 -641
  497. package/templates/database/config/.gitkeep +0 -0
  498. package/templates/database/config/schema.prisma +0 -441
  499. package/templates/database/content/.gitkeep +0 -0
  500. package/templates/database/content/schema.prisma +0 -339
  501. package/templates/database/eslint.config.mjs +0 -69
  502. package/templates/database/flows/listing-diagnostic.ts +0 -182
  503. package/templates/database/flows/presets.ts +0 -315
  504. package/templates/database/inbox/.gitkeep +0 -0
  505. package/templates/database/inbox/README.md +0 -3
  506. package/templates/database/inbox/__tests__/schema.test.ts +0 -44
  507. package/templates/database/inbox/schema.prisma +0 -1447
  508. package/templates/database/jest.config.cjs +0 -36
  509. package/templates/database/migrations/20260508000000_add_inbox_namespace/migration.sql +0 -328
  510. package/templates/database/migrations/20260513000000_better_auth_migration/migration.sql +0 -100
  511. package/templates/database/migrations/20260515000000_add_user_billing_fields/migration.sql +0 -28
  512. package/templates/database/migrations/20260516000000_add_user_tier_care_plan_seat/migration.sql +0 -25
  513. package/templates/database/migrations/20260518000000_add_telemetry_records/migration.sql +0 -42
  514. package/templates/database/migrations/20260525000000_theme_wiring/migration.sql +0 -67
  515. package/templates/database/migrations/20260710000000_retire_commercial_tier/migration.sql +0 -21
  516. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +0 -201
  517. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +0 -67
  518. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +0 -46
  519. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +0 -25
  520. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +0 -49
  521. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +0 -197
  522. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +0 -32
  523. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +0 -22
  524. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +0 -151
  525. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +0 -223
  526. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +0 -22
  527. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +0 -32
  528. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +0 -40
  529. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +0 -4
  530. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +0 -2
  531. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +0 -2
  532. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +0 -29
  533. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +0 -6
  534. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +0 -39
  535. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +0 -24
  536. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +0 -3
  537. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +0 -33
  538. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +0 -30
  539. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +0 -47
  540. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +0 -16
  541. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +0 -47
  542. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +0 -10
  543. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +0 -26
  544. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +0 -90
  545. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +0 -174
  546. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +0 -14
  547. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +0 -25
  548. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +0 -32
  549. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +0 -71
  550. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +0 -30
  551. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +0 -49
  552. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +0 -123
  553. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +0 -15
  554. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +0 -158
  555. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +0 -40
  556. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +0 -67
  557. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +0 -70
  558. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +0 -181
  559. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +0 -172
  560. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +0 -41
  561. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +0 -185
  562. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +0 -70
  563. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +0 -57
  564. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +0 -68
  565. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +0 -1067
  566. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +0 -210
  567. package/templates/database/migrations/20261003120000_spoke_registry/migration.sql +0 -80
  568. package/templates/database/migrations/20261003130000_scheduling_connection/migration.sql +0 -49
  569. package/templates/database/migrations/20261005120000_agents/migration.sql +0 -84
  570. package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +0 -35
  571. package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +0 -20
  572. package/templates/database/migrations/20261009120000_agent_grants/migration.sql +0 -33
  573. package/templates/database/migrations/20261010120000_person/migration.sql +0 -106
  574. package/templates/database/migrations/20261010121000_person_backfill/migration.sql +0 -132
  575. package/templates/database/migrations/20261010130000_agent_grants_people_read/migration.sql +0 -20
  576. package/templates/database/migrations/20261011120000_member_area/migration.sql +0 -158
  577. package/templates/database/migrations/20261011130000_agent_grants_member_pieces_manage/migration.sql +0 -21
  578. package/templates/database/migrations/20261012120000_member_contract/migration.sql +0 -192
  579. package/templates/database/migrations/20261013120000_lead_spoke_checkout/migration.sql +0 -20
  580. package/templates/database/migrations/20261014120000_consent_ledger/migration.sql +0 -122
  581. package/templates/database/migrations/20261014120000_person_contact/migration.sql +0 -231
  582. package/templates/database/migrations/20261015120000_person_notes/migration.sql +0 -77
  583. package/templates/database/migrations/20261015120000_rate_limit_counters/migration.sql +0 -43
  584. package/templates/database/migrations/20261016120000_piece_kind/migration.sql +0 -95
  585. package/templates/database/migrations/20261017120000_outbound_message_action/migration.sql +0 -30
  586. package/templates/database/migrations/20261018120000_flow_completion/migration.sql +0 -52
  587. package/templates/database/migrations/20261019120000_consent_source_checkout/migration.sql +0 -27
  588. package/templates/database/migrations/20261020120000_offerings_data_model/migration.sql +0 -146
  589. package/templates/database/migrations/20261021120000_offerings_checkout_session/migration.sql +0 -77
  590. package/templates/database/migrations/20261022120000_in_page_magnets/migration.sql +0 -45
  591. package/templates/database/migrations/20261023120000_offerings_shown_price_ids/migration.sql +0 -47
  592. package/templates/database/migrations/20261024120000_organization_plan_checkout_session/migration.sql +0 -16
  593. package/templates/database/migrations/migration_lock.toml +0 -3
  594. package/templates/database/ops/.gitkeep +0 -0
  595. package/templates/database/ops/schema.prisma +0 -43
  596. package/templates/database/package.json +0 -89
  597. package/templates/database/schema.prisma +0 -71
  598. package/templates/database/scripts/db-generate-locked.sh +0 -80
  599. package/templates/database/scripts/person-backfill.sql +0 -132
  600. package/templates/database/tenancy.ts +0 -183
  601. package/templates/database/tsconfig.build.json +0 -16
  602. package/templates/database/tsconfig.flows.json +0 -15
  603. package/templates/database/tsconfig.json +0 -16
  604. 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
- };