create-ailk 0.2.0 → 0.4.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 (292) hide show
  1. package/component-catalog.md +50 -53
  2. package/dist/cli.js +19 -9
  3. package/dist/lib/apply-module-patches.d.ts +47 -0
  4. package/dist/lib/apply-module-patches.js +370 -0
  5. package/dist/lib/extract-module-bundle.d.ts +51 -3
  6. package/dist/lib/extract-module-bundle.js +327 -28
  7. package/dist/lib/fetch-module.d.ts +3 -38
  8. package/dist/lib/fetch-module.js +26 -8
  9. package/dist/lib/fetch-template.d.ts +20 -0
  10. package/dist/lib/fetch-template.js +129 -0
  11. package/dist/lib/module-license-gate.d.ts +10 -0
  12. package/dist/lib/module-license-gate.js +13 -4
  13. package/dist/lib/paid-paths.d.ts +16 -0
  14. package/dist/lib/paid-paths.js +90 -0
  15. package/dist/module-architecture.d.ts +197 -23
  16. package/dist/module-architecture.js +820 -25
  17. package/dist/parse-args.d.ts +9 -2
  18. package/dist/parse-args.js +46 -1
  19. package/dist/programmatic.d.ts +28 -2
  20. package/dist/programmatic.js +115 -8
  21. package/dist/surfaces.d.ts +12 -36
  22. package/dist/surfaces.js +77 -5
  23. package/dist/sync-routes.js +31 -1
  24. package/dist/template-manifest.d.ts +92 -0
  25. package/dist/template-manifest.js +168 -0
  26. package/dist/template-names.d.ts +41 -0
  27. package/dist/template-names.js +44 -0
  28. package/package.json +3 -2
  29. package/site-templates/marketing-site-free/ailk-template.json +8 -0
  30. package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +103 -0
  31. package/site-templates/marketing-site-free/content/en/PrivacyPage/privacy.mdx +79 -0
  32. package/site-templates/marketing-site-free/content/en/TermsPage/terms.mdx +80 -0
  33. package/site-templates/marketing-site-free/provenance.json +4 -0
  34. package/templates/.claude/agents/web.md +2 -3
  35. package/templates/.claude/rules/architecture.md +5 -5
  36. package/templates/.claude/skills/README.md +2 -1
  37. package/templates/.env.example +21 -9
  38. package/templates/CONVENTIONS.md +6 -7
  39. package/templates/apps/api/.env.example +9 -0
  40. package/templates/apps/api/CLAUDE.md +33 -5
  41. package/templates/apps/api/package.json +0 -1
  42. package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
  43. package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
  44. package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
  45. package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +166 -0
  46. package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
  47. package/templates/apps/api/src/config/index.ts +19 -0
  48. package/templates/apps/api/src/config/modules.ts +12 -14
  49. package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
  50. package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
  51. package/templates/apps/api/src/lib/slice-load.ts +90 -0
  52. package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
  53. package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
  54. package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
  55. package/templates/apps/api/src/openapi/index.ts +9 -3
  56. package/templates/apps/api/src/openapi/spec.ts +505 -65
  57. package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
  58. package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
  59. package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
  60. package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
  61. package/templates/apps/api/src/routes/content/index.ts +38 -11
  62. package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +17 -0
  63. package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +17 -0
  64. package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
  65. package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +10 -0
  66. package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
  67. package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
  68. package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +10 -0
  69. package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +24 -0
  70. package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +17 -0
  71. package/templates/apps/api/src/routes/project-listings/index.ts +37 -10
  72. package/templates/apps/api/src/routes/project-listings/me.ts +10 -0
  73. package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
  74. package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
  75. package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
  76. package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
  77. package/templates/apps/api/src/routes/project-listings/start.ts +30 -0
  78. package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
  79. package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
  80. package/templates/apps/api/src/server.ts +504 -137
  81. package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
  82. package/templates/apps/api/src/services/listing-config.ts +58 -0
  83. package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
  84. package/templates/apps/api/src/services/project-listings.ts +229 -4
  85. package/templates/apps/api/src/vercel-handler.ts +60 -26
  86. package/templates/apps/mcp/.env.example +8 -0
  87. package/templates/apps/mcp/CLAUDE.md +2 -2
  88. package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
  89. package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
  90. package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
  91. package/templates/apps/mcp/src/config/modules.ts +14 -13
  92. package/templates/apps/mcp/src/server.ts +8 -4
  93. package/templates/apps/mcp/src/tools/index.ts +7 -22
  94. package/templates/apps/web/.env.example +15 -0
  95. package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
  96. package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
  97. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
  98. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
  99. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
  100. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
  101. package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
  102. package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
  103. package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
  104. package/templates/apps/web/app/[locale]/pricing/page.tsx +40 -0
  105. package/templates/apps/web/app/[locale]/privacy/page.tsx +40 -0
  106. package/templates/apps/web/app/[locale]/terms/page.tsx +40 -0
  107. package/templates/apps/web/app/llms.txt/route.ts +12 -9
  108. package/templates/apps/web/jest.config.cjs +19 -13
  109. package/templates/apps/web/lib/__tests__/server-api.test.ts +61 -0
  110. package/templates/apps/web/lib/server-api.ts +28 -12
  111. package/templates/apps/web/messages/en.json +58 -0
  112. package/templates/apps/web/next.config.mjs +1 -3
  113. package/templates/apps/web/package.json +0 -6
  114. package/templates/apps/web/public/android-chrome-192x192.png +0 -0
  115. package/templates/apps/web/public/android-chrome-512x512.png +0 -0
  116. package/templates/apps/web/public/apple-touch-icon.png +0 -0
  117. package/templates/apps/web/public/favicon-16x16.png +0 -0
  118. package/templates/apps/web/public/favicon-32x32.png +0 -0
  119. package/templates/apps/web/public/favicon.ico +0 -0
  120. package/templates/apps/web/public/favicon.svg +6 -3
  121. package/templates/apps/web/public/lockup-horizontal.svg +4 -0
  122. package/templates/apps/web/public/logomark.svg +4 -0
  123. package/templates/apps/web/public/site.webmanifest +2 -2
  124. package/templates/apps/web/public/wordmark.svg +4 -0
  125. package/templates/database/CHANGELOG.md +47 -0
  126. package/templates/database/inbox/schema.prisma +67 -0
  127. package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
  128. package/templates/database/package.json +1 -1
  129. package/templates/package.json +1 -1
  130. package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
  131. package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
  132. package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
  133. package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
  134. package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
  135. package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
  136. package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
  137. package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
  138. package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
  139. package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
  140. package/templates/apps/api/src/__tests__/server.test.ts +0 -253
  141. package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
  142. package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
  143. package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
  144. package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
  145. package/templates/apps/api/src/bin/seed-presets.ts +0 -58
  146. package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
  147. package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
  148. package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
  149. package/templates/apps/api/src/lib/stripe.ts +0 -52
  150. package/templates/apps/api/src/lib/tenant-db.ts +0 -225
  151. package/templates/apps/api/src/lib/ws-token.ts +0 -91
  152. package/templates/apps/api/src/middleware/tenant.ts +0 -99
  153. package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
  154. package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
  155. package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
  156. package/templates/apps/api/src/routes/billing/index.ts +0 -36
  157. package/templates/apps/api/src/routes/billing/portal.ts +0 -182
  158. package/templates/apps/api/src/routes/billing/read.ts +0 -75
  159. package/templates/apps/api/src/routes/billing/usage.ts +0 -153
  160. package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
  161. package/templates/apps/api/src/routes/checkout/index.ts +0 -13
  162. package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
  163. package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
  164. package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
  165. package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
  166. package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
  167. package/templates/apps/api/src/routes/flows/README.md +0 -147
  168. package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
  169. package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
  170. package/templates/apps/api/src/routes/flows/index.ts +0 -202
  171. package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
  172. package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
  173. package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +0 -221
  174. package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
  175. package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
  176. package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
  177. package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
  178. package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
  179. package/templates/apps/api/src/routes/schedule/index.ts +0 -249
  180. package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
  181. package/templates/apps/api/src/routes/slack/actions.ts +0 -177
  182. package/templates/apps/api/src/routes/slack/index.ts +0 -38
  183. package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
  184. package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
  185. package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
  186. package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
  187. package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
  188. package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
  189. package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
  190. package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
  191. package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
  192. package/templates/apps/api/src/routes/webhooks/README.md +0 -80
  193. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
  194. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
  195. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
  196. package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
  197. package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
  198. package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
  199. package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
  200. package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
  201. package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
  202. package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
  203. package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
  204. package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
  205. package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
  206. package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
  207. package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
  208. package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
  209. package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +0 -684
  210. package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
  211. package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
  212. package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
  213. package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
  214. package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
  215. package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
  216. package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
  217. package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
  218. package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
  219. package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
  220. package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
  221. package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
  222. package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
  223. package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
  224. package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
  225. package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
  226. package/templates/apps/api/src/services/flow-engine.ts +0 -1296
  227. package/templates/apps/api/src/services/lead-promotion.ts +0 -176
  228. package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
  229. package/templates/apps/api/src/services/listing-promotion.ts +0 -342
  230. package/templates/apps/api/src/services/playbook-compile.ts +0 -398
  231. package/templates/apps/api/src/services/playbook-render.ts +0 -263
  232. package/templates/apps/api/src/services/project-listing-decision.ts +0 -510
  233. package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
  234. package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
  235. package/templates/apps/api/src/services/recommender-capture.ts +0 -835
  236. package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
  237. package/templates/apps/api/src/services/scheduling/index.ts +0 -63
  238. package/templates/apps/api/src/services/scheduling/types.ts +0 -88
  239. package/templates/apps/api/src/services/tenant-context.ts +0 -451
  240. package/templates/apps/api/src/services/usage-metering.ts +0 -699
  241. package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
  242. package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
  243. package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
  244. package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
  245. package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
  246. package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
  247. package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
  248. package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
  249. package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
  250. package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
  251. package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
  252. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
  253. package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
  254. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
  255. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
  256. package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
  257. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
  258. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
  259. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
  260. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
  261. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
  262. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
  263. package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
  264. package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
  265. package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
  266. package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
  267. package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
  268. package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
  269. package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
  270. package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
  271. package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
  272. package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
  273. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
  274. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
  275. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
  276. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
  277. package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
  278. package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
  279. package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
  280. package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
  281. package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
  282. package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
  283. package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
  284. package/templates/apps/web/content/en/blog/README.txt +0 -25
  285. package/templates/apps/web/content/en/docs/index.mdx +0 -28
  286. package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
  287. package/templates/apps/web/lib/blog-list-data.ts +0 -95
  288. package/templates/apps/web/lib/blog-post-data.ts +0 -79
  289. package/templates/apps/web/lib/source.ts +0 -34
  290. package/templates/apps/web/public/logomark-dark.svg +0 -4
  291. package/templates/apps/web/public/logomark-light.svg +0 -4
  292. package/templates/apps/web/source.config.ts +0 -78
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Authenticated site-template-bundle fetch (spec 5481 § 4; parent record
3
+ * D1, § 6).
4
+ *
5
+ * The client half of `GET /v1/templates/:name/:version` (#5480's route),
6
+ * reached publicly at `/api/templates/:path*`. Posture is
7
+ * `fetch-module.ts`'s, mirrored one-for-one rather than generalised into a
8
+ * shared helper: bearer-JWT auth, `redirect: "error"` so a redirected
9
+ * response cannot drop the bearer token onto an unintended host, an
10
+ * empty-token guard before any network call, and the same status mapping —
11
+ * 401 → licence invalid/revoked/lapsed, 403 → not included / term ended,
12
+ * 404 → content not yet available, other non-2xx → the upstream body.
13
+ *
14
+ * ONE word differs, and it is the whole reason this is a sibling rather
15
+ * than a second call site of `fetchModule` (spec 5481 § 3 DD5): the 404
16
+ * sentence names its subject, and the subject here is a TEMPLATE. Parent
17
+ * record D17 makes that sentence a specified outcome rather than an
18
+ * incidental one, so the fourth refusal reuses it — with the noun
19
+ * parameterised, since left alone it would call the template a module.
20
+ *
21
+ * Deliberately depends on neither `@working-theory/license` nor
22
+ * `@working-theory/cli`, for the reason `fetch-module.ts`'s file doc states
23
+ * at length: both are `publishConfig.access: "restricted"` and this is the
24
+ * one package published to PUBLIC npm, so a runtime dependency on either
25
+ * would break `pnpm install` for every Free consumer rather than merely
26
+ * gate the paid path. The registry's own 401 IS the licence verdict.
27
+ */
28
+ import pkg from "../../package.json" with { type: "json" };
29
+ export const DEFAULT_AILK_TEMPLATE_REGISTRY_URL = "https://ailaunchkit.ai/api/templates";
30
+ /**
31
+ * This package's own `package.json` `"version"` — a static JSON import for
32
+ * the reason `fetch-module.ts`'s `CREATE_AILK_VERSION` doc gives: `import.meta`
33
+ * is a SyntaxError under ts-jest's CommonJS transform, and this file is
34
+ * reachable from jest. A template bundle overlays the scaffold this
35
+ * `create-ailk` just wrote, so the default version request is the running
36
+ * package's own.
37
+ */
38
+ const CREATE_AILK_VERSION = pkg.version;
39
+ export class FetchTemplateError extends Error {
40
+ isLicenseError;
41
+ constructor(message,
42
+ /** True iff the failure was a license-gating signal (HTTP 401 or 403). */
43
+ isLicenseError = false) {
44
+ super(message);
45
+ this.isLicenseError = isLicenseError;
46
+ this.name = "FetchTemplateError";
47
+ }
48
+ }
49
+ /**
50
+ * Join `registryUrl` with `templateName` and `version` as two trailing path
51
+ * segments. `encodeURIComponent` on each is defense in depth —
52
+ * `parse-args.ts` already restricts `templateName` to `TEMPLATE_MANIFESTS`'s
53
+ * keys, but a hostile value can never smuggle a path separator or query
54
+ * string here regardless.
55
+ */
56
+ function buildUrl(registryUrl, templateName, version) {
57
+ const trimmed = registryUrl.replace(/\/+$/, "");
58
+ return `${trimmed}/${encodeURIComponent(templateName)}/${encodeURIComponent(version)}`;
59
+ }
60
+ export async function fetchTemplate(options) {
61
+ const { templateName, token } = options;
62
+ if (token.length === 0) {
63
+ throw new FetchTemplateError("internal error: fetchTemplate called without a token");
64
+ }
65
+ const version = options.version ?? CREATE_AILK_VERSION;
66
+ const registryUrl = options.registryUrl ??
67
+ process.env["AILK_TEMPLATE_REGISTRY_URL"] ??
68
+ DEFAULT_AILK_TEMPLATE_REGISTRY_URL;
69
+ const url = buildUrl(registryUrl, templateName, version);
70
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
71
+ let response;
72
+ try {
73
+ response = await fetchImpl(url, {
74
+ method: "GET",
75
+ headers: {
76
+ Authorization: `Bearer ${token}`,
77
+ Accept: "application/gzip, application/octet-stream",
78
+ },
79
+ redirect: "error",
80
+ });
81
+ }
82
+ catch (err) {
83
+ const detail = err instanceof Error ? err.message : String(err);
84
+ throw new FetchTemplateError(`template registry unreachable: ${detail}`);
85
+ }
86
+ if (response.status === 401) {
87
+ throw new FetchTemplateError(`your licence does not cover the "${templateName}" template (invalid, revoked, or lapsed)`,
88
+ /* isLicenseError */ true);
89
+ }
90
+ if (response.status === 403) {
91
+ throw new FetchTemplateError(`your licence does not include the "${templateName}" template, or its term ended before create-ailk ${version} was released — use a create-ailk version released during your term`,
92
+ /* isLicenseError */ true);
93
+ }
94
+ if (response.status === 404) {
95
+ throw new FetchTemplateError(`"${templateName}" template content not yet available — ping us at hello@ailaunchkit.ai`);
96
+ }
97
+ if (!response.ok) {
98
+ const body = await safeReadBody(response);
99
+ throw new FetchTemplateError(`template registry returned ${response.status} for "${templateName}": ${body}`);
100
+ }
101
+ const buf = await response.arrayBuffer();
102
+ return new Uint8Array(buf);
103
+ }
104
+ /**
105
+ * Best-effort error body extraction — the registry uses `{ error: "..." }`
106
+ * shapes, falls back to text, and swallows any read failure so the caller's
107
+ * message stays useful. `fetch-module.ts`'s helper of the same name.
108
+ */
109
+ async function safeReadBody(response) {
110
+ try {
111
+ const text = await response.text();
112
+ try {
113
+ const parsed = JSON.parse(text);
114
+ if (parsed !== null &&
115
+ typeof parsed === "object" &&
116
+ "error" in parsed &&
117
+ typeof parsed.error === "string") {
118
+ return parsed.error;
119
+ }
120
+ }
121
+ catch {
122
+ /* not JSON — fall through to raw text */
123
+ }
124
+ return text || response.statusText;
125
+ }
126
+ catch {
127
+ return response.statusText;
128
+ }
129
+ }
@@ -35,8 +35,18 @@ export interface ResolvePaidModulesOptions {
35
35
  licenseKey?: string | undefined;
36
36
  /** Forwarded to `fetchModule`; defaults to `AILK_MODULE_REGISTRY_URL` / the built-in default. */
37
37
  registryUrl?: string;
38
+ /** Forwarded to `fetchModule`; defaults to this package's own version. */
39
+ version?: string;
38
40
  /** Forwarded to `fetchModule`; defaults to `globalThis.fetch`. Never a live call in tests. */
39
41
  fetchImpl?: typeof fetch;
42
+ /**
43
+ * The site template whose `requires` put these modules in `paidModules`
44
+ * (spec 5481 § 3 DD5 rows 1-2). Present ⟹ every refusal below names it
45
+ * alongside the specific module, because the buyer asked for a TEMPLATE and
46
+ * a message naming only a module they never typed does not tell them what
47
+ * to do. Absent ⟹ a plain `--modules` run, whose messages are unchanged.
48
+ */
49
+ templateName?: string;
40
50
  }
41
51
  /**
42
52
  * Resolve every requested paid module to its fetched bundle bytes, or throw
@@ -28,7 +28,7 @@ export async function resolvePaidModules(options) {
28
28
  }
29
29
  const token = options.licenseKey ?? process.env["AILK_LICENSE_KEY"];
30
30
  if (token === undefined || token.length === 0) {
31
- throw new LicenseRequiredError(conversionHint(paidModules));
31
+ throw new LicenseRequiredError(conversionHint(paidModules, options.templateName));
32
32
  }
33
33
  const bundles = new Map();
34
34
  for (const moduleName of paidModules) {
@@ -39,6 +39,7 @@ export async function resolvePaidModules(options) {
39
39
  ...(options.registryUrl !== undefined
40
40
  ? { registryUrl: options.registryUrl }
41
41
  : {}),
42
+ ...(options.version !== undefined ? { version: options.version } : {}),
42
43
  ...(options.fetchImpl !== undefined
43
44
  ? { fetchImpl: options.fetchImpl }
44
45
  : {}),
@@ -48,7 +49,12 @@ export async function resolvePaidModules(options) {
48
49
  catch (err) {
49
50
  if (err instanceof FetchModuleError) {
50
51
  if (err.isLicenseError) {
51
- throw new LicenseRequiredError(`${err.message} → ${CONVERSION_HINT_URL}`);
52
+ // DD5 row 2: the mapped 401/403 already names the module the key
53
+ // does not cover; the template is appended so the buyer can see
54
+ // WHICH of the things they asked for needs it.
55
+ throw new LicenseRequiredError(options.templateName !== undefined
56
+ ? `${err.message}, required by template "${options.templateName}" → ${CONVERSION_HINT_URL}`
57
+ : `${err.message} → ${CONVERSION_HINT_URL}`);
52
58
  }
53
59
  // Not a license-gating signal (e.g. 404 "not yet available", or the
54
60
  // registry unreachable) — a generic failure, not the honest-denial
@@ -61,7 +67,10 @@ export async function resolvePaidModules(options) {
61
67
  }
62
68
  return bundles;
63
69
  }
64
- function conversionHint(paidModules) {
70
+ function conversionHint(paidModules, templateName) {
65
71
  const list = paidModules.join(", ");
66
- return `Pro module(s) requested (${list}) — set AILK_LICENSE_KEY first → ${CONVERSION_HINT_URL}`;
72
+ const requested = templateName !== undefined
73
+ ? `template "${templateName}" requires Pro module(s) (${list})`
74
+ : `Pro module(s) requested (${list})`;
75
+ return `${requested} — set AILK_LICENSE_KEY first → ${CONVERSION_HINT_URL}`;
67
76
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The derived set of scaffold-relative paths a keyless scaffold must not
3
+ * contain — sorted, deduped.
4
+ *
5
+ * Throws if the result is empty: `PAID_MODULES` is never empty (it is
6
+ * `RECOGNIZED_MODULES` minus `FREE_MODULES`) and every paid module registered
7
+ * today owns at least one file under `apps/` or a whole app, so an empty
8
+ * result means the derivation itself broke — not that there is nothing to
9
+ * check (DEF-007: a silent `[]` must fail loudly, not pass the gate).
10
+ */
11
+ export declare function derivePaidScaffoldPaths(): string[];
12
+ /**
13
+ * Every derived paid path that exists under `scaffoldDir` — the leaked set,
14
+ * sorted. Empty means the scaffold is clean.
15
+ */
16
+ export declare function findLeakedPaidPaths(scaffoldDir: string): string[];
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Derived paid-path list (R2-5374 — the no-paid-code CI gate, D-44).
3
+ *
4
+ * The set of scaffold-relative paths a keyless (Free) `create-ailk` scaffold
5
+ * must never contain, computed from `MODULE_SLICES` / `PAID_MODULES` at call
6
+ * time — never hand-maintained, so it grows automatically once commerce
7
+ * (D-47) and signal-collectors (D-48) gain their own `MODULE_SLICES` entries.
8
+ *
9
+ * Only two of `ModuleSlice`'s shapes name a path that can actually appear in a
10
+ * scaffold's own output tree (`build-templates.mjs`'s `SNAPSHOT_DIRS` is
11
+ * `["apps", "database"]` — see its own header comment; `packages/*` is never
12
+ * copied into a scaffold, it is consumed as an npm dependency):
13
+ *
14
+ * - `ownedFiles` entries that already sit under `apps/` — the scaffold
15
+ * copies that tree verbatim, so the path is identical on both sides.
16
+ * - `apps[name].path` — a paid module's OWN app directory. Multi-tenant's
17
+ * `workspace` app has been extracted out of this repo's own `apps/` tree
18
+ * into a sibling paid-apps root (its `ownedFiles` entry is a
19
+ * whole-directory glob naming that monorepo-internal location, not a
20
+ * scaffold one), so the app it introduces into a SCAFFOLD is named here
21
+ * instead, via the slice's own declared path. `api` / `mcp` / `web` are
22
+ * excluded: they are base apps present in
23
+ * EVERY scaffold regardless of licence — a module only ever grants
24
+ * import permissions into them, never introduces the directory, and the
25
+ * paid FILES inside them are already covered by the `ownedFiles` branch
26
+ * above.
27
+ *
28
+ * `ownedFiles` entries under `packages/` are excluded for the reason above.
29
+ * `namespaces` (database/*) are excluded too: each slice's own doc comment
30
+ * (see multi-tenant's in `module-architecture.ts`) records that a namespace
31
+ * directory mixes base and paid Prisma models in one `schema.prisma` file, so
32
+ * the DIRECTORY itself is not paid-owned even though some models inside it
33
+ * are — asserting it absent would be wrong on every scaffold, paid or free.
34
+ */
35
+ import { existsSync } from "node:fs";
36
+ import { join } from "node:path";
37
+ import { MODULE_SLICES } from "../module-architecture.js";
38
+ import { PAID_MODULES } from "./module-tier.js";
39
+ /** Base app paths present in every scaffold regardless of the selected modules. */
40
+ const BASE_APP_PATHS = new Set([
41
+ "apps/web",
42
+ "apps/api",
43
+ "apps/mcp",
44
+ ]);
45
+ /** A slice's `ownedFiles` entries that live under `apps/` — copied verbatim into a scaffold. */
46
+ function ownedFilesUnderApps(slice) {
47
+ return (slice.ownedFiles ?? [])
48
+ .filter((entry) => entry.startsWith("apps/"))
49
+ .map((entry) => entry.endsWith("/**") ? entry.slice(0, -"/**".length) : entry);
50
+ }
51
+ /** A slice's own app directories, excluding the base apps every scaffold ships. */
52
+ function introducedAppPaths(slice) {
53
+ return Object.values(slice.apps ?? {})
54
+ .map((app) => app.path)
55
+ .filter((path) => !BASE_APP_PATHS.has(path));
56
+ }
57
+ /**
58
+ * The derived set of scaffold-relative paths a keyless scaffold must not
59
+ * contain — sorted, deduped.
60
+ *
61
+ * Throws if the result is empty: `PAID_MODULES` is never empty (it is
62
+ * `RECOGNIZED_MODULES` minus `FREE_MODULES`) and every paid module registered
63
+ * today owns at least one file under `apps/` or a whole app, so an empty
64
+ * result means the derivation itself broke — not that there is nothing to
65
+ * check (DEF-007: a silent `[]` must fail loudly, not pass the gate).
66
+ */
67
+ export function derivePaidScaffoldPaths() {
68
+ const paths = new Set();
69
+ for (const moduleName of PAID_MODULES) {
70
+ const slice = MODULE_SLICES[moduleName];
71
+ for (const p of ownedFilesUnderApps(slice))
72
+ paths.add(p);
73
+ for (const p of introducedAppPaths(slice))
74
+ paths.add(p);
75
+ }
76
+ const sorted = [...paths].sort();
77
+ if (sorted.length === 0) {
78
+ throw new Error("derivePaidScaffoldPaths() computed an empty list — MODULE_SLICES and " +
79
+ "PAID_MODULES have drifted out of sync (DEF-007: this must never " +
80
+ "silently pass the no-paid-code gate)");
81
+ }
82
+ return sorted;
83
+ }
84
+ /**
85
+ * Every derived paid path that exists under `scaffoldDir` — the leaked set,
86
+ * sorted. Empty means the scaffold is clean.
87
+ */
88
+ export function findLeakedPaidPaths(scaffoldDir) {
89
+ return derivePaidScaffoldPaths().filter((relPath) => existsSync(join(scaffoldDir, relPath)));
90
+ }
@@ -1,26 +1,3 @@
1
- /**
2
- * Module → architecture-slice registry.
3
- *
4
- * The single source that turns the `--modules` selection into a scaffolded
5
- * site's `architecture.yaml` (its code-boundary config — the apps/packages/
6
- * namespaces the linter walks + the import rules it enforces). Each recognized
7
- * module maps to the architecture FRAGMENT it contributes; `deriveArchitecture`
8
- * (derive-architecture.ts) composes a module-independent BASE + the selected
9
- * slices into one deterministic document.
10
- *
11
- * Grounded in the root AILK `architecture.yaml` `composition_model`: a customer
12
- * site is the three-tier AILK app (data · api · presentation) COMPOSED of the
13
- * selected modules. `marketing` is the thin presentation default and is
14
- * validated end-to-end; `docs` (a mountable presentation surface) and
15
- * `multi-tenant` (a full-stack module) are registry-defined ahead of their
16
- * template trees (per spec #3787 D3). `scheduling` (#4633) contributes all
17
- * three tiers — the provider client, the /v1/schedule/* routes, and the
18
- * booker + confirmation pages — into the marketing surface.
19
- *
20
- * Design (spec #3787 D1): a code registry, NOT N hand-maintained per-module
21
- * `architecture.yaml` template files — a fixed template IS the drift this
22
- * removes.
23
- */
24
1
  import { type ModuleName } from "./parse-args.js";
25
2
  /** A forbidden import/usage pattern the architecture linter enforces. */
26
3
  export interface ForbiddenPattern {
@@ -52,6 +29,39 @@ export interface ArchNamespaces {
52
29
  members: Record<string, ArchNamespaceMember>;
53
30
  rules: string[];
54
31
  }
32
+ /**
33
+ * One page a site template's roster declares (spec 5478 § D1).
34
+ *
35
+ * Both producers emit this same row type: a template's BASE roster
36
+ * (`TemplateManifest.roster`, template-manifest.ts) and a module's
37
+ * `pageContributions` below. One vocabulary, so the merge has exactly one
38
+ * collision key to reconcile rather than two (§ D1/D2).
39
+ *
40
+ * Declared HERE rather than in `template-manifest.ts` (§ D3) because
41
+ * `ModuleSlice` carries it and `template-manifest.ts` already imports
42
+ * `MODULE_SLICES` + `normalizeModules` from this file — so the dependency
43
+ * stays a DAG (`parse-args` ← `module-architecture` ← `template-manifest`)
44
+ * instead of becoming a cycle in the value graph.
45
+ *
46
+ * A row carries no nav label: `GATEABLE_SURFACES` already carries
47
+ * `navLabelKey?` per surface (`surfaces.ts`) and the nav is derived from the
48
+ * roster (parent spec 5462 § D9), so a label here would be a second authority
49
+ * for one fact.
50
+ */
51
+ export interface PageRosterRow {
52
+ /**
53
+ * Site-relative route path, e.g. `/blog` or `/flows/[slug]/start`. This is
54
+ * the row's IDENTITY and the merge's collision key (§ D2): a path claimed by
55
+ * two sources is refused at build time, never resolved by a precedence rule
56
+ * (parent § D8).
57
+ */
58
+ route: string;
59
+ /**
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).
62
+ */
63
+ content?: readonly string[];
64
+ }
55
65
  /**
56
66
  * The architecture fragment one module contributes.
57
67
  *
@@ -127,6 +137,141 @@ export interface ModuleSlice {
127
137
  * which miss eleven and over-mark three.
128
138
  */
129
139
  ownedFiles?: string[];
140
+ /**
141
+ * The page rows and seed content this module contributes to any site
142
+ * template that takes it (spec 5478; parent spec 5462 § D5) — `ownedFiles`'s
143
+ * sibling, and a DIFFERENT kind of fact again from every field above.
144
+ * `ownedFiles` names which files in THIS monorepo physically belong to the
145
+ * module; `pageContributions` names which PAGES the module brings to a
146
+ * template's roster. `template-manifest.ts`'s `mergeRoster` reads it.
147
+ *
148
+ * A contribution never carries route CODE. How the route code reaches a
149
+ * scaffold is a property of the module, not of the contribution (parent
150
+ * § D6): `docs-blog` and `scheduling` list their route files in `ownedFiles`
151
+ * so the code rides their own licence-checked bundle, while
152
+ * `signal-collectors` deliberately does not — its `flows` routes reach a
153
+ * scaffold from the `templates/` snapshot, retained because
154
+ * `MODULE_SURFACES` enables the surface (see this file's
155
+ * `signal-collectors.ownedFiles` comment for why listing them there broke
156
+ * `sync-routes`, #5366). Both shapes declare their rows here identically.
157
+ *
158
+ * Unset for a module that brings no pages to any declared template:
159
+ * `marketing` (base, no `ownedFiles` and `MODULE_SURFACES.marketing` is
160
+ * `[]`), and — deferred, not absent — `multi-tenant` and `commerce`, whose
161
+ * rows are their own instances' to enumerate. An unset field contributes
162
+ * zero rows openly; it is not a partial merge.
163
+ */
164
+ pageContributions?: readonly PageRosterRow[];
165
+ /**
166
+ * Bundle-relative source → scaffold-relative destination (spec 5373 § D7).
167
+ * Every source must be a file the validated tar extraction produced in the
168
+ * bundle producer's staging directory — repo-relative, coinciding with the
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.
175
+ */
176
+ installMap?: Record<string, string>;
177
+ /**
178
+ * Plain text `extractModuleBundle` prints to stdout after a successful
179
+ * merge, with C0/C1 control characters (other than newline) stripped
180
+ * first so a bundle cannot inject terminal escape sequences.
181
+ */
182
+ postInstall?: string;
183
+ /**
184
+ * Declarative, all-or-nothing edits to files the buyer's scaffold already
185
+ * has (spec 5418 § 3 D1) — a sibling of `installMap`/`postInstall` above.
186
+ * `installMap`/`postInstall` can only ADD or PRINT; `patches` is the only
187
+ * mechanism that edits an existing scaffolded file (`package.json`,
188
+ * `tsconfig.json`, `next.config.mjs`, `jest.config.cjs`). Applied by
189
+ * `applyModulePatches` after `installMap`, before `postInstall` (D5).
190
+ */
191
+ patches?: ModulePatch[];
192
+ }
193
+ /**
194
+ * The four target-file kinds a patch can address (spec 5418 § 3 D1).
195
+ * `"json"` covers both `package.json` (object-shaped fields: `dependencies`,
196
+ * `devDependencies`, `scripts`) and `tsconfig.json` (array-shaped:
197
+ * `references`) — distinguished by which op shape a `path` pairs with, not
198
+ * by a fourth kind. `next.config.mjs`/`jest.config.cjs` are code, not data,
199
+ * so each gets its own kind: a `next-config` op has no meaning against
200
+ * `jest.config.cjs`, and vice versa.
201
+ */
202
+ export type ModulePatchKind = "json" | "next-config" | "jest-config";
203
+ /** One target file's patch set — every op in `ops` applies to `target`. */
204
+ export interface ModulePatch {
205
+ /**
206
+ * Scaffold-relative destination, e.g. "apps/web/package.json". Validated
207
+ * the same way an `installMap` destination is (D6): `resolve(targetDir,
208
+ * target)` must stay inside `targetDir`.
209
+ */
210
+ target: string;
211
+ kind: ModulePatchKind;
212
+ ops: ModulePatchOp[];
213
+ }
214
+ export type ModulePatchOp = JsonMergeKeysOp | JsonAppendUniqueOp | AddTranspilePackageOp | WrapExportWithOp | AddModuleNameMapperOp | AddTransformIgnorePatternOp | AddTransformOp;
215
+ /** kind: "json" — object-shaped field (package.json dependencies/devDependencies/scripts). */
216
+ export interface JsonMergeKeysOp {
217
+ op: "merge-keys";
218
+ /** JSON-pointer segments to an OBJECT, e.g. ["dependencies"]. */
219
+ path: string[];
220
+ /** key -> value; each key is added, never overwritten (D2). */
221
+ entries: Record<string, string>;
222
+ }
223
+ /** kind: "json" — array-shaped field (tsconfig.json references). */
224
+ export interface JsonAppendUniqueOp {
225
+ op: "append-unique";
226
+ /** Segments to an ARRAY, e.g. ["references"]. */
227
+ path: string[];
228
+ /** Appended iff not already deep-equal-present (D2). */
229
+ items: unknown[];
230
+ }
231
+ /** kind: "next-config". */
232
+ export interface AddTranspilePackageOp {
233
+ op: "add-transpile-package";
234
+ /** e.g. "@working-theory/fumadocs-schema". */
235
+ package: string;
236
+ }
237
+ /** kind: "next-config". */
238
+ export interface WrapExportWithOp {
239
+ op: "wrap-export-with";
240
+ /** Exact line, e.g. 'import { createMDX } from "fumadocs-mdx/next";\n'. */
241
+ importLine: string;
242
+ /** Exact substring to wrap, e.g. "withNextIntl(nextConfig)". */
243
+ find: string;
244
+ /** e.g. "withNextIntl(createMDX()(nextConfig))". */
245
+ replaceWith: string;
246
+ }
247
+ /** kind: "jest-config". */
248
+ export interface AddModuleNameMapperOp {
249
+ op: "add-module-name-mapper";
250
+ /**
251
+ * The moduleNameMapper KEY, e.g. "^@/\\.source$" — the runtime value this
252
+ * op carries has two backslash characters, matching jest.config.cjs's own
253
+ * bytes (D3's escaping rule: the value is the literal text as it occurs in
254
+ * the target file, never re-escaped for a notation layer above it).
255
+ */
256
+ pattern: string;
257
+ /** The mapped value. */
258
+ value: string;
259
+ }
260
+ /** kind: "jest-config". */
261
+ export interface AddTransformIgnorePatternOp {
262
+ op: "add-transform-ignore-pattern";
263
+ /** The CURRENT array-element string. */
264
+ find: string;
265
+ /** The WIDENED replacement. */
266
+ replaceWith: string;
267
+ }
268
+ /** kind: "jest-config". */
269
+ export interface AddTransformOp {
270
+ op: "add-transform";
271
+ /** The transform object's KEY (a regex string). */
272
+ pattern: string;
273
+ /** The mapped value, verbatim source text. */
274
+ value: string;
130
275
  }
131
276
  /**
132
277
  * The module-independent base: `apps/web` (the Next.js presentation shell every
@@ -173,3 +318,32 @@ export declare const MODULE_SLICES: Record<ModuleName, ModuleSlice>;
173
318
  * rejects unrecognized values upstream before they reach derivation).
174
319
  */
175
320
  export declare function normalizeModules(modules: string[]): ModuleName[];
321
+ /**
322
+ * Template-relative paths that must never be written into `templates/` for a
323
+ * keyless starter: every PAID module's `ownedFiles` entry that resolves under
324
+ * a `build-templates` snapshot root, plus every file under those roots that
325
+ * imports one (transitively), which would otherwise ship dangling.
326
+ *
327
+ * This is the SINGLE starter-removal input for all four R1 extractions
328
+ * (#5364 commerce, #5365 docs + blog, #5366 signal-collectors, #5367 Pro Plus
329
+ * + scheduling). A module joins by gaining `ownedFiles` entries — never by
330
+ * adding a path to a list here.
331
+ *
332
+ * The input is `ownedFiles`, NOT the `oss: false` marker set. The two answer
333
+ * different questions: the strip's authority is their union (see the
334
+ * `ownedFiles` doc above), while the STARTER's authority is `ownedFiles`
335
+ * alone. The marker set includes the content-write trio, which D-44 keeps in
336
+ * public code licence-checked at runtime — deriving the starter exclusion from
337
+ * markers would remove a surface D-44 says must ship.
338
+ *
339
+ * Entries outside the snapshot roots — every `packages/**` path, and the paid
340
+ * app roots — are not snapshotted at all and are filtered here rather than
341
+ * special-cased at the call site. Today every glob entry is also out-of-root,
342
+ * so globs never reach the literal-path matcher below.
343
+ *
344
+ * @param snapshotRoots repo-relative roots `build-templates.mjs` snapshots.
345
+ * @param repoRoot absolute path to the monorepo root, for the import scan.
346
+ * Passed in rather than derived from `import.meta.url`, which ts-jest cannot
347
+ * load — the same reason `sync-routes.ts` documents for staying pure.
348
+ */
349
+ export declare function paidModuleTemplateExclusions(snapshotRoots: readonly string[], repoRoot: string): string[];