@object-ui/cli 17.5.0 → 17.7.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/dist/cli.js CHANGED
@@ -3,115 +3,36 @@ import {
3
3
  createTempApp,
4
4
  createTempAppWithRouting,
5
5
  init,
6
- parseSchemaFile,
7
- scanPagesDirectory,
6
+ isWorkspaceRoot,
7
+ prepareWorkspaceTempApp,
8
+ reportProjectSource,
9
+ resolveProjectSource,
8
10
  serve
9
- } from "./chunk-2MZA766D.js";
11
+ } from "./chunk-VMMIGYU3.js";
10
12
 
11
13
  // src/cli.ts
12
14
  import { Command } from "commander";
13
- import chalk14 from "chalk";
15
+ import chalk9 from "chalk";
14
16
 
15
17
  // src/commands/dev.ts
16
18
  import { createServer } from "vite";
17
19
  import react from "@vitejs/plugin-react";
18
- import { existsSync, mkdirSync, unlinkSync, statSync } from "fs";
19
- import { join, resolve, dirname } from "path";
20
+ import { mkdirSync } from "fs";
21
+ import { join } from "path";
20
22
  import chalk from "chalk";
21
23
  import { execSync } from "child_process";
22
- import { createRequire } from "module";
23
- async function loadTempAppPostcssPlugins() {
24
- try {
25
- const [tailwindPostcss, autoprefixer] = await Promise.all([
26
- import("@tailwindcss/postcss"),
27
- import("autoprefixer")
28
- ]);
29
- return [tailwindPostcss.default(), autoprefixer.default()];
30
- } catch (error) {
31
- throw new Error(
32
- `Failed to load the Tailwind CSS PostCSS pipeline: ${error instanceof Error ? error.message : error}
33
- Both '@tailwindcss/postcss' and 'autoprefixer' are dependencies of @object-ui/cli \u2014 a
34
- broken install of the CLI is the likeliest cause; reinstall it and try again.
35
- (Refusing to start unstyled: that failure is silent in the browser.)`
36
- );
37
- }
38
- }
39
24
  async function dev(schemaPath, options) {
40
25
  const cwd = process.cwd();
41
- let _projectRoot = cwd;
42
- const targetSchemaPath = schemaPath;
43
- let hasPagesDir = false;
44
- let pagesDir = "";
45
- let appConfig = null;
46
- const absoluteSchemaPath = resolve(cwd, schemaPath);
47
- if (existsSync(absoluteSchemaPath) && statSync(absoluteSchemaPath).isFile()) {
48
- const fileDir = dirname(absoluteSchemaPath);
49
- const potentialPagesDir = join(fileDir, "pages");
50
- if (existsSync(potentialPagesDir)) {
51
- console.log(chalk.blue(`\u{1F4C2} Detected project structure at ${fileDir}`));
52
- _projectRoot = fileDir;
53
- hasPagesDir = true;
54
- pagesDir = potentialPagesDir;
55
- try {
56
- appConfig = parseSchemaFile(absoluteSchemaPath);
57
- console.log(chalk.blue("\u2699\uFE0F Loaded App Config from app.json"));
58
- } catch (_e) {
59
- console.warn("Failed to parse app config");
60
- }
61
- }
62
- }
63
- if (!hasPagesDir) {
64
- const localPagesDir = join(cwd, "pages");
65
- if (existsSync(localPagesDir)) {
66
- hasPagesDir = true;
67
- pagesDir = localPagesDir;
68
- const appJsonPath = join(cwd, "app.json");
69
- if (existsSync(appJsonPath)) {
70
- try {
71
- appConfig = parseSchemaFile(appJsonPath);
72
- console.log(chalk.blue("\u2699\uFE0F Loaded App Config from app.json"));
73
- } catch (_e) {
74
- console.warn("Failed to parse app.json config");
75
- }
76
- }
77
- }
78
- }
79
- const require2 = createRequire(join(cwd, "package.json"));
80
- let routes = [];
81
- let schema = null;
82
- let useFileSystemRouting = false;
83
- if (hasPagesDir) {
84
- console.log(chalk.blue(`\u{1F4C1} Using file-system routing from ${pagesDir}`));
85
- routes = scanPagesDirectory(pagesDir);
86
- useFileSystemRouting = true;
87
- if (routes.length === 0) {
88
- throw new Error(`No schema files found in ${pagesDir}`);
89
- }
90
- console.log(chalk.green(`\u2713 Found ${routes.length} route(s)`));
91
- routes.forEach((route) => {
92
- console.log(chalk.dim(` ${route.path} \u2192 ${route.filePath.replace(cwd, ".")}`));
93
- });
94
- } else {
95
- const fullSchemaPath = resolve(cwd, schemaPath);
96
- if (!existsSync(fullSchemaPath)) {
97
- throw new Error(`Schema file not found: ${schemaPath}
98
- Run 'objectui init' to create a sample schema.`);
99
- }
100
- console.log(chalk.blue("\u{1F4CB} Loading schema:"), chalk.cyan(schemaPath));
101
- try {
102
- schema = parseSchemaFile(fullSchemaPath);
103
- } catch (error) {
104
- throw new Error(`Invalid schema file: ${error instanceof Error ? error.message : error}`);
105
- }
106
- }
26
+ const source = resolveProjectSource(cwd, schemaPath);
27
+ reportProjectSource(source, cwd);
107
28
  const tmpDir = join(cwd, ".objectui-tmp");
108
29
  mkdirSync(tmpDir, { recursive: true });
109
- if (useFileSystemRouting) {
110
- createTempAppWithRouting(tmpDir, routes, appConfig);
30
+ if (source.mode === "routes") {
31
+ createTempAppWithRouting(tmpDir, source.routes, source.appConfig);
111
32
  } else {
112
- createTempApp(tmpDir, schema);
33
+ createTempApp(tmpDir, source.schema);
113
34
  }
114
- const isMonorepo = existsSync(join(cwd, "pnpm-workspace.yaml"));
35
+ const isMonorepo = isWorkspaceRoot(cwd);
115
36
  if (isMonorepo) {
116
37
  console.log(chalk.blue("\u{1F4E6} Detected monorepo - using root node_modules"));
117
38
  } else {
@@ -129,6 +50,10 @@ Run 'objectui init' to create a sample schema.`);
129
50
  }
130
51
  console.log(chalk.green("\u2713 Schema loaded successfully"));
131
52
  console.log(chalk.blue("\u{1F680} Starting development server...\n"));
53
+ if (isMonorepo) {
54
+ console.log(chalk.blue("\u{1F4E6} Detected monorepo - configuring workspace aliases"));
55
+ }
56
+ const workspaceConfig = isMonorepo ? await prepareWorkspaceTempApp(cwd, tmpDir) : {};
132
57
  const viteConfig = {
133
58
  root: tmpDir,
134
59
  server: {
@@ -140,38 +65,9 @@ Run 'objectui init' to create a sample schema.`);
140
65
  allow: [cwd]
141
66
  }
142
67
  },
143
- resolve: {
144
- alias: {}
145
- },
146
- plugins: [react()]
68
+ plugins: [react()],
69
+ ...workspaceConfig
147
70
  };
148
- if (isMonorepo) {
149
- console.log(chalk.blue("\u{1F4E6} Detected monorepo - configuring workspace aliases"));
150
- const postcssPath = join(tmpDir, "postcss.config.js");
151
- if (existsSync(postcssPath)) {
152
- unlinkSync(postcssPath);
153
- }
154
- viteConfig.resolve.alias = {
155
- "@object-ui/react": join(cwd, "packages/react/src/index.ts"),
156
- "@object-ui/components": join(cwd, "packages/components/src/index.ts"),
157
- "@object-ui/core": join(cwd, "packages/core/src/index.ts"),
158
- "@object-ui/types": join(cwd, "packages/types/src/index.ts"),
159
- "@object-ui/plugin-charts": join(cwd, "packages/plugin-charts/src/index.tsx"),
160
- "@object-ui/plugin-editor": join(cwd, "packages/plugin-editor/src/index.tsx"),
161
- "@object-ui/plugin-kanban": join(cwd, "packages/plugin-kanban/src/index.tsx"),
162
- "@object-ui/plugin-markdown": join(cwd, "packages/plugin-markdown/src/index.tsx"),
163
- "@object-ui/plugin-form": join(cwd, "packages/plugin-form/src/index.tsx"),
164
- "@object-ui/plugin-grid": join(cwd, "packages/plugin-grid/src/index.tsx"),
165
- "@object-ui/plugin-view": join(cwd, "packages/plugin-view/src/index.tsx")
166
- };
167
- try {
168
- const lucidePath = require2.resolve("lucide-react", { paths: [join(cwd, "packages/components")] });
169
- viteConfig.resolve.alias["lucide-react"] = lucidePath;
170
- } catch (e) {
171
- console.warn("\u26A0\uFE0F Could not resolve lucide-react automatically:", e);
172
- }
173
- viteConfig.css = { postcss: { plugins: await loadTempAppPostcssPlugins() } };
174
- }
175
71
  const server = await createServer(viteConfig);
176
72
  await server.listen();
177
73
  const { port, host } = server.config.server;
@@ -189,62 +85,47 @@ Run 'objectui init' to create a sample schema.`);
189
85
  // src/commands/build.ts
190
86
  import { build as viteBuild } from "vite";
191
87
  import react2 from "@vitejs/plugin-react";
192
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, cpSync, rmSync } from "fs";
193
- import { join as join2, resolve as resolve2 } from "path";
88
+ import { existsSync, mkdirSync as mkdirSync2, cpSync, rmSync } from "fs";
89
+ import { join as join2, resolve } from "path";
194
90
  import chalk2 from "chalk";
195
91
  import { execSync as execSync2 } from "child_process";
196
92
  async function buildApp(schemaPath, options) {
197
93
  const cwd = process.cwd();
198
94
  const outDir = options.outDir || "dist";
199
- const outputPath = resolve2(cwd, outDir);
95
+ const outputPath = resolve(cwd, outDir);
200
96
  console.log(chalk2.blue("\u{1F528} Building application for production..."));
201
97
  console.log();
202
- const pagesDir = join2(cwd, "pages");
203
- const hasPagesDir = existsSync2(pagesDir);
204
- let routes = [];
205
- let schema = null;
206
- let useFileSystemRouting = false;
207
- if (hasPagesDir) {
208
- console.log(chalk2.blue("\u{1F4C1} Using file-system routing"));
209
- routes = scanPagesDirectory(pagesDir);
210
- useFileSystemRouting = true;
211
- if (routes.length === 0) {
212
- throw new Error("No schema files found in pages/ directory");
213
- }
214
- console.log(chalk2.green(`\u2713 Found ${routes.length} route(s)`));
215
- } else {
216
- const fullSchemaPath = resolve2(cwd, schemaPath);
217
- if (!existsSync2(fullSchemaPath)) {
218
- throw new Error(`Schema file not found: ${schemaPath}
219
- Run 'objectui init' to create a sample schema.`);
220
- }
221
- console.log(chalk2.blue("\u{1F4CB} Loading schema:"), chalk2.cyan(schemaPath));
222
- try {
223
- schema = parseSchemaFile(fullSchemaPath);
224
- } catch (error) {
225
- throw new Error(`Invalid schema file: ${error instanceof Error ? error.message : error}`);
226
- }
227
- }
98
+ const source = resolveProjectSource(cwd, schemaPath);
99
+ reportProjectSource(source, cwd);
228
100
  const tmpDir = join2(cwd, ".objectui-tmp");
229
101
  mkdirSync2(tmpDir, { recursive: true });
230
- if (useFileSystemRouting) {
231
- createTempAppWithRouting(tmpDir, routes);
102
+ if (source.mode === "routes") {
103
+ createTempAppWithRouting(tmpDir, source.routes, source.appConfig);
232
104
  } else {
233
- createTempApp(tmpDir, schema);
105
+ createTempApp(tmpDir, source.schema);
234
106
  }
235
- console.log(chalk2.blue("\u{1F4E6} Installing dependencies..."));
236
- try {
237
- execSync2("npm install --silent --prefer-offline", {
238
- cwd: tmpDir,
239
- stdio: "pipe"
240
- });
241
- console.log(chalk2.green("\u2713 Dependencies installed"));
242
- } catch {
243
- throw new Error("Failed to install dependencies. Please check your internet connection and try again.");
107
+ const isMonorepo = isWorkspaceRoot(cwd);
108
+ if (isMonorepo) {
109
+ console.log(chalk2.blue("\u{1F4E6} Detected monorepo - using root node_modules"));
110
+ } else {
111
+ console.log(chalk2.blue("\u{1F4E6} Installing dependencies..."));
112
+ try {
113
+ execSync2("npm install --silent --prefer-offline", {
114
+ cwd: tmpDir,
115
+ stdio: "pipe"
116
+ });
117
+ console.log(chalk2.green("\u2713 Dependencies installed"));
118
+ } catch {
119
+ throw new Error("Failed to install dependencies. Please check your internet connection and try again.");
120
+ }
121
+ }
122
+ if (isMonorepo) {
123
+ console.log(chalk2.blue("\u{1F4E6} Detected monorepo - configuring workspace aliases"));
244
124
  }
125
+ const workspaceConfig = isMonorepo ? await prepareWorkspaceTempApp(cwd, tmpDir) : {};
245
126
  console.log(chalk2.blue("\u2699\uFE0F Building with Vite..."));
246
127
  console.log();
247
- if (options.clean && existsSync2(outputPath)) {
128
+ if (options.clean && existsSync(outputPath)) {
248
129
  console.log(chalk2.dim(` Cleaning ${outDir}/ directory...`));
249
130
  rmSync(outputPath, { recursive: true, force: true });
250
131
  }
@@ -257,7 +138,8 @@ Run 'objectui init' to create a sample schema.`);
257
138
  reportCompressedSize: true
258
139
  },
259
140
  plugins: [react2()],
260
- logLevel: "info"
141
+ logLevel: "info",
142
+ ...workspaceConfig
261
143
  });
262
144
  mkdirSync2(outputPath, { recursive: true });
263
145
  cpSync(join2(tmpDir, "dist"), outputPath, { recursive: true });
@@ -277,21 +159,21 @@ Run 'objectui init' to create a sample schema.`);
277
159
  // src/commands/start.ts
278
160
  import express from "express";
279
161
  import rateLimit from "express-rate-limit";
280
- import { existsSync as existsSync3 } from "fs";
281
- import { join as join3, resolve as resolve3 } from "path";
162
+ import { existsSync as existsSync2 } from "fs";
163
+ import { join as join3, resolve as resolve2 } from "path";
282
164
  import chalk3 from "chalk";
283
165
  async function start(options) {
284
166
  const cwd = process.cwd();
285
167
  const distDir = options.dir || "dist";
286
- const distPath = resolve3(cwd, distDir);
287
- if (!existsSync3(distPath)) {
168
+ const distPath = resolve2(cwd, distDir);
169
+ if (!existsSync2(distPath)) {
288
170
  throw new Error(
289
171
  `Build directory not found: ${distDir}
290
172
  Run 'objectui build' first to create a production build.`
291
173
  );
292
174
  }
293
175
  const indexPath = join3(distPath, "index.html");
294
- if (!existsSync3(indexPath)) {
176
+ if (!existsSync2(indexPath)) {
295
177
  throw new Error(
296
178
  `index.html not found in ${distDir}/
297
179
  Make sure you have a valid production build.`
@@ -330,108 +212,10 @@ Make sure you have a valid production build.`
330
212
  });
331
213
  }
332
214
 
333
- // src/commands/lint.ts
334
- import { execSync as execSync3 } from "child_process";
335
- import { existsSync as existsSync4 } from "fs";
336
- import { join as join4 } from "path";
337
- import chalk4 from "chalk";
338
- async function lint(options) {
339
- const cwd = process.cwd();
340
- console.log(chalk4.blue("\u{1F50D} Running linter...\n"));
341
- const tmpDir = join4(cwd, ".objectui-tmp");
342
- const hasTempApp = existsSync4(tmpDir);
343
- if (!hasTempApp) {
344
- throw new Error(
345
- "No Object UI application found. Run 'objectui dev' first to generate the application."
346
- );
347
- }
348
- const packageJsonPath = join4(tmpDir, "package.json");
349
- const nodeModulesPath = join4(tmpDir, "node_modules");
350
- if (!existsSync4(packageJsonPath) || !existsSync4(nodeModulesPath)) {
351
- throw new Error(
352
- "Dependencies not installed. Run 'objectui dev' first to set up the application."
353
- );
354
- }
355
- try {
356
- const fixFlag = options.fix ? "--fix" : "";
357
- const command = `npx eslint "src/**/*.{js,jsx,ts,tsx}" ${fixFlag}`.trim();
358
- console.log(chalk4.dim(` Running: ${command}
359
- `));
360
- execSync3(command, {
361
- cwd: tmpDir,
362
- stdio: "inherit"
363
- });
364
- console.log();
365
- console.log(chalk4.green("\u2713 Linting completed successfully!"));
366
- console.log();
367
- } catch {
368
- console.log();
369
- console.log(chalk4.yellow("\u26A0 Linting found issues."));
370
- if (!options.fix) {
371
- console.log(chalk4.dim(" Run 'objectui lint --fix' to automatically fix some issues."));
372
- }
373
- console.log();
374
- process.exit(1);
375
- }
376
- }
377
-
378
- // src/commands/test.ts
379
- import { execSync as execSync4 } from "child_process";
380
- import { existsSync as existsSync5 } from "fs";
381
- import { join as join5 } from "path";
382
- import chalk5 from "chalk";
383
- async function test(options) {
384
- const cwd = process.cwd();
385
- console.log(chalk5.blue("\u{1F9EA} Running tests...\n"));
386
- const tmpDir = join5(cwd, ".objectui-tmp");
387
- const hasTempApp = existsSync5(tmpDir);
388
- if (!hasTempApp) {
389
- throw new Error(
390
- "No Object UI application found. Run 'objectui dev' first to generate the application."
391
- );
392
- }
393
- const packageJsonPath = join5(tmpDir, "package.json");
394
- const nodeModulesPath = join5(tmpDir, "node_modules");
395
- if (!existsSync5(packageJsonPath) || !existsSync5(nodeModulesPath)) {
396
- throw new Error(
397
- "Dependencies not installed. Run 'objectui dev' first to set up the application."
398
- );
399
- }
400
- try {
401
- let command = "npx vitest";
402
- if (options.watch) {
403
- command += " --watch";
404
- } else if (options.ui) {
405
- command += " --ui";
406
- } else {
407
- command += " run";
408
- }
409
- if (options.coverage) {
410
- command += " --coverage";
411
- }
412
- console.log(chalk5.dim(` Running: ${command}
413
- `));
414
- execSync4(command, {
415
- cwd: tmpDir,
416
- stdio: "inherit"
417
- });
418
- if (!options.watch && !options.ui) {
419
- console.log();
420
- console.log(chalk5.green("\u2713 Tests completed successfully!"));
421
- console.log();
422
- }
423
- } catch {
424
- console.log();
425
- console.log(chalk5.red("\u2717 Some tests failed."));
426
- console.log();
427
- process.exit(1);
428
- }
429
- }
430
-
431
215
  // src/commands/generate.ts
432
- import chalk6 from "chalk";
433
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync } from "fs";
434
- import { join as join6 } from "path";
216
+ import chalk4 from "chalk";
217
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, writeFileSync } from "fs";
218
+ import { join as join4 } from "path";
435
219
  async function generate(type, name) {
436
220
  const cwd = process.cwd();
437
221
  switch (type) {
@@ -446,18 +230,18 @@ async function generate(type, name) {
446
230
  generatePlugin(cwd, name);
447
231
  break;
448
232
  default:
449
- console.log(chalk6.red(`Unknown type: ${type}`));
233
+ console.log(chalk4.red(`Unknown type: ${type}`));
450
234
  console.log(`Available types: resource, page, plugin`);
451
235
  process.exit(1);
452
236
  }
453
237
  }
454
238
  function generateResource(cwd, name) {
455
- const dir = join6(cwd, "objects");
456
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
239
+ const dir = join4(cwd, "objects");
240
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
457
241
  const fileName = `${name.toLowerCase()}.json`;
458
- const filePath = join6(dir, fileName);
459
- if (existsSync6(filePath)) {
460
- console.log(chalk6.yellow(`Object ${name} already exists at ${filePath}`));
242
+ const filePath = join4(dir, fileName);
243
+ if (existsSync3(filePath)) {
244
+ console.log(chalk4.yellow(`Object ${name} already exists at ${filePath}`));
461
245
  return;
462
246
  }
463
247
  const content = {
@@ -467,34 +251,35 @@ function generateResource(cwd, name) {
467
251
  }
468
252
  };
469
253
  writeFileSync(filePath, JSON.stringify(content, null, 2));
470
- console.log(chalk6.green(`\u2713 Generated resource schema: ${filePath}`));
254
+ console.log(chalk4.green(`\u2713 Generated resource schema: ${filePath}`));
471
255
  }
472
256
  function generatePage(cwd, name) {
473
- const dir = join6(cwd, "pages");
474
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
257
+ const dir = join4(cwd, "pages");
258
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
475
259
  const fileName = `${name.toLowerCase()}.json`;
476
- const filePath = join6(dir, fileName);
477
- if (existsSync6(filePath)) {
478
- console.log(chalk6.yellow(`Page ${name} already exists at ${filePath}`));
260
+ const filePath = join4(dir, fileName);
261
+ if (existsSync3(filePath)) {
262
+ console.log(chalk4.yellow(`Page ${name} already exists at ${filePath}`));
479
263
  return;
480
264
  }
481
265
  const content = {
482
266
  type: "page",
267
+ pageType: "app",
483
268
  title: name,
484
- body: [
269
+ children: [
485
270
  {
486
271
  type: "markdown",
487
- content: `# Welcome to ${name}`
272
+ content: `Welcome to ${name}`
488
273
  }
489
274
  ]
490
275
  };
491
276
  writeFileSync(filePath, JSON.stringify(content, null, 2));
492
- console.log(chalk6.green(`\u2713 Generated page schema: ${filePath}`));
277
+ console.log(chalk4.green(`\u2713 Generated page schema: ${filePath}`));
493
278
  }
494
279
  function generatePlugin(cwd, name) {
495
- const dir = join6(cwd, "plugins", name);
496
- if (existsSync6(dir)) {
497
- console.log(chalk6.yellow(`Plugin ${name} already exists`));
280
+ const dir = join4(cwd, "plugins", name);
281
+ if (existsSync3(dir)) {
282
+ console.log(chalk4.yellow(`Plugin ${name} already exists`));
498
283
  return;
499
284
  }
500
285
  mkdirSync3(dir, { recursive: true });
@@ -506,14 +291,14 @@ export const ${name}Plugin = {
506
291
  }
507
292
  };
508
293
  `;
509
- writeFileSync(join6(dir, "index.ts"), content.trim());
510
- console.log(chalk6.green(`\u2713 Generated plugin: ${dir}`));
294
+ writeFileSync(join4(dir, "index.ts"), content.trim());
295
+ console.log(chalk4.green(`\u2713 Generated plugin: ${dir}`));
511
296
  }
512
297
 
513
298
  // src/commands/doctor.ts
514
- import chalk7 from "chalk";
515
- import { existsSync as existsSync7, readFileSync } from "fs";
516
- import { dirname as dirname2, join as join7, resolve as resolvePath } from "path";
299
+ import chalk5 from "chalk";
300
+ import { existsSync as existsSync4, readFileSync } from "fs";
301
+ import { dirname, join as join5, resolve as resolvePath } from "path";
517
302
  var CSS_ENTRY_CANDIDATES = [
518
303
  "src/index.css",
519
304
  "src/main.css",
@@ -548,15 +333,15 @@ var V4_POSTCSS_PLUGIN = /@tailwindcss\/postcss/;
548
333
  var V3_POSTCSS_PLUGIN = /["']tailwindcss["']|(?:^|[{,\s])tailwindcss\s*:/m;
549
334
  function readIfExists(path) {
550
335
  try {
551
- return existsSync7(path) ? readFileSync(path, "utf-8") : null;
336
+ return existsSync4(path) ? readFileSync(path, "utf-8") : null;
552
337
  } catch {
553
338
  return null;
554
339
  }
555
340
  }
556
341
  function findFile(cwd, candidates) {
557
342
  for (const rel of candidates) {
558
- const abs = join7(cwd, rel);
559
- if (existsSync7(abs)) return abs;
343
+ const abs = join5(cwd, rel);
344
+ if (existsSync4(abs)) return abs;
560
345
  }
561
346
  return null;
562
347
  }
@@ -569,8 +354,8 @@ function majorOf(range) {
569
354
  function isInstalledFrom(cwd, specifier) {
570
355
  let dir = resolvePath(cwd);
571
356
  for (; ; ) {
572
- if (existsSync7(join7(dir, "node_modules", ...specifier.split("/")))) return true;
573
- const parent = dirname2(dir);
357
+ if (existsSync4(join5(dir, "node_modules", ...specifier.split("/")))) return true;
358
+ const parent = dirname(dir);
574
359
  if (parent === dir) return false;
575
360
  dir = parent;
576
361
  }
@@ -586,7 +371,7 @@ function runDiagnostics(cwd) {
586
371
  const error = (id, message) => {
587
372
  results.push({ id, level: "error", message });
588
373
  };
589
- const pkgPath = join7(cwd, "package.json");
374
+ const pkgPath = join5(cwd, "package.json");
590
375
  const pkgRaw = readIfExists(pkgPath);
591
376
  let pkg = null;
592
377
  if (pkgRaw === null) {
@@ -654,7 +439,7 @@ function runDiagnostics(cwd) {
654
439
  }
655
440
  const cssEntries = CSS_ENTRY_CANDIDATES.map((rel) => ({
656
441
  rel,
657
- content: readIfExists(join7(cwd, rel))
442
+ content: readIfExists(join5(cwd, rel))
658
443
  })).filter((entry) => entry.content !== null);
659
444
  const importsTailwind = cssEntries.some((entry) => V4_CSS_IMPORT.test(entry.content));
660
445
  const usesV3Directives = cssEntries.some((entry) => V3_CSS_DIRECTIVE.test(entry.content));
@@ -688,7 +473,7 @@ function runDiagnostics(cwd) {
688
473
  ok("tailwind-config-active", "JS Tailwind config is loaded via `@config`");
689
474
  }
690
475
  }
691
- if (existsSync7(join7(cwd, "tsconfig.json"))) {
476
+ if (existsSync4(join5(cwd, "tsconfig.json"))) {
692
477
  ok("tsconfig-found", "tsconfig.json found");
693
478
  const tsRange = deps.typescript;
694
479
  if (tsRange) {
@@ -716,142 +501,879 @@ function countIssues(results) {
716
501
  return results.filter((result) => result.level !== "ok").length;
717
502
  }
718
503
  async function doctor() {
719
- console.log(chalk7.bold("Object UI Doctor"));
504
+ console.log(chalk5.bold("Object UI Doctor"));
720
505
  console.log("Diagnosis in progress...\n");
721
506
  const results = runDiagnostics(process.cwd());
722
507
  for (const result of results) {
723
508
  if (result.level === "ok") {
724
- console.log(chalk7.green(`\u2713 ${result.message}`));
509
+ console.log(chalk5.green(`\u2713 ${result.message}`));
725
510
  } else if (result.level === "warn") {
726
- console.log(chalk7.yellow(`\u26A0\uFE0F ${result.message}`));
511
+ console.log(chalk5.yellow(`\u26A0\uFE0F ${result.message}`));
727
512
  } else {
728
- console.log(chalk7.red(`x ${result.message}`));
513
+ console.log(chalk5.red(`x ${result.message}`));
729
514
  }
730
515
  }
731
516
  const issues = countIssues(results);
732
517
  console.log("\nResult:");
733
518
  if (issues === 0) {
734
- console.log(chalk7.green("Everything looks good! \u2728"));
519
+ console.log(chalk5.green("Everything looks good! \u2728"));
735
520
  } else {
736
- console.log(chalk7.yellow(`Found ${issues} issue(s).`));
521
+ console.log(chalk5.yellow(`Found ${issues} issue(s).`));
737
522
  }
738
523
  }
739
524
 
740
- // src/commands/add.ts
741
- import chalk8 from "chalk";
742
- async function add(component) {
743
- console.log(chalk8.bold(`Object UI Add: ${component}`));
744
- console.log(chalk8.yellow("Feature not implemented yet."));
745
- console.log(`This command will download the source code for ${component}Renderer to your project.`);
525
+ // src/commands/check.ts
526
+ import chalk6 from "chalk";
527
+ import { globSync } from "glob";
528
+ import { readFileSync as readFileSync2 } from "fs";
529
+ import { join as join6 } from "path";
530
+ import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
531
+ import { safeValidateSchema } from "@object-ui/types/zod";
532
+
533
+ // src/utils/issue-path.ts
534
+ function formatIssuePath(path) {
535
+ const segments = path ?? [];
536
+ return segments.length > 0 ? segments.join(" \u2192 ") : "(root)";
746
537
  }
747
538
 
748
- // src/commands/studio.ts
749
- import chalk9 from "chalk";
750
- import { spawn } from "child_process";
751
- import { existsSync as existsSync8 } from "fs";
752
- import { join as join8, resolve as resolve4, dirname as dirname3 } from "path";
753
- import { fileURLToPath } from "url";
754
- async function studio() {
755
- console.log(chalk9.bold("\n\u{1F3A8} Starting ObjectUI Studio...\n"));
756
- const cwd = process.cwd();
757
- const __filename3 = fileURLToPath(import.meta.url);
758
- const __dirname3 = dirname3(__filename3);
759
- const candidates = [
760
- join8(cwd, "apps/console"),
761
- resolve4(__dirname3, "../../apps/console"),
762
- resolve4(__dirname3, "../../../apps/console")
763
- ];
764
- let consolePath = null;
765
- for (const candidate of candidates) {
766
- if (existsSync8(join8(candidate, "package.json"))) {
767
- consolePath = candidate;
768
- break;
769
- }
539
+ // src/utils/known-schema-types.ts
540
+ var KNOWN_SCHEMA_TYPES = [
541
+ "a",
542
+ "abbr",
543
+ "accordion",
544
+ "action:bar",
545
+ "action:button",
546
+ "action:group",
547
+ "action:icon",
548
+ "action:menu",
549
+ "address",
550
+ "ai-form-assist",
551
+ "ai-recommendations",
552
+ "ai:feedback",
553
+ "ai:input",
554
+ "ai:suggestion",
555
+ "alert",
556
+ "alert-dialog",
557
+ "app",
558
+ "app-schema-renderer",
559
+ "app-shell:cloud-connection:panel",
560
+ "app-shell:cloud:ai-model-status",
561
+ "app-shell:cloud:onboarding-next",
562
+ "app-shell:marketplace:installed-list",
563
+ "app-shell:mcp:connect-agent",
564
+ "app:launcher",
565
+ "article",
566
+ "aside",
567
+ "aspect-ratio",
568
+ "avatar",
569
+ "b",
570
+ "badge",
571
+ "bar-chart",
572
+ "blockquote",
573
+ "box",
574
+ "br",
575
+ "breadcrumb",
576
+ "button",
577
+ "button-group",
578
+ "calendar",
579
+ "calendar-view",
580
+ "card",
581
+ "carousel",
582
+ "chart",
583
+ "chart:bar",
584
+ "chatbot",
585
+ "chatbot-enhanced",
586
+ "chatbot-floating",
587
+ "checkbox",
588
+ "cite",
589
+ "cloud-connection:panel",
590
+ "cloud:ai-model-status",
591
+ "cloud:onboarding-next",
592
+ "cloud:plan-status",
593
+ "code",
594
+ "code-editor",
595
+ "collapsible",
596
+ "combobox",
597
+ "command",
598
+ "container",
599
+ "context-menu",
600
+ "dashboard",
601
+ "data-model-designer",
602
+ "data-table",
603
+ "date-picker",
604
+ "dd",
605
+ "del",
606
+ "detail",
607
+ "detail-section",
608
+ "detail-view",
609
+ "dialog",
610
+ "div",
611
+ "dl",
612
+ "drawer",
613
+ "dropdown-menu",
614
+ "dt",
615
+ "element:button",
616
+ "element:definition-list",
617
+ "element:divider",
618
+ "element:image",
619
+ "element:metadata_viewer",
620
+ "element:number",
621
+ "element:record_picker",
622
+ "element:repeater",
623
+ "element:text",
624
+ "element:text_input",
625
+ "em",
626
+ "email",
627
+ "embeddable-form",
628
+ "empty",
629
+ "field-designer",
630
+ "field:address",
631
+ "field:audio",
632
+ "field:auto_number",
633
+ "field:avatar",
634
+ "field:boolean",
635
+ "field:checkboxes",
636
+ "field:code",
637
+ "field:color",
638
+ "field:currency",
639
+ "field:date",
640
+ "field:datetime",
641
+ "field:duration",
642
+ "field:email",
643
+ "field:file",
644
+ "field:filter-condition",
645
+ "field:formula",
646
+ "field:geolocation",
647
+ "field:grid",
648
+ "field:html",
649
+ "field:image",
650
+ "field:json",
651
+ "field:location",
652
+ "field:lookup",
653
+ "field:markdown",
654
+ "field:master_detail",
655
+ "field:multiselect",
656
+ "field:number",
657
+ "field:object",
658
+ "field:object-ref",
659
+ "field:password",
660
+ "field:percent",
661
+ "field:permission-facet-link",
662
+ "field:phone",
663
+ "field:progress",
664
+ "field:qrcode",
665
+ "field:radio",
666
+ "field:rating",
667
+ "field:recipient-picker",
668
+ "field:richtext",
669
+ "field:select",
670
+ "field:signature",
671
+ "field:slider",
672
+ "field:summary",
673
+ "field:tags",
674
+ "field:text",
675
+ "field:textarea",
676
+ "field:time",
677
+ "field:tree",
678
+ "field:url",
679
+ "field:user",
680
+ "field:vector",
681
+ "field:video",
682
+ "figcaption",
683
+ "figure",
684
+ "file-upload",
685
+ "filter-builder",
686
+ "filter-ui",
687
+ "flex",
688
+ "footer",
689
+ "form",
690
+ "global:notifications",
691
+ "global:search",
692
+ "grid",
693
+ "h1",
694
+ "h2",
695
+ "h3",
696
+ "h4",
697
+ "h5",
698
+ "h6",
699
+ "header",
700
+ "header-bar",
701
+ "home",
702
+ "hover-card",
703
+ "hr",
704
+ "html",
705
+ "i",
706
+ "icon",
707
+ "image",
708
+ "img",
709
+ "input",
710
+ "input-otp",
711
+ "ins",
712
+ "kbd",
713
+ "label",
714
+ "layout:app-schema-renderer",
715
+ "layout:page:card",
716
+ "li",
717
+ "list",
718
+ "list-view",
719
+ "loading",
720
+ "main",
721
+ "mark",
722
+ "markdown",
723
+ "marketplace:installed-list",
724
+ "mcp:connect-agent",
725
+ "menubar",
726
+ "nav",
727
+ "nav:breadcrumb",
728
+ "nav:menu",
729
+ "navigation-menu",
730
+ "nl-query",
731
+ "object-calendar",
732
+ "object-chart",
733
+ "object-data-table",
734
+ "object-form",
735
+ "object-gallery",
736
+ "object-gantt",
737
+ "object-grid",
738
+ "object-kanban",
739
+ "object-manager",
740
+ "object-map",
741
+ "object-master-detail-form",
742
+ "object-metric",
743
+ "object-pivot",
744
+ "object-timeline",
745
+ "object-tree",
746
+ "object-view",
747
+ "ol",
748
+ "p",
749
+ "page",
750
+ "page-designer",
751
+ "page:accordion",
752
+ "page:card",
753
+ "page:footer",
754
+ "page:header",
755
+ "page:section",
756
+ "page:sidebar",
757
+ "page:tabs",
758
+ "pagination",
759
+ "password",
760
+ "pivot",
761
+ "plugin-calendar:calendar-view",
762
+ "plugin-calendar:object-calendar",
763
+ "plugin-charts:bar-chart",
764
+ "plugin-charts:chart",
765
+ "plugin-charts:chart:bar",
766
+ "plugin-charts:object-chart",
767
+ "plugin-chatbot:chatbot",
768
+ "plugin-chatbot:chatbot-enhanced",
769
+ "plugin-chatbot:chatbot-floating",
770
+ "plugin-dashboard:dashboard",
771
+ "plugin-dashboard:metric",
772
+ "plugin-dashboard:metric-card",
773
+ "plugin-dashboard:object-data-table",
774
+ "plugin-dashboard:object-metric",
775
+ "plugin-dashboard:object-pivot",
776
+ "plugin-dashboard:pivot",
777
+ "plugin-designer:data-model-designer",
778
+ "plugin-designer:field-designer",
779
+ "plugin-designer:object-manager",
780
+ "plugin-designer:page-designer",
781
+ "plugin-designer:process-designer",
782
+ "plugin-designer:report-designer",
783
+ "plugin-detail:detail-section",
784
+ "plugin-detail:detail-view",
785
+ "plugin-editor:code-editor",
786
+ "plugin-form:embeddable-form",
787
+ "plugin-form:object-form",
788
+ "plugin-form:object-master-detail-form",
789
+ "plugin-gantt:object-gantt",
790
+ "plugin-grid:object-grid",
791
+ "plugin-kanban:object-kanban",
792
+ "plugin-list:list-view",
793
+ "plugin-list:object-gallery",
794
+ "plugin-map:object-map",
795
+ "plugin-markdown:markdown",
796
+ "plugin-report:report",
797
+ "plugin-report:report-viewer",
798
+ "plugin-timeline:object-timeline",
799
+ "plugin-timeline:timeline",
800
+ "plugin-tree:object-tree",
801
+ "plugin-view:object-view",
802
+ "plugin-view:view:simple",
803
+ "popover",
804
+ "pre",
805
+ "process-designer",
806
+ "progress",
807
+ "protocol-placeholder:ai:feedback",
808
+ "protocol-placeholder:ai:input",
809
+ "protocol-placeholder:ai:suggestion",
810
+ "protocol-placeholder:app:launcher",
811
+ "protocol-placeholder:field:address",
812
+ "protocol-placeholder:field:audio",
813
+ "protocol-placeholder:field:avatar",
814
+ "protocol-placeholder:field:boolean",
815
+ "protocol-placeholder:field:checkboxes",
816
+ "protocol-placeholder:field:code",
817
+ "protocol-placeholder:field:color",
818
+ "protocol-placeholder:field:currency",
819
+ "protocol-placeholder:field:date",
820
+ "protocol-placeholder:field:datetime",
821
+ "protocol-placeholder:field:duration",
822
+ "protocol-placeholder:field:email",
823
+ "protocol-placeholder:field:file",
824
+ "protocol-placeholder:field:html",
825
+ "protocol-placeholder:field:image",
826
+ "protocol-placeholder:field:json",
827
+ "protocol-placeholder:field:location",
828
+ "protocol-placeholder:field:lookup",
829
+ "protocol-placeholder:field:markdown",
830
+ "protocol-placeholder:field:master_detail",
831
+ "protocol-placeholder:field:multiselect",
832
+ "protocol-placeholder:field:number",
833
+ "protocol-placeholder:field:password",
834
+ "protocol-placeholder:field:percent",
835
+ "protocol-placeholder:field:phone",
836
+ "protocol-placeholder:field:progress",
837
+ "protocol-placeholder:field:qrcode",
838
+ "protocol-placeholder:field:radio",
839
+ "protocol-placeholder:field:rating",
840
+ "protocol-placeholder:field:select",
841
+ "protocol-placeholder:field:signature",
842
+ "protocol-placeholder:field:slider",
843
+ "protocol-placeholder:field:text",
844
+ "protocol-placeholder:field:textarea",
845
+ "protocol-placeholder:field:time",
846
+ "protocol-placeholder:field:tree",
847
+ "protocol-placeholder:field:url",
848
+ "protocol-placeholder:field:video",
849
+ "protocol-placeholder:global:notifications",
850
+ "protocol-placeholder:global:search",
851
+ "protocol-placeholder:nav:breadcrumb",
852
+ "protocol-placeholder:nav:menu",
853
+ "protocol-placeholder:page:accordion",
854
+ "protocol-placeholder:page:card",
855
+ "protocol-placeholder:page:footer",
856
+ "protocol-placeholder:page:header",
857
+ "protocol-placeholder:page:sidebar",
858
+ "protocol-placeholder:page:tabs",
859
+ "protocol-placeholder:record:activity",
860
+ "protocol-placeholder:record:chatter",
861
+ "protocol-placeholder:record:details",
862
+ "protocol-placeholder:record:highlights",
863
+ "protocol-placeholder:record:path",
864
+ "protocol-placeholder:record:related_list",
865
+ "protocol-placeholder:view:calendar",
866
+ "protocol-placeholder:view:drawer",
867
+ "protocol-placeholder:view:gallery",
868
+ "protocol-placeholder:view:gantt",
869
+ "protocol-placeholder:view:grid",
870
+ "protocol-placeholder:view:kanban",
871
+ "protocol-placeholder:view:map",
872
+ "protocol-placeholder:view:modal",
873
+ "protocol-placeholder:view:simple",
874
+ "protocol-placeholder:view:split",
875
+ "protocol-placeholder:view:tabbed",
876
+ "protocol-placeholder:view:timeline",
877
+ "protocol-placeholder:view:wizard",
878
+ "protocol-placeholder:widget:bar",
879
+ "protocol-placeholder:widget:funnel",
880
+ "protocol-placeholder:widget:heatmap",
881
+ "protocol-placeholder:widget:image",
882
+ "protocol-placeholder:widget:line",
883
+ "protocol-placeholder:widget:metric",
884
+ "protocol-placeholder:widget:pie",
885
+ "protocol-placeholder:widget:pivot",
886
+ "protocol-placeholder:widget:radar",
887
+ "protocol-placeholder:widget:scatter",
888
+ "protocol-placeholder:widget:table",
889
+ "protocol-placeholder:widget:text",
890
+ "q",
891
+ "radio-group",
892
+ "record",
893
+ "record:activity",
894
+ "record:alert",
895
+ "record:approvals",
896
+ "record:attachments",
897
+ "record:chatter",
898
+ "record:details",
899
+ "record:discussion",
900
+ "record:highlights",
901
+ "record:history",
902
+ "record:line_items",
903
+ "record:path",
904
+ "record:quick_actions",
905
+ "record:reference_rail",
906
+ "record:related_list",
907
+ "report",
908
+ "report-designer",
909
+ "report-viewer",
910
+ "resizable",
911
+ "scroll-area",
912
+ "section",
913
+ "select",
914
+ "separator",
915
+ "sheet",
916
+ "sidebar",
917
+ "skeleton",
918
+ "slider",
919
+ "small",
920
+ "sonner",
921
+ "sort-ui",
922
+ "span",
923
+ "spinner",
924
+ "stack",
925
+ "statistic",
926
+ "strong",
927
+ "sub",
928
+ "sup",
929
+ "switch",
930
+ "table",
931
+ "tabs",
932
+ "text",
933
+ "textarea",
934
+ "time",
935
+ "timeline",
936
+ "toast",
937
+ "toaster",
938
+ "toggle",
939
+ "toggle-group",
940
+ "tooltip",
941
+ "tree-view",
942
+ "u",
943
+ "ui:a",
944
+ "ui:abbr",
945
+ "ui:accordion",
946
+ "ui:address",
947
+ "ui:alert",
948
+ "ui:alert-dialog",
949
+ "ui:app",
950
+ "ui:article",
951
+ "ui:aside",
952
+ "ui:aspect-ratio",
953
+ "ui:avatar",
954
+ "ui:b",
955
+ "ui:badge",
956
+ "ui:blockquote",
957
+ "ui:box",
958
+ "ui:br",
959
+ "ui:breadcrumb",
960
+ "ui:button",
961
+ "ui:button-group",
962
+ "ui:calendar",
963
+ "ui:card",
964
+ "ui:carousel",
965
+ "ui:checkbox",
966
+ "ui:cite",
967
+ "ui:code",
968
+ "ui:collapsible",
969
+ "ui:combobox",
970
+ "ui:command",
971
+ "ui:container",
972
+ "ui:context-menu",
973
+ "ui:data-table",
974
+ "ui:date-picker",
975
+ "ui:dd",
976
+ "ui:del",
977
+ "ui:dialog",
978
+ "ui:div",
979
+ "ui:dl",
980
+ "ui:drawer",
981
+ "ui:dropdown-menu",
982
+ "ui:dt",
983
+ "ui:em",
984
+ "ui:email",
985
+ "ui:empty",
986
+ "ui:figcaption",
987
+ "ui:figure",
988
+ "ui:file-upload",
989
+ "ui:filter-builder",
990
+ "ui:flex",
991
+ "ui:footer",
992
+ "ui:grid",
993
+ "ui:h1",
994
+ "ui:h2",
995
+ "ui:h3",
996
+ "ui:h4",
997
+ "ui:h5",
998
+ "ui:h6",
999
+ "ui:header",
1000
+ "ui:header-bar",
1001
+ "ui:home",
1002
+ "ui:hover-card",
1003
+ "ui:hr",
1004
+ "ui:html",
1005
+ "ui:i",
1006
+ "ui:icon",
1007
+ "ui:image",
1008
+ "ui:img",
1009
+ "ui:input",
1010
+ "ui:input-otp",
1011
+ "ui:ins",
1012
+ "ui:kbd",
1013
+ "ui:label",
1014
+ "ui:li",
1015
+ "ui:list",
1016
+ "ui:loading",
1017
+ "ui:main",
1018
+ "ui:mark",
1019
+ "ui:menubar",
1020
+ "ui:nav",
1021
+ "ui:navigation-menu",
1022
+ "ui:ol",
1023
+ "ui:p",
1024
+ "ui:page",
1025
+ "ui:pagination",
1026
+ "ui:password",
1027
+ "ui:popover",
1028
+ "ui:pre",
1029
+ "ui:progress",
1030
+ "ui:q",
1031
+ "ui:radio-group",
1032
+ "ui:record",
1033
+ "ui:resizable",
1034
+ "ui:scroll-area",
1035
+ "ui:section",
1036
+ "ui:select",
1037
+ "ui:separator",
1038
+ "ui:sheet",
1039
+ "ui:sidebar",
1040
+ "ui:skeleton",
1041
+ "ui:slider",
1042
+ "ui:small",
1043
+ "ui:sonner",
1044
+ "ui:span",
1045
+ "ui:spinner",
1046
+ "ui:stack",
1047
+ "ui:statistic",
1048
+ "ui:strong",
1049
+ "ui:sub",
1050
+ "ui:sup",
1051
+ "ui:switch",
1052
+ "ui:table",
1053
+ "ui:tabs",
1054
+ "ui:text",
1055
+ "ui:textarea",
1056
+ "ui:time",
1057
+ "ui:toast",
1058
+ "ui:toaster",
1059
+ "ui:toggle",
1060
+ "ui:toggle-group",
1061
+ "ui:tooltip",
1062
+ "ui:tree-view",
1063
+ "ui:u",
1064
+ "ui:ul",
1065
+ "ui:utility",
1066
+ "ul",
1067
+ "utility",
1068
+ "view-switcher",
1069
+ "view:calendar",
1070
+ "view:chart",
1071
+ "view:detail",
1072
+ "view:drawer",
1073
+ "view:filter-ui",
1074
+ "view:form",
1075
+ "view:gallery",
1076
+ "view:gantt",
1077
+ "view:grid",
1078
+ "view:kanban",
1079
+ "view:list",
1080
+ "view:map",
1081
+ "view:modal",
1082
+ "view:simple",
1083
+ "view:sort-ui",
1084
+ "view:split",
1085
+ "view:tabbed",
1086
+ "view:timeline",
1087
+ "view:view-switcher",
1088
+ "view:wizard",
1089
+ "widget:bar",
1090
+ "widget:funnel",
1091
+ "widget:heatmap",
1092
+ "widget:image",
1093
+ "widget:line",
1094
+ "widget:metric",
1095
+ "widget:pie",
1096
+ "widget:pivot",
1097
+ "widget:radar",
1098
+ "widget:scatter",
1099
+ "widget:table",
1100
+ "widget:text"
1101
+ ];
1102
+ var KNOWN_SCHEMA_TYPE_SET = new Set(KNOWN_SCHEMA_TYPES);
1103
+ function isKnownSchemaType(type) {
1104
+ return KNOWN_SCHEMA_TYPE_SET.has(type);
1105
+ }
1106
+
1107
+ // src/utils/known-type-case-suggestion.ts
1108
+ function suggestKnownTypeByCase(type) {
1109
+ if (typeof type !== "string" || type === "") return void 0;
1110
+ const wanted = type.toLowerCase();
1111
+ for (const candidate of KNOWN_SCHEMA_TYPES) {
1112
+ if (candidate !== type && candidate.toLowerCase() === wanted) return candidate;
1113
+ }
1114
+ return void 0;
1115
+ }
1116
+ function didYouMeanClause(type) {
1117
+ const match = suggestKnownTypeByCase(type);
1118
+ return match === void 0 ? "" : ` \u2014 did you mean "${match}"?`;
1119
+ }
1120
+
1121
+ // src/utils/unbindable-text-expressions.ts
1122
+ import {
1123
+ EXPRESSION_BINDABLE_TEXT_KEYS,
1124
+ expressionBindableTextKeysFor
1125
+ } from "@objectstack/spec/ui";
1126
+ var EXPRESSION_PATTERN = /\$\{[^}]+\}/;
1127
+ var COMPOSITION_KEY = "children";
1128
+ var PAGE_DOCUMENT_ROOT_TYPE = "page";
1129
+ function isComponentNode(value) {
1130
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
1131
+ }
1132
+ function judge(node, path, into) {
1133
+ const evaluatedKeys = expressionBindableTextKeysFor(node.type);
1134
+ for (const key of EXPRESSION_BINDABLE_TEXT_KEYS) {
1135
+ const value = node[key];
1136
+ if (typeof value !== "string" || !EXPRESSION_PATTERN.test(value)) continue;
1137
+ if (evaluatedKeys.includes(key)) continue;
1138
+ into.push({
1139
+ severity: isKnownSchemaType(node.type) ? "refusal" : "warning",
1140
+ path: [...path, key],
1141
+ type: node.type,
1142
+ key,
1143
+ evaluatedKeys
1144
+ });
770
1145
  }
771
- if (!consolePath) {
772
- console.log(chalk9.yellow("\u26A0 Console app not found in workspace."));
773
- console.log(chalk9.dim(" Hint: Run this command from the monorepo root."));
774
- console.log(chalk9.dim(" Expected: apps/console/package.json\n"));
775
- process.exit(1);
1146
+ }
1147
+ function visit(node, path, isDocumentRoot, into) {
1148
+ if (!(isDocumentRoot && node.type === PAGE_DOCUMENT_ROOT_TYPE)) {
1149
+ judge(node, path, into);
1150
+ }
1151
+ const children = node[COMPOSITION_KEY];
1152
+ if (Array.isArray(children)) {
1153
+ children.forEach((child, index) => {
1154
+ if (isComponentNode(child)) visit(child, [...path, COMPOSITION_KEY, index], false, into);
1155
+ });
1156
+ } else if (isComponentNode(children)) {
1157
+ visit(children, [...path, COMPOSITION_KEY], false, into);
776
1158
  }
777
- console.log(chalk9.dim(` Console: ${consolePath}`));
778
- console.log(chalk9.dim(" Mode: MSW (in-browser mock server)\n"));
779
- const child = spawn("pnpm", ["run", "dev"], {
780
- cwd: consolePath,
781
- stdio: "inherit",
782
- env: { ...process.env, NODE_ENV: "development" },
783
- shell: true
784
- });
785
- child.on("error", (err) => {
786
- console.error(chalk9.red(`
787
- \u2717 Failed to start studio: ${err.message}`));
788
- process.exit(1);
789
- });
790
- child.on("exit", (code) => process.exit(code ?? 0));
1159
+ }
1160
+ function findUnbindableTextExpressions(document) {
1161
+ const found = [];
1162
+ if (isComponentNode(document)) visit(document, [], true, found);
1163
+ return found;
1164
+ }
1165
+ var inCode = (key) => "`" + key + "`";
1166
+ function describeUnbindableTextExpression(finding) {
1167
+ const at = formatIssuePath(finding.path);
1168
+ if (finding.severity === "warning") {
1169
+ return `at ${at}: "${finding.type}" is not a registered component, so the expression on ${inCode(finding.key)} is not refused; a custom renderer may evaluate it itself.`;
1170
+ }
1171
+ const evaluates = finding.evaluatedKeys.length > 0 ? `it evaluates only ${finding.evaluatedKeys.map(inCode).join(", ")}` : `it evaluates none of ${EXPRESSION_BINDABLE_TEXT_KEYS.map(inCode).join(", ")}`;
1172
+ return `at ${at}: "${finding.type}" never evaluates ${inCode(finding.key)} (${evaluates}), so the expression is never resolved: the user sees its literal text, or nothing.`;
1173
+ }
1174
+ function workingChannels(finding) {
1175
+ const channels = [
1176
+ ...finding.evaluatedKeys.map(inCode),
1177
+ inCode("content"),
1178
+ `${inCode(`properties.${finding.key}`)} (evaluated, then hoisted onto the node)`
1179
+ ];
1180
+ return `Channels SchemaRenderer evaluates: ${channels.join(", ")}; or resolve the value in the host before the schema reaches SchemaRenderer.`;
791
1181
  }
792
1182
 
793
1183
  // src/commands/check.ts
794
- import chalk10 from "chalk";
795
- import { globSync } from "glob";
796
- import { readFileSync as readFileSync2 } from "fs";
797
- import { join as join9 } from "path";
798
- async function check() {
799
- console.log(chalk10.bold("Object UI Schema Check"));
800
- const cwd = process.cwd();
1184
+ var OBJECTUI_STRUCTURAL_KEYS = [
1185
+ // Composition — the `children`/`body`-class keys the ruling names.
1186
+ // `body` is the RETIRED spelling, kept so old files are still identified and
1187
+ // then refused — see the docblock above (objectui#6771).
1188
+ "body",
1189
+ "children",
1190
+ // Presentation.
1191
+ "className",
1192
+ "style",
1193
+ "placeholder",
1194
+ // Conditional rendering and interaction state.
1195
+ "visible",
1196
+ "visibleWhen",
1197
+ "visibleOn",
1198
+ "hidden",
1199
+ "hiddenOn",
1200
+ "disabled",
1201
+ "disabledOn",
1202
+ // Test and accessibility handles.
1203
+ "testId",
1204
+ "ariaLabel"
1205
+ ];
1206
+ function recogniseObjectUiSchemaFile(content) {
1207
+ if (OBJECTUI_STRUCTURAL_KEYS.some((key) => key in content)) {
1208
+ return { recognised: true, validated: false };
1209
+ }
1210
+ const result = safeValidateSchema(content);
1211
+ return result.success ? { recognised: true, validated: true } : { recognised: false, issues: result.error.issues };
1212
+ }
1213
+ function describeFirstIssue(issues) {
1214
+ const [first] = issues;
1215
+ if (first === void 0) return void 0;
1216
+ const lead = issues.length === 1 ? "Issue" : `First of ${issues.length} issues`;
1217
+ return `${lead} at ${formatIssuePath(first.path)}: ${first.message}`;
1218
+ }
1219
+ function closingLine(tally) {
1220
+ return `ObjectUI schema files: ${tally.validated} validated, ${tally.notValidated} recognised but not validated, ${tally.didNotValidate} did not validate. This sweep is advisory; run \`objectui validate <file>\` for a verdict.`;
1221
+ }
1222
+ function isUnvalidatedCandidate(type) {
1223
+ return isKnownSchemaType(type);
1224
+ }
1225
+ function describeParseError(text, error) {
1226
+ const upTo = text.slice(0, error.offset);
1227
+ const line = upTo.split("\n").length;
1228
+ const column = error.offset - (upTo.lastIndexOf("\n") + 1) + 1;
1229
+ return `${printParseErrorCode(error.error)} at line ${line} column ${column}`;
1230
+ }
1231
+ async function check(cwd = process.cwd()) {
1232
+ console.log(chalk6.bold("Object UI Schema Check"));
801
1233
  const files = globSync("**/*.{json,yaml,yml}", {
802
1234
  cwd,
803
- ignore: ["node_modules/**", "dist/**", ".git/**"]
1235
+ // `glob` matches `ignore` patterns against the path relative to `cwd`, so
1236
+ // an unanchored `dist/**`/`node_modules/**` excludes only a directory of
1237
+ // that name at the scan root — every nested `packages/*/dist/`,
1238
+ // `examples/*/dist/`, `apps/*/dist/` (and their `node_modules/`) is still
1239
+ // scanned. `**/` anchors the match at any depth (objectui#6320): measured
1240
+ // on this repository, a built tree without it reports roughly double the
1241
+ // files of a clean checkout, almost all of it re-reading the author's own
1242
+ // schemas from build output.
1243
+ ignore: ["**/node_modules/**", "**/dist/**", ".git/**"]
804
1244
  });
805
1245
  console.log(`Analyzing ${files.length} files...`);
806
1246
  let errors = 0;
1247
+ let skipped = 0;
1248
+ const unvalidatedCandidates = [];
1249
+ let validated = 0;
1250
+ let notValidated = 0;
807
1251
  for (const file of files) {
808
1252
  try {
809
1253
  if (file.endsWith(".json")) {
810
- const content = JSON.parse(readFileSync2(join9(cwd, file), "utf-8"));
1254
+ const text = readFileSync2(join6(cwd, file), "utf-8");
1255
+ const parseErrors = [];
1256
+ const content = parseJsonc(text, parseErrors, { allowTrailingComma: true });
1257
+ if (parseErrors.length > 0) {
1258
+ const [first] = parseErrors;
1259
+ console.log(
1260
+ chalk6.red(`x Invalid JSON in ${file}: ${describeParseError(text, first)}`)
1261
+ );
1262
+ errors++;
1263
+ continue;
1264
+ }
811
1265
  if (content && typeof content === "object" && content.type) {
812
- const knownTypes = [
813
- "page",
814
- "form",
815
- "grid",
816
- "crud",
817
- "kanban",
818
- "calendar",
819
- "dashboard",
820
- "chart",
821
- "detail",
822
- "list",
823
- "timeline",
824
- "gantt",
825
- "map",
826
- "gallery",
827
- "object-view",
828
- "detail-view",
829
- "object-chart"
830
- ];
831
- if (typeof content.type === "string" && !knownTypes.includes(content.type)) {
832
- console.log(chalk10.yellow(`\u26A0\uFE0F Unknown schema type "${content.type}" in ${file}`));
1266
+ if (typeof content.type === "string") {
1267
+ const recognition = recogniseObjectUiSchemaFile(
1268
+ content
1269
+ );
1270
+ if (!recognition.recognised) {
1271
+ if (isUnvalidatedCandidate(content.type)) {
1272
+ unvalidatedCandidates.push({
1273
+ file,
1274
+ type: content.type,
1275
+ issues: recognition.issues
1276
+ });
1277
+ } else {
1278
+ skipped++;
1279
+ }
1280
+ continue;
1281
+ }
1282
+ if (recognition.validated) {
1283
+ validated++;
1284
+ } else {
1285
+ notValidated++;
1286
+ }
1287
+ if (!isKnownSchemaType(content.type)) {
1288
+ console.log(
1289
+ chalk6.yellow(
1290
+ `\u26A0\uFE0F Unknown schema type "${content.type}" in ${file}` + didYouMeanClause(content.type)
1291
+ )
1292
+ );
1293
+ }
1294
+ for (const finding of findUnbindableTextExpressions(content)) {
1295
+ const line = `${file} ${describeUnbindableTextExpression(finding)}`;
1296
+ if (finding.severity === "refusal") {
1297
+ console.log(chalk6.red(`x Unevaluated expression in ${line}`));
1298
+ console.log(chalk6.dim(` ${workingChannels(finding)}`));
1299
+ errors++;
1300
+ } else {
1301
+ console.log(chalk6.yellow(`\u26A0\uFE0F Expression not judged in ${line}`));
1302
+ }
1303
+ }
833
1304
  }
834
1305
  }
835
1306
  }
836
1307
  } catch (e) {
837
- console.log(chalk10.red(`x Invalid JSON in ${file}: ${e.message}`));
1308
+ console.log(chalk6.red(`x Invalid JSON in ${file}: ${e.message}`));
838
1309
  errors++;
839
1310
  }
840
1311
  }
1312
+ if (unvalidatedCandidates.length > 0) {
1313
+ const n = unvalidatedCandidates.length;
1314
+ console.log(
1315
+ chalk6.yellow(
1316
+ `\u26A0\uFE0F ${n} file${n === 1 ? "" : "s"} carr${n === 1 ? "ies" : "y"} a registered ObjectUI component type but did not validate as an ObjectUI schema:`
1317
+ )
1318
+ );
1319
+ for (const { file, type, issues } of unvalidatedCandidates) {
1320
+ console.log(chalk6.yellow(` ${file} (type "${type}")`));
1321
+ const firstIssue = describeFirstIssue(issues);
1322
+ if (firstIssue !== void 0) {
1323
+ console.log(chalk6.yellow(` ${firstIssue}`));
1324
+ }
1325
+ }
1326
+ console.log(
1327
+ chalk6.dim(" Run `objectui validate <file>` on any of them for the full diagnosis.")
1328
+ );
1329
+ console.log(
1330
+ chalk6.dim(
1331
+ " Either the document is off-spec, or its component type is not modelled by @object-ui/types."
1332
+ )
1333
+ );
1334
+ console.log(
1335
+ chalk6.dim(
1336
+ " These are NOT counted as skipped: a schema that fails validation is a broken schema, not a foreign file."
1337
+ )
1338
+ );
1339
+ }
1340
+ if (skipped > 0) {
1341
+ console.log(
1342
+ chalk6.dim(
1343
+ `Skipped ${skipped} file${skipped === 1 ? "" : "s"} with a root "type" that no ObjectUI recogniser admitted.`
1344
+ )
1345
+ );
1346
+ console.log(
1347
+ chalk6.dim(
1348
+ ` A file is checked when its root carries an ObjectUI structural key: ${OBJECTUI_STRUCTURAL_KEYS.join(", ")}.`
1349
+ )
1350
+ );
1351
+ console.log(
1352
+ chalk6.dim(
1353
+ " It is also checked when the whole document validates as an ObjectUI component schema."
1354
+ )
1355
+ );
1356
+ }
841
1357
  if (errors === 0) {
842
- console.log(chalk10.green("\u2713 All checks passed"));
1358
+ console.log(
1359
+ closingLine({
1360
+ validated,
1361
+ notValidated,
1362
+ didNotValidate: unvalidatedCandidates.length
1363
+ })
1364
+ );
843
1365
  } else {
844
- console.log(chalk10.red(`Found ${errors} errors`));
1366
+ console.log(chalk6.red(`Found ${errors} errors`));
845
1367
  process.exit(1);
846
1368
  }
847
1369
  }
848
1370
 
849
1371
  // src/commands/validate.ts
850
- import chalk11 from "chalk";
851
- import { readFileSync as readFileSync3, existsSync as existsSync9 } from "fs";
852
- import { resolve as resolve5 } from "path";
1372
+ import chalk7 from "chalk";
1373
+ import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
1374
+ import { resolve as resolve3 } from "path";
853
1375
  import { load as loadYaml } from "js-yaml";
854
- import { safeValidateSchema } from "@object-ui/types/zod";
1376
+ import { safeValidateSchema as safeValidateSchema2 } from "@object-ui/types/zod";
855
1377
 
856
1378
  // src/utils/spec-vocabulary-hint.ts
857
1379
  function findSpecVocabularyFormFields(node, path = "") {
@@ -882,53 +1404,185 @@ function findSpecVocabularyFormFields(node, path = "") {
882
1404
  return findings;
883
1405
  }
884
1406
 
1407
+ // src/utils/union-arm-diagnostics.ts
1408
+ var MAX_UNION_ARMS_REPORTED = 5;
1409
+ function editDistance(a, b) {
1410
+ if (a === b) return 0;
1411
+ if (a.length === 0) return b.length;
1412
+ if (b.length === 0) return a.length;
1413
+ let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
1414
+ for (let i = 1; i <= a.length; i += 1) {
1415
+ const current = [i];
1416
+ for (let j = 1; j <= b.length; j += 1) {
1417
+ current[j] = Math.min(
1418
+ previous[j] + 1,
1419
+ current[j - 1] + 1,
1420
+ previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
1421
+ );
1422
+ }
1423
+ previous = current;
1424
+ }
1425
+ return previous[b.length];
1426
+ }
1427
+ function nearestArmNames(authoredType, armNames) {
1428
+ const unique = [...new Set(armNames)].sort();
1429
+ if (typeof authoredType !== "string" || authoredType === "") {
1430
+ return { candidates: [], total: unique.length };
1431
+ }
1432
+ const ordered = [...unique].sort((x, y) => {
1433
+ const d = editDistance(authoredType, x) - editDistance(authoredType, y);
1434
+ return d !== 0 ? d : x.localeCompare(y);
1435
+ });
1436
+ return { candidates: ordered.slice(0, MAX_UNION_ARMS_REPORTED), total: unique.length };
1437
+ }
1438
+ function authoredTypeAt(document, path) {
1439
+ let node = document;
1440
+ for (const key of path) {
1441
+ if (node === null || typeof node !== "object") return void 0;
1442
+ node = node[key];
1443
+ }
1444
+ if (node === null || typeof node !== "object") return void 0;
1445
+ const type = node.type;
1446
+ return typeof type === "string" ? type : void 0;
1447
+ }
1448
+ function isUnion(issue) {
1449
+ return issue.code === "invalid_union" && Array.isArray(issue.errors);
1450
+ }
1451
+ var NO_MATCHING_DISCRIMINATOR = "No matching discriminator";
1452
+ function noArmAccepts(node, literals, document) {
1453
+ const authoredType = authoredTypeAt(document, node);
1454
+ const { candidates, total } = nearestArmNames(authoredType, literals);
1455
+ return {
1456
+ kind: "note",
1457
+ path: [...node],
1458
+ ...authoredType === void 0 ? {} : { authoredType },
1459
+ candidates,
1460
+ totalArmNames: total
1461
+ };
1462
+ }
1463
+ function noMatchingDiscriminator(issue, document) {
1464
+ if (issue.code !== "invalid_union") return void 0;
1465
+ if (issue.note !== NO_MATCHING_DISCRIMINATOR) return void 0;
1466
+ if (!Array.isArray(issue.options)) return void 0;
1467
+ if ((issue.errors ?? []).length > 0) return void 0;
1468
+ const path = issue.path ?? [];
1469
+ if (path[path.length - 1] !== "type") return void 0;
1470
+ if (issue.discriminator !== void 0 && issue.discriminator !== "type") return void 0;
1471
+ const literals = issue.options.filter((v) => typeof v === "string");
1472
+ if (literals.length === 0) return void 0;
1473
+ return noArmAccepts(path.slice(0, -1), literals, document);
1474
+ }
1475
+ function isAtTypeKey(issue) {
1476
+ const path = issue.path ?? [];
1477
+ return path.length === 1 && path[0] === "type";
1478
+ }
1479
+ function declaredTypeLiterals(armIssues) {
1480
+ const literals = [];
1481
+ let declares = false;
1482
+ for (const issue of armIssues) {
1483
+ if (isAtTypeKey(issue) && issue.code === "invalid_value" && Array.isArray(issue.values)) {
1484
+ declares = true;
1485
+ literals.push(...issue.values.filter((v) => typeof v === "string"));
1486
+ continue;
1487
+ }
1488
+ if (isAtTypeKey(issue) && isUnion(issue) && Array.isArray(issue.options)) {
1489
+ declares = true;
1490
+ literals.push(...issue.options.filter((v) => typeof v === "string"));
1491
+ continue;
1492
+ }
1493
+ if ((issue.path ?? []).length === 0 && isUnion(issue)) {
1494
+ const nested = (issue.errors ?? []).map((sub) => declaredTypeLiterals(sub));
1495
+ if (nested.length > 0 && nested.every((n) => n !== void 0)) {
1496
+ declares = true;
1497
+ for (const n of nested) literals.push(...n);
1498
+ }
1499
+ }
1500
+ }
1501
+ return declares ? literals : void 0;
1502
+ }
1503
+ function explain(unionIssue, prefix, document) {
1504
+ const arms = unionIssue.errors ?? [];
1505
+ const declared = arms.map((arm) => declaredTypeLiterals(arm));
1506
+ const declaringArms = declared.filter((d) => d !== void 0);
1507
+ const acceptingIndexes = declared.map((d, i) => d === void 0 ? i : -1).filter((i) => i >= 0);
1508
+ const expand = (armIssues, arm) => armIssues.flatMap((issue) => {
1509
+ const absolute = [...prefix, ...issue.path ?? []];
1510
+ const discriminated = noMatchingDiscriminator({ ...issue, path: absolute }, document);
1511
+ if (discriminated) return [discriminated];
1512
+ if (isUnion(issue)) return explain(issue, absolute, document);
1513
+ return [
1514
+ {
1515
+ kind: "issue",
1516
+ path: absolute,
1517
+ message: issue.message ?? "",
1518
+ code: issue.code,
1519
+ ...arm === void 0 ? {} : { arm }
1520
+ }
1521
+ ];
1522
+ });
1523
+ if (declaringArms.length > 0 && acceptingIndexes.length === 1) {
1524
+ return expand(arms[acceptingIndexes[0]]);
1525
+ }
1526
+ if (declaringArms.length > 0 && acceptingIndexes.length === 0) {
1527
+ return [noArmAccepts(prefix, declaringArms.flat(), document)];
1528
+ }
1529
+ return arms.slice(0, MAX_UNION_ARMS_REPORTED).flatMap((arm, i) => expand(arm, `${i + 1}/${arms.length}`));
1530
+ }
1531
+ function explainUnionIssue(issue, document) {
1532
+ const discriminated = noMatchingDiscriminator(issue, document);
1533
+ if (discriminated) return [discriminated];
1534
+ if (!isUnion(issue)) return [];
1535
+ return explain(issue, issue.path ?? [], document);
1536
+ }
1537
+
885
1538
  // src/commands/validate.ts
886
1539
  async function validate(schemaPath) {
887
- console.log(chalk11.blue("\u{1F50D} ObjectUI Schema Validator\n"));
888
- const resolvedPath = resolve5(process.cwd(), schemaPath);
889
- if (!existsSync9(resolvedPath)) {
890
- console.error(chalk11.red(`\u2717 Error: Schema file not found: ${schemaPath}`));
1540
+ console.log(chalk7.blue("\u{1F50D} ObjectUI Schema Validator\n"));
1541
+ const resolvedPath = resolve3(process.cwd(), schemaPath);
1542
+ if (!existsSync5(resolvedPath)) {
1543
+ console.error(chalk7.red(`\u2717 Error: Schema file not found: ${schemaPath}`));
891
1544
  process.exit(1);
892
1545
  }
893
1546
  try {
894
1547
  const fileContent = readFileSync3(resolvedPath, "utf-8");
895
1548
  let schema;
896
1549
  if (schemaPath.endsWith(".yaml") || schemaPath.endsWith(".yml")) {
897
- console.log(chalk11.gray(`Reading YAML schema from: ${schemaPath}`));
1550
+ console.log(chalk7.gray(`Reading YAML schema from: ${schemaPath}`));
898
1551
  schema = loadYaml(fileContent);
899
1552
  } else if (schemaPath.endsWith(".json")) {
900
- console.log(chalk11.gray(`Reading JSON schema from: ${schemaPath}`));
1553
+ console.log(chalk7.gray(`Reading JSON schema from: ${schemaPath}`));
901
1554
  schema = JSON.parse(fileContent);
902
1555
  } else {
903
1556
  try {
904
1557
  schema = JSON.parse(fileContent);
905
- console.log(chalk11.gray(`Reading schema from: ${schemaPath} (detected as JSON)`));
1558
+ console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as JSON)`));
906
1559
  } catch {
907
1560
  schema = loadYaml(fileContent);
908
- console.log(chalk11.gray(`Reading schema from: ${schemaPath} (detected as YAML)`));
1561
+ console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as YAML)`));
909
1562
  }
910
1563
  }
911
- console.log(chalk11.gray("Validating schema...\n"));
912
- const result = safeValidateSchema(schema);
1564
+ console.log(chalk7.gray("Validating schema...\n"));
1565
+ const result = safeValidateSchema2(schema);
913
1566
  if (result.success) {
914
- console.log(chalk11.green("\u2713 Schema is valid!\n"));
1567
+ console.log(chalk7.green("\u2713 Schema is valid!\n"));
915
1568
  const data = result.data;
916
- console.log(chalk11.bold("Schema Information:"));
917
- console.log(chalk11.gray(" Type:"), data.type || "unknown");
1569
+ console.log(chalk7.bold("Schema Information:"));
1570
+ console.log(chalk7.gray(" Type:"), data.type || "unknown");
918
1571
  if (data.id) {
919
- console.log(chalk11.gray(" ID:"), data.id);
1572
+ console.log(chalk7.gray(" ID:"), data.id);
920
1573
  }
921
- if (data.label || data.title) {
922
- console.log(chalk11.gray(" Label:"), data.label || data.title);
1574
+ const title = "title" in data ? data.title : void 0;
1575
+ if (data.label || title) {
1576
+ console.log(chalk7.gray(" Label:"), data.label || title);
923
1577
  }
924
1578
  if (data.children && Array.isArray(data.children)) {
925
- console.log(chalk11.gray(" Children:"), data.children.length);
1579
+ console.log(chalk7.gray(" Children:"), data.children.length);
926
1580
  }
927
1581
  const mixed = findSpecVocabularyFormFields(schema).filter((f) => f.mixedName);
928
1582
  if (mixed.length > 0) {
929
- console.log(chalk11.yellow("\nWarnings:"));
1583
+ console.log(chalk7.yellow("\nWarnings:"));
930
1584
  for (const m of mixed) {
931
- console.log(chalk11.yellow(
1585
+ console.log(chalk7.yellow(
932
1586
  ` ${m.path}: '${m.mixedName}' also carries { field: '${m.field}' } \u2014 mixed form-field vocabularies. The renderer ignores \`field\` here; drop one of the two.`
933
1587
  ));
934
1588
  }
@@ -936,27 +1590,56 @@ async function validate(schemaPath) {
936
1590
  console.log("");
937
1591
  process.exit(0);
938
1592
  } else {
939
- console.error(chalk11.red("\u2717 Schema validation failed!\n"));
940
- console.error(chalk11.bold("Validation Errors:"));
1593
+ console.error(chalk7.red("\u2717 Schema validation failed!\n"));
1594
+ console.error(chalk7.bold("Validation Errors:"));
941
1595
  result.error.issues.forEach((issue, index) => {
942
- console.error(chalk11.red(`
1596
+ console.error(chalk7.red(`
943
1597
  ${index + 1}. ${issue.message}`));
944
- if (issue.path && issue.path.length > 0) {
945
- console.error(chalk11.gray(` Path: ${issue.path.join(" \u2192 ")}`));
946
- }
1598
+ console.error(chalk7.gray(` Path: ${formatIssuePath(issue.path)}`));
947
1599
  if (issue.code) {
948
- console.error(chalk11.gray(` Code: ${issue.code}`));
1600
+ console.error(chalk7.gray(` Code: ${issue.code}`));
949
1601
  }
1602
+ explainUnionIssue(issue, schema).forEach((line, sub) => {
1603
+ const where = formatIssuePath(line.path);
1604
+ if (line.kind === "note") {
1605
+ const at = line.path.length > 0 ? ` at ${where}` : "";
1606
+ if (line.authoredType === void 0) {
1607
+ console.error(
1608
+ chalk7.yellow(
1609
+ ` No \`type\` is declared${at}, so none of the ${line.totalArmNames} accepted component types can be selected.`
1610
+ )
1611
+ );
1612
+ return;
1613
+ }
1614
+ console.error(
1615
+ chalk7.yellow(` No arm accepts type "${line.authoredType}"${at}.`)
1616
+ );
1617
+ if (line.candidates.length > 0) {
1618
+ console.error(
1619
+ chalk7.gray(
1620
+ ` Nearest of the ${line.totalArmNames} accepted types: ` + line.candidates.join(", ")
1621
+ )
1622
+ );
1623
+ }
1624
+ return;
1625
+ }
1626
+ const arm = line.arm === void 0 ? "" : `[arm ${line.arm}] `;
1627
+ console.error(chalk7.red(` ${index + 1}.${sub + 1} ${arm}${line.message}`));
1628
+ console.error(chalk7.gray(` Path: ${where}`));
1629
+ if (line.code) {
1630
+ console.error(chalk7.gray(` Code: ${line.code}`));
1631
+ }
1632
+ });
950
1633
  });
951
1634
  const specShaped = findSpecVocabularyFormFields(schema).filter((f) => !f.mixedName);
952
1635
  if (specShaped.length > 0) {
953
- console.error(chalk11.bold("\nLikely cause \u2014 spec form-view vocabulary in a standalone form:"));
1636
+ console.error(chalk7.bold("\nLikely cause \u2014 spec form-view vocabulary in a standalone form:"));
954
1637
  for (const s of specShaped) {
955
- console.error(chalk11.yellow(
1638
+ console.error(chalk7.yellow(
956
1639
  ` ${s.path}: { field: '${s.field}' } is the spec form-VIEW shape (an object-field reference).`
957
1640
  ));
958
1641
  }
959
- console.error(chalk11.yellow(
1642
+ console.error(chalk7.yellow(
960
1643
  ` A standalone \`type: 'form'\` component uses { name: \u2026 } (the form data path).
961
1644
  Fix: author the field with \`name\` and its own type/label \u2014 or, to reference object fields
962
1645
  with the spec shape, use an object-bound form (\`type: 'object-form'\` with objectName +
@@ -967,62 +1650,29 @@ ${index + 1}. ${issue.message}`));
967
1650
  process.exit(1);
968
1651
  }
969
1652
  } catch (error) {
970
- console.error(chalk11.red("\n\u2717 Error reading or parsing schema file:"));
971
- console.error(chalk11.red(error.message));
1653
+ console.error(chalk7.red("\n\u2717 Error reading or parsing schema file:"));
1654
+ console.error(chalk7.red(error.message));
972
1655
  console.error("");
973
1656
  process.exit(1);
974
1657
  }
975
1658
  }
976
1659
 
977
- // src/commands/create-plugin.ts
978
- import chalk12 from "chalk";
979
- import { spawn as spawn2 } from "child_process";
980
- import { resolve as resolve6, dirname as dirname4 } from "path";
981
- import { fileURLToPath as fileURLToPath2 } from "url";
982
- var __filename = fileURLToPath2(import.meta.url);
983
- var __dirname = dirname4(__filename);
984
- async function createPlugin(pluginName) {
985
- console.log(chalk12.blue("\u{1F680} Creating ObjectUI plugin...\n"));
986
- const createPluginScript = resolve6(
987
- __dirname,
988
- "../../../create-plugin/dist/index.js"
989
- );
990
- return new Promise((resolve8, reject) => {
991
- const child = spawn2("node", [createPluginScript, pluginName], {
992
- stdio: "inherit",
993
- shell: true
994
- });
995
- child.on("error", (error) => {
996
- console.error(chalk12.red("Failed to create plugin:"), error);
997
- reject(error);
998
- });
999
- child.on("exit", (code) => {
1000
- if (code === 0) {
1001
- resolve8();
1002
- } else {
1003
- reject(new Error(`create-plugin exited with code ${code}`));
1004
- }
1005
- });
1006
- });
1007
- }
1008
-
1009
1660
  // src/commands/analyze.ts
1010
- import chalk13 from "chalk";
1011
- import { existsSync as existsSync10, statSync as statSync2, readdirSync } from "fs";
1012
- import { resolve as resolve7, join as join10, extname } from "path";
1661
+ import chalk8 from "chalk";
1662
+ import { existsSync as existsSync6, statSync, readdirSync } from "fs";
1663
+ import { resolve as resolve4, join as join7, extname } from "path";
1013
1664
  async function analyzeBundleSize() {
1014
- console.log(chalk13.bold("\n\u{1F4E6} Bundle Size Analysis\n"));
1015
- const distDir = resolve7(process.cwd(), "dist");
1016
- if (!existsSync10(distDir)) {
1017
- console.log(chalk13.yellow("\u26A0 No dist directory found. Run build first."));
1018
- return;
1665
+ console.log(chalk8.bold("\n\u{1F4E6} Bundle Size Analysis\n"));
1666
+ const distDir = resolve4(process.cwd(), "dist");
1667
+ if (!existsSync6(distDir)) {
1668
+ throw new Error("No dist/ directory to analyze. Run `objectui build` first.");
1019
1669
  }
1020
1670
  const files = [];
1021
1671
  function scanDirectory(dir) {
1022
1672
  const items = readdirSync(dir);
1023
1673
  for (const item of items) {
1024
- const fullPath = join10(dir, item);
1025
- const stat = statSync2(fullPath);
1674
+ const fullPath = join7(dir, item);
1675
+ const stat = statSync(fullPath);
1026
1676
  if (stat.isDirectory()) {
1027
1677
  scanDirectory(fullPath);
1028
1678
  } else {
@@ -1040,48 +1690,34 @@ async function analyzeBundleSize() {
1040
1690
  const cssFiles = files.filter((f) => extname(f.path) === ".css");
1041
1691
  const jsSize = jsFiles.reduce((sum, file) => sum + file.size, 0);
1042
1692
  const cssSize = cssFiles.reduce((sum, file) => sum + file.size, 0);
1043
- console.log(chalk13.bold("Summary:"));
1044
- console.log(chalk13.gray(" Total Size:"), formatBytes(totalSize));
1045
- console.log(chalk13.gray(" JavaScript:"), formatBytes(jsSize), chalk13.dim(`(${jsFiles.length} files)`));
1046
- console.log(chalk13.gray(" CSS: "), formatBytes(cssSize), chalk13.dim(`(${cssFiles.length} files)`));
1047
- console.log(chalk13.gray(" Other: "), formatBytes(totalSize - jsSize - cssSize));
1048
- console.log(chalk13.bold("\nLargest Files:"));
1693
+ console.log(chalk8.bold("Summary:"));
1694
+ console.log(chalk8.gray(" Total Size:"), formatBytes(totalSize));
1695
+ console.log(chalk8.gray(" JavaScript:"), formatBytes(jsSize), chalk8.dim(`(${jsFiles.length} files)`));
1696
+ console.log(chalk8.gray(" CSS: "), formatBytes(cssSize), chalk8.dim(`(${cssFiles.length} files)`));
1697
+ console.log(chalk8.gray(" Other: "), formatBytes(totalSize - jsSize - cssSize));
1698
+ console.log(chalk8.bold("\nLargest Files:"));
1049
1699
  files.slice(0, 10).forEach((file) => {
1050
1700
  const sizeStr = formatBytes(file.size).padStart(10);
1051
- console.log(chalk13.gray(` ${sizeStr}`), file.path);
1701
+ console.log(chalk8.gray(` ${sizeStr}`), file.path);
1052
1702
  });
1053
- console.log(chalk13.bold("\n\u{1F4A1} Recommendations:"));
1703
+ console.log(chalk8.bold("\n\u{1F4A1} Recommendations:"));
1054
1704
  if (totalSize > 1024 * 1024) {
1055
- console.log(chalk13.yellow(" \u26A0 Total bundle size is large (> 1MB)"));
1056
- console.log(chalk13.gray(" Consider code splitting or lazy loading"));
1705
+ console.log(chalk8.yellow(" \u26A0 Total bundle size is large (> 1MB)"));
1706
+ console.log(chalk8.gray(" Consider code splitting or lazy loading"));
1057
1707
  }
1058
1708
  if (jsSize > 500 * 1024) {
1059
- console.log(chalk13.yellow(" \u26A0 JavaScript bundle is large (> 500KB)"));
1060
- console.log(chalk13.gray(" Consider:"));
1061
- console.log(chalk13.gray(" - Tree shaking unused code"));
1062
- console.log(chalk13.gray(" - Lazy loading components"));
1063
- console.log(chalk13.gray(" - Using dynamic imports"));
1709
+ console.log(chalk8.yellow(" \u26A0 JavaScript bundle is large (> 500KB)"));
1710
+ console.log(chalk8.gray(" Consider:"));
1711
+ console.log(chalk8.gray(" - Tree shaking unused code"));
1712
+ console.log(chalk8.gray(" - Lazy loading components"));
1713
+ console.log(chalk8.gray(" - Using dynamic imports"));
1064
1714
  }
1065
1715
  if (files.length > 100) {
1066
- console.log(chalk13.yellow(` \u26A0 Large number of files (${files.length})`));
1067
- console.log(chalk13.gray(" Consider bundling or combining files"));
1716
+ console.log(chalk8.yellow(` \u26A0 Large number of files (${files.length})`));
1717
+ console.log(chalk8.gray(" Consider bundling or combining files"));
1068
1718
  }
1069
1719
  console.log("");
1070
1720
  }
1071
- async function analyzeRenderPerformance() {
1072
- console.log(chalk13.bold("\n\u26A1 Render Performance Analysis\n"));
1073
- console.log(chalk13.gray("Performance analysis features:"));
1074
- console.log(chalk13.gray(" \u2713 Expression caching enabled"));
1075
- console.log(chalk13.gray(" \u2713 Component memoization available"));
1076
- console.log(chalk13.gray(" \u2713 Virtual scrolling support for large lists"));
1077
- console.log(chalk13.bold("\n\u{1F4A1} Performance Tips:"));
1078
- console.log(chalk13.gray(" \u2022 Use virtual scrolling for lists > 100 items"));
1079
- console.log(chalk13.gray(" \u2022 Cache frequently evaluated expressions"));
1080
- console.log(chalk13.gray(" \u2022 Use React.memo for expensive components"));
1081
- console.log(chalk13.gray(" \u2022 Implement pagination for large datasets"));
1082
- console.log(chalk13.gray(" \u2022 Use code splitting for large apps"));
1083
- console.log("");
1084
- }
1085
1721
  function formatBytes(bytes) {
1086
1722
  if (bytes === 0) return "0 B";
1087
1723
  const k = 1024;
@@ -1089,50 +1725,44 @@ function formatBytes(bytes) {
1089
1725
  const i = Math.floor(Math.log(bytes) / Math.log(k));
1090
1726
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
1091
1727
  }
1092
- async function analyze(options = {}) {
1093
- console.log(chalk13.blue("\u{1F50D} ObjectUI Performance Analyzer\n"));
1094
- const runAll = !options.bundleSize && !options.renderPerformance;
1095
- if (options.bundleSize || runAll) {
1096
- await analyzeBundleSize();
1097
- }
1098
- if (options.renderPerformance || runAll) {
1099
- await analyzeRenderPerformance();
1100
- }
1101
- console.log(chalk13.green("\u2713 Analysis complete!\n"));
1728
+ async function analyze() {
1729
+ console.log(chalk8.blue("\u{1F50D} ObjectUI Performance Analyzer\n"));
1730
+ await analyzeBundleSize();
1731
+ console.log(chalk8.green("\u2713 Analysis complete!\n"));
1102
1732
  }
1103
1733
 
1104
1734
  // src/cli.ts
1105
1735
  import { readFileSync as readFileSync4 } from "fs";
1106
- import { fileURLToPath as fileURLToPath3 } from "url";
1107
- import { dirname as dirname5, join as join11 } from "path";
1108
- var __filename2 = fileURLToPath3(import.meta.url);
1109
- var __dirname2 = dirname5(__filename2);
1736
+ import { fileURLToPath } from "url";
1737
+ import { dirname as dirname2, join as join8 } from "path";
1738
+ var __filename = fileURLToPath(import.meta.url);
1739
+ var __dirname = dirname2(__filename);
1110
1740
  var packageJson = JSON.parse(
1111
- readFileSync4(join11(__dirname2, "../package.json"), "utf-8")
1741
+ readFileSync4(join8(__dirname, "../package.json"), "utf-8")
1112
1742
  );
1113
1743
  var program = new Command();
1114
1744
  program.name("objectui").description("CLI tool for Object UI - Build applications from JSON schemas").version(packageJson.version);
1115
- program.command("serve").description("Start a development server with your JSON/YAML schema").argument("[schema]", "Path to JSON/YAML schema file", "app.json").option("-p, --port <port>", "Port to run the server on", "3000").option("-h, --host <host>", "Host to bind the server to", "localhost").action(async (schema, options) => {
1745
+ program.command("serve").description("Start a development server with your JSON/YAML schema").argument("[schema]", "Path to JSON/YAML schema file, or a project / pages directory", "app.json").option("-p, --port <port>", "Port to run the server on", "3000").option("-h, --host <host>", "Host to bind the server to", "localhost").option("--no-open", "Do not open browser automatically").action(async (schema, options) => {
1116
1746
  try {
1117
1747
  await serve(schema, options);
1118
1748
  } catch (error) {
1119
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1749
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1120
1750
  process.exit(1);
1121
1751
  }
1122
1752
  });
1123
- program.command("dev").description("Start development server (alias for serve)").argument("[schema]", "Path to JSON/YAML schema file", "app.json").option("-p, --port <port>", "Port to run the server on", "3000").option("-h, --host <host>", "Host to bind the server to", "localhost").option("--no-open", "Do not open browser automatically").action(async (schema, options) => {
1753
+ program.command("dev").description("Start development server (alias for serve)").argument("[schema]", "Path to JSON/YAML schema file, or a project / pages directory", "app.json").option("-p, --port <port>", "Port to run the server on", "3000").option("-h, --host <host>", "Host to bind the server to", "localhost").option("--no-open", "Do not open browser automatically").action(async (schema, options) => {
1124
1754
  try {
1125
1755
  await dev(schema, options);
1126
1756
  } catch (error) {
1127
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1757
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1128
1758
  process.exit(1);
1129
1759
  }
1130
1760
  });
1131
- program.command("build").description("Build application for production").argument("[schema]", "Path to JSON/YAML schema file", "app.json").option("-o, --out-dir <dir>", "Output directory", "dist").option("--clean", "Clean output directory before build", false).action(async (schema, options) => {
1761
+ program.command("build").description("Build application for production").argument("[schema]", "Path to JSON/YAML schema file, or a project / pages directory", "app.json").option("-o, --out-dir <dir>", "Output directory", "dist").option("--clean", "Clean output directory before build", false).action(async (schema, options) => {
1132
1762
  try {
1133
1763
  await buildApp(schema, options);
1134
1764
  } catch (error) {
1135
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1765
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1136
1766
  process.exit(1);
1137
1767
  }
1138
1768
  });
@@ -1140,7 +1770,7 @@ program.command("start").description("Start production server").option("-p, --po
1140
1770
  try {
1141
1771
  await start(options);
1142
1772
  } catch (error) {
1143
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1773
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1144
1774
  process.exit(1);
1145
1775
  }
1146
1776
  });
@@ -1148,68 +1778,31 @@ program.command("init").description("Initialize a new Object UI application with
1148
1778
  try {
1149
1779
  await init(name, options);
1150
1780
  } catch (error) {
1151
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1152
- process.exit(1);
1153
- }
1154
- });
1155
- program.command("lint").description("Lint the generated application code").option("--fix", "Automatically fix linting issues").action(async (options) => {
1156
- try {
1157
- await lint(options);
1158
- } catch (error) {
1159
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1781
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1160
1782
  process.exit(1);
1161
1783
  }
1162
1784
  });
1163
- program.command("test").description("Run tests for the application").option("-w, --watch", "Run tests in watch mode").option("-c, --coverage", "Generate test coverage report").option("--ui", "Run tests with Vitest UI").action(async (options) => {
1785
+ program.command("generate").alias("g").description("Generate new resources (objects, pages, plugins)").argument("<type>", "Type of resource to generate (resource/object, page, plugin)").argument("<name>", "Name of the resource").action(async (type, name) => {
1164
1786
  try {
1165
- await test(options);
1166
- } catch (error) {
1167
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1168
- process.exit(1);
1169
- }
1170
- });
1171
- program.command("generate").alias("g").description("Generate new resources (objects, pages, plugins)").argument("<type>", "Type of resource to generate (resource/object, page, plugin)").argument("<name>", "Name of the resource").option("--from <source>", "Generate schema from external source (openapi.yaml, prisma.schema)").option("--output <dir>", "Output directory for generated schemas", "schemas/").action(async (type, name, options) => {
1172
- try {
1173
- if (options.from) {
1174
- console.log(chalk14.yellow("\n\u26A0 Schema generation from external sources (OpenAPI/Prisma) is not yet implemented."));
1175
- console.log(chalk14.gray("This feature will be available in a future release.\n"));
1176
- process.exit(0);
1177
- }
1178
1787
  await generate(type, name);
1179
1788
  } catch (error) {
1180
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1789
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1181
1790
  process.exit(1);
1182
1791
  }
1183
1792
  });
1184
- program.command("doctor").description("Diagnose and fix common issues").action(async () => {
1793
+ program.command("doctor").description("Diagnose common project setup issues (reports only, changes nothing)").action(async () => {
1185
1794
  try {
1186
1795
  await doctor();
1187
1796
  } catch (error) {
1188
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1797
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1189
1798
  process.exit(1);
1190
1799
  }
1191
1800
  });
1192
- program.command("add").description("Add a new component renderer to your project").argument("<component>", "Component name (e.g. Input, Grid)").action(async (component) => {
1193
- try {
1194
- await add(component);
1195
- } catch (error) {
1196
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1197
- process.exit(1);
1198
- }
1199
- });
1200
- program.command("studio").description("Start the visual designer").action(async () => {
1201
- try {
1202
- await studio();
1203
- } catch (error) {
1204
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1205
- process.exit(1);
1206
- }
1207
- });
1208
- program.command("check").description("Validate schema files").action(async () => {
1801
+ program.command("check").description("Advisory JSON file sweep; run objectui validate for a verdict").action(async () => {
1209
1802
  try {
1210
1803
  await check();
1211
1804
  } catch (error) {
1212
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1805
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1213
1806
  process.exit(1);
1214
1807
  }
1215
1808
  });
@@ -1217,29 +1810,15 @@ program.command("validate").description("Validate a schema file against ObjectUI
1217
1810
  try {
1218
1811
  await validate(schema);
1219
1812
  } catch (error) {
1220
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1221
- process.exit(1);
1222
- }
1223
- });
1224
- program.command("create").description("Create new resources").argument("<type>", "Type of resource to create (plugin)").argument("<name>", "Name of the resource").action(async (type, name) => {
1225
- try {
1226
- if (type === "plugin") {
1227
- await createPlugin(name);
1228
- } else {
1229
- console.error(chalk14.red(`Unknown resource type: ${type}`));
1230
- console.log(chalk14.gray("Available types: plugin"));
1231
- process.exit(1);
1232
- }
1233
- } catch (error) {
1234
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1813
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1235
1814
  process.exit(1);
1236
1815
  }
1237
1816
  });
1238
- program.command("analyze").description("Analyze application performance").option("--bundle-size", "Analyze bundle size").option("--render-performance", "Analyze render performance").action(async (options) => {
1817
+ program.command("analyze").description("Analyze application performance").action(async () => {
1239
1818
  try {
1240
- await analyze(options);
1819
+ await analyze();
1241
1820
  } catch (error) {
1242
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1821
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1243
1822
  process.exit(1);
1244
1823
  }
1245
1824
  });