create-ailk 0.1.0 → 0.1.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 (605) hide show
  1. package/README.md +124 -3
  2. package/component-catalog.md +295 -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 +22 -14
  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 +75 -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 +231 -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 +547 -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 +742 -0
  115. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +424 -0
  116. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +364 -0
  117. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +566 -0
  118. package/templates/apps/api/src/routes/project-listings/get.ts +70 -0
  119. package/templates/apps/api/src/routes/project-listings/index.ts +105 -0
  120. package/templates/apps/api/src/routes/project-listings/list.ts +47 -0
  121. package/templates/apps/api/src/routes/project-listings/patch-copy.ts +222 -0
  122. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +97 -0
  123. package/templates/apps/api/src/routes/project-listings/patch.ts +136 -0
  124. package/templates/apps/api/src/routes/project-listings/public.ts +52 -0
  125. package/templates/apps/api/src/routes/project-listings/respond.ts +37 -0
  126. package/templates/apps/api/src/routes/project-listings/resume-token.ts +22 -0
  127. package/templates/apps/api/src/routes/project-listings/start.ts +98 -0
  128. package/templates/apps/api/src/routes/project-listings/submit.ts +122 -0
  129. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +490 -0
  130. package/templates/apps/api/src/routes/schedule/index.ts +249 -0
  131. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +385 -0
  132. package/templates/apps/api/src/routes/slack/actions.ts +177 -0
  133. package/templates/apps/api/src/routes/slack/index.ts +38 -0
  134. package/templates/apps/api/src/routes/test/auth/__tests__/session.test.ts +16 -6
  135. package/templates/apps/api/src/routes/test/auth/session.ts +8 -6
  136. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +577 -0
  137. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +78 -0
  138. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +41 -0
  139. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +64 -0
  140. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +42 -0
  141. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +84 -0
  142. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +119 -0
  143. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +238 -0
  144. package/templates/apps/api/src/routes/waitlist-signups/index.ts +101 -0
  145. package/templates/apps/api/src/routes/webhooks/README.md +35 -12
  146. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +315 -0
  147. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +270 -0
  148. package/templates/apps/api/src/routes/webhooks/stripe.ts +248 -20
  149. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +356 -0
  150. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +671 -0
  151. package/templates/apps/api/src/routes/workspaces/config.ts +125 -0
  152. package/templates/apps/api/src/routes/workspaces/index.ts +194 -0
  153. package/templates/apps/api/src/routes/workspaces/sessions.ts +218 -0
  154. package/templates/apps/api/src/server.ts +293 -7
  155. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +489 -0
  156. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +475 -0
  157. package/templates/apps/api/src/services/__tests__/answer-distributions.test.ts +132 -0
  158. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +523 -0
  159. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +359 -0
  160. package/templates/apps/api/src/services/__tests__/entitlements.test.ts +142 -0
  161. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +425 -0
  162. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +2840 -0
  163. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +393 -0
  164. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +8 -8
  165. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +560 -0
  166. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +406 -0
  167. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +290 -0
  168. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +736 -0
  169. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +475 -0
  170. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +340 -0
  171. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +983 -0
  172. package/templates/apps/api/src/services/__tests__/slack-notify.test.ts +278 -0
  173. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +71 -0
  174. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +379 -0
  175. package/templates/apps/api/src/services/__tests__/usage-aggregation.test.ts +941 -0
  176. package/templates/apps/api/src/services/__tests__/usage-credits.test.ts +588 -0
  177. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +768 -0
  178. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +1314 -0
  179. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +341 -0
  180. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +567 -0
  181. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +474 -0
  182. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +354 -0
  183. package/templates/apps/api/src/services/abandon-email.ts +307 -0
  184. package/templates/apps/api/src/services/aeo-score.ts +288 -0
  185. package/templates/apps/api/src/services/answer-distributions.ts +138 -0
  186. package/templates/apps/api/src/services/deliverable-fulfillment.ts +523 -0
  187. package/templates/apps/api/src/services/entitlements.ts +102 -0
  188. package/templates/apps/api/src/services/flow-aggregate.ts +232 -0
  189. package/templates/apps/api/src/services/flow-checkouts.ts +123 -0
  190. package/templates/apps/api/src/services/flow-engine.ts +1278 -0
  191. package/templates/apps/api/src/services/lead-promotion.ts +176 -0
  192. package/templates/apps/api/src/services/lead-routing.ts +3 -3
  193. package/templates/apps/api/src/services/listing-config.ts +102 -0
  194. package/templates/apps/api/src/services/listing-csv-sweep.ts +455 -0
  195. package/templates/apps/api/src/services/playbook-compile.ts +398 -0
  196. package/templates/apps/api/src/services/playbook-render.ts +263 -0
  197. package/templates/apps/api/src/services/project-listing-decision.ts +490 -0
  198. package/templates/apps/api/src/services/project-listing-flow.ts +426 -0
  199. package/templates/apps/api/src/services/project-listing-issue.ts +251 -0
  200. package/templates/apps/api/src/services/project-listings.ts +1124 -0
  201. package/templates/apps/api/src/services/recommender-capture.ts +835 -0
  202. package/templates/apps/api/src/services/scheduling/cal-provider.ts +392 -0
  203. package/templates/apps/api/src/services/scheduling/index.ts +63 -0
  204. package/templates/apps/api/src/services/scheduling/types.ts +88 -0
  205. package/templates/apps/api/src/services/slack-notify.ts +240 -0
  206. package/templates/apps/api/src/services/tenant-context.ts +451 -0
  207. package/templates/apps/api/src/services/usage-aggregation.ts +516 -0
  208. package/templates/apps/api/src/services/usage-credits.ts +340 -0
  209. package/templates/apps/api/src/services/usage-metering.ts +699 -0
  210. package/templates/apps/api/src/services/waitlist-dashboard.ts +947 -0
  211. package/templates/apps/api/src/services/waitlist-experiments.ts +213 -0
  212. package/templates/apps/api/src/services/waitlist-followup.ts +486 -0
  213. package/templates/apps/api/src/services/waitlist-scoring.ts +166 -0
  214. package/templates/apps/api/src/services/waitlist-signups.ts +165 -0
  215. package/templates/apps/api/src/vercel-handler.ts +165 -0
  216. package/templates/apps/api/tsconfig.json +0 -12
  217. package/templates/apps/mcp/CLAUDE.md +19 -9
  218. package/templates/apps/mcp/LICENSE +201 -0
  219. package/templates/apps/mcp/__tests__/create_page.test.ts +1 -0
  220. package/templates/apps/mcp/__tests__/delete_page.test.ts +1 -0
  221. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +105 -0
  222. package/templates/apps/mcp/__tests__/parity.test.ts +70 -6
  223. package/templates/apps/mcp/__tests__/request_callback.test.ts +4 -4
  224. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +242 -0
  225. package/templates/apps/mcp/__tests__/submit_lead.test.ts +4 -4
  226. package/templates/apps/mcp/__tests__/subscribe_newsletter.test.ts +4 -4
  227. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +47 -0
  228. package/templates/apps/mcp/__tests__/update_page.test.ts +1 -0
  229. package/templates/apps/mcp/package.json +6 -4
  230. package/templates/apps/mcp/src/config/modules.ts +37 -0
  231. package/templates/apps/mcp/src/server.ts +109 -18
  232. package/templates/apps/mcp/src/tools/capture_lead.ts +1 -1
  233. package/templates/apps/mcp/src/tools/create_booking.ts +58 -0
  234. package/templates/apps/mcp/src/tools/create_page.ts +1 -0
  235. package/templates/apps/mcp/src/tools/delete_page.ts +1 -0
  236. package/templates/apps/mcp/src/tools/get_event_meta.ts +55 -0
  237. package/templates/apps/mcp/src/tools/get_flow.ts +61 -0
  238. package/templates/apps/mcp/src/tools/index.ts +25 -0
  239. package/templates/apps/mcp/src/tools/list_availability.ts +67 -0
  240. package/templates/apps/mcp/src/tools/purchase.ts +3 -0
  241. package/templates/apps/mcp/src/tools/submit_flow_step.ts +83 -0
  242. package/templates/apps/mcp/src/tools/update_page.ts +1 -0
  243. package/templates/apps/mcp/tsconfig.json +1 -9
  244. package/templates/apps/web/.env.example +44 -0
  245. package/templates/apps/web/CLAUDE.md +28 -0
  246. package/templates/apps/web/LICENSE +201 -0
  247. package/templates/apps/web/__tests__/__mocks__/code-highlight.jest-stub.ts +24 -0
  248. package/templates/apps/web/__tests__/__mocks__/next-intl-server.ts +33 -0
  249. package/templates/apps/web/__tests__/__mocks__/next-intl.ts +18 -0
  250. package/templates/apps/web/__tests__/__mocks__/server-only.ts +13 -0
  251. package/templates/apps/web/__tests__/__mocks__/style-mock.js +4 -0
  252. package/templates/apps/web/__tests__/__mocks__/vercel-analytics-server.ts +9 -0
  253. package/templates/apps/web/__tests__/jest-support/import-meta-transformer.cjs +90 -0
  254. package/templates/apps/web/__tests__/setup.ts +22 -32
  255. package/templates/apps/web/app/.well-known/ai-plugin.json/route.ts +6 -3
  256. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +8 -2
  257. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +260 -0
  258. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +263 -0
  259. package/templates/apps/web/app/[locale]/(authed)/account/sign-out-button.tsx +39 -0
  260. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +54 -0
  261. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +96 -0
  262. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +309 -0
  263. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +149 -0
  264. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +273 -0
  265. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/gate.test.tsx +172 -0
  266. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +171 -0
  267. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +179 -0
  268. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +116 -0
  269. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ForbiddenState.tsx +24 -0
  270. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +66 -0
  271. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +61 -0
  272. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +114 -0
  273. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +139 -0
  274. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +85 -0
  275. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +67 -0
  276. package/templates/apps/web/app/[locale]/(authed)/waitlist/format.ts +111 -0
  277. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +179 -0
  278. package/templates/apps/web/app/[locale]/about/page.tsx +10 -1
  279. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +6 -1
  280. package/templates/apps/web/app/[locale]/careers/[slug]/page.tsx +9 -0
  281. package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +3 -3
  282. package/templates/apps/web/app/[locale]/careers/page.tsx +9 -1
  283. package/templates/apps/web/app/[locale]/case-studies/[slug]/page.tsx +9 -0
  284. package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +3 -3
  285. package/templates/apps/web/app/[locale]/case-studies/page.tsx +13 -1
  286. package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +11 -1
  287. package/templates/apps/web/app/[locale]/clients/page.tsx +9 -1
  288. package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +21 -1
  289. package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +188 -0
  290. package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +242 -0
  291. package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +29 -0
  292. package/templates/apps/web/app/[locale]/contact/__tests__/intake-analytics.test.ts +51 -0
  293. package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +39 -15
  294. package/templates/apps/web/app/[locale]/contact/contact.actions.ts +21 -3
  295. package/templates/apps/web/app/[locale]/contact/intake-analytics.ts +49 -0
  296. package/templates/apps/web/app/[locale]/contact/intake-script.ts +66 -0
  297. package/templates/apps/web/app/[locale]/contact/page.tsx +14 -4
  298. package/templates/apps/web/app/[locale]/courses/[slug]/page.tsx +13 -1
  299. package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +3 -3
  300. package/templates/apps/web/app/[locale]/courses/page.tsx +9 -1
  301. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +1 -0
  302. package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +11 -1
  303. package/templates/apps/web/app/[locale]/downloads/page.tsx +13 -1
  304. package/templates/apps/web/app/[locale]/events/[slug]/page.tsx +9 -0
  305. package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +12 -2
  306. package/templates/apps/web/app/[locale]/events/page.tsx +9 -1
  307. package/templates/apps/web/app/[locale]/faq/page.tsx +10 -1
  308. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +265 -0
  309. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +445 -0
  310. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +341 -0
  311. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +287 -0
  312. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +302 -0
  313. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +128 -0
  314. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +254 -0
  315. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +144 -0
  316. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +108 -0
  317. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +532 -0
  318. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +121 -0
  319. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +457 -0
  320. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +396 -0
  321. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +114 -0
  322. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +52 -0
  323. package/templates/apps/web/app/[locale]/how-to/[slug]/page.tsx +13 -1
  324. package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +1 -1
  325. package/templates/apps/web/app/[locale]/integrations/[slug]/page.tsx +9 -0
  326. package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +3 -3
  327. package/templates/apps/web/app/[locale]/integrations/page.tsx +13 -1
  328. package/templates/apps/web/app/[locale]/launch-service/page.tsx +14 -1
  329. package/templates/apps/web/app/[locale]/layout.tsx +115 -16
  330. package/templates/apps/web/app/[locale]/page.tsx +10 -1
  331. package/templates/apps/web/app/[locale]/partners/[slug]/page.tsx +9 -0
  332. package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +3 -3
  333. package/templates/apps/web/app/[locale]/partners/page.tsx +9 -1
  334. package/templates/apps/web/app/[locale]/products/[slug]/page.tsx +9 -0
  335. package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +3 -3
  336. package/templates/apps/web/app/[locale]/products/page.tsx +9 -1
  337. package/templates/apps/web/app/[locale]/projects/[slug]/page.tsx +13 -1
  338. package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +10 -0
  339. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +110 -0
  340. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +96 -0
  341. package/templates/apps/web/app/[locale]/projects/page.tsx +13 -1
  342. package/templates/apps/web/app/[locale]/resources/[slug]/page.tsx +9 -0
  343. package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +12 -2
  344. package/templates/apps/web/app/[locale]/resources/page.tsx +13 -1
  345. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +117 -0
  346. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +96 -0
  347. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +92 -0
  348. package/templates/apps/web/app/[locale]/schedule/page.tsx +82 -0
  349. package/templates/apps/web/app/[locale]/services/[slug]/page.tsx +9 -0
  350. package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +3 -3
  351. package/templates/apps/web/app/[locale]/services/page.tsx +9 -1
  352. package/templates/apps/web/app/[locale]/sign-in/__tests__/next-path.test.ts +31 -0
  353. package/templates/apps/web/app/[locale]/sign-in/__tests__/page.test.tsx +152 -0
  354. package/templates/apps/web/app/[locale]/sign-in/next-path.ts +0 -0
  355. package/templates/apps/web/app/[locale]/sign-in/page.tsx +83 -0
  356. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +123 -0
  357. package/templates/apps/web/app/[locale]/software/[slug]/page.tsx +13 -1
  358. package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +1 -1
  359. package/templates/apps/web/app/[locale]/tech-articles/[slug]/page.tsx +13 -1
  360. package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +1 -1
  361. package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +11 -1
  362. package/templates/apps/web/app/[locale]/testimonials/page.tsx +13 -1
  363. package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +93 -0
  364. package/templates/apps/web/app/[locale]/thanks/[slug]/page.tsx +48 -0
  365. package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +11 -1
  366. package/templates/apps/web/app/[locale]/use-cases/page.tsx +13 -1
  367. package/templates/apps/web/app/[locale]/whitepapers/[slug]/page.tsx +13 -1
  368. package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +1 -1
  369. package/templates/apps/web/app/globals.css +35 -0
  370. package/templates/apps/web/app/llms.txt/route.ts +2 -1
  371. package/templates/apps/web/app/og/route.tsx +10 -2
  372. package/templates/apps/web/auth.ts +8 -0
  373. package/templates/apps/web/components/sign-out-trigger.tsx +42 -0
  374. package/templates/apps/web/content/en/blog/README.txt +25 -0
  375. package/templates/apps/web/content/en/docs/index.mdx +28 -0
  376. package/templates/apps/web/jest.config.cjs +84 -27
  377. package/templates/apps/web/lib/__tests__/site-brand.test.ts +119 -0
  378. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +499 -0
  379. package/templates/apps/web/lib/account-config.ts +13 -0
  380. package/templates/apps/web/lib/server-api.ts +85 -0
  381. package/templates/apps/web/lib/site-brand.tsx +118 -0
  382. package/templates/apps/web/messages/en.json +275 -7
  383. package/templates/apps/web/middleware.ts +11 -3
  384. package/templates/apps/web/next.config.mjs +8 -2
  385. package/templates/apps/web/package.json +16 -7
  386. package/templates/apps/web/postcss.config.mjs +23 -0
  387. package/templates/apps/web/public/.well-known/security.txt +3 -3
  388. package/templates/apps/web/src/lib/routes.ts +15 -2
  389. package/templates/apps/web/tsconfig.json +1 -16
  390. package/templates/content/_site.mdx +24 -25
  391. package/templates/content/en/AboutPage/about.mdx +24 -14
  392. package/templates/content/en/ContactPage/contact.mdx +23 -14
  393. package/templates/content/en/FAQPage/faq.mdx +10 -392
  394. package/templates/content/en/HomePage/home.mdx +14 -285
  395. package/templates/database/CHANGELOG.md +382 -0
  396. package/templates/database/LICENSE +201 -0
  397. package/templates/database/README.md +3 -1
  398. package/templates/database/__tests__/flows-listing-diagnostic.test.ts +392 -0
  399. package/templates/database/__tests__/flows-presets.test.ts +173 -0
  400. package/templates/database/__tests__/prefixed-ids.db.test.ts +146 -0
  401. package/templates/database/__tests__/seed.test.ts +88 -0
  402. package/templates/database/__tests__/user-cascade.test.ts +19 -4
  403. package/templates/database/auth/schema.prisma +521 -0
  404. package/templates/database/config/schema.prisma +234 -0
  405. package/templates/database/content/schema.prisma +263 -0
  406. package/templates/database/flows/listing-diagnostic.ts +182 -0
  407. package/templates/database/flows/presets.ts +315 -0
  408. package/templates/database/inbox/__tests__/schema.test.ts +4 -0
  409. package/templates/database/inbox/schema.prisma +476 -3
  410. package/templates/database/jest.config.cjs +8 -0
  411. package/templates/database/migrations/20260712215647_add_multi_tenant_tenancy/migration.sql +201 -0
  412. package/templates/database/migrations/20260713143320_reanchor_site_config_to_workspace/migration.sql +67 -0
  413. package/templates/database/migrations/20260713174000_add_audit_logs/migration.sql +46 -0
  414. package/templates/database/migrations/20260713190000_drop_telemetry_records/migration.sql +25 -0
  415. package/templates/database/migrations/20260713192000_add_uuidv7_function/migration.sql +49 -0
  416. package/templates/database/migrations/20260713192429_prefixed_uuid_entity_ids/migration.sql +197 -0
  417. package/templates/database/migrations/20260713200000_add_org_billing/migration.sql +32 -0
  418. package/templates/database/migrations/20260714000000_add_lead_promotion_fields/migration.sql +22 -0
  419. package/templates/database/migrations/20260714010000_add_usage_billing_content_models/migration.sql +151 -0
  420. package/templates/database/migrations/20260714020000_add_usage_billing_auth_config_models/migration.sql +223 -0
  421. package/templates/database/migrations/20260714030000_credit_balance_non_negative/migration.sql +22 -0
  422. package/templates/database/migrations/20260714030000_widen_usage_billing_report_aggregates/migration.sql +32 -0
  423. package/templates/database/migrations/20260810233516_add_flow_engine_models/migration.sql +40 -0
  424. package/templates/database/migrations/20260811020000_add_flow_session_abandon_email_columns/migration.sql +4 -0
  425. package/templates/database/migrations/20260811030000_add_flow_session_abandon_email_index/migration.sql +2 -0
  426. package/templates/database/migrations/20260811040000_add_flow_session_segment/migration.sql +2 -0
  427. package/templates/database/migrations/20260811050000_add_deliverable_models/migration.sql +29 -0
  428. package/templates/database/migrations/20260812000000_add_deliverable_grant_send_columns/migration.sql +6 -0
  429. package/templates/database/migrations/20260902222948_add_waitlist_experiment_models/migration.sql +39 -0
  430. package/templates/database/migrations/20260902224500_add_waitlist_id_format_checks/migration.sql +24 -0
  431. package/templates/database/migrations/20260903004112_add_flow_session_flags_aeo_score/migration.sql +3 -0
  432. package/templates/database/migrations/20260903010000_add_playbook_instance/migration.sql +33 -0
  433. package/templates/database/migrations/20260904000000_add_waitlist_followup_send/migration.sql +30 -0
  434. package/templates/database/migrations/20260905230000_add_flow_checkout/migration.sql +47 -0
  435. package/templates/database/migrations/20260906000000_add_flow_checkout_idempotency/migration.sql +16 -0
  436. package/templates/database/migrations/20260907120000_add_project_listing/migration.sql +47 -0
  437. package/templates/database/migrations/20260907130000_add_project_listing_slack_columns/migration.sql +10 -0
  438. package/templates/database/migrations/20260907180000_add_listing_csv_send/migration.sql +26 -0
  439. package/templates/database/migrations/20260909120000_project_listings_platform/migration.sql +90 -0
  440. package/templates/database/migrations/20260909180000_listing_draft_and_fields/migration.sql +174 -0
  441. package/templates/database/migrations/20260910120000_listing_public_site_key/migration.sql +14 -0
  442. package/templates/database/migrations/20260911120000_listing_copy_edit_audit/migration.sql +25 -0
  443. package/templates/database/ops/schema.prisma +27 -0
  444. package/templates/database/package.json +42 -4
  445. package/templates/database/prisma.config.ts +1 -1
  446. package/templates/database/schema.prisma +19 -435
  447. package/templates/database/scripts/db-generate-locked.sh +0 -0
  448. package/templates/database/seed-test.ts +14 -4
  449. package/templates/database/seed.ts +148 -0
  450. package/templates/database/tenancy.ts +45 -0
  451. package/templates/database/tsconfig.flows.json +15 -0
  452. package/templates/database/tsconfig.json +7 -1
  453. package/templates/database/tsconfig.tenancy.json +14 -0
  454. package/templates/package.json +30 -55
  455. package/templates/pnpm-workspace.yaml +0 -3
  456. package/templates/project.yaml +1 -1
  457. package/templates/tsconfig.json +1 -37
  458. package/templates/apps/web/__tests__/auth/adversarial/cookie-attributes.test.ts +0 -94
  459. package/templates/apps/web/__tests__/auth/adversarial/oauth-state-parameter.test.ts +0 -75
  460. package/templates/apps/web/__tests__/auth/adversarial/redirect-uri-allowlist.test.ts +0 -82
  461. package/templates/apps/web/__tests__/auth/env-helpers.test.ts +0 -168
  462. package/templates/apps/web/__tests__/authority-list-routes.test.tsx +0 -208
  463. package/templates/apps/web/__tests__/better-stack.test.ts +0 -66
  464. package/templates/apps/web/__tests__/blog-dir-resolution.test.ts +0 -87
  465. package/templates/apps/web/__tests__/blog-source-populated.test.ts +0 -130
  466. package/templates/apps/web/__tests__/data-testid-coverage.test.tsx +0 -60
  467. package/templates/apps/web/__tests__/google-analytics.test.tsx +0 -104
  468. package/templates/apps/web/__tests__/i18n/middleware.test.ts +0 -119
  469. package/templates/apps/web/__tests__/i18n/routing.test.ts +0 -114
  470. package/templates/apps/web/__tests__/i18n/translations.test.ts +0 -191
  471. package/templates/apps/web/__tests__/json-ld.test.ts +0 -98
  472. package/templates/apps/web/__tests__/knowledge-events-routes.test.tsx +0 -580
  473. package/templates/apps/web/__tests__/layout-stealth.test.ts +0 -39
  474. package/templates/apps/web/__tests__/lighthouse-fixtures/improvement-image-shrink.tsx +0 -103
  475. package/templates/apps/web/__tests__/lighthouse-fixtures/override-fixture.md +0 -114
  476. package/templates/apps/web/__tests__/lighthouse-fixtures/regression-bloat.tsx +0 -94
  477. package/templates/apps/web/__tests__/page.test.tsx +0 -230
  478. package/templates/apps/web/__tests__/products-services-phase1.test.tsx +0 -321
  479. package/templates/apps/web/__tests__/projects-routes.test.tsx +0 -392
  480. package/templates/apps/web/__tests__/resources-downloads-integrations-phase2.test.tsx +0 -475
  481. package/templates/apps/web/__tests__/routes/ai-plugin-json.test.ts +0 -53
  482. package/templates/apps/web/__tests__/routes/blog-route.test.tsx +0 -191
  483. package/templates/apps/web/__tests__/routes/llms-txt.test.ts +0 -123
  484. package/templates/apps/web/__tests__/routes/robots-txt.test.ts +0 -52
  485. package/templates/apps/web/__tests__/routes/sitemap-xml.test.ts +0 -78
  486. package/templates/apps/web/__tests__/routes/well-known-mcp.test.ts +0 -203
  487. package/templates/apps/web/__tests__/section-routes.test.tsx +0 -693
  488. package/templates/apps/web/__tests__/security-headers.test.ts +0 -201
  489. package/templates/apps/web/__tests__/sentry-config.test.ts +0 -116
  490. package/templates/apps/web/__tests__/sitemap-locales.test.ts +0 -415
  491. package/templates/apps/web/__tests__/standalone-informational-routes.test.tsx +0 -844
  492. package/templates/apps/web/app/[locale]/__tests__/layout.test.tsx +0 -120
  493. package/templates/apps/web/app/[locale]/__tests__/page-renderer-migration.test.tsx +0 -202
  494. package/templates/apps/web/app/__tests__/error-boundaries.test.tsx +0 -152
  495. package/templates/apps/web/content/en/blog/index.mdx +0 -15
  496. package/templates/apps/web/content/en/blog/launching-ailk.mdx +0 -20
  497. package/templates/apps/web/content/en/docs/getting-started.mdx +0 -30
  498. package/templates/content/.gitkeep +0 -0
  499. package/templates/content/CLAUDE.md +0 -85
  500. package/templates/content/ar/HomePage/home.mdx +0 -123
  501. package/templates/content/ar/_site.mdx +0 -35
  502. package/templates/content/de/_site.mdx +0 -29
  503. package/templates/content/en/.gitkeep +0 -0
  504. package/templates/content/en/AboutPage/about.schema.json +0 -82
  505. package/templates/content/en/BookCall/book-call.mdx +0 -19
  506. package/templates/content/en/BookCall/book-call.schema.json +0 -121
  507. package/templates/content/en/BreadcrumbList/site-breadcrumbs.mdx +0 -20
  508. package/templates/content/en/CareersList/careers.mdx +0 -24
  509. package/templates/content/en/CaseStudiesList/case-studies.mdx +0 -22
  510. package/templates/content/en/CaseStudiesList/showcase.mdx +0 -8
  511. package/templates/content/en/CaseStudy/ailk-aeo-rollout.mdx +0 -32
  512. package/templates/content/en/CaseStudy/schema-builder-migration.mdx +0 -41
  513. package/templates/content/en/ClientsList/clients.mdx +0 -38
  514. package/templates/content/en/ContactPage/contact.schema.json +0 -100
  515. package/templates/content/en/ContactPoint/primary.mdx +0 -14
  516. package/templates/content/en/Course/aeo-fundamentals.mdx +0 -37
  517. package/templates/content/en/Course/schema-builder-workshop.mdx +0 -62
  518. package/templates/content/en/CoursesList/courses.mdx +0 -22
  519. package/templates/content/en/DownloadsList/downloads.mdx +0 -22
  520. package/templates/content/en/Event/aeo-office-hours-may-2026.mdx +0 -39
  521. package/templates/content/en/Event/ailk-launch-day-2026.mdx +0 -35
  522. package/templates/content/en/EventsList/events.mdx +0 -26
  523. package/templates/content/en/FAQPage/faq.schema.json +0 -231
  524. package/templates/content/en/GlossaryList/glossary.mdx +0 -19
  525. package/templates/content/en/GlossaryList/glossary.schema.json +0 -300
  526. package/templates/content/en/HomePage/home.schema.json +0 -88
  527. package/templates/content/en/HomePage/launch-service.mdx +0 -475
  528. package/templates/content/en/HomePage/launch-service.schema.json +0 -138
  529. package/templates/content/en/HowTo/add-mdx-content.mdx +0 -38
  530. package/templates/content/en/HowTo/configure-aeo-scoring.mdx +0 -35
  531. package/templates/content/en/HowTo/set-up-json-ld.mdx +0 -36
  532. package/templates/content/en/Integration/github-actions.mdx +0 -31
  533. package/templates/content/en/Integration/openai-api.mdx +0 -26
  534. package/templates/content/en/Integration/vercel.mdx +0 -32
  535. package/templates/content/en/IntegrationsList/integrations.mdx +0 -22
  536. package/templates/content/en/JobPosting/content-strategist.mdx +0 -46
  537. package/templates/content/en/JobPosting/developer-advocate.mdx +0 -47
  538. package/templates/content/en/JobPosting/senior-fullstack-engineer.mdx +0 -57
  539. package/templates/content/en/LandingPage/aeo-audit.mdx +0 -19
  540. package/templates/content/en/LandingPage/aeo-audit.schema.json +0 -92
  541. package/templates/content/en/LandingPage/agency-waitlist.mdx +0 -340
  542. package/templates/content/en/LandingPage/agency-waitlist.schema.json +0 -152
  543. package/templates/content/en/LandingPage/developers.mdx +0 -19
  544. package/templates/content/en/LandingPage/developers.schema.json +0 -109
  545. package/templates/content/en/LandingPage/for-agencies.mdx +0 -390
  546. package/templates/content/en/LandingPage/for-agencies.schema.json +0 -107
  547. package/templates/content/en/LandingPage/for-ai-search-agencies.mdx +0 -417
  548. package/templates/content/en/LandingPage/for-ai-search-agencies.schema.json +0 -105
  549. package/templates/content/en/LandingPage/for-businesses.mdx +0 -19
  550. package/templates/content/en/LandingPage/for-businesses.schema.json +0 -96
  551. package/templates/content/en/LandingPage/platform.mdx +0 -369
  552. package/templates/content/en/LandingPage/platform.schema.json +0 -166
  553. package/templates/content/en/LandingPage/vs-ai-website-builders.mdx +0 -19
  554. package/templates/content/en/LandingPage/vs-ai-website-builders.schema.json +0 -102
  555. package/templates/content/en/LandingPage/vs-wordpress.mdx +0 -19
  556. package/templates/content/en/LandingPage/vs-wordpress.schema.json +0 -53
  557. package/templates/content/en/NewsletterSignup/newsletter.mdx +0 -236
  558. package/templates/content/en/NewsletterSignup/newsletter.schema.json +0 -91
  559. package/templates/content/en/Organization/site-owner.mdx +0 -16
  560. package/templates/content/en/Partner/prisma.mdx +0 -31
  561. package/templates/content/en/Partner/supabase.mdx +0 -30
  562. package/templates/content/en/Partner/vercel.mdx +0 -28
  563. package/templates/content/en/PartnersList/partners.mdx +0 -22
  564. package/templates/content/en/Person/founder.mdx +0 -18
  565. package/templates/content/en/Pricing/pricing.mdx +0 -538
  566. package/templates/content/en/Pricing/pricing.schema.json +0 -209
  567. package/templates/content/en/PrivacyPage/privacy.mdx +0 -165
  568. package/templates/content/en/PrivacyPage/privacy.schema.json +0 -64
  569. package/templates/content/en/Product/ailk.mdx +0 -19
  570. package/templates/content/en/Product/ailk.schema.json +0 -94
  571. package/templates/content/en/Product/api-launch-kit.mdx +0 -26
  572. package/templates/content/en/Product/launch-kit.mdx +0 -28
  573. package/templates/content/en/ProductsList/products.mdx +0 -26
  574. package/templates/content/en/Project/ailk-phase-2.mdx +0 -54
  575. package/templates/content/en/ProjectsList/projects.mdx +0 -25
  576. package/templates/content/en/ResourceDetail/aeo-scoring-guide.mdx +0 -27
  577. package/templates/content/en/ResourceDetail/content-model-whitepaper.mdx +0 -28
  578. package/templates/content/en/ResourceDetail/schema-org-quick-reference.mdx +0 -39
  579. package/templates/content/en/ResourcesHub/resources.mdx +0 -22
  580. package/templates/content/en/Service/consulting.mdx +0 -22
  581. package/templates/content/en/Service/implementation.mdx +0 -30
  582. package/templates/content/en/Service/launch-service.mdx +0 -232
  583. package/templates/content/en/Service/launch.mdx +0 -228
  584. package/templates/content/en/ServicesList/services.mdx +0 -84
  585. package/templates/content/en/SoftwareProduct/ailk-aeo.mdx +0 -44
  586. package/templates/content/en/SoftwareProduct/ailk-content-adapters.mdx +0 -47
  587. package/templates/content/en/SoftwareProduct/ailk-schema.mdx +0 -49
  588. package/templates/content/en/TeamList/team.mdx +0 -63
  589. package/templates/content/en/TeamMember/founder.mdx +0 -72
  590. package/templates/content/en/TermsPage/terms.mdx +0 -19
  591. package/templates/content/en/TermsPage/terms.schema.json +0 -99
  592. package/templates/content/en/TestimonialsList/testimonials.mdx +0 -44
  593. package/templates/content/en/UseCasesList/use-cases.mdx +0 -30
  594. package/templates/content/en/Whitepaper/ax-first-product-development.mdx +0 -27
  595. package/templates/content/en/Whitepaper/open-core-commercial-strategy.mdx +0 -27
  596. package/templates/content/en/Whitepaper/structured-data-for-answer-engines.mdx +0 -31
  597. package/templates/content/en-XA/AboutPage/about.mdx +0 -8
  598. package/templates/content/en-XA/FAQPage/faq.mdx +0 -8
  599. package/templates/content/en-XA/HomePage/home.mdx +0 -8
  600. package/templates/content/es/_site.mdx +0 -30
  601. package/templates/content/fr/_site.mdx +0 -30
  602. package/templates/content/ja/_site.mdx +0 -30
  603. package/templates/content/pt-BR/AboutPage/about.mdx +0 -8
  604. package/templates/content/pt-BR/FAQPage/faq.mdx +0 -8
  605. package/templates/content/pt-BR/HomePage/home.mdx +0 -23
@@ -1,130 +0,0 @@
1
- /**
2
- * Integration test: Fumadocs corpus populated after U1 content migration + U2 codegen wire-up.
3
- *
4
- * Requires `pnpm --filter @working-theory/web build` to have run first — the build
5
- * step invokes `fumadocs-mdx/next` codegen and writes `apps/web/.source/`.
6
- *
7
- * fumadocs-core and fumadocs-mdx are ESM-only packages incompatible with
8
- * ts-jest's CJS mode. We mock them and assert on the observable data-layer
9
- * contract: the codegen output file exists, the source swap is in place,
10
- * and the content/en/blog/ directory has the expected MDX entries (which the
11
- * loader will serve at runtime). Per AC-U2-3, data-layer assertions are
12
- * sufficient when a render harness cannot easily be wired up.
13
- *
14
- * i18n Phase 3 S2 (2026-05-18) moved blog content from `content/blog/` to
15
- * `content/en/blog/`.
16
- */
17
-
18
- import * as fs from "fs";
19
- import * as path from "path";
20
-
21
- // Root of apps/web — two directories up from __tests__/
22
- const webRoot = path.resolve(__dirname, "..");
23
- const nextConfigSrc = fs.readFileSync(
24
- path.join(webRoot, "next.config.mjs"),
25
- "utf8",
26
- );
27
- const sourceSrc = fs.readFileSync(
28
- path.join(webRoot, "lib", "source.ts"),
29
- "utf8",
30
- );
31
-
32
- describe("U2 codegen wire-up — data-layer assertions (AC-U2-3)", () => {
33
- describe("AC-U2-1: next.config.mjs wraps with createMDX()", () => {
34
- it("next.config.mjs imports createMDX from fumadocs-mdx/next", () => {
35
- expect(nextConfigSrc).toContain('from "fumadocs-mdx/next"');
36
- expect(nextConfigSrc).toContain("createMDX");
37
- });
38
-
39
- it("next.config.mjs exports createMDX()(nextConfig)", () => {
40
- expect(nextConfigSrc).toContain("createMDX()(nextConfig)");
41
- });
42
-
43
- it("next.config.mjs preserves reactStrictMode: true", () => {
44
- expect(nextConfigSrc).toContain("reactStrictMode: true");
45
- });
46
- });
47
-
48
- describe("AC-U2-2: lib/source.ts swap completed", () => {
49
- it("lib/source.ts does not contain the Phase 0 placeholder arrays", () => {
50
- expect(sourceSrc).not.toContain("const docs: Parameters");
51
- expect(sourceSrc).not.toContain("const meta: Parameters");
52
- // Match the specific placeholder assignment pattern, not any occurrence of "= []"
53
- expect(sourceSrc).not.toMatch(/\bconst (?:docs|meta)[^=]+=\s*\[\]/);
54
- });
55
-
56
- it("lib/source.ts imports blog from @/.source/server", () => {
57
- // fumadocs-mdx@15 codegen splits output into server.ts / browser.ts /
58
- // dynamic.ts (no top-level index.ts). The `blog` collection lives on
59
- // the server entry point.
60
- expect(sourceSrc).toContain('@/.source/server"');
61
- expect(sourceSrc).toContain("blog");
62
- });
63
-
64
- it("lib/source.ts no longer contains Phase 0 skeleton comments", () => {
65
- expect(sourceSrc).not.toContain("Phase 0 skeleton");
66
- expect(sourceSrc).not.toContain("Phase 2 swap target");
67
- });
68
- });
69
-
70
- describe("AC-U2-4: codegen output exists and is gitignored", () => {
71
- it(".source/server.ts exists after build (codegen ran)", () => {
72
- // fumadocs-mdx@15 emits server.ts / browser.ts / dynamic.ts. server.ts
73
- // is the canonical entry the loader consumes — its existence proves
74
- // codegen ran.
75
- const sourcePath = path.join(webRoot, ".source", "server.ts");
76
- expect(fs.existsSync(sourcePath)).toBe(true);
77
- });
78
-
79
- it(".source/ is listed in .gitignore", () => {
80
- // Root .gitignore covers apps/web/.source/
81
- const rootGitignore = fs.readFileSync(
82
- path.resolve(webRoot, "..", "..", ".gitignore"),
83
- "utf8",
84
- );
85
- expect(rootGitignore).toContain("apps/web/.source/");
86
- });
87
- });
88
-
89
- describe("AC-U2-3: apps/web/content/en/blog/ corpus populated (issue #1459 path divergence)", () => {
90
- // Issue #1459 (2026-05-25): blog content moved from <repo-root>/content/en/blog/
91
- // to apps/web/content/en/blog/ (per-app content directory convention).
92
- const blogDir = path.resolve(webRoot, "content", "en", "blog");
93
-
94
- it("apps/web/content/en/blog/ directory exists", () => {
95
- expect(fs.existsSync(blogDir)).toBe(true);
96
- });
97
-
98
- it("apps/web/content/en/blog/ contains at least one MDX file (corpus non-empty)", () => {
99
- const files = fs.readdirSync(blogDir).filter((f) => f.endsWith(".mdx"));
100
- expect(files.length).toBeGreaterThanOrEqual(1);
101
- });
102
-
103
- it("launching-ailk.mdx exists in apps/web/content/en/blog/", () => {
104
- const filePath = path.join(blogDir, "launching-ailk.mdx");
105
- expect(fs.existsSync(filePath)).toBe(true);
106
- });
107
-
108
- it("launching-ailk.mdx contains the expected title in frontmatter", () => {
109
- const content = fs.readFileSync(
110
- path.join(blogDir, "launching-ailk.mdx"),
111
- "utf8",
112
- );
113
- expect(content).toContain("title: Launching AI Launch Kit");
114
- });
115
- });
116
-
117
- describe("BLOG_EMPTY_SENTINEL export", () => {
118
- it("page.tsx exports BLOG_EMPTY_SENTINEL as a non-empty string (smoke import check)", () => {
119
- // Reading the file directly avoids loading RSC-incompatible next/navigation
120
- // under jsdom. The sentinel value is a plain string constant.
121
- const src = fs.readFileSync(
122
- path.join(webRoot, "app", "[locale]", "blog", "[[...slug]]", "page.tsx"),
123
- "utf8",
124
- );
125
- const match = src.match(/BLOG_EMPTY_SENTINEL\s*=\s*"([^"]+)"/);
126
- expect(match).not.toBeNull();
127
- expect(match![1]!.length).toBeGreaterThan(0);
128
- });
129
- });
130
- });
@@ -1,60 +0,0 @@
1
- /**
2
- * data-testid coverage tests for issue #518.
3
- *
4
- * Verifies that the five high-leverage user-flow elements carry the expected
5
- * data-testid attributes. These selectors serve as stable anchors for the
6
- * post-OSS e2e test suite (docs/internal/roadmaps/e2e-testing-roadmap.md §5.2).
7
- */
8
-
9
- import { render, screen } from "@testing-library/react";
10
- import * as React from "react";
11
-
12
- // ---------------------------------------------------------------------------
13
- // ContactForm — submit button
14
- // ---------------------------------------------------------------------------
15
-
16
- // Mocks required by ContactForm (uses React 19 useFormState / useFormStatus)
17
- jest.mock("react-dom", () => ({
18
- ...jest.requireActual("react-dom"),
19
- useFormState: jest.fn((_action: unknown, init: unknown) => [init, jest.fn()]),
20
- useFormStatus: jest.fn(() => ({ pending: false })),
21
- }));
22
-
23
- // eslint-disable-next-line no-restricted-syntax -- test-seam mock required to isolate ContactForm from server action; tracked in #507
24
- jest.mock("../app/[locale]/contact/contact.actions", () => ({
25
- captureLeadAction: jest.fn(),
26
- }));
27
-
28
- import { ContactForm } from "../app/[locale]/contact/ContactForm";
29
-
30
- describe("ContactForm — data-testid coverage", () => {
31
- it("submit button has data-testid=contact-submit", () => {
32
- render(<ContactForm />);
33
- const btn = screen.getByTestId("contact-submit");
34
- expect(btn).toBeInTheDocument();
35
- expect(btn.tagName).toBe("BUTTON");
36
- expect(btn).toHaveAttribute("type", "submit");
37
- });
38
- });
39
-
40
- // ---------------------------------------------------------------------------
41
- // SiteNav — nav link testids
42
- // ---------------------------------------------------------------------------
43
-
44
- import { SiteNav } from "../src/components/SiteNav";
45
-
46
- describe("SiteNav — data-testid coverage", () => {
47
- it("home link has data-testid=nav-home-link", () => {
48
- render(<SiteNav />);
49
- const link = screen.getByTestId("nav-home-link");
50
- expect(link).toBeInTheDocument();
51
- expect(link).toHaveAttribute("href", "/");
52
- });
53
-
54
- it("contact link has data-testid=nav-contact-link", () => {
55
- render(<SiteNav />);
56
- const link = screen.getByTestId("nav-contact-link");
57
- expect(link).toBeInTheDocument();
58
- expect(link).toHaveAttribute("href", "/contact");
59
- });
60
- });
@@ -1,104 +0,0 @@
1
- // Tests for the env-driven Google Analytics component (#886).
2
- // When NEXT_PUBLIC_GOOGLE_TAG_ID is set, the component renders two script
3
- // elements: an external gtag.js loader and an inline init script. When unset,
4
- // the component renders nothing. STEALTH state is intentionally not consulted
5
- // — Sam's call per #886.
6
- //
7
- // next/script is mocked locally because Next's runtime script-injection
8
- // machinery isn't available under jsdom; the mock renders a plain <script>
9
- // element so RTL queries can observe the output. Mocking a third-party module
10
- // (not an internal one) keeps the test within the no-restricted-syntax rule.
11
-
12
- import * as React from "react";
13
- import { render } from "@testing-library/react";
14
-
15
- jest.mock("next/script", () => ({
16
- __esModule: true,
17
- default: ({
18
- src,
19
- id,
20
- children,
21
- }: {
22
- src?: string;
23
- id?: string;
24
- strategy?: string;
25
- children?: React.ReactNode;
26
- }): React.ReactElement =>
27
- React.createElement(
28
- "script",
29
- { "data-testid": id ? `script-${id}` : "script-external", src, id },
30
- children,
31
- ),
32
- }));
33
-
34
- describe("apps/web — GoogleAnalytics (#886)", () => {
35
- const originalTagId = process.env.NEXT_PUBLIC_GOOGLE_TAG_ID;
36
- const originalStealth = process.env.STEALTH;
37
-
38
- afterEach(() => {
39
- if (originalTagId === undefined) {
40
- delete process.env.NEXT_PUBLIC_GOOGLE_TAG_ID;
41
- } else {
42
- process.env.NEXT_PUBLIC_GOOGLE_TAG_ID = originalTagId;
43
- }
44
- if (originalStealth === undefined) {
45
- delete process.env.STEALTH;
46
- } else {
47
- process.env.STEALTH = originalStealth;
48
- }
49
- jest.resetModules();
50
- });
51
-
52
- it("renders gtag.js loader + init script when NEXT_PUBLIC_GOOGLE_TAG_ID is set", () => {
53
- jest.isolateModules(() => {
54
- process.env.NEXT_PUBLIC_GOOGLE_TAG_ID = "G-MJFBVBHCK9";
55
-
56
- const { GoogleAnalytics } = require("../components/google-analytics");
57
- const { container } = render(React.createElement(GoogleAnalytics));
58
- const scripts = container.querySelectorAll("script");
59
- expect(scripts).toHaveLength(2);
60
- const external = scripts[0]!;
61
- expect(external.getAttribute("src")).toBe(
62
- "https://www.googletagmanager.com/gtag/js?id=G-MJFBVBHCK9",
63
- );
64
- const init = scripts[1]!;
65
- expect(init.getAttribute("id")).toBe("ga-init");
66
- expect(init.textContent).toContain("gtag('config', 'G-MJFBVBHCK9')");
67
- expect(init.textContent).toContain(
68
- "window.dataLayer = window.dataLayer || []",
69
- );
70
- });
71
- });
72
-
73
- it("renders nothing when NEXT_PUBLIC_GOOGLE_TAG_ID is unset", () => {
74
- jest.isolateModules(() => {
75
- delete process.env.NEXT_PUBLIC_GOOGLE_TAG_ID;
76
-
77
- const { GoogleAnalytics } = require("../components/google-analytics");
78
- const { container } = render(React.createElement(GoogleAnalytics));
79
- expect(container.querySelectorAll("script")).toHaveLength(0);
80
- expect(container.textContent).toBe("");
81
- });
82
- });
83
-
84
- it("renders nothing when NEXT_PUBLIC_GOOGLE_TAG_ID is empty string", () => {
85
- jest.isolateModules(() => {
86
- process.env.NEXT_PUBLIC_GOOGLE_TAG_ID = "";
87
-
88
- const { GoogleAnalytics } = require("../components/google-analytics");
89
- const { container } = render(React.createElement(GoogleAnalytics));
90
- expect(container.querySelectorAll("script")).toHaveLength(0);
91
- });
92
- });
93
-
94
- it("renders scripts regardless of STEALTH=true (analytics not gated on stealth)", () => {
95
- jest.isolateModules(() => {
96
- process.env.NEXT_PUBLIC_GOOGLE_TAG_ID = "G-TESTABC123";
97
- process.env.STEALTH = "true";
98
-
99
- const { GoogleAnalytics } = require("../components/google-analytics");
100
- const { container } = render(React.createElement(GoogleAnalytics));
101
- expect(container.querySelectorAll("script")).toHaveLength(2);
102
- });
103
- });
104
- });
@@ -1,119 +0,0 @@
1
- /**
2
- * @jest-environment node
3
- */
4
- // i18n S2 — Middleware locale detection priority chain tests (#949)
5
- //
6
- // Tests that the composed next-intl + security-headers middleware preserves
7
- // security headers on every response, and that the middleware config exports
8
- // the expected matcher pattern excluding static assets.
9
- //
10
- // Note: next-intl middleware runs in the Next.js Edge runtime and relies on
11
- // Next.js internals (URL rewriting, response piping) that cannot be fully
12
- // exercised in a jest/node environment. We test:
13
- // 1. Security headers are preserved (S16 preservation guarantee)
14
- // 2. Matcher config excludes _next/static, _next/image, favicon.ico
15
- // 3. The module initialises without throwing (locales read correctly)
16
- //
17
- // Locale list is now read from NEXT_PUBLIC_ENABLED_LOCALES env var (baked at
18
- // build time by next.config.mjs). Tests control the env var directly rather
19
- // than mocking lib/locales (which no longer runs in the Edge middleware path).
20
-
21
- import { NextRequest, NextResponse } from "next/server";
22
-
23
- // Mock next-intl middleware so it returns a plain NextResponse (no Edge runtime needed).
24
- jest.mock("next-intl/middleware", () => {
25
- return function createMiddleware() {
26
- return function (_req: unknown) {
27
- return NextResponse.next();
28
- };
29
- };
30
- });
31
-
32
- import { middleware, config } from "../../middleware";
33
-
34
- function makeRequest(
35
- path: string,
36
- headers: Record<string, string> = {},
37
- ): NextRequest {
38
- return new NextRequest(new URL(path, "http://localhost:3000"), {
39
- headers: { host: "localhost:3000", ...headers },
40
- });
41
- }
42
-
43
- describe("i18n middleware — security headers preserved (S16 contract)", () => {
44
- it("applies Content-Security-Policy to every response", async () => {
45
- const res = await middleware(makeRequest("/"));
46
- expect(res.headers.get("Content-Security-Policy")).not.toBeNull();
47
- expect(res.headers.get("Content-Security-Policy")).toContain(
48
- "frame-ancestors 'none'",
49
- );
50
- });
51
-
52
- it("applies Strict-Transport-Security", async () => {
53
- const res = await middleware(makeRequest("/"));
54
- expect(res.headers.get("Strict-Transport-Security")).toContain(
55
- "max-age=63072000",
56
- );
57
- });
58
-
59
- it("applies X-Content-Type-Options: nosniff", async () => {
60
- const res = await middleware(makeRequest("/"));
61
- expect(res.headers.get("X-Content-Type-Options")).toBe("nosniff");
62
- });
63
-
64
- it("applies X-Frame-Options: DENY", async () => {
65
- const res = await middleware(makeRequest("/"));
66
- expect(res.headers.get("X-Frame-Options")).toBe("DENY");
67
- });
68
-
69
- it("applies Referrer-Policy", async () => {
70
- const res = await middleware(makeRequest("/"));
71
- expect(res.headers.get("Referrer-Policy")).toBe(
72
- "strict-origin-when-cross-origin",
73
- );
74
- });
75
-
76
- it("applies Permissions-Policy", async () => {
77
- const res = await middleware(makeRequest("/about"));
78
- expect(res.headers.get("Permissions-Policy")).toBeTruthy();
79
- });
80
- });
81
-
82
- describe("i18n middleware — locale config reads from NEXT_PUBLIC_ENABLED_LOCALES", () => {
83
- it("middleware initialises without throwing when env var is unset (falls back to [en])", async () => {
84
- // NEXT_PUBLIC_ENABLED_LOCALES is not set in test env — middleware falls back to ["en"].
85
- const res = await middleware(makeRequest("/"));
86
- expect(res).toBeDefined();
87
- expect(res.headers.get("Content-Security-Policy")).not.toBeNull();
88
- });
89
-
90
- it("middleware does not throw for two-locale config", async () => {
91
- // Middleware reads env at module load time. This test verifies the
92
- // middleware still handles requests regardless of locale path.
93
- const res = await middleware(makeRequest("/es/about"));
94
- expect(res).toBeDefined();
95
- expect(res.headers.get("Content-Security-Policy")).not.toBeNull();
96
- });
97
- });
98
-
99
- describe("i18n middleware — matcher config", () => {
100
- it("excludes _next/static from matcher", () => {
101
- const matcher = Array.isArray(config.matcher)
102
- ? config.matcher.join(",")
103
- : config.matcher;
104
- expect(matcher).toContain("_next");
105
- });
106
-
107
- it("excludes favicon.ico from matcher", () => {
108
- const matcher = Array.isArray(config.matcher)
109
- ? config.matcher.join(",")
110
- : config.matcher;
111
- expect(matcher).toContain("favicon.ico");
112
- });
113
-
114
- it("matcher is a string (single pattern) or array", () => {
115
- expect(
116
- typeof config.matcher === "string" || Array.isArray(config.matcher),
117
- ).toBe(true);
118
- });
119
- });
@@ -1,114 +0,0 @@
1
- /**
2
- * @jest-environment node
3
- */
4
- // i18n S2 — Routing integration tests (#949)
5
- //
6
- // Tests the 5 AC routing cases from the spec §5.1 at the middleware level.
7
- // Full curl integration verification (live HTTP server) is in the PR checklist.
8
- //
9
- // What we test in unit scope:
10
- // - Middleware does not throw for any of the 5 locale path shapes
11
- // - Security headers are always applied (S16 preservation)
12
- // - NEXT_PUBLIC_ENABLED_LOCALES controls the locale list (env var, baked at build)
13
- // - Middleware config has the correct matcher (static assets excluded)
14
-
15
- import { NextRequest, NextResponse } from "next/server";
16
-
17
- // Stub next-intl so we can test middleware composition without Edge runtime.
18
- // next-intl's actual locale detection is tested by the library's own suite;
19
- // our job is to verify the composition (security headers applied, no throws).
20
- jest.mock("next-intl/middleware", () => {
21
- return function createMiddleware() {
22
- return function (_req: unknown) {
23
- return NextResponse.next();
24
- };
25
- };
26
- });
27
-
28
- function makeRequest(
29
- path: string,
30
- headers: Record<string, string> = {},
31
- ): NextRequest {
32
- return new NextRequest(new URL(path, "http://localhost:3000"), {
33
- headers: { host: "localhost:3000", ...headers },
34
- });
35
- }
36
-
37
- // AC-1: curl http://localhost:3000/ returns 200 with English content + lang=en
38
- describe("AC-1: bare default-locale URL / → middleware OK", () => {
39
- it("does not throw for bare / path", async () => {
40
- const { middleware } = await import("../../middleware");
41
- const res = await middleware(makeRequest("/"));
42
- expect(res.status).not.toBe(500);
43
- });
44
-
45
- it("security headers present on /", async () => {
46
- const { middleware } = await import("../../middleware");
47
- const res = await middleware(makeRequest("/"));
48
- expect(res.headers.get("Content-Security-Policy")).not.toBeNull();
49
- });
50
- });
51
-
52
- // AC-2: curl http://localhost:3000/en/ returns 200 with English content + lang=en
53
- describe("AC-2: /en/ prefix → middleware OK", () => {
54
- it("does not throw for /en/ path", async () => {
55
- const { middleware } = await import("../../middleware");
56
- const res = await middleware(makeRequest("/en/"));
57
- expect(res.status).not.toBe(500);
58
- });
59
-
60
- it("security headers present on /en/", async () => {
61
- const { middleware } = await import("../../middleware");
62
- const res = await middleware(makeRequest("/en/"));
63
- expect(res.headers.get("Strict-Transport-Security")).toBeTruthy();
64
- });
65
- });
66
-
67
- // AC-3: curl -H "Accept-Language: es" http://localhost:3000/ → 200 English fallback
68
- describe("AC-3: Accept-Language: es on / → no error (English fallback)", () => {
69
- it("does not throw with es accept-language when only en is enabled", async () => {
70
- const { middleware } = await import("../../middleware");
71
- const res = await middleware(
72
- makeRequest("/", { "accept-language": "es,en;q=0.8" }),
73
- );
74
- expect(res.status).not.toBe(500);
75
- });
76
-
77
- it("security headers still applied with es accept-language", async () => {
78
- const { middleware } = await import("../../middleware");
79
- const res = await middleware(makeRequest("/", { "accept-language": "es" }));
80
- expect(res.headers.get("Content-Security-Policy")).not.toBeNull();
81
- });
82
- });
83
-
84
- // AC-4: /es/ with locales:[en,es] → 200 English fallback + lang=es
85
- // NEXT_PUBLIC_ENABLED_LOCALES is baked at build time; in tests we verify
86
- // middleware handles the path without throwing (actual locale routing is
87
- // tested via next-intl's own suite and the curl checklist).
88
- describe("AC-4: /es/ path → middleware handles without throwing", () => {
89
- it("does not throw for /es/ path", async () => {
90
- const { middleware } = await import("../../middleware");
91
- const res = await middleware(makeRequest("/es/"));
92
- expect(res.status).not.toBe(500);
93
- });
94
- });
95
-
96
- // AC-5: /es/ with locales:[en] only → 404 from next-intl's router
97
- // In unit test scope, we verify the middleware doesn't throw — the actual 404
98
- // from the Next.js router (not middleware layer) is verified via curl checklist.
99
- describe("AC-5: /es/ with locales:[en] only → middleware does not throw", () => {
100
- it("middleware processes /es/ request without throwing", async () => {
101
- const { middleware } = await import("../../middleware");
102
- // Middleware itself doesn't throw — Next.js router returns 404
103
- const res = await middleware(makeRequest("/es/"));
104
- expect(res).toBeDefined();
105
- });
106
-
107
- it("locale list falls back to [en] when NEXT_PUBLIC_ENABLED_LOCALES unset", () => {
108
- // In test env, NEXT_PUBLIC_ENABLED_LOCALES is not set → middleware uses ["en"].
109
- // This mirrors the next.config.mjs fallback behavior at build time.
110
- const localeEnv = process.env.NEXT_PUBLIC_ENABLED_LOCALES;
111
- const locales = (localeEnv ?? "en").split(",");
112
- expect(locales).toEqual(["en"]);
113
- });
114
- });