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.
- package/README.md +36 -5
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +67 -18
- package/dist/commands/doctor.command.js.map +1 -1
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +36 -5
- package/dist/commands/init.command.js.map +1 -1
- package/dist/commands/start.command.d.ts.map +1 -1
- package/dist/commands/start.command.js +22 -8
- package/dist/commands/start.command.js.map +1 -1
- package/dist/config/loadNurConfig.d.ts.map +1 -1
- package/dist/config/loadNurConfig.js +58 -25
- package/dist/config/loadNurConfig.js.map +1 -1
- package/dist/config/runtimeConfig.d.ts +23 -0
- package/dist/config/runtimeConfig.d.ts.map +1 -0
- package/dist/config/runtimeConfig.js +75 -0
- package/dist/config/runtimeConfig.js.map +1 -0
- package/dist/config/runtimeValidation.d.ts +11 -0
- package/dist/config/runtimeValidation.d.ts.map +1 -0
- package/dist/config/runtimeValidation.js +128 -0
- package/dist/config/runtimeValidation.js.map +1 -0
- package/dist/utils/packageMetadata.d.ts +1 -0
- package/dist/utils/packageMetadata.d.ts.map +1 -1
- package/dist/utils/packageMetadata.js +12 -3
- package/dist/utils/packageMetadata.js.map +1 -1
- package/package.json +5 -1
- package/templates/app/.env.example.tpl +2 -2
- package/templates/app/.env.tpl +2 -2
- package/templates/app/README.md.tpl +28 -1
- package/templates/app/src/config/app.config.ts.tpl +2 -2
- package/templates/app/src/config/env.ts.tpl +75 -15
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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":"
|
|
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
|
-
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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;
|
|
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":"
|
|
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 {
|
|
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
|
-
:
|
|
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":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,
|
|
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":"
|
|
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: ${
|
|
38
|
+
console.log(`Project: ${runtime.appName}`);
|
|
35
39
|
console.log(`Entry: ${config.runtime.entry}`);
|
|
36
|
-
console.log(`Host: ${
|
|
37
|
-
console.log(`Port: ${
|
|
38
|
-
console.log(`API prefix: ${
|
|
39
|
-
console.log(`
|
|
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,
|
|
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":"
|
|
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 {
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
return
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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:
|
|
32
|
+
projectName: requireString(config.projectName, "projectName"),
|
|
18
33
|
runtime: {
|
|
19
|
-
entry:
|
|
20
|
-
distEntry:
|
|
21
|
-
tsconfig:
|
|
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:
|
|
25
|
-
port:
|
|
26
|
-
apiPrefix:
|
|
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:
|
|
30
|
-
modulesDir:
|
|
31
|
-
moduleRegistryFile:
|
|
32
|
-
envFile:
|
|
33
|
-
envExampleFile:
|
|
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:
|
|
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,
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageMetadata.d.ts","sourceRoot":"","sources":["../../src/utils/packageMetadata.ts"],"names":[],"mappings":"
|
|
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
|
-
|
|
3
|
+
function readCliPackageJson() {
|
|
4
4
|
const packageJsonPath = fileURLToPath(new URL("../../package.json", import.meta.url));
|
|
5
|
-
|
|
6
|
-
|
|
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;
|
|
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.
|
|
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",
|
package/templates/app/.env.tpl
CHANGED
|
@@ -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
|
|
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.
|
|
8
|
-
port: env.
|
|
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
|
-
|
|
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"])
|
|
8
|
-
APP_NAME: z.string().
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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(
|
|
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
|
+
});
|