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,82 +0,0 @@
1
- /**
2
- * @file redirect-uri-allowlist.test.ts (S13 adversarial)
3
- *
4
- * Redirect-URI allow-listing. Better Auth uses `trustedOrigins` (option on
5
- * the betterAuth() config) to gate which origins may receive callback /
6
- * redirect responses. The OAuth-provider-side `redirect_uri` registration
7
- * is handled at the provider (e.g. Google Cloud Console); Better Auth's
8
- * defence-in-depth layer is `trustedOrigins`.
9
- *
10
- * Asserted on the AILK-owned config surface:
11
- * - `auth.options.trustedOrigins` is either undefined (Better Auth
12
- * defaults to baseURL only) or a non-empty list / function. NEVER
13
- * `["*"]` or an empty list.
14
- * - If the config sets `trustedOrigins` as an array, every entry is a
15
- * concrete origin (not a wildcard).
16
- * - `baseURL` (Better Auth's primary callback origin) is sourced from
17
- * `BETTER_AUTH_URL`, not hardcoded.
18
- *
19
- * Strategy: mock `better-auth` so the real ESM module never loads.
20
- */
21
-
22
- jest.mock("better-auth", () => ({
23
- betterAuth: (options: unknown) => ({ options }),
24
- }));
25
-
26
- jest.mock("better-auth/adapters/prisma", () => ({
27
- prismaAdapter: () => ({ id: "mock-prisma-adapter" }),
28
- }));
29
-
30
- jest.mock("better-auth/next-js", () => ({
31
- nextCookies: () => ({ id: "next-cookies" }),
32
- }));
33
-
34
- import { createAuth } from "../../../auth";
35
-
36
- // Stub PrismaClient — avoids a real DB connection at module-load time.
37
- // Injected via createAuth({ prisma }) instead of jest.mock("../../../lib/prisma-for-auth").
38
- // Pattern mirrors apps-paid/dashboard-web/auth.ts DI.
39
- const stubPrisma = {} as NonNullable<NonNullable<Parameters<typeof createAuth>[0]>["prisma"]>;
40
- const auth = createAuth({ prisma: stubPrisma });
41
-
42
- type TrustedOriginsOption =
43
- | string[]
44
- | ((req: unknown) => string[] | Promise<string[]>)
45
- | undefined;
46
-
47
- type AuthOptions = {
48
- trustedOrigins?: TrustedOriginsOption;
49
- baseURL?: string;
50
- };
51
-
52
- // eslint-disable-next-line no-restricted-syntax -- the mocked betterAuth returns { options: <unknown> }; the real type carries no public options shape, so a double assertion is the narrow path to inspect the AILK-owned config object.
53
- const options = (auth as unknown as { options: AuthOptions }).options;
54
-
55
- function isWildcardEntry(origin: string): boolean {
56
- return origin === "*" || origin.includes("*");
57
- }
58
-
59
- describe("S13 adversarial — redirect URI allow-list", () => {
60
- it("does NOT configure trustedOrigins as an empty array (would block all callbacks)", () => {
61
- const trusted = options.trustedOrigins;
62
- if (Array.isArray(trusted)) {
63
- expect(trusted.length).toBeGreaterThan(0);
64
- }
65
- });
66
-
67
- it("does NOT include a wildcard entry in trustedOrigins", () => {
68
- const trusted = options.trustedOrigins;
69
- if (Array.isArray(trusted)) {
70
- for (const origin of trusted) {
71
- expect(isWildcardEntry(origin)).toBe(false);
72
- }
73
- }
74
- });
75
-
76
- it("sources baseURL from the BETTER_AUTH_URL env var (no hardcoded production origin)", () => {
77
- const baseURL = options.baseURL;
78
- if (baseURL !== undefined) {
79
- expect(baseURL).toBe(process.env.BETTER_AUTH_URL);
80
- }
81
- });
82
- });
@@ -1,168 +0,0 @@
1
- /**
2
- * Tests for auth env-introspection helpers.
3
- *
4
- * Covers:
5
- * 1. isAuthRequired — AILK_AUTH_REQUIRED flag behavior
6
- * 2. getActiveProviders — provider list derived from env var presence
7
- *
8
- * Both helpers must be importable without next-auth being configured,
9
- * so they live in apps/web/lib/auth-env.ts (no next-auth import).
10
- */
11
-
12
- const ORIGINAL_ENV = process.env;
13
-
14
- beforeEach(() => {
15
- // Fresh env copy before each test; prevents cross-test leakage.
16
- process.env = { ...ORIGINAL_ENV };
17
- // Clear all auth-related vars to ensure clean isolation.
18
- delete process.env.AILK_AUTH_REQUIRED;
19
- delete process.env.GOOGLE_CLIENT_ID;
20
- delete process.env.GOOGLE_CLIENT_SECRET;
21
- delete process.env.GITHUB_CLIENT_ID;
22
- delete process.env.GITHUB_CLIENT_SECRET;
23
- delete process.env.AUTH_EMAIL_SERVER;
24
- delete process.env.AUTH_EMAIL_FROM;
25
- });
26
-
27
- afterAll(() => {
28
- process.env = ORIGINAL_ENV;
29
- });
30
-
31
- // ── isAuthRequired ────────────────────────────────────────────────────────────
32
-
33
- describe("isAuthRequired", () => {
34
- let isAuthRequired: () => boolean;
35
-
36
- beforeEach(() => {
37
- jest.resetModules();
38
-
39
- ({ isAuthRequired } = require("../../lib/auth-env"));
40
- });
41
-
42
- it("returns false when AILK_AUTH_REQUIRED is unset (OSS default)", () => {
43
- expect(isAuthRequired()).toBe(false);
44
- });
45
-
46
- it("returns false when AILK_AUTH_REQUIRED=0", () => {
47
- process.env.AILK_AUTH_REQUIRED = "0";
48
- expect(isAuthRequired()).toBe(false);
49
- });
50
-
51
- it("returns false when AILK_AUTH_REQUIRED=false", () => {
52
- process.env.AILK_AUTH_REQUIRED = "false";
53
- expect(isAuthRequired()).toBe(false);
54
- });
55
-
56
- it("returns true when AILK_AUTH_REQUIRED=1", () => {
57
- process.env.AILK_AUTH_REQUIRED = "1";
58
- expect(isAuthRequired()).toBe(true);
59
- });
60
-
61
- it("returns true when AILK_AUTH_REQUIRED=true", () => {
62
- process.env.AILK_AUTH_REQUIRED = "true";
63
- expect(isAuthRequired()).toBe(true);
64
- });
65
- });
66
-
67
- // ── getActiveProviderIds ──────────────────────────────────────────────────────
68
-
69
- describe("getActiveProviderIds", () => {
70
- let getActiveProviderIds: () => string[];
71
-
72
- beforeEach(() => {
73
- jest.resetModules();
74
-
75
- ({ getActiveProviderIds } = require("../../lib/auth-env"));
76
- });
77
-
78
- it("returns an empty array when no auth env vars are set", () => {
79
- expect(getActiveProviderIds()).toEqual([]);
80
- });
81
-
82
- it("activates 'google' only when both GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET are set", () => {
83
- process.env.GOOGLE_CLIENT_ID = "gid";
84
- process.env.GOOGLE_CLIENT_SECRET = "gsecret";
85
- expect(getActiveProviderIds()).toEqual(["google"]);
86
- });
87
-
88
- it("does NOT activate 'google' when only GOOGLE_CLIENT_ID is set", () => {
89
- process.env.GOOGLE_CLIENT_ID = "gid";
90
- expect(getActiveProviderIds()).not.toContain("google");
91
- });
92
-
93
- it("does NOT activate 'google' when only GOOGLE_CLIENT_SECRET is set", () => {
94
- process.env.GOOGLE_CLIENT_SECRET = "gsecret";
95
- expect(getActiveProviderIds()).not.toContain("google");
96
- });
97
-
98
- it("activates 'github' only when both GITHUB_CLIENT_ID and GITHUB_CLIENT_SECRET are set", () => {
99
- process.env.GITHUB_CLIENT_ID = "ghid";
100
- process.env.GITHUB_CLIENT_SECRET = "ghsecret";
101
- expect(getActiveProviderIds()).toEqual(["github"]);
102
- });
103
-
104
- it("does NOT activate 'github' when only one GitHub var is set", () => {
105
- process.env.GITHUB_CLIENT_ID = "ghid";
106
- expect(getActiveProviderIds()).not.toContain("github");
107
- });
108
-
109
- it("activates 'email' only when both AUTH_EMAIL_SERVER and AUTH_EMAIL_FROM are set", () => {
110
- process.env.AUTH_EMAIL_SERVER = "smtp://localhost";
111
- process.env.AUTH_EMAIL_FROM = "noreply@example.com";
112
- expect(getActiveProviderIds()).toEqual(["email"]);
113
- });
114
-
115
- it("does NOT activate 'email' when only AUTH_EMAIL_SERVER is set", () => {
116
- process.env.AUTH_EMAIL_SERVER = "smtp://localhost";
117
- expect(getActiveProviderIds()).not.toContain("email");
118
- });
119
-
120
- it("activates all three providers when all env vars are set", () => {
121
- process.env.GOOGLE_CLIENT_ID = "gid";
122
- process.env.GOOGLE_CLIENT_SECRET = "gsecret";
123
- process.env.GITHUB_CLIENT_ID = "ghid";
124
- process.env.GITHUB_CLIENT_SECRET = "ghsecret";
125
- process.env.AUTH_EMAIL_SERVER = "smtp://localhost";
126
- process.env.AUTH_EMAIL_FROM = "noreply@example.com";
127
- expect(getActiveProviderIds()).toEqual(
128
- expect.arrayContaining(["google", "github", "email"]),
129
- );
130
- expect(getActiveProviderIds()).toHaveLength(3);
131
- });
132
-
133
- it("activates only the providers whose vars are complete (mixed partial env)", () => {
134
- // Google: complete; GitHub: partial (missing secret); Email: complete
135
- process.env.GOOGLE_CLIENT_ID = "gid";
136
- process.env.GOOGLE_CLIENT_SECRET = "gsecret";
137
- process.env.GITHUB_CLIENT_ID = "ghid";
138
- // GITHUB_CLIENT_SECRET intentionally omitted
139
- process.env.AUTH_EMAIL_SERVER = "smtp://localhost";
140
- process.env.AUTH_EMAIL_FROM = "noreply@example.com";
141
- const active = getActiveProviderIds();
142
- expect(active).toContain("google");
143
- expect(active).toContain("email");
144
- expect(active).not.toContain("github");
145
- });
146
- });
147
-
148
- // ── Zero-auth boot (no env vars at all) ─────────────────────────────────────
149
-
150
- describe("zero-env auth boot", () => {
151
- beforeEach(() => {
152
- jest.resetModules();
153
- });
154
-
155
- it("getActiveProviderIds returns empty array — site boots without throwing", () => {
156
-
157
- const { getActiveProviderIds } = require("../../lib/auth-env");
158
- expect(() => getActiveProviderIds()).not.toThrow();
159
- expect(getActiveProviderIds()).toEqual([]);
160
- });
161
-
162
- it("isAuthRequired returns false — site boots without throwing", () => {
163
-
164
- const { isAuthRequired } = require("../../lib/auth-env");
165
- expect(() => isAuthRequired()).not.toThrow();
166
- expect(isAuthRequired()).toBe(false);
167
- });
168
- });
@@ -1,208 +0,0 @@
1
- /**
2
- * Authority list route tests: ClientsList + TestimonialsList.
3
- * These are list-only routes (no [slug]/ detail) mirroring the products/services pattern.
4
- * getMdxSource is injected via optional props on each page component.
5
- */
6
-
7
- import { render, screen } from "@testing-library/react";
8
-
9
- jest.mock("next-mdx-remote/rsc", () => ({
10
- __esModule: true,
11
- MDXRemote: ({ source }: { source: string }): React.ReactElement => (
12
- <div data-testid="mdx-rendered">{source}</div>
13
- ),
14
- }));
15
-
16
- import * as React from "react";
17
-
18
- import ClientsPage from "../app/[locale]/clients/page";
19
- import TestimonialsPage from "../app/[locale]/testimonials/page";
20
-
21
- import type { getMdxSource } from "@working-theory/page-renderer";
22
-
23
- type GetMdxSource = typeof getMdxSource;
24
-
25
- async function renderAsync(
26
- element: Promise<React.ReactElement> | React.ReactElement,
27
- ): Promise<void> {
28
- const resolved = await element;
29
- render(resolved);
30
- }
31
-
32
- describe("apps/web — Authority list routes (ClientsList + TestimonialsList)", () => {
33
- // ── /clients ──────────────────────────────────────────────────────────────
34
-
35
- describe("/clients", () => {
36
- describe("with ClientsList MDX present", () => {
37
- const stubGetMdxSource = jest
38
- .fn<ReturnType<GetMdxSource>, Parameters<GetMdxSource>>()
39
- .mockResolvedValue({
40
- frontmatter: {
41
- type: "ClientsList",
42
- slug: "clients",
43
- title: "Clients",
44
- description: "Organizations using AI Launch Kit.",
45
- },
46
- body: "# Clients\n\nBody.",
47
- });
48
-
49
- beforeEach(() => stubGetMdxSource.mockClear());
50
-
51
- it("renders the h1 from MDX frontmatter", async () => {
52
- await renderAsync(
53
- ClientsPage({
54
- params: Promise.resolve({ locale: "en" }),
55
- getMdxSource: stubGetMdxSource,
56
- }),
57
- );
58
- expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
59
- "Clients",
60
- );
61
- });
62
-
63
- it("emits a JSON-LD script tag with @type CollectionPage", async () => {
64
- await renderAsync(
65
- ClientsPage({
66
- params: Promise.resolve({ locale: "en" }),
67
- getMdxSource: stubGetMdxSource,
68
- }),
69
- );
70
- const script = screen.getByTestId("json-ld");
71
- expect(script.getAttribute("type")).toBe("application/ld+json");
72
- const parsed = JSON.parse(script.innerHTML) as { "@type": string };
73
- expect(parsed["@type"]).toBe("CollectionPage");
74
- });
75
-
76
- it("calls getMdxSource with ClientsList type, clients slug, and locale", async () => {
77
- await renderAsync(
78
- ClientsPage({
79
- params: Promise.resolve({ locale: "en" }),
80
- getMdxSource: stubGetMdxSource,
81
- }),
82
- );
83
- expect(stubGetMdxSource).toHaveBeenCalledWith(
84
- "ClientsList",
85
- "clients",
86
- "en",
87
- );
88
- });
89
-
90
- it("renders the description and MDX body", async () => {
91
- await renderAsync(
92
- ClientsPage({
93
- params: Promise.resolve({ locale: "en" }),
94
- getMdxSource: stubGetMdxSource,
95
- }),
96
- );
97
- expect(screen.getByTestId("clients-description")).toHaveTextContent(
98
- "Organizations using AI Launch Kit.",
99
- );
100
- expect(screen.getByTestId("mdx-body")).toBeInTheDocument();
101
- });
102
- });
103
-
104
- describe("with no ClientsList MDX (empty state)", () => {
105
- const stubGetMdxSource = jest
106
- .fn<ReturnType<GetMdxSource>, Parameters<GetMdxSource>>()
107
- .mockResolvedValue(null);
108
-
109
- it("renders the fallback sentinel without crashing", async () => {
110
- await renderAsync(
111
- ClientsPage({
112
- params: Promise.resolve({ locale: "en" }),
113
- getMdxSource: stubGetMdxSource,
114
- }),
115
- );
116
- expect(screen.getByTestId("section-empty-state")).toBeInTheDocument();
117
- });
118
- });
119
- });
120
-
121
- // ── /testimonials ─────────────────────────────────────────────────────────
122
-
123
- describe("/testimonials", () => {
124
- describe("with TestimonialsList MDX present", () => {
125
- const stubGetMdxSource = jest
126
- .fn<ReturnType<GetMdxSource>, Parameters<GetMdxSource>>()
127
- .mockResolvedValue({
128
- frontmatter: {
129
- type: "TestimonialsList",
130
- slug: "testimonials",
131
- title: "Testimonials",
132
- description: "What teams say about AI Launch Kit.",
133
- },
134
- body: "# Testimonials\n\nBody.",
135
- });
136
-
137
- beforeEach(() => stubGetMdxSource.mockClear());
138
-
139
- it("renders the h1 from MDX frontmatter", async () => {
140
- await renderAsync(
141
- TestimonialsPage({
142
- params: Promise.resolve({ locale: "en" }),
143
- getMdxSource: stubGetMdxSource,
144
- }),
145
- );
146
- expect(screen.getByRole("heading", { level: 1 })).toHaveTextContent(
147
- "Testimonials",
148
- );
149
- });
150
-
151
- it("emits a JSON-LD script tag with @type CollectionPage", async () => {
152
- await renderAsync(
153
- TestimonialsPage({
154
- params: Promise.resolve({ locale: "en" }),
155
- getMdxSource: stubGetMdxSource,
156
- }),
157
- );
158
- const script = screen.getByTestId("json-ld");
159
- expect(script.getAttribute("type")).toBe("application/ld+json");
160
- const parsed = JSON.parse(script.innerHTML) as { "@type": string };
161
- expect(parsed["@type"]).toBe("CollectionPage");
162
- });
163
-
164
- it("calls getMdxSource with TestimonialsList type, testimonials slug, and locale", async () => {
165
- await renderAsync(
166
- TestimonialsPage({
167
- params: Promise.resolve({ locale: "en" }),
168
- getMdxSource: stubGetMdxSource,
169
- }),
170
- );
171
- expect(stubGetMdxSource).toHaveBeenCalledWith(
172
- "TestimonialsList",
173
- "testimonials",
174
- "en",
175
- );
176
- });
177
-
178
- it("renders the description and MDX body", async () => {
179
- await renderAsync(
180
- TestimonialsPage({
181
- params: Promise.resolve({ locale: "en" }),
182
- getMdxSource: stubGetMdxSource,
183
- }),
184
- );
185
- expect(
186
- screen.getByTestId("testimonials-description"),
187
- ).toHaveTextContent("What teams say about AI Launch Kit.");
188
- expect(screen.getByTestId("mdx-body")).toBeInTheDocument();
189
- });
190
- });
191
-
192
- describe("with no TestimonialsList MDX (empty state)", () => {
193
- const stubGetMdxSource = jest
194
- .fn<ReturnType<GetMdxSource>, Parameters<GetMdxSource>>()
195
- .mockResolvedValue(null);
196
-
197
- it("renders the fallback sentinel without crashing", async () => {
198
- await renderAsync(
199
- TestimonialsPage({
200
- params: Promise.resolve({ locale: "en" }),
201
- getMdxSource: stubGetMdxSource,
202
- }),
203
- );
204
- expect(screen.getByTestId("section-empty-state")).toBeInTheDocument();
205
- });
206
- });
207
- });
208
- });
@@ -1,66 +0,0 @@
1
- // Tests for the env-driven Better Stack heartbeat util in apps/web (#1015).
2
- // When BETTER_STACK_HEARTBEAT_URL is set, pingHeartbeat() POSTs to that URL;
3
- // when unset it is a no-op. POST failures are handled gracefully — the function
4
- // returns without throwing.
5
- //
6
- // Mocking global fetch is allowed (external module boundary).
7
- // Mocking internal modules with `jest.mock('../foo')` is not allowed;
8
- // this test mocks the fetch boundary instead.
9
-
10
- const fetchMock = jest.fn();
11
- global.fetch = fetchMock;
12
-
13
- describe("apps/web — Better Stack heartbeat (#1015)", () => {
14
- const originalHeartbeatUrl = process.env.BETTER_STACK_HEARTBEAT_URL;
15
-
16
- beforeEach(() => {
17
- fetchMock.mockReset();
18
- });
19
-
20
- afterEach(() => {
21
- if (originalHeartbeatUrl === undefined) {
22
- delete process.env.BETTER_STACK_HEARTBEAT_URL;
23
- } else {
24
- process.env.BETTER_STACK_HEARTBEAT_URL = originalHeartbeatUrl;
25
- }
26
- jest.resetModules();
27
- });
28
-
29
- it("is a no-op when BETTER_STACK_HEARTBEAT_URL is unset", async () => {
30
- await jest.isolateModulesAsync(async () => {
31
- delete process.env.BETTER_STACK_HEARTBEAT_URL;
32
- const { pingHeartbeat } =
33
- await import("../lib/observability/better-stack");
34
- await pingHeartbeat();
35
- expect(fetchMock).not.toHaveBeenCalled();
36
- });
37
- });
38
-
39
- it("POSTs to BETTER_STACK_HEARTBEAT_URL when set", async () => {
40
- await jest.isolateModulesAsync(async () => {
41
- process.env.BETTER_STACK_HEARTBEAT_URL =
42
- "https://uptime.betterstack.com/api/v1/heartbeat/test-token";
43
- fetchMock.mockResolvedValueOnce({ ok: true } as Response);
44
- const { pingHeartbeat } =
45
- await import("../lib/observability/better-stack");
46
- await pingHeartbeat();
47
- expect(fetchMock).toHaveBeenCalledTimes(1);
48
- expect(fetchMock).toHaveBeenCalledWith(
49
- "https://uptime.betterstack.com/api/v1/heartbeat/test-token",
50
- expect.objectContaining({ method: "POST" }),
51
- );
52
- });
53
- });
54
-
55
- it("handles POST failure gracefully without throwing", async () => {
56
- await jest.isolateModulesAsync(async () => {
57
- process.env.BETTER_STACK_HEARTBEAT_URL =
58
- "https://uptime.betterstack.com/api/v1/heartbeat/test-token";
59
- fetchMock.mockRejectedValueOnce(new Error("network failure"));
60
- const { pingHeartbeat } =
61
- await import("../lib/observability/better-stack");
62
- await expect(pingHeartbeat()).resolves.toBeUndefined();
63
- expect(fetchMock).toHaveBeenCalledTimes(1);
64
- });
65
- });
66
- });
@@ -1,87 +0,0 @@
1
- /**
2
- * Integration test: source.config.ts blog dir resolves to apps/web/content/en/blog/.
3
- *
4
- * Regression guard for the bug introduced in PR #157 where `dir: "content/blog"`
5
- * resolved relative to apps/web (cwd when fumadocs-mdx prebuild runs) instead of
6
- * relative to the repo root. The fix uses an absolute path constructed from
7
- * `import.meta.url` so the resolved dir is stable regardless of the cwd at
8
- * build time.
9
- *
10
- * i18n Phase 3 S2 (2026-05-18) moved blog content from `content/blog/` to
11
- * `content/en/blog/` as part of the locale-prefixed content layout.
12
- *
13
- * Issue #1459 (2026-05-25): per-app content directory divergence — blog content
14
- * moved from `<repo-root>/content/en/blog/` to `apps/web/content/en/blog/`.
15
- *
16
- * Test approach: parse the `dir` value from source.config.ts statically and verify
17
- * it resolves to a path that (a) is an absolute path and (b) points to the real
18
- * apps/web/content/en/blog/ directory containing launching-ailk.mdx.
19
- *
20
- * Covers AC-373-1, AC-373-2, AC-373-4.
21
- */
22
-
23
- import * as fs from "fs";
24
- import * as path from "path";
25
-
26
- const webRoot = path.resolve(__dirname, "..");
27
- const blogDir = path.join(webRoot, "content", "en", "blog");
28
- const sourceConfigPath = path.join(webRoot, "source.config.ts");
29
- const sourceConfigSrc = fs.readFileSync(sourceConfigPath, "utf8");
30
-
31
- describe("fix(web): fumadocs blog dir resolves to correct path (#373)", () => {
32
- describe("AC-373-1: source.config.ts dir resolves to apps/web/content/en/blog/", () => {
33
- it("source.config.ts imports fileURLToPath or uses an absolute-path strategy for dir", () => {
34
- // The fix must not rely on a bare relative string like "content/en/blog"
35
- // or "../../content/en/blog" — fumadocs-mdx resolves dir via
36
- // path.resolve(dir) which anchors to process.cwd() (apps/web), not the
37
- // config file location. A bare relative string that works only when
38
- // cwd === repo-root is fragile. The correct pattern is to derive an
39
- // absolute path from import.meta.url.
40
- const hasImportMetaUrl = sourceConfigSrc.includes("import.meta.url");
41
- const hasFileURLToPath =
42
- sourceConfigSrc.includes("fileURLToPath") ||
43
- sourceConfigSrc.includes("url.fileURLToPath");
44
- expect(hasImportMetaUrl || hasFileURLToPath).toBe(true);
45
- });
46
-
47
- it("source.config.ts dir value is an absolute path expression (not a bare relative string)", () => {
48
- // The dir value must not be a literal bare string that caused the
49
- // original regression — path.resolve() must be used.
50
- expect(sourceConfigSrc).not.toContain('dir: "content/en/blog"');
51
- expect(sourceConfigSrc).not.toContain("dir: 'content/en/blog'");
52
- expect(sourceConfigSrc).not.toContain('dir: "content/blog"');
53
- expect(sourceConfigSrc).not.toContain("dir: 'content/blog'");
54
- });
55
-
56
- it("the resolved blog dir path exists on disk (apps/web/content/en/blog/)", () => {
57
- expect(fs.existsSync(blogDir)).toBe(true);
58
- });
59
- });
60
-
61
- describe("AC-373-2: apps/web/content/en/blog/ contains the expected post", () => {
62
- it("launching-ailk.mdx exists in apps/web/content/en/blog/", () => {
63
- const filePath = path.join(blogDir, "launching-ailk.mdx");
64
- expect(fs.existsSync(filePath)).toBe(true);
65
- });
66
-
67
- it("launching-ailk.mdx has valid frontmatter (title, description, date)", () => {
68
- const content = fs.readFileSync(
69
- path.join(blogDir, "launching-ailk.mdx"),
70
- "utf8",
71
- );
72
- expect(content).toContain("title:");
73
- expect(content).toContain("description:");
74
- expect(content).toContain("date:");
75
- });
76
- });
77
-
78
- describe("AC-373-4: frontmatter schema in source.config.ts matches launching-ailk.mdx fields", () => {
79
- it("source.config.ts schema includes title, description, date, updated, image", () => {
80
- expect(sourceConfigSrc).toContain("title");
81
- expect(sourceConfigSrc).toContain("description");
82
- expect(sourceConfigSrc).toContain("date");
83
- expect(sourceConfigSrc).toContain("updated");
84
- expect(sourceConfigSrc).toContain("image");
85
- });
86
- });
87
- });