@happyvertical/smrt-core 0.55.10 → 0.55.11
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/AGENTS.md +2 -0
- package/agents/build-knowledge.md +40 -0
- package/agents/cookbook-engine.md +102 -0
- package/agents/cookbook.md +63 -0
- package/dist/consumer-plugin/artifact-publication.js +3 -3
- package/dist/consumer-plugin/artifact-publication.js.map +1 -1
- package/dist/consumer-plugin/hosted-sti-collections.d.ts +10 -0
- package/dist/consumer-plugin/hosted-sti-collections.d.ts.map +1 -0
- package/dist/consumer-plugin/hosted-sti-collections.js +92 -0
- package/dist/consumer-plugin/hosted-sti-collections.js.map +1 -0
- package/dist/consumer-plugin/index.d.ts +1 -1
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +76 -51
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/cookbook/engine/commands.d.ts +37 -0
- package/dist/cookbook/engine/commands.d.ts.map +1 -0
- package/dist/cookbook/engine/commands.js +275 -0
- package/dist/cookbook/engine/commands.js.map +1 -0
- package/dist/cookbook/engine/defaults.d.ts +10 -0
- package/dist/cookbook/engine/defaults.d.ts.map +1 -0
- package/dist/cookbook/engine/defaults.js +49 -0
- package/dist/cookbook/engine/defaults.js.map +1 -0
- package/dist/cookbook/engine/document.d.ts +66 -0
- package/dist/cookbook/engine/document.d.ts.map +1 -0
- package/dist/cookbook/engine/document.js +357 -0
- package/dist/cookbook/engine/document.js.map +1 -0
- package/dist/cookbook/engine/engine.d.ts +49 -0
- package/dist/cookbook/engine/engine.d.ts.map +1 -0
- package/dist/cookbook/engine/engine.js +399 -0
- package/dist/cookbook/engine/engine.js.map +1 -0
- package/dist/cookbook/engine/index.d.ts +14 -0
- package/dist/cookbook/engine/index.d.ts.map +1 -0
- package/dist/cookbook/engine/index.js +8 -0
- package/dist/cookbook/engine/nav.d.ts +45 -0
- package/dist/cookbook/engine/nav.d.ts.map +1 -0
- package/dist/cookbook/engine/nav.js +191 -0
- package/dist/cookbook/engine/nav.js.map +1 -0
- package/dist/cookbook/engine/schemas.d.ts +29 -0
- package/dist/cookbook/engine/schemas.d.ts.map +1 -0
- package/dist/cookbook/engine/schemas.js +363 -0
- package/dist/cookbook/engine/schemas.js.map +1 -0
- package/dist/cookbook/engine/types.d.ts +331 -0
- package/dist/cookbook/engine/types.d.ts.map +1 -0
- package/dist/cookbook/engine/types.js +10 -0
- package/dist/cookbook/engine/types.js.map +1 -0
- package/dist/cookbook/engine.d.ts +2 -0
- package/dist/cookbook/engine.d.ts.map +1 -0
- package/dist/cookbook/engine.js +9 -0
- package/dist/cookbook/index.d.ts +6 -0
- package/dist/cookbook/index.d.ts.map +1 -0
- package/dist/cookbook/v1.schema.json +131 -0
- package/dist/cookbook/validate.d.ts +37 -0
- package/dist/cookbook/validate.d.ts.map +1 -0
- package/dist/cookbook/validate.js +172 -0
- package/dist/cookbook/validate.js.map +1 -0
- package/dist/cookbook.d.ts +7 -0
- package/dist/cookbook.d.ts.map +1 -0
- package/dist/cookbook.js +2 -0
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest.json +1 -1
- package/dist/prebuild/index.js +13 -13
- package/dist/prebuild/index.js.map +1 -1
- package/dist/smrt-knowledge.json +23 -7
- package/dist/vite-plugin/web-collections.d.ts +5 -0
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +1 -1
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/dist/vite-preset/cookbook-exposure.d.ts +11 -0
- package/dist/vite-preset/cookbook-exposure.d.ts.map +1 -0
- package/dist/vite-preset/cookbook-exposure.js +95 -0
- package/dist/vite-preset/cookbook-exposure.js.map +1 -0
- package/dist/vite-preset/index.d.ts +9 -0
- package/dist/vite-preset/index.d.ts.map +1 -1
- package/dist/vite-preset/index.js +17 -7
- package/dist/vite-preset/index.js.map +1 -1
- package/package.json +18 -6
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
//#region src/cookbook/validate.ts
|
|
2
|
+
/** What a v1 document carries in `$schema`. */
|
|
3
|
+
var COOKBOOK_SCHEMA_URL = "https://s-m-r-t.dev/schemas/cookbook/v1.json";
|
|
4
|
+
/** Accepted on read (the document used to be called a blueprint); never written. */
|
|
5
|
+
var COOKBOOK_LEGACY_SCHEMA_URL = "https://s-m-r-t.dev/schemas/blueprint/v1.json";
|
|
6
|
+
var COOKBOOK_VERSION = 1;
|
|
7
|
+
var VISIBILITIES = /* @__PURE__ */ new Set([
|
|
8
|
+
"basic",
|
|
9
|
+
"advanced",
|
|
10
|
+
"hidden"
|
|
11
|
+
]);
|
|
12
|
+
var SURFACES = /* @__PURE__ */ new Set([
|
|
13
|
+
"api",
|
|
14
|
+
"mcp",
|
|
15
|
+
"cli"
|
|
16
|
+
]);
|
|
17
|
+
var SCHEMES = /* @__PURE__ */ new Set([
|
|
18
|
+
"light",
|
|
19
|
+
"dark",
|
|
20
|
+
"system"
|
|
21
|
+
]);
|
|
22
|
+
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
23
|
+
var isStrings = (v) => Array.isArray(v) && v.every((x) => typeof x === "string");
|
|
24
|
+
var isStringRecord = (v, valid) => isObject(v) && !Object.hasOwn(v, "__proto__") && Object.values(v).every(valid);
|
|
25
|
+
function checkPolicy(value, at, errors) {
|
|
26
|
+
if (!isObject(value)) return errors.push(`${at} must be an object`);
|
|
27
|
+
for (const key of ["objectRef", "fieldName"]) if (typeof value[key] !== "string" || !value[key]) errors.push(`${at}.${key} must be a non-empty string`);
|
|
28
|
+
if (value.scopeType !== "app") errors.push(`${at}.scopeType must be "app"`);
|
|
29
|
+
for (const key of [
|
|
30
|
+
"defaultValue",
|
|
31
|
+
"help",
|
|
32
|
+
"label"
|
|
33
|
+
]) {
|
|
34
|
+
const v = value[key];
|
|
35
|
+
if (v !== void 0 && v !== null && typeof v !== "string") errors.push(`${at}.${key} must be a string or null`);
|
|
36
|
+
}
|
|
37
|
+
const vis = value.visibility;
|
|
38
|
+
if (vis != null && !(typeof vis === "string" && VISIBILITIES.has(vis))) errors.push(`${at}.visibility must be basic, advanced or hidden`);
|
|
39
|
+
const order = value.displayOrder;
|
|
40
|
+
if (order != null && !(typeof order === "number" && Number.isFinite(order))) errors.push(`${at}.displayOrder must be a finite number`);
|
|
41
|
+
if (value.locked != null && typeof value.locked !== "boolean") errors.push(`${at}.locked must be a boolean`);
|
|
42
|
+
}
|
|
43
|
+
function checkLayout(value, errors) {
|
|
44
|
+
if (!isObject(value) || value.version !== 1) return errors.push("layout must be an object with version 1");
|
|
45
|
+
for (const key of ["sectionOrder", "hidden"]) if (value[key] !== void 0 && !isStrings(value[key])) errors.push(`layout.${key} must be a list of strings`);
|
|
46
|
+
for (const key of [
|
|
47
|
+
"itemOrder",
|
|
48
|
+
"moved",
|
|
49
|
+
"placements"
|
|
50
|
+
]) {
|
|
51
|
+
const v = value[key];
|
|
52
|
+
if (v === void 0) continue;
|
|
53
|
+
if (!(key === "itemOrder" ? isStringRecord(v, isStrings) : isStringRecord(v, (x) => typeof x === "string"))) errors.push(key === "itemOrder" ? "layout.itemOrder must map ids to lists of strings" : `layout.${key} must map ids to strings`);
|
|
54
|
+
}
|
|
55
|
+
for (const key of [
|
|
56
|
+
"sections",
|
|
57
|
+
"items",
|
|
58
|
+
"panels"
|
|
59
|
+
]) if (value[key] !== void 0 && !isStringRecord(value[key], (x) => isObject(x))) errors.push(`layout.${key} must map ids to objects`);
|
|
60
|
+
const custom = value.customSections;
|
|
61
|
+
if (custom !== void 0 && !(Array.isArray(custom) && custom.every((c) => isObject(c) && typeof c.id === "string" && typeof c.label === "string"))) errors.push("layout.customSections must be a list of { id, label }");
|
|
62
|
+
}
|
|
63
|
+
function checkTheme(value, errors) {
|
|
64
|
+
if (!isObject(value)) return errors.push("theme must be an object");
|
|
65
|
+
if (value.preset !== void 0 && typeof value.preset !== "string") errors.push("theme.preset must be a string");
|
|
66
|
+
if (value.colorScheme !== void 0 && !(typeof value.colorScheme === "string" && SCHEMES.has(value.colorScheme))) errors.push("theme.colorScheme must be light, dark or system");
|
|
67
|
+
const custom = value.custom;
|
|
68
|
+
if (custom !== void 0) {
|
|
69
|
+
if (!isObject(custom) || typeof custom.primary !== "string") errors.push("theme.custom must be an object with a string primary");
|
|
70
|
+
else if (!/^#[0-9a-fA-F]{6}$/.test(custom.primary)) errors.push("theme.custom.primary must be a hex colour like #c2410c");
|
|
71
|
+
else if (custom.fontFamily !== void 0 && typeof custom.fontFamily !== "string") errors.push("theme.custom.fontFamily must be a string");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function checkOverview(value, at, errors) {
|
|
75
|
+
if (!isObject(value) || value.version !== 1) return errors.push(`${at} must be an object with version 1`);
|
|
76
|
+
for (const key of ["order", "removed"]) if (value[key] !== void 0 && !isStrings(value[key])) errors.push(`${at}.${key} must be a list of strings`);
|
|
77
|
+
const added = value.added;
|
|
78
|
+
if (added !== void 0 && !(Array.isArray(added) && added.every((w) => isObject(w)))) errors.push(`${at}.added must be a list of widgets`);
|
|
79
|
+
if (value.changed !== void 0 && !isStringRecord(value.changed, (x) => isObject(x))) errors.push(`${at}.changed must map widget ids to objects`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Structural validation of an unknown value (parsed JSON). Returns the
|
|
83
|
+
* normalised document (legacy `$schema` rewritten, `features` defaulted,
|
|
84
|
+
* recipes sorted and unique) or every problem found, each a readable line
|
|
85
|
+
* naming the path. Unknown top-level keys are kept (additive fields in v1).
|
|
86
|
+
*
|
|
87
|
+
* Deep checks that need other packages (overview widgets against a page
|
|
88
|
+
* definition, nav ids against recipes, theme presets) are the consumer's job;
|
|
89
|
+
* recipe existence runs only when `options.recipes` is given.
|
|
90
|
+
*/
|
|
91
|
+
function validateCookbook(input, options = {}) {
|
|
92
|
+
if (!isObject(input)) return {
|
|
93
|
+
ok: false,
|
|
94
|
+
errors: ["expected a JSON object"]
|
|
95
|
+
};
|
|
96
|
+
const errors = [];
|
|
97
|
+
const warnings = [];
|
|
98
|
+
const { $schema, version } = input;
|
|
99
|
+
if ($schema !== void 0 && $schema !== "https://s-m-r-t.dev/schemas/cookbook/v1.json" && $schema !== "https://s-m-r-t.dev/schemas/blueprint/v1.json") errors.push(`$schema must be ${COOKBOOK_SCHEMA_URL}`);
|
|
100
|
+
if (version === void 0) errors.push("version is required");
|
|
101
|
+
else if (typeof version !== "number" || !Number.isInteger(version)) errors.push("version must be a whole number");
|
|
102
|
+
else if (version > 1) errors.push(`version ${version} is newer than this reader understands (1)`);
|
|
103
|
+
else if (version !== 1) errors.push(`version ${version} is not supported`);
|
|
104
|
+
if ($schema === "https://s-m-r-t.dev/schemas/blueprint/v1.json") warnings.push("$schema uses the legacy blueprint URL; migrated");
|
|
105
|
+
for (const key of ["name", "description"]) if (input[key] !== void 0 && typeof input[key] !== "string") errors.push(`${key} must be a string`);
|
|
106
|
+
let recipes = [];
|
|
107
|
+
if (!isStrings(input.recipes)) errors.push("recipes must be a list of recipe ids");
|
|
108
|
+
else {
|
|
109
|
+
recipes = [...new Set(input.recipes)].sort();
|
|
110
|
+
if (options.recipes) {
|
|
111
|
+
const known = new Set(options.recipes);
|
|
112
|
+
for (const id of recipes) if (!known.has(id)) errors.push(`recipes: unknown recipe "${id}"`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let features = [];
|
|
116
|
+
if (input.features !== void 0) if (!isStrings(input.features)) errors.push("features must be a list of model names");
|
|
117
|
+
else {
|
|
118
|
+
features = [...new Set(input.features)].sort();
|
|
119
|
+
if (features.length !== input.features.length) warnings.push("features listed a model more than once; deduplicated");
|
|
120
|
+
}
|
|
121
|
+
const policies = [];
|
|
122
|
+
if (!Array.isArray(input.policies)) errors.push("policies must be a list");
|
|
123
|
+
else for (const [i, row] of input.policies.entries()) {
|
|
124
|
+
checkPolicy(row, `policies[${i}]`, errors);
|
|
125
|
+
policies.push(row);
|
|
126
|
+
}
|
|
127
|
+
if (input.exposure !== void 0) {
|
|
128
|
+
if (!isStringRecord(input.exposure, (s) => isStrings(s) && s.every((x) => SURFACES.has(x)))) errors.push("exposure must map model names to lists of api, mcp or cli");
|
|
129
|
+
}
|
|
130
|
+
if (input.layout !== void 0) checkLayout(input.layout, errors);
|
|
131
|
+
if (input.theme !== void 0) checkTheme(input.theme, errors);
|
|
132
|
+
if (input.overviews !== void 0) if (!isObject(input.overviews) || Object.hasOwn(input.overviews, "__proto__")) errors.push("overviews must map overview ids to overrides");
|
|
133
|
+
else for (const [id, value] of Object.entries(input.overviews)) checkOverview(value, `overviews.${id}`, errors);
|
|
134
|
+
if (errors.length) return {
|
|
135
|
+
ok: false,
|
|
136
|
+
errors
|
|
137
|
+
};
|
|
138
|
+
const cookbook = {
|
|
139
|
+
...input,
|
|
140
|
+
$schema: COOKBOOK_SCHEMA_URL,
|
|
141
|
+
version: 1,
|
|
142
|
+
recipes,
|
|
143
|
+
features,
|
|
144
|
+
policies
|
|
145
|
+
};
|
|
146
|
+
if (input.exposure !== void 0) cookbook.exposure = input.exposure;
|
|
147
|
+
if (input.layout !== void 0) cookbook.layout = input.layout;
|
|
148
|
+
if (input.theme !== void 0) cookbook.theme = input.theme;
|
|
149
|
+
if (input.overviews !== void 0) cookbook.overviews = input.overviews;
|
|
150
|
+
return {
|
|
151
|
+
ok: true,
|
|
152
|
+
cookbook,
|
|
153
|
+
warnings
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/** Parse JSON text, then {@link validateCookbook} it. */
|
|
157
|
+
function parseCookbookText(text, options = {}) {
|
|
158
|
+
let value;
|
|
159
|
+
try {
|
|
160
|
+
value = JSON.parse(text);
|
|
161
|
+
} catch {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
errors: ["not valid JSON"]
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return validateCookbook(value, options);
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
export { COOKBOOK_LEGACY_SCHEMA_URL, COOKBOOK_SCHEMA_URL, COOKBOOK_VERSION, parseCookbookText, validateCookbook };
|
|
171
|
+
|
|
172
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","names":[],"sources":["../../src/cookbook/validate.ts"],"sourcesContent":["import type {\n Cookbook,\n CookbookExposureSurface,\n CookbookLayout,\n CookbookOverviewOverride,\n CookbookPolicyRow,\n CookbookTheme,\n} from '@happyvertical/smrt-types';\n\nexport type { Cookbook } from '@happyvertical/smrt-types';\n\n/** What a v1 document carries in `$schema`. */\nexport const COOKBOOK_SCHEMA_URL =\n 'https://s-m-r-t.dev/schemas/cookbook/v1.json';\n/** Accepted on read (the document used to be called a blueprint); never written. */\nexport const COOKBOOK_LEGACY_SCHEMA_URL =\n 'https://s-m-r-t.dev/schemas/blueprint/v1.json';\nexport const COOKBOOK_VERSION = 1;\n\nexport interface CookbookValidateOptions {\n /**\n * The recipe ids that exist (from published manifests). When given, an id\n * outside it is an error. When absent no existence check runs: that is the\n * seam `smrt cookbook validate` fills.\n */\n recipes?: Iterable<string>;\n}\n\nexport type CookbookValidation =\n | { ok: true; cookbook: Cookbook; warnings: string[] }\n | { ok: false; errors: string[] };\n\nconst VISIBILITIES = new Set(['basic', 'advanced', 'hidden']);\nconst SURFACES = new Set(['api', 'mcp', 'cli']);\nconst SCHEMES = new Set(['light', 'dark', 'system']);\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v);\nconst isStrings = (v: unknown): v is string[] =>\n Array.isArray(v) && v.every((x) => typeof x === 'string');\nconst isStringRecord = (v: unknown, valid: (x: unknown) => boolean) =>\n isObject(v) &&\n !Object.hasOwn(v, '__proto__') &&\n Object.values(v).every(valid);\n\nfunction checkPolicy(value: unknown, at: string, errors: string[]) {\n if (!isObject(value)) return errors.push(`${at} must be an object`);\n for (const key of ['objectRef', 'fieldName'] as const) {\n if (typeof value[key] !== 'string' || !value[key]) {\n errors.push(`${at}.${key} must be a non-empty string`);\n }\n }\n if (value.scopeType !== 'app') errors.push(`${at}.scopeType must be \"app\"`);\n for (const key of ['defaultValue', 'help', 'label'] as const) {\n const v = value[key];\n if (v !== undefined && v !== null && typeof v !== 'string') {\n errors.push(`${at}.${key} must be a string or null`);\n }\n }\n const vis = value.visibility;\n if (vis != null && !(typeof vis === 'string' && VISIBILITIES.has(vis))) {\n errors.push(`${at}.visibility must be basic, advanced or hidden`);\n }\n const order = value.displayOrder;\n if (order != null && !(typeof order === 'number' && Number.isFinite(order))) {\n errors.push(`${at}.displayOrder must be a finite number`);\n }\n if (value.locked != null && typeof value.locked !== 'boolean') {\n errors.push(`${at}.locked must be a boolean`);\n }\n}\n\nfunction checkLayout(value: unknown, errors: string[]) {\n if (!isObject(value) || value.version !== 1) {\n return errors.push('layout must be an object with version 1');\n }\n for (const key of ['sectionOrder', 'hidden'] as const) {\n if (value[key] !== undefined && !isStrings(value[key])) {\n errors.push(`layout.${key} must be a list of strings`);\n }\n }\n for (const key of ['itemOrder', 'moved', 'placements'] as const) {\n const v = value[key];\n if (v === undefined) continue;\n const ok =\n key === 'itemOrder'\n ? isStringRecord(v, isStrings)\n : isStringRecord(v, (x) => typeof x === 'string');\n if (!ok) {\n errors.push(\n key === 'itemOrder'\n ? 'layout.itemOrder must map ids to lists of strings'\n : `layout.${key} must map ids to strings`,\n );\n }\n }\n for (const key of ['sections', 'items', 'panels'] as const) {\n if (\n value[key] !== undefined &&\n !isStringRecord(value[key], (x) => isObject(x))\n ) {\n errors.push(`layout.${key} must map ids to objects`);\n }\n }\n const custom = value.customSections;\n if (\n custom !== undefined &&\n !(\n Array.isArray(custom) &&\n custom.every(\n (c) =>\n isObject(c) &&\n typeof c.id === 'string' &&\n typeof c.label === 'string',\n )\n )\n ) {\n errors.push('layout.customSections must be a list of { id, label }');\n }\n}\n\nfunction checkTheme(value: unknown, errors: string[]) {\n if (!isObject(value)) return errors.push('theme must be an object');\n if (value.preset !== undefined && typeof value.preset !== 'string') {\n errors.push('theme.preset must be a string');\n }\n if (\n value.colorScheme !== undefined &&\n !(typeof value.colorScheme === 'string' && SCHEMES.has(value.colorScheme))\n ) {\n errors.push('theme.colorScheme must be light, dark or system');\n }\n const custom = value.custom;\n if (custom !== undefined) {\n if (!isObject(custom) || typeof custom.primary !== 'string') {\n errors.push('theme.custom must be an object with a string primary');\n } else if (!/^#[0-9a-fA-F]{6}$/.test(custom.primary)) {\n errors.push('theme.custom.primary must be a hex colour like #c2410c');\n } else if (\n custom.fontFamily !== undefined &&\n typeof custom.fontFamily !== 'string'\n ) {\n errors.push('theme.custom.fontFamily must be a string');\n }\n }\n}\n\nfunction checkOverview(value: unknown, at: string, errors: string[]) {\n if (!isObject(value) || value.version !== 1) {\n return errors.push(`${at} must be an object with version 1`);\n }\n for (const key of ['order', 'removed'] as const) {\n if (value[key] !== undefined && !isStrings(value[key])) {\n errors.push(`${at}.${key} must be a list of strings`);\n }\n }\n const added = value.added;\n if (\n added !== undefined &&\n !(Array.isArray(added) && added.every((w) => isObject(w)))\n ) {\n errors.push(`${at}.added must be a list of widgets`);\n }\n if (\n value.changed !== undefined &&\n !isStringRecord(value.changed, (x) => isObject(x))\n ) {\n errors.push(`${at}.changed must map widget ids to objects`);\n }\n}\n\n/**\n * Structural validation of an unknown value (parsed JSON). Returns the\n * normalised document (legacy `$schema` rewritten, `features` defaulted,\n * recipes sorted and unique) or every problem found, each a readable line\n * naming the path. Unknown top-level keys are kept (additive fields in v1).\n *\n * Deep checks that need other packages (overview widgets against a page\n * definition, nav ids against recipes, theme presets) are the consumer's job;\n * recipe existence runs only when `options.recipes` is given.\n */\nexport function validateCookbook(\n input: unknown,\n options: CookbookValidateOptions = {},\n): CookbookValidation {\n if (!isObject(input)) {\n return { ok: false, errors: ['expected a JSON object'] };\n }\n const errors: string[] = [];\n const warnings: string[] = [];\n\n const { $schema, version } = input;\n if (\n $schema !== undefined &&\n $schema !== COOKBOOK_SCHEMA_URL &&\n $schema !== COOKBOOK_LEGACY_SCHEMA_URL\n ) {\n errors.push(`$schema must be ${COOKBOOK_SCHEMA_URL}`);\n }\n if (version === undefined) {\n errors.push('version is required');\n } else if (typeof version !== 'number' || !Number.isInteger(version)) {\n errors.push('version must be a whole number');\n } else if (version > COOKBOOK_VERSION) {\n errors.push(\n `version ${version} is newer than this reader understands (${COOKBOOK_VERSION})`,\n );\n } else if (version !== COOKBOOK_VERSION) {\n errors.push(`version ${version} is not supported`);\n }\n if ($schema === COOKBOOK_LEGACY_SCHEMA_URL) {\n warnings.push('$schema uses the legacy blueprint URL; migrated');\n }\n\n for (const key of ['name', 'description'] as const) {\n if (input[key] !== undefined && typeof input[key] !== 'string') {\n errors.push(`${key} must be a string`);\n }\n }\n\n let recipes: string[] = [];\n if (!isStrings(input.recipes)) {\n errors.push('recipes must be a list of recipe ids');\n } else {\n recipes = [...new Set(input.recipes)].sort();\n if (options.recipes) {\n const known = new Set(options.recipes);\n for (const id of recipes) {\n if (!known.has(id)) errors.push(`recipes: unknown recipe \"${id}\"`);\n }\n }\n }\n\n let features: string[] = [];\n if (input.features !== undefined) {\n if (!isStrings(input.features)) {\n errors.push('features must be a list of model names');\n } else {\n features = [...new Set(input.features)].sort();\n if (features.length !== input.features.length) {\n warnings.push('features listed a model more than once; deduplicated');\n }\n }\n }\n\n const policies: CookbookPolicyRow[] = [];\n if (!Array.isArray(input.policies)) {\n errors.push('policies must be a list');\n } else {\n for (const [i, row] of input.policies.entries()) {\n checkPolicy(row, `policies[${i}]`, errors);\n policies.push(row as CookbookPolicyRow);\n }\n }\n\n if (input.exposure !== undefined) {\n const ok = isStringRecord(\n input.exposure,\n (s) => isStrings(s) && s.every((x) => SURFACES.has(x)),\n );\n if (!ok)\n errors.push('exposure must map model names to lists of api, mcp or cli');\n }\n if (input.layout !== undefined) checkLayout(input.layout, errors);\n if (input.theme !== undefined) checkTheme(input.theme, errors);\n if (input.overviews !== undefined) {\n if (\n !isObject(input.overviews) ||\n Object.hasOwn(input.overviews, '__proto__')\n ) {\n errors.push('overviews must map overview ids to overrides');\n } else {\n for (const [id, value] of Object.entries(input.overviews)) {\n checkOverview(value, `overviews.${id}`, errors);\n }\n }\n }\n\n if (errors.length) return { ok: false, errors };\n\n const cookbook = {\n ...input,\n $schema: COOKBOOK_SCHEMA_URL,\n version: COOKBOOK_VERSION,\n recipes,\n features,\n policies,\n } as unknown as Cookbook;\n if (input.exposure !== undefined) {\n cookbook.exposure = input.exposure as Record<\n string,\n CookbookExposureSurface[]\n >;\n }\n if (input.layout !== undefined)\n cookbook.layout = input.layout as CookbookLayout;\n if (input.theme !== undefined) cookbook.theme = input.theme as CookbookTheme;\n if (input.overviews !== undefined) {\n cookbook.overviews = input.overviews as Record<\n string,\n CookbookOverviewOverride\n >;\n }\n return { ok: true, cookbook, warnings };\n}\n\n/** Parse JSON text, then {@link validateCookbook} it. */\nexport function parseCookbookText(\n text: string,\n options: CookbookValidateOptions = {},\n): CookbookValidation {\n let value: unknown;\n try {\n value = JSON.parse(text);\n } catch {\n return { ok: false, errors: ['not valid JSON'] };\n }\n return validateCookbook(value, options);\n}\n"],"mappings":";;AAYA,IAAa,sBACX;;AAEF,IAAa,6BACX;AACF,IAAa,mBAAmB;AAehC,IAAM,+BAAe,IAAI,IAAI;CAAC;CAAS;CAAY;AAAQ,CAAC;AAC5D,IAAM,2BAAW,IAAI,IAAI;CAAC;CAAO;CAAO;AAAK,CAAC;AAC9C,IAAM,0BAAU,IAAI,IAAI;CAAC;CAAS;CAAQ;AAAQ,CAAC;AAEnD,IAAM,YAAY,MAChB,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,MAAM,QAAQ,CAAC;AACzD,IAAM,aAAa,MACjB,MAAM,QAAQ,CAAC,KAAK,EAAE,OAAO,MAAM,OAAO,MAAM,QAAQ;AAC1D,IAAM,kBAAkB,GAAY,UAClC,SAAS,CAAC,KACV,CAAC,OAAO,OAAO,GAAG,WAAW,KAC7B,OAAO,OAAO,CAAC,CAAC,CAAC,MAAM,KAAK;AAE9B,SAAS,YAAY,OAAgB,IAAY,QAAkB;CACjE,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,OAAO,KAAK,GAAG,GAAG,mBAAmB;CAClE,KAAK,MAAM,OAAO,CAAC,aAAa,WAAW,GACzC,IAAI,OAAO,MAAM,SAAS,YAAY,CAAC,MAAM,MAC3C,OAAO,KAAK,GAAG,GAAG,GAAG,IAAI,4BAA4B;CAGzD,IAAI,MAAM,cAAc,OAAO,OAAO,KAAK,GAAG,GAAG,yBAAyB;CAC1E,KAAK,MAAM,OAAO;EAAC;EAAgB;EAAQ;CAAO,GAAY;EAC5D,MAAM,IAAI,MAAM;EAChB,IAAI,MAAM,KAAA,KAAa,MAAM,QAAQ,OAAO,MAAM,UAChD,OAAO,KAAK,GAAG,GAAG,GAAG,IAAI,0BAA0B;CAEvD;CACA,MAAM,MAAM,MAAM;CAClB,IAAI,OAAO,QAAQ,EAAE,OAAO,QAAQ,YAAY,aAAa,IAAI,GAAG,IAClE,OAAO,KAAK,GAAG,GAAG,8CAA8C;CAElE,MAAM,QAAQ,MAAM;CACpB,IAAI,SAAS,QAAQ,EAAE,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IACvE,OAAO,KAAK,GAAG,GAAG,sCAAsC;CAE1D,IAAI,MAAM,UAAU,QAAQ,OAAO,MAAM,WAAW,WAClD,OAAO,KAAK,GAAG,GAAG,0BAA0B;AAEhD;AAEA,SAAS,YAAY,OAAgB,QAAkB;CACrD,IAAI,CAAC,SAAS,KAAK,KAAK,MAAM,YAAY,GACxC,OAAO,OAAO,KAAK,yCAAyC;CAE9D,KAAK,MAAM,OAAO,CAAC,gBAAgB,QAAQ,GACzC,IAAI,MAAM,SAAS,KAAA,KAAa,CAAC,UAAU,MAAM,IAAI,GACnD,OAAO,KAAK,UAAU,IAAI,2BAA2B;CAGzD,KAAK,MAAM,OAAO;EAAC;EAAa;EAAS;CAAY,GAAY;EAC/D,MAAM,IAAI,MAAM;EAChB,IAAI,MAAM,KAAA,GAAW;EAKrB,IAAI,EAHF,QAAQ,cACJ,eAAe,GAAG,SAAS,IAC3B,eAAe,IAAI,MAAM,OAAO,MAAM,QAAQ,IAElD,OAAO,KACL,QAAQ,cACJ,sDACA,UAAU,IAAI,yBACpB;CAEJ;CACA,KAAK,MAAM,OAAO;EAAC;EAAY;EAAS;CAAQ,GAC9C,IACE,MAAM,SAAS,KAAA,KACf,CAAC,eAAe,MAAM,OAAO,MAAM,SAAS,CAAC,CAAC,GAE9C,OAAO,KAAK,UAAU,IAAI,yBAAyB;CAGvD,MAAM,SAAS,MAAM;CACrB,IACE,WAAW,KAAA,KACX,EACE,MAAM,QAAQ,MAAM,KACpB,OAAO,OACJ,MACC,SAAS,CAAC,KACV,OAAO,EAAE,OAAO,YAChB,OAAO,EAAE,UAAU,QACvB,IAGF,OAAO,KAAK,uDAAuD;AAEvE;AAEA,SAAS,WAAW,OAAgB,QAAkB;CACpD,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,OAAO,KAAK,yBAAyB;CAClE,IAAI,MAAM,WAAW,KAAA,KAAa,OAAO,MAAM,WAAW,UACxD,OAAO,KAAK,+BAA+B;CAE7C,IACE,MAAM,gBAAgB,KAAA,KACtB,EAAE,OAAO,MAAM,gBAAgB,YAAY,QAAQ,IAAI,MAAM,WAAW,IAExE,OAAO,KAAK,iDAAiD;CAE/D,MAAM,SAAS,MAAM;CACrB,IAAI,WAAW,KAAA;MACT,CAAC,SAAS,MAAM,KAAK,OAAO,OAAO,YAAY,UACjD,OAAO,KAAK,sDAAsD;OAC7D,IAAI,CAAC,oBAAoB,KAAK,OAAO,OAAO,GACjD,OAAO,KAAK,wDAAwD;OAC/D,IACL,OAAO,eAAe,KAAA,KACtB,OAAO,OAAO,eAAe,UAE7B,OAAO,KAAK,0CAA0C;CAAA;AAG5D;AAEA,SAAS,cAAc,OAAgB,IAAY,QAAkB;CACnE,IAAI,CAAC,SAAS,KAAK,KAAK,MAAM,YAAY,GACxC,OAAO,OAAO,KAAK,GAAG,GAAG,kCAAkC;CAE7D,KAAK,MAAM,OAAO,CAAC,SAAS,SAAS,GACnC,IAAI,MAAM,SAAS,KAAA,KAAa,CAAC,UAAU,MAAM,IAAI,GACnD,OAAO,KAAK,GAAG,GAAG,GAAG,IAAI,2BAA2B;CAGxD,MAAM,QAAQ,MAAM;CACpB,IACE,UAAU,KAAA,KACV,EAAE,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,MAAM,SAAS,CAAC,CAAC,IAExD,OAAO,KAAK,GAAG,GAAG,iCAAiC;CAErD,IACE,MAAM,YAAY,KAAA,KAClB,CAAC,eAAe,MAAM,UAAU,MAAM,SAAS,CAAC,CAAC,GAEjD,OAAO,KAAK,GAAG,GAAG,wCAAwC;AAE9D;;;;;;;;;;;AAYA,SAAgB,iBACd,OACA,UAAmC,CAAC,GAChB;CACpB,IAAI,CAAC,SAAS,KAAK,GACjB,OAAO;EAAE,IAAI;EAAO,QAAQ,CAAC,wBAAwB;CAAE;CAEzD,MAAM,SAAmB,CAAC;CAC1B,MAAM,WAAqB,CAAC;CAE5B,MAAM,EAAE,SAAS,YAAY;CAC7B,IACE,YAAY,KAAA,KACZ,YAAA,kDACA,YAAA,iDAEA,OAAO,KAAK,mBAAmB,qBAAqB;CAEtD,IAAI,YAAY,KAAA,GACd,OAAO,KAAK,qBAAqB;MAC5B,IAAI,OAAO,YAAY,YAAY,CAAC,OAAO,UAAU,OAAO,GACjE,OAAO,KAAK,gCAAgC;MACvC,IAAI,UAAA,GACT,OAAO,KACL,WAAW,QAAQ,2CACrB;MACK,IAAI,YAAA,GACT,OAAO,KAAK,WAAW,QAAQ,kBAAkB;CAEnD,IAAI,YAAA,iDACF,SAAS,KAAK,iDAAiD;CAGjE,KAAK,MAAM,OAAO,CAAC,QAAQ,aAAa,GACtC,IAAI,MAAM,SAAS,KAAA,KAAa,OAAO,MAAM,SAAS,UACpD,OAAO,KAAK,GAAG,IAAI,kBAAkB;CAIzC,IAAI,UAAoB,CAAC;CACzB,IAAI,CAAC,UAAU,MAAM,OAAO,GAC1B,OAAO,KAAK,sCAAsC;MAC7C;EACL,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK;EAC3C,IAAI,QAAQ,SAAS;GACnB,MAAM,QAAQ,IAAI,IAAI,QAAQ,OAAO;GACrC,KAAK,MAAM,MAAM,SACf,IAAI,CAAC,MAAM,IAAI,EAAE,GAAG,OAAO,KAAK,4BAA4B,GAAG,EAAE;EAErE;CACF;CAEA,IAAI,WAAqB,CAAC;CAC1B,IAAI,MAAM,aAAa,KAAA,GACrB,IAAI,CAAC,UAAU,MAAM,QAAQ,GAC3B,OAAO,KAAK,wCAAwC;MAC/C;EACL,WAAW,CAAC,GAAG,IAAI,IAAI,MAAM,QAAQ,CAAC,CAAC,CAAC,KAAK;EAC7C,IAAI,SAAS,WAAW,MAAM,SAAS,QACrC,SAAS,KAAK,sDAAsD;CAExE;CAGF,MAAM,WAAgC,CAAC;CACvC,IAAI,CAAC,MAAM,QAAQ,MAAM,QAAQ,GAC/B,OAAO,KAAK,yBAAyB;MAErC,KAAK,MAAM,CAAC,GAAG,QAAQ,MAAM,SAAS,QAAQ,GAAG;EAC/C,YAAY,KAAK,YAAY,EAAE,IAAI,MAAM;EACzC,SAAS,KAAK,GAAwB;CACxC;CAGF,IAAI,MAAM,aAAa,KAAA;MAKjB,CAJO,eACT,MAAM,WACL,MAAM,UAAU,CAAC,KAAK,EAAE,OAAO,MAAM,SAAS,IAAI,CAAC,CAAC,CAElD,GACH,OAAO,KAAK,2DAA2D;CAAA;CAE3E,IAAI,MAAM,WAAW,KAAA,GAAW,YAAY,MAAM,QAAQ,MAAM;CAChE,IAAI,MAAM,UAAU,KAAA,GAAW,WAAW,MAAM,OAAO,MAAM;CAC7D,IAAI,MAAM,cAAc,KAAA,GACtB,IACE,CAAC,SAAS,MAAM,SAAS,KACzB,OAAO,OAAO,MAAM,WAAW,WAAW,GAE1C,OAAO,KAAK,8CAA8C;MAE1D,KAAK,MAAM,CAAC,IAAI,UAAU,OAAO,QAAQ,MAAM,SAAS,GACtD,cAAc,OAAO,aAAa,MAAM,MAAM;CAKpD,IAAI,OAAO,QAAQ,OAAO;EAAE,IAAI;EAAO;CAAO;CAE9C,MAAM,WAAW;EACf,GAAG;EACH,SAAS;EACT,SAAA;EACA;EACA;EACA;CACF;CACA,IAAI,MAAM,aAAa,KAAA,GACrB,SAAS,WAAW,MAAM;CAK5B,IAAI,MAAM,WAAW,KAAA,GACnB,SAAS,SAAS,MAAM;CAC1B,IAAI,MAAM,UAAU,KAAA,GAAW,SAAS,QAAQ,MAAM;CACtD,IAAI,MAAM,cAAc,KAAA,GACtB,SAAS,YAAY,MAAM;CAK7B,OAAO;EAAE,IAAI;EAAM;EAAU;CAAS;AACxC;;AAGA,SAAgB,kBACd,MACA,UAAmC,CAAC,GAChB;CACpB,IAAI;CACJ,IAAI;EACF,QAAQ,KAAK,MAAM,IAAI;CACzB,QAAQ;EACN,OAAO;GAAE,IAAI;GAAO,QAAQ,CAAC,gBAAgB;EAAE;CACjD;CACA,OAAO,iBAAiB,OAAO,OAAO;AACxC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@happyvertical/smrt-core/cookbook` entry. The build emits one file per
|
|
3
|
+
* export key (`dist/cookbook.js`), so the subpath needs a module at this name;
|
|
4
|
+
* the implementation lives in `./cookbook/`.
|
|
5
|
+
*/
|
|
6
|
+
export * from './cookbook/index.js';
|
|
7
|
+
//# sourceMappingURL=cookbook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cookbook.d.ts","sourceRoot":"","sources":["../src/cookbook.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,cAAc,qBAAqB,CAAC"}
|
package/dist/cookbook.js
ADDED