create-ailk 0.7.0 → 0.9.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/README.md +112 -10
- package/component-catalog.json +798 -49
- package/component-catalog.md +9 -6
- package/dist/add-module.d.ts +20 -0
- package/dist/add-module.js +43 -7
- package/dist/add-route.d.ts +56 -0
- package/dist/add-route.js +286 -0
- package/dist/apply-design-md.d.ts +7 -0
- package/dist/apply-design-md.js +50 -0
- package/dist/apply-design.d.ts +64 -0
- package/dist/apply-design.js +273 -0
- package/dist/claude-md.d.ts +111 -0
- package/dist/claude-md.js +334 -0
- package/dist/cli.js +114 -28
- package/dist/derive-architecture.js +41 -36
- package/dist/design-compile.d.ts +71 -0
- package/dist/design-compile.js +98 -0
- package/dist/design-package.d.ts +83 -0
- package/dist/design-package.js +468 -0
- package/dist/design-presets.d.ts +86 -0
- package/dist/design-presets.js +642 -0
- package/dist/design-scaffold.d.ts +76 -0
- package/dist/design-scaffold.js +220 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +4 -1
- package/dist/legacy-nav-renderers.d.ts +45 -0
- package/dist/legacy-nav-renderers.js +545 -0
- package/dist/lib/auth-secret-file.d.ts +19 -0
- package/dist/lib/auth-secret-file.js +112 -0
- package/dist/lib/design-checks.d.ts +78 -0
- package/dist/lib/design-checks.js +174 -0
- package/dist/lib/extract-module-bundle.js +5 -1
- package/dist/lib/license-key-files.d.ts +9 -0
- package/dist/lib/license-key-files.js +2 -2
- package/dist/lib/module-license-gate.d.ts +89 -0
- package/dist/lib/module-license-gate.js +171 -12
- package/dist/lib/module-tier.d.ts +45 -16
- package/dist/lib/module-tier.js +76 -0
- package/dist/lib/pro-callout.js +10 -2
- package/dist/lib/render-verdict.d.ts +52 -0
- package/dist/lib/render-verdict.js +77 -0
- package/dist/lib/scaffold-render-sweep.d.ts +105 -0
- package/dist/lib/scaffold-render-sweep.js +236 -0
- package/dist/module-architecture.d.ts +19 -16
- package/dist/module-architecture.js +64 -21
- package/dist/parse-args.d.ts +88 -10
- package/dist/parse-args.js +272 -21
- package/dist/programmatic.d.ts +66 -12
- package/dist/programmatic.js +361 -34
- package/dist/site-chrome.d.ts +19 -9
- package/dist/site-chrome.js +23 -13
- package/dist/site-manifest.d.ts +25 -0
- package/dist/site-manifest.js +254 -0
- package/dist/site-roster.d.ts +41 -0
- package/dist/site-roster.js +149 -11
- package/dist/starter-grid.d.ts +232 -0
- package/dist/starter-grid.js +545 -0
- package/dist/surfaces.d.ts +41 -9
- package/dist/surfaces.js +268 -92
- package/dist/template-manifest.d.ts +65 -29
- package/dist/template-manifest.js +161 -382
- package/dist/template-names.d.ts +14 -12
- package/dist/template-names.js +13 -17
- package/package.json +5 -4
- package/site-templates/_base/content/en/Feature/feature-1.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-2.mdx +69 -0
- package/site-templates/_base/content/en/Feature/feature-3.mdx +69 -0
- package/site-templates/_base/content/en/FeaturesList/features.mdx +91 -0
- package/site-templates/_base/content/en/GlossaryList/glossary.mdx +0 -1
- package/site-templates/_base/content/en/NewsletterSignup/newsletter.mdx +0 -1
- package/site-templates/_base/content/en/PortfolioItem/sample-four.mdx +105 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-one.mdx +34 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-three.mdx +94 -0
- package/site-templates/_base/content/en/PortfolioItem/sample-two.mdx +45 -0
- package/site-templates/_base/content/en/PortfolioList/portfolio.mdx +169 -0
- package/site-templates/_base/content/en/PrivacyPage/privacy.mdx +15 -0
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +70 -0
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +70 -0
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +70 -0
- package/site-templates/{minimalism-site/TEMPLATE-MANIFEST.json → aliases/minimalism-site.TEMPLATE-MANIFEST.json} +72 -40
- package/site-templates/designs/minimalism/DESIGN.md +98 -6
- package/site-templates/designs/minimalism/sample/README.md +48 -0
- package/site-templates/designs/standard/DESIGN.md +103 -6
- package/site-templates/marketing-site/DESIGN.md +103 -6
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +144 -47
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +82 -41
- package/site-templates/marketing-site-free/DESIGN.md +103 -6
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +150 -48
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +114 -67
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/marketing-site-free/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/marketing-site-free/content/en/Pricing/pricing.mdx +199 -0
- package/site-templates/saas-product/DESIGN.md +103 -6
- package/site-templates/saas-product/TEMPLATE-MANIFEST.md +9 -1
- package/site-templates/{simple-service → shapes/local-business-service}/TEMPLATE-MANIFEST.json +210 -81
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +356 -0
- package/site-templates/shapes/local-business-service/ailk-template.json +10 -0
- package/site-templates/{simple-service → shapes/local-business-service}/content/en/AboutPage/about.mdx +4 -4
- package/site-templates/shapes/local-business-service/content/en/HomePage/home.mdx +70 -0
- package/site-templates/shapes/local-business-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/local-business-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/{minimalism-site → shapes/local-business-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +7 -8
- package/site-templates/shapes/local-business-service/content/en/Service/first-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/second-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/Service/third-job.mdx +39 -0
- package/site-templates/shapes/local-business-service/content/en/ServicesList/services.mdx +61 -0
- package/site-templates/shapes/local-business-service/provenance.json +4 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +471 -0
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +354 -0
- package/site-templates/shapes/organization-service/ailk-template.json +10 -0
- package/site-templates/shapes/organization-service/content/en/AboutPage/about.mdx +38 -0
- package/site-templates/shapes/organization-service/content/en/HomePage/home.mdx +85 -0
- package/site-templates/shapes/organization-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/organization-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/shapes/organization-service/content/en/PurchasePolicyPage/purchase-policy.mdx +123 -0
- package/site-templates/{minimalism-site → shapes/organization-service}/content/en/ServicesList/services.mdx +3 -4
- package/site-templates/shapes/organization-service/provenance.json +4 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +478 -0
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +353 -0
- package/site-templates/shapes/person-service/ailk-template.json +10 -0
- package/site-templates/shapes/person-service/content/_site.mdx +85 -0
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +39 -0
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +97 -0
- package/site-templates/shapes/person-service/content/en/NewsletterSignup/newsletter.mdx +59 -0
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +171 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/PurchasePolicyPage/purchase-policy.mdx +10 -11
- package/site-templates/shapes/person-service/content/en/Service/first-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/second-engagement.mdx +38 -0
- package/site-templates/shapes/person-service/content/en/Service/third-engagement.mdx +38 -0
- package/site-templates/{simple-service → shapes/person-service}/content/en/ServicesList/services.mdx +16 -16
- package/site-templates/shapes/person-service/provenance.json +4 -0
- package/templates/.claude/rules/architecture.md +7 -0
- package/templates/.claude/skills/README.md +5 -3
- package/templates/.claude/skills/add-auth/references/env-vars.md +22 -15
- package/templates/.claude/skills/add-auth/references/oauth-app-setup.md +3 -2
- package/templates/.claude/skills/add-page/SKILL.md +241 -238
- package/templates/.claude/skills/add-page/references/page-types.md +85 -72
- package/templates/.claude/skills/add-page/scripts/site-pages.mjs +87 -0
- package/templates/.claude/skills/add-page/templates/comparison-page.mdx.tmpl +99 -0
- package/templates/.claude/skills/add-page/templates/content.mdx.tmpl +8 -18
- package/templates/.claude/skills/add-page/templates/flow-landing-page.mdx.tmpl +43 -0
- package/templates/.claude/skills/add-page/templates/report-page.mdx.tmpl +52 -0
- package/templates/.claude/skills/add-page/templates/reports-list.mdx.tmpl +38 -0
- package/templates/.claude/skills/check-site/SKILL.md +23 -0
- package/templates/.claude/skills/fill-page/SKILL.md +26 -7
- package/templates/.claude/skills/fill-page/profiles/plumbing.yaml +1 -1
- package/templates/.claude/skills/setup-database/SKILL.md +156 -0
- package/templates/.claude/skills/setup-database/scripts/verify-inbox.ts +154 -0
- package/templates/.claude/skills/setup-deploy/SKILL.md +64 -32
- package/templates/.claude/skills/setup-deploy/references/env-vars-template.md +44 -36
- package/templates/.claude/skills/setup-deploy/references/runbook-template.md +58 -102
- package/templates/.claude/skills/update-page/SKILL.md +117 -0
- package/templates/.claude/skills/update-page/scripts/update-page.ts +640 -0
- package/templates/.claude/skills/update-page/templates/redirects.mjs.tmpl +31 -0
- package/templates/.claude/skills/upgrade-plan/SKILL.md +12 -8
- package/templates/.github/workflows/ci.yml +4 -0
- package/templates/.prettierignore +1 -0
- package/templates/CLAUDE.md +146 -103
- package/templates/DESIGN.md +103 -6
- package/templates/README.md +63 -43
- package/templates/apps/api/.env.example +2 -1
- package/templates/apps/api/CLAUDE.md +3 -2
- package/templates/apps/api/src/__tests__/commerce-claim-registration.test.ts +2 -0
- package/templates/apps/api/src/__tests__/server.test.ts +116 -0
- package/templates/apps/api/src/config/__tests__/index.test.ts +115 -14
- package/templates/apps/api/src/config/index.ts +35 -16
- package/templates/apps/api/src/lib/__tests__/lead-capture-store.test.ts +30 -0
- package/templates/apps/api/src/lib/__tests__/lead-delivery.test.ts +96 -0
- package/templates/apps/api/src/lib/lead-capture-store.ts +12 -0
- package/templates/apps/api/src/lib/lead-delivery.ts +52 -0
- package/templates/apps/api/src/lib/stripe.ts +9 -0
- package/templates/apps/api/src/middleware/__tests__/auth.optional-cookie.test.ts +140 -0
- package/templates/apps/api/src/middleware/auth.ts +54 -0
- package/templates/apps/api/src/openapi/spec.ts +63 -12
- package/templates/apps/api/src/routes/checkout/__tests__/session.test.ts +5 -2
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +628 -0
- package/templates/apps/api/src/routes/checkout/sessions.ts +222 -24
- package/templates/apps/api/src/routes/leads/__tests__/capture.test.ts +88 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +100 -0
- package/templates/apps/api/src/routes/leads/index.ts +46 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-flow-checkout.test.ts +1 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-org-billing.test.ts +5 -1
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe.test.ts +521 -20
- package/templates/apps/api/src/routes/webhooks/stripe.ts +124 -32
- package/templates/apps/api/src/server.ts +35 -0
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +194 -0
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +124 -0
- package/templates/apps/api/src/services/__tests__/revalidation.test.ts +90 -15
- package/templates/apps/api/src/services/checkout-buyer.ts +72 -0
- package/templates/apps/api/src/services/lead-routing.ts +140 -30
- package/templates/apps/api/src/services/leads-inbox.ts +48 -11
- package/templates/apps/api/src/services/revalidation.ts +60 -15
- package/templates/apps/mcp/__tests__/parity.test.ts +5 -4
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +30 -24
- package/templates/apps/mcp/src/tools/create_project.ts +21 -6
- package/templates/apps/mcp/src/tools/purchase.ts +4 -2
- package/templates/apps/web/.env.example +13 -0
- package/templates/apps/web/CLAUDE.md +22 -28
- package/templates/apps/web/app/[locale]/(auth)/layout.tsx +1 -1
- package/templates/apps/web/app/[locale]/(authed)/leads/__tests__/page.test.tsx +24 -0
- package/templates/apps/web/app/[locale]/(authed)/leads/page.tsx +17 -6
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +110 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/page.tsx +60 -0
- package/templates/apps/web/app/[locale]/[slug]/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/alternatives/__tests__/page.test.tsx +161 -0
- package/templates/apps/web/app/[locale]/alternatives/page.tsx +104 -0
- package/templates/apps/web/app/[locale]/contact/ConversationalIntakeWrapper.tsx +14 -6
- package/templates/apps/web/app/[locale]/contact/__tests__/ConversationalIntakeWrapper.test.tsx +10 -9
- package/templates/apps/web/app/[locale]/contact/__tests__/contact-form-text.test.ts +32 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/contact.actions.test.ts +129 -0
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +227 -84
- package/templates/apps/web/app/[locale]/contact/contact-form-text.ts +50 -0
- package/templates/apps/web/app/[locale]/contact/contact.actions.ts +73 -20
- package/templates/apps/web/app/[locale]/contact/page.tsx +105 -15
- package/templates/apps/web/app/[locale]/contact/site-contact-email.ts +54 -0
- package/templates/apps/web/app/[locale]/features/[slug]/page.tsx +64 -0
- package/templates/apps/web/app/[locale]/features/page.tsx +52 -0
- package/templates/apps/web/app/[locale]/{use-cases → for}/[slug]/page.tsx +21 -5
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +73 -0
- package/templates/apps/web/app/[locale]/{use-cases → for}/page.tsx +8 -7
- package/templates/apps/web/app/[locale]/glossary/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/layout.tsx +65 -17
- package/templates/apps/web/app/[locale]/newsletter/__tests__/newsletter.actions.test.ts +75 -0
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +163 -0
- package/templates/apps/web/app/[locale]/newsletter/newsletter.actions.ts +66 -0
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +106 -14
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +59 -0
- package/templates/apps/web/app/[locale]/{book-a-call → portfolio}/page.tsx +12 -13
- package/templates/apps/web/app/[locale]/vs/[slug]/__tests__/page.test.tsx +112 -0
- package/templates/apps/web/app/[locale]/vs/[slug]/page.tsx +70 -0
- package/templates/apps/web/app/api/revalidate/__tests__/route.test.ts +236 -44
- package/templates/apps/web/app/api/revalidate/route.ts +78 -36
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +79 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +104 -11
- package/templates/apps/web/lib/__tests__/structured-data.test.tsx +21 -4
- package/templates/apps/web/lib/locale-guard.ts +18 -0
- package/templates/apps/web/lib/meet-placeholder.ts +5 -0
- package/templates/apps/web/lib/nav-config.ts +50 -37
- package/templates/apps/web/lib/site-brand.tsx +13 -9
- package/templates/apps/web/lib/site-identity.ts +14 -0
- package/templates/apps/web/lib/site-logo.tsx +36 -0
- package/templates/apps/web/lib/site-theme.ts +46 -26
- package/templates/apps/web/messages/en.json +45 -5
- package/templates/apps/web/middleware.ts +28 -4
- package/templates/content/_site.mdx +4 -2
- package/templates/content/en/BlogList/blog.mdx +37 -16
- package/templates/content/en/BookCall/book-a-call.mdx +80 -0
- package/templates/content/en/BookCall/onsite-estimate.mdx +56 -0
- package/templates/content/en/BookCall/onsite-service.mdx +56 -0
- package/templates/content/en/BookCall/start-project.mdx +56 -0
- package/templates/content/en/ContactPage/contact.mdx +43 -34
- package/templates/content/en/HomePage/home.mdx +10 -22
- package/templates/content/en/LandingPage/meet.mdx +59 -0
- package/templates/content/en/PurchasePolicyPage/purchase-policy.mdx +0 -1
- package/templates/content/en/UseCasesList/{use-cases.mdx → for.mdx} +19 -18
- package/templates/database/CHANGELOG.md +14 -0
- package/templates/database/package.json +1 -1
- package/templates/package.json +2 -1
- package/templates/tools/check-claude-md.ts +128 -0
- package/versions.json +33 -33
- package/site-templates/_base/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/_base/content/en/UseCase/first-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/second-use-case.mdx +0 -23
- package/site-templates/_base/content/en/UseCase/third-use-case.mdx +0 -23
- package/site-templates/minimalism-site/DESIGN.md +0 -162
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +0 -470
- package/site-templates/minimalism-site/ailk-template.json +0 -23
- package/site-templates/minimalism-site/content/en/AboutPage/about.mdx +0 -110
- package/site-templates/minimalism-site/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/minimalism-site/content/en/ContactPage/contact.mdx +0 -34
- package/site-templates/minimalism-site/content/en/FAQPage/faq.mdx +0 -57
- package/site-templates/minimalism-site/content/en/LandingPage/referral.mdx +0 -77
- package/site-templates/minimalism-site/content/en/LandingPage/reports.mdx +0 -73
- package/site-templates/minimalism-site/content/en/ReportPage/annual-letter-2024.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/field-notes-2023.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/platform-shift-2025.mdx +0 -52
- package/site-templates/minimalism-site/content/en/ReportPage/state-of-the-market-2026.mdx +0 -52
- package/site-templates/minimalism-site/flows/referral.flow.json +0 -71
- package/site-templates/minimalism-site/provenance.json +0 -4
- package/site-templates/simple-service/DESIGN.md +0 -162
- package/site-templates/simple-service/TEMPLATE-MANIFEST.md +0 -333
- package/site-templates/simple-service/ailk-template.json +0 -11
- package/site-templates/simple-service/content/en/BookCall/book-call.mdx +0 -53
- package/site-templates/simple-service/content/en/ContactPage/contact.mdx +0 -32
- package/site-templates/simple-service/content/en/FAQPage/faq.mdx +0 -56
- package/site-templates/simple-service/content/en/HomePage/home.mdx +0 -85
- package/site-templates/simple-service/content/en/Service/landscape-design.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/lawn-maintenance.mdx +0 -40
- package/site-templates/simple-service/content/en/Service/seasonal-cleanup.mdx +0 -40
- package/site-templates/simple-service/provenance.json +0 -4
- package/templates/.claude/skills/add-page/templates/route.tsx.tmpl +0 -28
- package/templates/apps/web/app/[locale]/contact/ContactForm.tsx +0 -211
- package/templates/apps/web/app/[locale]/contact/__tests__/ContactForm.test.tsx +0 -96
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +0 -72
- package/templates/content/en/SoftwareProduct/product.mdx +0 -175
- /package/site-templates/{minimalism-site → designs/minimalism/sample}/content/en/HomePage/home.mdx +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-1.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-2.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-3.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-4.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-5.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/avatar-placeholder-6.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/hero-backdrop.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-1.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-2.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/logo-placeholder-3.svg +0 -0
- /package/site-templates/{minimalism-site/assets → designs/minimalism/sample/public}/wordmark-placeholder.svg +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/first-engagement.mdx +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/second-engagement.mdx +0 -0
- /package/site-templates/{minimalism-site → shapes/organization-service}/content/en/Service/third-engagement.mdx +0 -0
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { classifyModules } from "./lib/module-tier.js";
|
|
2
2
|
import { MODULE_SLICES, normalizeModules, } from "./module-architecture.js";
|
|
3
|
+
import { ALIASES, SHAPE_ROSTERS, SHAPE_STARTER_ALIASES, } from "./starter-grid.js";
|
|
4
|
+
import { RECOGNIZED_TEMPLATES } from "./template-names.js";
|
|
3
5
|
/**
|
|
4
6
|
* The closed **shape** enum (starter-template contract § The three axes,
|
|
5
7
|
* operator-disposed 2026-09-24) — `<primary node>-<offering node>` in
|
|
@@ -17,28 +19,13 @@ export const STARTER_SHAPES = [
|
|
|
17
19
|
];
|
|
18
20
|
/**
|
|
19
21
|
* The closed **design** enum (same contract section) — the look applied to
|
|
20
|
-
* any shape; orthogonal to it by construction.
|
|
22
|
+
* any shape; orthogonal to it by construction. Each name RESOLVES to a
|
|
23
|
+
* package at `site-templates/designs/<name>/` (#6813 D2/D3, D11 pins this set
|
|
24
|
+
* equal to that directory listing): `resolveDesign` (`design-scaffold.ts`)
|
|
25
|
+
* reads the package's own `layout`, `colors` and treatments — no design fact
|
|
26
|
+
* lives in this module, or anywhere in `packages/create-ailk/src`, any more.
|
|
21
27
|
*/
|
|
22
28
|
export const STARTER_DESIGNS = ["standard", "minimalism"];
|
|
23
|
-
/**
|
|
24
|
-
* Each design's layout knobs (#6638) — the part of a design that is chrome,
|
|
25
|
-
* so a template names `design:` instead of re-typing a `layout` block, and any
|
|
26
|
-
* shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
|
|
27
|
-
* an explicit `TemplateManifest.layout` wins field by field, then this, then
|
|
28
|
-
* `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
|
|
29
|
-
* spelling them out here would be a second copy of them to drift. A plain
|
|
30
|
-
* object, not a file: this module touches no disk. The design's documented
|
|
31
|
-
* look lives in `site-templates/designs/<design>/DESIGN.md`.
|
|
32
|
-
*/
|
|
33
|
-
export const DESIGN_LAYOUTS = {
|
|
34
|
-
standard: {},
|
|
35
|
-
minimalism: {
|
|
36
|
-
nav: "mark-only",
|
|
37
|
-
footer: "centered",
|
|
38
|
-
announcementBanner: false,
|
|
39
|
-
home: "deck",
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
29
|
/**
|
|
43
30
|
* The base layer's directory under `site-templates/` (#5618 D1) — the seed
|
|
44
31
|
* files every template reaches by declaring `extends`, installed by
|
|
@@ -69,386 +56,178 @@ export class TemplateError extends Error {
|
|
|
69
56
|
}
|
|
70
57
|
}
|
|
71
58
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
59
|
+
* Compose one alias into its manifest (#6753 D5, extended #6757 D7): the
|
|
60
|
+
* shape's standard roster, minus the alias's `drop` routes, with its `add`
|
|
61
|
+
* rows inserted before the `before` route (appended when there is none),
|
|
62
|
+
* then each `insert` group's rows spliced before ITS OWN `before` route, in
|
|
63
|
+
* array order — the one alias with more than one anchor point
|
|
64
|
+
* (`minimalism-site`) uses this to interleave its own rows into the shape's
|
|
65
|
+
* row order. Rows are pushed by reference, never cloned, so
|
|
66
|
+
* `manifest.roster.includes(row)` identity checks downstream keep working.
|
|
67
|
+
* Every refusal names the alias, so a malformed table fails at load — the
|
|
68
|
+
* shipped one — or in the test that injected it.
|
|
69
|
+
*
|
|
70
|
+
* @throws TemplateError on a `drop`, `before` or an `insert` group's `before`
|
|
71
|
+
* naming a route the shape (or the roster built so far) does not carry, a
|
|
72
|
+
* `before` with nothing to add, an `insert` group with empty `rows`, an
|
|
73
|
+
* `add`/`insert` row whose route a kept shape row already claims, or two
|
|
74
|
+
* `add`/`insert` rows naming one route.
|
|
78
75
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
76
|
+
function composeManifest(name, alias) {
|
|
77
|
+
const rows = SHAPE_ROSTERS[alias.shape];
|
|
78
|
+
const { drop = [], add = [], before, insert = [] } = alias.differences ?? {};
|
|
79
|
+
for (const route of drop) {
|
|
80
|
+
if (!rows.some((row) => row.route === route)) {
|
|
81
|
+
throw new TemplateError(`alias "${name}": differences.drop names "${route}", which shape "${alias.shape}" does not carry`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const kept = rows.filter((row) => !drop.includes(row.route));
|
|
85
|
+
let roster;
|
|
86
|
+
if (before === undefined) {
|
|
87
|
+
roster = [...kept, ...add];
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
const at = kept.findIndex((row) => row.route === before);
|
|
91
|
+
if (at === -1) {
|
|
92
|
+
throw new TemplateError(`alias "${name}": differences.before names "${before}", which shape "${alias.shape}" does not carry after its drops`);
|
|
93
|
+
}
|
|
94
|
+
if (add.length === 0) {
|
|
95
|
+
throw new TemplateError(`alias "${name}": differences.before names "${before}" but differences.add is empty`);
|
|
96
|
+
}
|
|
97
|
+
roster = [...kept.slice(0, at), ...add, ...kept.slice(at)];
|
|
98
|
+
}
|
|
99
|
+
for (const group of insert) {
|
|
100
|
+
const at = roster.findIndex((row) => row.route === group.before);
|
|
101
|
+
if (at === -1) {
|
|
102
|
+
throw new TemplateError(`alias "${name}": differences.insert names "${group.before}", which shape "${alias.shape}" does not carry after its drops`);
|
|
103
|
+
}
|
|
104
|
+
if (group.rows.length === 0) {
|
|
105
|
+
throw new TemplateError(`alias "${name}": differences.insert names "${group.before}" but its rows is empty`);
|
|
106
|
+
}
|
|
107
|
+
roster = [...roster.slice(0, at), ...group.rows, ...roster.slice(at)];
|
|
108
|
+
}
|
|
109
|
+
const shapeRoutes = new Set(kept.map((row) => row.route));
|
|
110
|
+
const added = new Set();
|
|
111
|
+
for (const row of [...add, ...insert.flatMap((group) => group.rows)]) {
|
|
112
|
+
if (shapeRoutes.has(row.route)) {
|
|
113
|
+
throw new TemplateError(`route "${row.route}" is claimed by both shape "${alias.shape}" and alias "${name}"`);
|
|
114
|
+
}
|
|
115
|
+
if (added.has(row.route)) {
|
|
116
|
+
throw new TemplateError(`alias "${name}": differences.add names route "${row.route}" twice`);
|
|
117
|
+
}
|
|
118
|
+
added.add(row.route);
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
name,
|
|
122
|
+
requires: alias.modules,
|
|
123
|
+
...(alias.layout && { layout: alias.layout }),
|
|
124
|
+
categories: alias.categories,
|
|
125
|
+
shape: alias.shape,
|
|
126
|
+
design: alias.design,
|
|
127
|
+
extends: BASE_LAYER_DIR,
|
|
128
|
+
roster,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
87
131
|
/**
|
|
88
|
-
* The
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
* equivalent), edited only by the deviations the Free manifest's § 3a records
|
|
93
|
-
* with their reasons. With a template named, `scaffold()` keeps a `_base` or
|
|
94
|
-
* overlay seed only when a merged roster row names it (#6646 D1): the roster
|
|
95
|
-
* decides what installs, and the manifest generator's `--check` proves it.
|
|
96
|
-
*
|
|
97
|
-
* Two layers, one roster:
|
|
98
|
-
*
|
|
99
|
-
* - `/`, `/software/[slug]`, `/use-cases`, `/about`, `/faq`, `/contact` and
|
|
100
|
-
* `/purchase-policy` take their content from the overlay
|
|
101
|
-
* (`template-overlay/content/en/**`, copied whole into `templates/` by
|
|
102
|
-
* `build-templates.mjs`). `/software/[slug]` is the product's own detail
|
|
103
|
-
* page (`/software/product`, a SoftwareProduct), which replaced the
|
|
104
|
-
* `/features` stand-in (#6658 D3); its route is core, like `/about`.
|
|
105
|
-
* `/use-cases` lists the three UseCase pages at `/use-cases/[slug]`
|
|
106
|
-
* (#6658 D4).
|
|
107
|
-
* - `/pricing`, `/privacy`, `/terms`, `/glossary`, `/newsletter`,
|
|
108
|
-
* `/book-a-call` and the three UseCase pages take their content from the
|
|
109
|
-
* base layer (`site-templates/_base/content/**`, #5618 D1), which every
|
|
110
|
-
* template reaches by declaring `extends` — installed before the module
|
|
111
|
-
* bundles and the template's own tree, so a template may override a base
|
|
112
|
-
* page by shipping the same path. Their surfaces are module-less by
|
|
113
|
-
* construction (parent § D16, #6658 D5): a template enables them by
|
|
114
|
-
* NAMING them in its roster — `scaffold()` selects their
|
|
115
|
-
* `GATEABLE_SURFACES` entries from the merged roster's routes (#5469 D10).
|
|
132
|
+
* The declared templates, each composed once at load from its `ALIASES` row
|
|
133
|
+
* (#6753) — no manifest here is hand-typed, so a template's shape, design,
|
|
134
|
+
* modules and roster have one authority: `starter-grid.ts`. Declaration order
|
|
135
|
+
* is `ALIASES`'s, which is `RECOGNIZED_TEMPLATES`'s.
|
|
116
136
|
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
* them `kind: route` (#6646 D2).
|
|
122
|
-
*
|
|
123
|
-
* `/purchase-policy` is the product-shape purchase policy (#6646 D3): its
|
|
124
|
-
* overlay seed carries returns, shipping and refunds sections — the overlay,
|
|
125
|
-
* not `_base`, because the route is core and a scaffold naming no template
|
|
126
|
-
* serves it too. A service-shape template overrides that seed with its own
|
|
127
|
-
* cancellation and refunds file.
|
|
128
|
-
*
|
|
129
|
-
* `/blog` is deliberately NOT here (#5469 D3): the docs-blog module
|
|
130
|
-
* contributes its own `/blog` row, so a base row would make `mergeRoster`
|
|
131
|
-
* refuse every template that takes that module. The Free teaser row is
|
|
132
|
-
* declared on `marketing-site-free` alone, below.
|
|
137
|
+
* Frozen (#5507): this registry is also the issuer's authorization allowlist
|
|
138
|
+
* (spec 5480 § C7) — a mutable global there could be monkey-patched at
|
|
139
|
+
* runtime. A caller needing a different registry for a test injects its own
|
|
140
|
+
* fixture rather than mutating this one.
|
|
133
141
|
*/
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
route: "/use-cases",
|
|
140
|
-
content: ["content/en/UseCasesList/use-cases.mdx"],
|
|
141
|
-
},
|
|
142
|
-
{ route: "/use-cases/[slug]", contentDir: "content/en/UseCase" },
|
|
143
|
-
{ route: "/pricing", content: ["content/en/Pricing/pricing.mdx"] },
|
|
144
|
-
{ route: "/about", content: ["content/en/AboutPage/about.mdx"] },
|
|
145
|
-
{ route: "/faq", content: ["content/en/FAQPage/faq.mdx"] },
|
|
146
|
-
{ route: "/contact", content: ["content/en/ContactPage/contact.mdx"] },
|
|
147
|
-
...SHAPE_INDEPENDENT_ROWS,
|
|
148
|
-
{ route: "/sign-in" },
|
|
149
|
-
{ route: "/sign-up" },
|
|
150
|
-
{ route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
|
|
151
|
-
{ route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
|
|
152
|
-
{
|
|
153
|
-
route: "/purchase-policy",
|
|
154
|
-
content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"],
|
|
155
|
-
},
|
|
156
|
-
];
|
|
142
|
+
export const TEMPLATE_MANIFESTS = Object.freeze(Object.fromEntries(RECOGNIZED_TEMPLATES.map((name) => [
|
|
143
|
+
name,
|
|
144
|
+
composeManifest(name, ALIASES[name]),
|
|
145
|
+
])));
|
|
157
146
|
/**
|
|
158
|
-
* The
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* `
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* exists and the teaser renders. Declared on the Free template only — on
|
|
167
|
-
* `marketing-site` the docs-blog contribution claims the route.
|
|
147
|
+
* The composed FREE manifest per shape with no `--template` alias of its own
|
|
148
|
+
* (#6757 D5) — the coordinates a keyless `--shape S` builds. `Object.freeze`d,
|
|
149
|
+
* composed once at load by the same module-private `composeManifest` over
|
|
150
|
+
* `SHAPE_STARTER_ALIASES`, each manifest's `name` the shape. Data holding
|
|
151
|
+
* composed manifests, the status D5 gives `TEMPLATE_MANIFESTS`;
|
|
152
|
+
* `resolveTemplate` stays the only exported function that yields a
|
|
153
|
+
* template's roster, and its behavior is unchanged — it does NOT resolve a
|
|
154
|
+
* shape name (NFR-3, the issuer allowlist stays `TEMPLATE_MANIFESTS`-only).
|
|
168
155
|
*/
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
156
|
+
export const SHAPE_STARTERS = Object.freeze(Object.fromEntries(Object.entries(SHAPE_STARTER_ALIASES).map(([name, alias]) => [
|
|
157
|
+
name,
|
|
158
|
+
composeManifest(name, alias),
|
|
159
|
+
])));
|
|
173
160
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
* module-less `GATEABLE_SURFACES` entry, so naming it here also copies the
|
|
181
|
-
* route directory the samples render through (`rosterSurfaces`).
|
|
161
|
+
* Where a starter's files live under `site-templates/` (#6757 D5) — the one
|
|
162
|
+
* place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
|
|
163
|
+
* `--template` alias) lives at `shapes/<name>`, out of the template showcase
|
|
164
|
+
* (§ 1.6). A declared `--template` alias whose `ownTree` is `false` (spec
|
|
165
|
+
* 6892 D1) ships no directory of its own: its seed layer is its shape's tree.
|
|
166
|
+
* Every other name (a declared alias with its own tree) is unchanged.
|
|
182
167
|
*/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
];
|
|
168
|
+
export function templateTreeDir(name) {
|
|
169
|
+
if (Object.prototype.hasOwnProperty.call(SHAPE_STARTERS, name)) {
|
|
170
|
+
return `shapes/${name}`;
|
|
171
|
+
}
|
|
172
|
+
const alias = ALIASES[name];
|
|
173
|
+
if (alias?.ownTree === false) {
|
|
174
|
+
return `shapes/${alias.shape}`;
|
|
175
|
+
}
|
|
176
|
+
return name;
|
|
177
|
+
}
|
|
188
178
|
/**
|
|
189
|
-
* `
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* scaffold carries regardless — `CORE_ROUTE_DIRS` — which this template
|
|
195
|
-
* simply does not advertise as one of its pages). It does carry the three
|
|
196
|
-
* pages every starter carries (`SHAPE_INDEPENDENT_ROWS`, #6658), with its
|
|
197
|
-
* own `/book-a-call` seed pointing at the scheduling module's `/meet`.
|
|
198
|
-
*
|
|
199
|
-
* `/purchase-policy` is the service-shape purchase policy (#6646 D3) — this
|
|
200
|
-
* template's own cancellation and refunds seed, overriding `_base`'s.
|
|
179
|
+
* Whether `name` ships its own `site-templates/<name>/` directory (spec 6892
|
|
180
|
+
* D1) — `false` only for a declared `--template` alias whose `ownTree` is
|
|
181
|
+
* `false` (`minimalism-site` today). A `SHAPE_STARTERS` key owns its
|
|
182
|
+
* `shapes/<name>` tree outright, so it is never treeless by this predicate
|
|
183
|
+
* even though `templateTreeDir` also renames its directory.
|
|
201
184
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
* `
|
|
205
|
-
* `
|
|
206
|
-
*
|
|
185
|
+
* The one place a caller outside this module (or `template-names.ts`) asks
|
|
186
|
+
* "does this name have its own tree" without needing `ALIASES` itself —
|
|
187
|
+
* `starter-grid.ts`'s own header restricts its importers to this file and
|
|
188
|
+
* `template-names.ts`, so a producer script asking this question goes
|
|
189
|
+
* through this predicate rather than importing `ALIASES` directly.
|
|
207
190
|
*/
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
{ route: "/contact", content: ["content/en/ContactPage/contact.mdx"] },
|
|
215
|
-
{ route: "/referral", content: ["content/en/LandingPage/referral.mdx"] },
|
|
216
|
-
{ route: "/faq", content: ["content/en/FAQPage/faq.mdx"] },
|
|
217
|
-
...SHAPE_INDEPENDENT_ROWS,
|
|
218
|
-
{ route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
|
|
219
|
-
{ route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
|
|
220
|
-
{
|
|
221
|
-
route: "/purchase-policy",
|
|
222
|
-
content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"],
|
|
223
|
-
},
|
|
224
|
-
...SIGNAL_COLLECTORS_SAMPLE_ROWS,
|
|
225
|
-
];
|
|
191
|
+
export function isOwnTree(name) {
|
|
192
|
+
if (Object.prototype.hasOwnProperty.call(SHAPE_STARTERS, name))
|
|
193
|
+
return true;
|
|
194
|
+
const alias = ALIASES[name];
|
|
195
|
+
return alias?.ownTree !== false;
|
|
196
|
+
}
|
|
226
197
|
/**
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
* only for a row that names it (#6646 D1), exactly as for `minimalism-site`.
|
|
233
|
-
*
|
|
234
|
-
* `/meet`, `/meet/[slug]`, `/meet/confirmed` and `/appointments` are absent
|
|
235
|
-
* here on purpose: they are the `scheduling` module's own
|
|
236
|
-
* `pageContributions`, merged in by `mergeRoster`. Declaring one here would
|
|
237
|
-
* make that merge refuse the template outright (§ D8's both-claimants
|
|
238
|
-
* refusal).
|
|
239
|
-
*
|
|
240
|
-
* `/purchase/success` and `/purchase/cancel` ARE declared here, and that is a
|
|
241
|
-
* documented divergence from spec § D3. The spec was written at a pin where
|
|
242
|
-
* #6084 was expected to add the pair to `MODULE_SLICES.commerce`'s
|
|
243
|
-
* `pageContributions`. It did not: the operator's shape-2 disposition
|
|
244
|
-
* (#6084 D12) ships the return pair BASE TREE — `CORE_ROUTE_DIRS` carries
|
|
245
|
-
* `[locale]/purchase`, and neither `GATEABLE_SURFACES` nor
|
|
246
|
-
* `MODULE_SURFACES.commerce` gained an entry — so the `commerce` slice
|
|
247
|
-
* contributes no rows at all and nothing else claims these two routes. The
|
|
248
|
-
* issue's AC-1 requires the merged roster to carry them; with no module
|
|
249
|
-
* claimant, the template's own roster is the only place that can declare
|
|
250
|
-
* them, and doing so trips no collision.
|
|
251
|
-
*
|
|
252
|
-
* `/thanks/[slug]` is declared here for the same reason (#6646 D2): the
|
|
253
|
-
* post-purchase thank-you route ships base tree and no module claims it.
|
|
254
|
-
*
|
|
255
|
-
* `/purchase-policy` is the service-shape purchase policy (#6646 D3) — its
|
|
256
|
-
* cancellation and refunds sections replace the three #5947 policy rows.
|
|
257
|
-
*
|
|
258
|
-
* `/account` carries no `content`: it is a code route (the `(authed)` group),
|
|
259
|
-
* like `/sign-in` on the base roster and like the API-backed rows the
|
|
260
|
-
* scheduling module contributes. `/services/[slug]` declares its seed
|
|
261
|
-
* DIRECTORY (`contentDir`, `content/en/Service`) — the three service pages.
|
|
198
|
+
* Where a starter's GENERATED roster record lives (spec 6892 D1) — the
|
|
199
|
+
* companion to {@link templateTreeDir}. A treeless alias (`ownTree: false`)
|
|
200
|
+
* has no directory of its own to hold one, so its record lives under
|
|
201
|
+
* `site-templates/aliases/` instead, keyed by its name; every other name's
|
|
202
|
+
* record lives beside its tree, as it always has.
|
|
262
203
|
*/
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
{
|
|
269
|
-
|
|
270
|
-
...SHAPE_INDEPENDENT_ROWS,
|
|
271
|
-
{
|
|
272
|
-
route: "/purchase-policy",
|
|
273
|
-
content: ["content/en/PurchasePolicyPage/purchase-policy.mdx"],
|
|
274
|
-
},
|
|
275
|
-
{ route: "/privacy", content: ["content/en/PrivacyPage/privacy.mdx"] },
|
|
276
|
-
{ route: "/terms", content: ["content/en/TermsPage/terms.mdx"] },
|
|
277
|
-
{ route: "/account" },
|
|
278
|
-
{ route: "/purchase/success" },
|
|
279
|
-
{ route: "/purchase/cancel" },
|
|
280
|
-
{ route: "/thanks/[slug]" },
|
|
281
|
-
];
|
|
204
|
+
export function shippedManifestPath(name) {
|
|
205
|
+
const alias = ALIASES[name];
|
|
206
|
+
if (alias?.ownTree === false) {
|
|
207
|
+
return `aliases/${name}.TEMPLATE-MANIFEST.json`;
|
|
208
|
+
}
|
|
209
|
+
return `${templateTreeDir(name)}/TEMPLATE-MANIFEST.json`;
|
|
210
|
+
}
|
|
282
211
|
/**
|
|
283
|
-
*
|
|
284
|
-
*
|
|
285
|
-
*
|
|
286
|
-
*
|
|
287
|
-
* `workspace` appears in none of them. It is a Pro module, but an authed app
|
|
288
|
-
* surface contributing no PUBLIC page to a marketing site, and `requires`
|
|
289
|
-
* names what the roster uses (parent § D15). A Pro key unlocks it regardless
|
|
290
|
-
* of any template. `ModuleName` could express it either way — `workspace` is
|
|
291
|
-
* one of the seven `RECOGNIZED_MODULES` names; it is simply not one any
|
|
292
|
-
* template's fixed `requires` list names.
|
|
212
|
+
* Look up a declared template — the one exported function that yields a
|
|
213
|
+
* template's roster (#6753 AC-1); `TEMPLATE_MANIFESTS`, the frozen record it
|
|
214
|
+
* reads, holds only manifests composed from `ALIASES`.
|
|
293
215
|
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
216
|
+
* With no `aliases`, returns the frozen `TEMPLATE_MANIFESTS` entry itself.
|
|
217
|
+
* With an injected alias table — the `resolveTemplateLineage` precedent
|
|
218
|
+
* (#5507) — composes `aliases[name]` against the shipped shape rosters, so a
|
|
219
|
+
* test exercises the composer's refusals without touching the shipped table.
|
|
298
220
|
*
|
|
299
|
-
*
|
|
300
|
-
* (spec 5480 § C7) — a mutable global there could be monkey-patched at
|
|
301
|
-
* runtime. A caller needing a different registry for a test injects its own
|
|
302
|
-
* fixture rather than mutating this one.
|
|
221
|
+
* @throws TemplateError on an undeclared name, or a malformed injected alias.
|
|
303
222
|
*/
|
|
304
|
-
export
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
* template we ship anyway rather than by a throwaway fixture.
|
|
309
|
-
*/
|
|
310
|
-
"marketing-site-free": {
|
|
311
|
-
name: "marketing-site-free",
|
|
312
|
-
requires: ["marketing"],
|
|
313
|
-
// The demo site carries the "Get started with this template" banner
|
|
314
|
-
// (operator ruling 2026-09-23); every other layout knob keeps its default.
|
|
315
|
-
layout: { announcementBanner: true },
|
|
316
|
-
categories: ["accounting", "legal", "technology"],
|
|
317
|
-
shape: "organization-product",
|
|
318
|
-
design: "standard",
|
|
319
|
-
extends: BASE_LAYER_DIR,
|
|
320
|
-
roster: [...TEMPLATE_BASE_ROSTER, FREE_BLOG_TEASER_ROW],
|
|
321
|
-
},
|
|
322
|
-
/**
|
|
323
|
-
* The Pro flagship. Its module list is OFFERINGS § 4 offering 3's tiers line
|
|
324
|
-
* — "docs, scheduling and flows modules" — where "flows" is
|
|
325
|
-
* `signal-collectors` (`MODULE_SURFACES` maps it to `["flows"]`).
|
|
326
|
-
*/
|
|
327
|
-
"marketing-site": {
|
|
328
|
-
name: "marketing-site",
|
|
329
|
-
requires: ["marketing", "docs-blog", "scheduling", "signal-collectors"],
|
|
330
|
-
// The flagship is the general-purpose template: it carries the whole
|
|
331
|
-
// registry, so every group tile on the hub has at least one template.
|
|
332
|
-
categories: [
|
|
333
|
-
"accounting",
|
|
334
|
-
"legal",
|
|
335
|
-
"hvac",
|
|
336
|
-
"roofing",
|
|
337
|
-
"plumbing",
|
|
338
|
-
"electrical",
|
|
339
|
-
"landscaping",
|
|
340
|
-
"auto-repair",
|
|
341
|
-
"real-estate",
|
|
342
|
-
"technology",
|
|
343
|
-
"medical-specialty",
|
|
344
|
-
],
|
|
345
|
-
shape: "organization-product",
|
|
346
|
-
design: "standard",
|
|
347
|
-
extends: BASE_LAYER_DIR,
|
|
348
|
-
roster: [...TEMPLATE_BASE_ROSTER, ...SIGNAL_COLLECTORS_SAMPLE_ROWS],
|
|
349
|
-
},
|
|
350
|
-
/**
|
|
351
|
-
* Pro Plus — the Pro list plus `multi-tenant`, which is correctly in the
|
|
352
|
-
* list because it DOES contribute pages (the org and workspace surfaces).
|
|
353
|
-
* Its `pageContributions` is nonetheless unset today, deferred to its own
|
|
354
|
-
* instance, so this template's merged roster carries the same contributed
|
|
355
|
-
* rows as `marketing-site`'s and no more. That is a module which has
|
|
356
|
-
* declared no rows yet — visible in the diff — not a merge that dropped
|
|
357
|
-
* declared rows.
|
|
358
|
-
*/
|
|
359
|
-
"saas-product": {
|
|
360
|
-
name: "saas-product",
|
|
361
|
-
requires: [
|
|
362
|
-
"marketing",
|
|
363
|
-
"docs-blog",
|
|
364
|
-
"scheduling",
|
|
365
|
-
"signal-collectors",
|
|
366
|
-
"multi-tenant",
|
|
367
|
-
],
|
|
368
|
-
categories: ["technology", "medical-specialty"],
|
|
369
|
-
shape: "organization-product",
|
|
370
|
-
design: "standard",
|
|
371
|
-
extends: BASE_LAYER_DIR,
|
|
372
|
-
roster: [...TEMPLATE_BASE_ROSTER, ...SIGNAL_COLLECTORS_SAMPLE_ROWS],
|
|
373
|
-
},
|
|
374
|
-
/**
|
|
375
|
-
* The second Pro template (#5589) — the reports-first, scroll-snap deck
|
|
376
|
-
* site. NOT `marketing-site` plus a module: its `requires` omits
|
|
377
|
-
* `docs-blog`, so the two Pro templates are siblings under `_base`
|
|
378
|
-
* (#5618 D2), never a lineage. Level still derives from `requires`:
|
|
379
|
-
* `scheduling` and `signal-collectors` are paid, so `templateDelivery`
|
|
380
|
-
* returns `"paid"` exactly as it does for `marketing-site`.
|
|
381
|
-
*
|
|
382
|
-
* It declares its OWN roster rather than reusing `TEMPLATE_BASE_ROSTER`
|
|
383
|
-
* (the only declared template that does): the deck home replaces the
|
|
384
|
-
* document-scroll home, and `/how-it-works`, the product page, `/pricing` and
|
|
385
|
-
* `/sign-in` are not pages this site has. The roster is what the
|
|
386
|
-
* template IS, so an unrostered page has neither a route directory
|
|
387
|
-
* (`rosterSurfaces`) nor its `_base`/overlay seed (#6646 D1).
|
|
388
|
-
*
|
|
389
|
-
* It declares NO `layout` block (#6638): the deck home, mark-only nav and
|
|
390
|
-
* centered footer are the `minimalism` design's, supplied by
|
|
391
|
-
* `DESIGN_LAYOUTS` from `design:` — the look any shape can wear. The
|
|
392
|
-
* roster rows the `organization-service` shape does not derive
|
|
393
|
-
* (`/reports`, `/referral`) are recorded with their reasons in this
|
|
394
|
-
* template's TEMPLATE-MANIFEST.md § 3a.
|
|
395
|
-
*
|
|
396
|
-
* `/reports` and `/referral` carry no `GATEABLE_SURFACES` entry and need
|
|
397
|
-
* none: both are single-segment `LandingPage` slugs served by the generic
|
|
398
|
-
* `[slug]` route (#5469 D2), the same mechanism Free's `/how-it-works`
|
|
399
|
-
* uses. `/reports/[slug]` (the reader) is the signal-collectors module's
|
|
400
|
-
* own contributed row.
|
|
401
|
-
*
|
|
402
|
-
* `/services/[slug]` declares its seed DIRECTORY as `contentDir`
|
|
403
|
-
* (`content/en/Service`, as `routes-map.ts` records it). The three
|
|
404
|
-
* samples ride the template tree unmapped, landing at that directory.
|
|
405
|
-
*/
|
|
406
|
-
"minimalism-site": {
|
|
407
|
-
name: "minimalism-site",
|
|
408
|
-
requires: ["marketing", "scheduling", "signal-collectors"],
|
|
409
|
-
categories: ["real-estate", "technology", "legal"],
|
|
410
|
-
shape: "organization-service",
|
|
411
|
-
design: "minimalism",
|
|
412
|
-
extends: BASE_LAYER_DIR,
|
|
413
|
-
roster: MINIMALISM_ROSTER,
|
|
414
|
-
},
|
|
415
|
-
/**
|
|
416
|
-
* The T1 lawn-care template (#6087, Epic #5778) — the first site shape
|
|
417
|
-
* that composes `scheduling` and `commerce`: three priced services, a
|
|
418
|
-
* booker, a checkout return pair and the three #5947 policy pages.
|
|
419
|
-
*
|
|
420
|
-
* `requires` lists `marketing` for the registry convention every other
|
|
421
|
-
* manifest keeps: it is free (`module-tier.ts`'s `FREE_MODULES`), so it
|
|
422
|
-
* cannot change `templateDelivery`'s verdict, and listing it keeps the
|
|
423
|
-
* template's own declaration — not `DEFAULT_MODULES` — the authority for
|
|
424
|
-
* what a bare `--template simple-service` run installs. The order is
|
|
425
|
-
* `RECOGNIZED_MODULES` order, which is also `normalizeModules`' output
|
|
426
|
-
* order, so the declared list and the merge order read the same.
|
|
427
|
-
*
|
|
428
|
-
* `scheduling` and `commerce` are both paid, so `templateDelivery`
|
|
429
|
-
* returns `"paid"` from either one alone.
|
|
430
|
-
*
|
|
431
|
-
* It declares NO `layout` block: the default chrome (full nav, column
|
|
432
|
-
* footer, no announcement banner) is the one a lawn-care site wants, and
|
|
433
|
-
* the issue's STOP boundary excludes layout knobs the spec does not name.
|
|
434
|
-
*/
|
|
435
|
-
"simple-service": {
|
|
436
|
-
name: "simple-service",
|
|
437
|
-
requires: ["marketing", "scheduling", "commerce"],
|
|
438
|
-
categories: ["landscaping"],
|
|
439
|
-
shape: "local-business-service",
|
|
440
|
-
design: "standard",
|
|
441
|
-
extends: BASE_LAYER_DIR,
|
|
442
|
-
roster: SIMPLE_SERVICE_ROSTER,
|
|
443
|
-
},
|
|
444
|
-
});
|
|
445
|
-
/** Look up a declared template. @throws TemplateError on an undeclared name. */
|
|
446
|
-
export function resolveTemplate(name) {
|
|
447
|
-
const manifest = TEMPLATE_MANIFESTS[name];
|
|
448
|
-
if (!manifest) {
|
|
449
|
-
throw new TemplateError(`unknown template "${name}". Declared: ${Object.keys(TEMPLATE_MANIFESTS).join(", ")}`);
|
|
223
|
+
export function resolveTemplate(name, aliases) {
|
|
224
|
+
const declared = aliases ?? TEMPLATE_MANIFESTS;
|
|
225
|
+
if (!Object.prototype.hasOwnProperty.call(declared, name)) {
|
|
226
|
+
throw new TemplateError(`unknown template "${name}". Declared: ${Object.keys(declared).join(", ")}`);
|
|
450
227
|
}
|
|
451
|
-
return
|
|
228
|
+
return aliases
|
|
229
|
+
? composeManifest(name, aliases[name])
|
|
230
|
+
: TEMPLATE_MANIFESTS[name];
|
|
452
231
|
}
|
|
453
232
|
/**
|
|
454
233
|
* Resolve a template's `extends` chain — `[name, …ancestors, "_base"]` — in
|
package/dist/template-names.d.ts
CHANGED
|
@@ -20,22 +20,24 @@
|
|
|
20
20
|
* ("Deliberately imports nothing from `parse-args.ts`: that module imports
|
|
21
21
|
* THIS one … so a back-import would close a cycle"), and `surfaces.ts` is the
|
|
22
22
|
* precedent this file follows: the flag's vocabulary lives in a module the
|
|
23
|
-
* parser can import because it
|
|
23
|
+
* parser can import because it reaches nothing that imports the parser.
|
|
24
24
|
*
|
|
25
|
-
* THE
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* `
|
|
31
|
-
*
|
|
32
|
-
*
|
|
25
|
+
* THE NAMES ARE DERIVED, NOT TYPED (#6753 D1). They are the key set of
|
|
26
|
+
* `ALIASES` in `starter-grid.ts` — the alias table `template-manifest.ts`
|
|
27
|
+
* composes every manifest from — so the flag's vocabulary and the registry
|
|
28
|
+
* cannot drift. `starter-grid.ts` is the one file this module imports, and it
|
|
29
|
+
* imports nothing at runtime (every import there is `import type`), so
|
|
30
|
+
* `parse-args.ts` → here → `starter-grid.ts` still reaches no module that
|
|
31
|
+
* imports `parse-args.ts` back. `ALIASES` does not live in
|
|
32
|
+
* `template-manifest.ts` for exactly that reason: importing it from there
|
|
33
|
+
* would re-open the cycle above.
|
|
33
34
|
*
|
|
34
35
|
* Rejected: lazifying `module-tier.ts`'s `PAID_MODULES` to break the cycle at
|
|
35
36
|
* its source. It is the correct structural fix and it is not this unit's to
|
|
36
37
|
* make — `module-tier.ts` is a READ row in spec 5481 § 4, and `PAID_MODULES`
|
|
37
38
|
* is consumed as a plain array across the package.
|
|
38
39
|
*/
|
|
39
|
-
|
|
40
|
-
export
|
|
41
|
-
|
|
40
|
+
import { ALIASES } from "./starter-grid.js";
|
|
41
|
+
export type TemplateName = keyof typeof ALIASES;
|
|
42
|
+
/** The declared site templates, in `ALIASES` (and so `TEMPLATE_MANIFESTS`) declaration order. */
|
|
43
|
+
export declare const RECOGNIZED_TEMPLATES: readonly TemplateName[];
|