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
@@ -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
- });