create-ailk 0.13.0 → 0.15.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 +12 -4
- package/component-catalog.json +250 -36
- package/component-catalog.md +10 -6
- package/dist/add-module.d.ts +7 -0
- package/dist/add-module.js +34 -2
- package/dist/add-route.js +18 -2
- package/dist/apply-design.d.ts +32 -2
- package/dist/apply-design.js +155 -22
- package/dist/cli.js +24 -3
- package/dist/derive-architecture.d.ts +15 -2
- package/dist/derive-architecture.js +34 -6
- package/dist/design-compile.d.ts +70 -2
- package/dist/design-compile.js +97 -1
- package/dist/design-home.d.ts +74 -0
- package/dist/design-home.js +256 -0
- package/dist/design-package.d.ts +39 -2
- package/dist/design-package.js +224 -9
- package/dist/design-presets.d.ts +17 -1
- package/dist/design-presets.js +17 -5
- package/dist/design-scaffold.d.ts +6 -25
- package/dist/design-scaffold.js +26 -74
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/lib/chrome-readers.d.ts +21 -0
- package/dist/lib/chrome-readers.js +100 -0
- package/dist/lib/design-checks.d.ts +12 -1
- package/dist/lib/design-checks.js +38 -2
- package/dist/lib/extract-module-bundle.d.ts +11 -0
- package/dist/lib/extract-module-bundle.js +36 -6
- package/dist/lib/license-key-files.d.ts +6 -1
- package/dist/lib/license-key-files.js +9 -4
- package/dist/lib/module-license-gate.d.ts +9 -0
- package/dist/lib/module-license-gate.js +18 -0
- package/dist/module-architecture.d.ts +9 -0
- package/dist/module-architecture.js +6 -0
- package/dist/parse-args.d.ts +22 -0
- package/dist/parse-args.js +73 -0
- package/dist/programmatic.d.ts +20 -0
- package/dist/programmatic.js +180 -35
- package/dist/site-chrome.d.ts +42 -0
- package/dist/site-chrome.js +140 -2
- package/dist/site-fonts.d.ts +21 -0
- package/dist/site-fonts.js +54 -0
- package/dist/site-manifest.d.ts +12 -5
- package/dist/site-manifest.js +25 -11
- package/dist/site-roster.d.ts +16 -0
- package/dist/site-roster.js +81 -0
- package/dist/starter-grid.js +15 -0
- package/dist/surfaces.js +12 -0
- package/dist/sync-routes.js +11 -2
- package/dist/team-profiles.d.ts +29 -0
- package/dist/team-profiles.js +43 -0
- package/dist/topology.d.ts +131 -0
- package/dist/topology.js +533 -0
- package/package.json +3 -3
- package/release-scope.json +1 -1
- package/site-templates/_base/content/en/TeamList/team.mdx +68 -0
- package/site-templates/_base/content/en/TeamMember/first-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/second-member.mdx +22 -0
- package/site-templates/_base/content/en/TeamMember/third-member.mdx +22 -0
- package/site-templates/designs/minimalism/DESIGN.md +166 -36
- package/site-templates/designs/minimalism/home/local-business-service.mdx +119 -0
- package/site-templates/designs/minimalism/home/organization-product.mdx +132 -0
- package/site-templates/designs/minimalism/home/organization-service.mdx +131 -0
- package/site-templates/designs/minimalism/home/person-service.mdx +101 -0
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site/TEMPLATE-MANIFEST.md +20 -0
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/marketing-site-free/TEMPLATE-MANIFEST.md +22 -1
- package/site-templates/marketing-site-free/ailk-template.json +1 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/local-business-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/local-business-service/ailk-template.json +1 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.json +34 -1
- package/site-templates/shapes/organization-service/TEMPLATE-MANIFEST.md +26 -7
- package/site-templates/shapes/organization-service/ailk-template.json +1 -1
- package/templates/README.md +2 -0
- package/templates/apps/api/CLAUDE.md +6 -1
- package/templates/apps/api/package.json +1 -0
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +5 -1
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +10 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +114 -1
- package/templates/apps/api/src/lib/__tests__/site.test.ts +54 -0
- package/templates/apps/api/src/lib/agent-store.ts +95 -3
- package/templates/apps/api/src/lib/site.ts +37 -0
- package/templates/apps/api/src/lib/spoke-store.ts +18 -2
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +4 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +22 -0
- package/templates/apps/api/src/middleware/auth.ts +3 -0
- package/templates/apps/api/src/openapi/__tests__/openapi.test.ts +23 -0
- package/templates/apps/api/src/openapi/spec.ts +38 -2
- package/templates/apps/api/src/routes/checkout/__tests__/sessions.test.ts +50 -2
- package/templates/apps/api/src/routes/checkout/sessions.ts +7 -0
- package/templates/apps/api/src/routes/leads/__tests__/index.test.ts +275 -0
- package/templates/apps/api/src/routes/leads/index.ts +69 -6
- package/templates/apps/api/src/routes/webhooks/README.md +39 -0
- package/templates/apps/api/src/routes/webhooks/__tests__/stripe-site-filter.test.ts +366 -0
- package/templates/apps/api/src/routes/webhooks/stripe.ts +61 -1
- package/templates/apps/api/src/services/__tests__/lead-routing.test.ts +199 -0
- package/templates/apps/api/src/services/default-tenant.ts +41 -7
- package/templates/apps/api/src/services/lead-routing.ts +148 -29
- package/templates/apps/api/src/services/public-site-resolver.ts +48 -0
- package/templates/apps/mcp/__tests__/index.test.ts +5 -5
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/mcp/src/index.ts +6 -4
- package/templates/apps/mcp/src/lib/content-adapter.ts +2 -2
- package/templates/apps/mcp/src/lib/topology.generated.ts +24 -0
- package/templates/apps/web/CLAUDE.md +12 -0
- package/templates/apps/web/app/[locale]/(authed)/account/__tests__/settings.test.tsx +10 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/__tests__/theme-modes.test.ts +42 -0
- package/templates/apps/web/app/[locale]/(authed)/account/settings/theme-modes.ts +8 -0
- package/templates/apps/web/app/[locale]/[slug]/[detail]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/careers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/case-studies/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/clients/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/contact/__tests__/page-renderer-migration.test.tsx +66 -14
- package/templates/apps/web/app/[locale]/contact/__tests__/site-contact-lines.test.ts +163 -0
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +68 -0
- package/templates/apps/web/app/[locale]/courses/__tests__/page-renderer-migration.test.tsx +4 -4
- package/templates/apps/web/app/[locale]/downloads/__tests__/page-renderer-migration.test.tsx +15 -3
- package/templates/apps/web/app/[locale]/events/__tests__/page-renderer-migration.test.tsx +17 -6
- package/templates/apps/web/app/[locale]/for/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/how-to/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/integrations/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/layout.tsx +212 -57
- package/templates/apps/web/app/[locale]/newsletter/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/page.tsx +37 -0
- package/templates/apps/web/app/[locale]/partners/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/products/__tests__/page-renderer-migration.test.tsx +4 -6
- package/templates/apps/web/app/[locale]/projects/__tests__/page-renderer-migration.test.tsx +5 -2
- package/templates/apps/web/app/[locale]/resources/__tests__/page-renderer-migration.test.tsx +15 -2
- package/templates/apps/web/app/[locale]/services/__tests__/page-renderer-migration.test.tsx +43 -6
- package/templates/apps/web/app/[locale]/services/page.tsx +14 -2
- package/templates/apps/web/app/[locale]/software/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/team/[slug]/page.tsx +45 -0
- package/templates/apps/web/app/[locale]/team/__tests__/team-routes.test.tsx +155 -0
- package/templates/apps/web/app/[locale]/team/page.tsx +49 -0
- package/templates/apps/web/app/[locale]/tech-articles/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/[locale]/testimonials/__tests__/page-renderer-migration.test.tsx +14 -1
- package/templates/apps/web/app/[locale]/thanks/[slug]/__tests__/page.test.tsx +3 -0
- package/templates/apps/web/app/[locale]/whitepapers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/not-found.tsx +47 -0
- package/templates/apps/web/app/openapi.json/__tests__/route.test.ts +89 -1
- package/templates/apps/web/app/openapi.json/route.ts +69 -1
- package/templates/apps/web/instrumentation.ts +7 -4
- package/templates/apps/web/lib/__tests__/closing-footer-path.test.ts +96 -0
- package/templates/apps/web/lib/__tests__/profile-back-link.test.ts +95 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +290 -0
- package/templates/apps/web/lib/__tests__/site-brand.test.ts +52 -0
- package/templates/apps/web/lib/__tests__/site-theme-head.test.ts +70 -0
- package/templates/apps/web/lib/__tests__/site-theme.test.ts +64 -1
- package/templates/apps/web/lib/closing-footer-path.ts +50 -0
- package/templates/apps/web/lib/profile-back-link.ts +51 -0
- package/templates/apps/web/lib/relay.ts +206 -2
- package/templates/apps/web/lib/site-brand.tsx +22 -1
- package/templates/apps/web/lib/site-chrome.ts +15 -0
- package/templates/apps/web/lib/site-fonts.ts +15 -0
- package/templates/apps/web/lib/site-logo.tsx +39 -1
- package/templates/apps/web/lib/site-theme-head.ts +33 -0
- package/templates/apps/web/lib/site-theme.ts +13 -1
- package/templates/apps/web/lib/topology.generated.ts +23 -0
- package/templates/apps/web/messages/en.json +7 -1
- package/templates/apps/web/package.json +1 -1
- package/templates/database/CHANGELOG.md +32 -0
- package/templates/database/__tests__/agents.db.test.ts +65 -0
- package/templates/database/__tests__/lead-idempotency-key.db.test.ts +92 -0
- package/templates/database/auth/schema.prisma +23 -0
- package/templates/database/inbox/schema.prisma +13 -0
- package/templates/database/jest.config.cjs +7 -11
- package/templates/database/migrations/20261008120000_user_last_workspace/migration.sql +35 -0
- package/templates/database/migrations/20261008130000_lead_idempotency_key/migration.sql +20 -0
- package/templates/database/migrations/20261009120000_agent_grants/migration.sql +33 -0
- package/templates/database/package.json +1 -1
- package/versions.json +35 -34
- package/site-templates/designs/minimalism/sample/README.md +0 -48
- package/site-templates/designs/minimalism/sample/content/en/HomePage/home.mdx +0 -244
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-1.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-2.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-3.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-4.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-5.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/avatar-placeholder-6.svg +0 -7
- package/site-templates/designs/minimalism/sample/public/hero-backdrop.svg +0 -17
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-1.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-2.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/logo-placeholder-3.svg +0 -6
- package/site-templates/designs/minimalism/sample/public/wordmark-placeholder.svg +0 -28
package/dist/programmatic.js
CHANGED
|
@@ -15,33 +15,36 @@
|
|
|
15
15
|
* import { scaffoldSync } from './programmatic.js';
|
|
16
16
|
* const result = scaffoldSync({ targetDir: '/tmp/my-site', projectName: 'my-site' });
|
|
17
17
|
*/
|
|
18
|
-
import { existsSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
18
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
19
19
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
20
20
|
import { fileURLToPath } from "node:url";
|
|
21
|
-
import { AddModuleRefusedError, applyModuleToScaffold, } from "./add-module.js";
|
|
21
|
+
import { AddModuleRefusedError, addModuleSiteTopology, applyModuleToScaffold, } from "./add-module.js";
|
|
22
22
|
import { planDesignMd } from "./apply-design-md.js";
|
|
23
23
|
import { renderClaudeMd, syncClaudeMd, } from "./claude-md.js";
|
|
24
24
|
import { CATALOG_TARGET_PATH, writeComponentCatalog, } from "./component-catalog.js";
|
|
25
25
|
import { buildScaffoldMarkers, copyTemplate } from "./copy.js";
|
|
26
26
|
import { deriveArchitecture } from "./derive-architecture.js";
|
|
27
|
-
import { compileDesignTheme } from "./design-compile.js";
|
|
27
|
+
import { compileDesignFonts, compileDesignTheme } from "./design-compile.js";
|
|
28
|
+
import { HOME_PAGE_PATH, designHomeSeeds, planDesignHome, shapeHomePath, shippedHomeSeeds, } from "./design-home.js";
|
|
28
29
|
import { applyDesignPresets, readSectionLookCatalog, shippedPageTypes, validateDesignPresets, } from "./design-presets.js";
|
|
29
|
-
import {
|
|
30
|
+
import { resolveDesign, spliceThemeBlock, } from "./design-scaffold.js";
|
|
30
31
|
import { writeAuthSecretFile } from "./lib/auth-secret-file.js";
|
|
31
32
|
import { extractModuleBundle, installBundleTree, } from "./lib/extract-module-bundle.js";
|
|
32
33
|
import { fetchTemplate } from "./lib/fetch-template.js";
|
|
33
34
|
import { assertLicenseKeyShape, writeLicenseKeyFiles, } from "./lib/license-key-files.js";
|
|
34
|
-
import { assertDesignAllowed, assertReleased, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
35
|
+
import { assertDesignAllowed, assertReleased, deriveDesignLevel, deriveLevel, resolveModuleSet, resolvePaidModules, } from "./lib/module-license-gate.js";
|
|
35
36
|
import { classifyModules } from "./lib/module-tier.js";
|
|
36
37
|
import { normalizeModules } from "./module-architecture.js";
|
|
37
38
|
import { writePaidSections } from "./paid-sections.js";
|
|
38
39
|
import { RECOGNIZED_SHAPES, RESERVED_SHAPES, } from "./parse-args.js";
|
|
39
40
|
import { SITE_CHROME_PATH, renderSiteChrome, resolveSiteChrome, } from "./site-chrome.js";
|
|
41
|
+
import { SITE_FONTS_PATH, renderSiteFonts } from "./site-fonts.js";
|
|
40
42
|
import { planSiteManifest } from "./site-manifest.js";
|
|
41
|
-
import { SITE_ROSTER_PATH, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
43
|
+
import { SITE_ROSTER_PATH, planHomeRecord, planSiteManifestDesign, readSiteRoster, withMeetPlaceholderSeeds, } from "./site-roster.js";
|
|
42
44
|
import { GATEABLE_SURFACES, MODULE_SURFACES, NAV_CONFIG_PATH, excludedRoutePaths, renderNavConfig, resolveSurfaces, } from "./surfaces.js";
|
|
43
45
|
import { syncRoutes } from "./sync-routes.js";
|
|
44
46
|
import { BASE_LAYER_DIR, SHAPE_STARTERS, TEMPLATE_MANIFESTS, TemplateError, mergeRoster, resolveTemplate, resolveTemplateLineage, rosterDeclares, templateDelivery, templateTreeDir, } from "./template-manifest.js";
|
|
47
|
+
import { TopologyError, applySpokeTransforms, isSpokeExcluded, normalizeHubUrl, readSiteTopology, spokeExclusions, spokeModuleRefusal, spokeModules, writeHubOriginEnv, } from "./topology.js";
|
|
45
48
|
import { validateTarget } from "./validate.js";
|
|
46
49
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
47
50
|
const TEMPLATES_DIR = resolve(PKG_ROOT, "templates");
|
|
@@ -149,14 +152,23 @@ export function scaffoldSync(opts) {
|
|
|
149
152
|
// The declared route surface (#4154). Resolved BEFORE the copy: it decides
|
|
150
153
|
// both which route directories are skipped and what architecture.yaml records.
|
|
151
154
|
const enabledSurfaces = resolveSurfaces(opts.modules ?? ["marketing"], opts.surfaces ?? []);
|
|
155
|
+
// #7426 D4 — a spoke is this same copy with the spoke's exclusions added:
|
|
156
|
+
// one registry over the one template tree, never a second tree.
|
|
157
|
+
const spoke = opts.topology === "spoke" ? { mcp: opts.mcp !== false } : undefined;
|
|
152
158
|
const versions = readVersionMap();
|
|
153
159
|
copyTemplate({
|
|
154
160
|
templatesDir: TEMPLATES_DIR,
|
|
155
161
|
targetDir: targetAbs,
|
|
156
162
|
versions,
|
|
157
|
-
excludePaths:
|
|
163
|
+
excludePaths: [
|
|
164
|
+
...excludedRoutePaths(enabledSurfaces),
|
|
165
|
+
...(spoke !== undefined ? spokeExclusions(TEMPLATES_DIR, spoke) : []),
|
|
166
|
+
],
|
|
158
167
|
markers: buildScaffoldMarkers(projectName),
|
|
159
168
|
});
|
|
169
|
+
// #7426 D5/D6 — the root files a spoke rewrites, and the spoke seams.
|
|
170
|
+
if (spoke !== undefined)
|
|
171
|
+
applySpokeTransforms(targetAbs, spoke);
|
|
160
172
|
// Stamp the copied CLAUDE.md's template regions with their hashes (#6904
|
|
161
173
|
// D2). No later layer writes a root CLAUDE.md, so this is the final file.
|
|
162
174
|
writeFileSync(join(targetAbs, "CLAUDE.md"), renderClaudeMd(readFileSync(join(TEMPLATES_DIR, "CLAUDE.md"), "utf8"), projectName));
|
|
@@ -165,7 +177,7 @@ export function scaffoldSync(opts) {
|
|
|
165
177
|
// closes the dangling `architecture_rules: architecture.yaml` reference in
|
|
166
178
|
// the scaffolded project.yaml. Written AFTER the template copy, at the target
|
|
167
179
|
// root — a deterministic function of the selected module set.
|
|
168
|
-
writeFileSync(join(targetAbs, "architecture.yaml"), deriveArchitecture(opts.modules ?? ["marketing"], enabledSurfaces));
|
|
180
|
+
writeFileSync(join(targetAbs, "architecture.yaml"), deriveArchitecture(opts.modules ?? ["marketing"], enabledSurfaces, spoke !== undefined ? { topology: "spoke", mcp: spoke.mcp } : {}));
|
|
169
181
|
// Regenerate the site nav from the same surface set (#4154). The nav is a
|
|
170
182
|
// static list the layout renders into the header AND the footer, so leaving
|
|
171
183
|
// the template's copy in place would link /docs and /blog sitewide in a
|
|
@@ -250,6 +262,9 @@ export async function scaffold(opts) {
|
|
|
250
262
|
? resolveShapeTemplate(opts.shape)
|
|
251
263
|
: undefined;
|
|
252
264
|
assertReleased("modules", [...(opts.modules ?? []), ...(template?.requires ?? [])]);
|
|
265
|
+
// #7426 D1-D3/D7 — a spoke's refusals, before anything is fetched or
|
|
266
|
+
// written: the target directory never exists for a refused spoke.
|
|
267
|
+
const spoke = resolveSpoke(opts, template);
|
|
253
268
|
// #6813 D8 step 3 — the design this scaffold renders, resolved from its
|
|
254
269
|
// package ONCE, before any fetch or write: the caller's override, else the
|
|
255
270
|
// template's own. Unset for a scaffold naming neither. Replaces the
|
|
@@ -267,14 +282,22 @@ export async function scaffold(opts) {
|
|
|
267
282
|
// (D2): a non-look field or unknown variant throws `DesignPackageError`
|
|
268
283
|
// naming page type, section type and field, and the target is never
|
|
269
284
|
// created. A design with no `sections` block executes no new read (D8).
|
|
285
|
+
let lookCatalog;
|
|
286
|
+
const catalog = () => (lookCatalog ??= readSectionLookCatalog(join(PKG_ROOT, "component-catalog.json")));
|
|
270
287
|
if (resolvedDesign !== undefined &&
|
|
271
288
|
Object.keys(resolvedDesign.pkg.sections).length > 0) {
|
|
272
289
|
validateDesignPresets(resolvedDesign.pkg.sections, {
|
|
273
|
-
catalog:
|
|
290
|
+
catalog: catalog(),
|
|
274
291
|
pageTypes: shippedPageTypes(SITE_TEMPLATES_DIR),
|
|
275
292
|
file: join(resolvedDesign.dir, "DESIGN.md"),
|
|
276
293
|
});
|
|
277
294
|
}
|
|
295
|
+
// #7743 D5 — the design's home seeds are read and validated beside its
|
|
296
|
+
// presets: a malformed seed throws `DesignPackageError`, naming the file,
|
|
297
|
+
// before the target is created.
|
|
298
|
+
const designSeeds = resolvedDesign !== undefined
|
|
299
|
+
? designHomeSeeds(resolvedDesign, { designsDir: DESIGNS_DIR, catalog })
|
|
300
|
+
: [];
|
|
278
301
|
// #6813 D7 — the resolved design only when the CALLER chose one (`opts.design`);
|
|
279
302
|
// a template's own declared design is never gated (#6778 option (c)).
|
|
280
303
|
const chosenDesign = opts.design !== undefined ? resolvedDesign : undefined;
|
|
@@ -298,10 +321,12 @@ export async function scaffold(opts) {
|
|
|
298
321
|
// list.
|
|
299
322
|
let modules;
|
|
300
323
|
let bundles;
|
|
324
|
+
// #7426 D7 — the key's default modules a spoke does not admit.
|
|
325
|
+
let dropped = [];
|
|
301
326
|
// The run's derived level (#6759), for `assertDesignAllowed` below.
|
|
302
327
|
// Undefined on the alias branch and on a `--modules`-keep run (D2/D4 in
|
|
303
328
|
// module-license-gate.ts — the level plays no part in composing either),
|
|
304
|
-
// so a non-`standard` design on either path derives it separately
|
|
329
|
+
// so a non-`standard` design on either path derives it separately.
|
|
305
330
|
let level;
|
|
306
331
|
if (opts.template !== undefined) {
|
|
307
332
|
// Alias run — untouched (NFR-4): the union line and its
|
|
@@ -350,6 +375,27 @@ export async function scaffold(opts) {
|
|
|
350
375
|
});
|
|
351
376
|
bundles = chosen.bundles;
|
|
352
377
|
level = chosen.level;
|
|
378
|
+
// #7426 D7 — a key-derived default set keeps only what a spoke admits;
|
|
379
|
+
// the run names what it dropped (`droppedModules`).
|
|
380
|
+
if (spoke !== undefined && opts.modules === undefined) {
|
|
381
|
+
const admitted = new Set(spokeModules());
|
|
382
|
+
dropped = chosen.modules.filter((m) => !admitted.has(m));
|
|
383
|
+
chosen.modules = chosen.modules.filter((m) => admitted.has(m));
|
|
384
|
+
for (const name of dropped)
|
|
385
|
+
bundles.delete(name);
|
|
386
|
+
}
|
|
387
|
+
// #7010 — a `--modules`-keep run composes no level (NFR-2), so a chosen
|
|
388
|
+
// non-Free design derives one from the key here, as the addition branch
|
|
389
|
+
// does; a plain keep run makes no extra network call.
|
|
390
|
+
if (level === undefined && chosenDesign !== undefined && !chosenDesign.free) {
|
|
391
|
+
level = await deriveDesignLevel({
|
|
392
|
+
licenseKey,
|
|
393
|
+
...(opts.moduleRegistryUrl !== undefined
|
|
394
|
+
? { registryUrl: opts.moduleRegistryUrl }
|
|
395
|
+
: {}),
|
|
396
|
+
...(opts.fetchImpl !== undefined ? { fetchImpl: opts.fetchImpl } : {}),
|
|
397
|
+
});
|
|
398
|
+
}
|
|
353
399
|
modules =
|
|
354
400
|
template !== undefined
|
|
355
401
|
? normalizeModules([...template.requires, ...chosen.modules])
|
|
@@ -378,9 +424,9 @@ export async function scaffold(opts) {
|
|
|
378
424
|
}
|
|
379
425
|
// #6760 D4 — after the level is resolved, before any fetch or write below.
|
|
380
426
|
// Only the caller's chosen design is gated (#6778 option (c)); a template's
|
|
381
|
-
// own declared design is not. `level ?? "free"` fails closed: a
|
|
382
|
-
// `--modules`-keep run
|
|
383
|
-
//
|
|
427
|
+
// own declared design is not. `level ?? "free"` fails closed: a level left
|
|
428
|
+
// underived (a plain alias or `--modules`-keep run with no chosen non-Free
|
|
429
|
+
// design) is never trusted. The
|
|
384
430
|
// refusal names `chosen.display` — the name, or the absolute directory
|
|
385
431
|
// (#6813 D7) — and `chosen.free` decides Free-ness by identity (D2), never
|
|
386
432
|
// by `FREE_DESIGNS.includes(name)` alone (a path is never in that list).
|
|
@@ -431,6 +477,7 @@ export async function scaffold(opts) {
|
|
|
431
477
|
const result = scaffoldSync({
|
|
432
478
|
...opts,
|
|
433
479
|
modules,
|
|
480
|
+
...(spoke !== undefined ? { topology: "spoke", mcp: spoke.mcp } : {}),
|
|
434
481
|
...(surfaces !== undefined ? { surfaces } : {}),
|
|
435
482
|
// #5612 D1/D2 — the code registry's own `layout` block, known
|
|
436
483
|
// synchronously (unlike a bundle's `ailk-template.json`, only readable
|
|
@@ -464,6 +511,14 @@ export async function scaffold(opts) {
|
|
|
464
511
|
// anything. Starts empty at the base-layer boundary: the `templates/`
|
|
465
512
|
// snapshot (layer 0) is deliberately outside it.
|
|
466
513
|
const ownership = new Map();
|
|
514
|
+
// #7426 D4 — every layer below lands through the spoke filter: a module
|
|
515
|
+
// bundle's or the template's destination a spoke does not carry is dropped.
|
|
516
|
+
const exclusions = spoke !== undefined ? spokeExclusions(TEMPLATES_DIR, spoke) : [];
|
|
517
|
+
const spokeSkip = spoke !== undefined
|
|
518
|
+
? {
|
|
519
|
+
skip: (destination) => isSpokeExcluded(destination, { mcp: spoke.mcp, exclusions }),
|
|
520
|
+
}
|
|
521
|
+
: {};
|
|
467
522
|
// The BASE LAYER lands first (#5618 D1) — the seed files every template
|
|
468
523
|
// reaches by `extends` (today the three module-less base pages), read off
|
|
469
524
|
// the `site-templates/_base/` tree this package ships, through the same
|
|
@@ -483,6 +538,7 @@ export async function scaffold(opts) {
|
|
|
483
538
|
versions,
|
|
484
539
|
markers,
|
|
485
540
|
ownership,
|
|
541
|
+
...spokeSkip,
|
|
486
542
|
});
|
|
487
543
|
}
|
|
488
544
|
for (const [moduleName, bytes] of bundles) {
|
|
@@ -493,6 +549,7 @@ export async function scaffold(opts) {
|
|
|
493
549
|
versions,
|
|
494
550
|
markers,
|
|
495
551
|
ownership,
|
|
552
|
+
...spokeSkip,
|
|
496
553
|
});
|
|
497
554
|
}
|
|
498
555
|
// The template lands LAST — after `scaffoldSync` and after every module
|
|
@@ -528,6 +585,7 @@ export async function scaffold(opts) {
|
|
|
528
585
|
versions,
|
|
529
586
|
markers,
|
|
530
587
|
ownership,
|
|
588
|
+
...spokeSkip,
|
|
531
589
|
})
|
|
532
590
|
: (() => {
|
|
533
591
|
const sourceRoot = join(SITE_TEMPLATES_DIR, templateTreeDir(template.name));
|
|
@@ -543,6 +601,7 @@ export async function scaffold(opts) {
|
|
|
543
601
|
versions,
|
|
544
602
|
markers,
|
|
545
603
|
ownership,
|
|
604
|
+
...spokeSkip,
|
|
546
605
|
});
|
|
547
606
|
})();
|
|
548
607
|
installedLayout = installed.layout;
|
|
@@ -568,22 +627,6 @@ export async function scaffold(opts) {
|
|
|
568
627
|
: [];
|
|
569
628
|
writeFileSync(join(result.targetDir, NAV_CONFIG_PATH), renderNavConfig(result.surfaces, { serviceSlugs }));
|
|
570
629
|
pruneUnrosteredSeeds(result.targetDir, mergeRoster(template, template.requires), result.surfaces, ownership);
|
|
571
|
-
// spec 6892 D4 — the resolved design's own sample (the minimalism design's
|
|
572
|
-
// nine-slide deck home + its SVGs; no template declares `minimalism`
|
|
573
|
-
// since #6976, so none installs it today), installed AFTER the template
|
|
574
|
-
// layer so it wins over the shape's `HomePage/home.mdx`, and ONLY when
|
|
575
|
-
// the resolved design is the template's OWN declared design: a caller
|
|
576
|
-
// combination like `--shape organization-product --design minimalism`
|
|
577
|
-
// installs no sample (its roster carries neither the deck's link targets
|
|
578
|
-
// nor, in its generated record, the deck's sections).
|
|
579
|
-
if (resolvedDesign !== undefined &&
|
|
580
|
-
resolvedDesign.recorded === template.design) {
|
|
581
|
-
installDesignSample({
|
|
582
|
-
designDir: resolvedDesign.dir,
|
|
583
|
-
targetDir: result.targetDir,
|
|
584
|
-
ownership,
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
630
|
}
|
|
588
631
|
// #6813 D5/D8 steps 9-10 — whenever a design is resolved (the caller's,
|
|
589
632
|
// else the template's): the design's DESIGN.md (#6939); the compiled theme,
|
|
@@ -595,6 +638,15 @@ export async function scaffold(opts) {
|
|
|
595
638
|
// #6939 — the scaffold's DESIGN.md is the design it wears, not the one
|
|
596
639
|
// its template layer shipped; the site's own header lines are kept.
|
|
597
640
|
writeFileSync(join(result.targetDir, "DESIGN.md"), planDesignMd(resolvedDesign.dir, result.targetDir));
|
|
641
|
+
// #7720 D4.1 — the faces the design sources, beside site-chrome.ts. A
|
|
642
|
+
// design that sources none renders the overlay's committed default, so
|
|
643
|
+
// the file is left as the template tree wrote it.
|
|
644
|
+
const siteFontsPath = join(result.targetDir, SITE_FONTS_PATH);
|
|
645
|
+
const siteFonts = renderSiteFonts(compileDesignFonts(resolvedDesign.pkg));
|
|
646
|
+
if (!existsSync(siteFontsPath) || readFileSync(siteFontsPath, "utf8") !== siteFonts) {
|
|
647
|
+
mkdirSync(dirname(siteFontsPath), { recursive: true });
|
|
648
|
+
writeFileSync(siteFontsPath, siteFonts);
|
|
649
|
+
}
|
|
598
650
|
const compiledTheme = compileDesignTheme(resolvedDesign.pkg);
|
|
599
651
|
const siteMdxPath = join(result.targetDir, "content", "_site.mdx");
|
|
600
652
|
if (existsSync(siteMdxPath)) {
|
|
@@ -613,6 +665,27 @@ export async function scaffold(opts) {
|
|
|
613
665
|
writeFileSync(rosterPath, nextManifest);
|
|
614
666
|
}
|
|
615
667
|
}
|
|
668
|
+
// #7743 D5 — the design's home arc, seeded over a placeholder home once
|
|
669
|
+
// every layer has landed, before the presets dress it. A home that is not
|
|
670
|
+
// the shape's placeholder is never written; `--design standard` (no
|
|
671
|
+
// seeds) leaves the shape's home byte-identical. The one
|
|
672
|
+
// `planDesignHome` call site in scaffold().
|
|
673
|
+
const homePath = join(result.targetDir, ...HOME_PAGE_PATH.split("/"));
|
|
674
|
+
let homeSeeded = false;
|
|
675
|
+
if (template !== undefined) {
|
|
676
|
+
const homePlan = planDesignHome({
|
|
677
|
+
current: existsSync(homePath) ? readFileSync(homePath, "utf8") : undefined,
|
|
678
|
+
shape: template.shape,
|
|
679
|
+
target: designSeeds.find((seed) => seed.shape === template.shape),
|
|
680
|
+
shapeHome: readFileSync(shapeHomePath(template.shape, { siteTemplatesDir: SITE_TEMPLATES_DIR, templatesDir: TEMPLATES_DIR }), "utf8"),
|
|
681
|
+
shippedSeeds: shippedHomeSeeds({ designsDir: DESIGNS_DIR, catalog }),
|
|
682
|
+
catalog: catalog(),
|
|
683
|
+
});
|
|
684
|
+
if (homePlan.outcome === "seeded" && homePlan.text !== undefined) {
|
|
685
|
+
writeFileSync(homePath, homePlan.text);
|
|
686
|
+
homeSeeded = true;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
616
689
|
// #6816 D6 step 10b — the design's section presets, written onto the
|
|
617
690
|
// pages of the page types the roster of record places, once every layer
|
|
618
691
|
// has landed. The one `applyDesignPresets` call site. A scaffold with no
|
|
@@ -637,6 +710,14 @@ export async function scaffold(opts) {
|
|
|
637
710
|
writeFileSync(page.path, page.text);
|
|
638
711
|
}
|
|
639
712
|
}
|
|
713
|
+
// #7743 D6 — the manifest's `/` row records the seeded home's sections,
|
|
714
|
+
// as the presets left them.
|
|
715
|
+
const rosterPath = join(result.targetDir, SITE_ROSTER_PATH);
|
|
716
|
+
if (homeSeeded && existsSync(rosterPath)) {
|
|
717
|
+
const nextManifest = planHomeRecord(readFileSync(rosterPath, "utf8"), readFileSync(homePath, "utf8"), catalog());
|
|
718
|
+
if (nextManifest !== undefined)
|
|
719
|
+
writeFileSync(rosterPath, nextManifest);
|
|
720
|
+
}
|
|
640
721
|
}
|
|
641
722
|
// #6944 — the manifest records what this scaffold IS, once every layer has
|
|
642
723
|
// landed: the effective module set, a row for every route on disk, and the
|
|
@@ -652,6 +733,8 @@ export async function scaffold(opts) {
|
|
|
652
733
|
roster,
|
|
653
734
|
modules: result.modules,
|
|
654
735
|
level,
|
|
736
|
+
topology: spoke !== undefined ? "spoke" : "hub",
|
|
737
|
+
patternVersion: markers["CREATE_AILK_VERSION"] ?? "",
|
|
655
738
|
siteTemplatesDir: SITE_TEMPLATES_DIR,
|
|
656
739
|
templatesDir: TEMPLATES_DIR,
|
|
657
740
|
markers,
|
|
@@ -663,20 +746,70 @@ export async function scaffold(opts) {
|
|
|
663
746
|
writePaidSections(result.targetDir);
|
|
664
747
|
// #6107 D2 — after every layer landed, so nothing later overwrites it: a
|
|
665
748
|
// served scaffold needs BETTER_AUTH_SECRET (#6044), and this is its value.
|
|
666
|
-
|
|
749
|
+
// A spoke has no sign-in (#7426 D4), so it gets its hub's origin instead
|
|
750
|
+
// (D2) — never the spoke key, which the hub issues.
|
|
751
|
+
if (spoke !== undefined)
|
|
752
|
+
writeHubOriginEnv(result.targetDir, spoke.hubOrigin);
|
|
753
|
+
else
|
|
754
|
+
writeAuthSecretFile(result.targetDir);
|
|
667
755
|
// #6683 D9 — persisted only after every layer landed, and only when the
|
|
668
|
-
// caller passed `license`: a scaffold without it writes nothing new.
|
|
756
|
+
// caller passed `license`: a scaffold without it writes nothing new. A
|
|
757
|
+
// spoke has no apps/api, so it keeps the key in ailk.key alone (#7426 D6).
|
|
669
758
|
if (opts.license !== undefined) {
|
|
670
|
-
writeLicenseKeyFiles(result.targetDir, opts.license
|
|
759
|
+
writeLicenseKeyFiles(result.targetDir, opts.license, {
|
|
760
|
+
apiEnv: spoke === undefined,
|
|
761
|
+
});
|
|
671
762
|
}
|
|
672
763
|
// #6754 D4/D5 — the resolved starter, so the CLI names what it built.
|
|
764
|
+
const withDropped = {
|
|
765
|
+
...result,
|
|
766
|
+
...(dropped.length > 0 ? { droppedModules: dropped } : {}),
|
|
767
|
+
...(spoke !== undefined ? { hubOrigin: spoke.hubOrigin } : {}),
|
|
768
|
+
};
|
|
673
769
|
if (template !== undefined && resolvedDesign !== undefined) {
|
|
674
770
|
return {
|
|
675
|
-
...
|
|
771
|
+
...withDropped,
|
|
676
772
|
starter: { template: template.name, shape: template.shape, design: resolvedDesign.recorded },
|
|
677
773
|
};
|
|
678
774
|
}
|
|
679
|
-
return
|
|
775
|
+
return withDropped;
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* A spoke's resolved inputs (#7426 D1-D3/D7), or undefined for a hub. Every
|
|
779
|
+
* refusal is a `TemplateError` thrown before anything is fetched or written.
|
|
780
|
+
*/
|
|
781
|
+
function resolveSpoke(opts, template) {
|
|
782
|
+
if (opts.topology !== "spoke") {
|
|
783
|
+
const spokeOnly = [
|
|
784
|
+
opts.hubUrl !== undefined ? "hubUrl" : undefined,
|
|
785
|
+
opts.mcp !== undefined ? "mcp" : undefined,
|
|
786
|
+
].filter((f) => f !== undefined);
|
|
787
|
+
if (spokeOnly.length > 0) {
|
|
788
|
+
throw new TemplateError(`${spokeOnly.join(" and ")} only apply to a spoke — pass topology: "spoke"`);
|
|
789
|
+
}
|
|
790
|
+
return undefined;
|
|
791
|
+
}
|
|
792
|
+
if (template === undefined) {
|
|
793
|
+
throw new TemplateError("a spoke needs a starter — pass --shape <shape> or --template <name>: the site manifest is where the topology is recorded");
|
|
794
|
+
}
|
|
795
|
+
let hubOrigin;
|
|
796
|
+
try {
|
|
797
|
+
hubOrigin = normalizeHubUrl(opts.hubUrl ?? process.env["AILK_API_URL"]);
|
|
798
|
+
}
|
|
799
|
+
catch (err) {
|
|
800
|
+
if (err instanceof TopologyError)
|
|
801
|
+
throw new TemplateError(err.message);
|
|
802
|
+
throw err;
|
|
803
|
+
}
|
|
804
|
+
for (const name of normalizeModules([...(opts.modules ?? []), ...template.requires])) {
|
|
805
|
+
const refusal = spokeModuleRefusal(name);
|
|
806
|
+
if (refusal !== undefined) {
|
|
807
|
+
throw new TemplateError(template.requires.includes(name) && !(opts.modules ?? []).includes(name)
|
|
808
|
+
? `template "${template.name}" requires ${refusal}`
|
|
809
|
+
: refusal);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return { mcp: opts.mcp !== false, hubOrigin };
|
|
680
813
|
}
|
|
681
814
|
/**
|
|
682
815
|
* The per-row install of the two SHARED seed layers (#6646 D1, disposition
|
|
@@ -798,7 +931,12 @@ export function syncArchitectureSync(opts) {
|
|
|
798
931
|
throw new Error(`not a scaffolded repo: no architecture.yaml at ${targetAbs}. Run \`create-ailk <target-dir>\` to scaffold first.`);
|
|
799
932
|
}
|
|
800
933
|
const enabledSurfaces = resolveSurfaces(opts.modules, opts.surfaces ?? []);
|
|
801
|
-
|
|
934
|
+
// #7426 D13 — a spoke stays a spoke: its topology is read from its
|
|
935
|
+
// manifest, and `mcp` from whether it still carries apps/mcp.
|
|
936
|
+
const topology = readSiteTopology(targetAbs);
|
|
937
|
+
writeFileSync(archPath, deriveArchitecture(opts.modules, enabledSurfaces, topology === "spoke"
|
|
938
|
+
? { topology, mcp: existsSync(join(targetAbs, "apps", "mcp")) }
|
|
939
|
+
: {}));
|
|
802
940
|
return {
|
|
803
941
|
targetDir: targetAbs,
|
|
804
942
|
surfaces: enabledSurfaces,
|
|
@@ -916,6 +1054,13 @@ export async function addModule(opts) {
|
|
|
916
1054
|
if (!existsSync(join(targetAbs, "architecture.yaml"))) {
|
|
917
1055
|
throw new Error(`not a scaffolded repo: no architecture.yaml at ${targetAbs}. Run \`create-ailk <target-dir>\` to scaffold first.`);
|
|
918
1056
|
}
|
|
1057
|
+
// #7426 D13 — a spoke refuses a module it does not admit before any key
|
|
1058
|
+
// read or network call (`applyModuleToScaffold` refuses it again).
|
|
1059
|
+
if (addModuleSiteTopology(targetAbs) === "spoke") {
|
|
1060
|
+
const refusal = spokeModuleRefusal(opts.moduleName);
|
|
1061
|
+
if (refusal !== undefined)
|
|
1062
|
+
throw new AddModuleRefusedError(refusal);
|
|
1063
|
+
}
|
|
919
1064
|
const { paid } = classifyModules([opts.moduleName]);
|
|
920
1065
|
if (paid.length === 0) {
|
|
921
1066
|
throw new AddModuleRefusedError(`"${opts.moduleName}" ships with every scaffold — there is no module bundle to add`);
|
package/dist/site-chrome.d.ts
CHANGED
|
@@ -38,6 +38,24 @@ export type SiteChromeFooter = "columns" | "centered";
|
|
|
38
38
|
/** The `home` knob's vocabulary. `deck` → the locale-root route mounts
|
|
39
39
|
* MarketingShell with `snap="mandatory"` + `deckHero` (spec 5426 D7). */
|
|
40
40
|
export type SiteChromeHome = "default" | "deck";
|
|
41
|
+
/** The `innerPages` knob's vocabulary (#7721). `bare` → every non-home page
|
|
42
|
+
* gets the bare header and the one-bar footer. */
|
|
43
|
+
export type SiteChromeInnerPages = "default" | "bare";
|
|
44
|
+
/** The `colorMode` knob's vocabulary (#7742). `light` pins the site to light
|
|
45
|
+
* and removes every theme switch; dark then appears only as a deck ground. */
|
|
46
|
+
export type SiteChromeColorMode = "user" | "light";
|
|
47
|
+
/** The `chromeStyle` knob's vocabulary (#7742). `minimal` → the deck's closing
|
|
48
|
+
* slide is the wordmark + contact lines + one legal line, and the consent bar
|
|
49
|
+
* is a ground-coloured hairline bar that never covers the down cue. */
|
|
50
|
+
export type SiteChromeChromeStyle = "default" | "minimal";
|
|
51
|
+
/**
|
|
52
|
+
* The `deckGrounds` knob's vocabulary (#7719 D3) — the shared section surface
|
|
53
|
+
* enum, duplicated from `@working-theory/validation`'s `SectionSurfaceSchema`
|
|
54
|
+
* because `create-ailk` cannot import it (one runtime dependency, § 1.5);
|
|
55
|
+
* `site-chrome.test.ts` proves the two lists equal.
|
|
56
|
+
*/
|
|
57
|
+
export declare const DECK_GROUND_VALUES: readonly ["none", "gray-100", "wash", "wash-secondary", "gradient-brand", "gradient-dark", "solid-dark"];
|
|
58
|
+
export type DeckGround = (typeof DECK_GROUND_VALUES)[number];
|
|
41
59
|
/**
|
|
42
60
|
* A template's declared layout block — every field optional. Lives on
|
|
43
61
|
* `TemplateManifest.layout` (the code registry, `template-manifest.ts`) and
|
|
@@ -48,6 +66,18 @@ export interface TemplateLayout {
|
|
|
48
66
|
footer?: SiteChromeFooter;
|
|
49
67
|
announcementBanner?: boolean;
|
|
50
68
|
home?: SiteChromeHome;
|
|
69
|
+
/** #7719 D3 — the surfaces a deck home cycles, slide by slide, onto slides that author none. */
|
|
70
|
+
deckGrounds?: readonly DeckGround[];
|
|
71
|
+
/** #7721 D1 — `bare`: every non-home page wears the bare header and the one-bar footer. */
|
|
72
|
+
innerPages?: SiteChromeInnerPages;
|
|
73
|
+
/** #7740 D1 — `true`: the deck home presents slide by slide (a deck home only). */
|
|
74
|
+
deckPresentation?: boolean;
|
|
75
|
+
/** #7742 D1 — `light`: the site is pinned to light, with no theme switch. */
|
|
76
|
+
colorMode?: SiteChromeColorMode;
|
|
77
|
+
/** #7742 D1 — `minimal`: the minimal closing slide, consent bar and themed 404. */
|
|
78
|
+
chromeStyle?: SiteChromeChromeStyle;
|
|
79
|
+
/** #7797 D1 — route paths whose page ends on the closing footer (wordmark, contact lines, legal line). */
|
|
80
|
+
closingFooterPaths?: readonly string[];
|
|
51
81
|
}
|
|
52
82
|
/** `TemplateLayout` with every field resolved — what `site-chrome.ts` exports. */
|
|
53
83
|
export interface ResolvedSiteChrome {
|
|
@@ -55,6 +85,18 @@ export interface ResolvedSiteChrome {
|
|
|
55
85
|
footer: SiteChromeFooter;
|
|
56
86
|
announcementBanner: boolean;
|
|
57
87
|
home: SiteChromeHome;
|
|
88
|
+
/** Present only when a list resolves AND `home` is `deck` (#7719 D3) — never an `undefined` key. */
|
|
89
|
+
deckGrounds?: readonly DeckGround[];
|
|
90
|
+
/** Present only when it resolves to "bare" — never "default" (#7721 D1). */
|
|
91
|
+
innerPages?: "bare";
|
|
92
|
+
/** Present only when it resolves to `true` AND `home` is `deck` (#7740 D1) — never `false`. */
|
|
93
|
+
deckPresentation?: true;
|
|
94
|
+
/** Present only when it resolves to "light" — never "user" (#7742 D1). */
|
|
95
|
+
colorMode?: "light";
|
|
96
|
+
/** Present only when it resolves to "minimal" — never "default" (#7742 D1). */
|
|
97
|
+
chromeStyle?: "minimal";
|
|
98
|
+
/** Present only when a non-empty list resolves — never `[]` (#7797 D1). */
|
|
99
|
+
closingFooterPaths?: readonly string[];
|
|
58
100
|
}
|
|
59
101
|
/** Defaults (#5612 D1; banner off since #6469). */
|
|
60
102
|
export declare const DEFAULT_SITE_CHROME: ResolvedSiteChrome;
|