nurjs-cli 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +36 -5
  2. package/dist/commands/doctor.command.d.ts.map +1 -1
  3. package/dist/commands/doctor.command.js +67 -18
  4. package/dist/commands/doctor.command.js.map +1 -1
  5. package/dist/commands/init.command.d.ts.map +1 -1
  6. package/dist/commands/init.command.js +36 -5
  7. package/dist/commands/init.command.js.map +1 -1
  8. package/dist/commands/start.command.d.ts.map +1 -1
  9. package/dist/commands/start.command.js +22 -8
  10. package/dist/commands/start.command.js.map +1 -1
  11. package/dist/config/loadNurConfig.d.ts.map +1 -1
  12. package/dist/config/loadNurConfig.js +58 -25
  13. package/dist/config/loadNurConfig.js.map +1 -1
  14. package/dist/config/runtimeConfig.d.ts +23 -0
  15. package/dist/config/runtimeConfig.d.ts.map +1 -0
  16. package/dist/config/runtimeConfig.js +75 -0
  17. package/dist/config/runtimeConfig.js.map +1 -0
  18. package/dist/config/runtimeValidation.d.ts +11 -0
  19. package/dist/config/runtimeValidation.d.ts.map +1 -0
  20. package/dist/config/runtimeValidation.js +128 -0
  21. package/dist/config/runtimeValidation.js.map +1 -0
  22. package/dist/utils/packageMetadata.d.ts +1 -0
  23. package/dist/utils/packageMetadata.d.ts.map +1 -1
  24. package/dist/utils/packageMetadata.js +12 -3
  25. package/dist/utils/packageMetadata.js.map +1 -1
  26. package/package.json +5 -1
  27. package/templates/app/.env.example.tpl +2 -2
  28. package/templates/app/.env.tpl +2 -2
  29. package/templates/app/README.md.tpl +28 -1
  30. package/templates/app/src/config/app.config.ts.tpl +2 -2
  31. package/templates/app/src/config/env.ts.tpl +75 -15
  32. package/templates/app/src/shared/logger/logger.ts.tpl +2 -1
package/README.md CHANGED
@@ -36,6 +36,33 @@ nur start
36
36
 
37
37
  The generated application includes a health endpoint at `/api/v1/health` by default.
38
38
 
39
+ ## Runtime configuration
40
+
41
+ `nur init` writes project defaults to `nur.config.ts`. Runtime values can override those defaults through the configured env file or the process environment.
42
+
43
+ Precedence for runtime values is:
44
+
45
+ ```txt
46
+ process environment > env file > nur.config.ts
47
+ ```
48
+
49
+ Canonical variables are:
50
+
51
+ ```txt
52
+ HOST
53
+ PORT
54
+ API_PREFIX
55
+ APP_NAME
56
+ NODE_ENV
57
+ CORS_ENABLED
58
+ CORS_ORIGIN
59
+ LOG_LEVEL
60
+ ```
61
+
62
+ `APP_HOST` and `APP_PORT` remain supported as deprecated compatibility aliases for older generated projects. New projects use `HOST` and `PORT`.
63
+
64
+ The env file path comes from `paths.envFile` in `nur.config.ts`. `NUR_ENV_FILE` can override that path for a specific process.
65
+
39
66
  ## Commands
40
67
 
41
68
  ### `nur init [projectName]`
@@ -47,17 +74,19 @@ nur init my-api
47
74
  nur init my-api --no-install
48
75
  ```
49
76
 
50
- The wizard supports pnpm, npm, and Yarn.
77
+ The wizard supports pnpm, npm, and Yarn and validates the server port, host, API prefix, and CORS origin before creating the project.
51
78
 
52
79
  ### `nur start`
53
80
 
54
- Runs the current project through its configured package manager and local `tsx` installation.
81
+ Loads `nur.config.ts`, resolves the configured env file, validates the runtime configuration, and runs the current project through its configured package manager and local `tsx` installation.
55
82
 
56
83
  ```bash
57
84
  nur start
58
85
  nur start --no-watch
59
86
  ```
60
87
 
88
+ The host, port, API prefix, environment, and health URL printed by the CLI are the resolved runtime values used by the child process.
89
+
61
90
  ### `nur install <moduleName>`
62
91
 
63
92
  Installs a module template, updates the module registry, updates environment examples and package dependencies, and installs the declared dependencies by default.
@@ -69,7 +98,7 @@ nur install auth --no-install
69
98
  nur install auth --force
70
99
  ```
71
100
 
72
- In v0.1.0 the available installable template is:
101
+ The currently available installable template is:
73
102
 
74
103
  ```txt
75
104
  auth Authentication module skeleton
@@ -87,15 +116,17 @@ nur list
87
116
 
88
117
  ### `nur doctor`
89
118
 
90
- Checks the expected NurJS project files, registry markers, environment files, and health module registration.
119
+ Checks the configured project paths, loads and validates `nur.config.ts`, validates the resolved runtime configuration, checks registry markers, and verifies health module registration.
91
120
 
92
121
  ```bash
93
122
  nur doctor
94
123
  ```
95
124
 
125
+ A missing env file is reported as a warning because deployment environments may provide runtime variables directly.
126
+
96
127
  ## Generated project
97
128
 
98
- A generated project uses `nurjs` as its core dependency and includes scripts for development, type checking, building, and production startup.
129
+ A generated project uses `nurjs-core` as its core dependency and includes scripts for development, type checking, building, and production startup.
99
130
 
100
131
  Default API prefix:
101
132
 
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAQA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA8BnD"}
1
+ {"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAsBA,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CA0FnD"}
@@ -1,32 +1,81 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- function check(label, passed) {
3
+ import { loadNurConfig } from "../config/loadNurConfig.js";
4
+ import { resolveRuntimeConfig } from "../config/runtimeConfig.js";
5
+ function check(state, label, passed) {
4
6
  console.log(`${passed ? "✓" : "✗"} ${label}`);
7
+ state.checks.push(passed);
5
8
  return passed;
6
9
  }
10
+ function warn(state, label) {
11
+ console.log(`! ${label}`);
12
+ state.warnings += 1;
13
+ }
7
14
  export async function doctorCommand() {
8
15
  const cwd = process.cwd();
16
+ const state = {
17
+ checks: [],
18
+ warnings: 0,
19
+ };
9
20
  console.log("NurJS Doctor\n");
10
- const checks = [];
11
- checks.push(check("nur.config.ts found", existsSync(resolve(cwd, "nur.config.ts"))));
12
- checks.push(check("package.json found", existsSync(resolve(cwd, "package.json"))));
13
- checks.push(check("tsconfig.json found", existsSync(resolve(cwd, "tsconfig.json"))));
14
- checks.push(check("src/main.ts found", existsSync(resolve(cwd, "src/main.ts"))));
15
- checks.push(check("src/app.ts found", existsSync(resolve(cwd, "src/app.ts"))));
16
- checks.push(check("src/modules/index.ts found", existsSync(resolve(cwd, "src/modules/index.ts"))));
17
- checks.push(check(".env found", existsSync(resolve(cwd, ".env"))));
18
- checks.push(check(".env.example found", existsSync(resolve(cwd, ".env.example"))));
19
- const modulesIndexPath = resolve(cwd, "src/modules/index.ts");
21
+ const configPath = resolve(cwd, "nur.config.ts");
22
+ if (!check(state, "nur.config.ts found", existsSync(configPath))) {
23
+ console.log("\nProject has issues. Review the failed checks above.");
24
+ process.exitCode = 1;
25
+ return;
26
+ }
27
+ let config;
28
+ try {
29
+ config = await loadNurConfig(cwd);
30
+ check(state, "nur.config.ts valid", true);
31
+ }
32
+ catch (error) {
33
+ check(state, "nur.config.ts valid", false);
34
+ console.log(` ${error instanceof Error ? error.message : String(error)}`);
35
+ console.log("\nProject has issues. Review the failed checks above.");
36
+ process.exitCode = 1;
37
+ return;
38
+ }
39
+ check(state, "package.json found", existsSync(resolve(cwd, "package.json")));
40
+ check(state, `${config.runtime.tsconfig} found`, existsSync(resolve(cwd, config.runtime.tsconfig)));
41
+ check(state, `${config.runtime.entry} found`, existsSync(resolve(cwd, config.runtime.entry)));
42
+ check(state, `${config.paths.srcDir}/app.ts found`, existsSync(resolve(cwd, config.paths.srcDir, "app.ts")));
43
+ check(state, `${config.paths.moduleRegistryFile} found`, existsSync(resolve(cwd, config.paths.moduleRegistryFile)));
44
+ const envPath = resolve(cwd, process.env.NUR_ENV_FILE?.trim() || config.paths.envFile);
45
+ const envExamplePath = resolve(cwd, config.paths.envExampleFile);
46
+ if (existsSync(envPath)) {
47
+ check(state, `${process.env.NUR_ENV_FILE?.trim() || config.paths.envFile} found`, true);
48
+ }
49
+ else {
50
+ warn(state, `${process.env.NUR_ENV_FILE?.trim() || config.paths.envFile} not found. Runtime must be configured through process environment or project defaults.`);
51
+ }
52
+ check(state, `${config.paths.envExampleFile} found`, existsSync(envExamplePath));
53
+ try {
54
+ const runtime = await resolveRuntimeConfig(cwd, config);
55
+ check(state, `runtime config valid (${runtime.host}:${runtime.port}${runtime.apiPrefix})`, true);
56
+ for (const warning of runtime.warnings) {
57
+ warn(state, warning);
58
+ }
59
+ }
60
+ catch (error) {
61
+ check(state, "runtime config valid", false);
62
+ console.log(` ${error instanceof Error ? error.message : String(error)}`);
63
+ }
64
+ const modulesIndexPath = resolve(cwd, config.paths.moduleRegistryFile);
20
65
  if (existsSync(modulesIndexPath)) {
21
66
  const content = readFileSync(modulesIndexPath, "utf8");
22
- checks.push(check("module import marker found", content.includes("// nur:imports:start")));
23
- checks.push(check("module registry marker found", content.includes("// nur:modules:start")));
24
- checks.push(check("health module registered", content.includes("healthModule")));
67
+ check(state, "module import marker found", content.includes("// nur:imports:start"));
68
+ check(state, "module registry marker found", content.includes("// nur:modules:start"));
69
+ check(state, "health module registered", content.includes("healthModule"));
25
70
  }
26
- const passed = checks.every(Boolean);
27
- console.log(passed ? "\nProject looks healthy." : "\nProject has issues. Review the failed checks above.");
28
- if (!passed) {
29
- process.exitCode = 1;
71
+ const passed = state.checks.every(Boolean);
72
+ if (passed) {
73
+ console.log(state.warnings > 0
74
+ ? `\nProject looks healthy with ${state.warnings} warning${state.warnings === 1 ? "" : "s"}.`
75
+ : "\nProject looks healthy.");
76
+ return;
30
77
  }
78
+ console.log("\nProject has issues. Review the failed checks above.");
79
+ process.exitCode = 1;
31
80
  }
32
81
  //# sourceMappingURL=doctor.command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"doctor.command.js","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,SAAS,KAAK,CAAC,KAAa,EAAE,MAAe;IAC3C,OAAO,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,EAAE,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAE9B,MAAM,MAAM,GAAc,EAAE,CAAC;IAE7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,mBAAmB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IACjF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,kBAAkB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/E,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,4BAA4B,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;IACnG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,oBAAoB,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IAEnF,MAAM,gBAAgB,GAAG,OAAO,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAC9D,IAAI,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;QACvD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,4BAA4B,EAAE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC3F,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,8BAA8B,EAAE,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC;QAC7F,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,0BAA0B,EAAE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAErC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,uDAAuD,CAAC,CAAC;IAE3G,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"doctor.command.js","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"init.command.d.ts","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"AAQA,KAAK,WAAW,GAAG;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAiCF,wBAAsB,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAuGzG"}
1
+ {"version":3,"file":"init.command.d.ts","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"AAaA,KAAK,WAAW,GAAG;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AA4DF,wBAAsB,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA0GzG"}
@@ -2,8 +2,9 @@ import { confirm, input, select } from "@inquirer/prompts";
2
2
  import { existsSync } from "node:fs";
3
3
  import { resolve } from "node:path";
4
4
  import { copyTemplateDirectory } from "../utils/fileSystem.js";
5
- import { getCliPackageVersion } from "../utils/packageMetadata.js";
5
+ import { getNurCoreVersionRange } from "../utils/packageMetadata.js";
6
6
  import { runCommand } from "../utils/runCommand.js";
7
+ import { normalizeApiPrefix, normalizeCorsOrigin, normalizeHost, } from "../config/runtimeValidation.js";
7
8
  import { getPackageRoot, getTemplatesDir } from "../utils/paths.js";
8
9
  function normalizeProjectName(value) {
9
10
  return value
@@ -30,6 +31,33 @@ function validatePort(value) {
30
31
  const port = Number(value);
31
32
  return port >= 1 && port <= 65535 ? true : "Port must be between 1 and 65535";
32
33
  }
34
+ function validateHost(value) {
35
+ try {
36
+ normalizeHost(value);
37
+ return true;
38
+ }
39
+ catch (error) {
40
+ return error instanceof Error ? error.message : "Invalid host";
41
+ }
42
+ }
43
+ function validateApiPrefix(value) {
44
+ try {
45
+ normalizeApiPrefix(value);
46
+ return true;
47
+ }
48
+ catch (error) {
49
+ return error instanceof Error ? error.message : "Invalid API prefix";
50
+ }
51
+ }
52
+ function validateCorsOrigin(value) {
53
+ try {
54
+ normalizeCorsOrigin(value);
55
+ return true;
56
+ }
57
+ catch (error) {
58
+ return error instanceof Error ? error.message : "Invalid CORS origin";
59
+ }
60
+ }
33
61
  export async function initCommand(projectNameArg, options) {
34
62
  const requestedProjectName = projectNameArg ||
35
63
  (await input({
@@ -62,14 +90,17 @@ export async function initCommand(projectNameArg, options) {
62
90
  const host = await input({
63
91
  message: "Server host:",
64
92
  default: "127.0.0.1",
93
+ validate: validateHost,
65
94
  });
66
95
  const apiPrefix = await input({
67
96
  message: "API prefix:",
68
97
  default: "/api/v1",
98
+ validate: validateApiPrefix,
69
99
  });
70
100
  const corsOrigin = await input({
71
101
  message: "CORS origin:",
72
102
  default: "http://localhost:3000",
103
+ validate: validateCorsOrigin,
73
104
  });
74
105
  const shouldInstall = options.install === false
75
106
  ? false
@@ -90,7 +121,7 @@ export async function initCommand(projectNameArg, options) {
90
121
  existsSync(resolve(corePackageRoot, "package.json"));
91
122
  const nurCoreDependency = isLocalMonorepo
92
123
  ? `file:${corePackageRoot}`
93
- : `^${getCliPackageVersion()}`;
124
+ : getNurCoreVersionRange();
94
125
  console.log("\nCreating NurJS project...\n");
95
126
  await copyTemplateDirectory({
96
127
  from: appTemplateDir,
@@ -100,9 +131,9 @@ export async function initCommand(projectNameArg, options) {
100
131
  DESCRIPTION: description,
101
132
  PACKAGE_MANAGER: packageManager,
102
133
  APP_PORT: port,
103
- APP_HOST: host,
104
- API_PREFIX: apiPrefix,
105
- CORS_ORIGIN: corsOrigin,
134
+ APP_HOST: normalizeHost(host),
135
+ API_PREFIX: normalizeApiPrefix(apiPrefix),
136
+ CORS_ORIGIN: normalizeCorsOrigin(corsOrigin),
106
137
  NUR_CORE_DEP: nurCoreDependency,
107
138
  },
108
139
  });
@@ -1 +1 @@
1
- {"version":3,"file":"init.command.js","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"init.command.js","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"start.command.d.ts","sourceRoot":"","sources":["../../src/commands/start.command.ts"],"names":[],"mappings":"AAMA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA6BF,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6CvE"}
1
+ {"version":3,"file":"start.command.d.ts","sourceRoot":"","sources":["../../src/commands/start.command.ts"],"names":[],"mappings":"AAWA,KAAK,YAAY,GAAG;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AA6BF,wBAAsB,YAAY,CAChC,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CA2Ef"}
@@ -1,8 +1,10 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { existsSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
- import { spawn } from "node:child_process";
4
4
  import { assertNurProject } from "../config/projectDetector.js";
5
5
  import { loadNurConfig } from "../config/loadNurConfig.js";
6
+ import { createRuntimeProcessEnv, resolveRuntimeConfig, } from "../config/runtimeConfig.js";
7
+ import { formatHostForUrl } from "../config/runtimeValidation.js";
6
8
  function createTsxCommand(packageManager, entry, watch) {
7
9
  const tsxArgs = watch ? ["watch", entry] : [entry];
8
10
  if (packageManager === "pnpm") {
@@ -26,26 +28,38 @@ export async function startCommand(options) {
26
28
  const cwd = process.cwd();
27
29
  assertNurProject(cwd);
28
30
  const config = await loadNurConfig(cwd);
31
+ const runtime = await resolveRuntimeConfig(cwd, config);
29
32
  const entryPath = resolve(cwd, config.runtime.entry);
30
33
  if (!existsSync(entryPath)) {
31
34
  throw new Error(`Entry file not found: ${config.runtime.entry}`);
32
35
  }
36
+ const healthHost = formatHostForUrl(runtime.host);
33
37
  console.log("NurJS starting...\n");
34
- console.log(`Project: ${config.projectName}`);
38
+ console.log(`Project: ${runtime.appName}`);
35
39
  console.log(`Entry: ${config.runtime.entry}`);
36
- console.log(`Host: ${config.server.host}`);
37
- console.log(`Port: ${config.server.port}`);
38
- console.log(`API prefix: ${config.server.apiPrefix}`);
39
- console.log(`Health: http://${config.server.host}:${config.server.port}${config.server.apiPrefix}/health\n`);
40
+ console.log(`Host: ${runtime.host}`);
41
+ console.log(`Port: ${runtime.port}`);
42
+ console.log(`API prefix: ${runtime.apiPrefix}`);
43
+ console.log(`Environment: ${runtime.nodeEnv}`);
44
+ console.log(`Env file: ${runtime.envFile}${runtime.envFileExists
45
+ ? ""
46
+ : " (not found, using process environment and project defaults)"}`);
47
+ console.log(`Health: http://${healthHost}:${runtime.port}${runtime.apiPrefix}/health\n`);
48
+ for (const warning of runtime.warnings) {
49
+ console.warn(`Warning: ${warning}`);
50
+ }
51
+ if (runtime.warnings.length > 0) {
52
+ console.log();
53
+ }
40
54
  const runner = createTsxCommand(config.packageManager, config.runtime.entry, options.watch !== false);
41
55
  const child = spawn(runner.command, runner.args, {
42
56
  cwd,
43
57
  stdio: "inherit",
44
58
  shell: process.platform === "win32",
45
- env: {
59
+ env: createRuntimeProcessEnv(runtime, {
46
60
  ...process.env,
47
61
  NUR_PROJECT_NAME: config.projectName,
48
- },
62
+ }),
49
63
  });
50
64
  child.on("error", (error) => {
51
65
  console.error(`Failed to start ${runner.command}: ${error.message}`);
@@ -1 +1 @@
1
- {"version":3,"file":"start.command.js","sourceRoot":"","sources":["../../src/commands/start.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAM3D,SAAS,gBAAgB,CACvB,cAAuC,EACvC,KAAa,EACb,KAAc;IAEd,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEnD,IAAI,cAAc,KAAK,MAAM,EAAE,CAAC;QAC9B,OAAO;YACL,OAAO,EAAE,MAAM;YACf,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;SAClC,CAAC;IACJ,CAAC;IAED,IAAI,cAAc,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,IAAI,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,OAAO,EAAE,MAAM;QACf,IAAI,EAAE,CAAC,KAAK,EAAE,GAAG,OAAO,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAAqB;IACtD,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1B,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAEtB,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAErD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,yBAAyB,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACnE,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,UAAU,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3C,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IACtD,OAAO,CAAC,GAAG,CAAC,kBAAkB,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,WAAW,CAAC,CAAC;IAE7G,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IACtG,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE;QAC/C,GAAG;QACH,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,OAAO,CAAC,QAAQ,KAAK,OAAO;QACnC,GAAG,EAAE;YACH,GAAG,OAAO,CAAC,GAAG;YACd,gBAAgB,EAAE,MAAM,CAAC,WAAW;SACrC;KACF,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;QAC1B,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACrE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;QACzB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;YACxB,OAAO;QACT,CAAC;QAED,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACnC,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"start.command.js","sourceRoot":"","sources":["../../src/commands/start.command.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"loadNurConfig.d.ts","sourceRoot":"","sources":["../../src/config/loadNurConfig.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAcnD,wBAAsB,aAAa,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyBlF"}
1
+ {"version":3,"file":"loadNurConfig.d.ts","sourceRoot":"","sources":["../../src/config/loadNurConfig.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAgEnD,wBAAsB,aAAa,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAoBlF"}
@@ -1,38 +1,71 @@
1
- import { readFile } from "node:fs/promises";
1
+ import { existsSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- function matchString(content, key, fallback) {
4
- const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
5
- const match = content.match(new RegExp(`${escaped}\\s*:\\s*[\"']([^\"']+)[\"']`));
6
- return match?.[1] ?? fallback;
3
+ import { pathToFileURL } from "node:url";
4
+ import { normalizeApiPrefix, normalizeHost, parsePort, } from "./runtimeValidation.js";
5
+ function isRecord(value) {
6
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7
7
  }
8
- function matchNumber(content, key, fallback) {
9
- const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10
- const match = content.match(new RegExp(`${escaped}\\s*:\\s*(\\d+)`));
11
- return match?.[1] ? Number(match[1]) : fallback;
8
+ function requireRecord(value, label) {
9
+ if (!isRecord(value)) {
10
+ throw new Error(`${label} must be an object.`);
11
+ }
12
+ return value;
12
13
  }
13
- export async function loadNurConfig(cwd = process.cwd()) {
14
- const configPath = resolve(cwd, "nur.config.ts");
15
- const content = await readFile(configPath, "utf8");
14
+ function requireString(value, label) {
15
+ if (typeof value !== "string" || !value.trim()) {
16
+ throw new Error(`${label} must be a non-empty string.`);
17
+ }
18
+ return value.trim();
19
+ }
20
+ function requirePackageManager(value) {
21
+ if (value === "pnpm" || value === "npm" || value === "yarn") {
22
+ return value;
23
+ }
24
+ throw new Error('packageManager must be one of: "pnpm", "npm", "yarn".');
25
+ }
26
+ function validateNurConfig(value) {
27
+ const config = requireRecord(value, "NurJS config");
28
+ const runtime = requireRecord(config.runtime, "runtime");
29
+ const server = requireRecord(config.server, "server");
30
+ const paths = requireRecord(config.paths, "paths");
16
31
  return {
17
- projectName: matchString(content, "projectName", "nur-api"),
32
+ projectName: requireString(config.projectName, "projectName"),
18
33
  runtime: {
19
- entry: matchString(content, "entry", "src/main.ts"),
20
- distEntry: matchString(content, "distEntry", "dist/main.js"),
21
- tsconfig: matchString(content, "tsconfig", "tsconfig.json"),
34
+ entry: requireString(runtime.entry, "runtime.entry"),
35
+ distEntry: requireString(runtime.distEntry, "runtime.distEntry"),
36
+ tsconfig: requireString(runtime.tsconfig, "runtime.tsconfig"),
22
37
  },
23
38
  server: {
24
- host: matchString(content, "host", "127.0.0.1"),
25
- port: matchNumber(content, "port", 5000),
26
- apiPrefix: matchString(content, "apiPrefix", "/api/v1"),
39
+ host: normalizeHost(server.host, "server.host"),
40
+ port: parsePort(server.port, "server.port"),
41
+ apiPrefix: normalizeApiPrefix(server.apiPrefix, "server.apiPrefix"),
27
42
  },
28
43
  paths: {
29
- srcDir: matchString(content, "srcDir", "src"),
30
- modulesDir: matchString(content, "modulesDir", "src/modules"),
31
- moduleRegistryFile: matchString(content, "moduleRegistryFile", "src/modules/index.ts"),
32
- envFile: matchString(content, "envFile", ".env"),
33
- envExampleFile: matchString(content, "envExampleFile", ".env.example"),
44
+ srcDir: requireString(paths.srcDir, "paths.srcDir"),
45
+ modulesDir: requireString(paths.modulesDir, "paths.modulesDir"),
46
+ moduleRegistryFile: requireString(paths.moduleRegistryFile, "paths.moduleRegistryFile"),
47
+ envFile: requireString(paths.envFile, "paths.envFile"),
48
+ envExampleFile: requireString(paths.envExampleFile, "paths.envExampleFile"),
34
49
  },
35
- packageManager: matchString(content, "packageManager", "pnpm"),
50
+ packageManager: requirePackageManager(config.packageManager),
36
51
  };
37
52
  }
53
+ export async function loadNurConfig(cwd = process.cwd()) {
54
+ const configPath = resolve(cwd, "nur.config.ts");
55
+ if (!existsSync(configPath)) {
56
+ throw new Error(`NurJS config not found: ${configPath}`);
57
+ }
58
+ try {
59
+ const moduleUrl = pathToFileURL(configPath);
60
+ const loaded = (await import(moduleUrl.href));
61
+ if (loaded.default === undefined) {
62
+ throw new Error("nur.config.ts must export a default config object.");
63
+ }
64
+ return validateNurConfig(loaded.default);
65
+ }
66
+ catch (error) {
67
+ const message = error instanceof Error ? error.message : String(error);
68
+ throw new Error(`Failed to load nur.config.ts: ${message}`);
69
+ }
70
+ }
38
71
  //# sourceMappingURL=loadNurConfig.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"loadNurConfig.js","sourceRoot":"","sources":["../../src/config/loadNurConfig.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,SAAS,WAAW,CAAC,OAAe,EAAE,GAAW,EAAE,QAAgB;IACjE,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,8BAA8B,CAAC,CAAC,CAAC;IAClF,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;AAChC,CAAC;AAED,SAAS,WAAW,CAAC,OAAe,EAAE,GAAW,EAAE,QAAgB;IACjE,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,GAAG,OAAO,iBAAiB,CAAC,CAAC,CAAC;IACrE,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACrD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IAEnD,OAAO;QACL,WAAW,EAAE,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC;QAC3D,OAAO,EAAE;YACP,KAAK,EAAE,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,aAAa,CAAC;YACnD,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,cAAc,CAAC;YAC5D,QAAQ,EAAE,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,eAAe,CAAC;SAC5D;QACD,MAAM,EAAE;YACN,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC;YAC/C,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC;YACxC,SAAS,EAAE,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC;SACxD;QACD,KAAK,EAAE;YACL,MAAM,EAAE,WAAW,CAAC,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC;YAC7C,UAAU,EAAE,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,aAAa,CAAC;YAC7D,kBAAkB,EAAE,WAAW,CAAC,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,CAAC;YACtF,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC;YAChD,cAAc,EAAE,WAAW,CAAC,OAAO,EAAE,gBAAgB,EAAE,cAAc,CAAC;SACvE;QACD,cAAc,EAAE,WAAW,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAuC;KACrG,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"loadNurConfig.js","sourceRoot":"","sources":["../../src/config/loadNurConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { NurProjectConfig } from "nurjs-core";
2
+ import { type NurLogLevel, type NurNodeEnv } from "./runtimeValidation.js";
3
+ export type ResolvedRuntimeConfig = {
4
+ appName: string;
5
+ nodeEnv: NurNodeEnv;
6
+ host: string;
7
+ port: number;
8
+ apiPrefix: string;
9
+ cors: {
10
+ enabled: boolean;
11
+ origin: string;
12
+ };
13
+ logger: {
14
+ level: NurLogLevel;
15
+ };
16
+ envFile: string;
17
+ envFilePath: string;
18
+ envFileExists: boolean;
19
+ warnings: string[];
20
+ };
21
+ export declare function resolveRuntimeConfig(cwd: string, config: NurProjectConfig, processEnv?: NodeJS.ProcessEnv): Promise<ResolvedRuntimeConfig>;
22
+ export declare function createRuntimeProcessEnv(runtime: ResolvedRuntimeConfig, baseEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
23
+ //# sourceMappingURL=runtimeConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeConfig.d.ts","sourceRoot":"","sources":["../../src/config/runtimeConfig.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAQL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,UAAU,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE;QACJ,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,MAAM,EAAE;QACN,KAAK,EAAE,WAAW,CAAC;KACpB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AA+BF,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,gBAAgB,EACxB,UAAU,GAAE,MAAM,CAAC,UAAwB,GAC1C,OAAO,CAAC,qBAAqB,CAAC,CAuChC;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,GAAE,MAAM,CAAC,UAAwB,GACvC,MAAM,CAAC,UAAU,CAanB"}
@@ -0,0 +1,75 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { resolve } from "node:path";
4
+ import { parseEnv } from "node:util";
5
+ import { normalizeApiPrefix, normalizeCorsOrigin, normalizeHost, parseBoolean, parseLogLevel, parseNodeEnv, parsePort, } from "./runtimeValidation.js";
6
+ function firstDefined(...values) {
7
+ return values.find((value) => value !== undefined);
8
+ }
9
+ function mergeEnvironment(fileEnv, processEnv) {
10
+ const merged = {
11
+ ...fileEnv,
12
+ };
13
+ for (const [key, value] of Object.entries(processEnv)) {
14
+ if (value !== undefined) {
15
+ merged[key] = value;
16
+ }
17
+ }
18
+ return merged;
19
+ }
20
+ async function readEnvFile(path) {
21
+ if (!existsSync(path)) {
22
+ return {};
23
+ }
24
+ return parseEnv(await readFile(path, "utf8"));
25
+ }
26
+ export async function resolveRuntimeConfig(cwd, config, processEnv = process.env) {
27
+ const configuredEnvFile = processEnv.NUR_ENV_FILE?.trim() || config.paths.envFile;
28
+ const envFilePath = resolve(cwd, configuredEnvFile);
29
+ const fileEnv = await readEnvFile(envFilePath);
30
+ const env = mergeEnvironment(fileEnv, processEnv);
31
+ const warnings = [];
32
+ if (env.PORT === undefined && env.APP_PORT !== undefined) {
33
+ warnings.push("APP_PORT is deprecated. Use PORT instead.");
34
+ }
35
+ if (env.HOST === undefined && env.APP_HOST !== undefined) {
36
+ warnings.push("APP_HOST is deprecated. Use HOST instead.");
37
+ }
38
+ const appName = firstDefined(env.APP_NAME, config.projectName);
39
+ if (!appName?.trim()) {
40
+ throw new Error("APP_NAME cannot be empty.");
41
+ }
42
+ return {
43
+ appName: appName.trim(),
44
+ nodeEnv: parseNodeEnv(firstDefined(env.NODE_ENV, "development")),
45
+ host: normalizeHost(firstDefined(env.HOST, env.APP_HOST, config.server.host)),
46
+ port: parsePort(firstDefined(env.PORT, env.APP_PORT, String(config.server.port))),
47
+ apiPrefix: normalizeApiPrefix(firstDefined(env.API_PREFIX, config.server.apiPrefix)),
48
+ cors: {
49
+ enabled: parseBoolean(firstDefined(env.CORS_ENABLED, "true"), "CORS_ENABLED"),
50
+ origin: normalizeCorsOrigin(firstDefined(env.CORS_ORIGIN, "http://localhost:3000")),
51
+ },
52
+ logger: {
53
+ level: parseLogLevel(firstDefined(env.LOG_LEVEL, "info")),
54
+ },
55
+ envFile: configuredEnvFile,
56
+ envFilePath,
57
+ envFileExists: existsSync(envFilePath),
58
+ warnings,
59
+ };
60
+ }
61
+ export function createRuntimeProcessEnv(runtime, baseEnv = process.env) {
62
+ return {
63
+ ...baseEnv,
64
+ NUR_ENV_FILE: runtime.envFilePath,
65
+ APP_NAME: runtime.appName,
66
+ NODE_ENV: runtime.nodeEnv,
67
+ HOST: runtime.host,
68
+ PORT: String(runtime.port),
69
+ API_PREFIX: runtime.apiPrefix,
70
+ CORS_ENABLED: runtime.cors.enabled ? "true" : "false",
71
+ CORS_ORIGIN: runtime.cors.origin,
72
+ LOG_LEVEL: runtime.logger.level,
73
+ };
74
+ }
75
+ //# sourceMappingURL=runtimeConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeConfig.js","sourceRoot":"","sources":["../../src/config/runtimeConfig.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export type NurNodeEnv = "development" | "test" | "production";
2
+ export type NurLogLevel = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
3
+ export declare function normalizeHost(value: unknown, label?: string): string;
4
+ export declare function formatHostForUrl(host: string): string;
5
+ export declare function parsePort(value: unknown, label?: string): number;
6
+ export declare function normalizeApiPrefix(value: unknown, label?: string): string;
7
+ export declare function parseBoolean(value: unknown, label: string): boolean;
8
+ export declare function parseNodeEnv(value: unknown, label?: string): NurNodeEnv;
9
+ export declare function parseLogLevel(value: unknown, label?: string): NurLogLevel;
10
+ export declare function normalizeCorsOrigin(value: unknown, label?: string): string;
11
+ //# sourceMappingURL=runtimeValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeValidation.d.ts","sourceRoot":"","sources":["../../src/config/runtimeValidation.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,CAAC;AAE/D,MAAM,MAAM,WAAW,GACnB,OAAO,GACP,OAAO,GACP,MAAM,GACN,MAAM,GACN,OAAO,GACP,OAAO,CAAC;AA+BZ,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAS,GAAG,MAAM,CA0CpE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAS,GAAG,MAAM,CAahE;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,KAAK,SAAe,GACnB,MAAM,CAwBR;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAgBT;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,OAAO,EACd,KAAK,SAAa,GACjB,UAAU,CAUZ;AAED,wBAAgB,aAAa,CAC3B,KAAK,EAAE,OAAO,EACd,KAAK,SAAc,GAClB,WAAW,CAUb;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,EACd,KAAK,SAAgB,GACpB,MAAM,CA6BR"}
@@ -0,0 +1,128 @@
1
+ import { isIP } from "node:net";
2
+ const NODE_ENVS = new Set([
3
+ "development",
4
+ "test",
5
+ "production",
6
+ ]);
7
+ const LOG_LEVELS = new Set([
8
+ "trace",
9
+ "debug",
10
+ "info",
11
+ "warn",
12
+ "error",
13
+ "fatal",
14
+ ]);
15
+ function requireString(value, label) {
16
+ if (typeof value !== "string") {
17
+ throw new Error(`${label} must be a string.`);
18
+ }
19
+ const normalized = value.trim();
20
+ if (!normalized) {
21
+ throw new Error(`${label} cannot be empty.`);
22
+ }
23
+ return normalized;
24
+ }
25
+ export function normalizeHost(value, label = "HOST") {
26
+ const host = requireString(value, label);
27
+ if (/\s/.test(host)) {
28
+ throw new Error(`${label} cannot contain whitespace.`);
29
+ }
30
+ if (host.includes("/") ||
31
+ host.includes("?") ||
32
+ host.includes("#")) {
33
+ throw new Error(`${label} must be a hostname or IP address, not a URL.`);
34
+ }
35
+ if (isIP(host) !== 0) {
36
+ return host;
37
+ }
38
+ if (host.includes(":")) {
39
+ throw new Error(`${label} must not include a port. Configure the port separately.`);
40
+ }
41
+ if (host.length > 253) {
42
+ throw new Error(`${label} hostname is too long.`);
43
+ }
44
+ const labels = host.split(".");
45
+ for (const hostnameLabel of labels) {
46
+ if (hostnameLabel.length === 0 ||
47
+ hostnameLabel.length > 63 ||
48
+ !/^[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$/.test(hostnameLabel)) {
49
+ throw new Error(`${label} must be a valid hostname or IP address.`);
50
+ }
51
+ }
52
+ return host;
53
+ }
54
+ export function formatHostForUrl(host) {
55
+ return isIP(host) === 6 ? `[${host}]` : host;
56
+ }
57
+ export function parsePort(value, label = "PORT") {
58
+ const port = typeof value === "number"
59
+ ? value
60
+ : Number(requireString(value, label));
61
+ if (!Number.isInteger(port) || port < 1 || port > 65535) {
62
+ throw new Error(`${label} must be an integer between 1 and 65535.`);
63
+ }
64
+ return port;
65
+ }
66
+ export function normalizeApiPrefix(value, label = "API_PREFIX") {
67
+ const prefix = requireString(value, label);
68
+ if (!prefix.startsWith("/")) {
69
+ throw new Error(`${label} must start with "/".`);
70
+ }
71
+ if (/\s/.test(prefix) ||
72
+ prefix.includes("?") ||
73
+ prefix.includes("#")) {
74
+ throw new Error(`${label} must be a URL path without whitespace, query parameters, or fragments.`);
75
+ }
76
+ if (prefix === "/") {
77
+ throw new Error(`${label} cannot be the root path because NurJS module base paths are appended to it.`);
78
+ }
79
+ return prefix.replace(/\/+$/, "");
80
+ }
81
+ export function parseBoolean(value, label) {
82
+ if (typeof value === "boolean") {
83
+ return value;
84
+ }
85
+ const normalized = requireString(value, label).toLowerCase();
86
+ if (normalized === "true") {
87
+ return true;
88
+ }
89
+ if (normalized === "false") {
90
+ return false;
91
+ }
92
+ throw new Error(`${label} must be "true" or "false".`);
93
+ }
94
+ export function parseNodeEnv(value, label = "NODE_ENV") {
95
+ const normalized = requireString(value, label);
96
+ if (!NODE_ENVS.has(normalized)) {
97
+ throw new Error(`${label} must be one of: development, test, production.`);
98
+ }
99
+ return normalized;
100
+ }
101
+ export function parseLogLevel(value, label = "LOG_LEVEL") {
102
+ const normalized = requireString(value, label);
103
+ if (!LOG_LEVELS.has(normalized)) {
104
+ throw new Error(`${label} must be one of: trace, debug, info, warn, error, fatal.`);
105
+ }
106
+ return normalized;
107
+ }
108
+ export function normalizeCorsOrigin(value, label = "CORS_ORIGIN") {
109
+ const origin = requireString(value, label);
110
+ let parsed;
111
+ try {
112
+ parsed = new URL(origin);
113
+ }
114
+ catch {
115
+ throw new Error(`${label} must be a valid http(s) origin.`);
116
+ }
117
+ if (parsed.protocol !== "http:" &&
118
+ parsed.protocol !== "https:") {
119
+ throw new Error(`${label} must use http or https.`);
120
+ }
121
+ if ((parsed.pathname && parsed.pathname !== "/") ||
122
+ parsed.search ||
123
+ parsed.hash) {
124
+ throw new Error(`${label} must be an origin without a path, query, or fragment.`);
125
+ }
126
+ return parsed.origin;
127
+ }
128
+ //# sourceMappingURL=runtimeValidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtimeValidation.js","sourceRoot":"","sources":["../../src/config/runtimeValidation.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1,3 @@
1
1
  export declare function getCliPackageVersion(): string;
2
+ export declare function getNurCoreVersionRange(): string;
2
3
  //# sourceMappingURL=packageMetadata.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageMetadata.d.ts","sourceRoot":"","sources":["../../src/utils/packageMetadata.ts"],"names":[],"mappings":"AAOA,wBAAgB,oBAAoB,IAAI,MAAM,CAK7C"}
1
+ {"version":3,"file":"packageMetadata.d.ts","sourceRoot":"","sources":["../../src/utils/packageMetadata.ts"],"names":[],"mappings":"AAeA,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,sBAAsB,IAAI,MAAM,CAQ/C"}
@@ -1,8 +1,17 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
- export function getCliPackageVersion() {
3
+ function readCliPackageJson() {
4
4
  const packageJsonPath = fileURLToPath(new URL("../../package.json", import.meta.url));
5
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
6
- return packageJson.version;
5
+ return JSON.parse(readFileSync(packageJsonPath, "utf8"));
6
+ }
7
+ export function getCliPackageVersion() {
8
+ return readCliPackageJson().version;
9
+ }
10
+ export function getNurCoreVersionRange() {
11
+ const coreVersion = readCliPackageJson().nurjs?.coreVersion?.trim();
12
+ if (!coreVersion) {
13
+ throw new Error("nurjs-cli package metadata is missing nurjs.coreVersion.");
14
+ }
15
+ return coreVersion;
7
16
  }
8
17
  //# sourceMappingURL=packageMetadata.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageMetadata.js","sourceRoot":"","sources":["../../src/utils/packageMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAMzC,MAAM,UAAU,oBAAoB;IAClC,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAmB,CAAC;IAExF,OAAO,WAAW,CAAC,OAAO,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"packageMetadata.js","sourceRoot":"","sources":["../../src/utils/packageMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AASzC,SAAS,kBAAkB;IACzB,MAAM,eAAe,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,oBAAoB,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACtF,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAmB,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,kBAAkB,EAAE,CAAC,OAAO,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,MAAM,WAAW,GAAG,kBAAkB,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAEpE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nurjs-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CLI for NurJS backend projects.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,6 +16,7 @@
16
16
  "scripts": {
17
17
  "build": "tsc -p tsconfig.json",
18
18
  "check": "tsc --noEmit -p tsconfig.json",
19
+ "test": "tsc -p tsconfig.json && node --test tests/*.test.mjs",
19
20
  "dev": "tsx src/cli.ts",
20
21
  "prepack": "npm run build"
21
22
  },
@@ -31,6 +32,9 @@
31
32
  },
32
33
  "license": "MIT",
33
34
  "author": "WebDigiTech",
35
+ "nurjs": {
36
+ "coreVersion": "^0.1.1"
37
+ },
34
38
  "keywords": [
35
39
  "nurjs",
36
40
  "cli",
@@ -1,8 +1,8 @@
1
1
  NODE_ENV=development
2
2
 
3
3
  APP_NAME=__PROJECT_NAME__
4
- APP_HOST=__APP_HOST__
5
- APP_PORT=__APP_PORT__
4
+ HOST=__APP_HOST__
5
+ PORT=__APP_PORT__
6
6
  API_PREFIX=__API_PREFIX__
7
7
 
8
8
  CORS_ENABLED=true
@@ -1,8 +1,8 @@
1
1
  NODE_ENV=development
2
2
 
3
3
  APP_NAME=__PROJECT_NAME__
4
- APP_HOST=__APP_HOST__
5
- APP_PORT=__APP_PORT__
4
+ HOST=__APP_HOST__
5
+ PORT=__APP_PORT__
6
6
  API_PREFIX=__API_PREFIX__
7
7
 
8
8
  CORS_ENABLED=true
@@ -19,6 +19,33 @@ Default health endpoint:
19
19
  GET __API_PREFIX__/health
20
20
  ```
21
21
 
22
+ ## Runtime configuration
23
+
24
+ Project defaults are stored in `nur.config.ts`. Runtime values can override them through `.env` or the process environment.
25
+
26
+ Precedence:
27
+
28
+ ```txt
29
+ process environment > env file > nur.config.ts
30
+ ```
31
+
32
+ Canonical runtime variables:
33
+
34
+ ```txt
35
+ HOST
36
+ PORT
37
+ API_PREFIX
38
+ APP_NAME
39
+ NODE_ENV
40
+ CORS_ENABLED
41
+ CORS_ORIGIN
42
+ LOG_LEVEL
43
+ ```
44
+
45
+ Older `APP_HOST` and `APP_PORT` variables remain accepted as compatibility aliases.
46
+
47
+ The env file path is configured by `paths.envFile` in `nur.config.ts`. Set `NUR_ENV_FILE` only when you need a process-specific env file override.
48
+
22
49
  ## Project checks
23
50
 
24
51
  ```bash
@@ -33,7 +60,7 @@ nur doctor
33
60
  nur install auth
34
61
  ```
35
62
 
36
- The v0.1.0 auth repository is in-memory. Before production use, replace it with persistent storage and set a strong `JWT_SECRET`.
63
+ The auth repository is in-memory. Before production use, replace it with persistent storage and set a strong `JWT_SECRET`.
37
64
 
38
65
  ## Production build
39
66
 
@@ -4,8 +4,8 @@ import { env } from "./env.js";
4
4
  export const appConfig: NurAppConfig = {
5
5
  appName: env.APP_NAME,
6
6
  nodeEnv: env.NODE_ENV,
7
- host: env.APP_HOST,
8
- port: env.APP_PORT,
7
+ host: env.HOST,
8
+ port: env.PORT,
9
9
  apiPrefix: env.API_PREFIX,
10
10
  cors: {
11
11
  enabled: env.CORS_ENABLED,
@@ -1,22 +1,82 @@
1
+ import { existsSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { fileURLToPath, pathToFileURL } from "node:url";
1
4
  import { config as loadEnv } from "dotenv";
2
5
  import { z } from "zod";
6
+ import type { NurProjectConfig } from "nurjs-core";
3
7
 
4
- loadEnv({ quiet: true });
8
+ const projectRoot = fileURLToPath(new URL("../../", import.meta.url));
9
+ const configPath = resolve(projectRoot, "nur.config.ts");
10
+ const configModule = (await import(pathToFileURL(configPath).href)) as {
11
+ default?: NurProjectConfig;
12
+ };
13
+
14
+ if (!configModule.default) {
15
+ throw new Error("nur.config.ts must export a default NurJS config object.");
16
+ }
17
+
18
+ const projectConfig = configModule.default;
19
+ const envFile = process.env.NUR_ENV_FILE?.trim() || projectConfig.paths.envFile;
20
+ const envPath = resolve(projectRoot, envFile);
21
+
22
+ if (existsSync(envPath)) {
23
+ loadEnv({ path: envPath, quiet: true });
24
+ }
25
+
26
+ const apiPrefixSchema = z
27
+ .string()
28
+ .trim()
29
+ .min(1)
30
+ .refine((value: string) => value.startsWith("/"), {
31
+ message: 'API_PREFIX must start with "/".',
32
+ })
33
+ .refine((value: string) => value !== "/", {
34
+ message: "API_PREFIX cannot be the root path because NurJS module base paths are appended to it.",
35
+ })
36
+ .refine((value: string) => !/\s|\?|#/.test(value), {
37
+ message: "API_PREFIX must be a URL path without whitespace, query parameters, or fragments.",
38
+ })
39
+ .transform((value: string) => value.replace(/\/+$/, ""));
40
+
41
+ const corsOriginSchema = z.string().trim().min(1).refine(
42
+ (value: string) => {
43
+ try {
44
+ const parsed = new URL(value);
45
+ return (
46
+ (parsed.protocol === "http:" || parsed.protocol === "https:") &&
47
+ (parsed.pathname === "" || parsed.pathname === "/") &&
48
+ !parsed.search &&
49
+ !parsed.hash
50
+ );
51
+ } catch {
52
+ return false;
53
+ }
54
+ },
55
+ {
56
+ message: "CORS_ORIGIN must be a valid http(s) origin without a path, query, or fragment.",
57
+ },
58
+ );
5
59
 
6
60
  const envSchema = z.object({
7
- NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
8
- APP_NAME: z.string().default("nur-api"),
9
- APP_HOST: z.string().default("127.0.0.1"),
10
- APP_PORT: z.coerce.number().int().positive().default(5000),
11
- API_PREFIX: z.string().default("/api/v1"),
12
- CORS_ENABLED: z
13
- .enum(["true", "false"])
14
- .default("true")
15
- .transform((value) => value === "true"),
16
- CORS_ORIGIN: z.string().default("http://localhost:3000"),
17
- LOG_LEVEL: z
18
- .enum(["trace", "debug", "info", "warn", "error", "fatal"])
19
- .default("info"),
61
+ NODE_ENV: z.enum(["development", "test", "production"]),
62
+ APP_NAME: z.string().trim().min(1),
63
+ HOST: z.string().trim().min(1).refine((value: string) => !/\s|\/|\?|#/.test(value), {
64
+ message: "HOST must be a hostname or IP address, not a URL.",
65
+ }),
66
+ PORT: z.coerce.number().int().min(1).max(65535),
67
+ API_PREFIX: apiPrefixSchema,
68
+ CORS_ENABLED: z.enum(["true", "false"]).transform((value: string) => value === "true"),
69
+ CORS_ORIGIN: corsOriginSchema.transform((value: string) => new URL(value).origin),
70
+ LOG_LEVEL: z.enum(["trace", "debug", "info", "warn", "error", "fatal"]),
20
71
  });
21
72
 
22
- export const env = envSchema.parse(process.env);
73
+ export const env = envSchema.parse({
74
+ NODE_ENV: process.env.NODE_ENV ?? "development",
75
+ APP_NAME: process.env.APP_NAME ?? projectConfig.projectName,
76
+ HOST: process.env.HOST ?? process.env.APP_HOST ?? projectConfig.server.host,
77
+ PORT: process.env.PORT ?? process.env.APP_PORT ?? projectConfig.server.port,
78
+ API_PREFIX: process.env.API_PREFIX ?? projectConfig.server.apiPrefix,
79
+ CORS_ENABLED: process.env.CORS_ENABLED ?? "true",
80
+ CORS_ORIGIN: process.env.CORS_ORIGIN ?? "__CORS_ORIGIN__",
81
+ LOG_LEVEL: process.env.LOG_LEVEL ?? "info",
82
+ });
@@ -1,7 +1,8 @@
1
1
  import pino from "pino";
2
+ import { env } from "../../config/env.js";
2
3
 
3
4
  const pinoLogger = pino({
4
- level: process.env.LOG_LEVEL ?? "info",
5
+ level: env.LOG_LEVEL,
5
6
  });
6
7
 
7
8
  function normalizeMeta(meta?: unknown): Record<string, unknown> {