@codemation/cli 0.0.15 → 0.0.18
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/CHANGELOG.md +10 -0
- package/LICENSE +37 -0
- package/README.md +4 -4
- package/dist/{CliBin-BkY_gChN.js → CliBin-BYHuUedo.js} +348 -186
- package/dist/bin.js +1 -1
- package/dist/index.d.ts +278 -298
- package/dist/index.js +1 -1
- package/package.json +5 -4
- package/src/CliProgramFactory.ts +31 -37
- package/src/Program.ts +12 -0
- package/src/build/ConsumerBuildArtifactsPublisher.ts +12 -7
- package/src/commands/BuildCommand.ts +9 -9
- package/src/commands/DevCommand.ts +113 -47
- package/src/commands/DevPluginCommand.ts +19 -0
- package/src/commands/ServeWebCommand.ts +8 -34
- package/src/commands/devCommandLifecycle.types.ts +3 -2
- package/src/consumer/ConsumerOutputBuilder.ts +28 -0
- package/src/consumer/ConsumerOutputBuilderFactory.ts +17 -0
- package/src/dev/Builder.ts +2 -2
- package/src/dev/CliDevProxyServer.ts +20 -0
- package/src/dev/DevApiRuntimeFactory.ts +1 -0
- package/src/dev/DevApiRuntimeHost.ts +37 -6
- package/src/dev/DevApiRuntimeTypes.ts +1 -0
- package/src/dev/DevCliBannerRenderer.ts +21 -2
- package/src/dev/DevNextHostEnvironmentBuilder.ts +27 -36
- package/src/dev/DevRebuildQueue.ts +2 -2
- package/src/dev/DevSessionServices.ts +2 -2
- package/src/dev/DevSourceChangeClassifier.ts +12 -12
- package/src/dev/ListenPortConflictDescriber.ts +66 -5
- package/src/dev/{DevAuthSettingsLoader.ts → NextHostEdgeSeedLoader.ts} +13 -9
- package/src/dev/PluginDevConfigFactory.ts +64 -0
- package/src/consumer/Loader.ts +0 -8
- package/src/dev/DevConsumerPublishBootstrap.ts +0 -30
package/dist/index.js
CHANGED
|
@@ -1,4 +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-
|
|
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-BYHuUedo.js";
|
|
2
2
|
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
3
|
|
|
4
4
|
export { CliBin, CliPathResolver, CliProgram, CliProgramFactory, CodemationCliApplicationSession, CodemationPluginDiscovery, ConsumerBuildOptionsParser, ConsumerOutputBuilder };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemation/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
"url": "https://github.com/MadeRelevant/codemation",
|
|
12
12
|
"directory": "packages/cli"
|
|
13
13
|
},
|
|
14
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
14
15
|
"type": "module",
|
|
15
16
|
"main": "./dist/index.js",
|
|
16
17
|
"types": "./dist/index.d.ts",
|
|
@@ -37,8 +38,8 @@
|
|
|
37
38
|
"reflect-metadata": "^0.2.2",
|
|
38
39
|
"typescript": "^5.9.3",
|
|
39
40
|
"ws": "^8.19.0",
|
|
40
|
-
"@codemation/host": "0.0.
|
|
41
|
-
"@codemation/next-host": "0.0.
|
|
41
|
+
"@codemation/host": "0.0.18",
|
|
42
|
+
"@codemation/next-host": "0.0.18"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"@types/http-proxy": "^1.17.15",
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"tsx": "^4.21.0",
|
|
50
51
|
"typescript": "^5.9.3",
|
|
51
52
|
"vitest": "4.0.18",
|
|
52
|
-
"@codemation/eslint-config": "0.0.
|
|
53
|
+
"@codemation/eslint-config": "0.0.1"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"tsx": ">=4.0.0"
|
package/src/CliProgramFactory.ts
CHANGED
|
@@ -1,36 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AppConfigLoader,
|
|
3
|
-
CodemationConsumerConfigLoader,
|
|
4
|
-
CodemationFrontendAuthSnapshotFactory,
|
|
5
|
-
CodemationPluginDiscovery,
|
|
6
|
-
FrontendAppConfigJsonCodec,
|
|
7
|
-
} from "@codemation/host/server";
|
|
1
|
+
import { AppConfigLoader, CodemationConsumerConfigLoader, CodemationPluginDiscovery } from "@codemation/host/server";
|
|
8
2
|
import { AppContainerFactory } from "@codemation/host";
|
|
9
3
|
import { logLevelPolicyFactory, ServerLoggerFactory } from "@codemation/host/next/server";
|
|
10
4
|
|
|
11
|
-
import { ConsumerBuildArtifactsPublisher } from "./build/ConsumerBuildArtifactsPublisher";
|
|
12
5
|
import { ConsumerBuildOptionsParser } from "./build/ConsumerBuildOptionsParser";
|
|
6
|
+
import { ConsumerBuildArtifactsPublisher } from "./build/ConsumerBuildArtifactsPublisher";
|
|
13
7
|
import { BuildCommand } from "./commands/BuildCommand";
|
|
14
8
|
import { DbMigrateCommand } from "./commands/DbMigrateCommand";
|
|
15
9
|
import { DevCommand } from "./commands/DevCommand";
|
|
10
|
+
import { DevPluginCommand } from "./commands/DevPluginCommand";
|
|
16
11
|
import { ServeWebCommand } from "./commands/ServeWebCommand";
|
|
17
12
|
import { ServeWorkerCommand } from "./commands/ServeWorkerCommand";
|
|
18
13
|
import { UserCreateCommand } from "./commands/UserCreateCommand";
|
|
19
14
|
import { UserListCommand } from "./commands/UserListCommand";
|
|
20
15
|
import { ConsumerCliTsconfigPreparation } from "./consumer/ConsumerCliTsconfigPreparation";
|
|
21
16
|
import { ConsumerEnvLoader } from "./consumer/ConsumerEnvLoader";
|
|
22
|
-
import {
|
|
17
|
+
import { ConsumerOutputBuilderFactory } from "./consumer/ConsumerOutputBuilderFactory";
|
|
23
18
|
import { ConsumerDatabaseConnectionResolver } from "./database/ConsumerDatabaseConnectionResolver";
|
|
24
19
|
import { DatabaseMigrationsApplyService } from "./database/DatabaseMigrationsApplyService";
|
|
25
20
|
import { HostPackageRootResolver } from "./database/HostPackageRootResolver";
|
|
26
21
|
import { PrismaMigrationDeployer } from "@codemation/host/persistence";
|
|
27
22
|
import { DevBootstrapSummaryFetcher } from "./dev/DevBootstrapSummaryFetcher";
|
|
28
23
|
import { DevCliBannerRenderer } from "./dev/DevCliBannerRenderer";
|
|
29
|
-
import { DevConsumerPublishBootstrap } from "./dev/DevConsumerPublishBootstrap";
|
|
30
24
|
import { CliDevProxyServerFactory } from "./dev/CliDevProxyServerFactory";
|
|
31
25
|
import { DevApiRuntimeFactory } from "./dev/DevApiRuntimeFactory";
|
|
32
26
|
import { DevRebuildQueueFactory } from "./dev/DevRebuildQueueFactory";
|
|
33
27
|
import { DevSessionServicesBuilder } from "./dev/Builder";
|
|
28
|
+
import { PluginDevConfigFactory } from "./dev/PluginDevConfigFactory";
|
|
34
29
|
import { DevLockFactory } from "./dev/Factory";
|
|
35
30
|
import { ConsumerEnvDotenvFilePredicate } from "./dev/ConsumerEnvDotenvFilePredicate";
|
|
36
31
|
import { DevTrackedProcessTreeKiller } from "./dev/DevTrackedProcessTreeKiller";
|
|
@@ -59,9 +54,7 @@ export class CliProgramFactory {
|
|
|
59
54
|
const appConfigLoader = new AppConfigLoader();
|
|
60
55
|
const pathResolver = new CliPathResolver();
|
|
61
56
|
const pluginDiscovery = new CodemationPluginDiscovery();
|
|
62
|
-
const artifactsPublisher = new ConsumerBuildArtifactsPublisher();
|
|
63
57
|
const tsRuntime = new TypeScriptRuntimeConfigurator();
|
|
64
|
-
const outputBuilderLoader = new ConsumerOutputBuilderLoader();
|
|
65
58
|
const sourceMapNodeOptions = new SourceMapNodeOptions();
|
|
66
59
|
const nextHostConsumerServerCommandFactory = new NextHostConsumerServerCommandFactory();
|
|
67
60
|
const devSessionServices = new DevSessionServicesBuilder().build();
|
|
@@ -86,47 +79,48 @@ export class CliProgramFactory {
|
|
|
86
79
|
);
|
|
87
80
|
|
|
88
81
|
const buildOptionsParser = new ConsumerBuildOptionsParser();
|
|
89
|
-
const
|
|
82
|
+
const consumerOutputBuilderFactory = new ConsumerOutputBuilderFactory();
|
|
83
|
+
const consumerBuildArtifactsPublisher = new ConsumerBuildArtifactsPublisher();
|
|
84
|
+
const devCommand = new DevCommand(
|
|
85
|
+
pathResolver,
|
|
86
|
+
tsRuntime,
|
|
87
|
+
new DevLockFactory(),
|
|
88
|
+
new DevSourceWatcherFactory(),
|
|
90
89
|
cliLogger,
|
|
90
|
+
devSessionServices,
|
|
91
|
+
databaseMigrationsApplyService,
|
|
92
|
+
consumerOutputBuilderFactory,
|
|
91
93
|
pluginDiscovery,
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
consumerBuildArtifactsPublisher,
|
|
95
|
+
new DevBootstrapSummaryFetcher(),
|
|
96
|
+
new DevCliBannerRenderer(),
|
|
97
|
+
new ConsumerEnvDotenvFilePredicate(),
|
|
98
|
+
new DevTrackedProcessTreeKiller(),
|
|
99
|
+
nextHostConsumerServerCommandFactory,
|
|
100
|
+
new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery),
|
|
101
|
+
new CliDevProxyServerFactory(),
|
|
102
|
+
new DevRebuildQueueFactory(),
|
|
95
103
|
);
|
|
96
104
|
return new CliProgram(
|
|
97
105
|
buildOptionsParser,
|
|
98
|
-
new BuildCommand(
|
|
99
|
-
|
|
106
|
+
new BuildCommand(
|
|
107
|
+
cliLogger,
|
|
100
108
|
pathResolver,
|
|
109
|
+
consumerOutputBuilderFactory,
|
|
110
|
+
pluginDiscovery,
|
|
111
|
+
consumerBuildArtifactsPublisher,
|
|
101
112
|
tsRuntime,
|
|
102
|
-
new DevLockFactory(),
|
|
103
|
-
new DevSourceWatcherFactory(),
|
|
104
|
-
cliLogger,
|
|
105
|
-
devSessionServices,
|
|
106
|
-
databaseMigrationsApplyService,
|
|
107
|
-
new DevBootstrapSummaryFetcher(),
|
|
108
|
-
new DevCliBannerRenderer(),
|
|
109
|
-
devConsumerPublishBootstrap,
|
|
110
|
-
new ConsumerEnvDotenvFilePredicate(),
|
|
111
|
-
new DevTrackedProcessTreeKiller(),
|
|
112
|
-
nextHostConsumerServerCommandFactory,
|
|
113
|
-
new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery),
|
|
114
|
-
new CliDevProxyServerFactory(),
|
|
115
|
-
new DevRebuildQueueFactory(),
|
|
116
113
|
),
|
|
114
|
+
devCommand,
|
|
115
|
+
new DevPluginCommand(new PluginDevConfigFactory(), devCommand),
|
|
117
116
|
new ServeWebCommand(
|
|
118
117
|
pathResolver,
|
|
119
118
|
new CodemationConsumerConfigLoader(),
|
|
120
|
-
pluginDiscovery,
|
|
121
|
-
artifactsPublisher,
|
|
122
119
|
tsRuntime,
|
|
123
120
|
sourceMapNodeOptions,
|
|
124
|
-
outputBuilderLoader,
|
|
125
121
|
new ConsumerEnvLoader(),
|
|
126
122
|
new ListenPortResolver(),
|
|
127
123
|
nextHostConsumerServerCommandFactory,
|
|
128
|
-
new CodemationFrontendAuthSnapshotFactory(),
|
|
129
|
-
new FrontendAppConfigJsonCodec(),
|
|
130
124
|
),
|
|
131
125
|
new ServeWorkerCommand(pathResolver, appConfigLoader, new AppContainerFactory()),
|
|
132
126
|
new DbMigrateCommand(databaseMigrationsApplyService),
|
package/src/Program.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { ConsumerBuildOptionsParser } from "./build/ConsumerBuildOptionsParser";
|
|
|
8
8
|
import { BuildCommand } from "./commands/BuildCommand";
|
|
9
9
|
import type { DbMigrateCommand } from "./commands/DbMigrateCommand";
|
|
10
10
|
import { DevCommand } from "./commands/DevCommand";
|
|
11
|
+
import type { DevPluginCommand } from "./commands/DevPluginCommand";
|
|
11
12
|
import { ServeWebCommand } from "./commands/ServeWebCommand";
|
|
12
13
|
import { ServeWorkerCommand } from "./commands/ServeWorkerCommand";
|
|
13
14
|
import { UserCreateCommand } from "./commands/UserCreateCommand";
|
|
@@ -18,6 +19,7 @@ export class CliProgram {
|
|
|
18
19
|
private readonly buildOptionsParser: ConsumerBuildOptionsParser,
|
|
19
20
|
private readonly buildCommand: BuildCommand,
|
|
20
21
|
private readonly devCommand: DevCommand,
|
|
22
|
+
private readonly devPluginCommand: DevPluginCommand,
|
|
21
23
|
private readonly serveWebCommand: ServeWebCommand,
|
|
22
24
|
private readonly serveWorkerCommand: ServeWorkerCommand,
|
|
23
25
|
private readonly dbMigrateCommand: DbMigrateCommand,
|
|
@@ -68,6 +70,16 @@ export class CliProgram {
|
|
|
68
70
|
});
|
|
69
71
|
});
|
|
70
72
|
|
|
73
|
+
program
|
|
74
|
+
.command("dev:plugin")
|
|
75
|
+
.description("Start plugin sandbox development using `codemation.plugin.ts`.")
|
|
76
|
+
.option("--plugin-root <path>", "Path to the plugin project root (defaults to cwd)")
|
|
77
|
+
.action(async (opts: Readonly<{ pluginRoot?: string }>) => {
|
|
78
|
+
await this.devPluginCommand.execute({
|
|
79
|
+
pluginRoot: resolveConsumerRoot(opts.pluginRoot),
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
71
83
|
const serve = program.command("serve").description("Run production web or worker processes (no dev watchers).");
|
|
72
84
|
|
|
73
85
|
serve
|
|
@@ -32,14 +32,12 @@ export class ConsumerBuildArtifactsPublisher {
|
|
|
32
32
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
|
33
33
|
const outputLines: string[] = ["const codemationDiscoveredPlugins = [];", ""];
|
|
34
34
|
discoveredPlugins.forEach((discoveredPlugin: CodemationDiscoveredPluginPackage, index: number) => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
).href;
|
|
38
|
-
const exportNameAccessor = discoveredPlugin.manifest.exportName
|
|
39
|
-
? `pluginModule${index}[${JSON.stringify(discoveredPlugin.manifest.exportName)}]`
|
|
40
|
-
: `pluginModule${index}.default ?? pluginModule${index}.codemationPlugin`;
|
|
35
|
+
const pluginModulePath = path.resolve(discoveredPlugin.packageRoot, this.resolvePluginEntry(discoveredPlugin));
|
|
36
|
+
const pluginFileUrl = pathToFileURL(pluginModulePath).href;
|
|
41
37
|
outputLines.push(`const pluginModule${index} = await import(${JSON.stringify(pluginFileUrl)});`);
|
|
42
|
-
outputLines.push(
|
|
38
|
+
outputLines.push(
|
|
39
|
+
`const pluginValue${index} = pluginModule${index}.default ?? pluginModule${index}.codemationPlugin;`,
|
|
40
|
+
);
|
|
43
41
|
outputLines.push(`if (pluginValue${index} && typeof pluginValue${index}.register === "function") {`);
|
|
44
42
|
outputLines.push(` codemationDiscoveredPlugins.push(pluginValue${index});`);
|
|
45
43
|
outputLines.push(
|
|
@@ -74,4 +72,11 @@ export class ConsumerBuildArtifactsPublisher {
|
|
|
74
72
|
await rename(temporaryManifestPath, snapshot.manifestPath);
|
|
75
73
|
return manifest;
|
|
76
74
|
}
|
|
75
|
+
|
|
76
|
+
private resolvePluginEntry(discoveredPlugin: CodemationDiscoveredPluginPackage): string {
|
|
77
|
+
if (typeof discoveredPlugin.developmentEntry === "string" && discoveredPlugin.developmentEntry.trim().length > 0) {
|
|
78
|
+
return discoveredPlugin.developmentEntry;
|
|
79
|
+
}
|
|
80
|
+
return discoveredPlugin.pluginEntry;
|
|
81
|
+
}
|
|
77
82
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import type { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
1
2
|
import type { Logger } from "@codemation/host/next/server";
|
|
2
|
-
import { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
3
3
|
|
|
4
|
-
import { ConsumerBuildArtifactsPublisher } from "../build/ConsumerBuildArtifactsPublisher";
|
|
4
|
+
import type { ConsumerBuildArtifactsPublisher } from "../build/ConsumerBuildArtifactsPublisher";
|
|
5
|
+
import type { ConsumerOutputBuilderFactory } from "../consumer/ConsumerOutputBuilderFactory";
|
|
5
6
|
import type { ConsumerBuildOptions } from "../consumer/consumerBuildOptions.types";
|
|
6
|
-
import { ConsumerOutputBuilderLoader } from "../consumer/Loader";
|
|
7
7
|
import { CliPathResolver } from "../path/CliPathResolver";
|
|
8
8
|
import { TypeScriptRuntimeConfigurator } from "../runtime/TypeScriptRuntimeConfigurator";
|
|
9
9
|
|
|
@@ -11,21 +11,21 @@ export class BuildCommand {
|
|
|
11
11
|
constructor(
|
|
12
12
|
private readonly cliLogger: Logger,
|
|
13
13
|
private readonly pathResolver: CliPathResolver,
|
|
14
|
+
private readonly consumerOutputBuilderFactory: ConsumerOutputBuilderFactory,
|
|
14
15
|
private readonly pluginDiscovery: CodemationPluginDiscovery,
|
|
15
|
-
private readonly
|
|
16
|
+
private readonly consumerBuildArtifactsPublisher: ConsumerBuildArtifactsPublisher,
|
|
16
17
|
private readonly tsRuntime: TypeScriptRuntimeConfigurator,
|
|
17
|
-
private readonly outputBuilderLoader: ConsumerOutputBuilderLoader,
|
|
18
18
|
) {}
|
|
19
19
|
|
|
20
20
|
async execute(consumerRoot: string, buildOptions: ConsumerBuildOptions): Promise<void> {
|
|
21
21
|
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
22
22
|
this.tsRuntime.configure(paths.repoRoot);
|
|
23
|
-
const builder = this.
|
|
23
|
+
const builder = this.consumerOutputBuilderFactory.create(paths.consumerRoot, { buildOptions });
|
|
24
24
|
const snapshot = await builder.ensureBuilt();
|
|
25
25
|
const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
|
|
26
|
-
const manifest = await this.
|
|
26
|
+
const manifest = await this.consumerBuildArtifactsPublisher.publish(snapshot, discoveredPlugins);
|
|
27
27
|
this.cliLogger.info(`Built consumer output: ${snapshot.outputEntryPath}`);
|
|
28
|
-
this.cliLogger.info(`
|
|
29
|
-
this.cliLogger.info(`
|
|
28
|
+
this.cliLogger.info(`Build manifest: ${manifest.manifestPath}`);
|
|
29
|
+
this.cliLogger.info(`Workflow modules emitted: ${snapshot.workflowSourcePaths.length}`);
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import type { CodemationPluginDiscovery } from "@codemation/host/server";
|
|
1
2
|
import type { Logger } from "@codemation/host/next/server";
|
|
2
3
|
import { spawn, type ChildProcess } from "node:child_process";
|
|
3
4
|
import { createRequire } from "node:module";
|
|
4
5
|
import path from "node:path";
|
|
5
6
|
import process from "node:process";
|
|
6
7
|
|
|
8
|
+
import type { ConsumerBuildArtifactsPublisher } from "../build/ConsumerBuildArtifactsPublisher";
|
|
9
|
+
import type { ConsumerOutputBuilderFactory } from "../consumer/ConsumerOutputBuilderFactory";
|
|
7
10
|
import type { DatabaseMigrationsApplyService } from "../database/DatabaseMigrationsApplyService";
|
|
8
11
|
import type { DevApiRuntimeFactory, DevApiRuntimeServerHandle } from "../dev/DevApiRuntimeFactory";
|
|
9
12
|
import type { DevBootstrapSummaryFetcher } from "../dev/DevBootstrapSummaryFetcher";
|
|
10
13
|
import type { CliDevProxyServer } from "../dev/CliDevProxyServer";
|
|
11
14
|
import type { CliDevProxyServerFactory } from "../dev/CliDevProxyServerFactory";
|
|
12
15
|
import type { DevCliBannerRenderer } from "../dev/DevCliBannerRenderer";
|
|
13
|
-
import type { DevConsumerPublishBootstrap } from "../dev/DevConsumerPublishBootstrap";
|
|
14
16
|
import { ConsumerEnvDotenvFilePredicate } from "../dev/ConsumerEnvDotenvFilePredicate";
|
|
15
17
|
import type { DevRebuildQueueFactory } from "../dev/DevRebuildQueueFactory";
|
|
16
18
|
import type { DevSourceWatcher } from "../dev/DevSourceWatcher";
|
|
@@ -18,7 +20,7 @@ import { DevSessionServices } from "../dev/DevSessionServices";
|
|
|
18
20
|
import { DevLockFactory } from "../dev/Factory";
|
|
19
21
|
import { DevTrackedProcessTreeKiller } from "../dev/DevTrackedProcessTreeKiller";
|
|
20
22
|
import { DevSourceWatcherFactory } from "../dev/Runner";
|
|
21
|
-
import type {
|
|
23
|
+
import type { NextHostEdgeSeed } from "../dev/NextHostEdgeSeedLoader";
|
|
22
24
|
import { CliPathResolver, type CliPaths } from "../path/CliPathResolver";
|
|
23
25
|
import { NextHostConsumerServerCommandFactory } from "../runtime/NextHostConsumerServerCommandFactory";
|
|
24
26
|
import { TypeScriptRuntimeConfigurator } from "../runtime/TypeScriptRuntimeConfigurator";
|
|
@@ -36,9 +38,11 @@ export class DevCommand {
|
|
|
36
38
|
private readonly cliLogger: Logger,
|
|
37
39
|
private readonly session: DevSessionServices,
|
|
38
40
|
private readonly databaseMigrationsApplyService: DatabaseMigrationsApplyService,
|
|
41
|
+
private readonly consumerOutputBuilderFactory: ConsumerOutputBuilderFactory,
|
|
42
|
+
private readonly pluginDiscovery: CodemationPluginDiscovery,
|
|
43
|
+
private readonly consumerBuildArtifactsPublisher: ConsumerBuildArtifactsPublisher,
|
|
39
44
|
private readonly devBootstrapSummaryFetcher: DevBootstrapSummaryFetcher,
|
|
40
45
|
private readonly devCliBannerRenderer: DevCliBannerRenderer,
|
|
41
|
-
private readonly devConsumerPublishBootstrap: DevConsumerPublishBootstrap,
|
|
42
46
|
private readonly consumerEnvDotenvFilePredicate: ConsumerEnvDotenvFilePredicate,
|
|
43
47
|
private readonly devTrackedProcessTreeKiller: DevTrackedProcessTreeKiller,
|
|
44
48
|
private readonly nextHostConsumerServerCommandFactory: NextHostConsumerServerCommandFactory,
|
|
@@ -47,12 +51,22 @@ export class DevCommand {
|
|
|
47
51
|
private readonly devRebuildQueueFactory: DevRebuildQueueFactory,
|
|
48
52
|
) {}
|
|
49
53
|
|
|
50
|
-
async execute(
|
|
54
|
+
async execute(
|
|
55
|
+
args: Readonly<{
|
|
56
|
+
consumerRoot: string;
|
|
57
|
+
watchFramework?: boolean;
|
|
58
|
+
commandName?: "dev" | "dev:plugin";
|
|
59
|
+
configPathOverride?: string;
|
|
60
|
+
}>,
|
|
61
|
+
): Promise<void> {
|
|
51
62
|
const paths = await this.pathResolver.resolve(args.consumerRoot);
|
|
63
|
+
const commandName = args.commandName ?? "dev";
|
|
64
|
+
const previousDevelopmentServerToken = process.env.CODEMATION_DEV_SERVER_TOKEN;
|
|
52
65
|
this.devCliBannerRenderer.renderBrandHeader();
|
|
53
66
|
this.tsRuntime.configure(paths.repoRoot);
|
|
54
|
-
await this.databaseMigrationsApplyService.applyForConsumer(paths.consumerRoot
|
|
55
|
-
|
|
67
|
+
await this.databaseMigrationsApplyService.applyForConsumer(paths.consumerRoot, {
|
|
68
|
+
configPath: args.configPathOverride,
|
|
69
|
+
});
|
|
56
70
|
const devMode = this.resolveDevMode(args);
|
|
57
71
|
const { nextPort, gatewayPort } = await this.session.sessionPorts.resolve({
|
|
58
72
|
devMode,
|
|
@@ -64,14 +78,29 @@ export class DevCommand {
|
|
|
64
78
|
consumerRoot: paths.consumerRoot,
|
|
65
79
|
nextPort: devMode === "watch-framework" ? nextPort : gatewayPort,
|
|
66
80
|
});
|
|
67
|
-
const authSettings = await this.session.
|
|
81
|
+
const authSettings = await this.session.nextHostEdgeSeedLoader.loadForConsumer(paths.consumerRoot, {
|
|
82
|
+
configPathOverride: args.configPathOverride,
|
|
83
|
+
});
|
|
68
84
|
const watcher = this.devSourceWatcherFactory.create();
|
|
69
85
|
const processState = this.createInitialProcessState();
|
|
70
86
|
let proxyServer: CliDevProxyServer | null = null;
|
|
71
87
|
try {
|
|
72
|
-
const prepared = await this.prepareDevRuntime(
|
|
88
|
+
const prepared = await this.prepareDevRuntime(
|
|
89
|
+
paths,
|
|
90
|
+
devMode,
|
|
91
|
+
nextPort,
|
|
92
|
+
gatewayPort,
|
|
93
|
+
authSettings,
|
|
94
|
+
args.configPathOverride,
|
|
95
|
+
);
|
|
96
|
+
if (prepared.devMode === "packaged-ui") {
|
|
97
|
+
await this.publishConsumerArtifacts(prepared.paths, prepared.configPathOverride);
|
|
98
|
+
}
|
|
99
|
+
// The disposable runtime is created in-process, so config reloads must see the same token in
|
|
100
|
+
// `process.env` that we also pass through the child-facing env object.
|
|
101
|
+
process.env.CODEMATION_DEV_SERVER_TOKEN = prepared.developmentServerToken;
|
|
73
102
|
const stopPromise = this.wireStopPromise(processState);
|
|
74
|
-
const uiProxyBase = await this.
|
|
103
|
+
const uiProxyBase = await this.preparePackagedUiBaseUrlWhenNeeded(prepared, processState);
|
|
75
104
|
proxyServer = await this.startProxyServer(prepared.gatewayPort, uiProxyBase);
|
|
76
105
|
const gatewayBaseUrl = this.gatewayBaseHttpUrl(gatewayPort);
|
|
77
106
|
await this.bootInitialRuntime(prepared, processState, proxyServer);
|
|
@@ -80,12 +109,21 @@ export class DevCommand {
|
|
|
80
109
|
if (initialSummary) {
|
|
81
110
|
this.devCliBannerRenderer.renderRuntimeSummary(initialSummary);
|
|
82
111
|
}
|
|
112
|
+
await this.startPackagedUiWhenNeeded(prepared, processState, uiProxyBase);
|
|
83
113
|
this.bindShutdownSignalsToChildProcesses(processState, proxyServer);
|
|
84
114
|
await this.spawnDevUiWhenNeeded(prepared, processState, gatewayBaseUrl);
|
|
85
|
-
await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer
|
|
86
|
-
|
|
115
|
+
await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer, {
|
|
116
|
+
commandName,
|
|
117
|
+
configPathOverride: args.configPathOverride,
|
|
118
|
+
});
|
|
119
|
+
this.logPackagedUiDevHintWhenNeeded(devMode, gatewayPort, commandName);
|
|
87
120
|
await stopPromise;
|
|
88
121
|
} finally {
|
|
122
|
+
if (previousDevelopmentServerToken === undefined) {
|
|
123
|
+
delete process.env.CODEMATION_DEV_SERVER_TOKEN;
|
|
124
|
+
} else {
|
|
125
|
+
process.env.CODEMATION_DEV_SERVER_TOKEN = previousDevelopmentServerToken;
|
|
126
|
+
}
|
|
89
127
|
processState.stopRequested = true;
|
|
90
128
|
await this.stopLiveProcesses(processState, proxyServer);
|
|
91
129
|
await watcher.stop();
|
|
@@ -105,14 +143,16 @@ export class DevCommand {
|
|
|
105
143
|
devMode: DevMode,
|
|
106
144
|
nextPort: number,
|
|
107
145
|
gatewayPort: number,
|
|
108
|
-
authSettings:
|
|
146
|
+
authSettings: NextHostEdgeSeed,
|
|
147
|
+
configPathOverride?: string,
|
|
109
148
|
): Promise<DevPreparedRuntime> {
|
|
110
|
-
const developmentServerToken = this.session.
|
|
149
|
+
const developmentServerToken = this.session.nextHostEdgeSeedLoader.resolveDevelopmentServerToken(
|
|
111
150
|
process.env.CODEMATION_DEV_SERVER_TOKEN,
|
|
112
151
|
);
|
|
113
152
|
const consumerEnv = this.session.consumerEnvLoader.load(paths.consumerRoot);
|
|
114
153
|
return {
|
|
115
154
|
paths,
|
|
155
|
+
configPathOverride,
|
|
116
156
|
devMode,
|
|
117
157
|
nextPort,
|
|
118
158
|
gatewayPort,
|
|
@@ -146,48 +186,50 @@ export class DevCommand {
|
|
|
146
186
|
return `http://127.0.0.1:${gatewayPort}`;
|
|
147
187
|
}
|
|
148
188
|
|
|
149
|
-
private async
|
|
189
|
+
private async preparePackagedUiBaseUrlWhenNeeded(
|
|
150
190
|
prepared: DevPreparedRuntime,
|
|
151
191
|
state: DevMutableProcessState,
|
|
152
192
|
): Promise<string> {
|
|
153
193
|
if (prepared.devMode !== "packaged-ui") {
|
|
154
194
|
return "";
|
|
155
195
|
}
|
|
156
|
-
const websocketPort = prepared.gatewayPort;
|
|
157
196
|
const uiProxyBase =
|
|
158
197
|
state.currentPackagedUiBaseUrl ?? `http://127.0.0.1:${await this.session.loopbackPortAllocator.allocate()}`;
|
|
159
198
|
state.currentPackagedUiBaseUrl = uiProxyBase;
|
|
160
|
-
await this.spawnPackagedUi(prepared, state, prepared.authSettings, websocketPort, uiProxyBase);
|
|
161
199
|
return uiProxyBase;
|
|
162
200
|
}
|
|
163
201
|
|
|
202
|
+
private async startPackagedUiWhenNeeded(
|
|
203
|
+
prepared: DevPreparedRuntime,
|
|
204
|
+
state: DevMutableProcessState,
|
|
205
|
+
uiProxyBase: string,
|
|
206
|
+
): Promise<void> {
|
|
207
|
+
if (prepared.devMode !== "packaged-ui" || uiProxyBase.length === 0) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
await this.spawnPackagedUi(prepared, state, prepared.authSettings, prepared.gatewayPort, uiProxyBase);
|
|
211
|
+
}
|
|
212
|
+
|
|
164
213
|
private async spawnPackagedUi(
|
|
165
214
|
prepared: DevPreparedRuntime,
|
|
166
215
|
state: DevMutableProcessState,
|
|
167
|
-
authSettings:
|
|
216
|
+
authSettings: NextHostEdgeSeed,
|
|
168
217
|
websocketPort: number,
|
|
169
218
|
uiProxyBase: string,
|
|
170
219
|
): Promise<void> {
|
|
171
220
|
const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
|
|
172
221
|
const nextHostRoot = path.dirname(nextHostPackageJsonPath);
|
|
173
222
|
const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
|
|
174
|
-
const consumerOutputManifestPath = path.resolve(
|
|
175
|
-
prepared.paths.consumerRoot,
|
|
176
|
-
".codemation",
|
|
177
|
-
"output",
|
|
178
|
-
"current.json",
|
|
179
|
-
);
|
|
180
223
|
const uiPort = Number(new URL(uiProxyBase).port);
|
|
181
224
|
const nextHostEnvironment = this.session.nextHostEnvBuilder.buildConsumerUiProxy({
|
|
182
|
-
authConfigJson: authSettings.authConfigJson,
|
|
183
225
|
authSecret: authSettings.authSecret,
|
|
226
|
+
configPathOverride: prepared.configPathOverride,
|
|
184
227
|
consumerRoot: prepared.paths.consumerRoot,
|
|
185
|
-
consumerOutputManifestPath,
|
|
186
228
|
developmentServerToken: prepared.developmentServerToken,
|
|
187
229
|
nextPort: uiPort,
|
|
188
230
|
publicBaseUrl: this.gatewayBaseHttpUrl(prepared.gatewayPort),
|
|
189
231
|
runtimeDevUrl: this.gatewayBaseHttpUrl(prepared.gatewayPort),
|
|
190
|
-
skipUiAuth: authSettings.
|
|
232
|
+
skipUiAuth: !authSettings.uiAuthEnabled,
|
|
191
233
|
websocketPort,
|
|
192
234
|
});
|
|
193
235
|
state.currentPackagedUi = spawn(nextHostCommand.command, nextHostCommand.args, {
|
|
@@ -282,17 +324,18 @@ export class DevCommand {
|
|
|
282
324
|
prepared: DevPreparedRuntime,
|
|
283
325
|
state: DevMutableProcessState,
|
|
284
326
|
gatewayBaseUrl: string,
|
|
285
|
-
authSettings:
|
|
327
|
+
authSettings: NextHostEdgeSeed,
|
|
286
328
|
): Promise<void> {
|
|
287
329
|
const websocketPort = prepared.gatewayPort;
|
|
288
330
|
const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
|
|
289
331
|
const nextHostRoot = path.dirname(nextHostPackageJsonPath);
|
|
290
332
|
const nextHostEnvironment = this.session.nextHostEnvBuilder.build({
|
|
291
|
-
|
|
333
|
+
authSecret: authSettings.authSecret,
|
|
334
|
+
configPathOverride: prepared.configPathOverride,
|
|
292
335
|
consumerRoot: prepared.paths.consumerRoot,
|
|
293
336
|
developmentServerToken: prepared.developmentServerToken,
|
|
294
337
|
nextPort: prepared.nextPort,
|
|
295
|
-
skipUiAuth: authSettings.
|
|
338
|
+
skipUiAuth: !authSettings.uiAuthEnabled,
|
|
296
339
|
websocketPort,
|
|
297
340
|
runtimeDevUrl: gatewayBaseUrl,
|
|
298
341
|
});
|
|
@@ -331,6 +374,10 @@ export class DevCommand {
|
|
|
331
374
|
devMode: DevMode,
|
|
332
375
|
gatewayBaseUrl: string,
|
|
333
376
|
proxyServer: CliDevProxyServer,
|
|
377
|
+
options: Readonly<{
|
|
378
|
+
commandName: "dev" | "dev:plugin";
|
|
379
|
+
configPathOverride?: string;
|
|
380
|
+
}>,
|
|
334
381
|
): Promise<void> {
|
|
335
382
|
const rebuildQueue = this.devRebuildQueueFactory.create({
|
|
336
383
|
run: async (request) => {
|
|
@@ -351,22 +398,18 @@ export class DevCommand {
|
|
|
351
398
|
return;
|
|
352
399
|
}
|
|
353
400
|
try {
|
|
354
|
-
const shouldRepublishConsumerOutput = this.session.sourceChangeClassifier.shouldRepublishConsumerOutput({
|
|
355
|
-
changedPaths,
|
|
356
|
-
consumerRoot: prepared.paths.consumerRoot,
|
|
357
|
-
});
|
|
358
401
|
const shouldRestartUi = this.session.sourceChangeClassifier.requiresUiRestart({
|
|
359
402
|
changedPaths,
|
|
360
403
|
consumerRoot: prepared.paths.consumerRoot,
|
|
361
404
|
});
|
|
362
405
|
process.stdout.write(
|
|
363
406
|
shouldRestartUi
|
|
364
|
-
?
|
|
365
|
-
:
|
|
407
|
+
? `\n[codemation] Source change detected — rebuilding for \`${options.commandName}\`, restarting the runtime, and restarting the UI…\n`
|
|
408
|
+
: `\n[codemation] Source change detected — rebuilding for \`${options.commandName}\` and restarting the runtime…\n`,
|
|
366
409
|
);
|
|
367
410
|
await rebuildQueue.enqueue({
|
|
368
411
|
changedPaths,
|
|
369
|
-
|
|
412
|
+
configPathOverride: options.configPathOverride,
|
|
370
413
|
shouldRestartUi,
|
|
371
414
|
});
|
|
372
415
|
} catch (error) {
|
|
@@ -383,7 +426,7 @@ export class DevCommand {
|
|
|
383
426
|
proxyServer: CliDevProxyServer,
|
|
384
427
|
request: Readonly<{
|
|
385
428
|
changedPaths: ReadonlyArray<string>;
|
|
386
|
-
|
|
429
|
+
configPathOverride?: string;
|
|
387
430
|
shouldRestartUi: boolean;
|
|
388
431
|
}>,
|
|
389
432
|
): Promise<void> {
|
|
@@ -391,8 +434,8 @@ export class DevCommand {
|
|
|
391
434
|
proxyServer.setBuildStatus("building");
|
|
392
435
|
proxyServer.broadcastBuildStarted();
|
|
393
436
|
try {
|
|
394
|
-
if (
|
|
395
|
-
await this.
|
|
437
|
+
if (prepared.devMode === "packaged-ui") {
|
|
438
|
+
await this.publishConsumerArtifacts(prepared.paths, request.configPathOverride);
|
|
396
439
|
}
|
|
397
440
|
await this.stopCurrentRuntime(state, proxyServer);
|
|
398
441
|
process.stdout.write("[codemation] Waiting for runtime to accept traffic…\n");
|
|
@@ -402,15 +445,18 @@ export class DevCommand {
|
|
|
402
445
|
httpPort: runtime.httpPort,
|
|
403
446
|
workflowWebSocketPort: runtime.workflowWebSocketPort,
|
|
404
447
|
});
|
|
405
|
-
if (request.shouldRestartUi) {
|
|
406
|
-
await this.restartUiAfterSourceChange(prepared, state, gatewayBaseUrl);
|
|
407
|
-
}
|
|
408
448
|
await this.session.devHttpProbe.waitUntilBootstrapSummaryReady(gatewayBaseUrl);
|
|
409
449
|
const json = await this.devBootstrapSummaryFetcher.fetch(gatewayBaseUrl);
|
|
410
450
|
if (json) {
|
|
411
451
|
this.devCliBannerRenderer.renderCompact(json);
|
|
412
452
|
}
|
|
413
453
|
proxyServer.setBuildStatus("idle");
|
|
454
|
+
// Let the new runtime become queryable through the stable gateway before restarting the
|
|
455
|
+
// packaged UI; otherwise the UI bootstrap hits `/api/bootstrap/*` while the gateway still
|
|
456
|
+
// reports "Runtime is rebuilding" and the restart can deadlock indefinitely.
|
|
457
|
+
if (request.shouldRestartUi) {
|
|
458
|
+
await this.restartUiAfterSourceChange(prepared, state, gatewayBaseUrl);
|
|
459
|
+
}
|
|
414
460
|
proxyServer.broadcastBuildCompleted(runtime.buildVersion);
|
|
415
461
|
process.stdout.write("[codemation] Runtime ready.\n");
|
|
416
462
|
} catch (error) {
|
|
@@ -425,7 +471,12 @@ export class DevCommand {
|
|
|
425
471
|
state: DevMutableProcessState,
|
|
426
472
|
gatewayBaseUrl: string,
|
|
427
473
|
): Promise<void> {
|
|
428
|
-
const refreshedAuthSettings = await this.session.
|
|
474
|
+
const refreshedAuthSettings = await this.session.nextHostEdgeSeedLoader.loadForConsumer(
|
|
475
|
+
prepared.paths.consumerRoot,
|
|
476
|
+
{
|
|
477
|
+
configPathOverride: prepared.configPathOverride,
|
|
478
|
+
},
|
|
479
|
+
);
|
|
429
480
|
process.stdout.write("[codemation] Restarting the UI process to apply source changes…\n");
|
|
430
481
|
state.isRestartingUi = true;
|
|
431
482
|
try {
|
|
@@ -442,7 +493,7 @@ export class DevCommand {
|
|
|
442
493
|
private async restartPackagedUi(
|
|
443
494
|
prepared: DevPreparedRuntime,
|
|
444
495
|
state: DevMutableProcessState,
|
|
445
|
-
authSettings:
|
|
496
|
+
authSettings: NextHostEdgeSeed,
|
|
446
497
|
): Promise<void> {
|
|
447
498
|
if (
|
|
448
499
|
state.currentPackagedUi &&
|
|
@@ -463,7 +514,7 @@ export class DevCommand {
|
|
|
463
514
|
prepared: DevPreparedRuntime,
|
|
464
515
|
state: DevMutableProcessState,
|
|
465
516
|
gatewayBaseUrl: string,
|
|
466
|
-
authSettings:
|
|
517
|
+
authSettings: NextHostEdgeSeed,
|
|
467
518
|
): Promise<void> {
|
|
468
519
|
if (state.currentDevUi && state.currentDevUi.exitCode === null && state.currentDevUi.signalCode === null) {
|
|
469
520
|
await this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(state.currentDevUi);
|
|
@@ -517,6 +568,7 @@ export class DevCommand {
|
|
|
517
568
|
prepared.consumerEnv,
|
|
518
569
|
);
|
|
519
570
|
return await this.devApiRuntimeFactory.create({
|
|
571
|
+
configPathOverride: prepared.configPathOverride,
|
|
520
572
|
consumerRoot: prepared.paths.consumerRoot,
|
|
521
573
|
runtimeWorkingDirectory: process.cwd(),
|
|
522
574
|
env: {
|
|
@@ -530,12 +582,26 @@ export class DevCommand {
|
|
|
530
582
|
});
|
|
531
583
|
}
|
|
532
584
|
|
|
533
|
-
private
|
|
585
|
+
private async publishConsumerArtifacts(paths: CliPaths, configPathOverride?: string): Promise<void> {
|
|
586
|
+
const builder = this.consumerOutputBuilderFactory.create(paths.consumerRoot, {
|
|
587
|
+
configPathOverride,
|
|
588
|
+
});
|
|
589
|
+
const snapshot = await builder.ensureBuilt();
|
|
590
|
+
const discoveredPlugins = await this.pluginDiscovery.discover(paths.consumerRoot);
|
|
591
|
+
await this.consumerBuildArtifactsPublisher.publish(snapshot, discoveredPlugins);
|
|
592
|
+
this.cliLogger.debug(`Dev: consumer output published (${snapshot.buildVersion}).`);
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
private logPackagedUiDevHintWhenNeeded(
|
|
596
|
+
devMode: DevMode,
|
|
597
|
+
gatewayPort: number,
|
|
598
|
+
commandName: "dev" | "dev:plugin",
|
|
599
|
+
): void {
|
|
534
600
|
if (devMode !== "packaged-ui") {
|
|
535
601
|
return;
|
|
536
602
|
}
|
|
537
603
|
this.cliLogger.info(
|
|
538
|
-
`codemation
|
|
604
|
+
`codemation ${commandName}: open http://127.0.0.1:${gatewayPort} — this uses the packaged @codemation/next-host UI. Use \`codemation ${commandName} --watch-framework\` only when working on the framework UI itself.`,
|
|
539
605
|
);
|
|
540
606
|
}
|
|
541
607
|
}
|