@objectstack/cli 2.0.7 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,237 +1,27 @@
1
+ import {
2
+ collectMetadataStats,
3
+ configExists,
4
+ createTimer,
5
+ formatZodErrors,
6
+ loadConfig,
7
+ printError,
8
+ printHeader,
9
+ printInfo,
10
+ printKV,
11
+ printMetadataStats,
12
+ printServerReady,
13
+ printStep,
14
+ printSuccess,
15
+ printWarning,
16
+ resolveConfigPath
17
+ } from "./chunk-CSHQEILI.js";
18
+
1
19
  // src/commands/compile.ts
2
20
  import { Command } from "commander";
3
- import path2 from "path";
4
- import fs2 from "fs";
5
- import chalk3 from "chalk";
6
- import { ObjectStackDefinitionSchema } from "@objectstack/spec";
7
-
8
- // src/utils/config.ts
9
21
  import path from "path";
10
22
  import fs from "fs";
11
- import chalk2 from "chalk";
12
- import { bundleRequire } from "bundle-require";
13
-
14
- // src/utils/format.ts
15
23
  import chalk from "chalk";
16
- function printHeader(title) {
17
- console.log(chalk.bold(`
18
- \u25C6 ${title}`));
19
- console.log(chalk.dim("\u2500".repeat(40)));
20
- }
21
- function printKV(key, value, icon) {
22
- const prefix = icon ? `${icon} ` : " ";
23
- console.log(`${prefix}${chalk.dim(key + ":")} ${chalk.white(String(value))}`);
24
- }
25
- function printSuccess(msg) {
26
- console.log(chalk.green(` \u2713 ${msg}`));
27
- }
28
- function printWarning(msg) {
29
- console.log(chalk.yellow(` \u26A0 ${msg}`));
30
- }
31
- function printError(msg) {
32
- console.log(chalk.red(` \u2717 ${msg}`));
33
- }
34
- function printInfo(msg) {
35
- console.log(chalk.blue(` \u2139 ${msg}`));
36
- }
37
- function printStep(msg) {
38
- console.log(chalk.yellow(` \u2192 ${msg}`));
39
- }
40
- function createTimer() {
41
- const start = Date.now();
42
- return {
43
- elapsed: () => Date.now() - start,
44
- display: () => `${Date.now() - start}ms`
45
- };
46
- }
47
- function formatZodErrors(error) {
48
- const issues = error.issues || error.errors || [];
49
- if (issues.length === 0) {
50
- console.log(chalk.red(" Unknown validation error"));
51
- return;
52
- }
53
- const grouped = /* @__PURE__ */ new Map();
54
- for (const issue of issues) {
55
- const topPath = issue.path?.[0] || "_root";
56
- if (!grouped.has(String(topPath))) {
57
- grouped.set(String(topPath), []);
58
- }
59
- grouped.get(String(topPath)).push(issue);
60
- }
61
- for (const [section, sectionIssues] of grouped) {
62
- console.log(chalk.bold.red(`
63
- ${section}:`));
64
- for (const issue of sectionIssues) {
65
- const path12 = issue.path?.join(".") || "";
66
- const code = issue.code || "";
67
- const msg = issue.message || "";
68
- console.log(chalk.red(` \u2717 ${path12}`));
69
- console.log(chalk.dim(` ${code}: ${msg}`));
70
- if (issue.expected) {
71
- console.log(chalk.dim(` expected: ${chalk.green(issue.expected)}`));
72
- }
73
- if (issue.received) {
74
- console.log(chalk.dim(` received: ${chalk.red(issue.received)}`));
75
- }
76
- }
77
- }
78
- console.log("");
79
- console.log(chalk.dim(` ${issues.length} validation error(s) total`));
80
- }
81
- function collectMetadataStats(config) {
82
- const count = (arr) => Array.isArray(arr) ? arr.length : 0;
83
- let fields = 0;
84
- if (Array.isArray(config.objects)) {
85
- for (const obj of config.objects) {
86
- if (obj.fields && typeof obj.fields === "object") {
87
- fields += Object.keys(obj.fields).length;
88
- }
89
- }
90
- }
91
- return {
92
- objects: count(config.objects),
93
- objectExtensions: count(config.objectExtensions),
94
- fields,
95
- views: count(config.views),
96
- pages: count(config.pages),
97
- apps: count(config.apps),
98
- dashboards: count(config.dashboards),
99
- reports: count(config.reports),
100
- actions: count(config.actions),
101
- flows: count(config.flows),
102
- workflows: count(config.workflows),
103
- approvals: count(config.approvals),
104
- agents: count(config.agents),
105
- apis: count(config.apis),
106
- roles: count(config.roles),
107
- permissions: count(config.permissions),
108
- themes: count(config.themes),
109
- datasources: count(config.datasources),
110
- translations: count(config.translations),
111
- plugins: count(config.plugins),
112
- devPlugins: count(config.devPlugins)
113
- };
114
- }
115
- function printServerReady(opts) {
116
- const base = `http://localhost:${opts.port}`;
117
- console.log("");
118
- console.log(chalk.bold.green(" \u2713 Server is ready"));
119
- console.log("");
120
- console.log(chalk.cyan(" \u279C") + chalk.bold(" API: ") + chalk.cyan(base + "/"));
121
- if (opts.uiEnabled && opts.studioPath) {
122
- console.log(chalk.cyan(" \u279C") + chalk.bold(" Studio: ") + chalk.cyan(base + opts.studioPath + "/"));
123
- }
124
- console.log("");
125
- console.log(chalk.dim(` Config: ${opts.configFile}`));
126
- console.log(chalk.dim(` Mode: ${opts.isDev ? "development" : "production"}`));
127
- console.log(chalk.dim(` Plugins: ${opts.pluginCount} loaded`));
128
- if (opts.pluginNames && opts.pluginNames.length > 0) {
129
- console.log(chalk.dim(` ${opts.pluginNames.join(", ")}`));
130
- }
131
- console.log("");
132
- console.log(chalk.dim(" Press Ctrl+C to stop"));
133
- console.log("");
134
- }
135
- function printMetadataStats(stats) {
136
- const sections = [
137
- {
138
- label: "Data",
139
- items: [
140
- ["Objects", stats.objects],
141
- ["Fields", stats.fields],
142
- ["Extensions", stats.objectExtensions],
143
- ["Datasources", stats.datasources]
144
- ]
145
- },
146
- {
147
- label: "UI",
148
- items: [
149
- ["Apps", stats.apps],
150
- ["Views", stats.views],
151
- ["Pages", stats.pages],
152
- ["Dashboards", stats.dashboards],
153
- ["Reports", stats.reports],
154
- ["Actions", stats.actions],
155
- ["Themes", stats.themes]
156
- ]
157
- },
158
- {
159
- label: "Logic",
160
- items: [
161
- ["Flows", stats.flows],
162
- ["Workflows", stats.workflows],
163
- ["Approvals", stats.approvals],
164
- ["Agents", stats.agents],
165
- ["APIs", stats.apis]
166
- ]
167
- },
168
- {
169
- label: "Security",
170
- items: [
171
- ["Roles", stats.roles],
172
- ["Permissions", stats.permissions]
173
- ]
174
- }
175
- ];
176
- for (const section of sections) {
177
- const nonZero = section.items.filter(([, v]) => v > 0);
178
- if (nonZero.length === 0) continue;
179
- const line = nonZero.map(([k, v]) => `${chalk.white(v)} ${chalk.dim(k)}`).join(" ");
180
- console.log(` ${chalk.bold(section.label + ":")} ${line}`);
181
- }
182
- if (stats.plugins > 0 || stats.devPlugins > 0) {
183
- const parts = [];
184
- if (stats.plugins > 0) parts.push(`${stats.plugins} plugins`);
185
- if (stats.devPlugins > 0) parts.push(`${stats.devPlugins} devPlugins`);
186
- console.log(` ${chalk.bold("Runtime:")} ${chalk.dim(parts.join(", "))}`);
187
- }
188
- }
189
-
190
- // src/utils/config.ts
191
- function resolveConfigPath(source) {
192
- if (source) {
193
- const abs = path.resolve(process.cwd(), source);
194
- if (!fs.existsSync(abs)) {
195
- printError(`Config file not found: ${chalk2.white(abs)}`);
196
- console.log("");
197
- console.log(chalk2.dim(" Hint: Run this command from a directory with objectstack.config.ts"));
198
- console.log(chalk2.dim(" Or specify the path: objectstack <command> path/to/config.ts"));
199
- process.exit(1);
200
- }
201
- return abs;
202
- }
203
- const candidates = [
204
- "objectstack.config.ts",
205
- "objectstack.config.js",
206
- "objectstack.config.mjs"
207
- ];
208
- for (const candidate of candidates) {
209
- const abs = path.resolve(process.cwd(), candidate);
210
- if (fs.existsSync(abs)) return abs;
211
- }
212
- printError("No objectstack.config.{ts,js,mjs} found in current directory");
213
- console.log("");
214
- console.log(chalk2.dim(" Hint: Run `objectstack init` to create a new project"));
215
- process.exit(1);
216
- }
217
- async function loadConfig(source) {
218
- const absolutePath = resolveConfigPath(source);
219
- const start = Date.now();
220
- const { mod } = await bundleRequire({
221
- filepath: absolutePath
222
- });
223
- const config = mod.default || mod;
224
- if (!config) {
225
- throw new Error(`No default export found in ${path.basename(absolutePath)}`);
226
- }
227
- return {
228
- config,
229
- absolutePath,
230
- duration: Date.now() - start
231
- };
232
- }
233
-
234
- // src/commands/compile.ts
24
+ import { ObjectStackDefinitionSchema } from "@objectstack/spec";
235
25
  var compileCommand = new Command("compile").description("Compile ObjectStack configuration to JSON artifact").argument("[config]", "Source configuration file").option("-o, --output <path>", "Output JSON file", "dist/objectstack.json").option("--json", "Output compile result as JSON (for CI)").action(async (configPath, options) => {
236
26
  const timer = createTimer();
237
27
  if (!options.json) {
@@ -241,7 +31,7 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
241
31
  if (!options.json) printStep("Loading configuration...");
242
32
  const { config, absolutePath, duration } = await loadConfig(configPath);
243
33
  if (!options.json) {
244
- printKV("Config", path2.relative(process.cwd(), absolutePath));
34
+ printKV("Config", path.relative(process.cwd(), absolutePath));
245
35
  printKV("Load time", `${duration}ms`);
246
36
  }
247
37
  if (!options.json) printStep("Validating protocol compliance...");
@@ -258,13 +48,13 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
258
48
  }
259
49
  if (!options.json) printStep("Writing artifact...");
260
50
  const output = options.output;
261
- const artifactPath = path2.resolve(process.cwd(), output);
262
- const artifactDir = path2.dirname(artifactPath);
263
- if (!fs2.existsSync(artifactDir)) {
264
- fs2.mkdirSync(artifactDir, { recursive: true });
51
+ const artifactPath = path.resolve(process.cwd(), output);
52
+ const artifactDir = path.dirname(artifactPath);
53
+ if (!fs.existsSync(artifactDir)) {
54
+ fs.mkdirSync(artifactDir, { recursive: true });
265
55
  }
266
56
  const jsonContent = JSON.stringify(result.data, null, 2);
267
- fs2.writeFileSync(artifactPath, jsonContent);
57
+ fs.writeFileSync(artifactPath, jsonContent);
268
58
  const sizeKB = (jsonContent.length / 1024).toFixed(1);
269
59
  const stats = collectMetadataStats(config);
270
60
  if (options.json) {
@@ -278,11 +68,11 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
278
68
  return;
279
69
  }
280
70
  console.log("");
281
- printSuccess(`Build complete ${chalk3.dim(`(${timer.display()})`)}`);
71
+ printSuccess(`Build complete ${chalk.dim(`(${timer.display()})`)}`);
282
72
  console.log("");
283
73
  printMetadataStats(stats);
284
74
  console.log("");
285
- printKV("Artifact", `${output} ${chalk3.dim(`(${sizeKB} KB`)})`);
75
+ printKV("Artifact", `${output} ${chalk.dim(`(${sizeKB} KB`)})`);
286
76
  console.log("");
287
77
  } catch (error) {
288
78
  if (options.json) {
@@ -297,7 +87,7 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
297
87
 
298
88
  // src/commands/validate.ts
299
89
  import { Command as Command2 } from "commander";
300
- import chalk4 from "chalk";
90
+ import chalk2 from "chalk";
301
91
  import { ObjectStackDefinitionSchema as ObjectStackDefinitionSchema2 } from "@objectstack/spec";
302
92
  var validateCommand = new Command2("validate").description("Validate ObjectStack configuration against the protocol schema").argument("[config]", "Configuration file path").option("--strict", "Treat warnings as errors").option("--json", "Output results as JSON").action(async (configPath, options) => {
303
93
  const timer = createTimer();
@@ -351,12 +141,12 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
351
141
  warnings.push("Missing manifest.namespace \u2014 required for multi-app hosting");
352
142
  }
353
143
  console.log("");
354
- printSuccess(`Validation passed ${chalk4.dim(`(${timer.display()})`)}`);
144
+ printSuccess(`Validation passed ${chalk2.dim(`(${timer.display()})`)}`);
355
145
  console.log("");
356
146
  if (config.manifest) {
357
- console.log(` ${chalk4.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk4.dim(`v${config.manifest.version || "0.0.0"}`)}`);
147
+ console.log(` ${chalk2.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk2.dim(`v${config.manifest.version || "0.0.0"}`)}`);
358
148
  if (config.manifest.description) {
359
- console.log(chalk4.dim(` ${config.manifest.description}`));
149
+ console.log(chalk2.dim(` ${config.manifest.description}`));
360
150
  }
361
151
  console.log("");
362
152
  }
@@ -364,7 +154,7 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
364
154
  if (warnings.length > 0) {
365
155
  console.log("");
366
156
  for (const w of warnings) {
367
- console.log(chalk4.yellow(` \u26A0 ${w}`));
157
+ console.log(chalk2.yellow(` \u26A0 ${w}`));
368
158
  }
369
159
  if (options.strict) {
370
160
  console.log("");
@@ -390,7 +180,7 @@ var validateCommand = new Command2("validate").description("Validate ObjectStack
390
180
 
391
181
  // src/commands/info.ts
392
182
  import { Command as Command3 } from "commander";
393
- import chalk5 from "chalk";
183
+ import chalk3 from "chalk";
394
184
  var infoCommand = new Command3("info").description("Display metadata summary of an ObjectStack configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configPath, options) => {
395
185
  const timer = createTimer();
396
186
  if (!options.json) {
@@ -416,9 +206,9 @@ var infoCommand = new Command3("info").description("Display metadata summary of
416
206
  if (config.manifest) {
417
207
  const m = config.manifest;
418
208
  console.log("");
419
- console.log(` ${chalk5.bold(m.name || m.id || "Unnamed")} ${chalk5.dim(`v${m.version || "0.0.0"}`)}`);
420
- if (m.id) console.log(chalk5.dim(` ${m.id}`));
421
- if (m.description) console.log(chalk5.dim(` ${m.description}`));
209
+ console.log(` ${chalk3.bold(m.name || m.id || "Unnamed")} ${chalk3.dim(`v${m.version || "0.0.0"}`)}`);
210
+ if (m.id) console.log(chalk3.dim(` ${m.id}`));
211
+ if (m.description) console.log(chalk3.dim(` ${m.description}`));
422
212
  if (m.namespace) printKV(" Namespace", m.namespace);
423
213
  if (m.type) printKV(" Type", m.type);
424
214
  }
@@ -426,35 +216,35 @@ var infoCommand = new Command3("info").description("Display metadata summary of
426
216
  printMetadataStats(stats);
427
217
  if (config.objects && config.objects.length > 0) {
428
218
  console.log("");
429
- console.log(chalk5.bold(" Objects:"));
219
+ console.log(chalk3.bold(" Objects:"));
430
220
  for (const obj of config.objects) {
431
221
  const fieldCount = obj.fields ? Object.keys(obj.fields).length : 0;
432
222
  const ownership = obj.ownership || "own";
433
223
  console.log(
434
- ` ${chalk5.cyan(obj.name || "?")}` + chalk5.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk5.dim(` \u2014 ${obj.label}`) : "")
224
+ ` ${chalk3.cyan(obj.name || "?")}` + chalk3.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk3.dim(` \u2014 ${obj.label}`) : "")
435
225
  );
436
226
  }
437
227
  }
438
228
  if (config.agents && config.agents.length > 0) {
439
229
  console.log("");
440
- console.log(chalk5.bold(" Agents:"));
230
+ console.log(chalk3.bold(" Agents:"));
441
231
  for (const agent of config.agents) {
442
232
  console.log(
443
- ` ${chalk5.magenta(agent.name || "?")}` + (agent.role ? chalk5.dim(` \u2014 ${agent.role}`) : "")
233
+ ` ${chalk3.magenta(agent.name || "?")}` + (agent.role ? chalk3.dim(` \u2014 ${agent.role}`) : "")
444
234
  );
445
235
  }
446
236
  }
447
237
  if (config.apps && config.apps.length > 0) {
448
238
  console.log("");
449
- console.log(chalk5.bold(" Apps:"));
239
+ console.log(chalk3.bold(" Apps:"));
450
240
  for (const app of config.apps) {
451
241
  console.log(
452
- ` ${chalk5.green(app.name || "?")}` + (app.label ? chalk5.dim(` \u2014 ${app.label}`) : "")
242
+ ` ${chalk3.green(app.name || "?")}` + (app.label ? chalk3.dim(` \u2014 ${app.label}`) : "")
453
243
  );
454
244
  }
455
245
  }
456
246
  console.log("");
457
- console.log(chalk5.dim(` Loaded in ${duration}ms`));
247
+ console.log(chalk3.dim(` Loaded in ${duration}ms`));
458
248
  console.log("");
459
249
  } catch (error) {
460
250
  if (options.json) {
@@ -469,9 +259,9 @@ var infoCommand = new Command3("info").description("Display metadata summary of
469
259
 
470
260
  // src/commands/init.ts
471
261
  import { Command as Command4 } from "commander";
472
- import chalk6 from "chalk";
473
- import fs3 from "fs";
474
- import path3 from "path";
262
+ import chalk4 from "chalk";
263
+ import fs2 from "fs";
264
+ import path2 from "path";
475
265
  var TEMPLATES = {
476
266
  app: {
477
267
  description: "Full application with objects, views, and actions",
@@ -636,16 +426,16 @@ function toTitleCase(str) {
636
426
  var initCommand = new Command4("init").description("Initialize a new ObjectStack project in the current directory").argument("[name]", "Project name (defaults to directory name)").option("-t, --template <template>", "Template: app, plugin, empty", "app").option("--no-install", "Skip dependency installation").action(async (name, options) => {
637
427
  printHeader("Init");
638
428
  const cwd = process.cwd();
639
- const projectName = name || path3.basename(cwd);
429
+ const projectName = name || path2.basename(cwd);
640
430
  const template = TEMPLATES[options.template];
641
431
  if (!template) {
642
432
  printError(`Unknown template: ${options.template}`);
643
- console.log(chalk6.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
433
+ console.log(chalk4.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
644
434
  process.exit(1);
645
435
  }
646
- if (fs3.existsSync(path3.join(cwd, "objectstack.config.ts"))) {
436
+ if (fs2.existsSync(path2.join(cwd, "objectstack.config.ts"))) {
647
437
  printError("objectstack.config.ts already exists in this directory");
648
- console.log(chalk6.dim(" Use `objectstack generate` to add metadata to an existing project"));
438
+ console.log(chalk4.dim(" Use `objectstack generate` to add metadata to an existing project"));
649
439
  process.exit(1);
650
440
  }
651
441
  printKV("Project", projectName);
@@ -654,8 +444,8 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
654
444
  console.log("");
655
445
  const createdFiles = [];
656
446
  try {
657
- const pkgPath = path3.join(cwd, "package.json");
658
- if (!fs3.existsSync(pkgPath)) {
447
+ const pkgPath = path2.join(cwd, "package.json");
448
+ if (!fs2.existsSync(pkgPath)) {
659
449
  const pkg = {
660
450
  name: projectName,
661
451
  version: "0.1.0",
@@ -665,16 +455,16 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
665
455
  dependencies: template.dependencies,
666
456
  devDependencies: template.devDependencies
667
457
  };
668
- fs3.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
458
+ fs2.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
669
459
  createdFiles.push("package.json");
670
460
  } else {
671
461
  printInfo("package.json already exists, skipping");
672
462
  }
673
463
  const configContent = template.configContent(projectName);
674
- fs3.writeFileSync(path3.join(cwd, "objectstack.config.ts"), configContent);
464
+ fs2.writeFileSync(path2.join(cwd, "objectstack.config.ts"), configContent);
675
465
  createdFiles.push("objectstack.config.ts");
676
- const tsconfigPath = path3.join(cwd, "tsconfig.json");
677
- if (!fs3.existsSync(tsconfigPath)) {
466
+ const tsconfigPath = path2.join(cwd, "tsconfig.json");
467
+ if (!fs2.existsSync(tsconfigPath)) {
678
468
  const tsconfig = {
679
469
  compilerOptions: {
680
470
  target: "ES2022",
@@ -690,30 +480,30 @@ var initCommand = new Command4("init").description("Initialize a new ObjectStack
690
480
  include: ["*.ts", "src/**/*"],
691
481
  exclude: ["dist", "node_modules"]
692
482
  };
693
- fs3.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
483
+ fs2.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
694
484
  createdFiles.push("tsconfig.json");
695
485
  }
696
486
  for (const [filePath, contentFn] of Object.entries(template.srcFiles)) {
697
487
  const resolvedPath = filePath.replace("__name__", projectName);
698
- const fullPath = path3.join(cwd, resolvedPath);
699
- const dir = path3.dirname(fullPath);
700
- if (!fs3.existsSync(dir)) {
701
- fs3.mkdirSync(dir, { recursive: true });
488
+ const fullPath = path2.join(cwd, resolvedPath);
489
+ const dir = path2.dirname(fullPath);
490
+ if (!fs2.existsSync(dir)) {
491
+ fs2.mkdirSync(dir, { recursive: true });
702
492
  }
703
- fs3.writeFileSync(fullPath, contentFn(projectName));
493
+ fs2.writeFileSync(fullPath, contentFn(projectName));
704
494
  createdFiles.push(resolvedPath);
705
495
  }
706
- const gitignorePath = path3.join(cwd, ".gitignore");
707
- if (!fs3.existsSync(gitignorePath)) {
708
- fs3.writeFileSync(gitignorePath, `node_modules/
496
+ const gitignorePath = path2.join(cwd, ".gitignore");
497
+ if (!fs2.existsSync(gitignorePath)) {
498
+ fs2.writeFileSync(gitignorePath, `node_modules/
709
499
  dist/
710
500
  *.tsbuildinfo
711
501
  `);
712
502
  createdFiles.push(".gitignore");
713
503
  }
714
- console.log(chalk6.bold(" Created files:"));
504
+ console.log(chalk4.bold(" Created files:"));
715
505
  for (const f of createdFiles) {
716
- console.log(chalk6.green(` + ${f}`));
506
+ console.log(chalk4.green(` + ${f}`));
717
507
  }
718
508
  console.log("");
719
509
  if (options.install !== false) {
@@ -727,10 +517,10 @@ dist/
727
517
  }
728
518
  printSuccess("Project initialized!");
729
519
  console.log("");
730
- console.log(chalk6.bold(" Next steps:"));
731
- console.log(chalk6.dim(" objectstack validate # Check configuration"));
732
- console.log(chalk6.dim(" objectstack dev # Start development server"));
733
- console.log(chalk6.dim(" objectstack generate # Add objects, views, etc."));
520
+ console.log(chalk4.bold(" Next steps:"));
521
+ console.log(chalk4.dim(" objectstack validate # Check configuration"));
522
+ console.log(chalk4.dim(" objectstack dev # Start development server"));
523
+ console.log(chalk4.dim(" objectstack generate # Add objects, views, etc."));
734
524
  console.log("");
735
525
  } catch (error) {
736
526
  printError(error.message || String(error));
@@ -738,14 +528,14 @@ dist/
738
528
  }
739
529
  });
740
530
  function printWarning2(msg) {
741
- console.log(chalk6.yellow(` \u26A0 ${msg}`));
531
+ console.log(chalk4.yellow(` \u26A0 ${msg}`));
742
532
  }
743
533
 
744
534
  // src/commands/generate.ts
745
535
  import { Command as Command5 } from "commander";
746
- import chalk7 from "chalk";
747
- import fs4 from "fs";
748
- import path4 from "path";
536
+ import chalk5 from "chalk";
537
+ import fs3 from "fs";
538
+ import path3 from "path";
749
539
  var GENERATORS = {
750
540
  object: {
751
541
  description: "Business data object",
@@ -926,81 +716,589 @@ function toTitleCase2(str) {
926
716
  function toSnakeCase(str) {
927
717
  return str.replace(/[-]/g, "_").replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`).replace(/^_/, "");
928
718
  }
929
- var generateCommand = new Command5("generate").alias("g").description("Generate metadata files (object, view, action, flow, agent, dashboard, app)").argument("<type>", "Metadata type to generate").argument("<name>", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").action(async (type, name, options) => {
719
+ var FIELD_TYPE_MAP = {
720
+ text: "string",
721
+ textarea: "string",
722
+ richtext: "string",
723
+ html: "string",
724
+ markdown: "string",
725
+ number: "number",
726
+ integer: "number",
727
+ currency: "number",
728
+ percent: "number",
729
+ boolean: "boolean",
730
+ date: "string",
731
+ datetime: "string",
732
+ time: "string",
733
+ email: "string",
734
+ phone: "string",
735
+ url: "string",
736
+ select: "string",
737
+ multiselect: "string[]",
738
+ lookup: "string",
739
+ master_detail: "string",
740
+ formula: "unknown",
741
+ autonumber: "string",
742
+ json: "Record<string, unknown>",
743
+ file: "string",
744
+ image: "string",
745
+ password: "string",
746
+ slug: "string",
747
+ uuid: "string",
748
+ ip_address: "string",
749
+ color: "string",
750
+ rating: "number",
751
+ geo_point: "{ lat: number; lng: number }",
752
+ vector: "number[]",
753
+ encrypted: "string"
754
+ };
755
+ function fieldTypeToTs(fieldType, multiple) {
756
+ const base = FIELD_TYPE_MAP[fieldType] || "unknown";
757
+ return multiple ? `${base}[]` : base;
758
+ }
759
+ function generateTypesFromConfig(config) {
760
+ const lines = [
761
+ "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
762
+ `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
763
+ "",
764
+ "import type { Data } from '@objectstack/spec';",
765
+ ""
766
+ ];
767
+ const objects = [];
768
+ const rawObjects = config.objects ?? config.data?.objects ?? {};
769
+ if (Array.isArray(rawObjects)) {
770
+ objects.push(...rawObjects);
771
+ } else if (typeof rawObjects === "object") {
772
+ for (const val of Object.values(rawObjects)) {
773
+ if (val && typeof val === "object") objects.push(val);
774
+ }
775
+ }
776
+ if (objects.length === 0) {
777
+ lines.push("// No objects found in configuration");
778
+ return lines.join("\n") + "\n";
779
+ }
780
+ for (const obj of objects) {
781
+ const name = String(obj.name || "unknown");
782
+ const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
783
+ const fields = obj.fields ?? {};
784
+ lines.push(`/** ${String(obj.label || typeName)} record type */`);
785
+ lines.push(`export interface ${typeName}Record {`);
786
+ lines.push(" id: string;");
787
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
788
+ const fType = String(fieldDef.type || "text");
789
+ const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
790
+ const required = fieldDef.required ? "" : "?";
791
+ if (fieldDef.label) {
792
+ lines.push(` /** ${fieldDef.label} */`);
793
+ }
794
+ lines.push(` ${fieldName}${required}: ${tsType};`);
795
+ }
796
+ lines.push("}");
797
+ lines.push("");
798
+ }
799
+ return lines.join("\n") + "\n";
800
+ }
801
+ var generateMetadataCommand = new Command5("metadata").alias("m").description("Generate metadata scaffold (object, view, action, flow, agent, dashboard, app)").argument("<type>", "Metadata type to generate").argument("<name>", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").action(async (type, name, options) => {
930
802
  printHeader("Generate");
931
803
  const generator = GENERATORS[type];
932
804
  if (!generator) {
933
805
  printError(`Unknown type: ${type}`);
934
806
  console.log("");
935
- console.log(chalk7.bold(" Available types:"));
807
+ console.log(chalk5.bold(" Available types:"));
936
808
  for (const [key, gen] of Object.entries(GENERATORS)) {
937
- console.log(` ${chalk7.cyan(key.padEnd(12))} ${chalk7.dim(gen.description)}`);
809
+ console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
938
810
  }
939
811
  console.log("");
940
- console.log(chalk7.dim(" Usage: objectstack generate <type> <name>"));
941
- console.log(chalk7.dim(" Example: objectstack generate object project"));
942
- console.log(chalk7.dim(" Alias: os g object project"));
812
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
813
+ console.log(chalk5.dim(" Example: objectstack generate object project"));
814
+ console.log(chalk5.dim(" Alias: os g object project"));
943
815
  process.exit(1);
944
816
  }
945
817
  const dir = options.dir || generator.defaultDir;
946
818
  const fileName = `${toSnakeCase(name)}.ts`;
947
- const filePath = path4.join(process.cwd(), dir, fileName);
948
- console.log(` ${chalk7.dim("Type:")} ${chalk7.cyan(type)} \u2014 ${generator.description}`);
949
- console.log(` ${chalk7.dim("Name:")} ${chalk7.white(name)}`);
950
- console.log(` ${chalk7.dim("File:")} ${chalk7.white(path4.join(dir, fileName))}`);
819
+ const filePath = path3.join(process.cwd(), dir, fileName);
820
+ console.log(` ${chalk5.dim("Type:")} ${chalk5.cyan(type)} \u2014 ${generator.description}`);
821
+ console.log(` ${chalk5.dim("Name:")} ${chalk5.white(name)}`);
822
+ console.log(` ${chalk5.dim("File:")} ${chalk5.white(path3.join(dir, fileName))}`);
951
823
  console.log("");
952
824
  if (options.dryRun) {
953
825
  printInfo("Dry run \u2014 no files written");
954
826
  console.log("");
955
- console.log(chalk7.dim(" Content:"));
956
- console.log(chalk7.dim(" " + "-".repeat(38)));
827
+ console.log(chalk5.dim(" Content:"));
828
+ console.log(chalk5.dim(" " + "-".repeat(38)));
957
829
  const content = generator.generate(name);
958
830
  for (const line of content.split("\n")) {
959
- console.log(chalk7.dim(` ${line}`));
831
+ console.log(chalk5.dim(` ${line}`));
960
832
  }
961
833
  console.log("");
962
834
  return;
963
835
  }
964
- if (fs4.existsSync(filePath)) {
836
+ if (fs3.existsSync(filePath)) {
965
837
  printError(`File already exists: ${filePath}`);
966
838
  process.exit(1);
967
839
  }
968
840
  try {
969
- const fullDir = path4.dirname(filePath);
970
- if (!fs4.existsSync(fullDir)) {
971
- fs4.mkdirSync(fullDir, { recursive: true });
841
+ const fullDir = path3.dirname(filePath);
842
+ if (!fs3.existsSync(fullDir)) {
843
+ fs3.mkdirSync(fullDir, { recursive: true });
972
844
  }
973
845
  const content = generator.generate(name);
974
- fs4.writeFileSync(filePath, content);
975
- printSuccess(`Created ${path4.join(dir, fileName)}`);
976
- const indexPath = path4.join(process.cwd(), dir, "index.ts");
977
- if (fs4.existsSync(indexPath)) {
978
- const indexContent = fs4.readFileSync(indexPath, "utf-8");
846
+ fs3.writeFileSync(filePath, content);
847
+ printSuccess(`Created ${path3.join(dir, fileName)}`);
848
+ const indexPath = path3.join(process.cwd(), dir, "index.ts");
849
+ if (fs3.existsSync(indexPath)) {
850
+ const indexContent = fs3.readFileSync(indexPath, "utf-8");
979
851
  const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';`;
980
852
  if (!indexContent.includes(toCamelCase2(name))) {
981
- fs4.appendFileSync(indexPath, exportLine + "\n");
853
+ fs3.appendFileSync(indexPath, exportLine + "\n");
982
854
  printSuccess(`Updated ${dir}/index.ts with export`);
983
855
  }
984
856
  } else {
985
857
  const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';
986
858
  `;
987
- fs4.writeFileSync(indexPath, exportLine);
859
+ fs3.writeFileSync(indexPath, exportLine);
988
860
  printSuccess(`Created ${dir}/index.ts`);
989
861
  }
990
862
  console.log("");
991
- console.log(chalk7.dim(` Tip: Run \`objectstack validate\` to check your config`));
863
+ console.log(chalk5.dim(` Tip: Run \`objectstack validate\` to check your config`));
992
864
  console.log("");
993
865
  } catch (error) {
994
866
  printError(error.message || String(error));
995
867
  process.exit(1);
996
868
  }
997
869
  });
870
+ var generateTypesCommand = new Command5("types").description("Generate TypeScript type definitions from ObjectStack configuration").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path", "src/types/objectstack.d.ts").option("--dry-run", "Show what would be generated without writing files").action(async (configPath, options) => {
871
+ printHeader("Generate Types");
872
+ try {
873
+ const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
874
+ printInfo("Loading configuration...");
875
+ const { config, absolutePath } = await loadConfig2(configPath);
876
+ console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
877
+ console.log(` ${chalk5.dim("Output:")} ${chalk5.white(options.output)}`);
878
+ console.log("");
879
+ const content = generateTypesFromConfig(config);
880
+ if (options.dryRun) {
881
+ printInfo("Dry run \u2014 no files written");
882
+ console.log("");
883
+ for (const line of content.split("\n")) {
884
+ console.log(chalk5.dim(` ${line}`));
885
+ }
886
+ console.log("");
887
+ return;
888
+ }
889
+ const outPath = path3.resolve(process.cwd(), options.output);
890
+ const outDir = path3.dirname(outPath);
891
+ if (!fs3.existsSync(outDir)) {
892
+ fs3.mkdirSync(outDir, { recursive: true });
893
+ }
894
+ fs3.writeFileSync(outPath, content);
895
+ printSuccess(`Generated types at ${options.output}`);
896
+ console.log("");
897
+ } catch (error) {
898
+ printError(error.message || String(error));
899
+ process.exit(1);
900
+ }
901
+ });
902
+ function generateClientFromConfig(config) {
903
+ const lines = [
904
+ "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
905
+ `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
906
+ "",
907
+ "import type { Data } from '@objectstack/spec';",
908
+ ""
909
+ ];
910
+ const objects = [];
911
+ const rawObjects = config.objects ?? config.data?.objects ?? {};
912
+ if (Array.isArray(rawObjects)) {
913
+ objects.push(...rawObjects);
914
+ } else if (typeof rawObjects === "object") {
915
+ for (const val of Object.values(rawObjects)) {
916
+ if (val && typeof val === "object") objects.push(val);
917
+ }
918
+ }
919
+ if (objects.length === 0) {
920
+ lines.push("// No objects found in configuration");
921
+ return lines.join("\n") + "\n";
922
+ }
923
+ for (const obj of objects) {
924
+ const name = String(obj.name || "unknown");
925
+ const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
926
+ const fields = obj.fields ?? {};
927
+ lines.push(`export interface ${typeName}Record {`);
928
+ lines.push(" id: string;");
929
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
930
+ const fType = String(fieldDef.type || "text");
931
+ const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
932
+ const required = fieldDef.required ? "" : "?";
933
+ lines.push(` ${fieldName}${required}: ${tsType};`);
934
+ }
935
+ lines.push("}");
936
+ lines.push("");
937
+ }
938
+ lines.push("export class ObjectStackClient {");
939
+ lines.push(" constructor(private baseUrl: string, private headers: Record<string, string> = {}) {}");
940
+ lines.push("");
941
+ lines.push(" private async request<T>(method: string, path: string, body?: unknown): Promise<T> {");
942
+ lines.push(" const res = await fetch(`${this.baseUrl}${path}`, {");
943
+ lines.push(" method,");
944
+ lines.push(" headers: { 'Content-Type': 'application/json', ...this.headers },");
945
+ lines.push(" body: body ? JSON.stringify(body) : undefined,");
946
+ lines.push(" });");
947
+ lines.push(" if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);");
948
+ lines.push(" return res.json() as Promise<T>;");
949
+ lines.push(" }");
950
+ for (const obj of objects) {
951
+ const name = String(obj.name || "unknown");
952
+ const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
953
+ const endpoint = `/api/${name}`;
954
+ lines.push("");
955
+ lines.push(` async list${typeName}(): Promise<${typeName}Record[]> {`);
956
+ lines.push(` return this.request<${typeName}Record[]>('GET', '${endpoint}');`);
957
+ lines.push(" }");
958
+ lines.push("");
959
+ lines.push(` async get${typeName}(id: string): Promise<${typeName}Record> {`);
960
+ lines.push(` return this.request<${typeName}Record>('GET', '${endpoint}/\${id}');`);
961
+ lines.push(" }");
962
+ lines.push("");
963
+ lines.push(` async create${typeName}(data: Omit<${typeName}Record, 'id'>): Promise<${typeName}Record> {`);
964
+ lines.push(` return this.request<${typeName}Record>('POST', '${endpoint}', data);`);
965
+ lines.push(" }");
966
+ lines.push("");
967
+ lines.push(` async update${typeName}(id: string, data: Partial<${typeName}Record>): Promise<${typeName}Record> {`);
968
+ lines.push(` return this.request<${typeName}Record>('PATCH', '${endpoint}/\${id}', data);`);
969
+ lines.push(" }");
970
+ lines.push("");
971
+ lines.push(` async delete${typeName}(id: string): Promise<void> {`);
972
+ lines.push(` return this.request<void>('DELETE', '${endpoint}/\${id}');`);
973
+ lines.push(" }");
974
+ }
975
+ lines.push("}");
976
+ lines.push("");
977
+ return lines.join("\n") + "\n";
978
+ }
979
+ var generateClientCommand = new Command5("client").description("Generate a type-safe client SDK from ObjectStack configuration").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path", "src/client/objectstack-client.ts").option("--dry-run", "Show output without writing").action(async (configPath, options) => {
980
+ printHeader("Generate Client SDK");
981
+ try {
982
+ const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
983
+ const timer = createTimer();
984
+ printInfo("Loading configuration...");
985
+ const { config, absolutePath } = await loadConfig2(configPath);
986
+ console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
987
+ console.log(` ${chalk5.dim("Output:")} ${chalk5.white(options.output)}`);
988
+ console.log("");
989
+ printStep("Generating client SDK...");
990
+ const content = generateClientFromConfig(config);
991
+ if (options.dryRun) {
992
+ printInfo("Dry run \u2014 no files written");
993
+ console.log("");
994
+ for (const line of content.split("\n")) {
995
+ console.log(chalk5.dim(` ${line}`));
996
+ }
997
+ console.log("");
998
+ return;
999
+ }
1000
+ const outPath = path3.resolve(process.cwd(), options.output);
1001
+ const outDir = path3.dirname(outPath);
1002
+ if (!fs3.existsSync(outDir)) {
1003
+ fs3.mkdirSync(outDir, { recursive: true });
1004
+ }
1005
+ fs3.writeFileSync(outPath, content);
1006
+ printSuccess(`Generated client SDK at ${options.output} (${timer.display()})`);
1007
+ console.log("");
1008
+ } catch (error) {
1009
+ printError(error.message || String(error));
1010
+ process.exit(1);
1011
+ }
1012
+ });
1013
+ var FIELD_TYPE_SQL_MAP = {
1014
+ text: "VARCHAR(255)",
1015
+ textarea: "TEXT",
1016
+ richtext: "TEXT",
1017
+ html: "TEXT",
1018
+ markdown: "TEXT",
1019
+ number: "DECIMAL(18,2)",
1020
+ integer: "INTEGER",
1021
+ currency: "DECIMAL(18,2)",
1022
+ percent: "DECIMAL(5,2)",
1023
+ boolean: "BOOLEAN",
1024
+ date: "DATE",
1025
+ datetime: "TIMESTAMP",
1026
+ time: "TIME",
1027
+ email: "VARCHAR(255)",
1028
+ phone: "VARCHAR(50)",
1029
+ url: "VARCHAR(2048)",
1030
+ select: "VARCHAR(255)",
1031
+ multiselect: "TEXT",
1032
+ lookup: "VARCHAR(36)",
1033
+ master_detail: "VARCHAR(36)",
1034
+ formula: "TEXT",
1035
+ autonumber: "SERIAL",
1036
+ json: "JSONB",
1037
+ file: "VARCHAR(2048)",
1038
+ image: "VARCHAR(2048)",
1039
+ password: "VARCHAR(255)",
1040
+ slug: "VARCHAR(255)",
1041
+ uuid: "UUID",
1042
+ ip_address: "VARCHAR(45)",
1043
+ color: "VARCHAR(7)",
1044
+ rating: "INTEGER",
1045
+ geo_point: "POINT",
1046
+ vector: "VECTOR",
1047
+ encrypted: "TEXT"
1048
+ };
1049
+ function fieldTypeToSql(fieldType) {
1050
+ return FIELD_TYPE_SQL_MAP[fieldType] || "TEXT";
1051
+ }
1052
+ function generateMigrationSql(config) {
1053
+ const lines = [
1054
+ "-- Auto-generated by ObjectStack CLI \u2014 do not edit manually",
1055
+ `-- Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
1056
+ ""
1057
+ ];
1058
+ const objects = [];
1059
+ const rawObjects = config.objects ?? config.data?.objects ?? {};
1060
+ if (Array.isArray(rawObjects)) {
1061
+ objects.push(...rawObjects);
1062
+ } else if (typeof rawObjects === "object") {
1063
+ for (const val of Object.values(rawObjects)) {
1064
+ if (val && typeof val === "object") objects.push(val);
1065
+ }
1066
+ }
1067
+ if (objects.length === 0) {
1068
+ lines.push("-- No objects found in configuration");
1069
+ return lines.join("\n") + "\n";
1070
+ }
1071
+ for (const obj of objects) {
1072
+ const tableName = String(obj.name || "unknown");
1073
+ const fields = obj.fields ?? {};
1074
+ lines.push(`CREATE TABLE IF NOT EXISTS "${tableName}" (`);
1075
+ lines.push(' "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),');
1076
+ const fieldLines = [];
1077
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
1078
+ const sqlType = fieldTypeToSql(String(fieldDef.type || "text"));
1079
+ const notNull = fieldDef.required ? " NOT NULL" : "";
1080
+ fieldLines.push(` "${fieldName}" ${sqlType}${notNull}`);
1081
+ }
1082
+ fieldLines.push(' "created_at" TIMESTAMP NOT NULL DEFAULT now()');
1083
+ fieldLines.push(' "updated_at" TIMESTAMP NOT NULL DEFAULT now()');
1084
+ lines.push(fieldLines.join(",\n"));
1085
+ lines.push(");");
1086
+ lines.push("");
1087
+ }
1088
+ return lines.join("\n") + "\n";
1089
+ }
1090
+ function generateMigrationTs(config) {
1091
+ const lines = [
1092
+ "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
1093
+ `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
1094
+ "",
1095
+ "export async function up(db: any): Promise<void> {"
1096
+ ];
1097
+ const objects = [];
1098
+ const rawObjects = config.objects ?? config.data?.objects ?? {};
1099
+ if (Array.isArray(rawObjects)) {
1100
+ objects.push(...rawObjects);
1101
+ } else if (typeof rawObjects === "object") {
1102
+ for (const val of Object.values(rawObjects)) {
1103
+ if (val && typeof val === "object") objects.push(val);
1104
+ }
1105
+ }
1106
+ if (objects.length === 0) {
1107
+ lines.push(" // No objects found in configuration");
1108
+ lines.push("}");
1109
+ lines.push("");
1110
+ lines.push("export async function down(db: any): Promise<void> {");
1111
+ lines.push(" // No objects found in configuration");
1112
+ lines.push("}");
1113
+ return lines.join("\n") + "\n";
1114
+ }
1115
+ for (const obj of objects) {
1116
+ const tableName = String(obj.name || "unknown");
1117
+ const fields = obj.fields ?? {};
1118
+ lines.push(` await db.schema.createTable('${tableName}', (table: any) => {`);
1119
+ lines.push(" table.uuid('id').primary().defaultTo(db.fn.uuid());");
1120
+ for (const [fieldName, fieldDef] of Object.entries(fields)) {
1121
+ const fType = String(fieldDef.type || "text");
1122
+ const required = fieldDef.required ? ".notNullable()" : ".nullable()";
1123
+ let colMethod;
1124
+ switch (fType) {
1125
+ case "text":
1126
+ case "email":
1127
+ case "phone":
1128
+ case "url":
1129
+ case "select":
1130
+ case "slug":
1131
+ case "password":
1132
+ case "color":
1133
+ case "ip_address":
1134
+ colMethod = `table.string('${fieldName}')`;
1135
+ break;
1136
+ case "textarea":
1137
+ case "richtext":
1138
+ case "html":
1139
+ case "markdown":
1140
+ case "formula":
1141
+ case "encrypted":
1142
+ colMethod = `table.text('${fieldName}')`;
1143
+ break;
1144
+ case "number":
1145
+ case "currency":
1146
+ case "percent":
1147
+ colMethod = `table.decimal('${fieldName}')`;
1148
+ break;
1149
+ case "integer":
1150
+ case "rating":
1151
+ colMethod = `table.integer('${fieldName}')`;
1152
+ break;
1153
+ case "boolean":
1154
+ colMethod = `table.boolean('${fieldName}')`;
1155
+ break;
1156
+ case "date":
1157
+ colMethod = `table.date('${fieldName}')`;
1158
+ break;
1159
+ case "datetime":
1160
+ colMethod = `table.timestamp('${fieldName}')`;
1161
+ break;
1162
+ case "time":
1163
+ colMethod = `table.time('${fieldName}')`;
1164
+ break;
1165
+ case "json":
1166
+ case "multiselect":
1167
+ colMethod = `table.jsonb('${fieldName}')`;
1168
+ break;
1169
+ case "uuid":
1170
+ case "lookup":
1171
+ case "master_detail":
1172
+ colMethod = `table.uuid('${fieldName}')`;
1173
+ break;
1174
+ default:
1175
+ colMethod = `table.text('${fieldName}')`;
1176
+ }
1177
+ lines.push(` ${colMethod}${required};`);
1178
+ }
1179
+ lines.push(" table.timestamps(true, true);");
1180
+ lines.push(" });");
1181
+ }
1182
+ lines.push("}");
1183
+ lines.push("");
1184
+ lines.push("export async function down(db: any): Promise<void> {");
1185
+ const tableNames = objects.map((o) => String(o.name || "unknown")).reverse();
1186
+ for (const tableName of tableNames) {
1187
+ lines.push(` await db.schema.dropTableIfExists('${tableName}');`);
1188
+ }
1189
+ lines.push("}");
1190
+ return lines.join("\n") + "\n";
1191
+ }
1192
+ var generateMigrationCommand = new Command5("migration").description("Generate database migration from ObjectStack schema").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path").option("--format <format>", "Output format: sql or typescript", "typescript").option("--dry-run", "Show output without writing").action(async (configPath, options) => {
1193
+ printHeader("Generate Migration");
1194
+ try {
1195
+ const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
1196
+ const timer = createTimer();
1197
+ printInfo("Loading configuration...");
1198
+ const { config, absolutePath } = await loadConfig2(configPath);
1199
+ const ext = options.format === "sql" ? "sql" : "ts";
1200
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
1201
+ const defaultOutput = `migrations/${timestamp}_migration.${ext}`;
1202
+ const output = options.output || defaultOutput;
1203
+ console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
1204
+ console.log(` ${chalk5.dim("Format:")} ${chalk5.white(options.format)}`);
1205
+ console.log(` ${chalk5.dim("Output:")} ${chalk5.white(output)}`);
1206
+ console.log("");
1207
+ printStep("Generating migration...");
1208
+ const content = options.format === "sql" ? generateMigrationSql(config) : generateMigrationTs(config);
1209
+ if (options.dryRun) {
1210
+ printInfo("Dry run \u2014 no files written");
1211
+ console.log("");
1212
+ for (const line of content.split("\n")) {
1213
+ console.log(chalk5.dim(` ${line}`));
1214
+ }
1215
+ console.log("");
1216
+ return;
1217
+ }
1218
+ const outPath = path3.resolve(process.cwd(), output);
1219
+ const outDir = path3.dirname(outPath);
1220
+ if (!fs3.existsSync(outDir)) {
1221
+ fs3.mkdirSync(outDir, { recursive: true });
1222
+ }
1223
+ fs3.writeFileSync(outPath, content);
1224
+ printSuccess(`Generated migration at ${output} (${timer.display()})`);
1225
+ console.log("");
1226
+ } catch (error) {
1227
+ printError(error.message || String(error));
1228
+ process.exit(1);
1229
+ }
1230
+ });
1231
+ var generateSchemaCommand = new Command5("schema").description("Generate JSON Schema for objectstack.config.ts (for IDE autocomplete)").option("-o, --output <file>", "Output file path", "objectstack.schema.json").option("--dry-run", "Show output without writing").action(async (options) => {
1232
+ printHeader("Generate Schema");
1233
+ try {
1234
+ const timer = createTimer();
1235
+ printStep("Loading ObjectStackDefinitionSchema...");
1236
+ const { z } = await import("zod");
1237
+ const { ObjectStackDefinitionSchema: ObjectStackDefinitionSchema3 } = await import("@objectstack/spec");
1238
+ printStep("Converting to JSON Schema...");
1239
+ const jsonSchema = z.toJSONSchema(ObjectStackDefinitionSchema3, {
1240
+ target: "draft-2020-12"
1241
+ });
1242
+ const schema = {
1243
+ ...jsonSchema,
1244
+ $id: "https://schema.objectstack.io/objectstack.config.json",
1245
+ title: "ObjectStack Configuration",
1246
+ description: "JSON Schema for objectstack.config.ts \u2014 generated from ObjectStackDefinitionSchema"
1247
+ };
1248
+ const content = JSON.stringify(schema, null, 2) + "\n";
1249
+ if (options.dryRun) {
1250
+ printInfo("Dry run \u2014 no files written");
1251
+ console.log("");
1252
+ console.log(content);
1253
+ return;
1254
+ }
1255
+ const outPath = path3.resolve(process.cwd(), options.output);
1256
+ const outDir = path3.dirname(outPath);
1257
+ if (!fs3.existsSync(outDir)) {
1258
+ fs3.mkdirSync(outDir, { recursive: true });
1259
+ }
1260
+ fs3.writeFileSync(outPath, content);
1261
+ printSuccess(`Generated JSON Schema at ${options.output} (${timer.display()})`);
1262
+ console.log("");
1263
+ console.log(chalk5.dim(" Usage: Reference in your IDE or editor for autocomplete"));
1264
+ console.log(chalk5.dim(` Path: ${outPath}`));
1265
+ console.log("");
1266
+ } catch (error) {
1267
+ printError(error.message || String(error));
1268
+ process.exit(1);
1269
+ }
1270
+ });
1271
+ var generateCommand = new Command5("generate").alias("g").description("Generate metadata files or TypeScript types").argument("[type]", "Metadata type to generate (object, view, action, flow, agent, dashboard, app)").argument("[name]", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").addCommand(generateTypesCommand).addCommand(generateClientCommand).addCommand(generateMigrationCommand).addCommand(generateSchemaCommand).action(async (type, name, options) => {
1272
+ if (!type) {
1273
+ printHeader("Generate");
1274
+ console.log(chalk5.bold(" Sub-commands:"));
1275
+ console.log(` ${chalk5.cyan("types".padEnd(12))} Generate TypeScript type definitions from config`);
1276
+ console.log(` ${chalk5.cyan("client".padEnd(12))} Generate a type-safe client SDK from config`);
1277
+ console.log(` ${chalk5.cyan("migration".padEnd(12))} Generate database migration from schema`);
1278
+ console.log(` ${chalk5.cyan("schema".padEnd(12))} Generate JSON Schema for objectstack.config.ts (IDE autocomplete)`);
1279
+ console.log("");
1280
+ console.log(chalk5.bold(" Metadata types:"));
1281
+ for (const [key, gen] of Object.entries(GENERATORS)) {
1282
+ console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
1283
+ }
1284
+ console.log("");
1285
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
1286
+ console.log(chalk5.dim(" Usage: objectstack generate types [config]"));
1287
+ return;
1288
+ }
1289
+ if (!name) {
1290
+ printError("Missing required argument: <name>");
1291
+ console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
1292
+ process.exit(1);
1293
+ }
1294
+ await generateMetadataCommand.parseAsync([type, name, ...process.argv.slice(4)], { from: "user" });
1295
+ });
998
1296
 
999
1297
  // src/commands/create.ts
1000
1298
  import { Command as Command6 } from "commander";
1001
- import chalk8 from "chalk";
1002
- import fs5 from "fs";
1003
- import path5 from "path";
1299
+ import chalk6 from "chalk";
1300
+ import fs4 from "fs";
1301
+ import path4 from "path";
1004
1302
  var templates = {
1005
1303
  plugin: {
1006
1304
  description: "Create a new ObjectStack plugin",
@@ -1175,64 +1473,64 @@ function toCamelCase3(str) {
1175
1473
  return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1176
1474
  }
1177
1475
  var createCommand = new Command6("create").description("Create a new package, plugin, or example from template").argument("<type>", "Type of project to create (plugin, example)").argument("[name]", "Name of the project").option("-d, --dir <directory>", "Target directory").action(async (type, name, options) => {
1178
- console.log(chalk8.bold(`
1476
+ console.log(chalk6.bold(`
1179
1477
  \u{1F4E6} ObjectStack Project Creator`));
1180
- console.log(chalk8.dim(`-------------------------------`));
1478
+ console.log(chalk6.dim(`-------------------------------`));
1181
1479
  if (!templates[type]) {
1182
- console.error(chalk8.red(`
1480
+ console.error(chalk6.red(`
1183
1481
  \u274C Unknown type: ${type}`));
1184
- console.log(chalk8.dim("Available types: plugin, example"));
1482
+ console.log(chalk6.dim("Available types: plugin, example"));
1185
1483
  process.exit(1);
1186
1484
  }
1187
1485
  if (!name) {
1188
- console.error(chalk8.red("\n\u274C Project name is required"));
1189
- console.log(chalk8.dim(`Usage: objectstack create ${type} <name>`));
1486
+ console.error(chalk6.red("\n\u274C Project name is required"));
1487
+ console.log(chalk6.dim(`Usage: objectstack create ${type} <name>`));
1190
1488
  process.exit(1);
1191
1489
  }
1192
1490
  const template = templates[type];
1193
1491
  const cwd = process.cwd();
1194
1492
  let targetDir;
1195
1493
  if (options?.dir) {
1196
- targetDir = path5.resolve(cwd, options.dir);
1494
+ targetDir = path4.resolve(cwd, options.dir);
1197
1495
  } else {
1198
1496
  const baseDir = type === "plugin" ? "packages/plugins" : "examples";
1199
1497
  const projectName = type === "plugin" ? `plugin-${name}` : name;
1200
- targetDir = path5.join(cwd, baseDir, projectName);
1498
+ targetDir = path4.join(cwd, baseDir, projectName);
1201
1499
  }
1202
- if (fs5.existsSync(targetDir)) {
1203
- console.error(chalk8.red(`
1500
+ if (fs4.existsSync(targetDir)) {
1501
+ console.error(chalk6.red(`
1204
1502
  \u274C Directory already exists: ${targetDir}`));
1205
1503
  process.exit(1);
1206
1504
  }
1207
- console.log(`\u{1F4C1} Creating ${type}: ${chalk8.blue(name)}`);
1208
- console.log(`\u{1F4C2} Location: ${chalk8.dim(targetDir)}`);
1505
+ console.log(`\u{1F4C1} Creating ${type}: ${chalk6.blue(name)}`);
1506
+ console.log(`\u{1F4C2} Location: ${chalk6.dim(targetDir)}`);
1209
1507
  console.log("");
1210
1508
  try {
1211
- fs5.mkdirSync(targetDir, { recursive: true });
1509
+ fs4.mkdirSync(targetDir, { recursive: true });
1212
1510
  for (const [filePath, contentFn] of Object.entries(template.files)) {
1213
- const fullPath = path5.join(targetDir, filePath);
1214
- const dir = path5.dirname(fullPath);
1215
- if (!fs5.existsSync(dir)) {
1216
- fs5.mkdirSync(dir, { recursive: true });
1511
+ const fullPath = path4.join(targetDir, filePath);
1512
+ const dir = path4.dirname(fullPath);
1513
+ if (!fs4.existsSync(dir)) {
1514
+ fs4.mkdirSync(dir, { recursive: true });
1217
1515
  }
1218
1516
  const content = contentFn(name);
1219
1517
  const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
1220
- fs5.writeFileSync(fullPath, fileContent);
1221
- console.log(chalk8.green(`\u2713 Created ${filePath}`));
1518
+ fs4.writeFileSync(fullPath, fileContent);
1519
+ console.log(chalk6.green(`\u2713 Created ${filePath}`));
1222
1520
  }
1223
1521
  console.log("");
1224
- console.log(chalk8.green("\u2705 Project created successfully!"));
1522
+ console.log(chalk6.green("\u2705 Project created successfully!"));
1225
1523
  console.log("");
1226
- console.log(chalk8.bold("Next steps:"));
1227
- console.log(chalk8.dim(` cd ${path5.relative(cwd, targetDir)}`));
1228
- console.log(chalk8.dim(" pnpm install"));
1229
- console.log(chalk8.dim(" pnpm build"));
1524
+ console.log(chalk6.bold("Next steps:"));
1525
+ console.log(chalk6.dim(` cd ${path4.relative(cwd, targetDir)}`));
1526
+ console.log(chalk6.dim(" pnpm install"));
1527
+ console.log(chalk6.dim(" pnpm build"));
1230
1528
  console.log("");
1231
1529
  } catch (error) {
1232
- console.error(chalk8.red("\n\u274C Failed to create project:"));
1530
+ console.error(chalk6.red("\n\u274C Failed to create project:"));
1233
1531
  console.error(error.message || error);
1234
- if (fs5.existsSync(targetDir)) {
1235
- fs5.rmSync(targetDir, { recursive: true });
1532
+ if (fs4.existsSync(targetDir)) {
1533
+ fs4.rmSync(targetDir, { recursive: true });
1236
1534
  }
1237
1535
  process.exit(1);
1238
1536
  }
@@ -1240,9 +1538,9 @@ var createCommand = new Command6("create").description("Create a new package, pl
1240
1538
 
1241
1539
  // src/commands/plugin.ts
1242
1540
  import { Command as Command7 } from "commander";
1243
- import chalk9 from "chalk";
1244
- import fs6 from "fs";
1245
- import path6 from "path";
1541
+ import chalk7 from "chalk";
1542
+ import fs5 from "fs";
1543
+ import path5 from "path";
1246
1544
  function resolvePluginName(plugin) {
1247
1545
  if (typeof plugin === "string") return plugin;
1248
1546
  if (plugin && typeof plugin === "object") {
@@ -1267,7 +1565,7 @@ function resolvePluginType(plugin) {
1267
1565
  return "standard";
1268
1566
  }
1269
1567
  function readConfigText(configPath) {
1270
- return fs6.readFileSync(configPath, "utf-8");
1568
+ return fs5.readFileSync(configPath, "utf-8");
1271
1569
  }
1272
1570
  function addPluginToConfig(configPath, packageName) {
1273
1571
  let content = readConfigText(configPath);
@@ -1309,7 +1607,7 @@ function addPluginToConfig(configPath, packageName) {
1309
1607
  $2`
1310
1608
  );
1311
1609
  }
1312
- fs6.writeFileSync(configPath, content);
1610
+ fs5.writeFileSync(configPath, content);
1313
1611
  }
1314
1612
  function removePluginFromConfig(configPath, pluginName) {
1315
1613
  let content = readConfigText(configPath);
@@ -1331,7 +1629,7 @@ function removePluginFromConfig(configPath, pluginName) {
1331
1629
  content = content.replace(pattern, "\n");
1332
1630
  }
1333
1631
  content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
1334
- fs6.writeFileSync(configPath, content);
1632
+ fs5.writeFileSync(configPath, content);
1335
1633
  }
1336
1634
  function escapeRegex(str) {
1337
1635
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1363,31 +1661,31 @@ var listCommand = new Command7("list").alias("ls").description("List plugins def
1363
1661
  if (plugins.length === 0 && devPlugins.length === 0) {
1364
1662
  printInfo("No plugins configured");
1365
1663
  console.log("");
1366
- console.log(chalk9.dim(" Hint: Add plugins to your objectstack.config.ts"));
1367
- console.log(chalk9.dim(" Or run: os plugin add <package-name>"));
1664
+ console.log(chalk7.dim(" Hint: Add plugins to your objectstack.config.ts"));
1665
+ console.log(chalk7.dim(" Or run: os plugin add <package-name>"));
1368
1666
  console.log("");
1369
1667
  return;
1370
1668
  }
1371
1669
  if (plugins.length > 0) {
1372
- console.log(chalk9.bold(`
1670
+ console.log(chalk7.bold(`
1373
1671
  Plugins (${plugins.length}):`));
1374
1672
  for (const plugin of plugins) {
1375
1673
  const name = resolvePluginName(plugin);
1376
1674
  const version = resolvePluginVersion(plugin);
1377
1675
  const type = resolvePluginType(plugin);
1378
1676
  console.log(
1379
- ` ${chalk9.cyan("\u25CF")} ${chalk9.white(name)}` + (version !== "-" ? chalk9.dim(` v${version}`) : "") + (type !== "standard" ? chalk9.dim(` [${type}]`) : "")
1677
+ ` ${chalk7.cyan("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + (type !== "standard" ? chalk7.dim(` [${type}]`) : "")
1380
1678
  );
1381
1679
  }
1382
1680
  }
1383
1681
  if (devPlugins.length > 0) {
1384
- console.log(chalk9.bold(`
1682
+ console.log(chalk7.bold(`
1385
1683
  Dev Plugins (${devPlugins.length}):`));
1386
1684
  for (const plugin of devPlugins) {
1387
1685
  const name = resolvePluginName(plugin);
1388
1686
  const version = resolvePluginVersion(plugin);
1389
1687
  console.log(
1390
- ` ${chalk9.yellow("\u25CF")} ${chalk9.white(name)}` + (version !== "-" ? chalk9.dim(` v${version}`) : "") + chalk9.dim(" [dev]")
1688
+ ` ${chalk7.yellow("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + chalk7.dim(" [dev]")
1391
1689
  );
1392
1690
  }
1393
1691
  }
@@ -1411,9 +1709,9 @@ var infoSubCommand = new Command7("info").description("Show detailed information
1411
1709
  if (!found) {
1412
1710
  printError(`Plugin '${name}' not found in configuration`);
1413
1711
  console.log("");
1414
- console.log(chalk9.dim(" Available plugins:"));
1712
+ console.log(chalk7.dim(" Available plugins:"));
1415
1713
  for (const p of allPlugins) {
1416
- console.log(chalk9.dim(` - ${resolvePluginName(p)}`));
1714
+ console.log(chalk7.dim(` - ${resolvePluginName(p)}`));
1417
1715
  }
1418
1716
  console.log("");
1419
1717
  process.exit(1);
@@ -1449,15 +1747,15 @@ var addCommand = new Command7("add").description("Add a plugin to objectstack.co
1449
1747
  try {
1450
1748
  const configPath = resolveConfigPath(options?.config);
1451
1749
  printHeader("Add Plugin");
1452
- console.log(` ${chalk9.dim("Package:")} ${chalk9.white(packageName)}`);
1453
- console.log(` ${chalk9.dim("Config:")} ${chalk9.white(path6.relative(process.cwd(), configPath))}`);
1750
+ console.log(` ${chalk7.dim("Package:")} ${chalk7.white(packageName)}`);
1751
+ console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1454
1752
  console.log("");
1455
1753
  addPluginToConfig(configPath, packageName);
1456
- printSuccess(`Added ${chalk9.cyan(packageName)} to config`);
1754
+ printSuccess(`Added ${chalk7.cyan(packageName)} to config`);
1457
1755
  console.log("");
1458
- console.log(chalk9.dim(" Next steps:"));
1459
- console.log(chalk9.dim(` 1. Install the package: pnpm add ${packageName}`));
1460
- console.log(chalk9.dim(" 2. Run: os validate"));
1756
+ console.log(chalk7.dim(" Next steps:"));
1757
+ console.log(chalk7.dim(` 1. Install the package: pnpm add ${packageName}`));
1758
+ console.log(chalk7.dim(" 2. Run: os validate"));
1461
1759
  console.log("");
1462
1760
  } catch (error) {
1463
1761
  printError(error.message || String(error));
@@ -1468,13 +1766,13 @@ var removeCommand = new Command7("remove").alias("rm").description("Remove a plu
1468
1766
  try {
1469
1767
  const configPath = resolveConfigPath(options?.config);
1470
1768
  printHeader("Remove Plugin");
1471
- console.log(` ${chalk9.dim("Plugin:")} ${chalk9.white(pluginName)}`);
1472
- console.log(` ${chalk9.dim("Config:")} ${chalk9.white(path6.relative(process.cwd(), configPath))}`);
1769
+ console.log(` ${chalk7.dim("Plugin:")} ${chalk7.white(pluginName)}`);
1770
+ console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1473
1771
  console.log("");
1474
1772
  removePluginFromConfig(configPath, pluginName);
1475
- printSuccess(`Removed ${chalk9.cyan(pluginName)} from config`);
1773
+ printSuccess(`Removed ${chalk7.cyan(pluginName)} from config`);
1476
1774
  console.log("");
1477
- console.log(chalk9.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
1775
+ console.log(chalk7.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
1478
1776
  console.log("");
1479
1777
  } catch (error) {
1480
1778
  printError(error.message || String(error));
@@ -1485,14 +1783,14 @@ var pluginCommand = new Command7("plugin").description("Manage plugins (list, in
1485
1783
 
1486
1784
  // src/commands/dev.ts
1487
1785
  import { Command as Command8 } from "commander";
1488
- import chalk10 from "chalk";
1786
+ import chalk8 from "chalk";
1489
1787
  import { execSync, spawn } from "child_process";
1490
- import fs7 from "fs";
1491
- import path7 from "path";
1788
+ import fs6 from "fs";
1789
+ import path6 from "path";
1492
1790
  var devCommand = new Command8("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1493
1791
  printHeader("Development Mode");
1494
- const configPath = path7.resolve(process.cwd(), "objectstack.config.ts");
1495
- if (packageName === "all" && fs7.existsSync(configPath)) {
1792
+ const configPath = path6.resolve(process.cwd(), "objectstack.config.ts");
1793
+ if (packageName === "all" && fs6.existsSync(configPath)) {
1496
1794
  printKV("Config", configPath, "\u{1F4C2}");
1497
1795
  printStep("Starting dev server...");
1498
1796
  const binPath = process.argv[1];
@@ -1504,18 +1802,18 @@ var devCommand = new Command8("dev").description("Start development mode with ho
1504
1802
  }
1505
1803
  try {
1506
1804
  const cwd = process.cwd();
1507
- const workspaceConfigPath = path7.resolve(cwd, "pnpm-workspace.yaml");
1508
- const isWorkspaceRoot = fs7.existsSync(workspaceConfigPath);
1805
+ const workspaceConfigPath = path6.resolve(cwd, "pnpm-workspace.yaml");
1806
+ const isWorkspaceRoot = fs6.existsSync(workspaceConfigPath);
1509
1807
  if (packageName === "all" && !isWorkspaceRoot) {
1510
1808
  printError(`Config file not found in ${cwd}`);
1511
- console.error(chalk10.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
1809
+ console.error(chalk8.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
1512
1810
  process.exit(1);
1513
1811
  }
1514
1812
  const filter = packageName === "all" ? "" : `--filter ${packageName}`;
1515
1813
  printKV("Package", packageName === "all" ? "All packages" : packageName, "\u{1F4E6}");
1516
1814
  printKV("Watch", "enabled", "\u{1F504}");
1517
1815
  const command = `pnpm ${filter} dev`.trim();
1518
- console.log(chalk10.dim(`$ ${command}`));
1816
+ console.log(chalk8.dim(`$ ${command}`));
1519
1817
  console.log("");
1520
1818
  execSync(command, {
1521
1819
  stdio: "inherit",
@@ -1529,37 +1827,37 @@ var devCommand = new Command8("dev").description("Start development mode with ho
1529
1827
 
1530
1828
  // src/commands/serve.ts
1531
1829
  import { Command as Command9 } from "commander";
1532
- import path9 from "path";
1533
- import fs9 from "fs";
1830
+ import path8 from "path";
1831
+ import fs8 from "fs";
1534
1832
  import net from "net";
1535
- import chalk11 from "chalk";
1536
- import { bundleRequire as bundleRequire2 } from "bundle-require";
1833
+ import chalk9 from "chalk";
1834
+ import { bundleRequire } from "bundle-require";
1537
1835
 
1538
1836
  // src/utils/studio.ts
1539
- import path8 from "path";
1540
- import fs8 from "fs";
1837
+ import path7 from "path";
1838
+ import fs7 from "fs";
1541
1839
  import { createRequire } from "module";
1542
1840
  import { pathToFileURL } from "url";
1543
1841
  var STUDIO_PATH = "/_studio";
1544
1842
  function resolveStudioPath() {
1545
1843
  const cwd = process.cwd();
1546
1844
  const candidates = [
1547
- path8.resolve(cwd, "apps/studio"),
1548
- path8.resolve(cwd, "../../apps/studio"),
1549
- path8.resolve(cwd, "../apps/studio")
1845
+ path7.resolve(cwd, "apps/studio"),
1846
+ path7.resolve(cwd, "../../apps/studio"),
1847
+ path7.resolve(cwd, "../apps/studio")
1550
1848
  ];
1551
1849
  for (const candidate of candidates) {
1552
- const pkgPath = path8.join(candidate, "package.json");
1553
- if (fs8.existsSync(pkgPath)) {
1850
+ const pkgPath = path7.join(candidate, "package.json");
1851
+ if (fs7.existsSync(pkgPath)) {
1554
1852
  try {
1555
- const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
1853
+ const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
1556
1854
  if (pkg.name === "@objectstack/studio") return candidate;
1557
1855
  } catch {
1558
1856
  }
1559
1857
  }
1560
1858
  }
1561
1859
  const resolutionBases = [
1562
- pathToFileURL(path8.join(cwd, "package.json")).href,
1860
+ pathToFileURL(path7.join(cwd, "package.json")).href,
1563
1861
  // consumer workspace
1564
1862
  import.meta.url
1565
1863
  // CLI package itself
@@ -1568,18 +1866,18 @@ function resolveStudioPath() {
1568
1866
  try {
1569
1867
  const req = createRequire(base);
1570
1868
  const resolved = req.resolve("@objectstack/studio/package.json");
1571
- return path8.dirname(resolved);
1869
+ return path7.dirname(resolved);
1572
1870
  } catch {
1573
1871
  }
1574
1872
  }
1575
- const directPath = path8.join(cwd, "node_modules", "@objectstack", "studio");
1576
- if (fs8.existsSync(path8.join(directPath, "package.json"))) {
1873
+ const directPath = path7.join(cwd, "node_modules", "@objectstack", "studio");
1874
+ if (fs7.existsSync(path7.join(directPath, "package.json"))) {
1577
1875
  return directPath;
1578
1876
  }
1579
1877
  return null;
1580
1878
  }
1581
1879
  function hasStudioDist(studioPath) {
1582
- return fs8.existsSync(path8.join(studioPath, "dist", "index.html"));
1880
+ return fs7.existsSync(path7.join(studioPath, "dist", "index.html"));
1583
1881
  }
1584
1882
  function createStudioStaticPlugin(distPath, options) {
1585
1883
  return {
@@ -1593,13 +1891,13 @@ function createStudioStaticPlugin(distPath, options) {
1593
1891
  return;
1594
1892
  }
1595
1893
  const app = httpServer.getRawApp();
1596
- const absoluteDist = path8.resolve(distPath);
1597
- const indexPath = path8.join(absoluteDist, "index.html");
1598
- if (!fs8.existsSync(indexPath)) {
1894
+ const absoluteDist = path7.resolve(distPath);
1895
+ const indexPath = path7.join(absoluteDist, "index.html");
1896
+ if (!fs7.existsSync(indexPath)) {
1599
1897
  ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
1600
1898
  return;
1601
1899
  }
1602
- const rawHtml = fs8.readFileSync(indexPath, "utf-8");
1900
+ const rawHtml = fs7.readFileSync(indexPath, "utf-8");
1603
1901
  const rewrittenHtml = rawHtml.replace(
1604
1902
  /(\s(?:href|src))="\/(?!\/)/g,
1605
1903
  `$1="${STUDIO_PATH}/`
@@ -1610,12 +1908,12 @@ function createStudioStaticPlugin(distPath, options) {
1610
1908
  app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
1611
1909
  app.get(`${STUDIO_PATH}/*`, async (c) => {
1612
1910
  const reqPath = c.req.path.substring(STUDIO_PATH.length) || "/";
1613
- const filePath = path8.join(absoluteDist, reqPath);
1911
+ const filePath = path7.join(absoluteDist, reqPath);
1614
1912
  if (!filePath.startsWith(absoluteDist)) {
1615
1913
  return c.text("Forbidden", 403);
1616
1914
  }
1617
- if (fs8.existsSync(filePath) && fs8.statSync(filePath).isFile()) {
1618
- const content = fs8.readFileSync(filePath);
1915
+ if (fs7.existsSync(filePath) && fs7.statSync(filePath).isFile()) {
1916
+ const content = fs7.readFileSync(filePath);
1619
1917
  return new Response(content, {
1620
1918
  headers: { "content-type": mimeType(filePath) }
1621
1919
  });
@@ -1645,7 +1943,7 @@ var MIME_TYPES = {
1645
1943
  ".map": "application/json"
1646
1944
  };
1647
1945
  function mimeType(filePath) {
1648
- const ext = path8.extname(filePath).toLowerCase();
1946
+ const ext = path7.extname(filePath).toLowerCase();
1649
1947
  return MIME_TYPES[ext] || "application/octet-stream";
1650
1948
  }
1651
1949
 
@@ -1682,15 +1980,15 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1682
1980
  } catch (e) {
1683
1981
  }
1684
1982
  const isDev = options.dev || process.env.NODE_ENV === "development";
1685
- const absolutePath = path9.resolve(process.cwd(), configPath);
1686
- const relativeConfig = path9.relative(process.cwd(), absolutePath);
1687
- if (!fs9.existsSync(absolutePath)) {
1983
+ const absolutePath = path8.resolve(process.cwd(), configPath);
1984
+ const relativeConfig = path8.relative(process.cwd(), absolutePath);
1985
+ if (!fs8.existsSync(absolutePath)) {
1688
1986
  printError(`Configuration file not found: ${absolutePath}`);
1689
- console.log(chalk11.dim(" Hint: Run `objectstack init` to create a new project"));
1987
+ console.log(chalk9.dim(" Hint: Run `objectstack init` to create a new project"));
1690
1988
  process.exit(1);
1691
1989
  }
1692
1990
  console.log("");
1693
- console.log(chalk11.dim(` Loading ${relativeConfig}...`));
1991
+ console.log(chalk9.dim(` Loading ${relativeConfig}...`));
1694
1992
  const loadedPlugins = [];
1695
1993
  const shortPluginName = (raw) => {
1696
1994
  if (raw.includes("objectql")) return "ObjectQL";
@@ -1725,7 +2023,7 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1725
2023
  console.debug = (...args) => {
1726
2024
  if (!bootQuiet) originalConsoleDebug(...args);
1727
2025
  };
1728
- const { mod } = await bundleRequire2({
2026
+ const { mod } = await bundleRequire({
1729
2027
  filepath: absolutePath
1730
2028
  });
1731
2029
  const config = mod.default || mod;
@@ -1794,7 +2092,7 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1794
2092
  const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
1795
2093
  trackPlugin(pluginName);
1796
2094
  } catch (e) {
1797
- console.error(chalk11.red(` \u2717 Failed to load plugin: ${e.message}`));
2095
+ console.error(chalk9.red(` \u2717 Failed to load plugin: ${e.message}`));
1798
2096
  }
1799
2097
  }
1800
2098
  }
@@ -1805,7 +2103,7 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1805
2103
  await kernel.use(serverPlugin);
1806
2104
  trackPlugin("HonoServer");
1807
2105
  } catch (e) {
1808
- console.warn(chalk11.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
2106
+ console.warn(chalk9.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
1809
2107
  }
1810
2108
  try {
1811
2109
  const { createRestApiPlugin } = await import("@objectstack/rest");
@@ -1824,13 +2122,13 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1824
2122
  if (enableUI) {
1825
2123
  const studioPath = resolveStudioPath();
1826
2124
  if (!studioPath) {
1827
- console.warn(chalk11.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
2125
+ console.warn(chalk9.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
1828
2126
  } else if (hasStudioDist(studioPath)) {
1829
- const distPath = path9.join(studioPath, "dist");
2127
+ const distPath = path8.join(studioPath, "dist");
1830
2128
  await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
1831
2129
  trackPlugin("StudioUI");
1832
2130
  } else {
1833
- console.warn(chalk11.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
2131
+ console.warn(chalk9.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
1834
2132
  }
1835
2133
  }
1836
2134
  await runtime.start();
@@ -1846,33 +2144,33 @@ var serveCommand = new Command9("serve").description("Start ObjectStack server w
1846
2144
  studioPath: STUDIO_PATH
1847
2145
  });
1848
2146
  process.on("SIGINT", async () => {
1849
- console.warn(chalk11.yellow(`
2147
+ console.warn(chalk9.yellow(`
1850
2148
 
1851
2149
  \u23F9 Stopping server...`));
1852
2150
  await runtime.getKernel().shutdown();
1853
- console.log(chalk11.green(`\u2705 Server stopped`));
2151
+ console.log(chalk9.green(`\u2705 Server stopped`));
1854
2152
  process.exit(0);
1855
2153
  });
1856
2154
  } catch (error) {
1857
2155
  restoreOutput();
1858
2156
  console.log("");
1859
2157
  printError(error.message || String(error));
1860
- if (process.env.DEBUG) console.error(chalk11.dim(error.stack));
2158
+ if (process.env.DEBUG) console.error(chalk9.dim(error.stack));
1861
2159
  process.exit(1);
1862
2160
  }
1863
2161
  });
1864
2162
 
1865
2163
  // src/commands/test.ts
1866
2164
  import { Command as Command10 } from "commander";
1867
- import chalk12 from "chalk";
1868
- import path10 from "path";
1869
- import fs10 from "fs";
2165
+ import chalk10 from "chalk";
2166
+ import path9 from "path";
2167
+ import fs9 from "fs";
1870
2168
  import { QA as CoreQA } from "@objectstack/core";
1871
2169
  function resolveGlob(pattern) {
1872
2170
  if (!pattern.includes("*")) {
1873
- return fs10.existsSync(pattern) ? [pattern] : [];
2171
+ return fs9.existsSync(pattern) ? [pattern] : [];
1874
2172
  }
1875
- const parts = pattern.split(path10.sep.replace("\\", "/"));
2173
+ const parts = pattern.split(path9.sep.replace("\\", "/"));
1876
2174
  const segments = pattern.includes("/") ? pattern.split("/") : parts;
1877
2175
  let baseDir = ".";
1878
2176
  let globStart = 0;
@@ -1881,25 +2179,25 @@ function resolveGlob(pattern) {
1881
2179
  globStart = i;
1882
2180
  break;
1883
2181
  }
1884
- baseDir = i === 0 ? segments[i] : path10.join(baseDir, segments[i]);
2182
+ baseDir = i === 0 ? segments[i] : path9.join(baseDir, segments[i]);
1885
2183
  }
1886
- if (!fs10.existsSync(baseDir)) return [];
2184
+ if (!fs9.existsSync(baseDir)) return [];
1887
2185
  const globPortion = segments.slice(globStart).join("/");
1888
2186
  const regexStr = globPortion.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
1889
2187
  const regex = new RegExp(`^${regexStr}$`);
1890
- const entries = fs10.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
1891
- return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path10.join(baseDir, entry)).filter((fullPath) => fs10.statSync(fullPath).isFile());
2188
+ const entries = fs9.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
2189
+ return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path9.join(baseDir, entry)).filter((fullPath) => fs9.statSync(fullPath).isFile());
1892
2190
  }
1893
2191
  var testCommand = new Command10("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
1894
- console.log(chalk12.bold(`
2192
+ console.log(chalk10.bold(`
1895
2193
  \u{1F9EA} ObjectStack Quality Protocol Runner`));
1896
- console.log(chalk12.dim(`-------------------------------------`));
1897
- console.log(`Target: ${chalk12.blue(options.url)}`);
2194
+ console.log(chalk10.dim(`-------------------------------------`));
2195
+ console.log(`Target: ${chalk10.blue(options.url)}`);
1898
2196
  const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
1899
2197
  const runner = new CoreQA.TestRunner(adapter);
1900
2198
  const testFiles = resolveGlob(filesPattern);
1901
2199
  if (testFiles.length === 0) {
1902
- console.warn(chalk12.yellow(`No test files found matching: ${filesPattern}`));
2200
+ console.warn(chalk10.yellow(`No test files found matching: ${filesPattern}`));
1903
2201
  return;
1904
2202
  }
1905
2203
  console.log(`Found ${testFiles.length} test suites.`);
@@ -1907,19 +2205,19 @@ var testCommand = new Command10("test").description("Run Quality Protocol test s
1907
2205
  let totalFailed = 0;
1908
2206
  for (const file of testFiles) {
1909
2207
  console.log(`
1910
- \u{1F4C4} Running suite: ${chalk12.bold(path10.basename(file))}`);
2208
+ \u{1F4C4} Running suite: ${chalk10.bold(path9.basename(file))}`);
1911
2209
  try {
1912
- const content = fs10.readFileSync(file, "utf-8");
2210
+ const content = fs9.readFileSync(file, "utf-8");
1913
2211
  const suite = JSON.parse(content);
1914
2212
  const results = await runner.runSuite(suite);
1915
2213
  for (const result of results) {
1916
2214
  const icon = result.passed ? "\u2705" : "\u274C";
1917
2215
  console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
1918
2216
  if (!result.passed) {
1919
- console.error(chalk12.red(` Error: ${result.error}`));
2217
+ console.error(chalk10.red(` Error: ${result.error}`));
1920
2218
  result.steps.forEach((step) => {
1921
2219
  if (!step.passed) {
1922
- console.error(chalk12.red(` Step Failed: ${step.stepName}`));
2220
+ console.error(chalk10.red(` Step Failed: ${step.stepName}`));
1923
2221
  if (step.output) console.error(` Output:`, step.output);
1924
2222
  if (step.error) console.error(` Error:`, step.error);
1925
2223
  }
@@ -1930,28 +2228,258 @@ var testCommand = new Command10("test").description("Run Quality Protocol test s
1930
2228
  }
1931
2229
  }
1932
2230
  } catch (e) {
1933
- console.error(chalk12.red(`Failed to load or run suite ${file}: ${e}`));
2231
+ console.error(chalk10.red(`Failed to load or run suite ${file}: ${e}`));
1934
2232
  totalFailed++;
1935
2233
  }
1936
2234
  }
1937
- console.log(chalk12.dim(`
2235
+ console.log(chalk10.dim(`
1938
2236
  -------------------------------------`));
1939
2237
  if (totalFailed > 0) {
1940
- console.log(chalk12.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
2238
+ console.log(chalk10.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
1941
2239
  process.exit(1);
1942
2240
  } else {
1943
- console.log(chalk12.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
2241
+ console.log(chalk10.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
1944
2242
  process.exit(0);
1945
2243
  }
1946
2244
  });
1947
2245
 
1948
2246
  // src/commands/doctor.ts
1949
2247
  import { Command as Command11 } from "commander";
1950
- import chalk13 from "chalk";
2248
+ import chalk11 from "chalk";
1951
2249
  import { execSync as execSync2 } from "child_process";
1952
- import fs11 from "fs";
1953
- import path11 from "path";
1954
- var doctorCommand = new Command11("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
2250
+ import fs10 from "fs";
2251
+ import path10 from "path";
2252
+ function detectCircularDependencies(objects) {
2253
+ const issues = [];
2254
+ const graph = /* @__PURE__ */ new Map();
2255
+ for (const obj of objects) {
2256
+ const deps = [];
2257
+ if (obj.fields && typeof obj.fields === "object") {
2258
+ for (const field of Object.values(obj.fields)) {
2259
+ if (field?.type === "lookup" && field?.reference) {
2260
+ deps.push(field.reference);
2261
+ }
2262
+ }
2263
+ }
2264
+ graph.set(obj.name, deps);
2265
+ }
2266
+ const visited = /* @__PURE__ */ new Set();
2267
+ const stack = /* @__PURE__ */ new Set();
2268
+ function dfs(node, path11) {
2269
+ if (stack.has(node)) {
2270
+ const cycleStart = path11.indexOf(node);
2271
+ const cycle = path11.slice(cycleStart).concat(node);
2272
+ issues.push(`Circular dependency: ${cycle.join(" \u2192 ")}`);
2273
+ return true;
2274
+ }
2275
+ if (visited.has(node)) return false;
2276
+ visited.add(node);
2277
+ stack.add(node);
2278
+ for (const dep of graph.get(node) || []) {
2279
+ if (graph.has(dep)) {
2280
+ dfs(dep, [...path11, node]);
2281
+ }
2282
+ }
2283
+ stack.delete(node);
2284
+ return false;
2285
+ }
2286
+ for (const name of graph.keys()) {
2287
+ if (!visited.has(name)) {
2288
+ dfs(name, []);
2289
+ }
2290
+ }
2291
+ return issues;
2292
+ }
2293
+ function findOrphanViews(config) {
2294
+ const objectNames = /* @__PURE__ */ new Set();
2295
+ if (Array.isArray(config.objects)) {
2296
+ for (const obj of config.objects) {
2297
+ if (obj.name) objectNames.add(obj.name);
2298
+ }
2299
+ }
2300
+ const orphans = [];
2301
+ if (Array.isArray(config.views)) {
2302
+ for (const view of config.views) {
2303
+ if (view.object && !objectNames.has(view.object)) {
2304
+ orphans.push(`View "${view.name || "?"}" references non-existent object "${view.object}"`);
2305
+ }
2306
+ }
2307
+ }
2308
+ return orphans;
2309
+ }
2310
+ function findUnusedObjects(config) {
2311
+ const objectNames = /* @__PURE__ */ new Set();
2312
+ if (Array.isArray(config.objects)) {
2313
+ for (const obj of config.objects) {
2314
+ if (obj.name) objectNames.add(obj.name);
2315
+ }
2316
+ }
2317
+ const referencedObjects = /* @__PURE__ */ new Set();
2318
+ if (Array.isArray(config.views)) {
2319
+ for (const view of config.views) {
2320
+ if (view.object) referencedObjects.add(view.object);
2321
+ }
2322
+ }
2323
+ if (Array.isArray(config.flows)) {
2324
+ for (const flow of config.flows) {
2325
+ if (flow.trigger?.object) referencedObjects.add(flow.trigger.object);
2326
+ if (flow.object) referencedObjects.add(flow.object);
2327
+ }
2328
+ }
2329
+ if (Array.isArray(config.apps)) {
2330
+ for (const app of config.apps) {
2331
+ if (Array.isArray(app.navigation)) {
2332
+ for (const nav of app.navigation) {
2333
+ if (nav.object) referencedObjects.add(nav.object);
2334
+ }
2335
+ }
2336
+ }
2337
+ }
2338
+ if (Array.isArray(config.agents)) {
2339
+ for (const agent of config.agents) {
2340
+ if (Array.isArray(agent.objects)) {
2341
+ for (const o of agent.objects) referencedObjects.add(o);
2342
+ }
2343
+ }
2344
+ }
2345
+ if (Array.isArray(config.objects)) {
2346
+ for (const obj of config.objects) {
2347
+ if (obj.fields && typeof obj.fields === "object") {
2348
+ for (const field of Object.values(obj.fields)) {
2349
+ if (field?.type === "lookup" && field?.reference) {
2350
+ referencedObjects.add(field.reference);
2351
+ }
2352
+ }
2353
+ }
2354
+ }
2355
+ }
2356
+ const unused = [];
2357
+ for (const name of objectNames) {
2358
+ if (!referencedObjects.has(name)) {
2359
+ unused.push(`Object "${name}" is defined but not referenced by any view, flow, app, or agent`);
2360
+ }
2361
+ }
2362
+ return unused;
2363
+ }
2364
+ function walkDir(dir, ext) {
2365
+ const results = [];
2366
+ if (!fs10.existsSync(dir)) return results;
2367
+ const entries = fs10.readdirSync(dir, { withFileTypes: true });
2368
+ for (const entry of entries) {
2369
+ if (entry.name === "node_modules") continue;
2370
+ const fullPath = path10.join(dir, entry.name);
2371
+ if (entry.isDirectory()) {
2372
+ results.push(...walkDir(fullPath, ext));
2373
+ } else if (entry.name.endsWith(ext)) {
2374
+ results.push(fullPath);
2375
+ }
2376
+ }
2377
+ return results;
2378
+ }
2379
+ function findMissingTests(cwd) {
2380
+ const specSrcDir = path10.join(cwd, "packages/spec/src");
2381
+ if (!fs10.existsSync(specSrcDir)) return [];
2382
+ const missing = [];
2383
+ const zodFiles = walkDir(specSrcDir, ".zod.ts");
2384
+ for (const zodFile of zodFiles) {
2385
+ const testFile = zodFile.replace(".zod.ts", ".test.ts");
2386
+ if (!fs10.existsSync(testFile)) {
2387
+ const relZod = path10.relative(specSrcDir, zodFile);
2388
+ const relTest = path10.relative(specSrcDir, testFile);
2389
+ missing.push(`Missing test: ${relTest} (for ${relZod})`);
2390
+ }
2391
+ }
2392
+ return missing;
2393
+ }
2394
+ function findDeprecatedUsages(cwd) {
2395
+ const specSrcDir = path10.join(cwd, "packages/spec/src");
2396
+ if (!fs10.existsSync(specSrcDir)) return [];
2397
+ const deprecated = [];
2398
+ const tsFiles = walkDir(specSrcDir, ".ts").filter((f) => !f.endsWith(".test.ts"));
2399
+ for (const tsFile of tsFiles) {
2400
+ try {
2401
+ const content = fs10.readFileSync(tsFile, "utf-8");
2402
+ const lines = content.split("\n");
2403
+ const relPath = path10.relative(specSrcDir, tsFile);
2404
+ for (let i = 0; i < lines.length; i++) {
2405
+ if (lines[i].includes("@deprecated")) {
2406
+ deprecated.push(`${relPath}:${i + 1} \u2014 @deprecated tag found`);
2407
+ }
2408
+ }
2409
+ } catch {
2410
+ }
2411
+ }
2412
+ return deprecated;
2413
+ }
2414
+ var DEPRECATED_PATTERNS = [
2415
+ {
2416
+ pattern: /\bEnhancedObjectKernel\b/,
2417
+ description: "EnhancedObjectKernel is deprecated in v3",
2418
+ replacement: "Use ObjectKernel instead"
2419
+ },
2420
+ {
2421
+ pattern: /\bmax_length\b/,
2422
+ description: "snake_case config key: max_length",
2423
+ replacement: "Use maxLength (camelCase)"
2424
+ },
2425
+ {
2426
+ pattern: /\bdefault_value\b/,
2427
+ description: "snake_case config key: default_value",
2428
+ replacement: "Use defaultValue (camelCase)"
2429
+ },
2430
+ {
2431
+ pattern: /\bmin_length\b/,
2432
+ description: "snake_case config key: min_length",
2433
+ replacement: "Use minLength (camelCase)"
2434
+ },
2435
+ {
2436
+ pattern: /\breference_filters\b/,
2437
+ description: "snake_case config key: reference_filters",
2438
+ replacement: "Use referenceFilters (camelCase)"
2439
+ },
2440
+ {
2441
+ pattern: /\bunique_name\b/,
2442
+ description: "snake_case config key: unique_name",
2443
+ replacement: "Use uniqueName (camelCase)"
2444
+ },
2445
+ {
2446
+ pattern: /from\s+['"]@objectstack\/core\/enhanced['"]/,
2447
+ description: "Import from deprecated @objectstack/core/enhanced path",
2448
+ replacement: "Use import from '@objectstack/core'"
2449
+ },
2450
+ {
2451
+ pattern: /from\s+['"]@objectstack\/spec\/dist\/[^'"]+['"]/,
2452
+ description: "Import from deprecated @objectstack/spec/dist/ deep path",
2453
+ replacement: "Use import from '@objectstack/spec'"
2454
+ }
2455
+ ];
2456
+ function scanDeprecatedPatterns(dir) {
2457
+ const results = [];
2458
+ if (!fs10.existsSync(dir)) return results;
2459
+ const tsFiles = walkDir(dir, ".ts").filter((f) => !f.endsWith(".test.ts"));
2460
+ for (const tsFile of tsFiles) {
2461
+ try {
2462
+ const content = fs10.readFileSync(tsFile, "utf-8");
2463
+ const lines = content.split("\n");
2464
+ const relPath = path10.relative(process.cwd(), tsFile);
2465
+ for (let i = 0; i < lines.length; i++) {
2466
+ for (const dp of DEPRECATED_PATTERNS) {
2467
+ if (dp.pattern.test(lines[i])) {
2468
+ results.push({
2469
+ file: relPath,
2470
+ line: i + 1,
2471
+ description: dp.description,
2472
+ replacement: dp.replacement
2473
+ });
2474
+ }
2475
+ }
2476
+ }
2477
+ } catch {
2478
+ }
2479
+ }
2480
+ return results;
2481
+ }
2482
+ var doctorCommand = new Command11("doctor").description("Check development environment and configuration health").option("-v, --verbose", "Show detailed information").option("--scan-deprecations", "Scan for deprecated ObjectStack patterns").action(async (options) => {
1955
2483
  printHeader("Environment Health Check");
1956
2484
  const results = [];
1957
2485
  try {
@@ -2010,8 +2538,8 @@ var doctorCommand = new Command11("doctor").description("Check development envir
2010
2538
  });
2011
2539
  }
2012
2540
  const cwd = process.cwd();
2013
- const nodeModulesPath = path11.join(cwd, "node_modules");
2014
- if (fs11.existsSync(nodeModulesPath)) {
2541
+ const nodeModulesPath = path10.join(cwd, "node_modules");
2542
+ if (fs10.existsSync(nodeModulesPath)) {
2015
2543
  results.push({
2016
2544
  name: "Dependencies",
2017
2545
  status: "ok",
@@ -2025,8 +2553,8 @@ var doctorCommand = new Command11("doctor").description("Check development envir
2025
2553
  fix: "Run: pnpm install"
2026
2554
  });
2027
2555
  }
2028
- const specDistPath = path11.join(cwd, "packages/spec/dist");
2029
- if (fs11.existsSync(specDistPath)) {
2556
+ const specDistPath = path10.join(cwd, "packages/spec/dist");
2557
+ if (fs10.existsSync(specDistPath)) {
2030
2558
  results.push({
2031
2559
  name: "@objectstack/spec",
2032
2560
  status: "ok",
@@ -2068,27 +2596,108 @@ var doctorCommand = new Command11("doctor").description("Check development envir
2068
2596
  printError(`${padded} ${result.message}`);
2069
2597
  }
2070
2598
  if (result.fix && (options.verbose || result.status === "error")) {
2071
- console.log(chalk13.dim(` \u2192 ${result.fix}`));
2599
+ console.log(chalk11.dim(` \u2192 ${result.fix}`));
2072
2600
  }
2073
2601
  if (result.status === "error") hasErrors = true;
2074
2602
  if (result.status === "warning") hasWarnings = true;
2075
2603
  });
2604
+ printStep("Checking for missing test files...");
2605
+ const missingTests = findMissingTests(cwd);
2606
+ if (missingTests.length > 0) {
2607
+ hasWarnings = true;
2608
+ for (const msg of missingTests) {
2609
+ printWarning(msg);
2610
+ }
2611
+ } else {
2612
+ printSuccess("Test coverage All *.zod.ts files have matching tests");
2613
+ }
2614
+ printStep("Scanning for @deprecated usage...");
2615
+ const deprecatedUsages = findDeprecatedUsages(cwd);
2616
+ if (deprecatedUsages.length > 0) {
2617
+ hasWarnings = true;
2618
+ for (const msg of deprecatedUsages) {
2619
+ printWarning(`Deprecated: ${msg}`);
2620
+ }
2621
+ } else {
2622
+ printSuccess("Deprecations No @deprecated tags found");
2623
+ }
2624
+ if (configExists()) {
2625
+ printStep("Loading configuration for analysis...");
2626
+ try {
2627
+ const { config } = await loadConfig();
2628
+ if (Array.isArray(config.objects) && config.objects.length > 0) {
2629
+ printStep("Checking for circular dependencies...");
2630
+ const cycles = detectCircularDependencies(config.objects);
2631
+ if (cycles.length > 0) {
2632
+ hasWarnings = true;
2633
+ for (const msg of cycles) {
2634
+ printWarning(msg);
2635
+ }
2636
+ } else {
2637
+ printSuccess("Dependencies No circular references detected");
2638
+ }
2639
+ printStep("Checking for unused objects...");
2640
+ const unused = findUnusedObjects(config);
2641
+ if (unused.length > 0) {
2642
+ hasWarnings = true;
2643
+ for (const msg of unused) {
2644
+ printWarning(msg);
2645
+ }
2646
+ } else {
2647
+ printSuccess("Object usage All objects are referenced");
2648
+ }
2649
+ }
2650
+ if (Array.isArray(config.views) && config.views.length > 0) {
2651
+ printStep("Checking for orphan views...");
2652
+ const orphans = findOrphanViews(config);
2653
+ if (orphans.length > 0) {
2654
+ hasWarnings = true;
2655
+ for (const msg of orphans) {
2656
+ printWarning(msg);
2657
+ }
2658
+ } else {
2659
+ printSuccess("View integrity All views reference valid objects");
2660
+ }
2661
+ }
2662
+ } catch {
2663
+ printWarning("Could not load config for analysis (config checks skipped)");
2664
+ hasWarnings = true;
2665
+ }
2666
+ }
2667
+ if (options.scanDeprecations) {
2668
+ printStep("Scanning for deprecated ObjectStack patterns...");
2669
+ const scanDir = path10.join(cwd, "src");
2670
+ const deprecations = scanDeprecatedPatterns(scanDir);
2671
+ if (deprecations.length > 0) {
2672
+ hasWarnings = true;
2673
+ for (const dep of deprecations) {
2674
+ printWarning(`${dep.file}:${dep.line} \u2014 ${dep.description}`);
2675
+ if (options.verbose) {
2676
+ console.log(chalk11.dim(` \u2192 ${dep.replacement}`));
2677
+ }
2678
+ }
2679
+ console.log("");
2680
+ printInfo(`Found ${deprecations.length} deprecated pattern(s). Run \`objectstack codemod v2-to-v3\` to auto-fix.`);
2681
+ } else {
2682
+ printSuccess("Deprecation scan No deprecated patterns found");
2683
+ }
2684
+ }
2076
2685
  console.log("");
2077
2686
  if (hasErrors) {
2078
- console.log(chalk13.red("\u274C Some critical issues found. Please fix them before continuing."));
2079
- results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk13.dim(` ${r.fix}`)));
2687
+ console.log(chalk11.red("\u274C Some critical issues found. Please fix them before continuing."));
2688
+ results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk11.dim(` ${r.fix}`)));
2080
2689
  process.exit(1);
2081
2690
  } else if (hasWarnings) {
2082
- console.log(chalk13.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
2083
- console.log(chalk13.dim(" Run with --verbose to see fix suggestions."));
2691
+ console.log(chalk11.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
2692
+ console.log(chalk11.dim(" Run with --verbose to see fix suggestions."));
2084
2693
  } else {
2085
- console.log(chalk13.green("\u2705 Environment is healthy and ready for development!"));
2694
+ console.log(chalk11.green("\u2705 Environment is healthy and ready for development!"));
2086
2695
  }
2087
2696
  console.log("");
2088
2697
  });
2089
2698
 
2090
2699
  // src/utils/plugin-commands.ts
2091
- import chalk14 from "chalk";
2700
+ import chalk12 from "chalk";
2092
2701
  async function loadPluginCommands(program) {
2093
2702
  let config;
2094
2703
  try {
@@ -2132,7 +2741,7 @@ async function loadPluginCommands(program) {
2132
2741
  if (process.env.DEBUG) {
2133
2742
  const message = error instanceof Error ? error.message : String(error);
2134
2743
  console.error(
2135
- chalk14.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
2744
+ chalk12.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
2136
2745
  );
2137
2746
  }
2138
2747
  }