@objectstack/cli 2.0.7 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -6
- package/CHANGELOG.md +17 -0
- package/dist/bin.js +388 -464
- package/dist/chunk-CSHQEILI.js +246 -0
- package/dist/chunk-Q74JNWKD.js +248 -0
- package/dist/config-A7BN6UIT.js +11 -0
- package/dist/config-UN34WBHT.js +10 -0
- package/dist/index.js +373 -448
- package/package.json +9 -9
- package/src/commands/generate.ts +181 -3
package/dist/bin.js
CHANGED
|
@@ -1,244 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
collectMetadataStats,
|
|
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-Q74JNWKD.js";
|
|
2
18
|
|
|
3
19
|
// src/bin.ts
|
|
4
20
|
import { createRequire as createRequire2 } from "module";
|
|
5
21
|
import { Command as Command13 } from "commander";
|
|
6
|
-
import
|
|
22
|
+
import chalk13 from "chalk";
|
|
7
23
|
|
|
8
24
|
// src/commands/compile.ts
|
|
9
25
|
import { Command } from "commander";
|
|
10
|
-
import path2 from "path";
|
|
11
|
-
import fs2 from "fs";
|
|
12
|
-
import chalk3 from "chalk";
|
|
13
|
-
import { ObjectStackDefinitionSchema } from "@objectstack/spec";
|
|
14
|
-
|
|
15
|
-
// src/utils/config.ts
|
|
16
26
|
import path from "path";
|
|
17
27
|
import fs from "fs";
|
|
18
|
-
import chalk2 from "chalk";
|
|
19
|
-
import { bundleRequire } from "bundle-require";
|
|
20
|
-
|
|
21
|
-
// src/utils/format.ts
|
|
22
28
|
import chalk from "chalk";
|
|
23
|
-
|
|
24
|
-
console.log(chalk.bold(`
|
|
25
|
-
\u25C6 ${title}`));
|
|
26
|
-
console.log(chalk.dim("\u2500".repeat(40)));
|
|
27
|
-
}
|
|
28
|
-
function printKV(key, value, icon) {
|
|
29
|
-
const prefix = icon ? `${icon} ` : " ";
|
|
30
|
-
console.log(`${prefix}${chalk.dim(key + ":")} ${chalk.white(String(value))}`);
|
|
31
|
-
}
|
|
32
|
-
function printSuccess(msg) {
|
|
33
|
-
console.log(chalk.green(` \u2713 ${msg}`));
|
|
34
|
-
}
|
|
35
|
-
function printWarning(msg) {
|
|
36
|
-
console.log(chalk.yellow(` \u26A0 ${msg}`));
|
|
37
|
-
}
|
|
38
|
-
function printError(msg) {
|
|
39
|
-
console.log(chalk.red(` \u2717 ${msg}`));
|
|
40
|
-
}
|
|
41
|
-
function printInfo(msg) {
|
|
42
|
-
console.log(chalk.blue(` \u2139 ${msg}`));
|
|
43
|
-
}
|
|
44
|
-
function printStep(msg) {
|
|
45
|
-
console.log(chalk.yellow(` \u2192 ${msg}`));
|
|
46
|
-
}
|
|
47
|
-
function createTimer() {
|
|
48
|
-
const start = Date.now();
|
|
49
|
-
return {
|
|
50
|
-
elapsed: () => Date.now() - start,
|
|
51
|
-
display: () => `${Date.now() - start}ms`
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function formatZodErrors(error) {
|
|
55
|
-
const issues = error.issues || error.errors || [];
|
|
56
|
-
if (issues.length === 0) {
|
|
57
|
-
console.log(chalk.red(" Unknown validation error"));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
61
|
-
for (const issue of issues) {
|
|
62
|
-
const topPath = issue.path?.[0] || "_root";
|
|
63
|
-
if (!grouped.has(String(topPath))) {
|
|
64
|
-
grouped.set(String(topPath), []);
|
|
65
|
-
}
|
|
66
|
-
grouped.get(String(topPath)).push(issue);
|
|
67
|
-
}
|
|
68
|
-
for (const [section, sectionIssues] of grouped) {
|
|
69
|
-
console.log(chalk.bold.red(`
|
|
70
|
-
${section}:`));
|
|
71
|
-
for (const issue of sectionIssues) {
|
|
72
|
-
const path12 = issue.path?.join(".") || "";
|
|
73
|
-
const code = issue.code || "";
|
|
74
|
-
const msg = issue.message || "";
|
|
75
|
-
console.log(chalk.red(` \u2717 ${path12}`));
|
|
76
|
-
console.log(chalk.dim(` ${code}: ${msg}`));
|
|
77
|
-
if (issue.expected) {
|
|
78
|
-
console.log(chalk.dim(` expected: ${chalk.green(issue.expected)}`));
|
|
79
|
-
}
|
|
80
|
-
if (issue.received) {
|
|
81
|
-
console.log(chalk.dim(` received: ${chalk.red(issue.received)}`));
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
console.log("");
|
|
86
|
-
console.log(chalk.dim(` ${issues.length} validation error(s) total`));
|
|
87
|
-
}
|
|
88
|
-
function collectMetadataStats(config) {
|
|
89
|
-
const count = (arr) => Array.isArray(arr) ? arr.length : 0;
|
|
90
|
-
let fields = 0;
|
|
91
|
-
if (Array.isArray(config.objects)) {
|
|
92
|
-
for (const obj of config.objects) {
|
|
93
|
-
if (obj.fields && typeof obj.fields === "object") {
|
|
94
|
-
fields += Object.keys(obj.fields).length;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return {
|
|
99
|
-
objects: count(config.objects),
|
|
100
|
-
objectExtensions: count(config.objectExtensions),
|
|
101
|
-
fields,
|
|
102
|
-
views: count(config.views),
|
|
103
|
-
pages: count(config.pages),
|
|
104
|
-
apps: count(config.apps),
|
|
105
|
-
dashboards: count(config.dashboards),
|
|
106
|
-
reports: count(config.reports),
|
|
107
|
-
actions: count(config.actions),
|
|
108
|
-
flows: count(config.flows),
|
|
109
|
-
workflows: count(config.workflows),
|
|
110
|
-
approvals: count(config.approvals),
|
|
111
|
-
agents: count(config.agents),
|
|
112
|
-
apis: count(config.apis),
|
|
113
|
-
roles: count(config.roles),
|
|
114
|
-
permissions: count(config.permissions),
|
|
115
|
-
themes: count(config.themes),
|
|
116
|
-
datasources: count(config.datasources),
|
|
117
|
-
translations: count(config.translations),
|
|
118
|
-
plugins: count(config.plugins),
|
|
119
|
-
devPlugins: count(config.devPlugins)
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function printServerReady(opts) {
|
|
123
|
-
const base = `http://localhost:${opts.port}`;
|
|
124
|
-
console.log("");
|
|
125
|
-
console.log(chalk.bold.green(" \u2713 Server is ready"));
|
|
126
|
-
console.log("");
|
|
127
|
-
console.log(chalk.cyan(" \u279C") + chalk.bold(" API: ") + chalk.cyan(base + "/"));
|
|
128
|
-
if (opts.uiEnabled && opts.studioPath) {
|
|
129
|
-
console.log(chalk.cyan(" \u279C") + chalk.bold(" Studio: ") + chalk.cyan(base + opts.studioPath + "/"));
|
|
130
|
-
}
|
|
131
|
-
console.log("");
|
|
132
|
-
console.log(chalk.dim(` Config: ${opts.configFile}`));
|
|
133
|
-
console.log(chalk.dim(` Mode: ${opts.isDev ? "development" : "production"}`));
|
|
134
|
-
console.log(chalk.dim(` Plugins: ${opts.pluginCount} loaded`));
|
|
135
|
-
if (opts.pluginNames && opts.pluginNames.length > 0) {
|
|
136
|
-
console.log(chalk.dim(` ${opts.pluginNames.join(", ")}`));
|
|
137
|
-
}
|
|
138
|
-
console.log("");
|
|
139
|
-
console.log(chalk.dim(" Press Ctrl+C to stop"));
|
|
140
|
-
console.log("");
|
|
141
|
-
}
|
|
142
|
-
function printMetadataStats(stats) {
|
|
143
|
-
const sections = [
|
|
144
|
-
{
|
|
145
|
-
label: "Data",
|
|
146
|
-
items: [
|
|
147
|
-
["Objects", stats.objects],
|
|
148
|
-
["Fields", stats.fields],
|
|
149
|
-
["Extensions", stats.objectExtensions],
|
|
150
|
-
["Datasources", stats.datasources]
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
|
-
{
|
|
154
|
-
label: "UI",
|
|
155
|
-
items: [
|
|
156
|
-
["Apps", stats.apps],
|
|
157
|
-
["Views", stats.views],
|
|
158
|
-
["Pages", stats.pages],
|
|
159
|
-
["Dashboards", stats.dashboards],
|
|
160
|
-
["Reports", stats.reports],
|
|
161
|
-
["Actions", stats.actions],
|
|
162
|
-
["Themes", stats.themes]
|
|
163
|
-
]
|
|
164
|
-
},
|
|
165
|
-
{
|
|
166
|
-
label: "Logic",
|
|
167
|
-
items: [
|
|
168
|
-
["Flows", stats.flows],
|
|
169
|
-
["Workflows", stats.workflows],
|
|
170
|
-
["Approvals", stats.approvals],
|
|
171
|
-
["Agents", stats.agents],
|
|
172
|
-
["APIs", stats.apis]
|
|
173
|
-
]
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
label: "Security",
|
|
177
|
-
items: [
|
|
178
|
-
["Roles", stats.roles],
|
|
179
|
-
["Permissions", stats.permissions]
|
|
180
|
-
]
|
|
181
|
-
}
|
|
182
|
-
];
|
|
183
|
-
for (const section of sections) {
|
|
184
|
-
const nonZero = section.items.filter(([, v]) => v > 0);
|
|
185
|
-
if (nonZero.length === 0) continue;
|
|
186
|
-
const line = nonZero.map(([k, v]) => `${chalk.white(v)} ${chalk.dim(k)}`).join(" ");
|
|
187
|
-
console.log(` ${chalk.bold(section.label + ":")} ${line}`);
|
|
188
|
-
}
|
|
189
|
-
if (stats.plugins > 0 || stats.devPlugins > 0) {
|
|
190
|
-
const parts = [];
|
|
191
|
-
if (stats.plugins > 0) parts.push(`${stats.plugins} plugins`);
|
|
192
|
-
if (stats.devPlugins > 0) parts.push(`${stats.devPlugins} devPlugins`);
|
|
193
|
-
console.log(` ${chalk.bold("Runtime:")} ${chalk.dim(parts.join(", "))}`);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// src/utils/config.ts
|
|
198
|
-
function resolveConfigPath(source) {
|
|
199
|
-
if (source) {
|
|
200
|
-
const abs = path.resolve(process.cwd(), source);
|
|
201
|
-
if (!fs.existsSync(abs)) {
|
|
202
|
-
printError(`Config file not found: ${chalk2.white(abs)}`);
|
|
203
|
-
console.log("");
|
|
204
|
-
console.log(chalk2.dim(" Hint: Run this command from a directory with objectstack.config.ts"));
|
|
205
|
-
console.log(chalk2.dim(" Or specify the path: objectstack <command> path/to/config.ts"));
|
|
206
|
-
process.exit(1);
|
|
207
|
-
}
|
|
208
|
-
return abs;
|
|
209
|
-
}
|
|
210
|
-
const candidates = [
|
|
211
|
-
"objectstack.config.ts",
|
|
212
|
-
"objectstack.config.js",
|
|
213
|
-
"objectstack.config.mjs"
|
|
214
|
-
];
|
|
215
|
-
for (const candidate of candidates) {
|
|
216
|
-
const abs = path.resolve(process.cwd(), candidate);
|
|
217
|
-
if (fs.existsSync(abs)) return abs;
|
|
218
|
-
}
|
|
219
|
-
printError("No objectstack.config.{ts,js,mjs} found in current directory");
|
|
220
|
-
console.log("");
|
|
221
|
-
console.log(chalk2.dim(" Hint: Run `objectstack init` to create a new project"));
|
|
222
|
-
process.exit(1);
|
|
223
|
-
}
|
|
224
|
-
async function loadConfig(source) {
|
|
225
|
-
const absolutePath = resolveConfigPath(source);
|
|
226
|
-
const start = Date.now();
|
|
227
|
-
const { mod } = await bundleRequire({
|
|
228
|
-
filepath: absolutePath
|
|
229
|
-
});
|
|
230
|
-
const config = mod.default || mod;
|
|
231
|
-
if (!config) {
|
|
232
|
-
throw new Error(`No default export found in ${path.basename(absolutePath)}`);
|
|
233
|
-
}
|
|
234
|
-
return {
|
|
235
|
-
config,
|
|
236
|
-
absolutePath,
|
|
237
|
-
duration: Date.now() - start
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/commands/compile.ts
|
|
29
|
+
import { ObjectStackDefinitionSchema } from "@objectstack/spec";
|
|
242
30
|
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) => {
|
|
243
31
|
const timer = createTimer();
|
|
244
32
|
if (!options.json) {
|
|
@@ -248,7 +36,7 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
|
|
|
248
36
|
if (!options.json) printStep("Loading configuration...");
|
|
249
37
|
const { config, absolutePath, duration } = await loadConfig(configPath);
|
|
250
38
|
if (!options.json) {
|
|
251
|
-
printKV("Config",
|
|
39
|
+
printKV("Config", path.relative(process.cwd(), absolutePath));
|
|
252
40
|
printKV("Load time", `${duration}ms`);
|
|
253
41
|
}
|
|
254
42
|
if (!options.json) printStep("Validating protocol compliance...");
|
|
@@ -265,13 +53,13 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
|
|
|
265
53
|
}
|
|
266
54
|
if (!options.json) printStep("Writing artifact...");
|
|
267
55
|
const output = options.output;
|
|
268
|
-
const artifactPath =
|
|
269
|
-
const artifactDir =
|
|
270
|
-
if (!
|
|
271
|
-
|
|
56
|
+
const artifactPath = path.resolve(process.cwd(), output);
|
|
57
|
+
const artifactDir = path.dirname(artifactPath);
|
|
58
|
+
if (!fs.existsSync(artifactDir)) {
|
|
59
|
+
fs.mkdirSync(artifactDir, { recursive: true });
|
|
272
60
|
}
|
|
273
61
|
const jsonContent = JSON.stringify(result.data, null, 2);
|
|
274
|
-
|
|
62
|
+
fs.writeFileSync(artifactPath, jsonContent);
|
|
275
63
|
const sizeKB = (jsonContent.length / 1024).toFixed(1);
|
|
276
64
|
const stats = collectMetadataStats(config);
|
|
277
65
|
if (options.json) {
|
|
@@ -285,11 +73,11 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
|
|
|
285
73
|
return;
|
|
286
74
|
}
|
|
287
75
|
console.log("");
|
|
288
|
-
printSuccess(`Build complete ${
|
|
76
|
+
printSuccess(`Build complete ${chalk.dim(`(${timer.display()})`)}`);
|
|
289
77
|
console.log("");
|
|
290
78
|
printMetadataStats(stats);
|
|
291
79
|
console.log("");
|
|
292
|
-
printKV("Artifact", `${output} ${
|
|
80
|
+
printKV("Artifact", `${output} ${chalk.dim(`(${sizeKB} KB`)})`);
|
|
293
81
|
console.log("");
|
|
294
82
|
} catch (error) {
|
|
295
83
|
if (options.json) {
|
|
@@ -304,14 +92,14 @@ var compileCommand = new Command("compile").description("Compile ObjectStack con
|
|
|
304
92
|
|
|
305
93
|
// src/commands/dev.ts
|
|
306
94
|
import { Command as Command2 } from "commander";
|
|
307
|
-
import
|
|
95
|
+
import chalk2 from "chalk";
|
|
308
96
|
import { execSync, spawn } from "child_process";
|
|
309
|
-
import
|
|
310
|
-
import
|
|
97
|
+
import fs2 from "fs";
|
|
98
|
+
import path2 from "path";
|
|
311
99
|
var devCommand = new Command2("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) => {
|
|
312
100
|
printHeader("Development Mode");
|
|
313
|
-
const configPath =
|
|
314
|
-
if (packageName === "all" &&
|
|
101
|
+
const configPath = path2.resolve(process.cwd(), "objectstack.config.ts");
|
|
102
|
+
if (packageName === "all" && fs2.existsSync(configPath)) {
|
|
315
103
|
printKV("Config", configPath, "\u{1F4C2}");
|
|
316
104
|
printStep("Starting dev server...");
|
|
317
105
|
const binPath = process.argv[1];
|
|
@@ -323,18 +111,18 @@ var devCommand = new Command2("dev").description("Start development mode with ho
|
|
|
323
111
|
}
|
|
324
112
|
try {
|
|
325
113
|
const cwd = process.cwd();
|
|
326
|
-
const workspaceConfigPath =
|
|
327
|
-
const isWorkspaceRoot =
|
|
114
|
+
const workspaceConfigPath = path2.resolve(cwd, "pnpm-workspace.yaml");
|
|
115
|
+
const isWorkspaceRoot = fs2.existsSync(workspaceConfigPath);
|
|
328
116
|
if (packageName === "all" && !isWorkspaceRoot) {
|
|
329
117
|
printError(`Config file not found in ${cwd}`);
|
|
330
|
-
console.error(
|
|
118
|
+
console.error(chalk2.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
|
|
331
119
|
process.exit(1);
|
|
332
120
|
}
|
|
333
121
|
const filter = packageName === "all" ? "" : `--filter ${packageName}`;
|
|
334
122
|
printKV("Package", packageName === "all" ? "All packages" : packageName, "\u{1F4E6}");
|
|
335
123
|
printKV("Watch", "enabled", "\u{1F504}");
|
|
336
124
|
const command = `pnpm ${filter} dev`.trim();
|
|
337
|
-
console.log(
|
|
125
|
+
console.log(chalk2.dim(`$ ${command}`));
|
|
338
126
|
console.log("");
|
|
339
127
|
execSync(command, {
|
|
340
128
|
stdio: "inherit",
|
|
@@ -348,10 +136,10 @@ var devCommand = new Command2("dev").description("Start development mode with ho
|
|
|
348
136
|
|
|
349
137
|
// src/commands/doctor.ts
|
|
350
138
|
import { Command as Command3 } from "commander";
|
|
351
|
-
import
|
|
139
|
+
import chalk3 from "chalk";
|
|
352
140
|
import { execSync as execSync2 } from "child_process";
|
|
353
|
-
import
|
|
354
|
-
import
|
|
141
|
+
import fs3 from "fs";
|
|
142
|
+
import path3 from "path";
|
|
355
143
|
var doctorCommand = new Command3("doctor").description("Check development environment health").option("-v, --verbose", "Show detailed information").action(async (options) => {
|
|
356
144
|
printHeader("Environment Health Check");
|
|
357
145
|
const results = [];
|
|
@@ -411,8 +199,8 @@ var doctorCommand = new Command3("doctor").description("Check development enviro
|
|
|
411
199
|
});
|
|
412
200
|
}
|
|
413
201
|
const cwd = process.cwd();
|
|
414
|
-
const nodeModulesPath =
|
|
415
|
-
if (
|
|
202
|
+
const nodeModulesPath = path3.join(cwd, "node_modules");
|
|
203
|
+
if (fs3.existsSync(nodeModulesPath)) {
|
|
416
204
|
results.push({
|
|
417
205
|
name: "Dependencies",
|
|
418
206
|
status: "ok",
|
|
@@ -426,8 +214,8 @@ var doctorCommand = new Command3("doctor").description("Check development enviro
|
|
|
426
214
|
fix: "Run: pnpm install"
|
|
427
215
|
});
|
|
428
216
|
}
|
|
429
|
-
const specDistPath =
|
|
430
|
-
if (
|
|
217
|
+
const specDistPath = path3.join(cwd, "packages/spec/dist");
|
|
218
|
+
if (fs3.existsSync(specDistPath)) {
|
|
431
219
|
results.push({
|
|
432
220
|
name: "@objectstack/spec",
|
|
433
221
|
status: "ok",
|
|
@@ -469,30 +257,30 @@ var doctorCommand = new Command3("doctor").description("Check development enviro
|
|
|
469
257
|
printError(`${padded} ${result.message}`);
|
|
470
258
|
}
|
|
471
259
|
if (result.fix && (options.verbose || result.status === "error")) {
|
|
472
|
-
console.log(
|
|
260
|
+
console.log(chalk3.dim(` \u2192 ${result.fix}`));
|
|
473
261
|
}
|
|
474
262
|
if (result.status === "error") hasErrors = true;
|
|
475
263
|
if (result.status === "warning") hasWarnings = true;
|
|
476
264
|
});
|
|
477
265
|
console.log("");
|
|
478
266
|
if (hasErrors) {
|
|
479
|
-
console.log(
|
|
480
|
-
results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(
|
|
267
|
+
console.log(chalk3.red("\u274C Some critical issues found. Please fix them before continuing."));
|
|
268
|
+
results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk3.dim(` ${r.fix}`)));
|
|
481
269
|
process.exit(1);
|
|
482
270
|
} else if (hasWarnings) {
|
|
483
|
-
console.log(
|
|
484
|
-
console.log(
|
|
271
|
+
console.log(chalk3.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
|
|
272
|
+
console.log(chalk3.dim(" Run with --verbose to see fix suggestions."));
|
|
485
273
|
} else {
|
|
486
|
-
console.log(
|
|
274
|
+
console.log(chalk3.green("\u2705 Environment is healthy and ready for development!"));
|
|
487
275
|
}
|
|
488
276
|
console.log("");
|
|
489
277
|
});
|
|
490
278
|
|
|
491
279
|
// src/commands/create.ts
|
|
492
280
|
import { Command as Command4 } from "commander";
|
|
493
|
-
import
|
|
494
|
-
import
|
|
495
|
-
import
|
|
281
|
+
import chalk4 from "chalk";
|
|
282
|
+
import fs4 from "fs";
|
|
283
|
+
import path4 from "path";
|
|
496
284
|
var templates = {
|
|
497
285
|
plugin: {
|
|
498
286
|
description: "Create a new ObjectStack plugin",
|
|
@@ -667,64 +455,64 @@ function toCamelCase(str) {
|
|
|
667
455
|
return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
|
|
668
456
|
}
|
|
669
457
|
var createCommand = new Command4("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) => {
|
|
670
|
-
console.log(
|
|
458
|
+
console.log(chalk4.bold(`
|
|
671
459
|
\u{1F4E6} ObjectStack Project Creator`));
|
|
672
|
-
console.log(
|
|
460
|
+
console.log(chalk4.dim(`-------------------------------`));
|
|
673
461
|
if (!templates[type]) {
|
|
674
|
-
console.error(
|
|
462
|
+
console.error(chalk4.red(`
|
|
675
463
|
\u274C Unknown type: ${type}`));
|
|
676
|
-
console.log(
|
|
464
|
+
console.log(chalk4.dim("Available types: plugin, example"));
|
|
677
465
|
process.exit(1);
|
|
678
466
|
}
|
|
679
467
|
if (!name) {
|
|
680
|
-
console.error(
|
|
681
|
-
console.log(
|
|
468
|
+
console.error(chalk4.red("\n\u274C Project name is required"));
|
|
469
|
+
console.log(chalk4.dim(`Usage: objectstack create ${type} <name>`));
|
|
682
470
|
process.exit(1);
|
|
683
471
|
}
|
|
684
472
|
const template = templates[type];
|
|
685
473
|
const cwd = process.cwd();
|
|
686
474
|
let targetDir;
|
|
687
475
|
if (options?.dir) {
|
|
688
|
-
targetDir =
|
|
476
|
+
targetDir = path4.resolve(cwd, options.dir);
|
|
689
477
|
} else {
|
|
690
478
|
const baseDir = type === "plugin" ? "packages/plugins" : "examples";
|
|
691
479
|
const projectName = type === "plugin" ? `plugin-${name}` : name;
|
|
692
|
-
targetDir =
|
|
480
|
+
targetDir = path4.join(cwd, baseDir, projectName);
|
|
693
481
|
}
|
|
694
|
-
if (
|
|
695
|
-
console.error(
|
|
482
|
+
if (fs4.existsSync(targetDir)) {
|
|
483
|
+
console.error(chalk4.red(`
|
|
696
484
|
\u274C Directory already exists: ${targetDir}`));
|
|
697
485
|
process.exit(1);
|
|
698
486
|
}
|
|
699
|
-
console.log(`\u{1F4C1} Creating ${type}: ${
|
|
700
|
-
console.log(`\u{1F4C2} Location: ${
|
|
487
|
+
console.log(`\u{1F4C1} Creating ${type}: ${chalk4.blue(name)}`);
|
|
488
|
+
console.log(`\u{1F4C2} Location: ${chalk4.dim(targetDir)}`);
|
|
701
489
|
console.log("");
|
|
702
490
|
try {
|
|
703
|
-
|
|
491
|
+
fs4.mkdirSync(targetDir, { recursive: true });
|
|
704
492
|
for (const [filePath, contentFn] of Object.entries(template.files)) {
|
|
705
|
-
const fullPath =
|
|
706
|
-
const dir =
|
|
707
|
-
if (!
|
|
708
|
-
|
|
493
|
+
const fullPath = path4.join(targetDir, filePath);
|
|
494
|
+
const dir = path4.dirname(fullPath);
|
|
495
|
+
if (!fs4.existsSync(dir)) {
|
|
496
|
+
fs4.mkdirSync(dir, { recursive: true });
|
|
709
497
|
}
|
|
710
498
|
const content = contentFn(name);
|
|
711
499
|
const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
712
|
-
|
|
713
|
-
console.log(
|
|
500
|
+
fs4.writeFileSync(fullPath, fileContent);
|
|
501
|
+
console.log(chalk4.green(`\u2713 Created ${filePath}`));
|
|
714
502
|
}
|
|
715
503
|
console.log("");
|
|
716
|
-
console.log(
|
|
504
|
+
console.log(chalk4.green("\u2705 Project created successfully!"));
|
|
717
505
|
console.log("");
|
|
718
|
-
console.log(
|
|
719
|
-
console.log(
|
|
720
|
-
console.log(
|
|
721
|
-
console.log(
|
|
506
|
+
console.log(chalk4.bold("Next steps:"));
|
|
507
|
+
console.log(chalk4.dim(` cd ${path4.relative(cwd, targetDir)}`));
|
|
508
|
+
console.log(chalk4.dim(" pnpm install"));
|
|
509
|
+
console.log(chalk4.dim(" pnpm build"));
|
|
722
510
|
console.log("");
|
|
723
511
|
} catch (error) {
|
|
724
|
-
console.error(
|
|
512
|
+
console.error(chalk4.red("\n\u274C Failed to create project:"));
|
|
725
513
|
console.error(error.message || error);
|
|
726
|
-
if (
|
|
727
|
-
|
|
514
|
+
if (fs4.existsSync(targetDir)) {
|
|
515
|
+
fs4.rmSync(targetDir, { recursive: true });
|
|
728
516
|
}
|
|
729
517
|
process.exit(1);
|
|
730
518
|
}
|
|
@@ -732,37 +520,37 @@ var createCommand = new Command4("create").description("Create a new package, pl
|
|
|
732
520
|
|
|
733
521
|
// src/commands/serve.ts
|
|
734
522
|
import { Command as Command5 } from "commander";
|
|
735
|
-
import
|
|
736
|
-
import
|
|
523
|
+
import path6 from "path";
|
|
524
|
+
import fs6 from "fs";
|
|
737
525
|
import net from "net";
|
|
738
|
-
import
|
|
739
|
-
import { bundleRequire
|
|
526
|
+
import chalk5 from "chalk";
|
|
527
|
+
import { bundleRequire } from "bundle-require";
|
|
740
528
|
|
|
741
529
|
// src/utils/studio.ts
|
|
742
|
-
import
|
|
743
|
-
import
|
|
530
|
+
import path5 from "path";
|
|
531
|
+
import fs5 from "fs";
|
|
744
532
|
import { createRequire } from "module";
|
|
745
533
|
import { pathToFileURL } from "url";
|
|
746
534
|
var STUDIO_PATH = "/_studio";
|
|
747
535
|
function resolveStudioPath() {
|
|
748
536
|
const cwd = process.cwd();
|
|
749
537
|
const candidates = [
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
538
|
+
path5.resolve(cwd, "apps/studio"),
|
|
539
|
+
path5.resolve(cwd, "../../apps/studio"),
|
|
540
|
+
path5.resolve(cwd, "../apps/studio")
|
|
753
541
|
];
|
|
754
542
|
for (const candidate of candidates) {
|
|
755
|
-
const pkgPath =
|
|
756
|
-
if (
|
|
543
|
+
const pkgPath = path5.join(candidate, "package.json");
|
|
544
|
+
if (fs5.existsSync(pkgPath)) {
|
|
757
545
|
try {
|
|
758
|
-
const pkg2 = JSON.parse(
|
|
546
|
+
const pkg2 = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
759
547
|
if (pkg2.name === "@objectstack/studio") return candidate;
|
|
760
548
|
} catch {
|
|
761
549
|
}
|
|
762
550
|
}
|
|
763
551
|
}
|
|
764
552
|
const resolutionBases = [
|
|
765
|
-
pathToFileURL(
|
|
553
|
+
pathToFileURL(path5.join(cwd, "package.json")).href,
|
|
766
554
|
// consumer workspace
|
|
767
555
|
import.meta.url
|
|
768
556
|
// CLI package itself
|
|
@@ -771,18 +559,18 @@ function resolveStudioPath() {
|
|
|
771
559
|
try {
|
|
772
560
|
const req = createRequire(base);
|
|
773
561
|
const resolved = req.resolve("@objectstack/studio/package.json");
|
|
774
|
-
return
|
|
562
|
+
return path5.dirname(resolved);
|
|
775
563
|
} catch {
|
|
776
564
|
}
|
|
777
565
|
}
|
|
778
|
-
const directPath =
|
|
779
|
-
if (
|
|
566
|
+
const directPath = path5.join(cwd, "node_modules", "@objectstack", "studio");
|
|
567
|
+
if (fs5.existsSync(path5.join(directPath, "package.json"))) {
|
|
780
568
|
return directPath;
|
|
781
569
|
}
|
|
782
570
|
return null;
|
|
783
571
|
}
|
|
784
572
|
function hasStudioDist(studioPath) {
|
|
785
|
-
return
|
|
573
|
+
return fs5.existsSync(path5.join(studioPath, "dist", "index.html"));
|
|
786
574
|
}
|
|
787
575
|
function createStudioStaticPlugin(distPath, options) {
|
|
788
576
|
return {
|
|
@@ -796,13 +584,13 @@ function createStudioStaticPlugin(distPath, options) {
|
|
|
796
584
|
return;
|
|
797
585
|
}
|
|
798
586
|
const app = httpServer.getRawApp();
|
|
799
|
-
const absoluteDist =
|
|
800
|
-
const indexPath =
|
|
801
|
-
if (!
|
|
587
|
+
const absoluteDist = path5.resolve(distPath);
|
|
588
|
+
const indexPath = path5.join(absoluteDist, "index.html");
|
|
589
|
+
if (!fs5.existsSync(indexPath)) {
|
|
802
590
|
ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
|
|
803
591
|
return;
|
|
804
592
|
}
|
|
805
|
-
const rawHtml =
|
|
593
|
+
const rawHtml = fs5.readFileSync(indexPath, "utf-8");
|
|
806
594
|
const rewrittenHtml = rawHtml.replace(
|
|
807
595
|
/(\s(?:href|src))="\/(?!\/)/g,
|
|
808
596
|
`$1="${STUDIO_PATH}/`
|
|
@@ -813,12 +601,12 @@ function createStudioStaticPlugin(distPath, options) {
|
|
|
813
601
|
app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
|
|
814
602
|
app.get(`${STUDIO_PATH}/*`, async (c) => {
|
|
815
603
|
const reqPath = c.req.path.substring(STUDIO_PATH.length) || "/";
|
|
816
|
-
const filePath =
|
|
604
|
+
const filePath = path5.join(absoluteDist, reqPath);
|
|
817
605
|
if (!filePath.startsWith(absoluteDist)) {
|
|
818
606
|
return c.text("Forbidden", 403);
|
|
819
607
|
}
|
|
820
|
-
if (
|
|
821
|
-
const content =
|
|
608
|
+
if (fs5.existsSync(filePath) && fs5.statSync(filePath).isFile()) {
|
|
609
|
+
const content = fs5.readFileSync(filePath);
|
|
822
610
|
return new Response(content, {
|
|
823
611
|
headers: { "content-type": mimeType(filePath) }
|
|
824
612
|
});
|
|
@@ -848,7 +636,7 @@ var MIME_TYPES = {
|
|
|
848
636
|
".map": "application/json"
|
|
849
637
|
};
|
|
850
638
|
function mimeType(filePath) {
|
|
851
|
-
const ext =
|
|
639
|
+
const ext = path5.extname(filePath).toLowerCase();
|
|
852
640
|
return MIME_TYPES[ext] || "application/octet-stream";
|
|
853
641
|
}
|
|
854
642
|
|
|
@@ -885,15 +673,15 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
885
673
|
} catch (e) {
|
|
886
674
|
}
|
|
887
675
|
const isDev = options.dev || process.env.NODE_ENV === "development";
|
|
888
|
-
const absolutePath =
|
|
889
|
-
const relativeConfig =
|
|
890
|
-
if (!
|
|
676
|
+
const absolutePath = path6.resolve(process.cwd(), configPath);
|
|
677
|
+
const relativeConfig = path6.relative(process.cwd(), absolutePath);
|
|
678
|
+
if (!fs6.existsSync(absolutePath)) {
|
|
891
679
|
printError(`Configuration file not found: ${absolutePath}`);
|
|
892
|
-
console.log(
|
|
680
|
+
console.log(chalk5.dim(" Hint: Run `objectstack init` to create a new project"));
|
|
893
681
|
process.exit(1);
|
|
894
682
|
}
|
|
895
683
|
console.log("");
|
|
896
|
-
console.log(
|
|
684
|
+
console.log(chalk5.dim(` Loading ${relativeConfig}...`));
|
|
897
685
|
const loadedPlugins = [];
|
|
898
686
|
const shortPluginName = (raw) => {
|
|
899
687
|
if (raw.includes("objectql")) return "ObjectQL";
|
|
@@ -928,7 +716,7 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
928
716
|
console.debug = (...args) => {
|
|
929
717
|
if (!bootQuiet) originalConsoleDebug(...args);
|
|
930
718
|
};
|
|
931
|
-
const { mod } = await
|
|
719
|
+
const { mod } = await bundleRequire({
|
|
932
720
|
filepath: absolutePath
|
|
933
721
|
});
|
|
934
722
|
const config = mod.default || mod;
|
|
@@ -997,7 +785,7 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
997
785
|
const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
|
|
998
786
|
trackPlugin(pluginName);
|
|
999
787
|
} catch (e) {
|
|
1000
|
-
console.error(
|
|
788
|
+
console.error(chalk5.red(` \u2717 Failed to load plugin: ${e.message}`));
|
|
1001
789
|
}
|
|
1002
790
|
}
|
|
1003
791
|
}
|
|
@@ -1008,7 +796,7 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
1008
796
|
await kernel.use(serverPlugin);
|
|
1009
797
|
trackPlugin("HonoServer");
|
|
1010
798
|
} catch (e) {
|
|
1011
|
-
console.warn(
|
|
799
|
+
console.warn(chalk5.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
|
|
1012
800
|
}
|
|
1013
801
|
try {
|
|
1014
802
|
const { createRestApiPlugin } = await import("@objectstack/rest");
|
|
@@ -1027,13 +815,13 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
1027
815
|
if (enableUI) {
|
|
1028
816
|
const studioPath = resolveStudioPath();
|
|
1029
817
|
if (!studioPath) {
|
|
1030
|
-
console.warn(
|
|
818
|
+
console.warn(chalk5.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
|
|
1031
819
|
} else if (hasStudioDist(studioPath)) {
|
|
1032
|
-
const distPath =
|
|
820
|
+
const distPath = path6.join(studioPath, "dist");
|
|
1033
821
|
await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
|
|
1034
822
|
trackPlugin("StudioUI");
|
|
1035
823
|
} else {
|
|
1036
|
-
console.warn(
|
|
824
|
+
console.warn(chalk5.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
|
|
1037
825
|
}
|
|
1038
826
|
}
|
|
1039
827
|
await runtime.start();
|
|
@@ -1049,18 +837,18 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
1049
837
|
studioPath: STUDIO_PATH
|
|
1050
838
|
});
|
|
1051
839
|
process.on("SIGINT", async () => {
|
|
1052
|
-
console.warn(
|
|
840
|
+
console.warn(chalk5.yellow(`
|
|
1053
841
|
|
|
1054
842
|
\u23F9 Stopping server...`));
|
|
1055
843
|
await runtime.getKernel().shutdown();
|
|
1056
|
-
console.log(
|
|
844
|
+
console.log(chalk5.green(`\u2705 Server stopped`));
|
|
1057
845
|
process.exit(0);
|
|
1058
846
|
});
|
|
1059
847
|
} catch (error) {
|
|
1060
848
|
restoreOutput();
|
|
1061
849
|
console.log("");
|
|
1062
850
|
printError(error.message || String(error));
|
|
1063
|
-
if (process.env.DEBUG) console.error(
|
|
851
|
+
if (process.env.DEBUG) console.error(chalk5.dim(error.stack));
|
|
1064
852
|
process.exit(1);
|
|
1065
853
|
}
|
|
1066
854
|
});
|
|
@@ -1092,15 +880,15 @@ var studioCommand = new Command6("studio").description("Launch Studio UI with de
|
|
|
1092
880
|
|
|
1093
881
|
// src/commands/test.ts
|
|
1094
882
|
import { Command as Command7 } from "commander";
|
|
1095
|
-
import
|
|
1096
|
-
import
|
|
1097
|
-
import
|
|
883
|
+
import chalk6 from "chalk";
|
|
884
|
+
import path7 from "path";
|
|
885
|
+
import fs7 from "fs";
|
|
1098
886
|
import { QA as CoreQA } from "@objectstack/core";
|
|
1099
887
|
function resolveGlob(pattern) {
|
|
1100
888
|
if (!pattern.includes("*")) {
|
|
1101
|
-
return
|
|
889
|
+
return fs7.existsSync(pattern) ? [pattern] : [];
|
|
1102
890
|
}
|
|
1103
|
-
const parts = pattern.split(
|
|
891
|
+
const parts = pattern.split(path7.sep.replace("\\", "/"));
|
|
1104
892
|
const segments = pattern.includes("/") ? pattern.split("/") : parts;
|
|
1105
893
|
let baseDir = ".";
|
|
1106
894
|
let globStart = 0;
|
|
@@ -1109,25 +897,25 @@ function resolveGlob(pattern) {
|
|
|
1109
897
|
globStart = i;
|
|
1110
898
|
break;
|
|
1111
899
|
}
|
|
1112
|
-
baseDir = i === 0 ? segments[i] :
|
|
900
|
+
baseDir = i === 0 ? segments[i] : path7.join(baseDir, segments[i]);
|
|
1113
901
|
}
|
|
1114
|
-
if (!
|
|
902
|
+
if (!fs7.existsSync(baseDir)) return [];
|
|
1115
903
|
const globPortion = segments.slice(globStart).join("/");
|
|
1116
904
|
const regexStr = globPortion.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
1117
905
|
const regex = new RegExp(`^${regexStr}$`);
|
|
1118
|
-
const entries =
|
|
1119
|
-
return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) =>
|
|
906
|
+
const entries = fs7.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
|
|
907
|
+
return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path7.join(baseDir, entry)).filter((fullPath) => fs7.statSync(fullPath).isFile());
|
|
1120
908
|
}
|
|
1121
909
|
var testCommand = new Command7("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) => {
|
|
1122
|
-
console.log(
|
|
910
|
+
console.log(chalk6.bold(`
|
|
1123
911
|
\u{1F9EA} ObjectStack Quality Protocol Runner`));
|
|
1124
|
-
console.log(
|
|
1125
|
-
console.log(`Target: ${
|
|
912
|
+
console.log(chalk6.dim(`-------------------------------------`));
|
|
913
|
+
console.log(`Target: ${chalk6.blue(options.url)}`);
|
|
1126
914
|
const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
|
|
1127
915
|
const runner = new CoreQA.TestRunner(adapter);
|
|
1128
916
|
const testFiles = resolveGlob(filesPattern);
|
|
1129
917
|
if (testFiles.length === 0) {
|
|
1130
|
-
console.warn(
|
|
918
|
+
console.warn(chalk6.yellow(`No test files found matching: ${filesPattern}`));
|
|
1131
919
|
return;
|
|
1132
920
|
}
|
|
1133
921
|
console.log(`Found ${testFiles.length} test suites.`);
|
|
@@ -1135,19 +923,19 @@ var testCommand = new Command7("test").description("Run Quality Protocol test sc
|
|
|
1135
923
|
let totalFailed = 0;
|
|
1136
924
|
for (const file of testFiles) {
|
|
1137
925
|
console.log(`
|
|
1138
|
-
\u{1F4C4} Running suite: ${
|
|
926
|
+
\u{1F4C4} Running suite: ${chalk6.bold(path7.basename(file))}`);
|
|
1139
927
|
try {
|
|
1140
|
-
const content =
|
|
928
|
+
const content = fs7.readFileSync(file, "utf-8");
|
|
1141
929
|
const suite = JSON.parse(content);
|
|
1142
930
|
const results = await runner.runSuite(suite);
|
|
1143
931
|
for (const result of results) {
|
|
1144
932
|
const icon = result.passed ? "\u2705" : "\u274C";
|
|
1145
933
|
console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
|
|
1146
934
|
if (!result.passed) {
|
|
1147
|
-
console.error(
|
|
935
|
+
console.error(chalk6.red(` Error: ${result.error}`));
|
|
1148
936
|
result.steps.forEach((step) => {
|
|
1149
937
|
if (!step.passed) {
|
|
1150
|
-
console.error(
|
|
938
|
+
console.error(chalk6.red(` Step Failed: ${step.stepName}`));
|
|
1151
939
|
if (step.output) console.error(` Output:`, step.output);
|
|
1152
940
|
if (step.error) console.error(` Error:`, step.error);
|
|
1153
941
|
}
|
|
@@ -1158,24 +946,24 @@ var testCommand = new Command7("test").description("Run Quality Protocol test sc
|
|
|
1158
946
|
}
|
|
1159
947
|
}
|
|
1160
948
|
} catch (e) {
|
|
1161
|
-
console.error(
|
|
949
|
+
console.error(chalk6.red(`Failed to load or run suite ${file}: ${e}`));
|
|
1162
950
|
totalFailed++;
|
|
1163
951
|
}
|
|
1164
952
|
}
|
|
1165
|
-
console.log(
|
|
953
|
+
console.log(chalk6.dim(`
|
|
1166
954
|
-------------------------------------`));
|
|
1167
955
|
if (totalFailed > 0) {
|
|
1168
|
-
console.log(
|
|
956
|
+
console.log(chalk6.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
|
|
1169
957
|
process.exit(1);
|
|
1170
958
|
} else {
|
|
1171
|
-
console.log(
|
|
959
|
+
console.log(chalk6.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
|
|
1172
960
|
process.exit(0);
|
|
1173
961
|
}
|
|
1174
962
|
});
|
|
1175
963
|
|
|
1176
964
|
// src/commands/validate.ts
|
|
1177
965
|
import { Command as Command8 } from "commander";
|
|
1178
|
-
import
|
|
966
|
+
import chalk7 from "chalk";
|
|
1179
967
|
import { ObjectStackDefinitionSchema as ObjectStackDefinitionSchema2 } from "@objectstack/spec";
|
|
1180
968
|
var validateCommand = new Command8("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) => {
|
|
1181
969
|
const timer = createTimer();
|
|
@@ -1229,12 +1017,12 @@ var validateCommand = new Command8("validate").description("Validate ObjectStack
|
|
|
1229
1017
|
warnings.push("Missing manifest.namespace \u2014 required for multi-app hosting");
|
|
1230
1018
|
}
|
|
1231
1019
|
console.log("");
|
|
1232
|
-
printSuccess(`Validation passed ${
|
|
1020
|
+
printSuccess(`Validation passed ${chalk7.dim(`(${timer.display()})`)}`);
|
|
1233
1021
|
console.log("");
|
|
1234
1022
|
if (config.manifest) {
|
|
1235
|
-
console.log(` ${
|
|
1023
|
+
console.log(` ${chalk7.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk7.dim(`v${config.manifest.version || "0.0.0"}`)}`);
|
|
1236
1024
|
if (config.manifest.description) {
|
|
1237
|
-
console.log(
|
|
1025
|
+
console.log(chalk7.dim(` ${config.manifest.description}`));
|
|
1238
1026
|
}
|
|
1239
1027
|
console.log("");
|
|
1240
1028
|
}
|
|
@@ -1242,7 +1030,7 @@ var validateCommand = new Command8("validate").description("Validate ObjectStack
|
|
|
1242
1030
|
if (warnings.length > 0) {
|
|
1243
1031
|
console.log("");
|
|
1244
1032
|
for (const w of warnings) {
|
|
1245
|
-
console.log(
|
|
1033
|
+
console.log(chalk7.yellow(` \u26A0 ${w}`));
|
|
1246
1034
|
}
|
|
1247
1035
|
if (options.strict) {
|
|
1248
1036
|
console.log("");
|
|
@@ -1268,9 +1056,9 @@ var validateCommand = new Command8("validate").description("Validate ObjectStack
|
|
|
1268
1056
|
|
|
1269
1057
|
// src/commands/init.ts
|
|
1270
1058
|
import { Command as Command9 } from "commander";
|
|
1271
|
-
import
|
|
1272
|
-
import
|
|
1273
|
-
import
|
|
1059
|
+
import chalk8 from "chalk";
|
|
1060
|
+
import fs8 from "fs";
|
|
1061
|
+
import path8 from "path";
|
|
1274
1062
|
var TEMPLATES = {
|
|
1275
1063
|
app: {
|
|
1276
1064
|
description: "Full application with objects, views, and actions",
|
|
@@ -1435,16 +1223,16 @@ function toTitleCase(str) {
|
|
|
1435
1223
|
var initCommand = new Command9("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) => {
|
|
1436
1224
|
printHeader("Init");
|
|
1437
1225
|
const cwd = process.cwd();
|
|
1438
|
-
const projectName = name ||
|
|
1226
|
+
const projectName = name || path8.basename(cwd);
|
|
1439
1227
|
const template = TEMPLATES[options.template];
|
|
1440
1228
|
if (!template) {
|
|
1441
1229
|
printError(`Unknown template: ${options.template}`);
|
|
1442
|
-
console.log(
|
|
1230
|
+
console.log(chalk8.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
|
|
1443
1231
|
process.exit(1);
|
|
1444
1232
|
}
|
|
1445
|
-
if (
|
|
1233
|
+
if (fs8.existsSync(path8.join(cwd, "objectstack.config.ts"))) {
|
|
1446
1234
|
printError("objectstack.config.ts already exists in this directory");
|
|
1447
|
-
console.log(
|
|
1235
|
+
console.log(chalk8.dim(" Use `objectstack generate` to add metadata to an existing project"));
|
|
1448
1236
|
process.exit(1);
|
|
1449
1237
|
}
|
|
1450
1238
|
printKV("Project", projectName);
|
|
@@ -1453,8 +1241,8 @@ var initCommand = new Command9("init").description("Initialize a new ObjectStack
|
|
|
1453
1241
|
console.log("");
|
|
1454
1242
|
const createdFiles = [];
|
|
1455
1243
|
try {
|
|
1456
|
-
const pkgPath =
|
|
1457
|
-
if (!
|
|
1244
|
+
const pkgPath = path8.join(cwd, "package.json");
|
|
1245
|
+
if (!fs8.existsSync(pkgPath)) {
|
|
1458
1246
|
const pkg2 = {
|
|
1459
1247
|
name: projectName,
|
|
1460
1248
|
version: "0.1.0",
|
|
@@ -1464,16 +1252,16 @@ var initCommand = new Command9("init").description("Initialize a new ObjectStack
|
|
|
1464
1252
|
dependencies: template.dependencies,
|
|
1465
1253
|
devDependencies: template.devDependencies
|
|
1466
1254
|
};
|
|
1467
|
-
|
|
1255
|
+
fs8.writeFileSync(pkgPath, JSON.stringify(pkg2, null, 2) + "\n");
|
|
1468
1256
|
createdFiles.push("package.json");
|
|
1469
1257
|
} else {
|
|
1470
1258
|
printInfo("package.json already exists, skipping");
|
|
1471
1259
|
}
|
|
1472
1260
|
const configContent = template.configContent(projectName);
|
|
1473
|
-
|
|
1261
|
+
fs8.writeFileSync(path8.join(cwd, "objectstack.config.ts"), configContent);
|
|
1474
1262
|
createdFiles.push("objectstack.config.ts");
|
|
1475
|
-
const tsconfigPath =
|
|
1476
|
-
if (!
|
|
1263
|
+
const tsconfigPath = path8.join(cwd, "tsconfig.json");
|
|
1264
|
+
if (!fs8.existsSync(tsconfigPath)) {
|
|
1477
1265
|
const tsconfig = {
|
|
1478
1266
|
compilerOptions: {
|
|
1479
1267
|
target: "ES2022",
|
|
@@ -1489,30 +1277,30 @@ var initCommand = new Command9("init").description("Initialize a new ObjectStack
|
|
|
1489
1277
|
include: ["*.ts", "src/**/*"],
|
|
1490
1278
|
exclude: ["dist", "node_modules"]
|
|
1491
1279
|
};
|
|
1492
|
-
|
|
1280
|
+
fs8.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
|
|
1493
1281
|
createdFiles.push("tsconfig.json");
|
|
1494
1282
|
}
|
|
1495
1283
|
for (const [filePath, contentFn] of Object.entries(template.srcFiles)) {
|
|
1496
1284
|
const resolvedPath = filePath.replace("__name__", projectName);
|
|
1497
|
-
const fullPath =
|
|
1498
|
-
const dir =
|
|
1499
|
-
if (!
|
|
1500
|
-
|
|
1285
|
+
const fullPath = path8.join(cwd, resolvedPath);
|
|
1286
|
+
const dir = path8.dirname(fullPath);
|
|
1287
|
+
if (!fs8.existsSync(dir)) {
|
|
1288
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
1501
1289
|
}
|
|
1502
|
-
|
|
1290
|
+
fs8.writeFileSync(fullPath, contentFn(projectName));
|
|
1503
1291
|
createdFiles.push(resolvedPath);
|
|
1504
1292
|
}
|
|
1505
|
-
const gitignorePath =
|
|
1506
|
-
if (!
|
|
1507
|
-
|
|
1293
|
+
const gitignorePath = path8.join(cwd, ".gitignore");
|
|
1294
|
+
if (!fs8.existsSync(gitignorePath)) {
|
|
1295
|
+
fs8.writeFileSync(gitignorePath, `node_modules/
|
|
1508
1296
|
dist/
|
|
1509
1297
|
*.tsbuildinfo
|
|
1510
1298
|
`);
|
|
1511
1299
|
createdFiles.push(".gitignore");
|
|
1512
1300
|
}
|
|
1513
|
-
console.log(
|
|
1301
|
+
console.log(chalk8.bold(" Created files:"));
|
|
1514
1302
|
for (const f of createdFiles) {
|
|
1515
|
-
console.log(
|
|
1303
|
+
console.log(chalk8.green(` + ${f}`));
|
|
1516
1304
|
}
|
|
1517
1305
|
console.log("");
|
|
1518
1306
|
if (options.install !== false) {
|
|
@@ -1526,10 +1314,10 @@ dist/
|
|
|
1526
1314
|
}
|
|
1527
1315
|
printSuccess("Project initialized!");
|
|
1528
1316
|
console.log("");
|
|
1529
|
-
console.log(
|
|
1530
|
-
console.log(
|
|
1531
|
-
console.log(
|
|
1532
|
-
console.log(
|
|
1317
|
+
console.log(chalk8.bold(" Next steps:"));
|
|
1318
|
+
console.log(chalk8.dim(" objectstack validate # Check configuration"));
|
|
1319
|
+
console.log(chalk8.dim(" objectstack dev # Start development server"));
|
|
1320
|
+
console.log(chalk8.dim(" objectstack generate # Add objects, views, etc."));
|
|
1533
1321
|
console.log("");
|
|
1534
1322
|
} catch (error) {
|
|
1535
1323
|
printError(error.message || String(error));
|
|
@@ -1537,12 +1325,12 @@ dist/
|
|
|
1537
1325
|
}
|
|
1538
1326
|
});
|
|
1539
1327
|
function printWarning2(msg) {
|
|
1540
|
-
console.log(
|
|
1328
|
+
console.log(chalk8.yellow(` \u26A0 ${msg}`));
|
|
1541
1329
|
}
|
|
1542
1330
|
|
|
1543
1331
|
// src/commands/info.ts
|
|
1544
1332
|
import { Command as Command10 } from "commander";
|
|
1545
|
-
import
|
|
1333
|
+
import chalk9 from "chalk";
|
|
1546
1334
|
var infoCommand = new Command10("info").description("Display metadata summary of an ObjectStack configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configPath, options) => {
|
|
1547
1335
|
const timer = createTimer();
|
|
1548
1336
|
if (!options.json) {
|
|
@@ -1568,9 +1356,9 @@ var infoCommand = new Command10("info").description("Display metadata summary of
|
|
|
1568
1356
|
if (config.manifest) {
|
|
1569
1357
|
const m = config.manifest;
|
|
1570
1358
|
console.log("");
|
|
1571
|
-
console.log(` ${
|
|
1572
|
-
if (m.id) console.log(
|
|
1573
|
-
if (m.description) console.log(
|
|
1359
|
+
console.log(` ${chalk9.bold(m.name || m.id || "Unnamed")} ${chalk9.dim(`v${m.version || "0.0.0"}`)}`);
|
|
1360
|
+
if (m.id) console.log(chalk9.dim(` ${m.id}`));
|
|
1361
|
+
if (m.description) console.log(chalk9.dim(` ${m.description}`));
|
|
1574
1362
|
if (m.namespace) printKV(" Namespace", m.namespace);
|
|
1575
1363
|
if (m.type) printKV(" Type", m.type);
|
|
1576
1364
|
}
|
|
@@ -1578,35 +1366,35 @@ var infoCommand = new Command10("info").description("Display metadata summary of
|
|
|
1578
1366
|
printMetadataStats(stats);
|
|
1579
1367
|
if (config.objects && config.objects.length > 0) {
|
|
1580
1368
|
console.log("");
|
|
1581
|
-
console.log(
|
|
1369
|
+
console.log(chalk9.bold(" Objects:"));
|
|
1582
1370
|
for (const obj of config.objects) {
|
|
1583
1371
|
const fieldCount = obj.fields ? Object.keys(obj.fields).length : 0;
|
|
1584
1372
|
const ownership = obj.ownership || "own";
|
|
1585
1373
|
console.log(
|
|
1586
|
-
` ${
|
|
1374
|
+
` ${chalk9.cyan(obj.name || "?")}` + chalk9.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk9.dim(` \u2014 ${obj.label}`) : "")
|
|
1587
1375
|
);
|
|
1588
1376
|
}
|
|
1589
1377
|
}
|
|
1590
1378
|
if (config.agents && config.agents.length > 0) {
|
|
1591
1379
|
console.log("");
|
|
1592
|
-
console.log(
|
|
1380
|
+
console.log(chalk9.bold(" Agents:"));
|
|
1593
1381
|
for (const agent of config.agents) {
|
|
1594
1382
|
console.log(
|
|
1595
|
-
` ${
|
|
1383
|
+
` ${chalk9.magenta(agent.name || "?")}` + (agent.role ? chalk9.dim(` \u2014 ${agent.role}`) : "")
|
|
1596
1384
|
);
|
|
1597
1385
|
}
|
|
1598
1386
|
}
|
|
1599
1387
|
if (config.apps && config.apps.length > 0) {
|
|
1600
1388
|
console.log("");
|
|
1601
|
-
console.log(
|
|
1389
|
+
console.log(chalk9.bold(" Apps:"));
|
|
1602
1390
|
for (const app of config.apps) {
|
|
1603
1391
|
console.log(
|
|
1604
|
-
` ${
|
|
1392
|
+
` ${chalk9.green(app.name || "?")}` + (app.label ? chalk9.dim(` \u2014 ${app.label}`) : "")
|
|
1605
1393
|
);
|
|
1606
1394
|
}
|
|
1607
1395
|
}
|
|
1608
1396
|
console.log("");
|
|
1609
|
-
console.log(
|
|
1397
|
+
console.log(chalk9.dim(` Loaded in ${duration}ms`));
|
|
1610
1398
|
console.log("");
|
|
1611
1399
|
} catch (error) {
|
|
1612
1400
|
if (options.json) {
|
|
@@ -1621,9 +1409,9 @@ var infoCommand = new Command10("info").description("Display metadata summary of
|
|
|
1621
1409
|
|
|
1622
1410
|
// src/commands/generate.ts
|
|
1623
1411
|
import { Command as Command11 } from "commander";
|
|
1624
|
-
import
|
|
1625
|
-
import
|
|
1626
|
-
import
|
|
1412
|
+
import chalk10 from "chalk";
|
|
1413
|
+
import fs9 from "fs";
|
|
1414
|
+
import path9 from "path";
|
|
1627
1415
|
var GENERATORS = {
|
|
1628
1416
|
object: {
|
|
1629
1417
|
description: "Business data object",
|
|
@@ -1804,81 +1592,217 @@ function toTitleCase2(str) {
|
|
|
1804
1592
|
function toSnakeCase(str) {
|
|
1805
1593
|
return str.replace(/[-]/g, "_").replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`).replace(/^_/, "");
|
|
1806
1594
|
}
|
|
1807
|
-
var
|
|
1595
|
+
var FIELD_TYPE_MAP = {
|
|
1596
|
+
text: "string",
|
|
1597
|
+
textarea: "string",
|
|
1598
|
+
richtext: "string",
|
|
1599
|
+
html: "string",
|
|
1600
|
+
markdown: "string",
|
|
1601
|
+
number: "number",
|
|
1602
|
+
integer: "number",
|
|
1603
|
+
currency: "number",
|
|
1604
|
+
percent: "number",
|
|
1605
|
+
boolean: "boolean",
|
|
1606
|
+
date: "string",
|
|
1607
|
+
datetime: "string",
|
|
1608
|
+
time: "string",
|
|
1609
|
+
email: "string",
|
|
1610
|
+
phone: "string",
|
|
1611
|
+
url: "string",
|
|
1612
|
+
select: "string",
|
|
1613
|
+
multiselect: "string[]",
|
|
1614
|
+
lookup: "string",
|
|
1615
|
+
master_detail: "string",
|
|
1616
|
+
formula: "unknown",
|
|
1617
|
+
autonumber: "string",
|
|
1618
|
+
json: "Record<string, unknown>",
|
|
1619
|
+
file: "string",
|
|
1620
|
+
image: "string",
|
|
1621
|
+
password: "string",
|
|
1622
|
+
slug: "string",
|
|
1623
|
+
uuid: "string",
|
|
1624
|
+
ip_address: "string",
|
|
1625
|
+
color: "string",
|
|
1626
|
+
rating: "number",
|
|
1627
|
+
geo_point: "{ lat: number; lng: number }",
|
|
1628
|
+
vector: "number[]",
|
|
1629
|
+
encrypted: "string"
|
|
1630
|
+
};
|
|
1631
|
+
function fieldTypeToTs(fieldType, multiple) {
|
|
1632
|
+
const base = FIELD_TYPE_MAP[fieldType] || "unknown";
|
|
1633
|
+
return multiple ? `${base}[]` : base;
|
|
1634
|
+
}
|
|
1635
|
+
function generateTypesFromConfig(config) {
|
|
1636
|
+
const lines = [
|
|
1637
|
+
"// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
|
|
1638
|
+
`// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
1639
|
+
"",
|
|
1640
|
+
"import type { Data } from '@objectstack/spec';",
|
|
1641
|
+
""
|
|
1642
|
+
];
|
|
1643
|
+
const objects = [];
|
|
1644
|
+
const rawObjects = config.objects ?? config.data?.objects ?? {};
|
|
1645
|
+
if (Array.isArray(rawObjects)) {
|
|
1646
|
+
objects.push(...rawObjects);
|
|
1647
|
+
} else if (typeof rawObjects === "object") {
|
|
1648
|
+
for (const val of Object.values(rawObjects)) {
|
|
1649
|
+
if (val && typeof val === "object") objects.push(val);
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
if (objects.length === 0) {
|
|
1653
|
+
lines.push("// No objects found in configuration");
|
|
1654
|
+
return lines.join("\n") + "\n";
|
|
1655
|
+
}
|
|
1656
|
+
for (const obj of objects) {
|
|
1657
|
+
const name = String(obj.name || "unknown");
|
|
1658
|
+
const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
1659
|
+
const fields = obj.fields ?? {};
|
|
1660
|
+
lines.push(`/** ${String(obj.label || typeName)} record type */`);
|
|
1661
|
+
lines.push(`export interface ${typeName}Record {`);
|
|
1662
|
+
lines.push(" id: string;");
|
|
1663
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
1664
|
+
const fType = String(fieldDef.type || "text");
|
|
1665
|
+
const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
|
|
1666
|
+
const required = fieldDef.required ? "" : "?";
|
|
1667
|
+
if (fieldDef.label) {
|
|
1668
|
+
lines.push(` /** ${fieldDef.label} */`);
|
|
1669
|
+
}
|
|
1670
|
+
lines.push(` ${fieldName}${required}: ${tsType};`);
|
|
1671
|
+
}
|
|
1672
|
+
lines.push("}");
|
|
1673
|
+
lines.push("");
|
|
1674
|
+
}
|
|
1675
|
+
return lines.join("\n") + "\n";
|
|
1676
|
+
}
|
|
1677
|
+
var generateMetadataCommand = new Command11("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) => {
|
|
1808
1678
|
printHeader("Generate");
|
|
1809
1679
|
const generator = GENERATORS[type];
|
|
1810
1680
|
if (!generator) {
|
|
1811
1681
|
printError(`Unknown type: ${type}`);
|
|
1812
1682
|
console.log("");
|
|
1813
|
-
console.log(
|
|
1683
|
+
console.log(chalk10.bold(" Available types:"));
|
|
1814
1684
|
for (const [key, gen] of Object.entries(GENERATORS)) {
|
|
1815
|
-
console.log(` ${
|
|
1685
|
+
console.log(` ${chalk10.cyan(key.padEnd(12))} ${chalk10.dim(gen.description)}`);
|
|
1816
1686
|
}
|
|
1817
1687
|
console.log("");
|
|
1818
|
-
console.log(
|
|
1819
|
-
console.log(
|
|
1820
|
-
console.log(
|
|
1688
|
+
console.log(chalk10.dim(" Usage: objectstack generate <type> <name>"));
|
|
1689
|
+
console.log(chalk10.dim(" Example: objectstack generate object project"));
|
|
1690
|
+
console.log(chalk10.dim(" Alias: os g object project"));
|
|
1821
1691
|
process.exit(1);
|
|
1822
1692
|
}
|
|
1823
1693
|
const dir = options.dir || generator.defaultDir;
|
|
1824
1694
|
const fileName = `${toSnakeCase(name)}.ts`;
|
|
1825
|
-
const filePath =
|
|
1826
|
-
console.log(` ${
|
|
1827
|
-
console.log(` ${
|
|
1828
|
-
console.log(` ${
|
|
1695
|
+
const filePath = path9.join(process.cwd(), dir, fileName);
|
|
1696
|
+
console.log(` ${chalk10.dim("Type:")} ${chalk10.cyan(type)} \u2014 ${generator.description}`);
|
|
1697
|
+
console.log(` ${chalk10.dim("Name:")} ${chalk10.white(name)}`);
|
|
1698
|
+
console.log(` ${chalk10.dim("File:")} ${chalk10.white(path9.join(dir, fileName))}`);
|
|
1829
1699
|
console.log("");
|
|
1830
1700
|
if (options.dryRun) {
|
|
1831
1701
|
printInfo("Dry run \u2014 no files written");
|
|
1832
1702
|
console.log("");
|
|
1833
|
-
console.log(
|
|
1834
|
-
console.log(
|
|
1703
|
+
console.log(chalk10.dim(" Content:"));
|
|
1704
|
+
console.log(chalk10.dim(" " + "-".repeat(38)));
|
|
1835
1705
|
const content = generator.generate(name);
|
|
1836
1706
|
for (const line of content.split("\n")) {
|
|
1837
|
-
console.log(
|
|
1707
|
+
console.log(chalk10.dim(` ${line}`));
|
|
1838
1708
|
}
|
|
1839
1709
|
console.log("");
|
|
1840
1710
|
return;
|
|
1841
1711
|
}
|
|
1842
|
-
if (
|
|
1712
|
+
if (fs9.existsSync(filePath)) {
|
|
1843
1713
|
printError(`File already exists: ${filePath}`);
|
|
1844
1714
|
process.exit(1);
|
|
1845
1715
|
}
|
|
1846
1716
|
try {
|
|
1847
|
-
const fullDir =
|
|
1848
|
-
if (!
|
|
1849
|
-
|
|
1717
|
+
const fullDir = path9.dirname(filePath);
|
|
1718
|
+
if (!fs9.existsSync(fullDir)) {
|
|
1719
|
+
fs9.mkdirSync(fullDir, { recursive: true });
|
|
1850
1720
|
}
|
|
1851
1721
|
const content = generator.generate(name);
|
|
1852
|
-
|
|
1853
|
-
printSuccess(`Created ${
|
|
1854
|
-
const indexPath =
|
|
1855
|
-
if (
|
|
1856
|
-
const indexContent =
|
|
1722
|
+
fs9.writeFileSync(filePath, content);
|
|
1723
|
+
printSuccess(`Created ${path9.join(dir, fileName)}`);
|
|
1724
|
+
const indexPath = path9.join(process.cwd(), dir, "index.ts");
|
|
1725
|
+
if (fs9.existsSync(indexPath)) {
|
|
1726
|
+
const indexContent = fs9.readFileSync(indexPath, "utf-8");
|
|
1857
1727
|
const exportLine = `export { default as ${toCamelCase3(name)} } from './${toSnakeCase(name)}';`;
|
|
1858
1728
|
if (!indexContent.includes(toCamelCase3(name))) {
|
|
1859
|
-
|
|
1729
|
+
fs9.appendFileSync(indexPath, exportLine + "\n");
|
|
1860
1730
|
printSuccess(`Updated ${dir}/index.ts with export`);
|
|
1861
1731
|
}
|
|
1862
1732
|
} else {
|
|
1863
1733
|
const exportLine = `export { default as ${toCamelCase3(name)} } from './${toSnakeCase(name)}';
|
|
1864
1734
|
`;
|
|
1865
|
-
|
|
1735
|
+
fs9.writeFileSync(indexPath, exportLine);
|
|
1866
1736
|
printSuccess(`Created ${dir}/index.ts`);
|
|
1867
1737
|
}
|
|
1868
1738
|
console.log("");
|
|
1869
|
-
console.log(
|
|
1739
|
+
console.log(chalk10.dim(` Tip: Run \`objectstack validate\` to check your config`));
|
|
1740
|
+
console.log("");
|
|
1741
|
+
} catch (error) {
|
|
1742
|
+
printError(error.message || String(error));
|
|
1743
|
+
process.exit(1);
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
var generateTypesCommand = new Command11("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) => {
|
|
1747
|
+
printHeader("Generate Types");
|
|
1748
|
+
try {
|
|
1749
|
+
const { loadConfig: loadConfig2 } = await import("./config-A7BN6UIT.js");
|
|
1750
|
+
printInfo("Loading configuration...");
|
|
1751
|
+
const { config, absolutePath } = await loadConfig2(configPath);
|
|
1752
|
+
console.log(` ${chalk10.dim("Config:")} ${chalk10.white(absolutePath)}`);
|
|
1753
|
+
console.log(` ${chalk10.dim("Output:")} ${chalk10.white(options.output)}`);
|
|
1754
|
+
console.log("");
|
|
1755
|
+
const content = generateTypesFromConfig(config);
|
|
1756
|
+
if (options.dryRun) {
|
|
1757
|
+
printInfo("Dry run \u2014 no files written");
|
|
1758
|
+
console.log("");
|
|
1759
|
+
for (const line of content.split("\n")) {
|
|
1760
|
+
console.log(chalk10.dim(` ${line}`));
|
|
1761
|
+
}
|
|
1762
|
+
console.log("");
|
|
1763
|
+
return;
|
|
1764
|
+
}
|
|
1765
|
+
const outPath = path9.resolve(process.cwd(), options.output);
|
|
1766
|
+
const outDir = path9.dirname(outPath);
|
|
1767
|
+
if (!fs9.existsSync(outDir)) {
|
|
1768
|
+
fs9.mkdirSync(outDir, { recursive: true });
|
|
1769
|
+
}
|
|
1770
|
+
fs9.writeFileSync(outPath, content);
|
|
1771
|
+
printSuccess(`Generated types at ${options.output}`);
|
|
1870
1772
|
console.log("");
|
|
1871
1773
|
} catch (error) {
|
|
1872
1774
|
printError(error.message || String(error));
|
|
1873
1775
|
process.exit(1);
|
|
1874
1776
|
}
|
|
1875
1777
|
});
|
|
1778
|
+
var generateCommand = new Command11("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).action(async (type, name, options) => {
|
|
1779
|
+
if (!type) {
|
|
1780
|
+
printHeader("Generate");
|
|
1781
|
+
console.log(chalk10.bold(" Sub-commands:"));
|
|
1782
|
+
console.log(` ${chalk10.cyan("types".padEnd(12))} Generate TypeScript type definitions from config`);
|
|
1783
|
+
console.log("");
|
|
1784
|
+
console.log(chalk10.bold(" Metadata types:"));
|
|
1785
|
+
for (const [key, gen] of Object.entries(GENERATORS)) {
|
|
1786
|
+
console.log(` ${chalk10.cyan(key.padEnd(12))} ${chalk10.dim(gen.description)}`);
|
|
1787
|
+
}
|
|
1788
|
+
console.log("");
|
|
1789
|
+
console.log(chalk10.dim(" Usage: objectstack generate <type> <name>"));
|
|
1790
|
+
console.log(chalk10.dim(" Usage: objectstack generate types [config]"));
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
if (!name) {
|
|
1794
|
+
printError("Missing required argument: <name>");
|
|
1795
|
+
console.log(chalk10.dim(" Usage: objectstack generate <type> <name>"));
|
|
1796
|
+
process.exit(1);
|
|
1797
|
+
}
|
|
1798
|
+
await generateMetadataCommand.parseAsync([type, name, ...process.argv.slice(4)], { from: "user" });
|
|
1799
|
+
});
|
|
1876
1800
|
|
|
1877
1801
|
// src/commands/plugin.ts
|
|
1878
1802
|
import { Command as Command12 } from "commander";
|
|
1879
|
-
import
|
|
1880
|
-
import
|
|
1881
|
-
import
|
|
1803
|
+
import chalk11 from "chalk";
|
|
1804
|
+
import fs10 from "fs";
|
|
1805
|
+
import path10 from "path";
|
|
1882
1806
|
function resolvePluginName(plugin) {
|
|
1883
1807
|
if (typeof plugin === "string") return plugin;
|
|
1884
1808
|
if (plugin && typeof plugin === "object") {
|
|
@@ -1903,7 +1827,7 @@ function resolvePluginType(plugin) {
|
|
|
1903
1827
|
return "standard";
|
|
1904
1828
|
}
|
|
1905
1829
|
function readConfigText(configPath) {
|
|
1906
|
-
return
|
|
1830
|
+
return fs10.readFileSync(configPath, "utf-8");
|
|
1907
1831
|
}
|
|
1908
1832
|
function addPluginToConfig(configPath, packageName) {
|
|
1909
1833
|
let content = readConfigText(configPath);
|
|
@@ -1945,7 +1869,7 @@ function addPluginToConfig(configPath, packageName) {
|
|
|
1945
1869
|
$2`
|
|
1946
1870
|
);
|
|
1947
1871
|
}
|
|
1948
|
-
|
|
1872
|
+
fs10.writeFileSync(configPath, content);
|
|
1949
1873
|
}
|
|
1950
1874
|
function removePluginFromConfig(configPath, pluginName) {
|
|
1951
1875
|
let content = readConfigText(configPath);
|
|
@@ -1967,7 +1891,7 @@ function removePluginFromConfig(configPath, pluginName) {
|
|
|
1967
1891
|
content = content.replace(pattern, "\n");
|
|
1968
1892
|
}
|
|
1969
1893
|
content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
|
|
1970
|
-
|
|
1894
|
+
fs10.writeFileSync(configPath, content);
|
|
1971
1895
|
}
|
|
1972
1896
|
function escapeRegex(str) {
|
|
1973
1897
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -1999,31 +1923,31 @@ var listCommand = new Command12("list").alias("ls").description("List plugins de
|
|
|
1999
1923
|
if (plugins.length === 0 && devPlugins.length === 0) {
|
|
2000
1924
|
printInfo("No plugins configured");
|
|
2001
1925
|
console.log("");
|
|
2002
|
-
console.log(
|
|
2003
|
-
console.log(
|
|
1926
|
+
console.log(chalk11.dim(" Hint: Add plugins to your objectstack.config.ts"));
|
|
1927
|
+
console.log(chalk11.dim(" Or run: os plugin add <package-name>"));
|
|
2004
1928
|
console.log("");
|
|
2005
1929
|
return;
|
|
2006
1930
|
}
|
|
2007
1931
|
if (plugins.length > 0) {
|
|
2008
|
-
console.log(
|
|
1932
|
+
console.log(chalk11.bold(`
|
|
2009
1933
|
Plugins (${plugins.length}):`));
|
|
2010
1934
|
for (const plugin of plugins) {
|
|
2011
1935
|
const name = resolvePluginName(plugin);
|
|
2012
1936
|
const version = resolvePluginVersion(plugin);
|
|
2013
1937
|
const type = resolvePluginType(plugin);
|
|
2014
1938
|
console.log(
|
|
2015
|
-
` ${
|
|
1939
|
+
` ${chalk11.cyan("\u25CF")} ${chalk11.white(name)}` + (version !== "-" ? chalk11.dim(` v${version}`) : "") + (type !== "standard" ? chalk11.dim(` [${type}]`) : "")
|
|
2016
1940
|
);
|
|
2017
1941
|
}
|
|
2018
1942
|
}
|
|
2019
1943
|
if (devPlugins.length > 0) {
|
|
2020
|
-
console.log(
|
|
1944
|
+
console.log(chalk11.bold(`
|
|
2021
1945
|
Dev Plugins (${devPlugins.length}):`));
|
|
2022
1946
|
for (const plugin of devPlugins) {
|
|
2023
1947
|
const name = resolvePluginName(plugin);
|
|
2024
1948
|
const version = resolvePluginVersion(plugin);
|
|
2025
1949
|
console.log(
|
|
2026
|
-
` ${
|
|
1950
|
+
` ${chalk11.yellow("\u25CF")} ${chalk11.white(name)}` + (version !== "-" ? chalk11.dim(` v${version}`) : "") + chalk11.dim(" [dev]")
|
|
2027
1951
|
);
|
|
2028
1952
|
}
|
|
2029
1953
|
}
|
|
@@ -2047,9 +1971,9 @@ var infoSubCommand = new Command12("info").description("Show detailed informatio
|
|
|
2047
1971
|
if (!found) {
|
|
2048
1972
|
printError(`Plugin '${name}' not found in configuration`);
|
|
2049
1973
|
console.log("");
|
|
2050
|
-
console.log(
|
|
1974
|
+
console.log(chalk11.dim(" Available plugins:"));
|
|
2051
1975
|
for (const p of allPlugins) {
|
|
2052
|
-
console.log(
|
|
1976
|
+
console.log(chalk11.dim(` - ${resolvePluginName(p)}`));
|
|
2053
1977
|
}
|
|
2054
1978
|
console.log("");
|
|
2055
1979
|
process.exit(1);
|
|
@@ -2085,15 +2009,15 @@ var addCommand = new Command12("add").description("Add a plugin to objectstack.c
|
|
|
2085
2009
|
try {
|
|
2086
2010
|
const configPath = resolveConfigPath(options?.config);
|
|
2087
2011
|
printHeader("Add Plugin");
|
|
2088
|
-
console.log(` ${
|
|
2089
|
-
console.log(` ${
|
|
2012
|
+
console.log(` ${chalk11.dim("Package:")} ${chalk11.white(packageName)}`);
|
|
2013
|
+
console.log(` ${chalk11.dim("Config:")} ${chalk11.white(path10.relative(process.cwd(), configPath))}`);
|
|
2090
2014
|
console.log("");
|
|
2091
2015
|
addPluginToConfig(configPath, packageName);
|
|
2092
|
-
printSuccess(`Added ${
|
|
2016
|
+
printSuccess(`Added ${chalk11.cyan(packageName)} to config`);
|
|
2093
2017
|
console.log("");
|
|
2094
|
-
console.log(
|
|
2095
|
-
console.log(
|
|
2096
|
-
console.log(
|
|
2018
|
+
console.log(chalk11.dim(" Next steps:"));
|
|
2019
|
+
console.log(chalk11.dim(` 1. Install the package: pnpm add ${packageName}`));
|
|
2020
|
+
console.log(chalk11.dim(" 2. Run: os validate"));
|
|
2097
2021
|
console.log("");
|
|
2098
2022
|
} catch (error) {
|
|
2099
2023
|
printError(error.message || String(error));
|
|
@@ -2104,13 +2028,13 @@ var removeCommand = new Command12("remove").alias("rm").description("Remove a pl
|
|
|
2104
2028
|
try {
|
|
2105
2029
|
const configPath = resolveConfigPath(options?.config);
|
|
2106
2030
|
printHeader("Remove Plugin");
|
|
2107
|
-
console.log(` ${
|
|
2108
|
-
console.log(` ${
|
|
2031
|
+
console.log(` ${chalk11.dim("Plugin:")} ${chalk11.white(pluginName)}`);
|
|
2032
|
+
console.log(` ${chalk11.dim("Config:")} ${chalk11.white(path10.relative(process.cwd(), configPath))}`);
|
|
2109
2033
|
console.log("");
|
|
2110
2034
|
removePluginFromConfig(configPath, pluginName);
|
|
2111
|
-
printSuccess(`Removed ${
|
|
2035
|
+
printSuccess(`Removed ${chalk11.cyan(pluginName)} from config`);
|
|
2112
2036
|
console.log("");
|
|
2113
|
-
console.log(
|
|
2037
|
+
console.log(chalk11.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
|
|
2114
2038
|
console.log("");
|
|
2115
2039
|
} catch (error) {
|
|
2116
2040
|
printError(error.message || String(error));
|
|
@@ -2120,7 +2044,7 @@ var removeCommand = new Command12("remove").alias("rm").description("Remove a pl
|
|
|
2120
2044
|
var pluginCommand = new Command12("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
|
|
2121
2045
|
|
|
2122
2046
|
// src/utils/plugin-commands.ts
|
|
2123
|
-
import
|
|
2047
|
+
import chalk12 from "chalk";
|
|
2124
2048
|
async function loadPluginCommands(program2) {
|
|
2125
2049
|
let config;
|
|
2126
2050
|
try {
|
|
@@ -2164,7 +2088,7 @@ async function loadPluginCommands(program2) {
|
|
|
2164
2088
|
if (process.env.DEBUG) {
|
|
2165
2089
|
const message = error instanceof Error ? error.message : String(error);
|
|
2166
2090
|
console.error(
|
|
2167
|
-
|
|
2091
|
+
chalk12.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
|
|
2168
2092
|
);
|
|
2169
2093
|
}
|
|
2170
2094
|
}
|
|
@@ -2210,10 +2134,10 @@ function resolvePluginName2(plugin) {
|
|
|
2210
2134
|
var require2 = createRequire2(import.meta.url);
|
|
2211
2135
|
var pkg = require2("../package.json");
|
|
2212
2136
|
process.on("unhandledRejection", (err) => {
|
|
2213
|
-
console.error(
|
|
2137
|
+
console.error(chalk13.red(`
|
|
2214
2138
|
\u2717 Unhandled error: ${err?.message || err}`));
|
|
2215
2139
|
if (err?.stack && process.env.DEBUG) {
|
|
2216
|
-
console.error(
|
|
2140
|
+
console.error(chalk13.dim(err.stack));
|
|
2217
2141
|
}
|
|
2218
2142
|
process.exit(1);
|
|
2219
2143
|
});
|
|
@@ -2221,19 +2145,19 @@ var program = new Command13();
|
|
|
2221
2145
|
program.name("objectstack").description("ObjectStack CLI \u2014 Build metadata-driven apps with the ObjectStack Protocol").version(pkg.version, "-v, --version").configureHelp({
|
|
2222
2146
|
sortSubcommands: false
|
|
2223
2147
|
}).addHelpText("before", `
|
|
2224
|
-
${
|
|
2148
|
+
${chalk13.bold.cyan("\u25C6 ObjectStack CLI")} ${chalk13.dim(`v${pkg.version}`)}
|
|
2225
2149
|
`).addHelpText("after", `
|
|
2226
|
-
${
|
|
2227
|
-
${
|
|
2228
|
-
${
|
|
2229
|
-
${
|
|
2230
|
-
${
|
|
2231
|
-
${
|
|
2232
|
-
${
|
|
2233
|
-
${
|
|
2234
|
-
|
|
2235
|
-
${
|
|
2236
|
-
${
|
|
2150
|
+
${chalk13.bold("Workflow:")}
|
|
2151
|
+
${chalk13.dim("$")} os init ${chalk13.dim("# Create a new project")}
|
|
2152
|
+
${chalk13.dim("$")} os generate object task ${chalk13.dim("# Add metadata")}
|
|
2153
|
+
${chalk13.dim("$")} os plugin add <package> ${chalk13.dim("# Add a plugin")}
|
|
2154
|
+
${chalk13.dim("$")} os validate ${chalk13.dim("# Check configuration")}
|
|
2155
|
+
${chalk13.dim("$")} os dev ${chalk13.dim("# Start dev server")}
|
|
2156
|
+
${chalk13.dim("$")} os studio ${chalk13.dim("# Dev server + Studio UI")}
|
|
2157
|
+
${chalk13.dim("$")} os compile ${chalk13.dim("# Build for production")}
|
|
2158
|
+
|
|
2159
|
+
${chalk13.dim("Aliases: objectstack | os")}
|
|
2160
|
+
${chalk13.dim("Docs: https://objectstack.dev")}
|
|
2237
2161
|
`);
|
|
2238
2162
|
program.addCommand(initCommand);
|
|
2239
2163
|
program.addCommand(devCommand);
|
|
@@ -2251,7 +2175,7 @@ loadPluginCommands(program).then(() => {
|
|
|
2251
2175
|
program.parse(process.argv);
|
|
2252
2176
|
}).catch((err) => {
|
|
2253
2177
|
if (process.env.DEBUG) {
|
|
2254
|
-
console.error(
|
|
2178
|
+
console.error(chalk13.yellow(`
|
|
2255
2179
|
\u26A0 Plugin command loading failed: ${err?.message || err}`));
|
|
2256
2180
|
}
|
|
2257
2181
|
program.parse(process.argv);
|