@codemation/cli 0.0.5 → 0.0.11
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 +20 -26
- package/dist/{CliBin-C3ar49fj.js → CliBin-BAnFX1wL.js} +1105 -366
- package/dist/bin.js +1 -1
- package/dist/index.d.ts +655 -207
- package/dist/index.js +1 -1
- package/package.json +14 -6
- package/src/CliProgramFactory.ts +23 -8
- package/src/Program.ts +7 -3
- package/src/bootstrap/CodemationCliApplicationSession.ts +17 -19
- package/src/commands/DevCommand.ts +203 -171
- package/src/commands/ServeWebCommand.ts +26 -1
- package/src/commands/ServeWorkerCommand.ts +46 -30
- package/src/commands/devCommandLifecycle.types.ts +7 -11
- package/src/database/ConsumerDatabaseConnectionResolver.ts +55 -9
- package/src/database/DatabaseMigrationsApplyService.ts +2 -2
- package/src/dev/Builder.ts +1 -14
- package/src/dev/CliDevProxyServer.ts +457 -0
- package/src/dev/CliDevProxyServerFactory.ts +10 -0
- package/src/dev/DevApiRuntimeFactory.ts +44 -0
- package/src/dev/DevApiRuntimeHost.ts +130 -0
- package/src/dev/DevApiRuntimeServer.ts +107 -0
- package/src/dev/DevApiRuntimeTypes.ts +24 -0
- package/src/dev/DevAuthSettingsLoader.ts +9 -3
- package/src/dev/DevBootstrapSummaryFetcher.ts +1 -1
- package/src/dev/DevHttpProbe.ts +2 -2
- package/src/dev/DevNextHostEnvironmentBuilder.ts +35 -5
- package/src/dev/DevRebuildQueue.ts +54 -0
- package/src/dev/DevRebuildQueueFactory.ts +7 -0
- package/src/dev/DevSessionPortsResolver.ts +2 -2
- package/src/dev/DevSessionServices.ts +0 -4
- package/src/dev/DevSourceChangeClassifier.ts +33 -13
- package/src/dev/ListenPortConflictDescriber.ts +83 -0
- package/src/dev/WatchRootsResolver.ts +6 -4
- package/src/runtime/NextHostConsumerServerCommandFactory.ts +11 -2
- package/src/user/CliDatabaseUrlDescriptor.ts +2 -2
- package/src/user/UserAdminCliBootstrap.ts +9 -21
- package/codemation-cli-0.0.3.tgz +0 -0
- package/src/dev/DevSourceRestartCoordinator.ts +0 -48
- package/src/dev/DevelopmentGatewayNotifier.ts +0 -35
- package/src/dev/RuntimeToolEntrypointResolver.ts +0 -47
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import process$1 from "node:process";
|
|
3
|
-
import { CodemationConsumerConfigLoader, CodemationPluginDiscovery, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder } from "@codemation/host/server";
|
|
4
|
-
import {
|
|
3
|
+
import { AppConfigLoader, CodemationConsumerConfigLoader, CodemationFrontendAuthSnapshotFactory, CodemationPluginDiscovery, FrontendAppConfigJsonCodec, WorkflowDiscoveryPathSegmentsComputer, WorkflowModulePathFinder } from "@codemation/host/server";
|
|
4
|
+
import { ApiPaths, AppContainerFactory, AppContainerLifecycle, ApplicationTokens, CodemationFrontendAuthSnapshotFactory as CodemationFrontendAuthSnapshotFactory$1, DatabaseMigrations, FrontendAppConfigJsonCodec as FrontendAppConfigJsonCodec$1, ListUserAccountsQuery, PrismaClient, UpsertLocalBootstrapUserCommand, WorkerRuntime } from "@codemation/host";
|
|
5
|
+
import { AppContainerFactory as AppContainerFactory$1, AppContainerLifecycle as AppContainerLifecycle$1, CodemationHonoApiApp, CodemationPluginListMerger, FrontendRuntime, ServerLoggerFactory, logLevelPolicyFactory } from "@codemation/host/next/server";
|
|
5
6
|
import { randomUUID } from "node:crypto";
|
|
6
7
|
import { access, copyFile, cp, mkdir, open, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
7
8
|
import path from "node:path";
|
|
8
9
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
9
|
-
import { spawn } from "node:child_process";
|
|
10
|
-
import { ApiPaths, ApplicationTokens, CodemationApplication, CodemationBootstrapRequest, ListUserAccountsQuery, PrismaClient, UpsertLocalBootstrapUserCommand } from "@codemation/host";
|
|
10
|
+
import { execFile, spawn } from "node:child_process";
|
|
11
11
|
import { existsSync, readFileSync } from "node:fs";
|
|
12
12
|
import { config, parse } from "dotenv";
|
|
13
13
|
import { watch } from "chokidar";
|
|
14
14
|
import ts from "typescript";
|
|
15
|
-
import {
|
|
15
|
+
import { PrismaMigrationDeployer } from "@codemation/host/persistence";
|
|
16
16
|
import boxen from "boxen";
|
|
17
17
|
import chalk from "chalk";
|
|
18
18
|
import figlet from "figlet";
|
|
19
|
+
import { createServer } from "node:http";
|
|
20
|
+
import httpProxy from "http-proxy";
|
|
21
|
+
import { WebSocket, WebSocketServer } from "ws";
|
|
22
|
+
import { serve } from "@hono/node-server";
|
|
23
|
+
import { Hono } from "hono";
|
|
19
24
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
20
|
-
import { createServer } from "node:net";
|
|
25
|
+
import { createServer as createServer$1 } from "node:net";
|
|
21
26
|
import { Command } from "commander";
|
|
22
27
|
|
|
23
28
|
//#region src/build/ConsumerBuildArtifactsPublisher.ts
|
|
@@ -120,9 +125,8 @@ var DbMigrateCommand = class {
|
|
|
120
125
|
//#region src/commands/DevCommand.ts
|
|
121
126
|
var DevCommand = class {
|
|
122
127
|
require = createRequire(import.meta.url);
|
|
123
|
-
constructor(pathResolver,
|
|
128
|
+
constructor(pathResolver, tsRuntime, devLockFactory, devSourceWatcherFactory, cliLogger, session, databaseMigrationsApplyService, devBootstrapSummaryFetcher, devCliBannerRenderer, devConsumerPublishBootstrap, consumerEnvDotenvFilePredicate, devTrackedProcessTreeKiller, nextHostConsumerServerCommandFactory, devApiRuntimeFactory, cliDevProxyServerFactory, devRebuildQueueFactory) {
|
|
124
129
|
this.pathResolver = pathResolver;
|
|
125
|
-
this.pluginDiscovery = pluginDiscovery;
|
|
126
130
|
this.tsRuntime = tsRuntime;
|
|
127
131
|
this.devLockFactory = devLockFactory;
|
|
128
132
|
this.devSourceWatcherFactory = devSourceWatcherFactory;
|
|
@@ -135,14 +139,17 @@ var DevCommand = class {
|
|
|
135
139
|
this.consumerEnvDotenvFilePredicate = consumerEnvDotenvFilePredicate;
|
|
136
140
|
this.devTrackedProcessTreeKiller = devTrackedProcessTreeKiller;
|
|
137
141
|
this.nextHostConsumerServerCommandFactory = nextHostConsumerServerCommandFactory;
|
|
142
|
+
this.devApiRuntimeFactory = devApiRuntimeFactory;
|
|
143
|
+
this.cliDevProxyServerFactory = cliDevProxyServerFactory;
|
|
144
|
+
this.devRebuildQueueFactory = devRebuildQueueFactory;
|
|
138
145
|
}
|
|
139
|
-
async execute(
|
|
140
|
-
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
146
|
+
async execute(args) {
|
|
147
|
+
const paths = await this.pathResolver.resolve(args.consumerRoot);
|
|
141
148
|
this.devCliBannerRenderer.renderBrandHeader();
|
|
142
149
|
this.tsRuntime.configure(paths.repoRoot);
|
|
143
150
|
await this.databaseMigrationsApplyService.applyForConsumer(paths.consumerRoot);
|
|
144
151
|
await this.devConsumerPublishBootstrap.ensurePublished(paths);
|
|
145
|
-
const devMode = this.
|
|
152
|
+
const devMode = this.resolveDevMode(args);
|
|
146
153
|
const { nextPort, gatewayPort } = await this.session.sessionPorts.resolve({
|
|
147
154
|
devMode,
|
|
148
155
|
portEnv: process$1.env.PORT,
|
|
@@ -151,70 +158,56 @@ var DevCommand = class {
|
|
|
151
158
|
const devLock = this.devLockFactory.create();
|
|
152
159
|
await devLock.acquire({
|
|
153
160
|
consumerRoot: paths.consumerRoot,
|
|
154
|
-
nextPort: devMode === "framework" ? nextPort : gatewayPort
|
|
161
|
+
nextPort: devMode === "watch-framework" ? nextPort : gatewayPort
|
|
155
162
|
});
|
|
156
163
|
const authSettings = await this.session.devAuthLoader.loadForConsumer(paths.consumerRoot);
|
|
157
164
|
const watcher = this.devSourceWatcherFactory.create();
|
|
158
165
|
const processState = this.createInitialProcessState();
|
|
166
|
+
let proxyServer = null;
|
|
159
167
|
try {
|
|
160
168
|
const prepared = await this.prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings);
|
|
161
169
|
const stopPromise = this.wireStopPromise(processState);
|
|
162
|
-
const uiProxyBase = await this.
|
|
170
|
+
const uiProxyBase = await this.startPackagedUiWhenNeeded(prepared, processState);
|
|
171
|
+
proxyServer = await this.startProxyServer(prepared.gatewayPort, uiProxyBase);
|
|
163
172
|
const gatewayBaseUrl = this.gatewayBaseHttpUrl(gatewayPort);
|
|
164
|
-
await this.
|
|
173
|
+
await this.bootInitialRuntime(prepared, processState, proxyServer);
|
|
165
174
|
await this.session.devHttpProbe.waitUntilBootstrapSummaryReady(gatewayBaseUrl);
|
|
166
175
|
const initialSummary = await this.devBootstrapSummaryFetcher.fetch(gatewayBaseUrl);
|
|
167
176
|
if (initialSummary) this.devCliBannerRenderer.renderRuntimeSummary(initialSummary);
|
|
168
|
-
this.bindShutdownSignalsToChildProcesses(processState);
|
|
169
|
-
await this.
|
|
170
|
-
await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl);
|
|
171
|
-
this.
|
|
177
|
+
this.bindShutdownSignalsToChildProcesses(processState, proxyServer);
|
|
178
|
+
await this.spawnDevUiWhenNeeded(prepared, processState, gatewayBaseUrl);
|
|
179
|
+
await this.startWatcherForSourceRestart(prepared, processState, watcher, devMode, gatewayBaseUrl, proxyServer);
|
|
180
|
+
this.logPackagedUiDevHintWhenNeeded(devMode, gatewayPort);
|
|
172
181
|
await stopPromise;
|
|
173
182
|
} finally {
|
|
174
183
|
processState.stopRequested = true;
|
|
175
|
-
await this.
|
|
184
|
+
await this.stopLiveProcesses(processState, proxyServer);
|
|
176
185
|
await watcher.stop();
|
|
177
186
|
await devLock.release();
|
|
178
187
|
}
|
|
179
188
|
}
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
resolveDevMode(args) {
|
|
190
|
+
if (args.watchFramework === true || process$1.env.CODEMATION_DEV_MODE === "framework") return "watch-framework";
|
|
191
|
+
return "packaged-ui";
|
|
182
192
|
}
|
|
183
193
|
async prepareDevRuntime(paths, devMode, nextPort, gatewayPort, authSettings) {
|
|
184
|
-
const developmentServerToken = this.session.devAuthLoader.resolveDevelopmentServerToken(process$1.env.CODEMATION_DEV_SERVER_TOKEN);
|
|
185
|
-
const gatewayEntrypoint = await this.session.runtimeEntrypointResolver.resolve({
|
|
186
|
-
packageName: "@codemation/dev-gateway",
|
|
187
|
-
repoRoot: paths.repoRoot,
|
|
188
|
-
sourceEntrypoint: "packages/dev-gateway/src/bin.ts"
|
|
189
|
-
});
|
|
190
|
-
const runtimeEntrypoint = await this.session.runtimeEntrypointResolver.resolve({
|
|
191
|
-
packageName: "@codemation/runtime-dev",
|
|
192
|
-
repoRoot: paths.repoRoot,
|
|
193
|
-
sourceEntrypoint: "packages/runtime-dev/src/bin.ts"
|
|
194
|
-
});
|
|
195
|
-
const runtimeWorkingDirectory = paths.repoRoot ?? paths.consumerRoot;
|
|
196
|
-
const consumerEnv = this.session.consumerEnvLoader.load(paths.consumerRoot);
|
|
197
194
|
return {
|
|
198
195
|
paths,
|
|
199
196
|
devMode,
|
|
200
197
|
nextPort,
|
|
201
198
|
gatewayPort,
|
|
202
199
|
authSettings,
|
|
203
|
-
developmentServerToken,
|
|
204
|
-
|
|
205
|
-
runtimeEntrypoint,
|
|
206
|
-
runtimeWorkingDirectory,
|
|
207
|
-
discoveredPluginPackagesJson: JSON.stringify(await this.pluginDiscovery.discover(paths.consumerRoot)),
|
|
208
|
-
consumerEnv
|
|
200
|
+
developmentServerToken: this.session.devAuthLoader.resolveDevelopmentServerToken(process$1.env.CODEMATION_DEV_SERVER_TOKEN),
|
|
201
|
+
consumerEnv: this.session.consumerEnvLoader.load(paths.consumerRoot)
|
|
209
202
|
};
|
|
210
203
|
}
|
|
211
204
|
createInitialProcessState() {
|
|
212
205
|
return {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
206
|
+
currentDevUi: null,
|
|
207
|
+
currentPackagedUi: null,
|
|
208
|
+
currentPackagedUiBaseUrl: null,
|
|
209
|
+
currentRuntime: null,
|
|
210
|
+
isRestartingUi: false,
|
|
218
211
|
stopRequested: false,
|
|
219
212
|
stopResolve: null,
|
|
220
213
|
stopReject: null
|
|
@@ -229,19 +222,15 @@ var DevCommand = class {
|
|
|
229
222
|
gatewayBaseHttpUrl(gatewayPort) {
|
|
230
223
|
return `http://127.0.0.1:${gatewayPort}`;
|
|
231
224
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
* Framework mode: no separate UI child (Next runs in dev later).
|
|
235
|
-
*/
|
|
236
|
-
async startConsumerUiProxyWhenNeeded(prepared, state) {
|
|
237
|
-
if (prepared.devMode !== "consumer") return "";
|
|
225
|
+
async startPackagedUiWhenNeeded(prepared, state) {
|
|
226
|
+
if (prepared.devMode !== "packaged-ui") return "";
|
|
238
227
|
const websocketPort = prepared.gatewayPort;
|
|
239
|
-
const uiProxyBase = state.
|
|
240
|
-
state.
|
|
241
|
-
await this.
|
|
228
|
+
const uiProxyBase = state.currentPackagedUiBaseUrl ?? `http://127.0.0.1:${await this.session.loopbackPortAllocator.allocate()}`;
|
|
229
|
+
state.currentPackagedUiBaseUrl = uiProxyBase;
|
|
230
|
+
await this.spawnPackagedUi(prepared, state, prepared.authSettings, websocketPort, uiProxyBase);
|
|
242
231
|
return uiProxyBase;
|
|
243
232
|
}
|
|
244
|
-
async
|
|
233
|
+
async spawnPackagedUi(prepared, state, authSettings, websocketPort, uiProxyBase) {
|
|
245
234
|
const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
|
|
246
235
|
const nextHostRoot = path.dirname(nextHostPackageJsonPath);
|
|
247
236
|
const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
|
|
@@ -259,60 +248,38 @@ var DevCommand = class {
|
|
|
259
248
|
skipUiAuth: authSettings.skipUiAuth,
|
|
260
249
|
websocketPort
|
|
261
250
|
});
|
|
262
|
-
state.
|
|
251
|
+
state.currentPackagedUi = spawn(nextHostCommand.command, nextHostCommand.args, {
|
|
263
252
|
cwd: nextHostCommand.cwd,
|
|
264
253
|
...this.devDetachedChildSpawnOptions(),
|
|
265
254
|
env: nextHostEnvironment
|
|
266
255
|
});
|
|
267
|
-
state.
|
|
268
|
-
if (state.stopRequested || state.
|
|
256
|
+
state.currentPackagedUi.on("error", (error) => {
|
|
257
|
+
if (state.stopRequested || state.isRestartingUi) return;
|
|
269
258
|
state.stopRequested = true;
|
|
270
259
|
state.stopReject?.(error instanceof Error ? error : new Error(String(error)));
|
|
271
260
|
});
|
|
272
|
-
state.
|
|
273
|
-
if (state.stopRequested || state.
|
|
261
|
+
state.currentPackagedUi.on("exit", (code) => {
|
|
262
|
+
if (state.stopRequested || state.isRestartingUi) return;
|
|
274
263
|
state.stopRequested = true;
|
|
275
|
-
|
|
276
|
-
state.stopReject?.(/* @__PURE__ */ new Error(`next start (consumer UI) exited unexpectedly with code ${code ?? 0}.`));
|
|
264
|
+
state.stopReject?.(/* @__PURE__ */ new Error(`next start (packaged UI) exited unexpectedly with code ${code ?? 0}.`));
|
|
277
265
|
});
|
|
278
266
|
await this.session.devHttpProbe.waitUntilUrlRespondsOk(`${uiProxyBase}/`);
|
|
279
267
|
}
|
|
280
|
-
async
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
CODEMATION_CONSUMER_ROOT: prepared.paths.consumerRoot,
|
|
294
|
-
CODEMATION_DISCOVERED_PLUGIN_PACKAGES_JSON: prepared.discoveredPluginPackagesJson,
|
|
295
|
-
CODEMATION_PREFER_PLUGIN_SOURCE_ENTRY: "true",
|
|
296
|
-
CODEMATION_DEV_SERVER_TOKEN: prepared.developmentServerToken,
|
|
297
|
-
CODEMATION_SKIP_STARTUP_MIGRATIONS: "true",
|
|
298
|
-
NODE_OPTIONS: this.session.sourceMapNodeOptions.appendToNodeOptions(process$1.env.NODE_OPTIONS),
|
|
299
|
-
WS_NO_BUFFER_UTIL: "1",
|
|
300
|
-
WS_NO_UTF_8_VALIDATE: "1",
|
|
301
|
-
...uiProxyBase.length > 0 ? { CODEMATION_DEV_UI_PROXY_TARGET: uiProxyBase } : {}
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
state.currentGateway.on("error", (error) => {
|
|
305
|
-
if (!state.stopRequested) {
|
|
306
|
-
state.stopRequested = true;
|
|
307
|
-
state.stopReject?.(error instanceof Error ? error : new Error(String(error)));
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
state.currentGateway.on("exit", (code) => {
|
|
311
|
-
if (state.stopRequested) return;
|
|
312
|
-
state.stopRequested = true;
|
|
313
|
-
state.stopReject?.(/* @__PURE__ */ new Error(`codemation dev-gateway exited unexpectedly with code ${code ?? 0}.`));
|
|
268
|
+
async startProxyServer(gatewayPort, uiProxyBase) {
|
|
269
|
+
const proxyServer = this.cliDevProxyServerFactory.create(gatewayPort);
|
|
270
|
+
proxyServer.setUiProxyTarget(uiProxyBase.length > 0 ? uiProxyBase : null);
|
|
271
|
+
await proxyServer.start();
|
|
272
|
+
await this.session.devHttpProbe.waitUntilGatewayHealthy(this.gatewayBaseHttpUrl(gatewayPort));
|
|
273
|
+
return proxyServer;
|
|
274
|
+
}
|
|
275
|
+
async bootInitialRuntime(prepared, state, proxyServer) {
|
|
276
|
+
const runtime = await this.createRuntime(prepared);
|
|
277
|
+
state.currentRuntime = runtime;
|
|
278
|
+
await proxyServer.activateRuntime({
|
|
279
|
+
httpPort: runtime.httpPort,
|
|
280
|
+
workflowWebSocketPort: runtime.workflowWebSocketPort
|
|
314
281
|
});
|
|
315
|
-
|
|
282
|
+
proxyServer.setBuildStatus("idle");
|
|
316
283
|
}
|
|
317
284
|
devDetachedChildSpawnOptions() {
|
|
318
285
|
return process$1.platform === "win32" ? {
|
|
@@ -324,20 +291,14 @@ var DevCommand = class {
|
|
|
324
291
|
detached: true
|
|
325
292
|
};
|
|
326
293
|
}
|
|
327
|
-
bindShutdownSignalsToChildProcesses(state) {
|
|
294
|
+
bindShutdownSignalsToChildProcesses(state, proxyServer) {
|
|
328
295
|
let shutdownInProgress = false;
|
|
329
296
|
const runShutdown = async () => {
|
|
330
297
|
if (shutdownInProgress) return;
|
|
331
298
|
shutdownInProgress = true;
|
|
332
299
|
state.stopRequested = true;
|
|
333
300
|
process$1.stdout.write("\n[codemation] Stopping..\n");
|
|
334
|
-
|
|
335
|
-
for (const child of [
|
|
336
|
-
state.currentUiNext,
|
|
337
|
-
state.currentNextHost,
|
|
338
|
-
state.currentGateway
|
|
339
|
-
]) if (child && child.exitCode === null && child.signalCode === null) children.push(child);
|
|
340
|
-
await Promise.all(children.map((child) => this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(child)));
|
|
301
|
+
await this.stopLiveProcesses(state, proxyServer);
|
|
341
302
|
process$1.stdout.write("[codemation] Stopped.\n");
|
|
342
303
|
state.stopResolve?.();
|
|
343
304
|
};
|
|
@@ -349,14 +310,11 @@ var DevCommand = class {
|
|
|
349
310
|
runShutdown();
|
|
350
311
|
});
|
|
351
312
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
async spawnFrameworkNextHostWhenNeeded(prepared, state, gatewayBaseUrl) {
|
|
356
|
-
if (prepared.devMode !== "framework") return;
|
|
357
|
-
await this.spawnFrameworkNextHost(prepared, state, gatewayBaseUrl, prepared.authSettings);
|
|
313
|
+
async spawnDevUiWhenNeeded(prepared, state, gatewayBaseUrl) {
|
|
314
|
+
if (prepared.devMode !== "watch-framework") return;
|
|
315
|
+
await this.spawnDevUi(prepared, state, gatewayBaseUrl, prepared.authSettings);
|
|
358
316
|
}
|
|
359
|
-
async
|
|
317
|
+
async spawnDevUi(prepared, state, gatewayBaseUrl, authSettings) {
|
|
360
318
|
const websocketPort = prepared.gatewayPort;
|
|
361
319
|
const nextHostPackageJsonPath = this.require.resolve("@codemation/next-host/package.json");
|
|
362
320
|
const nextHostRoot = path.dirname(nextHostPackageJsonPath);
|
|
@@ -369,7 +327,7 @@ var DevCommand = class {
|
|
|
369
327
|
websocketPort,
|
|
370
328
|
runtimeDevUrl: gatewayBaseUrl
|
|
371
329
|
});
|
|
372
|
-
state.
|
|
330
|
+
state.currentDevUi = spawn("pnpm", [
|
|
373
331
|
"exec",
|
|
374
332
|
"next",
|
|
375
333
|
"dev"
|
|
@@ -378,26 +336,28 @@ var DevCommand = class {
|
|
|
378
336
|
...this.devDetachedChildSpawnOptions(),
|
|
379
337
|
env: nextHostEnvironment
|
|
380
338
|
});
|
|
381
|
-
state.
|
|
339
|
+
state.currentDevUi.on("exit", (code) => {
|
|
382
340
|
const normalizedCode = code ?? 0;
|
|
383
|
-
if (state.stopRequested || state.
|
|
341
|
+
if (state.stopRequested || state.isRestartingUi) return;
|
|
384
342
|
if (normalizedCode === 0) {
|
|
385
343
|
state.stopRequested = true;
|
|
386
|
-
if (state.currentGateway?.exitCode === null) this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(state.currentGateway);
|
|
387
344
|
state.stopResolve?.();
|
|
388
345
|
return;
|
|
389
346
|
}
|
|
390
347
|
state.stopRequested = true;
|
|
391
348
|
state.stopReject?.(/* @__PURE__ */ new Error(`next host exited with code ${normalizedCode}.`));
|
|
392
349
|
});
|
|
393
|
-
state.
|
|
394
|
-
if (state.stopRequested || state.
|
|
350
|
+
state.currentDevUi.on("error", (error) => {
|
|
351
|
+
if (state.stopRequested || state.isRestartingUi) return;
|
|
395
352
|
state.stopRequested = true;
|
|
396
353
|
state.stopReject?.(error instanceof Error ? error : new Error(String(error)));
|
|
397
354
|
});
|
|
398
355
|
await this.session.devHttpProbe.waitUntilUrlRespondsOk(`http://127.0.0.1:${prepared.nextPort}/`);
|
|
399
356
|
}
|
|
400
|
-
async startWatcherForSourceRestart(prepared, state, watcher, devMode, gatewayBaseUrl) {
|
|
357
|
+
async startWatcherForSourceRestart(prepared, state, watcher, devMode, gatewayBaseUrl, proxyServer) {
|
|
358
|
+
const rebuildQueue = this.devRebuildQueueFactory.create({ run: async (request) => {
|
|
359
|
+
await this.runQueuedRebuild(prepared, state, gatewayBaseUrl, proxyServer, request);
|
|
360
|
+
} });
|
|
401
361
|
await watcher.start({
|
|
402
362
|
roots: this.session.watchRootsResolver.resolve({
|
|
403
363
|
consumerRoot: prepared.paths.consumerRoot,
|
|
@@ -406,7 +366,7 @@ var DevCommand = class {
|
|
|
406
366
|
}),
|
|
407
367
|
onChange: async ({ changedPaths }) => {
|
|
408
368
|
if (changedPaths.length > 0 && changedPaths.every((p) => this.consumerEnvDotenvFilePredicate.matches(p))) {
|
|
409
|
-
process$1.stdout.write("\n[codemation] Consumer environment file changed (e.g. .env). Restart the `codemation dev` process so the
|
|
369
|
+
process$1.stdout.write("\n[codemation] Consumer environment file changed (e.g. .env). Restart the `codemation dev` process so the runtime picks up updated variables (host `process.env` does not hot-reload).\n");
|
|
410
370
|
return;
|
|
411
371
|
}
|
|
412
372
|
try {
|
|
@@ -414,69 +374,112 @@ var DevCommand = class {
|
|
|
414
374
|
changedPaths,
|
|
415
375
|
consumerRoot: prepared.paths.consumerRoot
|
|
416
376
|
});
|
|
417
|
-
const
|
|
377
|
+
const shouldRestartUi = this.session.sourceChangeClassifier.requiresUiRestart({
|
|
418
378
|
changedPaths,
|
|
419
379
|
consumerRoot: prepared.paths.consumerRoot
|
|
420
380
|
});
|
|
421
|
-
process$1.stdout.write(
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
const json = await this.devBootstrapSummaryFetcher.fetch(gatewayBaseUrl);
|
|
428
|
-
if (json) this.devCliBannerRenderer.renderCompact(json);
|
|
429
|
-
process$1.stdout.write("[codemation] Runtime ready.\n");
|
|
381
|
+
process$1.stdout.write(shouldRestartUi ? "\n[codemation] Source change detected — rebuilding consumer, restarting the runtime, and restarting the UI…\n" : "\n[codemation] Source change detected — rebuilding consumer and restarting the runtime…\n");
|
|
382
|
+
await rebuildQueue.enqueue({
|
|
383
|
+
changedPaths,
|
|
384
|
+
shouldRepublishConsumerOutput,
|
|
385
|
+
shouldRestartUi
|
|
386
|
+
});
|
|
430
387
|
} catch (error) {
|
|
431
|
-
await this.failDevSessionAfterIrrecoverableSourceError(state, error);
|
|
388
|
+
await this.failDevSessionAfterIrrecoverableSourceError(state, proxyServer, error);
|
|
432
389
|
}
|
|
433
390
|
}
|
|
434
391
|
});
|
|
435
392
|
}
|
|
436
|
-
async
|
|
393
|
+
async runQueuedRebuild(prepared, state, gatewayBaseUrl, proxyServer, request) {
|
|
394
|
+
request.changedPaths;
|
|
395
|
+
proxyServer.setBuildStatus("building");
|
|
396
|
+
proxyServer.broadcastBuildStarted();
|
|
397
|
+
try {
|
|
398
|
+
if (request.shouldRepublishConsumerOutput) await this.devConsumerPublishBootstrap.ensurePublished(prepared.paths);
|
|
399
|
+
await this.stopCurrentRuntime(state, proxyServer);
|
|
400
|
+
process$1.stdout.write("[codemation] Waiting for runtime to accept traffic…\n");
|
|
401
|
+
const runtime = await this.createRuntime(prepared);
|
|
402
|
+
state.currentRuntime = runtime;
|
|
403
|
+
await proxyServer.activateRuntime({
|
|
404
|
+
httpPort: runtime.httpPort,
|
|
405
|
+
workflowWebSocketPort: runtime.workflowWebSocketPort
|
|
406
|
+
});
|
|
407
|
+
if (request.shouldRestartUi) await this.restartUiAfterSourceChange(prepared, state, gatewayBaseUrl);
|
|
408
|
+
await this.session.devHttpProbe.waitUntilBootstrapSummaryReady(gatewayBaseUrl);
|
|
409
|
+
const json = await this.devBootstrapSummaryFetcher.fetch(gatewayBaseUrl);
|
|
410
|
+
if (json) this.devCliBannerRenderer.renderCompact(json);
|
|
411
|
+
proxyServer.setBuildStatus("idle");
|
|
412
|
+
proxyServer.broadcastBuildCompleted(runtime.buildVersion);
|
|
413
|
+
process$1.stdout.write("[codemation] Runtime ready.\n");
|
|
414
|
+
} catch (error) {
|
|
415
|
+
proxyServer.setBuildStatus("idle");
|
|
416
|
+
proxyServer.broadcastBuildFailed(error instanceof Error ? error.message : String(error));
|
|
417
|
+
throw error;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async restartUiAfterSourceChange(prepared, state, gatewayBaseUrl) {
|
|
437
421
|
const refreshedAuthSettings = await this.session.devAuthLoader.loadForConsumer(prepared.paths.consumerRoot);
|
|
438
|
-
process$1.stdout.write("[codemation] Restarting
|
|
439
|
-
state.
|
|
422
|
+
process$1.stdout.write("[codemation] Restarting the UI process to apply source changes…\n");
|
|
423
|
+
state.isRestartingUi = true;
|
|
440
424
|
try {
|
|
441
|
-
if (prepared.devMode === "
|
|
442
|
-
await this.
|
|
425
|
+
if (prepared.devMode === "packaged-ui") {
|
|
426
|
+
await this.restartPackagedUi(prepared, state, refreshedAuthSettings);
|
|
443
427
|
return;
|
|
444
428
|
}
|
|
445
|
-
await this.
|
|
429
|
+
await this.restartDevUi(prepared, state, gatewayBaseUrl, refreshedAuthSettings);
|
|
446
430
|
} finally {
|
|
447
|
-
state.
|
|
431
|
+
state.isRestartingUi = false;
|
|
448
432
|
}
|
|
449
433
|
}
|
|
450
|
-
async
|
|
451
|
-
if (state.
|
|
452
|
-
state.
|
|
453
|
-
const uiProxyBaseUrl = state.
|
|
454
|
-
if (!uiProxyBaseUrl) throw new Error("
|
|
455
|
-
await this.
|
|
434
|
+
async restartPackagedUi(prepared, state, authSettings) {
|
|
435
|
+
if (state.currentPackagedUi && state.currentPackagedUi.exitCode === null && state.currentPackagedUi.signalCode === null) await this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(state.currentPackagedUi);
|
|
436
|
+
state.currentPackagedUi = null;
|
|
437
|
+
const uiProxyBaseUrl = state.currentPackagedUiBaseUrl;
|
|
438
|
+
if (!uiProxyBaseUrl) throw new Error("Packaged UI proxy base URL is missing during UI restart.");
|
|
439
|
+
await this.spawnPackagedUi(prepared, state, authSettings, prepared.gatewayPort, uiProxyBaseUrl);
|
|
456
440
|
}
|
|
457
|
-
async
|
|
458
|
-
if (state.
|
|
459
|
-
state.
|
|
460
|
-
await this.
|
|
441
|
+
async restartDevUi(prepared, state, gatewayBaseUrl, authSettings) {
|
|
442
|
+
if (state.currentDevUi && state.currentDevUi.exitCode === null && state.currentDevUi.signalCode === null) await this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(state.currentDevUi);
|
|
443
|
+
state.currentDevUi = null;
|
|
444
|
+
await this.spawnDevUi(prepared, state, gatewayBaseUrl, authSettings);
|
|
461
445
|
}
|
|
462
|
-
async failDevSessionAfterIrrecoverableSourceError(state, error) {
|
|
446
|
+
async failDevSessionAfterIrrecoverableSourceError(state, proxyServer, error) {
|
|
463
447
|
const exception = error instanceof Error ? error : new Error(String(error));
|
|
464
448
|
state.stopRequested = true;
|
|
465
|
-
await this.
|
|
449
|
+
await this.stopLiveProcesses(state, proxyServer);
|
|
466
450
|
state.stopReject?.(exception);
|
|
467
451
|
}
|
|
468
|
-
async
|
|
452
|
+
async stopLiveProcesses(state, proxyServer) {
|
|
453
|
+
await this.stopCurrentRuntime(state, proxyServer);
|
|
469
454
|
const children = [];
|
|
470
|
-
for (const child of [
|
|
471
|
-
state.currentUiNext,
|
|
472
|
-
state.currentNextHost,
|
|
473
|
-
state.currentGateway
|
|
474
|
-
]) if (child && child.exitCode === null && child.signalCode === null) children.push(child);
|
|
455
|
+
for (const child of [state.currentPackagedUi, state.currentDevUi]) if (child && child.exitCode === null && child.signalCode === null) children.push(child);
|
|
475
456
|
await Promise.all(children.map((child) => this.devTrackedProcessTreeKiller.killProcessTreeRootedAt(child)));
|
|
457
|
+
if (proxyServer) await proxyServer.stop();
|
|
476
458
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
459
|
+
async stopCurrentRuntime(state, proxyServer) {
|
|
460
|
+
const runtime = state.currentRuntime;
|
|
461
|
+
state.currentRuntime = null;
|
|
462
|
+
if (proxyServer) await proxyServer.activateRuntime(null);
|
|
463
|
+
if (runtime) await runtime.stop();
|
|
464
|
+
}
|
|
465
|
+
async createRuntime(prepared) {
|
|
466
|
+
const runtimeEnvironment = this.session.consumerEnvLoader.mergeIntoProcessEnvironment(process$1.env, prepared.consumerEnv);
|
|
467
|
+
return await this.devApiRuntimeFactory.create({
|
|
468
|
+
consumerRoot: prepared.paths.consumerRoot,
|
|
469
|
+
runtimeWorkingDirectory: process$1.cwd(),
|
|
470
|
+
env: {
|
|
471
|
+
...runtimeEnvironment,
|
|
472
|
+
CODEMATION_DEV_SERVER_TOKEN: prepared.developmentServerToken,
|
|
473
|
+
CODEMATION_SKIP_STARTUP_MIGRATIONS: "true",
|
|
474
|
+
NODE_OPTIONS: this.session.sourceMapNodeOptions.appendToNodeOptions(process$1.env.NODE_OPTIONS),
|
|
475
|
+
WS_NO_BUFFER_UTIL: "1",
|
|
476
|
+
WS_NO_UTF_8_VALIDATE: "1"
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
logPackagedUiDevHintWhenNeeded(devMode, gatewayPort) {
|
|
481
|
+
if (devMode !== "packaged-ui") return;
|
|
482
|
+
this.cliLogger.info(`codemation dev: open http://127.0.0.1:${gatewayPort} — this uses the packaged @codemation/next-host UI. Use \`codemation dev --watch-framework\` only when working on the framework UI itself.`);
|
|
480
483
|
}
|
|
481
484
|
};
|
|
482
485
|
|
|
@@ -484,8 +487,9 @@ var DevCommand = class {
|
|
|
484
487
|
//#region src/commands/ServeWebCommand.ts
|
|
485
488
|
var ServeWebCommand = class {
|
|
486
489
|
require = createRequire(import.meta.url);
|
|
487
|
-
constructor(pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, sourceMapNodeOptions, outputBuilderLoader, envLoader, listenPortResolver, nextHostConsumerServerCommandFactory) {
|
|
490
|
+
constructor(pathResolver, configLoader, pluginDiscovery, artifactsPublisher, tsRuntime, sourceMapNodeOptions, outputBuilderLoader, envLoader, listenPortResolver, nextHostConsumerServerCommandFactory, frontendAuthSnapshotFactory, frontendAppConfigJsonCodec) {
|
|
488
491
|
this.pathResolver = pathResolver;
|
|
492
|
+
this.configLoader = configLoader;
|
|
489
493
|
this.pluginDiscovery = pluginDiscovery;
|
|
490
494
|
this.artifactsPublisher = artifactsPublisher;
|
|
491
495
|
this.tsRuntime = tsRuntime;
|
|
@@ -494,6 +498,8 @@ var ServeWebCommand = class {
|
|
|
494
498
|
this.envLoader = envLoader;
|
|
495
499
|
this.listenPortResolver = listenPortResolver;
|
|
496
500
|
this.nextHostConsumerServerCommandFactory = nextHostConsumerServerCommandFactory;
|
|
501
|
+
this.frontendAuthSnapshotFactory = frontendAuthSnapshotFactory;
|
|
502
|
+
this.frontendAppConfigJsonCodec = frontendAppConfigJsonCodec;
|
|
497
503
|
}
|
|
498
504
|
async execute(consumerRoot, buildOptions) {
|
|
499
505
|
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
@@ -504,6 +510,15 @@ var ServeWebCommand = class {
|
|
|
504
510
|
const nextHostRoot = path.dirname(this.require.resolve("@codemation/next-host/package.json"));
|
|
505
511
|
const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
|
|
506
512
|
const consumerEnv = this.envLoader.load(paths.consumerRoot);
|
|
513
|
+
const configResolution = await this.configLoader.load({ consumerRoot: paths.consumerRoot });
|
|
514
|
+
const frontendAuthSnapshot = this.frontendAuthSnapshotFactory.createFromResolvedInputs({
|
|
515
|
+
authConfig: configResolution.config.auth,
|
|
516
|
+
env: {
|
|
517
|
+
...process$1.env,
|
|
518
|
+
...consumerEnv
|
|
519
|
+
},
|
|
520
|
+
uiAuthEnabled: !(consumerEnv.NODE_ENV !== "production" && configResolution.config.auth?.allowUnauthenticatedInDevelopment === true)
|
|
521
|
+
});
|
|
507
522
|
const nextPort = this.listenPortResolver.resolvePrimaryApplicationPort(process$1.env.PORT);
|
|
508
523
|
const websocketPort = this.listenPortResolver.resolveWebsocketPortRelativeToHttp({
|
|
509
524
|
nextPort,
|
|
@@ -517,6 +532,11 @@ var ServeWebCommand = class {
|
|
|
517
532
|
...process$1.env,
|
|
518
533
|
...consumerEnv,
|
|
519
534
|
PORT: String(nextPort),
|
|
535
|
+
CODEMATION_FRONTEND_APP_CONFIG_JSON: this.frontendAppConfigJsonCodec.serialize({
|
|
536
|
+
auth: frontendAuthSnapshot,
|
|
537
|
+
productName: "Codemation",
|
|
538
|
+
logoUrl: null
|
|
539
|
+
}),
|
|
520
540
|
CODEMATION_CONSUMER_OUTPUT_MANIFEST_PATH: manifest.manifestPath,
|
|
521
541
|
CODEMATION_CONSUMER_ROOT: paths.consumerRoot,
|
|
522
542
|
CODEMATION_WS_PORT: String(websocketPort),
|
|
@@ -542,34 +562,46 @@ var ServeWebCommand = class {
|
|
|
542
562
|
//#endregion
|
|
543
563
|
//#region src/commands/ServeWorkerCommand.ts
|
|
544
564
|
var ServeWorkerCommand = class {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
this.
|
|
565
|
+
constructor(pathResolver, appConfigLoader, appContainerFactory) {
|
|
566
|
+
this.pathResolver = pathResolver;
|
|
567
|
+
this.appConfigLoader = appConfigLoader;
|
|
568
|
+
this.appContainerFactory = appContainerFactory;
|
|
548
569
|
}
|
|
549
570
|
async execute(consumerRoot, configPathOverride) {
|
|
550
|
-
const
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
stdio: "inherit",
|
|
557
|
-
env: {
|
|
558
|
-
...process$1.env,
|
|
559
|
-
NODE_OPTIONS: this.sourceMapNodeOptions.appendToNodeOptions(process$1.env.NODE_OPTIONS)
|
|
560
|
-
}
|
|
571
|
+
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
572
|
+
const loadResult = await this.appConfigLoader.load({
|
|
573
|
+
consumerRoot,
|
|
574
|
+
repoRoot: paths.repoRoot,
|
|
575
|
+
env: process$1.env,
|
|
576
|
+
configPathOverride
|
|
561
577
|
});
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
578
|
+
if (loadResult.appConfig.scheduler.kind !== "bullmq") throw new Error("Worker mode requires runtime.scheduler.kind = \"bullmq\".");
|
|
579
|
+
const container = await this.appContainerFactory.create({
|
|
580
|
+
appConfig: loadResult.appConfig,
|
|
581
|
+
sharedWorkflowWebsocketServer: null
|
|
582
|
+
});
|
|
583
|
+
const workerQueues = loadResult.appConfig.scheduler.workerQueues.length > 0 ? loadResult.appConfig.scheduler.workerQueues : ["default"];
|
|
584
|
+
const handle = await container.resolve(WorkerRuntime).start(workerQueues);
|
|
585
|
+
await new Promise((resolve) => {
|
|
586
|
+
this.bindSignals(handle.stop, resolve);
|
|
571
587
|
});
|
|
572
588
|
}
|
|
589
|
+
bindSignals(stop, resolve) {
|
|
590
|
+
let stopping = false;
|
|
591
|
+
const onSignal = async () => {
|
|
592
|
+
if (stopping) return;
|
|
593
|
+
stopping = true;
|
|
594
|
+
try {
|
|
595
|
+
await stop();
|
|
596
|
+
} finally {
|
|
597
|
+
resolve();
|
|
598
|
+
process$1.exit(0);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
process$1.on("SIGINT", () => void onSignal());
|
|
602
|
+
process$1.on("SIGTERM", () => void onSignal());
|
|
603
|
+
process$1.on("SIGQUIT", () => void onSignal());
|
|
604
|
+
}
|
|
573
605
|
};
|
|
574
606
|
|
|
575
607
|
//#endregion
|
|
@@ -1270,13 +1302,36 @@ var ConsumerOutputBuilderLoader = class {
|
|
|
1270
1302
|
* Resolves TCP PostgreSQL vs PGlite vs none from env + {@link CodemationConfig} (same rules as the host runtime).
|
|
1271
1303
|
*/
|
|
1272
1304
|
var ConsumerDatabaseConnectionResolver = class {
|
|
1273
|
-
resolver = new DatabasePersistenceResolver();
|
|
1274
1305
|
resolve(processEnv, config$1, consumerRoot) {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1306
|
+
const database = config$1.runtime?.database;
|
|
1307
|
+
if (!database) return { kind: "none" };
|
|
1308
|
+
if (this.resolveDatabaseKind(database.kind, database.url, processEnv) === "postgresql") {
|
|
1309
|
+
const databaseUrl = database.url?.trim() ?? "";
|
|
1310
|
+
if (!databaseUrl) throw new Error("runtime.database.kind is \"postgresql\" but no database URL was set (runtime.database.url).");
|
|
1311
|
+
return {
|
|
1312
|
+
kind: "postgresql",
|
|
1313
|
+
databaseUrl
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
1316
|
+
return {
|
|
1317
|
+
kind: "pglite",
|
|
1318
|
+
dataDir: this.resolvePgliteDataDir(database.pgliteDataDir, processEnv, consumerRoot)
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
resolveDatabaseKind(configuredKind, databaseUrl, env) {
|
|
1322
|
+
const kindFromEnv = env.CODEMATION_DATABASE_KIND?.trim();
|
|
1323
|
+
if (kindFromEnv === "postgresql" || kindFromEnv === "pglite") return kindFromEnv;
|
|
1324
|
+
if (configuredKind) return configuredKind;
|
|
1325
|
+
const trimmedUrl = databaseUrl?.trim();
|
|
1326
|
+
if (trimmedUrl && (trimmedUrl.startsWith("postgresql://") || trimmedUrl.startsWith("postgres://"))) return "postgresql";
|
|
1327
|
+
return "pglite";
|
|
1328
|
+
}
|
|
1329
|
+
resolvePgliteDataDir(configuredPath, env, consumerRoot) {
|
|
1330
|
+
const envPath = env.CODEMATION_PGLITE_DATA_DIR?.trim();
|
|
1331
|
+
if (envPath && envPath.length > 0) return path.isAbsolute(envPath) ? envPath : path.resolve(consumerRoot, envPath);
|
|
1332
|
+
const trimmedConfiguredPath = configuredPath?.trim();
|
|
1333
|
+
if (trimmedConfiguredPath && trimmedConfiguredPath.length > 0) return path.isAbsolute(trimmedConfiguredPath) ? trimmedConfiguredPath : path.resolve(consumerRoot, trimmedConfiguredPath);
|
|
1334
|
+
return path.resolve(consumerRoot, ".codemation", "pglite");
|
|
1280
1335
|
}
|
|
1281
1336
|
};
|
|
1282
1337
|
|
|
@@ -1352,7 +1407,7 @@ var HostPackageRootResolver = class {
|
|
|
1352
1407
|
//#endregion
|
|
1353
1408
|
//#region src/dev/DevBootstrapSummaryFetcher.ts
|
|
1354
1409
|
/**
|
|
1355
|
-
* Fetches {@link DevBootstrapSummaryJson} from the
|
|
1410
|
+
* Fetches {@link DevBootstrapSummaryJson} from the stable CLI-owned dev endpoint.
|
|
1356
1411
|
*/
|
|
1357
1412
|
var DevBootstrapSummaryFetcher = class {
|
|
1358
1413
|
async fetch(gatewayBaseUrl) {
|
|
@@ -1502,6 +1557,776 @@ var DevConsumerPublishBootstrap = class {
|
|
|
1502
1557
|
}
|
|
1503
1558
|
};
|
|
1504
1559
|
|
|
1560
|
+
//#endregion
|
|
1561
|
+
//#region src/dev/CliDevProxyServer.ts
|
|
1562
|
+
var CliDevProxyServer = class {
|
|
1563
|
+
proxy = httpProxy.createProxyServer({
|
|
1564
|
+
ws: true,
|
|
1565
|
+
xfwd: true
|
|
1566
|
+
});
|
|
1567
|
+
devClients = /* @__PURE__ */ new Set();
|
|
1568
|
+
devWss = new WebSocketServer({ noServer: true });
|
|
1569
|
+
workflowClients = /* @__PURE__ */ new Set();
|
|
1570
|
+
workflowWss = new WebSocketServer({ noServer: true });
|
|
1571
|
+
roomIdsByWorkflowClient = /* @__PURE__ */ new Map();
|
|
1572
|
+
workflowClientCountByRoomId = /* @__PURE__ */ new Map();
|
|
1573
|
+
activeRuntime = null;
|
|
1574
|
+
activeBuildStatus = "idle";
|
|
1575
|
+
childWorkflowSocket = null;
|
|
1576
|
+
server = null;
|
|
1577
|
+
uiProxyTarget = null;
|
|
1578
|
+
constructor(listenPort, listenPortConflictDescriber) {
|
|
1579
|
+
this.listenPort = listenPort;
|
|
1580
|
+
this.listenPortConflictDescriber = listenPortConflictDescriber;
|
|
1581
|
+
}
|
|
1582
|
+
async start() {
|
|
1583
|
+
if (this.server) return;
|
|
1584
|
+
this.bindDevWebSocket();
|
|
1585
|
+
this.bindWorkflowWebSocket();
|
|
1586
|
+
this.proxy.on("error", (error, _req, res) => {
|
|
1587
|
+
if (res && "writeHead" in res && typeof res.writeHead === "function") {
|
|
1588
|
+
const serverResponse = res;
|
|
1589
|
+
if (!serverResponse.headersSent) {
|
|
1590
|
+
serverResponse.writeHead(502, { "content-type": "text/plain" });
|
|
1591
|
+
serverResponse.end(`Bad gateway: ${error.message}`);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
});
|
|
1595
|
+
const server = createServer((req, res) => {
|
|
1596
|
+
this.handleHttpRequest(req, res);
|
|
1597
|
+
});
|
|
1598
|
+
server.on("upgrade", (request, socket, head) => {
|
|
1599
|
+
this.handleUpgrade(request, socket, head);
|
|
1600
|
+
});
|
|
1601
|
+
await new Promise((resolve, reject) => {
|
|
1602
|
+
server.once("error", (error) => {
|
|
1603
|
+
this.rejectListenError(error, reject);
|
|
1604
|
+
});
|
|
1605
|
+
server.listen(this.listenPort, "127.0.0.1", () => {
|
|
1606
|
+
resolve();
|
|
1607
|
+
});
|
|
1608
|
+
});
|
|
1609
|
+
this.server = server;
|
|
1610
|
+
}
|
|
1611
|
+
async stop() {
|
|
1612
|
+
await this.disconnectChildWorkflowSocket();
|
|
1613
|
+
this.activeRuntime = null;
|
|
1614
|
+
const server = this.server;
|
|
1615
|
+
this.server = null;
|
|
1616
|
+
for (const client of this.devClients) client.terminate();
|
|
1617
|
+
this.devClients.clear();
|
|
1618
|
+
for (const client of this.workflowClients) client.terminate();
|
|
1619
|
+
this.workflowClients.clear();
|
|
1620
|
+
this.roomIdsByWorkflowClient.clear();
|
|
1621
|
+
this.workflowClientCountByRoomId.clear();
|
|
1622
|
+
if (!server) return;
|
|
1623
|
+
await new Promise((resolve, reject) => {
|
|
1624
|
+
server.close((error) => {
|
|
1625
|
+
if (error) {
|
|
1626
|
+
reject(error);
|
|
1627
|
+
return;
|
|
1628
|
+
}
|
|
1629
|
+
resolve();
|
|
1630
|
+
});
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
setUiProxyTarget(target) {
|
|
1634
|
+
this.uiProxyTarget = target?.trim() ? target.trim() : null;
|
|
1635
|
+
}
|
|
1636
|
+
async activateRuntime(target) {
|
|
1637
|
+
this.activeRuntime = target;
|
|
1638
|
+
await this.connectChildWorkflowSocket();
|
|
1639
|
+
}
|
|
1640
|
+
setBuildStatus(status) {
|
|
1641
|
+
this.activeBuildStatus = status;
|
|
1642
|
+
}
|
|
1643
|
+
broadcastBuildStarted() {
|
|
1644
|
+
this.broadcastDev({ kind: "devBuildStarted" });
|
|
1645
|
+
this.broadcastWorkflowLifecycleToSubscribedRooms((roomId) => ({
|
|
1646
|
+
kind: "devBuildStarted",
|
|
1647
|
+
workflowId: roomId
|
|
1648
|
+
}));
|
|
1649
|
+
}
|
|
1650
|
+
broadcastBuildCompleted(buildVersion) {
|
|
1651
|
+
this.broadcastDev({
|
|
1652
|
+
kind: "devBuildCompleted",
|
|
1653
|
+
buildVersion
|
|
1654
|
+
});
|
|
1655
|
+
this.broadcastWorkflowLifecycleToSubscribedRooms((roomId) => ({
|
|
1656
|
+
kind: "devBuildCompleted",
|
|
1657
|
+
workflowId: roomId,
|
|
1658
|
+
buildVersion
|
|
1659
|
+
}));
|
|
1660
|
+
}
|
|
1661
|
+
broadcastBuildFailed(message) {
|
|
1662
|
+
this.broadcastDev({
|
|
1663
|
+
kind: "devBuildFailed",
|
|
1664
|
+
message
|
|
1665
|
+
});
|
|
1666
|
+
this.broadcastWorkflowLifecycleToSubscribedRooms((roomId) => ({
|
|
1667
|
+
kind: "devBuildFailed",
|
|
1668
|
+
workflowId: roomId,
|
|
1669
|
+
message
|
|
1670
|
+
}));
|
|
1671
|
+
}
|
|
1672
|
+
bindDevWebSocket() {
|
|
1673
|
+
this.devWss.on("connection", (socket) => {
|
|
1674
|
+
this.devClients.add(socket);
|
|
1675
|
+
socket.on("close", () => {
|
|
1676
|
+
this.devClients.delete(socket);
|
|
1677
|
+
});
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1680
|
+
bindWorkflowWebSocket() {
|
|
1681
|
+
this.workflowWss.on("connection", (socket) => {
|
|
1682
|
+
this.connectWorkflowClient(socket);
|
|
1683
|
+
});
|
|
1684
|
+
}
|
|
1685
|
+
async handleHttpRequest(req, res) {
|
|
1686
|
+
const pathname = this.safePathname(req.url ?? "");
|
|
1687
|
+
const uiProxyTarget = this.uiProxyTarget;
|
|
1688
|
+
if (pathname === "/api/dev/health" && req.method === "GET") {
|
|
1689
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
1690
|
+
res.end(JSON.stringify({
|
|
1691
|
+
ok: true,
|
|
1692
|
+
runtime: { status: this.activeRuntime ? this.activeBuildStatus === "building" ? "building" : "ready" : "stopped" }
|
|
1693
|
+
}));
|
|
1694
|
+
return;
|
|
1695
|
+
}
|
|
1696
|
+
if (uiProxyTarget && pathname.startsWith("/api/auth/")) {
|
|
1697
|
+
this.proxy.web(req, res, { target: uiProxyTarget.replace(/\/$/, "") });
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if (pathname.startsWith("/api/")) {
|
|
1701
|
+
if (this.activeBuildStatus === "building" || !this.activeRuntime) {
|
|
1702
|
+
res.writeHead(503, { "content-type": "text/plain" });
|
|
1703
|
+
res.end("Runtime is rebuilding.");
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
this.proxy.web(req, res, { target: `http://127.0.0.1:${this.activeRuntime.httpPort}` });
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
if (uiProxyTarget) {
|
|
1710
|
+
this.proxy.web(req, res, { target: uiProxyTarget.replace(/\/$/, "") });
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
res.writeHead(404, { "content-type": "text/plain" });
|
|
1714
|
+
res.end("Not found.");
|
|
1715
|
+
}
|
|
1716
|
+
handleUpgrade(request, socket, head) {
|
|
1717
|
+
const pathname = this.safePathname(request.url ?? "");
|
|
1718
|
+
if (pathname === ApiPaths.devGatewaySocket()) {
|
|
1719
|
+
this.devWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1720
|
+
this.devWss.emit("connection", ws, request);
|
|
1721
|
+
});
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
if (pathname === ApiPaths.workflowWebsocket()) {
|
|
1725
|
+
this.workflowWss.handleUpgrade(request, socket, head, (ws) => {
|
|
1726
|
+
this.workflowWss.emit("connection", ws, request);
|
|
1727
|
+
});
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
const uiProxyTarget = this.uiProxyTarget;
|
|
1731
|
+
if (uiProxyTarget && !pathname.startsWith("/api/")) {
|
|
1732
|
+
this.proxy.ws(request, socket, head, { target: uiProxyTarget.replace(/\/$/, "") });
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
socket.destroy();
|
|
1736
|
+
}
|
|
1737
|
+
safePathname(url) {
|
|
1738
|
+
try {
|
|
1739
|
+
return new URL(url, "http://127.0.0.1").pathname;
|
|
1740
|
+
} catch {
|
|
1741
|
+
return url.split("?")[0] ?? url;
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
async rejectListenError(error, reject) {
|
|
1745
|
+
if (error.code !== "EADDRINUSE") {
|
|
1746
|
+
reject(error);
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1749
|
+
const description = await this.listenPortConflictDescriber.describeLoopbackPort(this.listenPort);
|
|
1750
|
+
const baseMessage = `Dev gateway port ${this.listenPort} is already in use on 127.0.0.1.`;
|
|
1751
|
+
const suffix = description === null ? " Stop the process using that port or change the configured Codemation dev port." : ` Listener: ${description}. Stop that process or change the configured Codemation dev port.`;
|
|
1752
|
+
reject(new Error(`${baseMessage}${suffix}`, { cause: error instanceof Error ? error : void 0 }));
|
|
1753
|
+
}
|
|
1754
|
+
broadcastDev(message) {
|
|
1755
|
+
const text = JSON.stringify(message);
|
|
1756
|
+
for (const client of this.devClients) if (client.readyState === WebSocket.OPEN) client.send(text);
|
|
1757
|
+
}
|
|
1758
|
+
broadcastWorkflowLifecycleToSubscribedRooms(createMessage) {
|
|
1759
|
+
for (const roomId of this.workflowClientCountByRoomId.keys()) this.broadcastWorkflowTextToRoom(roomId, JSON.stringify(createMessage(roomId)));
|
|
1760
|
+
}
|
|
1761
|
+
async connectWorkflowClient(socket) {
|
|
1762
|
+
this.workflowClients.add(socket);
|
|
1763
|
+
this.roomIdsByWorkflowClient.set(socket, /* @__PURE__ */ new Set());
|
|
1764
|
+
socket.send(JSON.stringify({ kind: "ready" }));
|
|
1765
|
+
socket.on("message", (rawData) => {
|
|
1766
|
+
this.handleWorkflowClientMessage(socket, rawData);
|
|
1767
|
+
});
|
|
1768
|
+
socket.on("close", () => {
|
|
1769
|
+
this.disconnectWorkflowClient(socket);
|
|
1770
|
+
});
|
|
1771
|
+
socket.on("error", () => {
|
|
1772
|
+
this.disconnectWorkflowClient(socket);
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
disconnectWorkflowClient(socket) {
|
|
1776
|
+
const roomIds = this.roomIdsByWorkflowClient.get(socket);
|
|
1777
|
+
if (roomIds) for (const roomId of roomIds) this.releaseWorkflowRoom(roomId);
|
|
1778
|
+
this.roomIdsByWorkflowClient.delete(socket);
|
|
1779
|
+
this.workflowClients.delete(socket);
|
|
1780
|
+
}
|
|
1781
|
+
async handleWorkflowClientMessage(socket, rawData) {
|
|
1782
|
+
try {
|
|
1783
|
+
const message = this.parseWorkflowClientMessage(rawData);
|
|
1784
|
+
if (message.kind === "subscribe") {
|
|
1785
|
+
const roomIds$1 = this.roomIdsByWorkflowClient.get(socket);
|
|
1786
|
+
if (!roomIds$1) return;
|
|
1787
|
+
if (!roomIds$1.has(message.roomId)) {
|
|
1788
|
+
roomIds$1.add(message.roomId);
|
|
1789
|
+
this.retainWorkflowRoom(message.roomId);
|
|
1790
|
+
}
|
|
1791
|
+
socket.send(JSON.stringify({
|
|
1792
|
+
kind: "subscribed",
|
|
1793
|
+
roomId: message.roomId
|
|
1794
|
+
}));
|
|
1795
|
+
return;
|
|
1796
|
+
}
|
|
1797
|
+
const roomIds = this.roomIdsByWorkflowClient.get(socket);
|
|
1798
|
+
if (!roomIds) return;
|
|
1799
|
+
if (roomIds.delete(message.roomId)) this.releaseWorkflowRoom(message.roomId);
|
|
1800
|
+
socket.send(JSON.stringify({
|
|
1801
|
+
kind: "unsubscribed",
|
|
1802
|
+
roomId: message.roomId
|
|
1803
|
+
}));
|
|
1804
|
+
} catch (error) {
|
|
1805
|
+
const exception = error instanceof Error ? error : new Error(String(error));
|
|
1806
|
+
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({
|
|
1807
|
+
kind: "error",
|
|
1808
|
+
message: exception.message
|
|
1809
|
+
}));
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
parseWorkflowClientMessage(rawData) {
|
|
1813
|
+
const value = typeof rawData === "string" ? rawData : Buffer.isBuffer(rawData) ? rawData.toString("utf8") : "";
|
|
1814
|
+
const message = JSON.parse(value);
|
|
1815
|
+
if (message.kind === "subscribe" && typeof message.roomId === "string") return {
|
|
1816
|
+
kind: "subscribe",
|
|
1817
|
+
roomId: message.roomId
|
|
1818
|
+
};
|
|
1819
|
+
if (message.kind === "unsubscribe" && typeof message.roomId === "string") return {
|
|
1820
|
+
kind: "unsubscribe",
|
|
1821
|
+
roomId: message.roomId
|
|
1822
|
+
};
|
|
1823
|
+
throw new Error("Unsupported websocket client message.");
|
|
1824
|
+
}
|
|
1825
|
+
retainWorkflowRoom(roomId) {
|
|
1826
|
+
const nextCount = (this.workflowClientCountByRoomId.get(roomId) ?? 0) + 1;
|
|
1827
|
+
this.workflowClientCountByRoomId.set(roomId, nextCount);
|
|
1828
|
+
if (nextCount === 1) this.sendToChildWorkflowSocket({
|
|
1829
|
+
kind: "subscribe",
|
|
1830
|
+
roomId
|
|
1831
|
+
});
|
|
1832
|
+
}
|
|
1833
|
+
releaseWorkflowRoom(roomId) {
|
|
1834
|
+
const currentCount = this.workflowClientCountByRoomId.get(roomId) ?? 0;
|
|
1835
|
+
if (currentCount <= 1) {
|
|
1836
|
+
this.workflowClientCountByRoomId.delete(roomId);
|
|
1837
|
+
this.sendToChildWorkflowSocket({
|
|
1838
|
+
kind: "unsubscribe",
|
|
1839
|
+
roomId
|
|
1840
|
+
});
|
|
1841
|
+
return;
|
|
1842
|
+
}
|
|
1843
|
+
this.workflowClientCountByRoomId.set(roomId, currentCount - 1);
|
|
1844
|
+
}
|
|
1845
|
+
sendToChildWorkflowSocket(message) {
|
|
1846
|
+
if (!this.childWorkflowSocket || this.childWorkflowSocket.readyState !== WebSocket.OPEN) return;
|
|
1847
|
+
this.childWorkflowSocket.send(JSON.stringify(message));
|
|
1848
|
+
}
|
|
1849
|
+
async connectChildWorkflowSocket() {
|
|
1850
|
+
await this.disconnectChildWorkflowSocket();
|
|
1851
|
+
if (!this.activeRuntime || this.activeBuildStatus === "building") return;
|
|
1852
|
+
const childWorkflowSocket = await this.openChildWorkflowSocket(this.activeRuntime.workflowWebSocketPort);
|
|
1853
|
+
this.childWorkflowSocket = childWorkflowSocket;
|
|
1854
|
+
childWorkflowSocket.on("message", (rawData) => {
|
|
1855
|
+
this.handleChildWorkflowSocketMessage(rawData);
|
|
1856
|
+
});
|
|
1857
|
+
childWorkflowSocket.on("close", () => {
|
|
1858
|
+
if (this.childWorkflowSocket === childWorkflowSocket) this.childWorkflowSocket = null;
|
|
1859
|
+
});
|
|
1860
|
+
childWorkflowSocket.on("error", () => {
|
|
1861
|
+
if (this.childWorkflowSocket === childWorkflowSocket) this.childWorkflowSocket = null;
|
|
1862
|
+
});
|
|
1863
|
+
for (const roomId of this.workflowClientCountByRoomId.keys()) this.sendToChildWorkflowSocket({
|
|
1864
|
+
kind: "subscribe",
|
|
1865
|
+
roomId
|
|
1866
|
+
});
|
|
1867
|
+
}
|
|
1868
|
+
openChildWorkflowSocket(workflowWebSocketPort) {
|
|
1869
|
+
return new Promise((resolve, reject) => {
|
|
1870
|
+
const socket = new WebSocket(`ws://127.0.0.1:${workflowWebSocketPort}${ApiPaths.workflowWebsocket()}`);
|
|
1871
|
+
socket.once("open", () => {
|
|
1872
|
+
resolve(socket);
|
|
1873
|
+
});
|
|
1874
|
+
socket.once("error", (error) => {
|
|
1875
|
+
socket.close();
|
|
1876
|
+
reject(error);
|
|
1877
|
+
});
|
|
1878
|
+
});
|
|
1879
|
+
}
|
|
1880
|
+
async disconnectChildWorkflowSocket() {
|
|
1881
|
+
if (!this.childWorkflowSocket) return;
|
|
1882
|
+
const socket = this.childWorkflowSocket;
|
|
1883
|
+
this.childWorkflowSocket = null;
|
|
1884
|
+
await new Promise((resolve) => {
|
|
1885
|
+
socket.once("close", () => {
|
|
1886
|
+
resolve();
|
|
1887
|
+
});
|
|
1888
|
+
socket.close();
|
|
1889
|
+
});
|
|
1890
|
+
}
|
|
1891
|
+
handleChildWorkflowSocketMessage(rawData) {
|
|
1892
|
+
const text = typeof rawData === "string" ? rawData : Buffer.isBuffer(rawData) ? rawData.toString("utf8") : "";
|
|
1893
|
+
if (text.trim().length === 0) return;
|
|
1894
|
+
try {
|
|
1895
|
+
const message = JSON.parse(text);
|
|
1896
|
+
if (message.kind === "event" && typeof message.event?.workflowId === "string") {
|
|
1897
|
+
this.broadcastWorkflowTextToRoom(message.event.workflowId, text);
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1900
|
+
if ((message.kind === "workflowChanged" || message.kind === "devBuildStarted" || message.kind === "devBuildCompleted" || message.kind === "devBuildFailed") && typeof message.workflowId === "string") {
|
|
1901
|
+
this.broadcastWorkflowTextToRoom(message.workflowId, text);
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
if (message.kind === "error" && typeof message.message === "string") this.broadcastWorkflowTextToAll(text);
|
|
1905
|
+
} catch {}
|
|
1906
|
+
}
|
|
1907
|
+
broadcastWorkflowTextToRoom(roomId, text) {
|
|
1908
|
+
for (const [client, roomIds] of this.roomIdsByWorkflowClient) {
|
|
1909
|
+
if (client.readyState !== WebSocket.OPEN || !roomIds.has(roomId)) continue;
|
|
1910
|
+
client.send(text);
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
broadcastWorkflowTextToAll(text) {
|
|
1914
|
+
for (const client of this.workflowClients) if (client.readyState === WebSocket.OPEN) client.send(text);
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1917
|
+
|
|
1918
|
+
//#endregion
|
|
1919
|
+
//#region src/dev/ListenPortConflictDescriber.ts
|
|
1920
|
+
var ListenPortConflictDescriber = class {
|
|
1921
|
+
constructor(platform = process$1.platform) {
|
|
1922
|
+
this.platform = platform;
|
|
1923
|
+
}
|
|
1924
|
+
async describeLoopbackPort(port) {
|
|
1925
|
+
if (!Number.isInteger(port) || port <= 0) return null;
|
|
1926
|
+
if (this.platform !== "linux" && this.platform !== "darwin") return null;
|
|
1927
|
+
const raw = await this.readLsofOutput(port);
|
|
1928
|
+
if (raw === null) return null;
|
|
1929
|
+
const occupants = this.parseLsofOutput(raw);
|
|
1930
|
+
if (occupants.length === 0) return null;
|
|
1931
|
+
return occupants.map((occupant) => `pid=${occupant.pid} command=${occupant.command} endpoint=${occupant.endpoint}`).join("; ");
|
|
1932
|
+
}
|
|
1933
|
+
async readLsofOutput(port) {
|
|
1934
|
+
try {
|
|
1935
|
+
return await new Promise((resolve, reject) => {
|
|
1936
|
+
execFile("lsof", [
|
|
1937
|
+
"-nP",
|
|
1938
|
+
`-iTCP:${port}`,
|
|
1939
|
+
"-sTCP:LISTEN",
|
|
1940
|
+
"-Fpcn"
|
|
1941
|
+
], (error, stdout) => {
|
|
1942
|
+
if (error) {
|
|
1943
|
+
reject(error);
|
|
1944
|
+
return;
|
|
1945
|
+
}
|
|
1946
|
+
resolve(stdout);
|
|
1947
|
+
});
|
|
1948
|
+
});
|
|
1949
|
+
} catch {
|
|
1950
|
+
return null;
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
parseLsofOutput(raw) {
|
|
1954
|
+
const occupants = [];
|
|
1955
|
+
let currentPid = null;
|
|
1956
|
+
let currentCommand = null;
|
|
1957
|
+
for (const line of raw.split("\n")) {
|
|
1958
|
+
if (line.length < 2) continue;
|
|
1959
|
+
const prefix = line[0];
|
|
1960
|
+
const value = line.slice(1).trim();
|
|
1961
|
+
if (prefix === "p") {
|
|
1962
|
+
currentPid = Number.parseInt(value, 10);
|
|
1963
|
+
currentCommand = null;
|
|
1964
|
+
continue;
|
|
1965
|
+
}
|
|
1966
|
+
if (prefix === "c") {
|
|
1967
|
+
currentCommand = value;
|
|
1968
|
+
continue;
|
|
1969
|
+
}
|
|
1970
|
+
if (prefix === "n" && currentPid !== null && currentCommand !== null) occupants.push({
|
|
1971
|
+
pid: currentPid,
|
|
1972
|
+
command: currentCommand,
|
|
1973
|
+
endpoint: value
|
|
1974
|
+
});
|
|
1975
|
+
}
|
|
1976
|
+
return occupants;
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1980
|
+
//#endregion
|
|
1981
|
+
//#region src/dev/CliDevProxyServerFactory.ts
|
|
1982
|
+
var CliDevProxyServerFactory = class {
|
|
1983
|
+
listenPortConflictDescriber = new ListenPortConflictDescriber();
|
|
1984
|
+
create(gatewayPort) {
|
|
1985
|
+
return new CliDevProxyServer(gatewayPort, this.listenPortConflictDescriber);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
|
|
1989
|
+
//#endregion
|
|
1990
|
+
//#region ../host/src/presentation/server/CodemationTsyringeParamInfoReader.ts
|
|
1991
|
+
var CodemationTsyringeParamInfoReader = class {
|
|
1992
|
+
static injectionTokenMetadataKey = "injectionTokens";
|
|
1993
|
+
static designParamTypesMetadataKey = "design:paramtypes";
|
|
1994
|
+
static read(target) {
|
|
1995
|
+
const designParamTypes = this.readDesignParamTypes(target);
|
|
1996
|
+
const injectionTokens = this.readInjectionTokens(target);
|
|
1997
|
+
Object.keys(injectionTokens).forEach((key) => {
|
|
1998
|
+
designParamTypes[Number(key)] = injectionTokens[key];
|
|
1999
|
+
});
|
|
2000
|
+
return designParamTypes;
|
|
2001
|
+
}
|
|
2002
|
+
static readDesignParamTypes(target) {
|
|
2003
|
+
const reflected = Reflect.getMetadata?.(this.designParamTypesMetadataKey, target);
|
|
2004
|
+
return Array.isArray(reflected) ? [...reflected] : [];
|
|
2005
|
+
}
|
|
2006
|
+
static readInjectionTokens(target) {
|
|
2007
|
+
const reflected = Reflect.getOwnMetadata?.(this.injectionTokenMetadataKey, target);
|
|
2008
|
+
if (!reflected || typeof reflected !== "object") return {};
|
|
2009
|
+
return reflected;
|
|
2010
|
+
}
|
|
2011
|
+
};
|
|
2012
|
+
|
|
2013
|
+
//#endregion
|
|
2014
|
+
//#region ../host/src/presentation/server/CodemationTsyringeTypeInfoRegistrar.ts
|
|
2015
|
+
var CodemationTsyringeTypeInfoRegistrar = class {
|
|
2016
|
+
visitedTokens = /* @__PURE__ */ new Set();
|
|
2017
|
+
visitedConfigObjects = /* @__PURE__ */ new Set();
|
|
2018
|
+
constructor(container) {
|
|
2019
|
+
this.container = container;
|
|
2020
|
+
}
|
|
2021
|
+
registerWorkflowDefinitions(workflows) {
|
|
2022
|
+
for (const workflow of workflows) for (const node of workflow.nodes) {
|
|
2023
|
+
this.registerTypeToken(node.type);
|
|
2024
|
+
this.registerConfigTokens(node.config);
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
registerTypeToken(token) {
|
|
2028
|
+
if (typeof token !== "function" || this.visitedTokens.has(token)) return;
|
|
2029
|
+
this.visitedTokens.add(token);
|
|
2030
|
+
const paramInfo = CodemationTsyringeParamInfoReader.read(token);
|
|
2031
|
+
for (const dependency of paramInfo) this.registerDependency(dependency);
|
|
2032
|
+
this.registerFactoryProvider(token, paramInfo);
|
|
2033
|
+
}
|
|
2034
|
+
registerDependency(dependency) {
|
|
2035
|
+
const token = this.resolveDependencyToken(dependency);
|
|
2036
|
+
if (typeof token !== "function") return;
|
|
2037
|
+
if (!this.container.isRegistered(token, true)) return;
|
|
2038
|
+
this.registerTypeToken(token);
|
|
2039
|
+
}
|
|
2040
|
+
registerConfigTokens(value) {
|
|
2041
|
+
if (Array.isArray(value)) {
|
|
2042
|
+
value.forEach((entry) => this.registerConfigTokens(entry));
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
if (!value || typeof value !== "object") return;
|
|
2046
|
+
if (this.visitedConfigObjects.has(value)) return;
|
|
2047
|
+
this.visitedConfigObjects.add(value);
|
|
2048
|
+
if ("type" in value && typeof value.type === "function") this.registerTypeToken(value.type);
|
|
2049
|
+
Object.values(value).forEach((entry) => this.registerConfigTokens(entry));
|
|
2050
|
+
}
|
|
2051
|
+
registerFactoryProvider(token, paramInfo) {
|
|
2052
|
+
if (this.container.isRegistered(token, true)) return;
|
|
2053
|
+
const classToken = token;
|
|
2054
|
+
const constructorToken = token;
|
|
2055
|
+
this.container.register(classToken, { useFactory: (dependencyContainer) => {
|
|
2056
|
+
return new constructorToken(...paramInfo.map((dependency) => this.resolveFactoryDependency(dependencyContainer, dependency)));
|
|
2057
|
+
} });
|
|
2058
|
+
}
|
|
2059
|
+
resolveDependencyToken(dependency) {
|
|
2060
|
+
if (this.isInjectionDescriptor(dependency)) return dependency.token;
|
|
2061
|
+
return dependency;
|
|
2062
|
+
}
|
|
2063
|
+
resolveFactoryDependency(dependencyContainer, dependency) {
|
|
2064
|
+
const token = this.resolveDependencyToken(dependency);
|
|
2065
|
+
if (typeof token === "function") {
|
|
2066
|
+
if (dependencyContainer.isRegistered(token, true)) try {
|
|
2067
|
+
return dependencyContainer.resolve(token);
|
|
2068
|
+
} catch (error) {
|
|
2069
|
+
if (!this.isMissingTypeInfoError(error)) throw error;
|
|
2070
|
+
}
|
|
2071
|
+
this.registerTypeToken(token);
|
|
2072
|
+
return new token(...CodemationTsyringeParamInfoReader.read(token).map((entry) => this.resolveFactoryDependency(dependencyContainer, entry)));
|
|
2073
|
+
}
|
|
2074
|
+
return dependencyContainer.resolve(token);
|
|
2075
|
+
}
|
|
2076
|
+
isInjectionDescriptor(value) {
|
|
2077
|
+
return value !== null && typeof value === "object" && "token" in value;
|
|
2078
|
+
}
|
|
2079
|
+
isMissingTypeInfoError(error) {
|
|
2080
|
+
return error instanceof Error && error.message.includes("TypeInfo not known for");
|
|
2081
|
+
}
|
|
2082
|
+
};
|
|
2083
|
+
|
|
2084
|
+
//#endregion
|
|
2085
|
+
//#region src/dev/DevApiRuntimeHost.ts
|
|
2086
|
+
var DevApiRuntimeHost = class {
|
|
2087
|
+
pluginListMerger = new CodemationPluginListMerger();
|
|
2088
|
+
contextPromise = null;
|
|
2089
|
+
constructor(configLoader, pluginDiscovery, args) {
|
|
2090
|
+
this.configLoader = configLoader;
|
|
2091
|
+
this.pluginDiscovery = pluginDiscovery;
|
|
2092
|
+
this.args = args;
|
|
2093
|
+
}
|
|
2094
|
+
async prepare() {
|
|
2095
|
+
if (!this.contextPromise) this.contextPromise = this.createContext();
|
|
2096
|
+
return await this.contextPromise;
|
|
2097
|
+
}
|
|
2098
|
+
async stop() {
|
|
2099
|
+
const contextPromise = this.contextPromise;
|
|
2100
|
+
this.contextPromise = null;
|
|
2101
|
+
if (!contextPromise) return;
|
|
2102
|
+
await (await contextPromise).container.resolve(AppContainerLifecycle$1).stop();
|
|
2103
|
+
}
|
|
2104
|
+
async createContext() {
|
|
2105
|
+
const consumerRoot = path.resolve(this.args.consumerRoot);
|
|
2106
|
+
const repoRoot = await this.detectWorkspaceRoot(consumerRoot);
|
|
2107
|
+
const prismaCliOverride = await this.resolvePrismaCliOverride();
|
|
2108
|
+
const hostPackageRoot = path.resolve(repoRoot, "packages", "host");
|
|
2109
|
+
const env = { ...this.args.env };
|
|
2110
|
+
if (prismaCliOverride) env.CODEMATION_PRISMA_CLI_PATH = prismaCliOverride;
|
|
2111
|
+
env.CODEMATION_HOST_PACKAGE_ROOT = hostPackageRoot;
|
|
2112
|
+
env.CODEMATION_PRISMA_CONFIG_PATH = path.resolve(hostPackageRoot, "prisma.config.ts");
|
|
2113
|
+
env.CODEMATION_CONSUMER_ROOT = consumerRoot;
|
|
2114
|
+
const configResolution = await this.configLoader.load({
|
|
2115
|
+
consumerRoot,
|
|
2116
|
+
repoRoot,
|
|
2117
|
+
env
|
|
2118
|
+
});
|
|
2119
|
+
const discoveredPlugins = await this.loadDiscoveredPlugins(consumerRoot);
|
|
2120
|
+
const appConfig = {
|
|
2121
|
+
...configResolution.appConfig,
|
|
2122
|
+
env,
|
|
2123
|
+
plugins: discoveredPlugins.length > 0 ? this.pluginListMerger.merge(configResolution.appConfig.plugins, discoveredPlugins) : configResolution.appConfig.plugins
|
|
2124
|
+
};
|
|
2125
|
+
const container = await new AppContainerFactory$1().create({
|
|
2126
|
+
appConfig,
|
|
2127
|
+
sharedWorkflowWebsocketServer: null
|
|
2128
|
+
});
|
|
2129
|
+
new CodemationTsyringeTypeInfoRegistrar(container).registerWorkflowDefinitions(appConfig.workflows ?? []);
|
|
2130
|
+
await container.resolve(FrontendRuntime).start();
|
|
2131
|
+
return {
|
|
2132
|
+
buildVersion: this.createBuildVersion(),
|
|
2133
|
+
container,
|
|
2134
|
+
consumerRoot,
|
|
2135
|
+
repoRoot,
|
|
2136
|
+
workflowIds: appConfig.workflows.map((workflow) => workflow.id),
|
|
2137
|
+
workflowSources: appConfig.workflowSources
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
async loadDiscoveredPlugins(consumerRoot) {
|
|
2141
|
+
return (await this.pluginDiscovery.resolvePlugins(consumerRoot)).map((resolvedPackage) => resolvedPackage.plugin);
|
|
2142
|
+
}
|
|
2143
|
+
async detectWorkspaceRoot(startDirectory) {
|
|
2144
|
+
let currentDirectory = path.resolve(startDirectory);
|
|
2145
|
+
while (true) {
|
|
2146
|
+
if (await this.exists(path.resolve(currentDirectory, "pnpm-workspace.yaml"))) return currentDirectory;
|
|
2147
|
+
const parentDirectory = path.dirname(currentDirectory);
|
|
2148
|
+
if (parentDirectory === currentDirectory) return startDirectory;
|
|
2149
|
+
currentDirectory = parentDirectory;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
async resolvePrismaCliOverride() {
|
|
2153
|
+
const candidate = path.resolve(this.args.runtimeWorkingDirectory, "node_modules", "prisma", "build", "index.js");
|
|
2154
|
+
return await this.exists(candidate) ? candidate : null;
|
|
2155
|
+
}
|
|
2156
|
+
createBuildVersion() {
|
|
2157
|
+
return `${Date.now()}-${process$1.pid}`;
|
|
2158
|
+
}
|
|
2159
|
+
async exists(filePath) {
|
|
2160
|
+
try {
|
|
2161
|
+
await access(filePath);
|
|
2162
|
+
return true;
|
|
2163
|
+
} catch {
|
|
2164
|
+
return false;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
};
|
|
2168
|
+
|
|
2169
|
+
//#endregion
|
|
2170
|
+
//#region src/dev/DevApiRuntimeServer.ts
|
|
2171
|
+
var DevApiRuntimeServer = class {
|
|
2172
|
+
bootstrapLogger = new ServerLoggerFactory(logLevelPolicyFactory).create("codemation-cli.dev-runtime");
|
|
2173
|
+
server = null;
|
|
2174
|
+
constructor(httpPort, workflowWebSocketPort, host) {
|
|
2175
|
+
this.httpPort = httpPort;
|
|
2176
|
+
this.workflowWebSocketPort = workflowWebSocketPort;
|
|
2177
|
+
this.host = host;
|
|
2178
|
+
}
|
|
2179
|
+
async start() {
|
|
2180
|
+
const root = new Hono();
|
|
2181
|
+
root.get("/health", (c) => c.json({ ok: true }));
|
|
2182
|
+
root.all("*", async (c) => {
|
|
2183
|
+
return (await this.host.prepare()).container.resolve(CodemationHonoApiApp).fetch(c.req.raw);
|
|
2184
|
+
});
|
|
2185
|
+
await this.listen(root);
|
|
2186
|
+
try {
|
|
2187
|
+
return await this.host.prepare();
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
await this.stop();
|
|
2190
|
+
throw error;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
async stop() {
|
|
2194
|
+
const server = this.server;
|
|
2195
|
+
this.server = null;
|
|
2196
|
+
const failures = [];
|
|
2197
|
+
if (server) try {
|
|
2198
|
+
await this.closeServer(server);
|
|
2199
|
+
} catch (error) {
|
|
2200
|
+
failures.push(this.normalizeError(error));
|
|
2201
|
+
}
|
|
2202
|
+
try {
|
|
2203
|
+
await this.host.stop();
|
|
2204
|
+
} catch (error) {
|
|
2205
|
+
failures.push(this.normalizeError(error));
|
|
2206
|
+
}
|
|
2207
|
+
if (failures.length > 0) throw failures[0];
|
|
2208
|
+
}
|
|
2209
|
+
async listen(root) {
|
|
2210
|
+
if (this.server) return;
|
|
2211
|
+
await new Promise((resolve, reject) => {
|
|
2212
|
+
let resolved = false;
|
|
2213
|
+
const server = serve({
|
|
2214
|
+
fetch: root.fetch,
|
|
2215
|
+
port: this.httpPort,
|
|
2216
|
+
hostname: "127.0.0.1"
|
|
2217
|
+
}, () => {
|
|
2218
|
+
resolved = true;
|
|
2219
|
+
this.server = server;
|
|
2220
|
+
resolve();
|
|
2221
|
+
});
|
|
2222
|
+
server.on("error", (error) => {
|
|
2223
|
+
if (resolved) {
|
|
2224
|
+
this.bootstrapLogger.error("runtime HTTP server error", this.normalizeError(error));
|
|
2225
|
+
return;
|
|
2226
|
+
}
|
|
2227
|
+
reject(error);
|
|
2228
|
+
});
|
|
2229
|
+
});
|
|
2230
|
+
this.bootstrapLogger.debug(`runtime listening httpPort=${this.httpPort} workflowWebSocketPort=${this.workflowWebSocketPort}`);
|
|
2231
|
+
}
|
|
2232
|
+
closeServer(server) {
|
|
2233
|
+
return new Promise((resolve, reject) => {
|
|
2234
|
+
server.close((error) => {
|
|
2235
|
+
if (error) {
|
|
2236
|
+
reject(error);
|
|
2237
|
+
return;
|
|
2238
|
+
}
|
|
2239
|
+
resolve();
|
|
2240
|
+
});
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
normalizeError(error) {
|
|
2244
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
2245
|
+
}
|
|
2246
|
+
};
|
|
2247
|
+
|
|
2248
|
+
//#endregion
|
|
2249
|
+
//#region src/dev/DevApiRuntimeFactory.ts
|
|
2250
|
+
var DevApiRuntimeFactory = class {
|
|
2251
|
+
constructor(portAllocator, configLoader, pluginDiscovery) {
|
|
2252
|
+
this.portAllocator = portAllocator;
|
|
2253
|
+
this.configLoader = configLoader;
|
|
2254
|
+
this.pluginDiscovery = pluginDiscovery;
|
|
2255
|
+
}
|
|
2256
|
+
async create(args) {
|
|
2257
|
+
const httpPort = await this.portAllocator.allocate();
|
|
2258
|
+
const workflowWebSocketPort = await this.portAllocator.allocate();
|
|
2259
|
+
const runtime = new DevApiRuntimeServer(httpPort, workflowWebSocketPort, new DevApiRuntimeHost(this.configLoader, this.pluginDiscovery, {
|
|
2260
|
+
consumerRoot: args.consumerRoot,
|
|
2261
|
+
env: {
|
|
2262
|
+
...args.env,
|
|
2263
|
+
CODEMATION_WS_PORT: String(workflowWebSocketPort),
|
|
2264
|
+
NEXT_PUBLIC_CODEMATION_WS_PORT: String(workflowWebSocketPort)
|
|
2265
|
+
},
|
|
2266
|
+
runtimeWorkingDirectory: args.runtimeWorkingDirectory
|
|
2267
|
+
}));
|
|
2268
|
+
const context = await runtime.start();
|
|
2269
|
+
return {
|
|
2270
|
+
buildVersion: context.buildVersion,
|
|
2271
|
+
httpPort,
|
|
2272
|
+
stop: async () => {
|
|
2273
|
+
await runtime.stop();
|
|
2274
|
+
},
|
|
2275
|
+
workflowIds: context.workflowIds,
|
|
2276
|
+
workflowWebSocketPort
|
|
2277
|
+
};
|
|
2278
|
+
}
|
|
2279
|
+
};
|
|
2280
|
+
|
|
2281
|
+
//#endregion
|
|
2282
|
+
//#region src/dev/DevRebuildQueue.ts
|
|
2283
|
+
var DevRebuildQueue = class {
|
|
2284
|
+
pendingRequest = null;
|
|
2285
|
+
drainPromise = null;
|
|
2286
|
+
constructor(handler) {
|
|
2287
|
+
this.handler = handler;
|
|
2288
|
+
}
|
|
2289
|
+
async enqueue(request) {
|
|
2290
|
+
this.pendingRequest = this.mergePendingRequest(this.pendingRequest, request);
|
|
2291
|
+
if (!this.drainPromise) this.drainPromise = this.drain();
|
|
2292
|
+
return await this.drainPromise;
|
|
2293
|
+
}
|
|
2294
|
+
async drain() {
|
|
2295
|
+
try {
|
|
2296
|
+
while (this.pendingRequest) {
|
|
2297
|
+
const nextRequest = this.pendingRequest;
|
|
2298
|
+
this.pendingRequest = null;
|
|
2299
|
+
await this.handler.run(nextRequest);
|
|
2300
|
+
}
|
|
2301
|
+
} finally {
|
|
2302
|
+
this.drainPromise = null;
|
|
2303
|
+
if (this.pendingRequest) {
|
|
2304
|
+
this.drainPromise = this.drain();
|
|
2305
|
+
await this.drainPromise;
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
mergePendingRequest(current, next) {
|
|
2310
|
+
if (!current) return {
|
|
2311
|
+
...next,
|
|
2312
|
+
changedPaths: [...next.changedPaths]
|
|
2313
|
+
};
|
|
2314
|
+
return {
|
|
2315
|
+
changedPaths: [...new Set([...current.changedPaths, ...next.changedPaths])],
|
|
2316
|
+
shouldRepublishConsumerOutput: current.shouldRepublishConsumerOutput || next.shouldRepublishConsumerOutput,
|
|
2317
|
+
shouldRestartUi: current.shouldRestartUi || next.shouldRestartUi
|
|
2318
|
+
};
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
|
|
2322
|
+
//#endregion
|
|
2323
|
+
//#region src/dev/DevRebuildQueueFactory.ts
|
|
2324
|
+
var DevRebuildQueueFactory = class {
|
|
2325
|
+
create(handler) {
|
|
2326
|
+
return new DevRebuildQueue(handler);
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
|
|
1505
2330
|
//#endregion
|
|
1506
2331
|
//#region src/runtime/ListenPortResolver.ts
|
|
1507
2332
|
/**
|
|
@@ -1536,36 +2361,13 @@ var SourceMapNodeOptions = class {
|
|
|
1536
2361
|
}
|
|
1537
2362
|
};
|
|
1538
2363
|
|
|
1539
|
-
//#endregion
|
|
1540
|
-
//#region src/dev/DevelopmentGatewayNotifier.ts
|
|
1541
|
-
var DevelopmentGatewayNotifier = class {
|
|
1542
|
-
constructor(cliLogger) {
|
|
1543
|
-
this.cliLogger = cliLogger;
|
|
1544
|
-
}
|
|
1545
|
-
async notify(args) {
|
|
1546
|
-
const targetUrl = `${args.gatewayBaseUrl.replace(/\/$/, "")}${ApiPaths.devGatewayNotify()}`;
|
|
1547
|
-
try {
|
|
1548
|
-
const response = await fetch(targetUrl, {
|
|
1549
|
-
method: "POST",
|
|
1550
|
-
headers: {
|
|
1551
|
-
"content-type": "application/json",
|
|
1552
|
-
"x-codemation-dev-token": args.developmentServerToken
|
|
1553
|
-
},
|
|
1554
|
-
body: JSON.stringify(args.payload)
|
|
1555
|
-
});
|
|
1556
|
-
if (!response.ok) this.cliLogger.warn(`failed to notify dev gateway status=${response.status}`);
|
|
1557
|
-
} catch (error) {
|
|
1558
|
-
this.cliLogger.warn(`failed to notify dev gateway: ${error instanceof Error ? error.message : String(error)}`);
|
|
1559
|
-
}
|
|
1560
|
-
}
|
|
1561
|
-
};
|
|
1562
|
-
|
|
1563
2364
|
//#endregion
|
|
1564
2365
|
//#region src/dev/DevAuthSettingsLoader.ts
|
|
1565
2366
|
var DevAuthSettingsLoader = class DevAuthSettingsLoader {
|
|
1566
2367
|
static defaultDevelopmentAuthSecret = "codemation-dev-auth-secret-not-for-production";
|
|
1567
|
-
constructor(configLoader) {
|
|
2368
|
+
constructor(configLoader, consumerEnvLoader) {
|
|
1568
2369
|
this.configLoader = configLoader;
|
|
2370
|
+
this.consumerEnvLoader = consumerEnvLoader;
|
|
1569
2371
|
}
|
|
1570
2372
|
resolveDevelopmentServerToken(rawToken) {
|
|
1571
2373
|
if (rawToken && rawToken.trim().length > 0) return rawToken;
|
|
@@ -1573,14 +2375,15 @@ var DevAuthSettingsLoader = class DevAuthSettingsLoader {
|
|
|
1573
2375
|
}
|
|
1574
2376
|
async loadForConsumer(consumerRoot) {
|
|
1575
2377
|
const resolution = await this.configLoader.load({ consumerRoot });
|
|
2378
|
+
const envForAuthSecret = this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(consumerRoot, process.env);
|
|
1576
2379
|
return {
|
|
1577
2380
|
authConfigJson: JSON.stringify(resolution.config.auth ?? null),
|
|
1578
|
-
authSecret: this.resolveDevelopmentAuthSecret(
|
|
2381
|
+
authSecret: this.resolveDevelopmentAuthSecret(envForAuthSecret),
|
|
1579
2382
|
skipUiAuth: resolution.config.auth?.allowUnauthenticatedInDevelopment === true
|
|
1580
2383
|
};
|
|
1581
2384
|
}
|
|
1582
2385
|
resolveDevelopmentAuthSecret(env) {
|
|
1583
|
-
const configuredSecret = env.AUTH_SECRET
|
|
2386
|
+
const configuredSecret = env.AUTH_SECRET;
|
|
1584
2387
|
if (configuredSecret && configuredSecret.trim().length > 0) return configuredSecret;
|
|
1585
2388
|
return DevAuthSettingsLoader.defaultDevelopmentAuthSecret;
|
|
1586
2389
|
}
|
|
@@ -1607,10 +2410,10 @@ var DevHttpProbe = class {
|
|
|
1607
2410
|
} catch {}
|
|
1608
2411
|
await setTimeout$1(50);
|
|
1609
2412
|
}
|
|
1610
|
-
throw new Error("Timed out waiting for dev
|
|
2413
|
+
throw new Error("Timed out waiting for the stable dev HTTP health check.");
|
|
1611
2414
|
}
|
|
1612
2415
|
/**
|
|
1613
|
-
* Polls until the runtime
|
|
2416
|
+
* Polls until the active disposable runtime serves bootstrap summary through the stable CLI dev endpoint.
|
|
1614
2417
|
*/
|
|
1615
2418
|
async waitUntilBootstrapSummaryReady(gatewayBaseUrl) {
|
|
1616
2419
|
const url = `${gatewayBaseUrl.replace(/\/$/, "")}/api/dev/bootstrap-summary`;
|
|
@@ -1630,14 +2433,17 @@ var DevHttpProbe = class {
|
|
|
1630
2433
|
//#endregion
|
|
1631
2434
|
//#region src/dev/DevNextHostEnvironmentBuilder.ts
|
|
1632
2435
|
var DevNextHostEnvironmentBuilder = class {
|
|
1633
|
-
constructor(consumerEnvLoader, sourceMapNodeOptions) {
|
|
2436
|
+
constructor(consumerEnvLoader, sourceMapNodeOptions, frontendAuthSnapshotFactory = new CodemationFrontendAuthSnapshotFactory$1(), frontendAppConfigJsonCodec = new FrontendAppConfigJsonCodec$1()) {
|
|
1634
2437
|
this.consumerEnvLoader = consumerEnvLoader;
|
|
1635
2438
|
this.sourceMapNodeOptions = sourceMapNodeOptions;
|
|
2439
|
+
this.frontendAuthSnapshotFactory = frontendAuthSnapshotFactory;
|
|
2440
|
+
this.frontendAppConfigJsonCodec = frontendAppConfigJsonCodec;
|
|
1636
2441
|
}
|
|
1637
2442
|
buildConsumerUiProxy(args) {
|
|
1638
2443
|
return {
|
|
1639
2444
|
...this.build({
|
|
1640
2445
|
authConfigJson: args.authConfigJson,
|
|
2446
|
+
authSecret: args.authSecret,
|
|
1641
2447
|
consumerRoot: args.consumerRoot,
|
|
1642
2448
|
developmentServerToken: args.developmentServerToken,
|
|
1643
2449
|
nextPort: args.nextPort,
|
|
@@ -1646,23 +2452,33 @@ var DevNextHostEnvironmentBuilder = class {
|
|
|
1646
2452
|
websocketPort: args.websocketPort,
|
|
1647
2453
|
consumerOutputManifestPath: args.consumerOutputManifestPath
|
|
1648
2454
|
}),
|
|
2455
|
+
HOSTNAME: "127.0.0.1",
|
|
1649
2456
|
AUTH_SECRET: args.authSecret,
|
|
1650
|
-
AUTH_URL: args.publicBaseUrl
|
|
1651
|
-
NEXTAUTH_SECRET: args.authSecret,
|
|
1652
|
-
NEXTAUTH_URL: args.publicBaseUrl
|
|
2457
|
+
AUTH_URL: args.publicBaseUrl
|
|
1653
2458
|
};
|
|
1654
2459
|
}
|
|
1655
2460
|
build(args) {
|
|
1656
2461
|
const merged = this.consumerEnvLoader.mergeConsumerRootIntoProcessEnvironment(args.consumerRoot, process$1.env);
|
|
1657
2462
|
const manifestPath = args.consumerOutputManifestPath ?? path.resolve(args.consumerRoot, ".codemation", "output", "current.json");
|
|
2463
|
+
const authSecret = args.authSecret ?? merged.AUTH_SECRET;
|
|
2464
|
+
const authSnapshot = this.frontendAuthSnapshotFactory.createFromResolvedInputs({
|
|
2465
|
+
authConfig: this.parseAuthConfig(args.authConfigJson),
|
|
2466
|
+
env: {
|
|
2467
|
+
...merged,
|
|
2468
|
+
...typeof authSecret === "string" && authSecret.trim().length > 0 ? { AUTH_SECRET: authSecret } : {}
|
|
2469
|
+
},
|
|
2470
|
+
uiAuthEnabled: !args.skipUiAuth
|
|
2471
|
+
});
|
|
1658
2472
|
return {
|
|
1659
2473
|
...merged,
|
|
1660
2474
|
PORT: String(args.nextPort),
|
|
1661
|
-
CODEMATION_AUTH_CONFIG_JSON: args.authConfigJson,
|
|
1662
2475
|
CODEMATION_CONSUMER_ROOT: args.consumerRoot,
|
|
1663
2476
|
CODEMATION_CONSUMER_OUTPUT_MANIFEST_PATH: manifestPath,
|
|
1664
|
-
|
|
1665
|
-
|
|
2477
|
+
CODEMATION_FRONTEND_APP_CONFIG_JSON: this.frontendAppConfigJsonCodec.serialize({
|
|
2478
|
+
auth: authSnapshot,
|
|
2479
|
+
productName: "Codemation",
|
|
2480
|
+
logoUrl: null
|
|
2481
|
+
}),
|
|
1666
2482
|
CODEMATION_WS_PORT: String(args.websocketPort),
|
|
1667
2483
|
NEXT_PUBLIC_CODEMATION_WS_PORT: String(args.websocketPort),
|
|
1668
2484
|
CODEMATION_DEV_SERVER_TOKEN: args.developmentServerToken,
|
|
@@ -1673,6 +2489,10 @@ var DevNextHostEnvironmentBuilder = class {
|
|
|
1673
2489
|
...args.runtimeDevUrl !== void 0 && args.runtimeDevUrl.trim().length > 0 ? { CODEMATION_RUNTIME_DEV_URL: args.runtimeDevUrl.trim() } : {}
|
|
1674
2490
|
};
|
|
1675
2491
|
}
|
|
2492
|
+
parseAuthConfig(authConfigJson) {
|
|
2493
|
+
if (authConfigJson.trim().length === 0) return;
|
|
2494
|
+
return JSON.parse(authConfigJson) ?? void 0;
|
|
2495
|
+
}
|
|
1676
2496
|
};
|
|
1677
2497
|
|
|
1678
2498
|
//#endregion
|
|
@@ -1686,7 +2506,7 @@ var DevSessionPortsResolver = class {
|
|
|
1686
2506
|
const nextPort = this.listenPorts.resolvePrimaryApplicationPort(args.portEnv);
|
|
1687
2507
|
return {
|
|
1688
2508
|
nextPort,
|
|
1689
|
-
gatewayPort: this.listenPorts.parsePositiveInteger(args.gatewayPortEnv) ?? (args.devMode === "
|
|
2509
|
+
gatewayPort: this.listenPorts.parsePositiveInteger(args.gatewayPortEnv) ?? (args.devMode === "packaged-ui" ? nextPort : await this.loopbackPorts.allocate())
|
|
1690
2510
|
};
|
|
1691
2511
|
}
|
|
1692
2512
|
};
|
|
@@ -1697,86 +2517,48 @@ var DevSessionPortsResolver = class {
|
|
|
1697
2517
|
* Bundles dependencies for {@link DevCommand} so the command stays a thin orchestrator.
|
|
1698
2518
|
*/
|
|
1699
2519
|
var DevSessionServices = class {
|
|
1700
|
-
constructor(consumerEnvLoader, sourceMapNodeOptions, sessionPorts, loopbackPortAllocator, devHttpProbe,
|
|
2520
|
+
constructor(consumerEnvLoader, sourceMapNodeOptions, sessionPorts, loopbackPortAllocator, devHttpProbe, devAuthLoader, nextHostEnvBuilder, watchRootsResolver, sourceChangeClassifier) {
|
|
1701
2521
|
this.consumerEnvLoader = consumerEnvLoader;
|
|
1702
2522
|
this.sourceMapNodeOptions = sourceMapNodeOptions;
|
|
1703
2523
|
this.sessionPorts = sessionPorts;
|
|
1704
2524
|
this.loopbackPortAllocator = loopbackPortAllocator;
|
|
1705
2525
|
this.devHttpProbe = devHttpProbe;
|
|
1706
|
-
this.runtimeEntrypointResolver = runtimeEntrypointResolver;
|
|
1707
2526
|
this.devAuthLoader = devAuthLoader;
|
|
1708
2527
|
this.nextHostEnvBuilder = nextHostEnvBuilder;
|
|
1709
2528
|
this.watchRootsResolver = watchRootsResolver;
|
|
1710
2529
|
this.sourceChangeClassifier = sourceChangeClassifier;
|
|
1711
|
-
this.sourceRestartCoordinator = sourceRestartCoordinator;
|
|
1712
2530
|
}
|
|
1713
2531
|
};
|
|
1714
2532
|
|
|
1715
2533
|
//#endregion
|
|
1716
2534
|
//#region src/dev/DevSourceChangeClassifier.ts
|
|
1717
|
-
var DevSourceChangeClassifier = class {
|
|
2535
|
+
var DevSourceChangeClassifier = class DevSourceChangeClassifier {
|
|
2536
|
+
static configFileNames = new Set([
|
|
2537
|
+
"codemation.config.ts",
|
|
2538
|
+
"codemation.config.js",
|
|
2539
|
+
"codemation.config.mjs"
|
|
2540
|
+
]);
|
|
1718
2541
|
shouldRepublishConsumerOutput(args) {
|
|
1719
2542
|
const resolvedConsumerRoot = path.resolve(args.consumerRoot);
|
|
1720
2543
|
return args.changedPaths.some((changedPath) => this.isPathInsideDirectory(changedPath, resolvedConsumerRoot));
|
|
1721
2544
|
}
|
|
1722
|
-
|
|
1723
|
-
const
|
|
1724
|
-
return args.changedPaths.some((changedPath) =>
|
|
1725
|
-
}
|
|
1726
|
-
resolveConfigPaths(consumerRoot) {
|
|
1727
|
-
const resolvedConsumerRoot = path.resolve(consumerRoot);
|
|
1728
|
-
return [
|
|
1729
|
-
path.resolve(resolvedConsumerRoot, "codemation.config.ts"),
|
|
1730
|
-
path.resolve(resolvedConsumerRoot, "codemation.config.js"),
|
|
1731
|
-
path.resolve(resolvedConsumerRoot, "src", "codemation.config.ts"),
|
|
1732
|
-
path.resolve(resolvedConsumerRoot, "src", "codemation.config.js")
|
|
1733
|
-
];
|
|
2545
|
+
requiresUiRestart(args) {
|
|
2546
|
+
const resolvedConsumerRoot = path.resolve(args.consumerRoot);
|
|
2547
|
+
return args.changedPaths.some((changedPath) => this.pathRequiresUiRestart(path.resolve(changedPath), resolvedConsumerRoot));
|
|
1734
2548
|
}
|
|
1735
2549
|
isPathInsideDirectory(filePath, directoryPath) {
|
|
1736
2550
|
const resolvedFilePath = path.resolve(filePath);
|
|
1737
2551
|
const relativePath = path.relative(directoryPath, resolvedFilePath);
|
|
1738
2552
|
return relativePath.length === 0 || !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
1739
2553
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
this.cliLogger = cliLogger;
|
|
1749
|
-
}
|
|
1750
|
-
async runHandshakeAfterSourceChange(gatewayBaseUrl, developmentServerToken) {
|
|
1751
|
-
const restartStarted = performance.now();
|
|
1752
|
-
try {
|
|
1753
|
-
await this.gatewayNotifier.notify({
|
|
1754
|
-
gatewayBaseUrl,
|
|
1755
|
-
developmentServerToken,
|
|
1756
|
-
payload: { kind: "buildStarted" }
|
|
1757
|
-
});
|
|
1758
|
-
await this.gatewayNotifier.notify({
|
|
1759
|
-
gatewayBaseUrl,
|
|
1760
|
-
developmentServerToken,
|
|
1761
|
-
payload: {
|
|
1762
|
-
kind: "buildCompleted",
|
|
1763
|
-
buildVersion: `${Date.now()}-${process$1.pid}`
|
|
1764
|
-
}
|
|
1765
|
-
});
|
|
1766
|
-
const totalMs = performance.now() - restartStarted;
|
|
1767
|
-
this.performanceDiagnosticsLogger.info(`triggered source-based runtime restart timingMs={total:${totalMs.toFixed(1)}}`);
|
|
1768
|
-
} catch (error) {
|
|
1769
|
-
const exception = error instanceof Error ? error : new Error(String(error));
|
|
1770
|
-
await this.gatewayNotifier.notify({
|
|
1771
|
-
gatewayBaseUrl,
|
|
1772
|
-
developmentServerToken,
|
|
1773
|
-
payload: {
|
|
1774
|
-
kind: "buildFailed",
|
|
1775
|
-
message: exception.message
|
|
1776
|
-
}
|
|
1777
|
-
});
|
|
1778
|
-
this.cliLogger.error("source-based runtime restart request failed", exception);
|
|
1779
|
-
}
|
|
2554
|
+
pathRequiresUiRestart(resolvedPath, consumerRoot) {
|
|
2555
|
+
if (!this.isPathInsideDirectory(resolvedPath, consumerRoot)) return false;
|
|
2556
|
+
const relativePath = path.relative(consumerRoot, resolvedPath);
|
|
2557
|
+
if (DevSourceChangeClassifier.configFileNames.has(path.basename(relativePath))) return true;
|
|
2558
|
+
if (relativePath.startsWith(path.join("src", "workflows"))) return false;
|
|
2559
|
+
if (relativePath.startsWith(path.join("src", "plugins"))) return false;
|
|
2560
|
+
if (relativePath.includes("credential")) return true;
|
|
2561
|
+
return false;
|
|
1780
2562
|
}
|
|
1781
2563
|
};
|
|
1782
2564
|
|
|
@@ -1785,7 +2567,7 @@ var DevSourceRestartCoordinator = class {
|
|
|
1785
2567
|
var LoopbackPortAllocator = class {
|
|
1786
2568
|
async allocate() {
|
|
1787
2569
|
return await new Promise((resolve, reject) => {
|
|
1788
|
-
const server = createServer();
|
|
2570
|
+
const server = createServer$1();
|
|
1789
2571
|
server.once("error", reject);
|
|
1790
2572
|
server.listen(0, "127.0.0.1", () => {
|
|
1791
2573
|
const address = server.address();
|
|
@@ -1801,52 +2583,20 @@ var LoopbackPortAllocator = class {
|
|
|
1801
2583
|
}
|
|
1802
2584
|
};
|
|
1803
2585
|
|
|
1804
|
-
//#endregion
|
|
1805
|
-
//#region src/dev/RuntimeToolEntrypointResolver.ts
|
|
1806
|
-
var RuntimeToolEntrypointResolver = class {
|
|
1807
|
-
require = createRequire(import.meta.url);
|
|
1808
|
-
async resolve(args) {
|
|
1809
|
-
const sourceEntrypointPath = path.resolve(args.repoRoot, args.sourceEntrypoint);
|
|
1810
|
-
if (await this.exists(sourceEntrypointPath)) return {
|
|
1811
|
-
command: process$1.execPath,
|
|
1812
|
-
args: [
|
|
1813
|
-
"--import",
|
|
1814
|
-
"tsx",
|
|
1815
|
-
sourceEntrypointPath
|
|
1816
|
-
],
|
|
1817
|
-
env: { TSX_TSCONFIG_PATH: path.resolve(args.repoRoot, "tsconfig.codemation-tsx.json") }
|
|
1818
|
-
};
|
|
1819
|
-
return {
|
|
1820
|
-
command: process$1.execPath,
|
|
1821
|
-
args: [this.require.resolve(args.packageName)],
|
|
1822
|
-
env: {}
|
|
1823
|
-
};
|
|
1824
|
-
}
|
|
1825
|
-
async exists(filePath) {
|
|
1826
|
-
try {
|
|
1827
|
-
await access(filePath);
|
|
1828
|
-
return true;
|
|
1829
|
-
} catch {
|
|
1830
|
-
return false;
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1833
|
-
};
|
|
1834
|
-
|
|
1835
2586
|
//#endregion
|
|
1836
2587
|
//#region src/dev/WatchRootsResolver.ts
|
|
1837
2588
|
var WatchRootsResolver = class {
|
|
1838
2589
|
resolve(args) {
|
|
1839
|
-
if (args.devMode === "
|
|
2590
|
+
if (args.devMode === "packaged-ui") return [args.consumerRoot];
|
|
1840
2591
|
return [
|
|
1841
2592
|
args.consumerRoot,
|
|
2593
|
+
path.resolve(args.repoRoot, "packages", "cli"),
|
|
1842
2594
|
path.resolve(args.repoRoot, "packages", "core"),
|
|
1843
2595
|
path.resolve(args.repoRoot, "packages", "core-nodes"),
|
|
1844
2596
|
path.resolve(args.repoRoot, "packages", "core-nodes-gmail"),
|
|
1845
2597
|
path.resolve(args.repoRoot, "packages", "eventbus-redis"),
|
|
1846
2598
|
path.resolve(args.repoRoot, "packages", "host"),
|
|
1847
|
-
path.resolve(args.repoRoot, "packages", "node-example")
|
|
1848
|
-
path.resolve(args.repoRoot, "packages", "queue-bullmq"),
|
|
1849
|
-
path.resolve(args.repoRoot, "packages", "runtime-dev")
|
|
2599
|
+
path.resolve(args.repoRoot, "packages", "node-example")
|
|
1850
2600
|
];
|
|
1851
2601
|
}
|
|
1852
2602
|
};
|
|
@@ -1854,16 +2604,12 @@ var WatchRootsResolver = class {
|
|
|
1854
2604
|
//#endregion
|
|
1855
2605
|
//#region src/dev/Builder.ts
|
|
1856
2606
|
var DevSessionServicesBuilder = class {
|
|
1857
|
-
constructor(loggerFactory$1) {
|
|
1858
|
-
this.loggerFactory = loggerFactory$1;
|
|
1859
|
-
}
|
|
1860
2607
|
build() {
|
|
1861
2608
|
const consumerEnvLoader = new ConsumerEnvLoader();
|
|
1862
2609
|
const sourceMapNodeOptions = new SourceMapNodeOptions();
|
|
1863
2610
|
const listenPortResolver = new ListenPortResolver();
|
|
1864
2611
|
const loopbackPortAllocator = new LoopbackPortAllocator();
|
|
1865
|
-
|
|
1866
|
-
return new DevSessionServices(consumerEnvLoader, sourceMapNodeOptions, new DevSessionPortsResolver(listenPortResolver, loopbackPortAllocator), loopbackPortAllocator, new DevHttpProbe(), new RuntimeToolEntrypointResolver(), new DevAuthSettingsLoader(new CodemationConsumerConfigLoader()), new DevNextHostEnvironmentBuilder(consumerEnvLoader, sourceMapNodeOptions), new WatchRootsResolver(), new DevSourceChangeClassifier(), new DevSourceRestartCoordinator(new DevelopmentGatewayNotifier(cliLogger), this.loggerFactory.createPerformanceDiagnostics("codemation-cli.performance"), cliLogger));
|
|
2612
|
+
return new DevSessionServices(consumerEnvLoader, sourceMapNodeOptions, new DevSessionPortsResolver(listenPortResolver, loopbackPortAllocator), loopbackPortAllocator, new DevHttpProbe(), new DevAuthSettingsLoader(new CodemationConsumerConfigLoader(), consumerEnvLoader), new DevNextHostEnvironmentBuilder(consumerEnvLoader, sourceMapNodeOptions), new WatchRootsResolver(), new DevSourceChangeClassifier());
|
|
1867
2613
|
}
|
|
1868
2614
|
};
|
|
1869
2615
|
|
|
@@ -2136,14 +2882,17 @@ var CliProgram = class {
|
|
|
2136
2882
|
program.command("build").description("Build consumer workflows/plugins output and write the manifest.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--no-source-maps", "Disable .js.map files for emitted workflow modules (recommended for locked-down production bundles).").option("--target <es2020|es2022>", "ECMAScript language version for emitted workflow JavaScript (default: es2022).", "es2022").action(async (opts) => {
|
|
2137
2883
|
await this.buildCommand.execute(resolveConsumerRoot(opts.consumerRoot), this.buildOptionsParser.parse(opts));
|
|
2138
2884
|
});
|
|
2139
|
-
program.command("dev", { isDefault: true }).description("Start the dev
|
|
2140
|
-
await this.devCommand.execute(
|
|
2885
|
+
program.command("dev", { isDefault: true }).description("Start the stable dev endpoint and disposable API runtime. Uses the packaged Codemation UI by default.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--watch-framework", "Use Next dev HMR for framework UI work inside this repository.").action(async (opts) => {
|
|
2886
|
+
await this.devCommand.execute({
|
|
2887
|
+
consumerRoot: resolveConsumerRoot(opts.consumerRoot),
|
|
2888
|
+
watchFramework: opts.watchFramework === true
|
|
2889
|
+
});
|
|
2141
2890
|
});
|
|
2142
|
-
const serve = program.command("serve").description("Run production web or worker processes (no dev watchers).");
|
|
2143
|
-
serve.command("web").description("Start the packaged Codemation web host.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--no-source-maps", "Disable .js.map files for emitted workflow modules when this command runs the consumer build step.").option("--target <es2020|es2022>", "ECMAScript language version for emitted workflow JavaScript when building consumer output (default: es2022).", "es2022").action(async (opts) => {
|
|
2891
|
+
const serve$1 = program.command("serve").description("Run production web or worker processes (no dev watchers).");
|
|
2892
|
+
serve$1.command("web").description("Start the packaged Codemation web host.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--no-source-maps", "Disable .js.map files for emitted workflow modules when this command runs the consumer build step.").option("--target <es2020|es2022>", "ECMAScript language version for emitted workflow JavaScript when building consumer output (default: es2022).", "es2022").action(async (opts) => {
|
|
2144
2893
|
await this.serveWebCommand.execute(resolveConsumerRoot(opts.consumerRoot), this.buildOptionsParser.parse(opts));
|
|
2145
2894
|
});
|
|
2146
|
-
serve.command("worker").description("Start the Codemation worker process.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--config <path>", "Override path to codemation.config.ts / .js").action(async (opts) => {
|
|
2895
|
+
serve$1.command("worker").description("Start the Codemation worker process.").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--config <path>", "Override path to codemation.config.ts / .js").action(async (opts) => {
|
|
2147
2896
|
await this.serveWorkerCommand.execute(resolveConsumerRoot(opts.consumerRoot), opts.config);
|
|
2148
2897
|
});
|
|
2149
2898
|
program.command("db").description("Database utilities (PostgreSQL / Prisma).").command("migrate").description("Apply pending Prisma migrations using the consumer database URL (DATABASE_URL in `.env`, or CodemationConfig.runtime.database.url).").option("--consumer-root <path>", "Path to the consumer project root (defaults to cwd)").option("--config <path>", "Override path to codemation.config.ts / .js").action(async (opts) => {
|
|
@@ -2216,7 +2965,9 @@ var NextHostConsumerServerCommandFactory = class {
|
|
|
2216
2965
|
args: [
|
|
2217
2966
|
"exec",
|
|
2218
2967
|
"next",
|
|
2219
|
-
"start"
|
|
2968
|
+
"start",
|
|
2969
|
+
"-H",
|
|
2970
|
+
"127.0.0.1"
|
|
2220
2971
|
],
|
|
2221
2972
|
cwd: args.nextHostRoot
|
|
2222
2973
|
};
|
|
@@ -2288,22 +3039,20 @@ var CliDatabaseUrlDescriptor = class {
|
|
|
2288
3039
|
//#endregion
|
|
2289
3040
|
//#region src/bootstrap/CodemationCliApplicationSession.ts
|
|
2290
3041
|
/**
|
|
2291
|
-
* Opens
|
|
3042
|
+
* Opens an app container with persistence + command/query buses (no HTTP/WebSocket servers),
|
|
2292
3043
|
* for CLI tools that dispatch application commands or queries (e.g. user admin).
|
|
2293
3044
|
*/
|
|
2294
3045
|
var CodemationCliApplicationSession = class CodemationCliApplicationSession {
|
|
2295
|
-
constructor(
|
|
2296
|
-
this.
|
|
3046
|
+
constructor(container) {
|
|
3047
|
+
this.container = container;
|
|
2297
3048
|
}
|
|
2298
3049
|
static async open(args) {
|
|
2299
|
-
const
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
}));
|
|
2306
|
-
return new CodemationCliApplicationSession(app);
|
|
3050
|
+
const container = await new AppContainerFactory().create({
|
|
3051
|
+
appConfig: args.appConfig,
|
|
3052
|
+
sharedWorkflowWebsocketServer: null
|
|
3053
|
+
});
|
|
3054
|
+
if (args.appConfig.env.CODEMATION_SKIP_STARTUP_MIGRATIONS !== "true") await container.resolve(DatabaseMigrations).migrate();
|
|
3055
|
+
return new CodemationCliApplicationSession(container);
|
|
2307
3056
|
}
|
|
2308
3057
|
getPrismaClient() {
|
|
2309
3058
|
const container = this.getContainer();
|
|
@@ -2317,10 +3066,10 @@ var CodemationCliApplicationSession = class CodemationCliApplicationSession {
|
|
|
2317
3066
|
return this.getContainer().resolve(ApplicationTokens.QueryBus);
|
|
2318
3067
|
}
|
|
2319
3068
|
async close() {
|
|
2320
|
-
await this.
|
|
3069
|
+
await this.container.resolve(AppContainerLifecycle).stop({ stopWebsocketServer: false });
|
|
2321
3070
|
}
|
|
2322
3071
|
getContainer() {
|
|
2323
|
-
return this.
|
|
3072
|
+
return this.container;
|
|
2324
3073
|
}
|
|
2325
3074
|
};
|
|
2326
3075
|
|
|
@@ -2330,37 +3079,26 @@ var CodemationCliApplicationSession = class CodemationCliApplicationSession {
|
|
|
2330
3079
|
* Shared env/config/session wiring for `codemation user *` commands (local auth + database).
|
|
2331
3080
|
*/
|
|
2332
3081
|
var UserAdminCliBootstrap = class {
|
|
2333
|
-
constructor(
|
|
2334
|
-
this.
|
|
3082
|
+
constructor(appConfigLoader, pathResolver, consumerDotenvLoader, tsconfigPreparation) {
|
|
3083
|
+
this.appConfigLoader = appConfigLoader;
|
|
2335
3084
|
this.pathResolver = pathResolver;
|
|
2336
3085
|
this.consumerDotenvLoader = consumerDotenvLoader;
|
|
2337
3086
|
this.tsconfigPreparation = tsconfigPreparation;
|
|
2338
|
-
this.databasePersistenceResolver = databasePersistenceResolver;
|
|
2339
3087
|
}
|
|
2340
3088
|
async withSession(options, fn) {
|
|
2341
3089
|
const consumerRoot = options.consumerRoot ?? process.cwd();
|
|
2342
3090
|
this.consumerDotenvLoader.load(consumerRoot);
|
|
2343
3091
|
this.tsconfigPreparation.applyWorkspaceTsconfigForTsxIfPresent(consumerRoot);
|
|
2344
|
-
const
|
|
3092
|
+
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
3093
|
+
const loadResult = await this.appConfigLoader.load({
|
|
2345
3094
|
consumerRoot,
|
|
2346
|
-
|
|
2347
|
-
});
|
|
2348
|
-
if (resolution.config.auth?.kind !== "local") throw new Error("Codemation user commands require CodemationConfig.auth.kind to be \"local\".");
|
|
2349
|
-
if (this.databasePersistenceResolver.resolve({
|
|
2350
|
-
runtimeConfig: resolution.config.runtime ?? {},
|
|
3095
|
+
repoRoot: paths.repoRoot,
|
|
2351
3096
|
env: process.env,
|
|
2352
|
-
|
|
2353
|
-
}).kind === "none") throw new Error("Database persistence is not configured. Set CodemationConfig.runtime.database (postgresql URL or PGlite).");
|
|
2354
|
-
const paths = await this.pathResolver.resolve(consumerRoot);
|
|
2355
|
-
const session = await CodemationCliApplicationSession.open({
|
|
2356
|
-
resolution,
|
|
2357
|
-
bootstrap: new CodemationBootstrapRequest({
|
|
2358
|
-
repoRoot: paths.repoRoot,
|
|
2359
|
-
consumerRoot,
|
|
2360
|
-
env: process.env,
|
|
2361
|
-
workflowSources: resolution.workflowSources
|
|
2362
|
-
})
|
|
3097
|
+
configPathOverride: options.configPath
|
|
2363
3098
|
});
|
|
3099
|
+
if (loadResult.appConfig.auth?.kind !== "local") throw new Error("Codemation user commands require CodemationConfig.auth.kind to be \"local\".");
|
|
3100
|
+
if (loadResult.appConfig.persistence.kind === "none") throw new Error("Database persistence is not configured. Set CodemationConfig.runtime.database (postgresql URL or PGlite).");
|
|
3101
|
+
const session = await CodemationCliApplicationSession.open({ appConfig: loadResult.appConfig });
|
|
2364
3102
|
try {
|
|
2365
3103
|
return await fn(session);
|
|
2366
3104
|
} finally {
|
|
@@ -2414,6 +3152,7 @@ const loggerFactory = new ServerLoggerFactory(logLevelPolicyFactory);
|
|
|
2414
3152
|
var CliProgramFactory = class {
|
|
2415
3153
|
create() {
|
|
2416
3154
|
const cliLogger = loggerFactory.create("codemation-cli");
|
|
3155
|
+
const appConfigLoader = new AppConfigLoader();
|
|
2417
3156
|
const pathResolver = new CliPathResolver();
|
|
2418
3157
|
const pluginDiscovery = new CodemationPluginDiscovery();
|
|
2419
3158
|
const artifactsPublisher = new ConsumerBuildArtifactsPublisher();
|
|
@@ -2421,15 +3160,15 @@ var CliProgramFactory = class {
|
|
|
2421
3160
|
const outputBuilderLoader = new ConsumerOutputBuilderLoader();
|
|
2422
3161
|
const sourceMapNodeOptions = new SourceMapNodeOptions();
|
|
2423
3162
|
const nextHostConsumerServerCommandFactory = new NextHostConsumerServerCommandFactory();
|
|
3163
|
+
const devSessionServices = new DevSessionServicesBuilder().build();
|
|
2424
3164
|
const tsconfigPreparation = new ConsumerCliTsconfigPreparation();
|
|
2425
|
-
const
|
|
2426
|
-
const userAdminBootstrap = new UserAdminCliBootstrap(new CodemationConsumerConfigLoader(), pathResolver, new UserAdminConsumerDotenvLoader(), tsconfigPreparation, databasePersistenceResolver);
|
|
3165
|
+
const userAdminBootstrap = new UserAdminCliBootstrap(appConfigLoader, pathResolver, new UserAdminConsumerDotenvLoader(), tsconfigPreparation);
|
|
2427
3166
|
const hostPackageRoot = new HostPackageRootResolver().resolveHostPackageRoot();
|
|
2428
3167
|
const userAdminCliOptionsParser = new UserAdminCliOptionsParser();
|
|
2429
3168
|
const databaseMigrationsApplyService = new DatabaseMigrationsApplyService(cliLogger, new UserAdminConsumerDotenvLoader(), tsconfigPreparation, new CodemationConsumerConfigLoader(), new ConsumerDatabaseConnectionResolver(), new CliDatabaseUrlDescriptor(), hostPackageRoot, new PrismaMigrationDeployer());
|
|
2430
3169
|
const buildOptionsParser = new ConsumerBuildOptionsParser();
|
|
2431
3170
|
const devConsumerPublishBootstrap = new DevConsumerPublishBootstrap(cliLogger, pluginDiscovery, artifactsPublisher, outputBuilderLoader, buildOptionsParser);
|
|
2432
|
-
return new CliProgram(buildOptionsParser, new BuildCommand(cliLogger, pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, outputBuilderLoader), new DevCommand(pathResolver,
|
|
3171
|
+
return new CliProgram(buildOptionsParser, new BuildCommand(cliLogger, pathResolver, pluginDiscovery, artifactsPublisher, tsRuntime, outputBuilderLoader), new DevCommand(pathResolver, tsRuntime, new DevLockFactory(), new DevSourceWatcherFactory(), cliLogger, devSessionServices, databaseMigrationsApplyService, new DevBootstrapSummaryFetcher(), new DevCliBannerRenderer(), devConsumerPublishBootstrap, new ConsumerEnvDotenvFilePredicate(), new DevTrackedProcessTreeKiller(), nextHostConsumerServerCommandFactory, new DevApiRuntimeFactory(devSessionServices.loopbackPortAllocator, appConfigLoader, pluginDiscovery), new CliDevProxyServerFactory(), new DevRebuildQueueFactory()), new ServeWebCommand(pathResolver, new CodemationConsumerConfigLoader(), pluginDiscovery, artifactsPublisher, tsRuntime, sourceMapNodeOptions, outputBuilderLoader, new ConsumerEnvLoader(), new ListenPortResolver(), nextHostConsumerServerCommandFactory, new CodemationFrontendAuthSnapshotFactory(), new FrontendAppConfigJsonCodec()), new ServeWorkerCommand(pathResolver, appConfigLoader, new AppContainerFactory()), new DbMigrateCommand(databaseMigrationsApplyService), new UserCreateCommand(new LocalUserCreator(userAdminBootstrap), userAdminCliOptionsParser), new UserListCommand(cliLogger, userAdminBootstrap, new CliDatabaseUrlDescriptor(), userAdminCliOptionsParser));
|
|
2433
3172
|
}
|
|
2434
3173
|
};
|
|
2435
3174
|
|