From b3e5e5b999989ef3be5caf90f77de1c408ca32fe Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Mon, 20 Jul 2026 09:22:26 +0530 Subject: [PATCH] feat: configure workspace environment runtimes --- .publish/ai/package.json | 2 +- .publish/authz/package.json | 2 +- .publish/cli/package.json | 22 +++++------ .publish/compiler/package.json | 2 +- .publish/core/package.json | 2 +- .publish/csr/package.json | 4 +- .publish/db/package.json | 2 +- .publish/dev-server/package.json | 26 ++++++------- .publish/encryption/package.json | 2 +- .publish/helpers/package.json | 4 +- .publish/i18n/package.json | 4 +- .publish/jwt/package.json | 2 +- .publish/mobile/package.json | 4 +- .publish/native/package.json | 2 +- .publish/oauth/package.json | 2 +- .publish/pubsub/package.json | 2 +- .publish/queue/package.json | 2 +- .publish/reactive/package.json | 2 +- .publish/router/package.json | 6 +-- .publish/ssr/package.json | 4 +- .publish/styles/package.json | 4 +- .publish/test/package.json | 2 +- .publish/tracking/package.json | 2 +- .publish/ui/package.json | 4 +- .publish/uploader/package.json | 4 +- .publish/validation/package.json | 2 +- packages/ai/package.json | 2 +- packages/authz/package.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/index.ts | 16 +++++++- packages/cli/src/update.ts | 9 +++++ packages/cli/src/workspace.ts | 52 ++++++++++++++++++-------- packages/cli/test/workspace.test.ts | 28 ++++++++++++++ packages/compiler/package.json | 2 +- packages/core/package.json | 2 +- packages/csr/package.json | 2 +- packages/db/package.json | 2 +- packages/dev-server/package.json | 2 +- packages/dev-server/src/gateway.ts | 10 ++++- packages/dev-server/src/serve-entry.ts | 3 +- packages/encryption/package.json | 2 +- packages/helpers/package.json | 2 +- packages/i18n/package.json | 2 +- packages/jwt/package.json | 2 +- packages/mobile/package.json | 2 +- packages/native/package.json | 2 +- packages/oauth/package.json | 2 +- packages/pubsub/package.json | 2 +- packages/queue/package.json | 2 +- packages/reactive/package.json | 2 +- packages/router/package.json | 2 +- packages/ssr/package.json | 2 +- packages/styles/package.json | 2 +- packages/test/package.json | 2 +- packages/tracking/package.json | 2 +- packages/ui/package.json | 2 +- packages/uploader/package.json | 2 +- packages/validation/package.json | 2 +- 58 files changed, 183 insertions(+), 103 deletions(-) diff --git a/.publish/ai/package.json b/.publish/ai/package.json index 957b36d9..576e88f1 100644 --- a/.publish/ai/package.json +++ b/.publish/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", "license": "MIT", diff --git a/.publish/authz/package.json b/.publish/authz/package.json index 84ca115a..05906c51 100644 --- a/.publish/authz/package.json +++ b/.publish/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/authz — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/cli/package.json b/.publish/cli/package.json index a827577f..c9fd61f6 100644 --- a/.publish/cli/package.json +++ b/.publish/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/cli — part of the WrNexus framework.", "license": "MIT", @@ -28,16 +28,16 @@ "wrnexus": "./dist/index.js" }, "dependencies": { - "@wrnexus/core": "^0.2.66", - "@wrnexus/router": "^0.2.66", - "@wrnexus/csr": "^0.2.66", - "@wrnexus/compiler": "^0.2.66", - "@wrnexus/styles": "^0.2.66", - "@wrnexus/dev-server": "^0.2.66", - "@wrnexus/ui": "^0.2.66", - "@wrnexus/validation": "^0.2.66", - "@wrnexus/i18n": "^0.2.66", - "@wrnexus/db": "^0.2.66" + "@wrnexus/core": "^0.2.67", + "@wrnexus/router": "^0.2.67", + "@wrnexus/csr": "^0.2.67", + "@wrnexus/compiler": "^0.2.67", + "@wrnexus/styles": "^0.2.67", + "@wrnexus/dev-server": "^0.2.67", + "@wrnexus/ui": "^0.2.67", + "@wrnexus/validation": "^0.2.67", + "@wrnexus/i18n": "^0.2.67", + "@wrnexus/db": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/compiler/package.json b/.publish/compiler/package.json index 49960e33..5108bfcb 100644 --- a/.publish/compiler/package.json +++ b/.publish/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/compiler — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/core/package.json b/.publish/core/package.json index 6992f38c..c90f990e 100644 --- a/.publish/core/package.json +++ b/.publish/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/core — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/csr/package.json b/.publish/csr/package.json index a891d9d5..7f007a19 100644 --- a/.publish/csr/package.json +++ b/.publish/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/csr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/db/package.json b/.publish/db/package.json index 4df9ef30..12298140 100644 --- a/.publish/db/package.json +++ b/.publish/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/db — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/dev-server/package.json b/.publish/dev-server/package.json index 4953538d..58d7c866 100644 --- a/.publish/dev-server/package.json +++ b/.publish/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/dev-server — part of the WrNexus framework.", "license": "MIT", @@ -25,18 +25,18 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66", - "@wrnexus/router": "^0.2.66", - "@wrnexus/ssr": "^0.2.66", - "@wrnexus/csr": "^0.2.66", - "@wrnexus/compiler": "^0.2.66", - "@wrnexus/styles": "^0.2.66", - "@wrnexus/ui": "^0.2.66", - "@wrnexus/validation": "^0.2.66", - "@wrnexus/i18n": "^0.2.66", - "@wrnexus/db": "^0.2.66", - "@wrnexus/pubsub": "^0.2.66", - "@wrnexus/uploader": "^0.2.66" + "@wrnexus/core": "^0.2.67", + "@wrnexus/router": "^0.2.67", + "@wrnexus/ssr": "^0.2.67", + "@wrnexus/csr": "^0.2.67", + "@wrnexus/compiler": "^0.2.67", + "@wrnexus/styles": "^0.2.67", + "@wrnexus/ui": "^0.2.67", + "@wrnexus/validation": "^0.2.67", + "@wrnexus/i18n": "^0.2.67", + "@wrnexus/db": "^0.2.67", + "@wrnexus/pubsub": "^0.2.67", + "@wrnexus/uploader": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/encryption/package.json b/.publish/encryption/package.json index 77b5dca9..f8b7e083 100644 --- a/.publish/encryption/package.json +++ b/.publish/encryption/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/encryption", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/encryption — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/helpers/package.json b/.publish/helpers/package.json index 9e3dd3c1..8b9c09e2 100644 --- a/.publish/helpers/package.json +++ b/.publish/helpers/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/helpers", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "Safe convenience helpers for WrNexus request contexts and common application flows.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/i18n/package.json b/.publish/i18n/package.json index 67437178..e02f0f6e 100644 --- a/.publish/i18n/package.json +++ b/.publish/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/i18n", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/i18n — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/jwt/package.json b/.publish/jwt/package.json index f5247879..e612638e 100644 --- a/.publish/jwt/package.json +++ b/.publish/jwt/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/jwt", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/jwt — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/mobile/package.json b/.publish/mobile/package.json index a27ee891..6dc73612 100644 --- a/.publish/mobile/package.json +++ b/.publish/mobile/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/mobile", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/mobile — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/native": "^0.2.66" + "@wrnexus/native": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/native/package.json b/.publish/native/package.json index bf6d178a..aaa87627 100644 --- a/.publish/native/package.json +++ b/.publish/native/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/native", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/native — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/oauth/package.json b/.publish/oauth/package.json index fdaf6c0f..091666ca 100644 --- a/.publish/oauth/package.json +++ b/.publish/oauth/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/oauth", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/oauth — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/pubsub/package.json b/.publish/pubsub/package.json index 90b84175..d89c9acb 100644 --- a/.publish/pubsub/package.json +++ b/.publish/pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/pubsub", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/pubsub — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/queue/package.json b/.publish/queue/package.json index e88c4468..bc9cfddf 100644 --- a/.publish/queue/package.json +++ b/.publish/queue/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/queue", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/queue — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/reactive/package.json b/.publish/reactive/package.json index 10621472..a93c840e 100644 --- a/.publish/reactive/package.json +++ b/.publish/reactive/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/reactive", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/reactive — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/router/package.json b/.publish/router/package.json index c710f7f0..7b7547b4 100644 --- a/.publish/router/package.json +++ b/.publish/router/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/router", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/router — part of the WrNexus framework.", "license": "MIT", @@ -21,8 +21,8 @@ } }, "dependencies": { - "@wrnexus/compiler": "^0.2.66", - "@wrnexus/core": "^0.2.66" + "@wrnexus/compiler": "^0.2.67", + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/ssr/package.json b/.publish/ssr/package.json index c2eeb481..73cac920 100644 --- a/.publish/ssr/package.json +++ b/.publish/ssr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ssr", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/ssr — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/styles/package.json b/.publish/styles/package.json index 547f10e7..0efd9a5f 100644 --- a/.publish/styles/package.json +++ b/.publish/styles/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/styles", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/styles — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/uploader": "^0.2.66" + "@wrnexus/uploader": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/test/package.json b/.publish/test/package.json index 8de37cc3..db21f77a 100644 --- a/.publish/test/package.json +++ b/.publish/test/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/test", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/test — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/tracking/package.json b/.publish/tracking/package.json index 04e62dd9..fd7cbb81 100644 --- a/.publish/tracking/package.json +++ b/.publish/tracking/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/tracking", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/tracking — part of the WrNexus framework.", "license": "MIT", diff --git a/.publish/ui/package.json b/.publish/ui/package.json index 81f9931d..bcfab30c 100644 --- a/.publish/ui/package.json +++ b/.publish/ui/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ui", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/ui — part of the WrNexus framework.", "license": "MIT", @@ -25,7 +25,7 @@ "./ui.css": "./ui.css" }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist", diff --git a/.publish/uploader/package.json b/.publish/uploader/package.json index 3fbd0606..0f738ece 100644 --- a/.publish/uploader/package.json +++ b/.publish/uploader/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/uploader", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/uploader — part of the WrNexus framework.", "license": "MIT", @@ -21,7 +21,7 @@ } }, "dependencies": { - "@wrnexus/core": "^0.2.66" + "@wrnexus/core": "^0.2.67" }, "files": [ "dist" diff --git a/.publish/validation/package.json b/.publish/validation/package.json index 2b431d04..693d4cec 100644 --- a/.publish/validation/package.json +++ b/.publish/validation/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/validation", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "description": "@wrnexus/validation — part of the WrNexus framework.", "license": "MIT", diff --git a/packages/ai/package.json b/packages/ai/package.json index 98bb04bc..fdab59b6 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/ai", - "version": "0.2.66", + "version": "0.2.67", "private": true, "type": "module", "description": "Zero-dependency Claude (Anthropic) client for WrNexus apps.", diff --git a/packages/authz/package.json b/packages/authz/package.json index b26e9fab..dc5c5ab8 100644 --- a/packages/authz/package.json +++ b/packages/authz/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/authz", - "version": "0.2.66", + "version": "0.2.67", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/cli/package.json b/packages/cli/package.json index f4b4a9f7..d9329c4c 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/cli", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 3b3b1b30..3a3b1820 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -65,6 +65,11 @@ Usage: Profiles: pass --profile= to dev/build/db (or set WRNEXUS_PROFILE) to load that profile's .env cascade and config overrides. e.g. wrnexus dev --profile=uat +Workspace environments: configure protocol, rootDomain, port, hostname, runtime +(development|production), hmr, build, migrate, and profile in wrnexus.workspace.ts. +CLI overrides: --port, --host, --build/--no-build, --migrate/--no-migrate, +--profile, and --prepare-only. + Update options: --dry-run previews changes; --no-verify skips post-update check/build. `); } @@ -210,7 +215,16 @@ async function main(): Promise { if (!knownEnvironments.has(command)) throw new Error("unknown-environment-command"); const environmentArgs = rest.filter((arg) => arg !== workspaceRoot); environmentArgs.push(`--environment=${command}`); - if (command === "development") { + const environmentConfig = workspace.environments?.[command]; + const runtime = + environmentConfig?.runtime ?? (command === "development" ? "development" : "production"); + if (runtime === "development") { + if (environmentArgs.includes("--prepare-only")) { + console.log( + `\n ✓ Workspace environment "${command}" uses the development runtime (HMR ${environmentConfig?.hmr === false ? "disabled" : "enabled"}).\n`, + ); + break; + } await runGateway(workspaceRoot, environmentArgs); } else { await runProduction(workspaceRoot, environmentArgs); diff --git a/packages/cli/src/update.ts b/packages/cli/src/update.ts index 83a38b80..4b69ffc8 100644 --- a/packages/cli/src/update.ts +++ b/packages/cli/src/update.ts @@ -664,6 +664,15 @@ const MIGRATIONS: Migration[] = [ // Existing workspaces gain the CLI command aliases and optional UI props automatically. }, }, + { + version: "0.2.67", + id: "configurable-workspace-environment-runtime", + description: + "Adds per-environment runtime, HMR, build, migration, profile, hostname, and port controls to workspace commands.", + apply() { + // Existing workspace configurations remain compatible; new controls are opt-in. + }, + }, ]; /** Release tooling uses this to require an explicit migration entry per version. */ diff --git a/packages/cli/src/workspace.ts b/packages/cli/src/workspace.ts index a2f6e560..3dc61013 100644 --- a/packages/cli/src/workspace.ts +++ b/packages/cli/src/workspace.ts @@ -25,6 +25,12 @@ export interface WorkspaceEnvironmentConfig { protocol?: "http" | "https"; rootDomain?: string; port?: number; + hostname?: string; + runtime?: "development" | "production"; + hmr?: boolean; + build?: boolean; + migrate?: boolean; + profile?: string; } export interface WorkspaceApp { @@ -54,7 +60,7 @@ export interface WorkspaceApp { export interface WorkspaceConfig { defaultEnvironment?: WorkspaceEnvironment; - environments?: Record; + environments?: Partial>; apps: WorkspaceApp[]; security?: GatewaySecurity; @@ -81,6 +87,12 @@ export const workspaceFiles = (name: string): Record => ({ // Map each app to the domains it serves. \`wrnexus gateway\` runs them all behind // one port and routes by Host header (add these hosts to your /etc/hosts). const config: WorkspaceConfig = { + defaultEnvironment: "development", + environments: { + development: { protocol: "http", rootDomain: "localhost", port: 3000, runtime: "development", hmr: true, build: false, migrate: false }, + staging: { protocol: "https", rootDomain: "staging.example.com", port: 443, runtime: "production", hmr: false, build: true, migrate: true }, + production: { protocol: "https", rootDomain: "example.com", port: 443, runtime: "production", hmr: false, build: true, migrate: true }, + }, // Gateway-wide security (all optional): security: { trustedHostsOnly: true, // reject requests for unknown domains @@ -398,26 +410,23 @@ export async function runGateway(root: string, args: string[]): Promise { (args.includes("--prod") ? "production" : config.defaultEnvironment || "development"); const resolved = resolveWorkspaceConfig(config, environment); + const environmentConfig = resolved.config; + const runtime = + environmentConfig.runtime ?? (environment === "development" ? "development" : "production"); const port = portArg ? Number(portArg.split("=")[1]) - : resolved.environment === "development" - ? 3000 - : 80; + : (environmentConfig.port ?? (runtime === "development" ? 3000 : 80)); - const hostname = hostArg?.split("=")[1]; + const hostname = hostArg?.split("=")[1] ?? environmentConfig.hostname; const { startGateway } = await import("@wrnexus/dev-server"); await startGateway({ port, hostname, - mode: - environment === "production" || - args.includes("--prod") || - process.env.NODE_ENV === "production" - ? "production" - : "development", + mode: args.includes("--prod") || process.env.NODE_ENV === "production" ? "production" : runtime, + hmr: runtime === "development" ? (environmentConfig.hmr ?? true) : false, environment, security: resolved.security, apps: resolved.apps.map((app) => ({ @@ -458,16 +467,21 @@ export function workspaceMigrationTargets(appRoot: string): Array export async function runProduction(root: string, args: string[]): Promise { const workspaceRoot = resolve(root); const config = await loadWorkspaceConfig(workspaceRoot); - const shouldBuild = !args.includes("--no-build"); - const shouldMigrate = !args.includes("--no-migrate"); const prepareOnly = args.includes("--prepare-only"); const environmentArg = args.find((arg) => arg.startsWith("--environment=")); const profileArg = args.find((arg) => arg.startsWith("--profile=")); const environment = environmentArg?.split("=")[1] || profileArg?.split("=")[1] || "production"; + const environmentConfig = config.environments?.[environment] ?? {}; + const profile = environmentConfig.profile ?? environment; + const shouldBuild = + !args.includes("--no-build") && (args.includes("--build") || environmentConfig.build !== false); + const shouldMigrate = + !args.includes("--no-migrate") && + (args.includes("--migrate") || environmentConfig.migrate !== false); process.env.NODE_ENV = "production"; process.env.WRNEXUS_ENV = environment; - process.env.WRNEXUS_PROFILE = environment; + process.env.WRNEXUS_PROFILE = profile; console.log(`\n ⚡ Preparing WRNexus workspace (${environment})\n`); if (shouldBuild) { @@ -512,8 +526,12 @@ export async function runProduction(root: string, args: string[]): Promise gatewayArgs.push(`--environment=${environment}`); } if (!gatewayArgs.includes("--prod")) gatewayArgs.push("--prod"); - if (!gatewayArgs.some((arg) => arg.startsWith("--host="))) gatewayArgs.push("--host=0.0.0.0"); - if (!gatewayArgs.some((arg) => arg.startsWith("--port="))) gatewayArgs.push("--port=3000"); + if ( + !gatewayArgs.some((arg) => arg.startsWith("--host=")) && + environmentConfig.hostname === undefined + ) { + gatewayArgs.push("--host=0.0.0.0"); + } await runGateway(workspaceRoot, gatewayArgs); } @@ -555,6 +573,7 @@ export interface ResolvedWorkspaceApp extends WorkspaceApp { export interface ResolvedWorkspaceConfig { environment: WorkspaceEnvironment; + config: WorkspaceEnvironmentConfig; apps: ResolvedWorkspaceApp[]; security?: GatewaySecurity; } @@ -635,6 +654,7 @@ export function resolveWorkspaceConfig( return { environment, + config: environmentConfig, apps, security: config.security, }; diff --git a/packages/cli/test/workspace.test.ts b/packages/cli/test/workspace.test.ts index d547430c..42a5f31a 100644 --- a/packages/cli/test/workspace.test.ts +++ b/packages/cli/test/workspace.test.ts @@ -6,10 +6,36 @@ import { currentCliVersion } from "../src/update-notifier.ts"; import { addWorkspaceApp, insertWorkspaceApp, + resolveWorkspaceConfig, workspaceFiles, workspaceMigrationTargets, } from "../src/workspace.ts"; +test("workspace environments preserve runtime and HMR policy", () => { + const resolved = resolveWorkspaceConfig( + { + environments: { + staging: { + protocol: "https", + rootDomain: "staging.example.com", + port: 443, + runtime: "development", + hmr: false, + build: false, + migrate: false, + profile: "uat", + }, + }, + apps: [{ name: "web", dir: "apps/web", subdomain: "www" }], + }, + "staging", + ); + expect(resolved.config.runtime).toBe("development"); + expect(resolved.config.hmr).toBe(false); + expect(resolved.config.profile).toBe("uat"); + expect(resolved.apps[0]?.publicOrigin).toBe("https://www.staging.example.com"); +}); + test("workspace templates pin the running framework release", () => { const files = workspaceFiles("acme"); const rootPackage = JSON.parse(files["package.json"]!); @@ -21,6 +47,8 @@ test("workspace templates pin the running framework release", () => { expect(files["README.md"]).toContain("http://127.0.0.1:3000"); expect(files["README.md"]).toContain("internal gateway targets"); expect(JSON.parse(files["package.json"]!).scripts.production).toBe("wrnexus production"); + expect(files["wrnexus.workspace.ts"]).toContain('runtime: "development"'); + expect(files["wrnexus.workspace.ts"]).toContain("hmr: false"); }); test("production workspace detects default and named SQL migrations", () => { diff --git a/packages/compiler/package.json b/packages/compiler/package.json index 8bbb5815..98037a64 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/compiler", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/core/package.json b/packages/core/package.json index 01f9b95d..673cee0b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/core", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/csr/package.json b/packages/csr/package.json index 40d471cd..cee1e490 100644 --- a/packages/csr/package.json +++ b/packages/csr/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/csr", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/db/package.json b/packages/db/package.json index dd812cff..445f0c59 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/db", - "version": "0.2.66", + "version": "0.2.67", "private": true, "type": "module", "main": "src/index.ts", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index f31ef77e..90fdeafb 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.2.66", + "version": "0.2.67", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/dev-server/src/gateway.ts b/packages/dev-server/src/gateway.ts index 1f577906..5d857b19 100644 --- a/packages/dev-server/src/gateway.ts +++ b/packages/dev-server/src/gateway.ts @@ -70,6 +70,7 @@ export interface GatewayOptions { hostname?: string; mode?: "development" | "production"; environment?: string; + hmr?: boolean; apps: GatewayApp[]; security?: GatewaySecurity; } @@ -348,7 +349,14 @@ export async function startGateway(opts: GatewayOptions): Promise