@codemation/cli 0.0.1
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 +148 -0
- package/bin/codemation.js +24 -0
- package/bin/codemation.ts +5 -0
- package/dist/CliBin-vjSSUDWE.js +2304 -0
- package/dist/bin.d.ts +1 -0
- package/dist/bin.js +9 -0
- package/dist/index.d.ts +23456 -0
- package/dist/index.js +4 -0
- package/package.json +56 -0
- package/src/CliBin.ts +17 -0
- package/src/CliProgramFactory.ts +118 -0
- package/src/Program.ts +157 -0
- package/src/bin.ts +6 -0
- package/src/bootstrap/CodemationCliApplicationSession.ts +60 -0
- package/src/build/ConsumerBuildArtifactsPublisher.ts +77 -0
- package/src/build/ConsumerBuildOptionsParser.ts +26 -0
- package/src/commands/BuildCommand.ts +31 -0
- package/src/commands/DbMigrateCommand.ts +19 -0
- package/src/commands/DevCommand.ts +391 -0
- package/src/commands/ServeWebCommand.ts +72 -0
- package/src/commands/ServeWorkerCommand.ts +40 -0
- package/src/commands/UserCreateCommand.ts +25 -0
- package/src/commands/UserListCommand.ts +59 -0
- package/src/commands/devCommandLifecycle.types.ts +32 -0
- package/src/consumer/ConsumerCliTsconfigPreparation.ts +26 -0
- package/src/consumer/ConsumerEnvLoader.ts +47 -0
- package/src/consumer/ConsumerOutputBuilder.ts +898 -0
- package/src/consumer/Loader.ts +8 -0
- package/src/consumer/consumerBuildOptions.types.ts +12 -0
- package/src/database/ConsumerDatabaseConnectionResolver.ts +18 -0
- package/src/database/DatabaseMigrationsApplyService.ts +76 -0
- package/src/database/HostPackageRootResolver.ts +26 -0
- package/src/database/PrismaMigrateDeployInvoker.ts +24 -0
- package/src/dev/Builder.ts +45 -0
- package/src/dev/ConsumerEnvDotenvFilePredicate.ts +12 -0
- package/src/dev/DevAuthSettingsLoader.ts +27 -0
- package/src/dev/DevBootstrapSummaryFetcher.ts +15 -0
- package/src/dev/DevCliBannerRenderer.ts +106 -0
- package/src/dev/DevConsumerPublishBootstrap.ts +30 -0
- package/src/dev/DevHttpProbe.ts +54 -0
- package/src/dev/DevLock.ts +98 -0
- package/src/dev/DevNextHostEnvironmentBuilder.ts +49 -0
- package/src/dev/DevSessionPortsResolver.ts +23 -0
- package/src/dev/DevSessionServices.ts +29 -0
- package/src/dev/DevSourceRestartCoordinator.ts +48 -0
- package/src/dev/DevSourceWatcher.ts +102 -0
- package/src/dev/DevTrackedProcessTreeKiller.ts +107 -0
- package/src/dev/DevelopmentGatewayNotifier.ts +35 -0
- package/src/dev/Factory.ts +7 -0
- package/src/dev/LoopbackPortAllocator.ts +20 -0
- package/src/dev/Runner.ts +7 -0
- package/src/dev/RuntimeToolEntrypointResolver.ts +47 -0
- package/src/dev/WatchRootsResolver.ts +26 -0
- package/src/index.ts +12 -0
- package/src/path/CliPathResolver.ts +41 -0
- package/src/runtime/ListenPortResolver.ts +35 -0
- package/src/runtime/SourceMapNodeOptions.ts +12 -0
- package/src/runtime/TypeScriptRuntimeConfigurator.ts +8 -0
- package/src/user/CliDatabaseUrlDescriptor.ts +33 -0
- package/src/user/LocalUserCreator.ts +29 -0
- package/src/user/UserAdminCliBootstrap.ts +67 -0
- package/src/user/UserAdminCliOptionsParser.ts +24 -0
- package/src/user/UserAdminConsumerDotenvLoader.ts +24 -0
- package/tsconfig.json +10 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { a as CliProgram, i as CliPathResolver, n as CliProgramFactory, o as ConsumerOutputBuilder, r as CodemationCliApplicationSession, s as ConsumerBuildOptionsParser, t as CliBin } from "./CliBin-vjSSUDWE.js";
|
|
2
|
+
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
|
+
|
|
4
|
+
export { CliBin, CliPathResolver, CliProgram, CliProgramFactory, CodemationCliApplicationSession, CodemationPluginDiscovery, ConsumerBuildOptionsParser, ConsumerOutputBuilder };
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codemation/cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"author": "Made Relevant B.V.",
|
|
8
|
+
"homepage": "https://www.maderelevant.com",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"bin": {
|
|
13
|
+
"codemation": "./bin/codemation.js"
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"bcryptjs": "^3.0.3",
|
|
23
|
+
"boxen": "^8.0.1",
|
|
24
|
+
"chalk": "^5.6.2",
|
|
25
|
+
"chokidar": "^5.0.0",
|
|
26
|
+
"commander": "^14.0.3",
|
|
27
|
+
"dotenv": "^17.3.1",
|
|
28
|
+
"figlet": "^1.11.0",
|
|
29
|
+
"reflect-metadata": "^0.2.2",
|
|
30
|
+
"typescript": "^5.9.3",
|
|
31
|
+
"@codemation/host": "0.0.1",
|
|
32
|
+
"@codemation/dev-gateway": "0.0.1",
|
|
33
|
+
"@codemation/next-host": "0.0.1",
|
|
34
|
+
"@codemation/runtime-dev": "0.0.1",
|
|
35
|
+
"@codemation/worker-cli": "0.0.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.3.5",
|
|
39
|
+
"eslint": "^10.0.3",
|
|
40
|
+
"tsdown": "^0.15.5",
|
|
41
|
+
"tsx": "^4.21.0",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "4.0.18",
|
|
44
|
+
"@codemation/eslint-config": "0.0.0"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"tsx": ">=4.0.0"
|
|
48
|
+
},
|
|
49
|
+
"scripts": {
|
|
50
|
+
"dev": "tsdown src/index.ts src/bin.ts --out-dir dist --watch",
|
|
51
|
+
"build": "tsdown src/index.ts src/bin.ts --out-dir dist",
|
|
52
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
|
+
"lint": "eslint .",
|
|
54
|
+
"test": "vitest run"
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/CliBin.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
|
|
3
|
+
import { CliProgramFactory } from "./CliProgramFactory";
|
|
4
|
+
|
|
5
|
+
export class CliBin {
|
|
6
|
+
static async run(argv: ReadonlyArray<string>): Promise<void> {
|
|
7
|
+
try {
|
|
8
|
+
const cli = new CliProgramFactory().create();
|
|
9
|
+
await cli.run([...argv]);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
// Always print to stderr: host ServerLogger respects CODEMATION_LOG_LEVEL=silent and would
|
|
12
|
+
// suppress logger.error here; Prisma/DB errors also need the full Error (message + stack).
|
|
13
|
+
console.error("codemation:", error);
|
|
14
|
+
process.exitCode = 1;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { CodemationConsumerConfigLoader, CodemationPluginDiscovery } from "@codemation/host/server";
|
|
2
|
+
import { logLevelPolicyFactory, ServerLoggerFactory } from "@codemation/host/next/server";
|
|
3
|
+
|
|
4
|
+
import { ConsumerBuildArtifactsPublisher } from "./build/ConsumerBuildArtifactsPublisher";
|
|
5
|
+
import { ConsumerBuildOptionsParser } from "./build/ConsumerBuildOptionsParser";
|
|
6
|
+
import { BuildCommand } from "./commands/BuildCommand";
|
|
7
|
+
import { DbMigrateCommand } from "./commands/DbMigrateCommand";
|
|
8
|
+
import { DevCommand } from "./commands/DevCommand";
|
|
9
|
+
import { ServeWebCommand } from "./commands/ServeWebCommand";
|
|
10
|
+
import { ServeWorkerCommand } from "./commands/ServeWorkerCommand";
|
|
11
|
+
import { UserCreateCommand } from "./commands/UserCreateCommand";
|
|
12
|
+
import { UserListCommand } from "./commands/UserListCommand";
|
|
13
|
+
import { ConsumerCliTsconfigPreparation } from "./consumer/ConsumerCliTsconfigPreparation";
|
|
14
|
+
import { ConsumerEnvLoader } from "./consumer/ConsumerEnvLoader";
|
|
15
|
+
import { ConsumerOutputBuilderLoader } from "./consumer/Loader";
|
|
16
|
+
import { ConsumerDatabaseConnectionResolver } from "./database/ConsumerDatabaseConnectionResolver";
|
|
17
|
+
import { DatabaseMigrationsApplyService } from "./database/DatabaseMigrationsApplyService";
|
|
18
|
+
import { HostPackageRootResolver } from "./database/HostPackageRootResolver";
|
|
19
|
+
import { DatabasePersistenceResolver, PrismaMigrationDeployer } from "@codemation/host/persistence";
|
|
20
|
+
import { DevBootstrapSummaryFetcher } from "./dev/DevBootstrapSummaryFetcher";
|
|
21
|
+
import { DevCliBannerRenderer } from "./dev/DevCliBannerRenderer";
|
|
22
|
+
import { DevConsumerPublishBootstrap } from "./dev/DevConsumerPublishBootstrap";
|
|
23
|
+
import { DevSessionServicesBuilder } from "./dev/Builder";
|
|
24
|
+
import { DevLockFactory } from "./dev/Factory";
|
|
25
|
+
import { ConsumerEnvDotenvFilePredicate } from "./dev/ConsumerEnvDotenvFilePredicate";
|
|
26
|
+
import { DevTrackedProcessTreeKiller } from "./dev/DevTrackedProcessTreeKiller";
|
|
27
|
+
import { DevSourceWatcherFactory } from "./dev/Runner";
|
|
28
|
+
import { CliProgram } from "./Program";
|
|
29
|
+
import { CliPathResolver } from "./path/CliPathResolver";
|
|
30
|
+
import { ListenPortResolver } from "./runtime/ListenPortResolver";
|
|
31
|
+
import { SourceMapNodeOptions } from "./runtime/SourceMapNodeOptions";
|
|
32
|
+
import { TypeScriptRuntimeConfigurator } from "./runtime/TypeScriptRuntimeConfigurator";
|
|
33
|
+
import { LocalUserCreator } from "./user/LocalUserCreator";
|
|
34
|
+
import { CliDatabaseUrlDescriptor } from "./user/CliDatabaseUrlDescriptor";
|
|
35
|
+
import { UserAdminCliBootstrap } from "./user/UserAdminCliBootstrap";
|
|
36
|
+
import { UserAdminCliOptionsParser } from "./user/UserAdminCliOptionsParser";
|
|
37
|
+
import { UserAdminConsumerDotenvLoader } from "./user/UserAdminConsumerDotenvLoader";
|
|
38
|
+
|
|
39
|
+
const loggerFactory = new ServerLoggerFactory(logLevelPolicyFactory);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Single composition root for the CLI: constructs the object graph and returns {@link CliProgram}.
|
|
43
|
+
* No tsyringe; keeps the package thin while commands remain constructor-injected.
|
|
44
|
+
*/
|
|
45
|
+
export class CliProgramFactory {
|
|
46
|
+
create(): CliProgram {
|
|
47
|
+
const cliLogger = loggerFactory.create("codemation-cli");
|
|
48
|
+
const pathResolver = new CliPathResolver();
|
|
49
|
+
const pluginDiscovery = new CodemationPluginDiscovery();
|
|
50
|
+
const artifactsPublisher = new ConsumerBuildArtifactsPublisher();
|
|
51
|
+
const tsRuntime = new TypeScriptRuntimeConfigurator();
|
|
52
|
+
const outputBuilderLoader = new ConsumerOutputBuilderLoader();
|
|
53
|
+
const sourceMapNodeOptions = new SourceMapNodeOptions();
|
|
54
|
+
const tsconfigPreparation = new ConsumerCliTsconfigPreparation();
|
|
55
|
+
const databasePersistenceResolver = new DatabasePersistenceResolver();
|
|
56
|
+
const userAdminBootstrap = new UserAdminCliBootstrap(
|
|
57
|
+
new CodemationConsumerConfigLoader(),
|
|
58
|
+
pathResolver,
|
|
59
|
+
new UserAdminConsumerDotenvLoader(),
|
|
60
|
+
tsconfigPreparation,
|
|
61
|
+
databasePersistenceResolver,
|
|
62
|
+
);
|
|
63
|
+
const hostPackageRoot = new HostPackageRootResolver().resolveHostPackageRoot();
|
|
64
|
+
const userAdminCliOptionsParser = new UserAdminCliOptionsParser();
|
|
65
|
+
const databaseMigrationsApplyService = new DatabaseMigrationsApplyService(
|
|
66
|
+
cliLogger,
|
|
67
|
+
new UserAdminConsumerDotenvLoader(),
|
|
68
|
+
tsconfigPreparation,
|
|
69
|
+
new CodemationConsumerConfigLoader(),
|
|
70
|
+
new ConsumerDatabaseConnectionResolver(),
|
|
71
|
+
new CliDatabaseUrlDescriptor(),
|
|
72
|
+
hostPackageRoot,
|
|
73
|
+
new PrismaMigrationDeployer(),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const buildOptionsParser = new ConsumerBuildOptionsParser();
|
|
77
|
+
const devConsumerPublishBootstrap = new DevConsumerPublishBootstrap(
|
|
78
|
+
cliLogger,
|
|
79
|
+
pluginDiscovery,
|
|
80
|
+
artifactsPublisher,
|
|
81
|
+
outputBuilderLoader,
|
|
82
|
+
buildOptionsParser,
|
|
83
|
+
);
|
|
84
|
+
return new CliProgram(
|
|
85
|
+
buildOptionsParser,
|
|
86
|
+
new BuildCommand(cliLogger, pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, outputBuilderLoader),
|
|
87
|
+
new DevCommand(
|
|
88
|
+
pathResolver,
|
|
89
|
+
pluginDiscovery,
|
|
90
|
+
tsRuntime,
|
|
91
|
+
new DevLockFactory(),
|
|
92
|
+
new DevSourceWatcherFactory(),
|
|
93
|
+
cliLogger,
|
|
94
|
+
new DevSessionServicesBuilder(loggerFactory).build(),
|
|
95
|
+
databaseMigrationsApplyService,
|
|
96
|
+
new DevBootstrapSummaryFetcher(),
|
|
97
|
+
new DevCliBannerRenderer(),
|
|
98
|
+
devConsumerPublishBootstrap,
|
|
99
|
+
new ConsumerEnvDotenvFilePredicate(),
|
|
100
|
+
new DevTrackedProcessTreeKiller(),
|
|
101
|
+
),
|
|
102
|
+
new ServeWebCommand(
|
|
103
|
+
pathResolver,
|
|
104
|
+
pluginDiscovery,
|
|
105
|
+
artifactsPublisher,
|
|
106
|
+
tsRuntime,
|
|
107
|
+
sourceMapNodeOptions,
|
|
108
|
+
outputBuilderLoader,
|
|
109
|
+
new ConsumerEnvLoader(),
|
|
110
|
+
new ListenPortResolver(),
|
|
111
|
+
),
|
|
112
|
+
new ServeWorkerCommand(sourceMapNodeOptions),
|
|
113
|
+
new DbMigrateCommand(databaseMigrationsApplyService),
|
|
114
|
+
new UserCreateCommand(new LocalUserCreator(userAdminBootstrap), userAdminCliOptionsParser),
|
|
115
|
+
new UserListCommand(cliLogger, userAdminBootstrap, new CliDatabaseUrlDescriptor(), userAdminCliOptionsParser),
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
package/src/Program.ts
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { ConsumerBuildOptionsParser } from "./build/ConsumerBuildOptionsParser";
|
|
8
|
+
import { BuildCommand } from "./commands/BuildCommand";
|
|
9
|
+
import type { DbMigrateCommand } from "./commands/DbMigrateCommand";
|
|
10
|
+
import { DevCommand } from "./commands/DevCommand";
|
|
11
|
+
import { ServeWebCommand } from "./commands/ServeWebCommand";
|
|
12
|
+
import { ServeWorkerCommand } from "./commands/ServeWorkerCommand";
|
|
13
|
+
import { UserCreateCommand } from "./commands/UserCreateCommand";
|
|
14
|
+
import { UserListCommand } from "./commands/UserListCommand";
|
|
15
|
+
|
|
16
|
+
export class CliProgram {
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly buildOptionsParser: ConsumerBuildOptionsParser,
|
|
19
|
+
private readonly buildCommand: BuildCommand,
|
|
20
|
+
private readonly devCommand: DevCommand,
|
|
21
|
+
private readonly serveWebCommand: ServeWebCommand,
|
|
22
|
+
private readonly serveWorkerCommand: ServeWorkerCommand,
|
|
23
|
+
private readonly dbMigrateCommand: DbMigrateCommand,
|
|
24
|
+
private readonly userCreateCommand: UserCreateCommand,
|
|
25
|
+
private readonly userListCommand: UserListCommand,
|
|
26
|
+
) {}
|
|
27
|
+
|
|
28
|
+
async run(argv: ReadonlyArray<string>): Promise<void> {
|
|
29
|
+
const program = new Command();
|
|
30
|
+
program
|
|
31
|
+
.name("codemation")
|
|
32
|
+
.description("Build and run the Codemation Next host against a consumer project.")
|
|
33
|
+
.version(this.readCliPackageVersion(), "-V, --version", "Output CLI version")
|
|
34
|
+
.showHelpAfterError("(add --help for usage)")
|
|
35
|
+
.configureHelp({ sortSubcommands: true });
|
|
36
|
+
|
|
37
|
+
const resolveConsumerRoot = (raw: string | undefined): string =>
|
|
38
|
+
raw !== undefined && raw.trim().length > 0 ? path.resolve(process.cwd(), raw.trim()) : process.cwd();
|
|
39
|
+
|
|
40
|
+
program
|
|
41
|
+
.command("build")
|
|
42
|
+
.description("Build consumer workflows/plugins output and write the manifest.")
|
|
43
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
44
|
+
.option(
|
|
45
|
+
"--no-source-maps",
|
|
46
|
+
"Disable .js.map files for emitted workflow modules (recommended for locked-down production bundles).",
|
|
47
|
+
)
|
|
48
|
+
.option(
|
|
49
|
+
"--target <es2020|es2022>",
|
|
50
|
+
"ECMAScript language version for emitted workflow JavaScript (default: es2022).",
|
|
51
|
+
"es2022",
|
|
52
|
+
)
|
|
53
|
+
.action(async (opts: Readonly<{ consumerRoot?: string; noSourceMaps?: boolean; target?: string }>) => {
|
|
54
|
+
await this.buildCommand.execute(resolveConsumerRoot(opts.consumerRoot), this.buildOptionsParser.parse(opts));
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
program
|
|
58
|
+
.command("dev", { isDefault: true })
|
|
59
|
+
.description(
|
|
60
|
+
"Start the dev gateway and runtime child. Use CODEMATION_DEV_MODE=framework with Next dev for framework UI HMR; default consumer mode serves API/WebSocket from the gateway only.",
|
|
61
|
+
)
|
|
62
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
63
|
+
.action(async (opts: Readonly<{ consumerRoot?: string }>) => {
|
|
64
|
+
await this.devCommand.execute(resolveConsumerRoot(opts.consumerRoot));
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const serve = program.command("serve").description("Run production web or worker processes (no dev watchers).");
|
|
68
|
+
|
|
69
|
+
serve
|
|
70
|
+
.command("web")
|
|
71
|
+
.description("Start the built Next.js Codemation host (next start).")
|
|
72
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
73
|
+
.option(
|
|
74
|
+
"--no-source-maps",
|
|
75
|
+
"Disable .js.map files for emitted workflow modules when this command runs the consumer build step.",
|
|
76
|
+
)
|
|
77
|
+
.option(
|
|
78
|
+
"--target <es2020|es2022>",
|
|
79
|
+
"ECMAScript language version for emitted workflow JavaScript when building consumer output (default: es2022).",
|
|
80
|
+
"es2022",
|
|
81
|
+
)
|
|
82
|
+
.action(async (opts: Readonly<{ consumerRoot?: string; noSourceMaps?: boolean; target?: string }>) => {
|
|
83
|
+
await this.serveWebCommand.execute(resolveConsumerRoot(opts.consumerRoot), this.buildOptionsParser.parse(opts));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
serve
|
|
87
|
+
.command("worker")
|
|
88
|
+
.description("Start the Codemation worker process.")
|
|
89
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
90
|
+
.option("--config <path>", "Override path to codemation.config.ts / .js")
|
|
91
|
+
.action(async (opts: Readonly<{ consumerRoot?: string; config?: string }>) => {
|
|
92
|
+
await this.serveWorkerCommand.execute(resolveConsumerRoot(opts.consumerRoot), opts.config);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const db = program.command("db").description("Database utilities (PostgreSQL / Prisma).");
|
|
96
|
+
|
|
97
|
+
db.command("migrate")
|
|
98
|
+
.description(
|
|
99
|
+
"Apply pending Prisma migrations using the consumer database URL (DATABASE_URL in `.env`, or CodemationConfig.runtime.database.url).",
|
|
100
|
+
)
|
|
101
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
102
|
+
.option("--config <path>", "Override path to codemation.config.ts / .js")
|
|
103
|
+
.action(async (opts: Readonly<{ consumerRoot?: string; config?: string }>) => {
|
|
104
|
+
await this.dbMigrateCommand.execute({
|
|
105
|
+
consumerRoot: resolveConsumerRoot(opts.consumerRoot),
|
|
106
|
+
configPath: opts.config,
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const user = program.command("user").description("User administration (local auth)");
|
|
111
|
+
|
|
112
|
+
user
|
|
113
|
+
.command("create")
|
|
114
|
+
.description(
|
|
115
|
+
'Create or update a user in the database when CodemationConfig.auth.kind is "local". Uses DATABASE_URL or configured database URL.',
|
|
116
|
+
)
|
|
117
|
+
.requiredOption("--email <email>", "Login email")
|
|
118
|
+
.requiredOption("--password <password>", "Plain password (stored as a bcrypt hash)")
|
|
119
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
120
|
+
.option("--config <path>", "Override path to codemation.config.ts / .js")
|
|
121
|
+
.action(
|
|
122
|
+
async (
|
|
123
|
+
opts: Readonly<{
|
|
124
|
+
email: string;
|
|
125
|
+
password: string;
|
|
126
|
+
consumerRoot?: string;
|
|
127
|
+
config?: string;
|
|
128
|
+
}>,
|
|
129
|
+
) => {
|
|
130
|
+
await this.userCreateCommand.execute(opts);
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
user
|
|
135
|
+
.command("list")
|
|
136
|
+
.description(
|
|
137
|
+
'List users in the database when CodemationConfig.auth.kind is "local". Uses DATABASE_URL or configured database URL.',
|
|
138
|
+
)
|
|
139
|
+
.option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)")
|
|
140
|
+
.option("--config <path>", "Override path to codemation.config.ts / .js")
|
|
141
|
+
.action(async (opts: Readonly<{ consumerRoot?: string; config?: string }>) => {
|
|
142
|
+
await this.userListCommand.execute(opts);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await program.parseAsync(argv as string[], { from: "user" });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private readCliPackageVersion(): string {
|
|
149
|
+
try {
|
|
150
|
+
const packageJsonPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
151
|
+
const parsed = JSON.parse(readFileSync(packageJsonPath, "utf8")) as { version?: unknown };
|
|
152
|
+
return typeof parsed.version === "string" ? parsed.version : "0.0.0";
|
|
153
|
+
} catch {
|
|
154
|
+
return "0.0.0";
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { type Container } from "@codemation/core";
|
|
2
|
+
import {
|
|
3
|
+
ApplicationTokens,
|
|
4
|
+
CodemationApplication,
|
|
5
|
+
CodemationBootstrapRequest,
|
|
6
|
+
PrismaClient,
|
|
7
|
+
type CommandBus,
|
|
8
|
+
type QueryBus,
|
|
9
|
+
} from "@codemation/host";
|
|
10
|
+
import type { CodemationConsumerConfigResolution } from "@codemation/host/server";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Opens a {@link CodemationApplication} with persistence + command/query buses (no HTTP/WebSocket servers),
|
|
14
|
+
* for CLI tools that dispatch application commands or queries (e.g. user admin).
|
|
15
|
+
*/
|
|
16
|
+
export class CodemationCliApplicationSession {
|
|
17
|
+
private constructor(private readonly application: CodemationApplication) {}
|
|
18
|
+
|
|
19
|
+
static async open(
|
|
20
|
+
args: Readonly<{
|
|
21
|
+
resolution: CodemationConsumerConfigResolution;
|
|
22
|
+
bootstrap: CodemationBootstrapRequest;
|
|
23
|
+
}>,
|
|
24
|
+
): Promise<CodemationCliApplicationSession> {
|
|
25
|
+
const app = new CodemationApplication().useConfig(args.resolution.config);
|
|
26
|
+
await app.bootCli(
|
|
27
|
+
new CodemationBootstrapRequest({
|
|
28
|
+
repoRoot: args.bootstrap.repoRoot,
|
|
29
|
+
consumerRoot: args.bootstrap.consumerRoot,
|
|
30
|
+
env: args.bootstrap.env,
|
|
31
|
+
workflowSources: args.resolution.workflowSources,
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
34
|
+
return new CodemationCliApplicationSession(app);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getPrismaClient(): PrismaClient | undefined {
|
|
38
|
+
const container = this.getContainer();
|
|
39
|
+
if (!container.isRegistered(PrismaClient, true)) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
return container.resolve(PrismaClient);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getCommandBus(): CommandBus {
|
|
46
|
+
return this.getContainer().resolve(ApplicationTokens.CommandBus);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
getQueryBus(): QueryBus {
|
|
50
|
+
return this.getContainer().resolve(ApplicationTokens.QueryBus);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async close(): Promise<void> {
|
|
54
|
+
await this.application.stop({ stopWebsocketServer: false });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
private getContainer(): Container {
|
|
58
|
+
return this.application.getContainer();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { CodemationDiscoveredPluginPackage } from "@codemation/host/server";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { mkdir, rename, writeFile } from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
import type { ConsumerOutputBuildSnapshot } from "../consumer/ConsumerOutputBuilder";
|
|
8
|
+
|
|
9
|
+
export type ConsumerBuildManifest = Readonly<{
|
|
10
|
+
buildVersion: string;
|
|
11
|
+
consumerRoot: string;
|
|
12
|
+
entryPath: string;
|
|
13
|
+
manifestPath: string;
|
|
14
|
+
pluginEntryPath: string;
|
|
15
|
+
workflowSourcePaths: ReadonlyArray<string>;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export class ConsumerBuildArtifactsPublisher {
|
|
19
|
+
async publish(
|
|
20
|
+
snapshot: ConsumerOutputBuildSnapshot,
|
|
21
|
+
discoveredPlugins: ReadonlyArray<CodemationDiscoveredPluginPackage>,
|
|
22
|
+
): Promise<ConsumerBuildManifest> {
|
|
23
|
+
const pluginEntryPath = await this.writeDiscoveredPluginsOutput(snapshot, discoveredPlugins);
|
|
24
|
+
return await this.writeBuildManifest(snapshot, pluginEntryPath);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private async writeDiscoveredPluginsOutput(
|
|
28
|
+
snapshot: ConsumerOutputBuildSnapshot,
|
|
29
|
+
discoveredPlugins: ReadonlyArray<CodemationDiscoveredPluginPackage>,
|
|
30
|
+
): Promise<string> {
|
|
31
|
+
const outputPath = path.resolve(snapshot.emitOutputRoot, "plugins.js");
|
|
32
|
+
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
33
|
+
const outputLines: string[] = ["const codemationDiscoveredPlugins = [];", ""];
|
|
34
|
+
discoveredPlugins.forEach((discoveredPlugin: CodemationDiscoveredPluginPackage, index: number) => {
|
|
35
|
+
const pluginFileUrl = pathToFileURL(
|
|
36
|
+
path.resolve(discoveredPlugin.packageRoot, discoveredPlugin.manifest.entry),
|
|
37
|
+
).href;
|
|
38
|
+
const exportNameAccessor = discoveredPlugin.manifest.exportName
|
|
39
|
+
? `pluginModule${index}[${JSON.stringify(discoveredPlugin.manifest.exportName)}]`
|
|
40
|
+
: `pluginModule${index}.default ?? pluginModule${index}.codemationPlugin`;
|
|
41
|
+
outputLines.push(`const pluginModule${index} = await import(${JSON.stringify(pluginFileUrl)});`);
|
|
42
|
+
outputLines.push(`const pluginValue${index} = ${exportNameAccessor};`);
|
|
43
|
+
outputLines.push(`if (pluginValue${index} && typeof pluginValue${index}.register === "function") {`);
|
|
44
|
+
outputLines.push(` codemationDiscoveredPlugins.push(pluginValue${index});`);
|
|
45
|
+
outputLines.push(
|
|
46
|
+
`} else if (typeof pluginValue${index} === "function" && pluginValue${index}.prototype && typeof pluginValue${index}.prototype.register === "function") {`,
|
|
47
|
+
);
|
|
48
|
+
outputLines.push(` codemationDiscoveredPlugins.push(new pluginValue${index}());`);
|
|
49
|
+
outputLines.push("}");
|
|
50
|
+
outputLines.push("");
|
|
51
|
+
});
|
|
52
|
+
outputLines.push("export { codemationDiscoveredPlugins };");
|
|
53
|
+
outputLines.push("export default codemationDiscoveredPlugins;");
|
|
54
|
+
outputLines.push("");
|
|
55
|
+
await writeFile(outputPath, outputLines.join("\n"), "utf8");
|
|
56
|
+
return outputPath;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private async writeBuildManifest(
|
|
60
|
+
snapshot: ConsumerOutputBuildSnapshot,
|
|
61
|
+
pluginEntryPath: string,
|
|
62
|
+
): Promise<ConsumerBuildManifest> {
|
|
63
|
+
const manifest: ConsumerBuildManifest = {
|
|
64
|
+
buildVersion: snapshot.buildVersion,
|
|
65
|
+
consumerRoot: snapshot.consumerRoot,
|
|
66
|
+
entryPath: snapshot.outputEntryPath,
|
|
67
|
+
manifestPath: snapshot.manifestPath,
|
|
68
|
+
pluginEntryPath,
|
|
69
|
+
workflowSourcePaths: snapshot.workflowSourcePaths,
|
|
70
|
+
};
|
|
71
|
+
await mkdir(path.dirname(snapshot.manifestPath), { recursive: true });
|
|
72
|
+
const temporaryManifestPath = `${snapshot.manifestPath}.${snapshot.buildVersion}.${randomUUID()}.tmp`;
|
|
73
|
+
await writeFile(temporaryManifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
74
|
+
await rename(temporaryManifestPath, snapshot.manifestPath);
|
|
75
|
+
return manifest;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ConsumerBuildOptions, EcmaScriptBuildTarget } from "../consumer/consumerBuildOptions.types";
|
|
2
|
+
|
|
3
|
+
export class ConsumerBuildOptionsParser {
|
|
4
|
+
parse(
|
|
5
|
+
args: Readonly<{
|
|
6
|
+
noSourceMaps?: boolean;
|
|
7
|
+
target?: string;
|
|
8
|
+
}>,
|
|
9
|
+
): ConsumerBuildOptions {
|
|
10
|
+
return {
|
|
11
|
+
sourceMaps: args.noSourceMaps !== true,
|
|
12
|
+
target: this.parseTarget(args.target),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
private parseTarget(raw: string | undefined): EcmaScriptBuildTarget {
|
|
17
|
+
if (raw === undefined || raw.trim() === "") {
|
|
18
|
+
return "es2022";
|
|
19
|
+
}
|
|
20
|
+
const normalized = raw.trim();
|
|
21
|
+
if (normalized === "es2020" || normalized === "es2022") {
|
|
22
|
+
return normalized;
|
|
23
|
+
}
|
|
24
|
+
throw new Error(`Invalid --target "${raw}". Use es2020 or es2022.`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Logger } from "@codemation/host/next/server";
|
|
2
|
+
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
|
+
|
|
4
|
+
import { ConsumerBuildArtifactsPublisher } from "../build/ConsumerBuildArtifactsPublisher";
|
|
5
|
+
import type { ConsumerBuildOptions } from "../consumer/consumerBuildOptions.types";
|
|
6
|
+
import { ConsumerOutputBuilderLoader } from "../consumer/Loader";
|
|
7
|
+
import { CliPathResolver } from "../path/CliPathResolver";
|
|
8
|
+
import { TypeScriptRuntimeConfigurator } from "../runtime/TypeScriptRuntimeConfigurator";
|
|
9
|
+
|
|
10
|
+
export class BuildCommand {
|
|
11
|
+
constructor(
|
|
12
|
+
private readonly cliLogger: Logger,
|
|
13
|
+
private readonly pathResolver: CliPathResolver,
|
|
14
|
+
private readonly pluginDiscovery: CodemationPluginDiscovery,
|
|
15
|
+
private readonly artifactsPublisher: ConsumerBuildArtifactsPublisher,
|
|
16
|
+
private readonly tsRuntime: TypeScriptRuntimeConfigurator,
|
|
17
|
+
private readonly outputBuilderLoader: ConsumerOutputBuilderLoader,
|
|
18
|
+
) {}
|
|
19
|
+
|
|
20
|
+
async execute(consumerRoot: string, buildOptions: ConsumerBuildOptions): Promise<void> {
|
|
21
|
+
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
22
|
+
this.tsRuntime.configure(paths.repoRoot);
|
|
23
|
+
const builder = this.outputBuilderLoader.create(paths.consumerRoot, buildOptions);
|
|
24
|
+
const snapshot = await builder.ensureBuilt();
|
|
25
|
+
const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
|
|
26
|
+
const manifest = await this.artifactsPublisher.publish(snapshot, discoveredPlugins);
|
|
27
|
+
this.cliLogger.info(`Built consumer output: ${snapshot.outputEntryPath}`);
|
|
28
|
+
this.cliLogger.info(`Discovered plugins: ${discoveredPlugins.length}`);
|
|
29
|
+
this.cliLogger.info(`Published build: ${manifest.buildVersion}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { DatabaseMigrationsApplyService } from "../database/DatabaseMigrationsApplyService";
|
|
2
|
+
|
|
3
|
+
export type DbMigrateCommandOptions = Readonly<{
|
|
4
|
+
consumerRoot?: string;
|
|
5
|
+
configPath?: string;
|
|
6
|
+
}>;
|
|
7
|
+
|
|
8
|
+
export class DbMigrateCommand {
|
|
9
|
+
constructor(private readonly databaseMigrationsApplyService: DatabaseMigrationsApplyService) {}
|
|
10
|
+
|
|
11
|
+
async execute(options: DbMigrateCommandOptions): Promise<void> {
|
|
12
|
+
await this.databaseMigrationsApplyService.applyForConsumerRequiringPersistence(
|
|
13
|
+
options.consumerRoot ?? process.cwd(),
|
|
14
|
+
{
|
|
15
|
+
configPath: options.configPath,
|
|
16
|
+
},
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|