@ambitresearch/genie 1.3.1
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 +15 -0
- package/dist/auth/bearer.d.ts +49 -0
- package/dist/auth/bearer.d.ts.map +1 -0
- package/dist/auth/bearer.js +159 -0
- package/dist/auth/bearer.js.map +1 -0
- package/dist/auth/oauth/authorize.d.ts +19 -0
- package/dist/auth/oauth/authorize.d.ts.map +1 -0
- package/dist/auth/oauth/authorize.js +111 -0
- package/dist/auth/oauth/authorize.js.map +1 -0
- package/dist/auth/oauth/config.d.ts +10 -0
- package/dist/auth/oauth/config.d.ts.map +1 -0
- package/dist/auth/oauth/config.js +16 -0
- package/dist/auth/oauth/config.js.map +1 -0
- package/dist/auth/oauth/dcr.d.ts +15 -0
- package/dist/auth/oauth/dcr.d.ts.map +1 -0
- package/dist/auth/oauth/dcr.js +38 -0
- package/dist/auth/oauth/dcr.js.map +1 -0
- package/dist/auth/oauth/jwt.d.ts +13 -0
- package/dist/auth/oauth/jwt.d.ts.map +1 -0
- package/dist/auth/oauth/jwt.js +46 -0
- package/dist/auth/oauth/jwt.js.map +1 -0
- package/dist/auth/oauth/metadata.d.ts +9 -0
- package/dist/auth/oauth/metadata.d.ts.map +1 -0
- package/dist/auth/oauth/metadata.js +19 -0
- package/dist/auth/oauth/metadata.js.map +1 -0
- package/dist/auth/oauth/router.d.ts +32 -0
- package/dist/auth/oauth/router.d.ts.map +1 -0
- package/dist/auth/oauth/router.js +159 -0
- package/dist/auth/oauth/router.js.map +1 -0
- package/dist/auth/oauth/store.d.ts +55 -0
- package/dist/auth/oauth/store.d.ts.map +1 -0
- package/dist/auth/oauth/store.js +63 -0
- package/dist/auth/oauth/store.js.map +1 -0
- package/dist/auth/oauth/token.d.ts +21 -0
- package/dist/auth/oauth/token.d.ts.map +1 -0
- package/dist/auth/oauth/token.js +77 -0
- package/dist/auth/oauth/token.js.map +1 -0
- package/dist/auth/oidc/group-policy.d.ts +43 -0
- package/dist/auth/oidc/group-policy.d.ts.map +1 -0
- package/dist/auth/oidc/group-policy.js +54 -0
- package/dist/auth/oidc/group-policy.js.map +1 -0
- package/dist/auth/oidc/verifier.d.ts +42 -0
- package/dist/auth/oidc/verifier.d.ts.map +1 -0
- package/dist/auth/oidc/verifier.js +145 -0
- package/dist/auth/oidc/verifier.js.map +1 -0
- package/dist/auth/token-cli.d.ts +9 -0
- package/dist/auth/token-cli.d.ts.map +1 -0
- package/dist/auth/token-cli.js +98 -0
- package/dist/auth/token-cli.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +149 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/redact.d.ts +20 -0
- package/dist/config/redact.d.ts.map +1 -0
- package/dist/config/redact.js +77 -0
- package/dist/config/redact.js.map +1 -0
- package/dist/config/secrets.d.ts +100 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +130 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/framework/html.d.ts +103 -0
- package/dist/framework/html.d.ts.map +1 -0
- package/dist/framework/html.js +271 -0
- package/dist/framework/html.js.map +1 -0
- package/dist/framework/interface.d.ts +130 -0
- package/dist/framework/interface.d.ts.map +1 -0
- package/dist/framework/interface.js +112 -0
- package/dist/framework/interface.js.map +1 -0
- package/dist/framework/react.d.ts +133 -0
- package/dist/framework/react.d.ts.map +1 -0
- package/dist/framework/react.js +243 -0
- package/dist/framework/react.js.map +1 -0
- package/dist/framework/vue.d.ts +99 -0
- package/dist/framework/vue.d.ts.map +1 -0
- package/dist/framework/vue.js +375 -0
- package/dist/framework/vue.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/client.d.ts +81 -0
- package/dist/llm/client.d.ts.map +1 -0
- package/dist/llm/client.js +141 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/component-response.d.ts +111 -0
- package/dist/llm/component-response.d.ts.map +1 -0
- package/dist/llm/component-response.js +167 -0
- package/dist/llm/component-response.js.map +1 -0
- package/dist/llm/generated-files.d.ts +10 -0
- package/dist/llm/generated-files.d.ts.map +1 -0
- package/dist/llm/generated-files.js +26 -0
- package/dist/llm/generated-files.js.map +1 -0
- package/dist/llm/prompts/CHANGELOG.md +70 -0
- package/dist/llm/prompts/generate-component.system.md +99 -0
- package/dist/llm/prompts/refine-component.system.md +112 -0
- package/dist/llm/prompts.d.ts +66 -0
- package/dist/llm/prompts.d.ts.map +1 -0
- package/dist/llm/prompts.js +78 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/retry.d.ts +183 -0
- package/dist/llm/retry.d.ts.map +1 -0
- package/dist/llm/retry.js +475 -0
- package/dist/llm/retry.js.map +1 -0
- package/dist/llm/schema.d.ts +129 -0
- package/dist/llm/schema.d.ts.map +1 -0
- package/dist/llm/schema.js +147 -0
- package/dist/llm/schema.js.map +1 -0
- package/dist/llm/validate.d.ts +107 -0
- package/dist/llm/validate.d.ts.map +1 -0
- package/dist/llm/validate.js +174 -0
- package/dist/llm/validate.js.map +1 -0
- package/dist/manifest/compiler.d.ts +112 -0
- package/dist/manifest/compiler.d.ts.map +1 -0
- package/dist/manifest/compiler.js +306 -0
- package/dist/manifest/compiler.js.map +1 -0
- package/dist/manifest/ensure.d.ts +46 -0
- package/dist/manifest/ensure.d.ts.map +1 -0
- package/dist/manifest/ensure.js +49 -0
- package/dist/manifest/ensure.js.map +1 -0
- package/dist/manifest/index.d.ts +13 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +13 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/middleware/plan-guard.d.ts +167 -0
- package/dist/middleware/plan-guard.d.ts.map +1 -0
- package/dist/middleware/plan-guard.js +190 -0
- package/dist/middleware/plan-guard.js.map +1 -0
- package/dist/plans/index.d.ts +71 -0
- package/dist/plans/index.d.ts.map +1 -0
- package/dist/plans/index.js +252 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/schemas/component.schema.json +114 -0
- package/dist/server.d.ts +96 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +258 -0
- package/dist/server.js.map +1 -0
- package/dist/store/empty-manifest.d.ts +58 -0
- package/dist/store/empty-manifest.d.ts.map +1 -0
- package/dist/store/empty-manifest.js +59 -0
- package/dist/store/empty-manifest.js.map +1 -0
- package/dist/store/git-host.d.ts +120 -0
- package/dist/store/git-host.d.ts.map +1 -0
- package/dist/store/git-host.js +796 -0
- package/dist/store/git-host.js.map +1 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/interface.d.ts +322 -0
- package/dist/store/interface.d.ts.map +1 -0
- package/dist/store/interface.js +100 -0
- package/dist/store/interface.js.map +1 -0
- package/dist/store/kit-files.d.ts +88 -0
- package/dist/store/kit-files.d.ts.map +1 -0
- package/dist/store/kit-files.js +199 -0
- package/dist/store/kit-files.js.map +1 -0
- package/dist/store/local.d.ts +70 -0
- package/dist/store/local.d.ts.map +1 -0
- package/dist/store/local.js +651 -0
- package/dist/store/local.js.map +1 -0
- package/dist/store/manifest.d.ts +44 -0
- package/dist/store/manifest.d.ts.map +1 -0
- package/dist/store/manifest.js +116 -0
- package/dist/store/manifest.js.map +1 -0
- package/dist/store/viewer-assets.d.ts +20 -0
- package/dist/store/viewer-assets.d.ts.map +1 -0
- package/dist/store/viewer-assets.js +118 -0
- package/dist/store/viewer-assets.js.map +1 -0
- package/dist/sync/anchor.d.ts +125 -0
- package/dist/sync/anchor.d.ts.map +1 -0
- package/dist/sync/anchor.js +210 -0
- package/dist/sync/anchor.js.map +1 -0
- package/dist/sync/index.d.ts +15 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +15 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/orchestrator.d.ts +160 -0
- package/dist/sync/orchestrator.d.ts.map +1 -0
- package/dist/sync/orchestrator.js +288 -0
- package/dist/sync/orchestrator.js.map +1 -0
- package/dist/tools/bind_kit.d.ts +29 -0
- package/dist/tools/bind_kit.d.ts.map +1 -0
- package/dist/tools/bind_kit.js +70 -0
- package/dist/tools/bind_kit.js.map +1 -0
- package/dist/tools/conjure.d.ts +141 -0
- package/dist/tools/conjure.d.ts.map +1 -0
- package/dist/tools/conjure.js +655 -0
- package/dist/tools/conjure.js.map +1 -0
- package/dist/tools/conjure_screen.d.ts +174 -0
- package/dist/tools/conjure_screen.d.ts.map +1 -0
- package/dist/tools/conjure_screen.js +458 -0
- package/dist/tools/conjure_screen.js.map +1 -0
- package/dist/tools/create_kit.d.ts +32 -0
- package/dist/tools/create_kit.d.ts.map +1 -0
- package/dist/tools/create_kit.js +131 -0
- package/dist/tools/create_kit.js.map +1 -0
- package/dist/tools/create_project.d.ts +267 -0
- package/dist/tools/create_project.d.ts.map +1 -0
- package/dist/tools/create_project.js +546 -0
- package/dist/tools/create_project.js.map +1 -0
- package/dist/tools/delete_files.d.ts +93 -0
- package/dist/tools/delete_files.d.ts.map +1 -0
- package/dist/tools/delete_files.js +276 -0
- package/dist/tools/delete_files.js.map +1 -0
- package/dist/tools/delete_project.d.ts +43 -0
- package/dist/tools/delete_project.d.ts.map +1 -0
- package/dist/tools/delete_project.js +118 -0
- package/dist/tools/delete_project.js.map +1 -0
- package/dist/tools/get_kit.d.ts +28 -0
- package/dist/tools/get_kit.d.ts.map +1 -0
- package/dist/tools/get_kit.js +95 -0
- package/dist/tools/get_kit.js.map +1 -0
- package/dist/tools/get_project.d.ts +37 -0
- package/dist/tools/get_project.d.ts.map +1 -0
- package/dist/tools/get_project.js +71 -0
- package/dist/tools/get_project.js.map +1 -0
- package/dist/tools/list_components.d.ts +42 -0
- package/dist/tools/list_components.d.ts.map +1 -0
- package/dist/tools/list_components.js +125 -0
- package/dist/tools/list_components.js.map +1 -0
- package/dist/tools/list_files.d.ts +32 -0
- package/dist/tools/list_files.d.ts.map +1 -0
- package/dist/tools/list_files.js +109 -0
- package/dist/tools/list_files.js.map +1 -0
- package/dist/tools/list_kits.d.ts +14 -0
- package/dist/tools/list_kits.d.ts.map +1 -0
- package/dist/tools/list_kits.js +41 -0
- package/dist/tools/list_kits.js.map +1 -0
- package/dist/tools/list_projects.d.ts +23 -0
- package/dist/tools/list_projects.d.ts.map +1 -0
- package/dist/tools/list_projects.js +66 -0
- package/dist/tools/list_projects.js.map +1 -0
- package/dist/tools/plan.d.ts +16 -0
- package/dist/tools/plan.d.ts.map +1 -0
- package/dist/tools/plan.js +146 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/preview.d.ts +289 -0
- package/dist/tools/preview.d.ts.map +1 -0
- package/dist/tools/preview.js +672 -0
- package/dist/tools/preview.js.map +1 -0
- package/dist/tools/read_file.d.ts +40 -0
- package/dist/tools/read_file.d.ts.map +1 -0
- package/dist/tools/read_file.js +114 -0
- package/dist/tools/read_file.js.map +1 -0
- package/dist/tools/refine.d.ts +220 -0
- package/dist/tools/refine.d.ts.map +1 -0
- package/dist/tools/refine.js +501 -0
- package/dist/tools/refine.js.map +1 -0
- package/dist/tools/validate.d.ts +74 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +224 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/write_files.d.ts +158 -0
- package/dist/tools/write_files.d.ts.map +1 -0
- package/dist/tools/write_files.js +541 -0
- package/dist/tools/write_files.js.map +1 -0
- package/dist/transport.d.ts +77 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +448 -0
- package/dist/transport.js.map +1 -0
- package/dist/ui/card-asset-broker.d.ts +42 -0
- package/dist/ui/card-asset-broker.d.ts.map +1 -0
- package/dist/ui/card-asset-broker.js +479 -0
- package/dist/ui/card-asset-broker.js.map +1 -0
- package/dist/ui/grid-resource.d.ts +252 -0
- package/dist/ui/grid-resource.d.ts.map +1 -0
- package/dist/ui/grid-resource.js +680 -0
- package/dist/ui/grid-resource.js.map +1 -0
- package/dist/ui/viewer-static/index.html +95 -0
- package/dist/ui/viewer-static/viewer.css +451 -0
- package/dist/ui/viewer-static/viewer.js +1405 -0
- package/dist/validate/full-scan.d.ts +87 -0
- package/dist/validate/full-scan.d.ts.map +1 -0
- package/dist/validate/full-scan.js +299 -0
- package/dist/validate/full-scan.js.map +1 -0
- package/dist/validate/index.d.ts +15 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +18 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/marker.d.ts +89 -0
- package/dist/validate/marker.d.ts.map +1 -0
- package/dist/validate/marker.js +87 -0
- package/dist/validate/marker.js.map +1 -0
- package/dist/validate/phash.d.ts +135 -0
- package/dist/validate/phash.d.ts.map +1 -0
- package/dist/validate/phash.js +307 -0
- package/dist/validate/phash.js.map +1 -0
- package/dist/validate/render.d.ts +38 -0
- package/dist/validate/render.d.ts.map +1 -0
- package/dist/validate/render.js +155 -0
- package/dist/validate/render.js.map +1 -0
- package/dist/watch/index.d.ts +12 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +12 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/watcher.d.ts +65 -0
- package/dist/watch/watcher.d.ts.map +1 -0
- package/dist/watch/watcher.js +238 -0
- package/dist/watch/watcher.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `COMPONENT_SCHEMA` (M2-02) — the JSON Schema `conjure`/`refine` (M2-03/M2-04)
|
|
3
|
+
* demand from the model via `response_format: { type: "json_schema",
|
|
4
|
+
* json_schema: COMPONENT_SCHEMA }`, and that M2-07's Ajv validation pass
|
|
5
|
+
* (`validateComponent`) compiles against.
|
|
6
|
+
*
|
|
7
|
+
* Draft 7 only (AC2): no `anyOf`/`oneOf` discriminator pattern, and the only
|
|
8
|
+
* `$ref` in the whole schema is a single level deep (`manifestEntry.viewport`
|
|
9
|
+
* → `#/definitions/Viewport`, mirroring the RFC §9.20 shared-`$defs` shape).
|
|
10
|
+
* LiteLLM's structured-output passthrough to Anthropic (via tool-use) is the
|
|
11
|
+
* reason to keep nesting shallow — a deep `$ref` chain is exactly the shape
|
|
12
|
+
* that passthrough has been observed not to handle reliably.
|
|
13
|
+
*
|
|
14
|
+
* `as const satisfies JSONSchema7` (AC1) does two jobs at once: `satisfies`
|
|
15
|
+
* runs the object through `@types/json-schema`'s `JSONSchema7` shape so a
|
|
16
|
+
* typo'd keyword (e.g. `additionalProperty` instead of `additionalProperties`)
|
|
17
|
+
* is a compile error, while `as const` preserves the literal types `FromSchema`
|
|
18
|
+
* (AC7) needs to actually infer `ValidatedComponent` below rather than widening
|
|
19
|
+
* every string field to `string`.
|
|
20
|
+
*/
|
|
21
|
+
/**
|
|
22
|
+
* AC4 — every `files[].path` must land under `components/<group>/<Name>/`,
|
|
23
|
+
* matching the on-disk layout `write_files` (M1-08) ultimately writes to and
|
|
24
|
+
* the `@genie`-marker registration convention (D-A/D-B). `<group>` is
|
|
25
|
+
* `[a-z0-9-]+` (kebab-case, same shape as the top-level `group` field);
|
|
26
|
+
* `<Name>` is `[A-Z][A-Za-z0-9]+` (PascalCase, same shape as `componentName`);
|
|
27
|
+
* the file's own basename allows the broader `[A-Za-z0-9._-]+` (AC4's own
|
|
28
|
+
* pattern) since it must cover `<Name>.tsx`, `<Name>.d.ts`, `<Name>.prompt.md`,
|
|
29
|
+
* `<Name>.html`, and `meta.json` — five different suffix shapes — rather than
|
|
30
|
+
* repeating `<Name>` a second time in the basename segment.
|
|
31
|
+
*/
|
|
32
|
+
const PATH_PATTERN = "^components/[a-z0-9-]+/[A-Z][A-Za-z0-9]+/[A-Za-z0-9._-]+$";
|
|
33
|
+
/**
|
|
34
|
+
* AC5 — at least one `files[]` entry must be a `<Name>.html` file (the
|
|
35
|
+
* `@genie`-marker carrier M3-01 later validates post-hoc). `contains` is the
|
|
36
|
+
* Draft-7 keyword for "at least one array item matches this sub-schema" — it
|
|
37
|
+
* does not require every item to match, unlike `items`. The pattern captures
|
|
38
|
+
* the directory's `<Name>` segment and backreferences it against the
|
|
39
|
+
* filename (`([A-Z][A-Za-z0-9]+)/\1\.html$`), so `Button/Button.html`
|
|
40
|
+
* matches but a mismatched `Button/Wrong.html` does not — enforcing the same
|
|
41
|
+
* `<Name>/<Name>.html` self-consistency the RFC's file-layout convention
|
|
42
|
+
* assumes throughout (§7.3/§7.4/§9.10). It deliberately does NOT also check
|
|
43
|
+
* the file's `content` for the literal `@genie` marker text — that
|
|
44
|
+
* cross-field, regex-on-a-sibling-field check is exactly what M2-07's
|
|
45
|
+
* `validateComponent` does as a second pass after schema validation (this
|
|
46
|
+
* issue's AC5 note: "validated post-hoc by M3-01, not by the schema").
|
|
47
|
+
*/
|
|
48
|
+
const HTML_FILE_CONTAINS = {
|
|
49
|
+
type: "object",
|
|
50
|
+
properties: {
|
|
51
|
+
path: {
|
|
52
|
+
type: "string",
|
|
53
|
+
pattern: "^components/[a-z0-9-]+/([A-Z][A-Za-z0-9]{1,63})/\\1\\.html$",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export const COMPONENT_SCHEMA = {
|
|
58
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
59
|
+
$id: "https://genie.dev/schema/component.schema.json",
|
|
60
|
+
title: "GenieComponent",
|
|
61
|
+
description: "The file set + manifest metadata conjure/refine demand from the configured " +
|
|
62
|
+
"LLM endpoint via response_format.json_schema (M2-02).",
|
|
63
|
+
type: "object",
|
|
64
|
+
additionalProperties: false,
|
|
65
|
+
// AC3 — top-level shape: { componentName, group, files, manifestEntry }.
|
|
66
|
+
required: ["componentName", "group", "files", "manifestEntry"],
|
|
67
|
+
// The only $ref in this schema, one level deep (AC2) — mirrors the shared
|
|
68
|
+
// `Viewport` $def in RFC §9.20 rather than inlining width/height twice.
|
|
69
|
+
definitions: {
|
|
70
|
+
Viewport: {
|
|
71
|
+
type: "object",
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
required: ["width", "height"],
|
|
74
|
+
properties: {
|
|
75
|
+
width: { type: "integer", minimum: 1, maximum: 4096 },
|
|
76
|
+
height: { type: "integer", minimum: 1, maximum: 4096 },
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
properties: {
|
|
81
|
+
componentName: {
|
|
82
|
+
type: "string",
|
|
83
|
+
description: 'PascalCase component name, e.g. "Button".',
|
|
84
|
+
// Same shape as the equivalent `name` field in RFC §7.1/§7.5
|
|
85
|
+
// (manifest.json cards / the RFC's own COMPONENT_SCHEMA draft) — a
|
|
86
|
+
// 2-64 char PascalCase identifier. Minimum length is 2 (not 1) so a
|
|
87
|
+
// single-letter name like "B" cannot slip past `componentName` only to
|
|
88
|
+
// be trapped later by the `files[].path` pattern's `[A-Z][A-Za-z0-9]+`
|
|
89
|
+
// `<Name>` segment (which itself requires min length 2) — the two
|
|
90
|
+
// constraints move together.
|
|
91
|
+
pattern: "^[A-Z][A-Za-z0-9]{1,63}$",
|
|
92
|
+
},
|
|
93
|
+
group: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: 'Kebab-case UI-kit group/category, e.g. "actions".',
|
|
96
|
+
pattern: "^[a-z0-9-]{1,32}$",
|
|
97
|
+
},
|
|
98
|
+
files: {
|
|
99
|
+
type: "array",
|
|
100
|
+
description: "The component's file set (e.g. <Name>.jsx, <Name>.tsx, <Name>.d.ts, " +
|
|
101
|
+
"<Name>.prompt.md, <Name>.html, meta.json) — at least one must be a " +
|
|
102
|
+
"<Name>.html preview file (AC5).",
|
|
103
|
+
minItems: 1,
|
|
104
|
+
maxItems: 12,
|
|
105
|
+
contains: HTML_FILE_CONTAINS,
|
|
106
|
+
items: {
|
|
107
|
+
type: "object",
|
|
108
|
+
additionalProperties: false,
|
|
109
|
+
required: ["path", "content", "mimeType"],
|
|
110
|
+
properties: {
|
|
111
|
+
path: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Kit-relative path under components/<group>/<Name>/.",
|
|
114
|
+
pattern: PATH_PATTERN,
|
|
115
|
+
},
|
|
116
|
+
content: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Full file contents (AC5: an <Name>.html entry's content " +
|
|
119
|
+
"must begin with the @genie marker — checked post-hoc by M3-01/M2-07, " +
|
|
120
|
+
"not by this schema).",
|
|
121
|
+
minLength: 1,
|
|
122
|
+
maxLength: 65536,
|
|
123
|
+
},
|
|
124
|
+
mimeType: {
|
|
125
|
+
type: "string",
|
|
126
|
+
description: 'MIME type of `content`, e.g. "text/html".',
|
|
127
|
+
pattern: "^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
manifestEntry: {
|
|
133
|
+
type: "object",
|
|
134
|
+
description: "Metadata the manifest compiler (M3-03) needs to add this card to " +
|
|
135
|
+
".genie/manifest.json without re-deriving it from the files themselves.",
|
|
136
|
+
additionalProperties: false,
|
|
137
|
+
// AC6 — viewport is required; subtitle/tags are optional.
|
|
138
|
+
required: ["viewport"],
|
|
139
|
+
properties: {
|
|
140
|
+
viewport: { $ref: "#/definitions/Viewport" },
|
|
141
|
+
subtitle: { type: "string", maxLength: 256 },
|
|
142
|
+
tags: { type: "array", items: { type: "string" }, maxItems: 16 },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/llm/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2-07 — strict Ajv validation for the structured LLM output that conjure
|
|
3
|
+
* (M2-03) and refine (M2-04) receive from the configured OpenAI-compatible
|
|
4
|
+
* endpoint via `response_format: { type: "json_schema", ... }`.
|
|
5
|
+
*
|
|
6
|
+
* The single public entry point is {@link validateComponent}. It's called
|
|
7
|
+
* once per LLM completion, on the deserialised JSON payload the client got
|
|
8
|
+
* back. On success it returns the payload typed as {@link ValidatedComponent}
|
|
9
|
+
* (a compile-time re-typing — no reshape at runtime). On failure it throws
|
|
10
|
+
* {@link SchemaValidationError} carrying the full Ajv `errors: ErrorObject[]`
|
|
11
|
+
* so the retry path in M2-03/M2-04 can append the failure to the next prompt
|
|
12
|
+
* and give the model exactly one chance to self-correct.
|
|
13
|
+
*
|
|
14
|
+
* Two-pass design (AC3 + AC4):
|
|
15
|
+
* 1. Ajv strict-mode structural validation against `COMPONENT_SCHEMA`
|
|
16
|
+
* (M2-02). Catches missing fields, wrong types, bad path patterns, the
|
|
17
|
+
* "no <Name>.html preview" case, and the `manifestEntry.viewport`
|
|
18
|
+
* requirement.
|
|
19
|
+
* 2. `@genie` first-line marker cross-check on every `<Name>/<Name>.html`
|
|
20
|
+
* preview file the (now shape-valid) payload contains. This is
|
|
21
|
+
* intentionally *not* modelled in the JSON Schema itself — a regex-on-a-
|
|
22
|
+
* sibling-field is exactly the kind of cross-field constraint Draft 7
|
|
23
|
+
* can't express, and pushing it into JSON Schema would force the same
|
|
24
|
+
* shallow-`$ref` shape M2-02 was carefully written to avoid (see
|
|
25
|
+
* `schema.ts` AC2 note).
|
|
26
|
+
*
|
|
27
|
+
* The schema is compiled exactly once, at module load (AC5) — Ajv compilation
|
|
28
|
+
* is measurable per call and every LLM completion goes through this function,
|
|
29
|
+
* so caching pays for itself immediately.
|
|
30
|
+
*/
|
|
31
|
+
import { type ErrorObject } from "ajv";
|
|
32
|
+
import { type ValidatedComponent } from "./schema.js";
|
|
33
|
+
export type { ValidatedComponent } from "./schema.js";
|
|
34
|
+
export type { ErrorObject } from "ajv";
|
|
35
|
+
/**
|
|
36
|
+
* AC4 — the canonical `@genie` first-line marker regex from
|
|
37
|
+
* Every generated `<Name>.html` preview
|
|
38
|
+
* must open with a comment matching this pattern so the manifest compiler
|
|
39
|
+
* (M3-03) and the validate tool (M3-04) can register / verify the card
|
|
40
|
+
* without re-parsing the component's JSX.
|
|
41
|
+
*
|
|
42
|
+
* M3-01 (DRO-257) landed the single source of truth for this pattern at
|
|
43
|
+
* `packages/server/src/validate/marker.ts`'s `MARKER_REGEX`, imported here
|
|
44
|
+
* via the `validate/index.ts` public barrel (Copilot review, PR #142) rather
|
|
45
|
+
* than reaching into `marker.js` directly, so this call site tracks the
|
|
46
|
+
* barrel's stable surface instead of the module's internal file layout.
|
|
47
|
+
* This is now a re-export alias, kept under its original M2-07 name for
|
|
48
|
+
* backwards compatibility with existing consumers (this module's own
|
|
49
|
+
* cross-check below, plus `packages/e2e/test/m2-generation.test.ts`) rather
|
|
50
|
+
* than forcing every call site to rename in the same change that removes
|
|
51
|
+
* the duplicate literal.
|
|
52
|
+
*/
|
|
53
|
+
export declare const MARKER_REGEX_M2_07: RegExp;
|
|
54
|
+
/**
|
|
55
|
+
* Same selector as `COMPONENT_SCHEMA.properties.files.items.contains.pattern`
|
|
56
|
+
* — the `<group>/<Name>/<Name>.html` self-consistency shape ("preview file
|
|
57
|
+
* whose basename matches its containing directory"). Kept here as a compiled
|
|
58
|
+
* `RegExp` so the cross-check pass in {@link validateComponent} doesn't
|
|
59
|
+
* re-compile the pattern on every file iteration.
|
|
60
|
+
*
|
|
61
|
+
* The backreference `\1` locks the basename to the same PascalCase segment
|
|
62
|
+
* as the enclosing directory — this is what makes `Button/Wrong.html` fail
|
|
63
|
+
* schema-level AC5 but still lets `Button/Button.html` through. We reuse the
|
|
64
|
+
* exact shape here so the schema's `contains` guarantee (at least one file
|
|
65
|
+
* matches) and this cross-check (every matching file also has the `@genie`
|
|
66
|
+
* marker) can never disagree about which files are "preview files".
|
|
67
|
+
*
|
|
68
|
+
* Exported (Copilot review, PR #136) so consumers outside this module that
|
|
69
|
+
* need to identify "which files are actually preview files" — e.g.
|
|
70
|
+
* `m2-generation.test.ts`'s AC5 assertions — share this exact definition
|
|
71
|
+
* instead of re-deriving a looser one (`files[].path` also legally permits
|
|
72
|
+
* non-preview `.html` basenames like `dark-mode.html` per `schema.ts`'s
|
|
73
|
+
* `PATH_PATTERN`, which a naive `.endsWith(".html")` filter would wrongly
|
|
74
|
+
* subject to the marker rule).
|
|
75
|
+
*/
|
|
76
|
+
export declare const NAMED_HTML_PATH: RegExp;
|
|
77
|
+
/**
|
|
78
|
+
* `Error` subclass thrown by {@link validateComponent} on structural or
|
|
79
|
+
* marker-cross-check failure. Callers in M2-03/M2-04 catch this narrowly and
|
|
80
|
+
* append `error.errors` to the next prompt — the retry loop is exactly one
|
|
81
|
+
* round-trip, per DRO-254's "Out of Scope: auto-repair" note.
|
|
82
|
+
*
|
|
83
|
+
* `name` is set explicitly so `error.name === "SchemaValidationError"` works
|
|
84
|
+
* even after ESM module boundaries strip the class identity (defensive
|
|
85
|
+
* against `instanceof` foot-guns in vitest / workers).
|
|
86
|
+
*/
|
|
87
|
+
export declare class SchemaValidationError extends Error {
|
|
88
|
+
/** Ajv-style structured errors; always non-empty when this is thrown. */
|
|
89
|
+
readonly errors: ErrorObject[];
|
|
90
|
+
constructor(message: string, errors: ErrorObject[]);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* AC1 — the module's sole export contract. Validates `output` against
|
|
94
|
+
* `COMPONENT_SCHEMA` (M2-02) and the `@genie` marker convention (AC4). On
|
|
95
|
+
* success returns `output` cast to {@link ValidatedComponent} — an assertion
|
|
96
|
+
* of type, not a copy: callers get the exact same object identity back so
|
|
97
|
+
* downstream `write_files` (M1-08) can pass it through without an extra
|
|
98
|
+
* allocation per completion.
|
|
99
|
+
*
|
|
100
|
+
* @throws {SchemaValidationError} on any structural or marker failure. The
|
|
101
|
+
* thrown error's `errors` array holds Ajv `ErrorObject`s (schema-level
|
|
102
|
+
* failures) and/or synthetic `ErrorObject`s with `keyword: "@genie-marker"`
|
|
103
|
+
* (marker-level failures). Both live in the same array so M2-03/M2-04's
|
|
104
|
+
* retry prompt can serialise them uniformly.
|
|
105
|
+
*/
|
|
106
|
+
export declare function validateComponent(output: unknown): ValidatedComponent;
|
|
107
|
+
//# sourceMappingURL=validate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/llm/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAO,KAAK,WAAW,EAAyB,MAAM,KAAK,CAAC;AAEnE,OAAO,EAAoB,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGxE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,KAAK,CAAC;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,kBAAkB,QAAe,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,eAAe,QAAiE,CAAC;AAE9F;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,yEAAyE;IACzE,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC;gBAEnB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE;CAKnD;AAgBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,kBAAkB,CAkErE"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M2-07 — strict Ajv validation for the structured LLM output that conjure
|
|
3
|
+
* (M2-03) and refine (M2-04) receive from the configured OpenAI-compatible
|
|
4
|
+
* endpoint via `response_format: { type: "json_schema", ... }`.
|
|
5
|
+
*
|
|
6
|
+
* The single public entry point is {@link validateComponent}. It's called
|
|
7
|
+
* once per LLM completion, on the deserialised JSON payload the client got
|
|
8
|
+
* back. On success it returns the payload typed as {@link ValidatedComponent}
|
|
9
|
+
* (a compile-time re-typing — no reshape at runtime). On failure it throws
|
|
10
|
+
* {@link SchemaValidationError} carrying the full Ajv `errors: ErrorObject[]`
|
|
11
|
+
* so the retry path in M2-03/M2-04 can append the failure to the next prompt
|
|
12
|
+
* and give the model exactly one chance to self-correct.
|
|
13
|
+
*
|
|
14
|
+
* Two-pass design (AC3 + AC4):
|
|
15
|
+
* 1. Ajv strict-mode structural validation against `COMPONENT_SCHEMA`
|
|
16
|
+
* (M2-02). Catches missing fields, wrong types, bad path patterns, the
|
|
17
|
+
* "no <Name>.html preview" case, and the `manifestEntry.viewport`
|
|
18
|
+
* requirement.
|
|
19
|
+
* 2. `@genie` first-line marker cross-check on every `<Name>/<Name>.html`
|
|
20
|
+
* preview file the (now shape-valid) payload contains. This is
|
|
21
|
+
* intentionally *not* modelled in the JSON Schema itself — a regex-on-a-
|
|
22
|
+
* sibling-field is exactly the kind of cross-field constraint Draft 7
|
|
23
|
+
* can't express, and pushing it into JSON Schema would force the same
|
|
24
|
+
* shallow-`$ref` shape M2-02 was carefully written to avoid (see
|
|
25
|
+
* `schema.ts` AC2 note).
|
|
26
|
+
*
|
|
27
|
+
* The schema is compiled exactly once, at module load (AC5) — Ajv compilation
|
|
28
|
+
* is measurable per call and every LLM completion goes through this function,
|
|
29
|
+
* so caching pays for itself immediately.
|
|
30
|
+
*/
|
|
31
|
+
import { Ajv } from "ajv";
|
|
32
|
+
import { COMPONENT_SCHEMA } from "./schema.js";
|
|
33
|
+
import { MARKER_REGEX } from "../validate/index.js";
|
|
34
|
+
/**
|
|
35
|
+
* AC4 — the canonical `@genie` first-line marker regex from
|
|
36
|
+
* Every generated `<Name>.html` preview
|
|
37
|
+
* must open with a comment matching this pattern so the manifest compiler
|
|
38
|
+
* (M3-03) and the validate tool (M3-04) can register / verify the card
|
|
39
|
+
* without re-parsing the component's JSX.
|
|
40
|
+
*
|
|
41
|
+
* M3-01 (DRO-257) landed the single source of truth for this pattern at
|
|
42
|
+
* `packages/server/src/validate/marker.ts`'s `MARKER_REGEX`, imported here
|
|
43
|
+
* via the `validate/index.ts` public barrel (Copilot review, PR #142) rather
|
|
44
|
+
* than reaching into `marker.js` directly, so this call site tracks the
|
|
45
|
+
* barrel's stable surface instead of the module's internal file layout.
|
|
46
|
+
* This is now a re-export alias, kept under its original M2-07 name for
|
|
47
|
+
* backwards compatibility with existing consumers (this module's own
|
|
48
|
+
* cross-check below, plus `packages/e2e/test/m2-generation.test.ts`) rather
|
|
49
|
+
* than forcing every call site to rename in the same change that removes
|
|
50
|
+
* the duplicate literal.
|
|
51
|
+
*/
|
|
52
|
+
export const MARKER_REGEX_M2_07 = MARKER_REGEX;
|
|
53
|
+
/**
|
|
54
|
+
* Same selector as `COMPONENT_SCHEMA.properties.files.items.contains.pattern`
|
|
55
|
+
* — the `<group>/<Name>/<Name>.html` self-consistency shape ("preview file
|
|
56
|
+
* whose basename matches its containing directory"). Kept here as a compiled
|
|
57
|
+
* `RegExp` so the cross-check pass in {@link validateComponent} doesn't
|
|
58
|
+
* re-compile the pattern on every file iteration.
|
|
59
|
+
*
|
|
60
|
+
* The backreference `\1` locks the basename to the same PascalCase segment
|
|
61
|
+
* as the enclosing directory — this is what makes `Button/Wrong.html` fail
|
|
62
|
+
* schema-level AC5 but still lets `Button/Button.html` through. We reuse the
|
|
63
|
+
* exact shape here so the schema's `contains` guarantee (at least one file
|
|
64
|
+
* matches) and this cross-check (every matching file also has the `@genie`
|
|
65
|
+
* marker) can never disagree about which files are "preview files".
|
|
66
|
+
*
|
|
67
|
+
* Exported (Copilot review, PR #136) so consumers outside this module that
|
|
68
|
+
* need to identify "which files are actually preview files" — e.g.
|
|
69
|
+
* `m2-generation.test.ts`'s AC5 assertions — share this exact definition
|
|
70
|
+
* instead of re-deriving a looser one (`files[].path` also legally permits
|
|
71
|
+
* non-preview `.html` basenames like `dark-mode.html` per `schema.ts`'s
|
|
72
|
+
* `PATH_PATTERN`, which a naive `.endsWith(".html")` filter would wrongly
|
|
73
|
+
* subject to the marker rule).
|
|
74
|
+
*/
|
|
75
|
+
export const NAMED_HTML_PATH = /^components\/[a-z0-9-]+\/([A-Z][A-Za-z0-9]{1,63})\/\1\.html$/;
|
|
76
|
+
/**
|
|
77
|
+
* `Error` subclass thrown by {@link validateComponent} on structural or
|
|
78
|
+
* marker-cross-check failure. Callers in M2-03/M2-04 catch this narrowly and
|
|
79
|
+
* append `error.errors` to the next prompt — the retry loop is exactly one
|
|
80
|
+
* round-trip, per DRO-254's "Out of Scope: auto-repair" note.
|
|
81
|
+
*
|
|
82
|
+
* `name` is set explicitly so `error.name === "SchemaValidationError"` works
|
|
83
|
+
* even after ESM module boundaries strip the class identity (defensive
|
|
84
|
+
* against `instanceof` foot-guns in vitest / workers).
|
|
85
|
+
*/
|
|
86
|
+
export class SchemaValidationError extends Error {
|
|
87
|
+
/** Ajv-style structured errors; always non-empty when this is thrown. */
|
|
88
|
+
errors;
|
|
89
|
+
constructor(message, errors) {
|
|
90
|
+
super(message);
|
|
91
|
+
this.name = "SchemaValidationError";
|
|
92
|
+
this.errors = errors;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* AC2 + AC5 — one Ajv instance, compiled once, at module load. The `strict:
|
|
97
|
+
* true` flag turns every unknown/ambiguous keyword into a *compile-time*
|
|
98
|
+
* throw, so if `schema.ts` ever grows a typo (e.g. `additionalProperty`
|
|
99
|
+
* instead of `additionalProperties`) the server refuses to boot rather than
|
|
100
|
+
* silently letting malformed output through at runtime. `allErrors: true`
|
|
101
|
+
* asks Ajv to keep validating past the first failure and return every
|
|
102
|
+
* problem — critical for the "append failures to the prompt" retry loop,
|
|
103
|
+
* which can only fix errors the model actually sees.
|
|
104
|
+
*/
|
|
105
|
+
const ajv = new Ajv({ strict: true, allErrors: true });
|
|
106
|
+
const validate = ajv.compile(COMPONENT_SCHEMA);
|
|
107
|
+
/**
|
|
108
|
+
* AC1 — the module's sole export contract. Validates `output` against
|
|
109
|
+
* `COMPONENT_SCHEMA` (M2-02) and the `@genie` marker convention (AC4). On
|
|
110
|
+
* success returns `output` cast to {@link ValidatedComponent} — an assertion
|
|
111
|
+
* of type, not a copy: callers get the exact same object identity back so
|
|
112
|
+
* downstream `write_files` (M1-08) can pass it through without an extra
|
|
113
|
+
* allocation per completion.
|
|
114
|
+
*
|
|
115
|
+
* @throws {SchemaValidationError} on any structural or marker failure. The
|
|
116
|
+
* thrown error's `errors` array holds Ajv `ErrorObject`s (schema-level
|
|
117
|
+
* failures) and/or synthetic `ErrorObject`s with `keyword: "@genie-marker"`
|
|
118
|
+
* (marker-level failures). Both live in the same array so M2-03/M2-04's
|
|
119
|
+
* retry prompt can serialise them uniformly.
|
|
120
|
+
*/
|
|
121
|
+
export function validateComponent(output) {
|
|
122
|
+
// Pass 1 — Ajv structural validation. `validate` mutates `.errors` on the
|
|
123
|
+
// ValidateFunction itself (Ajv's API quirk); snapshot into a local so a
|
|
124
|
+
// concurrent second call from another async caller can't clobber it before
|
|
125
|
+
// we've thrown.
|
|
126
|
+
if (!validate(output)) {
|
|
127
|
+
const errors = validate.errors ?? [];
|
|
128
|
+
throw new SchemaValidationError(`LLM output failed COMPONENT_SCHEMA validation (${errors.length} error${errors.length === 1 ? "" : "s"}).`,
|
|
129
|
+
// Defensive copy — a caller inspecting `.errors` shouldn't be able to
|
|
130
|
+
// mutate the ValidateFunction's internal buffer through this handle.
|
|
131
|
+
errors.map((e) => ({ ...e })));
|
|
132
|
+
}
|
|
133
|
+
// At this point Ajv has narrowed `output` to `ValidatedComponent` via the
|
|
134
|
+
// typed ValidateFunction type-guard return; `output` is the same object
|
|
135
|
+
// identity the caller passed in.
|
|
136
|
+
const component = output;
|
|
137
|
+
// Pass 2 — @genie marker cross-check (AC4). Ajv can't express "field A
|
|
138
|
+
// matches regex X on field B's first line", so this runs in plain code
|
|
139
|
+
// against the (now shape-valid) file set. Collect every failure so the
|
|
140
|
+
// retry prompt can name all of them at once, mirroring `allErrors: true`.
|
|
141
|
+
const markerErrors = [];
|
|
142
|
+
for (let i = 0; i < component.files.length; i++) {
|
|
143
|
+
const file = component.files[i];
|
|
144
|
+
if (!NAMED_HTML_PATH.test(file.path)) {
|
|
145
|
+
// Not a `<Name>/<Name>.html` preview — the schema's `contains`
|
|
146
|
+
// guarantees at least one file *does* match, but non-preview files
|
|
147
|
+
// (e.g. `Button.tsx`, `meta.json`) are exempt from the marker rule.
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
// First line only (`\n` split limit=1 style, but done via indexOf to
|
|
151
|
+
// avoid allocating the whole `split()` array on files that pass — the
|
|
152
|
+
// vast majority in the steady state).
|
|
153
|
+
const newlineAt = file.content.indexOf("\n");
|
|
154
|
+
const firstLine = newlineAt === -1 ? file.content : file.content.slice(0, newlineAt);
|
|
155
|
+
if (!MARKER_REGEX_M2_07.test(firstLine)) {
|
|
156
|
+
markerErrors.push({
|
|
157
|
+
keyword: "@genie-marker",
|
|
158
|
+
instancePath: `/files/${i}/content`,
|
|
159
|
+
schemaPath: "#/properties/files/items/content",
|
|
160
|
+
params: {
|
|
161
|
+
expected: MARKER_REGEX_M2_07.source,
|
|
162
|
+
path: file.path,
|
|
163
|
+
},
|
|
164
|
+
message: `first line of ${file.path} does not match the @genie marker ` +
|
|
165
|
+
`regex ${MARKER_REGEX_M2_07} (required for card registration)`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (markerErrors.length > 0) {
|
|
170
|
+
throw new SchemaValidationError(`LLM output has ${markerErrors.length} @genie marker error${markerErrors.length === 1 ? "" : "s"} (structural schema OK, marker cross-check failed).`, markerErrors);
|
|
171
|
+
}
|
|
172
|
+
return component;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../../src/llm/validate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* M3-03 (DRO-259) — the `.genie/manifest.json` writer (client-side compiler).
|
|
3
|
+
*
|
|
4
|
+
* This is genie's substitute for Anthropic's server-side manifest
|
|
5
|
+
* compilation (research report §6): a small, dependency-light walker that
|
|
6
|
+
* recompiles `<projectRoot>/.genie/manifest.json` from the `@genie` markers
|
|
7
|
+
* on disk whenever the M3-02 watcher fires (or on any direct call — this
|
|
8
|
+
* module has no opinion on WHEN it runs, only on producing a correct
|
|
9
|
+
* manifest for a given `projectRoot` snapshot).
|
|
10
|
+
*
|
|
11
|
+
* ── Schema reconciliation (recorded on DRO-259 before this file was written) ──
|
|
12
|
+
* This issue's own AC4 (and the RFC §6.8/§7.1 sketch, and the M4-03 viewer's
|
|
13
|
+
* `fetch` code) describe a root key `cards` with `viewport: {width,height}`
|
|
14
|
+
* as an object. But `../store/manifest.ts` — already merged, backing the
|
|
15
|
+
* shipped/tested M1-15 `list_components` tool, read by BOTH `LocalFsKitStore`
|
|
16
|
+
* and `GitHostKitStore` — has a zod schema that requires the root key
|
|
17
|
+
* `components`, with `viewport` as the raw marker STRING (e.g. `"400x200"`
|
|
18
|
+
* or `"desktop"`), plus `{name, group, path, hash, lastModified}`.
|
|
19
|
+
*
|
|
20
|
+
* Emitting `cards` would silently break `list_components` (a live, shipped
|
|
21
|
+
* P0 tool): its zod parse requires `components` and would throw
|
|
22
|
+
* `ManifestParseError` on a `cards`-shaped file. So this compiler emits
|
|
23
|
+
* `components` with a STRING `viewport`, matching the already-shipped reader
|
|
24
|
+
* contract. `../store/manifest.ts`'s schemas are `.passthrough()`, so the
|
|
25
|
+
* RFC's additional fields are layered on top without conflict: top-level
|
|
26
|
+
* `version: 1`, `generatedAt`, `name` (the kit/project's directory basename),
|
|
27
|
+
* `groups: string[]`; per-card `subtitle`/`tags` when a sibling `meta.json`
|
|
28
|
+
* supplies them. This is additive/forward-compatible per RFC §16.2 ("add a
|
|
29
|
+
* new output field — allowed in minor/patch").
|
|
30
|
+
*
|
|
31
|
+
* Pure-ish orchestration: the only side effects are reading the component
|
|
32
|
+
* tree + sibling `meta.json`/`_groups.json` files and the final atomic write.
|
|
33
|
+
* Marker PRESENCE validation and card sort order are delegated to
|
|
34
|
+
* already-shipped, independently-tested modules (M3-01's `validateMarker`,
|
|
35
|
+
* `store/manifest.ts`'s `compareComponents`); group/viewport extraction is
|
|
36
|
+
* a narrow regex read of the same already-validated marker line (see
|
|
37
|
+
* `extractGroup`/`extractViewportToken` below for why M3-01's own
|
|
38
|
+
* `extractViewport` isn't reused verbatim here).
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* One compiled card (AC2–AC5). Matches `../store/manifest.ts`'s
|
|
42
|
+
* `ComponentEntry` six required fields exactly, plus the two optional
|
|
43
|
+
* `meta.json`-sourced fields already defined by `llm/schema.ts`'s
|
|
44
|
+
* `manifestEntry` (`subtitle`, `tags`).
|
|
45
|
+
*/
|
|
46
|
+
export interface ManifestCard {
|
|
47
|
+
name: string;
|
|
48
|
+
group: string;
|
|
49
|
+
path: string;
|
|
50
|
+
/** The raw marker token (e.g. "400x200" or "desktop") — AC5 note; not decomposed. */
|
|
51
|
+
viewport: string;
|
|
52
|
+
/** `sha256-<base64>` SRI hash of the HTML file's bytes (AC5). */
|
|
53
|
+
hash: string;
|
|
54
|
+
/** ISO-8601 mtime of the HTML file. */
|
|
55
|
+
lastModified: string;
|
|
56
|
+
subtitle?: string;
|
|
57
|
+
tags?: string[];
|
|
58
|
+
}
|
|
59
|
+
/** The full compiled manifest (AC4, reconciled — see module doc). */
|
|
60
|
+
export interface Manifest {
|
|
61
|
+
version: 1;
|
|
62
|
+
name: string;
|
|
63
|
+
generatedAt: string;
|
|
64
|
+
groups: string[];
|
|
65
|
+
components: ManifestCard[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* One skipped file the compiler declined to card — surfaced so a caller (the
|
|
69
|
+
* M3-02 watcher's `onChange`, or a future `validate` full-scan) can report
|
|
70
|
+
* `markerMissing` rather than the omission vanishing silently. Mirrors the
|
|
71
|
+
* RFC §6.8 failure mode: "the watcher silently skips it; `validate` surfaces
|
|
72
|
+
* the omission."
|
|
73
|
+
*/
|
|
74
|
+
export interface ManifestSkip {
|
|
75
|
+
path: string;
|
|
76
|
+
reason: "MARKER_MISSING";
|
|
77
|
+
}
|
|
78
|
+
/** {@link compileManifest}'s full result: the manifest plus any skips. */
|
|
79
|
+
export interface CompileResult {
|
|
80
|
+
manifest: Manifest;
|
|
81
|
+
skipped: ManifestSkip[];
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Recompile the manifest for `projectRoot` (AC1) purely from what's on disk
|
|
85
|
+
* right now — no incremental state, no cache. Safe to call as often as the
|
|
86
|
+
* watcher likes; a debounce (M3-02's own 100 ms window) is the caller's
|
|
87
|
+
* concern, not this function's.
|
|
88
|
+
*
|
|
89
|
+
* Steps (AC2/AC3):
|
|
90
|
+
* 1. Walk `${projectRoot}/components/**\/*.html`.
|
|
91
|
+
* 2. For each, validate the `@genie` first line (M3-01). A file that fails
|
|
92
|
+
* is SKIPPED (not carded) and reported in `skipped` — mirrors the RFC
|
|
93
|
+
* §6.8 failure mode ("the watcher silently skips it; validate surfaces
|
|
94
|
+
* the omission").
|
|
95
|
+
* 3. Extract `viewport` from the marker line; join a sibling `meta.json`
|
|
96
|
+
* for `subtitle`/`tags` when present (AC3).
|
|
97
|
+
* 4. Hash the HTML file's bytes as `sha256-<base64>` SRI (AC5, same format
|
|
98
|
+
* `list_files`/`.genie/sync.json` use — `store/kit-files.ts`'s
|
|
99
|
+
* `sriSha256`).
|
|
100
|
+
* 5. Sort groups alphabetically unless a root `_groups.json` sibling pins an
|
|
101
|
+
* explicit order (Impl Notes); sort cards alphabetically by `name`
|
|
102
|
+
* within a group, ties broken by `path` (the shared `compareComponents`
|
|
103
|
+
* from `store/manifest.ts` — AC6-equivalent ordering used everywhere
|
|
104
|
+
* else a component list is sorted in this codebase).
|
|
105
|
+
* 6. Atomically write `.genie/manifest.json` (AC6): stage under
|
|
106
|
+
* `${projectRoot}/.genie-tmp/`, then rename over the destination — same
|
|
107
|
+
* same-filesystem-rename pattern `sync/anchor.ts` and `store/local.ts`
|
|
108
|
+
* already use, so a crash mid-write leaves the PRIOR manifest (or none)
|
|
109
|
+
* intact, never a half-written one.
|
|
110
|
+
*/
|
|
111
|
+
export declare function compileManifest(projectRoot: string): Promise<CompileResult>;
|
|
112
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/manifest/compiler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAWH;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,qEAAqE;AACrE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,YAAY,EAAE,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,gBAAgB,CAAC;CAC1B;AAED,0EAA0E;AAC1E,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAqKD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA2DjF"}
|