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