@objectstack/cli 3.0.6 → 3.0.8
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 +2 -26
- package/CHANGELOG.md +27 -0
- package/README.md +98 -54
- package/bin/run-dev.js +5 -0
- package/bin/run.js +5 -0
- package/dist/bin.d.ts +11 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +12 -3767
- package/dist/bin.js.map +1 -0
- package/dist/commands/codemod/v2-to-v3.d.ts +10 -0
- package/dist/commands/codemod/v2-to-v3.d.ts.map +1 -0
- package/dist/commands/codemod/v2-to-v3.js +145 -0
- package/dist/commands/codemod/v2-to-v3.js.map +1 -0
- package/dist/commands/compile.d.ts +13 -0
- package/dist/commands/compile.d.ts.map +1 -0
- package/dist/commands/compile.js +91 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/create.d.ts +91 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +259 -0
- package/dist/commands/create.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +67 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/diff.d.ts +16 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +239 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/doctor.d.ts +10 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +532 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/explain.d.ts +12 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +368 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/generate.d.ts +17 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +833 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/info.d.ts +12 -0
- package/dist/commands/info.d.ts.map +1 -0
- package/dist/commands/info.js +100 -0
- package/dist/commands/info.js.map +1 -0
- package/dist/commands/init.d.ts +22 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +295 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/lint.d.ts +13 -0
- package/dist/commands/lint.d.ts.map +1 -0
- package/dist/commands/lint.js +255 -0
- package/dist/commands/lint.js.map +1 -0
- package/dist/commands/plugin/add.d.ts +22 -0
- package/dist/commands/plugin/add.d.ts.map +1 -0
- package/dist/commands/plugin/add.js +93 -0
- package/dist/commands/plugin/add.js.map +1 -0
- package/dist/commands/plugin/info.d.ts +10 -0
- package/dist/commands/plugin/info.d.ts.map +1 -0
- package/dist/commands/plugin/info.js +65 -0
- package/dist/commands/plugin/info.js.map +1 -0
- package/dist/commands/plugin/list.d.ts +13 -0
- package/dist/commands/plugin/list.d.ts.map +1 -0
- package/dist/commands/plugin/list.js +78 -0
- package/dist/commands/plugin/list.js.map +1 -0
- package/dist/commands/plugin/remove.d.ts +20 -0
- package/dist/commands/plugin/remove.d.ts.map +1 -0
- package/dist/commands/plugin/remove.js +79 -0
- package/dist/commands/plugin/remove.js.map +1 -0
- package/dist/commands/serve.d.ts +15 -0
- package/dist/commands/serve.d.ts.map +1 -0
- package/dist/commands/serve.js +286 -0
- package/dist/commands/serve.js.map +1 -0
- package/dist/commands/studio.d.ts +19 -0
- package/dist/commands/studio.d.ts.map +1 -0
- package/dist/commands/studio.js +43 -0
- package/dist/commands/studio.js.map +1 -0
- package/dist/commands/test.d.ts +13 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +120 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/validate.d.ts +13 -0
- package/dist/commands/validate.d.ts.map +1 -0
- package/dist/commands/validate.js +115 -0
- package/dist/commands/validate.js.map +1 -0
- package/dist/index.d.ts +15 -114
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -2805
- package/dist/index.js.map +1 -0
- package/dist/utils/config.d.ts +21 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +66 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/format.d.ts +52 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +202 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/plugin-helpers.d.ts +14 -0
- package/dist/utils/plugin-helpers.d.ts.map +1 -0
- package/dist/utils/plugin-helpers.js +40 -0
- package/dist/utils/plugin-helpers.js.map +1 -0
- package/dist/utils/studio.d.ts +58 -0
- package/dist/utils/studio.d.ts.map +1 -0
- package/dist/utils/studio.js +288 -0
- package/dist/utils/studio.js.map +1 -0
- package/package.json +33 -15
- package/src/bin.ts +11 -104
- package/src/commands/{codemod.ts → codemod/v2-to-v3.ts} +21 -28
- package/src/commands/compile.ts +31 -22
- package/src/commands/create.ts +29 -19
- package/src/commands/dev.ts +21 -10
- package/src/commands/diff.ts +28 -19
- package/src/commands/doctor.ts +17 -10
- package/src/commands/explain.ts +20 -10
- package/src/commands/generate.ts +81 -90
- package/src/commands/info.ts +20 -11
- package/src/commands/init.ts +32 -20
- package/src/commands/lint.ts +24 -14
- package/src/commands/plugin/add.ts +112 -0
- package/src/commands/plugin/info.ts +79 -0
- package/src/commands/plugin/list.ts +93 -0
- package/src/commands/plugin/remove.ts +97 -0
- package/src/commands/serve.ts +30 -20
- package/src/commands/studio.ts +21 -11
- package/src/commands/test.ts +21 -10
- package/src/commands/validate.ts +32 -22
- package/src/index.ts +20 -12
- package/src/utils/plugin-helpers.ts +37 -0
- package/src/utils/studio.ts +0 -1
- package/test/commands.test.ts +76 -37
- package/test/plugin-commands.test.ts +42 -160
- package/test/plugin.test.ts +19 -23
- package/tsconfig.build.json +18 -0
- package/bin/objectstack.js +0 -2
- package/dist/chunk-T2YN4AB7.js +0 -249
- package/dist/chunk-XNACYTC5.js +0 -251
- package/dist/config-FOXDQ5F7.js +0 -10
- package/dist/config-GBR54FKL.js +0 -11
- package/src/commands/plugin.ts +0 -372
- package/src/utils/plugin-commands.ts +0 -163
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { resolveConfigPath } from '../../utils/config.js';
|
|
8
|
+
import { printHeader, printSuccess, printError } from '../../utils/format.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Add a plugin import and entry to objectstack.config.ts.
|
|
12
|
+
*
|
|
13
|
+
* This performs a simple text-based transformation:
|
|
14
|
+
* 1. Adds an import statement for the package at the top of the file.
|
|
15
|
+
* 2. Inserts the imported identifier into the `plugins` array, creating one if absent.
|
|
16
|
+
*/
|
|
17
|
+
function addPluginToConfig(configPath: string, packageName: string): void {
|
|
18
|
+
let content = fs.readFileSync(configPath, 'utf-8');
|
|
19
|
+
|
|
20
|
+
// Derive a variable name from the package name
|
|
21
|
+
// e.g. "@objectstack/plugin-auth" → "authPlugin"
|
|
22
|
+
const shortName = packageName
|
|
23
|
+
.replace(/^@[^/]+\//, '') // strip scope
|
|
24
|
+
.replace(/^plugin-/, '') // strip "plugin-" prefix
|
|
25
|
+
.replace(/-+/g, '-') // collapse consecutive hyphens
|
|
26
|
+
.replace(/^-|-$/g, ''); // trim leading/trailing hyphens
|
|
27
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) + 'Plugin';
|
|
28
|
+
|
|
29
|
+
// 1. Add import
|
|
30
|
+
const importLine = `import ${varName} from '${packageName}';\n`;
|
|
31
|
+
|
|
32
|
+
if (content.includes(packageName)) {
|
|
33
|
+
throw new Error(`Plugin '${packageName}' is already referenced in the config`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Insert import after the last existing import
|
|
37
|
+
const importRegex = /^import .+$/gm;
|
|
38
|
+
let lastImportEnd = 0;
|
|
39
|
+
let match: RegExpExecArray | null;
|
|
40
|
+
while ((match = importRegex.exec(content)) !== null) {
|
|
41
|
+
lastImportEnd = match.index + match[0].length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (lastImportEnd > 0) {
|
|
45
|
+
content = content.slice(0, lastImportEnd) + '\n' + importLine + content.slice(lastImportEnd);
|
|
46
|
+
} else {
|
|
47
|
+
content = importLine + '\n' + content;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 2. Add to plugins array (target only the first plugins: [ within defineStack)
|
|
51
|
+
if (/plugins\s*:\s*\[/.test(content)) {
|
|
52
|
+
// plugins array exists — append to it (first occurrence only)
|
|
53
|
+
let replaced = false;
|
|
54
|
+
content = content.replace(
|
|
55
|
+
/(plugins\s*:\s*\[)/,
|
|
56
|
+
(match) => {
|
|
57
|
+
if (replaced) return match;
|
|
58
|
+
replaced = true;
|
|
59
|
+
return `${match}\n ${varName},`;
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
// No plugins array — add one before the closing of defineStack({...})
|
|
64
|
+
// Look for the last property before the closing `})` or `})`
|
|
65
|
+
content = content.replace(
|
|
66
|
+
/(defineStack\(\{[\s\S]*?)(}\s*\))/,
|
|
67
|
+
`$1 plugins: [\n ${varName},\n ],\n$2`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
fs.writeFileSync(configPath, content);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { addPluginToConfig };
|
|
75
|
+
|
|
76
|
+
export default class PluginAdd extends Command {
|
|
77
|
+
static override description = 'Add a plugin to objectstack.config.ts';
|
|
78
|
+
|
|
79
|
+
static override args = {
|
|
80
|
+
package: Args.string({ description: 'Plugin package name (e.g. @objectstack/plugin-auth)', required: true }),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
static override flags = {
|
|
84
|
+
dev: Flags.boolean({ char: 'd', description: 'Add as a dev-only plugin' }),
|
|
85
|
+
config: Flags.string({ char: 'c', description: 'Configuration file path' }),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
async run(): Promise<void> {
|
|
89
|
+
const { args, flags } = await this.parse(PluginAdd);
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const configPath = resolveConfigPath(flags.config);
|
|
93
|
+
|
|
94
|
+
printHeader('Add Plugin');
|
|
95
|
+
console.log(` ${chalk.dim('Package:')} ${chalk.white(args.package)}`);
|
|
96
|
+
console.log(` ${chalk.dim('Config:')} ${chalk.white(path.relative(process.cwd(), configPath))}`);
|
|
97
|
+
console.log('');
|
|
98
|
+
|
|
99
|
+
addPluginToConfig(configPath, args.package);
|
|
100
|
+
printSuccess(`Added ${chalk.cyan(args.package)} to config`);
|
|
101
|
+
|
|
102
|
+
console.log('');
|
|
103
|
+
console.log(chalk.dim(' Next steps:'));
|
|
104
|
+
console.log(chalk.dim(` 1. Install the package: pnpm add ${args.package}`));
|
|
105
|
+
console.log(chalk.dim(' 2. Run: os validate'));
|
|
106
|
+
console.log('');
|
|
107
|
+
} catch (error: any) {
|
|
108
|
+
printError(error.message || String(error));
|
|
109
|
+
this.exit(1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { Args, Command } from '@oclif/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { loadConfig } from '../../utils/config.js';
|
|
6
|
+
import { printHeader, printError, printInfo, printKV } from '../../utils/format.js';
|
|
7
|
+
import { resolvePluginName, resolvePluginVersion, resolvePluginType } from '../../utils/plugin-helpers.js';
|
|
8
|
+
|
|
9
|
+
export default class PluginInfo extends Command {
|
|
10
|
+
static override description = 'Show detailed information about a plugin';
|
|
11
|
+
|
|
12
|
+
static override args = {
|
|
13
|
+
name: Args.string({ description: 'Plugin name or package name', required: true }),
|
|
14
|
+
config: Args.string({ description: 'Configuration file path', required: false }),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
async run(): Promise<void> {
|
|
18
|
+
const { args } = await this.parse(PluginInfo);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const { config } = await loadConfig(args.config);
|
|
22
|
+
const allPlugins: unknown[] = [
|
|
23
|
+
...(config.plugins || []),
|
|
24
|
+
...(config.devPlugins || []),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const found = allPlugins.find((p) => {
|
|
28
|
+
const pName = resolvePluginName(p);
|
|
29
|
+
return pName === args.name || pName.includes(args.name);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (!found) {
|
|
33
|
+
printError(`Plugin '${args.name}' not found in configuration`);
|
|
34
|
+
console.log('');
|
|
35
|
+
console.log(chalk.dim(' Available plugins:'));
|
|
36
|
+
for (const p of allPlugins) {
|
|
37
|
+
console.log(chalk.dim(` - ${resolvePluginName(p)}`));
|
|
38
|
+
}
|
|
39
|
+
console.log('');
|
|
40
|
+
this.exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
printHeader(`Plugin: ${resolvePluginName(found)}`);
|
|
44
|
+
|
|
45
|
+
printKV('Name', resolvePluginName(found));
|
|
46
|
+
printKV('Version', resolvePluginVersion(found));
|
|
47
|
+
printKV('Type', resolvePluginType(found));
|
|
48
|
+
|
|
49
|
+
const isDev = (config.devPlugins || []).includes(found);
|
|
50
|
+
printKV('Environment', isDev ? 'development' : 'production');
|
|
51
|
+
|
|
52
|
+
if (found && typeof found === 'object') {
|
|
53
|
+
const p = found as Record<string, unknown>;
|
|
54
|
+
|
|
55
|
+
if (typeof p.description === 'string') {
|
|
56
|
+
printKV('Description', p.description);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (Array.isArray(p.dependencies) && p.dependencies.length > 0) {
|
|
60
|
+
printKV('Dependencies', p.dependencies.join(', '));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Show services if it's a loaded plugin instance
|
|
64
|
+
if (typeof p.init === 'function') {
|
|
65
|
+
printInfo('This is a runtime plugin instance (has init function)');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof found === 'string') {
|
|
70
|
+
printInfo('This is a string reference (will be imported at runtime)');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log('');
|
|
74
|
+
} catch (error: any) {
|
|
75
|
+
printError(error.message || String(error));
|
|
76
|
+
this.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import { loadConfig } from '../../utils/config.js';
|
|
6
|
+
import { printHeader, printInfo, printError } from '../../utils/format.js';
|
|
7
|
+
import { resolvePluginName, resolvePluginVersion, resolvePluginType } from '../../utils/plugin-helpers.js';
|
|
8
|
+
|
|
9
|
+
export default class PluginList extends Command {
|
|
10
|
+
static override description = 'List plugins defined in the configuration';
|
|
11
|
+
|
|
12
|
+
static override aliases = ['plugin ls'];
|
|
13
|
+
|
|
14
|
+
static override args = {
|
|
15
|
+
config: Args.string({ description: 'Configuration file path', required: false }),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
static override flags = {
|
|
19
|
+
json: Flags.boolean({ description: 'Output as JSON' }),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
async run(): Promise<void> {
|
|
23
|
+
const { args, flags } = await this.parse(PluginList);
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const { config } = await loadConfig(args.config);
|
|
27
|
+
const plugins: unknown[] = config.plugins || [];
|
|
28
|
+
const devPlugins: unknown[] = config.devPlugins || [];
|
|
29
|
+
|
|
30
|
+
if (flags.json) {
|
|
31
|
+
const data = {
|
|
32
|
+
plugins: plugins.map(p => ({
|
|
33
|
+
name: resolvePluginName(p),
|
|
34
|
+
version: resolvePluginVersion(p),
|
|
35
|
+
type: resolvePluginType(p),
|
|
36
|
+
dev: false,
|
|
37
|
+
})),
|
|
38
|
+
devPlugins: devPlugins.map(p => ({
|
|
39
|
+
name: resolvePluginName(p),
|
|
40
|
+
version: resolvePluginVersion(p),
|
|
41
|
+
type: resolvePluginType(p),
|
|
42
|
+
dev: true,
|
|
43
|
+
})),
|
|
44
|
+
};
|
|
45
|
+
console.log(JSON.stringify(data, null, 2));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
printHeader('Plugins');
|
|
50
|
+
|
|
51
|
+
if (plugins.length === 0 && devPlugins.length === 0) {
|
|
52
|
+
printInfo('No plugins configured');
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(chalk.dim(' Hint: Add plugins to your objectstack.config.ts'));
|
|
55
|
+
console.log(chalk.dim(' Or run: os plugin add <package-name>'));
|
|
56
|
+
console.log('');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (plugins.length > 0) {
|
|
61
|
+
console.log(chalk.bold(`\n Plugins (${plugins.length}):`));
|
|
62
|
+
for (const plugin of plugins) {
|
|
63
|
+
const name = resolvePluginName(plugin);
|
|
64
|
+
const version = resolvePluginVersion(plugin);
|
|
65
|
+
const type = resolvePluginType(plugin);
|
|
66
|
+
console.log(
|
|
67
|
+
` ${chalk.cyan('●')} ${chalk.white(name)}` +
|
|
68
|
+
(version !== '-' ? chalk.dim(` v${version}`) : '') +
|
|
69
|
+
(type !== 'standard' ? chalk.dim(` [${type}]`) : '')
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (devPlugins.length > 0) {
|
|
75
|
+
console.log(chalk.bold(`\n Dev Plugins (${devPlugins.length}):`));
|
|
76
|
+
for (const plugin of devPlugins) {
|
|
77
|
+
const name = resolvePluginName(plugin);
|
|
78
|
+
const version = resolvePluginVersion(plugin);
|
|
79
|
+
console.log(
|
|
80
|
+
` ${chalk.yellow('●')} ${chalk.white(name)}` +
|
|
81
|
+
(version !== '-' ? chalk.dim(` v${version}`) : '') +
|
|
82
|
+
chalk.dim(' [dev]')
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
console.log('');
|
|
88
|
+
} catch (error: any) {
|
|
89
|
+
printError(error.message || String(error));
|
|
90
|
+
this.exit(1);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
|
+
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { resolveConfigPath } from '../../utils/config.js';
|
|
8
|
+
import { printHeader, printSuccess, printError } from '../../utils/format.js';
|
|
9
|
+
|
|
10
|
+
function escapeRegex(str: string): string {
|
|
11
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Remove a plugin reference from objectstack.config.ts.
|
|
16
|
+
*
|
|
17
|
+
* Removes matching import line and the entry from the plugins array.
|
|
18
|
+
*/
|
|
19
|
+
function removePluginFromConfig(configPath: string, pluginName: string): void {
|
|
20
|
+
let content = fs.readFileSync(configPath, 'utf-8');
|
|
21
|
+
|
|
22
|
+
// Remove the import line that references this plugin (exact package name match)
|
|
23
|
+
const importRegex = new RegExp(`^import .+['"]${escapeRegex(pluginName)}['"]\\s*;?\\s*$\\n?`, 'gm');
|
|
24
|
+
const hadImport = importRegex.test(content);
|
|
25
|
+
// Reset regex lastIndex after test()
|
|
26
|
+
importRegex.lastIndex = 0;
|
|
27
|
+
content = content.replace(importRegex, '');
|
|
28
|
+
|
|
29
|
+
// Also try to remove by a derived variable name
|
|
30
|
+
const shortName = pluginName
|
|
31
|
+
.replace(/^@[^/]+\//, '')
|
|
32
|
+
.replace(/^plugin-/, '')
|
|
33
|
+
.replace(/-+/g, '-')
|
|
34
|
+
.replace(/^-|-$/g, '');
|
|
35
|
+
const varName = shortName.replace(/-([a-z])/g, (_, c: string) => c.toUpperCase()) + 'Plugin';
|
|
36
|
+
|
|
37
|
+
// Remove import by variable name if it wasn't caught above
|
|
38
|
+
if (!hadImport) {
|
|
39
|
+
const varImportRegex = new RegExp(`^import .* ${escapeRegex(varName)} .+$\\n?`, 'gm');
|
|
40
|
+
content = content.replace(varImportRegex, '');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Remove the entry from the plugins array
|
|
44
|
+
// Match: varName, or 'package-name', or "package-name"
|
|
45
|
+
const entryPatterns = [
|
|
46
|
+
new RegExp(`\\s*${escapeRegex(varName)},?\\n?`, 'g'),
|
|
47
|
+
new RegExp(`\\s*['"]${escapeRegex(pluginName)}['"],?\\n?`, 'g'),
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const pattern of entryPatterns) {
|
|
51
|
+
content = content.replace(pattern, '\n');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Clean up empty plugins array: plugins: [\n ],
|
|
55
|
+
content = content.replace(/plugins\s*:\s*\[\s*\],?\n?/g, '');
|
|
56
|
+
|
|
57
|
+
fs.writeFileSync(configPath, content);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { removePluginFromConfig };
|
|
61
|
+
|
|
62
|
+
export default class PluginRemove extends Command {
|
|
63
|
+
static override description = 'Remove a plugin from objectstack.config.ts';
|
|
64
|
+
|
|
65
|
+
static override aliases = ['plugin rm'];
|
|
66
|
+
|
|
67
|
+
static override args = {
|
|
68
|
+
name: Args.string({ description: 'Plugin name or package name to remove', required: true }),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
static override flags = {
|
|
72
|
+
config: Flags.string({ char: 'c', description: 'Configuration file path' }),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
async run(): Promise<void> {
|
|
76
|
+
const { args, flags } = await this.parse(PluginRemove);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const configPath = resolveConfigPath(flags.config);
|
|
80
|
+
|
|
81
|
+
printHeader('Remove Plugin');
|
|
82
|
+
console.log(` ${chalk.dim('Plugin:')} ${chalk.white(args.name)}`);
|
|
83
|
+
console.log(` ${chalk.dim('Config:')} ${chalk.white(path.relative(process.cwd(), configPath))}`);
|
|
84
|
+
console.log('');
|
|
85
|
+
|
|
86
|
+
removePluginFromConfig(configPath, args.name);
|
|
87
|
+
printSuccess(`Removed ${chalk.cyan(args.name)} from config`);
|
|
88
|
+
|
|
89
|
+
console.log('');
|
|
90
|
+
console.log(chalk.dim(' Tip: Run `pnpm remove ' + args.name + '` to uninstall the package'));
|
|
91
|
+
console.log('');
|
|
92
|
+
} catch (error: any) {
|
|
93
|
+
printError(error.message || String(error));
|
|
94
|
+
this.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
package/src/commands/serve.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
|
-
import { Command } from '
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import net from 'net';
|
|
@@ -48,15 +48,24 @@ const getAvailablePort = async (startPort: number): Promise<number> => {
|
|
|
48
48
|
return port;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
export
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
export default class Serve extends Command {
|
|
52
|
+
static override description = 'Start ObjectStack server with plugins from configuration';
|
|
53
|
+
|
|
54
|
+
static override args = {
|
|
55
|
+
config: Args.string({ description: 'Configuration file path', required: false, default: 'objectstack.config.ts' }),
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
static override flags = {
|
|
59
|
+
port: Flags.string({ char: 'p', description: 'Server port', default: '3000' }),
|
|
60
|
+
dev: Flags.boolean({ description: 'Run in development mode (load devPlugins)' }),
|
|
61
|
+
ui: Flags.boolean({ description: 'Enable Studio UI at /_studio/ (default: true in dev mode)' }),
|
|
62
|
+
server: Flags.boolean({ description: 'Start HTTP server plugin', default: true, allowNo: true }),
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
async run(): Promise<void> {
|
|
66
|
+
const { args, flags } = await this.parse(Serve);
|
|
67
|
+
|
|
68
|
+
let port = parseInt(flags.port);
|
|
60
69
|
try {
|
|
61
70
|
const availablePort = await getAvailablePort(port);
|
|
62
71
|
if (availablePort !== port) {
|
|
@@ -66,15 +75,15 @@ export const serveCommand = new Command('serve')
|
|
|
66
75
|
// Ignore error and try with original port
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
const isDev =
|
|
78
|
+
const isDev = flags.dev || process.env.NODE_ENV === 'development';
|
|
70
79
|
|
|
71
|
-
const absolutePath = path.resolve(process.cwd(),
|
|
80
|
+
const absolutePath = path.resolve(process.cwd(), args.config!);
|
|
72
81
|
const relativeConfig = path.relative(process.cwd(), absolutePath);
|
|
73
82
|
|
|
74
83
|
if (!fs.existsSync(absolutePath)) {
|
|
75
84
|
printError(`Configuration file not found: ${absolutePath}`);
|
|
76
85
|
console.log(chalk.dim(' Hint: Run `objectstack init` to create a new project'));
|
|
77
|
-
|
|
86
|
+
this.exit(1);
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
// Quiet loading — only show a single spinner line
|
|
@@ -106,7 +115,7 @@ export const serveCommand = new Command('serve')
|
|
|
106
115
|
console.debug = originalConsoleDebug;
|
|
107
116
|
};
|
|
108
117
|
|
|
109
|
-
const portShifted = parseInt(
|
|
118
|
+
const portShifted = parseInt(flags.port) !== port;
|
|
110
119
|
|
|
111
120
|
try {
|
|
112
121
|
// ── Suppress ALL runtime noise during boot ────────────────────
|
|
@@ -131,7 +140,7 @@ export const serveCommand = new Command('serve')
|
|
|
131
140
|
const config = mod.default || mod;
|
|
132
141
|
|
|
133
142
|
if (!config) {
|
|
134
|
-
throw new Error(`No default export found in ${
|
|
143
|
+
throw new Error(`No default export found in ${args.config}`);
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
// Import ObjectStack runtime
|
|
@@ -151,7 +160,7 @@ export const serveCommand = new Command('serve')
|
|
|
151
160
|
let plugins = config.plugins || [];
|
|
152
161
|
|
|
153
162
|
// Merge devPlugins if in dev mode
|
|
154
|
-
if (
|
|
163
|
+
if (flags.dev && config.devPlugins) {
|
|
155
164
|
plugins = [...plugins, ...config.devPlugins];
|
|
156
165
|
}
|
|
157
166
|
|
|
@@ -228,7 +237,7 @@ export const serveCommand = new Command('serve')
|
|
|
228
237
|
}
|
|
229
238
|
|
|
230
239
|
// Add HTTP server plugin if not disabled
|
|
231
|
-
if (
|
|
240
|
+
if (flags.server) {
|
|
232
241
|
try {
|
|
233
242
|
const { HonoServerPlugin } = await import('@objectstack/plugin-hono-server');
|
|
234
243
|
const serverPlugin = new HonoServerPlugin({ port });
|
|
@@ -260,7 +269,7 @@ export const serveCommand = new Command('serve')
|
|
|
260
269
|
// ── Studio UI ─────────────────────────────────────────────────
|
|
261
270
|
// In dev mode, Studio UI is enabled by default (use --no-ui to disable).
|
|
262
271
|
// Always serves the pre-built dist/ — no Vite dev server, no extra port.
|
|
263
|
-
const enableUI =
|
|
272
|
+
const enableUI = flags.ui || isDev;
|
|
264
273
|
|
|
265
274
|
if (enableUI) {
|
|
266
275
|
const studioPath = resolveStudioPath();
|
|
@@ -306,6 +315,7 @@ export const serveCommand = new Command('serve')
|
|
|
306
315
|
console.log('');
|
|
307
316
|
printError(error.message || String(error));
|
|
308
317
|
if (process.env.DEBUG) console.error(chalk.dim(error.stack));
|
|
309
|
-
|
|
318
|
+
this.exit(1);
|
|
310
319
|
}
|
|
311
|
-
}
|
|
320
|
+
}
|
|
321
|
+
}
|
package/src/commands/studio.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
|
-
import { Command } from '
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import { spawn } from 'child_process';
|
|
6
6
|
import { printHeader, printKV, printStep } from '../utils/format.js';
|
|
@@ -12,11 +12,20 @@ import { printHeader, printKV, printStep } from '../utils/format.js';
|
|
|
12
12
|
* Starts the ObjectStack server in development mode with the Studio
|
|
13
13
|
* UI available at http://localhost:<port>/_studio/
|
|
14
14
|
*/
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
export default class Studio extends Command {
|
|
16
|
+
static override description = 'Launch Studio UI with development server';
|
|
17
|
+
|
|
18
|
+
static override args = {
|
|
19
|
+
config: Args.string({ description: 'Configuration file path', required: false, default: 'objectstack.config.ts' }),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
static override flags = {
|
|
23
|
+
port: Flags.string({ char: 'p', description: 'Server port', default: '3000' }),
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
async run(): Promise<void> {
|
|
27
|
+
const { args, flags } = await this.parse(Studio);
|
|
28
|
+
|
|
20
29
|
printHeader('Studio');
|
|
21
30
|
printKV('Mode', 'dev + ui', '🎨');
|
|
22
31
|
printStep('Delegating to serve --dev --ui …');
|
|
@@ -24,19 +33,20 @@ export const studioCommand = new Command('studio')
|
|
|
24
33
|
|
|
25
34
|
// Delegate to the serve command with --dev --ui flags
|
|
26
35
|
const binPath = process.argv[1];
|
|
27
|
-
const
|
|
36
|
+
const spawnArgs = [
|
|
28
37
|
binPath,
|
|
29
38
|
'serve',
|
|
30
|
-
|
|
39
|
+
args.config!,
|
|
31
40
|
'--dev',
|
|
32
41
|
'--ui',
|
|
33
|
-
'--port',
|
|
42
|
+
'--port', flags.port,
|
|
34
43
|
];
|
|
35
44
|
|
|
36
|
-
const child = spawn(process.execPath,
|
|
45
|
+
const child = spawn(process.execPath, spawnArgs, {
|
|
37
46
|
stdio: 'inherit',
|
|
38
47
|
env: { ...process.env, NODE_ENV: 'development' },
|
|
39
48
|
});
|
|
40
49
|
|
|
41
50
|
child.on('exit', (code) => process.exit(code ?? 0));
|
|
42
|
-
}
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/commands/test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
|
|
2
2
|
|
|
3
|
-
import { Command } from '
|
|
3
|
+
import { Args, Command, Flags } from '@oclif/core';
|
|
4
4
|
import chalk from 'chalk';
|
|
5
5
|
import path from 'path';
|
|
6
6
|
import fs from 'fs';
|
|
@@ -52,18 +52,28 @@ function resolveGlob(pattern: string): string[] {
|
|
|
52
52
|
.filter(fullPath => fs.statSync(fullPath).isFile());
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
export
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
export default class Test extends Command {
|
|
56
|
+
static override description = 'Run Quality Protocol test scenarios against a running server';
|
|
57
|
+
|
|
58
|
+
static override args = {
|
|
59
|
+
files: Args.string({ description: 'Glob pattern for test files (e.g. "qa/*.test.json")', required: false, default: 'qa/*.test.json' }),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
static override flags = {
|
|
63
|
+
url: Flags.string({ description: 'Target base URL', default: 'http://localhost:3000' }),
|
|
64
|
+
token: Flags.string({ description: 'Authentication token' }),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
async run(): Promise<void> {
|
|
68
|
+
const { args, flags } = await this.parse(Test);
|
|
69
|
+
const filesPattern = args.files;
|
|
70
|
+
|
|
61
71
|
console.log(chalk.bold(`\n🧪 ObjectStack Quality Protocol Runner`));
|
|
62
72
|
console.log(chalk.dim(`-------------------------------------`));
|
|
63
|
-
console.log(`Target: ${chalk.blue(
|
|
73
|
+
console.log(`Target: ${chalk.blue(flags.url)}`);
|
|
64
74
|
|
|
65
75
|
// 1. Setup Runner
|
|
66
|
-
const adapter = new CoreQA.HttpTestAdapter(
|
|
76
|
+
const adapter = new CoreQA.HttpTestAdapter(flags.url, flags.token);
|
|
67
77
|
const runner = new CoreQA.TestRunner(adapter);
|
|
68
78
|
|
|
69
79
|
// 2. Find test files using glob-style pattern matching
|
|
@@ -121,4 +131,5 @@ export const testCommand = new Command('test')
|
|
|
121
131
|
console.log(chalk.green(`SUCCESS: All ${totalPassed} scenarios passed.`));
|
|
122
132
|
process.exit(0);
|
|
123
133
|
}
|
|
124
|
-
}
|
|
134
|
+
}
|
|
135
|
+
}
|