create-ailk 0.8.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 +68 -0
- package/component-catalog.json +279 -45
- 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 +92 -4
- 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/license-key-files.d.ts +9 -0
- package/dist/lib/license-key-files.js +2 -2
- package/dist/lib/module-license-gate.d.ts +19 -9
- package/dist/lib/module-license-gate.js +19 -8
- 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 +12 -2
- package/dist/parse-args.js +74 -13
- package/dist/programmatic.d.ts +30 -6
- package/dist/programmatic.js +206 -17
- 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 +141 -8
- package/dist/starter-grid.d.ts +32 -16
- package/dist/starter-grid.js +129 -33
- package/dist/surfaces.d.ts +41 -9
- package/dist/surfaces.js +200 -83
- package/dist/template-manifest.d.ts +30 -13
- package/dist/template-manifest.js +49 -24
- package/package.json +5 -4
- package/site-templates/_base/content/en/UseCase/persona-1.mdx +7 -6
- package/site-templates/_base/content/en/UseCase/persona-2.mdx +7 -6
- package/site-templates/_base/content/en/UseCase/persona-3.mdx +7 -6
- 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 +98 -6
- package/site-templates/marketing-site/DESIGN.md +98 -6
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +60 -27
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +50 -30
- package/site-templates/marketing-site-free/DESIGN.md +98 -6
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +60 -24
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +44 -27
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/saas-product/DESIGN.md +98 -6
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +74 -20
- package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +74 -20
- package/site-templates/shapes/organization-service/ailk-template.json +1 -1
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.json +127 -18
- package/site-templates/shapes/person-service/TEMPLATE-MANIFEST.md +75 -21
- package/site-templates/shapes/person-service/ailk-template.json +1 -1
- package/site-templates/shapes/person-service/content/en/AboutPage/about.mdx +1 -1
- package/site-templates/shapes/person-service/content/en/HomePage/home.mdx +2 -2
- package/site-templates/shapes/person-service/content/en/PortfolioList/portfolio.mdx +4 -2
- package/site-templates/shapes/person-service/content/en/ServicesList/services.mdx +1 -1
- package/templates/.claude/rules/architecture.md +7 -0
- package/templates/.claude/skills/README.md +3 -2
- 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 -76
- 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-deploy/references/env-vars-template.md +28 -11
- 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 +98 -6
- package/templates/README.md +50 -38
- package/templates/apps/api/.env.example +2 -1
- 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/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/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/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 +28 -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/revalidation.ts +60 -15
- package/templates/apps/mcp/__tests__/tools/create_project.test.ts +30 -24
- 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]/{use-cases → for}/[slug]/page.tsx +4 -3
- 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 +35 -19
- package/templates/apps/web/app/[locale]/newsletter/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/portfolio/[slug]/page.tsx +1 -1
- package/templates/apps/web/app/[locale]/portfolio/page.tsx +1 -1
- 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 +14 -13
- package/templates/apps/web/lib/site-brand.tsx +13 -9
- 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 +23 -9
- package/templates/apps/web/middleware.ts +28 -4
- 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 +40 -1
- package/templates/content/en/HomePage/home.mdx +2 -6
- package/templates/content/en/LandingPage/meet.mdx +59 -0
- package/templates/content/en/UseCasesList/{use-cases.mdx → for.mdx} +19 -18
- package/templates/database/CHANGELOG.md +7 -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 -52
- package/site-templates/minimalism-site/DESIGN.md +0 -162
- package/site-templates/minimalism-site/TEMPLATE-MANIFEST.md +0 -458
- 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 -52
- 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/PurchasePolicyPage/purchase-policy.mdx +0 -123
- 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/content/en/Service/first-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/Service/second-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/Service/third-engagement.mdx +0 -38
- package/site-templates/minimalism-site/content/en/ServicesList/services.mdx +0 -73
- package/site-templates/minimalism-site/flows/referral.flow.json +0 -71
- package/site-templates/minimalism-site/provenance.json +0 -4
- package/templates/.claude/skills/add-page/templates/route.tsx.tmpl +0 -28
- package/templates/apps/web/app/[locale]/book-a-call/page.tsx +0 -50
- package/templates/apps/web/app/[locale]/use-cases/__tests__/page-renderer-migration.test.tsx +0 -72
- /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/dist/surfaces.js
CHANGED
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
* `MODULE_SURFACES`, and `__tests__/surface-gating.test.ts` asserts those keys
|
|
32
32
|
* equal `RECOGNIZED_MODULES` so the two cannot drift.
|
|
33
33
|
*/
|
|
34
|
+
import { createHash } from "node:crypto";
|
|
34
35
|
/** Template-relative prefix every `apps/web` route directory sits under. */
|
|
35
36
|
const ROUTE_ROOT = "apps/web/app/[locale]";
|
|
36
37
|
/**
|
|
@@ -114,19 +115,22 @@ export const NON_ROUTE_DIRS = new Set([
|
|
|
114
115
|
* byte-deterministic).
|
|
115
116
|
*/
|
|
116
117
|
export const GATEABLE_SURFACES = [
|
|
118
|
+
{
|
|
119
|
+
// #6878 D9 — the comparison hub/spoke pair (`ComparisonsList` at
|
|
120
|
+
// /alternatives, `ComparisonPage` at /vs/<slug>). No `navLabelKey`: no
|
|
121
|
+
// roster names it by default, so it excludes at scaffold time exactly
|
|
122
|
+
// like any other gateable surface a roster leaves off. `add-page`
|
|
123
|
+
// installs it (D11) the first time a site adds a comparison.
|
|
124
|
+
id: "alternatives",
|
|
125
|
+
routes: [`${ROUTE_ROOT}/alternatives`, `${ROUTE_ROOT}/vs`],
|
|
126
|
+
content: ["content/<locale>/ComparisonsList", "content/<locale>/ComparisonPage"],
|
|
127
|
+
},
|
|
117
128
|
{
|
|
118
129
|
id: "blog",
|
|
119
130
|
routes: [`${ROUTE_ROOT}/blog`],
|
|
120
131
|
content: ["apps/web/content/<locale>/blog"],
|
|
121
132
|
navLabelKey: "blog",
|
|
122
133
|
},
|
|
123
|
-
{
|
|
124
|
-
// Module-less (#6658 D5), like `pricing`: every starter's roster names
|
|
125
|
-
// it. No `navLabelKey` — the footer links it, gated on this surface.
|
|
126
|
-
id: "book-a-call",
|
|
127
|
-
routes: [`${ROUTE_ROOT}/book-a-call`],
|
|
128
|
-
content: ["content/<locale>/BookCall"],
|
|
129
|
-
},
|
|
130
134
|
{
|
|
131
135
|
id: "careers",
|
|
132
136
|
routes: [`${ROUTE_ROOT}/careers`],
|
|
@@ -186,6 +190,14 @@ export const GATEABLE_SURFACES = [
|
|
|
186
190
|
routes: [`${ROUTE_ROOT}/flows`],
|
|
187
191
|
content: [],
|
|
188
192
|
},
|
|
193
|
+
{
|
|
194
|
+
// #6878 D3 — the persona hub, renamed from `use-cases` (route-naming
|
|
195
|
+
// convention #3450: /for/{persona} spokes under a /for hub). Every shape
|
|
196
|
+
// roster carries it now (D7), service shapes included.
|
|
197
|
+
id: "for",
|
|
198
|
+
routes: [`${ROUTE_ROOT}/for`],
|
|
199
|
+
content: ["content/<locale>/UseCasesList", "content/<locale>/UseCase"],
|
|
200
|
+
},
|
|
189
201
|
{
|
|
190
202
|
// Module-less (#6658 D5) — see `book-a-call`.
|
|
191
203
|
id: "glossary",
|
|
@@ -303,11 +315,6 @@ export const GATEABLE_SURFACES = [
|
|
|
303
315
|
routes: [`${ROUTE_ROOT}/testimonials`],
|
|
304
316
|
content: ["content/<locale>/TestimonialsList"],
|
|
305
317
|
},
|
|
306
|
-
{
|
|
307
|
-
id: "use-cases",
|
|
308
|
-
routes: [`${ROUTE_ROOT}/use-cases`],
|
|
309
|
-
content: ["content/<locale>/UseCasesList", "content/<locale>/UseCase"],
|
|
310
|
-
},
|
|
311
318
|
{
|
|
312
319
|
id: "whitepapers",
|
|
313
320
|
routes: [`${ROUTE_ROOT}/whitepapers`],
|
|
@@ -323,6 +330,9 @@ export const RECOGNIZED_SURFACES = GATEABLE_SURFACES.map((s) => s.id);
|
|
|
323
330
|
*/
|
|
324
331
|
export const SURFACE_ALIASES = {
|
|
325
332
|
schedule: "meet",
|
|
333
|
+
// #6878 D3 — one-release alias for the /use-cases -> /for rename, same
|
|
334
|
+
// removal trigger as `schedule` above.
|
|
335
|
+
"use-cases": "for",
|
|
326
336
|
};
|
|
327
337
|
/**
|
|
328
338
|
* Surface ids that became core routes (#6658 D3: `software` — the product's
|
|
@@ -507,10 +517,6 @@ export function excludedContentPaths(enabled) {
|
|
|
507
517
|
export const ROUTE_SINCE = {
|
|
508
518
|
about: "0.0.0",
|
|
509
519
|
"blog/[[...slug]]": "0.0.0",
|
|
510
|
-
// #6658 — four genuinely new route directories (D4, D5), attaching at
|
|
511
|
-
// today's fixed-group version, the same "attach at today's version" rule
|
|
512
|
-
// #6084, #5977, #5678, #5479, #5371 and #5048 applied.
|
|
513
|
-
"book-a-call": "0.26.0",
|
|
514
520
|
careers: "0.0.0",
|
|
515
521
|
"careers/[slug]": "0.0.0",
|
|
516
522
|
"case-studies": "0.0.0",
|
|
@@ -531,6 +537,10 @@ export const ROUTE_SINCE = {
|
|
|
531
537
|
"flows/[slug]": "0.0.0",
|
|
532
538
|
"flows/[slug]/results/[payload]": "0.14.0",
|
|
533
539
|
"flows/[slug]/start": "0.18.0",
|
|
540
|
+
// #6658 — genuinely new route directories (D4, D5), attaching at today's
|
|
541
|
+
// fixed-group version, the same "attach at today's version" rule #6084,
|
|
542
|
+
// #5977, #5678, #5479, #5371 and #5048 applied. `book-a-call` was a third
|
|
543
|
+
// such entry, retired by #6897 D10.
|
|
534
544
|
glossary: "0.26.0",
|
|
535
545
|
"how-to": "0.0.0",
|
|
536
546
|
"how-to/[slug]": "0.0.0",
|
|
@@ -579,8 +589,14 @@ export const ROUTE_SINCE = {
|
|
|
579
589
|
terms: "0.22.0",
|
|
580
590
|
testimonials: "0.0.0",
|
|
581
591
|
"thanks/[slug]": "0.14.0",
|
|
582
|
-
|
|
583
|
-
|
|
592
|
+
// #6878 — the persona family, renamed from `use-cases`/`use-cases/[slug]`.
|
|
593
|
+
// Attaches at today's fixed-group version, the same "attach at today's
|
|
594
|
+
// version" rule #6084, #5977, #5678, #5479, #5371 and #5048 applied.
|
|
595
|
+
for: "0.28.0",
|
|
596
|
+
"for/[slug]": "0.28.0",
|
|
597
|
+
// #6878 D9 — the comparison hub/spoke pair.
|
|
598
|
+
alternatives: "0.28.0",
|
|
599
|
+
"vs/[slug]": "0.28.0",
|
|
584
600
|
whitepapers: "0.0.0",
|
|
585
601
|
"whitepapers/[slug]": "0.0.0",
|
|
586
602
|
// #5014 — the (authed) route GROUP: layout.tsx + sign-in-gate.tsx sit
|
|
@@ -645,6 +661,10 @@ export const ROUTE_SINCE = {
|
|
|
645
661
|
// directory, so it attaches at today's fixed-group version, the same rule
|
|
646
662
|
// #5479, #5371 and #5048 applied.
|
|
647
663
|
"[slug]": "0.23.0",
|
|
664
|
+
// #6897 D2 — the Free /meet/<slug> placeholder detail route. A genuinely
|
|
665
|
+
// new route directory, so it attaches at today's fixed-group version, the
|
|
666
|
+
// same rule applied above.
|
|
667
|
+
"[slug]/[detail]": "0.28.0",
|
|
648
668
|
};
|
|
649
669
|
/**
|
|
650
670
|
* Surface id of the registry that owns `routeDir` (ROUTE_ROOT-relative): the
|
|
@@ -680,12 +700,18 @@ export function surfaceForRouteDir(routeDir) {
|
|
|
680
700
|
*/
|
|
681
701
|
export const ROUTE_DEPENDENCIES = {
|
|
682
702
|
// #5963 — the (auth) group's layout renders the brand mark through
|
|
683
|
-
// `siteLogo()
|
|
684
|
-
//
|
|
685
|
-
//
|
|
686
|
-
//
|
|
687
|
-
//
|
|
688
|
-
|
|
703
|
+
// `siteLogo()` — which lives in `site-logo.tsx` since #6820 and reads the
|
|
704
|
+
// raw mark + name from `site-brand.tsx`, so BOTH files travel with the
|
|
705
|
+
// group — and the LOCALE layout above it branches on `isAuthPath()` to drop
|
|
706
|
+
// the marketing chrome for these five paths: without `auth-routes.ts` a
|
|
707
|
+
// synced group renders inside the Nav + Footer it exists to replace. The
|
|
708
|
+
// sign-in page no longer imports `site-brand` — the mark is the shell's
|
|
709
|
+
// since #5963 — so its own list is just the env reader.
|
|
710
|
+
"(auth)": [
|
|
711
|
+
"apps/web/lib/site-brand.tsx",
|
|
712
|
+
"apps/web/lib/site-logo.tsx",
|
|
713
|
+
"apps/web/lib/auth-routes.ts",
|
|
714
|
+
],
|
|
689
715
|
"(auth)/sign-in": ["apps/web/lib/auth-env.ts"],
|
|
690
716
|
"(auth)/sign-up": ["apps/web/lib/auth-env.ts"],
|
|
691
717
|
"(auth)/forgot-password": ["apps/web/lib/auth-env.ts"],
|
|
@@ -760,6 +786,46 @@ export const ROUTE_MESSAGE_NAMESPACES = {
|
|
|
760
786
|
};
|
|
761
787
|
/** Template-relative path of the generated site-nav registry. */
|
|
762
788
|
export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
789
|
+
/**
|
|
790
|
+
* `renderNavConfig`'s first-line stamp (#6908 D1): `// ailk:generated
|
|
791
|
+
* sha256=<64 lowercase hex>`, recognised whether it ends in `\n` or `\r\n`.
|
|
792
|
+
* `<hex>` is the SHA-256 of every byte after the stamp line's terminator,
|
|
793
|
+
* CRLF folded to LF first — the same fold-and-hash rule `claude-md.ts`
|
|
794
|
+
* `hashBody` uses for the scaffold's `CLAUDE.md` regions (#6904), given its
|
|
795
|
+
* own definition here (N1) rather than an import, since the two files hash
|
|
796
|
+
* unrelated bodies.
|
|
797
|
+
*/
|
|
798
|
+
const NAV_STAMP_RE = /^\/\/ ailk:generated sha256=([0-9a-f]{64})\r?\n/;
|
|
799
|
+
/** SHA-256, lowercase hex, of a nav body with every CRLF folded to LF (D1, N1). */
|
|
800
|
+
function hashNavBody(body) {
|
|
801
|
+
return createHash("sha256")
|
|
802
|
+
.update(body.replace(/\r\n/g, "\n"), "utf8")
|
|
803
|
+
.digest("hex");
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Verify a rendered nav's self-hashing stamp (#6908 D1/F1–F2): `"valid"` when
|
|
807
|
+
* the first line's hash matches every byte after it, `"invalid"` when a
|
|
808
|
+
* stamp line is present but the hash fails (an edited, still-stamped nav),
|
|
809
|
+
* `"absent"` when there is no stamp line at all. A stamp that verifies proves
|
|
810
|
+
* the file unedited whatever generator wrote it — the check needs only the
|
|
811
|
+
* file, never the generator's code.
|
|
812
|
+
*/
|
|
813
|
+
export function readNavConfigStamp(text) {
|
|
814
|
+
const match = NAV_STAMP_RE.exec(text);
|
|
815
|
+
if (match === null)
|
|
816
|
+
return "absent";
|
|
817
|
+
const body = text.slice(match[0].length);
|
|
818
|
+
return match[1] === hashNavBody(body) ? "valid" : "invalid";
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Every byte after a nav's stamp line, or `text` unchanged when it carries
|
|
822
|
+
* none. The one place the D1 stamp-line shape is matched outside
|
|
823
|
+
* `readNavConfigStamp` itself, so a test simulating an edit below the stamp
|
|
824
|
+
* (or a bare pre-D1 body) never re-derives `NAV_STAMP_RE`'s pattern by hand.
|
|
825
|
+
*/
|
|
826
|
+
export function stripNavConfigStamp(text) {
|
|
827
|
+
return text.replace(NAV_STAMP_RE, "");
|
|
828
|
+
}
|
|
763
829
|
/**
|
|
764
830
|
* Render `apps/web/lib/nav-config.ts` for an enabled surface set.
|
|
765
831
|
*
|
|
@@ -770,14 +836,19 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
|
770
836
|
* surface set for the same reason the route tree does.
|
|
771
837
|
*
|
|
772
838
|
* The base items are the Free marketing-site chrome (#5469 D5, the approved
|
|
773
|
-
* chrome diff recorded on that card, re-pointed by #6658 D8):
|
|
774
|
-
*
|
|
775
|
-
*
|
|
776
|
-
*
|
|
777
|
-
* module serves it). Pricing
|
|
778
|
-
*
|
|
779
|
-
* roster enables it, #5469 D10), so the nav still never links a page
|
|
780
|
-
* scaffold does not carry.
|
|
839
|
+
* chrome diff recorded on that card, re-pointed by #6658 D8, then by #6878 D6):
|
|
840
|
+
* ONE flyout rule for every shape — an offering column (Features on product
|
|
841
|
+
* shapes, Services on service shapes) beside a Customers column linking the
|
|
842
|
+
* /for persona pages — then Blog, Pricing and About. `/blog` is always linked
|
|
843
|
+
* (the teaser or the docs-blog module serves it). Pricing and every other
|
|
844
|
+
* gateable surface: each item is emitted only when its surface is enabled (a
|
|
845
|
+
* template roster enables it, #5469 D10), so the nav still never links a page
|
|
846
|
+
* the scaffold does not carry. Portfolio is never a flyout link (D6): on a
|
|
847
|
+
* service shape it is a plain top-level link directly after the Services
|
|
848
|
+
* flyout, and every shape's footer Site pages column links it (#6895), both
|
|
849
|
+
* under the `portfolio` surface's gate. No empty flyout is ever emitted: with
|
|
850
|
+
* neither column non-empty, no flyout item is emitted at all (closes #6766's
|
|
851
|
+
* AC-1).
|
|
781
852
|
*
|
|
782
853
|
* Only gateable surfaces carrying a `navLabelKey` are linked after the base
|
|
783
854
|
* items, because a nav entry needs a translated label. Emitting an entry whose
|
|
@@ -791,23 +862,20 @@ export const NAV_CONFIG_PATH = "apps/web/lib/nav-config.ts";
|
|
|
791
862
|
* Deterministic: gateable entries follow GATEABLE_SURFACES order (alphabetical
|
|
792
863
|
* by id), after the fixed base items.
|
|
793
864
|
*/
|
|
794
|
-
export function renderNavConfig(enabled) {
|
|
865
|
+
export function renderNavConfig(enabled, options) {
|
|
795
866
|
const on = new Set(enabled);
|
|
796
867
|
// `blog` is linked unconditionally below (operator ruling 2026-09-23), so
|
|
797
|
-
// it is excluded here; `
|
|
798
|
-
//
|
|
799
|
-
|
|
800
|
-
const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined &&
|
|
801
|
-
on.has(s.id) &&
|
|
802
|
-
s.id !== "blog" &&
|
|
803
|
-
s.id !== "use-cases");
|
|
804
|
-
const useCases = on.has("use-cases");
|
|
868
|
+
// it is excluded here; `for` is the Customers flyout/footer column below,
|
|
869
|
+
// linked in its own fixed positions rather than appended.
|
|
870
|
+
const linked = GATEABLE_SURFACES.filter((s) => s.navLabelKey !== undefined && on.has(s.id) && s.id !== "blog");
|
|
805
871
|
const features = on.has("features");
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
//
|
|
872
|
+
const services = on.has("services");
|
|
873
|
+
const forEnabled = on.has("for");
|
|
874
|
+
const portfolio = on.has("portfolio");
|
|
875
|
+
const serviceSlugs = [...(options?.serviceSlugs ?? [])].sort().slice(0, 3);
|
|
876
|
+
// #6878 D6 (operator ruling 2026-09-27): one flyout rule for every shape.
|
|
877
|
+
// The `groups` render the desktop panel; `features` carries the same links
|
|
878
|
+
// for the mobile disclosure and the active-path match.
|
|
811
879
|
const featureLinks = features
|
|
812
880
|
? [
|
|
813
881
|
' { labelKey: "feature1", path: "/features/feature-1" },',
|
|
@@ -815,11 +883,14 @@ export function renderNavConfig(enabled) {
|
|
|
815
883
|
' { labelKey: "feature3", path: "/features/feature-3" },',
|
|
816
884
|
]
|
|
817
885
|
: [];
|
|
818
|
-
const
|
|
886
|
+
const serviceLinks = !features && services
|
|
887
|
+
? serviceSlugs.map((slug, i) => ` { labelKey: "service${i + 1}", path: "/services/${slug}" },`)
|
|
888
|
+
: [];
|
|
889
|
+
const customerLinks = forEnabled
|
|
819
890
|
? [
|
|
820
|
-
' { labelKey: "
|
|
821
|
-
' { labelKey: "
|
|
822
|
-
' { labelKey: "
|
|
891
|
+
' { labelKey: "customer1", path: "/for/persona-1" },',
|
|
892
|
+
' { labelKey: "customer2", path: "/for/persona-2" },',
|
|
893
|
+
' { labelKey: "customer3", path: "/for/persona-3" },',
|
|
823
894
|
]
|
|
824
895
|
: [];
|
|
825
896
|
const indent = (line) => ` ${line}`;
|
|
@@ -833,24 +904,56 @@ export function renderNavConfig(enabled) {
|
|
|
833
904
|
" ],",
|
|
834
905
|
" },",
|
|
835
906
|
];
|
|
907
|
+
// Offering column: Features on a product shape, Services on a service
|
|
908
|
+
// shape. `features` wins when both surfaces are somehow on at once — the
|
|
909
|
+
// Free roster never enables both.
|
|
910
|
+
const offeringLinks = features ? featureLinks : serviceLinks;
|
|
911
|
+
const offeringLabelKey = features ? "product" : "services";
|
|
912
|
+
const offeringHeadingKey = features
|
|
913
|
+
? "flyoutFeaturesHeading"
|
|
914
|
+
: "flyoutServicesHeading";
|
|
915
|
+
const flyoutGroups = [
|
|
916
|
+
...group(offeringHeadingKey, offeringLinks),
|
|
917
|
+
...group("flyoutCustomersHeading", customerLinks),
|
|
918
|
+
];
|
|
919
|
+
// #6942 — the Services flyout's action bar links the /services hub. The
|
|
920
|
+
// hub rides the `services` surface's gate: its route directory serves both
|
|
921
|
+
// /services and /services/[slug], so every roster that enables the surface
|
|
922
|
+
// carries the hub. Emitted only inside a flyout that has a column (never
|
|
923
|
+
// an action-only flyout). A flyout action requires an icon
|
|
924
|
+
// (`NavFlyoutAction`), imported below only when this link is emitted.
|
|
925
|
+
const servicesHub = !features && services && flyoutGroups.length > 0;
|
|
836
926
|
const entries = [
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
927
|
+
// No empty flyout (D6): emitted only when at least one column has links.
|
|
928
|
+
...(flyoutGroups.length === 0
|
|
929
|
+
? []
|
|
930
|
+
: [
|
|
931
|
+
" {",
|
|
932
|
+
` labelKey: "${offeringLabelKey}",`,
|
|
933
|
+
" flyout: {",
|
|
934
|
+
// SOFT_LOCK_OK: #6508 — width "panel" (D4/D5): a fixed 640px panel.
|
|
935
|
+
' width: "panel",',
|
|
936
|
+
" features: [",
|
|
937
|
+
...offeringLinks,
|
|
938
|
+
...customerLinks,
|
|
939
|
+
" ],",
|
|
940
|
+
" groups: [",
|
|
941
|
+
...flyoutGroups,
|
|
942
|
+
" ],",
|
|
943
|
+
servicesHub
|
|
944
|
+
? ' actions: [{ labelKey: "services", icon: ArrowRight, path: "/services" }],'
|
|
945
|
+
: " actions: [],",
|
|
946
|
+
" },",
|
|
947
|
+
" },",
|
|
948
|
+
]),
|
|
949
|
+
// #6895 (operator decision 2026-09-27): a service shape's header reads
|
|
950
|
+
// Services ▾ · Portfolio · Blog · About — Portfolio a plain top-level
|
|
951
|
+
// link, never a flyout link (D6), and the first item when no flyout is
|
|
952
|
+
// emitted. Product shapes keep their header.
|
|
953
|
+
...(!features && services && portfolio
|
|
954
|
+
? [' { labelKey: "portfolio", path: "/portfolio" },']
|
|
955
|
+
: []),
|
|
956
|
+
// Operator ruling 2026-09-25: [flyout ·] Blog · Pricing · About.
|
|
854
957
|
// `/blog` is always linked — the `blog` surface serves the real route, and
|
|
855
958
|
// with it off the `[slug]` route serves the overlay's module page.
|
|
856
959
|
' { labelKey: "blog", path: "/blog" },',
|
|
@@ -858,13 +961,17 @@ export function renderNavConfig(enabled) {
|
|
|
858
961
|
' { labelKey: "about", path: "/about" },',
|
|
859
962
|
...linked.map((s) => ` { labelKey: "${s.navLabelKey}", path: "/${s.id}" },`),
|
|
860
963
|
];
|
|
861
|
-
// The footer's column groups (operator rulings 2026-09-23 + 2026-09-25
|
|
862
|
-
// Site pages, Features,
|
|
863
|
-
// `pricing` gate as the header item; `/blog` is always linked (above).
|
|
864
|
-
// Features column links the three feature pages and the /features hub;
|
|
865
|
-
//
|
|
866
|
-
//
|
|
867
|
-
//
|
|
964
|
+
// The footer's column groups (operator rulings 2026-09-23 + 2026-09-25,
|
|
965
|
+
// #6878 D6). Site pages, Features, Customers, Resources. Pricing rides the
|
|
966
|
+
// same `pricing` gate as the header item; `/blog` is always linked (above).
|
|
967
|
+
// The Features column links the three feature pages and the /features hub;
|
|
968
|
+
// the Customers column the three personas and the /for hub — each rides its
|
|
969
|
+
// surface's gate. Glossary and Newsletter (#6658 D8) each ride their own
|
|
970
|
+
// surface's gate. Book a call is always linked (#6897 D9), the same /blog
|
|
971
|
+
// precedent above: it retargets to /meet/book-a-call, served as a Free
|
|
972
|
+
// placeholder or the live scheduling page depending on what the site
|
|
973
|
+
// carries. No other footer column changes (D6) — there is deliberately no
|
|
974
|
+
// footer Services column.
|
|
868
975
|
const footerEntries = [
|
|
869
976
|
" {",
|
|
870
977
|
' headingKey: "footerSitePagesHeading",',
|
|
@@ -873,11 +980,13 @@ export function renderNavConfig(enabled) {
|
|
|
873
980
|
...(on.has("pricing")
|
|
874
981
|
? [' { labelKey: "pricing", path: "/pricing" },']
|
|
875
982
|
: []),
|
|
983
|
+
// #6895 — every shape's Site pages column links Portfolio, gated.
|
|
984
|
+
...(portfolio
|
|
985
|
+
? [' { labelKey: "portfolio", path: "/portfolio" },']
|
|
986
|
+
: []),
|
|
876
987
|
' { labelKey: "about", path: "/about" },',
|
|
877
988
|
' { labelKey: "contact", path: "/contact" },',
|
|
878
|
-
|
|
879
|
-
? [' { labelKey: "bookACall", path: "/book-a-call" },']
|
|
880
|
-
: []),
|
|
989
|
+
' { labelKey: "bookACall", path: "/meet/book-a-call" },',
|
|
881
990
|
" ],",
|
|
882
991
|
" },",
|
|
883
992
|
...(features
|
|
@@ -891,13 +1000,13 @@ export function renderNavConfig(enabled) {
|
|
|
891
1000
|
" },",
|
|
892
1001
|
]
|
|
893
1002
|
: []),
|
|
894
|
-
...(
|
|
1003
|
+
...(forEnabled
|
|
895
1004
|
? [
|
|
896
1005
|
" {",
|
|
897
|
-
' headingKey: "
|
|
1006
|
+
' headingKey: "footerCustomersHeading",',
|
|
898
1007
|
" items: [",
|
|
899
|
-
...
|
|
900
|
-
' { labelKey: "
|
|
1008
|
+
...customerLinks.map((l) => l.slice(2)),
|
|
1009
|
+
' { labelKey: "allCustomers", path: "/for" },',
|
|
901
1010
|
" ],",
|
|
902
1011
|
" },",
|
|
903
1012
|
]
|
|
@@ -927,18 +1036,23 @@ export function renderNavConfig(enabled) {
|
|
|
927
1036
|
"from ",
|
|
928
1037
|
'"@working-theory/ui";',
|
|
929
1038
|
].join("");
|
|
930
|
-
|
|
1039
|
+
const body = `// nav-config.ts — site-nav registry.
|
|
931
1040
|
//
|
|
932
1041
|
// GENERATED by create-ailk from the declared route surfaces. Every entry here
|
|
933
1042
|
// points at a route this site actually carries — the nav is regenerated with
|
|
934
1043
|
// the surface set so it can never link a page that was not scaffolded.
|
|
935
1044
|
//
|
|
1045
|
+
// The first line hashes the rest of this file: while it matches, \`create-ailk
|
|
1046
|
+
// add-module\` regenerates this nav when a module adds pages; once you edit
|
|
1047
|
+
// below it, the nav is yours and add-module reports the change it wanted
|
|
1048
|
+
// instead.
|
|
1049
|
+
//
|
|
936
1050
|
// Adding a link: scaffold the page (the \`add-page\` skill), then add an
|
|
937
1051
|
// entry below with a \`labelKey\` that exists in the \`nav\` namespace of
|
|
938
1052
|
// \`apps/web/messages/<locale>.json\`. Paths are locale-agnostic — Nav prefixes
|
|
939
1053
|
// them with the active locale at render.
|
|
940
1054
|
${navItemImport}
|
|
941
|
-
|
|
1055
|
+
${servicesHub ? 'import { ArrowRight } from "lucide-react";\n' : ""}
|
|
942
1056
|
export const navItems: NavItem[] = [
|
|
943
1057
|
${entries.join("\n")}
|
|
944
1058
|
];
|
|
@@ -954,4 +1068,7 @@ export const footerColumns: FooterColumnConfig[] = [
|
|
|
954
1068
|
${footerEntries.join("\n")}
|
|
955
1069
|
];
|
|
956
1070
|
`;
|
|
1071
|
+
// D1 — a first-line stamp holding the SHA-256 of the rest of the file
|
|
1072
|
+
// (`body`), so an unedited nav proves itself whatever generator wrote it.
|
|
1073
|
+
return `// ailk:generated sha256=${hashNavBody(body)}\n${body}`;
|
|
957
1074
|
}
|
|
@@ -31,21 +31,14 @@ export declare const STARTER_SHAPES: readonly ["organization-product", "organiza
|
|
|
31
31
|
export type StarterShape = (typeof STARTER_SHAPES)[number];
|
|
32
32
|
/**
|
|
33
33
|
* The closed **design** enum (same contract section) — the look applied to
|
|
34
|
-
* any shape; orthogonal to it by construction.
|
|
34
|
+
* any shape; orthogonal to it by construction. Each name RESOLVES to a
|
|
35
|
+
* package at `site-templates/designs/<name>/` (#6813 D2/D3, D11 pins this set
|
|
36
|
+
* equal to that directory listing): `resolveDesign` (`design-scaffold.ts`)
|
|
37
|
+
* reads the package's own `layout`, `colors` and treatments — no design fact
|
|
38
|
+
* lives in this module, or anywhere in `packages/create-ailk/src`, any more.
|
|
35
39
|
*/
|
|
36
40
|
export declare const STARTER_DESIGNS: readonly ["standard", "minimalism"];
|
|
37
41
|
export type StarterDesign = (typeof STARTER_DESIGNS)[number];
|
|
38
|
-
/**
|
|
39
|
-
* Each design's layout knobs (#6638) — the part of a design that is chrome,
|
|
40
|
-
* so a template names `design:` instead of re-typing a `layout` block, and any
|
|
41
|
-
* shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
|
|
42
|
-
* an explicit `TemplateManifest.layout` wins field by field, then this, then
|
|
43
|
-
* `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
|
|
44
|
-
* spelling them out here would be a second copy of them to drift. A plain
|
|
45
|
-
* object, not a file: this module touches no disk. The design's documented
|
|
46
|
-
* look lives in `site-templates/designs/<design>/DESIGN.md`.
|
|
47
|
-
*/
|
|
48
|
-
export declare const DESIGN_LAYOUTS: Readonly<Record<StarterDesign, TemplateLayout>>;
|
|
49
42
|
/**
|
|
50
43
|
* One site template: its fixed module list and the page rows it brings itself.
|
|
51
44
|
*
|
|
@@ -182,9 +175,33 @@ export declare const SHAPE_STARTERS: Readonly<Partial<Record<BuildableShape, Tem
|
|
|
182
175
|
* Where a starter's files live under `site-templates/` (#6757 D5) — the one
|
|
183
176
|
* place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
|
|
184
177
|
* `--template` alias) lives at `shapes/<name>`, out of the template showcase
|
|
185
|
-
* (§ 1.6)
|
|
178
|
+
* (§ 1.6). A declared `--template` alias whose `ownTree` is `false` (spec
|
|
179
|
+
* 6892 D1) ships no directory of its own: its seed layer is its shape's tree.
|
|
180
|
+
* Every other name (a declared alias with its own tree) is unchanged.
|
|
186
181
|
*/
|
|
187
182
|
export declare function templateTreeDir(name: string): string;
|
|
183
|
+
/**
|
|
184
|
+
* Whether `name` ships its own `site-templates/<name>/` directory (spec 6892
|
|
185
|
+
* D1) — `false` only for a declared `--template` alias whose `ownTree` is
|
|
186
|
+
* `false` (`minimalism-site` today). A `SHAPE_STARTERS` key owns its
|
|
187
|
+
* `shapes/<name>` tree outright, so it is never treeless by this predicate
|
|
188
|
+
* even though `templateTreeDir` also renames its directory.
|
|
189
|
+
*
|
|
190
|
+
* The one place a caller outside this module (or `template-names.ts`) asks
|
|
191
|
+
* "does this name have its own tree" without needing `ALIASES` itself —
|
|
192
|
+
* `starter-grid.ts`'s own header restricts its importers to this file and
|
|
193
|
+
* `template-names.ts`, so a producer script asking this question goes
|
|
194
|
+
* through this predicate rather than importing `ALIASES` directly.
|
|
195
|
+
*/
|
|
196
|
+
export declare function isOwnTree(name: string): boolean;
|
|
197
|
+
/**
|
|
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.
|
|
203
|
+
*/
|
|
204
|
+
export declare function shippedManifestPath(name: string): string;
|
|
188
205
|
/**
|
|
189
206
|
* Look up a declared template — the one exported function that yields a
|
|
190
207
|
* template's roster (#6753 AC-1); `TEMPLATE_MANIFESTS`, the frozen record it
|
|
@@ -19,28 +19,13 @@ export const STARTER_SHAPES = [
|
|
|
19
19
|
];
|
|
20
20
|
/**
|
|
21
21
|
* The closed **design** enum (same contract section) — the look applied to
|
|
22
|
-
* 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.
|
|
23
27
|
*/
|
|
24
28
|
export const STARTER_DESIGNS = ["standard", "minimalism"];
|
|
25
|
-
/**
|
|
26
|
-
* Each design's layout knobs (#6638) — the part of a design that is chrome,
|
|
27
|
-
* so a template names `design:` instead of re-typing a `layout` block, and any
|
|
28
|
-
* shape can wear any design. `resolveSiteChrome(layout, design)` reads it:
|
|
29
|
-
* an explicit `TemplateManifest.layout` wins field by field, then this, then
|
|
30
|
-
* `DEFAULT_SITE_CHROME`. `standard` declares nothing — it IS the defaults, so
|
|
31
|
-
* spelling them out here would be a second copy of them to drift. A plain
|
|
32
|
-
* object, not a file: this module touches no disk. The design's documented
|
|
33
|
-
* look lives in `site-templates/designs/<design>/DESIGN.md`.
|
|
34
|
-
*/
|
|
35
|
-
export const DESIGN_LAYOUTS = {
|
|
36
|
-
standard: {},
|
|
37
|
-
minimalism: {
|
|
38
|
-
nav: "mark-only",
|
|
39
|
-
footer: "centered",
|
|
40
|
-
announcementBanner: false,
|
|
41
|
-
home: "deck",
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
29
|
/**
|
|
45
30
|
* The base layer's directory under `site-templates/` (#5618 D1) — the seed
|
|
46
31
|
* files every template reaches by declaring `extends`, installed by
|
|
@@ -176,12 +161,52 @@ export const SHAPE_STARTERS = Object.freeze(Object.fromEntries(Object.entries(SH
|
|
|
176
161
|
* Where a starter's files live under `site-templates/` (#6757 D5) — the one
|
|
177
162
|
* place that says so. A `SHAPE_STARTERS` key (a bare-shape starter with no
|
|
178
163
|
* `--template` alias) lives at `shapes/<name>`, out of the template showcase
|
|
179
|
-
* (§ 1.6)
|
|
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.
|
|
180
167
|
*/
|
|
181
168
|
export function templateTreeDir(name) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
+
}
|
|
178
|
+
/**
|
|
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.
|
|
184
|
+
*
|
|
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.
|
|
190
|
+
*/
|
|
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
|
+
}
|
|
197
|
+
/**
|
|
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.
|
|
203
|
+
*/
|
|
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`;
|
|
185
210
|
}
|
|
186
211
|
/**
|
|
187
212
|
* Look up a declared template — the one exported function that yields a
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-ailk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Scaffolder for AI Launch Kit — `pnpm create ailk@latest <dir>` produces a starter repo consuming @working-theory/* from npm (per ADR 0007).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"tar": "^7.5.21"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@working-theory/
|
|
29
|
-
"@working-theory/
|
|
28
|
+
"@working-theory/schema": "0.29.0",
|
|
29
|
+
"@working-theory/templates": "0.29.0"
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"build-catalog": "node scripts/build-component-catalog.mjs",
|
|
59
59
|
"check-catalog": "node scripts/build-component-catalog.mjs --check",
|
|
60
60
|
"manifest:build": "node scripts/build-template-manifest.mjs --template marketing-site-free",
|
|
61
|
-
"manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check",
|
|
61
|
+
"manifest:check": "node scripts/build-template-manifest.mjs --template marketing-site-free --check && node scripts/build-template-manifest.mjs --template marketing-site --check && node scripts/build-template-manifest.mjs --template minimalism-site --check",
|
|
62
|
+
"design:gen": "tsc -b && node scripts/build-design-md.mjs",
|
|
62
63
|
"page-types:build": "tsx scripts/generate-page-types-reference.ts",
|
|
63
64
|
"page-types:check": "tsx scripts/generate-page-types-reference.ts --check"
|
|
64
65
|
}
|