@objectstack/cli 3.0.5 → 3.0.7

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.
Files changed (141) hide show
  1. package/.turbo/turbo-build.log +2 -26
  2. package/CHANGELOG.md +27 -0
  3. package/README.md +98 -54
  4. package/bin/run-dev.js +5 -0
  5. package/bin/run.js +5 -0
  6. package/dist/bin.d.ts +11 -0
  7. package/dist/bin.d.ts.map +1 -0
  8. package/dist/bin.js +12 -3759
  9. package/dist/bin.js.map +1 -0
  10. package/dist/commands/codemod/v2-to-v3.d.ts +10 -0
  11. package/dist/commands/codemod/v2-to-v3.d.ts.map +1 -0
  12. package/dist/commands/codemod/v2-to-v3.js +145 -0
  13. package/dist/commands/codemod/v2-to-v3.js.map +1 -0
  14. package/dist/commands/compile.d.ts +13 -0
  15. package/dist/commands/compile.d.ts.map +1 -0
  16. package/dist/commands/compile.js +91 -0
  17. package/dist/commands/compile.js.map +1 -0
  18. package/dist/commands/create.d.ts +91 -0
  19. package/dist/commands/create.d.ts.map +1 -0
  20. package/dist/commands/create.js +259 -0
  21. package/dist/commands/create.js.map +1 -0
  22. package/dist/commands/dev.d.ts +14 -0
  23. package/dist/commands/dev.d.ts.map +1 -0
  24. package/dist/commands/dev.js +67 -0
  25. package/dist/commands/dev.js.map +1 -0
  26. package/dist/commands/diff.d.ts +16 -0
  27. package/dist/commands/diff.d.ts.map +1 -0
  28. package/dist/commands/diff.js +239 -0
  29. package/dist/commands/diff.js.map +1 -0
  30. package/dist/commands/doctor.d.ts +10 -0
  31. package/dist/commands/doctor.d.ts.map +1 -0
  32. package/dist/commands/doctor.js +532 -0
  33. package/dist/commands/doctor.js.map +1 -0
  34. package/dist/commands/explain.d.ts +12 -0
  35. package/dist/commands/explain.d.ts.map +1 -0
  36. package/dist/commands/explain.js +368 -0
  37. package/dist/commands/explain.js.map +1 -0
  38. package/dist/commands/generate.d.ts +17 -0
  39. package/dist/commands/generate.d.ts.map +1 -0
  40. package/dist/commands/generate.js +833 -0
  41. package/dist/commands/generate.js.map +1 -0
  42. package/dist/commands/info.d.ts +12 -0
  43. package/dist/commands/info.d.ts.map +1 -0
  44. package/dist/commands/info.js +100 -0
  45. package/dist/commands/info.js.map +1 -0
  46. package/dist/commands/init.d.ts +22 -0
  47. package/dist/commands/init.d.ts.map +1 -0
  48. package/dist/commands/init.js +295 -0
  49. package/dist/commands/init.js.map +1 -0
  50. package/dist/commands/lint.d.ts +13 -0
  51. package/dist/commands/lint.d.ts.map +1 -0
  52. package/dist/commands/lint.js +255 -0
  53. package/dist/commands/lint.js.map +1 -0
  54. package/dist/commands/plugin/add.d.ts +22 -0
  55. package/dist/commands/plugin/add.d.ts.map +1 -0
  56. package/dist/commands/plugin/add.js +93 -0
  57. package/dist/commands/plugin/add.js.map +1 -0
  58. package/dist/commands/plugin/info.d.ts +10 -0
  59. package/dist/commands/plugin/info.d.ts.map +1 -0
  60. package/dist/commands/plugin/info.js +65 -0
  61. package/dist/commands/plugin/info.js.map +1 -0
  62. package/dist/commands/plugin/list.d.ts +13 -0
  63. package/dist/commands/plugin/list.d.ts.map +1 -0
  64. package/dist/commands/plugin/list.js +78 -0
  65. package/dist/commands/plugin/list.js.map +1 -0
  66. package/dist/commands/plugin/remove.d.ts +20 -0
  67. package/dist/commands/plugin/remove.d.ts.map +1 -0
  68. package/dist/commands/plugin/remove.js +79 -0
  69. package/dist/commands/plugin/remove.js.map +1 -0
  70. package/dist/commands/serve.d.ts +15 -0
  71. package/dist/commands/serve.d.ts.map +1 -0
  72. package/dist/commands/serve.js +286 -0
  73. package/dist/commands/serve.js.map +1 -0
  74. package/dist/commands/studio.d.ts +19 -0
  75. package/dist/commands/studio.d.ts.map +1 -0
  76. package/dist/commands/studio.js +43 -0
  77. package/dist/commands/studio.js.map +1 -0
  78. package/dist/commands/test.d.ts +13 -0
  79. package/dist/commands/test.d.ts.map +1 -0
  80. package/dist/commands/test.js +120 -0
  81. package/dist/commands/test.js.map +1 -0
  82. package/dist/commands/validate.d.ts +13 -0
  83. package/dist/commands/validate.d.ts.map +1 -0
  84. package/dist/commands/validate.js +115 -0
  85. package/dist/commands/validate.js.map +1 -0
  86. package/dist/index.d.ts +15 -114
  87. package/dist/index.d.ts.map +1 -0
  88. package/dist/index.js +20 -2799
  89. package/dist/index.js.map +1 -0
  90. package/dist/utils/config.d.ts +21 -0
  91. package/dist/utils/config.d.ts.map +1 -0
  92. package/dist/utils/config.js +66 -0
  93. package/dist/utils/config.js.map +1 -0
  94. package/dist/utils/format.d.ts +52 -0
  95. package/dist/utils/format.d.ts.map +1 -0
  96. package/dist/utils/format.js +202 -0
  97. package/dist/utils/format.js.map +1 -0
  98. package/dist/utils/plugin-helpers.d.ts +14 -0
  99. package/dist/utils/plugin-helpers.d.ts.map +1 -0
  100. package/dist/utils/plugin-helpers.js +40 -0
  101. package/dist/utils/plugin-helpers.js.map +1 -0
  102. package/dist/utils/studio.d.ts +58 -0
  103. package/dist/utils/studio.d.ts.map +1 -0
  104. package/dist/utils/studio.js +288 -0
  105. package/dist/utils/studio.js.map +1 -0
  106. package/package.json +33 -15
  107. package/src/bin.ts +11 -104
  108. package/src/commands/{codemod.ts → codemod/v2-to-v3.ts} +21 -28
  109. package/src/commands/compile.ts +35 -25
  110. package/src/commands/create.ts +29 -19
  111. package/src/commands/dev.ts +21 -10
  112. package/src/commands/diff.ts +28 -19
  113. package/src/commands/doctor.ts +20 -11
  114. package/src/commands/explain.ts +20 -10
  115. package/src/commands/generate.ts +81 -90
  116. package/src/commands/info.ts +22 -11
  117. package/src/commands/init.ts +32 -20
  118. package/src/commands/lint.ts +27 -15
  119. package/src/commands/plugin/add.ts +112 -0
  120. package/src/commands/plugin/info.ts +79 -0
  121. package/src/commands/plugin/list.ts +93 -0
  122. package/src/commands/plugin/remove.ts +97 -0
  123. package/src/commands/serve.ts +30 -20
  124. package/src/commands/studio.ts +21 -11
  125. package/src/commands/test.ts +21 -10
  126. package/src/commands/validate.ts +36 -25
  127. package/src/index.ts +20 -12
  128. package/src/utils/format.ts +10 -6
  129. package/src/utils/plugin-helpers.ts +37 -0
  130. package/src/utils/studio.ts +0 -1
  131. package/test/commands.test.ts +76 -37
  132. package/test/plugin-commands.test.ts +42 -160
  133. package/test/plugin.test.ts +19 -23
  134. package/tsconfig.build.json +18 -0
  135. package/bin/objectstack.js +0 -2
  136. package/dist/chunk-CSHQEILI.js +0 -246
  137. package/dist/chunk-Q74JNWKD.js +0 -248
  138. package/dist/config-A7BN6UIT.js +0 -11
  139. package/dist/config-UN34WBHT.js +0 -10
  140. package/src/commands/plugin.ts +0 -372
  141. package/src/utils/plugin-commands.ts +0 -163
package/dist/index.js CHANGED
@@ -1,2799 +1,20 @@
1
- import {
2
- collectMetadataStats,
3
- configExists,
4
- createTimer,
5
- formatZodErrors,
6
- loadConfig,
7
- printError,
8
- printHeader,
9
- printInfo,
10
- printKV,
11
- printMetadataStats,
12
- printServerReady,
13
- printStep,
14
- printSuccess,
15
- printWarning,
16
- resolveConfigPath
17
- } from "./chunk-CSHQEILI.js";
18
-
19
- // src/commands/compile.ts
20
- import { Command } from "commander";
21
- import path from "path";
22
- import fs from "fs";
23
- import chalk from "chalk";
24
- import { ObjectStackDefinitionSchema } from "@objectstack/spec";
25
- var compileCommand = new Command("compile").description("Compile ObjectStack configuration to JSON artifact").argument("[config]", "Source configuration file").option("-o, --output <path>", "Output JSON file", "dist/objectstack.json").option("--json", "Output compile result as JSON (for CI)").action(async (configPath, options) => {
26
- const timer = createTimer();
27
- if (!options.json) {
28
- printHeader("Compile");
29
- }
30
- try {
31
- if (!options.json) printStep("Loading configuration...");
32
- const { config, absolutePath, duration } = await loadConfig(configPath);
33
- if (!options.json) {
34
- printKV("Config", path.relative(process.cwd(), absolutePath));
35
- printKV("Load time", `${duration}ms`);
36
- }
37
- if (!options.json) printStep("Validating protocol compliance...");
38
- const result = ObjectStackDefinitionSchema.safeParse(config);
39
- if (!result.success) {
40
- if (options.json) {
41
- console.log(JSON.stringify({ success: false, errors: result.error.issues }));
42
- process.exit(1);
43
- }
44
- console.log("");
45
- printError("Validation failed");
46
- formatZodErrors(result.error);
47
- process.exit(1);
48
- }
49
- if (!options.json) printStep("Writing artifact...");
50
- const output = options.output;
51
- const artifactPath = path.resolve(process.cwd(), output);
52
- const artifactDir = path.dirname(artifactPath);
53
- if (!fs.existsSync(artifactDir)) {
54
- fs.mkdirSync(artifactDir, { recursive: true });
55
- }
56
- const jsonContent = JSON.stringify(result.data, null, 2);
57
- fs.writeFileSync(artifactPath, jsonContent);
58
- const sizeKB = (jsonContent.length / 1024).toFixed(1);
59
- const stats = collectMetadataStats(config);
60
- if (options.json) {
61
- console.log(JSON.stringify({
62
- success: true,
63
- output: artifactPath,
64
- size: jsonContent.length,
65
- stats,
66
- duration: timer.elapsed()
67
- }));
68
- return;
69
- }
70
- console.log("");
71
- printSuccess(`Build complete ${chalk.dim(`(${timer.display()})`)}`);
72
- console.log("");
73
- printMetadataStats(stats);
74
- console.log("");
75
- printKV("Artifact", `${output} ${chalk.dim(`(${sizeKB} KB`)})`);
76
- console.log("");
77
- } catch (error) {
78
- if (options.json) {
79
- console.log(JSON.stringify({ success: false, error: error.message }));
80
- process.exit(1);
81
- }
82
- console.log("");
83
- printError(error.message || String(error));
84
- process.exit(1);
85
- }
86
- });
87
-
88
- // src/commands/validate.ts
89
- import { Command as Command2 } from "commander";
90
- import chalk2 from "chalk";
91
- import { ObjectStackDefinitionSchema as ObjectStackDefinitionSchema2 } from "@objectstack/spec";
92
- var validateCommand = new Command2("validate").description("Validate ObjectStack configuration against the protocol schema").argument("[config]", "Configuration file path").option("--strict", "Treat warnings as errors").option("--json", "Output results as JSON").action(async (configPath, options) => {
93
- const timer = createTimer();
94
- if (!options.json) {
95
- printHeader("Validate");
96
- }
97
- try {
98
- if (!options.json) printStep("Loading configuration...");
99
- const { config, absolutePath, duration } = await loadConfig(configPath);
100
- if (!options.json) {
101
- printKV("Config", absolutePath);
102
- printKV("Load time", `${duration}ms`);
103
- }
104
- if (!options.json) printStep("Validating against ObjectStack Protocol...");
105
- const result = ObjectStackDefinitionSchema2.safeParse(config);
106
- if (!result.success) {
107
- if (options.json) {
108
- console.log(JSON.stringify({
109
- valid: false,
110
- errors: result.error.issues,
111
- duration: timer.elapsed()
112
- }, null, 2));
113
- process.exit(1);
114
- }
115
- console.log("");
116
- printError("Validation failed");
117
- formatZodErrors(result.error);
118
- process.exit(1);
119
- }
120
- const stats = collectMetadataStats(config);
121
- if (options.json) {
122
- console.log(JSON.stringify({
123
- valid: true,
124
- manifest: config.manifest,
125
- stats,
126
- duration: timer.elapsed()
127
- }, null, 2));
128
- return;
129
- }
130
- const warnings = [];
131
- if (stats.objects === 0) {
132
- warnings.push("No objects defined \u2014 this stack has no data model");
133
- }
134
- if (stats.apps === 0 && stats.plugins === 0) {
135
- warnings.push("No apps or plugins defined \u2014 this stack may not do much");
136
- }
137
- if (!config.manifest?.id) {
138
- warnings.push("Missing manifest.id \u2014 required for deployment");
139
- }
140
- if (!config.manifest?.namespace) {
141
- warnings.push("Missing manifest.namespace \u2014 required for multi-app hosting");
142
- }
143
- console.log("");
144
- printSuccess(`Validation passed ${chalk2.dim(`(${timer.display()})`)}`);
145
- console.log("");
146
- if (config.manifest) {
147
- console.log(` ${chalk2.bold(config.manifest.name || config.manifest.id || "Unnamed")} ${chalk2.dim(`v${config.manifest.version || "0.0.0"}`)}`);
148
- if (config.manifest.description) {
149
- console.log(chalk2.dim(` ${config.manifest.description}`));
150
- }
151
- console.log("");
152
- }
153
- printMetadataStats(stats);
154
- if (warnings.length > 0) {
155
- console.log("");
156
- for (const w of warnings) {
157
- console.log(chalk2.yellow(` \u26A0 ${w}`));
158
- }
159
- if (options.strict) {
160
- console.log("");
161
- printError("Strict mode: warnings treated as errors");
162
- process.exit(1);
163
- }
164
- }
165
- console.log("");
166
- } catch (error) {
167
- if (options.json) {
168
- console.log(JSON.stringify({
169
- valid: false,
170
- error: error.message,
171
- duration: timer.elapsed()
172
- }, null, 2));
173
- process.exit(1);
174
- }
175
- console.log("");
176
- printError(error.message || String(error));
177
- process.exit(1);
178
- }
179
- });
180
-
181
- // src/commands/info.ts
182
- import { Command as Command3 } from "commander";
183
- import chalk3 from "chalk";
184
- var infoCommand = new Command3("info").description("Display metadata summary of an ObjectStack configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configPath, options) => {
185
- const timer = createTimer();
186
- if (!options.json) {
187
- printHeader("Info");
188
- }
189
- try {
190
- const { config, absolutePath, duration } = await loadConfig(configPath);
191
- const stats = collectMetadataStats(config);
192
- if (options.json) {
193
- console.log(JSON.stringify({
194
- config: absolutePath,
195
- manifest: config.manifest || null,
196
- stats,
197
- objects: (config.objects || []).map((o) => ({
198
- name: o.name,
199
- label: o.label,
200
- fields: o.fields ? Object.keys(o.fields).length : 0
201
- })),
202
- loadTime: duration
203
- }, null, 2));
204
- return;
205
- }
206
- if (config.manifest) {
207
- const m = config.manifest;
208
- console.log("");
209
- console.log(` ${chalk3.bold(m.name || m.id || "Unnamed")} ${chalk3.dim(`v${m.version || "0.0.0"}`)}`);
210
- if (m.id) console.log(chalk3.dim(` ${m.id}`));
211
- if (m.description) console.log(chalk3.dim(` ${m.description}`));
212
- if (m.namespace) printKV(" Namespace", m.namespace);
213
- if (m.type) printKV(" Type", m.type);
214
- }
215
- console.log("");
216
- printMetadataStats(stats);
217
- if (config.objects && config.objects.length > 0) {
218
- console.log("");
219
- console.log(chalk3.bold(" Objects:"));
220
- for (const obj of config.objects) {
221
- const fieldCount = obj.fields ? Object.keys(obj.fields).length : 0;
222
- const ownership = obj.ownership || "own";
223
- console.log(
224
- ` ${chalk3.cyan(obj.name || "?")}` + chalk3.dim(` (${fieldCount} fields, ${ownership})`) + (obj.label ? chalk3.dim(` \u2014 ${obj.label}`) : "")
225
- );
226
- }
227
- }
228
- if (config.agents && config.agents.length > 0) {
229
- console.log("");
230
- console.log(chalk3.bold(" Agents:"));
231
- for (const agent of config.agents) {
232
- console.log(
233
- ` ${chalk3.magenta(agent.name || "?")}` + (agent.role ? chalk3.dim(` \u2014 ${agent.role}`) : "")
234
- );
235
- }
236
- }
237
- if (config.apps && config.apps.length > 0) {
238
- console.log("");
239
- console.log(chalk3.bold(" Apps:"));
240
- for (const app of config.apps) {
241
- console.log(
242
- ` ${chalk3.green(app.name || "?")}` + (app.label ? chalk3.dim(` \u2014 ${app.label}`) : "")
243
- );
244
- }
245
- }
246
- console.log("");
247
- console.log(chalk3.dim(` Loaded in ${duration}ms`));
248
- console.log("");
249
- } catch (error) {
250
- if (options.json) {
251
- console.log(JSON.stringify({ error: error.message }));
252
- process.exit(1);
253
- }
254
- console.log("");
255
- printError(error.message || String(error));
256
- process.exit(1);
257
- }
258
- });
259
-
260
- // src/commands/init.ts
261
- import { Command as Command4 } from "commander";
262
- import chalk4 from "chalk";
263
- import fs2 from "fs";
264
- import path2 from "path";
265
- var TEMPLATES = {
266
- app: {
267
- description: "Full application with objects, views, and actions",
268
- dependencies: {
269
- "@objectstack/spec": "workspace:*",
270
- "@objectstack/runtime": "workspace:^",
271
- "@objectstack/objectql": "workspace:^",
272
- "@objectstack/driver-memory": "workspace:^"
273
- },
274
- devDependencies: {
275
- "@objectstack/cli": "workspace:*",
276
- "typescript": "^5.3.0"
277
- },
278
- scripts: {
279
- dev: "objectstack dev",
280
- start: "objectstack serve",
281
- build: "objectstack compile",
282
- validate: "objectstack validate",
283
- typecheck: "tsc --noEmit"
284
- },
285
- configContent: (name) => `import { defineStack } from '@objectstack/spec';
286
- import * as objects from './src/objects';
287
-
288
- export default defineStack({
289
- manifest: {
290
- id: 'com.example.${name}',
291
- namespace: '${name}',
292
- version: '0.1.0',
293
- type: 'app',
294
- name: '${toTitleCase(name)}',
295
- description: '${toTitleCase(name)} application built with ObjectStack',
296
- },
297
-
298
- objects: Object.values(objects),
299
- });
300
- `,
301
- srcFiles: {
302
- "src/objects/index.ts": (name) => `export { default as ${toCamelCase(name)} } from './${name}';
303
- `,
304
- "src/objects/__name__.ts": (name) => `import { Data } from '@objectstack/spec';
305
-
306
- const ${toCamelCase(name)}: Data.Object = {
307
- name: '${name}',
308
- label: '${toTitleCase(name)}',
309
- ownership: 'own',
310
- fields: {
311
- name: {
312
- type: 'text',
313
- label: 'Name',
314
- required: true,
315
- },
316
- description: {
317
- type: 'textarea',
318
- label: 'Description',
319
- },
320
- status: {
321
- type: 'select',
322
- label: 'Status',
323
- options: [
324
- { label: 'Draft', value: 'draft' },
325
- { label: 'Active', value: 'active' },
326
- { label: 'Archived', value: 'archived' },
327
- ],
328
- defaultValue: 'draft',
329
- },
330
- },
331
- };
332
-
333
- export default ${toCamelCase(name)};
334
- `
335
- }
336
- },
337
- plugin: {
338
- description: "Reusable plugin with objects and extensions",
339
- dependencies: {
340
- "@objectstack/spec": "workspace:*"
341
- },
342
- devDependencies: {
343
- "typescript": "^5.3.0",
344
- "vitest": "^4.0.18"
345
- },
346
- scripts: {
347
- build: "objectstack compile",
348
- validate: "objectstack validate",
349
- test: "vitest run",
350
- typecheck: "tsc --noEmit"
351
- },
352
- configContent: (name) => `import { defineStack } from '@objectstack/spec';
353
- import * as objects from './src/objects';
354
-
355
- export default defineStack({
356
- manifest: {
357
- id: 'com.objectstack.plugin-${name}',
358
- namespace: 'plugin_${name}',
359
- version: '0.1.0',
360
- type: 'plugin',
361
- name: '${toTitleCase(name)} Plugin',
362
- description: 'ObjectStack Plugin: ${toTitleCase(name)}',
363
- },
364
-
365
- objects: Object.values(objects),
366
- });
367
- `,
368
- srcFiles: {
369
- "src/objects/index.ts": (name) => `export { default as ${toCamelCase(name)} } from './${name}';
370
- `,
371
- "src/objects/__name__.ts": (name) => `import { Data } from '@objectstack/spec';
372
-
373
- const ${toCamelCase(name)}: Data.Object = {
374
- name: '${name}',
375
- label: '${toTitleCase(name)}',
376
- ownership: 'own',
377
- fields: {
378
- name: {
379
- type: 'text',
380
- label: 'Name',
381
- required: true,
382
- },
383
- },
384
- };
385
-
386
- export default ${toCamelCase(name)};
387
- `
388
- }
389
- },
390
- empty: {
391
- description: "Minimal project with just a config file",
392
- dependencies: {
393
- "@objectstack/spec": "workspace:*"
394
- },
395
- devDependencies: {
396
- "@objectstack/cli": "workspace:*",
397
- "typescript": "^5.3.0"
398
- },
399
- scripts: {
400
- build: "objectstack compile",
401
- validate: "objectstack validate",
402
- typecheck: "tsc --noEmit"
403
- },
404
- configContent: (name) => `import { defineStack } from '@objectstack/spec';
405
-
406
- export default defineStack({
407
- manifest: {
408
- id: 'com.example.${name}',
409
- namespace: '${name}',
410
- version: '0.1.0',
411
- type: 'app',
412
- name: '${toTitleCase(name)}',
413
- description: '',
414
- },
415
- });
416
- `,
417
- srcFiles: {}
418
- }
419
- };
420
- function toCamelCase(str) {
421
- return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
422
- }
423
- function toTitleCase(str) {
424
- return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
425
- }
426
- var initCommand = new Command4("init").description("Initialize a new ObjectStack project in the current directory").argument("[name]", "Project name (defaults to directory name)").option("-t, --template <template>", "Template: app, plugin, empty", "app").option("--no-install", "Skip dependency installation").action(async (name, options) => {
427
- printHeader("Init");
428
- const cwd = process.cwd();
429
- const projectName = name || path2.basename(cwd);
430
- const template = TEMPLATES[options.template];
431
- if (!template) {
432
- printError(`Unknown template: ${options.template}`);
433
- console.log(chalk4.dim(` Available: ${Object.keys(TEMPLATES).join(", ")}`));
434
- process.exit(1);
435
- }
436
- if (fs2.existsSync(path2.join(cwd, "objectstack.config.ts"))) {
437
- printError("objectstack.config.ts already exists in this directory");
438
- console.log(chalk4.dim(" Use `objectstack generate` to add metadata to an existing project"));
439
- process.exit(1);
440
- }
441
- printKV("Project", projectName);
442
- printKV("Template", `${options.template} \u2014 ${template.description}`);
443
- printKV("Directory", cwd);
444
- console.log("");
445
- const createdFiles = [];
446
- try {
447
- const pkgPath = path2.join(cwd, "package.json");
448
- if (!fs2.existsSync(pkgPath)) {
449
- const pkg = {
450
- name: projectName,
451
- version: "0.1.0",
452
- private: true,
453
- type: "module",
454
- scripts: template.scripts,
455
- dependencies: template.dependencies,
456
- devDependencies: template.devDependencies
457
- };
458
- fs2.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
459
- createdFiles.push("package.json");
460
- } else {
461
- printInfo("package.json already exists, skipping");
462
- }
463
- const configContent = template.configContent(projectName);
464
- fs2.writeFileSync(path2.join(cwd, "objectstack.config.ts"), configContent);
465
- createdFiles.push("objectstack.config.ts");
466
- const tsconfigPath = path2.join(cwd, "tsconfig.json");
467
- if (!fs2.existsSync(tsconfigPath)) {
468
- const tsconfig = {
469
- compilerOptions: {
470
- target: "ES2022",
471
- module: "ESNext",
472
- moduleResolution: "bundler",
473
- strict: true,
474
- esModuleInterop: true,
475
- skipLibCheck: true,
476
- outDir: "dist",
477
- rootDir: ".",
478
- declaration: true
479
- },
480
- include: ["*.ts", "src/**/*"],
481
- exclude: ["dist", "node_modules"]
482
- };
483
- fs2.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + "\n");
484
- createdFiles.push("tsconfig.json");
485
- }
486
- for (const [filePath, contentFn] of Object.entries(template.srcFiles)) {
487
- const resolvedPath = filePath.replace("__name__", projectName);
488
- const fullPath = path2.join(cwd, resolvedPath);
489
- const dir = path2.dirname(fullPath);
490
- if (!fs2.existsSync(dir)) {
491
- fs2.mkdirSync(dir, { recursive: true });
492
- }
493
- fs2.writeFileSync(fullPath, contentFn(projectName));
494
- createdFiles.push(resolvedPath);
495
- }
496
- const gitignorePath = path2.join(cwd, ".gitignore");
497
- if (!fs2.existsSync(gitignorePath)) {
498
- fs2.writeFileSync(gitignorePath, `node_modules/
499
- dist/
500
- *.tsbuildinfo
501
- `);
502
- createdFiles.push(".gitignore");
503
- }
504
- console.log(chalk4.bold(" Created files:"));
505
- for (const f of createdFiles) {
506
- console.log(chalk4.green(` + ${f}`));
507
- }
508
- console.log("");
509
- if (options.install !== false) {
510
- printStep("Installing dependencies...");
511
- const { execSync: execSync3 } = await import("child_process");
512
- try {
513
- execSync3("pnpm install", { stdio: "inherit", cwd });
514
- } catch {
515
- printWarning2("Dependency installation failed. Run `pnpm install` manually.");
516
- }
517
- }
518
- printSuccess("Project initialized!");
519
- console.log("");
520
- console.log(chalk4.bold(" Next steps:"));
521
- console.log(chalk4.dim(" objectstack validate # Check configuration"));
522
- console.log(chalk4.dim(" objectstack dev # Start development server"));
523
- console.log(chalk4.dim(" objectstack generate # Add objects, views, etc."));
524
- console.log("");
525
- } catch (error) {
526
- printError(error.message || String(error));
527
- process.exit(1);
528
- }
529
- });
530
- function printWarning2(msg) {
531
- console.log(chalk4.yellow(` \u26A0 ${msg}`));
532
- }
533
-
534
- // src/commands/generate.ts
535
- import { Command as Command5 } from "commander";
536
- import chalk5 from "chalk";
537
- import fs3 from "fs";
538
- import path3 from "path";
539
- var GENERATORS = {
540
- object: {
541
- description: "Business data object",
542
- defaultDir: "src/objects",
543
- generate: (name) => `import { Data } from '@objectstack/spec';
544
-
545
- /**
546
- * ${toTitleCase2(name)} Object
547
- */
548
- const ${toCamelCase2(name)}: Data.Object = {
549
- name: '${toSnakeCase(name)}',
550
- label: '${toTitleCase2(name)}',
551
- pluralLabel: '${toTitleCase2(name)}s',
552
- ownership: 'own',
553
- fields: {
554
- name: {
555
- type: 'text',
556
- label: 'Name',
557
- required: true,
558
- maxLength: 255,
559
- },
560
- description: {
561
- type: 'textarea',
562
- label: 'Description',
563
- },
564
- },
565
- };
566
-
567
- export default ${toCamelCase2(name)};
568
- `
569
- },
570
- view: {
571
- description: "List or form view",
572
- defaultDir: "src/views",
573
- generate: (name) => `import { UI } from '@objectstack/spec';
574
-
575
- /**
576
- * ${toTitleCase2(name)} List View
577
- */
578
- const ${toCamelCase2(name)}ListView: UI.View = {
579
- name: '${toSnakeCase(name)}_list',
580
- label: '${toTitleCase2(name)} List',
581
- type: 'list',
582
- objectName: '${toSnakeCase(name)}',
583
- list: {
584
- type: 'grid',
585
- columns: [
586
- { field: 'name', width: 200 },
587
- ],
588
- defaultSort: { field: 'name', direction: 'asc' },
589
- pageSize: 25,
590
- },
591
- };
592
-
593
- export default ${toCamelCase2(name)}ListView;
594
- `
595
- },
596
- action: {
597
- description: "Button or batch action",
598
- defaultDir: "src/actions",
599
- generate: (name) => `import { UI } from '@objectstack/spec';
600
-
601
- /**
602
- * ${toTitleCase2(name)} Action
603
- */
604
- const ${toCamelCase2(name)}Action: UI.Action = {
605
- name: '${toSnakeCase(name)}',
606
- label: '${toTitleCase2(name)}',
607
- type: 'custom',
608
- objectName: '${toSnakeCase(name)}',
609
- handler: {
610
- type: 'flow',
611
- target: '${toSnakeCase(name)}_flow',
612
- },
613
- };
614
-
615
- export default ${toCamelCase2(name)}Action;
616
- `
617
- },
618
- flow: {
619
- description: "Automation flow",
620
- defaultDir: "src/flows",
621
- generate: (name) => `import { Automation } from '@objectstack/spec';
622
-
623
- /**
624
- * ${toTitleCase2(name)} Flow
625
- */
626
- const ${toCamelCase2(name)}Flow: Automation.Flow = {
627
- name: '${toSnakeCase(name)}_flow',
628
- label: '${toTitleCase2(name)} Flow',
629
- type: 'autolaunched',
630
- status: 'draft',
631
- trigger: {
632
- type: 'record_change',
633
- object: '${toSnakeCase(name)}',
634
- events: ['after_insert', 'after_update'],
635
- },
636
- nodes: [
637
- {
638
- id: 'start',
639
- type: 'start',
640
- name: 'Start',
641
- next: 'end',
642
- },
643
- ],
644
- };
645
-
646
- export default ${toCamelCase2(name)}Flow;
647
- `
648
- },
649
- agent: {
650
- description: "AI agent",
651
- defaultDir: "src/agents",
652
- generate: (name) => `import { AI } from '@objectstack/spec';
653
-
654
- /**
655
- * ${toTitleCase2(name)} Agent
656
- */
657
- const ${toCamelCase2(name)}Agent: AI.Agent = {
658
- name: '${toSnakeCase(name)}_agent',
659
- label: '${toTitleCase2(name)} Agent',
660
- role: '${toTitleCase2(name)} assistant',
661
- instructions: 'You are a helpful ${toTitleCase2(name).toLowerCase()} assistant.',
662
- model: {
663
- provider: 'openai',
664
- model: 'gpt-4o',
665
- },
666
- tools: [],
667
- };
668
-
669
- export default ${toCamelCase2(name)}Agent;
670
- `
671
- },
672
- dashboard: {
673
- description: "Analytics dashboard",
674
- defaultDir: "src/dashboards",
675
- generate: (name) => `import { UI } from '@objectstack/spec';
676
-
677
- /**
678
- * ${toTitleCase2(name)} Dashboard
679
- */
680
- const ${toCamelCase2(name)}Dashboard: UI.Dashboard = {
681
- name: '${toSnakeCase(name)}_dashboard',
682
- label: '${toTitleCase2(name)} Dashboard',
683
- widgets: [],
684
- };
685
-
686
- export default ${toCamelCase2(name)}Dashboard;
687
- `
688
- },
689
- app: {
690
- description: "Application navigation",
691
- defaultDir: "src/apps",
692
- generate: (name) => `import { UI } from '@objectstack/spec';
693
-
694
- /**
695
- * ${toTitleCase2(name)} App
696
- */
697
- const ${toCamelCase2(name)}App: UI.App = {
698
- name: '${toSnakeCase(name)}_app',
699
- label: '${toTitleCase2(name)}',
700
- navigation: {
701
- type: 'sidebar',
702
- items: [],
703
- },
704
- };
705
-
706
- export default ${toCamelCase2(name)}App;
707
- `
708
- }
709
- };
710
- function toCamelCase2(str) {
711
- return str.replace(/[-_]([a-z])/g, (_, c) => c.toUpperCase());
712
- }
713
- function toTitleCase2(str) {
714
- return str.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
715
- }
716
- function toSnakeCase(str) {
717
- return str.replace(/[-]/g, "_").replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`).replace(/^_/, "");
718
- }
719
- var FIELD_TYPE_MAP = {
720
- text: "string",
721
- textarea: "string",
722
- richtext: "string",
723
- html: "string",
724
- markdown: "string",
725
- number: "number",
726
- integer: "number",
727
- currency: "number",
728
- percent: "number",
729
- boolean: "boolean",
730
- date: "string",
731
- datetime: "string",
732
- time: "string",
733
- email: "string",
734
- phone: "string",
735
- url: "string",
736
- select: "string",
737
- multiselect: "string[]",
738
- lookup: "string",
739
- master_detail: "string",
740
- formula: "unknown",
741
- autonumber: "string",
742
- json: "Record<string, unknown>",
743
- file: "string",
744
- image: "string",
745
- password: "string",
746
- slug: "string",
747
- uuid: "string",
748
- ip_address: "string",
749
- color: "string",
750
- rating: "number",
751
- geo_point: "{ lat: number; lng: number }",
752
- vector: "number[]",
753
- encrypted: "string"
754
- };
755
- function fieldTypeToTs(fieldType, multiple) {
756
- const base = FIELD_TYPE_MAP[fieldType] || "unknown";
757
- return multiple ? `${base}[]` : base;
758
- }
759
- function generateTypesFromConfig(config) {
760
- const lines = [
761
- "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
762
- `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
763
- "",
764
- "import type { Data } from '@objectstack/spec';",
765
- ""
766
- ];
767
- const objects = [];
768
- const rawObjects = config.objects ?? config.data?.objects ?? {};
769
- if (Array.isArray(rawObjects)) {
770
- objects.push(...rawObjects);
771
- } else if (typeof rawObjects === "object") {
772
- for (const val of Object.values(rawObjects)) {
773
- if (val && typeof val === "object") objects.push(val);
774
- }
775
- }
776
- if (objects.length === 0) {
777
- lines.push("// No objects found in configuration");
778
- return lines.join("\n") + "\n";
779
- }
780
- for (const obj of objects) {
781
- const name = String(obj.name || "unknown");
782
- const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
783
- const fields = obj.fields ?? {};
784
- lines.push(`/** ${String(obj.label || typeName)} record type */`);
785
- lines.push(`export interface ${typeName}Record {`);
786
- lines.push(" id: string;");
787
- for (const [fieldName, fieldDef] of Object.entries(fields)) {
788
- const fType = String(fieldDef.type || "text");
789
- const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
790
- const required = fieldDef.required ? "" : "?";
791
- if (fieldDef.label) {
792
- lines.push(` /** ${fieldDef.label} */`);
793
- }
794
- lines.push(` ${fieldName}${required}: ${tsType};`);
795
- }
796
- lines.push("}");
797
- lines.push("");
798
- }
799
- return lines.join("\n") + "\n";
800
- }
801
- var generateMetadataCommand = new Command5("metadata").alias("m").description("Generate metadata scaffold (object, view, action, flow, agent, dashboard, app)").argument("<type>", "Metadata type to generate").argument("<name>", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").action(async (type, name, options) => {
802
- printHeader("Generate");
803
- const generator = GENERATORS[type];
804
- if (!generator) {
805
- printError(`Unknown type: ${type}`);
806
- console.log("");
807
- console.log(chalk5.bold(" Available types:"));
808
- for (const [key, gen] of Object.entries(GENERATORS)) {
809
- console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
810
- }
811
- console.log("");
812
- console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
813
- console.log(chalk5.dim(" Example: objectstack generate object project"));
814
- console.log(chalk5.dim(" Alias: os g object project"));
815
- process.exit(1);
816
- }
817
- const dir = options.dir || generator.defaultDir;
818
- const fileName = `${toSnakeCase(name)}.ts`;
819
- const filePath = path3.join(process.cwd(), dir, fileName);
820
- console.log(` ${chalk5.dim("Type:")} ${chalk5.cyan(type)} \u2014 ${generator.description}`);
821
- console.log(` ${chalk5.dim("Name:")} ${chalk5.white(name)}`);
822
- console.log(` ${chalk5.dim("File:")} ${chalk5.white(path3.join(dir, fileName))}`);
823
- console.log("");
824
- if (options.dryRun) {
825
- printInfo("Dry run \u2014 no files written");
826
- console.log("");
827
- console.log(chalk5.dim(" Content:"));
828
- console.log(chalk5.dim(" " + "-".repeat(38)));
829
- const content = generator.generate(name);
830
- for (const line of content.split("\n")) {
831
- console.log(chalk5.dim(` ${line}`));
832
- }
833
- console.log("");
834
- return;
835
- }
836
- if (fs3.existsSync(filePath)) {
837
- printError(`File already exists: ${filePath}`);
838
- process.exit(1);
839
- }
840
- try {
841
- const fullDir = path3.dirname(filePath);
842
- if (!fs3.existsSync(fullDir)) {
843
- fs3.mkdirSync(fullDir, { recursive: true });
844
- }
845
- const content = generator.generate(name);
846
- fs3.writeFileSync(filePath, content);
847
- printSuccess(`Created ${path3.join(dir, fileName)}`);
848
- const indexPath = path3.join(process.cwd(), dir, "index.ts");
849
- if (fs3.existsSync(indexPath)) {
850
- const indexContent = fs3.readFileSync(indexPath, "utf-8");
851
- const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';`;
852
- if (!indexContent.includes(toCamelCase2(name))) {
853
- fs3.appendFileSync(indexPath, exportLine + "\n");
854
- printSuccess(`Updated ${dir}/index.ts with export`);
855
- }
856
- } else {
857
- const exportLine = `export { default as ${toCamelCase2(name)} } from './${toSnakeCase(name)}';
858
- `;
859
- fs3.writeFileSync(indexPath, exportLine);
860
- printSuccess(`Created ${dir}/index.ts`);
861
- }
862
- console.log("");
863
- console.log(chalk5.dim(` Tip: Run \`objectstack validate\` to check your config`));
864
- console.log("");
865
- } catch (error) {
866
- printError(error.message || String(error));
867
- process.exit(1);
868
- }
869
- });
870
- var generateTypesCommand = new Command5("types").description("Generate TypeScript type definitions from ObjectStack configuration").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path", "src/types/objectstack.d.ts").option("--dry-run", "Show what would be generated without writing files").action(async (configPath, options) => {
871
- printHeader("Generate Types");
872
- try {
873
- const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
874
- printInfo("Loading configuration...");
875
- const { config, absolutePath } = await loadConfig2(configPath);
876
- console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
877
- console.log(` ${chalk5.dim("Output:")} ${chalk5.white(options.output)}`);
878
- console.log("");
879
- const content = generateTypesFromConfig(config);
880
- if (options.dryRun) {
881
- printInfo("Dry run \u2014 no files written");
882
- console.log("");
883
- for (const line of content.split("\n")) {
884
- console.log(chalk5.dim(` ${line}`));
885
- }
886
- console.log("");
887
- return;
888
- }
889
- const outPath = path3.resolve(process.cwd(), options.output);
890
- const outDir = path3.dirname(outPath);
891
- if (!fs3.existsSync(outDir)) {
892
- fs3.mkdirSync(outDir, { recursive: true });
893
- }
894
- fs3.writeFileSync(outPath, content);
895
- printSuccess(`Generated types at ${options.output}`);
896
- console.log("");
897
- } catch (error) {
898
- printError(error.message || String(error));
899
- process.exit(1);
900
- }
901
- });
902
- function generateClientFromConfig(config) {
903
- const lines = [
904
- "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
905
- `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
906
- "",
907
- "import type { Data } from '@objectstack/spec';",
908
- ""
909
- ];
910
- const objects = [];
911
- const rawObjects = config.objects ?? config.data?.objects ?? {};
912
- if (Array.isArray(rawObjects)) {
913
- objects.push(...rawObjects);
914
- } else if (typeof rawObjects === "object") {
915
- for (const val of Object.values(rawObjects)) {
916
- if (val && typeof val === "object") objects.push(val);
917
- }
918
- }
919
- if (objects.length === 0) {
920
- lines.push("// No objects found in configuration");
921
- return lines.join("\n") + "\n";
922
- }
923
- for (const obj of objects) {
924
- const name = String(obj.name || "unknown");
925
- const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
926
- const fields = obj.fields ?? {};
927
- lines.push(`export interface ${typeName}Record {`);
928
- lines.push(" id: string;");
929
- for (const [fieldName, fieldDef] of Object.entries(fields)) {
930
- const fType = String(fieldDef.type || "text");
931
- const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
932
- const required = fieldDef.required ? "" : "?";
933
- lines.push(` ${fieldName}${required}: ${tsType};`);
934
- }
935
- lines.push("}");
936
- lines.push("");
937
- }
938
- lines.push("export class ObjectStackClient {");
939
- lines.push(" constructor(private baseUrl: string, private headers: Record<string, string> = {}) {}");
940
- lines.push("");
941
- lines.push(" private async request<T>(method: string, path: string, body?: unknown): Promise<T> {");
942
- lines.push(" const res = await fetch(`${this.baseUrl}${path}`, {");
943
- lines.push(" method,");
944
- lines.push(" headers: { 'Content-Type': 'application/json', ...this.headers },");
945
- lines.push(" body: body ? JSON.stringify(body) : undefined,");
946
- lines.push(" });");
947
- lines.push(" if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);");
948
- lines.push(" return res.json() as Promise<T>;");
949
- lines.push(" }");
950
- for (const obj of objects) {
951
- const name = String(obj.name || "unknown");
952
- const typeName = name.split("_").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
953
- const endpoint = `/api/${name}`;
954
- lines.push("");
955
- lines.push(` async list${typeName}(): Promise<${typeName}Record[]> {`);
956
- lines.push(` return this.request<${typeName}Record[]>('GET', '${endpoint}');`);
957
- lines.push(" }");
958
- lines.push("");
959
- lines.push(` async get${typeName}(id: string): Promise<${typeName}Record> {`);
960
- lines.push(` return this.request<${typeName}Record>('GET', '${endpoint}/\${id}');`);
961
- lines.push(" }");
962
- lines.push("");
963
- lines.push(` async create${typeName}(data: Omit<${typeName}Record, 'id'>): Promise<${typeName}Record> {`);
964
- lines.push(` return this.request<${typeName}Record>('POST', '${endpoint}', data);`);
965
- lines.push(" }");
966
- lines.push("");
967
- lines.push(` async update${typeName}(id: string, data: Partial<${typeName}Record>): Promise<${typeName}Record> {`);
968
- lines.push(` return this.request<${typeName}Record>('PATCH', '${endpoint}/\${id}', data);`);
969
- lines.push(" }");
970
- lines.push("");
971
- lines.push(` async delete${typeName}(id: string): Promise<void> {`);
972
- lines.push(` return this.request<void>('DELETE', '${endpoint}/\${id}');`);
973
- lines.push(" }");
974
- }
975
- lines.push("}");
976
- lines.push("");
977
- return lines.join("\n") + "\n";
978
- }
979
- var generateClientCommand = new Command5("client").description("Generate a type-safe client SDK from ObjectStack configuration").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path", "src/client/objectstack-client.ts").option("--dry-run", "Show output without writing").action(async (configPath, options) => {
980
- printHeader("Generate Client SDK");
981
- try {
982
- const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
983
- const timer = createTimer();
984
- printInfo("Loading configuration...");
985
- const { config, absolutePath } = await loadConfig2(configPath);
986
- console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
987
- console.log(` ${chalk5.dim("Output:")} ${chalk5.white(options.output)}`);
988
- console.log("");
989
- printStep("Generating client SDK...");
990
- const content = generateClientFromConfig(config);
991
- if (options.dryRun) {
992
- printInfo("Dry run \u2014 no files written");
993
- console.log("");
994
- for (const line of content.split("\n")) {
995
- console.log(chalk5.dim(` ${line}`));
996
- }
997
- console.log("");
998
- return;
999
- }
1000
- const outPath = path3.resolve(process.cwd(), options.output);
1001
- const outDir = path3.dirname(outPath);
1002
- if (!fs3.existsSync(outDir)) {
1003
- fs3.mkdirSync(outDir, { recursive: true });
1004
- }
1005
- fs3.writeFileSync(outPath, content);
1006
- printSuccess(`Generated client SDK at ${options.output} (${timer.display()})`);
1007
- console.log("");
1008
- } catch (error) {
1009
- printError(error.message || String(error));
1010
- process.exit(1);
1011
- }
1012
- });
1013
- var FIELD_TYPE_SQL_MAP = {
1014
- text: "VARCHAR(255)",
1015
- textarea: "TEXT",
1016
- richtext: "TEXT",
1017
- html: "TEXT",
1018
- markdown: "TEXT",
1019
- number: "DECIMAL(18,2)",
1020
- integer: "INTEGER",
1021
- currency: "DECIMAL(18,2)",
1022
- percent: "DECIMAL(5,2)",
1023
- boolean: "BOOLEAN",
1024
- date: "DATE",
1025
- datetime: "TIMESTAMP",
1026
- time: "TIME",
1027
- email: "VARCHAR(255)",
1028
- phone: "VARCHAR(50)",
1029
- url: "VARCHAR(2048)",
1030
- select: "VARCHAR(255)",
1031
- multiselect: "TEXT",
1032
- lookup: "VARCHAR(36)",
1033
- master_detail: "VARCHAR(36)",
1034
- formula: "TEXT",
1035
- autonumber: "SERIAL",
1036
- json: "JSONB",
1037
- file: "VARCHAR(2048)",
1038
- image: "VARCHAR(2048)",
1039
- password: "VARCHAR(255)",
1040
- slug: "VARCHAR(255)",
1041
- uuid: "UUID",
1042
- ip_address: "VARCHAR(45)",
1043
- color: "VARCHAR(7)",
1044
- rating: "INTEGER",
1045
- geo_point: "POINT",
1046
- vector: "VECTOR",
1047
- encrypted: "TEXT"
1048
- };
1049
- function fieldTypeToSql(fieldType) {
1050
- return FIELD_TYPE_SQL_MAP[fieldType] || "TEXT";
1051
- }
1052
- function generateMigrationSql(config) {
1053
- const lines = [
1054
- "-- Auto-generated by ObjectStack CLI \u2014 do not edit manually",
1055
- `-- Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
1056
- ""
1057
- ];
1058
- const objects = [];
1059
- const rawObjects = config.objects ?? config.data?.objects ?? {};
1060
- if (Array.isArray(rawObjects)) {
1061
- objects.push(...rawObjects);
1062
- } else if (typeof rawObjects === "object") {
1063
- for (const val of Object.values(rawObjects)) {
1064
- if (val && typeof val === "object") objects.push(val);
1065
- }
1066
- }
1067
- if (objects.length === 0) {
1068
- lines.push("-- No objects found in configuration");
1069
- return lines.join("\n") + "\n";
1070
- }
1071
- for (const obj of objects) {
1072
- const tableName = String(obj.name || "unknown");
1073
- const fields = obj.fields ?? {};
1074
- lines.push(`CREATE TABLE IF NOT EXISTS "${tableName}" (`);
1075
- lines.push(' "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),');
1076
- const fieldLines = [];
1077
- for (const [fieldName, fieldDef] of Object.entries(fields)) {
1078
- const sqlType = fieldTypeToSql(String(fieldDef.type || "text"));
1079
- const notNull = fieldDef.required ? " NOT NULL" : "";
1080
- fieldLines.push(` "${fieldName}" ${sqlType}${notNull}`);
1081
- }
1082
- fieldLines.push(' "created_at" TIMESTAMP NOT NULL DEFAULT now()');
1083
- fieldLines.push(' "updated_at" TIMESTAMP NOT NULL DEFAULT now()');
1084
- lines.push(fieldLines.join(",\n"));
1085
- lines.push(");");
1086
- lines.push("");
1087
- }
1088
- return lines.join("\n") + "\n";
1089
- }
1090
- function generateMigrationTs(config) {
1091
- const lines = [
1092
- "// Auto-generated by ObjectStack CLI \u2014 do not edit manually",
1093
- `// Generated at ${(/* @__PURE__ */ new Date()).toISOString()}`,
1094
- "",
1095
- "export async function up(db: any): Promise<void> {"
1096
- ];
1097
- const objects = [];
1098
- const rawObjects = config.objects ?? config.data?.objects ?? {};
1099
- if (Array.isArray(rawObjects)) {
1100
- objects.push(...rawObjects);
1101
- } else if (typeof rawObjects === "object") {
1102
- for (const val of Object.values(rawObjects)) {
1103
- if (val && typeof val === "object") objects.push(val);
1104
- }
1105
- }
1106
- if (objects.length === 0) {
1107
- lines.push(" // No objects found in configuration");
1108
- lines.push("}");
1109
- lines.push("");
1110
- lines.push("export async function down(db: any): Promise<void> {");
1111
- lines.push(" // No objects found in configuration");
1112
- lines.push("}");
1113
- return lines.join("\n") + "\n";
1114
- }
1115
- for (const obj of objects) {
1116
- const tableName = String(obj.name || "unknown");
1117
- const fields = obj.fields ?? {};
1118
- lines.push(` await db.schema.createTable('${tableName}', (table: any) => {`);
1119
- lines.push(" table.uuid('id').primary().defaultTo(db.fn.uuid());");
1120
- for (const [fieldName, fieldDef] of Object.entries(fields)) {
1121
- const fType = String(fieldDef.type || "text");
1122
- const required = fieldDef.required ? ".notNullable()" : ".nullable()";
1123
- let colMethod;
1124
- switch (fType) {
1125
- case "text":
1126
- case "email":
1127
- case "phone":
1128
- case "url":
1129
- case "select":
1130
- case "slug":
1131
- case "password":
1132
- case "color":
1133
- case "ip_address":
1134
- colMethod = `table.string('${fieldName}')`;
1135
- break;
1136
- case "textarea":
1137
- case "richtext":
1138
- case "html":
1139
- case "markdown":
1140
- case "formula":
1141
- case "encrypted":
1142
- colMethod = `table.text('${fieldName}')`;
1143
- break;
1144
- case "number":
1145
- case "currency":
1146
- case "percent":
1147
- colMethod = `table.decimal('${fieldName}')`;
1148
- break;
1149
- case "integer":
1150
- case "rating":
1151
- colMethod = `table.integer('${fieldName}')`;
1152
- break;
1153
- case "boolean":
1154
- colMethod = `table.boolean('${fieldName}')`;
1155
- break;
1156
- case "date":
1157
- colMethod = `table.date('${fieldName}')`;
1158
- break;
1159
- case "datetime":
1160
- colMethod = `table.timestamp('${fieldName}')`;
1161
- break;
1162
- case "time":
1163
- colMethod = `table.time('${fieldName}')`;
1164
- break;
1165
- case "json":
1166
- case "multiselect":
1167
- colMethod = `table.jsonb('${fieldName}')`;
1168
- break;
1169
- case "uuid":
1170
- case "lookup":
1171
- case "master_detail":
1172
- colMethod = `table.uuid('${fieldName}')`;
1173
- break;
1174
- default:
1175
- colMethod = `table.text('${fieldName}')`;
1176
- }
1177
- lines.push(` ${colMethod}${required};`);
1178
- }
1179
- lines.push(" table.timestamps(true, true);");
1180
- lines.push(" });");
1181
- }
1182
- lines.push("}");
1183
- lines.push("");
1184
- lines.push("export async function down(db: any): Promise<void> {");
1185
- const tableNames = objects.map((o) => String(o.name || "unknown")).reverse();
1186
- for (const tableName of tableNames) {
1187
- lines.push(` await db.schema.dropTableIfExists('${tableName}');`);
1188
- }
1189
- lines.push("}");
1190
- return lines.join("\n") + "\n";
1191
- }
1192
- var generateMigrationCommand = new Command5("migration").description("Generate database migration from ObjectStack schema").argument("[config]", "Configuration file path").option("-o, --output <file>", "Output file path").option("--format <format>", "Output format: sql or typescript", "typescript").option("--dry-run", "Show output without writing").action(async (configPath, options) => {
1193
- printHeader("Generate Migration");
1194
- try {
1195
- const { loadConfig: loadConfig2 } = await import("./config-UN34WBHT.js");
1196
- const timer = createTimer();
1197
- printInfo("Loading configuration...");
1198
- const { config, absolutePath } = await loadConfig2(configPath);
1199
- const ext = options.format === "sql" ? "sql" : "ts";
1200
- const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, "").slice(0, 14);
1201
- const defaultOutput = `migrations/${timestamp}_migration.${ext}`;
1202
- const output = options.output || defaultOutput;
1203
- console.log(` ${chalk5.dim("Config:")} ${chalk5.white(absolutePath)}`);
1204
- console.log(` ${chalk5.dim("Format:")} ${chalk5.white(options.format)}`);
1205
- console.log(` ${chalk5.dim("Output:")} ${chalk5.white(output)}`);
1206
- console.log("");
1207
- printStep("Generating migration...");
1208
- const content = options.format === "sql" ? generateMigrationSql(config) : generateMigrationTs(config);
1209
- if (options.dryRun) {
1210
- printInfo("Dry run \u2014 no files written");
1211
- console.log("");
1212
- for (const line of content.split("\n")) {
1213
- console.log(chalk5.dim(` ${line}`));
1214
- }
1215
- console.log("");
1216
- return;
1217
- }
1218
- const outPath = path3.resolve(process.cwd(), output);
1219
- const outDir = path3.dirname(outPath);
1220
- if (!fs3.existsSync(outDir)) {
1221
- fs3.mkdirSync(outDir, { recursive: true });
1222
- }
1223
- fs3.writeFileSync(outPath, content);
1224
- printSuccess(`Generated migration at ${output} (${timer.display()})`);
1225
- console.log("");
1226
- } catch (error) {
1227
- printError(error.message || String(error));
1228
- process.exit(1);
1229
- }
1230
- });
1231
- var generateSchemaCommand = new Command5("schema").description("Generate JSON Schema for objectstack.config.ts (for IDE autocomplete)").option("-o, --output <file>", "Output file path", "objectstack.schema.json").option("--dry-run", "Show output without writing").action(async (options) => {
1232
- printHeader("Generate Schema");
1233
- try {
1234
- const timer = createTimer();
1235
- printStep("Loading ObjectStackDefinitionSchema...");
1236
- const { z } = await import("zod");
1237
- const { ObjectStackDefinitionSchema: ObjectStackDefinitionSchema3 } = await import("@objectstack/spec");
1238
- printStep("Converting to JSON Schema...");
1239
- const jsonSchema = z.toJSONSchema(ObjectStackDefinitionSchema3, {
1240
- target: "draft-2020-12"
1241
- });
1242
- const schema = {
1243
- ...jsonSchema,
1244
- $id: "https://schema.objectstack.io/objectstack.config.json",
1245
- title: "ObjectStack Configuration",
1246
- description: "JSON Schema for objectstack.config.ts \u2014 generated from ObjectStackDefinitionSchema"
1247
- };
1248
- const content = JSON.stringify(schema, null, 2) + "\n";
1249
- if (options.dryRun) {
1250
- printInfo("Dry run \u2014 no files written");
1251
- console.log("");
1252
- console.log(content);
1253
- return;
1254
- }
1255
- const outPath = path3.resolve(process.cwd(), options.output);
1256
- const outDir = path3.dirname(outPath);
1257
- if (!fs3.existsSync(outDir)) {
1258
- fs3.mkdirSync(outDir, { recursive: true });
1259
- }
1260
- fs3.writeFileSync(outPath, content);
1261
- printSuccess(`Generated JSON Schema at ${options.output} (${timer.display()})`);
1262
- console.log("");
1263
- console.log(chalk5.dim(" Usage: Reference in your IDE or editor for autocomplete"));
1264
- console.log(chalk5.dim(` Path: ${outPath}`));
1265
- console.log("");
1266
- } catch (error) {
1267
- printError(error.message || String(error));
1268
- process.exit(1);
1269
- }
1270
- });
1271
- var generateCommand = new Command5("generate").alias("g").description("Generate metadata files or TypeScript types").argument("[type]", "Metadata type to generate (object, view, action, flow, agent, dashboard, app)").argument("[name]", "Name for the metadata (use kebab-case)").option("-d, --dir <directory>", "Target directory (overrides default)").option("--dry-run", "Show what would be created without writing files").addCommand(generateTypesCommand).addCommand(generateClientCommand).addCommand(generateMigrationCommand).addCommand(generateSchemaCommand).action(async (type, name, options) => {
1272
- if (!type) {
1273
- printHeader("Generate");
1274
- console.log(chalk5.bold(" Sub-commands:"));
1275
- console.log(` ${chalk5.cyan("types".padEnd(12))} Generate TypeScript type definitions from config`);
1276
- console.log(` ${chalk5.cyan("client".padEnd(12))} Generate a type-safe client SDK from config`);
1277
- console.log(` ${chalk5.cyan("migration".padEnd(12))} Generate database migration from schema`);
1278
- console.log(` ${chalk5.cyan("schema".padEnd(12))} Generate JSON Schema for objectstack.config.ts (IDE autocomplete)`);
1279
- console.log("");
1280
- console.log(chalk5.bold(" Metadata types:"));
1281
- for (const [key, gen] of Object.entries(GENERATORS)) {
1282
- console.log(` ${chalk5.cyan(key.padEnd(12))} ${chalk5.dim(gen.description)}`);
1283
- }
1284
- console.log("");
1285
- console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
1286
- console.log(chalk5.dim(" Usage: objectstack generate types [config]"));
1287
- return;
1288
- }
1289
- if (!name) {
1290
- printError("Missing required argument: <name>");
1291
- console.log(chalk5.dim(" Usage: objectstack generate <type> <name>"));
1292
- process.exit(1);
1293
- }
1294
- await generateMetadataCommand.parseAsync([type, name, ...process.argv.slice(4)], { from: "user" });
1295
- });
1296
-
1297
- // src/commands/create.ts
1298
- import { Command as Command6 } from "commander";
1299
- import chalk6 from "chalk";
1300
- import fs4 from "fs";
1301
- import path4 from "path";
1302
- var templates = {
1303
- plugin: {
1304
- description: "Create a new ObjectStack plugin",
1305
- files: {
1306
- "package.json": (name) => ({
1307
- name: `@objectstack/plugin-${name}`,
1308
- version: "0.1.0",
1309
- description: `ObjectStack Plugin: ${name}`,
1310
- main: "dist/index.js",
1311
- types: "dist/index.d.ts",
1312
- scripts: {
1313
- build: "tsc",
1314
- dev: "tsc --watch",
1315
- test: "vitest"
1316
- },
1317
- keywords: ["objectstack", "plugin", name],
1318
- author: "",
1319
- license: "MIT",
1320
- dependencies: {
1321
- "@objectstack/spec": "workspace:*",
1322
- zod: "^4.3.6"
1323
- },
1324
- devDependencies: {
1325
- "@types/node": "^22.0.0",
1326
- typescript: "^5.8.0",
1327
- vitest: "^4.0.0"
1328
- }
1329
- }),
1330
- "tsconfig.json": () => ({
1331
- extends: "../../tsconfig.json",
1332
- compilerOptions: {
1333
- outDir: "dist",
1334
- rootDir: "src"
1335
- },
1336
- include: ["src/**/*"]
1337
- }),
1338
- "src/index.ts": (name) => `import type { Plugin } from '@objectstack/spec';
1339
-
1340
- /**
1341
- * ${name} Plugin for ObjectStack
1342
- */
1343
- export const ${toCamelCase3(name)}Plugin: Plugin = {
1344
- name: '${name}',
1345
- version: '0.1.0',
1346
-
1347
- async initialize(context) {
1348
- console.log('Initializing ${name} plugin...');
1349
- // Plugin initialization logic
1350
- },
1351
-
1352
- async destroy() {
1353
- console.log('Destroying ${name} plugin...');
1354
- // Plugin cleanup logic
1355
- },
1356
- };
1357
-
1358
- export default ${toCamelCase3(name)}Plugin;
1359
- `,
1360
- "README.md": (name) => `# @objectstack/plugin-${name}
1361
-
1362
- ObjectStack Plugin: ${name}
1363
-
1364
- ## Installation
1365
-
1366
- \`\`\`bash
1367
- pnpm add @objectstack/plugin-${name}
1368
- \`\`\`
1369
-
1370
- ## Usage
1371
-
1372
- \`\`\`typescript
1373
- import { ${toCamelCase3(name)}Plugin } from '@objectstack/plugin-${name}';
1374
-
1375
- // Use the plugin in your ObjectStack configuration
1376
- export default {
1377
- plugins: [
1378
- ${toCamelCase3(name)}Plugin,
1379
- ],
1380
- };
1381
- \`\`\`
1382
-
1383
- ## License
1384
-
1385
- MIT
1386
- `
1387
- }
1388
- },
1389
- example: {
1390
- description: "Create a new ObjectStack example application",
1391
- files: {
1392
- "package.json": (name) => ({
1393
- name: `@example/${name}`,
1394
- version: "0.1.0",
1395
- private: true,
1396
- description: `ObjectStack Example: ${name}`,
1397
- scripts: {
1398
- build: "objectstack compile",
1399
- dev: "objectstack dev",
1400
- test: "vitest"
1401
- },
1402
- dependencies: {
1403
- "@objectstack/spec": "workspace:*",
1404
- "@objectstack/cli": "workspace:*",
1405
- zod: "^4.3.6"
1406
- },
1407
- devDependencies: {
1408
- "@types/node": "^22.0.0",
1409
- tsx: "^4.21.0",
1410
- typescript: "^5.8.0",
1411
- vitest: "^4.0.0"
1412
- }
1413
- }),
1414
- "objectstack.config.ts": (name) => `import { defineStack } from '@objectstack/spec';
1415
-
1416
- // Barrel imports \u2014 add more as you create new type folders
1417
- // import * as objects from './src/objects';
1418
- // import * as actions from './src/actions';
1419
- // import * as apps from './src/apps';
1420
-
1421
- export default defineStack({
1422
- manifest: {
1423
- name: '${name}',
1424
- version: '0.1.0',
1425
- description: '${name} example application',
1426
- },
1427
-
1428
- objects: [
1429
- // Object.values(objects), // Uncomment after creating src/objects/index.ts
1430
- ],
1431
-
1432
- apps: [
1433
- // Object.values(apps), // Uncomment after creating src/apps/index.ts
1434
- ],
1435
- });
1436
- `,
1437
- "README.md": (name) => `# ${name} Example
1438
-
1439
- ObjectStack example application: ${name}
1440
-
1441
- ## Quick Start
1442
-
1443
- \`\`\`bash
1444
- # Build the configuration
1445
- pnpm build
1446
-
1447
- # Run in development mode
1448
- pnpm dev
1449
- \`\`\`
1450
-
1451
- ## Structure
1452
-
1453
- - \`objectstack.config.ts\` - Main configuration file
1454
- - \`dist/objectstack.json\` - Compiled artifact
1455
-
1456
- ## Learn More
1457
-
1458
- - [ObjectStack Documentation](../../content/docs)
1459
- - [Examples](../)
1460
- `,
1461
- "tsconfig.json": () => ({
1462
- extends: "../../tsconfig.json",
1463
- compilerOptions: {
1464
- outDir: "dist",
1465
- rootDir: "."
1466
- },
1467
- include: ["*.ts", "src/**/*"]
1468
- })
1469
- }
1470
- }
1471
- };
1472
- function toCamelCase3(str) {
1473
- return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
1474
- }
1475
- var createCommand = new Command6("create").description("Create a new package, plugin, or example from template").argument("<type>", "Type of project to create (plugin, example)").argument("[name]", "Name of the project").option("-d, --dir <directory>", "Target directory").action(async (type, name, options) => {
1476
- console.log(chalk6.bold(`
1477
- \u{1F4E6} ObjectStack Project Creator`));
1478
- console.log(chalk6.dim(`-------------------------------`));
1479
- if (!templates[type]) {
1480
- console.error(chalk6.red(`
1481
- \u274C Unknown type: ${type}`));
1482
- console.log(chalk6.dim("Available types: plugin, example"));
1483
- process.exit(1);
1484
- }
1485
- if (!name) {
1486
- console.error(chalk6.red("\n\u274C Project name is required"));
1487
- console.log(chalk6.dim(`Usage: objectstack create ${type} <name>`));
1488
- process.exit(1);
1489
- }
1490
- const template = templates[type];
1491
- const cwd = process.cwd();
1492
- let targetDir;
1493
- if (options?.dir) {
1494
- targetDir = path4.resolve(cwd, options.dir);
1495
- } else {
1496
- const baseDir = type === "plugin" ? "packages/plugins" : "examples";
1497
- const projectName = type === "plugin" ? `plugin-${name}` : name;
1498
- targetDir = path4.join(cwd, baseDir, projectName);
1499
- }
1500
- if (fs4.existsSync(targetDir)) {
1501
- console.error(chalk6.red(`
1502
- \u274C Directory already exists: ${targetDir}`));
1503
- process.exit(1);
1504
- }
1505
- console.log(`\u{1F4C1} Creating ${type}: ${chalk6.blue(name)}`);
1506
- console.log(`\u{1F4C2} Location: ${chalk6.dim(targetDir)}`);
1507
- console.log("");
1508
- try {
1509
- fs4.mkdirSync(targetDir, { recursive: true });
1510
- for (const [filePath, contentFn] of Object.entries(template.files)) {
1511
- const fullPath = path4.join(targetDir, filePath);
1512
- const dir = path4.dirname(fullPath);
1513
- if (!fs4.existsSync(dir)) {
1514
- fs4.mkdirSync(dir, { recursive: true });
1515
- }
1516
- const content = contentFn(name);
1517
- const fileContent = typeof content === "string" ? content : JSON.stringify(content, null, 2);
1518
- fs4.writeFileSync(fullPath, fileContent);
1519
- console.log(chalk6.green(`\u2713 Created ${filePath}`));
1520
- }
1521
- console.log("");
1522
- console.log(chalk6.green("\u2705 Project created successfully!"));
1523
- console.log("");
1524
- console.log(chalk6.bold("Next steps:"));
1525
- console.log(chalk6.dim(` cd ${path4.relative(cwd, targetDir)}`));
1526
- console.log(chalk6.dim(" pnpm install"));
1527
- console.log(chalk6.dim(" pnpm build"));
1528
- console.log("");
1529
- } catch (error) {
1530
- console.error(chalk6.red("\n\u274C Failed to create project:"));
1531
- console.error(error.message || error);
1532
- if (fs4.existsSync(targetDir)) {
1533
- fs4.rmSync(targetDir, { recursive: true });
1534
- }
1535
- process.exit(1);
1536
- }
1537
- });
1538
-
1539
- // src/commands/plugin.ts
1540
- import { Command as Command7 } from "commander";
1541
- import chalk7 from "chalk";
1542
- import fs5 from "fs";
1543
- import path5 from "path";
1544
- function resolvePluginName(plugin) {
1545
- if (typeof plugin === "string") return plugin;
1546
- if (plugin && typeof plugin === "object") {
1547
- const p = plugin;
1548
- if (typeof p.name === "string") return p.name;
1549
- if (p.constructor && p.constructor.name !== "Object") return p.constructor.name;
1550
- }
1551
- return "unknown";
1552
- }
1553
- function resolvePluginVersion(plugin) {
1554
- if (plugin && typeof plugin === "object") {
1555
- const p = plugin;
1556
- if (typeof p.version === "string") return p.version;
1557
- }
1558
- return "-";
1559
- }
1560
- function resolvePluginType(plugin) {
1561
- if (plugin && typeof plugin === "object") {
1562
- const p = plugin;
1563
- if (typeof p.type === "string") return p.type;
1564
- }
1565
- return "standard";
1566
- }
1567
- function readConfigText(configPath) {
1568
- return fs5.readFileSync(configPath, "utf-8");
1569
- }
1570
- function addPluginToConfig(configPath, packageName) {
1571
- let content = readConfigText(configPath);
1572
- const shortName = packageName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1573
- const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1574
- const importLine = `import ${varName} from '${packageName}';
1575
- `;
1576
- if (content.includes(packageName)) {
1577
- throw new Error(`Plugin '${packageName}' is already referenced in the config`);
1578
- }
1579
- const importRegex = /^import .+$/gm;
1580
- let lastImportEnd = 0;
1581
- let match;
1582
- while ((match = importRegex.exec(content)) !== null) {
1583
- lastImportEnd = match.index + match[0].length;
1584
- }
1585
- if (lastImportEnd > 0) {
1586
- content = content.slice(0, lastImportEnd) + "\n" + importLine + content.slice(lastImportEnd);
1587
- } else {
1588
- content = importLine + "\n" + content;
1589
- }
1590
- if (/plugins\s*:\s*\[/.test(content)) {
1591
- let replaced = false;
1592
- content = content.replace(
1593
- /(plugins\s*:\s*\[)/,
1594
- (match2) => {
1595
- if (replaced) return match2;
1596
- replaced = true;
1597
- return `${match2}
1598
- ${varName},`;
1599
- }
1600
- );
1601
- } else {
1602
- content = content.replace(
1603
- /(defineStack\(\{[\s\S]*?)(}\s*\))/,
1604
- `$1 plugins: [
1605
- ${varName},
1606
- ],
1607
- $2`
1608
- );
1609
- }
1610
- fs5.writeFileSync(configPath, content);
1611
- }
1612
- function removePluginFromConfig(configPath, pluginName) {
1613
- let content = readConfigText(configPath);
1614
- const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, "gm");
1615
- const hadImport = importRegex.test(content);
1616
- importRegex.lastIndex = 0;
1617
- content = content.replace(importRegex, "");
1618
- const shortName = pluginName.replace(/^@[^/]+\//, "").replace(/^plugin-/, "").replace(/-+/g, "-").replace(/^-|-$/g, "");
1619
- const varName = shortName.replace(/-([a-z])/g, (_, c) => c.toUpperCase()) + "Plugin";
1620
- if (!hadImport) {
1621
- const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, "gm");
1622
- content = content.replace(varImportRegex, "");
1623
- }
1624
- const entryPatterns = [
1625
- new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, "g"),
1626
- new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, "g")
1627
- ];
1628
- for (const pattern of entryPatterns) {
1629
- content = content.replace(pattern, "\n");
1630
- }
1631
- content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, "");
1632
- fs5.writeFileSync(configPath, content);
1633
- }
1634
- function escapeRegex(str) {
1635
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1636
- }
1637
- var listCommand = new Command7("list").alias("ls").description("List plugins defined in the configuration").argument("[config]", "Configuration file path").option("--json", "Output as JSON").action(async (configSource, options) => {
1638
- try {
1639
- const { config } = await loadConfig(configSource);
1640
- const plugins = config.plugins || [];
1641
- const devPlugins = config.devPlugins || [];
1642
- if (options?.json) {
1643
- const data = {
1644
- plugins: plugins.map((p) => ({
1645
- name: resolvePluginName(p),
1646
- version: resolvePluginVersion(p),
1647
- type: resolvePluginType(p),
1648
- dev: false
1649
- })),
1650
- devPlugins: devPlugins.map((p) => ({
1651
- name: resolvePluginName(p),
1652
- version: resolvePluginVersion(p),
1653
- type: resolvePluginType(p),
1654
- dev: true
1655
- }))
1656
- };
1657
- console.log(JSON.stringify(data, null, 2));
1658
- return;
1659
- }
1660
- printHeader("Plugins");
1661
- if (plugins.length === 0 && devPlugins.length === 0) {
1662
- printInfo("No plugins configured");
1663
- console.log("");
1664
- console.log(chalk7.dim(" Hint: Add plugins to your objectstack.config.ts"));
1665
- console.log(chalk7.dim(" Or run: os plugin add <package-name>"));
1666
- console.log("");
1667
- return;
1668
- }
1669
- if (plugins.length > 0) {
1670
- console.log(chalk7.bold(`
1671
- Plugins (${plugins.length}):`));
1672
- for (const plugin of plugins) {
1673
- const name = resolvePluginName(plugin);
1674
- const version = resolvePluginVersion(plugin);
1675
- const type = resolvePluginType(plugin);
1676
- console.log(
1677
- ` ${chalk7.cyan("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + (type !== "standard" ? chalk7.dim(` [${type}]`) : "")
1678
- );
1679
- }
1680
- }
1681
- if (devPlugins.length > 0) {
1682
- console.log(chalk7.bold(`
1683
- Dev Plugins (${devPlugins.length}):`));
1684
- for (const plugin of devPlugins) {
1685
- const name = resolvePluginName(plugin);
1686
- const version = resolvePluginVersion(plugin);
1687
- console.log(
1688
- ` ${chalk7.yellow("\u25CF")} ${chalk7.white(name)}` + (version !== "-" ? chalk7.dim(` v${version}`) : "") + chalk7.dim(" [dev]")
1689
- );
1690
- }
1691
- }
1692
- console.log("");
1693
- } catch (error) {
1694
- printError(error.message || String(error));
1695
- process.exit(1);
1696
- }
1697
- });
1698
- var infoSubCommand = new Command7("info").description("Show detailed information about a plugin").argument("<name>", "Plugin name or package name").argument("[config]", "Configuration file path").action(async (name, configSource) => {
1699
- try {
1700
- const { config } = await loadConfig(configSource);
1701
- const allPlugins = [
1702
- ...config.plugins || [],
1703
- ...config.devPlugins || []
1704
- ];
1705
- const found = allPlugins.find((p) => {
1706
- const pName = resolvePluginName(p);
1707
- return pName === name || pName.includes(name);
1708
- });
1709
- if (!found) {
1710
- printError(`Plugin '${name}' not found in configuration`);
1711
- console.log("");
1712
- console.log(chalk7.dim(" Available plugins:"));
1713
- for (const p of allPlugins) {
1714
- console.log(chalk7.dim(` - ${resolvePluginName(p)}`));
1715
- }
1716
- console.log("");
1717
- process.exit(1);
1718
- }
1719
- printHeader(`Plugin: ${resolvePluginName(found)}`);
1720
- printKV("Name", resolvePluginName(found));
1721
- printKV("Version", resolvePluginVersion(found));
1722
- printKV("Type", resolvePluginType(found));
1723
- const isDev = (config.devPlugins || []).includes(found);
1724
- printKV("Environment", isDev ? "development" : "production");
1725
- if (found && typeof found === "object") {
1726
- const p = found;
1727
- if (typeof p.description === "string") {
1728
- printKV("Description", p.description);
1729
- }
1730
- if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
1731
- printKV("Dependencies", p.dependencies.join(", "));
1732
- }
1733
- if (typeof p.init === "function") {
1734
- printInfo("This is a runtime plugin instance (has init function)");
1735
- }
1736
- }
1737
- if (typeof found === "string") {
1738
- printInfo("This is a string reference (will be imported at runtime)");
1739
- }
1740
- console.log("");
1741
- } catch (error) {
1742
- printError(error.message || String(error));
1743
- process.exit(1);
1744
- }
1745
- });
1746
- var addCommand = new Command7("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) => {
1747
- try {
1748
- const configPath = resolveConfigPath(options?.config);
1749
- printHeader("Add Plugin");
1750
- console.log(` ${chalk7.dim("Package:")} ${chalk7.white(packageName)}`);
1751
- console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1752
- console.log("");
1753
- addPluginToConfig(configPath, packageName);
1754
- printSuccess(`Added ${chalk7.cyan(packageName)} to config`);
1755
- console.log("");
1756
- console.log(chalk7.dim(" Next steps:"));
1757
- console.log(chalk7.dim(` 1. Install the package: pnpm add ${packageName}`));
1758
- console.log(chalk7.dim(" 2. Run: os validate"));
1759
- console.log("");
1760
- } catch (error) {
1761
- printError(error.message || String(error));
1762
- process.exit(1);
1763
- }
1764
- });
1765
- var removeCommand = new Command7("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) => {
1766
- try {
1767
- const configPath = resolveConfigPath(options?.config);
1768
- printHeader("Remove Plugin");
1769
- console.log(` ${chalk7.dim("Plugin:")} ${chalk7.white(pluginName)}`);
1770
- console.log(` ${chalk7.dim("Config:")} ${chalk7.white(path5.relative(process.cwd(), configPath))}`);
1771
- console.log("");
1772
- removePluginFromConfig(configPath, pluginName);
1773
- printSuccess(`Removed ${chalk7.cyan(pluginName)} from config`);
1774
- console.log("");
1775
- console.log(chalk7.dim(" Tip: Run `pnpm remove " + pluginName + "` to uninstall the package"));
1776
- console.log("");
1777
- } catch (error) {
1778
- printError(error.message || String(error));
1779
- process.exit(1);
1780
- }
1781
- });
1782
- var pluginCommand = new Command7("plugin").description("Manage plugins (list, info, add, remove)").addCommand(listCommand).addCommand(infoSubCommand).addCommand(addCommand).addCommand(removeCommand);
1783
-
1784
- // src/commands/dev.ts
1785
- import { Command as Command8 } from "commander";
1786
- import chalk8 from "chalk";
1787
- import { execSync, spawn } from "child_process";
1788
- import fs6 from "fs";
1789
- import path6 from "path";
1790
- var devCommand = new Command8("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1791
- printHeader("Development Mode");
1792
- const configPath = path6.resolve(process.cwd(), "objectstack.config.ts");
1793
- if (packageName === "all" && fs6.existsSync(configPath)) {
1794
- printKV("Config", configPath, "\u{1F4C2}");
1795
- printStep("Starting dev server...");
1796
- const binPath = process.argv[1];
1797
- const child = spawn(process.execPath, [binPath, "serve", "--dev", ...options.ui ? ["--ui"] : [], ...options.verbose ? ["--verbose"] : []], {
1798
- stdio: "inherit",
1799
- env: { ...process.env, NODE_ENV: "development" }
1800
- });
1801
- return;
1802
- }
1803
- try {
1804
- const cwd = process.cwd();
1805
- const workspaceConfigPath = path6.resolve(cwd, "pnpm-workspace.yaml");
1806
- const isWorkspaceRoot = fs6.existsSync(workspaceConfigPath);
1807
- if (packageName === "all" && !isWorkspaceRoot) {
1808
- printError(`Config file not found in ${cwd}`);
1809
- console.error(chalk8.yellow(" Run in a directory with objectstack.config.ts, or from the monorepo root."));
1810
- process.exit(1);
1811
- }
1812
- const filter = packageName === "all" ? "" : `--filter ${packageName}`;
1813
- printKV("Package", packageName === "all" ? "All packages" : packageName, "\u{1F4E6}");
1814
- printKV("Watch", "enabled", "\u{1F504}");
1815
- const command = `pnpm ${filter} dev`.trim();
1816
- console.log(chalk8.dim(`$ ${command}`));
1817
- console.log("");
1818
- execSync(command, {
1819
- stdio: "inherit",
1820
- cwd
1821
- });
1822
- } catch (error) {
1823
- printError(`Development mode failed: ${error.message || error}`);
1824
- process.exit(1);
1825
- }
1826
- });
1827
-
1828
- // src/commands/serve.ts
1829
- import { Command as Command9 } from "commander";
1830
- import path8 from "path";
1831
- import fs8 from "fs";
1832
- import net from "net";
1833
- import chalk9 from "chalk";
1834
- import { bundleRequire } from "bundle-require";
1835
-
1836
- // src/utils/studio.ts
1837
- import path7 from "path";
1838
- import fs7 from "fs";
1839
- import { createRequire } from "module";
1840
- import { pathToFileURL } from "url";
1841
- var STUDIO_PATH = "/_studio";
1842
- function resolveStudioPath() {
1843
- const cwd = process.cwd();
1844
- const candidates = [
1845
- path7.resolve(cwd, "apps/studio"),
1846
- path7.resolve(cwd, "../../apps/studio"),
1847
- path7.resolve(cwd, "../apps/studio")
1848
- ];
1849
- for (const candidate of candidates) {
1850
- const pkgPath = path7.join(candidate, "package.json");
1851
- if (fs7.existsSync(pkgPath)) {
1852
- try {
1853
- const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
1854
- if (pkg.name === "@objectstack/studio") return candidate;
1855
- } catch {
1856
- }
1857
- }
1858
- }
1859
- const resolutionBases = [
1860
- pathToFileURL(path7.join(cwd, "package.json")).href,
1861
- // consumer workspace
1862
- import.meta.url
1863
- // CLI package itself
1864
- ];
1865
- for (const base of resolutionBases) {
1866
- try {
1867
- const req = createRequire(base);
1868
- const resolved = req.resolve("@objectstack/studio/package.json");
1869
- return path7.dirname(resolved);
1870
- } catch {
1871
- }
1872
- }
1873
- const directPath = path7.join(cwd, "node_modules", "@objectstack", "studio");
1874
- if (fs7.existsSync(path7.join(directPath, "package.json"))) {
1875
- return directPath;
1876
- }
1877
- return null;
1878
- }
1879
- function hasStudioDist(studioPath) {
1880
- return fs7.existsSync(path7.join(studioPath, "dist", "index.html"));
1881
- }
1882
- function createStudioStaticPlugin(distPath, options) {
1883
- return {
1884
- name: "com.objectstack.studio-static",
1885
- init: async () => {
1886
- },
1887
- start: async (ctx) => {
1888
- const httpServer = ctx.getService?.("http.server");
1889
- if (!httpServer?.getRawApp) {
1890
- ctx.logger?.warn?.("Studio static: http.server service not found \u2014 skipping");
1891
- return;
1892
- }
1893
- const app = httpServer.getRawApp();
1894
- const absoluteDist = path7.resolve(distPath);
1895
- const indexPath = path7.join(absoluteDist, "index.html");
1896
- if (!fs7.existsSync(indexPath)) {
1897
- ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
1898
- return;
1899
- }
1900
- const rawHtml = fs7.readFileSync(indexPath, "utf-8");
1901
- const rewrittenHtml = rawHtml.replace(
1902
- /(\s(?:href|src))="\/(?!\/)/g,
1903
- `$1="${STUDIO_PATH}/`
1904
- );
1905
- if (options?.isDev) {
1906
- app.get("/", (c) => c.redirect(`${STUDIO_PATH}/`));
1907
- }
1908
- app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
1909
- app.get(`${STUDIO_PATH}/*`, async (c) => {
1910
- const reqPath = c.req.path.substring(STUDIO_PATH.length) || "/";
1911
- const filePath = path7.join(absoluteDist, reqPath);
1912
- if (!filePath.startsWith(absoluteDist)) {
1913
- return c.text("Forbidden", 403);
1914
- }
1915
- if (fs7.existsSync(filePath) && fs7.statSync(filePath).isFile()) {
1916
- const content = fs7.readFileSync(filePath);
1917
- return new Response(content, {
1918
- headers: { "content-type": mimeType(filePath) }
1919
- });
1920
- }
1921
- return new Response(rewrittenHtml, {
1922
- headers: { "content-type": "text/html; charset=utf-8" }
1923
- });
1924
- });
1925
- }
1926
- };
1927
- }
1928
- var MIME_TYPES = {
1929
- ".html": "text/html; charset=utf-8",
1930
- ".js": "application/javascript; charset=utf-8",
1931
- ".mjs": "application/javascript; charset=utf-8",
1932
- ".css": "text/css; charset=utf-8",
1933
- ".json": "application/json; charset=utf-8",
1934
- ".svg": "image/svg+xml",
1935
- ".png": "image/png",
1936
- ".jpg": "image/jpeg",
1937
- ".jpeg": "image/jpeg",
1938
- ".gif": "image/gif",
1939
- ".ico": "image/x-icon",
1940
- ".woff": "font/woff",
1941
- ".woff2": "font/woff2",
1942
- ".ttf": "font/ttf",
1943
- ".map": "application/json"
1944
- };
1945
- function mimeType(filePath) {
1946
- const ext = path7.extname(filePath).toLowerCase();
1947
- return MIME_TYPES[ext] || "application/octet-stream";
1948
- }
1949
-
1950
- // src/commands/serve.ts
1951
- var getAvailablePort = async (startPort) => {
1952
- const isPortAvailable = (port2) => {
1953
- return new Promise((resolve) => {
1954
- const server = net.createServer();
1955
- server.once("error", (err) => {
1956
- resolve(false);
1957
- });
1958
- server.once("listening", () => {
1959
- server.close(() => resolve(true));
1960
- });
1961
- server.listen(port2);
1962
- });
1963
- };
1964
- let port = startPort;
1965
- while (!await isPortAvailable(port)) {
1966
- port++;
1967
- if (port > startPort + 100) {
1968
- throw new Error(`Could not find an available port starting from ${startPort}`);
1969
- }
1970
- }
1971
- return port;
1972
- };
1973
- var serveCommand = new Command9("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Studio UI at /_studio/ (default: true in dev mode)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
1974
- let port = parseInt(options.port);
1975
- try {
1976
- const availablePort = await getAvailablePort(port);
1977
- if (availablePort !== port) {
1978
- port = availablePort;
1979
- }
1980
- } catch (e) {
1981
- }
1982
- const isDev = options.dev || process.env.NODE_ENV === "development";
1983
- const absolutePath = path8.resolve(process.cwd(), configPath);
1984
- const relativeConfig = path8.relative(process.cwd(), absolutePath);
1985
- if (!fs8.existsSync(absolutePath)) {
1986
- printError(`Configuration file not found: ${absolutePath}`);
1987
- console.log(chalk9.dim(" Hint: Run `objectstack init` to create a new project"));
1988
- process.exit(1);
1989
- }
1990
- console.log("");
1991
- console.log(chalk9.dim(` Loading ${relativeConfig}...`));
1992
- const loadedPlugins = [];
1993
- const shortPluginName = (raw) => {
1994
- if (raw.includes("objectql")) return "ObjectQL";
1995
- if (raw.includes("driver") && raw.includes("memory")) return "MemoryDriver";
1996
- if (raw.startsWith("plugin.app.")) return raw.replace("plugin.app.", "").split(".").pop() || raw;
1997
- if (raw.includes("hono")) return "HonoServer";
1998
- return raw;
1999
- };
2000
- const trackPlugin = (name) => {
2001
- loadedPlugins.push(shortPluginName(name));
2002
- };
2003
- const originalConsoleLog = console.log;
2004
- const originalConsoleDebug = console.debug;
2005
- const origStdoutWrite = process.stdout.write.bind(process.stdout);
2006
- let bootQuiet = false;
2007
- const restoreOutput = () => {
2008
- bootQuiet = false;
2009
- process.stdout.write = origStdoutWrite;
2010
- console.log = originalConsoleLog;
2011
- console.debug = originalConsoleDebug;
2012
- };
2013
- const portShifted = parseInt(options.port) !== port;
2014
- try {
2015
- bootQuiet = true;
2016
- process.stdout.write = (chunk, ...rest) => {
2017
- if (bootQuiet) return true;
2018
- return origStdoutWrite(chunk, ...rest);
2019
- };
2020
- console.log = (...args) => {
2021
- if (!bootQuiet) originalConsoleLog(...args);
2022
- };
2023
- console.debug = (...args) => {
2024
- if (!bootQuiet) originalConsoleDebug(...args);
2025
- };
2026
- const { mod } = await bundleRequire({
2027
- filepath: absolutePath
2028
- });
2029
- const config = mod.default || mod;
2030
- if (!config) {
2031
- throw new Error(`No default export found in ${configPath}`);
2032
- }
2033
- const { Runtime } = await import("@objectstack/runtime");
2034
- const loggerConfig = { level: "silent" };
2035
- const runtime = new Runtime({
2036
- kernel: {
2037
- logger: loggerConfig
2038
- }
2039
- });
2040
- const kernel = runtime.getKernel();
2041
- let plugins = config.plugins || [];
2042
- if (options.dev && config.devPlugins) {
2043
- plugins = [...plugins, ...config.devPlugins];
2044
- }
2045
- const hasObjectQL = plugins.some((p) => p.name?.includes("objectql") || p.constructor?.name?.includes("ObjectQL"));
2046
- if (config.objects && !hasObjectQL) {
2047
- try {
2048
- const { ObjectQLPlugin } = await import("@objectstack/objectql");
2049
- await kernel.use(new ObjectQLPlugin());
2050
- trackPlugin("ObjectQL");
2051
- } catch (e) {
2052
- }
2053
- }
2054
- const hasDriver = plugins.some((p) => p.name?.includes("driver") || p.constructor?.name?.includes("Driver"));
2055
- if (isDev && !hasDriver && config.objects) {
2056
- try {
2057
- const { DriverPlugin } = await import("@objectstack/runtime");
2058
- const { InMemoryDriver } = await import("@objectstack/driver-memory");
2059
- await kernel.use(new DriverPlugin(new InMemoryDriver()));
2060
- trackPlugin("MemoryDriver");
2061
- } catch (e) {
2062
- }
2063
- }
2064
- if (config.objects || config.manifest || config.apps) {
2065
- try {
2066
- const { AppPlugin } = await import("@objectstack/runtime");
2067
- await kernel.use(new AppPlugin(config));
2068
- trackPlugin("App");
2069
- } catch (e) {
2070
- }
2071
- }
2072
- if (plugins.length > 0) {
2073
- for (const plugin of plugins) {
2074
- try {
2075
- let pluginToLoad = plugin;
2076
- if (typeof plugin === "string") {
2077
- try {
2078
- const imported = await import(plugin);
2079
- pluginToLoad = imported.default || imported;
2080
- } catch (importError) {
2081
- throw new Error(`Failed to import plugin '${plugin}': ${importError.message}`);
2082
- }
2083
- }
2084
- if (pluginToLoad && typeof pluginToLoad === "object" && !pluginToLoad.init) {
2085
- try {
2086
- const { AppPlugin } = await import("@objectstack/runtime");
2087
- pluginToLoad = new AppPlugin(pluginToLoad);
2088
- } catch (e) {
2089
- }
2090
- }
2091
- await kernel.use(pluginToLoad);
2092
- const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
2093
- trackPlugin(pluginName);
2094
- } catch (e) {
2095
- console.error(chalk9.red(` \u2717 Failed to load plugin: ${e.message}`));
2096
- }
2097
- }
2098
- }
2099
- if (options.server !== false) {
2100
- try {
2101
- const { HonoServerPlugin } = await import("@objectstack/plugin-hono-server");
2102
- const serverPlugin = new HonoServerPlugin({ port });
2103
- await kernel.use(serverPlugin);
2104
- trackPlugin("HonoServer");
2105
- } catch (e) {
2106
- console.warn(chalk9.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
2107
- }
2108
- try {
2109
- const { createRestApiPlugin } = await import("@objectstack/rest");
2110
- await kernel.use(createRestApiPlugin());
2111
- trackPlugin("RestAPI");
2112
- } catch (e) {
2113
- }
2114
- try {
2115
- const { createDispatcherPlugin } = await import("@objectstack/runtime");
2116
- await kernel.use(createDispatcherPlugin());
2117
- trackPlugin("Dispatcher");
2118
- } catch (e) {
2119
- }
2120
- }
2121
- const enableUI = options.ui ?? isDev;
2122
- if (enableUI) {
2123
- const studioPath = resolveStudioPath();
2124
- if (!studioPath) {
2125
- console.warn(chalk9.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
2126
- } else if (hasStudioDist(studioPath)) {
2127
- const distPath = path8.join(studioPath, "dist");
2128
- await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
2129
- trackPlugin("StudioUI");
2130
- } else {
2131
- console.warn(chalk9.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
2132
- }
2133
- }
2134
- await runtime.start();
2135
- await new Promise((r) => setTimeout(r, 100));
2136
- restoreOutput();
2137
- printServerReady({
2138
- port,
2139
- configFile: relativeConfig,
2140
- isDev,
2141
- pluginCount: loadedPlugins.length,
2142
- pluginNames: loadedPlugins,
2143
- uiEnabled: enableUI,
2144
- studioPath: STUDIO_PATH
2145
- });
2146
- process.on("SIGINT", async () => {
2147
- console.warn(chalk9.yellow(`
2148
-
2149
- \u23F9 Stopping server...`));
2150
- await runtime.getKernel().shutdown();
2151
- console.log(chalk9.green(`\u2705 Server stopped`));
2152
- process.exit(0);
2153
- });
2154
- } catch (error) {
2155
- restoreOutput();
2156
- console.log("");
2157
- printError(error.message || String(error));
2158
- if (process.env.DEBUG) console.error(chalk9.dim(error.stack));
2159
- process.exit(1);
2160
- }
2161
- });
2162
-
2163
- // src/commands/test.ts
2164
- import { Command as Command10 } from "commander";
2165
- import chalk10 from "chalk";
2166
- import path9 from "path";
2167
- import fs9 from "fs";
2168
- import { QA as CoreQA } from "@objectstack/core";
2169
- function resolveGlob(pattern) {
2170
- if (!pattern.includes("*")) {
2171
- return fs9.existsSync(pattern) ? [pattern] : [];
2172
- }
2173
- const parts = pattern.split(path9.sep.replace("\\", "/"));
2174
- const segments = pattern.includes("/") ? pattern.split("/") : parts;
2175
- let baseDir = ".";
2176
- let globStart = 0;
2177
- for (let i = 0; i < segments.length; i++) {
2178
- if (segments[i].includes("*")) {
2179
- globStart = i;
2180
- break;
2181
- }
2182
- baseDir = i === 0 ? segments[i] : path9.join(baseDir, segments[i]);
2183
- }
2184
- if (!fs9.existsSync(baseDir)) return [];
2185
- const globPortion = segments.slice(globStart).join("/");
2186
- const regexStr = globPortion.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
2187
- const regex = new RegExp(`^${regexStr}$`);
2188
- const entries = fs9.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
2189
- return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path9.join(baseDir, entry)).filter((fullPath) => fs9.statSync(fullPath).isFile());
2190
- }
2191
- var testCommand = new Command10("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
2192
- console.log(chalk10.bold(`
2193
- \u{1F9EA} ObjectStack Quality Protocol Runner`));
2194
- console.log(chalk10.dim(`-------------------------------------`));
2195
- console.log(`Target: ${chalk10.blue(options.url)}`);
2196
- const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
2197
- const runner = new CoreQA.TestRunner(adapter);
2198
- const testFiles = resolveGlob(filesPattern);
2199
- if (testFiles.length === 0) {
2200
- console.warn(chalk10.yellow(`No test files found matching: ${filesPattern}`));
2201
- return;
2202
- }
2203
- console.log(`Found ${testFiles.length} test suites.`);
2204
- let totalPassed = 0;
2205
- let totalFailed = 0;
2206
- for (const file of testFiles) {
2207
- console.log(`
2208
- \u{1F4C4} Running suite: ${chalk10.bold(path9.basename(file))}`);
2209
- try {
2210
- const content = fs9.readFileSync(file, "utf-8");
2211
- const suite = JSON.parse(content);
2212
- const results = await runner.runSuite(suite);
2213
- for (const result of results) {
2214
- const icon = result.passed ? "\u2705" : "\u274C";
2215
- console.log(` ${icon} Scenario: ${result.scenarioId} (${result.duration}ms)`);
2216
- if (!result.passed) {
2217
- console.error(chalk10.red(` Error: ${result.error}`));
2218
- result.steps.forEach((step) => {
2219
- if (!step.passed) {
2220
- console.error(chalk10.red(` Step Failed: ${step.stepName}`));
2221
- if (step.output) console.error(` Output:`, step.output);
2222
- if (step.error) console.error(` Error:`, step.error);
2223
- }
2224
- });
2225
- totalFailed++;
2226
- } else {
2227
- totalPassed++;
2228
- }
2229
- }
2230
- } catch (e) {
2231
- console.error(chalk10.red(`Failed to load or run suite ${file}: ${e}`));
2232
- totalFailed++;
2233
- }
2234
- }
2235
- console.log(chalk10.dim(`
2236
- -------------------------------------`));
2237
- if (totalFailed > 0) {
2238
- console.log(chalk10.red(`FAILED: ${totalFailed} scenarios failed. ${totalPassed} passed.`));
2239
- process.exit(1);
2240
- } else {
2241
- console.log(chalk10.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
2242
- process.exit(0);
2243
- }
2244
- });
2245
-
2246
- // src/commands/doctor.ts
2247
- import { Command as Command11 } from "commander";
2248
- import chalk11 from "chalk";
2249
- import { execSync as execSync2 } from "child_process";
2250
- import fs10 from "fs";
2251
- import path10 from "path";
2252
- function detectCircularDependencies(objects) {
2253
- const issues = [];
2254
- const graph = /* @__PURE__ */ new Map();
2255
- for (const obj of objects) {
2256
- const deps = [];
2257
- if (obj.fields && typeof obj.fields === "object") {
2258
- for (const field of Object.values(obj.fields)) {
2259
- if (field?.type === "lookup" && field?.reference) {
2260
- deps.push(field.reference);
2261
- }
2262
- }
2263
- }
2264
- graph.set(obj.name, deps);
2265
- }
2266
- const visited = /* @__PURE__ */ new Set();
2267
- const stack = /* @__PURE__ */ new Set();
2268
- function dfs(node, path11) {
2269
- if (stack.has(node)) {
2270
- const cycleStart = path11.indexOf(node);
2271
- const cycle = path11.slice(cycleStart).concat(node);
2272
- issues.push(`Circular dependency: ${cycle.join(" \u2192 ")}`);
2273
- return true;
2274
- }
2275
- if (visited.has(node)) return false;
2276
- visited.add(node);
2277
- stack.add(node);
2278
- for (const dep of graph.get(node) || []) {
2279
- if (graph.has(dep)) {
2280
- dfs(dep, [...path11, node]);
2281
- }
2282
- }
2283
- stack.delete(node);
2284
- return false;
2285
- }
2286
- for (const name of graph.keys()) {
2287
- if (!visited.has(name)) {
2288
- dfs(name, []);
2289
- }
2290
- }
2291
- return issues;
2292
- }
2293
- function findOrphanViews(config) {
2294
- const objectNames = /* @__PURE__ */ new Set();
2295
- if (Array.isArray(config.objects)) {
2296
- for (const obj of config.objects) {
2297
- if (obj.name) objectNames.add(obj.name);
2298
- }
2299
- }
2300
- const orphans = [];
2301
- if (Array.isArray(config.views)) {
2302
- for (const view of config.views) {
2303
- if (view.object && !objectNames.has(view.object)) {
2304
- orphans.push(`View "${view.name || "?"}" references non-existent object "${view.object}"`);
2305
- }
2306
- }
2307
- }
2308
- return orphans;
2309
- }
2310
- function findUnusedObjects(config) {
2311
- const objectNames = /* @__PURE__ */ new Set();
2312
- if (Array.isArray(config.objects)) {
2313
- for (const obj of config.objects) {
2314
- if (obj.name) objectNames.add(obj.name);
2315
- }
2316
- }
2317
- const referencedObjects = /* @__PURE__ */ new Set();
2318
- if (Array.isArray(config.views)) {
2319
- for (const view of config.views) {
2320
- if (view.object) referencedObjects.add(view.object);
2321
- }
2322
- }
2323
- if (Array.isArray(config.flows)) {
2324
- for (const flow of config.flows) {
2325
- if (flow.trigger?.object) referencedObjects.add(flow.trigger.object);
2326
- if (flow.object) referencedObjects.add(flow.object);
2327
- }
2328
- }
2329
- if (Array.isArray(config.apps)) {
2330
- for (const app of config.apps) {
2331
- if (Array.isArray(app.navigation)) {
2332
- for (const nav of app.navigation) {
2333
- if (nav.object) referencedObjects.add(nav.object);
2334
- }
2335
- }
2336
- }
2337
- }
2338
- if (Array.isArray(config.agents)) {
2339
- for (const agent of config.agents) {
2340
- if (Array.isArray(agent.objects)) {
2341
- for (const o of agent.objects) referencedObjects.add(o);
2342
- }
2343
- }
2344
- }
2345
- if (Array.isArray(config.objects)) {
2346
- for (const obj of config.objects) {
2347
- if (obj.fields && typeof obj.fields === "object") {
2348
- for (const field of Object.values(obj.fields)) {
2349
- if (field?.type === "lookup" && field?.reference) {
2350
- referencedObjects.add(field.reference);
2351
- }
2352
- }
2353
- }
2354
- }
2355
- }
2356
- const unused = [];
2357
- for (const name of objectNames) {
2358
- if (!referencedObjects.has(name)) {
2359
- unused.push(`Object "${name}" is defined but not referenced by any view, flow, app, or agent`);
2360
- }
2361
- }
2362
- return unused;
2363
- }
2364
- function walkDir(dir, ext) {
2365
- const results = [];
2366
- if (!fs10.existsSync(dir)) return results;
2367
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
2368
- for (const entry of entries) {
2369
- if (entry.name === "node_modules") continue;
2370
- const fullPath = path10.join(dir, entry.name);
2371
- if (entry.isDirectory()) {
2372
- results.push(...walkDir(fullPath, ext));
2373
- } else if (entry.name.endsWith(ext)) {
2374
- results.push(fullPath);
2375
- }
2376
- }
2377
- return results;
2378
- }
2379
- function findMissingTests(cwd) {
2380
- const specSrcDir = path10.join(cwd, "packages/spec/src");
2381
- if (!fs10.existsSync(specSrcDir)) return [];
2382
- const missing = [];
2383
- const zodFiles = walkDir(specSrcDir, ".zod.ts");
2384
- for (const zodFile of zodFiles) {
2385
- const testFile = zodFile.replace(".zod.ts", ".test.ts");
2386
- if (!fs10.existsSync(testFile)) {
2387
- const relZod = path10.relative(specSrcDir, zodFile);
2388
- const relTest = path10.relative(specSrcDir, testFile);
2389
- missing.push(`Missing test: ${relTest} (for ${relZod})`);
2390
- }
2391
- }
2392
- return missing;
2393
- }
2394
- function findDeprecatedUsages(cwd) {
2395
- const specSrcDir = path10.join(cwd, "packages/spec/src");
2396
- if (!fs10.existsSync(specSrcDir)) return [];
2397
- const deprecated = [];
2398
- const tsFiles = walkDir(specSrcDir, ".ts").filter((f) => !f.endsWith(".test.ts"));
2399
- for (const tsFile of tsFiles) {
2400
- try {
2401
- const content = fs10.readFileSync(tsFile, "utf-8");
2402
- const lines = content.split("\n");
2403
- const relPath = path10.relative(specSrcDir, tsFile);
2404
- for (let i = 0; i < lines.length; i++) {
2405
- if (lines[i].includes("@deprecated")) {
2406
- deprecated.push(`${relPath}:${i + 1} \u2014 @deprecated tag found`);
2407
- }
2408
- }
2409
- } catch {
2410
- }
2411
- }
2412
- return deprecated;
2413
- }
2414
- var DEPRECATED_PATTERNS = [
2415
- {
2416
- pattern: /\bEnhancedObjectKernel\b/,
2417
- description: "EnhancedObjectKernel is deprecated in v3",
2418
- replacement: "Use ObjectKernel instead"
2419
- },
2420
- {
2421
- pattern: /\bmax_length\b/,
2422
- description: "snake_case config key: max_length",
2423
- replacement: "Use maxLength (camelCase)"
2424
- },
2425
- {
2426
- pattern: /\bdefault_value\b/,
2427
- description: "snake_case config key: default_value",
2428
- replacement: "Use defaultValue (camelCase)"
2429
- },
2430
- {
2431
- pattern: /\bmin_length\b/,
2432
- description: "snake_case config key: min_length",
2433
- replacement: "Use minLength (camelCase)"
2434
- },
2435
- {
2436
- pattern: /\breference_filters\b/,
2437
- description: "snake_case config key: reference_filters",
2438
- replacement: "Use referenceFilters (camelCase)"
2439
- },
2440
- {
2441
- pattern: /\bunique_name\b/,
2442
- description: "snake_case config key: unique_name",
2443
- replacement: "Use uniqueName (camelCase)"
2444
- },
2445
- {
2446
- pattern: /from\s+['"]@objectstack\/core\/enhanced['"]/,
2447
- description: "Import from deprecated @objectstack/core/enhanced path",
2448
- replacement: "Use import from '@objectstack/core'"
2449
- },
2450
- {
2451
- pattern: /from\s+['"]@objectstack\/spec\/dist\/[^'"]+['"]/,
2452
- description: "Import from deprecated @objectstack/spec/dist/ deep path",
2453
- replacement: "Use import from '@objectstack/spec'"
2454
- }
2455
- ];
2456
- function scanDeprecatedPatterns(dir) {
2457
- const results = [];
2458
- if (!fs10.existsSync(dir)) return results;
2459
- const tsFiles = walkDir(dir, ".ts").filter((f) => !f.endsWith(".test.ts"));
2460
- for (const tsFile of tsFiles) {
2461
- try {
2462
- const content = fs10.readFileSync(tsFile, "utf-8");
2463
- const lines = content.split("\n");
2464
- const relPath = path10.relative(process.cwd(), tsFile);
2465
- for (let i = 0; i < lines.length; i++) {
2466
- for (const dp of DEPRECATED_PATTERNS) {
2467
- if (dp.pattern.test(lines[i])) {
2468
- results.push({
2469
- file: relPath,
2470
- line: i + 1,
2471
- description: dp.description,
2472
- replacement: dp.replacement
2473
- });
2474
- }
2475
- }
2476
- }
2477
- } catch {
2478
- }
2479
- }
2480
- return results;
2481
- }
2482
- var doctorCommand = new Command11("doctor").description("Check development environment and configuration health").option("-v, --verbose", "Show detailed information").option("--scan-deprecations", "Scan for deprecated ObjectStack patterns").action(async (options) => {
2483
- printHeader("Environment Health Check");
2484
- const results = [];
2485
- try {
2486
- const nodeVersion = process.version;
2487
- const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
2488
- if (majorVersion >= 18) {
2489
- results.push({
2490
- name: "Node.js",
2491
- status: "ok",
2492
- message: `Version ${nodeVersion}`
2493
- });
2494
- } else {
2495
- results.push({
2496
- name: "Node.js",
2497
- status: "error",
2498
- message: `Version ${nodeVersion} (requires >= 18.0.0)`,
2499
- fix: "Upgrade Node.js: https://nodejs.org"
2500
- });
2501
- }
2502
- } catch (error) {
2503
- results.push({
2504
- name: "Node.js",
2505
- status: "error",
2506
- message: "Not found",
2507
- fix: "Install Node.js: https://nodejs.org"
2508
- });
2509
- }
2510
- try {
2511
- const pnpmVersion = execSync2("pnpm -v", { encoding: "utf-8" }).trim();
2512
- results.push({
2513
- name: "pnpm",
2514
- status: "ok",
2515
- message: `Version ${pnpmVersion}`
2516
- });
2517
- } catch (error) {
2518
- results.push({
2519
- name: "pnpm",
2520
- status: "error",
2521
- message: "Not found",
2522
- fix: "Install pnpm: npm install -g pnpm@10.28.1"
2523
- });
2524
- }
2525
- try {
2526
- const tscVersion = execSync2("tsc -v", { encoding: "utf-8" }).trim();
2527
- results.push({
2528
- name: "TypeScript",
2529
- status: "ok",
2530
- message: tscVersion
2531
- });
2532
- } catch (error) {
2533
- results.push({
2534
- name: "TypeScript",
2535
- status: "warning",
2536
- message: "Not found in PATH",
2537
- fix: "Installed locally via pnpm"
2538
- });
2539
- }
2540
- const cwd = process.cwd();
2541
- const nodeModulesPath = path10.join(cwd, "node_modules");
2542
- if (fs10.existsSync(nodeModulesPath)) {
2543
- results.push({
2544
- name: "Dependencies",
2545
- status: "ok",
2546
- message: "Installed"
2547
- });
2548
- } else {
2549
- results.push({
2550
- name: "Dependencies",
2551
- status: "error",
2552
- message: "Not installed",
2553
- fix: "Run: pnpm install"
2554
- });
2555
- }
2556
- const specDistPath = path10.join(cwd, "packages/spec/dist");
2557
- if (fs10.existsSync(specDistPath)) {
2558
- results.push({
2559
- name: "@objectstack/spec",
2560
- status: "ok",
2561
- message: "Built"
2562
- });
2563
- } else {
2564
- results.push({
2565
- name: "@objectstack/spec",
2566
- status: "warning",
2567
- message: "Not built",
2568
- fix: "Run: pnpm --filter @objectstack/spec build"
2569
- });
2570
- }
2571
- try {
2572
- const gitVersion = execSync2("git --version", { encoding: "utf-8" }).trim();
2573
- results.push({
2574
- name: "Git",
2575
- status: "ok",
2576
- message: gitVersion
2577
- });
2578
- } catch (error) {
2579
- results.push({
2580
- name: "Git",
2581
- status: "warning",
2582
- message: "Not found",
2583
- fix: "Install Git for version control"
2584
- });
2585
- }
2586
- let hasErrors = false;
2587
- let hasWarnings = false;
2588
- console.log("");
2589
- results.forEach((result) => {
2590
- const padded = result.name.padEnd(20);
2591
- if (result.status === "ok") {
2592
- printSuccess(`${padded} ${result.message}`);
2593
- } else if (result.status === "warning") {
2594
- printWarning(`${padded} ${result.message}`);
2595
- } else {
2596
- printError(`${padded} ${result.message}`);
2597
- }
2598
- if (result.fix && (options.verbose || result.status === "error")) {
2599
- console.log(chalk11.dim(` \u2192 ${result.fix}`));
2600
- }
2601
- if (result.status === "error") hasErrors = true;
2602
- if (result.status === "warning") hasWarnings = true;
2603
- });
2604
- printStep("Checking for missing test files...");
2605
- const missingTests = findMissingTests(cwd);
2606
- if (missingTests.length > 0) {
2607
- hasWarnings = true;
2608
- for (const msg of missingTests) {
2609
- printWarning(msg);
2610
- }
2611
- } else {
2612
- printSuccess("Test coverage All *.zod.ts files have matching tests");
2613
- }
2614
- printStep("Scanning for @deprecated usage...");
2615
- const deprecatedUsages = findDeprecatedUsages(cwd);
2616
- if (deprecatedUsages.length > 0) {
2617
- hasWarnings = true;
2618
- for (const msg of deprecatedUsages) {
2619
- printWarning(`Deprecated: ${msg}`);
2620
- }
2621
- } else {
2622
- printSuccess("Deprecations No @deprecated tags found");
2623
- }
2624
- if (configExists()) {
2625
- printStep("Loading configuration for analysis...");
2626
- try {
2627
- const { config } = await loadConfig();
2628
- if (Array.isArray(config.objects) && config.objects.length > 0) {
2629
- printStep("Checking for circular dependencies...");
2630
- const cycles = detectCircularDependencies(config.objects);
2631
- if (cycles.length > 0) {
2632
- hasWarnings = true;
2633
- for (const msg of cycles) {
2634
- printWarning(msg);
2635
- }
2636
- } else {
2637
- printSuccess("Dependencies No circular references detected");
2638
- }
2639
- printStep("Checking for unused objects...");
2640
- const unused = findUnusedObjects(config);
2641
- if (unused.length > 0) {
2642
- hasWarnings = true;
2643
- for (const msg of unused) {
2644
- printWarning(msg);
2645
- }
2646
- } else {
2647
- printSuccess("Object usage All objects are referenced");
2648
- }
2649
- }
2650
- if (Array.isArray(config.views) && config.views.length > 0) {
2651
- printStep("Checking for orphan views...");
2652
- const orphans = findOrphanViews(config);
2653
- if (orphans.length > 0) {
2654
- hasWarnings = true;
2655
- for (const msg of orphans) {
2656
- printWarning(msg);
2657
- }
2658
- } else {
2659
- printSuccess("View integrity All views reference valid objects");
2660
- }
2661
- }
2662
- } catch {
2663
- printWarning("Could not load config for analysis (config checks skipped)");
2664
- hasWarnings = true;
2665
- }
2666
- }
2667
- if (options.scanDeprecations) {
2668
- printStep("Scanning for deprecated ObjectStack patterns...");
2669
- const scanDir = path10.join(cwd, "src");
2670
- const deprecations = scanDeprecatedPatterns(scanDir);
2671
- if (deprecations.length > 0) {
2672
- hasWarnings = true;
2673
- for (const dep of deprecations) {
2674
- printWarning(`${dep.file}:${dep.line} \u2014 ${dep.description}`);
2675
- if (options.verbose) {
2676
- console.log(chalk11.dim(` \u2192 ${dep.replacement}`));
2677
- }
2678
- }
2679
- console.log("");
2680
- printInfo(`Found ${deprecations.length} deprecated pattern(s). Run \`objectstack codemod v2-to-v3\` to auto-fix.`);
2681
- } else {
2682
- printSuccess("Deprecation scan No deprecated patterns found");
2683
- }
2684
- }
2685
- console.log("");
2686
- if (hasErrors) {
2687
- console.log(chalk11.red("\u274C Some critical issues found. Please fix them before continuing."));
2688
- results.filter((r) => r.status === "error" && r.fix).forEach((r) => console.log(chalk11.dim(` ${r.fix}`)));
2689
- process.exit(1);
2690
- } else if (hasWarnings) {
2691
- console.log(chalk11.yellow("\u26A0\uFE0F Environment is functional but has some warnings."));
2692
- console.log(chalk11.dim(" Run with --verbose to see fix suggestions."));
2693
- } else {
2694
- console.log(chalk11.green("\u2705 Environment is healthy and ready for development!"));
2695
- }
2696
- console.log("");
2697
- });
2698
-
2699
- // src/utils/plugin-commands.ts
2700
- import chalk12 from "chalk";
2701
- async function loadPluginCommands(program) {
2702
- let config;
2703
- try {
2704
- const loaded = await loadConfig();
2705
- config = loaded.config;
2706
- } catch {
2707
- return;
2708
- }
2709
- const plugins = [
2710
- ...config.plugins || [],
2711
- ...config.devPlugins || []
2712
- ];
2713
- const contributions = [];
2714
- for (const plugin of plugins) {
2715
- if (!plugin || typeof plugin !== "object") continue;
2716
- const p = plugin;
2717
- const manifest = p.manifest;
2718
- const contributes = manifest?.contributes ?? p.contributes;
2719
- if (!contributes) continue;
2720
- const commands = contributes.commands;
2721
- if (!Array.isArray(commands)) continue;
2722
- const pluginName = resolvePluginName2(p);
2723
- for (const cmd of commands) {
2724
- if (!cmd || typeof cmd.name !== "string") continue;
2725
- contributions.push({
2726
- name: cmd.name,
2727
- description: typeof cmd.description === "string" ? cmd.description : void 0,
2728
- module: typeof cmd.module === "string" ? cmd.module : void 0,
2729
- pluginName
2730
- });
2731
- }
2732
- }
2733
- if (contributions.length === 0) return;
2734
- for (const contribution of contributions) {
2735
- try {
2736
- const commands = await importPluginCommands(contribution);
2737
- for (const cmd of commands) {
2738
- program.addCommand(cmd);
2739
- }
2740
- } catch (error) {
2741
- if (process.env.DEBUG) {
2742
- const message = error instanceof Error ? error.message : String(error);
2743
- console.error(
2744
- chalk12.yellow(` \u26A0 Failed to load CLI command '${contribution.name}' from plugin '${contribution.pluginName}': ${message}`)
2745
- );
2746
- }
2747
- }
2748
- }
2749
- }
2750
- async function importPluginCommands(contribution) {
2751
- const moduleId = contribution.module ? `${contribution.pluginName}/${contribution.module.replace(/^\.\//, "")}` : contribution.pluginName;
2752
- const mod = await import(moduleId);
2753
- if (Array.isArray(mod.commands)) {
2754
- return mod.commands.filter(isCommandInstance);
2755
- }
2756
- const defaultExport = mod.default;
2757
- if (defaultExport) {
2758
- if (Array.isArray(defaultExport)) {
2759
- return defaultExport.filter(isCommandInstance);
2760
- }
2761
- if (isCommandInstance(defaultExport)) {
2762
- return [defaultExport];
2763
- }
2764
- }
2765
- const commands = [];
2766
- for (const key of Object.keys(mod)) {
2767
- if (isCommandInstance(mod[key])) {
2768
- commands.push(mod[key]);
2769
- }
2770
- }
2771
- return commands;
2772
- }
2773
- function isCommandInstance(value) {
2774
- if (value === null || typeof value !== "object") return false;
2775
- const obj = value;
2776
- return typeof obj.name === "function" && typeof obj.description === "function" && typeof obj.action === "function" && typeof obj.parse === "function";
2777
- }
2778
- function resolvePluginName2(plugin) {
2779
- if (typeof plugin.name === "string") return plugin.name;
2780
- const manifest = plugin.manifest;
2781
- if (manifest && typeof manifest.name === "string") return manifest.name;
2782
- if (plugin.constructor && plugin.constructor.name !== "Object") return plugin.constructor.name;
2783
- return "unknown";
2784
- }
2785
- export {
2786
- compileCommand,
2787
- createCommand,
2788
- devCommand,
2789
- doctorCommand,
2790
- generateCommand,
2791
- infoCommand,
2792
- initCommand,
2793
- loadPluginCommands,
2794
- pluginCommand,
2795
- serveCommand,
2796
- templates,
2797
- testCommand,
2798
- validateCommand
2799
- };
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+ // ─── oclif Command Classes ──────────────────────────────────────────
3
+ // Each command is auto-discovered by oclif from `src/commands/`.
4
+ // These re-exports provide programmatic access for testing and integration.
5
+ export { default as CompileCommand } from './commands/compile.js';
6
+ export { default as ValidateCommand } from './commands/validate.js';
7
+ export { default as InfoCommand } from './commands/info.js';
8
+ export { default as InitCommand } from './commands/init.js';
9
+ export { default as GenerateCommand } from './commands/generate.js';
10
+ export { default as CreateCommand } from './commands/create.js';
11
+ export { default as DevCommand } from './commands/dev.js';
12
+ export { default as ServeCommand } from './commands/serve.js';
13
+ export { default as TestCommand } from './commands/test.js';
14
+ export { default as DoctorCommand } from './commands/doctor.js';
15
+ // ─── Plugin topic subcommands ───────────────────────────────────────
16
+ export { default as PluginListCommand } from './commands/plugin/list.js';
17
+ export { default as PluginInfoCommand } from './commands/plugin/info.js';
18
+ export { default as PluginAddCommand } from './commands/plugin/add.js';
19
+ export { default as PluginRemoveCommand } from './commands/plugin/remove.js';
20
+ //# sourceMappingURL=index.js.map