@objectstack/cli 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  # @objectstack/cli
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - # Upgrade to Zod v4 and Protocol Improvements
8
+
9
+ This release includes a major upgrade to the core validation engine (Zod v4) and aligns all protocol definitions with stricter type safety.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+ - @objectstack/spec@1.0.0
15
+ - @objectstack/core@1.0.0
16
+ - @objectstack/plugin-hono-server@1.0.0
17
+
18
+ ## 0.7.2
19
+
20
+ ### Patch Changes
21
+
22
+ - fb41cc0: Patch release: Updated documentation and JSON schemas
23
+ - Updated dependencies [fb41cc0]
24
+ - @objectstack/spec@0.7.2
25
+ - @objectstack/core@0.7.2
26
+ - @objectstack/plugin-hono-server@0.7.2
27
+
3
28
  ## 0.7.1
4
29
 
5
30
  ### Patch Changes
package/dist/bin.js CHANGED
@@ -1,11 +1,505 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  compileCommand
4
- } from "./chunk-RQRAZ23Z.js";
4
+ } from "./chunk-2YXVEYO7.js";
5
5
 
6
6
  // src/bin.ts
7
+ import { Command as Command5 } from "commander";
8
+
9
+ // src/commands/dev.ts
7
10
  import { Command } from "commander";
8
- var program = new Command();
9
- program.name("objectstack").description("CLI for ObjectStack Protocol").version("0.1.0");
11
+ import chalk from "chalk";
12
+ import { execSync } from "child_process";
13
+ var devCommand = new Command("dev").description("Start development mode for a package").argument("[package]", "Package name (without @objectstack/ prefix)", "all").option("-w, --watch", "Enable watch mode (default)", true).option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
14
+ console.log(chalk.bold(`
15
+ \u{1F680} ObjectStack Development Mode`));
16
+ console.log(chalk.dim(`-------------------------------`));
17
+ try {
18
+ const cwd = process.cwd();
19
+ const filter = packageName === "all" ? "" : `--filter @objectstack/${packageName}`;
20
+ console.log(`\u{1F4E6} Package: ${chalk.blue(packageName === "all" ? "All packages" : `@objectstack/${packageName}`)}`);
21
+ console.log(`\u{1F504} Watch mode: ${chalk.green("enabled")}`);
22
+ console.log("");
23
+ const command = `pnpm ${filter} dev`.trim();
24
+ console.log(chalk.dim(`$ ${command}`));
25
+ console.log("");
26
+ execSync(command, {
27
+ stdio: "inherit",
28
+ cwd
29
+ });
30
+ } catch (error) {
31
+ console.error(chalk.red(`
32
+ \u274C Development mode failed:`));
33
+ console.error(error.message || error);
34
+ process.exit(1);
35
+ }
36
+ });
37
+
38
+ // src/commands/doctor.ts
39
+ import { Command as Command2 } from "commander";
40
+ import chalk2 from "chalk";
41
+ import { execSync as execSync2 } from "child_process";
42
+ import fs from "fs";
43
+ import path from "path";
44
+ var doctorCommand = new Command2("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
45
+ console.log(chalk2.bold(`
46
+ \u{1F3E5} ObjectStack Environment Health Check`));
47
+ console.log(chalk2.dim(`-----------------------------------------`));
48
+ console.log("");
49
+ const results = [];
50
+ try {
51
+ const nodeVersion = process.version;
52
+ const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
53
+ if (majorVersion >= 18) {
54
+ results.push({
55
+ name: "Node.js",
56
+ status: "ok",
57
+ message: `Version ${nodeVersion}`
58
+ });
59
+ } else {
60
+ results.push({
61
+ name: "Node.js",
62
+ status: "error",
63
+ message: `Version ${nodeVersion} (requires >= 18.0.0)`,
64
+ fix: "Upgrade Node.js: https://nodejs.org"
65
+ });
66
+ }
67
+ } catch (error) {
68
+ results.push({
69
+ name: "Node.js",
70
+ status: "error",
71
+ message: "Not found",
72
+ fix: "Install Node.js: https://nodejs.org"
73
+ });
74
+ }
75
+ try {
76
+ const pnpmVersion = execSync2("pnpm -v", { encoding: "utf-8" }).trim();
77
+ results.push({
78
+ name: "pnpm",
79
+ status: "ok",
80
+ message: `Version ${pnpmVersion}`
81
+ });
82
+ } catch (error) {
83
+ results.push({
84
+ name: "pnpm",
85
+ status: "error",
86
+ message: "Not found",
87
+ fix: "Install pnpm: npm install -g pnpm@10.28.1"
88
+ });
89
+ }
90
+ try {
91
+ const tscVersion = execSync2("tsc -v", { encoding: "utf-8" }).trim();
92
+ results.push({
93
+ name: "TypeScript",
94
+ status: "ok",
95
+ message: tscVersion
96
+ });
97
+ } catch (error) {
98
+ results.push({
99
+ name: "TypeScript",
100
+ status: "warning",
101
+ message: "Not found in PATH",
102
+ fix: "Installed locally via pnpm"
103
+ });
104
+ }
105
+ const cwd = process.cwd();
106
+ const nodeModulesPath = path.join(cwd, "node_modules");
107
+ if (fs.existsSync(nodeModulesPath)) {
108
+ results.push({
109
+ name: "Dependencies",
110
+ status: "ok",
111
+ message: "Installed"
112
+ });
113
+ } else {
114
+ results.push({
115
+ name: "Dependencies",
116
+ status: "error",
117
+ message: "Not installed",
118
+ fix: "Run: pnpm install"
119
+ });
120
+ }
121
+ const specDistPath = path.join(cwd, "packages/spec/dist");
122
+ if (fs.existsSync(specDistPath)) {
123
+ results.push({
124
+ name: "@objectstack/spec",
125
+ status: "ok",
126
+ message: "Built"
127
+ });
128
+ } else {
129
+ results.push({
130
+ name: "@objectstack/spec",
131
+ status: "warning",
132
+ message: "Not built",
133
+ fix: "Run: pnpm --filter @objectstack/spec build"
134
+ });
135
+ }
136
+ try {
137
+ const gitVersion = execSync2("git --version", { encoding: "utf-8" }).trim();
138
+ results.push({
139
+ name: "Git",
140
+ status: "ok",
141
+ message: gitVersion
142
+ });
143
+ } catch (error) {
144
+ results.push({
145
+ name: "Git",
146
+ status: "warning",
147
+ message: "Not found",
148
+ fix: "Install Git for version control"
149
+ });
150
+ }
151
+ let hasErrors = false;
152
+ let hasWarnings = false;
153
+ results.forEach((result) => {
154
+ const icon = result.status === "ok" ? "\u2713" : result.status === "warning" ? "\u26A0" : "\u2717";
155
+ const color = result.status === "ok" ? chalk2.green : result.status === "warning" ? chalk2.yellow : chalk2.red;
156
+ console.log(color(`${icon} ${result.name.padEnd(20)} ${result.message}`));
157
+ if (result.fix && options.verbose) {
158
+ console.log(chalk2.dim(` \u2192 ${result.fix}`));
159
+ }
160
+ if (result.status === "error") hasErrors = true;
161
+ if (result.status === "warning") hasWarnings = true;
162
+ });
163
+ console.log("");
164
+ if (hasErrors) {
165
+ console.log(chalk2.red("\u274C Some critical issues found. Please fix them before continuing."));
166
+ results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk2.dim(` ${r.fix}`)));
167
+ process.exit(1);
168
+ } else if (hasWarnings) {
169
+ console.log(chalk2.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
170
+ console.log(chalk2.dim(" Run with --verbose to see fix suggestions."));
171
+ } else {
172
+ console.log(chalk2.green("\u2705 Environment is healthy and ready for development!"));
173
+ }
174
+ console.log("");
175
+ });
176
+
177
+ // src/commands/create.ts
178
+ import { Command as Command3 } from "commander";
179
+ import chalk3 from "chalk";
180
+ import fs2 from "fs";
181
+ import path2 from "path";
182
+ var templates = {
183
+ plugin: {
184
+ description: "Create a new ObjectStack plugin",
185
+ files: {
186
+ "package.json": (name) => ({
187
+ name: `@objectstack/plugin-${name}`,
188
+ version: "0.1.0",
189
+ description: `ObjectStack Plugin: ${name}`,
190
+ main: "dist/index.js",
191
+ types: "dist/index.d.ts",
192
+ scripts: {
193
+ build: "tsc",
194
+ dev: "tsc --watch",
195
+ test: "vitest"
196
+ },
197
+ keywords: ["objectstack", "plugin", name],
198
+ author: "",
199
+ license: "MIT",
200
+ dependencies: {
201
+ "@objectstack/spec": "workspace:*",
202
+ zod: "^3.22.4"
203
+ },
204
+ devDependencies: {
205
+ "@types/node": "^20.10.0",
206
+ typescript: "^5.3.0",
207
+ vitest: "^2.1.8"
208
+ }
209
+ }),
210
+ "tsconfig.json": () => ({
211
+ extends: "../../tsconfig.json",
212
+ compilerOptions: {
213
+ outDir: "dist",
214
+ rootDir: "src"
215
+ },
216
+ include: ["src/**/*"]
217
+ }),
218
+ "src/index.ts": (name) => `import type { Plugin } from '@objectstack/spec';
219
+
220
+ /**
221
+ * ${name} Plugin for ObjectStack
222
+ */
223
+ export const ${toCamelCase(name)}Plugin: Plugin = {
224
+ name: '${name}',
225
+ version: '0.1.0',
226
+
227
+ async initialize(context) {
228
+ console.log('Initializing ${name} plugin...');
229
+ // Plugin initialization logic
230
+ },
231
+
232
+ async destroy() {
233
+ console.log('Destroying ${name} plugin...');
234
+ // Plugin cleanup logic
235
+ },
236
+ };
237
+
238
+ export default ${toCamelCase(name)}Plugin;
239
+ `,
240
+ "README.md": (name) => `# @objectstack/plugin-${name}
241
+
242
+ ObjectStack Plugin: ${name}
243
+
244
+ ## Installation
245
+
246
+ \`\`\`bash
247
+ pnpm add @objectstack/plugin-${name}
248
+ \`\`\`
249
+
250
+ ## Usage
251
+
252
+ \`\`\`typescript
253
+ import { ${toCamelCase(name)}Plugin } from '@objectstack/plugin-${name}';
254
+
255
+ // Use the plugin in your ObjectStack configuration
256
+ export default {
257
+ plugins: [
258
+ ${toCamelCase(name)}Plugin,
259
+ ],
260
+ };
261
+ \`\`\`
262
+
263
+ ## License
264
+
265
+ MIT
266
+ `
267
+ }
268
+ },
269
+ example: {
270
+ description: "Create a new ObjectStack example application",
271
+ files: {
272
+ "package.json": (name) => ({
273
+ name: `@objectstack/example-${name}`,
274
+ version: "0.1.0",
275
+ private: true,
276
+ description: `ObjectStack Example: ${name}`,
277
+ scripts: {
278
+ build: "objectstack compile",
279
+ dev: "tsx watch objectstack.config.ts",
280
+ test: "vitest"
281
+ },
282
+ dependencies: {
283
+ "@objectstack/spec": "workspace:*",
284
+ "@objectstack/cli": "workspace:*",
285
+ zod: "^3.22.4"
286
+ },
287
+ devDependencies: {
288
+ "@types/node": "^20.10.0",
289
+ tsx: "^4.21.0",
290
+ typescript: "^5.3.0",
291
+ vitest: "^2.1.8"
292
+ }
293
+ }),
294
+ "objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
295
+
296
+ export default defineStack({
297
+ metadata: {
298
+ name: '${name}',
299
+ version: '0.1.0',
300
+ description: '${name} example application',
301
+ },
302
+
303
+ objects: {
304
+ // Define your data objects here
305
+ },
306
+
307
+ ui: {
308
+ apps: [],
309
+ views: [],
310
+ },
311
+ });
312
+ `,
313
+ "README.md": (name) => `# ${name} Example
314
+
315
+ ObjectStack example application: ${name}
316
+
317
+ ## Quick Start
318
+
319
+ \`\`\`bash
320
+ # Build the configuration
321
+ pnpm build
322
+
323
+ # Run in development mode
324
+ pnpm dev
325
+ \`\`\`
326
+
327
+ ## Structure
328
+
329
+ - \`objectstack.config.ts\` - Main configuration file
330
+ - \`dist/objectstack.json\` - Compiled artifact
331
+
332
+ ## Learn More
333
+
334
+ - [ObjectStack Documentation](../../content/docs)
335
+ - [Examples](../)
336
+ `,
337
+ "tsconfig.json": () => ({
338
+ extends: "../../tsconfig.json",
339
+ compilerOptions: {
340
+ outDir: "dist",
341
+ rootDir: "."
342
+ },
343
+ include: ["*.ts", "src/**/*"]
344
+ })
345
+ }
346
+ }
347
+ };
348
+ function toCamelCase(str) {
349
+ return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
350
+ }
351
+ var createCommand = new Command3("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) => {
352
+ console.log(chalk3.bold(`
353
+ \u{1F4E6} ObjectStack Project Creator`));
354
+ console.log(chalk3.dim(`-------------------------------`));
355
+ if (!templates[type]) {
356
+ console.error(chalk3.red(`
357
+ \u274C Unknown type: ${type}`));
358
+ console.log(chalk3.dim("Available types: plugin, example"));
359
+ process.exit(1);
360
+ }
361
+ if (!name) {
362
+ console.error(chalk3.red("\n\u274C Project name is required"));
363
+ console.log(chalk3.dim(`Usage: objectstack create ${type} <name>`));
364
+ process.exit(1);
365
+ }
366
+ const template = templates[type];
367
+ const cwd = process.cwd();
368
+ let targetDir;
369
+ if (options?.dir) {
370
+ targetDir = path2.resolve(cwd, options.dir);
371
+ } else {
372
+ const baseDir = type === "plugin" ? "packages/plugins" : "examples";
373
+ const projectName = type === "plugin" ? `plugin-${name}` : name;
374
+ targetDir = path2.join(cwd, baseDir, projectName);
375
+ }
376
+ if (fs2.existsSync(targetDir)) {
377
+ console.error(chalk3.red(`
378
+ \u274C Directory already exists: ${targetDir}`));
379
+ process.exit(1);
380
+ }
381
+ console.log(`\u{1F4C1} Creating ${type}: ${chalk3.blue(name)}`);
382
+ console.log(`\u{1F4C2} Location: ${chalk3.dim(targetDir)}`);
383
+ console.log("");
384
+ try {
385
+ fs2.mkdirSync(targetDir, { recursive: true });
386
+ for (const [filePath, contentFn] of Object.entries(template.files)) {
387
+ const fullPath = path2.join(targetDir, filePath);
388
+ const dir = path2.dirname(fullPath);
389
+ if (!fs2.existsSync(dir)) {
390
+ fs2.mkdirSync(dir, { recursive: true });
391
+ }
392
+ const content = contentFn(name);
393
+ const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
394
+ fs2.writeFileSync(fullPath, fileContent);
395
+ console.log(chalk3.green(`\u2713 Created ${filePath}`));
396
+ }
397
+ console.log("");
398
+ console.log(chalk3.green("\u2705 Project created successfully!"));
399
+ console.log("");
400
+ console.log(chalk3.bold("Next steps:"));
401
+ console.log(chalk3.dim(` cd ${path2.relative(cwd, targetDir)}`));
402
+ console.log(chalk3.dim(" pnpm install"));
403
+ console.log(chalk3.dim(" pnpm build"));
404
+ console.log("");
405
+ } catch (error) {
406
+ console.error(chalk3.red("\n\u274C Failed to create project:"));
407
+ console.error(error.message || error);
408
+ if (fs2.existsSync(targetDir)) {
409
+ fs2.rmSync(targetDir, { recursive: true });
410
+ }
411
+ process.exit(1);
412
+ }
413
+ });
414
+
415
+ // src/commands/serve.ts
416
+ import { Command as Command4 } from "commander";
417
+ import path3 from "path";
418
+ import fs3 from "fs";
419
+ import chalk4 from "chalk";
420
+ import { bundleRequire } from "bundle-require";
421
+ var serveCommand = new Command4("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
422
+ console.log(chalk4.bold(`
423
+ \u{1F680} ObjectStack Server`));
424
+ console.log(chalk4.dim(`------------------------`));
425
+ console.log(`\u{1F4C2} Config: ${chalk4.blue(configPath)}`);
426
+ console.log(`\u{1F310} Port: ${chalk4.blue(options.port)}`);
427
+ console.log("");
428
+ const absolutePath = path3.resolve(process.cwd(), configPath);
429
+ if (!fs3.existsSync(absolutePath)) {
430
+ console.error(chalk4.red(`
431
+ \u274C Configuration file not found: ${absolutePath}`));
432
+ process.exit(1);
433
+ }
434
+ try {
435
+ console.log(chalk4.yellow(`\u{1F4E6} Loading configuration...`));
436
+ const { mod } = await bundleRequire({
437
+ filepath: absolutePath
438
+ });
439
+ const config = mod.default || mod;
440
+ if (!config) {
441
+ throw new Error(`Default export not found in ${configPath}`);
442
+ }
443
+ console.log(chalk4.green(`\u2713 Configuration loaded`));
444
+ const { ObjectKernel } = await import("@objectstack/core");
445
+ console.log(chalk4.yellow(`\u{1F527} Initializing ObjectStack kernel...`));
446
+ const kernel = new ObjectKernel({
447
+ metadata: config.metadata || {},
448
+ objects: config.objects || {}
449
+ });
450
+ const plugins = config.plugins || [];
451
+ if (plugins.length > 0) {
452
+ console.log(chalk4.yellow(`\u{1F4E6} Loading ${plugins.length} plugin(s)...`));
453
+ for (const plugin of plugins) {
454
+ try {
455
+ kernel.registerPlugin(plugin);
456
+ const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
457
+ console.log(chalk4.green(` \u2713 Registered plugin: ${pluginName}`));
458
+ } catch (e) {
459
+ console.error(chalk4.red(` \u2717 Failed to register plugin: ${e.message}`));
460
+ }
461
+ }
462
+ }
463
+ if (options.server !== false) {
464
+ try {
465
+ const { HonoServerPlugin } = await import("@objectstack/plugin-hono-server");
466
+ const serverPlugin = new HonoServerPlugin({ port: parseInt(options.port) });
467
+ kernel.registerPlugin(serverPlugin);
468
+ console.log(chalk4.green(` \u2713 Registered HTTP server plugin (port: ${options.port})`));
469
+ } catch (e) {
470
+ console.warn(chalk4.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
471
+ }
472
+ }
473
+ console.log(chalk4.yellow(`
474
+ \u{1F680} Starting ObjectStack...`));
475
+ await kernel.boot();
476
+ console.log(chalk4.green(`
477
+ \u2705 ObjectStack server is running!`));
478
+ console.log(chalk4.dim(` Press Ctrl+C to stop
479
+ `));
480
+ process.on("SIGINT", async () => {
481
+ console.log(chalk4.yellow(`
482
+
483
+ \u23F9 Stopping server...`));
484
+ await kernel.shutdown();
485
+ console.log(chalk4.green(`\u2705 Server stopped`));
486
+ process.exit(0);
487
+ });
488
+ } catch (error) {
489
+ console.error(chalk4.red(`
490
+ \u274C Server Error:`));
491
+ console.error(error.message || error);
492
+ console.error(error.stack);
493
+ process.exit(1);
494
+ }
495
+ });
496
+
497
+ // src/bin.ts
498
+ var program = new Command5();
499
+ program.name("objectstack").description("CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture").version("0.7.1");
10
500
  program.addCommand(compileCommand);
501
+ program.addCommand(serveCommand);
502
+ program.addCommand(devCommand);
503
+ program.addCommand(doctorCommand);
504
+ program.addCommand(createCommand);
11
505
  program.parse(process.argv);
@@ -33,7 +33,8 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
33
33
  if (!result.success) {
34
34
  console.error(chalk.red(`
35
35
  \u274C Validation Failed!`));
36
- result.error.errors.forEach((e) => {
36
+ const error = result.error;
37
+ error.issues.forEach((e) => {
37
38
  console.error(chalk.red(` - [${e.path.join(".")}] ${e.message}`));
38
39
  });
39
40
  process.exit(1);
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  compileCommand
4
- } from "./chunk-RQRAZ23Z.js";
4
+ } from "./chunk-2YXVEYO7.js";
5
5
  export {
6
6
  compileCommand
7
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/cli",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Command Line Interface for ObjectStack Protocol",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -21,11 +21,13 @@
21
21
  "chalk": "^5.3.0",
22
22
  "commander": "^11.1.0",
23
23
  "tsx": "^4.7.1",
24
- "zod": "^3.22.4",
25
- "@objectstack/spec": "0.7.1"
24
+ "zod": "^4.3.6",
25
+ "@objectstack/spec": "0.8.0",
26
+ "@objectstack/core": "0.8.0",
27
+ "@objectstack/plugin-hono-server": "0.8.0"
26
28
  },
27
29
  "devDependencies": {
28
- "@types/node": "^20.11.19",
30
+ "@types/node": "^25.1.0",
29
31
  "tsup": "^8.0.2",
30
32
  "typescript": "^5.3.3"
31
33
  },
package/src/bin.ts CHANGED
@@ -1,13 +1,22 @@
1
1
  import { Command } from 'commander';
2
2
  import { compileCommand } from './commands/compile.js';
3
+ import { devCommand } from './commands/dev.js';
4
+ import { doctorCommand } from './commands/doctor.js';
5
+ import { createCommand } from './commands/create.js';
6
+ import { serveCommand } from './commands/serve.js';
3
7
 
4
8
  const program = new Command();
5
9
 
6
10
  program
7
11
  .name('objectstack')
8
- .description('CLI for ObjectStack Protocol')
9
- .version('0.1.0');
12
+ .description('CLI for ObjectStack Protocol - Development Tools for Microkernel Architecture')
13
+ .version('0.7.1');
10
14
 
15
+ // Add all commands
11
16
  program.addCommand(compileCommand);
17
+ program.addCommand(serveCommand);
18
+ program.addCommand(devCommand);
19
+ program.addCommand(doctorCommand);
20
+ program.addCommand(createCommand);
12
21
 
13
22
  program.parse(process.argv);
@@ -4,6 +4,7 @@ import fs from 'fs';
4
4
  import { pathToFileURL } from 'url';
5
5
  import chalk from 'chalk';
6
6
  import { bundleRequire } from 'bundle-require';
7
+ import { ZodError } from 'zod';
7
8
  import { ObjectStackDefinitionSchema } from '@objectstack/spec';
8
9
 
9
10
  export const compileCommand = new Command('compile')
@@ -42,7 +43,8 @@ export const compileCommand = new Command('compile')
42
43
  if (!result.success) {
43
44
  console.error(chalk.red(`\n❌ Validation Failed!`));
44
45
 
45
- result.error.errors.forEach(e => {
46
+ const error = result.error as ZodError;
47
+ error.issues.forEach((e: any) => {
46
48
  console.error(chalk.red(` - [${e.path.join('.')}] ${e.message}`));
47
49
  });
48
50
 
@@ -0,0 +1,265 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import { execSync } from 'child_process';
6
+
7
+ const templates = {
8
+ plugin: {
9
+ description: 'Create a new ObjectStack plugin',
10
+ files: {
11
+ 'package.json': (name: string) => ({
12
+ name: `@objectstack/plugin-${name}`,
13
+ version: '0.1.0',
14
+ description: `ObjectStack Plugin: ${name}`,
15
+ main: 'dist/index.js',
16
+ types: 'dist/index.d.ts',
17
+ scripts: {
18
+ build: 'tsc',
19
+ dev: 'tsc --watch',
20
+ test: 'vitest',
21
+ },
22
+ keywords: ['objectstack', 'plugin', name],
23
+ author: '',
24
+ license: 'MIT',
25
+ dependencies: {
26
+ '@objectstack/spec': 'workspace:*',
27
+ zod: '^3.22.4',
28
+ },
29
+ devDependencies: {
30
+ '@types/node': '^20.10.0',
31
+ typescript: '^5.3.0',
32
+ vitest: '^2.1.8',
33
+ },
34
+ }),
35
+ 'tsconfig.json': () => ({
36
+ extends: '../../tsconfig.json',
37
+ compilerOptions: {
38
+ outDir: 'dist',
39
+ rootDir: 'src',
40
+ },
41
+ include: ['src/**/*'],
42
+ }),
43
+ 'src/index.ts': (name: string) => `import type { Plugin } from '@objectstack/spec';
44
+
45
+ /**
46
+ * ${name} Plugin for ObjectStack
47
+ */
48
+ export const ${toCamelCase(name)}Plugin: Plugin = {
49
+ name: '${name}',
50
+ version: '0.1.0',
51
+
52
+ async initialize(context) {
53
+ console.log('Initializing ${name} plugin...');
54
+ // Plugin initialization logic
55
+ },
56
+
57
+ async destroy() {
58
+ console.log('Destroying ${name} plugin...');
59
+ // Plugin cleanup logic
60
+ },
61
+ };
62
+
63
+ export default ${toCamelCase(name)}Plugin;
64
+ `,
65
+ 'README.md': (name: string) => `# @objectstack/plugin-${name}
66
+
67
+ ObjectStack Plugin: ${name}
68
+
69
+ ## Installation
70
+
71
+ \`\`\`bash
72
+ pnpm add @objectstack/plugin-${name}
73
+ \`\`\`
74
+
75
+ ## Usage
76
+
77
+ \`\`\`typescript
78
+ import { ${toCamelCase(name)}Plugin } from '@objectstack/plugin-${name}';
79
+
80
+ // Use the plugin in your ObjectStack configuration
81
+ export default {
82
+ plugins: [
83
+ ${toCamelCase(name)}Plugin,
84
+ ],
85
+ };
86
+ \`\`\`
87
+
88
+ ## License
89
+
90
+ MIT
91
+ `,
92
+ },
93
+ },
94
+
95
+ example: {
96
+ description: 'Create a new ObjectStack example application',
97
+ files: {
98
+ 'package.json': (name: string) => ({
99
+ name: `@objectstack/example-${name}`,
100
+ version: '0.1.0',
101
+ private: true,
102
+ description: `ObjectStack Example: ${name}`,
103
+ scripts: {
104
+ build: 'objectstack compile',
105
+ dev: 'tsx watch objectstack.config.ts',
106
+ test: 'vitest',
107
+ },
108
+ dependencies: {
109
+ '@objectstack/spec': 'workspace:*',
110
+ '@objectstack/cli': 'workspace:*',
111
+ zod: '^3.22.4',
112
+ },
113
+ devDependencies: {
114
+ '@types/node': '^20.10.0',
115
+ tsx: '^4.21.0',
116
+ typescript: '^5.3.0',
117
+ vitest: '^2.1.8',
118
+ },
119
+ }),
120
+ 'objectstack.config.ts': (name: string) => `import { defineStack } from '@objectstack/spec';
121
+
122
+ export default defineStack({
123
+ metadata: {
124
+ name: '${name}',
125
+ version: '0.1.0',
126
+ description: '${name} example application',
127
+ },
128
+
129
+ objects: {
130
+ // Define your data objects here
131
+ },
132
+
133
+ ui: {
134
+ apps: [],
135
+ views: [],
136
+ },
137
+ });
138
+ `,
139
+ 'README.md': (name: string) => `# ${name} Example
140
+
141
+ ObjectStack example application: ${name}
142
+
143
+ ## Quick Start
144
+
145
+ \`\`\`bash
146
+ # Build the configuration
147
+ pnpm build
148
+
149
+ # Run in development mode
150
+ pnpm dev
151
+ \`\`\`
152
+
153
+ ## Structure
154
+
155
+ - \`objectstack.config.ts\` - Main configuration file
156
+ - \`dist/objectstack.json\` - Compiled artifact
157
+
158
+ ## Learn More
159
+
160
+ - [ObjectStack Documentation](../../content/docs)
161
+ - [Examples](../)
162
+ `,
163
+ 'tsconfig.json': () => ({
164
+ extends: '../../tsconfig.json',
165
+ compilerOptions: {
166
+ outDir: 'dist',
167
+ rootDir: '.',
168
+ },
169
+ include: ['*.ts', 'src/**/*'],
170
+ }),
171
+ },
172
+ },
173
+ };
174
+
175
+ function toCamelCase(str: string): string {
176
+ return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
177
+ }
178
+
179
+ export const createCommand = new Command('create')
180
+ .description('Create a new package, plugin, or example from template')
181
+ .argument('<type>', 'Type of project to create (plugin, example)')
182
+ .argument('[name]', 'Name of the project')
183
+ .option('-d, --dir <directory>', 'Target directory')
184
+ .action(async (type: string, name?: string, options?: { dir?: string }) => {
185
+ console.log(chalk.bold(`\n📦 ObjectStack Project Creator`));
186
+ console.log(chalk.dim(`-------------------------------`));
187
+
188
+ if (!templates[type as keyof typeof templates]) {
189
+ console.error(chalk.red(`\n❌ Unknown type: ${type}`));
190
+ console.log(chalk.dim('Available types: plugin, example'));
191
+ process.exit(1);
192
+ }
193
+
194
+ if (!name) {
195
+ console.error(chalk.red('\n❌ Project name is required'));
196
+ console.log(chalk.dim(`Usage: objectstack create ${type} <name>`));
197
+ process.exit(1);
198
+ }
199
+
200
+ const template = templates[type as keyof typeof templates];
201
+ const cwd = process.cwd();
202
+
203
+ // Determine target directory
204
+ let targetDir: string;
205
+ if (options?.dir) {
206
+ targetDir = path.resolve(cwd, options.dir);
207
+ } else {
208
+ const baseDir = type === 'plugin' ? 'packages/plugins' : 'examples';
209
+ const projectName = type === 'plugin' ? `plugin-${name}` : name;
210
+ targetDir = path.join(cwd, baseDir, projectName);
211
+ }
212
+
213
+ // Check if directory already exists
214
+ if (fs.existsSync(targetDir)) {
215
+ console.error(chalk.red(`\n❌ Directory already exists: ${targetDir}`));
216
+ process.exit(1);
217
+ }
218
+
219
+ console.log(`📁 Creating ${type}: ${chalk.blue(name)}`);
220
+ console.log(`📂 Location: ${chalk.dim(targetDir)}`);
221
+ console.log('');
222
+
223
+ try {
224
+ // Create directory
225
+ fs.mkdirSync(targetDir, { recursive: true });
226
+
227
+ // Create files from template
228
+ for (const [filePath, contentFn] of Object.entries(template.files)) {
229
+ const fullPath = path.join(targetDir, filePath);
230
+ const dir = path.dirname(fullPath);
231
+
232
+ if (!fs.existsSync(dir)) {
233
+ fs.mkdirSync(dir, { recursive: true });
234
+ }
235
+
236
+ const content = contentFn(name);
237
+ const fileContent = typeof content === 'string'
238
+ ? content
239
+ : JSON.stringify(content, null, 2);
240
+
241
+ fs.writeFileSync(fullPath, fileContent);
242
+ console.log(chalk.green(`✓ Created ${filePath}`));
243
+ }
244
+
245
+ console.log('');
246
+ console.log(chalk.green('✅ Project created successfully!'));
247
+ console.log('');
248
+ console.log(chalk.bold('Next steps:'));
249
+ console.log(chalk.dim(` cd ${path.relative(cwd, targetDir)}`));
250
+ console.log(chalk.dim(' pnpm install'));
251
+ console.log(chalk.dim(' pnpm build'));
252
+ console.log('');
253
+
254
+ } catch (error: any) {
255
+ console.error(chalk.red('\n❌ Failed to create project:'));
256
+ console.error(error.message || error);
257
+
258
+ // Clean up on error
259
+ if (fs.existsSync(targetDir)) {
260
+ fs.rmSync(targetDir, { recursive: true });
261
+ }
262
+
263
+ process.exit(1);
264
+ }
265
+ });
@@ -0,0 +1,40 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execSync } from 'child_process';
4
+ import os from 'os';
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+
8
+ export const devCommand = new Command('dev')
9
+ .description('Start development mode for a package')
10
+ .argument('[package]', 'Package name (without @objectstack/ prefix)', 'all')
11
+ .option('-w, --watch', 'Enable watch mode (default)', true)
12
+ .option('-v, --verbose', 'Verbose output')
13
+ .action(async (packageName, options) => {
14
+ console.log(chalk.bold(`\n🚀 ObjectStack Development Mode`));
15
+ console.log(chalk.dim(`-------------------------------`));
16
+
17
+ try {
18
+ const cwd = process.cwd();
19
+ const filter = packageName === 'all' ? '' : `--filter @objectstack/${packageName}`;
20
+
21
+ console.log(`📦 Package: ${chalk.blue(packageName === 'all' ? 'All packages' : `@objectstack/${packageName}`)}`);
22
+ console.log(`🔄 Watch mode: ${chalk.green('enabled')}`);
23
+ console.log('');
24
+
25
+ // Start dev mode
26
+ const command = `pnpm ${filter} dev`.trim();
27
+ console.log(chalk.dim(`$ ${command}`));
28
+ console.log('');
29
+
30
+ execSync(command, {
31
+ stdio: 'inherit',
32
+ cwd
33
+ });
34
+
35
+ } catch (error: any) {
36
+ console.error(chalk.red(`\n❌ Development mode failed:`));
37
+ console.error(error.message || error);
38
+ process.exit(1);
39
+ }
40
+ });
@@ -0,0 +1,175 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { execSync } from 'child_process';
4
+ import fs from 'fs';
5
+ import path from 'path';
6
+
7
+ interface HealthCheckResult {
8
+ name: string;
9
+ status: 'ok' | 'warning' | 'error';
10
+ message: string;
11
+ fix?: string;
12
+ }
13
+
14
+ export const doctorCommand = new Command('doctor')
15
+ .description('Check development environment health')
16
+ .option('-v, --verbose', 'Show detailed information')
17
+ .action(async (options) => {
18
+ console.log(chalk.bold(`\n🏥 ObjectStack Environment Health Check`));
19
+ console.log(chalk.dim(`-----------------------------------------`));
20
+ console.log('');
21
+
22
+ const results: HealthCheckResult[] = [];
23
+
24
+ // Check Node.js version
25
+ try {
26
+ const nodeVersion = process.version;
27
+ const majorVersion = parseInt(nodeVersion.slice(1).split('.')[0]);
28
+
29
+ if (majorVersion >= 18) {
30
+ results.push({
31
+ name: 'Node.js',
32
+ status: 'ok',
33
+ message: `Version ${nodeVersion}`,
34
+ });
35
+ } else {
36
+ results.push({
37
+ name: 'Node.js',
38
+ status: 'error',
39
+ message: `Version ${nodeVersion} (requires >= 18.0.0)`,
40
+ fix: 'Upgrade Node.js: https://nodejs.org',
41
+ });
42
+ }
43
+ } catch (error) {
44
+ results.push({
45
+ name: 'Node.js',
46
+ status: 'error',
47
+ message: 'Not found',
48
+ fix: 'Install Node.js: https://nodejs.org',
49
+ });
50
+ }
51
+
52
+ // Check pnpm
53
+ try {
54
+ const pnpmVersion = execSync('pnpm -v', { encoding: 'utf-8' }).trim();
55
+ results.push({
56
+ name: 'pnpm',
57
+ status: 'ok',
58
+ message: `Version ${pnpmVersion}`,
59
+ });
60
+ } catch (error) {
61
+ results.push({
62
+ name: 'pnpm',
63
+ status: 'error',
64
+ message: 'Not found',
65
+ fix: 'Install pnpm: npm install -g pnpm@10.28.1',
66
+ });
67
+ }
68
+
69
+ // Check TypeScript
70
+ try {
71
+ const tscVersion = execSync('tsc -v', { encoding: 'utf-8' }).trim();
72
+ results.push({
73
+ name: 'TypeScript',
74
+ status: 'ok',
75
+ message: tscVersion,
76
+ });
77
+ } catch (error) {
78
+ results.push({
79
+ name: 'TypeScript',
80
+ status: 'warning',
81
+ message: 'Not found in PATH',
82
+ fix: 'Installed locally via pnpm',
83
+ });
84
+ }
85
+
86
+ // Check if dependencies are installed
87
+ const cwd = process.cwd();
88
+ const nodeModulesPath = path.join(cwd, 'node_modules');
89
+
90
+ if (fs.existsSync(nodeModulesPath)) {
91
+ results.push({
92
+ name: 'Dependencies',
93
+ status: 'ok',
94
+ message: 'Installed',
95
+ });
96
+ } else {
97
+ results.push({
98
+ name: 'Dependencies',
99
+ status: 'error',
100
+ message: 'Not installed',
101
+ fix: 'Run: pnpm install',
102
+ });
103
+ }
104
+
105
+ // Check if spec package is built
106
+ const specDistPath = path.join(cwd, 'packages/spec/dist');
107
+
108
+ if (fs.existsSync(specDistPath)) {
109
+ results.push({
110
+ name: '@objectstack/spec',
111
+ status: 'ok',
112
+ message: 'Built',
113
+ });
114
+ } else {
115
+ results.push({
116
+ name: '@objectstack/spec',
117
+ status: 'warning',
118
+ message: 'Not built',
119
+ fix: 'Run: pnpm --filter @objectstack/spec build',
120
+ });
121
+ }
122
+
123
+ // Check Git
124
+ try {
125
+ const gitVersion = execSync('git --version', { encoding: 'utf-8' }).trim();
126
+ results.push({
127
+ name: 'Git',
128
+ status: 'ok',
129
+ message: gitVersion,
130
+ });
131
+ } catch (error) {
132
+ results.push({
133
+ name: 'Git',
134
+ status: 'warning',
135
+ message: 'Not found',
136
+ fix: 'Install Git for version control',
137
+ });
138
+ }
139
+
140
+ // Display results
141
+ let hasErrors = false;
142
+ let hasWarnings = false;
143
+
144
+ results.forEach((result) => {
145
+ const icon = result.status === 'ok' ? '✓' : result.status === 'warning' ? '⚠' : '✗';
146
+ const color = result.status === 'ok' ? chalk.green : result.status === 'warning' ? chalk.yellow : chalk.red;
147
+
148
+ console.log(color(`${icon} ${result.name.padEnd(20)} ${result.message}`));
149
+
150
+ if (result.fix && options.verbose) {
151
+ console.log(chalk.dim(` → ${result.fix}`));
152
+ }
153
+
154
+ if (result.status === 'error') hasErrors = true;
155
+ if (result.status === 'warning') hasWarnings = true;
156
+ });
157
+
158
+ console.log('');
159
+
160
+ // Summary
161
+ if (hasErrors) {
162
+ console.log(chalk.red('❌ Some critical issues found. Please fix them before continuing.'));
163
+ results
164
+ .filter(r => r.status === 'error' && r.fix)
165
+ .forEach(r => console.log(chalk.dim(` ${r.fix}`)));
166
+ process.exit(1);
167
+ } else if (hasWarnings) {
168
+ console.log(chalk.yellow('⚠️ Environment is functional but has some warnings.'));
169
+ console.log(chalk.dim(' Run with --verbose to see fix suggestions.'));
170
+ } else {
171
+ console.log(chalk.green('✅ Environment is healthy and ready for development!'));
172
+ }
173
+
174
+ console.log('');
175
+ });
@@ -0,0 +1,101 @@
1
+ import { Command } from 'commander';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import chalk from 'chalk';
5
+ import { bundleRequire } from 'bundle-require';
6
+
7
+ export const serveCommand = new Command('serve')
8
+ .description('Start ObjectStack server with plugins from configuration')
9
+ .argument('[config]', 'Configuration file path', 'objectstack.config.ts')
10
+ .option('-p, --port <port>', 'Server port', '3000')
11
+ .option('--no-server', 'Skip starting HTTP server plugin')
12
+ .action(async (configPath, options) => {
13
+ console.log(chalk.bold(`\n🚀 ObjectStack Server`));
14
+ console.log(chalk.dim(`------------------------`));
15
+ console.log(`📂 Config: ${chalk.blue(configPath)}`);
16
+ console.log(`🌐 Port: ${chalk.blue(options.port)}`);
17
+ console.log('');
18
+
19
+ const absolutePath = path.resolve(process.cwd(), configPath);
20
+
21
+ if (!fs.existsSync(absolutePath)) {
22
+ console.error(chalk.red(`\n❌ Configuration file not found: ${absolutePath}`));
23
+ process.exit(1);
24
+ }
25
+
26
+ try {
27
+ // Load configuration
28
+ console.log(chalk.yellow(`📦 Loading configuration...`));
29
+ const { mod } = await bundleRequire({
30
+ filepath: absolutePath,
31
+ });
32
+
33
+ const config = mod.default || mod;
34
+
35
+ if (!config) {
36
+ throw new Error(`Default export not found in ${configPath}`);
37
+ }
38
+
39
+ console.log(chalk.green(`✓ Configuration loaded`));
40
+
41
+ // Import ObjectStack runtime
42
+ const { ObjectKernel } = await import('@objectstack/core');
43
+
44
+ // Create kernel instance
45
+ console.log(chalk.yellow(`🔧 Initializing ObjectStack kernel...`));
46
+ const kernel = new ObjectKernel({
47
+ metadata: config.metadata || {},
48
+ objects: config.objects || {},
49
+ });
50
+
51
+ // Load plugins from configuration
52
+ const plugins = config.plugins || [];
53
+
54
+ if (plugins.length > 0) {
55
+ console.log(chalk.yellow(`📦 Loading ${plugins.length} plugin(s)...`));
56
+
57
+ for (const plugin of plugins) {
58
+ try {
59
+ kernel.registerPlugin(plugin);
60
+ const pluginName = plugin.name || plugin.constructor?.name || 'unnamed';
61
+ console.log(chalk.green(` ✓ Registered plugin: ${pluginName}`));
62
+ } catch (e: any) {
63
+ console.error(chalk.red(` ✗ Failed to register plugin: ${e.message}`));
64
+ }
65
+ }
66
+ }
67
+
68
+ // Add HTTP server plugin if not disabled
69
+ if (options.server !== false) {
70
+ try {
71
+ const { HonoServerPlugin } = await import('@objectstack/plugin-hono-server');
72
+ const serverPlugin = new HonoServerPlugin({ port: parseInt(options.port) });
73
+ kernel.registerPlugin(serverPlugin);
74
+ console.log(chalk.green(` ✓ Registered HTTP server plugin (port: ${options.port})`));
75
+ } catch (e: any) {
76
+ console.warn(chalk.yellow(` ⚠ HTTP server plugin not available: ${e.message}`));
77
+ }
78
+ }
79
+
80
+ // Boot the kernel
81
+ console.log(chalk.yellow(`\n🚀 Starting ObjectStack...`));
82
+ await kernel.boot();
83
+
84
+ console.log(chalk.green(`\n✅ ObjectStack server is running!`));
85
+ console.log(chalk.dim(` Press Ctrl+C to stop\n`));
86
+
87
+ // Keep process alive
88
+ process.on('SIGINT', async () => {
89
+ console.log(chalk.yellow(`\n\n⏹ Stopping server...`));
90
+ await kernel.shutdown();
91
+ console.log(chalk.green(`✅ Server stopped`));
92
+ process.exit(0);
93
+ });
94
+
95
+ } catch (error: any) {
96
+ console.error(chalk.red(`\n❌ Server Error:`));
97
+ console.error(error.message || error);
98
+ console.error(error.stack);
99
+ process.exit(1);
100
+ }
101
+ });