@apso/cli 0.8.6 → 0.10.2
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 +221 -1653
- package/dist/commands/config.d.ts +13 -0
- package/dist/commands/config.js +114 -0
- package/dist/commands/deploy.d.ts +11 -0
- package/dist/commands/deploy.js +141 -0
- package/dist/commands/dev.d.ts +15 -0
- package/dist/commands/dev.js +95 -0
- package/dist/commands/generate.d.ts +12 -0
- package/dist/commands/generate.js +179 -0
- package/dist/commands/init.d.ts +16 -0
- package/dist/commands/init.js +313 -0
- package/dist/commands/link.d.ts +11 -0
- package/dist/commands/link.js +139 -0
- package/dist/commands/login.d.ts +22 -0
- package/dist/commands/login.js +345 -0
- package/dist/commands/logout.d.ts +9 -0
- package/dist/commands/logout.js +38 -0
- package/dist/commands/logs.d.ts +10 -0
- package/dist/commands/logs.js +64 -0
- package/dist/commands/mcp/serve.d.ts +11 -0
- package/dist/commands/mcp/serve.js +818 -0
- package/dist/commands/migrate.d.ts +11 -0
- package/dist/commands/migrate.js +137 -0
- package/dist/commands/open.d.ts +10 -0
- package/dist/commands/open.js +79 -0
- package/dist/commands/projects.d.ts +10 -0
- package/dist/commands/projects.js +106 -0
- package/dist/commands/schema/diff.d.ts +7 -0
- package/dist/commands/schema/diff.js +63 -0
- package/dist/commands/schema/pull.d.ts +9 -0
- package/dist/commands/schema/pull.js +81 -0
- package/dist/commands/schema/push.d.ts +9 -0
- package/dist/commands/schema/push.js +97 -0
- package/dist/commands/schema/validate.d.ts +9 -0
- package/dist/commands/schema/validate.js +90 -0
- package/dist/commands/server/new.d.ts +2 -19
- package/dist/commands/server/new.js +6 -192
- package/dist/commands/server/scaffold.d.ts +2 -25
- package/dist/commands/server/scaffold.js +6 -312
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.js +53 -0
- package/dist/commands/unlink.d.ts +9 -0
- package/dist/commands/unlink.js +47 -0
- package/dist/commands/whoami.d.ts +10 -0
- package/dist/commands/whoami.js +97 -0
- package/dist/lib/api/client.js +1 -1
- package/dist/lib/api/services.js +3 -3
- package/dist/lib/api/types.d.ts +9 -2
- package/dist/lib/apsorc-parser.d.ts +0 -19
- package/dist/lib/apsorc-parser.js +2 -73
- package/dist/lib/guards.d.ts +3 -20
- package/dist/lib/guards.js +1 -113
- package/dist/lib/index.d.ts +1 -9
- package/dist/lib/index.js +1 -18
- package/dist/lib/migrate/entity-generator.d.ts +14 -0
- package/dist/lib/migrate/entity-generator.js +7 -0
- package/dist/lib/migrate/sandbox.d.ts +42 -0
- package/dist/lib/migrate/sandbox.js +337 -0
- package/dist/lib/migrate/snapshot.d.ts +80 -0
- package/dist/lib/migrate/snapshot.js +100 -0
- package/dist/lib/templates/python/models/model-col-datetime.eta +1 -1
- package/dist/lib/templates/python/models/model-col-string.eta +1 -1
- package/dist/lib/templates/python/models/model-col-text.eta +1 -1
- package/dist/lib/templates/python/models/model-col-varchar.eta +1 -1
- package/dist/lib/utils/field.d.ts +12 -0
- package/dist/lib/utils/field.js +80 -1
- package/dist/lib/utils/schema-convert.d.ts +71 -0
- package/dist/lib/utils/schema-convert.js +169 -0
- package/dist/lib/utils/spinner.d.ts +19 -0
- package/dist/lib/utils/spinner.js +87 -0
- package/dist/lib/utils/template.d.ts +12 -0
- package/dist/lib/utils/template.js +49 -0
- package/npm-shrinkwrap.json +18118 -0
- package/oclif.manifest.json +547 -41
- package/package.json +26 -5
- package/dist/lib/controller.d.ts +0 -10
- package/dist/lib/controller.js +0 -99
- package/dist/lib/dto.d.ts +0 -16
- package/dist/lib/dto.js +0 -92
- package/dist/lib/entity.d.ts +0 -19
- package/dist/lib/entity.js +0 -56
- package/dist/lib/enums.d.ts +0 -6
- package/dist/lib/enums.js +0 -28
- package/dist/lib/gql-dto.d.ts +0 -3
- package/dist/lib/gql-dto.js +0 -39
- package/dist/lib/index-module.d.ts +0 -13
- package/dist/lib/index-module.js +0 -26
- package/dist/lib/module.d.ts +0 -13
- package/dist/lib/module.js +0 -42
- package/dist/lib/service.d.ts +0 -2
- package/dist/lib/service.js +0 -61
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import BaseCommand from "../lib/base-command";
|
|
2
|
+
export default class Config extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static strict: boolean;
|
|
6
|
+
static args: {
|
|
7
|
+
action: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
+
key: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
9
|
+
value: import("@oclif/core/lib/interfaces/parser").Arg<string | undefined, Record<string, unknown>>;
|
|
10
|
+
};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
private showAll;
|
|
13
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
|
|
6
|
+
const config_1 = require("../lib/config");
|
|
7
|
+
const types_1 = require("../lib/config/types");
|
|
8
|
+
const VALID_KEYS = [
|
|
9
|
+
"apiUrl",
|
|
10
|
+
"webUrl",
|
|
11
|
+
"verbose",
|
|
12
|
+
"noColor",
|
|
13
|
+
"telemetryDisabled",
|
|
14
|
+
"defaultWorkspace",
|
|
15
|
+
];
|
|
16
|
+
const BOOLEAN_KEYS = [
|
|
17
|
+
"verbose",
|
|
18
|
+
"noColor",
|
|
19
|
+
"telemetryDisabled",
|
|
20
|
+
];
|
|
21
|
+
function parseBoolean(value) {
|
|
22
|
+
const lower = value.toLowerCase();
|
|
23
|
+
if (lower === "true" || lower === "1")
|
|
24
|
+
return true;
|
|
25
|
+
if (lower === "false" || lower === "0")
|
|
26
|
+
return false;
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
class Config extends base_command_1.default {
|
|
30
|
+
async run() {
|
|
31
|
+
const { args, argv } = await this.parse(Config);
|
|
32
|
+
const action = args.action;
|
|
33
|
+
const rawArgv = argv;
|
|
34
|
+
// No action: show all config
|
|
35
|
+
if (!action) {
|
|
36
|
+
this.showAll();
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (action === "reset") {
|
|
40
|
+
config_1.globalConfig.reset();
|
|
41
|
+
this.log("Configuration reset to defaults.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (action === "get") {
|
|
45
|
+
const key = args.key;
|
|
46
|
+
if (!key) {
|
|
47
|
+
this.error("Usage: apso config get <key>");
|
|
48
|
+
}
|
|
49
|
+
if (!VALID_KEYS.includes(key)) {
|
|
50
|
+
this.error(`Unknown key: ${key}\nValid keys: ${VALID_KEYS.join(", ")}`);
|
|
51
|
+
}
|
|
52
|
+
const value = config_1.globalConfig.get(key);
|
|
53
|
+
this.log(String(value !== null && value !== void 0 ? value : ""));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (action === "set") {
|
|
57
|
+
const key = args.key || rawArgv[1];
|
|
58
|
+
const value = args.value || rawArgv[2];
|
|
59
|
+
if (!key || value === undefined) {
|
|
60
|
+
this.error("Usage: apso config set <key> <value>");
|
|
61
|
+
}
|
|
62
|
+
if (!VALID_KEYS.includes(key)) {
|
|
63
|
+
this.error(`Unknown key: ${key}\nValid keys: ${VALID_KEYS.join(", ")}`);
|
|
64
|
+
}
|
|
65
|
+
if (BOOLEAN_KEYS.includes(key)) {
|
|
66
|
+
const boolVal = parseBoolean(value);
|
|
67
|
+
if (boolVal === null) {
|
|
68
|
+
this.error(`Invalid boolean value: ${value}. Use true/false or 1/0.`);
|
|
69
|
+
}
|
|
70
|
+
config_1.globalConfig.set(key, boolVal);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
config_1.globalConfig.set(key, value);
|
|
74
|
+
}
|
|
75
|
+
this.log(`${key} = ${value}`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.error(`Unknown action: ${action}\nUsage: apso config [get|set|reset]`);
|
|
79
|
+
}
|
|
80
|
+
showAll() {
|
|
81
|
+
const config = config_1.globalConfig.read();
|
|
82
|
+
this.log("Current configuration:\n");
|
|
83
|
+
for (const key of VALID_KEYS) {
|
|
84
|
+
const value = config[key];
|
|
85
|
+
const defaultVal = types_1.DEFAULT_GLOBAL_CONFIG[key];
|
|
86
|
+
const isDefault = value === defaultVal;
|
|
87
|
+
const suffix = isDefault ? " (default)" : "";
|
|
88
|
+
this.log(` ${key} = ${value !== null && value !== void 0 ? value : "(not set)"}${suffix}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
Config.description = "View or modify CLI configuration";
|
|
93
|
+
Config.examples = [
|
|
94
|
+
`$ apso config`,
|
|
95
|
+
`$ apso config get apiUrl`,
|
|
96
|
+
`$ apso config set verbose true`,
|
|
97
|
+
`$ apso config reset`,
|
|
98
|
+
];
|
|
99
|
+
Config.strict = false;
|
|
100
|
+
Config.args = {
|
|
101
|
+
action: core_1.Args.string({
|
|
102
|
+
description: "Action: get, set, or reset",
|
|
103
|
+
required: false,
|
|
104
|
+
}),
|
|
105
|
+
key: core_1.Args.string({
|
|
106
|
+
description: "Configuration key",
|
|
107
|
+
required: false,
|
|
108
|
+
}),
|
|
109
|
+
value: core_1.Args.string({
|
|
110
|
+
description: "Value to set",
|
|
111
|
+
required: false,
|
|
112
|
+
}),
|
|
113
|
+
};
|
|
114
|
+
exports.default = Config;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import BaseCommand from "../lib/base-command";
|
|
2
|
+
export default class Deploy extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
"no-wait": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
"skip-migrate": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
|
|
6
|
+
const config_1 = require("../lib/config");
|
|
7
|
+
const services_1 = require("../lib/api/services");
|
|
8
|
+
const apsorc_parser_1 = require("../lib/apsorc-parser");
|
|
9
|
+
const sandbox_1 = require("../lib/migrate/sandbox");
|
|
10
|
+
const spinner_1 = require("../lib/utils/spinner");
|
|
11
|
+
class Deploy extends base_command_1.default {
|
|
12
|
+
async run() {
|
|
13
|
+
const { flags } = await this.parse(Deploy);
|
|
14
|
+
// Auth guard
|
|
15
|
+
if (!config_1.credentials.isValid()) {
|
|
16
|
+
this.error("Not logged in. Run 'apso login' first.");
|
|
17
|
+
}
|
|
18
|
+
// Link guard
|
|
19
|
+
const link = config_1.projectLink.read();
|
|
20
|
+
if (!link) {
|
|
21
|
+
this.error("Project not linked. Run 'apso link' first.");
|
|
22
|
+
}
|
|
23
|
+
// Fetch service
|
|
24
|
+
const service = await services_1.servicesApi.get(link.workspaceSlug, link.serviceSlug);
|
|
25
|
+
// Run migration sandbox (unless skipped)
|
|
26
|
+
let migrationCount = 0;
|
|
27
|
+
if (!flags["skip-migrate"]) {
|
|
28
|
+
let parsed;
|
|
29
|
+
try {
|
|
30
|
+
parsed = (0, apsorc_parser_1.parseApsorc)();
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
34
|
+
this.error(`Failed to parse .apsorc: ${msg}`);
|
|
35
|
+
}
|
|
36
|
+
this.log("Running migration check...");
|
|
37
|
+
const result = await (0, sandbox_1.runMigrationSandbox)({
|
|
38
|
+
entities: parsed.entities,
|
|
39
|
+
relationshipMap: parsed.relationshipMap,
|
|
40
|
+
});
|
|
41
|
+
if (result.needed) {
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
this.log("");
|
|
44
|
+
this.log("Migration FAILED locally. Deploy aborted.");
|
|
45
|
+
if (result.error) {
|
|
46
|
+
this.log(`Error: ${result.error}`);
|
|
47
|
+
}
|
|
48
|
+
this.log("Fix the schema issue before deploying.");
|
|
49
|
+
this.log("Run 'apso migrate' for details.");
|
|
50
|
+
this.exit(1);
|
|
51
|
+
}
|
|
52
|
+
migrationCount = result.upSql.length;
|
|
53
|
+
this.log(`Migration validated: ${migrationCount} statement(s).`);
|
|
54
|
+
// Show SQL preview
|
|
55
|
+
this.log("");
|
|
56
|
+
for (const sql of result.upSql) {
|
|
57
|
+
this.log(` ${sql};`);
|
|
58
|
+
}
|
|
59
|
+
this.log("");
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
this.log("No schema changes detected.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Determine source
|
|
66
|
+
if (service.githubRepo) {
|
|
67
|
+
this.log(`Deploying "${service.name}" from ${service.githubRepo}#${service.githubBranch || "main"}...`);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.log(`Deploying "${service.name}" via Apso Platform...`);
|
|
71
|
+
}
|
|
72
|
+
// Confirmation
|
|
73
|
+
if (!flags.yes && migrationCount > 0) {
|
|
74
|
+
const inquirer = await Promise.resolve().then(() => tslib_1.__importStar(require("inquirer")));
|
|
75
|
+
const { confirm } = await inquirer.default.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: "confirm",
|
|
78
|
+
name: "confirm",
|
|
79
|
+
message: `Deploy with ${migrationCount} migration statement(s)?`,
|
|
80
|
+
default: true,
|
|
81
|
+
},
|
|
82
|
+
]);
|
|
83
|
+
if (!confirm) {
|
|
84
|
+
this.log("Deploy cancelled.");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Trigger build
|
|
89
|
+
const build = await services_1.buildApi.trigger(link.workspaceSlug, link.serviceSlug);
|
|
90
|
+
this.log(`Build started: ${build.id}`);
|
|
91
|
+
if (flags["no-wait"]) {
|
|
92
|
+
this.log("Deploy triggered. Run 'apso status' to check progress.");
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Poll with spinner
|
|
96
|
+
const spinner = await (0, spinner_1.createSpinner)("Deploying...");
|
|
97
|
+
spinner.start();
|
|
98
|
+
const finalStatus = await services_1.buildApi.waitForCompletion(build.id, {
|
|
99
|
+
onProgress: (status) => {
|
|
100
|
+
spinner.text = `Deploy status: ${status.status}...`;
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
if (finalStatus.status === "success") {
|
|
104
|
+
spinner.succeed("Deploy succeeded.");
|
|
105
|
+
if (service.endpoint) {
|
|
106
|
+
this.log(`Endpoint: ${service.endpoint}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
spinner.fail("Deploy failed.");
|
|
111
|
+
if (finalStatus.error) {
|
|
112
|
+
this.log(`Error: ${finalStatus.error}`);
|
|
113
|
+
}
|
|
114
|
+
this.log("Run 'apso logs' to view build logs.");
|
|
115
|
+
this.exit(1);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
Deploy.description = "Deploy the linked service";
|
|
120
|
+
Deploy.examples = [
|
|
121
|
+
`$ apso deploy`,
|
|
122
|
+
`$ apso deploy --yes`,
|
|
123
|
+
`$ apso deploy --skip-migrate`,
|
|
124
|
+
`$ apso deploy --no-wait`,
|
|
125
|
+
];
|
|
126
|
+
Deploy.flags = {
|
|
127
|
+
"no-wait": core_1.Flags.boolean({
|
|
128
|
+
description: "Trigger deploy without waiting for completion",
|
|
129
|
+
default: false,
|
|
130
|
+
}),
|
|
131
|
+
yes: core_1.Flags.boolean({
|
|
132
|
+
char: "y",
|
|
133
|
+
description: "Skip confirmation prompt",
|
|
134
|
+
default: false,
|
|
135
|
+
}),
|
|
136
|
+
"skip-migrate": core_1.Flags.boolean({
|
|
137
|
+
description: "Skip local migration validation",
|
|
138
|
+
default: false,
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
exports.default = Deploy;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import BaseCommand from "../lib/base-command";
|
|
2
|
+
export default class Dev extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
detach: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
build: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* Detect whether to use `docker compose` (v2 plugin) or `docker-compose` (v1).
|
|
12
|
+
* Returns the command parts as an array.
|
|
13
|
+
*/
|
|
14
|
+
private detectComposeVariant;
|
|
15
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
7
|
+
const shelljs_1 = tslib_1.__importDefault(require("shelljs"));
|
|
8
|
+
const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
|
|
9
|
+
class Dev extends base_command_1.default {
|
|
10
|
+
async run() {
|
|
11
|
+
const { flags } = await this.parse(Dev);
|
|
12
|
+
// Check Docker is installed
|
|
13
|
+
if (!shelljs_1.default.which("docker")) {
|
|
14
|
+
this.error("Docker is required but not found. Please install Docker and try again.\nhttps://docs.docker.com/get-docker/");
|
|
15
|
+
}
|
|
16
|
+
// Detect compose variant (v2 plugin vs v1 standalone)
|
|
17
|
+
const composeCmd = this.detectComposeVariant();
|
|
18
|
+
// Verify docker-compose.yml exists
|
|
19
|
+
if (!fs.existsSync("docker-compose.yml")) {
|
|
20
|
+
this.error("No docker-compose.yml found in the current directory.\nMake sure you are in an Apso project root.");
|
|
21
|
+
}
|
|
22
|
+
// Build the command args
|
|
23
|
+
const args = [...composeCmd, "up"];
|
|
24
|
+
if (flags.build) {
|
|
25
|
+
args.push("--build");
|
|
26
|
+
}
|
|
27
|
+
if (flags.detach) {
|
|
28
|
+
args.push("-d");
|
|
29
|
+
}
|
|
30
|
+
this.log(`Running: ${args.join(" ")}`);
|
|
31
|
+
const child = (0, child_process_1.spawn)(args[0], args.slice(1), {
|
|
32
|
+
stdio: "inherit",
|
|
33
|
+
env: {
|
|
34
|
+
...process.env,
|
|
35
|
+
AUTH_DISABLED: "true",
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
// Forward signals for clean shutdown
|
|
39
|
+
const forwardSignal = (signal) => {
|
|
40
|
+
child.kill(signal);
|
|
41
|
+
};
|
|
42
|
+
process.on("SIGINT", forwardSignal);
|
|
43
|
+
process.on("SIGTERM", forwardSignal);
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
child.on("close", (code) => {
|
|
46
|
+
process.removeListener("SIGINT", forwardSignal);
|
|
47
|
+
process.removeListener("SIGTERM", forwardSignal);
|
|
48
|
+
if (code !== 0 && code !== null) {
|
|
49
|
+
reject(new Error(`docker compose exited with code ${code}`));
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
resolve();
|
|
53
|
+
});
|
|
54
|
+
child.on("error", (err) => {
|
|
55
|
+
this.error(`Failed to start Docker Compose: ${err.message}`);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Detect whether to use `docker compose` (v2 plugin) or `docker-compose` (v1).
|
|
61
|
+
* Returns the command parts as an array.
|
|
62
|
+
*/
|
|
63
|
+
detectComposeVariant() {
|
|
64
|
+
try {
|
|
65
|
+
(0, child_process_1.execSync)("docker compose version", { stdio: "ignore" });
|
|
66
|
+
return ["docker", "compose"];
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Fall back to v1
|
|
70
|
+
if (shelljs_1.default.which("docker-compose")) {
|
|
71
|
+
return ["docker-compose"];
|
|
72
|
+
}
|
|
73
|
+
this.error("Docker Compose is required but not found.\nInstall the Docker Compose plugin or standalone docker-compose.\nhttps://docs.docker.com/compose/install/");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
Dev.description = "Start the local development server using Docker Compose";
|
|
78
|
+
Dev.examples = [
|
|
79
|
+
`$ apso dev`,
|
|
80
|
+
`$ apso dev --build`,
|
|
81
|
+
`$ apso dev --detach`,
|
|
82
|
+
`$ apso dev --build --detach`,
|
|
83
|
+
];
|
|
84
|
+
Dev.flags = {
|
|
85
|
+
detach: core_1.Flags.boolean({
|
|
86
|
+
char: "d",
|
|
87
|
+
description: "Run containers in the background",
|
|
88
|
+
default: false,
|
|
89
|
+
}),
|
|
90
|
+
build: core_1.Flags.boolean({
|
|
91
|
+
description: "Rebuild images before starting",
|
|
92
|
+
default: false,
|
|
93
|
+
}),
|
|
94
|
+
};
|
|
95
|
+
exports.default = Dev;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import BaseCommand from "../lib/base-command";
|
|
2
|
+
export default class Generate extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
help: import("@oclif/core/lib/interfaces").BooleanFlag<void>;
|
|
7
|
+
"skip-format": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
language: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
9
|
+
};
|
|
10
|
+
static args: {};
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
|
|
7
|
+
const lib_1 = require("../lib");
|
|
8
|
+
const base_command_1 = tslib_1.__importDefault(require("../lib/base-command"));
|
|
9
|
+
const perf_hooks_1 = require("perf_hooks");
|
|
10
|
+
const file_system_1 = require("../lib/utils/file-system");
|
|
11
|
+
class Generate extends base_command_1.default {
|
|
12
|
+
async run() {
|
|
13
|
+
const { flags } = await this.parse(Generate);
|
|
14
|
+
const skipFormat = flags["skip-format"];
|
|
15
|
+
const totalBuildStart = perf_hooks_1.performance.now();
|
|
16
|
+
const { rootFolder, entities, relationshipMap, apiType, auth, language: configLanguage } = (0, lib_1.parseApsorc)();
|
|
17
|
+
// Resolve language: flag > .apsorc > prompt
|
|
18
|
+
let language;
|
|
19
|
+
if (flags.language) {
|
|
20
|
+
language = flags.language;
|
|
21
|
+
}
|
|
22
|
+
else if (configLanguage && (0, lib_1.isLanguageSupported)(configLanguage)) {
|
|
23
|
+
language = configLanguage;
|
|
24
|
+
console.log(`[apso] Using language from .apsorc: ${language}`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const implementedLanguages = (0, lib_1.getImplementedLanguages)();
|
|
28
|
+
const { selectedLanguage } = await inquirer_1.default.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: "list",
|
|
31
|
+
name: "selectedLanguage",
|
|
32
|
+
message: "Select target language for code generation:",
|
|
33
|
+
choices: implementedLanguages.map((lang) => ({
|
|
34
|
+
name: lang === "typescript" ? "TypeScript (NestJS + TypeORM)" :
|
|
35
|
+
lang === "python" ? "Python (FastAPI + SQLAlchemy)" :
|
|
36
|
+
"Go (Gin + GORM)",
|
|
37
|
+
value: lang,
|
|
38
|
+
})),
|
|
39
|
+
default: "typescript",
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
language = selectedLanguage;
|
|
43
|
+
}
|
|
44
|
+
if (!(0, lib_1.isLanguageSupported)(language)) {
|
|
45
|
+
this.error(`Unsupported language: ${language}. Supported: typescript, python, go`);
|
|
46
|
+
}
|
|
47
|
+
const implementedLanguages = (0, lib_1.getImplementedLanguages)();
|
|
48
|
+
if (!implementedLanguages.includes(language)) {
|
|
49
|
+
this.error(`Language '${language}' is not yet implemented. Currently available: ${implementedLanguages.join(", ")}`);
|
|
50
|
+
}
|
|
51
|
+
const rootPath = path.join(process.cwd(), rootFolder);
|
|
52
|
+
const autogenPath = path.join(rootPath, "autogen");
|
|
53
|
+
const lowerCaseApiType = apiType.toLowerCase();
|
|
54
|
+
console.log(`[apso] Generating ${language} code for ${entities.length} entities...`);
|
|
55
|
+
const generatorConfig = {
|
|
56
|
+
language,
|
|
57
|
+
rootFolder,
|
|
58
|
+
apiType: lowerCaseApiType,
|
|
59
|
+
entities,
|
|
60
|
+
relationshipMap,
|
|
61
|
+
auth,
|
|
62
|
+
};
|
|
63
|
+
const generator = (0, lib_1.createGenerator)(generatorConfig);
|
|
64
|
+
const validationResult = generator.validateConfig(generatorConfig);
|
|
65
|
+
if (!validationResult.valid) {
|
|
66
|
+
this.error(`Configuration validation failed:\n${validationResult.errors.join("\n")}`);
|
|
67
|
+
}
|
|
68
|
+
if (validationResult.warnings.length > 0) {
|
|
69
|
+
console.log(`[apso] Warnings:\n${validationResult.warnings.join("\n")}`);
|
|
70
|
+
}
|
|
71
|
+
// Generate enums
|
|
72
|
+
const enumFiles = await generator.generateEnums(entities, lowerCaseApiType);
|
|
73
|
+
for (const file of enumFiles) {
|
|
74
|
+
const fullPath = path.join(autogenPath, file.path);
|
|
75
|
+
// eslint-disable-next-line no-await-in-loop
|
|
76
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
77
|
+
}
|
|
78
|
+
// Generate per-entity files
|
|
79
|
+
for (const entity of entities) {
|
|
80
|
+
console.log(`[apso] Building... ${entity.name}`);
|
|
81
|
+
const entityBuildStart = perf_hooks_1.performance.now();
|
|
82
|
+
const entityRelationships = relationshipMap[entity.name] || [];
|
|
83
|
+
// eslint-disable-next-line no-await-in-loop
|
|
84
|
+
const allFiles = await Promise.all([
|
|
85
|
+
generator.generateEntity({
|
|
86
|
+
entity,
|
|
87
|
+
relationships: entityRelationships,
|
|
88
|
+
allEntities: entities,
|
|
89
|
+
apiType: lowerCaseApiType,
|
|
90
|
+
}),
|
|
91
|
+
generator.generateDto({
|
|
92
|
+
entity,
|
|
93
|
+
relationships: entityRelationships,
|
|
94
|
+
allEntities: entities,
|
|
95
|
+
apiType: lowerCaseApiType,
|
|
96
|
+
}),
|
|
97
|
+
generator.generateService({
|
|
98
|
+
entity,
|
|
99
|
+
relationships: entityRelationships,
|
|
100
|
+
allEntities: entities,
|
|
101
|
+
apiType: lowerCaseApiType,
|
|
102
|
+
relationshipMap,
|
|
103
|
+
}),
|
|
104
|
+
generator.generateController({
|
|
105
|
+
entity,
|
|
106
|
+
relationships: entityRelationships,
|
|
107
|
+
allEntities: entities,
|
|
108
|
+
apiType: lowerCaseApiType,
|
|
109
|
+
relationshipMap,
|
|
110
|
+
}),
|
|
111
|
+
generator.generateModule({
|
|
112
|
+
entity,
|
|
113
|
+
relationships: entityRelationships,
|
|
114
|
+
allEntities: entities,
|
|
115
|
+
apiType: lowerCaseApiType,
|
|
116
|
+
}),
|
|
117
|
+
]);
|
|
118
|
+
// eslint-disable-next-line no-await-in-loop
|
|
119
|
+
for (const files of allFiles) {
|
|
120
|
+
for (const file of files) {
|
|
121
|
+
const fullPath = path.join(autogenPath, file.path);
|
|
122
|
+
// eslint-disable-next-line no-await-in-loop
|
|
123
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const entityBuildTime = perf_hooks_1.performance.now() - entityBuildStart;
|
|
127
|
+
console.log(`[apso] Finished building entity '${entity.name}' in ${entityBuildTime.toFixed(2)} ms`);
|
|
128
|
+
}
|
|
129
|
+
// Generate guards
|
|
130
|
+
const guardFiles = await generator.generateGuards(entities, auth);
|
|
131
|
+
for (const file of guardFiles) {
|
|
132
|
+
const fullPath = path.join(autogenPath, file.path);
|
|
133
|
+
// eslint-disable-next-line no-await-in-loop
|
|
134
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
135
|
+
}
|
|
136
|
+
// Generate index module
|
|
137
|
+
const indexFiles = await generator.generateIndexModule(entities, lowerCaseApiType);
|
|
138
|
+
for (const file of indexFiles) {
|
|
139
|
+
const fullPath = path.join(autogenPath, file.path);
|
|
140
|
+
// eslint-disable-next-line no-await-in-loop
|
|
141
|
+
await (0, file_system_1.createFile)(fullPath, file.content);
|
|
142
|
+
}
|
|
143
|
+
// Format generated files (TypeScript only)
|
|
144
|
+
if (language === "typescript" && !skipFormat) {
|
|
145
|
+
const formatStart = perf_hooks_1.performance.now();
|
|
146
|
+
console.log("[apso] Formatting files...");
|
|
147
|
+
await this.runNpmCommand(["run", "format", "src/autogen/**/*.ts", "src/guards/**/*.ts"], true);
|
|
148
|
+
const formatTime = perf_hooks_1.performance.now() - formatStart;
|
|
149
|
+
console.log(`[apso] Finished formatting in ${formatTime.toFixed(2)} ms`);
|
|
150
|
+
}
|
|
151
|
+
else if (skipFormat) {
|
|
152
|
+
console.log("[apso] Skipping formatting (--skip-format flag set)");
|
|
153
|
+
}
|
|
154
|
+
const totalBuildTime = perf_hooks_1.performance.now() - totalBuildStart;
|
|
155
|
+
console.log(`[apso] Finished building all entities in ${totalBuildTime.toFixed(2)} ms`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
Generate.description = "Generate code from .apsorc schema";
|
|
159
|
+
Generate.examples = [
|
|
160
|
+
`$ apso generate`,
|
|
161
|
+
`$ apso generate --skip-format`,
|
|
162
|
+
`$ apso generate --language typescript`,
|
|
163
|
+
`$ apso generate --language python`,
|
|
164
|
+
`$ apso generate --language go`,
|
|
165
|
+
];
|
|
166
|
+
Generate.flags = {
|
|
167
|
+
help: core_1.Flags.help({ char: "h" }),
|
|
168
|
+
"skip-format": core_1.Flags.boolean({
|
|
169
|
+
description: "Skip the formatting step (useful when prettier is not available)",
|
|
170
|
+
default: false,
|
|
171
|
+
}),
|
|
172
|
+
language: core_1.Flags.string({
|
|
173
|
+
char: "l",
|
|
174
|
+
description: "Target language for code generation (typescript, python, go). If not specified, uses .apsorc config or prompts.",
|
|
175
|
+
options: ["typescript", "python", "go"],
|
|
176
|
+
}),
|
|
177
|
+
};
|
|
178
|
+
Generate.args = {};
|
|
179
|
+
exports.default = Generate;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import BaseCommand from "../lib/base-command";
|
|
2
|
+
export default class Init extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
|
+
language: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
"skip-platform": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
};
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
private runAuthenticated;
|
|
12
|
+
private cloneExisting;
|
|
13
|
+
private createNew;
|
|
14
|
+
private runOffline;
|
|
15
|
+
private postSetup;
|
|
16
|
+
}
|