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,198 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; mirrors deliverable-resend.ts's
3
- coverage-ignore rationale — the register/revoke/list logic is unit-tested at
4
- the boundary it calls (lib/__tests__/spoke-store.test.ts). This file is a
5
- thin entry: parse args, dispatch, print, set exit code. */
6
- /**
7
- * The hub's spoke registry CLI (#7532, spec
8
- * docs/specs/7532-a-spoke-s-public-reads-spec.md D7).
9
- *
10
- * Dev: `pnpm --filter @working-theory/api spokes <command> [flags]`
11
- * Production: `node dist/bin/spokes.js <command> [flags]`
12
- *
13
- * register --workspace <wksp_id> --name <label>
14
- * Bind a new spoke to one existing workspace and print its key ONCE.
15
- * The hub stores only the key's digest; the key cannot be shown again.
16
- * Do not run this where stdout is captured (CI logs, a shared terminal
17
- * recording) — set the printed key as the spoke's `AILK_SPOKE_KEY`.
18
- * revoke --id <spoke_id>
19
- * Stop that one spoke's key on its next request. Other spokes of the
20
- * same workspace keep working.
21
- * list
22
- * Every spoke: id, name, workspace, key prefix, created, revoked. Never
23
- * the digest.
24
- * sign-in-client --id <spoke_id> --redirect-uri <url>
25
- * Register (or rotate) the spoke's sign-in client (#7894 D3): the hub
26
- * signs that spoke's visitors in, and the callback is the ONE registered
27
- * URL — `https:` (or `http:` on a loopback host), path exactly
28
- * `/api/hub-session/callback`, no query, fragment or userinfo. Prints the
29
- * client id and secret ONCE; re-running rotates the secret. Refuses an
30
- * unknown or revoked spoke.
31
- * sign-in-client --id <spoke_id> --disable
32
- * Switch that spoke's sign-in client off: its codes and credentials stop
33
- * at once.
34
- *
35
- * A key rotates by registering a second spoke for the workspace, deploying
36
- * its key, then revoking the first. There is no HTTP route for any of this.
37
- */
38
- import {
39
- disableSignInClient,
40
- registerSignInClient,
41
- } from "../lib/hub-sign-in-store.js";
42
- import { prisma } from "../lib/prisma.js";
43
- import {
44
- listSpokes,
45
- registerSpoke,
46
- revokeSpoke,
47
- } from "../lib/spoke-store.js";
48
-
49
- const USAGE = [
50
- "usage: spokes register --workspace <wksp_id> --name <label>",
51
- " spokes revoke --id <spoke_id>",
52
- " spokes list",
53
- " spokes sign-in-client --id <spoke_id> --redirect-uri <url> | --disable",
54
- ].join("\n");
55
-
56
- type Flags = {
57
- workspace?: string | undefined;
58
- name?: string | undefined;
59
- id?: string | undefined;
60
- redirectUri?: string | undefined;
61
- disable?: boolean | undefined;
62
- };
63
-
64
- /** Minimal `--flag value` parser — no external dependency for a 3-flag CLI. */
65
- function parseFlags(argv: string[]): Flags {
66
- const out: Flags = {};
67
- for (let i = 0; i < argv.length; i += 1) {
68
- const flag = argv[i];
69
- const value = argv[i + 1];
70
- if (flag === "--workspace") out.workspace = value;
71
- else if (flag === "--name") out.name = value;
72
- else if (flag === "--id") out.id = value;
73
- else if (flag === "--redirect-uri") out.redirectUri = value;
74
- else if (flag === "--disable") {
75
- out.disable = true;
76
- continue; // a bare flag: it takes no value
77
- } else continue;
78
- i += 1;
79
- }
80
- return out;
81
- }
82
-
83
- async function runRegister(flags: Flags): Promise<void> {
84
- const name = flags.name?.trim();
85
- if (!flags.workspace || !name || name.length > 120) {
86
- console.error(`[spokes] ${USAGE}\n(--name is 1–120 characters)`);
87
- process.exitCode = 1;
88
- return;
89
- }
90
- const result = await registerSpoke({ workspaceId: flags.workspace, name });
91
- if (!result.ok) {
92
- console.error(`[spokes] ${result.reason}: no workspace "${flags.workspace}"`);
93
- process.exitCode = 1;
94
- return;
95
- }
96
- console.log(
97
- `[spokes] registered spoke="${result.spoke.id}" workspace="${result.spoke.workspaceId}" name="${result.spoke.name}"`,
98
- );
99
- console.log(`AILK_SPOKE_KEY=${result.key}`);
100
- console.log(
101
- "[spokes] This key will not be shown again. Set it as the spoke's AILK_SPOKE_KEY now.",
102
- );
103
- }
104
-
105
- async function runRevoke(flags: Flags): Promise<void> {
106
- if (!flags.id) {
107
- console.error(`[spokes] ${USAGE}`);
108
- process.exitCode = 1;
109
- return;
110
- }
111
- const revoked = await revokeSpoke(flags.id);
112
- if (!revoked) {
113
- console.error(`[spokes] no unrevoked spoke "${flags.id}"`);
114
- process.exitCode = 1;
115
- return;
116
- }
117
- console.log(`[spokes] revoked spoke="${flags.id}"`);
118
- }
119
-
120
- async function runSignInClient(flags: Flags): Promise<void> {
121
- if (!flags.id || (!flags.disable && !flags.redirectUri)) {
122
- console.error(`[spokes] ${USAGE}`);
123
- process.exitCode = 1;
124
- return;
125
- }
126
- if (flags.disable) {
127
- const disabled = await disableSignInClient(flags.id);
128
- if (!disabled) {
129
- console.error(`[spokes] no sign-in client for spoke "${flags.id}"`);
130
- process.exitCode = 1;
131
- return;
132
- }
133
- console.log(`[spokes] disabled the sign-in client of spoke="${flags.id}"`);
134
- return;
135
- }
136
- const result = await registerSignInClient({
137
- spokeId: flags.id,
138
- redirectUri: flags.redirectUri ?? "",
139
- });
140
- if (!result.ok) {
141
- const why = {
142
- "invalid-redirect-uri":
143
- "the redirect URI must be https (or http on localhost / 127.0.0.1), its path exactly /api/hub-session/callback, with no query, fragment or userinfo",
144
- "spoke-not-found": `no spoke "${flags.id}"`,
145
- "spoke-revoked": `spoke "${flags.id}" is revoked`,
146
- }[result.reason];
147
- console.error(`[spokes] ${result.reason}: ${why}`);
148
- process.exitCode = 1;
149
- return;
150
- }
151
- console.log(`[spokes] sign-in client ready for spoke="${result.spokeId}"`);
152
- console.log(`AILK_HUB_CLIENT_ID=${result.spokeId}`);
153
- console.log(`AILK_HUB_CLIENT_SECRET=${result.secret}`);
154
- console.log(
155
- "[spokes] This secret will not be shown again. Set both as the spoke's AILK_HUB_CLIENT_ID / AILK_HUB_CLIENT_SECRET now. Re-running rotates the secret.",
156
- );
157
- }
158
-
159
- async function runList(): Promise<void> {
160
- const spokes = await listSpokes();
161
- if (spokes.length === 0) {
162
- console.log("[spokes] no spokes registered");
163
- return;
164
- }
165
- for (const spoke of spokes) {
166
- console.log(
167
- [
168
- spoke.id,
169
- `name="${spoke.name}"`,
170
- `workspace=${spoke.workspaceId}`,
171
- `prefix=${spoke.credentialPrefix}`,
172
- `created=${spoke.createdAt.toISOString()}`,
173
- `revoked=${spoke.revokedAt ? spoke.revokedAt.toISOString() : "-"}`,
174
- ].join(" "),
175
- );
176
- }
177
- }
178
-
179
- async function main(): Promise<void> {
180
- const [command, ...rest] = process.argv.slice(2);
181
- const flags = parseFlags(rest);
182
- if (command === "register") return runRegister(flags);
183
- if (command === "revoke") return runRevoke(flags);
184
- if (command === "list") return runList();
185
- if (command === "sign-in-client") return runSignInClient(flags);
186
- console.error(`[spokes] ${USAGE}`);
187
- process.exitCode = 1;
188
- }
189
-
190
- main()
191
- .catch((err: unknown) => {
192
- console.error(
193
- "[spokes] failed:",
194
- err instanceof Error ? err.message : String(err),
195
- );
196
- process.exitCode = 1;
197
- })
198
- .finally(() => prisma.$disconnect());
@@ -1,45 +0,0 @@
1
- #!/usr/bin/env node
2
- /* istanbul ignore file -- CLI bootstrap; rekeyStripeConnections and
3
- formatRekeyReport are unit-tested in
4
- services/offerings/__tests__/connection.test.ts. */
5
- /**
6
- * Re-encrypt every Organization Stripe connection under the current
7
- * STRIPE_CONNECTION_KEY (#8907 D13, contract C10).
8
- *
9
- * Dev: `pnpm --filter @working-theory/api stripe-connections:rekey` (tsx,
10
- * this file). Production: `node dist/bin/stripe-connections-rekey.js`.
11
- *
12
- * The rotation: set STRIPE_CONNECTION_KEY_PREVIOUS to the old key and the new
13
- * key as STRIPE_CONNECTION_KEY, redeploy, run this, and unset _PREVIOUS once
14
- * it prints `remaining=0`. A row counted `unreadable` decrypts under neither
15
- * key; its owner reconnects or disconnects. A second run rekeys nothing.
16
- *
17
- * Run it with the production environment — `VERCEL_ENV=production`, or
18
- * `NODE_ENV=production` off Vercel — the same predicate the API goes live on;
19
- * anywhere else it prints `not_production`, reads nothing and exits 1.
20
- *
21
- * Prints one line of counts only — never a key, a value or a row id — and
22
- * exits 1 when the key is not configured, off production, or the walk fails.
23
- */
24
- import {
25
- formatRekeyReport,
26
- rekeyStripeConnections,
27
- } from "../services/offerings/connection.js";
28
-
29
- async function main(): Promise<void> {
30
- const result = await rekeyStripeConnections();
31
- if (!result.ok) {
32
- console.error(formatRekeyReport(result));
33
- process.exitCode = 1;
34
- return;
35
- }
36
- console.log(formatRekeyReport(result));
37
- }
38
-
39
- main().catch((err: unknown) => {
40
- // The class name only: a store error's message is fixed, but nothing else
41
- // a failure carries is printed.
42
- const name = err instanceof Error ? err.name : "unknown";
43
- console.error(`[stripe-connections-rekey] failed: ${name}`);
44
- process.exitCode = 1;
45
- });
@@ -1,196 +0,0 @@
1
- /**
2
- * @file hub-require-spoke-key.test.ts
3
- * @description `AILK_HUB_REQUIRE_SPOKE_KEY` — the hub's spoke-key mode
4
- * (#7532 D7; three-state since #7633, spec
5
- * docs/specs/7633-expected-a-keyless-public-read-spec.md D2, AC-5).
6
- *
7
- * Unset, empty or blank is `"auto"` (refuse a keyless call once the registry
8
- * holds a row); `true`/`1` is `"on"`; `false`/`0` is `"off"`, the emergency
9
- * opt-out, which warns at boot — case-insensitively, after trimming. Any other
10
- * value refuses the boot with a message naming the variable, never the value.
11
- * `"on"` with no `DATABASE_URL` refuses the boot too: it would refuse every
12
- * call. `"auto"` and `"off"` boot without a database.
13
- */
14
-
15
- // Mock dotenv so a developer's local .env never leaks into the config module.
16
- jest.mock('dotenv', () => ({
17
- config: jest.fn(),
18
- }));
19
-
20
- const mockLoggerError = jest.fn();
21
- const mockLoggerWarn = jest.fn();
22
- jest.mock('@working-theory/observability', () => ({
23
- logger: {
24
- error: (...args: unknown[]) => mockLoggerError(...args),
25
- warn: (...args: unknown[]) => mockLoggerWarn(...args),
26
- info: jest.fn(),
27
- debug: jest.fn(),
28
- },
29
- }));
30
-
31
- describe('AILK_HUB_REQUIRE_SPOKE_KEY (#7532 D7, #7633 D2)', () => {
32
- const originalEnv = process.env;
33
-
34
- beforeEach(() => {
35
- jest.resetModules();
36
- process.env = { ...originalEnv };
37
- delete process.env.AILK_HUB_REQUIRE_SPOKE_KEY;
38
- process.env.DATABASE_URL = 'postgresql://test:test@localhost/test';
39
- jest.clearAllMocks();
40
- });
41
-
42
- afterEach(() => {
43
- process.env = originalEnv;
44
- jest.restoreAllMocks();
45
- });
46
-
47
- /** The boot warnings that name this variable. */
48
- function hubWarnings(): unknown[][] {
49
- return mockLoggerWarn.mock.calls.filter((call) =>
50
- JSON.stringify(call).includes('AILK_HUB_REQUIRE_SPOKE_KEY'),
51
- );
52
- }
53
-
54
- it.each([undefined, '', ' '])('%j parses "auto" — the default — and warns nothing', (raw) => {
55
- if (raw !== undefined) process.env.AILK_HUB_REQUIRE_SPOKE_KEY = raw;
56
- const { config } = require('../index');
57
- expect(config).toMatchObject({ AILK_HUB_REQUIRE_SPOKE_KEY: 'auto' });
58
- expect(hubWarnings()).toHaveLength(0);
59
- });
60
-
61
- it.each(['true', 'TRUE', '1', ' True '])('%j parses "on" and warns nothing', (raw) => {
62
- process.env.AILK_HUB_REQUIRE_SPOKE_KEY = raw;
63
- const { config } = require('../index');
64
- expect(config).toMatchObject({ AILK_HUB_REQUIRE_SPOKE_KEY: 'on' });
65
- expect(hubWarnings()).toHaveLength(0);
66
- });
67
-
68
- it.each(['false', 'FALSE', '0'])('%j parses "off" — the emergency opt-out — and warns once at boot', (raw) => {
69
- process.env.AILK_HUB_REQUIRE_SPOKE_KEY = raw;
70
- const { config } = require('../index');
71
- expect(config).toMatchObject({ AILK_HUB_REQUIRE_SPOKE_KEY: 'off' });
72
- const warnings = hubWarnings();
73
- expect(warnings).toHaveLength(1);
74
- expect(JSON.stringify(warnings)).toContain('AILK_HUB_REQUIRE_SPOKE_KEY=false');
75
- });
76
-
77
- it('any other value refuses the boot, naming the variable and never the value', () => {
78
- process.env.AILK_HUB_REQUIRE_SPOKE_KEY = 'yes-sentinel-value';
79
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
80
- throw new Error('Process exit called');
81
- });
82
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
83
-
84
- expect(() => require('../index')).toThrow('Process exit called');
85
- expect(exitSpy).toHaveBeenCalledWith(1);
86
-
87
- const logged = JSON.stringify([mockLoggerError.mock.calls, consoleSpy.mock.calls]);
88
- expect(logged).toContain('AILK_HUB_REQUIRE_SPOKE_KEY');
89
- expect(logged).not.toContain('yes-sentinel-value');
90
- });
91
-
92
- it('on with DATABASE_URL unset refuses the boot', () => {
93
- process.env.AILK_HUB_REQUIRE_SPOKE_KEY = 'true';
94
- delete process.env.DATABASE_URL;
95
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
96
- throw new Error('Process exit called');
97
- });
98
- jest.spyOn(console, 'error').mockImplementation(() => undefined);
99
-
100
- expect(() => require('../index')).toThrow('Process exit called');
101
- expect(exitSpy).toHaveBeenCalledWith(1);
102
- expect(JSON.stringify(mockLoggerError.mock.calls)).toContain(
103
- 'AILK_HUB_REQUIRE_SPOKE_KEY',
104
- );
105
- });
106
-
107
- it.each([
108
- ['auto', undefined],
109
- ['off', 'false'],
110
- ])('"%s" with DATABASE_URL unset still boots database-less', (mode, raw) => {
111
- if (raw !== undefined) process.env.AILK_HUB_REQUIRE_SPOKE_KEY = raw;
112
- delete process.env.DATABASE_URL;
113
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
114
- throw new Error('Process exit called');
115
- });
116
-
117
- const { config } = require('../index');
118
- expect(exitSpy).not.toHaveBeenCalled();
119
- expect(config).toMatchObject({ AILK_HUB_REQUIRE_SPOKE_KEY: mode });
120
- });
121
- });
122
-
123
- /**
124
- * `AILK_SPOKE_FLOW_SEED` — whether a hub lets a spoke key write its own
125
- * workspace's flow definitions (#7876 D3, AC-3). Off unless set: unset, empty
126
- * or blank is `false`; `true`/`1` is `true`; `false`/`0` is `false` —
127
- * case-insensitively, after trimming. Any other value refuses the boot with a
128
- * message naming the variable, never the value. `true` warns once at boot.
129
- */
130
- describe('AILK_SPOKE_FLOW_SEED (#7876 D3)', () => {
131
- const originalEnv = process.env;
132
-
133
- beforeEach(() => {
134
- jest.resetModules();
135
- process.env = { ...originalEnv };
136
- delete process.env.AILK_SPOKE_FLOW_SEED;
137
- delete process.env.AILK_HUB_REQUIRE_SPOKE_KEY;
138
- process.env.DATABASE_URL = 'postgresql://test:test@localhost/test';
139
- jest.clearAllMocks();
140
- });
141
-
142
- afterEach(() => {
143
- process.env = originalEnv;
144
- jest.restoreAllMocks();
145
- });
146
-
147
- function seedWarnings(): unknown[][] {
148
- return mockLoggerWarn.mock.calls.filter((call) =>
149
- JSON.stringify(call).includes('AILK_SPOKE_FLOW_SEED'),
150
- );
151
- }
152
-
153
- it.each([
154
- [undefined, false],
155
- ['', false],
156
- [' ', false],
157
- ['false', false],
158
- ['0', false],
159
- ['true', true],
160
- ['TRUE', true],
161
- ['1', true],
162
- [' True ', true],
163
- ['yes', null],
164
- ])('parseSpokeFlowSeed(%j) is %j', (raw, expected) => {
165
- const { parseSpokeFlowSeed } = require('../index');
166
- expect(parseSpokeFlowSeed(raw)).toBe(expected);
167
- });
168
-
169
- it('is off by default and warns nothing', () => {
170
- const { config } = require('../index');
171
- expect(config).toMatchObject({ AILK_SPOKE_FLOW_SEED: false });
172
- expect(seedWarnings()).toHaveLength(0);
173
- });
174
-
175
- it('on warns once at boot', () => {
176
- process.env.AILK_SPOKE_FLOW_SEED = 'true';
177
- const { config } = require('../index');
178
- expect(config).toMatchObject({ AILK_SPOKE_FLOW_SEED: true });
179
- expect(seedWarnings()).toHaveLength(1);
180
- });
181
-
182
- it('"yes" refuses the boot, naming the variable and never the value', () => {
183
- process.env.AILK_SPOKE_FLOW_SEED = 'yes';
184
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
185
- throw new Error('Process exit called');
186
- });
187
- const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => undefined);
188
-
189
- expect(() => require('../index')).toThrow('Process exit called');
190
- expect(exitSpy).toHaveBeenCalledWith(1);
191
-
192
- const logged = JSON.stringify([mockLoggerError.mock.calls, consoleSpy.mock.calls]);
193
- expect(logged).toContain('AILK_SPOKE_FLOW_SEED must be true, 1, false, 0 or unset');
194
- expect(logged).not.toContain('yes');
195
- });
196
- });
@@ -1,233 +0,0 @@
1
- // Mock dotenv
2
- jest.mock('dotenv', () => ({
3
- config: jest.fn(),
4
- }));
5
-
6
- // Mock the observability logger so assertions don't depend on pino's stdout serialization.
7
- const mockLoggerError = jest.fn();
8
- const mockLoggerWarn = jest.fn();
9
- jest.mock('@working-theory/observability', () => ({
10
- logger: {
11
- error: (...args: unknown[]) => mockLoggerError(...args),
12
- warn: (...args: unknown[]) => mockLoggerWarn(...args),
13
- info: jest.fn(),
14
- debug: jest.fn(),
15
- },
16
- }));
17
-
18
- describe('Config', () => {
19
- const originalEnv = process.env;
20
-
21
- beforeEach(() => {
22
- jest.resetModules();
23
- process.env = { ...originalEnv };
24
- jest.clearAllMocks();
25
- });
26
-
27
- afterEach(() => {
28
- process.env = originalEnv;
29
- });
30
-
31
- it('should load environment variables', () => {
32
- // ARRANGE
33
- process.env.DATABASE_URL = 'postgresql://test:test@localhost/test';
34
-
35
- // ACT — require dotenv after resetModules so we observe the same mock
36
- // instance the freshly-required config module will use.
37
- const dotenv = require('dotenv');
38
- const { config } = require('../index');
39
-
40
- // ASSERT
41
- expect(config.DATABASE_URL).toBe('postgresql://test:test@localhost/test');
42
- expect(dotenv.config).toHaveBeenCalled();
43
- });
44
-
45
- // #6730 security review (M1): a production build whose enabled modules keep
46
- // their state in the database still refuses to boot without DATABASE_URL —
47
- // a missing database there is a broken deploy (e.g. a commerce licence read
48
- // as absent would ack-and-drop signed Stripe events), not a Free site.
49
- it.each(['commerce', 'content-write', 'multi-tenant', 'scheduling', 'signal-collectors', 'workspace'])(
50
- 'should exit on missing DATABASE_URL in production when the %s module is enabled',
51
- (moduleName) => {
52
- process.env.NODE_ENV = 'production';
53
- process.env.AILK_ENABLED_MODULES = `marketing,${moduleName}`;
54
- delete process.env.DATABASE_URL;
55
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
56
- throw new Error('Process exit called');
57
- });
58
-
59
- expect(() => require('../index')).toThrow('Process exit called');
60
- expect(exitSpy).toHaveBeenCalledWith(1);
61
- expect(mockLoggerError).toHaveBeenCalled();
62
-
63
- exitSpy.mockRestore();
64
- },
65
- );
66
-
67
- it('should start database-less in production with only non-database modules enabled', () => {
68
- process.env.NODE_ENV = 'production';
69
- process.env.AILK_ENABLED_MODULES = 'marketing,docs-blog';
70
- delete process.env.DATABASE_URL;
71
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
72
- throw new Error('Process exit called');
73
- });
74
-
75
- const { config } = require('../index');
76
-
77
- expect(exitSpy).not.toHaveBeenCalled();
78
- expect(config.DATABASE_URL).toBeUndefined();
79
- exitSpy.mockRestore();
80
- });
81
-
82
- // #6482 — a missing DATABASE_URL starts the API database-less (one
83
- // warning) instead of exiting, so a fresh scaffold's `pnpm dev` keeps its
84
- // "no database needed" promise. #6730 (operator decision (a)) extends that
85
- // to production: a Free site delivers leads by email with no Postgres.
86
- it.each([
87
- ['production', 'production'],
88
- ['development', 'development'],
89
- ['unset', undefined],
90
- ['test', 'test'],
91
- ])('should start database-less with DATABASE_URL unset when NODE_ENV is %s', (_label, nodeEnv) => {
92
- // ARRANGE
93
- if (nodeEnv === undefined) {
94
- delete process.env.NODE_ENV;
95
- } else {
96
- process.env.NODE_ENV = nodeEnv;
97
- }
98
- delete process.env.DATABASE_URL;
99
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
100
- throw new Error('Process exit called');
101
- });
102
-
103
- // ACT
104
- const { config } = require('../index');
105
-
106
- // ASSERT
107
- expect(exitSpy).not.toHaveBeenCalled();
108
- expect(config.DATABASE_URL).toBeUndefined();
109
- expect(config.NODE_ENV).toBe(nodeEnv ?? 'development');
110
- expect(mockLoggerError).not.toHaveBeenCalled();
111
- expect(mockLoggerWarn).toHaveBeenCalledTimes(1);
112
- expect(mockLoggerWarn.mock.calls[0]?.[1]).toContain('database_not_configured');
113
-
114
- exitSpy.mockRestore();
115
- });
116
-
117
- it('should not require any external-platform forwarding env vars', () => {
118
- // ARRANGE — only DATABASE_URL set; legacy forwarding vars explicitly absent
119
- process.env.DATABASE_URL = 'postgresql://test:test@localhost/test';
120
- const legacyVars = ['THEORY' + '_API_URL', 'THEORY' + '_API_KEY'];
121
- legacyVars.forEach((k) => {
122
- delete process.env[k];
123
- });
124
-
125
- // ACT
126
- const { config } = require('../index');
127
-
128
- // ASSERT — config loads without error and exposes no legacy forwarding keys
129
- expect(config.DATABASE_URL).toBe('postgresql://test:test@localhost/test');
130
- legacyVars.forEach((k) => {
131
- expect((config as Record<string, unknown>)[k]).toBeUndefined();
132
- });
133
- });
134
-
135
- it('should set default values for optional config', () => {
136
- // ARRANGE
137
- process.env.DATABASE_URL = 'postgresql://test:test@localhost/test';
138
- delete process.env.NODE_ENV;
139
- delete process.env.PORT;
140
- delete process.env.FRONTEND_URL;
141
-
142
- // ACT
143
- const { config } = require('../index');
144
-
145
- // ASSERT
146
- expect(config.NODE_ENV).toBe('development');
147
- expect(config.PORT).toBe(3002);
148
- expect(config.FRONTEND_URL).toBe('http://localhost:3000');
149
- });
150
- });
151
-
152
- // #6730 (operator decision (a)) — the REAL server boots in production with
153
- // no database: an email-only Free site must be able to run its API, answer
154
- // `GET /v1/leads/capture`, and refuse authenticated routes with a 503 rather
155
- // than exit at startup.
156
- describe('production boot without a database (#6730)', () => {
157
- const originalEnv = process.env;
158
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- the Fastify instance comes from an isolated require; its generics add nothing here
159
- let app: any;
160
-
161
- const EMAIL_ENV = {
162
- RESEND_API_KEY: 're_test',
163
- AILK_LEAD_NOTIFICATION_TO: 'owner@example.com',
164
- AILK_LEAD_NOTIFICATION_FROM: 'noreply@example.com',
165
- };
166
-
167
- // External boundaries only, as src/__tests__/server.test.ts mocks them.
168
- beforeAll(() => {
169
- jest.doMock('@fastify/cors', () => jest.fn().mockResolvedValue(undefined));
170
- jest.doMock('@fastify/rate-limit', () => jest.fn().mockResolvedValue(undefined));
171
- jest.doMock('@working-theory/email', () => ({
172
- sendLeadNotification: jest.fn().mockResolvedValue({ ok: true, messageId: 'm' }),
173
- sendRequestConfirmEmail: jest.fn(),
174
- sendRequestPaidLinkEmail: jest.fn(),
175
- }));
176
- });
177
-
178
- afterEach(async () => {
179
- if (app) await app.close();
180
- app = undefined;
181
- process.env = originalEnv;
182
- });
183
-
184
- async function bootProduction(extra: Record<string, string> = {}) {
185
- jest.resetModules();
186
- const env: NodeJS.ProcessEnv = { ...originalEnv, NODE_ENV: 'production', ...extra };
187
- delete env.DATABASE_URL;
188
- delete env.DATABASE_URL_NON_POOLED;
189
- delete env.RESEND_API_KEY;
190
- delete env.AILK_LEAD_NOTIFICATION_TO;
191
- delete env.AILK_LEAD_NOTIFICATION_FROM;
192
- Object.assign(env, extra);
193
- process.env = env;
194
- const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
195
- throw new Error('Process exit called');
196
- });
197
- try {
198
- const { build } = require('../../server');
199
- app = await build();
200
- } finally {
201
- expect(exitSpy).not.toHaveBeenCalled();
202
- exitSpy.mockRestore();
203
- }
204
- return app;
205
- }
206
-
207
- it('with only the email vars, starts and reports contact accepted, newsletter not', async () => {
208
- const server = await bootProduction(EMAIL_ENV);
209
-
210
- const res = await server.inject({ method: 'GET', url: '/v1/leads/capture' });
211
-
212
- expect(res.statusCode).toBe(200);
213
- expect(res.json()).toEqual({ ok: true, data: { contact: true, newsletter: false } });
214
- });
215
-
216
- it('with nothing configured, starts and reports nothing accepted', async () => {
217
- const server = await bootProduction();
218
-
219
- const res = await server.inject({ method: 'GET', url: '/v1/leads/capture' });
220
-
221
- expect(res.statusCode).toBe(200);
222
- expect(res.json()).toEqual({ ok: true, data: { contact: false, newsletter: false } });
223
- });
224
-
225
- it('refuses an authenticated route with 503 database_not_configured', async () => {
226
- const server = await bootProduction(EMAIL_ENV);
227
-
228
- const res = await server.inject({ method: 'GET', url: '/v1/leads' });
229
-
230
- expect(res.statusCode).toBe(503);
231
- expect(res.json()).toEqual({ error: 'database_not_configured' });
232
- });
233
- });