create-ailk 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (620) hide show
  1. package/README.md +124 -3
  2. package/component-catalog.md +456 -0
  3. package/dist/cli.js +92 -16
  4. package/dist/component-catalog.d.ts +11 -0
  5. package/dist/component-catalog.js +39 -0
  6. package/dist/copy.d.ts +7 -0
  7. package/dist/copy.js +24 -0
  8. package/dist/derive-architecture.d.ts +13 -0
  9. package/dist/derive-architecture.js +238 -0
  10. package/dist/index.d.ts +7 -2
  11. package/dist/index.js +19 -2
  12. package/dist/lib/extract-module-bundle.d.ts +16 -0
  13. package/dist/lib/extract-module-bundle.js +184 -0
  14. package/dist/lib/fetch-module.d.ts +55 -0
  15. package/dist/lib/fetch-module.js +125 -0
  16. package/dist/lib/module-license-gate.d.ts +48 -0
  17. package/dist/lib/module-license-gate.js +67 -0
  18. package/dist/lib/module-tier.d.ts +35 -0
  19. package/dist/lib/module-tier.js +37 -0
  20. package/dist/module-architecture.d.ts +175 -0
  21. package/dist/module-architecture.js +663 -0
  22. package/dist/parse-args.d.ts +47 -1
  23. package/dist/parse-args.js +143 -2
  24. package/dist/programmatic.d.ts +160 -3
  25. package/dist/programmatic.js +196 -7
  26. package/dist/surfaces.d.ts +211 -0
  27. package/dist/surfaces.js +532 -0
  28. package/dist/sync-routes.d.ts +92 -0
  29. package/dist/sync-routes.js +350 -0
  30. package/package.json +12 -5
  31. package/templates/.claude/rules/architecture.md +6 -2
  32. package/templates/.claude/skills/README.md +9 -9
  33. package/templates/.claude/skills/add-schema/SKILL.md +14 -7
  34. package/templates/.claude/skills/add-schema/references/schema-types.md +109 -6
  35. package/templates/.claude/skills/provision-config/SKILL.md +1 -1
  36. package/templates/.claude/skills/scaffold-commerce/SKILL.md +498 -1
  37. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +370 -0
  38. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +376 -0
  39. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +471 -0
  40. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +365 -0
  41. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +60 -0
  42. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +446 -0
  43. package/templates/.claude/skills/suggest-site-pages/references/categories.md +4 -4
  44. package/templates/.env.example +126 -2
  45. package/templates/CLAUDE.md +11 -5
  46. package/templates/README.md +1 -1
  47. package/templates/apps/api/.env.example +38 -0
  48. package/templates/apps/api/CLAUDE.md +78 -1
  49. package/templates/apps/api/LICENSE +201 -0
  50. package/templates/apps/api/api/index.ts +6 -141
  51. package/templates/apps/api/jest.config.cjs +16 -0
  52. package/templates/apps/api/package.json +22 -6
  53. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +141 -0
  54. package/templates/apps/api/src/__tests__/server.test.ts +46 -0
  55. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +211 -0
  56. package/templates/apps/api/src/bin/abandon-sweep.ts +42 -0
  57. package/templates/apps/api/src/bin/deliverable-resend.ts +144 -0
  58. package/templates/apps/api/src/bin/deliverable-upload.ts +114 -0
  59. package/templates/apps/api/src/bin/followup-sweep.ts +44 -0
  60. package/templates/apps/api/src/bin/listing-csv-sweep.ts +46 -0
  61. package/templates/apps/api/src/bin/seed-presets.ts +58 -0
  62. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +104 -0
  63. package/templates/apps/api/src/bin/session-retention-sweep.ts +46 -0
  64. package/templates/apps/api/src/cli.ts +5 -11
  65. package/templates/apps/api/src/config/index.ts +35 -0
  66. package/templates/apps/api/src/config/modules.ts +43 -0
  67. package/templates/apps/api/src/lib/__mocks__/prisma-client.js +11 -0
  68. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -0
  69. package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +2 -2
  70. package/templates/apps/api/src/lib/site.ts +16 -0
  71. package/templates/apps/api/src/lib/stripe.ts +22 -0
  72. package/templates/apps/api/src/lib/tenant-db.ts +225 -0
  73. package/templates/apps/api/src/lib/ws-token.ts +91 -0
  74. package/templates/apps/api/src/middleware/__tests__/adversarial/session-fixation.test.ts +7 -1
  75. package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +7 -1
  76. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +91 -4
  77. package/templates/apps/api/src/middleware/auth.ts +43 -8
  78. package/templates/apps/api/src/middleware/rate-limit.ts +78 -3
  79. package/templates/apps/api/src/middleware/tenant.ts +99 -0
  80. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +92 -0
  81. package/templates/apps/api/src/openapi/spec.ts +312 -3
  82. package/templates/apps/api/src/routes/aeo/__tests__/index.test.ts +175 -0
  83. package/templates/apps/api/src/routes/aeo/index.ts +107 -0
  84. package/templates/apps/api/src/routes/auth/__tests__/auth-tokens.functional.test.ts +5 -2
  85. package/templates/apps/api/src/routes/auth/__tests__/tokens.test.ts +22 -3
  86. package/templates/apps/api/src/routes/auth/tokens.ts +13 -1
  87. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +450 -0
  88. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +262 -0
  89. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +436 -0
  90. package/templates/apps/api/src/routes/billing/index.ts +36 -0
  91. package/templates/apps/api/src/routes/billing/portal.ts +182 -0
  92. package/templates/apps/api/src/routes/billing/read.ts +75 -0
  93. package/templates/apps/api/src/routes/billing/usage.ts +153 -0
  94. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +99 -0
  95. package/templates/apps/api/src/routes/checkout/sessions.ts +20 -8
  96. package/templates/apps/api/src/routes/content/create.ts +1 -0
  97. package/templates/apps/api/src/routes/content/delete.ts +1 -0
  98. package/templates/apps/api/src/routes/content/index.ts +42 -6
  99. package/templates/apps/api/src/routes/content/update.ts +1 -0
  100. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +393 -0
  101. package/templates/apps/api/src/routes/deliverables/index.ts +200 -0
  102. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +443 -0
  103. package/templates/apps/api/src/routes/flow-checkouts/index.ts +82 -0
  104. package/templates/apps/api/src/routes/flows/README.md +147 -0
  105. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +752 -0
  106. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +671 -0
  107. package/templates/apps/api/src/routes/flows/index.ts +202 -0
  108. package/templates/apps/api/src/routes/flows/recommender.ts +443 -0
  109. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +438 -31
  110. package/templates/apps/api/src/routes/leads/__tests__/track.test.ts +10 -10
  111. package/templates/apps/api/src/routes/leads/index.ts +43 -9
  112. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +558 -0
  113. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +815 -0
  114. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +869 -0
  115. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +221 -0
  116. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +468 -0
  117. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
  118. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +707 -0
  119. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +375 -0
  120. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +467 -0
  121. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
  122. package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
  123. package/templates/apps/api/src/routes/project-listings/index.ts +120 -0
  124. package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
  125. package/templates/apps/api/src/routes/project-listings/me.ts +76 -0
  126. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
  127. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
  128. package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
  129. package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
  130. package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
  131. package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
  132. package/templates/apps/api/src/routes/project-listings/start.ts +106 -0
  133. package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
  134. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
  135. package/templates/apps/api/src/routes/schedule/index.ts +249 -0
  136. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
  137. package/templates/apps/api/src/routes/slack/actions.ts +177 -0
  138. package/templates/apps/api/src/routes/slack/index.ts +38 -0
  139. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
  140. package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
  141. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
  142. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
  143. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
  144. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
  145. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
  146. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
  147. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
  148. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
  149. package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
  150. package/templates/apps/api/src/routes/webhooks/README.md +35 -12
  151. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
  152. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
  153. package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
  154. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
  155. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
  156. package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
  157. package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
  158. package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
  159. package/templates/apps/api/src/server.ts +307 -7
  160. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
  161. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
  162. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
  163. package/templates/apps/api/src/services/__tests__/consent-migration.test.ts +65 -0
  164. package/templates/apps/api/src/services/__tests__/consent.test.ts +282 -0
  165. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
  166. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
  167. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
  168. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
  169. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
  170. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
  171. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
  172. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
  173. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +684 -0
  174. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
  175. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
  176. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
  177. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
  178. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
  179. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
  180. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
  181. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
  182. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
  183. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
  184. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
  185. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
  186. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
  187. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
  188. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
  189. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
  190. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
  191. package/templates/apps/api/src/services/abandon-email.ts +307 -0
  192. package/templates/apps/api/src/services/aeo-score.ts +288 -0
  193. package/templates/apps/api/src/services/answer-distributions.ts +138 -0
  194. package/templates/apps/api/src/services/consent.ts +236 -0
  195. package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
  196. package/templates/apps/api/src/services/entitlements.ts +102 -0
  197. package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
  198. package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
  199. package/templates/apps/api/src/services/flow-engine.ts +1296 -0
  200. package/templates/apps/api/src/services/lead-promotion.ts +176 -0
  201. package/templates/apps/api/src/services/lead-routing.ts +3 -3
  202. package/templates/apps/api/src/services/listing-config.ts +102 -0
  203. package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
  204. package/templates/apps/api/src/services/listing-promotion.ts +342 -0
  205. package/templates/apps/api/src/services/playbook-compile.ts +398 -0
  206. package/templates/apps/api/src/services/playbook-render.ts +263 -0
  207. package/templates/apps/api/src/services/project-listing-decision.ts +510 -0
  208. package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
  209. package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
  210. package/templates/apps/api/src/services/project-listings.ts +1472 -0
  211. package/templates/apps/api/src/services/recommender-capture.ts +835 -0
  212. package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
  213. package/templates/apps/api/src/services/scheduling/index.ts +63 -0
  214. package/templates/apps/api/src/services/scheduling/types.ts +88 -0
  215. package/templates/apps/api/src/services/slack-notify.ts +240 -0
  216. package/templates/apps/api/src/services/tenant-context.ts +451 -0
  217. package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
  218. package/templates/apps/api/src/services/usage-credits.ts +340 -0
  219. package/templates/apps/api/src/services/usage-metering.ts +699 -0
  220. package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
  221. package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
  222. package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
  223. package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
  224. package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
  225. package/templates/apps/api/src/vercel-handler.ts +165 -0
  226. package/templates/apps/api/tsconfig.json +0 -12
  227. package/templates/apps/mcp/CLAUDE.md +19 -9
  228. package/templates/apps/mcp/LICENSE +201 -0
  229. package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
  230. package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
  231. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
  232. package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
  233. package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
  234. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
  235. package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
  236. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
  237. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
  238. package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
  239. package/templates/apps/mcp/package.json +6 -4
  240. package/templates/apps/mcp/src/config/modules.ts +37 -0
  241. package/templates/apps/mcp/src/server.ts +109 -18
  242. package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
  243. package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
  244. package/templates/apps/mcp/src/tools/create_page.ts +1 -0
  245. package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
  246. package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
  247. package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
  248. package/templates/apps/mcp/src/tools/index.ts +25 -0
  249. package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
  250. package/templates/apps/mcp/src/tools/purchase.ts +3 -0
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
  252. package/templates/apps/mcp/src/tools/update_page.ts +1 -0
  253. package/templates/apps/mcp/tsconfig.json +1 -9
  254. package/templates/apps/web/.env.example +44 -0
  255. package/templates/apps/web/CLAUDE.md +28 -0
  256. package/templates/apps/web/LICENSE +201 -0
  257. package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
  258. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
  259. package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
  260. package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
  261. package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
  262. package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
  263. package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
  264. package/templates/apps/web/__tests__/setup.ts +22 -32
  265. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
  266. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
  267. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
  268. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
  269. package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
  270. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
  271. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
  272. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
  273. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
  274. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
  275. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
  276. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
  277. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
  278. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
  279. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
  280. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
  281. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
  282. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
  283. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
  284. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
  285. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
  286. package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
  287. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
  288. package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
  289. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
  290. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
  291. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  292. package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
  293. package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
  294. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  295. package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
  296. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
  297. package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
  298. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
  299. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
  300. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
  301. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
  302. package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
  303. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
  304. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
  305. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
  306. package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
  307. package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
  308. package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
  309. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  310. package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
  311. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
  312. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
  313. package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
  314. package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
  315. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
  316. package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
  317. package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
  318. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
  319. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
  320. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
  321. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
  322. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
  323. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
  324. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
  325. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
  326. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
  327. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
  328. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
  329. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
  330. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
  331. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
  332. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
  333. package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
  334. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  335. package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
  336. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  337. package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
  338. package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
  339. package/templates/apps/web/app/[locale]/layout.tsx +127 -16
  340. package/templates/apps/web/app/[locale]/page.tsx +10 -1
  341. package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
  342. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  343. package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
  344. package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
  345. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  346. package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
  347. package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
  348. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
  349. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
  350. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
  351. package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
  352. package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
  353. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
  354. package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
  355. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
  356. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
  357. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
  358. package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
  359. package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
  360. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  361. package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
  362. package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
  363. package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
  364. package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
  365. package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
  366. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
  367. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
  368. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  369. package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
  370. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  371. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
  372. package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
  373. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
  374. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
  375. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
  376. package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
  377. package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
  378. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  379. package/templates/apps/web/app/globals.css +35 -0
  380. package/templates/apps/web/app/llms.txt/route.ts +2 -1
  381. package/templates/apps/web/app/og/route.tsx +10 -2
  382. package/templates/apps/web/auth.ts +8 -0
  383. package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
  384. package/templates/apps/web/content/en/blog/README.txt +25 -0
  385. package/templates/apps/web/content/en/docs/index.mdx +28 -0
  386. package/templates/apps/web/jest.config.cjs +90 -27
  387. package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
  388. package/templates/apps/web/lib/__tests__/site-theme.test.ts +112 -0
  389. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
  390. package/templates/apps/web/lib/account-config.ts +13 -0
  391. package/templates/apps/web/lib/server-api.ts +85 -0
  392. package/templates/apps/web/lib/site-brand.tsx +121 -0
  393. package/templates/apps/web/lib/site-theme.ts +74 -0
  394. package/templates/apps/web/messages/en.json +275 -7
  395. package/templates/apps/web/middleware.ts +11 -3
  396. package/templates/apps/web/next.config.mjs +8 -2
  397. package/templates/apps/web/package.json +17 -7
  398. package/templates/apps/web/postcss.config.mjs +23 -0
  399. package/templates/apps/web/public/.well-known/security.txt +3 -3
  400. package/templates/apps/web/src/lib/routes.ts +15 -2
  401. package/templates/apps/web/tsconfig.json +1 -16
  402. package/templates/content/_site.mdx +36 -25
  403. package/templates/content/en/AboutPage/about.mdx +24 -14
  404. package/templates/content/en/ContactPage/contact.mdx +23 -14
  405. package/templates/content/en/FAQPage/faq.mdx +10 -392
  406. package/templates/content/en/HomePage/home.mdx +14 -285
  407. package/templates/database/CHANGELOG.md +443 -0
  408. package/templates/database/LICENSE +201 -0
  409. package/templates/database/README.md +3 -1
  410. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
  411. package/templates/database/__tests__/flows-presets.test.ts +173 -0
  412. package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
  413. package/templates/database/__tests__/seed.test.ts +88 -0
  414. package/templates/database/__tests__/user-cascade.test.ts +19 -4
  415. package/templates/database/auth/schema.prisma +521 -0
  416. package/templates/database/config/schema.prisma +234 -0
  417. package/templates/database/content/schema.prisma +263 -0
  418. package/templates/database/flows/listing-diagnostic.ts +182 -0
  419. package/templates/database/flows/presets.ts +315 -0
  420. package/templates/database/inbox/__tests__/schema.test.ts +4 -0
  421. package/templates/database/inbox/schema.prisma +574 -3
  422. package/templates/database/jest.config.cjs +8 -0
  423. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
  424. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
  425. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
  426. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
  427. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
  428. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
  429. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
  430. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
  431. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
  432. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
  433. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
  434. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
  435. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
  436. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
  437. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
  438. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
  439. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
  440. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
  441. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
  442. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
  443. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
  444. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
  445. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
  446. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
  447. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
  448. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
  449. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
  450. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
  451. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
  452. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
  453. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
  454. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
  455. package/templates/database/migrations/20260911140000_listing_structured_address/migration.sql +32 -0
  456. package/templates/database/migrations/20260911180000_consent_grants/migration.sql +71 -0
  457. package/templates/database/migrations/20260911200000_listing_site_answers/migration.sql +30 -0
  458. package/templates/database/migrations/20260912120000_listing_owner_link/migration.sql +49 -0
  459. package/templates/database/ops/schema.prisma +27 -0
  460. package/templates/database/package.json +42 -4
  461. package/templates/database/prisma.config.ts +1 -1
  462. package/templates/database/schema.prisma +19 -435
  463. package/templates/database/seed-test.ts +14 -4
  464. package/templates/database/seed.ts +148 -0
  465. package/templates/database/tenancy.ts +45 -0
  466. package/templates/database/tsconfig.flows.json +15 -0
  467. package/templates/database/tsconfig.json +7 -1
  468. package/templates/database/tsconfig.tenancy.json +14 -0
  469. package/templates/package.json +30 -55
  470. package/templates/pnpm-workspace.yaml +0 -3
  471. package/templates/project.yaml +1 -1
  472. package/templates/tsconfig.json +1 -37
  473. package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
  474. package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
  475. package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
  476. package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
  477. package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
  478. package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
  479. package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
  480. package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
  481. package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
  482. package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
  483. package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
  484. package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
  485. package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
  486. package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
  487. package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
  488. package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
  489. package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
  490. package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
  491. package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
  492. package/templates/apps/web/__tests__/page.test.tsx +0 -230
  493. package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
  494. package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
  495. package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
  496. package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
  497. package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
  498. package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
  499. package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
  500. package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
  501. package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
  502. package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
  503. package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
  504. package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
  505. package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
  506. package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
  507. package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
  508. package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
  509. package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
  510. package/templates/apps/web/content/en/blog/index.mdx +0 -15
  511. package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
  512. package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
  513. package/templates/content/.gitkeep +0 -0
  514. package/templates/content/CLAUDE.md +0 -85
  515. package/templates/content/ar/HomePage/home.mdx +0 -123
  516. package/templates/content/ar/_site.mdx +0 -35
  517. package/templates/content/de/_site.mdx +0 -29
  518. package/templates/content/en/.gitkeep +0 -0
  519. package/templates/content/en/AboutPage/about.schema.json +0 -82
  520. package/templates/content/en/BookCall/book-call.mdx +0 -19
  521. package/templates/content/en/BookCall/book-call.schema.json +0 -121
  522. package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
  523. package/templates/content/en/CareersList/careers.mdx +0 -24
  524. package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
  525. package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
  526. package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
  527. package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
  528. package/templates/content/en/ClientsList/clients.mdx +0 -38
  529. package/templates/content/en/ContactPage/contact.schema.json +0 -100
  530. package/templates/content/en/ContactPoint/primary.mdx +0 -14
  531. package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
  532. package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
  533. package/templates/content/en/CoursesList/courses.mdx +0 -22
  534. package/templates/content/en/DownloadsList/downloads.mdx +0 -22
  535. package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
  536. package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
  537. package/templates/content/en/EventsList/events.mdx +0 -26
  538. package/templates/content/en/FAQPage/faq.schema.json +0 -231
  539. package/templates/content/en/GlossaryList/glossary.mdx +0 -19
  540. package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
  541. package/templates/content/en/HomePage/home.schema.json +0 -88
  542. package/templates/content/en/HomePage/launch-service.mdx +0 -475
  543. package/templates/content/en/HomePage/launch-service.schema.json +0 -138
  544. package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
  545. package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
  546. package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
  547. package/templates/content/en/Integration/github-actions.mdx +0 -31
  548. package/templates/content/en/Integration/openai-api.mdx +0 -26
  549. package/templates/content/en/Integration/vercel.mdx +0 -32
  550. package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
  551. package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
  552. package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
  553. package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
  554. package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
  555. package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
  556. package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
  557. package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
  558. package/templates/content/en/LandingPage/developers.mdx +0 -19
  559. package/templates/content/en/LandingPage/developers.schema.json +0 -109
  560. package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
  561. package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
  562. package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
  563. package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
  564. package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
  565. package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
  566. package/templates/content/en/LandingPage/platform.mdx +0 -369
  567. package/templates/content/en/LandingPage/platform.schema.json +0 -166
  568. package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
  569. package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
  570. package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
  571. package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
  572. package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
  573. package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
  574. package/templates/content/en/Organization/site-owner.mdx +0 -16
  575. package/templates/content/en/Partner/prisma.mdx +0 -31
  576. package/templates/content/en/Partner/supabase.mdx +0 -30
  577. package/templates/content/en/Partner/vercel.mdx +0 -28
  578. package/templates/content/en/PartnersList/partners.mdx +0 -22
  579. package/templates/content/en/Person/founder.mdx +0 -18
  580. package/templates/content/en/Pricing/pricing.mdx +0 -538
  581. package/templates/content/en/Pricing/pricing.schema.json +0 -209
  582. package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
  583. package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
  584. package/templates/content/en/Product/ailk.mdx +0 -19
  585. package/templates/content/en/Product/ailk.schema.json +0 -94
  586. package/templates/content/en/Product/api-launch-kit.mdx +0 -26
  587. package/templates/content/en/Product/launch-kit.mdx +0 -28
  588. package/templates/content/en/ProductsList/products.mdx +0 -26
  589. package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
  590. package/templates/content/en/ProjectsList/projects.mdx +0 -25
  591. package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
  592. package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
  593. package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
  594. package/templates/content/en/ResourcesHub/resources.mdx +0 -22
  595. package/templates/content/en/Service/consulting.mdx +0 -22
  596. package/templates/content/en/Service/implementation.mdx +0 -30
  597. package/templates/content/en/Service/launch-service.mdx +0 -232
  598. package/templates/content/en/Service/launch.mdx +0 -228
  599. package/templates/content/en/ServicesList/services.mdx +0 -84
  600. package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
  601. package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
  602. package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
  603. package/templates/content/en/TeamList/team.mdx +0 -63
  604. package/templates/content/en/TeamMember/founder.mdx +0 -72
  605. package/templates/content/en/TermsPage/terms.mdx +0 -19
  606. package/templates/content/en/TermsPage/terms.schema.json +0 -99
  607. package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
  608. package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
  609. package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
  610. package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
  611. package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
  612. package/templates/content/en-XA/AboutPage/about.mdx +0 -8
  613. package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
  614. package/templates/content/en-XA/HomePage/home.mdx +0 -8
  615. package/templates/content/es/_site.mdx +0 -30
  616. package/templates/content/fr/_site.mdx +0 -30
  617. package/templates/content/ja/_site.mdx +0 -30
  618. package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
  619. package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
  620. package/templates/content/pt-BR/HomePage/home.mdx +0 -23
@@ -0,0 +1,446 @@
1
+ /**
2
+ * Subscription routes — instantiated by `scaffold-commerce`'s
3
+ * surface-selection branch (SKILL.md § Step 10h, FEAT-023 U5) into the
4
+ * consumer's own `apps/api`. Lands at, e.g.,
5
+ * `apps/api/src/routes/commerce/subscriptions.ts`. TWO handlers with
6
+ * DELIBERATELY OPPOSITE auth postures — do not register them in the same
7
+ * scope:
8
+ *
9
+ * 1. `subscribeHandler` — a PUBLIC, unauthenticated "subscribe" action.
10
+ * Mirrors `checkout-route.template.ts` exactly (this surface reuses
11
+ * `createCheckoutSession()`, pinned `mode: "subscription"` — spec §3
12
+ * design decision: no parallel subscription client module).
13
+ * 2. `subscriptionRoutes` — an ADMIN + TENANT-GUARDED Fastify plugin
14
+ * exposing the subscription-status READ. Mirrors
15
+ * `invoice-route.template.ts`'s `requireSiteAdmin` +
16
+ * `requireSingleTenantScope` guards verbatim (spec §5.3 escalation
17
+ * boundary: "retrieve-on-view is the v1 default, mirroring U4" — no
18
+ * subscription webhook is scaffolded at v1).
19
+ *
20
+ * Registration (BOTH required, in DIFFERENT scopes):
21
+ *
22
+ * // 1. subscribeHandler — public, rate-limited (mirrors checkout):
23
+ * await fastify.register(async (subscribeScope) => {
24
+ * subscribeScope.register(rateLimitMiddleware); // apps/api/src/middleware/rate-limit.ts
25
+ * subscribeScope.post("/api/subscriptions/subscribe", subscribeHandler);
26
+ * });
27
+ *
28
+ * // 2. subscriptionRoutes — tenant-guarded admin read (mirrors invoices):
29
+ * await tenantScope.register(subscriptionRoutes);
30
+ *
31
+ * ── Auth posture — split by handler, on purpose ─────────────────────────────
32
+ * `subscribeHandler` is a storefront "subscribe" action — the recurring-price
33
+ * analog of `checkout-route.template.ts`'s one-time "buy" action. It is
34
+ * intentionally UNAUTHENTICATED and does exactly ONE thing: create a NEW
35
+ * Stripe Checkout Session (`mode: "subscription"`, pinned — never
36
+ * caller-overridable) for ONE OF THE RECURRING PRICES this surface was
37
+ * scaffolded to sell (`ALLOWED_LOOKUP_KEYS`, enforced server-side before any
38
+ * Stripe call). It has NO list/read of existing subscriptions, no
39
+ * update/cancel — see `subscriptionRoutes` below for that, and see the
40
+ * "point management at the U6 portal" design decision (spec §3): this surface
41
+ * never builds self-service cancel/change, it only shows state + a portal
42
+ * link (`packages/ui`'s `SubscriptionStatus`).
43
+ *
44
+ * `subscriptionRoutes` READS an existing subscription's status — a
45
+ * MANAGEMENT-shaped action, so it is authenticated + site-admin + tenant
46
+ * guarded, the OPPOSITE of `subscribeHandler`'s public posture. Do not copy
47
+ * `subscribeHandler`'s public registration for it.
48
+ *
49
+ * ── Entitlement is NOT authorization (repeats the AILK#3744 lesson) ─────────
50
+ * `assertCommerceEntitled()`/`hasFeature("commerce")` answers "does this
51
+ * DEPLOYMENT have commerce?", never "is this caller allowed to do this?" — it
52
+ * is a SCAFFOLD-TIME check (this file is not emitted without
53
+ * `feature:commerce`); there is deliberately no runtime entitlement call
54
+ * here, and none is claimed for either handler.
55
+ *
56
+ * ── v1 tenancy limitation (mirrors invoice-route.template.ts verbatim) ──────
57
+ * `loadStripeSurfaceConfig()` resolves ONE deployment-wide
58
+ * `STRIPE_SECRET_KEY` and v1 has NO per-organization Stripe customer
59
+ * namespace (see `docs/research/commerce-template-audit/subscriptions.md`).
60
+ * A subscription therefore carries no org attribution, so on a multi-tenant
61
+ * deployment there is no honest way to decide whether a caller-supplied
62
+ * `subscriptionId` belongs to the caller's org. `requireSingleTenantScope`
63
+ * admits ONLY the deployment's own operator org (the fixed single-tenant
64
+ * sentinel `DEFAULT_ORG_ID`) and REFUSES every tenant org's admin outright —
65
+ * subscription-status reading is an OPERATOR surface at v1, not a per-tenant
66
+ * self-service one. Lifting the refusal needs a per-org Stripe Connect /
67
+ * customer-namespace model (tracked follow-up, not silently claimed fixed).
68
+ *
69
+ * TEST-MODE ONLY (v1), and the live-key guard has no remaining gap.
70
+ * `assertSandboxKey` (`packages/stripe-client/src/client.ts`) is an ALLOW-LIST
71
+ * as of AILK#3734: it accepts only `sk_test_`, `pk_test_` and `rk_test_` and
72
+ * refuses everything else, so a restricted live key and any future live prefix
73
+ * are refused by construction. It is a shared file outside this template's
74
+ * scope; both handlers below inherit that coverage via
75
+ * `loadStripeSurfaceConfig()` + `getStripeClient()`, never a second/divergent
76
+ * prefix check.
77
+ */
78
+ import { DEFAULT_ORG_ID } from "@working-theory/database/tenancy";
79
+ import type {
80
+ FastifyPluginAsync,
81
+ FastifyReply,
82
+ FastifyRequest,
83
+ } from "fastify";
84
+ import { z } from "zod";
85
+
86
+ import {
87
+ createCheckoutSession,
88
+ retrieveSubscriptionStatus,
89
+ StripeConfigError,
90
+ UnknownProductCodeError,
91
+ } from "@working-theory/stripe-client";
92
+
93
+ import { prisma } from "../../lib/prisma.js";
94
+ import { loadStripeSurfaceConfig } from "./stripe-config.js";
95
+
96
+ // ═══════════════════════════════════════════════════════════════════════════
97
+ // 1. subscribeHandler — PUBLIC "subscribe" action (mirrors checkout-route)
98
+ // ═══════════════════════════════════════════════════════════════════════════
99
+
100
+ // ── Purchasable-plan allowlist — SCAFFOLD-TIME EDIT ───────────────────────────
101
+ //
102
+ // The ONLY recurring lookup_keys this surface is allowed to sell. Without
103
+ // this allowlist the route resolves a caller-supplied `lookupKey` against
104
+ // the merchant's ENTIRE Stripe account — the exact one-time-checkout hole
105
+ // closed in PR #3733, applied here for the subscription surface. Step 10h
106
+ // replaces the placeholder below with the recurring lookup_key(s) the
107
+ // consumer selected. Until it does, EVERY subscribe request 404s.
108
+ const ALLOWED_LOOKUP_KEYS: ReadonlySet<string> = new Set<string>([
109
+ "REPLACE_WITH_YOUR_RECURRING_STRIPE_LOOKUP_KEY",
110
+ ]);
111
+
112
+ const LOOKUP_KEY_REGEX = /^[A-Za-z0-9._-]{1,200}$/;
113
+ const REDIRECT_MAX_LENGTH = 2048;
114
+
115
+ const SubscribeRequestSchema = z.object({
116
+ lookupKey: z
117
+ .string()
118
+ .regex(LOOKUP_KEY_REGEX, "lookupKey must match /^[A-Za-z0-9._-]{1,200}$/"),
119
+ successUrl: z
120
+ .string()
121
+ .min(1, "successUrl is required")
122
+ .max(REDIRECT_MAX_LENGTH, "successUrl is too long"),
123
+ cancelUrl: z
124
+ .string()
125
+ .min(1, "cancelUrl is required")
126
+ .max(REDIRECT_MAX_LENGTH, "cancelUrl is too long"),
127
+ });
128
+
129
+ /** Site's own origin — the ONLY origin a checkout redirect may resolve to (mirrors checkout-route.template.ts). */
130
+ function loadSiteOrigin(): URL {
131
+ const raw = process.env.FRONTEND_URL;
132
+ if (!raw) {
133
+ throw new Error(
134
+ "FRONTEND_URL is missing. It is the site origin checkout redirects are locked to.",
135
+ );
136
+ }
137
+ try {
138
+ return new URL(raw);
139
+ } catch {
140
+ throw new Error("FRONTEND_URL is not a valid absolute URL.");
141
+ }
142
+ }
143
+
144
+ /** Resolves a caller-supplied redirect against the site's own origin, or null (mirrors checkout-route.template.ts). */
145
+ function toSameOriginRedirect(raw: string, siteOrigin: URL): string | null {
146
+ let resolved: URL;
147
+ try {
148
+ resolved = new URL(raw, siteOrigin);
149
+ } catch {
150
+ return null;
151
+ }
152
+ if (resolved.protocol !== "https:" && resolved.protocol !== "http:") {
153
+ return null;
154
+ }
155
+ if (resolved.origin !== siteOrigin.origin) {
156
+ return null;
157
+ }
158
+ return resolved.toString();
159
+ }
160
+
161
+ /**
162
+ * POST handler — creates a test-mode SUBSCRIPTION (`mode: "subscription"`,
163
+ * pinned) Checkout Session for `lookupKey` and returns `{ sessionId, url }`.
164
+ * The caller (the scaffolded `PricingTable` section's `onSubscribe`)
165
+ * redirects the browser to `url` on success.
166
+ *
167
+ * @public-by-design — a storefront "subscribe" action, intentionally
168
+ * UNAUTHENTICATED (the recurring-price analog of checkout's public "buy"; see
169
+ * the file-header "Auth posture" note). It creates ONE allowlisted
170
+ * subscription Checkout Session and reads/mutates nothing else. Its ADMIN
171
+ * sibling `subscriptionRoutes` below is identity + tenant guarded — the marker
172
+ * exempts ONLY this handler (route-template-auth-gate, #3751), never the file.
173
+ */
174
+ export async function subscribeHandler(
175
+ request: FastifyRequest,
176
+ reply: FastifyReply,
177
+ ): Promise<void> {
178
+ let siteOrigin: URL;
179
+ try {
180
+ if (!process.env.STRIPE_SECRET_KEY || !process.env.STRIPE_PUBLISHABLE_KEY) {
181
+ throw new Error(
182
+ "STRIPE_SECRET_KEY / STRIPE_PUBLISHABLE_KEY are missing. Run scaffold-commerce (direct payments path) first.",
183
+ );
184
+ }
185
+ siteOrigin = loadSiteOrigin();
186
+ loadStripeSurfaceConfig();
187
+ } catch (err) {
188
+ if (err instanceof StripeConfigError) {
189
+ request.log.error(
190
+ { method: request.method, path: request.url, message: err.message },
191
+ "[subscriptions] subscribe: LIVE Stripe key REFUSED — live-mode-forbidden",
192
+ );
193
+ return reply.status(500).send({ error: "live-mode-forbidden" });
194
+ }
195
+ request.log.error(
196
+ {
197
+ method: request.method,
198
+ path: request.url,
199
+ message: err instanceof Error ? err.message : "unknown",
200
+ },
201
+ "[subscriptions] subscribe: config error",
202
+ );
203
+ return reply.status(500).send({ error: "config-error" });
204
+ }
205
+
206
+ const parsed = SubscribeRequestSchema.safeParse(request.body);
207
+ if (!parsed.success) {
208
+ return reply.status(400).send({
209
+ error: "validation-error",
210
+ details: parsed.error.issues,
211
+ });
212
+ }
213
+
214
+ const { lookupKey, successUrl, cancelUrl } = parsed.data;
215
+
216
+ if (!ALLOWED_LOOKUP_KEYS.has(lookupKey)) {
217
+ request.log.warn(
218
+ { method: request.method, path: request.url, status: 404 },
219
+ "[subscriptions] subscribe: lookupKey is not in this surface's allowlist; refused",
220
+ );
221
+ return reply.status(404).send({ error: "unknown-product-code" });
222
+ }
223
+
224
+ const safeSuccessUrl = toSameOriginRedirect(successUrl, siteOrigin);
225
+ const safeCancelUrl = toSameOriginRedirect(cancelUrl, siteOrigin);
226
+ if (safeSuccessUrl === null || safeCancelUrl === null) {
227
+ request.log.warn(
228
+ { method: request.method, path: request.url, status: 400 },
229
+ "[subscriptions] subscribe: redirect URL is not same-origin; refused",
230
+ );
231
+ return reply.status(400).send({ error: "invalid-redirect-url" });
232
+ }
233
+
234
+ try {
235
+ // Pinned mode: "subscription" — this endpoint can never open a one-time
236
+ // checkout session, regardless of caller input (spec §3 design decision).
237
+ const { sessionId, url } = await createCheckoutSession({
238
+ productCode: lookupKey,
239
+ successUrl: safeSuccessUrl,
240
+ cancelUrl: safeCancelUrl,
241
+ mode: "subscription",
242
+ });
243
+
244
+ request.log.info(
245
+ {
246
+ method: request.method,
247
+ path: request.url,
248
+ sessionIdPrefix: sessionId.substring(0, 8),
249
+ status: 201,
250
+ },
251
+ "[subscriptions] subscribe: session created",
252
+ );
253
+
254
+ return reply.status(201).send({ sessionId, url });
255
+ } catch (err) {
256
+ if (err instanceof UnknownProductCodeError) {
257
+ return reply.status(404).send({ error: "unknown-product-code" });
258
+ }
259
+ if (err instanceof StripeConfigError) {
260
+ request.log.error(
261
+ {
262
+ method: request.method,
263
+ path: request.url,
264
+ errName: err.name,
265
+ message: err.message,
266
+ },
267
+ "[subscriptions] subscribe: config error",
268
+ );
269
+ return reply.status(500).send({ error: "config-error" });
270
+ }
271
+ request.log.error(
272
+ {
273
+ method: request.method,
274
+ path: request.url,
275
+ errName: err instanceof Error ? err.name : "unknown",
276
+ },
277
+ "[subscriptions] subscribe: stripe error; checkout-unavailable",
278
+ );
279
+ return reply.status(502).send({ error: "checkout-unavailable" });
280
+ }
281
+ }
282
+
283
+ // ═══════════════════════════════════════════════════════════════════════════
284
+ // 2. subscriptionRoutes — ADMIN + TENANT-GUARDED status read (mirrors
285
+ // invoice-route.template.ts's guards verbatim)
286
+ // ═══════════════════════════════════════════════════════════════════════════
287
+
288
+ function unauthorized(reply: FastifyReply): void {
289
+ reply.status(401).send({
290
+ ok: false,
291
+ error: { code: "unauthorized", message: "Authentication required." },
292
+ });
293
+ }
294
+
295
+ function forbidden(reply: FastifyReply): void {
296
+ reply.status(403).send({
297
+ ok: false,
298
+ error: { code: "forbidden", message: "Admin access required." },
299
+ });
300
+ }
301
+
302
+ /** Identical for EVERY request — leaks no information about which subscriptions exist (no enumeration oracle). */
303
+ function tenancyUnsupported(reply: FastifyReply): void {
304
+ reply.status(403).send({
305
+ ok: false,
306
+ error: {
307
+ code: "tenancy_unsupported",
308
+ message:
309
+ "Subscription-status reading is unavailable on a multi-tenant deployment: v1 has no per-organization Stripe customer namespace, so a subscription cannot be attributed to an organization.",
310
+ },
311
+ });
312
+ }
313
+
314
+ function invalidBody(reply: FastifyReply): void {
315
+ reply.status(400).send({
316
+ ok: false,
317
+ error: { code: "invalid_body", message: "Invalid request." },
318
+ });
319
+ }
320
+
321
+ function serverError(reply: FastifyReply): void {
322
+ // Never echo the raw Stripe/internal error — it may embed request params.
323
+ reply.status(500).send({
324
+ ok: false,
325
+ error: { code: "internal_error", message: "Something went wrong." },
326
+ });
327
+ }
328
+
329
+ /**
330
+ * The site-admin role set. `User.role` is a per-deployment String
331
+ * ("user" | "admin" | "owner") — DISTINCT from the org-membership `Role` enum
332
+ * the multi-tenant module uses. Named rather than compared inline so the next
333
+ * role change is one greppable edit per template; mirrored from
334
+ * `payment-link-route.template.ts:135` because these are copied-not-imported
335
+ * scaffold sources with nowhere to share a constant (AILK#3765).
336
+ */
337
+ const SITE_ADMIN_ROLES: ReadonlySet<string> = new Set(["admin", "owner"]);
338
+
339
+ /**
340
+ * Gate 1 — requires an authenticated, site-admin caller. `request.userId` is
341
+ * populated by `authPreHandlerPlugin` (see the Registration note above); this
342
+ * re-checks it defensively. Sends the 401/403 itself and returns `null` on
343
+ * denial; the caller's handler must `return` immediately when this returns
344
+ * `null`.
345
+ */
346
+ async function requireSiteAdmin(
347
+ request: FastifyRequest,
348
+ reply: FastifyReply,
349
+ ): Promise<string | null> {
350
+ const userId = request.userId;
351
+ if (!userId) {
352
+ unauthorized(reply);
353
+ return null;
354
+ }
355
+
356
+ let role: string | undefined;
357
+ try {
358
+ const user = await prisma.user.findUnique({
359
+ where: { id: userId },
360
+ select: { role: true },
361
+ });
362
+ role = user?.role;
363
+ } catch (err) {
364
+ // FAIL CLOSED. Never `catch {}` into the happy path.
365
+ //
366
+ // Unwrapped, a rejection here escapes to Fastify's default error handler,
367
+ // which serializes `error.message` back to the caller — contradicting this
368
+ // file's own no-raw-echo contract. It always denied; it denied while
369
+ // leaking.
370
+ request.log.error(
371
+ {
372
+ method: request.method,
373
+ path: request.url,
374
+ message: err instanceof Error ? err.message : "unknown",
375
+ },
376
+ "[subscriptions] role resolution failed; DENYING",
377
+ );
378
+ serverError(reply);
379
+ return null;
380
+ }
381
+
382
+ // A missing user row (deleted account, stale session) has no role -> denied.
383
+ if (!role || !SITE_ADMIN_ROLES.has(role)) {
384
+ forbidden(reply);
385
+ return null;
386
+ }
387
+
388
+ return userId;
389
+ }
390
+
391
+ /**
392
+ * Gate 2 — the tenancy guard. v1 resolves ONE deployment-wide
393
+ * `STRIPE_SECRET_KEY` and has no per-org Stripe customer namespace, so a
394
+ * subscription carries no organization attribution — this guard admits ONLY
395
+ * the deployment's own operator org (the fixed single-tenant sentinel
396
+ * `DEFAULT_ORG_ID`) and refuses everyone else, before any Stripe call.
397
+ * Mirrors `invoice-route.template.ts`'s `requireSingleTenantScope` verbatim.
398
+ */
399
+ async function requireSingleTenantScope(
400
+ request: FastifyRequest,
401
+ reply: FastifyReply,
402
+ ): Promise<boolean> {
403
+ const tenant = request.tenant;
404
+ if (!tenant) {
405
+ forbidden(reply);
406
+ return false;
407
+ }
408
+
409
+ if (tenant.organizationId !== DEFAULT_ORG_ID) {
410
+ tenancyUnsupported(reply);
411
+ return false;
412
+ }
413
+
414
+ return true;
415
+ }
416
+
417
+ const statusQuerySchema = z.object({
418
+ subscriptionId: z.string().min(1).max(255),
419
+ });
420
+
421
+ /**
422
+ * `subscriptionRoutes` — a single GET endpoint: retrieve-on-view by the
423
+ * subscription's own Stripe id (spec §5.3 escalation boundary — no
424
+ * subscription webhook is scaffolded at v1; mirrors
425
+ * `invoice-route.template.ts`'s `?invoiceId=` shape).
426
+ */
427
+ export const subscriptionRoutes: FastifyPluginAsync = async (fastify) => {
428
+ // GET /v1/commerce/subscriptions?subscriptionId=sub_...
429
+ fastify.get("/v1/commerce/subscriptions", async (request, reply) => {
430
+ if (!(await requireSiteAdmin(request, reply))) return;
431
+ if (!(await requireSingleTenantScope(request, reply))) return;
432
+
433
+ const query = statusQuerySchema.safeParse(request.query);
434
+ if (!query.success) return invalidBody(reply);
435
+
436
+ try {
437
+ loadStripeSurfaceConfig();
438
+ const status = await retrieveSubscriptionStatus({
439
+ subscriptionId: query.data.subscriptionId,
440
+ });
441
+ return reply.send({ ok: true, data: status });
442
+ } catch {
443
+ return serverError(reply);
444
+ }
445
+ });
446
+ };
@@ -2,8 +2,8 @@
2
2
 
3
3
  The 10 business categories `suggest-site-pages` knows about, with the
4
4
  ordered page list and primary schema.org type for each page. Schemas
5
- are drawn from the 45 types `@working-theory/schema` builds; nothing in this
6
- file should reference a type the package can't generate.
5
+ are drawn from the 60 page types `@working-theory/schema` builds; nothing in
6
+ this file should reference a type the package can't generate.
7
7
 
8
8
  ## How to read this file
9
9
 
@@ -275,7 +275,7 @@ operates as an entity.
275
275
 
276
276
  Every `Schema.orgType` referenced above is a member of the
277
277
  `SchemaOrgType` union exported by `@working-theory/schema` and has a builder
278
- function. The v1 catalog uses 21 of the 45 supported types:
278
+ function. The v1 catalog uses 21 of the 60 supported types:
279
279
 
280
280
  `HomePage`, `AboutPage`, `ContactPage`, `PrivacyPage`, `TermsPage`,
281
281
  `Organization`, `Person`, `ServicesList`, `Service`, `ProductsList`,
@@ -283,5 +283,5 @@ function. The v1 catalog uses 21 of the 45 supported types:
283
283
  `EventsList`, `CoursesList`, `Course`, `BlogList`, `BlogPosting`,
284
284
  `CaseStudiesList`.
285
285
 
286
- The remaining 24 types are available to `scaffold-page` and `add-schema`
286
+ The remaining 39 types are available to `scaffold-page` and `add-schema`
287
287
  for per-page use but don't drive category recommendations.
@@ -50,8 +50,67 @@ CONTENT_BACKEND=mdx
50
50
  # BETTER_AUTH_URL=http://localhost:3000
51
51
  # BETTER_AUTH_SECRET=
52
52
 
53
+ # ── Single-tenant operator pin (multi-tenant module, apps/api) ───────────────
54
+ # On a single-tenant deployment (the default Workspace exists and is the ONLY
55
+ # Workspace) the FIRST authenticated user to resolve a tenant context is
56
+ # bound as the sole owner (TOFU — trust on first use); every subsequent
57
+ # caller is denied. AILK_OPERATOR_EMAIL is OPTIONAL and pins that binding to
58
+ # one email address instead of "whoever logs in first" — set it when you
59
+ # want to guarantee only a specific operator account can ever become owner.
60
+ # Case-insensitive, trimmed. Leave unset for pure TOFU.
61
+ # AILK_OPERATOR_EMAIL=
62
+ #
63
+ # Belt-and-suspenders: the owner-pin denies any non-operator account at
64
+ # tenant resolution regardless, but for a single-tenant deployment you should
65
+ # also disable open sign-up / configure an OAuth allowlist on your identity
66
+ # provider (or Better Auth's `disableSignUp` option, if you wire it in) so a
67
+ # random account can't even authenticate in the first place.
68
+
53
69
  # ── Email provider (Resend default) ──────────────────────────────────────────
54
70
  # RESEND_API_KEY=
71
+ #
72
+ # Lead capture's owner-notification email — read by lead-routing.ts. Both
73
+ # must be set for the operator notification to send; either unset skips it.
74
+ # AILK_LEAD_NOTIFICATION_TO=
75
+ # AILK_LEAD_NOTIFICATION_FROM=
76
+ #
77
+ # Abandon-email's lead-facing sender (#3962). Optional — falls back to
78
+ # AILK_LEAD_NOTIFICATION_FROM when unset. Only needed for flows whose config
79
+ # declares an `abandonEmail` block with `mode:'enabled'`; `mode:'dark'` never
80
+ # sends and needs no sender.
81
+ # AILK_ABANDON_EMAIL_FROM=
82
+ #
83
+ # Deliverable-delivery's lead-facing sender (#3961). Optional — falls back to
84
+ # AILK_LEAD_NOTIFICATION_FROM when unset. Only needed for flows whose config
85
+ # declares a `deliverables` block; unset means the grant still mints but the
86
+ # email send is skipped (log-and-skip, never a lost grant).
87
+ # AILK_DELIVERABLE_EMAIL_FROM=
88
+ #
89
+ # Waitlist follow-up sequence's lead-facing sender (#4732). Optional — falls
90
+ # back to AILK_LEAD_NOTIFICATION_FROM when unset. Only needed for flows whose
91
+ # config declares a `followUps` block.
92
+ # AILK_FOLLOWUP_EMAIL_FROM=
93
+ #
94
+ # The Resend Contacts audience the #4732 follow-up sweep checks before
95
+ # sending — a lead found unsubscribed in this audience is suppressed.
96
+ # Optional: unset degrades to "cannot verify — proceed" (never to "block all
97
+ # sends"), since nothing in this repo syncs leads into a Resend audience by
98
+ # default.
99
+ # RESEND_AUDIENCE_ID=
100
+ #
101
+ # The public base URL this API is reachable at — used ONLY to build the
102
+ # deliverable-delivery download link (#3961 D5):
103
+ # `${AILK_PUBLIC_API_URL}/v1/deliverables/:grantId`. Unset means a relative
104
+ # link (base ""), which is not usable from an email client — set this in any
105
+ # deployment that uses the `deliverables` block.
106
+ # AILK_PUBLIC_API_URL=
107
+ #
108
+ # The apps/api origin @working-theory/api-client calls from
109
+ # apps-paid/marketing-web's SERVER (RSC + Server Actions) for the pricing
110
+ # recommender's /v1/flows/* routes (#4884). Unset means /pricing makes no
111
+ # request and serves the manifest content page. Format: https://<host> (no
112
+ # trailing slash). Server-only — never NEXT_PUBLIC_.
113
+ # AILK_API_URL=
55
114
 
56
115
  # ── Analytics ────────────────────────────────────────────────────────────────
57
116
  # All providers off by default. Set the env var(s) for any provider(s) you want.
@@ -188,8 +247,9 @@ CONTENT_BACKEND=mdx
188
247
  # Allowlist format: comma-separated user IDs (exact-match strings; no regex,
189
248
  # no wildcard). Empty entries between commas are trimmed.
190
249
  # Run `pnpm --filter @working-theory/database db:seed:test` to seed three test users
191
- # (test-e2e-user / test-e2e-admin / test-e2e-ownerdistinguished by the
192
- # Better Auth `role` field). The seed script itself refuses to run unless
250
+ # (user_00000000-0000-7000-8000-0000000000e1 / …e2 / …e3reserved-range
251
+ # prefixed ids per ADR-0031, distinguished by the Better Auth `role` field).
252
+ # The seed script itself refuses to run unless
193
253
  # AILK_TEST_AUTH_ENABLED === "true".
194
254
  #
195
255
  # AILK_TEST_AUTH_ENABLED=
@@ -203,3 +263,67 @@ CONTENT_BACKEND=mdx
203
263
  # LEAD_CAPTURE_FILE_DISABLED=false
204
264
  # Set to "true" to include the submitter's IP in metadata (default: off for privacy).
205
265
  # LEAD_CAPTURE_INCLUDE_IP=false
266
+
267
+ # ── Instant AEO score service (#4666) ─────────────────────────────────────────
268
+ # Total crawl wall-clock budget (ms) for POST /v1/aeo/score. Must leave
269
+ # cold-start headroom inside the deployment's function timeout (assumed
270
+ # >= 10s) and stay below the handler's 25s request guard — a value above
271
+ # 15000 is rejected on each request (logged once per request, falls back
272
+ # to the default below; there is no persistent startup phase to check
273
+ # this once in a serverless deployment).
274
+ # AEO_SCORE_BUDGET_MS=6000
275
+
276
+ # ── Scheduling (the `booker` section, #4416) ──────────────────────────────────
277
+ # Server-side only — read inside apps/api, never exposed to the browser.
278
+ # Unset is a supported state: the booker degrades to the provider's own
279
+ # booking page. Full notes in apps/api/.env.example.
280
+ # CAL_API_KEY=cal_live_xxx
281
+ # CAL_API_BASE_URL=https://api.cal.com
282
+ # SCHEDULING_PUBLIC_BASE_URL=https://cal.com
283
+ # SCHEDULING_PROVIDER=cal
284
+ #
285
+ # Which event the /schedule page books, as "<owner>/<event-slug>" (the path of
286
+ # the provider's own public booking page). Read server-side by apps/web; unset
287
+ # means the /schedule route 404s, exactly as if the scheduling module were not
288
+ # installed. Deliberately NOT a NEXT_PUBLIC_ variable — the whole scheduling
289
+ # config surface is server-read.
290
+ # SCHEDULING_CAL_LINK=owner/event-slug
291
+
292
+ # ── Recommender mode — the purchase hand-off (REL-013, #4762 / #4879) ────────
293
+ # A `recommending` flow's email + checkout moment spans two deployments:
294
+ # apps/api serves POST /v1/flows/:slug/save + /checkout and the Stripe
295
+ # webhook; apps/web runs the Server Action that calls them
296
+ # (app/[locale]/flows/[slug]/checkout.actions.ts). Who sets what, and what
297
+ # happens unset, is tabulated in docs/runbooks/site-configuration.md
298
+ # § Recommender mode. apps/api's three reads are declared above:
299
+ # DATABASE_URL (required — /save + /checkout answer 503 flows_not_configured
300
+ # without it), STRIPE_SECRET_KEY (sk_test_ only — the checkout route refuses
301
+ # any other prefix before its first side effect) and STRIPE_WEBHOOK_SECRET
302
+ # (whsec_ — the completed/expired stamp on the FlowCheckout buyer row).
303
+ #
304
+ # The public origin of THIS site (apps/web). The Stripe success/cancel return
305
+ # URLs are built from it (resolveBaseUrl(), apps/web/src/lib/routes.ts) as
306
+ # `${NEXT_PUBLIC_BASE_URL}/<locale>/flows/<slug>/results/<payload>?checkout=…`;
307
+ # it also drives the llms.txt / sitemap.xml / robots.txt canonical URLs. Unset
308
+ # falls back to https://ailk.example.com — a placeholder host a paying buyer
309
+ # would be returned to after payment. Set on every apps/web deployment that
310
+ # serves a recommending flow. Scheme + host only, no trailing slash.
311
+ # NEXT_PUBLIC_BASE_URL=https://www.example.com
312
+ #
313
+ # apps/api's origin as seen from apps/web's SERVER process — the api-client's
314
+ # base URL (getApiBase(), packages/api-client/src/http.ts) for every flow
315
+ # Server Action call (flows.get / submitStep / save / checkout). AILK_API_URL
316
+ # wins; NEXT_PUBLIC_API_URL is the fallback; both unset resolves to
317
+ # http://localhost:3001 (local dev only — off-localhost every save / checkout
318
+ # call fails as `network`). Distinct from NEXT_PUBLIC_AILK_API_URL
319
+ # (apps/web/.env.example), which BROWSER-side blocks post to directly. Set on
320
+ # every apps/web deployment whose API is not on localhost.
321
+ # AILK_API_URL=http://localhost:3001
322
+ #
323
+ # NEXT_PUBLIC_ENABLED_LOCALES — do NOT set by hand. Baked at build time by
324
+ # apps/web/next.config.mjs from project.yaml `product.locales` (falls back to
325
+ # "en" when project.yaml is absent) and read by middleware.ts, the sitemap
326
+ # route and the flow Server Actions / clients to validate the locale segment
327
+ # of every URL they build. Declared here so the read is not an undocumented
328
+ # dependency; a value set in the platform env is overridden by the build.
329
+ # NEXT_PUBLIC_ENABLED_LOCALES=en
@@ -14,7 +14,7 @@ docs/ Project notes (ADRs, architecture, planning)
14
14
  ```
15
15
 
16
16
  You consume `@working-theory/*` packages from npm. Source for those packages is at
17
- <https://github.com/tailwind-ai/ai-launch-kit>; you don't have it locally.
17
+ <https://github.com/working-theory-labs/ai-launch-kit>; you don't have it locally.
18
18
  Treat each `@working-theory/*` package as a third-party dependency — read its README
19
19
  or its types, don't go looking for the source.
20
20
 
@@ -108,9 +108,15 @@ checking what `@working-theory/schema` exports.
108
108
  These are non-negotiable in this repo. Don't ask, don't try to route
109
109
  around them.
110
110
 
111
- - **No live Stripe keys in code.** `sk_live_*` and `pk_live_*` belong in
112
- the deployment platform's secrets store, never in `.env`, source files,
113
- or commit messages. Use Stripe sandbox keys for everything in source.
111
+ - **No live Stripe keys in code.** The runtime guard is an ALLOW-LIST, not
112
+ a list of banned prefixes: it accepts only recognized test-mode keys
113
+ (`sk_test_`, `pk_test_`, `rk_test_`) and refuses everything else. Every
114
+ live key is therefore refused — a restricted key (`rk_live_`) and any
115
+ prefix Stripe introduces later included — because it is absent from the
116
+ allow-list, not because someone remembered to ban it. Live keys belong
117
+ in the deployment platform's secrets store, never in `.env`, source
118
+ files, or commit messages. Use Stripe sandbox keys for everything in
119
+ source.
114
120
  - **No secrets in commits.** API keys, license keys, database URLs — all
115
121
  go through `.env.local` (gitignored) for local dev and the deployment
116
122
  platform's secrets store for prod.
@@ -127,7 +133,7 @@ when:
127
133
 
128
134
  - An architecture invariant would have to bend.
129
135
  - A change to a shared `@working-theory/*` package would be needed (file an issue
130
- upstream at <https://github.com/tailwind-ai/ai-launch-kit>; don't fork).
136
+ upstream at <https://github.com/working-theory-labs/ai-launch-kit>; don't fork).
131
137
  - You're about to delete or move >5 files at once.
132
138
  - The acceptance criteria contradict each other or the existing code.
133
139
 
@@ -105,6 +105,6 @@ If you need to override a section component, swap its registration in the sectio
105
105
 
106
106
  Your project — `{{PROJECT_NAME}}`, owned by {{COMPANY_NAME}} ({{COMPANY_DOMAIN}}) — is yours. Pick whatever license suits you.
107
107
 
108
- The AI Launch Kit framework upstream is **Apache 2.0** — see <https://github.com/tailwind-ai/ai-launch-kit/blob/main/LICENSE>. Contributions are welcome at the upstream repo; start with the [contributing guide](https://github.com/tailwind-ai/ai-launch-kit/blob/main/CONTRIBUTING.md).
108
+ The AI Launch Kit framework upstream is **Apache 2.0** — see <https://github.com/working-theory-labs/ai-launch-kit/blob/main/LICENSE>. Contributions are welcome at the upstream repo; start with the [contributing guide](https://github.com/working-theory-labs/ai-launch-kit/blob/main/CONTRIBUTING.md).
109
109
 
110
110
  For the full handbook, examples, and Pro features: <https://ailaunchkit.ai>.