@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.
Files changed (64) hide show
  1. package/README.md +148 -0
  2. package/bin/codemation.js +24 -0
  3. package/bin/codemation.ts +5 -0
  4. package/dist/CliBin-vjSSUDWE.js +2304 -0
  5. package/dist/bin.d.ts +1 -0
  6. package/dist/bin.js +9 -0
  7. package/dist/index.d.ts +23456 -0
  8. package/dist/index.js +4 -0
  9. package/package.json +56 -0
  10. package/src/CliBin.ts +17 -0
  11. package/src/CliProgramFactory.ts +118 -0
  12. package/src/Program.ts +157 -0
  13. package/src/bin.ts +6 -0
  14. package/src/bootstrap/CodemationCliApplicationSession.ts +60 -0
  15. package/src/build/ConsumerBuildArtifactsPublisher.ts +77 -0
  16. package/src/build/ConsumerBuildOptionsParser.ts +26 -0
  17. package/src/commands/BuildCommand.ts +31 -0
  18. package/src/commands/DbMigrateCommand.ts +19 -0
  19. package/src/commands/DevCommand.ts +391 -0
  20. package/src/commands/ServeWebCommand.ts +72 -0
  21. package/src/commands/ServeWorkerCommand.ts +40 -0
  22. package/src/commands/UserCreateCommand.ts +25 -0
  23. package/src/commands/UserListCommand.ts +59 -0
  24. package/src/commands/devCommandLifecycle.types.ts +32 -0
  25. package/src/consumer/ConsumerCliTsconfigPreparation.ts +26 -0
  26. package/src/consumer/ConsumerEnvLoader.ts +47 -0
  27. package/src/consumer/ConsumerOutputBuilder.ts +898 -0
  28. package/src/consumer/Loader.ts +8 -0
  29. package/src/consumer/consumerBuildOptions.types.ts +12 -0
  30. package/src/database/ConsumerDatabaseConnectionResolver.ts +18 -0
  31. package/src/database/DatabaseMigrationsApplyService.ts +76 -0
  32. package/src/database/HostPackageRootResolver.ts +26 -0
  33. package/src/database/PrismaMigrateDeployInvoker.ts +24 -0
  34. package/src/dev/Builder.ts +45 -0
  35. package/src/dev/ConsumerEnvDotenvFilePredicate.ts +12 -0
  36. package/src/dev/DevAuthSettingsLoader.ts +27 -0
  37. package/src/dev/DevBootstrapSummaryFetcher.ts +15 -0
  38. package/src/dev/DevCliBannerRenderer.ts +106 -0
  39. package/src/dev/DevConsumerPublishBootstrap.ts +30 -0
  40. package/src/dev/DevHttpProbe.ts +54 -0
  41. package/src/dev/DevLock.ts +98 -0
  42. package/src/dev/DevNextHostEnvironmentBuilder.ts +49 -0
  43. package/src/dev/DevSessionPortsResolver.ts +23 -0
  44. package/src/dev/DevSessionServices.ts +29 -0
  45. package/src/dev/DevSourceRestartCoordinator.ts +48 -0
  46. package/src/dev/DevSourceWatcher.ts +102 -0
  47. package/src/dev/DevTrackedProcessTreeKiller.ts +107 -0
  48. package/src/dev/DevelopmentGatewayNotifier.ts +35 -0
  49. package/src/dev/Factory.ts +7 -0
  50. package/src/dev/LoopbackPortAllocator.ts +20 -0
  51. package/src/dev/Runner.ts +7 -0
  52. package/src/dev/RuntimeToolEntrypointResolver.ts +47 -0
  53. package/src/dev/WatchRootsResolver.ts +26 -0
  54. package/src/index.ts +12 -0
  55. package/src/path/CliPathResolver.ts +41 -0
  56. package/src/runtime/ListenPortResolver.ts +35 -0
  57. package/src/runtime/SourceMapNodeOptions.ts +12 -0
  58. package/src/runtime/TypeScriptRuntimeConfigurator.ts +8 -0
  59. package/src/user/CliDatabaseUrlDescriptor.ts +33 -0
  60. package/src/user/LocalUserCreator.ts +29 -0
  61. package/src/user/UserAdminCliBootstrap.ts +67 -0
  62. package/src/user/UserAdminCliOptionsParser.ts +24 -0
  63. package/src/user/UserAdminConsumerDotenvLoader.ts +24 -0
  64. 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,6 @@
1
+ import process from "node:process";
2
+ import "reflect-metadata";
3
+
4
+ import { CliBin } from "./CliBin";
5
+
6
+ void CliBin.run(process.argv.slice(2));
@@ -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
+ }