@jsnw/srv-utils 1.3.1 → 2.0.0
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 +0 -10
- package/dist/index.cjs +348 -0
- package/dist/index.d.cts +167 -0
- package/package.json +22 -28
- package/dist/config/config-loader.js +0 -177
- package/dist/config/config-loader.types.js +0 -2
- package/dist/config/index.js +0 -7
- package/dist/config/predefined-schemas/connection.schema.js +0 -10
- package/dist/config/predefined-schemas/index.js +0 -17
- package/dist/config/predefined-schemas/mongodb-connection.schema.js +0 -15
- package/dist/config/predefined-schemas/mysql-connection.schema.js +0 -8
- package/dist/config/predefined-schemas/rabbit-connection.schema.js +0 -8
- package/dist/config/predefined-schemas/redis-connection.schema.js +0 -9
- package/dist/config/predefined-schemas/timestring.schema.js +0 -14
- package/dist/create-random-string.js +0 -21
- package/dist/file-exists.js +0 -34
- package/dist/getRootPackageDirname.js +0 -32
- package/dist/index.js +0 -17
- package/dist/types/config/config-loader.d.ts +0 -52
- package/dist/types/config/config-loader.types.d.ts +0 -4
- package/dist/types/config/index.d.ts +0 -3
- package/dist/types/config/predefined-schemas/connection.schema.d.ts +0 -7
- package/dist/types/config/predefined-schemas/index.d.ts +0 -57
- package/dist/types/config/predefined-schemas/mongodb-connection.schema.d.ts +0 -27
- package/dist/types/config/predefined-schemas/mysql-connection.schema.d.ts +0 -8
- package/dist/types/config/predefined-schemas/rabbit-connection.schema.d.ts +0 -8
- package/dist/types/config/predefined-schemas/redis-connection.schema.d.ts +0 -9
- package/dist/types/config/predefined-schemas/timestring.schema.d.ts +0 -2
- package/dist/types/create-random-string.d.ts +0 -5
- package/dist/types/file-exists.d.ts +0 -12
- package/dist/types/getRootPackageDirname.d.ts +0 -4
- package/dist/types/index.d.ts +0 -6
- package/dist/types/useTsconfigPaths.d.ts +0 -9
- package/dist/types/withNest.d.ts +0 -20
- package/dist/useTsconfigPaths.js +0 -34
- package/dist/withNest.js +0 -84
package/README.md
CHANGED
|
@@ -9,20 +9,10 @@ npm install @jsnw/srv-utils
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Optional dependencies:
|
|
12
|
-
- `module-alias` for `useTsconfigPaths`
|
|
13
12
|
- `@nestjs/core` and `@nestjs/common` for `withNest`
|
|
14
13
|
|
|
15
14
|
## Exports
|
|
16
15
|
|
|
17
|
-
### useTsconfigPaths(fromPath, tsconfigPath)
|
|
18
|
-
Loads `compilerOptions.paths` from a tsconfig file and registers them via `module-alias`.
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
import {useTsconfigPaths} from '@jsnw/srv-utils';
|
|
22
|
-
|
|
23
|
-
useTsconfigPaths(__dirname, './tsconfig.json');
|
|
24
|
-
```
|
|
25
|
-
|
|
26
16
|
### getRootPackageDirnameSync()
|
|
27
17
|
Finds the highest directory (from the current module) that still has a readable `package.json`.
|
|
28
18
|
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let node_fs_promises = require("node:fs/promises");
|
|
25
|
+
let node_fs = require("node:fs");
|
|
26
|
+
let node_path = require("node:path");
|
|
27
|
+
let zod = require("zod");
|
|
28
|
+
let yaml = require("yaml");
|
|
29
|
+
let ms = require("ms");
|
|
30
|
+
ms = __toESM(ms);
|
|
31
|
+
let node_crypto = require("node:crypto");
|
|
32
|
+
//#region src/file-exists.ts
|
|
33
|
+
/**
|
|
34
|
+
* @param {string} path
|
|
35
|
+
* @param {number} [mode]
|
|
36
|
+
* @returns {boolean}
|
|
37
|
+
*/
|
|
38
|
+
async function fileExists(path, mode = node_fs.constants.F_OK | node_fs.constants.R_OK) {
|
|
39
|
+
try {
|
|
40
|
+
await (0, node_fs_promises.access)(path, mode);
|
|
41
|
+
return true;
|
|
42
|
+
} catch (e) {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} path
|
|
48
|
+
* @param {number} [mode]
|
|
49
|
+
* @returns {boolean}
|
|
50
|
+
*/
|
|
51
|
+
function fileExistsSync(path, mode = node_fs.constants.F_OK | node_fs.constants.R_OK) {
|
|
52
|
+
try {
|
|
53
|
+
(0, node_fs.accessSync)(path, mode);
|
|
54
|
+
return true;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/getRootPackageDirname.ts
|
|
61
|
+
let cachedRootPackageDirname = null;
|
|
62
|
+
/**
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/
|
|
65
|
+
function getRootPackageDirnameSync() {
|
|
66
|
+
if (cachedRootPackageDirname !== null) return cachedRootPackageDirname;
|
|
67
|
+
let path = (0, node_path.resolve)(__dirname, ".."), lastValidPath = path;
|
|
68
|
+
if (/[\\\/]node_modules[\\\/]/i.test(path)) {
|
|
69
|
+
const pieces = path.split(/[\\\/]node_modules[\\\/]/i);
|
|
70
|
+
if (pieces.length > 0) path = pieces[0];
|
|
71
|
+
}
|
|
72
|
+
while (true) {
|
|
73
|
+
const packageJsonPath = (0, node_path.resolve)(path, "package.json");
|
|
74
|
+
try {
|
|
75
|
+
(0, node_fs.accessSync)(packageJsonPath, node_fs.constants.F_OK | node_fs.constants.R_OK);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
lastValidPath = path;
|
|
80
|
+
path = (0, node_path.resolve)(path, "..");
|
|
81
|
+
}
|
|
82
|
+
cachedRootPackageDirname = lastValidPath;
|
|
83
|
+
return lastValidPath;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/config/config-loader.ts
|
|
87
|
+
const PKG_ROOT_REGEX = /^%pkgroot(?:[\/\\]|$)/i;
|
|
88
|
+
const ENV_VAR_REGEX = /^%env:(?<var_name>[A-Za-z0-9_]+)(?:[\/\\]|$)/i;
|
|
89
|
+
var ConfigLoader = class ConfigLoader {
|
|
90
|
+
static _instance;
|
|
91
|
+
/**
|
|
92
|
+
* @returns {ConfigLoader}
|
|
93
|
+
*/
|
|
94
|
+
static instance() {
|
|
95
|
+
if (!ConfigLoader._instance) ConfigLoader._instance = new ConfigLoader();
|
|
96
|
+
return ConfigLoader._instance;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @template {z.ZodObject} S
|
|
100
|
+
* @template {object} P
|
|
101
|
+
* @param {string} path You can use %pkgroot prefix for automatic project root resolution by AppConfigLoader.
|
|
102
|
+
* Also, you can use %env:ENV_VARIABLE to resolve config path based on environment variable
|
|
103
|
+
* Example 1: %pkgroot/config.yml
|
|
104
|
+
* Example 2: %env:CONFIG_PATH/config.yml
|
|
105
|
+
* @param {S} schema
|
|
106
|
+
* @param {P} [addProps]
|
|
107
|
+
* @returns {ResolvedConfig<S, P>}
|
|
108
|
+
*/
|
|
109
|
+
static loadConfig(path, schema, addProps) {
|
|
110
|
+
const loader = ConfigLoader.instance();
|
|
111
|
+
const [yaml$1, loadError] = loader.loadYamlFile(path);
|
|
112
|
+
if (loadError) {
|
|
113
|
+
console.error(loadError.message);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
const [processedYaml, directiveErrors] = loader.processDirectives((0, node_path.dirname)(loader.resolveFilePath(path)), yaml$1);
|
|
117
|
+
if (directiveErrors && directiveErrors.length > 0) {
|
|
118
|
+
for (const err of directiveErrors) console.error(`directive error: ${err.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
const [validatedYaml, validateError] = loader.validateYaml(processedYaml, schema);
|
|
122
|
+
if (validateError) {
|
|
123
|
+
console.error(validateError.message);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
validatedYaml["isDev"] = (process?.env?.APP_CONTEXT ?? process?.env?.APPLICATION_CONTEXT ?? process?.env?.NODE_ENV ?? "").toLowerCase() !== "production";
|
|
127
|
+
return {
|
|
128
|
+
...validatedYaml,
|
|
129
|
+
...addProps ?? {}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
constructor() {}
|
|
133
|
+
/**
|
|
134
|
+
* @param {string} path
|
|
135
|
+
* @returns {ErrorResult<any, Error>}
|
|
136
|
+
* @protected
|
|
137
|
+
*/
|
|
138
|
+
loadYamlFile(path) {
|
|
139
|
+
path = this.resolveFilePath(path);
|
|
140
|
+
if (!fileExistsSync(path)) return [null, /* @__PURE__ */ new Error(`YAML file does not exists at path: ${path}`)];
|
|
141
|
+
let data = void 0, parsedYml = void 0;
|
|
142
|
+
try {
|
|
143
|
+
data = (0, node_fs.readFileSync)(path, "utf-8");
|
|
144
|
+
} catch (e) {
|
|
145
|
+
return [null, /* @__PURE__ */ new Error(`Failed to read config file at path: ${path}`)];
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
parsedYml = (0, yaml.parse)(data, { prettyErrors: true });
|
|
149
|
+
} catch (e) {
|
|
150
|
+
return [null, /* @__PURE__ */ new Error(`Failed to parse YAML file at path: ${path}`)];
|
|
151
|
+
}
|
|
152
|
+
return [parsedYml, null];
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* @template {z.ZodTypeAny} T
|
|
156
|
+
* @param data
|
|
157
|
+
* @param {T} schema
|
|
158
|
+
* @returns {ErrorResult<output<T>, Error>}
|
|
159
|
+
* @protected
|
|
160
|
+
*/
|
|
161
|
+
validateYaml(data, schema) {
|
|
162
|
+
const { data: parsed, error, success } = schema.safeParse(data);
|
|
163
|
+
if (!success) {
|
|
164
|
+
if (error && error instanceof zod.ZodError) return [null, /* @__PURE__ */ new Error(`Failed to validate yaml (#1). Error message: ${zod.z.prettifyError(error)}`)];
|
|
165
|
+
return [null, /* @__PURE__ */ new Error(`Failed to validate yaml (#2)`)];
|
|
166
|
+
}
|
|
167
|
+
return [parsed, null];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* @param {string} mainYamlDirname
|
|
171
|
+
* @param yaml
|
|
172
|
+
* @returns {ErrorResult<any, Error[]>}
|
|
173
|
+
* @protected
|
|
174
|
+
*/
|
|
175
|
+
processDirectives(mainYamlDirname, yaml$2) {
|
|
176
|
+
if (typeof yaml$2 !== "object") return yaml$2;
|
|
177
|
+
const nodesToVisit = [yaml$2], errors = [];
|
|
178
|
+
for (let i = 0; i < nodesToVisit.length; i++) {
|
|
179
|
+
const node = nodesToVisit[i];
|
|
180
|
+
if (typeof node !== "object" || Array.isArray(node)) continue;
|
|
181
|
+
const nodeFields = Object.keys(node);
|
|
182
|
+
for (const field of nodeFields) if (field === "$include") {
|
|
183
|
+
const $includePaths = [];
|
|
184
|
+
if (typeof node["$include"] === "string") $includePaths.push(node["$include"]);
|
|
185
|
+
else if (Array.isArray(node["$include"])) $includePaths.push(...node["$include"].filter((v) => v && typeof v === "string" && v.trim() !== ""));
|
|
186
|
+
delete node["$include"];
|
|
187
|
+
for (const path of $includePaths) {
|
|
188
|
+
const [loadedYaml, loadError] = this.loadYamlFile(this.resolveIncludePath(mainYamlDirname, path));
|
|
189
|
+
if (loadError) errors.push(loadError);
|
|
190
|
+
else for (const [k, v] of Object.entries(loadedYaml)) node[k] = v;
|
|
191
|
+
}
|
|
192
|
+
} else if (typeof node[field] === "string" && /^\s*\$[A-Z0-9_]+$/.test(node[field])) node[field] = process.env?.[node[field].trim().slice(1)] ?? "";
|
|
193
|
+
for (const k of Object.keys(node)) if (Object.hasOwn(node, k) && typeof node[k] === "object" && !Array.isArray(node[k])) nodesToVisit.push(node[k]);
|
|
194
|
+
}
|
|
195
|
+
return [yaml$2, errors];
|
|
196
|
+
}
|
|
197
|
+
resolveFilePath(path) {
|
|
198
|
+
const initialPath = path;
|
|
199
|
+
while (ENV_VAR_REGEX.test(path)) {
|
|
200
|
+
const m = path.match(ENV_VAR_REGEX);
|
|
201
|
+
if (m.groups?.["var_name"]) if (process.env?.[m.groups["var_name"]]) path = path.replace(ENV_VAR_REGEX, process.env[m.groups["var_name"]].replace(/[\/\\]+$/, "") + node_path.sep);
|
|
202
|
+
else throw new Error(`Failed to resolve path: "${initialPath}"`);
|
|
203
|
+
}
|
|
204
|
+
if (PKG_ROOT_REGEX.test(path)) path = path.replace(PKG_ROOT_REGEX, getRootPackageDirnameSync() + node_path.sep);
|
|
205
|
+
return (0, node_path.resolve)(path);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* @param {string} fromDirname
|
|
209
|
+
* @param {string} toPath
|
|
210
|
+
* @returns {string}
|
|
211
|
+
* @private
|
|
212
|
+
*/
|
|
213
|
+
resolveIncludePath(fromDirname, toPath) {
|
|
214
|
+
return (0, node_path.resolve)(this.resolveFilePath(fromDirname), toPath);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/config/predefined-schemas/timestring.schema.ts
|
|
219
|
+
const TIMESTRING_REGEX = new RegExp(`\\d+\\s*(?:${[
|
|
220
|
+
"Years",
|
|
221
|
+
"Year",
|
|
222
|
+
"Yrs",
|
|
223
|
+
"Yr",
|
|
224
|
+
"Y",
|
|
225
|
+
"Weeks",
|
|
226
|
+
"Week",
|
|
227
|
+
"W",
|
|
228
|
+
"Days",
|
|
229
|
+
"Day",
|
|
230
|
+
"D",
|
|
231
|
+
"Hours",
|
|
232
|
+
"Hour",
|
|
233
|
+
"Hrs",
|
|
234
|
+
"Hr",
|
|
235
|
+
"H",
|
|
236
|
+
"Minutes",
|
|
237
|
+
"Minute",
|
|
238
|
+
"Mins",
|
|
239
|
+
"Min",
|
|
240
|
+
"M",
|
|
241
|
+
"Seconds",
|
|
242
|
+
"Second",
|
|
243
|
+
"Secs",
|
|
244
|
+
"Sec",
|
|
245
|
+
"s",
|
|
246
|
+
"Milliseconds",
|
|
247
|
+
"Millisecond",
|
|
248
|
+
"Msecs",
|
|
249
|
+
"Msec",
|
|
250
|
+
"Ms"
|
|
251
|
+
].map((unit) => unit.toLowerCase()).join("|")})`, "i");
|
|
252
|
+
const timeStringSchema = zod.z.string().nonempty().regex(TIMESTRING_REGEX).transform((v) => (0, ms.default)(v));
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/config/predefined-schemas/connection.schema.ts
|
|
255
|
+
const connectionSchema = zod.z.object({
|
|
256
|
+
host: zod.z.string().nonempty(),
|
|
257
|
+
port: zod.z.coerce.number().min(1).max(65535),
|
|
258
|
+
user: zod.z.string(),
|
|
259
|
+
pass: zod.z.string()
|
|
260
|
+
});
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/config/predefined-schemas/index.ts
|
|
263
|
+
const configSchemas = {
|
|
264
|
+
timeString: timeStringSchema,
|
|
265
|
+
connection: connectionSchema,
|
|
266
|
+
mongodbConnection: connectionSchema.extend({
|
|
267
|
+
db: zod.z.string().nonempty(),
|
|
268
|
+
authDb: zod.z.string().optional(),
|
|
269
|
+
connectTimeout: timeStringSchema.default(5e3)
|
|
270
|
+
}).transform((v) => ({
|
|
271
|
+
...v,
|
|
272
|
+
authDb: v.authDb || v.db,
|
|
273
|
+
dsn: `mongodb://${v.host}:${v.port}/${v.db}`
|
|
274
|
+
})),
|
|
275
|
+
mysqlConnection: connectionSchema.extend({ dbname: zod.z.string().nonempty() }),
|
|
276
|
+
redisConnection: connectionSchema.extend({
|
|
277
|
+
db: zod.z.number().min(0).default(0),
|
|
278
|
+
keyPrefix: zod.z.string().default("")
|
|
279
|
+
}),
|
|
280
|
+
rabbitConnection: connectionSchema.extend({ vhost: zod.z.string().default("/") })
|
|
281
|
+
};
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/withNest.ts
|
|
284
|
+
/**
|
|
285
|
+
* Executes a given function within the context of a NestJS application.
|
|
286
|
+
*
|
|
287
|
+
* This asynchronous function initializes a NestJS application context using the specified
|
|
288
|
+
* module class and then executes the provided function `fn` with the application instance.
|
|
289
|
+
* The application context is automatically closed upon completion of the function execution,
|
|
290
|
+
* unless the function execution requests otherwise by invoking a callback.
|
|
291
|
+
*
|
|
292
|
+
* @param {any} moduleCls - The module class to be used for creating the NestJS application context.
|
|
293
|
+
* @param {WithNestFn} fn - The function to be executed with the NestJS application context.
|
|
294
|
+
* It receives the application instance and a callback to prevent
|
|
295
|
+
* automatic application context closure.
|
|
296
|
+
* @returns {Promise<void>} A promise that resolves when the function execution is complete.
|
|
297
|
+
* @throws - Will throw an error if the `@nestjs/core` and/or `@nestjs/common` library is not found.
|
|
298
|
+
* @throws - Will propagate any error thrown by the executed function `fn`.
|
|
299
|
+
*/
|
|
300
|
+
async function withNest(moduleCls, fn) {
|
|
301
|
+
let nestFactory = null;
|
|
302
|
+
try {
|
|
303
|
+
nestFactory = (await import("@nestjs/core"))?.NestFactory;
|
|
304
|
+
if (!nestFactory) throw new Error("NestFactory not found");
|
|
305
|
+
} catch (e) {
|
|
306
|
+
throw new Error("@nestjs/core and @nestjs/common are required for this function to work");
|
|
307
|
+
}
|
|
308
|
+
const app = await nestFactory.createApplicationContext(moduleCls);
|
|
309
|
+
let closeApplication = true;
|
|
310
|
+
let _e = void 0;
|
|
311
|
+
try {
|
|
312
|
+
await fn(app, () => {
|
|
313
|
+
closeApplication = false;
|
|
314
|
+
});
|
|
315
|
+
} catch (e) {
|
|
316
|
+
console.error("Error caught while executing the WithNestFn. Error:", e);
|
|
317
|
+
_e = e;
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
if (closeApplication) await app.close();
|
|
321
|
+
} catch (e) {
|
|
322
|
+
console.error("Failed to close application. Error:", e);
|
|
323
|
+
}
|
|
324
|
+
if (_e) throw _e;
|
|
325
|
+
}
|
|
326
|
+
//#endregion
|
|
327
|
+
//#region src/create-random-string.ts
|
|
328
|
+
/**
|
|
329
|
+
* @param {number} length The length of the string
|
|
330
|
+
* @return {Promise<string>}
|
|
331
|
+
*/
|
|
332
|
+
function createRandomString(length) {
|
|
333
|
+
if (length <= 0) return Promise.resolve("");
|
|
334
|
+
return new Promise((resolve, reject) => {
|
|
335
|
+
(0, node_crypto.randomBytes)(Math.ceil(length / 2), (err, buff) => {
|
|
336
|
+
if (err) return reject(err);
|
|
337
|
+
return resolve(buff.toString("hex").slice(0, length));
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
exports.ConfigLoader = ConfigLoader;
|
|
343
|
+
exports.configSchemas = configSchemas;
|
|
344
|
+
exports.createRandomString = createRandomString;
|
|
345
|
+
exports.fileExists = fileExists;
|
|
346
|
+
exports.fileExistsSync = fileExistsSync;
|
|
347
|
+
exports.getRootPackageDirnameSync = getRootPackageDirnameSync;
|
|
348
|
+
exports.withNest = withNest;
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { ErrorResult } from "@jsnw/common-utils";
|
|
3
|
+
import { INestApplicationContext } from "@nestjs/common";
|
|
4
|
+
|
|
5
|
+
//#region src/file-exists.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} path
|
|
8
|
+
* @param {number} [mode]
|
|
9
|
+
* @returns {boolean}
|
|
10
|
+
*/
|
|
11
|
+
declare function fileExists(path: string, mode?: number): Promise<boolean>;
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} path
|
|
14
|
+
* @param {number} [mode]
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
declare function fileExistsSync(path: string, mode?: number): boolean;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/getRootPackageDirname.d.ts
|
|
20
|
+
/**
|
|
21
|
+
* @returns {string}
|
|
22
|
+
*/
|
|
23
|
+
declare function getRootPackageDirnameSync(): string;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/config/config-loader.types.d.ts
|
|
26
|
+
type ResolvedConfig<T extends z.ZodObject, P extends object | undefined = undefined> = z.output<T> & {
|
|
27
|
+
isDev: boolean;
|
|
28
|
+
} & (P extends undefined ? {} : P);
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/config/config-loader.d.ts
|
|
31
|
+
declare class ConfigLoader {
|
|
32
|
+
private static _instance;
|
|
33
|
+
/**
|
|
34
|
+
* @returns {ConfigLoader}
|
|
35
|
+
*/
|
|
36
|
+
static instance(): ConfigLoader;
|
|
37
|
+
/**
|
|
38
|
+
* @template {z.ZodObject} S
|
|
39
|
+
* @template {object} P
|
|
40
|
+
* @param {string} path You can use %pkgroot prefix for automatic project root resolution by AppConfigLoader.
|
|
41
|
+
* Also, you can use %env:ENV_VARIABLE to resolve config path based on environment variable
|
|
42
|
+
* Example 1: %pkgroot/config.yml
|
|
43
|
+
* Example 2: %env:CONFIG_PATH/config.yml
|
|
44
|
+
* @param {S} schema
|
|
45
|
+
* @param {P} [addProps]
|
|
46
|
+
* @returns {ResolvedConfig<S, P>}
|
|
47
|
+
*/
|
|
48
|
+
static loadConfig<S extends z.ZodObject, P extends object | undefined = undefined>(path: string, schema: S, addProps?: P): ResolvedConfig<S, P>;
|
|
49
|
+
private constructor();
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} path
|
|
52
|
+
* @returns {ErrorResult<any, Error>}
|
|
53
|
+
* @protected
|
|
54
|
+
*/
|
|
55
|
+
protected loadYamlFile(path: string): ErrorResult<any, Error>;
|
|
56
|
+
/**
|
|
57
|
+
* @template {z.ZodTypeAny} T
|
|
58
|
+
* @param data
|
|
59
|
+
* @param {T} schema
|
|
60
|
+
* @returns {ErrorResult<output<T>, Error>}
|
|
61
|
+
* @protected
|
|
62
|
+
*/
|
|
63
|
+
protected validateYaml<T extends z.ZodTypeAny>(data: any, schema: T): ErrorResult<z.infer<T>, Error>;
|
|
64
|
+
/**
|
|
65
|
+
* @param {string} mainYamlDirname
|
|
66
|
+
* @param yaml
|
|
67
|
+
* @returns {ErrorResult<any, Error[]>}
|
|
68
|
+
* @protected
|
|
69
|
+
*/
|
|
70
|
+
protected processDirectives(mainYamlDirname: string, yaml: any): ErrorResult<any, Error[]>;
|
|
71
|
+
private resolveFilePath;
|
|
72
|
+
/**
|
|
73
|
+
* @param {string} fromDirname
|
|
74
|
+
* @param {string} toPath
|
|
75
|
+
* @returns {string}
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
private resolveIncludePath;
|
|
79
|
+
}
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/config/predefined-schemas/index.d.ts
|
|
82
|
+
declare const configSchemas: {
|
|
83
|
+
timeString: import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<number, string>>;
|
|
84
|
+
connection: import("zod").ZodObject<{
|
|
85
|
+
host: import("zod").ZodString;
|
|
86
|
+
port: import("zod").ZodCoercedNumber<unknown>;
|
|
87
|
+
user: import("zod").ZodString;
|
|
88
|
+
pass: import("zod").ZodString;
|
|
89
|
+
}, import("zod/v4/core").$strip>;
|
|
90
|
+
mongodbConnection: import("zod").ZodPipe<import("zod").ZodObject<{
|
|
91
|
+
host: import("zod").ZodString;
|
|
92
|
+
port: import("zod").ZodCoercedNumber<unknown>;
|
|
93
|
+
user: import("zod").ZodString;
|
|
94
|
+
pass: import("zod").ZodString;
|
|
95
|
+
db: import("zod").ZodString;
|
|
96
|
+
authDb: import("zod").ZodOptional<import("zod").ZodString>;
|
|
97
|
+
connectTimeout: import("zod").ZodDefault<import("zod").ZodPipe<import("zod").ZodString, import("zod").ZodTransform<number, string>>>;
|
|
98
|
+
}, import("zod/v4/core").$strip>, import("zod").ZodTransform<{
|
|
99
|
+
authDb: string;
|
|
100
|
+
dsn: string;
|
|
101
|
+
host: string;
|
|
102
|
+
port: number;
|
|
103
|
+
user: string;
|
|
104
|
+
pass: string;
|
|
105
|
+
db: string;
|
|
106
|
+
connectTimeout: number;
|
|
107
|
+
}, {
|
|
108
|
+
host: string;
|
|
109
|
+
port: number;
|
|
110
|
+
user: string;
|
|
111
|
+
pass: string;
|
|
112
|
+
db: string;
|
|
113
|
+
connectTimeout: number;
|
|
114
|
+
authDb?: string;
|
|
115
|
+
}>>;
|
|
116
|
+
mysqlConnection: import("zod").ZodObject<{
|
|
117
|
+
host: import("zod").ZodString;
|
|
118
|
+
port: import("zod").ZodCoercedNumber<unknown>;
|
|
119
|
+
user: import("zod").ZodString;
|
|
120
|
+
pass: import("zod").ZodString;
|
|
121
|
+
dbname: import("zod").ZodString;
|
|
122
|
+
}, import("zod/v4/core").$strip>;
|
|
123
|
+
redisConnection: import("zod").ZodObject<{
|
|
124
|
+
host: import("zod").ZodString;
|
|
125
|
+
port: import("zod").ZodCoercedNumber<unknown>;
|
|
126
|
+
user: import("zod").ZodString;
|
|
127
|
+
pass: import("zod").ZodString;
|
|
128
|
+
db: import("zod").ZodDefault<import("zod").ZodNumber>;
|
|
129
|
+
keyPrefix: import("zod").ZodDefault<import("zod").ZodString>;
|
|
130
|
+
}, import("zod/v4/core").$strip>;
|
|
131
|
+
rabbitConnection: import("zod").ZodObject<{
|
|
132
|
+
host: import("zod").ZodString;
|
|
133
|
+
port: import("zod").ZodCoercedNumber<unknown>;
|
|
134
|
+
user: import("zod").ZodString;
|
|
135
|
+
pass: import("zod").ZodString;
|
|
136
|
+
vhost: import("zod").ZodDefault<import("zod").ZodString>;
|
|
137
|
+
}, import("zod/v4/core").$strip>;
|
|
138
|
+
};
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/withNest.d.ts
|
|
141
|
+
type WithNestFn = (app: INestApplicationContext, preventClosing: () => void) => Promise<void>;
|
|
142
|
+
/**
|
|
143
|
+
* Executes a given function within the context of a NestJS application.
|
|
144
|
+
*
|
|
145
|
+
* This asynchronous function initializes a NestJS application context using the specified
|
|
146
|
+
* module class and then executes the provided function `fn` with the application instance.
|
|
147
|
+
* The application context is automatically closed upon completion of the function execution,
|
|
148
|
+
* unless the function execution requests otherwise by invoking a callback.
|
|
149
|
+
*
|
|
150
|
+
* @param {any} moduleCls - The module class to be used for creating the NestJS application context.
|
|
151
|
+
* @param {WithNestFn} fn - The function to be executed with the NestJS application context.
|
|
152
|
+
* It receives the application instance and a callback to prevent
|
|
153
|
+
* automatic application context closure.
|
|
154
|
+
* @returns {Promise<void>} A promise that resolves when the function execution is complete.
|
|
155
|
+
* @throws - Will throw an error if the `@nestjs/core` and/or `@nestjs/common` library is not found.
|
|
156
|
+
* @throws - Will propagate any error thrown by the executed function `fn`.
|
|
157
|
+
*/
|
|
158
|
+
declare function withNest(moduleCls: any, fn: WithNestFn): Promise<void>;
|
|
159
|
+
//#endregion
|
|
160
|
+
//#region src/create-random-string.d.ts
|
|
161
|
+
/**
|
|
162
|
+
* @param {number} length The length of the string
|
|
163
|
+
* @return {Promise<string>}
|
|
164
|
+
*/
|
|
165
|
+
declare function createRandomString(length: number): Promise<string>;
|
|
166
|
+
//#endregion
|
|
167
|
+
export { ConfigLoader, type ResolvedConfig, configSchemas, createRandomString, fileExists, fileExistsSync, getRootPackageDirnameSync, withNest };
|
package/package.json
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsnw/srv-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Server-side utilities for Node.js/TypeScript: tsconfig paths, Nest helpers, and config loading.",
|
|
5
|
-
"
|
|
6
|
-
|
|
5
|
+
"engines": {
|
|
6
|
+
"node": ">=20"
|
|
7
|
+
},
|
|
7
8
|
"exports": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"types": "./dist/
|
|
9
|
+
"node": {
|
|
10
|
+
"default": "./dist/index.cjs",
|
|
11
|
+
"types": "./dist/index.d.cts"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
|
-
"scripts": {
|
|
14
|
-
"clean": "rimraf ./dist",
|
|
15
|
-
"build": "npm run clean && tsc -p tsconfig.json",
|
|
16
|
-
"test": "jest --passWithNoTests",
|
|
17
|
-
"test:watch": "jest --watch",
|
|
18
|
-
"test:coverage": "jest --coverage",
|
|
19
|
-
"prepublishOnly": "npm run build"
|
|
20
|
-
},
|
|
21
14
|
"files": [
|
|
22
15
|
"./dist",
|
|
23
16
|
"README.md"
|
|
@@ -42,25 +35,26 @@
|
|
|
42
35
|
"dependencies": {
|
|
43
36
|
"@jsnw/common-utils": "^1.0.0",
|
|
44
37
|
"ms": "^2.1.3",
|
|
45
|
-
"yaml": "^2.
|
|
46
|
-
"zod": "^4.3
|
|
38
|
+
"yaml": "^2.9.0",
|
|
39
|
+
"zod": "^4.4.3"
|
|
47
40
|
},
|
|
48
41
|
"optionalDependencies": {
|
|
49
|
-
"@nestjs/common": "^11.1.
|
|
50
|
-
"@nestjs/core": "^11.1.
|
|
51
|
-
"module-alias": "^2.3.3"
|
|
42
|
+
"@nestjs/common": "^11.1.27",
|
|
43
|
+
"@nestjs/core": "^11.1.27"
|
|
52
44
|
},
|
|
53
45
|
"devDependencies": {
|
|
54
|
-
"@nestjs/common": "^11.1.12",
|
|
55
|
-
"@nestjs/core": "^11.1.12",
|
|
56
46
|
"@types/jest": "^30.0.0",
|
|
57
|
-
"@types/module-alias": "^2.0.4",
|
|
58
47
|
"@types/ms": "^2.1.0",
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"tslib": "^2.8.1",
|
|
48
|
+
"@types/node": "^24.13.2",
|
|
49
|
+
"jest": "^30.4.2",
|
|
50
|
+
"ts-jest": "^29.4.11",
|
|
51
|
+
"tsdown": "^0.22.3",
|
|
64
52
|
"typescript": "^5.9.3"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsdown",
|
|
56
|
+
"test": "jest --passWithNoTests",
|
|
57
|
+
"test:watch": "jest --watch",
|
|
58
|
+
"test:coverage": "jest --coverage"
|
|
65
59
|
}
|
|
66
|
-
}
|
|
60
|
+
}
|