aub-workspace 0.3.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/LICENSE +201 -0
- package/README.md +23 -0
- package/bin/aub-workspace.mjs +246 -0
- package/package.json +32 -0
- package/vendor/aub/apps/editor/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/angular-importer.lib-dB_jK4mR.js +32 -0
- package/vendor/aub/apps/editor/dist/assets/canvas-tools-CuYC7cA2.js +364 -0
- package/vendor/aub/apps/editor/dist/assets/design-bridge.lib-DJvaK6AX.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/export-agent-prompt.lib-BsP0KNqo.js +2 -0
- package/vendor/aub/apps/editor/dist/assets/export-md.lib-DdmdeWgO.js +3 -0
- package/vendor/aub/apps/editor/dist/assets/handoff-package.lib-DDYpcEma.js +20 -0
- package/vendor/aub/apps/editor/dist/assets/implementation-report.lib-CmsSB_8s.js +1 -0
- package/vendor/aub/apps/editor/dist/assets/index-BCH-ek3h.js +2 -0
- package/vendor/aub/apps/editor/dist/assets/index-lAnc928Q.css +1 -0
- package/vendor/aub/apps/editor/dist/assets/index-vt1nM1M4.js +507 -0
- package/vendor/aub/apps/editor/dist/assets/jszip.min-CRfXyL92.js +12 -0
- package/vendor/aub/apps/editor/dist/assets/react-vendor-ByX9Pqse.js +40 -0
- package/vendor/aub/apps/editor/dist/brand/android-chrome-192x192.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/android-chrome-512x512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-1024.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-192.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/app-icon-512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/apple-touch-icon.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/aub-logo-mark.svg +28 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-16x16.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-32x32.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon-48x48.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon.ico +0 -0
- package/vendor/aub/apps/editor/dist/brand/favicon.svg +9 -0
- package/vendor/aub/apps/editor/dist/brand/maskable-icon-512.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/mstile-150x150.png +0 -0
- package/vendor/aub/apps/editor/dist/brand/safari-pinned-tab.svg +8 -0
- package/vendor/aub/apps/editor/dist/browserconfig.xml +9 -0
- package/vendor/aub/apps/editor/dist/index.html +22 -0
- package/vendor/aub/apps/editor/dist/manifest.webmanifest +28 -0
- package/vendor/aub/apps/editor/dist/template-previews/admin-table.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/booking.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/calendar.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/catalog.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/chat.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/checkout.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/crm.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/dashboard.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/feed.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/files.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/kanban.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/landing.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/mail.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/onboarding.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/pricing.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/product-detail.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/settings.png +0 -0
- package/vendor/aub/apps/editor/dist/template-previews/wiki.png +0 -0
- package/vendor/aub/apps/mcp-server/dist/aub.js +15 -0
- package/vendor/aub/apps/mcp-server/dist/context.js +1 -0
- package/vendor/aub/apps/mcp-server/dist/http.js +123 -0
- package/vendor/aub/apps/mcp-server/dist/index.js +23 -0
- package/vendor/aub/apps/mcp-server/dist/repo.js +17 -0
- package/vendor/aub/apps/mcp-server/dist/schema.js +42 -0
- package/vendor/aub/apps/mcp-server/dist/server.js +80 -0
- package/vendor/aub/apps/mcp-server/dist/tools/approve-component-candidate.js +27 -0
- package/vendor/aub/apps/mcp-server/dist/tools/diff-blueprints.js +27 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-handoff.js +87 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-prompt.js +35 -0
- package/vendor/aub/apps/mcp-server/dist/tools/export-template-authoring-prompt.js +13 -0
- package/vendor/aub/apps/mcp-server/dist/tools/generate-template-from-source.js +25 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-aub-session.js +13 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-blueprint.js +28 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-project.js +45 -0
- package/vendor/aub/apps/mcp-server/dist/tools/get-workspace-status.js +10 -0
- package/vendor/aub/apps/mcp-server/dist/tools/import-design-bridge.js +62 -0
- package/vendor/aub/apps/mcp-server/dist/tools/list-blueprints.js +11 -0
- package/vendor/aub/apps/mcp-server/dist/tools/list-projects.js +11 -0
- package/vendor/aub/apps/mcp-server/dist/tools/lock-blueprint.js +33 -0
- package/vendor/aub/apps/mcp-server/dist/tools/migrate-blueprint.js +38 -0
- package/vendor/aub/apps/mcp-server/dist/tools/resolve-component.js +51 -0
- package/vendor/aub/apps/mcp-server/dist/tools/scaffold-blueprint.js +53 -0
- package/vendor/aub/apps/mcp-server/dist/tools/scan-project-ui.js +18 -0
- package/vendor/aub/apps/mcp-server/dist/tools/submit-report.js +48 -0
- package/vendor/aub/apps/mcp-server/dist/tools/update-aub-session.js +14 -0
- package/vendor/aub/apps/mcp-server/dist/tools/validate-blueprint.js +67 -0
- package/vendor/aub/apps/mcp-server/dist/tools/validate-project.js +74 -0
- package/vendor/aub/apps/mcp-server/dist/tools/write-blueprint.js +72 -0
- package/vendor/aub/apps/mcp-server/dist/workspace.js +138 -0
- package/vendor/aub/docs/agent-handoff.md +85 -0
- package/vendor/aub/docs/agent-handoff.zh-Hant.md +85 -0
- package/vendor/aub/docs/template-authoring-agent.md +86 -0
- package/vendor/aub/schema/aub-ci.schema.json +34 -0
- package/vendor/aub/schema/aub.registry.schema.json +118 -0
- package/vendor/aub/schema/design-bridge.schema.json +44 -0
- package/vendor/aub/schema/implementation-report.schema.json +93 -0
- package/vendor/aub/schema/project-types.ts +72 -0
- package/vendor/aub/schema/registry/components.json +118 -0
- package/vendor/aub/schema/types.js +13 -0
- package/vendor/aub/schema/types.ts +348 -0
- package/vendor/aub/schema/ui-blueprint-lock.schema.json +61 -0
- package/vendor/aub/schema/ui-blueprint.schema.json +1339 -0
- package/vendor/aub/schema/ui-project.schema.json +139 -0
- package/vendor/aub/scripts/agent-implementation-benchmark.lib.mjs +125 -0
- package/vendor/aub/scripts/angular-importer.lib.mjs +982 -0
- package/vendor/aub/scripts/check-editor-bundle-budget.mjs +36 -0
- package/vendor/aub/scripts/ci-verify.lib.mjs +256 -0
- package/vendor/aub/scripts/ci-verify.mjs +45 -0
- package/vendor/aub/scripts/create-authoring-kit.mjs +84 -0
- package/vendor/aub/scripts/create-implementation-report.mjs +24 -0
- package/vendor/aub/scripts/design-bridge.lib.d.mts +32 -0
- package/vendor/aub/scripts/design-bridge.lib.mjs +69 -0
- package/vendor/aub/scripts/diff-blueprint.lib.d.mts +18 -0
- package/vendor/aub/scripts/diff-blueprint.lib.mjs +148 -0
- package/vendor/aub/scripts/diff-blueprint.mjs +25 -0
- package/vendor/aub/scripts/export-agent-prompt.lib.d.mts +10 -0
- package/vendor/aub/scripts/export-agent-prompt.lib.mjs +160 -0
- package/vendor/aub/scripts/export-agent-prompt.mjs +79 -0
- package/vendor/aub/scripts/export-md.lib.d.mts +3 -0
- package/vendor/aub/scripts/export-md.lib.mjs +302 -0
- package/vendor/aub/scripts/export-md.mjs +43 -0
- package/vendor/aub/scripts/generate-registry-artifacts.lib.mjs +118 -0
- package/vendor/aub/scripts/generate-registry-artifacts.mjs +65 -0
- package/vendor/aub/scripts/generate-site-locales.mjs +545 -0
- package/vendor/aub/scripts/handoff-package.lib.d.mts +20 -0
- package/vendor/aub/scripts/handoff-package.lib.mjs +111 -0
- package/vendor/aub/scripts/implementation-report.lib.d.mts +21 -0
- package/vendor/aub/scripts/implementation-report.lib.mjs +97 -0
- package/vendor/aub/scripts/import-angular-component.mjs +72 -0
- package/vendor/aub/scripts/import-design-bridge.mjs +59 -0
- package/vendor/aub/scripts/lock-blueprint.lib.d.mts +23 -0
- package/vendor/aub/scripts/lock-blueprint.lib.mjs +58 -0
- package/vendor/aub/scripts/lock-blueprint.mjs +36 -0
- package/vendor/aub/scripts/migrate-blueprint-cli.mjs +28 -0
- package/vendor/aub/scripts/migrate-blueprint.d.mts +5 -0
- package/vendor/aub/scripts/migrate-blueprint.mjs +95 -0
- package/vendor/aub/scripts/package-workspace-cli.mjs +34 -0
- package/vendor/aub/scripts/project.lib.d.mts +44 -0
- package/vendor/aub/scripts/project.lib.mjs +175 -0
- package/vendor/aub/scripts/project.mjs +332 -0
- package/vendor/aub/scripts/registry.lib.d.mts +52 -0
- package/vendor/aub/scripts/registry.lib.mjs +222 -0
- package/vendor/aub/scripts/run-agent-implementation.mjs +423 -0
- package/vendor/aub/scripts/run-agent-readability.mjs +145 -0
- package/vendor/aub/scripts/run-ollama-prompt.mjs +30 -0
- package/vendor/aub/scripts/scaffold-blueprint.lib.d.mts +38 -0
- package/vendor/aub/scripts/scaffold-blueprint.lib.mjs +316 -0
- package/vendor/aub/scripts/scaffold-blueprint.mjs +86 -0
- package/vendor/aub/scripts/score-agent-implementation.mjs +27 -0
- package/vendor/aub/scripts/score-agent-readability.mjs +54 -0
- package/vendor/aub/scripts/sync-brand-assets.mjs +33 -0
- package/vendor/aub/scripts/validate-blueprint.lib.d.mts +14 -0
- package/vendor/aub/scripts/validate-blueprint.lib.mjs +136 -0
- package/vendor/aub/scripts/validate.mjs +128 -0
- package/vendor/aub/scripts/verify-implementation-report.mjs +36 -0
- package/vendor/aub/scripts/workspace-loop.lib.d.mts +17 -0
- package/vendor/aub/scripts/workspace-loop.lib.mjs +674 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { copyFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
const root = resolve(import.meta.dirname, '..');
|
|
5
|
+
const source = resolve(root, 'brand');
|
|
6
|
+
const targets = [
|
|
7
|
+
resolve(root, 'site/assets/brand'),
|
|
8
|
+
resolve(root, 'apps/editor/public/brand'),
|
|
9
|
+
];
|
|
10
|
+
const files = [
|
|
11
|
+
'aub-logo-mark.svg',
|
|
12
|
+
'favicon.svg',
|
|
13
|
+
'favicon-16x16.png',
|
|
14
|
+
'favicon-32x32.png',
|
|
15
|
+
'favicon-48x48.png',
|
|
16
|
+
'favicon.ico',
|
|
17
|
+
'apple-touch-icon.png',
|
|
18
|
+
'app-icon-192.png',
|
|
19
|
+
'app-icon-512.png',
|
|
20
|
+
'app-icon-1024.png',
|
|
21
|
+
'android-chrome-192x192.png',
|
|
22
|
+
'android-chrome-512x512.png',
|
|
23
|
+
'mstile-150x150.png',
|
|
24
|
+
'safari-pinned-tab.svg',
|
|
25
|
+
'maskable-icon-512.png',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
for (const target of targets) {
|
|
29
|
+
await mkdir(target, { recursive: true });
|
|
30
|
+
await Promise.all(files.map((file) => copyFile(resolve(source, file), resolve(target, file))));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log(`Synced ${files.length} brand assets to ${targets.length} deploy targets.`);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type Blueprint = Record<string, any>;
|
|
2
|
+
|
|
3
|
+
export interface KnownTypeMeta {
|
|
4
|
+
isContainer: boolean;
|
|
5
|
+
source?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type KnownTypes = Map<string, KnownTypeMeta>;
|
|
9
|
+
|
|
10
|
+
export function validateBlueprintSemantics(
|
|
11
|
+
blueprint: Blueprint,
|
|
12
|
+
options?: { knownTypes?: KnownTypes }
|
|
13
|
+
): string[];
|
|
14
|
+
export function resolvePlacement(node: Record<string, any>, viewportId: string): Record<string, any> | null;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
export function validateBlueprintSemantics(blueprint, { knownTypes } = {}) {
|
|
2
|
+
const errors = [];
|
|
3
|
+
const nodes = Array.isArray(blueprint?.nodes) ? blueprint.nodes : [];
|
|
4
|
+
const byId = new Map();
|
|
5
|
+
const interactionSources = new Set();
|
|
6
|
+
|
|
7
|
+
for (const node of nodes) {
|
|
8
|
+
if (byId.has(node.id)) errors.push(`duplicate node id: ${node.id}`);
|
|
9
|
+
byId.set(node.id, node);
|
|
10
|
+
interactionSources.add(node.id);
|
|
11
|
+
for (const item of node.content?.items ?? []) {
|
|
12
|
+
if (item?.id) interactionSources.add(item.id);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (knownTypes) {
|
|
17
|
+
for (const node of nodes) {
|
|
18
|
+
const type = node?.type;
|
|
19
|
+
if (typeof type !== 'string') continue;
|
|
20
|
+
const isExtension = type.includes(':');
|
|
21
|
+
const meta = knownTypes.get(type);
|
|
22
|
+
if (!meta) {
|
|
23
|
+
if (isExtension) {
|
|
24
|
+
errors.push(
|
|
25
|
+
`${node.id}: unknown component type "${type}" — declare it in aub.registry.json`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
// Core-looking unknown types are already rejected by the JSON Schema enum.
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (isExtension && !meta.isContainer && (node.children ?? []).length > 0) {
|
|
32
|
+
errors.push(
|
|
33
|
+
`${node.id}: extension type "${type}" is a leaf (isContainer:false) but declares children`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const roots = nodes.filter((node) => node.parent_id == null);
|
|
40
|
+
if (roots.length !== 1) errors.push(`expected exactly one root node, found ${roots.length}`);
|
|
41
|
+
|
|
42
|
+
for (const node of nodes) {
|
|
43
|
+
if (node.parent_id != null && !byId.has(node.parent_id)) {
|
|
44
|
+
errors.push(`${node.id}: parent_id references missing node ${node.parent_id}`);
|
|
45
|
+
} else if (node.parent_id != null) {
|
|
46
|
+
const parent = byId.get(node.parent_id);
|
|
47
|
+
if (!(parent.children ?? []).includes(node.id)) {
|
|
48
|
+
errors.push(`${node.id}: parent ${node.parent_id} does not declare it in children`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const childId of node.children ?? []) {
|
|
52
|
+
const child = byId.get(childId);
|
|
53
|
+
if (!child) {
|
|
54
|
+
errors.push(`${node.id}: children references missing node ${childId}`);
|
|
55
|
+
} else if (child.parent_id !== node.id) {
|
|
56
|
+
errors.push(`${node.id}: child ${childId} has parent_id ${String(child.parent_id)}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const stateKey of ['empty_state', 'loading_state', 'error_state']) {
|
|
60
|
+
const stateId = node.content?.[stateKey];
|
|
61
|
+
if (stateId && !byId.has(stateId)) {
|
|
62
|
+
errors.push(`${node.id}: content.${stateKey} references missing node ${stateId}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (node.layout?.mode === 'freeform') {
|
|
66
|
+
const children = (node.children ?? []).map((id) => byId.get(id)).filter(Boolean);
|
|
67
|
+
const viewports = blueprint.viewports ?? [];
|
|
68
|
+
for (const child of children) {
|
|
69
|
+
for (const viewport of viewports) {
|
|
70
|
+
const placement = child.placements?.[viewport.id];
|
|
71
|
+
if (!placement) {
|
|
72
|
+
errors.push(`${child.id}: missing placement for ${viewport.id} inside freeform parent ${node.id}`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (placement.x < 0 || placement.y < 0) {
|
|
76
|
+
errors.push(`${child.id}: placement for ${viewport.id} has negative coordinates`);
|
|
77
|
+
}
|
|
78
|
+
if (placement.width <= 0 || placement.height <= 0) {
|
|
79
|
+
errors.push(`${child.id}: placement for ${viewport.id} must have positive size`);
|
|
80
|
+
}
|
|
81
|
+
if (placement.x + placement.width > viewport.width) {
|
|
82
|
+
errors.push(`${child.id}: placement for ${viewport.id} overflows viewport width ${viewport.width}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (roots.length === 1) {
|
|
90
|
+
const visited = new Set();
|
|
91
|
+
const active = new Set();
|
|
92
|
+
const walk = (id) => {
|
|
93
|
+
if (active.has(id)) {
|
|
94
|
+
errors.push(`component tree contains a cycle at ${id}`);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (visited.has(id)) return;
|
|
98
|
+
visited.add(id);
|
|
99
|
+
active.add(id);
|
|
100
|
+
const node = byId.get(id);
|
|
101
|
+
for (const childId of node?.children ?? []) {
|
|
102
|
+
if (byId.has(childId)) walk(childId);
|
|
103
|
+
}
|
|
104
|
+
active.delete(id);
|
|
105
|
+
};
|
|
106
|
+
walk(roots[0].id);
|
|
107
|
+
for (const node of nodes) {
|
|
108
|
+
if (!visited.has(node.id)) errors.push(`${node.id}: node is not reachable from root ${roots[0].id}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const interaction of blueprint?.interactions ?? []) {
|
|
113
|
+
if (!interactionSources.has(interaction.source_node_id)) {
|
|
114
|
+
errors.push(`${interaction.id}: source_node_id references missing node ${interaction.source_node_id}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const responsive of blueprint?.responsive ?? []) {
|
|
119
|
+
if (!byId.has(responsive.target_node_id)) {
|
|
120
|
+
errors.push(`responsive ${responsive.viewport}/${responsive.rule}: missing target ${responsive.target_node_id}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return errors;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function resolvePlacement(node, viewportId) {
|
|
128
|
+
const placements = node?.placements;
|
|
129
|
+
if (!placements) return null;
|
|
130
|
+
return placements[viewportId]
|
|
131
|
+
?? placements.desktop
|
|
132
|
+
?? placements.tablet
|
|
133
|
+
?? placements.mobile
|
|
134
|
+
?? placements.wide
|
|
135
|
+
?? null;
|
|
136
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Validates a UI Blueprint file against the JSON Schema.
|
|
3
|
+
// Usage: node scripts/validate.mjs <file.ui.json|file.ui.yaml>
|
|
4
|
+
|
|
5
|
+
import { readFile } from 'node:fs/promises';
|
|
6
|
+
import { resolve, extname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { dirname, join } from 'node:path';
|
|
9
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
10
|
+
import addFormats from 'ajv-formats';
|
|
11
|
+
import yaml from 'js-yaml';
|
|
12
|
+
import { validateBlueprintSemantics } from './validate-blueprint.lib.mjs';
|
|
13
|
+
import { buildKnownTypes } from './registry.lib.mjs';
|
|
14
|
+
import { validateProjectFile } from './project.mjs';
|
|
15
|
+
|
|
16
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
const ROOT = resolve(__dirname, '..');
|
|
18
|
+
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const args = { file: null, registry: null };
|
|
21
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
22
|
+
const token = argv[i];
|
|
23
|
+
if (token === '--registry') {
|
|
24
|
+
args.registry = argv[i + 1];
|
|
25
|
+
i += 1;
|
|
26
|
+
} else if (token.startsWith('--registry=')) {
|
|
27
|
+
args.registry = token.slice('--registry='.length);
|
|
28
|
+
} else if (!args.file) {
|
|
29
|
+
args.file = token;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return args;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function main() {
|
|
36
|
+
const { file: arg, registry: registryArg } = parseArgs(process.argv.slice(2));
|
|
37
|
+
if (!arg) {
|
|
38
|
+
console.error('Usage: pnpm validate <file.ui.json|file.ui.yaml> [--registry <aub.registry.json>]');
|
|
39
|
+
process.exit(2);
|
|
40
|
+
}
|
|
41
|
+
const filePath = resolve(arg);
|
|
42
|
+
const ext = extname(filePath).toLowerCase();
|
|
43
|
+
|
|
44
|
+
const schemaPath = join(ROOT, 'schema', 'ui-blueprint.schema.json');
|
|
45
|
+
const schemaRaw = await readFile(schemaPath, 'utf8');
|
|
46
|
+
const schema = JSON.parse(schemaRaw);
|
|
47
|
+
|
|
48
|
+
const ajv = new Ajv2020({ allErrors: true, strict: true });
|
|
49
|
+
addFormats(ajv);
|
|
50
|
+
const validate = ajv.compile(schema);
|
|
51
|
+
|
|
52
|
+
const fileRaw = await readFile(filePath, 'utf8');
|
|
53
|
+
let document;
|
|
54
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
55
|
+
document = yaml.load(fileRaw);
|
|
56
|
+
} else {
|
|
57
|
+
document = JSON.parse(fileRaw);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const looksLikeProject =
|
|
61
|
+
/\.aub\.project\.json$/i.test(filePath) ||
|
|
62
|
+
(document &&
|
|
63
|
+
typeof document === 'object' &&
|
|
64
|
+
Array.isArray(document.screens) &&
|
|
65
|
+
document.entry_screen != null &&
|
|
66
|
+
!('nodes' in document));
|
|
67
|
+
if (looksLikeProject) {
|
|
68
|
+
console.log('→ detected project document; validating as project');
|
|
69
|
+
const result = await validateProjectFile(filePath);
|
|
70
|
+
if (result.perScreen) {
|
|
71
|
+
for (const screen of result.perScreen) {
|
|
72
|
+
if (screen.ok) {
|
|
73
|
+
console.log(` ✓ screen ${screen.id}`);
|
|
74
|
+
} else {
|
|
75
|
+
console.error(` ✗ screen ${screen.id}`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (result.ok) {
|
|
80
|
+
console.log(`✓ valid project: ${arg}`);
|
|
81
|
+
process.exit(0);
|
|
82
|
+
}
|
|
83
|
+
console.error(`✗ invalid project: ${arg}`);
|
|
84
|
+
for (const error of result.errors) {
|
|
85
|
+
console.error(` ${error}`);
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let knownTypes;
|
|
91
|
+
let extensionPath = null;
|
|
92
|
+
try {
|
|
93
|
+
const resolved = await buildKnownTypes({
|
|
94
|
+
extensionPath: registryArg ? resolve(registryArg) : null,
|
|
95
|
+
startDir: dirname(filePath),
|
|
96
|
+
});
|
|
97
|
+
knownTypes = resolved.knownTypes;
|
|
98
|
+
extensionPath = resolved.extensionPath;
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(`✗ invalid: ${arg}`);
|
|
101
|
+
console.error(` registry: ${err.message}`);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const ok = validate(document);
|
|
106
|
+
const semanticErrors = ok ? validateBlueprintSemantics(document, { knownTypes }) : [];
|
|
107
|
+
if (ok && semanticErrors.length === 0) {
|
|
108
|
+
const suffix = extensionPath ? ` (extensions: ${extensionPath})` : '';
|
|
109
|
+
console.log(`✓ valid: ${arg}${suffix}`);
|
|
110
|
+
process.exit(0);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
console.error(`✗ invalid: ${arg}`);
|
|
114
|
+
for (const err of validate.errors ?? []) {
|
|
115
|
+
const path = err.instancePath || '(root)';
|
|
116
|
+
console.error(` ${path} ${err.message}`);
|
|
117
|
+
if (err.params) console.error(` params: ${JSON.stringify(err.params)}`);
|
|
118
|
+
}
|
|
119
|
+
for (const error of semanticErrors) {
|
|
120
|
+
console.error(` semantic: ${error}`);
|
|
121
|
+
}
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
main().catch((err) => {
|
|
126
|
+
console.error(err);
|
|
127
|
+
process.exit(2);
|
|
128
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
4
|
+
import { readFile } from 'node:fs/promises';
|
|
5
|
+
import { extname, resolve } from 'node:path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
import reportSchema from '../schema/implementation-report.schema.json' with { type: 'json' };
|
|
8
|
+
import { verifyImplementationReport } from './implementation-report.lib.mjs';
|
|
9
|
+
|
|
10
|
+
const [blueprintPath, reportPath] = process.argv.slice(2);
|
|
11
|
+
if (!blueprintPath || !reportPath) {
|
|
12
|
+
console.error('Usage: node scripts/verify-implementation-report.mjs <blueprint.ui.json|yaml> <report.json>');
|
|
13
|
+
process.exit(2);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const [blueprint, report] = await Promise.all([
|
|
17
|
+
readBlueprint(blueprintPath),
|
|
18
|
+
readFile(resolve(reportPath), 'utf8').then(JSON.parse),
|
|
19
|
+
]);
|
|
20
|
+
const validate = new Ajv2020({ allErrors: true, strict: false }).compile(reportSchema);
|
|
21
|
+
if (!validate(report)) {
|
|
22
|
+
for (const error of validate.errors ?? []) {
|
|
23
|
+
console.error(`✗ ${error.instancePath || '(root)'} ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const result = verifyImplementationReport(blueprint, report);
|
|
29
|
+
console.log(JSON.stringify(result, null, 2));
|
|
30
|
+
process.exit(result.ready ? 0 : 1);
|
|
31
|
+
|
|
32
|
+
async function readBlueprint(path) {
|
|
33
|
+
const text = await readFile(resolve(path), 'utf8');
|
|
34
|
+
const extension = extname(path).toLowerCase();
|
|
35
|
+
return extension === '.yaml' || extension === '.yml' ? yaml.load(text) : JSON.parse(text);
|
|
36
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const WORKSPACE_LOOP_VERSION: string;
|
|
2
|
+
export const AUB_DIR: string;
|
|
3
|
+
export const SESSION_PATH: string;
|
|
4
|
+
export const COMPONENT_CANDIDATES_PATH: string;
|
|
5
|
+
export const TEMPLATE_DIR: string;
|
|
6
|
+
export const TEMPLATE_FORMAT: string;
|
|
7
|
+
export const TEMPLATE_FORMAT_VERSION: string;
|
|
8
|
+
|
|
9
|
+
export function readAubSession(root: string): Promise<Record<string, any>>;
|
|
10
|
+
export function updateAubSession(root: string, patch?: Record<string, any>): Promise<{ path: string; session: Record<string, any> }>;
|
|
11
|
+
export function readComponentCandidates(root: string): Promise<{ format: string; format_version: string; candidates: any[] }>;
|
|
12
|
+
export function listWorkspaceTemplates(root: string): Promise<any[]>;
|
|
13
|
+
export function getWorkspaceStatus(root: string): Promise<Record<string, any>>;
|
|
14
|
+
export function scanProjectUi(root: string, options?: Record<string, any>): Promise<Record<string, any>>;
|
|
15
|
+
export function generateTemplateFromSource(root: string, args?: Record<string, any>): Promise<{ savedPath: string; template: Record<string, any> }>;
|
|
16
|
+
export function approveComponentCandidate(root: string, args?: Record<string, any>): Promise<Record<string, any>>;
|
|
17
|
+
export function templateAuthoringPrompt(): string;
|