@object-ui/cli 17.6.0 → 18.0.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
@@ -8,11 +8,11 @@ import {
8
8
  reportProjectSource,
9
9
  resolveProjectSource,
10
10
  serve
11
- } from "./chunk-AP2INOE7.js";
11
+ } from "./chunk-VMMIGYU3.js";
12
12
 
13
13
  // src/cli.ts
14
14
  import { Command } from "commander";
15
- import chalk14 from "chalk";
15
+ import chalk9 from "chalk";
16
16
 
17
17
  // src/commands/dev.ts
18
18
  import { createServer } from "vite";
@@ -212,108 +212,10 @@ Make sure you have a valid production build.`
212
212
  });
213
213
  }
214
214
 
215
- // src/commands/lint.ts
216
- import { execSync as execSync3 } from "child_process";
217
- import { existsSync as existsSync3 } from "fs";
218
- import { join as join4 } from "path";
219
- import chalk4 from "chalk";
220
- async function lint(options) {
221
- const cwd = process.cwd();
222
- console.log(chalk4.blue("\u{1F50D} Running linter...\n"));
223
- const tmpDir = join4(cwd, ".objectui-tmp");
224
- const hasTempApp = existsSync3(tmpDir);
225
- if (!hasTempApp) {
226
- throw new Error(
227
- "No Object UI application found. Run 'objectui dev' first to generate the application."
228
- );
229
- }
230
- const packageJsonPath = join4(tmpDir, "package.json");
231
- const nodeModulesPath = join4(tmpDir, "node_modules");
232
- if (!existsSync3(packageJsonPath) || !existsSync3(nodeModulesPath)) {
233
- throw new Error(
234
- "Dependencies not installed. Run 'objectui dev' first to set up the application."
235
- );
236
- }
237
- try {
238
- const fixFlag = options.fix ? "--fix" : "";
239
- const command = `npx eslint "src/**/*.{js,jsx,ts,tsx}" ${fixFlag}`.trim();
240
- console.log(chalk4.dim(` Running: ${command}
241
- `));
242
- execSync3(command, {
243
- cwd: tmpDir,
244
- stdio: "inherit"
245
- });
246
- console.log();
247
- console.log(chalk4.green("\u2713 Linting completed successfully!"));
248
- console.log();
249
- } catch {
250
- console.log();
251
- console.log(chalk4.yellow("\u26A0 Linting found issues."));
252
- if (!options.fix) {
253
- console.log(chalk4.dim(" Run 'objectui lint --fix' to automatically fix some issues."));
254
- }
255
- console.log();
256
- process.exit(1);
257
- }
258
- }
259
-
260
- // src/commands/test.ts
261
- import { execSync as execSync4 } from "child_process";
262
- import { existsSync as existsSync4 } from "fs";
263
- import { join as join5 } from "path";
264
- import chalk5 from "chalk";
265
- async function test(options) {
266
- const cwd = process.cwd();
267
- console.log(chalk5.blue("\u{1F9EA} Running tests...\n"));
268
- const tmpDir = join5(cwd, ".objectui-tmp");
269
- const hasTempApp = existsSync4(tmpDir);
270
- if (!hasTempApp) {
271
- throw new Error(
272
- "No Object UI application found. Run 'objectui dev' first to generate the application."
273
- );
274
- }
275
- const packageJsonPath = join5(tmpDir, "package.json");
276
- const nodeModulesPath = join5(tmpDir, "node_modules");
277
- if (!existsSync4(packageJsonPath) || !existsSync4(nodeModulesPath)) {
278
- throw new Error(
279
- "Dependencies not installed. Run 'objectui dev' first to set up the application."
280
- );
281
- }
282
- try {
283
- let command = "npx vitest";
284
- if (options.watch) {
285
- command += " --watch";
286
- } else if (options.ui) {
287
- command += " --ui";
288
- } else {
289
- command += " run";
290
- }
291
- if (options.coverage) {
292
- command += " --coverage";
293
- }
294
- console.log(chalk5.dim(` Running: ${command}
295
- `));
296
- execSync4(command, {
297
- cwd: tmpDir,
298
- stdio: "inherit"
299
- });
300
- if (!options.watch && !options.ui) {
301
- console.log();
302
- console.log(chalk5.green("\u2713 Tests completed successfully!"));
303
- console.log();
304
- }
305
- } catch {
306
- console.log();
307
- console.log(chalk5.red("\u2717 Some tests failed."));
308
- console.log();
309
- process.exit(1);
310
- }
311
- }
312
-
313
215
  // src/commands/generate.ts
314
- import chalk6 from "chalk";
315
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, writeFileSync } from "fs";
316
- 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";
317
219
  async function generate(type, name) {
318
220
  const cwd = process.cwd();
319
221
  switch (type) {
@@ -328,18 +230,18 @@ async function generate(type, name) {
328
230
  generatePlugin(cwd, name);
329
231
  break;
330
232
  default:
331
- console.log(chalk6.red(`Unknown type: ${type}`));
233
+ console.log(chalk4.red(`Unknown type: ${type}`));
332
234
  console.log(`Available types: resource, page, plugin`);
333
235
  process.exit(1);
334
236
  }
335
237
  }
336
238
  function generateResource(cwd, name) {
337
- const dir = join6(cwd, "objects");
338
- if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
239
+ const dir = join4(cwd, "objects");
240
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
339
241
  const fileName = `${name.toLowerCase()}.json`;
340
- const filePath = join6(dir, fileName);
341
- if (existsSync5(filePath)) {
342
- 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}`));
343
245
  return;
344
246
  }
345
247
  const content = {
@@ -349,34 +251,35 @@ function generateResource(cwd, name) {
349
251
  }
350
252
  };
351
253
  writeFileSync(filePath, JSON.stringify(content, null, 2));
352
- console.log(chalk6.green(`\u2713 Generated resource schema: ${filePath}`));
254
+ console.log(chalk4.green(`\u2713 Generated resource schema: ${filePath}`));
353
255
  }
354
256
  function generatePage(cwd, name) {
355
- const dir = join6(cwd, "pages");
356
- if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
257
+ const dir = join4(cwd, "pages");
258
+ if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
357
259
  const fileName = `${name.toLowerCase()}.json`;
358
- const filePath = join6(dir, fileName);
359
- if (existsSync5(filePath)) {
360
- 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}`));
361
263
  return;
362
264
  }
363
265
  const content = {
364
266
  type: "page",
267
+ pageType: "app",
365
268
  title: name,
366
- body: [
269
+ children: [
367
270
  {
368
271
  type: "markdown",
369
- content: `# Welcome to ${name}`
272
+ content: `Welcome to ${name}`
370
273
  }
371
274
  ]
372
275
  };
373
276
  writeFileSync(filePath, JSON.stringify(content, null, 2));
374
- console.log(chalk6.green(`\u2713 Generated page schema: ${filePath}`));
277
+ console.log(chalk4.green(`\u2713 Generated page schema: ${filePath}`));
375
278
  }
376
279
  function generatePlugin(cwd, name) {
377
- const dir = join6(cwd, "plugins", name);
378
- if (existsSync5(dir)) {
379
- 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`));
380
283
  return;
381
284
  }
382
285
  mkdirSync3(dir, { recursive: true });
@@ -388,14 +291,14 @@ export const ${name}Plugin = {
388
291
  }
389
292
  };
390
293
  `;
391
- writeFileSync(join6(dir, "index.ts"), content.trim());
392
- 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}`));
393
296
  }
394
297
 
395
298
  // src/commands/doctor.ts
396
- import chalk7 from "chalk";
397
- import { existsSync as existsSync6, readFileSync } from "fs";
398
- import { dirname, 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";
399
302
  var CSS_ENTRY_CANDIDATES = [
400
303
  "src/index.css",
401
304
  "src/main.css",
@@ -430,15 +333,15 @@ var V4_POSTCSS_PLUGIN = /@tailwindcss\/postcss/;
430
333
  var V3_POSTCSS_PLUGIN = /["']tailwindcss["']|(?:^|[{,\s])tailwindcss\s*:/m;
431
334
  function readIfExists(path) {
432
335
  try {
433
- return existsSync6(path) ? readFileSync(path, "utf-8") : null;
336
+ return existsSync4(path) ? readFileSync(path, "utf-8") : null;
434
337
  } catch {
435
338
  return null;
436
339
  }
437
340
  }
438
341
  function findFile(cwd, candidates) {
439
342
  for (const rel of candidates) {
440
- const abs = join7(cwd, rel);
441
- if (existsSync6(abs)) return abs;
343
+ const abs = join5(cwd, rel);
344
+ if (existsSync4(abs)) return abs;
442
345
  }
443
346
  return null;
444
347
  }
@@ -451,7 +354,7 @@ function majorOf(range) {
451
354
  function isInstalledFrom(cwd, specifier) {
452
355
  let dir = resolvePath(cwd);
453
356
  for (; ; ) {
454
- if (existsSync6(join7(dir, "node_modules", ...specifier.split("/")))) return true;
357
+ if (existsSync4(join5(dir, "node_modules", ...specifier.split("/")))) return true;
455
358
  const parent = dirname(dir);
456
359
  if (parent === dir) return false;
457
360
  dir = parent;
@@ -468,7 +371,7 @@ function runDiagnostics(cwd) {
468
371
  const error = (id, message) => {
469
372
  results.push({ id, level: "error", message });
470
373
  };
471
- const pkgPath = join7(cwd, "package.json");
374
+ const pkgPath = join5(cwd, "package.json");
472
375
  const pkgRaw = readIfExists(pkgPath);
473
376
  let pkg = null;
474
377
  if (pkgRaw === null) {
@@ -536,7 +439,7 @@ function runDiagnostics(cwd) {
536
439
  }
537
440
  const cssEntries = CSS_ENTRY_CANDIDATES.map((rel) => ({
538
441
  rel,
539
- content: readIfExists(join7(cwd, rel))
442
+ content: readIfExists(join5(cwd, rel))
540
443
  })).filter((entry) => entry.content !== null);
541
444
  const importsTailwind = cssEntries.some((entry) => V4_CSS_IMPORT.test(entry.content));
542
445
  const usesV3Directives = cssEntries.some((entry) => V3_CSS_DIRECTIVE.test(entry.content));
@@ -570,7 +473,7 @@ function runDiagnostics(cwd) {
570
473
  ok("tailwind-config-active", "JS Tailwind config is loaded via `@config`");
571
474
  }
572
475
  }
573
- if (existsSync6(join7(cwd, "tsconfig.json"))) {
476
+ if (existsSync4(join5(cwd, "tsconfig.json"))) {
574
477
  ok("tsconfig-found", "tsconfig.json found");
575
478
  const tsRange = deps.typescript;
576
479
  if (tsRange) {
@@ -598,86 +501,224 @@ function countIssues(results) {
598
501
  return results.filter((result) => result.level !== "ok").length;
599
502
  }
600
503
  async function doctor() {
601
- console.log(chalk7.bold("Object UI Doctor"));
504
+ console.log(chalk5.bold("Object UI Doctor"));
602
505
  console.log("Diagnosis in progress...\n");
603
506
  const results = runDiagnostics(process.cwd());
604
507
  for (const result of results) {
605
508
  if (result.level === "ok") {
606
- console.log(chalk7.green(`\u2713 ${result.message}`));
509
+ console.log(chalk5.green(`\u2713 ${result.message}`));
607
510
  } else if (result.level === "warn") {
608
- console.log(chalk7.yellow(`\u26A0\uFE0F ${result.message}`));
511
+ console.log(chalk5.yellow(`\u26A0\uFE0F ${result.message}`));
609
512
  } else {
610
- console.log(chalk7.red(`x ${result.message}`));
513
+ console.log(chalk5.red(`x ${result.message}`));
611
514
  }
612
515
  }
613
516
  const issues = countIssues(results);
614
517
  console.log("\nResult:");
615
518
  if (issues === 0) {
616
- console.log(chalk7.green("Everything looks good! \u2728"));
519
+ console.log(chalk5.green("Everything looks good! \u2728"));
617
520
  } else {
618
- console.log(chalk7.yellow(`Found ${issues} issue(s).`));
521
+ console.log(chalk5.yellow(`Found ${issues} issue(s).`));
619
522
  }
620
523
  }
621
524
 
622
- // src/commands/add.ts
623
- import chalk8 from "chalk";
624
- async function add(component) {
625
- console.log(chalk8.bold(`Object UI Add: ${component}`));
626
- console.log(chalk8.yellow("Feature not implemented yet."));
627
- 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
+
532
+ // src/utils/authoring-face.ts
533
+ import { StrictAnyComponentSchema } from "@object-ui/types/zod";
534
+
535
+ // src/utils/issue-path.ts
536
+ function formatIssuePath(path) {
537
+ const segments = path ?? [];
538
+ return segments.length > 0 ? segments.join(" \u2192 ") : "(root)";
628
539
  }
629
540
 
630
- // src/commands/studio.ts
631
- import chalk9 from "chalk";
632
- import { spawn } from "child_process";
633
- import { existsSync as existsSync7 } from "fs";
634
- import { join as join8, resolve as resolve3, dirname as dirname2 } from "path";
635
- import { fileURLToPath } from "url";
636
- async function studio() {
637
- console.log(chalk9.bold("\n\u{1F3A8} Starting ObjectUI Studio...\n"));
638
- const cwd = process.cwd();
639
- const __filename3 = fileURLToPath(import.meta.url);
640
- const __dirname3 = dirname2(__filename3);
641
- const candidates = [
642
- join8(cwd, "apps/console"),
643
- resolve3(__dirname3, "../../apps/console"),
644
- resolve3(__dirname3, "../../../apps/console")
645
- ];
646
- let consolePath = null;
647
- for (const candidate of candidates) {
648
- if (existsSync7(join8(candidate, "package.json"))) {
649
- consolePath = candidate;
650
- break;
541
+ // src/utils/union-arm-diagnostics.ts
542
+ var MAX_UNION_ARMS_REPORTED = 5;
543
+ function editDistance(a, b) {
544
+ if (a === b) return 0;
545
+ if (a.length === 0) return b.length;
546
+ if (b.length === 0) return a.length;
547
+ let previous = Array.from({ length: b.length + 1 }, (_, i) => i);
548
+ for (let i = 1; i <= a.length; i += 1) {
549
+ const current = [i];
550
+ for (let j = 1; j <= b.length; j += 1) {
551
+ current[j] = Math.min(
552
+ previous[j] + 1,
553
+ current[j - 1] + 1,
554
+ previous[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1)
555
+ );
651
556
  }
557
+ previous = current;
652
558
  }
653
- if (!consolePath) {
654
- console.log(chalk9.yellow("\u26A0 Console app not found in workspace."));
655
- console.log(chalk9.dim(" Hint: Run this command from the monorepo root."));
656
- console.log(chalk9.dim(" Expected: apps/console/package.json\n"));
657
- process.exit(1);
559
+ return previous[b.length];
560
+ }
561
+ function nearestArmNames(authoredType, armNames) {
562
+ const unique = [...new Set(armNames)].sort();
563
+ if (typeof authoredType !== "string" || authoredType === "") {
564
+ return { candidates: [], total: unique.length };
565
+ }
566
+ const ordered = [...unique].sort((x, y) => {
567
+ const d = editDistance(authoredType, x) - editDistance(authoredType, y);
568
+ return d !== 0 ? d : x.localeCompare(y);
569
+ });
570
+ return { candidates: ordered.slice(0, MAX_UNION_ARMS_REPORTED), total: unique.length };
571
+ }
572
+ function authoredTypeAt(document, path) {
573
+ let node = document;
574
+ for (const key of path) {
575
+ if (node === null || typeof node !== "object") return void 0;
576
+ node = node[key];
577
+ }
578
+ if (node === null || typeof node !== "object") return void 0;
579
+ const type = node.type;
580
+ return typeof type === "string" ? type : void 0;
581
+ }
582
+ function isUnion(issue) {
583
+ return issue.code === "invalid_union" && Array.isArray(issue.errors);
584
+ }
585
+ var NO_MATCHING_DISCRIMINATOR = "No matching discriminator";
586
+ function noArmAccepts(node, literals, document) {
587
+ const authoredType = authoredTypeAt(document, node);
588
+ const { candidates, total } = nearestArmNames(authoredType, literals);
589
+ return {
590
+ kind: "note",
591
+ path: [...node],
592
+ ...authoredType === void 0 ? {} : { authoredType },
593
+ candidates,
594
+ totalArmNames: total
595
+ };
596
+ }
597
+ function noMatchingDiscriminator(issue, document) {
598
+ if (issue.code !== "invalid_union") return void 0;
599
+ if (issue.note !== NO_MATCHING_DISCRIMINATOR) return void 0;
600
+ if (!Array.isArray(issue.options)) return void 0;
601
+ if ((issue.errors ?? []).length > 0) return void 0;
602
+ const path = issue.path ?? [];
603
+ if (path[path.length - 1] !== "type") return void 0;
604
+ if (issue.discriminator !== void 0 && issue.discriminator !== "type") return void 0;
605
+ const literals = issue.options.filter((v) => typeof v === "string");
606
+ if (literals.length === 0) return void 0;
607
+ return noArmAccepts(path.slice(0, -1), literals, document);
608
+ }
609
+ function isAtTypeKey(issue) {
610
+ const path = issue.path ?? [];
611
+ return path.length === 1 && path[0] === "type";
612
+ }
613
+ function declaredTypeLiterals(armIssues) {
614
+ const literals = [];
615
+ let declares = false;
616
+ for (const issue of armIssues) {
617
+ if (isAtTypeKey(issue) && issue.code === "invalid_value" && Array.isArray(issue.values)) {
618
+ declares = true;
619
+ literals.push(...issue.values.filter((v) => typeof v === "string"));
620
+ continue;
621
+ }
622
+ if (isAtTypeKey(issue) && isUnion(issue) && Array.isArray(issue.options)) {
623
+ declares = true;
624
+ literals.push(...issue.options.filter((v) => typeof v === "string"));
625
+ continue;
626
+ }
627
+ if ((issue.path ?? []).length === 0 && isUnion(issue)) {
628
+ const nested = (issue.errors ?? []).map((sub) => declaredTypeLiterals(sub));
629
+ if (nested.length > 0 && nested.every((n) => n !== void 0)) {
630
+ declares = true;
631
+ for (const n of nested) literals.push(...n);
632
+ }
633
+ }
658
634
  }
659
- console.log(chalk9.dim(` Console: ${consolePath}`));
660
- console.log(chalk9.dim(" Mode: MSW (in-browser mock server)\n"));
661
- const child = spawn("pnpm", ["run", "dev"], {
662
- cwd: consolePath,
663
- stdio: "inherit",
664
- env: { ...process.env, NODE_ENV: "development" },
665
- shell: true
635
+ return declares ? literals : void 0;
636
+ }
637
+ function explain(unionIssue, prefix, document) {
638
+ const arms = unionIssue.errors ?? [];
639
+ const declared = arms.map((arm) => declaredTypeLiterals(arm));
640
+ const declaringArms = declared.filter((d) => d !== void 0);
641
+ const acceptingIndexes = declared.map((d, i) => d === void 0 ? i : -1).filter((i) => i >= 0);
642
+ const expand = (armIssues, arm) => armIssues.flatMap((issue) => {
643
+ const absolute = [...prefix, ...issue.path ?? []];
644
+ const discriminated = noMatchingDiscriminator({ ...issue, path: absolute }, document);
645
+ if (discriminated) return [discriminated];
646
+ if (isUnion(issue)) return explain(issue, absolute, document);
647
+ return [
648
+ {
649
+ kind: "issue",
650
+ path: absolute,
651
+ message: issue.message ?? "",
652
+ code: issue.code,
653
+ ...arm === void 0 ? {} : { arm }
654
+ }
655
+ ];
666
656
  });
667
- child.on("error", (err) => {
668
- console.error(chalk9.red(`
669
- \u2717 Failed to start studio: ${err.message}`));
670
- process.exit(1);
657
+ if (declaringArms.length > 0 && acceptingIndexes.length === 1) {
658
+ return expand(arms[acceptingIndexes[0]]);
659
+ }
660
+ if (declaringArms.length > 0 && acceptingIndexes.length === 0) {
661
+ return [noArmAccepts(prefix, declaringArms.flat(), document)];
662
+ }
663
+ return arms.slice(0, MAX_UNION_ARMS_REPORTED).flatMap((arm, i) => expand(arm, `${i + 1}/${arms.length}`));
664
+ }
665
+ function explainUnionIssue(issue, document) {
666
+ const discriminated = noMatchingDiscriminator(issue, document);
667
+ if (discriminated) return [discriminated];
668
+ if (!isUnion(issue)) return [];
669
+ return explain(issue, issue.path ?? [], document);
670
+ }
671
+ function isTypeMismatch(issue) {
672
+ return isAtTypeKey(issue) && (issue.code === "invalid_value" || isUnion(issue));
673
+ }
674
+ function isShapeMismatch(issue) {
675
+ if ((issue.path ?? []).length !== 0) return false;
676
+ if (issue.code === "invalid_type") return true;
677
+ return isUnion(issue) && (issue.errors ?? []).every((arm) => !isViableArm(arm));
678
+ }
679
+ function isViableArm(armIssues) {
680
+ if (armIssues.some(isTypeMismatch)) return false;
681
+ return !armIssues.every(isShapeMismatch);
682
+ }
683
+ function findUndeclaredKeys(issues) {
684
+ const idOf = (finding) => JSON.stringify([...finding.path.map(String), finding.key]);
685
+ const collect = (list, prefix) => {
686
+ const found = [];
687
+ for (const issue of list) {
688
+ const path = [...prefix, ...issue.path ?? []];
689
+ if (issue.code === "unrecognized_keys") {
690
+ for (const key of issue.keys ?? []) found.push({ path, key });
691
+ }
692
+ if (isUnion(issue)) {
693
+ const perArm = (issue.errors ?? []).filter(isViableArm).map((arm) => collect(arm, path));
694
+ const [first, ...others] = perArm;
695
+ if (first === void 0) continue;
696
+ const refusedByAll = first.filter(
697
+ (finding) => others.every((arm) => arm.some((other) => idOf(other) === idOf(finding)))
698
+ );
699
+ found.push(...refusedByAll);
700
+ }
701
+ }
702
+ return found;
703
+ };
704
+ const seen = /* @__PURE__ */ new Set();
705
+ return collect(issues, []).filter((finding) => {
706
+ const id = idOf(finding);
707
+ if (seen.has(id)) return false;
708
+ seen.add(id);
709
+ return true;
671
710
  });
672
- child.on("exit", (code) => process.exit(code ?? 0));
673
711
  }
674
712
 
675
- // src/commands/check.ts
676
- import chalk10 from "chalk";
677
- import { globSync } from "glob";
678
- import { readFileSync as readFileSync2 } from "fs";
679
- import { join as join9 } from "path";
680
- import { parse as parseJsonc, printParseErrorCode } from "jsonc-parser";
713
+ // src/utils/authoring-face.ts
714
+ function validateAuthoredDocument(document) {
715
+ return StrictAnyComponentSchema.safeParse(document);
716
+ }
717
+ function describeUndeclaredKey(finding, document) {
718
+ const type = authoredTypeAt(document, finding.path);
719
+ const owner = type === void 0 ? "" : ` (type "${type}")`;
720
+ return `Undeclared key "${finding.key}" at ${formatIssuePath(finding.path)}${owner}: no schema declares it there. Remove it, or check its spelling against the keys declared at that position.`;
721
+ }
681
722
 
682
723
  // src/utils/known-schema-types.ts
683
724
  var KNOWN_SCHEMA_TYPES = [
@@ -690,7 +731,6 @@ var KNOWN_SCHEMA_TYPES = [
690
731
  "action:icon",
691
732
  "action:menu",
692
733
  "address",
693
- "advanced-chart",
694
734
  "ai-form-assist",
695
735
  "ai-recommendations",
696
736
  "ai:feedback",
@@ -699,27 +739,21 @@ var KNOWN_SCHEMA_TYPES = [
699
739
  "alert",
700
740
  "alert-dialog",
701
741
  "app",
702
- "app-creation-wizard",
703
742
  "app-schema-renderer",
704
743
  "app-shell:cloud-connection:panel",
705
744
  "app-shell:cloud:ai-model-status",
706
- "app-shell:cloud:onboarding-next",
707
- "app-shell:marketplace:installed-list",
708
745
  "app-shell:mcp:connect-agent",
709
746
  "app:launcher",
710
- "area-chart",
711
747
  "article",
712
748
  "aside",
713
749
  "aspect-ratio",
714
- "auto_number",
715
750
  "avatar",
716
751
  "b",
717
752
  "badge",
718
753
  "bar-chart",
719
754
  "blockquote",
720
- "boolean",
755
+ "box",
721
756
  "br",
722
- "branding-editor",
723
757
  "breadcrumb",
724
758
  "button",
725
759
  "button-group",
@@ -733,28 +767,22 @@ var KNOWN_SCHEMA_TYPES = [
733
767
  "chatbot-enhanced",
734
768
  "chatbot-floating",
735
769
  "checkbox",
736
- "checkboxes",
770
+ "checklist",
737
771
  "cite",
738
772
  "cloud-connection:panel",
739
773
  "cloud:ai-model-status",
740
- "cloud:onboarding-next",
774
+ "cloud:workspace-timezone-notice",
741
775
  "code",
742
776
  "code-editor",
743
777
  "collapsible",
744
- "color",
745
778
  "combobox",
746
779
  "command",
747
780
  "container",
748
781
  "context-menu",
749
- "currency",
750
782
  "dashboard",
751
- "dashboard-editor",
752
- "dashboard-grid",
753
783
  "data-model-designer",
754
784
  "data-table",
755
- "date",
756
785
  "date-picker",
757
- "datetime",
758
786
  "dd",
759
787
  "del",
760
788
  "detail",
@@ -763,7 +791,6 @@ var KNOWN_SCHEMA_TYPES = [
763
791
  "dialog",
764
792
  "div",
765
793
  "dl",
766
- "donut-chart",
767
794
  "drawer",
768
795
  "dropdown-menu",
769
796
  "dt",
@@ -813,8 +840,9 @@ var KNOWN_SCHEMA_TYPES = [
813
840
  "field:object-ref",
814
841
  "field:password",
815
842
  "field:percent",
816
- "field:permission-facet-link",
843
+ "field:permission-ref",
817
844
  "field:phone",
845
+ "field:position-ref",
818
846
  "field:progress",
819
847
  "field:qrcode",
820
848
  "field:radio",
@@ -836,17 +864,12 @@ var KNOWN_SCHEMA_TYPES = [
836
864
  "field:video",
837
865
  "figcaption",
838
866
  "figure",
839
- "file",
840
867
  "file-upload",
841
868
  "filter-builder",
842
869
  "filter-ui",
843
870
  "flex",
844
871
  "footer",
845
872
  "form",
846
- "form-analytics",
847
- "formula",
848
- "gantt",
849
- "geolocation",
850
873
  "global:notifications",
851
874
  "global:search",
852
875
  "grid",
@@ -858,6 +881,7 @@ var KNOWN_SCHEMA_TYPES = [
858
881
  "h6",
859
882
  "header",
860
883
  "header-bar",
884
+ "hero",
861
885
  "home",
862
886
  "hover-card",
863
887
  "hr",
@@ -866,47 +890,27 @@ var KNOWN_SCHEMA_TYPES = [
866
890
  "icon",
867
891
  "image",
868
892
  "img",
869
- "import-wizard",
870
893
  "input",
871
894
  "input-otp",
872
895
  "ins",
873
- "kanban",
874
- "kanban-enhanced",
875
- "kanban-ui",
876
896
  "kbd",
877
897
  "label",
878
898
  "layout:app-schema-renderer",
879
- "layout:navigation-renderer",
880
- "layout:page-header",
881
899
  "layout:page:card",
882
- "layout:responsive-grid",
883
900
  "li",
884
- "line-chart",
885
901
  "list",
886
902
  "list-view",
887
903
  "loading",
888
- "location",
889
- "lookup",
890
904
  "main",
891
- "map",
892
905
  "mark",
893
906
  "markdown",
894
- "marketplace:installed-list",
895
- "master_detail",
896
907
  "mcp:connect-agent",
897
908
  "menubar",
898
- "metric",
899
- "metric-card",
900
- "multiselect",
901
909
  "nav",
902
910
  "nav:breadcrumb",
903
911
  "nav:menu",
904
- "navigation-designer",
905
912
  "navigation-menu",
906
- "navigation-renderer",
907
913
  "nl-query",
908
- "number",
909
- "object",
910
914
  "object-calendar",
911
915
  "object-chart",
912
916
  "object-data-table",
@@ -927,7 +931,6 @@ var KNOWN_SCHEMA_TYPES = [
927
931
  "p",
928
932
  "page",
929
933
  "page-designer",
930
- "page-header",
931
934
  "page:accordion",
932
935
  "page:card",
933
936
  "page:footer",
@@ -937,57 +940,37 @@ var KNOWN_SCHEMA_TYPES = [
937
940
  "page:tabs",
938
941
  "pagination",
939
942
  "password",
940
- "percent",
941
- "phone",
942
- "pie-chart",
943
943
  "pivot",
944
944
  "plugin-calendar:calendar-view",
945
945
  "plugin-calendar:object-calendar",
946
- "plugin-charts:advanced-chart",
947
- "plugin-charts:area-chart",
948
946
  "plugin-charts:bar-chart",
949
947
  "plugin-charts:chart",
950
948
  "plugin-charts:chart:bar",
951
- "plugin-charts:donut-chart",
952
- "plugin-charts:line-chart",
953
949
  "plugin-charts:object-chart",
954
- "plugin-charts:pie-chart",
955
- "plugin-charts:radar-chart",
956
- "plugin-charts:scatter-chart",
957
950
  "plugin-chatbot:chatbot",
958
951
  "plugin-chatbot:chatbot-enhanced",
959
952
  "plugin-chatbot:chatbot-floating",
960
953
  "plugin-dashboard:dashboard",
961
- "plugin-dashboard:dashboard-grid",
962
954
  "plugin-dashboard:metric",
963
955
  "plugin-dashboard:metric-card",
964
956
  "plugin-dashboard:object-data-table",
965
957
  "plugin-dashboard:object-metric",
966
958
  "plugin-dashboard:object-pivot",
967
959
  "plugin-dashboard:pivot",
968
- "plugin-designer:app-creation-wizard",
969
- "plugin-designer:branding-editor",
970
- "plugin-designer:dashboard-editor",
971
960
  "plugin-designer:data-model-designer",
972
961
  "plugin-designer:field-designer",
973
- "plugin-designer:navigation-designer",
974
962
  "plugin-designer:object-manager",
975
963
  "plugin-designer:page-designer",
976
964
  "plugin-designer:process-designer",
977
965
  "plugin-designer:report-designer",
978
966
  "plugin-detail:detail-section",
979
967
  "plugin-detail:detail-view",
980
- "plugin-detail:related-list",
981
968
  "plugin-editor:code-editor",
982
969
  "plugin-form:embeddable-form",
983
- "plugin-form:form-analytics",
984
970
  "plugin-form:object-form",
985
971
  "plugin-form:object-master-detail-form",
986
972
  "plugin-gantt:object-gantt",
987
- "plugin-grid:import-wizard",
988
973
  "plugin-grid:object-grid",
989
- "plugin-kanban:kanban-enhanced",
990
- "plugin-kanban:kanban-ui",
991
974
  "plugin-kanban:object-kanban",
992
975
  "plugin-list:list-view",
993
976
  "plugin-list:object-gallery",
@@ -995,12 +978,10 @@ var KNOWN_SCHEMA_TYPES = [
995
978
  "plugin-markdown:markdown",
996
979
  "plugin-report:report",
997
980
  "plugin-report:report-viewer",
998
- "plugin-report:spec-report",
999
981
  "plugin-timeline:object-timeline",
1000
982
  "plugin-timeline:timeline",
1001
983
  "plugin-tree:object-tree",
1002
984
  "plugin-view:object-view",
1003
- "plugin-view:view",
1004
985
  "plugin-view:view:simple",
1005
986
  "popover",
1006
987
  "pre",
@@ -1052,7 +1033,6 @@ var KNOWN_SCHEMA_TYPES = [
1052
1033
  "protocol-placeholder:global:search",
1053
1034
  "protocol-placeholder:nav:breadcrumb",
1054
1035
  "protocol-placeholder:nav:menu",
1055
- "protocol-placeholder:page-header",
1056
1036
  "protocol-placeholder:page:accordion",
1057
1037
  "protocol-placeholder:page:card",
1058
1038
  "protocol-placeholder:page:footer",
@@ -1065,7 +1045,6 @@ var KNOWN_SCHEMA_TYPES = [
1065
1045
  "protocol-placeholder:record:highlights",
1066
1046
  "protocol-placeholder:record:path",
1067
1047
  "protocol-placeholder:record:related_list",
1068
- "protocol-placeholder:user:profile",
1069
1048
  "protocol-placeholder:view:calendar",
1070
1049
  "protocol-placeholder:view:drawer",
1071
1050
  "protocol-placeholder:view:gallery",
@@ -1092,14 +1071,11 @@ var KNOWN_SCHEMA_TYPES = [
1092
1071
  "protocol-placeholder:widget:table",
1093
1072
  "protocol-placeholder:widget:text",
1094
1073
  "q",
1095
- "qrcode",
1096
- "radar-chart",
1097
- "radio",
1098
1074
  "radio-group",
1099
- "rating",
1100
1075
  "record",
1101
1076
  "record:activity",
1102
1077
  "record:alert",
1078
+ "record:approval_decision",
1103
1079
  "record:approvals",
1104
1080
  "record:attachments",
1105
1081
  "record:chatter",
@@ -1112,55 +1088,35 @@ var KNOWN_SCHEMA_TYPES = [
1112
1088
  "record:quick_actions",
1113
1089
  "record:reference_rail",
1114
1090
  "record:related_list",
1115
- "related-list",
1116
1091
  "report",
1117
1092
  "report-designer",
1118
1093
  "report-viewer",
1119
- "report:report",
1120
- "report:report-viewer",
1121
- "report:spec-report",
1122
1094
  "resizable",
1123
- "responsive-grid",
1124
- "richtext",
1125
- "scatter-chart",
1126
1095
  "scroll-area",
1127
1096
  "section",
1128
1097
  "select",
1129
1098
  "separator",
1130
- "shared-view-link",
1131
1099
  "sheet",
1132
1100
  "sidebar",
1133
- "sidebar-content",
1134
- "sidebar-footer",
1135
- "sidebar-group",
1136
- "sidebar-header",
1137
- "sidebar-inset",
1138
- "sidebar-menu",
1139
- "sidebar-menu-button",
1140
- "sidebar-menu-item",
1141
- "sidebar-provider",
1142
- "sidebar-trigger",
1143
- "signature",
1144
1101
  "skeleton",
1145
1102
  "slider",
1146
1103
  "small",
1147
1104
  "sonner",
1148
1105
  "sort-ui",
1149
1106
  "span",
1150
- "spec-report",
1151
1107
  "spinner",
1152
1108
  "stack",
1109
+ "stat",
1153
1110
  "statistic",
1154
1111
  "strong",
1155
1112
  "sub",
1156
- "summary",
1157
1113
  "sup",
1158
1114
  "switch",
1159
1115
  "table",
1160
1116
  "tabs",
1161
- "tags",
1162
1117
  "text",
1163
1118
  "textarea",
1119
+ "tile-grid",
1164
1120
  "time",
1165
1121
  "timeline",
1166
1122
  "toast",
@@ -1168,7 +1124,6 @@ var KNOWN_SCHEMA_TYPES = [
1168
1124
  "toggle",
1169
1125
  "toggle-group",
1170
1126
  "tooltip",
1171
- "tree",
1172
1127
  "tree-view",
1173
1128
  "u",
1174
1129
  "ui:a",
@@ -1177,6 +1132,7 @@ var KNOWN_SCHEMA_TYPES = [
1177
1132
  "ui:address",
1178
1133
  "ui:alert",
1179
1134
  "ui:alert-dialog",
1135
+ "ui:app",
1180
1136
  "ui:article",
1181
1137
  "ui:aside",
1182
1138
  "ui:aspect-ratio",
@@ -1184,6 +1140,7 @@ var KNOWN_SCHEMA_TYPES = [
1184
1140
  "ui:b",
1185
1141
  "ui:badge",
1186
1142
  "ui:blockquote",
1143
+ "ui:box",
1187
1144
  "ui:br",
1188
1145
  "ui:breadcrumb",
1189
1146
  "ui:button",
@@ -1193,6 +1150,7 @@ var KNOWN_SCHEMA_TYPES = [
1193
1150
  "ui:carousel",
1194
1151
  "ui:checkbox",
1195
1152
  "ui:cite",
1153
+ "ui:code",
1196
1154
  "ui:collapsible",
1197
1155
  "ui:combobox",
1198
1156
  "ui:command",
@@ -1226,6 +1184,7 @@ var KNOWN_SCHEMA_TYPES = [
1226
1184
  "ui:h6",
1227
1185
  "ui:header",
1228
1186
  "ui:header-bar",
1187
+ "ui:home",
1229
1188
  "ui:hover-card",
1230
1189
  "ui:hr",
1231
1190
  "ui:html",
@@ -1248,6 +1207,7 @@ var KNOWN_SCHEMA_TYPES = [
1248
1207
  "ui:navigation-menu",
1249
1208
  "ui:ol",
1250
1209
  "ui:p",
1210
+ "ui:page",
1251
1211
  "ui:pagination",
1252
1212
  "ui:password",
1253
1213
  "ui:popover",
@@ -1255,6 +1215,7 @@ var KNOWN_SCHEMA_TYPES = [
1255
1215
  "ui:progress",
1256
1216
  "ui:q",
1257
1217
  "ui:radio-group",
1218
+ "ui:record",
1258
1219
  "ui:resizable",
1259
1220
  "ui:scroll-area",
1260
1221
  "ui:section",
@@ -1262,16 +1223,6 @@ var KNOWN_SCHEMA_TYPES = [
1262
1223
  "ui:separator",
1263
1224
  "ui:sheet",
1264
1225
  "ui:sidebar",
1265
- "ui:sidebar-content",
1266
- "ui:sidebar-footer",
1267
- "ui:sidebar-group",
1268
- "ui:sidebar-header",
1269
- "ui:sidebar-inset",
1270
- "ui:sidebar-menu",
1271
- "ui:sidebar-menu-button",
1272
- "ui:sidebar-menu-item",
1273
- "ui:sidebar-provider",
1274
- "ui:sidebar-trigger",
1275
1226
  "ui:skeleton",
1276
1227
  "ui:slider",
1277
1228
  "ui:small",
@@ -1297,17 +1248,13 @@ var KNOWN_SCHEMA_TYPES = [
1297
1248
  "ui:tree-view",
1298
1249
  "ui:u",
1299
1250
  "ui:ul",
1251
+ "ui:utility",
1300
1252
  "ul",
1301
- "url",
1302
- "user",
1303
- "user:profile",
1253
+ "usage-bar",
1304
1254
  "utility",
1305
- "vector",
1306
- "view",
1307
1255
  "view-switcher",
1308
1256
  "view:calendar",
1309
1257
  "view:chart",
1310
- "view:dashboard",
1311
1258
  "view:detail",
1312
1259
  "view:drawer",
1313
1260
  "view:filter-ui",
@@ -1319,13 +1266,11 @@ var KNOWN_SCHEMA_TYPES = [
1319
1266
  "view:list",
1320
1267
  "view:map",
1321
1268
  "view:modal",
1322
- "view:shared-view-link",
1323
1269
  "view:simple",
1324
1270
  "view:sort-ui",
1325
1271
  "view:split",
1326
1272
  "view:tabbed",
1327
1273
  "view:timeline",
1328
- "view:tree",
1329
1274
  "view:view-switcher",
1330
1275
  "view:wizard",
1331
1276
  "widget:bar",
@@ -1346,9 +1291,93 @@ function isKnownSchemaType(type) {
1346
1291
  return KNOWN_SCHEMA_TYPE_SET.has(type);
1347
1292
  }
1348
1293
 
1294
+ // src/utils/known-type-case-suggestion.ts
1295
+ function suggestKnownTypeByCase(type) {
1296
+ if (typeof type !== "string" || type === "") return void 0;
1297
+ const wanted = type.toLowerCase();
1298
+ for (const candidate of KNOWN_SCHEMA_TYPES) {
1299
+ if (candidate !== type && candidate.toLowerCase() === wanted) return candidate;
1300
+ }
1301
+ return void 0;
1302
+ }
1303
+ function didYouMeanClause(type) {
1304
+ const match = suggestKnownTypeByCase(type);
1305
+ return match === void 0 ? "" : ` \u2014 did you mean "${match}"?`;
1306
+ }
1307
+
1308
+ // src/utils/unbindable-text-expressions.ts
1309
+ import {
1310
+ EXPRESSION_BINDABLE_TEXT_KEYS,
1311
+ expressionBindableTextKeysFor
1312
+ } from "@objectstack/spec/ui";
1313
+ import { nodeSlotValues, nodeSlotsFor } from "@object-ui/types";
1314
+ var EXPRESSION_PATTERN = /\$\{[^}]+\}/;
1315
+ var COMPOSITION_KEY = "children";
1316
+ var PAGE_DOCUMENT_ROOT_TYPE = "page";
1317
+ function isComponentNode(value) {
1318
+ return typeof value === "object" && value !== null && !Array.isArray(value) && typeof value.type === "string";
1319
+ }
1320
+ function judge(node, path, into) {
1321
+ const evaluatedKeys = expressionBindableTextKeysFor(node.type);
1322
+ for (const key of EXPRESSION_BINDABLE_TEXT_KEYS) {
1323
+ const value = node[key];
1324
+ if (typeof value !== "string" || !EXPRESSION_PATTERN.test(value)) continue;
1325
+ if (evaluatedKeys.includes(key)) continue;
1326
+ into.push({
1327
+ severity: isKnownSchemaType(node.type) ? "refusal" : "warning",
1328
+ path: [...path, key],
1329
+ type: node.type,
1330
+ key,
1331
+ evaluatedKeys
1332
+ });
1333
+ }
1334
+ }
1335
+ function visit(node, path, isDocumentRoot, into) {
1336
+ if (!(isDocumentRoot && node.type === PAGE_DOCUMENT_ROOT_TYPE)) {
1337
+ judge(node, path, into);
1338
+ }
1339
+ const children = node[COMPOSITION_KEY];
1340
+ if (Array.isArray(children)) {
1341
+ children.forEach((child, index) => {
1342
+ if (isComponentNode(child)) visit(child, [...path, COMPOSITION_KEY, index], false, into);
1343
+ });
1344
+ } else if (isComponentNode(children)) {
1345
+ visit(children, [...path, COMPOSITION_KEY], false, into);
1346
+ }
1347
+ for (const slot of nodeSlotsFor(node.type)) {
1348
+ for (const { segments, value } of nodeSlotValues(node, slot.path)) {
1349
+ if (isComponentNode(value)) visit(value, [...path, ...segments], false, into);
1350
+ }
1351
+ }
1352
+ }
1353
+ function findUnbindableTextExpressions(document) {
1354
+ const found = [];
1355
+ if (isComponentNode(document)) visit(document, [], true, found);
1356
+ return found;
1357
+ }
1358
+ var inCode = (key) => "`" + key + "`";
1359
+ function describeUnbindableTextExpression(finding) {
1360
+ const at = formatIssuePath(finding.path);
1361
+ if (finding.severity === "warning") {
1362
+ 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.`;
1363
+ }
1364
+ 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(", ")}`;
1365
+ 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.`;
1366
+ }
1367
+ function workingChannels(finding) {
1368
+ const channels = [
1369
+ ...finding.evaluatedKeys.map(inCode),
1370
+ inCode("content"),
1371
+ `${inCode(`properties.${finding.key}`)} (evaluated, then hoisted onto the node)`
1372
+ ];
1373
+ return `Channels SchemaRenderer evaluates: ${channels.join(", ")}; or resolve the value in the host before the schema reaches SchemaRenderer.`;
1374
+ }
1375
+
1349
1376
  // src/commands/check.ts
1350
1377
  var OBJECTUI_STRUCTURAL_KEYS = [
1351
1378
  // Composition — the `children`/`body`-class keys the ruling names.
1379
+ // `body` is the RETIRED spelling, kept so old files are still identified and
1380
+ // then refused — see the docblock above (objectui#6771).
1352
1381
  "body",
1353
1382
  "children",
1354
1383
  // Presentation.
@@ -1367,8 +1396,24 @@ var OBJECTUI_STRUCTURAL_KEYS = [
1367
1396
  "testId",
1368
1397
  "ariaLabel"
1369
1398
  ];
1370
- function isObjectUiSchemaFile(content) {
1371
- return OBJECTUI_STRUCTURAL_KEYS.some((key) => key in content);
1399
+ function recogniseObjectUiSchemaFile(content) {
1400
+ if (OBJECTUI_STRUCTURAL_KEYS.some((key) => key in content)) {
1401
+ return { recognised: true, validated: false };
1402
+ }
1403
+ const result = validateAuthoredDocument(content);
1404
+ return result.success ? { recognised: true, validated: true } : { recognised: false, issues: result.error.issues };
1405
+ }
1406
+ function describeFirstIssue(issues) {
1407
+ const [first] = issues;
1408
+ if (first === void 0) return void 0;
1409
+ const lead = issues.length === 1 ? "Issue" : `First of ${issues.length} issues`;
1410
+ return `${lead} at ${formatIssuePath(first.path)}: ${first.message}`;
1411
+ }
1412
+ function closingLine(tally) {
1413
+ 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.`;
1414
+ }
1415
+ function isUnvalidatedCandidate(type) {
1416
+ return isKnownSchemaType(type);
1372
1417
  }
1373
1418
  function describeParseError(text, error) {
1374
1419
  const upTo = text.slice(0, error.offset);
@@ -1377,69 +1422,158 @@ function describeParseError(text, error) {
1377
1422
  return `${printParseErrorCode(error.error)} at line ${line} column ${column}`;
1378
1423
  }
1379
1424
  async function check(cwd = process.cwd()) {
1380
- console.log(chalk10.bold("Object UI Schema Check"));
1425
+ console.log(chalk6.bold("Object UI Schema Check"));
1381
1426
  const files = globSync("**/*.{json,yaml,yml}", {
1382
1427
  cwd,
1383
- ignore: ["node_modules/**", "dist/**", ".git/**"]
1428
+ // `glob` matches `ignore` patterns against the path relative to `cwd`, so
1429
+ // an unanchored `dist/**`/`node_modules/**` excludes only a directory of
1430
+ // that name at the scan root — every nested `packages/*/dist/`,
1431
+ // `examples/*/dist/`, `apps/*/dist/` (and their `node_modules/`) is still
1432
+ // scanned. `**/` anchors the match at any depth (objectui#6320): measured
1433
+ // on this repository, a built tree without it reports roughly double the
1434
+ // files of a clean checkout, almost all of it re-reading the author's own
1435
+ // schemas from build output.
1436
+ ignore: ["**/node_modules/**", "**/dist/**", ".git/**"]
1384
1437
  });
1385
1438
  console.log(`Analyzing ${files.length} files...`);
1386
1439
  let errors = 0;
1387
1440
  let skipped = 0;
1441
+ const unvalidatedCandidates = [];
1442
+ let validated = 0;
1443
+ let notValidated = 0;
1444
+ const judgeTextExpressions = (file, document) => {
1445
+ for (const finding of findUnbindableTextExpressions(document)) {
1446
+ const line = `${file} ${describeUnbindableTextExpression(finding)}`;
1447
+ if (finding.severity === "refusal") {
1448
+ console.log(chalk6.red(`x Unevaluated expression in ${line}`));
1449
+ console.log(chalk6.dim(` ${workingChannels(finding)}`));
1450
+ errors++;
1451
+ } else {
1452
+ console.log(chalk6.yellow(`\u26A0\uFE0F Expression not judged in ${line}`));
1453
+ }
1454
+ }
1455
+ };
1388
1456
  for (const file of files) {
1389
1457
  try {
1390
1458
  if (file.endsWith(".json")) {
1391
- const text = readFileSync2(join9(cwd, file), "utf-8");
1459
+ const text = readFileSync2(join6(cwd, file), "utf-8");
1392
1460
  const parseErrors = [];
1393
1461
  const content = parseJsonc(text, parseErrors, { allowTrailingComma: true });
1394
1462
  if (parseErrors.length > 0) {
1395
1463
  const [first] = parseErrors;
1396
1464
  console.log(
1397
- chalk10.red(`x Invalid JSON in ${file}: ${describeParseError(text, first)}`)
1465
+ chalk6.red(`x Invalid JSON in ${file}: ${describeParseError(text, first)}`)
1398
1466
  );
1399
1467
  errors++;
1400
1468
  continue;
1401
1469
  }
1402
1470
  if (content && typeof content === "object" && content.type) {
1403
1471
  if (typeof content.type === "string") {
1404
- if (!isObjectUiSchemaFile(content)) {
1405
- skipped++;
1406
- } else if (!isKnownSchemaType(content.type)) {
1407
- console.log(chalk10.yellow(`\u26A0\uFE0F Unknown schema type "${content.type}" in ${file}`));
1472
+ const recognition = recogniseObjectUiSchemaFile(
1473
+ content
1474
+ );
1475
+ if (!recognition.recognised) {
1476
+ if (isUnvalidatedCandidate(content.type)) {
1477
+ unvalidatedCandidates.push({
1478
+ file,
1479
+ type: content.type,
1480
+ issues: recognition.issues,
1481
+ document: content
1482
+ });
1483
+ judgeTextExpressions(file, content);
1484
+ } else {
1485
+ skipped++;
1486
+ }
1487
+ continue;
1408
1488
  }
1489
+ if (recognition.validated) {
1490
+ validated++;
1491
+ } else {
1492
+ notValidated++;
1493
+ }
1494
+ if (!isKnownSchemaType(content.type)) {
1495
+ console.log(
1496
+ chalk6.yellow(
1497
+ `\u26A0\uFE0F Unknown schema type "${content.type}" in ${file}` + didYouMeanClause(content.type)
1498
+ )
1499
+ );
1500
+ }
1501
+ judgeTextExpressions(file, content);
1409
1502
  }
1410
1503
  }
1411
1504
  }
1412
1505
  } catch (e) {
1413
- console.log(chalk10.red(`x Invalid JSON in ${file}: ${e.message}`));
1506
+ console.log(chalk6.red(`x Invalid JSON in ${file}: ${e.message}`));
1414
1507
  errors++;
1415
1508
  }
1416
1509
  }
1510
+ if (unvalidatedCandidates.length > 0) {
1511
+ const n = unvalidatedCandidates.length;
1512
+ console.log(
1513
+ chalk6.yellow(
1514
+ `\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:`
1515
+ )
1516
+ );
1517
+ for (const { file, type, issues, document } of unvalidatedCandidates) {
1518
+ console.log(chalk6.yellow(` ${file} (type "${type}")`));
1519
+ const firstIssue = describeFirstIssue(issues);
1520
+ if (firstIssue !== void 0) {
1521
+ console.log(chalk6.yellow(` ${firstIssue}`));
1522
+ }
1523
+ for (const finding of findUndeclaredKeys(issues)) {
1524
+ console.log(chalk6.yellow(` ${describeUndeclaredKey(finding, document)}`));
1525
+ }
1526
+ }
1527
+ console.log(
1528
+ chalk6.dim(" Run `objectui validate <file>` on any of them for the full diagnosis.")
1529
+ );
1530
+ console.log(
1531
+ chalk6.dim(
1532
+ " Either the document is off-spec, or its component type is not modelled by @object-ui/types."
1533
+ )
1534
+ );
1535
+ console.log(
1536
+ chalk6.dim(
1537
+ " These are NOT counted as skipped: a schema that fails validation is a broken schema, not a foreign file."
1538
+ )
1539
+ );
1540
+ }
1417
1541
  if (skipped > 0) {
1418
1542
  console.log(
1419
- chalk10.dim(
1420
- `Skipped ${skipped} file${skipped === 1 ? "" : "s"} with a root "type" and no ObjectUI schema marker.`
1543
+ chalk6.dim(
1544
+ `Skipped ${skipped} file${skipped === 1 ? "" : "s"} with a root "type" that no ObjectUI recogniser admitted.`
1421
1545
  )
1422
1546
  );
1423
1547
  console.log(
1424
- chalk10.dim(
1548
+ chalk6.dim(
1425
1549
  ` A file is checked when its root carries an ObjectUI structural key: ${OBJECTUI_STRUCTURAL_KEYS.join(", ")}.`
1426
1550
  )
1427
1551
  );
1552
+ console.log(
1553
+ chalk6.dim(
1554
+ " It is also checked when the whole document validates as an ObjectUI component schema."
1555
+ )
1556
+ );
1428
1557
  }
1429
1558
  if (errors === 0) {
1430
- console.log(chalk10.green("\u2713 All checks passed"));
1559
+ console.log(
1560
+ closingLine({
1561
+ validated,
1562
+ notValidated,
1563
+ didNotValidate: unvalidatedCandidates.length
1564
+ })
1565
+ );
1431
1566
  } else {
1432
- console.log(chalk10.red(`Found ${errors} errors`));
1567
+ console.log(chalk6.red(`Found ${errors} errors`));
1433
1568
  process.exit(1);
1434
1569
  }
1435
1570
  }
1436
1571
 
1437
1572
  // src/commands/validate.ts
1438
- import chalk11 from "chalk";
1439
- import { readFileSync as readFileSync3, existsSync as existsSync8 } from "fs";
1440
- import { resolve as resolve4 } from "path";
1573
+ import chalk7 from "chalk";
1574
+ import { readFileSync as readFileSync3, existsSync as existsSync5 } from "fs";
1575
+ import { resolve as resolve3 } from "path";
1441
1576
  import { load as loadYaml } from "js-yaml";
1442
- import { safeValidateSchema } from "@object-ui/types/zod";
1443
1577
 
1444
1578
  // src/utils/spec-vocabulary-hint.ts
1445
1579
  function findSpecVocabularyFormFields(node, path = "") {
@@ -1470,81 +1604,165 @@ function findSpecVocabularyFormFields(node, path = "") {
1470
1604
  return findings;
1471
1605
  }
1472
1606
 
1607
+ // src/utils/record-block-binding-hint.ts
1608
+ var ONE_RECORD_BLOCK_TYPES = /* @__PURE__ */ new Set([
1609
+ "record:details",
1610
+ "record:highlights",
1611
+ "record:quick_actions"
1612
+ ]);
1613
+ var PARENT_BINDING_BLOCK_TYPE = "record:related_list";
1614
+ var isRecord = (value) => !!value && typeof value === "object" && !Array.isArray(value);
1615
+ var unboundedObjectOf = (binding) => isRecord(binding) && typeof binding.object === "string" && binding.object.length > 0 && binding.limit !== 1 ? binding.object : void 0;
1616
+ function findRecordBindingsWithoutLimitOne(document, path = []) {
1617
+ if (Array.isArray(document)) {
1618
+ return document.flatMap((child, i) => findRecordBindingsWithoutLimitOne(child, [...path, i]));
1619
+ }
1620
+ if (!isRecord(document)) return [];
1621
+ const findings = [];
1622
+ if (typeof document.type === "string" && ONE_RECORD_BLOCK_TYPES.has(document.type)) {
1623
+ const object = unboundedObjectOf(document.dataSource);
1624
+ if (object) findings.push({ path: formatIssuePath(path), type: document.type, object, key: "dataSource" });
1625
+ }
1626
+ if (document.type === PARENT_BINDING_BLOCK_TYPE && isRecord(document.properties)) {
1627
+ const object = unboundedObjectOf(document.properties.parentDataSource);
1628
+ if (object) {
1629
+ findings.push({ path: formatIssuePath(path), type: document.type, object, key: "properties.parentDataSource" });
1630
+ }
1631
+ }
1632
+ for (const [key, value] of Object.entries(document)) {
1633
+ findings.push(...findRecordBindingsWithoutLimitOne(value, [...path, key]));
1634
+ }
1635
+ return findings;
1636
+ }
1637
+ function describeRecordBindingWithoutLimitOne(finding) {
1638
+ if (finding.key === "properties.parentDataSource") {
1639
+ return `${finding.path}: ${finding.type}'s parent record is bound to "${finding.object}" by a \`properties.parentDataSource\` without \`limit: 1\`. A related list hangs off ONE parent record \u2014 the first row of the binding by its \`sort\` \u2014 so declare \`limit: 1\` (and a \`sort\` when several rows can match) to say which record that is.`;
1640
+ }
1641
+ return `${finding.path}: ${finding.type} is bound to "${finding.object}" by a dataSource without \`limit: 1\`. A record block shows ONE record \u2014 the first row of the binding by its \`sort\` \u2014 so declare \`limit: 1\` (and a \`sort\` when several rows can match) to say which record that is.`;
1642
+ }
1643
+
1473
1644
  // src/commands/validate.ts
1474
1645
  async function validate(schemaPath) {
1475
- console.log(chalk11.blue("\u{1F50D} ObjectUI Schema Validator\n"));
1476
- const resolvedPath = resolve4(process.cwd(), schemaPath);
1477
- if (!existsSync8(resolvedPath)) {
1478
- console.error(chalk11.red(`\u2717 Error: Schema file not found: ${schemaPath}`));
1646
+ console.log(chalk7.blue("\u{1F50D} ObjectUI Schema Validator\n"));
1647
+ const resolvedPath = resolve3(process.cwd(), schemaPath);
1648
+ if (!existsSync5(resolvedPath)) {
1649
+ console.error(chalk7.red(`\u2717 Error: Schema file not found: ${schemaPath}`));
1479
1650
  process.exit(1);
1480
1651
  }
1481
1652
  try {
1482
1653
  const fileContent = readFileSync3(resolvedPath, "utf-8");
1483
1654
  let schema;
1484
1655
  if (schemaPath.endsWith(".yaml") || schemaPath.endsWith(".yml")) {
1485
- console.log(chalk11.gray(`Reading YAML schema from: ${schemaPath}`));
1656
+ console.log(chalk7.gray(`Reading YAML schema from: ${schemaPath}`));
1486
1657
  schema = loadYaml(fileContent);
1487
1658
  } else if (schemaPath.endsWith(".json")) {
1488
- console.log(chalk11.gray(`Reading JSON schema from: ${schemaPath}`));
1659
+ console.log(chalk7.gray(`Reading JSON schema from: ${schemaPath}`));
1489
1660
  schema = JSON.parse(fileContent);
1490
1661
  } else {
1491
1662
  try {
1492
1663
  schema = JSON.parse(fileContent);
1493
- console.log(chalk11.gray(`Reading schema from: ${schemaPath} (detected as JSON)`));
1664
+ console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as JSON)`));
1494
1665
  } catch {
1495
1666
  schema = loadYaml(fileContent);
1496
- console.log(chalk11.gray(`Reading schema from: ${schemaPath} (detected as YAML)`));
1667
+ console.log(chalk7.gray(`Reading schema from: ${schemaPath} (detected as YAML)`));
1497
1668
  }
1498
1669
  }
1499
- console.log(chalk11.gray("Validating schema...\n"));
1500
- const result = safeValidateSchema(schema);
1670
+ console.log(chalk7.gray("Validating schema...\n"));
1671
+ const result = validateAuthoredDocument(schema);
1501
1672
  if (result.success) {
1502
- console.log(chalk11.green("\u2713 Schema is valid!\n"));
1673
+ console.log(chalk7.green("\u2713 Schema is valid!\n"));
1503
1674
  const data = result.data;
1504
- console.log(chalk11.bold("Schema Information:"));
1505
- console.log(chalk11.gray(" Type:"), data.type || "unknown");
1675
+ console.log(chalk7.bold("Schema Information:"));
1676
+ console.log(chalk7.gray(" Type:"), data.type || "unknown");
1506
1677
  if (data.id) {
1507
- console.log(chalk11.gray(" ID:"), data.id);
1678
+ console.log(chalk7.gray(" ID:"), data.id);
1508
1679
  }
1509
- if (data.label || data.title) {
1510
- console.log(chalk11.gray(" Label:"), data.label || data.title);
1680
+ const title = "title" in data ? data.title : void 0;
1681
+ if (data.label || title) {
1682
+ console.log(chalk7.gray(" Label:"), data.label || title);
1511
1683
  }
1512
1684
  if (data.children && Array.isArray(data.children)) {
1513
- console.log(chalk11.gray(" Children:"), data.children.length);
1685
+ console.log(chalk7.gray(" Children:"), data.children.length);
1514
1686
  }
1515
1687
  const mixed = findSpecVocabularyFormFields(schema).filter((f) => f.mixedName);
1516
- if (mixed.length > 0) {
1517
- console.log(chalk11.yellow("\nWarnings:"));
1688
+ const unboundedRecordBindings = findRecordBindingsWithoutLimitOne(schema);
1689
+ if (mixed.length > 0 || unboundedRecordBindings.length > 0) {
1690
+ console.log(chalk7.yellow("\nWarnings:"));
1518
1691
  for (const m of mixed) {
1519
- console.log(chalk11.yellow(
1692
+ console.log(chalk7.yellow(
1520
1693
  ` ${m.path}: '${m.mixedName}' also carries { field: '${m.field}' } \u2014 mixed form-field vocabularies. The renderer ignores \`field\` here; drop one of the two.`
1521
1694
  ));
1522
1695
  }
1696
+ for (const finding of unboundedRecordBindings) {
1697
+ console.log(chalk7.yellow(` ${describeRecordBindingWithoutLimitOne(finding)}`));
1698
+ }
1523
1699
  }
1524
1700
  console.log("");
1525
1701
  process.exit(0);
1526
1702
  } else {
1527
- console.error(chalk11.red("\u2717 Schema validation failed!\n"));
1528
- console.error(chalk11.bold("Validation Errors:"));
1703
+ console.error(chalk7.red("\u2717 Schema validation failed!\n"));
1704
+ console.error(chalk7.bold("Validation Errors:"));
1529
1705
  result.error.issues.forEach((issue, index) => {
1530
- console.error(chalk11.red(`
1706
+ console.error(chalk7.red(`
1531
1707
  ${index + 1}. ${issue.message}`));
1532
- if (issue.path && issue.path.length > 0) {
1533
- console.error(chalk11.gray(` Path: ${issue.path.join(" \u2192 ")}`));
1534
- }
1708
+ console.error(chalk7.gray(` Path: ${formatIssuePath(issue.path)}`));
1535
1709
  if (issue.code) {
1536
- console.error(chalk11.gray(` Code: ${issue.code}`));
1710
+ console.error(chalk7.gray(` Code: ${issue.code}`));
1537
1711
  }
1712
+ explainUnionIssue(issue, schema).forEach((line, sub) => {
1713
+ const where = formatIssuePath(line.path);
1714
+ if (line.kind === "note") {
1715
+ const at = line.path.length > 0 ? ` at ${where}` : "";
1716
+ if (line.authoredType === void 0) {
1717
+ console.error(
1718
+ chalk7.yellow(
1719
+ ` No \`type\` is declared${at}, so none of the ${line.totalArmNames} accepted component types can be selected.`
1720
+ )
1721
+ );
1722
+ return;
1723
+ }
1724
+ console.error(
1725
+ chalk7.yellow(` No arm accepts type "${line.authoredType}"${at}.`)
1726
+ );
1727
+ if (line.candidates.length > 0) {
1728
+ console.error(
1729
+ chalk7.gray(
1730
+ ` Nearest of the ${line.totalArmNames} accepted types: ` + line.candidates.join(", ")
1731
+ )
1732
+ );
1733
+ }
1734
+ return;
1735
+ }
1736
+ const arm = line.arm === void 0 ? "" : `[arm ${line.arm}] `;
1737
+ console.error(chalk7.red(` ${index + 1}.${sub + 1} ${arm}${line.message}`));
1738
+ console.error(chalk7.gray(` Path: ${where}`));
1739
+ if (line.code) {
1740
+ console.error(chalk7.gray(` Code: ${line.code}`));
1741
+ }
1742
+ });
1538
1743
  });
1744
+ const undeclared = findUndeclaredKeys(result.error.issues);
1745
+ if (undeclared.length > 0) {
1746
+ console.error(chalk7.bold("\nUndeclared keys \u2014 the strict authoring face refuses them:"));
1747
+ for (const finding of undeclared) {
1748
+ console.error(chalk7.yellow(` ${describeUndeclaredKey(finding, schema)}`));
1749
+ }
1750
+ }
1751
+ const mixedHere = findSpecVocabularyFormFields(schema).filter((f) => f.mixedName);
1752
+ for (const m of mixedHere) {
1753
+ console.error(chalk7.yellow(
1754
+ ` ${m.path}: '${m.mixedName}' also carries { field: '${m.field}' } \u2014 mixed form-field vocabularies. The renderer ignores \`field\` here; drop one of the two.`
1755
+ ));
1756
+ }
1539
1757
  const specShaped = findSpecVocabularyFormFields(schema).filter((f) => !f.mixedName);
1540
1758
  if (specShaped.length > 0) {
1541
- console.error(chalk11.bold("\nLikely cause \u2014 spec form-view vocabulary in a standalone form:"));
1759
+ console.error(chalk7.bold("\nLikely cause \u2014 spec form-view vocabulary in a standalone form:"));
1542
1760
  for (const s of specShaped) {
1543
- console.error(chalk11.yellow(
1761
+ console.error(chalk7.yellow(
1544
1762
  ` ${s.path}: { field: '${s.field}' } is the spec form-VIEW shape (an object-field reference).`
1545
1763
  ));
1546
1764
  }
1547
- console.error(chalk11.yellow(
1765
+ console.error(chalk7.yellow(
1548
1766
  ` A standalone \`type: 'form'\` component uses { name: \u2026 } (the form data path).
1549
1767
  Fix: author the field with \`name\` and its own type/label \u2014 or, to reference object fields
1550
1768
  with the spec shape, use an object-bound form (\`type: 'object-form'\` with objectName +
@@ -1555,61 +1773,28 @@ ${index + 1}. ${issue.message}`));
1555
1773
  process.exit(1);
1556
1774
  }
1557
1775
  } catch (error) {
1558
- console.error(chalk11.red("\n\u2717 Error reading or parsing schema file:"));
1559
- console.error(chalk11.red(error.message));
1776
+ console.error(chalk7.red("\n\u2717 Error reading or parsing schema file:"));
1777
+ console.error(chalk7.red(error.message));
1560
1778
  console.error("");
1561
1779
  process.exit(1);
1562
1780
  }
1563
1781
  }
1564
1782
 
1565
- // src/commands/create-plugin.ts
1566
- import chalk12 from "chalk";
1567
- import { spawn as spawn2 } from "child_process";
1568
- import { resolve as resolve5, dirname as dirname3 } from "path";
1569
- import { fileURLToPath as fileURLToPath2 } from "url";
1570
- var __filename = fileURLToPath2(import.meta.url);
1571
- var __dirname = dirname3(__filename);
1572
- async function createPlugin(pluginName) {
1573
- console.log(chalk12.blue("\u{1F680} Creating ObjectUI plugin...\n"));
1574
- const createPluginScript = resolve5(
1575
- __dirname,
1576
- "../../../create-plugin/dist/index.js"
1577
- );
1578
- return new Promise((resolve7, reject) => {
1579
- const child = spawn2("node", [createPluginScript, pluginName], {
1580
- stdio: "inherit",
1581
- shell: true
1582
- });
1583
- child.on("error", (error) => {
1584
- console.error(chalk12.red("Failed to create plugin:"), error);
1585
- reject(error);
1586
- });
1587
- child.on("exit", (code) => {
1588
- if (code === 0) {
1589
- resolve7();
1590
- } else {
1591
- reject(new Error(`create-plugin exited with code ${code}`));
1592
- }
1593
- });
1594
- });
1595
- }
1596
-
1597
1783
  // src/commands/analyze.ts
1598
- import chalk13 from "chalk";
1599
- import { existsSync as existsSync9, statSync, readdirSync } from "fs";
1600
- import { resolve as resolve6, join as join10, extname } from "path";
1784
+ import chalk8 from "chalk";
1785
+ import { existsSync as existsSync6, statSync, readdirSync } from "fs";
1786
+ import { resolve as resolve4, join as join7, extname } from "path";
1601
1787
  async function analyzeBundleSize() {
1602
- console.log(chalk13.bold("\n\u{1F4E6} Bundle Size Analysis\n"));
1603
- const distDir = resolve6(process.cwd(), "dist");
1604
- if (!existsSync9(distDir)) {
1605
- console.log(chalk13.yellow("\u26A0 No dist directory found. Run build first."));
1606
- return;
1788
+ console.log(chalk8.bold("\n\u{1F4E6} Bundle Size Analysis\n"));
1789
+ const distDir = resolve4(process.cwd(), "dist");
1790
+ if (!existsSync6(distDir)) {
1791
+ throw new Error("No dist/ directory to analyze. Run `objectui build` first.");
1607
1792
  }
1608
1793
  const files = [];
1609
1794
  function scanDirectory(dir) {
1610
1795
  const items = readdirSync(dir);
1611
1796
  for (const item of items) {
1612
- const fullPath = join10(dir, item);
1797
+ const fullPath = join7(dir, item);
1613
1798
  const stat = statSync(fullPath);
1614
1799
  if (stat.isDirectory()) {
1615
1800
  scanDirectory(fullPath);
@@ -1628,48 +1813,34 @@ async function analyzeBundleSize() {
1628
1813
  const cssFiles = files.filter((f) => extname(f.path) === ".css");
1629
1814
  const jsSize = jsFiles.reduce((sum, file) => sum + file.size, 0);
1630
1815
  const cssSize = cssFiles.reduce((sum, file) => sum + file.size, 0);
1631
- console.log(chalk13.bold("Summary:"));
1632
- console.log(chalk13.gray(" Total Size:"), formatBytes(totalSize));
1633
- console.log(chalk13.gray(" JavaScript:"), formatBytes(jsSize), chalk13.dim(`(${jsFiles.length} files)`));
1634
- console.log(chalk13.gray(" CSS: "), formatBytes(cssSize), chalk13.dim(`(${cssFiles.length} files)`));
1635
- console.log(chalk13.gray(" Other: "), formatBytes(totalSize - jsSize - cssSize));
1636
- console.log(chalk13.bold("\nLargest Files:"));
1816
+ console.log(chalk8.bold("Summary:"));
1817
+ console.log(chalk8.gray(" Total Size:"), formatBytes(totalSize));
1818
+ console.log(chalk8.gray(" JavaScript:"), formatBytes(jsSize), chalk8.dim(`(${jsFiles.length} files)`));
1819
+ console.log(chalk8.gray(" CSS: "), formatBytes(cssSize), chalk8.dim(`(${cssFiles.length} files)`));
1820
+ console.log(chalk8.gray(" Other: "), formatBytes(totalSize - jsSize - cssSize));
1821
+ console.log(chalk8.bold("\nLargest Files:"));
1637
1822
  files.slice(0, 10).forEach((file) => {
1638
1823
  const sizeStr = formatBytes(file.size).padStart(10);
1639
- console.log(chalk13.gray(` ${sizeStr}`), file.path);
1824
+ console.log(chalk8.gray(` ${sizeStr}`), file.path);
1640
1825
  });
1641
- console.log(chalk13.bold("\n\u{1F4A1} Recommendations:"));
1826
+ console.log(chalk8.bold("\n\u{1F4A1} Recommendations:"));
1642
1827
  if (totalSize > 1024 * 1024) {
1643
- console.log(chalk13.yellow(" \u26A0 Total bundle size is large (> 1MB)"));
1644
- console.log(chalk13.gray(" Consider code splitting or lazy loading"));
1828
+ console.log(chalk8.yellow(" \u26A0 Total bundle size is large (> 1MB)"));
1829
+ console.log(chalk8.gray(" Consider code splitting or lazy loading"));
1645
1830
  }
1646
1831
  if (jsSize > 500 * 1024) {
1647
- console.log(chalk13.yellow(" \u26A0 JavaScript bundle is large (> 500KB)"));
1648
- console.log(chalk13.gray(" Consider:"));
1649
- console.log(chalk13.gray(" - Tree shaking unused code"));
1650
- console.log(chalk13.gray(" - Lazy loading components"));
1651
- console.log(chalk13.gray(" - Using dynamic imports"));
1832
+ console.log(chalk8.yellow(" \u26A0 JavaScript bundle is large (> 500KB)"));
1833
+ console.log(chalk8.gray(" Consider:"));
1834
+ console.log(chalk8.gray(" - Tree shaking unused code"));
1835
+ console.log(chalk8.gray(" - Lazy loading components"));
1836
+ console.log(chalk8.gray(" - Using dynamic imports"));
1652
1837
  }
1653
1838
  if (files.length > 100) {
1654
- console.log(chalk13.yellow(` \u26A0 Large number of files (${files.length})`));
1655
- console.log(chalk13.gray(" Consider bundling or combining files"));
1839
+ console.log(chalk8.yellow(` \u26A0 Large number of files (${files.length})`));
1840
+ console.log(chalk8.gray(" Consider bundling or combining files"));
1656
1841
  }
1657
1842
  console.log("");
1658
1843
  }
1659
- async function analyzeRenderPerformance() {
1660
- console.log(chalk13.bold("\n\u26A1 Render Performance Analysis\n"));
1661
- console.log(chalk13.gray("Performance analysis features:"));
1662
- console.log(chalk13.gray(" \u2713 Expression caching enabled"));
1663
- console.log(chalk13.gray(" \u2713 Component memoization available"));
1664
- console.log(chalk13.gray(" \u2713 Virtual scrolling support for large lists"));
1665
- console.log(chalk13.bold("\n\u{1F4A1} Performance Tips:"));
1666
- console.log(chalk13.gray(" \u2022 Use virtual scrolling for lists > 100 items"));
1667
- console.log(chalk13.gray(" \u2022 Cache frequently evaluated expressions"));
1668
- console.log(chalk13.gray(" \u2022 Use React.memo for expensive components"));
1669
- console.log(chalk13.gray(" \u2022 Implement pagination for large datasets"));
1670
- console.log(chalk13.gray(" \u2022 Use code splitting for large apps"));
1671
- console.log("");
1672
- }
1673
1844
  function formatBytes(bytes) {
1674
1845
  if (bytes === 0) return "0 B";
1675
1846
  const k = 1024;
@@ -1677,26 +1848,20 @@ function formatBytes(bytes) {
1677
1848
  const i = Math.floor(Math.log(bytes) / Math.log(k));
1678
1849
  return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`;
1679
1850
  }
1680
- async function analyze(options = {}) {
1681
- console.log(chalk13.blue("\u{1F50D} ObjectUI Performance Analyzer\n"));
1682
- const runAll = !options.bundleSize && !options.renderPerformance;
1683
- if (options.bundleSize || runAll) {
1684
- await analyzeBundleSize();
1685
- }
1686
- if (options.renderPerformance || runAll) {
1687
- await analyzeRenderPerformance();
1688
- }
1689
- console.log(chalk13.green("\u2713 Analysis complete!\n"));
1851
+ async function analyze() {
1852
+ console.log(chalk8.blue("\u{1F50D} ObjectUI Performance Analyzer\n"));
1853
+ await analyzeBundleSize();
1854
+ console.log(chalk8.green("\u2713 Analysis complete!\n"));
1690
1855
  }
1691
1856
 
1692
1857
  // src/cli.ts
1693
1858
  import { readFileSync as readFileSync4 } from "fs";
1694
- import { fileURLToPath as fileURLToPath3 } from "url";
1695
- import { dirname as dirname4, join as join11 } from "path";
1696
- var __filename2 = fileURLToPath3(import.meta.url);
1697
- var __dirname2 = dirname4(__filename2);
1859
+ import { fileURLToPath } from "url";
1860
+ import { dirname as dirname2, join as join8 } from "path";
1861
+ var __filename = fileURLToPath(import.meta.url);
1862
+ var __dirname = dirname2(__filename);
1698
1863
  var packageJson = JSON.parse(
1699
- readFileSync4(join11(__dirname2, "../package.json"), "utf-8")
1864
+ readFileSync4(join8(__dirname, "../package.json"), "utf-8")
1700
1865
  );
1701
1866
  var program = new Command();
1702
1867
  program.name("objectui").description("CLI tool for Object UI - Build applications from JSON schemas").version(packageJson.version);
@@ -1704,7 +1869,7 @@ program.command("serve").description("Start a development server with your JSON/
1704
1869
  try {
1705
1870
  await serve(schema, options);
1706
1871
  } catch (error) {
1707
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1872
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1708
1873
  process.exit(1);
1709
1874
  }
1710
1875
  });
@@ -1712,7 +1877,7 @@ program.command("dev").description("Start development server (alias for serve)")
1712
1877
  try {
1713
1878
  await dev(schema, options);
1714
1879
  } catch (error) {
1715
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1880
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1716
1881
  process.exit(1);
1717
1882
  }
1718
1883
  });
@@ -1720,7 +1885,7 @@ program.command("build").description("Build application for production").argumen
1720
1885
  try {
1721
1886
  await buildApp(schema, options);
1722
1887
  } catch (error) {
1723
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1888
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1724
1889
  process.exit(1);
1725
1890
  }
1726
1891
  });
@@ -1728,7 +1893,7 @@ program.command("start").description("Start production server").option("-p, --po
1728
1893
  try {
1729
1894
  await start(options);
1730
1895
  } catch (error) {
1731
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1896
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1732
1897
  process.exit(1);
1733
1898
  }
1734
1899
  });
@@ -1736,68 +1901,31 @@ program.command("init").description("Initialize a new Object UI application with
1736
1901
  try {
1737
1902
  await init(name, options);
1738
1903
  } catch (error) {
1739
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1740
- process.exit(1);
1741
- }
1742
- });
1743
- program.command("lint").description("Lint the generated application code").option("--fix", "Automatically fix linting issues").action(async (options) => {
1744
- try {
1745
- await lint(options);
1746
- } catch (error) {
1747
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1748
- process.exit(1);
1749
- }
1750
- });
1751
- 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) => {
1752
- try {
1753
- await test(options);
1754
- } catch (error) {
1755
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1904
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1756
1905
  process.exit(1);
1757
1906
  }
1758
1907
  });
1759
- 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) => {
1908
+ 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) => {
1760
1909
  try {
1761
- if (options.from) {
1762
- console.log(chalk14.yellow("\n\u26A0 Schema generation from external sources (OpenAPI/Prisma) is not yet implemented."));
1763
- console.log(chalk14.gray("This feature will be available in a future release.\n"));
1764
- process.exit(0);
1765
- }
1766
1910
  await generate(type, name);
1767
1911
  } catch (error) {
1768
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1912
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1769
1913
  process.exit(1);
1770
1914
  }
1771
1915
  });
1772
- program.command("doctor").description("Diagnose and fix common issues").action(async () => {
1916
+ program.command("doctor").description("Diagnose common project setup issues (reports only, changes nothing)").action(async () => {
1773
1917
  try {
1774
1918
  await doctor();
1775
1919
  } catch (error) {
1776
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1777
- process.exit(1);
1778
- }
1779
- });
1780
- program.command("add").description("Add a new component renderer to your project").argument("<component>", "Component name (e.g. Input, Grid)").action(async (component) => {
1781
- try {
1782
- await add(component);
1783
- } catch (error) {
1784
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1920
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1785
1921
  process.exit(1);
1786
1922
  }
1787
1923
  });
1788
- program.command("studio").description("Start the visual designer").action(async () => {
1789
- try {
1790
- await studio();
1791
- } catch (error) {
1792
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1793
- process.exit(1);
1794
- }
1795
- });
1796
- program.command("check").description("Validate schema files").action(async () => {
1924
+ program.command("check").description("Advisory JSON file sweep; run objectui validate for a verdict").action(async () => {
1797
1925
  try {
1798
1926
  await check();
1799
1927
  } catch (error) {
1800
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1928
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1801
1929
  process.exit(1);
1802
1930
  }
1803
1931
  });
@@ -1805,29 +1933,15 @@ program.command("validate").description("Validate a schema file against ObjectUI
1805
1933
  try {
1806
1934
  await validate(schema);
1807
1935
  } catch (error) {
1808
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1809
- process.exit(1);
1810
- }
1811
- });
1812
- 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) => {
1813
- try {
1814
- if (type === "plugin") {
1815
- await createPlugin(name);
1816
- } else {
1817
- console.error(chalk14.red(`Unknown resource type: ${type}`));
1818
- console.log(chalk14.gray("Available types: plugin"));
1819
- process.exit(1);
1820
- }
1821
- } catch (error) {
1822
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1936
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1823
1937
  process.exit(1);
1824
1938
  }
1825
1939
  });
1826
- program.command("analyze").description("Analyze application performance").option("--bundle-size", "Analyze bundle size").option("--render-performance", "Analyze render performance").action(async (options) => {
1940
+ program.command("analyze").description("Analyze application performance").action(async () => {
1827
1941
  try {
1828
- await analyze(options);
1942
+ await analyze();
1829
1943
  } catch (error) {
1830
- console.error(chalk14.red("Error:"), error instanceof Error ? error.message : error);
1944
+ console.error(chalk9.red("Error:"), error instanceof Error ? error.message : error);
1831
1945
  process.exit(1);
1832
1946
  }
1833
1947
  });