@h-ai/core 1.0.0-beta.2 → 1.0.0-beta.21
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.en.md +715 -474
- package/README.ja.md +714 -473
- package/README.md +714 -473
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +39 -11
- package/dist/browser.js.map +1 -1
- package/dist/{chunk-BEGTB6GL.js → chunk-WU6HUTBO.js} +262 -59
- package/dist/chunk-WU6HUTBO.js.map +1 -0
- package/dist/{core-types-_JqVAGGT.d.ts → core-types-Dp3Vr2uI.d.ts} +161 -15
- package/dist/node.d.ts +2 -2
- package/dist/node.js +495 -123
- package/dist/node.js.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-BEGTB6GL.js.map +0 -1
package/dist/node.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { installRequestLocales, installRequestContext, createCore, i18n, err, HaiConfigError, ok, RequestAccessConfigSchema, RequestAccessPolicySchema, AccessConditionSchema, requestContext, sanitize, emitLogRecord, sanitizeLogFields,
|
|
2
|
-
export { AccessConditionSchema, CoreConfigSchema, EnvSchema, HaiCommonError, HaiConfigError, LogFormatSchema, LogLevelSchema, LoggingConfigSchema, RequestAccessConfigSchema, RequestAccessPolicySchema, TrustedProxiesSchema, err, ok } from './chunk-
|
|
1
|
+
import { installRequestLocales, installRequestContext, createCore, i18n, err, HaiConfigError, ok, RequestAccessConfigSchema, RequestAccessPolicySchema, AccessConditionSchema, requestContext, sanitize, emitLogRecord, sanitizeLogFields, CoreConfigSchema } from './chunk-WU6HUTBO.js';
|
|
2
|
+
export { AccessConditionSchema, CoreConfigSchema, EnvSchema, HaiCommonError, HaiConfigError, LogFormatSchema, LogLevelSchema, LoggingConfigSchema, RequestAccessConfigSchema, RequestAccessPolicySchema, TrustedProxiesSchema, err, ok } from './chunk-WU6HUTBO.js';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
4
|
import { Buffer } from 'buffer';
|
|
5
5
|
import { timingSafeEqual } from 'crypto';
|
|
6
6
|
import { existsSync, readFileSync, openSync, constants, fstatSync, readSync, closeSync, readdirSync, watch } from 'fs';
|
|
7
|
-
import path, { join } from 'path';
|
|
7
|
+
import path, { join, resolve, basename, dirname } from 'path';
|
|
8
8
|
import { BlockList, isIP } from 'net';
|
|
9
9
|
import { networkInterfaces } from 'os';
|
|
10
|
-
import
|
|
10
|
+
import process4 from 'process';
|
|
11
11
|
import { parse } from 'yaml';
|
|
12
12
|
import { parseEnv } from 'util';
|
|
13
13
|
import { execSync } from 'child_process';
|
|
@@ -36,13 +36,14 @@ function addAddress(list, entry) {
|
|
|
36
36
|
return;
|
|
37
37
|
}
|
|
38
38
|
const bits = Number(prefix);
|
|
39
|
-
if (!/^\d+$/.test(prefix) || bits < 0 || bits > (family === 4 ? 32 : 128))
|
|
39
|
+
if (!/^\d+$/.test(prefix) || bits < 0 || bits > (family === 4 ? 32 : 128)) {
|
|
40
40
|
throw new Error("Invalid CIDR prefix");
|
|
41
|
+
}
|
|
41
42
|
list.addSubnet(address, bits, type);
|
|
42
43
|
}
|
|
43
44
|
function createClientIpResolver(trustedProxies = "auto") {
|
|
44
|
-
const entries = trustedProxies === "auto" ?
|
|
45
|
-
if (trustedProxies === "auto" &&
|
|
45
|
+
const entries = trustedProxies === "auto" ? process4.env.HAI_DEPLOY_PRIVATE_PROXY === "1" ? Object.values(networkInterfaces()).flatMap((interfaces) => interfaces?.filter((value) => !value.internal && value.cidr && !value.cidr.includes("%")).map((value) => value.cidr) ?? []) : [] : trustedProxies;
|
|
46
|
+
if (trustedProxies === "auto" && process4.env.HAI_DEPLOY_PRIVATE_PROXY === "1" && !entries.length)
|
|
46
47
|
throw new Error("Private proxy mode requires an isolated container network");
|
|
47
48
|
const proxies = new BlockList();
|
|
48
49
|
for (const entry of entries)
|
|
@@ -73,8 +74,22 @@ function createClientIpResolver(trustedProxies = "auto") {
|
|
|
73
74
|
};
|
|
74
75
|
}
|
|
75
76
|
function timeReader(timezone) {
|
|
76
|
-
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
77
|
-
|
|
77
|
+
const formatter = new Intl.DateTimeFormat("en-US", {
|
|
78
|
+
timeZone: timezone,
|
|
79
|
+
weekday: "short",
|
|
80
|
+
hour: "2-digit",
|
|
81
|
+
minute: "2-digit",
|
|
82
|
+
hourCycle: "h23"
|
|
83
|
+
});
|
|
84
|
+
const weekdays = [
|
|
85
|
+
"Mon",
|
|
86
|
+
"Tue",
|
|
87
|
+
"Wed",
|
|
88
|
+
"Thu",
|
|
89
|
+
"Fri",
|
|
90
|
+
"Sat",
|
|
91
|
+
"Sun"
|
|
92
|
+
];
|
|
78
93
|
let lastMinute = Number.NaN;
|
|
79
94
|
let local;
|
|
80
95
|
return (now) => {
|
|
@@ -120,7 +135,7 @@ function createConditionCheck(input) {
|
|
|
120
135
|
const condition = AccessConditionSchema.parse(input);
|
|
121
136
|
return compileCondition(condition, condition.windows.length ? timeReader(condition.timezone) : void 0);
|
|
122
137
|
}
|
|
123
|
-
function loadPolicy(input = false, baseDirectory =
|
|
138
|
+
function loadPolicy(input = false, baseDirectory = process4.cwd()) {
|
|
124
139
|
const config2 = RequestAccessConfigSchema.parse(input);
|
|
125
140
|
if (config2 === false || !("file" in config2))
|
|
126
141
|
return config2;
|
|
@@ -180,8 +195,12 @@ function normalizeRequestPath(value) {
|
|
|
180
195
|
}
|
|
181
196
|
function createRequestCheck(input = false) {
|
|
182
197
|
const policy = loadPolicy(input);
|
|
183
|
-
if (policy === false)
|
|
184
|
-
return Object.assign(() => allowed, {
|
|
198
|
+
if (policy === false) {
|
|
199
|
+
return Object.assign(() => allowed, {
|
|
200
|
+
encryption: () => void 0,
|
|
201
|
+
requiresEncryption: false
|
|
202
|
+
});
|
|
203
|
+
}
|
|
185
204
|
const clocks = /* @__PURE__ */ new Map();
|
|
186
205
|
const compile = (condition, id) => {
|
|
187
206
|
let readTime;
|
|
@@ -193,12 +212,29 @@ function createRequestCheck(input = false) {
|
|
|
193
212
|
};
|
|
194
213
|
const globalCheck = policy.global ? compile(policy.global) : () => allowed;
|
|
195
214
|
const exact = /* @__PURE__ */ new Map();
|
|
196
|
-
const prefixes = {
|
|
197
|
-
|
|
215
|
+
const prefixes = {
|
|
216
|
+
children: /* @__PURE__ */ new Map(),
|
|
217
|
+
rules: []
|
|
218
|
+
};
|
|
219
|
+
const allMethods = [
|
|
220
|
+
"GET",
|
|
221
|
+
"HEAD",
|
|
222
|
+
"POST",
|
|
223
|
+
"PUT",
|
|
224
|
+
"PATCH",
|
|
225
|
+
"DELETE",
|
|
226
|
+
"OPTIONS",
|
|
227
|
+
"CONNECT",
|
|
228
|
+
"TRACE",
|
|
229
|
+
"*"
|
|
230
|
+
];
|
|
198
231
|
let requiresEncryption = false;
|
|
199
232
|
let hasEncryption = false;
|
|
200
233
|
for (const rule of policy.rules) {
|
|
201
|
-
const compiled = {
|
|
234
|
+
const compiled = {
|
|
235
|
+
methods: new Set(rule.methods),
|
|
236
|
+
check: compile(rule, rule.id)
|
|
237
|
+
};
|
|
202
238
|
for (const pattern of new Set(rule.paths)) {
|
|
203
239
|
if (rule.encryption) {
|
|
204
240
|
hasEncryption = true;
|
|
@@ -207,11 +243,14 @@ function createRequestCheck(input = false) {
|
|
|
207
243
|
const nodes = [prefixes];
|
|
208
244
|
let node = prefixes;
|
|
209
245
|
const path2 = normalizeRequestPath(prefix ? pattern.slice(0, -3) || "/" : pattern);
|
|
210
|
-
for (const
|
|
211
|
-
let child = node.children.get(
|
|
246
|
+
for (const segment2 of path2.split("/").filter(Boolean)) {
|
|
247
|
+
let child = node.children.get(segment2);
|
|
212
248
|
if (!child) {
|
|
213
|
-
child = {
|
|
214
|
-
|
|
249
|
+
child = {
|
|
250
|
+
children: /* @__PURE__ */ new Map(),
|
|
251
|
+
rules: []
|
|
252
|
+
};
|
|
253
|
+
node.children.set(segment2, child);
|
|
215
254
|
}
|
|
216
255
|
node = child;
|
|
217
256
|
nodes.push(node);
|
|
@@ -233,11 +272,14 @@ function createRequestCheck(input = false) {
|
|
|
233
272
|
}
|
|
234
273
|
if (pattern.endsWith("/**")) {
|
|
235
274
|
let node = prefixes;
|
|
236
|
-
for (const
|
|
237
|
-
let child = node.children.get(
|
|
275
|
+
for (const segment2 of pattern.slice(0, -3).split("/").filter(Boolean)) {
|
|
276
|
+
let child = node.children.get(segment2);
|
|
238
277
|
if (!child) {
|
|
239
|
-
child = {
|
|
240
|
-
|
|
278
|
+
child = {
|
|
279
|
+
children: /* @__PURE__ */ new Map(),
|
|
280
|
+
rules: []
|
|
281
|
+
};
|
|
282
|
+
node.children.set(segment2, child);
|
|
241
283
|
}
|
|
242
284
|
node = child;
|
|
243
285
|
}
|
|
@@ -273,8 +315,8 @@ function createRequestCheck(input = false) {
|
|
|
273
315
|
}
|
|
274
316
|
}
|
|
275
317
|
if (node.children.size) {
|
|
276
|
-
for (const
|
|
277
|
-
node = node.children.get(
|
|
318
|
+
for (const segment2 of path2.split("/").filter(Boolean)) {
|
|
319
|
+
node = node.children.get(segment2);
|
|
278
320
|
if (!node)
|
|
279
321
|
break;
|
|
280
322
|
for (const rule of node.rules) {
|
|
@@ -294,87 +336,303 @@ function createRequestCheck(input = false) {
|
|
|
294
336
|
return void 0;
|
|
295
337
|
let node = prefixes;
|
|
296
338
|
let mode = node.encryption?.get(method) ?? node.encryption?.get("*");
|
|
297
|
-
for (const
|
|
298
|
-
node = node.children.get(
|
|
339
|
+
for (const segment2 of path2.split("/").filter(Boolean)) {
|
|
340
|
+
node = node.children.get(segment2);
|
|
299
341
|
if (!node)
|
|
300
342
|
return mode;
|
|
301
343
|
mode ??= node.encryption?.get(method) ?? node.encryption?.get("*");
|
|
302
344
|
}
|
|
303
345
|
return mode ?? node?.exactEncryption?.get(method) ?? node?.exactEncryption?.get("*");
|
|
304
346
|
};
|
|
305
|
-
return Object.assign(check, {
|
|
347
|
+
return Object.assign(check, {
|
|
348
|
+
encryption: hasEncryption ? encryption : () => void 0,
|
|
349
|
+
requiresEncryption
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
var access = {
|
|
353
|
+
normalizeClientIp,
|
|
354
|
+
createClientIpResolver,
|
|
355
|
+
createConditionCheck,
|
|
356
|
+
createRequestCheck,
|
|
357
|
+
loadPolicy
|
|
358
|
+
};
|
|
359
|
+
function object(value) {
|
|
360
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
361
|
+
}
|
|
362
|
+
function segment(value) {
|
|
363
|
+
return value.replace(/[^a-z0-9]+/gi, "_").replace(/^_|_$/g, "").toUpperCase();
|
|
364
|
+
}
|
|
365
|
+
function shapes(node, root, value, depth = 0) {
|
|
366
|
+
if (depth > 32)
|
|
367
|
+
return [];
|
|
368
|
+
const result = [node];
|
|
369
|
+
if (typeof node.$ref === "string" && node.$ref.startsWith("#/")) {
|
|
370
|
+
let target = root;
|
|
371
|
+
for (const key of node.$ref.slice(2).split("/").map((item) => item.replaceAll("~1", "/").replaceAll("~0", "~")))
|
|
372
|
+
target = object(target)?.[key];
|
|
373
|
+
if (object(target))
|
|
374
|
+
result.push(...shapes(object(target), root, value, depth + 1));
|
|
375
|
+
}
|
|
376
|
+
for (const keyword of ["anyOf", "oneOf", "allOf"]) {
|
|
377
|
+
if (!Array.isArray(node[keyword]))
|
|
378
|
+
continue;
|
|
379
|
+
for (const item of node[keyword]) {
|
|
380
|
+
const branch = object(item);
|
|
381
|
+
if (!branch)
|
|
382
|
+
continue;
|
|
383
|
+
const fields = object(branch.properties);
|
|
384
|
+
const input = object(value);
|
|
385
|
+
if (fields && input && Object.entries(fields).some(([key, field]) => input[key] !== void 0 && object(field)?.const !== void 0 && object(field).const !== input[key]))
|
|
386
|
+
continue;
|
|
387
|
+
result.push(...shapes(branch, root, value, depth + 1));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
306
391
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
392
|
+
function combined(nodes) {
|
|
393
|
+
const entries = nodes.map(object).filter((item) => item !== void 0);
|
|
394
|
+
return entries.length === 1 ? entries[0] : { anyOf: entries };
|
|
395
|
+
}
|
|
396
|
+
function scalar(text, node, root) {
|
|
397
|
+
const types = new Set(shapes(node, root).flatMap((item) => Array.isArray(item.type) ? item.type : typeof item.type === "string" ? [item.type] : []));
|
|
398
|
+
if (types.has("string") && [...types].every((type) => type === "string" || type === "null") && !(text === "null" && types.has("null")))
|
|
399
|
+
return text;
|
|
400
|
+
if (text === "")
|
|
312
401
|
return "";
|
|
313
402
|
try {
|
|
314
|
-
|
|
315
|
-
return parsed === void 0 ? value : parsed;
|
|
403
|
+
return parse(text) ?? (text === "null" ? null : text);
|
|
316
404
|
} catch {
|
|
317
|
-
return
|
|
405
|
+
return text;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function mergeEnv(layers) {
|
|
409
|
+
const values = {};
|
|
410
|
+
for (const layer of layers) {
|
|
411
|
+
for (const name of Object.keys(layer).sort((left, right) => left.length - right.length || left.localeCompare(right))) {
|
|
412
|
+
if (layer[name] === void 0)
|
|
413
|
+
continue;
|
|
414
|
+
if (name.startsWith("HAI_")) {
|
|
415
|
+
for (const previous of Object.keys(values)) {
|
|
416
|
+
if (previous !== "HAI_SERV_REFRESH_COOKIE_SECURE" && previous.startsWith(`${name}_`))
|
|
417
|
+
delete values[previous];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
values[name] = layer[name];
|
|
421
|
+
}
|
|
318
422
|
}
|
|
423
|
+
return values;
|
|
319
424
|
}
|
|
320
|
-
function
|
|
321
|
-
|
|
425
|
+
function parseEnvironment(text) {
|
|
426
|
+
const result = parseEnv(text);
|
|
427
|
+
const names = /* @__PURE__ */ new Set();
|
|
428
|
+
const assignments = /^[ \t]*(?:export[ \t]+)?([A-Z_]\w*)[ \t]*=[ \t]*('[^']*'|"(?:[^"\\]|\\.)*"|`[^`]*`|[^\r\n]*)/gim;
|
|
429
|
+
for (const match of text.matchAll(assignments)) {
|
|
430
|
+
const name = match[1];
|
|
431
|
+
if (names.has(name))
|
|
432
|
+
throw new Error(`Duplicate environment variable: ${name}`);
|
|
433
|
+
names.add(name);
|
|
434
|
+
if (match[2].startsWith('"') && (match[2].includes('\\"') || match[2].includes("\\u0027"))) {
|
|
435
|
+
try {
|
|
436
|
+
result[name] = JSON.parse(match[2]);
|
|
437
|
+
} catch {
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
return Object.fromEntries(Object.entries(result).filter((entry) => entry[1] !== void 0));
|
|
322
442
|
}
|
|
323
|
-
function
|
|
324
|
-
return
|
|
443
|
+
function stringifyEnv(values, options = {}) {
|
|
444
|
+
return `${Object.keys(values).sort().map((name) => {
|
|
445
|
+
if (!/^[A-Z_]\w*$/i.test(name))
|
|
446
|
+
throw new Error("Invalid environment variable name");
|
|
447
|
+
const value = values[name];
|
|
448
|
+
let literal;
|
|
449
|
+
if (!value.includes("'"))
|
|
450
|
+
literal = `'${value}'`;
|
|
451
|
+
else if (options.target === "compose")
|
|
452
|
+
literal = JSON.stringify(value).replaceAll("$", () => "$$");
|
|
453
|
+
else literal = JSON.stringify(value).replaceAll("'", "\\u0027");
|
|
454
|
+
return `${name}=${literal}`;
|
|
455
|
+
}).join("\n")}
|
|
456
|
+
`;
|
|
325
457
|
}
|
|
326
|
-
function
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
458
|
+
function resolveEnvironment(options, literal = false) {
|
|
459
|
+
const environment = options.environment ?? process4.env;
|
|
460
|
+
const missingNames = /* @__PURE__ */ new Set();
|
|
461
|
+
let root;
|
|
462
|
+
try {
|
|
463
|
+
root = options.schema ? options.schema.toJSONSchema({ io: "input", unrepresentable: "any" }) : options.shape ?? {};
|
|
464
|
+
} catch {
|
|
465
|
+
root = {};
|
|
466
|
+
}
|
|
467
|
+
const prefix = `HAI_${segment(options.name)}`;
|
|
468
|
+
function visit(original, name, node, inheritedLiteral, depth, declaration = original) {
|
|
469
|
+
if (depth > 32)
|
|
470
|
+
throw new Error(name);
|
|
471
|
+
let value = original;
|
|
472
|
+
let fromEnvironment = inheritedLiteral;
|
|
473
|
+
if (environment[name] !== void 0) {
|
|
474
|
+
value = scalar(environment[name], node, root);
|
|
475
|
+
fromEnvironment = true;
|
|
476
|
+
}
|
|
477
|
+
const alternatives = shapes(node, root);
|
|
478
|
+
const discriminators = /* @__PURE__ */ new Map();
|
|
479
|
+
for (const candidate of alternatives) {
|
|
480
|
+
for (const [key, field] of Object.entries(object(candidate.properties) ?? {})) {
|
|
481
|
+
if (shapes(object(field) ?? {}, root).some((item) => Object.hasOwn(item, "const"))) {
|
|
482
|
+
discriminators.set(key, [
|
|
483
|
+
...discriminators.get(key) ?? [],
|
|
484
|
+
field
|
|
485
|
+
]);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const selection = {
|
|
490
|
+
...object(value)
|
|
491
|
+
};
|
|
492
|
+
for (const [key, fields] of discriminators) {
|
|
493
|
+
const text = environment[`${name}_${segment(key)}`];
|
|
494
|
+
if (text !== void 0)
|
|
495
|
+
selection[key] = scalar(text, combined(fields), root);
|
|
496
|
+
}
|
|
497
|
+
const candidates = shapes(node, root, Object.keys(selection).length ? selection : value);
|
|
498
|
+
const synthesized = value === void 0;
|
|
499
|
+
const valueType = typeof value;
|
|
500
|
+
const primitiveOverride = value === void 0 || candidates.some((candidate) => value !== null && candidate.type === valueType || candidate.const === value || value === null && candidate.type === "null");
|
|
501
|
+
if (primitiveOverride && !object(value) && !Array.isArray(value)) {
|
|
502
|
+
const declared = /* @__PURE__ */ new Set([
|
|
503
|
+
...Object.keys(object(declaration) ?? {}),
|
|
504
|
+
...candidates.flatMap((candidate) => Object.keys(object(candidate.properties) ?? {}))
|
|
505
|
+
]);
|
|
506
|
+
const specified = [...declared].filter((key) => Object.keys(environment).some((candidate) => environment[candidate] !== void 0 && (candidate === `${name}_${segment(key)}` || candidate.startsWith(`${name}_${segment(key)}_`))));
|
|
507
|
+
if (specified.length)
|
|
508
|
+
value = Object.fromEntries(specified.map((key) => [key, void 0]));
|
|
509
|
+
}
|
|
510
|
+
if (value === void 0 && Object.keys(environment).some((key) => environment[key] !== void 0 && key.startsWith(`${name}_`))) {
|
|
511
|
+
if (candidates.some((item) => item.type === "object" || object(item.properties)))
|
|
512
|
+
value = {};
|
|
513
|
+
}
|
|
514
|
+
const indexed = Object.keys(environment).filter((key) => environment[key] !== void 0 && key.startsWith(`${name}_`) && /^\d+(?:_|$)/.test(key.slice(name.length + 1)));
|
|
515
|
+
if (value === void 0 && indexed.length && candidates.some((candidate) => candidate.type === "array")) {
|
|
516
|
+
const defaults = candidates.find((candidate) => Array.isArray(candidate.default))?.default;
|
|
517
|
+
if (!Array.isArray(defaults))
|
|
518
|
+
throw new TypeError(indexed[0]);
|
|
519
|
+
value = structuredClone(defaults);
|
|
520
|
+
}
|
|
521
|
+
if (Array.isArray(value)) {
|
|
522
|
+
const itemShape = combined(candidates.map((item) => item.items));
|
|
523
|
+
for (const key of indexed) {
|
|
524
|
+
const index = key.slice(name.length + 1).split("_")[0];
|
|
525
|
+
if (/^\d+$/.test(index) && (String(Number(index)) !== index || Number(index) >= value.length))
|
|
526
|
+
throw new Error(key);
|
|
527
|
+
}
|
|
528
|
+
return value.map((item, index) => visit(item, `${name}_${index}`, itemShape, fromEnvironment, depth + 1, Array.isArray(declaration) ? declaration[index] : void 0));
|
|
529
|
+
}
|
|
530
|
+
const input = object(value);
|
|
531
|
+
if (input) {
|
|
532
|
+
const properties = /* @__PURE__ */ new Map();
|
|
533
|
+
for (const candidate of candidates) {
|
|
534
|
+
for (const [key, field] of Object.entries(object(candidate.properties) ?? {})) {
|
|
535
|
+
properties.set(key, [
|
|
536
|
+
...properties.get(key) ?? [],
|
|
537
|
+
field
|
|
538
|
+
]);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const declaredKeys = /* @__PURE__ */ new Set([
|
|
542
|
+
...Object.keys(object(declaration) ?? {}),
|
|
543
|
+
...properties.keys()
|
|
544
|
+
]);
|
|
545
|
+
const keys = /* @__PURE__ */ new Set([
|
|
546
|
+
...Object.keys(input),
|
|
547
|
+
...[...declaredKeys].filter((key) => Object.keys(environment).some((nameInEnv) => environment[nameInEnv] !== void 0 && (nameInEnv === `${name}_${segment(key)}` || nameInEnv.startsWith(`${name}_${segment(key)}_`))))
|
|
548
|
+
]);
|
|
549
|
+
const entries = [...keys].map((key) => {
|
|
550
|
+
if (["__proto__", "prototype", "constructor"].includes(key))
|
|
551
|
+
throw new Error(name);
|
|
552
|
+
return [
|
|
553
|
+
key,
|
|
554
|
+
visit(input[key], `${name}_${segment(key)}`, combined(properties.get(key) ?? candidates.map((candidate) => candidate.additionalProperties)), fromEnvironment, depth + 1, object(declaration)?.[key])
|
|
555
|
+
];
|
|
556
|
+
}).filter(([, child]) => child !== void 0);
|
|
557
|
+
return synthesized && !entries.length ? void 0 : Object.fromEntries(entries);
|
|
558
|
+
}
|
|
559
|
+
if (typeof value !== "string" || fromEnvironment)
|
|
560
|
+
return value;
|
|
561
|
+
const parts = [];
|
|
562
|
+
let consumed = 0;
|
|
563
|
+
let search = 0;
|
|
564
|
+
let full = false;
|
|
565
|
+
while (search < value.length) {
|
|
566
|
+
const start = value.indexOf("${", search);
|
|
567
|
+
if (start < 0)
|
|
568
|
+
break;
|
|
569
|
+
if (value[start + 2] === ":" || value[start + 2] === "}") {
|
|
570
|
+
search = start + 2;
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const end = value.indexOf("}", start + 2);
|
|
574
|
+
if (end < 0)
|
|
340
575
|
break;
|
|
341
|
-
const
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
576
|
+
const expression = value.slice(start + 2, end);
|
|
577
|
+
const separator = expression.indexOf(":");
|
|
578
|
+
const key = separator < 0 ? expression : expression.slice(0, separator);
|
|
579
|
+
const fallback = separator < 0 ? void 0 : expression.slice(separator + 1);
|
|
580
|
+
if (environment[key] === void 0 && fallback === void 0 && options.missing !== "empty") {
|
|
581
|
+
missingNames.add(key);
|
|
582
|
+
throw new Error(key);
|
|
345
583
|
}
|
|
346
|
-
|
|
584
|
+
parts.push(value.slice(consumed, start), environment[key] ?? fallback ?? "");
|
|
585
|
+
consumed = end + 1;
|
|
586
|
+
search = consumed;
|
|
587
|
+
full = start === 0 && consumed === value.length;
|
|
347
588
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
return ok(result);
|
|
589
|
+
const resolved = parts.join("") + value.slice(consumed);
|
|
590
|
+
return full ? scalar(resolved, node, root) : resolved;
|
|
351
591
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
592
|
+
try {
|
|
593
|
+
return ok(visit(options.value, prefix, root, literal, 0));
|
|
594
|
+
} catch (failure) {
|
|
595
|
+
const name = failure instanceof Error && /^[A-Z_]\w*$/i.test(failure.message) ? failure.message : prefix;
|
|
596
|
+
if (!missingNames.has(name)) {
|
|
597
|
+
return err(HaiConfigError.CONFIG_VALIDATION_ERROR, i18n.coreM("core_configValidationFailed"), {
|
|
598
|
+
fields: [name]
|
|
599
|
+
});
|
|
359
600
|
}
|
|
360
|
-
return
|
|
601
|
+
return err(HaiConfigError.CONFIG_ENV_VAR_MISSING, i18n.coreM("core_configEnvVarMissing", {
|
|
602
|
+
params: { varName: name }
|
|
603
|
+
}));
|
|
361
604
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
605
|
+
}
|
|
606
|
+
function toEnv(options) {
|
|
607
|
+
const result = {};
|
|
608
|
+
const root = options.schema ? options.schema.toJSONSchema({ io: "input", unrepresentable: "any" }) : options.shape ?? {};
|
|
609
|
+
function visit(value, name, node) {
|
|
610
|
+
if (!/^[A-Z][A-Z0-9_]*$/.test(name))
|
|
611
|
+
throw new Error("Invalid environment variable name");
|
|
612
|
+
if (value === void 0)
|
|
613
|
+
return;
|
|
614
|
+
const input = object(value);
|
|
615
|
+
const candidates = shapes(node, root, value);
|
|
616
|
+
const properties = Object.assign({}, ...candidates.map((candidate) => object(candidate.properties) ?? {}));
|
|
617
|
+
const dynamic = candidates.some((candidate) => candidate.additionalProperties === true || object(candidate.additionalProperties));
|
|
618
|
+
if (input && Object.keys(input).length && !dynamic && Object.keys(input).every((key) => /^[a-z][a-zA-Z0-9]*$/.test(key))) {
|
|
619
|
+
for (const [key, item] of Object.entries(input))
|
|
620
|
+
visit(item, `${name}_${segment(key)}`, object(properties[key]) ?? {});
|
|
621
|
+
} else {
|
|
622
|
+
result[name] = value === null || typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
369
623
|
}
|
|
370
|
-
return ok(results);
|
|
371
624
|
}
|
|
372
|
-
|
|
625
|
+
visit(options.value, options.prefix, root);
|
|
626
|
+
return result;
|
|
373
627
|
}
|
|
628
|
+
|
|
629
|
+
// src/functions/core-function-config.node.ts
|
|
374
630
|
var configCache = /* @__PURE__ */ new Map();
|
|
375
631
|
var watchEntries = /* @__PURE__ */ new Map();
|
|
376
632
|
function createNotLoadedError(name) {
|
|
377
|
-
const result = err(HaiConfigError.CONFIG_NOT_LOADED, i18n.coreM("core_configNotLoaded", {
|
|
633
|
+
const result = err(HaiConfigError.CONFIG_NOT_LOADED, i18n.coreM("core_configNotLoaded", {
|
|
634
|
+
params: { name }
|
|
635
|
+
}));
|
|
378
636
|
if (!result.success)
|
|
379
637
|
return result.error;
|
|
380
638
|
throw new Error("unreachable");
|
|
@@ -399,20 +657,24 @@ function notifyWatchCallbacks(name, result) {
|
|
|
399
657
|
}
|
|
400
658
|
}
|
|
401
659
|
}
|
|
402
|
-
function loadYaml(name, filePath) {
|
|
660
|
+
function loadYaml(name, filePath, schema) {
|
|
403
661
|
if (!existsSync(filePath)) {
|
|
404
|
-
return err(HaiConfigError.CONFIG_FILE_NOT_FOUND, i18n.coreM("core_configFileNotExist", {
|
|
662
|
+
return err(HaiConfigError.CONFIG_FILE_NOT_FOUND, i18n.coreM("core_configFileNotExist", {
|
|
663
|
+
params: { filePath }
|
|
664
|
+
}));
|
|
405
665
|
}
|
|
406
666
|
try {
|
|
407
667
|
const content = readFileSync(filePath, "utf-8");
|
|
408
668
|
const parsed = parse(content);
|
|
409
|
-
return
|
|
669
|
+
return resolveEnvironment({ name, value: parsed, schema });
|
|
410
670
|
} catch (error) {
|
|
411
|
-
return err(HaiConfigError.CONFIG_PARSE_ERROR, i18n.coreM("core_configParseFailed", {
|
|
671
|
+
return err(HaiConfigError.CONFIG_PARSE_ERROR, i18n.coreM("core_configParseFailed", {
|
|
672
|
+
params: { filePath }
|
|
673
|
+
}), error);
|
|
412
674
|
}
|
|
413
675
|
}
|
|
414
676
|
function loadConfig(name, filePath, schema) {
|
|
415
|
-
const yamlResult = loadYaml(name, filePath);
|
|
677
|
+
const yamlResult = loadYaml(name, filePath, schema);
|
|
416
678
|
if (!yamlResult.success)
|
|
417
679
|
return yamlResult;
|
|
418
680
|
const parseResult = schema.safeParse(yamlResult.data);
|
|
@@ -433,12 +695,27 @@ function loadAndCache(name, filePath, schema) {
|
|
|
433
695
|
}
|
|
434
696
|
return result;
|
|
435
697
|
}
|
|
698
|
+
function loadConfigStructure(name, filePath, schemaHint) {
|
|
699
|
+
const result = loadYaml(name, filePath, schemaHint);
|
|
700
|
+
if (result.success) {
|
|
701
|
+
configCache.set(name, {
|
|
702
|
+
data: result.data,
|
|
703
|
+
filePath,
|
|
704
|
+
schemaHint,
|
|
705
|
+
loadedAt: Date.now()
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
436
710
|
function validateLoadedConfig(name, schema) {
|
|
437
711
|
const entry = configCache.get(name);
|
|
438
712
|
if (!entry) {
|
|
439
713
|
return err(createNotLoadedError(name));
|
|
440
714
|
}
|
|
441
|
-
const
|
|
715
|
+
const resolved = resolveEnvironment({ name, value: entry.data, schema }, true);
|
|
716
|
+
if (!resolved.success)
|
|
717
|
+
return resolved;
|
|
718
|
+
const parseResult = schema.safeParse(resolved.data);
|
|
442
719
|
if (!parseResult.success) {
|
|
443
720
|
return err(HaiConfigError.CONFIG_VALIDATION_ERROR, i18n.coreM("core_configValidationFailed"), parseResult.error.issues);
|
|
444
721
|
}
|
|
@@ -458,7 +735,7 @@ function reloadAndNotify(name) {
|
|
|
458
735
|
notifyWatchCallbacks(name, result2);
|
|
459
736
|
return result2;
|
|
460
737
|
}
|
|
461
|
-
const result = entry.schema ? loadConfig(name, entry.filePath, entry.schema) : loadYaml(name, entry.filePath);
|
|
738
|
+
const result = entry.schema ? loadConfig(name, entry.filePath, entry.schema) : loadYaml(name, entry.filePath, entry.schemaHint);
|
|
462
739
|
if (result.success) {
|
|
463
740
|
configCache.set(name, {
|
|
464
741
|
...entry,
|
|
@@ -482,15 +759,18 @@ function startFileWatcher(name) {
|
|
|
482
759
|
callbacks: /* @__PURE__ */ new Set(),
|
|
483
760
|
debounceTimer: null
|
|
484
761
|
};
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
762
|
+
const filePath = resolve(entry.filePath);
|
|
763
|
+
const fileName = basename(filePath);
|
|
764
|
+
const watcher = watch(dirname(filePath), (_eventType, changedName) => {
|
|
765
|
+
if (changedName !== null && changedName.toString() !== fileName)
|
|
766
|
+
return;
|
|
767
|
+
if (watchEntry.debounceTimer)
|
|
768
|
+
clearTimeout(watchEntry.debounceTimer);
|
|
769
|
+
watchEntry.debounceTimer = setTimeout(() => {
|
|
770
|
+
watchEntry.debounceTimer = null;
|
|
771
|
+
if (watchEntries.get(name) === watchEntry)
|
|
491
772
|
reloadAndNotify(name);
|
|
492
|
-
|
|
493
|
-
}
|
|
773
|
+
}, 100);
|
|
494
774
|
});
|
|
495
775
|
watchEntry.watcher = watcher;
|
|
496
776
|
watchEntries.set(name, watchEntry);
|
|
@@ -509,7 +789,7 @@ function registerWatch(name, callback) {
|
|
|
509
789
|
entry.callbacks.add(callback);
|
|
510
790
|
return () => {
|
|
511
791
|
const current = watchEntries.get(name);
|
|
512
|
-
if (
|
|
792
|
+
if (current !== entry)
|
|
513
793
|
return;
|
|
514
794
|
current.callbacks.delete(callback);
|
|
515
795
|
if (current.callbacks.size === 0) {
|
|
@@ -548,10 +828,67 @@ function clearCache(name) {
|
|
|
548
828
|
}
|
|
549
829
|
}
|
|
550
830
|
var config = {
|
|
831
|
+
/**
|
|
832
|
+
* 合并从低到高的环境来源;高来源父项替换旧节点,同来源细项覆盖父 JSON。
|
|
833
|
+
* @param layers - 按优先级排列的字面变量。
|
|
834
|
+
* @returns 合并后的变量,可能含秘密。
|
|
835
|
+
* @example
|
|
836
|
+
* ```ts
|
|
837
|
+
* import { core } from '@h-ai/core'
|
|
838
|
+
* const value = core.config.mergeEnv([{ HAI_APP_NAME: 'base' }, { HAI_APP_NAME: 'test' }])
|
|
839
|
+
* ```
|
|
840
|
+
*/
|
|
841
|
+
mergeEnv,
|
|
842
|
+
/**
|
|
843
|
+
* 解析 dotenv 字面文本,拒绝重复键,不执行 shell 或变量展开。
|
|
844
|
+
* @param text - dotenv 文本,可能包含秘密。
|
|
845
|
+
* @returns 字面变量;重复键抛出不含变量值的错误。
|
|
846
|
+
* @example
|
|
847
|
+
* ```ts
|
|
848
|
+
* import { core } from '@h-ai/core'
|
|
849
|
+
* const value = core.config.parseEnv('HAI_APP_NAME=Example\n')
|
|
850
|
+
* ```
|
|
851
|
+
*/
|
|
852
|
+
parseEnv: parseEnvironment,
|
|
853
|
+
/**
|
|
854
|
+
* 无损编码 dotenv,父变量排在细项前;含秘密时只能写入受保护文件。
|
|
855
|
+
* @param values - 字面变量,不进行脱敏。
|
|
856
|
+
* @param options - 输出语法目标,默认 Node dotenv。
|
|
857
|
+
* @param options.target - Compose 目标同时处理其美元符号插值规则。
|
|
858
|
+
* @returns dotenv 文本;非法变量名抛错。
|
|
859
|
+
* @example
|
|
860
|
+
* ```ts
|
|
861
|
+
* import { core } from '@h-ai/core'
|
|
862
|
+
* const value = core.config.stringifyEnv({ HAI_APP_NAME: 'Example' })
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
stringifyEnv,
|
|
866
|
+
/**
|
|
867
|
+
* 按已有结构或 Schema 解析父 JSON 和细项,不修改缓存或进程环境。
|
|
868
|
+
* @param options - 配置名、原始结构、变量、可选 Schema 和缺失插值规则。
|
|
869
|
+
* @returns 解析结果;调用方仍须执行最终 Schema 校验。
|
|
870
|
+
* @example
|
|
871
|
+
* ```ts
|
|
872
|
+
* import { core } from '@h-ai/core'
|
|
873
|
+
* const value = core.config.resolve({ name: 'app', value: { name: 'Example' }, environment: { HAI_APP_NAME: 'Test' } })
|
|
874
|
+
* ```
|
|
875
|
+
*/
|
|
876
|
+
resolve: (options) => resolveEnvironment(options),
|
|
877
|
+
/**
|
|
878
|
+
* 默认将对象展开为细项;数组、动态字典和空对象保留 JSON。
|
|
879
|
+
* @param options - 完整变量前缀、配置值和可选 Schema。
|
|
880
|
+
* @returns 字面环境变量,不脱敏;非法名称抛错。
|
|
881
|
+
* @example
|
|
882
|
+
* ```ts
|
|
883
|
+
* import { core } from '@h-ai/core'
|
|
884
|
+
* const value = core.config.toEnv({ prefix: 'HAI_APP', value: { name: 'Example', tags: ['demo'] } })
|
|
885
|
+
* ```
|
|
886
|
+
*/
|
|
887
|
+
toEnv,
|
|
551
888
|
/**
|
|
552
889
|
* 读取 dotenv 文件并按统一顺序合并,不修改进程环境。
|
|
553
890
|
*
|
|
554
|
-
* 优先级:已有进程环境 > .env.local > .env.<mode> > .env。
|
|
891
|
+
* 优先级:已有进程环境 > .env.<mode>.local > .env.local > .env.<mode> > .env。
|
|
555
892
|
* development/production 分别映射到 dev/prod;不存在的文件跳过。
|
|
556
893
|
* 返回值可能包含私有配置,仅在 Node 启动/构建边界注入,不下发浏览器。
|
|
557
894
|
* @param options - 文件所在目录及环境模式
|
|
@@ -570,21 +907,27 @@ var config = {
|
|
|
570
907
|
const mode = options.mode === "development" ? "dev" : options.mode === "production" ? "prod" : options.mode;
|
|
571
908
|
if (!/^[a-z0-9][\w-]*$/i.test(mode))
|
|
572
909
|
return err(HaiConfigError.CONFIG_VALIDATION_ERROR, i18n.coreM("core_configValidationFailed"));
|
|
573
|
-
const envDir = options.envDir ??
|
|
910
|
+
const envDir = options.envDir ?? process4.cwd();
|
|
574
911
|
try {
|
|
575
|
-
const
|
|
576
|
-
for (const name of [
|
|
912
|
+
const layers = [];
|
|
913
|
+
for (const name of [
|
|
914
|
+
".env",
|
|
915
|
+
`.env.${mode}`,
|
|
916
|
+
".env.local",
|
|
917
|
+
`.env.${mode}.local`
|
|
918
|
+
]) {
|
|
577
919
|
const file = join(envDir, name);
|
|
578
920
|
if (existsSync(file))
|
|
579
|
-
|
|
580
|
-
}
|
|
581
|
-
for (const name of Object.keys(merged)) {
|
|
582
|
-
if (process3.env[name] !== void 0)
|
|
583
|
-
merged[name] = process3.env[name];
|
|
921
|
+
layers.push(parseEnvironment(readFileSync(file, "utf8")));
|
|
584
922
|
}
|
|
923
|
+
const declared = new Set(layers.flatMap((layer) => Object.keys(layer)));
|
|
924
|
+
layers.push(Object.fromEntries(Object.entries(process4.env).filter(([name]) => declared.has(name) || name.startsWith("HAI_"))));
|
|
925
|
+
const merged = mergeEnv(layers);
|
|
585
926
|
return ok(merged);
|
|
586
927
|
} catch {
|
|
587
|
-
return err(HaiConfigError.CONFIG_PARSE_ERROR, i18n.coreM("core_configParseFailed", {
|
|
928
|
+
return err(HaiConfigError.CONFIG_PARSE_ERROR, i18n.coreM("core_configParseFailed", {
|
|
929
|
+
params: { filePath: envDir }
|
|
930
|
+
}));
|
|
588
931
|
}
|
|
589
932
|
},
|
|
590
933
|
/**
|
|
@@ -664,7 +1007,9 @@ var config = {
|
|
|
664
1007
|
getOrThrow(name) {
|
|
665
1008
|
const data = this.get(name);
|
|
666
1009
|
if (data === void 0) {
|
|
667
|
-
throw new Error(i18n.coreM("core_configNotLoaded", {
|
|
1010
|
+
throw new Error(i18n.coreM("core_configNotLoaded", {
|
|
1011
|
+
params: { name }
|
|
1012
|
+
}));
|
|
668
1013
|
}
|
|
669
1014
|
return data;
|
|
670
1015
|
},
|
|
@@ -730,7 +1075,8 @@ var config = {
|
|
|
730
1075
|
/**
|
|
731
1076
|
* 监听配置文件变更并自动重载。
|
|
732
1077
|
*
|
|
733
|
-
*
|
|
1078
|
+
* 普通写入、原子替换及删除重建均触发重载;失败保留最近成功的缓存并通知错误。
|
|
1079
|
+
* 配置未加载时立即回调 NOT_LOADED 错误。
|
|
734
1080
|
*
|
|
735
1081
|
* @param name - 配置名称
|
|
736
1082
|
* @param callback - 配置变更回调,接收新配置或错误
|
|
@@ -781,14 +1127,14 @@ var config = {
|
|
|
781
1127
|
}
|
|
782
1128
|
};
|
|
783
1129
|
var nodeRequire = createRequire(import.meta.url);
|
|
784
|
-
if (
|
|
1130
|
+
if (process4.platform === "win32" && process4.stdout.isTTY) {
|
|
785
1131
|
try {
|
|
786
1132
|
execSync("chcp 65001 > nul", { stdio: "ignore" });
|
|
787
|
-
if (
|
|
788
|
-
|
|
1133
|
+
if (process4.stdout.setDefaultEncoding) {
|
|
1134
|
+
process4.stdout.setDefaultEncoding("utf8");
|
|
789
1135
|
}
|
|
790
|
-
if (
|
|
791
|
-
|
|
1136
|
+
if (process4.stderr.setDefaultEncoding) {
|
|
1137
|
+
process4.stderr.setDefaultEncoding("utf8");
|
|
792
1138
|
}
|
|
793
1139
|
} catch (_e) {
|
|
794
1140
|
}
|
|
@@ -806,8 +1152,12 @@ function configureLogger(config2) {
|
|
|
806
1152
|
globalFormat = config2.format;
|
|
807
1153
|
outputRevision++;
|
|
808
1154
|
}
|
|
809
|
-
if (config2.context)
|
|
810
|
-
globalContext = {
|
|
1155
|
+
if (config2.context) {
|
|
1156
|
+
globalContext = {
|
|
1157
|
+
...globalContext,
|
|
1158
|
+
...config2.context
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
811
1161
|
if (config2.redact) {
|
|
812
1162
|
globalRedact = [...config2.redact];
|
|
813
1163
|
outputRevision++;
|
|
@@ -830,11 +1180,21 @@ function wrapPino(resolvePino, resolveContext) {
|
|
|
830
1180
|
const output = resolvePino();
|
|
831
1181
|
if (!output.isLevelEnabled(level))
|
|
832
1182
|
return;
|
|
833
|
-
const context = sanitizeLogContext({
|
|
1183
|
+
const context = sanitizeLogContext({
|
|
1184
|
+
...resolveContext(),
|
|
1185
|
+
...ctx,
|
|
1186
|
+
...requestContext.get()
|
|
1187
|
+
}) ?? {};
|
|
834
1188
|
message = sanitize.sanitizeText(message);
|
|
835
1189
|
output[level](context, message);
|
|
836
|
-
if (!requestContext.get().suppressLogObservers)
|
|
837
|
-
emitLogRecord({
|
|
1190
|
+
if (!requestContext.get().suppressLogObservers) {
|
|
1191
|
+
emitLogRecord({
|
|
1192
|
+
timestamp: Date.now(),
|
|
1193
|
+
level,
|
|
1194
|
+
message,
|
|
1195
|
+
context
|
|
1196
|
+
});
|
|
1197
|
+
}
|
|
838
1198
|
};
|
|
839
1199
|
return {
|
|
840
1200
|
trace(message, ctx) {
|
|
@@ -856,12 +1216,18 @@ function wrapPino(resolvePino, resolveContext) {
|
|
|
856
1216
|
write("fatal", message, ctx);
|
|
857
1217
|
},
|
|
858
1218
|
child(childContext) {
|
|
859
|
-
return wrapPino(resolvePino, () => ({
|
|
1219
|
+
return wrapPino(resolvePino, () => ({
|
|
1220
|
+
...resolveContext(),
|
|
1221
|
+
...childContext
|
|
1222
|
+
}));
|
|
860
1223
|
}
|
|
861
1224
|
};
|
|
862
1225
|
}
|
|
863
1226
|
function createLogger(options = {}) {
|
|
864
|
-
const localOptions = {
|
|
1227
|
+
const localOptions = {
|
|
1228
|
+
...options,
|
|
1229
|
+
context: { ...options.context }
|
|
1230
|
+
};
|
|
865
1231
|
let revision = outputRevision;
|
|
866
1232
|
let output = createPinoOutput(localOptions);
|
|
867
1233
|
const resolvePino = () => {
|
|
@@ -876,7 +1242,10 @@ function createLogger(options = {}) {
|
|
|
876
1242
|
output.instance.level = level;
|
|
877
1243
|
return output.instance;
|
|
878
1244
|
};
|
|
879
|
-
return wrapPino(resolvePino, () => ({
|
|
1245
|
+
return wrapPino(resolvePino, () => ({
|
|
1246
|
+
...globalContext,
|
|
1247
|
+
...localOptions.context
|
|
1248
|
+
}));
|
|
880
1249
|
}
|
|
881
1250
|
function createPinoOutput(options) {
|
|
882
1251
|
const level = options.level ?? globalLevel;
|
|
@@ -919,7 +1288,10 @@ function createPinoOutput(options) {
|
|
|
919
1288
|
} else {
|
|
920
1289
|
pinoInstance = pino(pinoOptions);
|
|
921
1290
|
}
|
|
922
|
-
return {
|
|
1291
|
+
return {
|
|
1292
|
+
instance: pinoInstance,
|
|
1293
|
+
close: () => transport?.end()
|
|
1294
|
+
};
|
|
923
1295
|
}
|
|
924
1296
|
function isPrettyTransportAvailable() {
|
|
925
1297
|
try {
|
|
@@ -1044,7 +1416,7 @@ function initCore(options = {}) {
|
|
|
1044
1416
|
});
|
|
1045
1417
|
}
|
|
1046
1418
|
} else {
|
|
1047
|
-
const result =
|
|
1419
|
+
const result = loadConfigStructure(item.name, item.filePath, options.configSchemas?.[item.name]);
|
|
1048
1420
|
if (result.success) {
|
|
1049
1421
|
logger2.info(`[core] Config loaded: ${item.name} <- ${item.filePath}`);
|
|
1050
1422
|
} else {
|