@objectstack/cli 2.0.7 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +10 -6
- package/CHANGELOG.md +17 -0
- package/dist/bin.js +388 -464
- package/dist/chunk-CSHQEILI.js +246 -0
- package/dist/chunk-Q74JNWKD.js +248 -0
- package/dist/config-A7BN6UIT.js +11 -0
- package/dist/config-UN34WBHT.js +10 -0
- package/dist/index.js +373 -448
- package/package.json +9 -9
- package/src/commands/generate.ts +181 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Command Line Interface for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -22,16 +22,16 @@
|
|
|
22
22
|
"commander": "^14.0.3",
|
|
23
23
|
"tsx": "^4.7.1",
|
|
24
24
|
"zod": "^4.3.6",
|
|
25
|
-
"@objectstack/core": "
|
|
26
|
-
"@objectstack/driver-memory": "^
|
|
27
|
-
"@objectstack/objectql": "^
|
|
28
|
-
"@objectstack/plugin-hono-server": "
|
|
29
|
-
"@objectstack/rest": "
|
|
30
|
-
"@objectstack/runtime": "^
|
|
31
|
-
"@objectstack/spec": "
|
|
25
|
+
"@objectstack/core": "3.0.0",
|
|
26
|
+
"@objectstack/driver-memory": "^3.0.0",
|
|
27
|
+
"@objectstack/objectql": "^3.0.0",
|
|
28
|
+
"@objectstack/plugin-hono-server": "3.0.0",
|
|
29
|
+
"@objectstack/rest": "3.0.0",
|
|
30
|
+
"@objectstack/runtime": "^3.0.0",
|
|
31
|
+
"@objectstack/spec": "3.0.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@objectstack/core": "
|
|
34
|
+
"@objectstack/core": "3.0.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/node": "^25.2.2",
|
package/src/commands/generate.ts
CHANGED
|
@@ -204,11 +204,110 @@ function toSnakeCase(str: string): string {
|
|
|
204
204
|
return str.replace(/[-]/g, '_').replace(/[A-Z]/g, c => `_${c.toLowerCase()}`).replace(/^_/, '');
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
// ─── Field Type Mapping ─────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
const FIELD_TYPE_MAP: Record<string, string> = {
|
|
210
|
+
text: 'string',
|
|
211
|
+
textarea: 'string',
|
|
212
|
+
richtext: 'string',
|
|
213
|
+
html: 'string',
|
|
214
|
+
markdown: 'string',
|
|
215
|
+
number: 'number',
|
|
216
|
+
integer: 'number',
|
|
217
|
+
currency: 'number',
|
|
218
|
+
percent: 'number',
|
|
219
|
+
boolean: 'boolean',
|
|
220
|
+
date: 'string',
|
|
221
|
+
datetime: 'string',
|
|
222
|
+
time: 'string',
|
|
223
|
+
email: 'string',
|
|
224
|
+
phone: 'string',
|
|
225
|
+
url: 'string',
|
|
226
|
+
select: 'string',
|
|
227
|
+
multiselect: 'string[]',
|
|
228
|
+
lookup: 'string',
|
|
229
|
+
master_detail: 'string',
|
|
230
|
+
formula: 'unknown',
|
|
231
|
+
autonumber: 'string',
|
|
232
|
+
json: 'Record<string, unknown>',
|
|
233
|
+
file: 'string',
|
|
234
|
+
image: 'string',
|
|
235
|
+
password: 'string',
|
|
236
|
+
slug: 'string',
|
|
237
|
+
uuid: 'string',
|
|
238
|
+
ip_address: 'string',
|
|
239
|
+
color: 'string',
|
|
240
|
+
rating: 'number',
|
|
241
|
+
geo_point: '{ lat: number; lng: number }',
|
|
242
|
+
vector: 'number[]',
|
|
243
|
+
encrypted: 'string',
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
function fieldTypeToTs(fieldType: string, multiple?: boolean): string {
|
|
247
|
+
const base = FIELD_TYPE_MAP[fieldType] || 'unknown';
|
|
248
|
+
return multiple ? `${base}[]` : base;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function generateTypesFromConfig(config: Record<string, unknown>): string {
|
|
252
|
+
const lines: string[] = [
|
|
253
|
+
'// Auto-generated by ObjectStack CLI — do not edit manually',
|
|
254
|
+
`// Generated at ${new Date().toISOString()}`,
|
|
255
|
+
'',
|
|
256
|
+
"import type { Data } from '@objectstack/spec';",
|
|
257
|
+
'',
|
|
258
|
+
];
|
|
259
|
+
|
|
260
|
+
// Extract objects from config (supports both top-level and nested)
|
|
261
|
+
const objects: Record<string, unknown>[] = [];
|
|
262
|
+
const rawObjects = (config as any).objects ?? (config as any).data?.objects ?? {};
|
|
263
|
+
|
|
264
|
+
if (Array.isArray(rawObjects)) {
|
|
265
|
+
objects.push(...rawObjects);
|
|
266
|
+
} else if (typeof rawObjects === 'object') {
|
|
267
|
+
for (const val of Object.values(rawObjects)) {
|
|
268
|
+
if (val && typeof val === 'object') objects.push(val as Record<string, unknown>);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (objects.length === 0) {
|
|
273
|
+
lines.push('// No objects found in configuration');
|
|
274
|
+
return lines.join('\n') + '\n';
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (const obj of objects) {
|
|
278
|
+
const name = String(obj.name || 'unknown');
|
|
279
|
+
const typeName = name
|
|
280
|
+
.split('_')
|
|
281
|
+
.map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))
|
|
282
|
+
.join('');
|
|
283
|
+
const fields = (obj.fields ?? {}) as Record<string, Record<string, unknown>>;
|
|
284
|
+
|
|
285
|
+
lines.push(`/** ${String(obj.label || typeName)} record type */`);
|
|
286
|
+
lines.push(`export interface ${typeName}Record {`);
|
|
287
|
+
lines.push(' id: string;');
|
|
288
|
+
|
|
289
|
+
for (const [fieldName, fieldDef] of Object.entries(fields)) {
|
|
290
|
+
const fType = String(fieldDef.type || 'text');
|
|
291
|
+
const tsType = fieldTypeToTs(fType, !!fieldDef.multiple);
|
|
292
|
+
const required = fieldDef.required ? '' : '?';
|
|
293
|
+
if (fieldDef.label) {
|
|
294
|
+
lines.push(` /** ${fieldDef.label} */`);
|
|
295
|
+
}
|
|
296
|
+
lines.push(` ${fieldName}${required}: ${tsType};`);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
lines.push('}');
|
|
300
|
+
lines.push('');
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return lines.join('\n') + '\n';
|
|
304
|
+
}
|
|
305
|
+
|
|
207
306
|
// ─── Command ────────────────────────────────────────────────────────
|
|
208
307
|
|
|
209
|
-
|
|
210
|
-
.alias('
|
|
211
|
-
.description('Generate metadata
|
|
308
|
+
const generateMetadataCommand = new Command('metadata')
|
|
309
|
+
.alias('m')
|
|
310
|
+
.description('Generate metadata scaffold (object, view, action, flow, agent, dashboard, app)')
|
|
212
311
|
.argument('<type>', 'Metadata type to generate')
|
|
213
312
|
.argument('<name>', 'Name for the metadata (use kebab-case)')
|
|
214
313
|
.option('-d, --dir <directory>', 'Target directory (overrides default)')
|
|
@@ -297,3 +396,82 @@ export const generateCommand = new Command('generate')
|
|
|
297
396
|
process.exit(1);
|
|
298
397
|
}
|
|
299
398
|
});
|
|
399
|
+
|
|
400
|
+
const generateTypesCommand = new Command('types')
|
|
401
|
+
.description('Generate TypeScript type definitions from ObjectStack configuration')
|
|
402
|
+
.argument('[config]', 'Configuration file path')
|
|
403
|
+
.option('-o, --output <file>', 'Output file path', 'src/types/objectstack.d.ts')
|
|
404
|
+
.option('--dry-run', 'Show what would be generated without writing files')
|
|
405
|
+
.action(async (configPath, options) => {
|
|
406
|
+
printHeader('Generate Types');
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
const { loadConfig } = await import('../utils/config.js');
|
|
410
|
+
printInfo('Loading configuration...');
|
|
411
|
+
const { config, absolutePath } = await loadConfig(configPath);
|
|
412
|
+
|
|
413
|
+
console.log(` ${chalk.dim('Config:')} ${chalk.white(absolutePath)}`);
|
|
414
|
+
console.log(` ${chalk.dim('Output:')} ${chalk.white(options.output)}`);
|
|
415
|
+
console.log('');
|
|
416
|
+
|
|
417
|
+
const content = generateTypesFromConfig(config as Record<string, unknown>);
|
|
418
|
+
|
|
419
|
+
if (options.dryRun) {
|
|
420
|
+
printInfo('Dry run — no files written');
|
|
421
|
+
console.log('');
|
|
422
|
+
for (const line of content.split('\n')) {
|
|
423
|
+
console.log(chalk.dim(` ${line}`));
|
|
424
|
+
}
|
|
425
|
+
console.log('');
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const outPath = path.resolve(process.cwd(), options.output);
|
|
430
|
+
const outDir = path.dirname(outPath);
|
|
431
|
+
if (!fs.existsSync(outDir)) {
|
|
432
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
433
|
+
}
|
|
434
|
+
fs.writeFileSync(outPath, content);
|
|
435
|
+
printSuccess(`Generated types at ${options.output}`);
|
|
436
|
+
console.log('');
|
|
437
|
+
|
|
438
|
+
} catch (error: any) {
|
|
439
|
+
printError(error.message || String(error));
|
|
440
|
+
process.exit(1);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
export const generateCommand = new Command('generate')
|
|
445
|
+
.alias('g')
|
|
446
|
+
.description('Generate metadata files or TypeScript types')
|
|
447
|
+
.argument('[type]', 'Metadata type to generate (object, view, action, flow, agent, dashboard, app)')
|
|
448
|
+
.argument('[name]', 'Name for the metadata (use kebab-case)')
|
|
449
|
+
.option('-d, --dir <directory>', 'Target directory (overrides default)')
|
|
450
|
+
.option('--dry-run', 'Show what would be created without writing files')
|
|
451
|
+
.addCommand(generateTypesCommand)
|
|
452
|
+
.action(async (type: string | undefined, name: string | undefined, options) => {
|
|
453
|
+
if (!type) {
|
|
454
|
+
printHeader('Generate');
|
|
455
|
+
console.log(chalk.bold(' Sub-commands:'));
|
|
456
|
+
console.log(` ${chalk.cyan('types'.padEnd(12))} Generate TypeScript type definitions from config`);
|
|
457
|
+
console.log('');
|
|
458
|
+
console.log(chalk.bold(' Metadata types:'));
|
|
459
|
+
for (const [key, gen] of Object.entries(GENERATORS)) {
|
|
460
|
+
console.log(` ${chalk.cyan(key.padEnd(12))} ${chalk.dim(gen.description)}`);
|
|
461
|
+
}
|
|
462
|
+
console.log('');
|
|
463
|
+
console.log(chalk.dim(' Usage: objectstack generate <type> <name>'));
|
|
464
|
+
console.log(chalk.dim(' Usage: objectstack generate types [config]'));
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Delegate to metadata command action
|
|
469
|
+
if (!name) {
|
|
470
|
+
printError('Missing required argument: <name>');
|
|
471
|
+
console.log(chalk.dim(' Usage: objectstack generate <type> <name>'));
|
|
472
|
+
process.exit(1);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Execute metadata generation inline
|
|
476
|
+
await generateMetadataCommand.parseAsync([type, name, ...process.argv.slice(4)], { from: 'user' });
|
|
477
|
+
});
|