@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.
Files changed (40) hide show
  1. package/README.md +20 -26
  2. package/dist/{CliBin-C3ar49fj.js → CliBin-BAnFX1wL.js} +1105 -366
  3. package/dist/bin.js +1 -1
  4. package/dist/index.d.ts +655 -207
  5. package/dist/index.js +1 -1
  6. package/package.json +14 -6
  7. package/src/CliProgramFactory.ts +23 -8
  8. package/src/Program.ts +7 -3
  9. package/src/bootstrap/CodemationCliApplicationSession.ts +17 -19
  10. package/src/commands/DevCommand.ts +203 -171
  11. package/src/commands/ServeWebCommand.ts +26 -1
  12. package/src/commands/ServeWorkerCommand.ts +46 -30
  13. package/src/commands/devCommandLifecycle.types.ts +7 -11
  14. package/src/database/ConsumerDatabaseConnectionResolver.ts +55 -9
  15. package/src/database/DatabaseMigrationsApplyService.ts +2 -2
  16. package/src/dev/Builder.ts +1 -14
  17. package/src/dev/CliDevProxyServer.ts +457 -0
  18. package/src/dev/CliDevProxyServerFactory.ts +10 -0
  19. package/src/dev/DevApiRuntimeFactory.ts +44 -0
  20. package/src/dev/DevApiRuntimeHost.ts +130 -0
  21. package/src/dev/DevApiRuntimeServer.ts +107 -0
  22. package/src/dev/DevApiRuntimeTypes.ts +24 -0
  23. package/src/dev/DevAuthSettingsLoader.ts +9 -3
  24. package/src/dev/DevBootstrapSummaryFetcher.ts +1 -1
  25. package/src/dev/DevHttpProbe.ts +2 -2
  26. package/src/dev/DevNextHostEnvironmentBuilder.ts +35 -5
  27. package/src/dev/DevRebuildQueue.ts +54 -0
  28. package/src/dev/DevRebuildQueueFactory.ts +7 -0
  29. package/src/dev/DevSessionPortsResolver.ts +2 -2
  30. package/src/dev/DevSessionServices.ts +0 -4
  31. package/src/dev/DevSourceChangeClassifier.ts +33 -13
  32. package/src/dev/ListenPortConflictDescriber.ts +83 -0
  33. package/src/dev/WatchRootsResolver.ts +6 -4
  34. package/src/runtime/NextHostConsumerServerCommandFactory.ts +11 -2
  35. package/src/user/CliDatabaseUrlDescriptor.ts +2 -2
  36. package/src/user/UserAdminCliBootstrap.ts +9 -21
  37. package/codemation-cli-0.0.3.tgz +0 -0
  38. package/src/dev/DevSourceRestartCoordinator.ts +0 -48
  39. package/src/dev/DevelopmentGatewayNotifier.ts +0 -35
  40. package/src/dev/RuntimeToolEntrypointResolver.ts +0 -47
@@ -1,4 +1,9 @@
1
- import { CodemationPluginDiscovery } from "@codemation/host/server";
1
+ import {
2
+ CodemationConsumerConfigLoader,
3
+ CodemationFrontendAuthSnapshotFactory,
4
+ CodemationPluginDiscovery,
5
+ FrontendAppConfigJsonCodec,
6
+ } from "@codemation/host/server";
2
7
  import { spawn } from "node:child_process";
3
8
  import { createRequire } from "node:module";
4
9
  import path from "node:path";
@@ -19,6 +24,7 @@ export class ServeWebCommand {
19
24
 
20
25
  constructor(
21
26
  private readonly pathResolver: CliPathResolver,
27
+ private readonly configLoader: CodemationConsumerConfigLoader,
22
28
  private readonly pluginDiscovery: CodemationPluginDiscovery,
23
29
  private readonly artifactsPublisher: ConsumerBuildArtifactsPublisher,
24
30
  private readonly tsRuntime: TypeScriptRuntimeConfigurator,
@@ -27,6 +33,8 @@ export class ServeWebCommand {
27
33
  private readonly envLoader: ConsumerEnvLoader,
28
34
  private readonly listenPortResolver: ListenPortResolver,
29
35
  private readonly nextHostConsumerServerCommandFactory: NextHostConsumerServerCommandFactory,
36
+ private readonly frontendAuthSnapshotFactory: CodemationFrontendAuthSnapshotFactory,
37
+ private readonly frontendAppConfigJsonCodec: FrontendAppConfigJsonCodec,
30
38
  ) {}
31
39
 
32
40
  async execute(consumerRoot: string, buildOptions: ConsumerBuildOptions): Promise<void> {
@@ -39,6 +47,18 @@ export class ServeWebCommand {
39
47
  const nextHostRoot = path.dirname(this.require.resolve("@codemation/next-host/package.json"));
40
48
  const nextHostCommand = await this.nextHostConsumerServerCommandFactory.create({ nextHostRoot });
41
49
  const consumerEnv = this.envLoader.load(paths.consumerRoot);
50
+ const configResolution = await this.configLoader.load({ consumerRoot: paths.consumerRoot });
51
+ const frontendAuthSnapshot = this.frontendAuthSnapshotFactory.createFromResolvedInputs({
52
+ authConfig: configResolution.config.auth,
53
+ env: {
54
+ ...process.env,
55
+ ...consumerEnv,
56
+ },
57
+ uiAuthEnabled: !(
58
+ consumerEnv.NODE_ENV !== "production" &&
59
+ configResolution.config.auth?.allowUnauthenticatedInDevelopment === true
60
+ ),
61
+ });
42
62
  const nextPort = this.listenPortResolver.resolvePrimaryApplicationPort(process.env.PORT);
43
63
  const websocketPort = this.listenPortResolver.resolveWebsocketPortRelativeToHttp({
44
64
  nextPort,
@@ -52,6 +72,11 @@ export class ServeWebCommand {
52
72
  ...process.env,
53
73
  ...consumerEnv,
54
74
  PORT: String(nextPort),
75
+ CODEMATION_FRONTEND_APP_CONFIG_JSON: this.frontendAppConfigJsonCodec.serialize({
76
+ auth: frontendAuthSnapshot,
77
+ productName: "Codemation",
78
+ logoUrl: null,
79
+ }),
55
80
  CODEMATION_CONSUMER_OUTPUT_MANIFEST_PATH: manifest.manifestPath,
56
81
  CODEMATION_CONSUMER_ROOT: paths.consumerRoot,
57
82
  CODEMATION_WS_PORT: String(websocketPort),
@@ -1,40 +1,56 @@
1
- import { spawn } from "node:child_process";
2
- import { createRequire } from "node:module";
3
- import path from "node:path";
1
+ import { AppContainerFactory, WorkerRuntime } from "@codemation/host";
2
+ import { AppConfigLoader } from "@codemation/host/server";
4
3
  import process from "node:process";
5
-
6
- import { SourceMapNodeOptions } from "../runtime/SourceMapNodeOptions";
4
+ import { CliPathResolver } from "../path/CliPathResolver";
7
5
 
8
6
  export class ServeWorkerCommand {
9
- private readonly require = createRequire(import.meta.url);
10
-
11
- constructor(private readonly sourceMapNodeOptions: SourceMapNodeOptions) {}
7
+ constructor(
8
+ private readonly pathResolver: CliPathResolver,
9
+ private readonly appConfigLoader: AppConfigLoader,
10
+ private readonly appContainerFactory: AppContainerFactory,
11
+ ) {}
12
12
 
13
13
  async execute(consumerRoot: string, configPathOverride?: string): Promise<void> {
14
- const workerPackageRoot = path.dirname(this.require.resolve("@codemation/worker-cli/package.json"));
15
- const workerBin = path.join(workerPackageRoot, "bin", "codemation-worker.js");
16
- const args = [workerBin];
17
- if (configPathOverride !== undefined && configPathOverride.trim().length > 0) {
18
- args.push("--config", path.resolve(process.cwd(), configPathOverride.trim()));
14
+ const paths = await this.pathResolver.resolve(consumerRoot);
15
+ const loadResult = await this.appConfigLoader.load({
16
+ consumerRoot,
17
+ repoRoot: paths.repoRoot,
18
+ env: process.env,
19
+ configPathOverride,
20
+ });
21
+ if (loadResult.appConfig.scheduler.kind !== "bullmq") {
22
+ throw new Error('Worker mode requires runtime.scheduler.kind = "bullmq".');
19
23
  }
20
- args.push("--consumer-root", consumerRoot);
21
- const child = spawn(process.execPath, args, {
22
- cwd: consumerRoot,
23
- stdio: "inherit",
24
- env: {
25
- ...process.env,
26
- NODE_OPTIONS: this.sourceMapNodeOptions.appendToNodeOptions(process.env.NODE_OPTIONS),
27
- },
24
+ const container = await this.appContainerFactory.create({
25
+ appConfig: loadResult.appConfig,
26
+ sharedWorkflowWebsocketServer: null,
28
27
  });
29
- await new Promise<void>((resolve, reject) => {
30
- child.on("exit", (code) => {
31
- if ((code ?? 0) === 0) {
32
- resolve();
33
- return;
34
- }
35
- reject(new Error(`codemation-worker exited with code ${code ?? 0}.`));
36
- });
37
- child.on("error", reject);
28
+ const workerQueues =
29
+ loadResult.appConfig.scheduler.workerQueues.length > 0
30
+ ? loadResult.appConfig.scheduler.workerQueues
31
+ : ["default"];
32
+ const handle = await container.resolve(WorkerRuntime).start(workerQueues);
33
+ await new Promise<void>((resolve) => {
34
+ this.bindSignals(handle.stop, resolve);
38
35
  });
39
36
  }
37
+
38
+ private bindSignals(stop: () => Promise<void>, resolve: () => void): void {
39
+ let stopping = false;
40
+ const onSignal = async (): Promise<void> => {
41
+ if (stopping) {
42
+ return;
43
+ }
44
+ stopping = true;
45
+ try {
46
+ await stop();
47
+ } finally {
48
+ resolve();
49
+ process.exit(0);
50
+ }
51
+ };
52
+ process.on("SIGINT", () => void onSignal());
53
+ process.on("SIGTERM", () => void onSignal());
54
+ process.on("SIGQUIT", () => void onSignal());
55
+ }
40
56
  }
@@ -1,18 +1,18 @@
1
1
  import type { ChildProcess } from "node:child_process";
2
2
 
3
3
  import type { DevResolvedAuthSettings } from "../dev/DevAuthSettingsLoader";
4
- import type { ResolvedRuntimeToolEntrypoint } from "../dev/RuntimeToolEntrypointResolver";
4
+ import type { DevApiRuntimeServerHandle } from "../dev/DevApiRuntimeFactory";
5
5
  import type { CliPaths } from "../path/CliPathResolver";
6
6
 
7
- export type DevMode = "consumer" | "framework";
7
+ export type DevMode = "packaged-ui" | "watch-framework";
8
8
 
9
9
  /** Mutable child process handles and stop coordination (shared across dev session helpers). */
10
10
  export type DevMutableProcessState = {
11
- currentGateway: ChildProcess | null;
12
- currentNextHost: ChildProcess | null;
13
- currentUiNext: ChildProcess | null;
14
- currentUiProxyBaseUrl: string | null;
15
- isRestartingNextHost: boolean;
11
+ currentDevUi: ChildProcess | null;
12
+ currentPackagedUi: ChildProcess | null;
13
+ currentPackagedUiBaseUrl: string | null;
14
+ currentRuntime: DevApiRuntimeServerHandle | null;
15
+ isRestartingUi: boolean;
16
16
  stopRequested: boolean;
17
17
  stopResolve: (() => void) | null;
18
18
  stopReject: ((error: Error) => void) | null;
@@ -26,9 +26,5 @@ export type DevPreparedRuntime = Readonly<{
26
26
  gatewayPort: number;
27
27
  authSettings: DevResolvedAuthSettings;
28
28
  developmentServerToken: string;
29
- gatewayEntrypoint: ResolvedRuntimeToolEntrypoint;
30
- runtimeEntrypoint: ResolvedRuntimeToolEntrypoint;
31
- runtimeWorkingDirectory: string;
32
- discoveredPluginPackagesJson: string;
33
29
  consumerEnv: Readonly<Record<string, string>>;
34
30
  }>;
@@ -1,18 +1,64 @@
1
1
  import type { CodemationConfig } from "@codemation/host";
2
- import { DatabasePersistenceResolver } from "@codemation/host/persistence";
3
- import type { ResolvedDatabasePersistence } from "@codemation/host/persistence";
2
+ import type { AppPersistenceConfig } from "@codemation/host/persistence";
3
+ import path from "node:path";
4
4
 
5
5
  /**
6
6
  * Resolves TCP PostgreSQL vs PGlite vs none from env + {@link CodemationConfig} (same rules as the host runtime).
7
7
  */
8
8
  export class ConsumerDatabaseConnectionResolver {
9
- private readonly resolver = new DatabasePersistenceResolver();
9
+ resolve(processEnv: NodeJS.ProcessEnv, config: CodemationConfig, consumerRoot: string): AppPersistenceConfig {
10
+ const database = config.runtime?.database;
11
+ if (!database) {
12
+ return { kind: "none" };
13
+ }
14
+ const databaseKind = this.resolveDatabaseKind(database.kind, database.url, processEnv);
15
+ if (databaseKind === "postgresql") {
16
+ const databaseUrl = database.url?.trim() ?? "";
17
+ if (!databaseUrl) {
18
+ throw new Error('runtime.database.kind is "postgresql" but no database URL was set (runtime.database.url).');
19
+ }
20
+ return { kind: "postgresql", databaseUrl };
21
+ }
22
+ return {
23
+ kind: "pglite",
24
+ dataDir: this.resolvePgliteDataDir(database.pgliteDataDir, processEnv, consumerRoot),
25
+ };
26
+ }
27
+
28
+ private resolveDatabaseKind(
29
+ configuredKind: "postgresql" | "pglite" | undefined,
30
+ databaseUrl: string | undefined,
31
+ env: NodeJS.ProcessEnv,
32
+ ): "postgresql" | "pglite" {
33
+ const kindFromEnv = env.CODEMATION_DATABASE_KIND?.trim();
34
+ if (kindFromEnv === "postgresql" || kindFromEnv === "pglite") {
35
+ return kindFromEnv;
36
+ }
37
+ if (configuredKind) {
38
+ return configuredKind;
39
+ }
40
+ const trimmedUrl = databaseUrl?.trim();
41
+ if (trimmedUrl && (trimmedUrl.startsWith("postgresql://") || trimmedUrl.startsWith("postgres://"))) {
42
+ return "postgresql";
43
+ }
44
+ return "pglite";
45
+ }
10
46
 
11
- resolve(processEnv: NodeJS.ProcessEnv, config: CodemationConfig, consumerRoot: string): ResolvedDatabasePersistence {
12
- return this.resolver.resolve({
13
- runtimeConfig: config.runtime ?? {},
14
- env: processEnv,
15
- consumerRoot,
16
- });
47
+ private resolvePgliteDataDir(
48
+ configuredPath: string | undefined,
49
+ env: NodeJS.ProcessEnv,
50
+ consumerRoot: string,
51
+ ): string {
52
+ const envPath = env.CODEMATION_PGLITE_DATA_DIR?.trim();
53
+ if (envPath && envPath.length > 0) {
54
+ return path.isAbsolute(envPath) ? envPath : path.resolve(consumerRoot, envPath);
55
+ }
56
+ const trimmedConfiguredPath = configuredPath?.trim();
57
+ if (trimmedConfiguredPath && trimmedConfiguredPath.length > 0) {
58
+ return path.isAbsolute(trimmedConfiguredPath)
59
+ ? trimmedConfiguredPath
60
+ : path.resolve(consumerRoot, trimmedConfiguredPath);
61
+ }
62
+ return path.resolve(consumerRoot, ".codemation", "pglite");
17
63
  }
18
64
  }
@@ -1,5 +1,5 @@
1
1
  import { CodemationConsumerConfigLoader } from "@codemation/host/server";
2
- import type { ResolvedDatabasePersistence } from "@codemation/host/persistence";
2
+ import type { AppPersistenceConfig } from "@codemation/host/persistence";
3
3
  import type { Logger } from "@codemation/host/next/server";
4
4
  import path from "node:path";
5
5
 
@@ -9,7 +9,7 @@ import type { CliDatabaseUrlDescriptor } from "../user/CliDatabaseUrlDescriptor"
9
9
  import type { UserAdminConsumerDotenvLoader } from "../user/UserAdminConsumerDotenvLoader";
10
10
 
11
11
  export type DatabaseMigrationDeployer = {
12
- deployPersistence(persistence: ResolvedDatabasePersistence, env?: Readonly<NodeJS.ProcessEnv>): Promise<void>;
12
+ deployPersistence(persistence: AppPersistenceConfig, env?: Readonly<NodeJS.ProcessEnv>): Promise<void>;
13
13
  };
14
14
 
15
15
  /**
@@ -1,47 +1,34 @@
1
1
  import { CodemationConsumerConfigLoader } from "@codemation/host/server";
2
- import type { ServerLoggerFactory } from "@codemation/host/next/server";
3
2
 
4
3
  import { ConsumerEnvLoader } from "../consumer/ConsumerEnvLoader";
5
4
  import { ListenPortResolver } from "../runtime/ListenPortResolver";
6
5
  import { SourceMapNodeOptions } from "../runtime/SourceMapNodeOptions";
7
6
 
8
- import { DevelopmentGatewayNotifier } from "./DevelopmentGatewayNotifier";
9
7
  import { DevAuthSettingsLoader } from "./DevAuthSettingsLoader";
10
8
  import { DevHttpProbe } from "./DevHttpProbe";
11
9
  import { DevNextHostEnvironmentBuilder } from "./DevNextHostEnvironmentBuilder";
12
10
  import { DevSessionPortsResolver } from "./DevSessionPortsResolver";
13
11
  import { DevSessionServices } from "./DevSessionServices";
14
12
  import { DevSourceChangeClassifier } from "./DevSourceChangeClassifier";
15
- import { DevSourceRestartCoordinator } from "./DevSourceRestartCoordinator";
16
13
  import { LoopbackPortAllocator } from "./LoopbackPortAllocator";
17
- import { RuntimeToolEntrypointResolver } from "./RuntimeToolEntrypointResolver";
18
14
  import { WatchRootsResolver } from "./WatchRootsResolver";
19
15
 
20
16
  export class DevSessionServicesBuilder {
21
- constructor(private readonly loggerFactory: ServerLoggerFactory) {}
22
-
23
17
  build(): DevSessionServices {
24
18
  const consumerEnvLoader = new ConsumerEnvLoader();
25
19
  const sourceMapNodeOptions = new SourceMapNodeOptions();
26
20
  const listenPortResolver = new ListenPortResolver();
27
21
  const loopbackPortAllocator = new LoopbackPortAllocator();
28
- const cliLogger = this.loggerFactory.create("codemation-cli");
29
22
  return new DevSessionServices(
30
23
  consumerEnvLoader,
31
24
  sourceMapNodeOptions,
32
25
  new DevSessionPortsResolver(listenPortResolver, loopbackPortAllocator),
33
26
  loopbackPortAllocator,
34
27
  new DevHttpProbe(),
35
- new RuntimeToolEntrypointResolver(),
36
- new DevAuthSettingsLoader(new CodemationConsumerConfigLoader()),
28
+ new DevAuthSettingsLoader(new CodemationConsumerConfigLoader(), consumerEnvLoader),
37
29
  new DevNextHostEnvironmentBuilder(consumerEnvLoader, sourceMapNodeOptions),
38
30
  new WatchRootsResolver(),
39
31
  new DevSourceChangeClassifier(),
40
- new DevSourceRestartCoordinator(
41
- new DevelopmentGatewayNotifier(cliLogger),
42
- this.loggerFactory.createPerformanceDiagnostics("codemation-cli.performance"),
43
- cliLogger,
44
- ),
45
32
  );
46
33
  }
47
34
  }