@objectql/cli 1.8.3 → 1.9.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/README.md +157 -1
- package/dist/commands/ai.js +4 -3
- package/dist/commands/ai.js.map +1 -1
- package/dist/commands/build.d.ts +12 -0
- package/dist/commands/build.js +119 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/database-push.d.ts +5 -0
- package/dist/commands/database-push.js +15 -0
- package/dist/commands/database-push.js.map +1 -0
- package/dist/commands/dev.d.ts +11 -0
- package/dist/commands/dev.js +111 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/doctor.d.ts +4 -0
- package/dist/commands/doctor.js +37 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/format.d.ts +9 -0
- package/dist/commands/format.js +137 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/init.js +31 -30
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/lint.d.ts +9 -0
- package/dist/commands/lint.js +120 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/new.js +0 -52
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/serve.d.ts +2 -0
- package/dist/commands/serve.js +122 -46
- package/dist/commands/serve.js.map +1 -1
- package/dist/commands/start.d.ts +12 -0
- package/dist/commands/start.js +134 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/test.d.ts +10 -0
- package/dist/commands/test.js +120 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/index.js +189 -149
- package/dist/index.js.map +1 -1
- package/package.json +13 -8
- package/templates/hello-world/.vscode/extensions.json +7 -0
- package/templates/hello-world/CHANGELOG.md +41 -0
- package/templates/hello-world/README.md +29 -0
- package/templates/hello-world/package.json +24 -0
- package/templates/hello-world/src/index.ts +58 -0
- package/templates/hello-world/tsconfig.json +10 -0
- package/templates/starter/.vscode/extensions.json +7 -0
- package/{CHANGELOG.md → templates/starter/CHANGELOG.md} +47 -41
- package/templates/starter/README.md +17 -0
- package/templates/starter/__tests__/projects-hooks-actions.test.ts +490 -0
- package/templates/starter/jest.config.js +16 -0
- package/templates/starter/package.json +52 -0
- package/templates/starter/src/README.pages.md +110 -0
- package/templates/starter/src/demo.app.yml +4 -0
- package/templates/starter/src/i18n/zh-CN/projects.json +22 -0
- package/templates/starter/src/modules/kitchen-sink/kitchen_sink.data.yml +18 -0
- package/templates/starter/src/modules/kitchen-sink/kitchen_sink.object.yml +156 -0
- package/templates/starter/src/modules/projects/project_approval.workflow.yml +51 -0
- package/templates/starter/src/modules/projects/projects.action.ts +472 -0
- package/templates/starter/src/modules/projects/projects.data.yml +13 -0
- package/templates/starter/src/modules/projects/projects.hook.ts +339 -0
- package/templates/starter/src/modules/projects/projects.object.yml +148 -0
- package/templates/starter/src/modules/projects/projects.permission.yml +141 -0
- package/templates/starter/src/modules/projects/projects.validation.yml +37 -0
- package/templates/starter/src/modules/tasks/tasks.data.yml +23 -0
- package/templates/starter/src/modules/tasks/tasks.object.yml +34 -0
- package/templates/starter/src/modules/tasks/tasks.permission.yml +167 -0
- package/templates/starter/src/seed.ts +55 -0
- package/templates/starter/src/types/index.ts +3 -0
- package/templates/starter/src/types/kitchen_sink.ts +101 -0
- package/templates/starter/src/types/projects.ts +49 -0
- package/templates/starter/src/types/tasks.ts +33 -0
- package/templates/starter/tsconfig.json +11 -0
- package/templates/starter/tsconfig.tsbuildinfo +1 -0
- package/AI_EXAMPLES.md +0 -154
- package/AI_IMPLEMENTATION_SUMMARY.md +0 -509
- package/AI_TUTORIAL.md +0 -144
- package/IMPLEMENTATION_SUMMARY.md +0 -437
- package/USAGE_EXAMPLES.md +0 -951
- package/__tests__/commands.test.ts +0 -316
- package/dist/commands/studio.d.ts +0 -5
- package/dist/commands/studio.js +0 -364
- package/dist/commands/studio.js.map +0 -1
- package/jest.config.js +0 -19
- package/src/commands/ai.ts +0 -508
- package/src/commands/generate.ts +0 -135
- package/src/commands/i18n.ts +0 -303
- package/src/commands/init.ts +0 -191
- package/src/commands/migrate.ts +0 -314
- package/src/commands/new.ts +0 -273
- package/src/commands/repl.ts +0 -120
- package/src/commands/serve.ts +0 -96
- package/src/commands/studio.ts +0 -354
- package/src/commands/sync.ts +0 -328
- package/src/index.ts +0 -274
- package/tsconfig.json +0 -15
- package/tsconfig.tsbuildinfo +0 -1
package/src/commands/sync.ts
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import * as yaml from 'js-yaml';
|
|
5
|
-
import { IntrospectedSchema, IntrospectedTable, IntrospectedColumn, ObjectConfig, IObjectQL, FieldConfig, FieldType } from '@objectql/types';
|
|
6
|
-
|
|
7
|
-
interface SyncOptions {
|
|
8
|
-
config?: string;
|
|
9
|
-
output?: string;
|
|
10
|
-
tables?: string[];
|
|
11
|
-
force?: boolean;
|
|
12
|
-
app?: IObjectQL; // Allow passing app instance directly for testing
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Sync database schema to ObjectQL .object.yml files
|
|
17
|
-
* Introspects existing SQL database and generates object definitions
|
|
18
|
-
*/
|
|
19
|
-
export async function syncDatabase(options: SyncOptions) {
|
|
20
|
-
const outputDir = path.resolve(process.cwd(), options.output || './src/objects');
|
|
21
|
-
|
|
22
|
-
console.log(chalk.blue('🔄 Syncing database schema to ObjectQL...'));
|
|
23
|
-
console.log(chalk.gray(`Output directory: ${outputDir}\n`));
|
|
24
|
-
|
|
25
|
-
let app: IObjectQL | undefined = options.app;
|
|
26
|
-
const shouldClose = !options.app; // Only close if we loaded it ourselves
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
// Load ObjectQL instance from config if not provided
|
|
30
|
-
if (!app) {
|
|
31
|
-
app = await loadObjectQLInstance(options.config);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Check if driver supports introspection
|
|
35
|
-
const driver = app.datasource('default');
|
|
36
|
-
if (!driver || !driver.introspectSchema) {
|
|
37
|
-
const errorMsg = 'The configured driver does not support schema introspection. Only SQL drivers (PostgreSQL, MySQL, SQLite) support this feature.';
|
|
38
|
-
console.error(chalk.red(`❌ ${errorMsg}`));
|
|
39
|
-
throw new Error(errorMsg);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Introspect database schema
|
|
43
|
-
console.log(chalk.blue('📊 Introspecting database schema...'));
|
|
44
|
-
const schema: IntrospectedSchema = await driver.introspectSchema();
|
|
45
|
-
|
|
46
|
-
const tableNames = Object.keys(schema.tables);
|
|
47
|
-
if (tableNames.length === 0) {
|
|
48
|
-
console.log(chalk.yellow('⚠ No tables found in database'));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
console.log(chalk.green(`✓ Found ${tableNames.length} table(s)\n`));
|
|
53
|
-
|
|
54
|
-
// Filter tables if specified
|
|
55
|
-
let tablesToSync = tableNames;
|
|
56
|
-
if (options.tables && options.tables.length > 0) {
|
|
57
|
-
tablesToSync = tableNames.filter(t => options.tables!.includes(t));
|
|
58
|
-
if (tablesToSync.length === 0) {
|
|
59
|
-
console.log(chalk.yellow('⚠ No matching tables found'));
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Create output directory if it doesn't exist
|
|
65
|
-
if (!fs.existsSync(outputDir)) {
|
|
66
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
67
|
-
console.log(chalk.gray(`Created directory: ${outputDir}\n`));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Generate .object.yml files
|
|
71
|
-
let createdCount = 0;
|
|
72
|
-
let skippedCount = 0;
|
|
73
|
-
|
|
74
|
-
for (const tableName of tablesToSync) {
|
|
75
|
-
const table = schema.tables[tableName];
|
|
76
|
-
const filename = `${tableName}.object.yml`;
|
|
77
|
-
const filePath = path.join(outputDir, filename);
|
|
78
|
-
|
|
79
|
-
// Check if file already exists
|
|
80
|
-
if (fs.existsSync(filePath) && !options.force) {
|
|
81
|
-
console.log(chalk.yellow(`⊘ ${tableName} (file exists, use --force to overwrite)`));
|
|
82
|
-
skippedCount++;
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Generate object definition
|
|
87
|
-
const objectDef = generateObjectDefinition(table, schema);
|
|
88
|
-
|
|
89
|
-
// Write to file
|
|
90
|
-
const yamlContent = yaml.dump(objectDef, {
|
|
91
|
-
indent: 2,
|
|
92
|
-
lineWidth: -1,
|
|
93
|
-
noRefs: true,
|
|
94
|
-
sortKeys: false
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
fs.writeFileSync(filePath, yamlContent, 'utf-8');
|
|
98
|
-
|
|
99
|
-
console.log(chalk.green(`✓ ${tableName} → ${filename}`));
|
|
100
|
-
createdCount++;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
console.log(chalk.blue('\n📊 Summary:'));
|
|
104
|
-
console.log(chalk.gray(`Total tables: ${tablesToSync.length}`));
|
|
105
|
-
console.log(chalk.gray(`Created: ${createdCount}`));
|
|
106
|
-
console.log(chalk.gray(`Skipped: ${skippedCount}`));
|
|
107
|
-
|
|
108
|
-
if (createdCount > 0) {
|
|
109
|
-
console.log(chalk.green(`\n✅ Successfully synced ${createdCount} table(s) to ${outputDir}`));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
} catch (error: any) {
|
|
113
|
-
console.error(chalk.red(`❌ Sync failed: ${error.message}`));
|
|
114
|
-
if (error.stack) {
|
|
115
|
-
console.error(chalk.gray(error.stack));
|
|
116
|
-
}
|
|
117
|
-
throw error;
|
|
118
|
-
} finally {
|
|
119
|
-
// Ensure connection is closed if we opened it
|
|
120
|
-
if (shouldClose && app) {
|
|
121
|
-
if (app.close) {
|
|
122
|
-
await app.close();
|
|
123
|
-
} else {
|
|
124
|
-
// Fallback for older versions if close isn't available
|
|
125
|
-
const driver = app.datasource('default');
|
|
126
|
-
if (driver && (driver as any).disconnect) {
|
|
127
|
-
await (driver as any).disconnect();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Generate ObjectQL object definition from introspected table
|
|
136
|
-
*/
|
|
137
|
-
function generateObjectDefinition(table: IntrospectedTable, schema: IntrospectedSchema): ObjectConfig {
|
|
138
|
-
const obj: ObjectConfig = {
|
|
139
|
-
name: table.name,
|
|
140
|
-
label: formatLabel(table.name),
|
|
141
|
-
fields: {}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
// Process each column
|
|
145
|
-
for (const column of table.columns) {
|
|
146
|
-
// Skip system fields (id, created_at, updated_at) - they're automatic
|
|
147
|
-
if (['id', 'created_at', 'updated_at'].includes(column.name)) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const field: Partial<FieldConfig> = {};
|
|
152
|
-
|
|
153
|
-
// Check if this is a foreign key
|
|
154
|
-
const fk = table.foreignKeys.find(fk => fk.columnName === column.name);
|
|
155
|
-
if (fk) {
|
|
156
|
-
// This is a lookup/relationship field
|
|
157
|
-
field.type = 'lookup';
|
|
158
|
-
field.reference_to = fk.referencedTable;
|
|
159
|
-
|
|
160
|
-
// Add label
|
|
161
|
-
field.label = formatLabel(column.name);
|
|
162
|
-
|
|
163
|
-
// Add required constraint
|
|
164
|
-
if (!column.nullable) {
|
|
165
|
-
field.required = true;
|
|
166
|
-
}
|
|
167
|
-
} else {
|
|
168
|
-
// Regular field - map SQL type to ObjectQL type
|
|
169
|
-
const fieldType = mapSqlTypeToObjectQL(column.type, column);
|
|
170
|
-
field.type = fieldType;
|
|
171
|
-
|
|
172
|
-
// Add label
|
|
173
|
-
field.label = formatLabel(column.name);
|
|
174
|
-
|
|
175
|
-
// Add constraints
|
|
176
|
-
if (!column.nullable) {
|
|
177
|
-
field.required = true;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (column.isUnique) {
|
|
181
|
-
field.unique = true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Add max_length for text-based fields
|
|
185
|
-
if (column.maxLength && (fieldType === 'text' || fieldType === 'textarea')) {
|
|
186
|
-
field.max_length = column.maxLength;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
if (column.defaultValue !== undefined && column.defaultValue !== null) {
|
|
190
|
-
// Only include simple default values
|
|
191
|
-
if (typeof column.defaultValue === 'string' ||
|
|
192
|
-
typeof column.defaultValue === 'number' ||
|
|
193
|
-
typeof column.defaultValue === 'boolean') {
|
|
194
|
-
field.defaultValue = column.defaultValue;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
obj.fields[column.name] = field as FieldConfig;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
return obj;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Map SQL native type to ObjectQL field type
|
|
207
|
-
*/
|
|
208
|
-
function mapSqlTypeToObjectQL(sqlType: string, column: IntrospectedColumn): FieldType {
|
|
209
|
-
const type = sqlType.toLowerCase();
|
|
210
|
-
|
|
211
|
-
// Integer types - map to 'number'
|
|
212
|
-
if (type.includes('int') || type.includes('serial') || type.includes('bigserial')) {
|
|
213
|
-
return 'number';
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// Float/Decimal types
|
|
217
|
-
if (type.includes('float') || type.includes('double') ||
|
|
218
|
-
type.includes('decimal') || type.includes('numeric') || type.includes('real')) {
|
|
219
|
-
return 'number';
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Boolean
|
|
223
|
-
if (type.includes('bool') || type === 'bit') {
|
|
224
|
-
return 'boolean';
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Date/Time types
|
|
228
|
-
if (type.includes('timestamp') || type.includes('datetime')) {
|
|
229
|
-
return 'datetime';
|
|
230
|
-
}
|
|
231
|
-
if (type === 'date') {
|
|
232
|
-
return 'date';
|
|
233
|
-
}
|
|
234
|
-
if (type === 'time') {
|
|
235
|
-
return 'time';
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Text types
|
|
239
|
-
if (type.includes('text') || type.includes('clob') || type.includes('long')) {
|
|
240
|
-
return 'textarea';
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// JSON types - map to 'object'
|
|
244
|
-
if (type.includes('json') || type.includes('jsonb')) {
|
|
245
|
-
return 'object';
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Binary/Blob types
|
|
249
|
-
if (type.includes('blob') || type.includes('binary') || type.includes('bytea')) {
|
|
250
|
-
return 'file';
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// String types (varchar, char, etc.)
|
|
254
|
-
// Default to 'text' for general string fields
|
|
255
|
-
return 'text';
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* Format table/column name to human-readable label
|
|
260
|
-
* e.g., "user_profile" -> "User Profile"
|
|
261
|
-
*/
|
|
262
|
-
function formatLabel(name: string): string {
|
|
263
|
-
return name
|
|
264
|
-
.split('_')
|
|
265
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
266
|
-
.join(' ');
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Load ObjectQL instance from config file
|
|
271
|
-
*/
|
|
272
|
-
async function loadObjectQLInstance(configPath?: string): Promise<IObjectQL> {
|
|
273
|
-
const cwd = process.cwd();
|
|
274
|
-
|
|
275
|
-
// Try to load from config file
|
|
276
|
-
let configFile = configPath;
|
|
277
|
-
if (!configFile) {
|
|
278
|
-
const potentialFiles = ['objectql.config.ts', 'objectql.config.js'];
|
|
279
|
-
for (const file of potentialFiles) {
|
|
280
|
-
if (fs.existsSync(path.join(cwd, file))) {
|
|
281
|
-
configFile = path.join(cwd, file);
|
|
282
|
-
break;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
} else if (!path.isAbsolute(configFile)) {
|
|
286
|
-
// If configPath is provided but relative, make it absolute
|
|
287
|
-
configFile = path.join(cwd, configFile);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (!configFile) {
|
|
291
|
-
throw new Error('No configuration file found (objectql.config.ts/js). Please create one with database connection.');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Register ts-node for TypeScript support
|
|
295
|
-
if (configFile.endsWith('.ts')) {
|
|
296
|
-
try {
|
|
297
|
-
require('ts-node').register({
|
|
298
|
-
transpileOnly: true,
|
|
299
|
-
compilerOptions: {
|
|
300
|
-
module: 'commonjs'
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
} catch (err) {
|
|
304
|
-
throw new Error('TypeScript config file detected but ts-node is not installed. Please run: npm install --save-dev ts-node');
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
const configModule = require(configFile);
|
|
309
|
-
|
|
310
|
-
// Clear cache to support multiple runs in same process (e.g. tests)
|
|
311
|
-
try {
|
|
312
|
-
const resolvedPath = require.resolve(configFile);
|
|
313
|
-
delete require.cache[resolvedPath];
|
|
314
|
-
} catch (e) {
|
|
315
|
-
// Ignore resolution errors
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Support multiple export patterns: default, app, objectql, or db (in order of precedence)
|
|
319
|
-
const app = configModule.default || configModule.app || configModule.objectql || configModule.db;
|
|
320
|
-
|
|
321
|
-
if (!app) {
|
|
322
|
-
throw new Error('Config file must export an ObjectQL instance as default export or named export (app/objectql/db)');
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// Initialize app (but don't sync schema - we're reading it)
|
|
326
|
-
await app.init();
|
|
327
|
-
return app;
|
|
328
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { generateTypes } from './commands/generate';
|
|
3
|
-
import { startRepl } from './commands/repl';
|
|
4
|
-
import { serve } from './commands/serve';
|
|
5
|
-
import { startStudio } from './commands/studio';
|
|
6
|
-
import { initProject } from './commands/init';
|
|
7
|
-
import { newMetadata } from './commands/new';
|
|
8
|
-
import { i18nExtract, i18nInit, i18nValidate } from './commands/i18n';
|
|
9
|
-
import { migrate, migrateCreate, migrateStatus } from './commands/migrate';
|
|
10
|
-
import { aiGenerate, aiValidate, aiChat, aiConversational } from './commands/ai';
|
|
11
|
-
import { syncDatabase } from './commands/sync';
|
|
12
|
-
|
|
13
|
-
const program = new Command();
|
|
14
|
-
|
|
15
|
-
program
|
|
16
|
-
.name('objectql')
|
|
17
|
-
.description('ObjectQL CLI tool')
|
|
18
|
-
.version('1.5.0');
|
|
19
|
-
|
|
20
|
-
// Init command - Create new project
|
|
21
|
-
program
|
|
22
|
-
.command('init')
|
|
23
|
-
.description('Create a new ObjectQL project from template')
|
|
24
|
-
.option('-t, --template <template>', 'Template to use (basic, express-api, enterprise)', 'basic')
|
|
25
|
-
.option('-n, --name <name>', 'Project name')
|
|
26
|
-
.option('-d, --dir <path>', 'Target directory')
|
|
27
|
-
.option('--skip-install', 'Skip dependency installation')
|
|
28
|
-
.option('--skip-git', 'Skip git initialization')
|
|
29
|
-
.action(async (options) => {
|
|
30
|
-
try {
|
|
31
|
-
await initProject(options);
|
|
32
|
-
} catch (error) {
|
|
33
|
-
console.error(error);
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
// New command - Generate metadata files
|
|
39
|
-
program
|
|
40
|
-
.command('new <type> <name>')
|
|
41
|
-
.description('Generate a new metadata file (object, view, form, etc.)')
|
|
42
|
-
.option('-d, --dir <path>', 'Output directory', '.')
|
|
43
|
-
.action(async (type, name, options) => {
|
|
44
|
-
try {
|
|
45
|
-
await newMetadata({ type, name, dir: options.dir });
|
|
46
|
-
} catch (error) {
|
|
47
|
-
console.error(error);
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
// Generate command - Generate TypeScript types
|
|
53
|
-
program
|
|
54
|
-
.command('generate')
|
|
55
|
-
.alias('g')
|
|
56
|
-
.description('Generate TypeScript interfaces from ObjectQL schema files')
|
|
57
|
-
.option('-s, --source <path>', 'Source directory containing *.object.yml', '.')
|
|
58
|
-
.option('-o, --output <path>', 'Output directory for generated types', './src/generated')
|
|
59
|
-
.action(async (options) => {
|
|
60
|
-
try {
|
|
61
|
-
await generateTypes(options.source, options.output);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
console.error(error);
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
// I18n commands
|
|
69
|
-
const i18nCmd = program
|
|
70
|
-
.command('i18n')
|
|
71
|
-
.description('Internationalization commands');
|
|
72
|
-
|
|
73
|
-
i18nCmd
|
|
74
|
-
.command('extract')
|
|
75
|
-
.description('Extract translatable strings from metadata files')
|
|
76
|
-
.option('-s, --source <path>', 'Source directory', '.')
|
|
77
|
-
.option('-o, --output <path>', 'Output directory', './src/i18n')
|
|
78
|
-
.option('-l, --lang <lang>', 'Language code', 'en')
|
|
79
|
-
.action(async (options) => {
|
|
80
|
-
try {
|
|
81
|
-
await i18nExtract(options);
|
|
82
|
-
} catch (error) {
|
|
83
|
-
console.error(error);
|
|
84
|
-
process.exit(1);
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
i18nCmd
|
|
89
|
-
.command('init <lang>')
|
|
90
|
-
.description('Initialize i18n for a new language')
|
|
91
|
-
.option('-b, --base-dir <path>', 'Base i18n directory', './src/i18n')
|
|
92
|
-
.action(async (lang, options) => {
|
|
93
|
-
try {
|
|
94
|
-
await i18nInit({ lang, baseDir: options.baseDir });
|
|
95
|
-
} catch (error) {
|
|
96
|
-
console.error(error);
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
i18nCmd
|
|
102
|
-
.command('validate <lang>')
|
|
103
|
-
.description('Validate translation completeness')
|
|
104
|
-
.option('-b, --base-dir <path>', 'Base i18n directory', './src/i18n')
|
|
105
|
-
.option('--base-lang <lang>', 'Base language to compare against', 'en')
|
|
106
|
-
.action(async (lang, options) => {
|
|
107
|
-
try {
|
|
108
|
-
await i18nValidate({ lang, baseDir: options.baseDir, baseLang: options.baseLang });
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error(error);
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Migration commands
|
|
116
|
-
const migrateCmd = program
|
|
117
|
-
.command('migrate')
|
|
118
|
-
.description('Run pending database migrations')
|
|
119
|
-
.option('-c, --config <path>', 'Path to objectql.config.ts/js')
|
|
120
|
-
.option('-d, --dir <path>', 'Migrations directory', './migrations')
|
|
121
|
-
.action(async (options) => {
|
|
122
|
-
try {
|
|
123
|
-
await migrate(options);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
console.error(error);
|
|
126
|
-
process.exit(1);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
migrateCmd
|
|
131
|
-
.command('create <name>')
|
|
132
|
-
.description('Create a new migration file')
|
|
133
|
-
.option('-d, --dir <path>', 'Migrations directory', './migrations')
|
|
134
|
-
.action(async (name, options) => {
|
|
135
|
-
try {
|
|
136
|
-
await migrateCreate({ name, dir: options.dir });
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error(error);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
migrateCmd
|
|
144
|
-
.command('status')
|
|
145
|
-
.description('Show migration status')
|
|
146
|
-
.option('-c, --config <path>', 'Path to objectql.config.ts/js')
|
|
147
|
-
.option('-d, --dir <path>', 'Migrations directory', './migrations')
|
|
148
|
-
.action(async (options) => {
|
|
149
|
-
try {
|
|
150
|
-
await migrateStatus(options);
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error(error);
|
|
153
|
-
process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Sync command - Introspect database and generate .object.yml files
|
|
158
|
-
program
|
|
159
|
-
.command('sync')
|
|
160
|
-
.description('Sync database schema to ObjectQL object definitions')
|
|
161
|
-
.option('-c, --config <path>', 'Path to objectql.config.ts/js')
|
|
162
|
-
.option('-o, --output <path>', 'Output directory for .object.yml files', './src/objects')
|
|
163
|
-
.option('-t, --tables <tables...>', 'Specific tables to sync (default: all)')
|
|
164
|
-
.option('-f, --force', 'Overwrite existing files')
|
|
165
|
-
.action(async (options) => {
|
|
166
|
-
try {
|
|
167
|
-
await syncDatabase(options);
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error(error);
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
// REPL command
|
|
175
|
-
program
|
|
176
|
-
.command('repl')
|
|
177
|
-
.alias('r')
|
|
178
|
-
.description('Start an interactive shell (REPL) to query the database')
|
|
179
|
-
.option('-c, --config <path>', 'Path to objectql.config.ts/js')
|
|
180
|
-
.action(async (options) => {
|
|
181
|
-
await startRepl(options.config);
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
// Serve command
|
|
185
|
-
program
|
|
186
|
-
.command('serve')
|
|
187
|
-
.alias('s')
|
|
188
|
-
.description('Start a development server')
|
|
189
|
-
.option('-p, --port <number>', 'Port to listen on', '3000')
|
|
190
|
-
.option('-d, --dir <path>', 'Directory containing schema', '.')
|
|
191
|
-
.action(async (options) => {
|
|
192
|
-
await serve({ port: parseInt(options.port), dir: options.dir });
|
|
193
|
-
});
|
|
194
|
-
|
|
195
|
-
// Studio command
|
|
196
|
-
program
|
|
197
|
-
.command('studio')
|
|
198
|
-
.alias('ui')
|
|
199
|
-
.description('Start the ObjectQL Studio')
|
|
200
|
-
.option('-p, --port <number>', 'Port to listen on', '5555')
|
|
201
|
-
.option('-d, --dir <path>', 'Directory containing schema', '.')
|
|
202
|
-
.option('--no-open', 'Do not open browser automatically')
|
|
203
|
-
.action(async (options) => {
|
|
204
|
-
await startStudio({
|
|
205
|
-
port: parseInt(options.port),
|
|
206
|
-
dir: options.dir,
|
|
207
|
-
open: options.open
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// AI command - Interactive by default, with specific subcommands for other modes
|
|
212
|
-
const aiCmd = program
|
|
213
|
-
.command('ai')
|
|
214
|
-
.description('AI-powered interactive application builder (starts conversational mode by default)');
|
|
215
|
-
|
|
216
|
-
// Default action: Interactive conversational mode
|
|
217
|
-
aiCmd
|
|
218
|
-
.argument('[output-dir]', 'Output directory for generated files', './src')
|
|
219
|
-
.action(async (outputDir) => {
|
|
220
|
-
try {
|
|
221
|
-
await aiConversational({ output: outputDir });
|
|
222
|
-
} catch (error) {
|
|
223
|
-
console.error(error);
|
|
224
|
-
process.exit(1);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
// Subcommand: Generate (one-shot generation)
|
|
229
|
-
aiCmd
|
|
230
|
-
.command('generate')
|
|
231
|
-
.description('Generate application from description (one-shot, non-interactive)')
|
|
232
|
-
.requiredOption('-d, --description <text>', 'Application description')
|
|
233
|
-
.option('-o, --output <path>', 'Output directory', './src')
|
|
234
|
-
.option('-t, --type <type>', 'Generation type: basic, complete, or custom', 'custom')
|
|
235
|
-
.action(async (options) => {
|
|
236
|
-
try {
|
|
237
|
-
await aiGenerate(options);
|
|
238
|
-
} catch (error) {
|
|
239
|
-
console.error(error);
|
|
240
|
-
process.exit(1);
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// Subcommand: Validate
|
|
245
|
-
aiCmd
|
|
246
|
-
.command('validate')
|
|
247
|
-
.description('Validate metadata files with AI analysis')
|
|
248
|
-
.argument('<path>', 'Path to metadata files directory')
|
|
249
|
-
.option('--fix', 'Automatically fix issues')
|
|
250
|
-
.option('-v, --verbose', 'Detailed output')
|
|
251
|
-
.action(async (pathArg, options) => {
|
|
252
|
-
try {
|
|
253
|
-
await aiValidate({ path: pathArg, ...options });
|
|
254
|
-
} catch (error) {
|
|
255
|
-
console.error(error);
|
|
256
|
-
process.exit(1);
|
|
257
|
-
}
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Subcommand: Chat
|
|
261
|
-
aiCmd
|
|
262
|
-
.command('chat')
|
|
263
|
-
.description('AI assistant for questions and guidance')
|
|
264
|
-
.option('-p, --prompt <text>', 'Initial prompt')
|
|
265
|
-
.action(async (options) => {
|
|
266
|
-
try {
|
|
267
|
-
await aiChat({ initialPrompt: options.prompt });
|
|
268
|
-
} catch (error) {
|
|
269
|
-
console.error(error);
|
|
270
|
-
process.exit(1);
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
program.parse();
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../../tsconfig.base.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "dist",
|
|
5
|
-
"rootDir": "src"
|
|
6
|
-
},
|
|
7
|
-
"include": ["src"],
|
|
8
|
-
"references": [
|
|
9
|
-
{ "path": "../../foundation/types" },
|
|
10
|
-
{ "path": "../../foundation/core" },
|
|
11
|
-
{ "path": "../../runtime/server" },
|
|
12
|
-
{ "path": "../../foundation/platform-node" },
|
|
13
|
-
{ "path": "../../drivers/sql" }
|
|
14
|
-
]
|
|
15
|
-
}
|