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.
- package/component-catalog.md +50 -53
- package/dist/cli.js +19 -9
- package/dist/lib/apply-module-patches.d.ts +47 -0
- package/dist/lib/apply-module-patches.js +370 -0
- package/dist/lib/extract-module-bundle.d.ts +51 -3
- package/dist/lib/extract-module-bundle.js +327 -28
- package/dist/lib/fetch-module.d.ts +3 -38
- package/dist/lib/fetch-module.js +26 -8
- package/dist/lib/fetch-template.d.ts +20 -0
- package/dist/lib/fetch-template.js +129 -0
- package/dist/lib/module-license-gate.d.ts +10 -0
- package/dist/lib/module-license-gate.js +13 -4
- package/dist/lib/paid-paths.d.ts +16 -0
- package/dist/lib/paid-paths.js +90 -0
- package/dist/module-architecture.d.ts +197 -23
- package/dist/module-architecture.js +820 -25
- package/dist/parse-args.d.ts +9 -2
- package/dist/parse-args.js +46 -1
- package/dist/programmatic.d.ts +28 -2
- package/dist/programmatic.js +115 -8
- package/dist/surfaces.d.ts +12 -36
- package/dist/surfaces.js +77 -5
- package/dist/sync-routes.js +31 -1
- package/dist/template-manifest.d.ts +92 -0
- package/dist/template-manifest.js +168 -0
- package/dist/template-names.d.ts +41 -0
- package/dist/template-names.js +44 -0
- package/package.json +3 -2
- package/site-templates/marketing-site-free/ailk-template.json +8 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +103 -0
- package/site-templates/marketing-site-free/content/en/PrivacyPage/privacy.mdx +79 -0
- package/site-templates/marketing-site-free/content/en/TermsPage/terms.mdx +80 -0
- package/site-templates/marketing-site-free/provenance.json +4 -0
- package/templates/.claude/agents/web.md +2 -3
- package/templates/.claude/rules/architecture.md +5 -5
- package/templates/.claude/skills/README.md +2 -1
- package/templates/.env.example +21 -9
- package/templates/CONVENTIONS.md +6 -7
- package/templates/apps/api/.env.example +9 -0
- package/templates/apps/api/CLAUDE.md +33 -5
- package/templates/apps/api/package.json +0 -1
- package/templates/apps/api/src/__tests__/cors.test.ts +195 -0
- package/templates/apps/api/src/__tests__/module-exclusion.test.ts +60 -2
- package/templates/apps/api/src/__tests__/vercel-handler.test.ts +159 -0
- package/templates/apps/api/src/__tests__/workspace-module-registration.test.ts +166 -0
- package/templates/apps/api/src/config/__tests__/modules.test.ts +76 -0
- package/templates/apps/api/src/config/index.ts +19 -0
- package/templates/apps/api/src/config/modules.ts +12 -14
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +3 -0
- package/templates/apps/api/src/lib/__tests__/slice-load.test.ts +89 -0
- package/templates/apps/api/src/lib/slice-load.ts +90 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +52 -32
- package/templates/apps/api/src/openapi/__tests__/spec-identity.test.ts +182 -0
- package/templates/apps/api/src/openapi/__tests__/surface-drift.test.ts +293 -0
- package/templates/apps/api/src/openapi/index.ts +9 -3
- package/templates/apps/api/src/openapi/spec.ts +505 -65
- package/templates/apps/api/src/openapi/surface-drift.ts +195 -0
- package/templates/apps/api/src/routes/content/__tests__/create.test.ts +14 -0
- package/templates/apps/api/src/routes/content/__tests__/delete.test.ts +14 -0
- package/templates/apps/api/src/routes/content/__tests__/update.test.ts +14 -0
- package/templates/apps/api/src/routes/content/index.ts +38 -11
- package/templates/apps/api/src/routes/project-listings/__tests__/configured-application.test.ts +17 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/drafts.test.ts +17 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/founder-identity.test.ts +796 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +10 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/public.test.ts +38 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/resume-email.test.ts +814 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-answers.test.ts +10 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/site-key.test.ts +24 -0
- package/templates/apps/api/src/routes/project-listings/__tests__/structured-address.test.ts +17 -0
- package/templates/apps/api/src/routes/project-listings/index.ts +37 -10
- package/templates/apps/api/src/routes/project-listings/me.ts +10 -0
- package/templates/apps/api/src/routes/project-listings/patch-draft.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/public.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/respond.ts +11 -0
- package/templates/apps/api/src/routes/project-listings/resume-token.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/start.ts +30 -0
- package/templates/apps/api/src/routes/project-listings/submit.ts +5 -0
- package/templates/apps/api/src/routes/project-listings/verify-email.ts +150 -0
- package/templates/apps/api/src/server.ts +504 -137
- package/templates/apps/api/src/services/deliverable-fulfillment.ts +10 -0
- package/templates/apps/api/src/services/listing-config.ts +58 -0
- package/templates/apps/api/src/services/project-listing-resume-email.ts +326 -0
- package/templates/apps/api/src/services/project-listings.ts +229 -4
- package/templates/apps/api/src/vercel-handler.ts +60 -26
- package/templates/apps/mcp/.env.example +8 -0
- package/templates/apps/mcp/CLAUDE.md +2 -2
- package/templates/apps/mcp/__tests__/catalog-drift.test.ts +21 -0
- package/templates/apps/mcp/__tests__/config/modules.test.ts +78 -0
- package/templates/apps/mcp/__tests__/module-exclusion.test.ts +1 -1
- package/templates/apps/mcp/src/config/modules.ts +14 -13
- package/templates/apps/mcp/src/server.ts +8 -4
- package/templates/apps/mcp/src/tools/index.ts +7 -22
- package/templates/apps/web/.env.example +15 -0
- package/templates/apps/web/app/[locale]/(authed)/{waitlist/__tests__ → __tests__}/gate.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/flows/[slug]/FlowStepperClient.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.permalink.test.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/FlowStepperClient.test.tsx +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/flow.actions.test.ts +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/two-route-tool.test.tsx +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/flow.actions.ts +2 -1
- package/templates/apps/web/app/[locale]/flows/[slug]/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/flows/[slug]/start/page.tsx +1 -0
- package/templates/apps/web/app/[locale]/pricing/page.tsx +40 -0
- package/templates/apps/web/app/[locale]/privacy/page.tsx +40 -0
- package/templates/apps/web/app/[locale]/terms/page.tsx +40 -0
- package/templates/apps/web/app/llms.txt/route.ts +12 -9
- package/templates/apps/web/jest.config.cjs +19 -13
- package/templates/apps/web/lib/__tests__/server-api.test.ts +61 -0
- package/templates/apps/web/lib/server-api.ts +28 -12
- package/templates/apps/web/messages/en.json +58 -0
- package/templates/apps/web/next.config.mjs +1 -3
- package/templates/apps/web/package.json +0 -6
- package/templates/apps/web/public/android-chrome-192x192.png +0 -0
- package/templates/apps/web/public/android-chrome-512x512.png +0 -0
- package/templates/apps/web/public/apple-touch-icon.png +0 -0
- package/templates/apps/web/public/favicon-16x16.png +0 -0
- package/templates/apps/web/public/favicon-32x32.png +0 -0
- package/templates/apps/web/public/favicon.ico +0 -0
- package/templates/apps/web/public/favicon.svg +6 -3
- package/templates/apps/web/public/lockup-horizontal.svg +4 -0
- package/templates/apps/web/public/logomark.svg +4 -0
- package/templates/apps/web/public/site.webmanifest +2 -2
- package/templates/apps/web/public/wordmark.svg +4 -0
- package/templates/database/CHANGELOG.md +47 -0
- package/templates/database/inbox/schema.prisma +67 -0
- package/templates/database/migrations/20260914120000_listing_founder_identity/migration.sql +123 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +1 -1
- package/templates/.claude/skills/scaffold-commerce/SKILL.md +0 -807
- package/templates/.claude/skills/scaffold-commerce/references/lookup-keys-template.md +0 -71
- package/templates/.claude/skills/scaffold-commerce/references/marketplace-brand-pages.md +0 -83
- package/templates/.claude/skills/scaffold-commerce/templates/checkout-route.template.ts +0 -370
- package/templates/.claude/skills/scaffold-commerce/templates/invoice-route.template.ts +0 -376
- package/templates/.claude/skills/scaffold-commerce/templates/marketplace-cta-route.tsx.tmpl +0 -72
- package/templates/.claude/skills/scaffold-commerce/templates/payment-link-route.template.ts +0 -471
- package/templates/.claude/skills/scaffold-commerce/templates/portal-route.template.ts +0 -365
- package/templates/.claude/skills/scaffold-commerce/templates/stripe-config.template.ts +0 -60
- package/templates/.claude/skills/scaffold-commerce/templates/subscription-route.template.ts +0 -446
- package/templates/apps/api/src/__tests__/server.test.ts +0 -253
- package/templates/apps/api/src/bin/deliverable-resend.ts +0 -144
- package/templates/apps/api/src/bin/deliverable-upload.ts +0 -114
- package/templates/apps/api/src/bin/followup-sweep.ts +0 -44
- package/templates/apps/api/src/bin/listing-csv-sweep.ts +0 -46
- package/templates/apps/api/src/bin/seed-presets.ts +0 -58
- package/templates/apps/api/src/bin/seed-waitlist-experiments.ts +0 -104
- package/templates/apps/api/src/bin/session-retention-sweep.ts +0 -46
- package/templates/apps/api/src/lib/__tests__/stripe.test.ts +0 -74
- package/templates/apps/api/src/lib/stripe.ts +0 -52
- package/templates/apps/api/src/lib/tenant-db.ts +0 -225
- package/templates/apps/api/src/lib/ws-token.ts +0 -91
- package/templates/apps/api/src/middleware/tenant.ts +0 -99
- package/templates/apps/api/src/routes/billing/__tests__/portal.test.ts +0 -450
- package/templates/apps/api/src/routes/billing/__tests__/read.test.ts +0 -262
- package/templates/apps/api/src/routes/billing/__tests__/usage.test.ts +0 -436
- package/templates/apps/api/src/routes/billing/index.ts +0 -36
- package/templates/apps/api/src/routes/billing/portal.ts +0 -182
- package/templates/apps/api/src/routes/billing/read.ts +0 -75
- package/templates/apps/api/src/routes/billing/usage.ts +0 -153
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +0 -687
- package/templates/apps/api/src/routes/checkout/index.ts +0 -13
- package/templates/apps/api/src/routes/checkout/sessions.ts +0 -197
- package/templates/apps/api/src/routes/deliverables/__tests__/index.test.ts +0 -393
- package/templates/apps/api/src/routes/deliverables/index.ts +0 -200
- package/templates/apps/api/src/routes/flow-checkouts/__tests__/index.test.ts +0 -443
- package/templates/apps/api/src/routes/flow-checkouts/index.ts +0 -82
- package/templates/apps/api/src/routes/flows/README.md +0 -147
- package/templates/apps/api/src/routes/flows/__tests__/index.test.ts +0 -752
- package/templates/apps/api/src/routes/flows/__tests__/recommender.test.ts +0 -671
- package/templates/apps/api/src/routes/flows/index.ts +0 -202
- package/templates/apps/api/src/routes/flows/recommender.ts +0 -443
- package/templates/apps/api/src/routes/project-listings/__tests__/copy-edit.test.ts +0 -815
- package/templates/apps/api/src/routes/project-listings/__tests__/me-route-precedence.test.ts +0 -221
- package/templates/apps/api/src/routes/project-listings/__tests__/tenant-isolation.test.ts +0 -566
- package/templates/apps/api/src/routes/project-listings/get.ts +0 -70
- package/templates/apps/api/src/routes/project-listings/list.ts +0 -47
- package/templates/apps/api/src/routes/project-listings/patch.ts +0 -136
- package/templates/apps/api/src/routes/schedule/__tests__/index.test.ts +0 -490
- package/templates/apps/api/src/routes/schedule/index.ts +0 -249
- package/templates/apps/api/src/routes/slack/__tests__/actions.test.ts +0 -385
- package/templates/apps/api/src/routes/slack/actions.ts +0 -177
- package/templates/apps/api/src/routes/slack/index.ts +0 -38
- package/templates/apps/api/src/routes/waitlist-experiments/__tests__/tenant-isolation.test.ts +0 -577
- package/templates/apps/api/src/routes/waitlist-experiments/comparison.ts +0 -78
- package/templates/apps/api/src/routes/waitlist-experiments/index.ts +0 -41
- package/templates/apps/api/src/routes/waitlist-experiments/list.ts +0 -64
- package/templates/apps/api/src/routes/waitlist-experiments/respond.ts +0 -42
- package/templates/apps/api/src/routes/waitlist-experiments/signup.ts +0 -84
- package/templates/apps/api/src/routes/waitlist-experiments/signups.ts +0 -119
- package/templates/apps/api/src/routes/waitlist-signups/__tests__/index.test.ts +0 -238
- package/templates/apps/api/src/routes/waitlist-signups/index.ts +0 -101
- package/templates/apps/api/src/routes/webhooks/README.md +0 -80
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +0 -315
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-idempotency.test.ts +0 -247
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +0 -270
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-rate-limit.test.ts +0 -198
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +0 -633
- package/templates/apps/api/src/routes/webhooks/index.ts +0 -41
- package/templates/apps/api/src/routes/webhooks/stripe.ts +0 -481
- package/templates/apps/api/src/routes/workspaces/__tests__/config-tenant-isolation.test.ts +0 -356
- package/templates/apps/api/src/routes/workspaces/__tests__/tenant-isolation.test.ts +0 -671
- package/templates/apps/api/src/routes/workspaces/config.ts +0 -125
- package/templates/apps/api/src/routes/workspaces/index.ts +0 -194
- package/templates/apps/api/src/routes/workspaces/sessions.ts +0 -218
- package/templates/apps/api/src/services/__tests__/flow-aggregate.test.ts +0 -425
- package/templates/apps/api/src/services/__tests__/flow-engine.test.ts +0 -2840
- package/templates/apps/api/src/services/__tests__/lead-promotion.test.ts +0 -393
- package/templates/apps/api/src/services/__tests__/listing-csv-sweep.test.ts +0 -560
- package/templates/apps/api/src/services/__tests__/listing-promotion.test.ts +0 -684
- package/templates/apps/api/src/services/__tests__/playbook-compile.test.ts +0 -406
- package/templates/apps/api/src/services/__tests__/playbook-render.test.ts +0 -290
- package/templates/apps/api/src/services/__tests__/project-listing-decision.test.ts +0 -736
- package/templates/apps/api/src/services/__tests__/project-listing-flow.test.ts +0 -475
- package/templates/apps/api/src/services/__tests__/project-listing-issue.test.ts +0 -340
- package/templates/apps/api/src/services/__tests__/recommender-capture.test.ts +0 -983
- package/templates/apps/api/src/services/__tests__/tenant-context-cascade.test.ts +0 -71
- package/templates/apps/api/src/services/__tests__/tenant-context.test.ts +0 -379
- package/templates/apps/api/src/services/__tests__/usage-metering.test.ts +0 -768
- package/templates/apps/api/src/services/__tests__/waitlist-dashboard.test.ts +0 -1314
- package/templates/apps/api/src/services/__tests__/waitlist-experiments.test.ts +0 -341
- package/templates/apps/api/src/services/__tests__/waitlist-followup.test.ts +0 -567
- package/templates/apps/api/src/services/__tests__/waitlist-scoring.test.ts +0 -474
- package/templates/apps/api/src/services/__tests__/waitlist-signups.test.ts +0 -354
- package/templates/apps/api/src/services/flow-aggregate.ts +0 -232
- package/templates/apps/api/src/services/flow-checkouts.ts +0 -123
- package/templates/apps/api/src/services/flow-engine.ts +0 -1296
- package/templates/apps/api/src/services/lead-promotion.ts +0 -176
- package/templates/apps/api/src/services/listing-csv-sweep.ts +0 -455
- package/templates/apps/api/src/services/listing-promotion.ts +0 -342
- package/templates/apps/api/src/services/playbook-compile.ts +0 -398
- package/templates/apps/api/src/services/playbook-render.ts +0 -263
- package/templates/apps/api/src/services/project-listing-decision.ts +0 -510
- package/templates/apps/api/src/services/project-listing-flow.ts +0 -426
- package/templates/apps/api/src/services/project-listing-issue.ts +0 -251
- package/templates/apps/api/src/services/recommender-capture.ts +0 -835
- package/templates/apps/api/src/services/scheduling/cal-provider.ts +0 -392
- package/templates/apps/api/src/services/scheduling/index.ts +0 -63
- package/templates/apps/api/src/services/scheduling/types.ts +0 -88
- package/templates/apps/api/src/services/tenant-context.ts +0 -451
- package/templates/apps/api/src/services/usage-metering.ts +0 -699
- package/templates/apps/api/src/services/waitlist-dashboard.ts +0 -947
- package/templates/apps/api/src/services/waitlist-experiments.ts +0 -213
- package/templates/apps/api/src/services/waitlist-followup.ts +0 -486
- package/templates/apps/api/src/services/waitlist-scoring.ts +0 -166
- package/templates/apps/api/src/services/waitlist-signups.ts +0 -165
- package/templates/apps/mcp/__tests__/schedule_tools.test.ts +0 -242
- package/templates/apps/mcp/src/tools/create_booking.ts +0 -58
- package/templates/apps/mcp/src/tools/get_event_meta.ts +0 -55
- package/templates/apps/mcp/src/tools/get_flow.ts +0 -61
- package/templates/apps/mcp/src/tools/list_availability.ts +0 -67
- package/templates/apps/mcp/src/tools/submit_flow_step.ts +0 -83
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/page.tsx +0 -309
- package/templates/apps/web/app/[locale]/(authed)/waitlist/[experimentId]/signups/[leadId]/page.tsx +0 -149
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/comparison.test.tsx +0 -273
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/list.test.tsx +0 -171
- package/templates/apps/web/app/[locale]/(authed)/waitlist/__tests__/signup.test.tsx +0 -179
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ExperimentsTable.tsx +0 -116
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/OfferFunnelTable.tsx +0 -66
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/RollupTicker.tsx +0 -61
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/ScoreTrace.tsx +0 -114
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/SignupsTable.tsx +0 -139
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/WaitlistComparisonTabs.tsx +0 -85
- package/templates/apps/web/app/[locale]/(authed)/waitlist/components/status-badges.tsx +0 -67
- package/templates/apps/web/app/[locale]/(authed)/waitlist/page.tsx +0 -179
- package/templates/apps/web/app/[locale]/blog/[[...slug]]/page.tsx +0 -178
- package/templates/apps/web/app/[locale]/dev/purchase/__tests__/actions.test.ts +0 -160
- package/templates/apps/web/app/[locale]/dev/purchase/error.tsx +0 -25
- package/templates/apps/web/app/[locale]/dev/purchase/page.tsx +0 -117
- package/templates/apps/web/app/[locale]/dev/purchase/purchase.actions.ts +0 -58
- package/templates/apps/web/app/[locale]/docs/[[...slug]]/page.tsx +0 -119
- package/templates/apps/web/app/[locale]/flows/[slug]/__tests__/checkout.actions.test.ts +0 -287
- package/templates/apps/web/app/[locale]/flows/[slug]/checkout.actions.ts +0 -254
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsClient.tsx +0 -532
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/ResultsEmailStep.tsx +0 -121
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/ResultsClient.checkout.test.tsx +0 -457
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/__tests__/page.test.tsx +0 -396
- package/templates/apps/web/app/[locale]/flows/[slug]/results/[payload]/page.tsx +0 -114
- package/templates/apps/web/app/[locale]/projects/apply/ListingApplicationClient.tsx +0 -110
- package/templates/apps/web/app/[locale]/projects/apply/__tests__/ListingApplicationClient.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/schedule/__tests__/page.test.tsx +0 -117
- package/templates/apps/web/app/[locale]/schedule/confirmed/__tests__/page.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/schedule/confirmed/page.tsx +0 -92
- package/templates/apps/web/app/[locale]/schedule/page.tsx +0 -82
- package/templates/apps/web/content/en/blog/README.txt +0 -25
- package/templates/apps/web/content/en/docs/index.mdx +0 -28
- package/templates/apps/web/lib/__mocks__/source-server.js +0 -31
- package/templates/apps/web/lib/blog-list-data.ts +0 -95
- package/templates/apps/web/lib/blog-post-data.ts +0 -79
- package/templates/apps/web/lib/source.ts +0 -34
- package/templates/apps/web/public/logomark-dark.svg +0 -4
- package/templates/apps/web/public/logomark-light.svg +0 -4
- 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
|
-
|
|
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
|
-
|
|
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[];
|