@objectstack/cli 2.0.6 → 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 +30 -0
- package/README.md +88 -3
- package/dist/bin.js +691 -425
- 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.d.ts +14 -1
- package/dist/index.js +641 -382
- package/package.json +9 -9
- package/src/bin.ts +18 -1
- package/src/commands/generate.ts +181 -3
- package/src/commands/plugin.ts +372 -0
- package/src/index.ts +2 -0
- package/src/utils/plugin-commands.ts +163 -0
- package/test/commands.test.ts +6 -0
- package/test/plugin-commands.test.ts +162 -0
- package/test/plugin.test.ts +173 -0
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
|
-
import { Command as
|
|
21
|
+
import { Command as Command13 } from "commander";
|
|
6
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 path11 = issue.path?.join(".") || "";
|
|
73
|
-
const code = issue.code || "";
|
|
74
|
-
const msg = issue.message || "";
|
|
75
|
-
console.log(chalk.red(` \u2717 ${path11}`));
|
|
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,75 +1592,543 @@ 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}`);
|
|
1772
|
+
console.log("");
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
printError(error.message || String(error));
|
|
1775
|
+
process.exit(1);
|
|
1776
|
+
}
|
|
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
|
+
});
|
|
1800
|
+
|
|
1801
|
+
// src/commands/plugin.ts
|
|
1802
|
+
import { Command as Command12 } from "commander";
|
|
1803
|
+
import chalk11 from "chalk";
|
|
1804
|
+
import fs10 from "fs";
|
|
1805
|
+
import path10 from "path";
|
|
1806
|
+
function resolvePluginName(plugin) {
|
|
1807
|
+
if (typeof plugin === "string") return plugin;
|
|
1808
|
+
if (plugin && typeof plugin === "object") {
|
|
1809
|
+
const p = plugin;
|
|
1810
|
+
if (typeof p.name === "string") return p.name;
|
|
1811
|
+
if (p.constructor && p.constructor.name !== "Object") return p.constructor.name;
|
|
1812
|
+
}
|
|
1813
|
+
return "unknown";
|
|
1814
|
+
}
|
|
1815
|
+
function resolvePluginVersion(plugin) {
|
|
1816
|
+
if (plugin && typeof plugin === "object") {
|
|
1817
|
+
const p = plugin;
|
|
1818
|
+
if (typeof p.version === "string") return p.version;
|
|
1819
|
+
}
|
|
1820
|
+
return "-";
|
|
1821
|
+
}
|
|
1822
|
+
function resolvePluginType(plugin) {
|
|
1823
|
+
if (plugin && typeof plugin === "object") {
|
|
1824
|
+
const p = plugin;
|
|
1825
|
+
if (typeof p.type === "string") return p.type;
|
|
1826
|
+
}
|
|
1827
|
+
return "standard";
|
|
1828
|
+
}
|
|
1829
|
+
function readConfigText(configPath) {
|
|
1830
|
+
return fs10.readFileSync(configPath, "utf-8");
|
|
1831
|
+
}
|
|
1832
|
+
function addPluginToConfig(configPath, packageName) {
|
|
1833
|
+
let content = readConfigText(configPath);
|
|
1834
|
+
const shortName = packageName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1835
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
|
|
1836
|
+
const importLine = `import ${varName} from '${packageName}';
|
|
1837
|
+
`;
|
|
1838
|
+
if (content.includes(packageName)) {
|
|
1839
|
+
throw new Error(`Plugin '${packageName}' is already referenced in the config`);
|
|
1840
|
+
}
|
|
1841
|
+
const importRegex = /^import .+$/gm;
|
|
1842
|
+
let lastImportEnd = 0;
|
|
1843
|
+
let match;
|
|
1844
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
1845
|
+
lastImportEnd = match.index + match[0].length;
|
|
1846
|
+
}
|
|
1847
|
+
if (lastImportEnd > 0) {
|
|
1848
|
+
content = content.slice(0, lastImportEnd) + "\n" + importLine + content.slice(lastImportEnd);
|
|
1849
|
+
} else {
|
|
1850
|
+
content = importLine + "\n" + content;
|
|
1851
|
+
}
|
|
1852
|
+
if (/plugins\s*:\s*\[/.test(content)) {
|
|
1853
|
+
let replaced = false;
|
|
1854
|
+
content = content.replace(
|
|
1855
|
+
/(plugins\s*:\s*\[)/,
|
|
1856
|
+
(match2) => {
|
|
1857
|
+
if (replaced) return match2;
|
|
1858
|
+
replaced = true;
|
|
1859
|
+
return `${match2}
|
|
1860
|
+
${varName},`;
|
|
1861
|
+
}
|
|
1862
|
+
);
|
|
1863
|
+
} else {
|
|
1864
|
+
content = content.replace(
|
|
1865
|
+
/(defineStack\(\{[\s\S]*?)(}\s*\))/,
|
|
1866
|
+
`$1 plugins: [
|
|
1867
|
+
${varName},
|
|
1868
|
+
],
|
|
1869
|
+
$2`
|
|
1870
|
+
);
|
|
1871
|
+
}
|
|
1872
|
+
fs10.writeFileSync(configPath, content);
|
|
1873
|
+
}
|
|
1874
|
+
function removePluginFromConfig(configPath, pluginName) {
|
|
1875
|
+
let content = readConfigText(configPath);
|
|
1876
|
+
const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, "gm");
|
|
1877
|
+
const hadImport = importRegex.test(content);
|
|
1878
|
+
importRegex.lastIndex = 0;
|
|
1879
|
+
content = content.replace(importRegex, "");
|
|
1880
|
+
const shortName = pluginName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
1881
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
|
|
1882
|
+
if (!hadImport) {
|
|
1883
|
+
const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, "gm");
|
|
1884
|
+
content = content.replace(varImportRegex, "");
|
|
1885
|
+
}
|
|
1886
|
+
const entryPatterns = [
|
|
1887
|
+
new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, "g"),
|
|
1888
|
+
new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, "g")
|
|
1889
|
+
];
|
|
1890
|
+
for (const pattern of entryPatterns) {
|
|
1891
|
+
content = content.replace(pattern, "\n");
|
|
1892
|
+
}
|
|
1893
|
+
content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
|
|
1894
|
+
fs10.writeFileSync(configPath, content);
|
|
1895
|
+
}
|
|
1896
|
+
function escapeRegex(str) {
|
|
1897
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1898
|
+
}
|
|
1899
|
+
var listCommand = new Command12("list").alias("ls").description("List plugins defined in the configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configSource, options) => {
|
|
1900
|
+
try {
|
|
1901
|
+
const { config } = await loadConfig(configSource);
|
|
1902
|
+
const plugins = config.plugins || [];
|
|
1903
|
+
const devPlugins = config.devPlugins || [];
|
|
1904
|
+
if (options?.json) {
|
|
1905
|
+
const data = {
|
|
1906
|
+
plugins: plugins.map((p) => ({
|
|
1907
|
+
name: resolvePluginName(p),
|
|
1908
|
+
version: resolvePluginVersion(p),
|
|
1909
|
+
type: resolvePluginType(p),
|
|
1910
|
+
dev: false
|
|
1911
|
+
})),
|
|
1912
|
+
devPlugins: devPlugins.map((p) => ({
|
|
1913
|
+
name: resolvePluginName(p),
|
|
1914
|
+
version: resolvePluginVersion(p),
|
|
1915
|
+
type: resolvePluginType(p),
|
|
1916
|
+
dev: true
|
|
1917
|
+
}))
|
|
1918
|
+
};
|
|
1919
|
+
console.log(JSON.stringify(data, null, 2));
|
|
1920
|
+
return;
|
|
1921
|
+
}
|
|
1922
|
+
printHeader("Plugins");
|
|
1923
|
+
if (plugins.length === 0 && devPlugins.length === 0) {
|
|
1924
|
+
printInfo("No plugins configured");
|
|
1925
|
+
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>"));
|
|
1928
|
+
console.log("");
|
|
1929
|
+
return;
|
|
1930
|
+
}
|
|
1931
|
+
if (plugins.length > 0) {
|
|
1932
|
+
console.log(chalk11.bold(`
|
|
1933
|
+
Plugins (${plugins.length}):`));
|
|
1934
|
+
for (const plugin of plugins) {
|
|
1935
|
+
const name = resolvePluginName(plugin);
|
|
1936
|
+
const version = resolvePluginVersion(plugin);
|
|
1937
|
+
const type = resolvePluginType(plugin);
|
|
1938
|
+
console.log(
|
|
1939
|
+
` ${chalk11.cyan("\u25CF")} ${chalk11.white(name)}` + (version !== "-" ? chalk11.dim(` v${version}`) : "") + (type !== "standard" ? chalk11.dim(` [${type}]`) : "")
|
|
1940
|
+
);
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if (devPlugins.length > 0) {
|
|
1944
|
+
console.log(chalk11.bold(`
|
|
1945
|
+
Dev Plugins (${devPlugins.length}):`));
|
|
1946
|
+
for (const plugin of devPlugins) {
|
|
1947
|
+
const name = resolvePluginName(plugin);
|
|
1948
|
+
const version = resolvePluginVersion(plugin);
|
|
1949
|
+
console.log(
|
|
1950
|
+
` ${chalk11.yellow("\u25CF")} ${chalk11.white(name)}` + (version !== "-" ? chalk11.dim(` v${version}`) : "") + chalk11.dim(" [dev]")
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
console.log("");
|
|
1955
|
+
} catch (error) {
|
|
1956
|
+
printError(error.message || String(error));
|
|
1957
|
+
process.exit(1);
|
|
1958
|
+
}
|
|
1959
|
+
});
|
|
1960
|
+
var infoSubCommand = new Command12("info").description("Show detailed information about a plugin").argument("<name>", "Plugin name or package name").argument("[config]", "Configuration file path").action(async (name, configSource) => {
|
|
1961
|
+
try {
|
|
1962
|
+
const { config } = await loadConfig(configSource);
|
|
1963
|
+
const allPlugins = [
|
|
1964
|
+
...config.plugins || [],
|
|
1965
|
+
...config.devPlugins || []
|
|
1966
|
+
];
|
|
1967
|
+
const found = allPlugins.find((p) => {
|
|
1968
|
+
const pName = resolvePluginName(p);
|
|
1969
|
+
return pName === name || pName.includes(name);
|
|
1970
|
+
});
|
|
1971
|
+
if (!found) {
|
|
1972
|
+
printError(`Plugin '${name}' not found in configuration`);
|
|
1973
|
+
console.log("");
|
|
1974
|
+
console.log(chalk11.dim(" Available plugins:"));
|
|
1975
|
+
for (const p of allPlugins) {
|
|
1976
|
+
console.log(chalk11.dim(` - ${resolvePluginName(p)}`));
|
|
1977
|
+
}
|
|
1978
|
+
console.log("");
|
|
1979
|
+
process.exit(1);
|
|
1980
|
+
}
|
|
1981
|
+
printHeader(`Plugin: ${resolvePluginName(found)}`);
|
|
1982
|
+
printKV("Name", resolvePluginName(found));
|
|
1983
|
+
printKV("Version", resolvePluginVersion(found));
|
|
1984
|
+
printKV("Type", resolvePluginType(found));
|
|
1985
|
+
const isDev = (config.devPlugins || []).includes(found);
|
|
1986
|
+
printKV("Environment", isDev ? "development" : "production");
|
|
1987
|
+
if (found && typeof found === "object") {
|
|
1988
|
+
const p = found;
|
|
1989
|
+
if (typeof p.description === "string") {
|
|
1990
|
+
printKV("Description", p.description);
|
|
1991
|
+
}
|
|
1992
|
+
if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
|
|
1993
|
+
printKV("Dependencies", p.dependencies.join(", "));
|
|
1994
|
+
}
|
|
1995
|
+
if (typeof p.init === "function") {
|
|
1996
|
+
printInfo("This is a runtime plugin instance (has init function)");
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
if (typeof found === "string") {
|
|
2000
|
+
printInfo("This is a string reference (will be imported at runtime)");
|
|
2001
|
+
}
|
|
2002
|
+
console.log("");
|
|
2003
|
+
} catch (error) {
|
|
2004
|
+
printError(error.message || String(error));
|
|
2005
|
+
process.exit(1);
|
|
2006
|
+
}
|
|
2007
|
+
});
|
|
2008
|
+
var addCommand = new Command12("add").description("Add a plugin to objectstack.config.ts").argument("<package>", "Plugin package name (e.g. @objectstack/plugin-auth)").option("-d, --dev", "Add as a dev-only plugin").option("-c, --config <path>", "Configuration file path").action(async (packageName, options) => {
|
|
2009
|
+
try {
|
|
2010
|
+
const configPath = resolveConfigPath(options?.config);
|
|
2011
|
+
printHeader("Add Plugin");
|
|
2012
|
+
console.log(` ${chalk11.dim("Package:")} ${chalk11.white(packageName)}`);
|
|
2013
|
+
console.log(` ${chalk11.dim("Config:")} ${chalk11.white(path10.relative(process.cwd(), configPath))}`);
|
|
2014
|
+
console.log("");
|
|
2015
|
+
addPluginToConfig(configPath, packageName);
|
|
2016
|
+
printSuccess(`Added ${chalk11.cyan(packageName)} to config`);
|
|
2017
|
+
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"));
|
|
2021
|
+
console.log("");
|
|
2022
|
+
} catch (error) {
|
|
2023
|
+
printError(error.message || String(error));
|
|
2024
|
+
process.exit(1);
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
var removeCommand = new Command12("remove").alias("rm").description("Remove a plugin from objectstack.config.ts").argument("<name>", "Plugin name or package name to remove").option("-c, --config <path>", "Configuration file path").action(async (pluginName, options) => {
|
|
2028
|
+
try {
|
|
2029
|
+
const configPath = resolveConfigPath(options?.config);
|
|
2030
|
+
printHeader("Remove Plugin");
|
|
2031
|
+
console.log(` ${chalk11.dim("Plugin:")} ${chalk11.white(pluginName)}`);
|
|
2032
|
+
console.log(` ${chalk11.dim("Config:")} ${chalk11.white(path10.relative(process.cwd(), configPath))}`);
|
|
2033
|
+
console.log("");
|
|
2034
|
+
removePluginFromConfig(configPath, pluginName);
|
|
2035
|
+
printSuccess(`Removed ${chalk11.cyan(pluginName)} from config`);
|
|
2036
|
+
console.log("");
|
|
2037
|
+
console.log(chalk11.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
|
|
1870
2038
|
console.log("");
|
|
1871
2039
|
} catch (error) {
|
|
1872
2040
|
printError(error.message || String(error));
|
|
1873
2041
|
process.exit(1);
|
|
1874
2042
|
}
|
|
1875
2043
|
});
|
|
2044
|
+
var pluginCommand = new Command12("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
|
|
2045
|
+
|
|
2046
|
+
// src/utils/plugin-commands.ts
|
|
2047
|
+
import chalk12 from "chalk";
|
|
2048
|
+
async function loadPluginCommands(program2) {
|
|
2049
|
+
let config;
|
|
2050
|
+
try {
|
|
2051
|
+
const loaded = await loadConfig();
|
|
2052
|
+
config = loaded.config;
|
|
2053
|
+
} catch {
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
const plugins = [
|
|
2057
|
+
...config.plugins || [],
|
|
2058
|
+
...config.devPlugins || []
|
|
2059
|
+
];
|
|
2060
|
+
const contributions = [];
|
|
2061
|
+
for (const plugin of plugins) {
|
|
2062
|
+
if (!plugin || typeof plugin !== "object") continue;
|
|
2063
|
+
const p = plugin;
|
|
2064
|
+
const manifest = p.manifest;
|
|
2065
|
+
const contributes = manifest?.contributes ?? p.contributes;
|
|
2066
|
+
if (!contributes) continue;
|
|
2067
|
+
const commands = contributes.commands;
|
|
2068
|
+
if (!Array.isArray(commands)) continue;
|
|
2069
|
+
const pluginName = resolvePluginName2(p);
|
|
2070
|
+
for (const cmd of commands) {
|
|
2071
|
+
if (!cmd || typeof cmd.name !== "string") continue;
|
|
2072
|
+
contributions.push({
|
|
2073
|
+
name: cmd.name,
|
|
2074
|
+
description: typeof cmd.description === "string" ? cmd.description : void 0,
|
|
2075
|
+
module: typeof cmd.module === "string" ? cmd.module : void 0,
|
|
2076
|
+
pluginName
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
if (contributions.length === 0) return;
|
|
2081
|
+
for (const contribution of contributions) {
|
|
2082
|
+
try {
|
|
2083
|
+
const commands = await importPluginCommands(contribution);
|
|
2084
|
+
for (const cmd of commands) {
|
|
2085
|
+
program2.addCommand(cmd);
|
|
2086
|
+
}
|
|
2087
|
+
} catch (error) {
|
|
2088
|
+
if (process.env.DEBUG) {
|
|
2089
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2090
|
+
console.error(
|
|
2091
|
+
chalk12.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
async function importPluginCommands(contribution) {
|
|
2098
|
+
const moduleId = contribution.module ? `${contribution.pluginName}/${contribution.module.replace(/^\.\//, "")}` : contribution.pluginName;
|
|
2099
|
+
const mod = await import(moduleId);
|
|
2100
|
+
if (Array.isArray(mod.commands)) {
|
|
2101
|
+
return mod.commands.filter(isCommandInstance);
|
|
2102
|
+
}
|
|
2103
|
+
const defaultExport = mod.default;
|
|
2104
|
+
if (defaultExport) {
|
|
2105
|
+
if (Array.isArray(defaultExport)) {
|
|
2106
|
+
return defaultExport.filter(isCommandInstance);
|
|
2107
|
+
}
|
|
2108
|
+
if (isCommandInstance(defaultExport)) {
|
|
2109
|
+
return [defaultExport];
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
const commands = [];
|
|
2113
|
+
for (const key of Object.keys(mod)) {
|
|
2114
|
+
if (isCommandInstance(mod[key])) {
|
|
2115
|
+
commands.push(mod[key]);
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
return commands;
|
|
2119
|
+
}
|
|
2120
|
+
function isCommandInstance(value) {
|
|
2121
|
+
if (value === null || typeof value !== "object") return false;
|
|
2122
|
+
const obj = value;
|
|
2123
|
+
return typeof obj.name === "function" && typeof obj.description === "function" && typeof obj.action === "function" && typeof obj.parse === "function";
|
|
2124
|
+
}
|
|
2125
|
+
function resolvePluginName2(plugin) {
|
|
2126
|
+
if (typeof plugin.name === "string") return plugin.name;
|
|
2127
|
+
const manifest = plugin.manifest;
|
|
2128
|
+
if (manifest && typeof manifest.name === "string") return manifest.name;
|
|
2129
|
+
if (plugin.constructor && plugin.constructor.name !== "Object") return plugin.constructor.name;
|
|
2130
|
+
return "unknown";
|
|
2131
|
+
}
|
|
1876
2132
|
|
|
1877
2133
|
// src/bin.ts
|
|
1878
2134
|
var require2 = createRequire2(import.meta.url);
|
|
@@ -1885,7 +2141,7 @@ process.on("unhandledRejection", (err) => {
|
|
|
1885
2141
|
}
|
|
1886
2142
|
process.exit(1);
|
|
1887
2143
|
});
|
|
1888
|
-
var program = new
|
|
2144
|
+
var program = new Command13();
|
|
1889
2145
|
program.name("objectstack").description("ObjectStack CLI \u2014 Build metadata-driven apps with the ObjectStack Protocol").version(pkg.version, "-v, --version").configureHelp({
|
|
1890
2146
|
sortSubcommands: false
|
|
1891
2147
|
}).addHelpText("before", `
|
|
@@ -1894,6 +2150,7 @@ ${chalk13.bold.cyan("\u25C6 ObjectStack CLI")} ${chalk13.dim(`v${pkg.version}`)}
|
|
|
1894
2150
|
${chalk13.bold("Workflow:")}
|
|
1895
2151
|
${chalk13.dim("$")} os init ${chalk13.dim("# Create a new project")}
|
|
1896
2152
|
${chalk13.dim("$")} os generate object task ${chalk13.dim("# Add metadata")}
|
|
2153
|
+
${chalk13.dim("$")} os plugin add <package> ${chalk13.dim("# Add a plugin")}
|
|
1897
2154
|
${chalk13.dim("$")} os validate ${chalk13.dim("# Check configuration")}
|
|
1898
2155
|
${chalk13.dim("$")} os dev ${chalk13.dim("# Start dev server")}
|
|
1899
2156
|
${chalk13.dim("$")} os studio ${chalk13.dim("# Dev server + Studio UI")}
|
|
@@ -1911,6 +2168,15 @@ program.addCommand(validateCommand);
|
|
|
1911
2168
|
program.addCommand(infoCommand);
|
|
1912
2169
|
program.addCommand(generateCommand);
|
|
1913
2170
|
program.addCommand(createCommand);
|
|
2171
|
+
program.addCommand(pluginCommand);
|
|
1914
2172
|
program.addCommand(testCommand);
|
|
1915
2173
|
program.addCommand(doctorCommand);
|
|
1916
|
-
program.
|
|
2174
|
+
loadPluginCommands(program).then(() => {
|
|
2175
|
+
program.parse(process.argv);
|
|
2176
|
+
}).catch((err) => {
|
|
2177
|
+
if (process.env.DEBUG) {
|
|
2178
|
+
console.error(chalk13.yellow(`
|
|
2179
|
+
\u26A0 Plugin command loading failed: ${err?.message || err}`));
|
|
2180
|
+
}
|
|
2181
|
+
program.parse(process.argv);
|
|
2182
|
+
});
|