@objectstack/cli 1.0.10 → 1.0.12
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/.turbo/turbo-build.log +14 -9
- package/CHANGELOG.md +24 -0
- package/README.md +132 -13
- package/dist/bin.js +1488 -178
- package/dist/index.d.ts +97 -1
- package/dist/index.js +1938 -5
- package/package.json +9 -9
- package/src/bin.ts +53 -6
- package/src/commands/compile.ts +66 -39
- package/src/commands/create.ts +15 -11
- package/src/commands/dev.ts +12 -16
- package/src/commands/doctor.ts +13 -9
- package/src/commands/generate.ts +297 -0
- package/src/commands/info.ts +111 -0
- package/src/commands/init.ts +313 -0
- package/src/commands/serve.ts +134 -48
- package/src/commands/studio.ts +40 -0
- package/src/commands/test.ts +2 -2
- package/src/commands/validate.ts +130 -0
- package/src/index.ts +9 -0
- package/src/utils/config.ts +78 -0
- package/src/utils/console.ts +319 -0
- package/src/utils/format.ts +261 -0
- package/test/commands.test.ts +26 -1
- package/tsup.config.ts +18 -9
- package/dist/bin.d.ts +0 -2
- package/dist/chunk-2YXVEYO7.js +0 -64
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
4
4
|
"description": "Command Line Interface for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,16 +21,16 @@
|
|
|
21
21
|
"chalk": "^5.3.0",
|
|
22
22
|
"commander": "^14.0.3",
|
|
23
23
|
"tsx": "^4.7.1",
|
|
24
|
-
"zod": "^
|
|
25
|
-
"@objectstack/core": "1.0.
|
|
26
|
-
"@objectstack/driver-memory": "^1.0.
|
|
27
|
-
"@objectstack/objectql": "^1.0.
|
|
28
|
-
"@objectstack/plugin-hono-server": "1.0.
|
|
29
|
-
"@objectstack/runtime": "^1.0.
|
|
30
|
-
"@objectstack/spec": "1.0.
|
|
24
|
+
"zod": "^3.24.1",
|
|
25
|
+
"@objectstack/core": "1.0.12",
|
|
26
|
+
"@objectstack/driver-memory": "^1.0.12",
|
|
27
|
+
"@objectstack/objectql": "^1.0.12",
|
|
28
|
+
"@objectstack/plugin-hono-server": "1.0.12",
|
|
29
|
+
"@objectstack/runtime": "^1.0.12",
|
|
30
|
+
"@objectstack/spec": "1.0.12"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
|
-
"@objectstack/core": "^1.0.
|
|
33
|
+
"@objectstack/core": "^1.0.12"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^25.1.0",
|
package/src/bin.ts
CHANGED
|
@@ -1,28 +1,75 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
// Commands
|
|
3
6
|
import { compileCommand } from './commands/compile.js';
|
|
4
7
|
import { devCommand } from './commands/dev.js';
|
|
5
8
|
import { doctorCommand } from './commands/doctor.js';
|
|
6
9
|
import { createCommand } from './commands/create.js';
|
|
7
10
|
import { serveCommand } from './commands/serve.js';
|
|
11
|
+
import { studioCommand } from './commands/studio.js';
|
|
8
12
|
import { testCommand } from './commands/test.js';
|
|
13
|
+
import { validateCommand } from './commands/validate.js';
|
|
14
|
+
import { initCommand } from './commands/init.js';
|
|
15
|
+
import { infoCommand } from './commands/info.js';
|
|
16
|
+
import { generateCommand } from './commands/generate.js';
|
|
9
17
|
|
|
10
18
|
const require = createRequire(import.meta.url);
|
|
11
19
|
const pkg = require('../package.json');
|
|
12
20
|
|
|
21
|
+
// ─── Global Error Handling ──────────────────────────────────────────
|
|
22
|
+
process.on('unhandledRejection', (err: any) => {
|
|
23
|
+
console.error(chalk.red(`\n ✗ Unhandled error: ${err?.message || err}`));
|
|
24
|
+
if (err?.stack && process.env.DEBUG) {
|
|
25
|
+
console.error(chalk.dim(err.stack));
|
|
26
|
+
}
|
|
27
|
+
process.exit(1);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ─── Program Definition ─────────────────────────────────────────────
|
|
13
31
|
const program = new Command();
|
|
14
32
|
|
|
15
33
|
program
|
|
16
34
|
.name('objectstack')
|
|
17
|
-
.description('CLI
|
|
18
|
-
.version(pkg.version)
|
|
35
|
+
.description('ObjectStack CLI — Build metadata-driven apps with the ObjectStack Protocol')
|
|
36
|
+
.version(pkg.version, '-v, --version')
|
|
37
|
+
.configureHelp({
|
|
38
|
+
sortSubcommands: false,
|
|
39
|
+
})
|
|
40
|
+
.addHelpText('before', `
|
|
41
|
+
${chalk.bold.cyan('◆ ObjectStack CLI')} ${chalk.dim(`v${pkg.version}`)}
|
|
42
|
+
`)
|
|
43
|
+
.addHelpText('after', `
|
|
44
|
+
${chalk.bold('Workflow:')}
|
|
45
|
+
${chalk.dim('$')} os init ${chalk.dim('# Create a new project')}
|
|
46
|
+
${chalk.dim('$')} os generate object task ${chalk.dim('# Add metadata')}
|
|
47
|
+
${chalk.dim('$')} os validate ${chalk.dim('# Check configuration')}
|
|
48
|
+
${chalk.dim('$')} os dev ${chalk.dim('# Start dev server')}
|
|
49
|
+
${chalk.dim('$')} os studio ${chalk.dim('# Dev server + Console UI')}
|
|
50
|
+
${chalk.dim('$')} os compile ${chalk.dim('# Build for production')}
|
|
19
51
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
${chalk.dim('Aliases: objectstack | os')}
|
|
53
|
+
${chalk.dim('Docs: https://objectstack.dev')}
|
|
54
|
+
`);
|
|
55
|
+
|
|
56
|
+
// ── Development ──
|
|
57
|
+
program.addCommand(initCommand);
|
|
23
58
|
program.addCommand(devCommand);
|
|
24
|
-
program.addCommand(
|
|
59
|
+
program.addCommand(serveCommand);
|
|
60
|
+
program.addCommand(studioCommand);
|
|
61
|
+
|
|
62
|
+
// ── Build & Validate ──
|
|
63
|
+
program.addCommand(compileCommand);
|
|
64
|
+
program.addCommand(validateCommand);
|
|
65
|
+
program.addCommand(infoCommand);
|
|
66
|
+
|
|
67
|
+
// ── Scaffolding ──
|
|
68
|
+
program.addCommand(generateCommand);
|
|
25
69
|
program.addCommand(createCommand);
|
|
70
|
+
|
|
71
|
+
// ── Quality ──
|
|
26
72
|
program.addCommand(testCommand);
|
|
73
|
+
program.addCommand(doctorCommand);
|
|
27
74
|
|
|
28
75
|
program.parse(process.argv);
|
package/src/commands/compile.ts
CHANGED
|
@@ -1,75 +1,102 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
-
import { pathToFileURL } from 'url';
|
|
5
4
|
import chalk from 'chalk';
|
|
6
|
-
import { bundleRequire } from 'bundle-require';
|
|
7
5
|
import { ZodError } from 'zod';
|
|
8
6
|
import { ObjectStackDefinitionSchema } from '@objectstack/spec';
|
|
7
|
+
import { loadConfig } from '../utils/config.js';
|
|
8
|
+
import {
|
|
9
|
+
printHeader,
|
|
10
|
+
printKV,
|
|
11
|
+
printSuccess,
|
|
12
|
+
printError,
|
|
13
|
+
printStep,
|
|
14
|
+
createTimer,
|
|
15
|
+
formatZodErrors,
|
|
16
|
+
collectMetadataStats,
|
|
17
|
+
printMetadataStats,
|
|
18
|
+
} from '../utils/format.js';
|
|
9
19
|
|
|
10
20
|
export const compileCommand = new Command('compile')
|
|
11
|
-
.description('Compile ObjectStack configuration to JSON
|
|
12
|
-
.argument('[
|
|
13
|
-
.
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const absolutePath = path.resolve(process.cwd(), source);
|
|
21
|
-
if (!fs.existsSync(absolutePath)) {
|
|
22
|
-
console.error(chalk.red(`\n❌ Config file not found: ${absolutePath}`));
|
|
23
|
-
process.exit(1);
|
|
21
|
+
.description('Compile ObjectStack configuration to JSON artifact')
|
|
22
|
+
.argument('[config]', 'Source configuration file')
|
|
23
|
+
.option('-o, --output <path>', 'Output JSON file', 'dist/objectstack.json')
|
|
24
|
+
.option('--json', 'Output compile result as JSON (for CI)')
|
|
25
|
+
.action(async (configPath, options) => {
|
|
26
|
+
const timer = createTimer();
|
|
27
|
+
|
|
28
|
+
if (!options.json) {
|
|
29
|
+
printHeader('Compile');
|
|
24
30
|
}
|
|
25
31
|
|
|
26
32
|
try {
|
|
27
33
|
// 1. Load Configuration
|
|
28
|
-
|
|
29
|
-
const {
|
|
30
|
-
filepath: absolutePath,
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const config = mod.default || mod;
|
|
34
|
+
if (!options.json) printStep('Loading configuration...');
|
|
35
|
+
const { config, absolutePath, duration } = await loadConfig(configPath);
|
|
34
36
|
|
|
35
|
-
if (!
|
|
36
|
-
|
|
37
|
+
if (!options.json) {
|
|
38
|
+
printKV('Config', path.relative(process.cwd(), absolutePath));
|
|
39
|
+
printKV('Load time', `${duration}ms`);
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
// 2. Validate against Protocol
|
|
40
|
-
|
|
43
|
+
if (!options.json) printStep('Validating protocol compliance...');
|
|
41
44
|
const result = ObjectStackDefinitionSchema.safeParse(config);
|
|
42
45
|
|
|
43
46
|
if (!result.success) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
if (options.json) {
|
|
48
|
+
console.log(JSON.stringify({ success: false, errors: (result.error as unknown as ZodError).issues }));
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
console.log('');
|
|
52
|
+
printError('Validation failed');
|
|
53
|
+
formatZodErrors(result.error as unknown as ZodError);
|
|
51
54
|
process.exit(1);
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
// 3. Generate Artifact
|
|
58
|
+
if (!options.json) printStep('Writing artifact...');
|
|
59
|
+
const output = options.output;
|
|
55
60
|
const artifactPath = path.resolve(process.cwd(), output);
|
|
56
61
|
const artifactDir = path.dirname(artifactPath);
|
|
57
|
-
|
|
62
|
+
|
|
58
63
|
if (!fs.existsSync(artifactDir)) {
|
|
59
64
|
fs.mkdirSync(artifactDir, { recursive: true });
|
|
60
65
|
}
|
|
61
|
-
|
|
66
|
+
|
|
62
67
|
const jsonContent = JSON.stringify(result.data, null, 2);
|
|
63
68
|
fs.writeFileSync(artifactPath, jsonContent);
|
|
64
69
|
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
const sizeKB = (jsonContent.length / 1024).toFixed(1);
|
|
71
|
+
const stats = collectMetadataStats(config);
|
|
72
|
+
|
|
73
|
+
if (options.json) {
|
|
74
|
+
console.log(JSON.stringify({
|
|
75
|
+
success: true,
|
|
76
|
+
output: artifactPath,
|
|
77
|
+
size: jsonContent.length,
|
|
78
|
+
stats,
|
|
79
|
+
duration: timer.elapsed(),
|
|
80
|
+
}));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 4. Summary
|
|
85
|
+
console.log('');
|
|
86
|
+
printSuccess(`Build complete ${chalk.dim(`(${timer.display()})`)}`);
|
|
87
|
+
console.log('');
|
|
88
|
+
printMetadataStats(stats);
|
|
89
|
+
console.log('');
|
|
90
|
+
printKV('Artifact', `${output} ${chalk.dim(`(${sizeKB} KB`)})`);
|
|
91
|
+
console.log('');
|
|
69
92
|
|
|
70
93
|
} catch (error: any) {
|
|
71
|
-
|
|
72
|
-
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify({ success: false, error: error.message }));
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
console.log('');
|
|
99
|
+
printError(error.message || String(error));
|
|
73
100
|
process.exit(1);
|
|
74
101
|
}
|
|
75
102
|
});
|
package/src/commands/create.ts
CHANGED
|
@@ -24,12 +24,12 @@ export const templates = {
|
|
|
24
24
|
license: 'MIT',
|
|
25
25
|
dependencies: {
|
|
26
26
|
'@objectstack/spec': 'workspace:*',
|
|
27
|
-
zod: '^3.
|
|
27
|
+
zod: '^4.3.6',
|
|
28
28
|
},
|
|
29
29
|
devDependencies: {
|
|
30
|
-
'@types/node': '^
|
|
31
|
-
typescript: '^5.
|
|
32
|
-
vitest: '^
|
|
30
|
+
'@types/node': '^22.0.0',
|
|
31
|
+
typescript: '^5.8.0',
|
|
32
|
+
vitest: '^4.0.0',
|
|
33
33
|
},
|
|
34
34
|
}),
|
|
35
35
|
'tsconfig.json': () => ({
|
|
@@ -108,17 +108,22 @@ MIT
|
|
|
108
108
|
dependencies: {
|
|
109
109
|
'@objectstack/spec': 'workspace:*',
|
|
110
110
|
'@objectstack/cli': 'workspace:*',
|
|
111
|
-
zod: '^3.
|
|
111
|
+
zod: '^4.3.6',
|
|
112
112
|
},
|
|
113
113
|
devDependencies: {
|
|
114
|
-
'@types/node': '^
|
|
114
|
+
'@types/node': '^22.0.0',
|
|
115
115
|
tsx: '^4.21.0',
|
|
116
|
-
typescript: '^5.
|
|
117
|
-
vitest: '^
|
|
116
|
+
typescript: '^5.8.0',
|
|
117
|
+
vitest: '^4.0.0',
|
|
118
118
|
},
|
|
119
119
|
}),
|
|
120
120
|
'objectstack.config.ts': (name: string) => `import { defineStack } from '@objectstack/spec';
|
|
121
121
|
|
|
122
|
+
// Barrel imports — add more as you create new type folders
|
|
123
|
+
// import * as objects from './src/objects';
|
|
124
|
+
// import * as actions from './src/actions';
|
|
125
|
+
// import * as apps from './src/apps';
|
|
126
|
+
|
|
122
127
|
export default defineStack({
|
|
123
128
|
manifest: {
|
|
124
129
|
name: '${name}',
|
|
@@ -127,12 +132,11 @@ export default defineStack({
|
|
|
127
132
|
},
|
|
128
133
|
|
|
129
134
|
objects: [
|
|
130
|
-
//
|
|
131
|
-
// { name: 'my_object', fields: { ... } }
|
|
135
|
+
// Object.values(objects), // Uncomment after creating src/objects/index.ts
|
|
132
136
|
],
|
|
133
137
|
|
|
134
138
|
apps: [
|
|
135
|
-
//
|
|
139
|
+
// Object.values(apps), // Uncomment after creating src/apps/index.ts
|
|
136
140
|
],
|
|
137
141
|
});
|
|
138
142
|
`,
|
package/src/commands/dev.ts
CHANGED
|
@@ -1,33 +1,32 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { execSync, spawn } from 'child_process';
|
|
4
|
-
import os from 'os';
|
|
5
4
|
import fs from 'fs';
|
|
6
5
|
import path from 'path';
|
|
6
|
+
import { printHeader, printKV, printStep, printError } from '../utils/format.js';
|
|
7
7
|
|
|
8
8
|
export const devCommand = new Command('dev')
|
|
9
|
-
.description('Start development mode
|
|
9
|
+
.description('Start development mode with hot-reload')
|
|
10
10
|
.argument('[package]', 'Package name or filter pattern', 'all')
|
|
11
11
|
.option('-w, --watch', 'Enable watch mode (default)', true)
|
|
12
|
+
.option('--ui', 'Enable Console UI at /_studio/')
|
|
12
13
|
.option('-v, --verbose', 'Verbose output')
|
|
13
14
|
.action(async (packageName, options) => {
|
|
14
|
-
|
|
15
|
-
console.log(chalk.dim(`-------------------------------`));
|
|
15
|
+
printHeader('Development Mode');
|
|
16
16
|
|
|
17
17
|
// Check if we are running inside a package (Single Package Mode)
|
|
18
18
|
// If "package" argument is 'all' (default) AND objectstack.config.ts exists in CWD
|
|
19
19
|
const configPath = path.resolve(process.cwd(), 'objectstack.config.ts');
|
|
20
20
|
if (packageName === 'all' && fs.existsSync(configPath)) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
console.log('');
|
|
21
|
+
printKV('Config', configPath, '📂');
|
|
22
|
+
printStep('Starting dev server...');
|
|
24
23
|
|
|
25
24
|
// Delegate to 'serve --dev'
|
|
26
25
|
// We spawn a new process to ensure clean environment and watch capabilities (plugin-loader etc)
|
|
27
26
|
// usage: objectstack serve --dev
|
|
28
27
|
const binPath = process.argv[1]; // path to objectstack bin
|
|
29
28
|
|
|
30
|
-
const child = spawn(process.execPath, [binPath, 'serve', '--dev', ...(options.verbose ? ['--verbose'] : [])], {
|
|
29
|
+
const child = spawn(process.execPath, [binPath, 'serve', '--dev', ...(options.ui ? ['--ui'] : []), ...(options.verbose ? ['--verbose'] : [])], {
|
|
31
30
|
stdio: 'inherit',
|
|
32
31
|
env: { ...process.env, NODE_ENV: 'development' }
|
|
33
32
|
});
|
|
@@ -44,17 +43,15 @@ export const devCommand = new Command('dev')
|
|
|
44
43
|
const isWorkspaceRoot = fs.existsSync(workspaceConfigPath);
|
|
45
44
|
|
|
46
45
|
if (packageName === 'all' && !isWorkspaceRoot) {
|
|
47
|
-
|
|
48
|
-
console.error(chalk.yellow(
|
|
49
|
-
console.error(chalk.yellow(` OR run from the monorepo root to start all packages.`));
|
|
46
|
+
printError(`Config file not found in ${cwd}`);
|
|
47
|
+
console.error(chalk.yellow(' Run in a directory with objectstack.config.ts, or from the monorepo root.'));
|
|
50
48
|
process.exit(1);
|
|
51
49
|
}
|
|
52
50
|
|
|
53
51
|
const filter = packageName === 'all' ? '' : `--filter ${packageName}`;
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
console.log('');
|
|
53
|
+
printKV('Package', packageName === 'all' ? 'All packages' : packageName, '📦');
|
|
54
|
+
printKV('Watch', 'enabled', '🔄');
|
|
58
55
|
|
|
59
56
|
// Start dev mode
|
|
60
57
|
const command = `pnpm ${filter} dev`.trim();
|
|
@@ -67,8 +64,7 @@ export const devCommand = new Command('dev')
|
|
|
67
64
|
});
|
|
68
65
|
|
|
69
66
|
} catch (error: any) {
|
|
70
|
-
|
|
71
|
-
console.error(error.message || error);
|
|
67
|
+
printError(`Development mode failed: ${error.message || error}`);
|
|
72
68
|
process.exit(1);
|
|
73
69
|
}
|
|
74
70
|
});
|
package/src/commands/doctor.ts
CHANGED
|
@@ -3,6 +3,7 @@ import chalk from 'chalk';
|
|
|
3
3
|
import { execSync } from 'child_process';
|
|
4
4
|
import fs from 'fs';
|
|
5
5
|
import path from 'path';
|
|
6
|
+
import { printHeader, printSuccess, printWarning, printError } from '../utils/format.js';
|
|
6
7
|
|
|
7
8
|
interface HealthCheckResult {
|
|
8
9
|
name: string;
|
|
@@ -15,9 +16,7 @@ export const doctorCommand = new Command('doctor')
|
|
|
15
16
|
.description('Check development environment health')
|
|
16
17
|
.option('-v, --verbose', 'Show detailed information')
|
|
17
18
|
.action(async (options) => {
|
|
18
|
-
|
|
19
|
-
console.log(chalk.dim(`-----------------------------------------`));
|
|
20
|
-
console.log('');
|
|
19
|
+
printHeader('Environment Health Check');
|
|
21
20
|
|
|
22
21
|
const results: HealthCheckResult[] = [];
|
|
23
22
|
|
|
@@ -141,14 +140,19 @@ export const doctorCommand = new Command('doctor')
|
|
|
141
140
|
let hasErrors = false;
|
|
142
141
|
let hasWarnings = false;
|
|
143
142
|
|
|
143
|
+
console.log('');
|
|
144
144
|
results.forEach((result) => {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
const padded = result.name.padEnd(20);
|
|
146
|
+
if (result.status === 'ok') {
|
|
147
|
+
printSuccess(`${padded} ${result.message}`);
|
|
148
|
+
} else if (result.status === 'warning') {
|
|
149
|
+
printWarning(`${padded} ${result.message}`);
|
|
150
|
+
} else {
|
|
151
|
+
printError(`${padded} ${result.message}`);
|
|
152
|
+
}
|
|
149
153
|
|
|
150
|
-
if (result.fix && options.verbose) {
|
|
151
|
-
console.log(chalk.dim(`
|
|
154
|
+
if (result.fix && (options.verbose || result.status === 'error')) {
|
|
155
|
+
console.log(chalk.dim(` → ${result.fix}`));
|
|
152
156
|
}
|
|
153
157
|
|
|
154
158
|
if (result.status === 'error') hasErrors = true;
|