create-ailk 0.4.0 → 0.6.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 (396) hide show
  1. package/README.md +55 -8
  2. package/component-catalog.json +12248 -0
  3. package/component-catalog.md +193 -68
  4. package/dist/cli.js +29 -1
  5. package/dist/derive-architecture.js +5 -2
  6. package/dist/lib/api-boot-assertion.d.ts +46 -0
  7. package/dist/lib/api-boot-assertion.js +70 -0
  8. package/dist/lib/extract-module-bundle.d.ts +50 -2
  9. package/dist/lib/extract-module-bundle.js +223 -15
  10. package/dist/lib/fetch-guard.d.ts +28 -0
  11. package/dist/lib/fetch-guard.js +101 -0
  12. package/dist/lib/fetch-module.js +4 -0
  13. package/dist/lib/fetch-template.js +4 -0
  14. package/dist/lib/import-graph.d.ts +29 -0
  15. package/dist/lib/import-graph.js +140 -0
  16. package/dist/module-architecture.d.ts +60 -8
  17. package/dist/module-architecture.js +587 -242
  18. package/dist/parse-args.js +6 -2
  19. package/dist/programmatic.d.ts +10 -0
  20. package/dist/programmatic.js +134 -19
  21. package/dist/site-chrome.d.ts +80 -0
  22. package/dist/site-chrome.js +135 -0
  23. package/dist/surfaces.d.ts +21 -6
  24. package/dist/surfaces.js +209 -36
  25. package/dist/template-manifest.d.ts +88 -3
  26. package/dist/template-manifest.js +281 -29
  27. package/dist/template-names.d.ts +1 -1
  28. package/dist/template-names.js +2 -0
  29. package/package.json +10 -2
  30. package/site-templates/_base/ailk-template.json +6 -0
  31. package/site-templates/_base/content/en/Pricing/pricing.mdx +169 -0
  32. package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +351 -0
  33. package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +400 -0
  34. package/site-templates/marketing-site/ailk-template.json +11 -0
  35. package/site-templates/marketing-site/content/en/blog/05-this-vc-morning-routine-will-make-you-super-annoying.mdx +36 -0
  36. package/site-templates/marketing-site/provenance.json +4 -0
  37. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +288 -0
  38. package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +286 -0
  39. package/site-templates/marketing-site-free/ailk-template.json +2 -1
  40. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.json +279 -0
  41. package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +336 -0
  42. package/site-templates/minimalism-site/ailk-template.json +22 -0
  43. package/site-templates/minimalism-site/assets/avatar-placeholder-1.svg +7 -0
  44. package/site-templates/minimalism-site/assets/avatar-placeholder-2.svg +7 -0
  45. package/site-templates/minimalism-site/assets/avatar-placeholder-3.svg +7 -0
  46. package/site-templates/minimalism-site/assets/avatar-placeholder-4.svg +7 -0
  47. package/site-templates/minimalism-site/assets/avatar-placeholder-5.svg +7 -0
  48. package/site-templates/minimalism-site/assets/avatar-placeholder-6.svg +7 -0
  49. package/site-templates/minimalism-site/assets/hero-backdrop.svg +17 -0
  50. package/site-templates/minimalism-site/assets/logo-placeholder-1.svg +6 -0
  51. package/site-templates/minimalism-site/assets/logo-placeholder-2.svg +6 -0
  52. package/site-templates/minimalism-site/assets/logo-placeholder-3.svg +6 -0
  53. package/site-templates/minimalism-site/assets/wordmark-placeholder.svg +28 -0
  54. package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +110 -0
  55. package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +68 -0
  56. package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +57 -0
  57. package/site-templates/minimalism-site/content/en/HomePage/home.mdx +244 -0
  58. package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +77 -0
  59. package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +73 -0
  60. package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +52 -0
  61. package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +52 -0
  62. package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +52 -0
  63. package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +52 -0
  64. package/site-templates/minimalism-site/content/en/Service/first-engagement.mdx +38 -0
  65. package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +38 -0
  66. package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +38 -0
  67. package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +73 -0
  68. package/site-templates/minimalism-site/flows/referral.flow.json +71 -0
  69. package/site-templates/minimalism-site/provenance.json +4 -0
  70. package/site-templates/saas-product/TEMPLATE-MANIFEST.md +61 -0
  71. package/site-templates/saas-product/ailk-template.json +16 -0
  72. package/site-templates/saas-product/provenance.json +10 -0
  73. package/site-templates/simple-service/TEMPLATE-MANIFEST.json +240 -0
  74. package/site-templates/simple-service/TEMPLATE-MANIFEST.md +278 -0
  75. package/site-templates/simple-service/ailk-template.json +10 -0
  76. package/site-templates/simple-service/content/en/AboutPage/about.mdx +38 -0
  77. package/site-templates/simple-service/content/en/ContactPage/contact.mdx +67 -0
  78. package/site-templates/simple-service/content/en/FAQPage/faq.mdx +56 -0
  79. package/site-templates/simple-service/content/en/HomePage/home.mdx +85 -0
  80. package/site-templates/simple-service/content/en/Service/landscape-design.mdx +40 -0
  81. package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +40 -0
  82. package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +40 -0
  83. package/site-templates/simple-service/content/en/ServicesList/services.mdx +72 -0
  84. package/site-templates/simple-service/provenance.json +4 -0
  85. package/templates/.claude/skills/README.md +16 -25
  86. package/templates/.env.example +102 -12
  87. package/templates/.github/workflows/ci.yml +48 -0
  88. package/templates/README.md +3 -1
  89. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-31.552Z-fallback-1789916251590-037e9005.json +8 -0
  90. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.840Z-fallback-1789916253842-b52659e9.json +12 -0
  91. package/templates/apps/api/.ailk/inbox/2026-09-20T14-57-33.844Z-fallback-1789916253845-6b266f8c.json +12 -0
  92. package/templates/apps/api/.env.example +13 -0
  93. package/templates/apps/api/CLAUDE.md +29 -2
  94. package/templates/apps/api/eslint.config.mjs +69 -0
  95. package/templates/apps/api/jest.config.cjs +3 -0
  96. package/templates/apps/api/package.json +7 -2
  97. package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +407 -0
  98. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +89 -9
  99. package/templates/apps/api/src/__tests__/review-surface-registration.test.ts +167 -0
  100. package/templates/apps/api/src/__tests__/server.test.ts +471 -0
  101. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +45 -0
  102. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +10 -1
  103. package/templates/apps/api/src/config/index.ts +62 -0
  104. package/templates/apps/api/src/config/modules.ts +7 -3
  105. package/templates/apps/api/src/lib/__mocks__/prisma.ts +241 -2
  106. package/templates/apps/api/src/lib/__tests__/auth-email-budget.test.ts +143 -0
  107. package/templates/apps/api/src/lib/__tests__/content-adapter.test.ts +23 -0
  108. package/templates/apps/api/src/lib/__tests__/deliverable-asset-store.test.ts +125 -0
  109. package/templates/apps/api/src/lib/__tests__/error-serializer.test.ts +20 -0
  110. package/templates/apps/api/src/lib/__tests__/require-commerce-claim.test.ts +317 -0
  111. package/templates/apps/api/src/lib/__tests__/site.test.ts +238 -0
  112. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +75 -0
  113. package/templates/apps/api/src/lib/auth-email-budget.ts +160 -0
  114. package/templates/apps/api/src/lib/content-adapter.ts +57 -31
  115. package/templates/apps/api/src/lib/deliverable-asset-store.ts +76 -0
  116. package/templates/apps/api/src/lib/error-serializer.ts +23 -3
  117. package/templates/apps/api/src/lib/outbound-http.ts +7 -2
  118. package/templates/apps/api/src/lib/require-commerce-claim.ts +250 -0
  119. package/templates/apps/api/src/lib/site.ts +208 -9
  120. package/templates/apps/api/src/lib/stripe.ts +66 -0
  121. package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +521 -0
  122. package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +150 -0
  123. package/templates/apps/api/src/middleware/default-tenant.ts +136 -0
  124. package/templates/apps/api/src/middleware/rate-limit.ts +7 -1
  125. package/templates/apps/api/src/openapi/spec.ts +543 -2
  126. package/templates/apps/api/src/routes/auth/__tests__/auth-user.functional.test.ts +48 -0
  127. package/templates/apps/api/src/routes/auth/__tests__/transactional-email.test.ts +309 -0
  128. package/templates/apps/api/src/routes/auth/__tests__/user.test.ts +123 -0
  129. package/templates/apps/api/src/routes/auth/transactional-email.ts +273 -0
  130. package/templates/apps/api/src/routes/auth/user.ts +61 -0
  131. package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +434 -0
  132. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +888 -0
  133. package/templates/apps/api/src/routes/checkout/index.ts +34 -0
  134. package/templates/apps/api/src/routes/checkout/session.ts +139 -0
  135. package/templates/apps/api/src/routes/checkout/sessions.ts +197 -0
  136. package/templates/apps/api/src/routes/content/__tests__/get-page.test.ts +554 -0
  137. package/templates/apps/api/src/routes/content/__tests__/list-pages.test.ts +108 -0
  138. package/templates/apps/api/src/routes/content/__tests__/search.test.ts +450 -0
  139. package/templates/apps/api/src/routes/content/get-page.ts +94 -12
  140. package/templates/apps/api/src/routes/content/index.ts +13 -73
  141. package/templates/apps/api/src/routes/content/list-pages.ts +15 -3
  142. package/templates/apps/api/src/routes/content/rate-limit.ts +15 -6
  143. package/templates/apps/api/src/routes/content/search.ts +129 -0
  144. package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +447 -31
  145. package/templates/apps/api/src/routes/leads/__tests__/list.test.ts +171 -0
  146. package/templates/apps/api/src/routes/leads/index.ts +100 -13
  147. package/templates/apps/api/src/routes/leads/list.ts +104 -0
  148. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +51 -0
  149. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +351 -0
  150. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +51 -0
  151. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -2
  152. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +4 -1
  153. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +58 -0
  154. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +107 -6
  155. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +87 -2
  156. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +51 -0
  157. package/templates/apps/api/src/routes/project-listings/start.ts +5 -1
  158. package/templates/apps/api/src/routes/requests/__tests__/confirm.test.ts +686 -0
  159. package/templates/apps/api/src/routes/requests/confirm.ts +601 -0
  160. package/templates/apps/api/src/routes/requests/index.ts +54 -0
  161. package/templates/apps/api/src/routes/webhooks/README.md +265 -0
  162. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-dunning.test.ts +749 -0
  163. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +330 -0
  164. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +262 -0
  165. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +285 -0
  166. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +213 -0
  167. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-request-paid.test.ts +301 -0
  168. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +648 -0
  169. package/templates/apps/api/src/routes/webhooks/index.ts +89 -0
  170. package/templates/apps/api/src/routes/webhooks/stripe.ts +1399 -0
  171. package/templates/apps/api/src/server.ts +521 -55
  172. package/templates/apps/api/src/services/__tests__/abandon-email.test.ts +39 -0
  173. package/templates/apps/api/src/services/__tests__/aeo-score.test.ts +217 -40
  174. package/templates/apps/api/src/services/__tests__/audit-log.test.ts +620 -0
  175. package/templates/apps/api/src/services/__tests__/consent.test.ts +2 -0
  176. package/templates/apps/api/src/services/__tests__/deliverable-fulfillment.test.ts +300 -5
  177. package/templates/apps/api/src/services/__tests__/deliverable-resend.test.ts +20 -0
  178. package/templates/apps/api/src/services/__tests__/dunning-policy.test.ts +368 -0
  179. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +408 -0
  180. package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +160 -0
  181. package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +329 -0
  182. package/templates/apps/api/src/services/__tests__/listing-config.test.ts +166 -0
  183. package/templates/apps/api/src/services/__tests__/project-listings.fence.test.ts +154 -0
  184. package/templates/apps/api/src/services/__tests__/public-site-resolver.test.ts +137 -0
  185. package/templates/apps/api/src/services/__tests__/request-confirm.test.ts +444 -0
  186. package/templates/apps/api/src/services/__tests__/revalidation.test.ts +238 -0
  187. package/templates/apps/api/src/services/__tests__/verified-email.test.ts +78 -0
  188. package/templates/apps/api/src/services/abandon-email.ts +8 -2
  189. package/templates/apps/api/src/services/aeo-score.ts +49 -12
  190. package/templates/apps/api/src/services/audit-log.ts +668 -0
  191. package/templates/apps/api/src/services/consent.ts +18 -0
  192. package/templates/apps/api/src/services/default-tenant.ts +300 -0
  193. package/templates/apps/api/src/services/deliverable-fulfillment.ts +141 -13
  194. package/templates/apps/api/src/services/dunning-policy.ts +294 -0
  195. package/templates/apps/api/src/services/entitlements.ts +80 -0
  196. package/templates/apps/api/src/services/lead-promotion.ts +160 -0
  197. package/templates/apps/api/src/services/lead-routing.ts +161 -2
  198. package/templates/apps/api/src/services/leads-inbox.ts +284 -0
  199. package/templates/apps/api/src/services/listing-config.ts +27 -9
  200. package/templates/apps/api/src/services/owner-workspaces.ts +132 -0
  201. package/templates/apps/api/src/services/project-listings.ts +201 -6
  202. package/templates/apps/api/src/services/public-site-resolver.ts +106 -0
  203. package/templates/apps/api/src/services/request-confirm.ts +646 -0
  204. package/templates/apps/api/src/services/revalidation.ts +173 -0
  205. package/templates/apps/api/src/services/verified-email.ts +71 -0
  206. package/templates/apps/api/src/vercel-handler.ts +75 -35
  207. package/templates/apps/mcp/.env.example +17 -0
  208. package/templates/apps/mcp/CLAUDE.md +58 -25
  209. package/templates/apps/mcp/__tests__/aeo-cli-parity.test.ts +6 -0
  210. package/templates/apps/mcp/__tests__/audit.test.ts +137 -8
  211. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +79 -36
  212. package/templates/apps/mcp/__tests__/get_offerings.test.ts +111 -2
  213. package/templates/apps/mcp/__tests__/get_pricing.test.ts +82 -2
  214. package/templates/apps/mcp/__tests__/http-transport.test.ts +541 -2
  215. package/templates/apps/mcp/__tests__/index.test.ts +39 -0
  216. package/templates/apps/mcp/__tests__/lib/audit-single-page-readiness-scan.test.ts +143 -0
  217. package/templates/apps/mcp/__tests__/lib/commerce-claim.test.ts +111 -0
  218. package/templates/apps/mcp/__tests__/lib/content-adapter-parity.test.ts +65 -0
  219. package/templates/apps/mcp/__tests__/lib/content-adapter.test.ts +188 -0
  220. package/templates/apps/mcp/__tests__/lib/fail-closed-tools.test.ts +165 -0
  221. package/templates/apps/mcp/__tests__/lib/public-workspace.test.ts +124 -0
  222. package/templates/apps/mcp/__tests__/list_pages.test.ts +116 -0
  223. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +7 -1
  224. package/templates/apps/mcp/__tests__/parity.test.ts +12 -9
  225. package/templates/apps/mcp/__tests__/purchase-disabled.test.ts +89 -22
  226. package/templates/apps/mcp/__tests__/read_content.test.ts +322 -1
  227. package/templates/apps/mcp/__tests__/search_content.test.ts +105 -2
  228. package/templates/apps/mcp/__tests__/server.test.ts +67 -14
  229. package/templates/apps/mcp/__tests__/start_purchase.test.ts +252 -0
  230. package/templates/apps/mcp/__tests__/tools/{scaffold.test.ts → create_project.test.ts} +23 -23
  231. package/templates/apps/mcp/__tests__/tools/purchase.test.ts +300 -123
  232. package/templates/apps/mcp/eslint.config.mjs +69 -0
  233. package/templates/apps/mcp/jest.config.cjs +13 -0
  234. package/templates/apps/mcp/package.json +8 -2
  235. package/templates/apps/mcp/src/abuse/index.ts +159 -24
  236. package/templates/apps/mcp/src/config/modules.ts +10 -5
  237. package/templates/apps/mcp/src/http-transport.ts +102 -29
  238. package/templates/apps/mcp/src/index.ts +73 -11
  239. package/templates/apps/mcp/src/lib/audit-single-page.ts +90 -3
  240. package/templates/apps/mcp/src/lib/commerce-claim.ts +94 -0
  241. package/templates/apps/mcp/src/lib/content-adapter.ts +113 -0
  242. package/templates/apps/mcp/src/lib/empty-content-adapter.ts +139 -0
  243. package/templates/apps/mcp/src/lib/prisma.ts +39 -0
  244. package/templates/apps/mcp/src/lib/public-workspace.ts +133 -0
  245. package/templates/apps/mcp/src/lib/stripe-tax-signal.ts +59 -0
  246. package/templates/apps/mcp/src/server.ts +61 -37
  247. package/templates/apps/mcp/src/tools/audit.ts +26 -2
  248. package/templates/apps/mcp/src/tools/{scaffold.ts → create_project.ts} +3 -3
  249. package/templates/apps/mcp/src/tools/get_business_hours.ts +4 -3
  250. package/templates/apps/mcp/src/tools/get_faq.ts +4 -3
  251. package/templates/apps/mcp/src/tools/get_locations.ts +5 -3
  252. package/templates/apps/mcp/src/tools/get_offerings.ts +16 -4
  253. package/templates/apps/mcp/src/tools/get_pricing.ts +25 -6
  254. package/templates/apps/mcp/src/tools/get_site_info.ts +4 -3
  255. package/templates/apps/mcp/src/tools/index.ts +9 -7
  256. package/templates/apps/mcp/src/tools/list_pages.ts +4 -3
  257. package/templates/apps/mcp/src/tools/purchase.ts +128 -45
  258. package/templates/apps/mcp/src/tools/read_content.ts +20 -6
  259. package/templates/apps/mcp/src/tools/search_content.ts +11 -4
  260. package/templates/apps/web/.env.example +74 -8
  261. package/templates/apps/web/CLAUDE.md +46 -0
  262. package/templates/apps/web/app/.well-known/mcp.json/build-manifest.ts +18 -2
  263. package/templates/apps/web/app/.well-known/mcp.json/route.ts +11 -3
  264. package/templates/apps/web/app/[locale]/(auth)/__tests__/auth-pages.test.tsx +282 -0
  265. package/templates/apps/web/app/[locale]/(auth)/__tests__/password-reset.test.tsx +515 -0
  266. package/templates/apps/web/app/[locale]/(auth)/forgot-password/forgot-password-card.tsx +89 -0
  267. package/templates/apps/web/app/[locale]/(auth)/forgot-password/page.tsx +109 -0
  268. package/templates/apps/web/app/[locale]/(auth)/layout.tsx +92 -0
  269. package/templates/apps/web/app/[locale]/(auth)/reset-password/page.tsx +135 -0
  270. package/templates/apps/web/app/[locale]/(auth)/reset-password/reset-password-card.tsx +109 -0
  271. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/page.test.tsx +48 -2
  272. package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/page.tsx +20 -6
  273. package/templates/apps/web/app/[locale]/(auth)/sign-in/sign-in-card.tsx +125 -0
  274. package/templates/apps/web/app/[locale]/(auth)/sign-up/page.tsx +96 -0
  275. package/templates/apps/web/app/[locale]/(auth)/sign-up/sign-up-card.tsx +101 -0
  276. package/templates/apps/web/app/[locale]/(auth)/use-auth-card-flow.ts +102 -0
  277. package/templates/apps/web/app/[locale]/(auth)/verify-email/page.tsx +87 -0
  278. package/templates/apps/web/app/[locale]/(authed)/__tests__/gate.test.tsx +20 -10
  279. package/templates/apps/web/app/[locale]/(authed)/account/__tests__/page.test.tsx +84 -0
  280. package/templates/apps/web/app/[locale]/(authed)/account/actions.ts +58 -0
  281. package/templates/apps/web/app/[locale]/(authed)/account/page.tsx +67 -14
  282. package/templates/apps/web/app/[locale]/(authed)/layout.tsx +19 -2
  283. package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +181 -0
  284. package/templates/apps/web/app/[locale]/(authed)/leads/components/LeadsTable.tsx +137 -0
  285. package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +111 -0
  286. package/templates/apps/web/app/[locale]/(authed)/sign-in-gate.tsx +1 -1
  287. package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +111 -0
  288. package/templates/apps/web/app/[locale]/[slug]/page.tsx +65 -0
  289. package/templates/apps/web/app/[locale]/cancellation/page.tsx +44 -0
  290. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +1 -1
  291. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +1 -1
  292. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +6 -5
  293. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +1 -1
  294. package/templates/apps/web/app/[locale]/layout.tsx +308 -65
  295. package/templates/apps/web/app/[locale]/purchase/__tests__/cancel.test.tsx +162 -0
  296. package/templates/apps/web/app/[locale]/purchase/__tests__/module-scope.test.ts +68 -0
  297. package/templates/apps/web/app/[locale]/purchase/__tests__/success.test.tsx +361 -0
  298. package/templates/apps/web/app/[locale]/purchase/cancel/page.tsx +115 -0
  299. package/templates/apps/web/app/[locale]/purchase/success/page.tsx +307 -0
  300. package/templates/apps/web/app/[locale]/returns/page.tsx +44 -0
  301. package/templates/apps/web/app/[locale]/shipping/page.tsx +44 -0
  302. package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +670 -0
  303. package/templates/apps/web/app/api/revalidate/route.ts +194 -0
  304. package/templates/apps/web/auth.ts +407 -8
  305. package/templates/apps/web/components/google-analytics.tsx +18 -1
  306. package/templates/apps/web/eslint.config.mjs +69 -0
  307. package/templates/apps/web/jest.config.cjs +15 -19
  308. package/templates/apps/web/lib/__tests__/auth-email.test.ts +214 -0
  309. package/templates/apps/web/lib/__tests__/auth-routes.test.ts +74 -0
  310. package/templates/apps/web/lib/__tests__/meeting-slugs.test.ts +72 -0
  311. package/templates/apps/web/lib/__tests__/structured-data.test.tsx +50 -6
  312. package/templates/apps/web/lib/auth-email.ts +318 -0
  313. package/templates/apps/web/lib/auth-env.ts +85 -0
  314. package/templates/apps/web/lib/auth-routes.ts +55 -0
  315. package/templates/apps/web/lib/meeting-slugs.ts +53 -0
  316. package/templates/apps/web/lib/nav-config.ts +27 -7
  317. package/templates/apps/web/lib/server-api.ts +1 -1
  318. package/templates/apps/web/lib/site-chrome.ts +29 -0
  319. package/templates/apps/web/lib/telemetry-scrub.ts +50 -0
  320. package/templates/apps/web/messages/en.json +216 -2
  321. package/templates/apps/web/next.config.mjs +20 -0
  322. package/templates/apps/web/package.json +8 -3
  323. package/templates/apps/web/sentry.client.config.ts +131 -0
  324. package/templates/apps/web/sentry.edge.config.ts +72 -0
  325. package/templates/apps/web/sentry.server.config.ts +72 -0
  326. package/templates/apps/web/src/lib/commerce-claim.ts +33 -0
  327. package/templates/apps/web/src/lib/commerce-sections.ts +61 -0
  328. package/templates/apps/web/src/lib/routes.ts +13 -18
  329. package/templates/brand_assets/README.md +24 -0
  330. package/templates/content/en/AboutPage/about.mdx +160 -15
  331. package/templates/content/en/CancellationPolicyPage/cancellation.mdx +108 -0
  332. package/templates/content/en/ContactPage/contact.mdx +90 -13
  333. package/templates/content/en/FAQPage/faq.mdx +62 -22
  334. package/templates/content/en/HomePage/home.mdx +244 -20
  335. package/templates/content/en/LandingPage/blog.mdx +83 -0
  336. package/templates/content/en/LandingPage/features.mdx +103 -0
  337. package/templates/content/en/LandingPage/how-it-works.mdx +143 -0
  338. package/templates/content/en/ReturnsPolicyPage/returns.mdx +108 -0
  339. package/templates/content/en/ShippingPolicyPage/shipping.mdx +108 -0
  340. package/templates/database/CHANGELOG.md +149 -0
  341. package/templates/database/README.md +10 -3
  342. package/templates/database/__tests__/appointment.db.test.ts +106 -0
  343. package/templates/database/__tests__/artifact-content-store.db.test.ts +316 -0
  344. package/templates/database/__tests__/composite-fence.db.test.ts +206 -0
  345. package/templates/database/__tests__/fence-fan-out.db.test.ts +711 -0
  346. package/templates/database/__tests__/fixtures/appointment-create-probe.ts +79 -0
  347. package/templates/database/__tests__/fixtures/project-listing-consent-probe.ts +100 -0
  348. package/templates/database/__tests__/flow-workspace-key.db.test.ts +263 -0
  349. package/templates/database/__tests__/prefixed-ids.db.test.ts +85 -9
  350. package/templates/database/__tests__/project-listing-consent-at.db.test.ts +111 -0
  351. package/templates/database/__tests__/seed.test.ts +1 -1
  352. package/templates/database/__tests__/tenancy.test.ts +141 -0
  353. package/templates/database/config/schema.prisma +79 -2
  354. package/templates/database/content/schema.prisma +87 -11
  355. package/templates/database/eslint.config.mjs +69 -0
  356. package/templates/database/inbox/schema.prisma +452 -9
  357. package/templates/database/jest.config.cjs +5 -3
  358. package/templates/database/migrations/20260915120000_project_listing_solution/migration.sql +15 -0
  359. package/templates/database/migrations/20260916120000_artifact_content_store/migration.sql +158 -0
  360. package/templates/database/migrations/20260917120000_callback_request_confirm/migration.sql +40 -0
  361. package/templates/database/migrations/20260918120000_add_appointment/migration.sql +67 -0
  362. package/templates/database/migrations/20260918120000_asset_metadata_columns/migration.sql +70 -0
  363. package/templates/database/migrations/20260919120000_flow_workspace_key/migration.sql +181 -0
  364. package/templates/database/migrations/20260919130000_flow_definition_admin_columns/migration.sql +172 -0
  365. package/templates/database/migrations/20260919130000_workspace_meetings/migration.sql +41 -0
  366. package/templates/database/migrations/20260919140000_inbox_workspace_key/migration.sql +185 -0
  367. package/templates/database/migrations/20260920120000_lead_handled_at/migration.sql +70 -0
  368. package/templates/database/migrations/20260920130000_lead_workspace_source_index/migration.sql +57 -0
  369. package/templates/database/migrations/20260921120000_deliverable_grant_workspace_key/migration.sql +68 -0
  370. package/templates/database/migrations/20260922120000_tenancy_fence/migration.sql +1067 -0
  371. package/templates/database/migrations/20260923120000_listing_capturing_magnet/migration.sql +210 -0
  372. package/templates/database/package.json +7 -3
  373. package/templates/database/seed.ts +1 -1
  374. package/templates/database/tenancy.ts +86 -1
  375. package/templates/database/tsconfig.json +1 -0
  376. package/templates/package.json +1 -14
  377. package/versions.json +52 -0
  378. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +0 -103
  379. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +0 -253
  380. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +0 -215
  381. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +0 -266
  382. package/templates/apps/api/src/routes/content/create.ts +0 -115
  383. package/templates/apps/api/src/routes/content/delete.ts +0 -82
  384. package/templates/apps/api/src/routes/content/update.ts +0 -112
  385. package/templates/apps/mcp/__tests__/create_page.test.ts +0 -202
  386. package/templates/apps/mcp/__tests__/delete_page.test.ts +0 -177
  387. package/templates/apps/mcp/__tests__/update_page.test.ts +0 -196
  388. package/templates/apps/mcp/src/tools/create_page.ts +0 -96
  389. package/templates/apps/mcp/src/tools/delete_page.ts +0 -74
  390. package/templates/apps/mcp/src/tools/update_page.ts +0 -92
  391. package/templates/apps/web/app/[locale]/sign-in/sign-in-card.tsx +0 -123
  392. /package/site-templates/{marketing-site-free → _base}/content/en/PrivacyPage/privacy.mdx +0 -0
  393. /package/site-templates/{marketing-site-free → _base}/content/en/TermsPage/terms.mdx +0 -0
  394. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/__tests__/next-path.test.ts +0 -0
  395. /package/templates/apps/web/app/[locale]/{sign-in → (auth)/sign-in}/next-path.ts +0 -0
  396. /package/templates/apps/web/{src/components → components}/SiteNav.tsx +0 -0
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Shared network-safety guards for `fetch-template.ts` / `fetch-module.ts`
3
+ * (issue #5514, Lows 2 and 5 — a post-merge backstop on #5510's
4
+ * `create-ailk --template` feature).
5
+ *
6
+ * A plain internal helper module, not a shared package: both callers already
7
+ * state at length (see their file docs) why `create-ailk` — the one package
8
+ * in this monorepo published to public npm — cannot take a runtime
9
+ * dependency on anything `restricted`-tier. A sibling file inside
10
+ * `packages/create-ailk/src/lib/` carries no such dependency and is exactly
11
+ * the right place for logic the two fetchers would otherwise duplicate a
12
+ * second time.
13
+ *
14
+ * Three guards live here:
15
+ * - `REQUEST_TIMEOUT_MS` / `fetchTimeoutSignal()` — neither fetcher had a
16
+ * request timeout before this; a hung or slow-drip response could block
17
+ * `create-ailk` forever.
18
+ * - `assertHttpsRegistryUrl()` — neither fetcher validated the scheme of
19
+ * the registry URL (env-var-overridable) it was about to send a bearer
20
+ * JWT to. Rejects any non-`https:` origin, UNLESS the test-mode seam
21
+ * (`NODE_ENV === "test"`) is active — the same seam
22
+ * `template-scaffold.test.ts`'s `run()` helper already relies on when it
23
+ * spreads `process.env` into the CLI subprocess it spawns against the
24
+ * loopback mock registry (`http://127.0.0.1:<port>`). Confirmed
25
+ * empirically: Jest sets `NODE_ENV=test` for its own process (and thus
26
+ * every child it spawns, since `run()` inherits `process.env`) whenever
27
+ * the ambient environment does not already set it — there is no other
28
+ * test-mode flag anywhere in this codebase (`AILK_ALLOW*`, `insecure`,
29
+ * `__TEST__`, etc. do not exist).
30
+ * - `assertContentLengthWithinLimit()` — neither fetcher checked a
31
+ * response's declared size against `MAX_TOTAL_SIZE_BYTES` before buffering
32
+ * the WHOLE body via `.arrayBuffer()`. That cap was previously enforced
33
+ * only downstream, against tar entries, in
34
+ * `extract-module-bundle.ts`'s `safeExtractTarGz` — too late to stop an
35
+ * oversized response from being held in memory first.
36
+ */
37
+ import { MAX_TOTAL_SIZE_BYTES } from "./extract-module-bundle.js";
38
+ /**
39
+ * Per-request timeout applied to every template/module registry fetch.
40
+ * 30s is generous for a small tarball fetch over a real network while still
41
+ * bounding a hung connection or slow-drip response.
42
+ */
43
+ export const REQUEST_TIMEOUT_MS = 30_000;
44
+ /** `AbortSignal.timeout(REQUEST_TIMEOUT_MS)` — one call site, one constant. */
45
+ export function fetchTimeoutSignal() {
46
+ return AbortSignal.timeout(REQUEST_TIMEOUT_MS);
47
+ }
48
+ /**
49
+ * `true` iff `hostname` is a loopback host — the ONLY hosts a plain `http:`
50
+ * registry URL is accepted for. The test suite spawns its mock registry on
51
+ * `127.0.0.1`; nothing else needs cleartext. (An earlier revision keyed this
52
+ * on `NODE_ENV=test`, an ambient variable any environment can export — a
53
+ * security control in a public npm package must not carry a kill switch.)
54
+ */
55
+ function isLoopbackHost(hostname) {
56
+ return hostname === "127.0.0.1" || hostname === "localhost" || hostname === "[::1]" || hostname === "::1";
57
+ }
58
+ /**
59
+ * Reject a registry URL whose origin is not `https:`, unless its host is
60
+ * loopback (above) — the mock registry the test suite spawns is deliberately
61
+ * plain `http://127.0.0.1:<port>`. `ErrorCtor` lets each
62
+ * caller throw its own error class (`FetchTemplateError` / `FetchModuleError`)
63
+ * so this is a new FAILURE MODE, never a new error type from the caller's
64
+ * point of view — and never a licence error (`isLicenseError` stays false).
65
+ */
66
+ export function assertHttpsRegistryUrl(registryUrl, ErrorCtor) {
67
+ let scheme;
68
+ let hostname;
69
+ try {
70
+ const parsed = new URL(registryUrl);
71
+ scheme = parsed.protocol;
72
+ hostname = parsed.hostname;
73
+ }
74
+ catch {
75
+ throw new ErrorCtor(`refusing to use registry URL "${registryUrl}": not a valid URL`);
76
+ }
77
+ if (scheme !== "https:" && !isLoopbackHost(hostname)) {
78
+ throw new ErrorCtor(`refusing to use registry URL "${registryUrl}": scheme must be https:, got "${scheme}"`);
79
+ }
80
+ }
81
+ /**
82
+ * Reject a `Response` whose `Content-Length` header declares a size larger
83
+ * than `MAX_TOTAL_SIZE_BYTES` — called by the caller right after its
84
+ * existing status-code checks succeed, BEFORE `.arrayBuffer()` ever runs, so
85
+ * an oversized body is never buffered into memory. A missing or unparseable
86
+ * `Content-Length` is not itself a violation (a chunked/streamed response may
87
+ * omit it) — this is defense in depth against a declared size, not a
88
+ * replacement for the tar-entry cumulative-size cap `extract-module-bundle.ts`
89
+ * still enforces downstream.
90
+ */
91
+ export function assertContentLengthWithinLimit(response, ErrorCtor) {
92
+ const header = response.headers.get("content-length");
93
+ if (header === null)
94
+ return;
95
+ const declared = Number(header);
96
+ if (!Number.isFinite(declared))
97
+ return;
98
+ if (declared > MAX_TOTAL_SIZE_BYTES) {
99
+ throw new ErrorCtor(`refusing response: declared Content-Length ${declared} exceeds the ${MAX_TOTAL_SIZE_BYTES}-byte limit`);
100
+ }
101
+ }
@@ -36,6 +36,7 @@
36
36
  * exit uniformly.
37
37
  */
38
38
  import pkg from "../../package.json" with { type: "json" };
39
+ import { assertContentLengthWithinLimit, assertHttpsRegistryUrl, fetchTimeoutSignal, } from "./fetch-guard.js";
39
40
  export const DEFAULT_AILK_MODULE_REGISTRY_URL = "https://ailaunchkit.ai/api/modules";
40
41
  /**
41
42
  * This package's own `package.json` `"version"`. Deliberately a static JSON
@@ -79,6 +80,7 @@ export async function fetchModule(options) {
79
80
  const registryUrl = options.registryUrl ??
80
81
  process.env["AILK_MODULE_REGISTRY_URL"] ??
81
82
  DEFAULT_AILK_MODULE_REGISTRY_URL;
83
+ assertHttpsRegistryUrl(registryUrl, FetchModuleError);
82
84
  const url = buildUrl(registryUrl, moduleName, version);
83
85
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
84
86
  let response;
@@ -90,6 +92,7 @@ export async function fetchModule(options) {
90
92
  Accept: "application/gzip, application/octet-stream",
91
93
  },
92
94
  redirect: "error",
95
+ signal: fetchTimeoutSignal(),
93
96
  });
94
97
  }
95
98
  catch (err) {
@@ -111,6 +114,7 @@ export async function fetchModule(options) {
111
114
  const body = await safeReadBody(response);
112
115
  throw new FetchModuleError(`module registry returned ${response.status} for "${moduleName}": ${body}`);
113
116
  }
117
+ assertContentLengthWithinLimit(response, FetchModuleError);
114
118
  const buf = await response.arrayBuffer();
115
119
  return new Uint8Array(buf);
116
120
  }
@@ -26,6 +26,7 @@
26
26
  * gate the paid path. The registry's own 401 IS the licence verdict.
27
27
  */
28
28
  import pkg from "../../package.json" with { type: "json" };
29
+ import { assertContentLengthWithinLimit, assertHttpsRegistryUrl, fetchTimeoutSignal, } from "./fetch-guard.js";
29
30
  export const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://ailaunchkit.ai/api/templates";
30
31
  /**
31
32
  * This package's own `package.json` `"version"` — a static JSON import for
@@ -66,6 +67,7 @@ export async function fetchTemplate(options) {
66
67
  const registryUrl = options.registryUrl ??
67
68
  process.env["AILK_TEMPLATE_REGISTRY_URL"] ??
68
69
  DEFAULT_AILK_TEMPLATE_REGISTRY_URL;
70
+ assertHttpsRegistryUrl(registryUrl, FetchTemplateError);
69
71
  const url = buildUrl(registryUrl, templateName, version);
70
72
  const fetchImpl = options.fetchImpl ?? globalThis.fetch;
71
73
  let response;
@@ -77,6 +79,7 @@ export async function fetchTemplate(options) {
77
79
  Accept: "application/gzip, application/octet-stream",
78
80
  },
79
81
  redirect: "error",
82
+ signal: fetchTimeoutSignal(),
80
83
  });
81
84
  }
82
85
  catch (err) {
@@ -98,6 +101,7 @@ export async function fetchTemplate(options) {
98
101
  const body = await safeReadBody(response);
99
102
  throw new FetchTemplateError(`template registry returned ${response.status} for "${templateName}": ${body}`);
100
103
  }
104
+ assertContentLengthWithinLimit(response, FetchTemplateError);
101
105
  const buf = await response.arrayBuffer();
102
106
  return new Uint8Array(buf);
103
107
  }
@@ -0,0 +1,29 @@
1
+ export declare const SOURCE_EXTS: string[];
2
+ /** Every source file under the given repo-relative roots, as "/"-joined paths. */
3
+ export declare function listFilesUnder(repoRoot: string, roots: string[]): Set<string>;
4
+ /** The relative import specifiers a source file names (static imports + re-exports). */
5
+ export declare function relativeImportsOf(absPath: string): string[];
6
+ /**
7
+ * Resolve a relative specifier against the candidate set. TypeScript ESM writes
8
+ * `./x.js` for `./x.ts`, so the `.js` → `.ts`/`.tsx` rewrite is the common case.
9
+ */
10
+ export declare function resolveRelativeImport(fromFile: string, spec: string, candidates: Set<string>): string | undefined;
11
+ /**
12
+ * The companion closure of `seeds` under `roots`: the seeds themselves plus
13
+ * every source file under `roots` that reaches one transitively through a
14
+ * relative import, to a fixed point.
15
+ *
16
+ * This is the shape both consumers need, and the reason the walk is shared
17
+ * rather than copied. `paidModuleTemplateExclusions()` seeds it with every
18
+ * paid module's `ownedFiles` to get the files a keyless starter must not
19
+ * carry; `tools/lint-slice-registration.ts` seeds it with the same set to
20
+ * derive, by subtraction, the files a scaffold ships UNCONDITIONALLY.
21
+ *
22
+ * Never a filename-stem rule (#5182 D7), which misses eleven files and
23
+ * over-marks three.
24
+ *
25
+ * @param repoRoot absolute path to the tree being walked.
26
+ * @param roots repo-relative roots the walk is confined to.
27
+ * @param seeds repo-relative, "/"-separated files the closure starts from.
28
+ */
29
+ export declare function importClosure(repoRoot: string, roots: readonly string[], seeds: Iterable<string>): Set<string>;
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The relative-import graph walker over a repo-relative source tree.
3
+ *
4
+ * Extracted from `module-architecture.ts` (#5701) so the two consumers that
5
+ * need this resolution share ONE implementation rather than two that can
6
+ * disagree:
7
+ *
8
+ * - `paidModuleTemplateExclusions()` (module-architecture.ts) walks it to take
9
+ * the companion closure of every paid module's `ownedFiles` — the files that
10
+ * would ship dangling into a keyless starter.
11
+ * - `tools/lint-slice-registration.ts` walks it to close over the specifiers an
12
+ * `isModuleEnabled("X")` branch in `apps/api/src/server.ts` loads, and to
13
+ * check every reached file against what module X's slice delivers.
14
+ *
15
+ * Both questions are "which files does this one reach through relative
16
+ * imports", and the answer must be the same function in both mouths: a lint
17
+ * that resolved specifiers differently from the exclusion set it checks
18
+ * against would pass on a tree the scaffold cannot boot.
19
+ *
20
+ * The resolution is deliberately TEXTUAL — a regex over import/export
21
+ * specifiers plus a candidate-set lookup, no type-checker and no bundler — so
22
+ * it runs inside `pnpm preflight`'s memory budget.
23
+ */
24
+ import { readFileSync, readdirSync } from "node:fs";
25
+ import { dirname, join, sep } from "node:path";
26
+ export const SOURCE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
27
+ /** Every source file under the given repo-relative roots, as "/"-joined paths. */
28
+ export function listFilesUnder(repoRoot, roots) {
29
+ const out = new Set();
30
+ const skip = new Set(["node_modules", ".next", "dist", ".turbo", ".source"]);
31
+ const stack = roots.map((r) => r);
32
+ while (stack.length > 0) {
33
+ const rel = stack.pop();
34
+ const abs = join(repoRoot, rel);
35
+ let entries;
36
+ try {
37
+ entries = readdirSync(abs, { withFileTypes: true });
38
+ }
39
+ catch {
40
+ continue;
41
+ }
42
+ for (const entry of entries) {
43
+ if (skip.has(entry.name))
44
+ continue;
45
+ const childRel = `${rel}/${entry.name}`;
46
+ if (entry.isDirectory())
47
+ stack.push(childRel);
48
+ else if (SOURCE_EXTS.some((e) => entry.name.endsWith(e)))
49
+ out.add(childRel);
50
+ }
51
+ }
52
+ return out;
53
+ }
54
+ /** The relative import specifiers a source file names (static imports + re-exports). */
55
+ export function relativeImportsOf(absPath) {
56
+ let src;
57
+ try {
58
+ src = readFileSync(absPath, "utf8");
59
+ }
60
+ catch {
61
+ return [];
62
+ }
63
+ const out = [];
64
+ for (const m of src.matchAll(/(?:^|\n)\s*(?:import|export)[\s\S]{0,400}?from\s*["'](\.[^"']*)["']/g))
65
+ out.push(m[1]);
66
+ for (const m of src.matchAll(/\bimport\s*\(\s*["'](\.[^"']*)["']\s*\)/g))
67
+ out.push(m[1]);
68
+ return out;
69
+ }
70
+ /**
71
+ * Resolve a relative specifier against the candidate set. TypeScript ESM writes
72
+ * `./x.js` for `./x.ts`, so the `.js` → `.ts`/`.tsx` rewrite is the common case.
73
+ */
74
+ export function resolveRelativeImport(fromFile, spec, candidates) {
75
+ const base = posixJoin(dirname(fromFile.split("/").join(sep)), spec);
76
+ const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
77
+ for (const cand of [
78
+ base,
79
+ ...SOURCE_EXTS.map((e) => stem + e),
80
+ ...SOURCE_EXTS.map((e) => `${stem}/index${e}`),
81
+ ])
82
+ if (candidates.has(cand))
83
+ return cand;
84
+ return undefined;
85
+ }
86
+ /** Join and normalize a "/"-separated path, resolving `.` and `..` segments. */
87
+ function posixJoin(dir, spec) {
88
+ const parts = `${dir.split(sep).join("/")}/${spec}`.split("/");
89
+ const out = [];
90
+ for (const part of parts) {
91
+ if (part === "" || part === ".")
92
+ continue;
93
+ if (part === "..")
94
+ out.pop();
95
+ else
96
+ out.push(part);
97
+ }
98
+ return out.join("/");
99
+ }
100
+ /**
101
+ * The companion closure of `seeds` under `roots`: the seeds themselves plus
102
+ * every source file under `roots` that reaches one transitively through a
103
+ * relative import, to a fixed point.
104
+ *
105
+ * This is the shape both consumers need, and the reason the walk is shared
106
+ * rather than copied. `paidModuleTemplateExclusions()` seeds it with every
107
+ * paid module's `ownedFiles` to get the files a keyless starter must not
108
+ * carry; `tools/lint-slice-registration.ts` seeds it with the same set to
109
+ * derive, by subtraction, the files a scaffold ships UNCONDITIONALLY.
110
+ *
111
+ * Never a filename-stem rule (#5182 D7), which misses eleven files and
112
+ * over-marks three.
113
+ *
114
+ * @param repoRoot absolute path to the tree being walked.
115
+ * @param roots repo-relative roots the walk is confined to.
116
+ * @param seeds repo-relative, "/"-separated files the closure starts from.
117
+ */
118
+ export function importClosure(repoRoot, roots, seeds) {
119
+ const closed = new Set(seeds);
120
+ const candidates = listFilesUnder(repoRoot, [...roots]);
121
+ for (;;) {
122
+ let grew = false;
123
+ for (const file of candidates) {
124
+ if (closed.has(file))
125
+ continue;
126
+ const imports = relativeImportsOf(join(repoRoot, file));
127
+ for (const spec of imports) {
128
+ const target = resolveRelativeImport(file, spec, candidates);
129
+ if (target !== undefined && closed.has(target)) {
130
+ closed.add(file);
131
+ grew = true;
132
+ break;
133
+ }
134
+ }
135
+ }
136
+ if (!grew)
137
+ break;
138
+ }
139
+ return closed;
140
+ }
@@ -58,7 +58,7 @@ export interface PageRosterRow {
58
58
  route: string;
59
59
  /**
60
60
  * Repo-relative seed content this row renders. Absent for an API-backed row
61
- * (`/schedule` renders from the scheduling API, not from an MDX file).
61
+ * (`/meet` renders from the scheduling API, not from an MDX file).
62
62
  */
63
63
  content?: readonly string[];
64
64
  }
@@ -105,7 +105,7 @@ export interface ModuleSlice {
105
105
  * disappears just because one module's slice of it does).
106
106
  *
107
107
  * One entry in the arrays below is deliberately a GLOB
108
- * (`"apps-paid/workspace/**"`) rather than enumerated file-by-file — a
108
+ * (`"apps-paid/admin/**"`) rather than enumerated file-by-file — a
109
109
  * whole app with dozens of files, owned outright by one module; every other
110
110
  * entry is a literal file
111
111
  * path. A file that a module's own docs mention but that lives inside a
@@ -128,7 +128,7 @@ export interface ModuleSlice {
128
128
  * app, never a customer opt-in (see `apps/api/src/config/modules.ts`).
129
129
  * - Two are this-list-only and the manifest simply never recorded them:
130
130
  * `apps/web/app/[locale]/docs/[[...slug]]/page.tsx` and
131
- * `apps/web/app/[locale]/schedule/confirmed/page.tsx`.
131
+ * `apps/web/app/[locale]/meet/confirmed/page.tsx`.
132
132
  *
133
133
  * A file's TEST and STORY companions are never listed here (the
134
134
  * SOURCE-files-only rule above still holds) but DO carry the marker. They
@@ -167,13 +167,37 @@ export interface ModuleSlice {
167
167
  * Every source must be a file the validated tar extraction produced in the
168
168
  * bundle producer's staging directory — repo-relative, coinciding with the
169
169
  * `ownedFiles`/`installMap`-source paths this module's producer step
170
- * stages at, by construction. `extractModuleBundle` applies this map after
171
- * the tar entries are validated: an absolute path, a `..` segment, or a
172
- * path absent from staging rejects the whole bundle, as does a destination
173
- * that resolves outside the scaffold. A mapped source is copied to its
174
- * destination only, never also to its original path.
170
+ * stages at, by construction. ONE exception: `build-module-bundles.mjs`
171
+ * also stages the proprietary licence-client LICENSE at the archive root
172
+ * as `LICENSE` and adds its own `installMap["LICENSE"]` entry (never a
173
+ * slice field — the producer adds it uniformly, #5552 D1), so a slice's
174
+ * own `installMap` must not declare a `"LICENSE"` key. `extractModuleBundle`
175
+ * applies this map after the tar entries are validated: an absolute path, a
176
+ * `..` segment, or a path absent from staging rejects the whole bundle, as
177
+ * does a destination that resolves outside the scaffold. A mapped source is
178
+ * copied to its destination only, never also to its original path.
175
179
  */
176
180
  installMap?: Record<string, string>;
181
+ /**
182
+ * Rename root for this slice's whole-app glob `ownedFiles` entry(ies)
183
+ * (a `"<dir>/**"` entry — see `ownedFiles`'s own doc above) — the glob
184
+ * sibling of `installMap` (#5644 D1). Every file `expandEntry` yields
185
+ * under `<dir>/` installs at `installRoot + <rel-path-under-dir>` instead
186
+ * of its repo-relative path. `build-module-bundles.mjs`'s
187
+ * `deriveGlobInstallMap` derives one `installMap` entry per file from this
188
+ * rule at bundle-build time, and the mock registry fixture
189
+ * (`__tests__/fixtures/mock-module-registry-server.mjs`) calls the SAME
190
+ * function so its served manifest cannot drift from the producer's.
191
+ * Undefined for a slice with no glob `ownedFiles` entry, or one whose glob
192
+ * files install unchanged at their repo-relative path.
193
+ *
194
+ * `installMap` above stays the mechanism for hand-enumerated, non-glob
195
+ * remaps (see `commerce.installMap`) — a literal map is fine there because
196
+ * enumerating ten skill files costs ten lines; a whole app's worth of
197
+ * files committed one line per file into this OSS-shipped source is the
198
+ * leak class `scripts/check-oss-strip.sh` exists to catch (#5644).
199
+ */
200
+ installRoot?: string;
177
201
  /**
178
202
  * Plain text `extractModuleBundle` prints to stdout after a successful
179
203
  * merge, with C0/C1 control characters (other than newline) stripped
@@ -346,4 +370,32 @@ export declare function normalizeModules(modules: string[]): ModuleName[];
346
370
  * Passed in rather than derived from `import.meta.url`, which ts-jest cannot
347
371
  * load — the same reason `sync-routes.ts` documents for staying pure.
348
372
  */
373
+ /**
374
+ * Scaffold destinations that MORE THAN ONE module's `ownedFiles` declares —
375
+ * the co-ownership the `#5618 D4` install-time ownership registry must treat
376
+ * as intentional rather than as an overwrite (#5702 follow-up).
377
+ *
378
+ * Co-ownership is how a file reaches a scaffold under either of two modules
379
+ * without being transferred away from the other. #5677 needed it for the
380
+ * submissions review surface: the routes register under
381
+ * `multi-tenant || signal-collectors`, so BOTH bundles must be able to
382
+ * deliver them, or the module that lacks them throws
383
+ * `ERR_MODULE_NOT_FOUND` during `build()`.
384
+ *
385
+ * `validateLayerOwnership` (`lib/extract-module-bundle.ts`) refuses a
386
+ * destination an earlier layer already wrote, because a silent
387
+ * last-writer-wins between two modules that disagree is the bug `#5618 D4`
388
+ * exists to catch. Two modules that both DECLARE the same repo file are not
389
+ * that case: they stage the same bytes from the same source, so whichever
390
+ * lands second writes what is already there. Without this allowance a
391
+ * scaffold selecting both modules — `saas-product`, whose `requires` names
392
+ * `signal-collectors` AND `multi-tenant` — aborts mid-install, after the
393
+ * first module's files are on disk.
394
+ *
395
+ * Only a DECLARED overlap is allowed. A destination two layers collide on
396
+ * without both naming it in `ownedFiles` is still refused, which is the
397
+ * whole invariant.
398
+ */
399
+ export declare function coOwnedModuleDestinations(): ReadonlySet<string>;
400
+ export declare const EXCLUDE_TEMPLATE_DIRS: readonly string[];
349
401
  export declare function paidModuleTemplateExclusions(snapshotRoots: readonly string[], repoRoot: string): string[];