create-ailk 0.12.1 → 0.14.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 +247 -33
- package/component-catalog.md +10 -6
- package/dist/apply-design.d.ts +21 -2
- package/dist/apply-design.js +120 -19
- package/dist/cli.js +1 -0
- 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 +223 -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 +1 -0
- package/dist/lib/design-checks.d.ts +12 -1
- package/dist/lib/design-checks.js +38 -2
- package/dist/lib/module-license-gate.d.ts +9 -0
- package/dist/lib/module-license-gate.js +18 -0
- package/dist/module-architecture.js +27 -0
- package/dist/programmatic.js +70 -26
- package/dist/site-chrome.d.ts +38 -0
- package/dist/site-chrome.js +109 -2
- package/dist/site-fonts.d.ts +21 -0
- package/dist/site-fonts.js +54 -0
- 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/team-profiles.d.ts +29 -0
- package/dist/team-profiles.js +43 -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 +164 -34
- 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/.env.example +23 -9
- package/templates/README.md +2 -0
- package/templates/apps/api/CLAUDE.md +24 -17
- package/templates/apps/api/src/__tests__/factories/agent-caller.ts +98 -0
- package/templates/apps/api/src/__tests__/factories/in-memory-prisma.ts +321 -0
- package/templates/apps/api/src/__tests__/schedule-forwarded-rate-limit.test.ts +117 -0
- package/templates/apps/api/src/config/__tests__/hub-require-spoke-key.test.ts +39 -14
- package/templates/apps/api/src/config/index.ts +35 -20
- package/templates/apps/api/src/lib/__mocks__/prisma-client.js +3 -0
- package/templates/apps/api/src/lib/__mocks__/prisma.ts +14 -0
- package/templates/apps/api/src/lib/__tests__/agent-store.test.ts +468 -0
- package/templates/apps/api/src/lib/__tests__/spoke-store.test.ts +32 -2
- package/templates/apps/api/src/lib/agent-store.ts +521 -0
- package/templates/apps/api/src/lib/spoke-store.ts +13 -2
- package/templates/apps/api/src/middleware/__tests__/auth.bearer-path.test.ts +384 -1
- package/templates/apps/api/src/middleware/__tests__/auth.cookie-path.test.ts +61 -0
- package/templates/apps/api/src/middleware/__tests__/default-tenant.test.ts +176 -0
- package/templates/apps/api/src/middleware/__tests__/rate-limit.test.ts +76 -0
- package/templates/apps/api/src/middleware/auth.ts +219 -5
- package/templates/apps/api/src/middleware/default-tenant.ts +29 -8
- package/templates/apps/api/src/middleware/rate-limit.ts +35 -13
- package/templates/apps/api/src/routes/leads/__tests__/spoke.test.ts +76 -15
- package/templates/apps/api/src/routes/leads/index.ts +14 -6
- package/templates/apps/api/src/routes/project-listings/__tests__/me.test.ts +5 -6
- package/templates/apps/api/src/routes/project-listings/index.ts +14 -9
- package/templates/apps/api/src/routes/project-listings/me.ts +4 -3
- package/templates/apps/api/src/routes/project-listings/patch-copy.ts +7 -5
- package/templates/apps/api/src/routes/workspaces/__tests__/inbox-wire-schemas.test.ts +198 -0
- package/templates/apps/api/src/server.ts +27 -0
- package/templates/apps/api/src/services/__tests__/audit-log.test.ts +374 -57
- package/templates/apps/api/src/services/__tests__/leads-inbox.test.ts +25 -2
- package/templates/apps/api/src/services/__tests__/spoke-resolution.test.ts +303 -17
- package/templates/apps/api/src/services/audit-log.ts +278 -47
- package/templates/apps/api/src/services/default-tenant.ts +55 -5
- package/templates/apps/api/src/services/leads-inbox.ts +12 -21
- package/templates/apps/api/src/services/project-listings.ts +1 -21
- package/templates/apps/api/src/services/public-site-resolver.ts +103 -13
- package/templates/apps/mcp/CLAUDE.md +44 -5
- package/templates/apps/mcp/__tests__/parity.test.ts +53 -1
- package/templates/apps/mcp/__tests__/workspace-surface.test.ts +364 -0
- package/templates/apps/mcp/package.json +1 -1
- package/templates/apps/mcp/src/http-transport.ts +107 -1
- package/templates/apps/mcp/src/workspace-server.ts +193 -0
- package/templates/apps/web/.env.example +64 -37
- package/templates/apps/web/CLAUDE.md +11 -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]/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__/site-contact-lines.test.ts +69 -0
- package/templates/apps/web/app/[locale]/contact/site-contact-lines.ts +64 -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 +156 -53
- 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]/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]/whitepapers/__tests__/page-renderer-migration.test.tsx +2 -2
- package/templates/apps/web/app/not-found.tsx +47 -0
- package/templates/apps/web/app/v1/[...path]/route.ts +16 -0
- package/templates/apps/web/lib/__tests__/profile-back-link.test.ts +95 -0
- package/templates/apps/web/lib/__tests__/relay.test.ts +515 -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/profile-back-link.ts +51 -0
- package/templates/apps/web/lib/relay.ts +279 -0
- package/templates/apps/web/lib/site-chrome.ts +12 -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/messages/en.json +7 -1
- package/templates/apps/web/middleware.ts +5 -4
- package/templates/apps/web/next.config.mjs +6 -0
- package/templates/database/CHANGELOG.md +22 -0
- package/templates/database/__tests__/agents.db.test.ts +235 -0
- package/templates/database/auth/schema.prisma +61 -0
- package/templates/database/migrations/20261005120000_agents/migration.sql +84 -0
- package/templates/database/package.json +1 -1
- package/versions.json +35 -35
- 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/design-presets.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { type DesignSections } from "./design-package.js";
|
|
|
2
2
|
export interface SectionLookCatalogEntry {
|
|
3
3
|
/** The look group, in registry order (D2). */
|
|
4
4
|
readonly look: readonly string[];
|
|
5
|
+
/** The type's rendering component (#7743 D6 — what the site manifest records), when the catalog names one. */
|
|
6
|
+
readonly component: string | undefined;
|
|
5
7
|
/** The type's `data` JSON Schema `properties` (the catalog's `schema.properties`). */
|
|
6
8
|
readonly properties: Readonly<Record<string, unknown>>;
|
|
7
9
|
}
|
|
@@ -37,6 +39,9 @@ export interface LocatedDataEntry {
|
|
|
37
39
|
export interface LocatedSection {
|
|
38
40
|
readonly type: string;
|
|
39
41
|
readonly form: "flow" | "block";
|
|
42
|
+
/** #7743 D3 — the element's `[start, end)` span: flow, its `{ … }`; block, from its `- ` to its last line's end. */
|
|
43
|
+
readonly start: number;
|
|
44
|
+
readonly end: number;
|
|
40
45
|
/** Flow: the index of `data`'s `{`. Block: the end of the `data:` line (before its newline). */
|
|
41
46
|
readonly dataOpen: number;
|
|
42
47
|
/** Flow: the index of `data`'s `}`. Block: `dataOpen`. */
|
|
@@ -45,6 +50,15 @@ export interface LocatedSection {
|
|
|
45
50
|
readonly entryColumn: number;
|
|
46
51
|
readonly entries: readonly LocatedDataEntry[];
|
|
47
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* A page's located sections, with the `[valueStart, valueEnd)` span of the
|
|
55
|
+
* whole `sections:` value (#7743 D3): from just after `sections:` (and the
|
|
56
|
+
* spaces after it) to the flow form's `]`, or the block form's last line end.
|
|
57
|
+
*/
|
|
58
|
+
export type LocatedSections = LocatedSection[] & {
|
|
59
|
+
readonly valueStart: number;
|
|
60
|
+
readonly valueEnd: number;
|
|
61
|
+
};
|
|
48
62
|
/**
|
|
49
63
|
* Locate a page's `sections[]` in its frontmatter — the prettier-formatted
|
|
50
64
|
* flow form (`sections:\n [ { "id": …, "type": …, "data": { … } }, … ]`)
|
|
@@ -52,7 +66,7 @@ export interface LocatedSection {
|
|
|
52
66
|
* Undefined when the page has no frontmatter or no column-0 `sections:` key.
|
|
53
67
|
* Any other shape throws `TemplateError` naming the element (D5).
|
|
54
68
|
*/
|
|
55
|
-
export declare function locatePageSections(text: string, file: string):
|
|
69
|
+
export declare function locatePageSections(text: string, file: string): LocatedSections | undefined;
|
|
56
70
|
export interface PresetPage {
|
|
57
71
|
readonly path: string;
|
|
58
72
|
readonly text: string;
|
|
@@ -64,6 +78,8 @@ export interface PresetApplication {
|
|
|
64
78
|
readonly sectionType: string;
|
|
65
79
|
readonly field: string;
|
|
66
80
|
}
|
|
81
|
+
/** The frontmatter's column-0 `type:` value, or undefined when it has none. */
|
|
82
|
+
export declare function pageTypeOf(text: string, file: string): string | undefined;
|
|
67
83
|
/**
|
|
68
84
|
* Set one look field on section `index` of a page, splicing only its value
|
|
69
85
|
* span (D5): an existing value is replaced; an absent field is inserted as
|
package/dist/design-presets.js
CHANGED
|
@@ -44,6 +44,7 @@ export function readSectionLookCatalog(catalogPath) {
|
|
|
44
44
|
const properties = entry.schema["properties"];
|
|
45
45
|
out[type] = {
|
|
46
46
|
look: entry.look.map(String),
|
|
47
|
+
component: typeof entry.component === "string" ? entry.component : undefined,
|
|
47
48
|
properties: isPlainObject(properties) ? properties : {},
|
|
48
49
|
};
|
|
49
50
|
}
|
|
@@ -397,6 +398,8 @@ function locateFlow(text, open, end, file) {
|
|
|
397
398
|
out.push({
|
|
398
399
|
type,
|
|
399
400
|
form: "flow",
|
|
401
|
+
start: element.open,
|
|
402
|
+
end: element.close + 1,
|
|
400
403
|
dataOpen: data.open,
|
|
401
404
|
dataClose: data.close,
|
|
402
405
|
entryColumn: -1,
|
|
@@ -410,7 +413,7 @@ function locateFlow(text, open, end, file) {
|
|
|
410
413
|
}
|
|
411
414
|
if (text[i] !== "]")
|
|
412
415
|
cannotLocate(file, undefined, "the closing ]");
|
|
413
|
-
return out;
|
|
416
|
+
return { sections: out, close: i };
|
|
414
417
|
}
|
|
415
418
|
function splitLines(text, start, end) {
|
|
416
419
|
const lines = [];
|
|
@@ -510,9 +513,12 @@ function locateBlock(text, lines, file) {
|
|
|
510
513
|
lastDeep = l.end;
|
|
511
514
|
}
|
|
512
515
|
close();
|
|
516
|
+
const last = item.filter((l) => !l.blank).at(-1);
|
|
513
517
|
return {
|
|
514
518
|
type,
|
|
515
519
|
form: "block",
|
|
520
|
+
start: item[0].start + itemIndent,
|
|
521
|
+
end: last.end,
|
|
516
522
|
dataOpen: data.end,
|
|
517
523
|
dataClose: data.end,
|
|
518
524
|
entryColumn,
|
|
@@ -545,20 +551,26 @@ export function locatePageSections(text, file) {
|
|
|
545
551
|
const nextKey = /\n(?=[^\s#-])/.exec(text.slice(afterKey, fmEnd));
|
|
546
552
|
const valueEnd = nextKey ? afterKey + nextKey.index : fmEnd;
|
|
547
553
|
const first = skipSpace(text, afterKey, valueEnd);
|
|
548
|
-
if (text[first] === "[")
|
|
549
|
-
|
|
554
|
+
if (text[first] === "[") {
|
|
555
|
+
const { sections, close } = locateFlow(text, first, valueEnd, file);
|
|
556
|
+
return Object.assign(sections, { valueStart: afterKey, valueEnd: close + 1 });
|
|
557
|
+
}
|
|
550
558
|
const lineEnd = text.indexOf("\n", afterKey);
|
|
551
559
|
if (lineEnd >= 0 && text.slice(afterKey, lineEnd).trim() === "") {
|
|
552
560
|
const lines = splitLines(text, lineEnd + 1, valueEnd);
|
|
553
561
|
const firstLine = lines.find((l) => !l.blank);
|
|
554
562
|
if (firstLine === undefined || text.startsWith("- ", firstLine.start + firstLine.indent)) {
|
|
555
|
-
|
|
563
|
+
const lastLine = lines.filter((l) => !l.blank).at(-1);
|
|
564
|
+
return Object.assign(locateBlock(text, lines, file), {
|
|
565
|
+
valueStart: afterKey,
|
|
566
|
+
valueEnd: lastLine?.end ?? afterKey,
|
|
567
|
+
});
|
|
556
568
|
}
|
|
557
569
|
}
|
|
558
570
|
return cannotLocate(file, undefined, "a flow or block sequence");
|
|
559
571
|
}
|
|
560
572
|
/** The frontmatter's column-0 `type:` value, or undefined when it has none. */
|
|
561
|
-
function pageTypeOf(text, file) {
|
|
573
|
+
export function pageTypeOf(text, file) {
|
|
562
574
|
const fm = /^---\r?\n([\s\S]*?)\r?\n---[ \t]*\r?$/m.exec(text);
|
|
563
575
|
if (!fm || fm.index !== 0)
|
|
564
576
|
return undefined;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { type CompiledDesignTheme } from "./design-compile.js";
|
|
2
2
|
import { type DesignPackage } from "./design-package.js";
|
|
3
|
-
import type { LayerOwnership } from "./lib/extract-module-bundle.js";
|
|
4
3
|
/** One resolved `--design` reference (D2). */
|
|
5
4
|
export interface ResolvedDesign {
|
|
6
5
|
/** "name" when the reference was a registry name, "path" when a directory. */
|
|
@@ -38,9 +37,12 @@ export declare function resolveDesign(ref: string, opts: {
|
|
|
38
37
|
}): ResolvedDesign;
|
|
39
38
|
/**
|
|
40
39
|
* Render a compiled theme's `theme:` frontmatter block (D5) — `theme:`, each
|
|
41
|
-
* treatment present as a bare `key: value
|
|
42
|
-
* `
|
|
43
|
-
*
|
|
40
|
+
* treatment present as a bare `key: value` (#7739's `darkInk`/`figures` after
|
|
41
|
+
* `checkmarks`), the `typography:` group when compiled — every role and field
|
|
42
|
+
* in the compile's order, role keys unquoted, sizes as plain scalars (#7720
|
|
43
|
+
* D2, #7739 D2.5) — then `colors:` with `light:`/`dark:` sub-blocks, each
|
|
44
|
+
* colour slot as `slot: <JSON.stringify(value)>` in `DESIGN_COLOR_MAP`'s
|
|
45
|
+
* order. Ends with a trailing newline.
|
|
44
46
|
*/
|
|
45
47
|
export declare function renderThemeBlock(theme: CompiledDesignTheme): string;
|
|
46
48
|
/**
|
|
@@ -53,24 +55,3 @@ export declare function renderThemeBlock(theme: CompiledDesignTheme): string;
|
|
|
53
55
|
* @throws TemplateError when `text` carries no frontmatter to write into.
|
|
54
56
|
*/
|
|
55
57
|
export declare function spliceThemeBlock(text: string, theme: CompiledDesignTheme, file: string): string;
|
|
56
|
-
/**
|
|
57
|
-
* Install a design's `sample/` directory onto a scaffold (spec 6892 D4) — a
|
|
58
|
-
* FIXED, undeclared convention, not a design-package field: `sample/content/**`
|
|
59
|
-
* lands at the scaffold's `content/**`, `sample/public/**` at
|
|
60
|
-
* `apps/web/public/**`. A design with no `sample/` directory (e.g. `standard`)
|
|
61
|
-
* installs nothing.
|
|
62
|
-
*
|
|
63
|
-
* Refuses — before its first write, so a refused sample leaves `targetDir`
|
|
64
|
-
* untouched — any file outside those two roots, or any `.ts`/`.tsx`/`.js`/
|
|
65
|
-
* `.mjs` file (content only, the template §5.2 rule this design package
|
|
66
|
-
* amendment carries over). Every installed file's destination is recorded in
|
|
67
|
-
* `ownership` as `design:<name>` (`readDesignPackage(designDir).name`), the
|
|
68
|
-
* same call-scoped registry `installBundleTree` writes into.
|
|
69
|
-
*
|
|
70
|
-
* @throws Error on a file outside `content/`/`public/`, or a forbidden extension.
|
|
71
|
-
*/
|
|
72
|
-
export declare function installDesignSample(opts: {
|
|
73
|
-
designDir: string;
|
|
74
|
-
targetDir: string;
|
|
75
|
-
ownership: LayerOwnership;
|
|
76
|
-
}): void;
|
package/dist/design-scaffold.js
CHANGED
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
* directory it reads is supplied by the caller — so it stays importable from
|
|
11
11
|
* Jest's CJS transform, unlike `programmatic.ts`.
|
|
12
12
|
*/
|
|
13
|
-
import {
|
|
14
|
-
import { join,
|
|
13
|
+
import { statSync } from "node:fs";
|
|
14
|
+
import { join, resolve } from "node:path";
|
|
15
15
|
import { isDeepStrictEqual } from "node:util";
|
|
16
16
|
import { DESIGN_COLOR_MAP } from "./design-compile.js";
|
|
17
17
|
import { readDesignPackage } from "./design-package.js";
|
|
@@ -82,9 +82,12 @@ export function resolveDesign(ref, opts) {
|
|
|
82
82
|
const RUNTIME_COLOR_SLOT_ORDER = DESIGN_COLOR_MAP.filter((row) => row.mode === "light").map((row) => row.runtime);
|
|
83
83
|
/**
|
|
84
84
|
* Render a compiled theme's `theme:` frontmatter block (D5) — `theme:`, each
|
|
85
|
-
* treatment present as a bare `key: value
|
|
86
|
-
* `
|
|
87
|
-
*
|
|
85
|
+
* treatment present as a bare `key: value` (#7739's `darkInk`/`figures` after
|
|
86
|
+
* `checkmarks`), the `typography:` group when compiled — every role and field
|
|
87
|
+
* in the compile's order, role keys unquoted, sizes as plain scalars (#7720
|
|
88
|
+
* D2, #7739 D2.5) — then `colors:` with `light:`/`dark:` sub-blocks, each
|
|
89
|
+
* colour slot as `slot: <JSON.stringify(value)>` in `DESIGN_COLOR_MAP`'s
|
|
90
|
+
* order. Ends with a trailing newline.
|
|
88
91
|
*/
|
|
89
92
|
export function renderThemeBlock(theme) {
|
|
90
93
|
const lines = ["theme:"];
|
|
@@ -96,6 +99,24 @@ export function renderThemeBlock(theme) {
|
|
|
96
99
|
lines.push(` placeholders: ${theme.placeholders}`);
|
|
97
100
|
if (theme.checkmarks !== undefined)
|
|
98
101
|
lines.push(` checkmarks: ${theme.checkmarks}`);
|
|
102
|
+
if (theme.buttonShape !== undefined)
|
|
103
|
+
lines.push(` buttonShape: ${theme.buttonShape}`);
|
|
104
|
+
if (theme.darkInk !== undefined)
|
|
105
|
+
lines.push(` darkInk: ${theme.darkInk}`);
|
|
106
|
+
if (theme.figures !== undefined)
|
|
107
|
+
lines.push(` figures: ${theme.figures}`);
|
|
108
|
+
if (theme.typography !== undefined) {
|
|
109
|
+
lines.push(" typography:");
|
|
110
|
+
for (const [role, fields] of Object.entries(theme.typography)) {
|
|
111
|
+
lines.push(` ${role}:`);
|
|
112
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
113
|
+
lines.push(` ${field}: ${String(value)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// #7719 D6 — present only for a design that uses `solid-dark`, or (#7739 D2.3) sets `darkInk: accent`.
|
|
118
|
+
if (theme.darkBands !== undefined)
|
|
119
|
+
lines.push(` darkBands: ${theme.darkBands}`);
|
|
99
120
|
lines.push(" colors:");
|
|
100
121
|
for (const mode of ["light", "dark"]) {
|
|
101
122
|
lines.push(` ${mode}:`);
|
|
@@ -149,72 +170,3 @@ export function spliceThemeBlock(text, theme, file) {
|
|
|
149
170
|
: [...lines.slice(0, themeStart), ...rendered, ...lines.slice(themeEnd)];
|
|
150
171
|
return next.join("\n");
|
|
151
172
|
}
|
|
152
|
-
/** Forbidden extensions for a design sample's files (spec 6892 D4) — content only, never code. */
|
|
153
|
-
const SAMPLE_FORBIDDEN_EXTENSIONS = [".ts", ".tsx", ".js", ".mjs"];
|
|
154
|
-
function hasSampleForbiddenExtension(p) {
|
|
155
|
-
return SAMPLE_FORBIDDEN_EXTENSIONS.some((ext) => p.toLowerCase().endsWith(ext));
|
|
156
|
-
}
|
|
157
|
-
/** Every file under `dir`, as `/`-separated paths relative to `dir`. */
|
|
158
|
-
function listFiles(dir) {
|
|
159
|
-
const out = [];
|
|
160
|
-
const stack = [dir];
|
|
161
|
-
for (let cur = stack.pop(); cur !== undefined; cur = stack.pop()) {
|
|
162
|
-
for (const entry of readdirSync(cur, { withFileTypes: true })) {
|
|
163
|
-
const abs = join(cur, entry.name);
|
|
164
|
-
if (entry.isDirectory())
|
|
165
|
-
stack.push(abs);
|
|
166
|
-
else if (entry.isFile())
|
|
167
|
-
out.push(relative(dir, abs).split(sep).join("/"));
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
return out;
|
|
171
|
-
}
|
|
172
|
-
/** Where one `sample/` file lands on a scaffold (spec 6892 D4's fixed convention). */
|
|
173
|
-
function sampleDestination(rel) {
|
|
174
|
-
if (rel.startsWith("content/"))
|
|
175
|
-
return rel;
|
|
176
|
-
return `apps/web/public/${rel.slice("public/".length)}`;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Install a design's `sample/` directory onto a scaffold (spec 6892 D4) — a
|
|
180
|
-
* FIXED, undeclared convention, not a design-package field: `sample/content/**`
|
|
181
|
-
* lands at the scaffold's `content/**`, `sample/public/**` at
|
|
182
|
-
* `apps/web/public/**`. A design with no `sample/` directory (e.g. `standard`)
|
|
183
|
-
* installs nothing.
|
|
184
|
-
*
|
|
185
|
-
* Refuses — before its first write, so a refused sample leaves `targetDir`
|
|
186
|
-
* untouched — any file outside those two roots, or any `.ts`/`.tsx`/`.js`/
|
|
187
|
-
* `.mjs` file (content only, the template §5.2 rule this design package
|
|
188
|
-
* amendment carries over). Every installed file's destination is recorded in
|
|
189
|
-
* `ownership` as `design:<name>` (`readDesignPackage(designDir).name`), the
|
|
190
|
-
* same call-scoped registry `installBundleTree` writes into.
|
|
191
|
-
*
|
|
192
|
-
* @throws Error on a file outside `content/`/`public/`, or a forbidden extension.
|
|
193
|
-
*/
|
|
194
|
-
export function installDesignSample(opts) {
|
|
195
|
-
const sampleDir = join(opts.designDir, "sample");
|
|
196
|
-
if (!existsSync(sampleDir))
|
|
197
|
-
return;
|
|
198
|
-
const name = readDesignPackage(opts.designDir).name;
|
|
199
|
-
const subject = `design:${name}`;
|
|
200
|
-
// README.md at the sample root is the ONE documented exception (spec 6892
|
|
201
|
-
// D5: "the narrative sections move to designs/<name>/sample/README.md,
|
|
202
|
-
// which is not installed") — every other file is real content and is
|
|
203
|
-
// validated against the two roots below.
|
|
204
|
-
const files = listFiles(sampleDir).filter((rel) => rel !== "README.md");
|
|
205
|
-
for (const rel of files) {
|
|
206
|
-
if (!rel.startsWith("content/") && !rel.startsWith("public/")) {
|
|
207
|
-
throw new Error(`${subject}: sample file "${rel}" is outside content/ and public/ (spec 6892 D4)`);
|
|
208
|
-
}
|
|
209
|
-
if (hasSampleForbiddenExtension(rel)) {
|
|
210
|
-
throw new Error(`${subject}: sample file "${rel}" is a code file — a design sample is content only (spec 6892 D4)`);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
for (const rel of files) {
|
|
214
|
-
const destinationRel = sampleDestination(rel);
|
|
215
|
-
const destinationAbs = resolve(opts.targetDir, destinationRel);
|
|
216
|
-
mkdirSync(join(destinationAbs, ".."), { recursive: true });
|
|
217
|
-
copyFileSync(join(sampleDir, rel), destinationAbs);
|
|
218
|
-
opts.ownership.set(destinationRel, subject);
|
|
219
|
-
}
|
|
220
|
-
}
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export { DEFAULT_AILK_MODULE_REGISTRY_URL, FetchModuleError, fetchModule, type F
|
|
|
10
10
|
export { extractModuleBundle, type ExistingFilePolicy, type ExtractModuleBundleOptions, type InstallReportRow, } from "./lib/extract-module-bundle.js";
|
|
11
11
|
export { ADD_MODULE_HELP, AddModuleRefusedError, applyModuleToScaffold, formatAddModuleReport, parseAddModuleArgs, type AddModuleReportRow, type AddModuleResult, type ApplyModuleOptions, } from "./add-module.js";
|
|
12
12
|
export { APPLY_DESIGN_HELP, applyDesign, formatApplyDesignReport, parseApplyDesignArgs, type ApplyDesignArgs, type ApplyDesignChange, type ApplyDesignOptions, type ApplyDesignResult, } from "./apply-design.js";
|
|
13
|
+
export type { HomeSeedOutcome } from "./design-home.js";
|
|
13
14
|
export { SITE_ROSTER_PATH, readSiteRoster, siteRosterProblems, } from "./site-roster.js";
|
|
14
15
|
export { SYNC_ROUTES_HELP, formatSyncRoutesReport, listTemplateRouteDirs, parseSyncRoutesArgs, syncRoutes, type RouteReport, type SyncRoutesOptions, type SyncRoutesResult, } from "./sync-routes.js";
|
|
15
16
|
export { deriveArchitecture } from "./derive-architecture.js";
|
|
@@ -46,7 +46,18 @@ export type JsonLdDriftVerdict = {
|
|
|
46
46
|
changed: DriftNode[];
|
|
47
47
|
reason: string;
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* `after` with the standard home put back when the seeded home's JSON-LD is
|
|
51
|
+
* exactly the standard home's node followed by one `Person` per
|
|
52
|
+
* `TEAM_SEED_MEMBERS` member, in order (the people slide's `team` section,
|
|
53
|
+
* #7744 D8). Any other home, or a home the people slide did not change, is
|
|
54
|
+
* returned as it is.
|
|
55
|
+
*/
|
|
56
|
+
export declare function admitSeededPeopleSlide(before: ComposedPages, after: ComposedPages): ComposedPages;
|
|
57
|
+
/**
|
|
58
|
+
* Decide whether `apply-design <design>` left every page's JSON-LD unchanged
|
|
59
|
+
* (logo excluded), the seeded people slide's Persons admitted.
|
|
60
|
+
*/
|
|
50
61
|
export declare function assessJsonLdDrift(design: string, before: ComposedPages, after: ComposedPages): JsonLdDriftVerdict;
|
|
51
62
|
export interface MeasureApplyDesignDriftOptions {
|
|
52
63
|
/** create-ailk's built `dist/cli.js`. */
|
|
@@ -16,10 +16,17 @@
|
|
|
16
16
|
* touches the filesystem and processes only through the paths it is handed,
|
|
17
17
|
* so the driver and `__tests__/verify-scaffold-checks.test.ts` run the same
|
|
18
18
|
* procedure. Nothing here imports `@working-theory/theme`.
|
|
19
|
+
*
|
|
20
|
+
* One change is content, not drift (#7744 D8): a design that seeds an
|
|
21
|
+
* organization home's people slide adds one `Person` per seeded team member to
|
|
22
|
+
* the home's graph. `admitSeededPeopleSlide` puts the standard home back only
|
|
23
|
+
* when the seeded home is exactly that, so any other change still counts.
|
|
19
24
|
*/
|
|
20
25
|
import { execFileSync } from "node:child_process";
|
|
21
26
|
import { writeFileSync } from "node:fs";
|
|
22
27
|
import { join } from "node:path";
|
|
28
|
+
import { isDeepStrictEqual } from "node:util";
|
|
29
|
+
import { TEAM_SEED_MEMBERS } from "../team-profiles.js";
|
|
23
30
|
function describePair(row) {
|
|
24
31
|
const measured = row.status === "unmeasurable" || row.ratio === null
|
|
25
32
|
? "unmeasurable"
|
|
@@ -107,15 +114,44 @@ export function jsonLdDrift(before, after) {
|
|
|
107
114
|
const cmp = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
|
|
108
115
|
return nodes.sort((x, y) => cmp(x.page, y.page) || cmp(x.path, y.path));
|
|
109
116
|
}
|
|
117
|
+
const HOME_PAGE = "content/en/HomePage/home.mdx";
|
|
118
|
+
/**
|
|
119
|
+
* `after` with the standard home put back when the seeded home's JSON-LD is
|
|
120
|
+
* exactly the standard home's node followed by one `Person` per
|
|
121
|
+
* `TEAM_SEED_MEMBERS` member, in order (the people slide's `team` section,
|
|
122
|
+
* #7744 D8). Any other home, or a home the people slide did not change, is
|
|
123
|
+
* returned as it is.
|
|
124
|
+
*/
|
|
125
|
+
export function admitSeededPeopleSlide(before, after) {
|
|
126
|
+
const standard = before[HOME_PAGE];
|
|
127
|
+
const seeded = after[HOME_PAGE];
|
|
128
|
+
if (standard === undefined || seeded === undefined || !seeded.sectionsValid)
|
|
129
|
+
return after;
|
|
130
|
+
const graph = JSON.parse(seeded.jsonld)["@graph"];
|
|
131
|
+
if (!Array.isArray(graph))
|
|
132
|
+
return after;
|
|
133
|
+
const [page, ...people] = graph;
|
|
134
|
+
const expected = TEAM_SEED_MEMBERS.map((m) => ({
|
|
135
|
+
"@context": "https://schema.org",
|
|
136
|
+
"@type": "Person",
|
|
137
|
+
name: m.name,
|
|
138
|
+
jobTitle: m.role,
|
|
139
|
+
}));
|
|
140
|
+
const isPeopleSlide = isDeepStrictEqual(page, JSON.parse(standard.jsonld)) && isDeepStrictEqual(people, expected);
|
|
141
|
+
return isPeopleSlide ? { ...after, [HOME_PAGE]: standard } : after;
|
|
142
|
+
}
|
|
110
143
|
/** How many changed nodes a drift reason lists before it stops. */
|
|
111
144
|
const REASON_NODE_LIMIT = 10;
|
|
112
|
-
/**
|
|
145
|
+
/**
|
|
146
|
+
* Decide whether `apply-design <design>` left every page's JSON-LD unchanged
|
|
147
|
+
* (logo excluded), the seeded people slide's Persons admitted.
|
|
148
|
+
*/
|
|
113
149
|
export function assessJsonLdDrift(design, before, after) {
|
|
114
150
|
const pages = Object.keys(before).length;
|
|
115
151
|
if (pages === 0) {
|
|
116
152
|
return { ok: false, pages, changed: [], reason: `design ${design}: no page was composed` };
|
|
117
153
|
}
|
|
118
|
-
const changed = jsonLdDrift(before, after);
|
|
154
|
+
const changed = jsonLdDrift(before, admitSeededPeopleSlide(before, after));
|
|
119
155
|
if (changed.length === 0)
|
|
120
156
|
return { ok: true, pages };
|
|
121
157
|
const listed = changed
|
|
@@ -139,6 +139,15 @@ export declare function deriveLevel(options: GateOptions): Promise<{
|
|
|
139
139
|
level: Level;
|
|
140
140
|
bundles: Map<ModuleName, Uint8Array>;
|
|
141
141
|
}>;
|
|
142
|
+
/**
|
|
143
|
+
* The level a `--modules`-keep run's chosen non-Free design is gated on
|
|
144
|
+
* (#7010). `resolveModuleSet`'s keep branch composes no level (NFR-2), so
|
|
145
|
+
* `scaffold()` derives one here through `deriveLevel`, as the addition branch
|
|
146
|
+
* does. A key the registry refuses for a Pro module — lapsed, revoked, or one
|
|
147
|
+
* that does not cover Pro — reads as `"free"`, so `assertDesignAllowed`
|
|
148
|
+
* refuses with its own message (fail closed); any other failure propagates.
|
|
149
|
+
*/
|
|
150
|
+
export declare function deriveDesignLevel(options: GateOptions): Promise<Level>;
|
|
142
151
|
/**
|
|
143
152
|
* The one entry point that turns `--modules` + the key into the effective
|
|
144
153
|
* module set and its resolved bundles (D5) — `programmatic.ts`'s `scaffold()`
|
|
@@ -294,6 +294,24 @@ export async function deriveLevel(options) {
|
|
|
294
294
|
throw err;
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* The level a `--modules`-keep run's chosen non-Free design is gated on
|
|
299
|
+
* (#7010). `resolveModuleSet`'s keep branch composes no level (NFR-2), so
|
|
300
|
+
* `scaffold()` derives one here through `deriveLevel`, as the addition branch
|
|
301
|
+
* does. A key the registry refuses for a Pro module — lapsed, revoked, or one
|
|
302
|
+
* that does not cover Pro — reads as `"free"`, so `assertDesignAllowed`
|
|
303
|
+
* refuses with its own message (fail closed); any other failure propagates.
|
|
304
|
+
*/
|
|
305
|
+
export async function deriveDesignLevel(options) {
|
|
306
|
+
try {
|
|
307
|
+
return (await deriveLevel(options)).level;
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
if (err instanceof LicenseRequiredError)
|
|
311
|
+
return "free";
|
|
312
|
+
throw err;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
297
315
|
/**
|
|
298
316
|
* The one entry point that turns `--modules` + the key into the effective
|
|
299
317
|
* module set and its resolved bundles (D5) — `programmatic.ts`'s `scaffold()`
|
|
@@ -806,6 +806,19 @@ export const MODULE_SLICES = {
|
|
|
806
806
|
// `__tests__/tenancy-slice-resolution.test.ts` holds the whole gate set.
|
|
807
807
|
"apps/api/src/routes/workspaces/general.ts",
|
|
808
808
|
"apps/api/src/routes/workspaces/members.ts",
|
|
809
|
+
// #3591 PR-B — the agent seat's routes, a sibling of `members.ts` under
|
|
810
|
+
// the same `workspace || multi-tenant` gate, so co-owned with
|
|
811
|
+
// `workspace` for the #7399 reason above.
|
|
812
|
+
"apps/api/src/routes/workspaces/agents.ts",
|
|
813
|
+
// #3591 PR-E — the signed-in workspace MCP tools that twin the agent
|
|
814
|
+
// context, message and submission routes above. Each registers when
|
|
815
|
+
// its twin route does (`workspace || multi-tenant`), so co-owned with
|
|
816
|
+
// `workspace` for the same reason; the application tools' file is
|
|
817
|
+
// co-owned with `signal-collectors` instead, its routes' gate.
|
|
818
|
+
"apps/mcp/src/tools/agent_context.ts",
|
|
819
|
+
"apps/mcp/src/tools/inbox_messages.ts",
|
|
820
|
+
"apps/mcp/src/tools/inbox_submissions.ts",
|
|
821
|
+
"apps/mcp/src/tools/inbox_applications.ts",
|
|
809
822
|
"apps/api/src/lib/tenant-db.ts",
|
|
810
823
|
"apps/api/src/lib/ws-token.ts",
|
|
811
824
|
// #6157 D16 — the magnet Admin authoring surface, co-owned with
|
|
@@ -910,6 +923,16 @@ export const MODULE_SLICES = {
|
|
|
910
923
|
// register under the workspace||multi-tenant gate.
|
|
911
924
|
"apps/api/src/routes/workspaces/general.ts",
|
|
912
925
|
"apps/api/src/routes/workspaces/members.ts",
|
|
926
|
+
// Co-owned with `multi-tenant` (#3591 PR-B), for the reason the two
|
|
927
|
+
// lines above carry.
|
|
928
|
+
"apps/api/src/routes/workspaces/agents.ts",
|
|
929
|
+
// Co-owned with `multi-tenant` (#3591 PR-E): the signed-in MCP tools
|
|
930
|
+
// that twin the agent context, message and submission routes register
|
|
931
|
+
// under the same `workspace || multi-tenant` disjunction as those
|
|
932
|
+
// routes.
|
|
933
|
+
"apps/mcp/src/tools/agent_context.ts",
|
|
934
|
+
"apps/mcp/src/tools/inbox_messages.ts",
|
|
935
|
+
"apps/mcp/src/tools/inbox_submissions.ts",
|
|
913
936
|
// Co-owned with `multi-tenant` (#6216) — see that slice's entry: the
|
|
914
937
|
// route registers under the SAME workspace||multi-tenant gate as the
|
|
915
938
|
// two lines above, and its service function imports `lib/tenant-db.ts`
|
|
@@ -1370,6 +1393,10 @@ export const MODULE_SLICES = {
|
|
|
1370
1393
|
"apps/api/src/bin/seed-waitlist-experiments.ts",
|
|
1371
1394
|
"apps/mcp/src/tools/get_flow.ts",
|
|
1372
1395
|
"apps/mcp/src/tools/submit_flow_step.ts",
|
|
1396
|
+
// #3591 PR-E — the signed-in MCP tools that twin the tenant-scoped
|
|
1397
|
+
// project-listing routes, CO-OWNED with multi-tenant: they register
|
|
1398
|
+
// under the routes' own `multi-tenant || signal-collectors` gate.
|
|
1399
|
+
"apps/mcp/src/tools/inbox_applications.ts",
|
|
1373
1400
|
// ── The review surface (#5677) — CO-OWNED with multi-tenant ──────────
|
|
1374
1401
|
//
|
|
1375
1402
|
// Every path below is ALSO a multi-tenant `ownedFiles` entry and stays
|