@fluojs/config 1.0.2 → 1.0.4
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.ko.md +6 -2
- package/README.md +6 -2
- package/dist/load.d.ts.map +1 -1
- package/dist/load.js +219 -27
- package/dist/options.d.ts +2 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +22 -4
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -6
package/README.ko.md
CHANGED
|
@@ -20,6 +20,8 @@ fluo 애플리케이션을 위한 설정 로드, 병합, 검증, 타입 안전
|
|
|
20
20
|
npm install @fluojs/config
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
패키지는 Node.js 20.16.0 이상을 지원합니다. Env-file loading, 기본 `.env` loading, watch mode는 Node filesystem, path, crypto builtin을 host runtime boundary를 통해 lazy하게 해석하며 `process.getBuiltinModule(...)`을 요구합니다. 이 API가 존재하지만 filesystem/path/crypto direct lookup이 불가능할 때는 published ESM과 호환되는 `node:module` fallback을 사용합니다. `ConfigService`와 `loadConfig({ defaults, processEnv, runtimeOverrides })`의 in-memory 사용은 env-file access를 요구하지 않지만, 배포 계약은 package-level Node.js 20.16.0 engine을 따릅니다.
|
|
24
|
+
|
|
23
25
|
## 사용 시점
|
|
24
26
|
|
|
25
27
|
- `.env` 파일과 명시적인 `processEnv` 스냅샷을 하나의 설정 스냅샷으로 합쳐야 할 때
|
|
@@ -79,7 +81,9 @@ class MyService {
|
|
|
79
81
|
|
|
80
82
|
`@fluojs/config`는 주변 환경 변수를 자동으로 스캔하지 않습니다. 환경 기반 값을 우선순위에 포함하려면 부트스트랩 경계에서 `processEnv` 스냅샷을 명시적으로 전달하세요.
|
|
81
83
|
|
|
82
|
-
`envFilePath`는 `envFile`보다 우선하며, `parse`를 사용하면 flat key/value 파일을 위한 custom parser로 dotenv parsing을 대체할 수 있습니다. 누락된 env file은 load 시 빈 입력처럼 처리됩니다. watch mode에서는 parent directory도 관찰하므로 나중에 파일을 생성해도 reload를 트리거할 수 있습니다.
|
|
84
|
+
`envFilePath`는 `envFile`보다 우선하며, `parse`를 사용하면 flat key/value 파일을 위한 custom parser로 dotenv parsing을 대체할 수 있습니다. 빈 load/module option은 `loadConfig({})`와 `ConfigModule.forRoot()`에 대해 기본 `<cwd>/.env` 동작을 보존합니다. 누락된 env file은 load 시 빈 입력처럼 처리됩니다. watch mode에서는 parent directory도 관찰하므로 나중에 파일을 생성해도 reload를 트리거할 수 있습니다.
|
|
85
|
+
|
|
86
|
+
Root `@fluojs/config` 패키지를 import하는 것만으로는 Node filesystem, path, crypto builtin을 해석하지 않습니다. `ConfigService`, option type, 또는 명시적 in-memory 입력을 쓰는 `loadConfig(...)` consumer는 root import를 안전하게 사용할 수 있고, Node builtin은 env-file load 또는 watch mode가 실제로 실행될 때 lazy하게 해석됩니다. `loadConfig({ defaults, processEnv, runtimeOverrides })`는 `process.cwd()`, 기본 `.env` path, Node filesystem/path/crypto builtin을 해석하지 않습니다. Published package engine이 Node.js 20.16.0 이상이므로 Node.js 20.0.0부터 20.15.x까지와 Node.js 밖의 runtime은 env-file, 기본 `.env`, watch execution path의 지원 package contract에 포함되지 않습니다.
|
|
83
87
|
|
|
84
88
|
### 객체 단위 딥 머지
|
|
85
89
|
|
|
@@ -97,7 +101,7 @@ class MyService {
|
|
|
97
101
|
|
|
98
102
|
`ConfigReloadManager.reload()`는 리로드 작업을 직렬화합니다. 현재 리로드가 listener 알림을 수행하는 동안 다른 리로드가 요청되면 후속 리로드는 큐에 들어가 활성 알림이 끝난 뒤 적용됩니다. 활성 알림이 실패하면 직전 snapshot을 복구하고 큐에 있던 리로드는 폐기합니다. 동일한 직렬화와 rollback 계약은 `createConfigReloader(...).reload()`에도 적용되며, watch로 시작된 알림 중 큐에 들어간 manual reload도 이 계약을 따릅니다.
|
|
99
103
|
|
|
100
|
-
Module registration과 reloader 생성은 caller-owned options를 저장하기 전에 snapshot으로 분리합니다. `ConfigModule.forRoot(...)`, `ConfigReloadModule.forRoot(...)`, `createConfigReloader(...)`에 넘긴 객체를 나중에 변경해도 bootstrap, manual reload, watch reload 입력은 바뀌지 않습니다. `ConfigModule.forRoot({ watch: true, ... })`를 사용하면 module은 application bootstrap 중 env-file watcher를 시작하고, 먼저 injected `ConfigService`를 watch reloader baseline과 맞춘 다음 watch reload가 성공한 뒤 같은 injected `ConfigService` instance를 갱신합니다. `ConfigModule`의 automatic watch reload 실패를 애플리케이션이 소유해야 한다면 `onReloadError`를 전달하세요. Watch mode에서는 기존 env file과 누락된 env file 모두에 대해 parent directory를 watch하므로, 나중에 env file을 생성하거나 atomic replacement로 교체해도 reload가 트리거될 수 있습니다. Watch reload는 reload 전에 최종 env file content를 마지막으로 commit된 watch baseline과 비교하므로, 내용이 바뀌지 않은 저장이나 변경 후 debounce 안에서 원래 내용으로 되돌린 burst는 인프로세스 config snapshot을 교체하지 않습니다.
|
|
104
|
+
Module registration과 reloader 생성은 `schema`로 전달한 nested Standard Schema validator object를 포함해 caller-owned options를 저장하기 전에 snapshot으로 분리합니다. `ConfigModule.forRoot(...)`, `ConfigReloadModule.forRoot(...)`, `createConfigReloader(...)`에 넘긴 객체를 나중에 변경해도 bootstrap, manual reload, watch reload 입력은 바뀌지 않습니다. `ConfigModule.forRoot({ watch: true, ... })`를 사용하면 module은 application bootstrap 중 env-file watcher를 시작하고, 먼저 injected `ConfigService`를 watch reloader baseline과 맞춘 다음 watch reload가 성공한 뒤 같은 injected `ConfigService` instance를 갱신합니다. `ConfigModule`의 automatic watch reload 실패를 애플리케이션이 소유해야 한다면 `onReloadError`를 전달하세요. Watch mode에서는 기존 env file과 누락된 env file 모두에 대해 parent directory를 watch하므로, 나중에 env file을 생성하거나 atomic replacement로 교체해도 reload가 트리거될 수 있습니다. Watch reload는 reload 전에 최종 env file content를 마지막으로 commit된 watch baseline과 비교하므로, 내용이 바뀌지 않은 저장이나 변경 후 debounce 안에서 원래 내용으로 되돌린 burst는 인프로세스 config snapshot을 교체하지 않습니다.
|
|
101
105
|
|
|
102
106
|
`ConfigReloadModule`은 명시적으로 주입 가능한 reload layer이며 standalone config source가 아닙니다. manual reload나 subscription을 위해 `CONFIG_RELOADER`가 필요한 caller는 `ConfigModule` 또는 다른 `ConfigService` provider와 함께 사용하세요. `ConfigModule` 또는 `ConfigReloadModule`이 만든 watcher는 `watch: true`일 때만 생성되며 module shutdown 중에 닫힙니다. 같은 env file에 대해서는 한 layer에서만 `watch: true`를 활성화하세요. 자동 `ConfigService` 갱신만 필요하면 `ConfigModule`을 사용하고, subscription/manual reload를 위한 injected reloader 계약이 필요하면 `ConfigReloadModule`을 사용합니다.
|
|
103
107
|
|
package/README.md
CHANGED
|
@@ -20,6 +20,8 @@ Configuration loading, merging, validation, and typed runtime access for fluo ap
|
|
|
20
20
|
npm install @fluojs/config
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
The package supports Node.js 20.16.0 or newer. Env-file loading, default `.env` loading, and watch mode resolve Node filesystem, path, and crypto builtins lazily through the host runtime boundary, requiring `process.getBuiltinModule(...)`; when that API is present but direct filesystem/path/crypto lookup is unavailable, a published-ESM-compatible `node:module` fallback is used. In-memory use of `ConfigService` and `loadConfig({ defaults, processEnv, runtimeOverrides })` does not require env-file access, but it is still distributed under the package-level Node.js 20.16.0 engine contract.
|
|
24
|
+
|
|
23
25
|
## When to Use
|
|
24
26
|
|
|
25
27
|
Use this package when you need to:
|
|
@@ -85,7 +87,9 @@ Configuration is merged in the following order (highest precedence wins):
|
|
|
85
87
|
|
|
86
88
|
`@fluojs/config` does not scan ambient environment variables automatically. Pass an explicit `processEnv` snapshot at the bootstrap boundary when process-backed values should participate in precedence.
|
|
87
89
|
|
|
88
|
-
`envFilePath` overrides `envFile`, and `parse` lets callers replace dotenv parsing with a custom parser for flat key/value files. Missing env files are treated as empty input during load; watch mode also observes the parent directory so creating the file later can trigger a reload.
|
|
90
|
+
`envFilePath` overrides `envFile`, and `parse` lets callers replace dotenv parsing with a custom parser for flat key/value files. Empty load/module options preserve the default `<cwd>/.env` behavior for `loadConfig({})` and `ConfigModule.forRoot()`. Missing env files are treated as empty input during load; watch mode also observes the parent directory so creating the file later can trigger a reload.
|
|
91
|
+
|
|
92
|
+
Importing the root `@fluojs/config` package is safe for in-memory consumers that only need `ConfigService`, option types, or `loadConfig(...)` with explicit in-memory inputs: Node filesystem, path, and crypto builtins are resolved lazily only when env-file loading or watch mode actually runs. `loadConfig({ defaults, processEnv, runtimeOverrides })` does not resolve `process.cwd()`, a default `.env` path, or Node filesystem/path/crypto builtins. Because the published package engine is Node.js 20.16.0 or newer, Node.js 20.0.0 through 20.15.x and non-Node runtimes are outside the supported package contract for env-file, default `.env`, and watch execution paths.
|
|
89
93
|
|
|
90
94
|
### Deep Merging
|
|
91
95
|
Plain objects are deep-merged by key. Arrays and primitive values from higher-precedence sources completely replace lower-precedence ones.
|
|
@@ -100,7 +104,7 @@ The `schema` option accepts a synchronous [Standard Schema](https://standardsche
|
|
|
100
104
|
|
|
101
105
|
`ConfigReloadManager.reload()` serializes reload work. If another reload is requested while the current reload is notifying listeners, the follow-up reload is queued and applied after the active notification finishes; if the active notification fails, the previous snapshot is restored and the queued reload is discarded. The same serialization and rollback contract applies to `createConfigReloader(...).reload()`, including manual reloads queued during watch-triggered notifications.
|
|
102
106
|
|
|
103
|
-
Module registration and reloader creation snapshot caller-owned options before storing them
|
|
107
|
+
Module registration and reloader creation snapshot caller-owned options before storing them, including nested Standard Schema validator objects supplied through `schema`. Later mutations to objects passed to `ConfigModule.forRoot(...)`, `ConfigReloadModule.forRoot(...)`, or `createConfigReloader(...)` do not affect bootstrap, manual reloads, or watch reloads. When `ConfigModule.forRoot({ watch: true, ... })` is used, the module starts an env-file watcher during application bootstrap, first aligns the injected `ConfigService` with the watch reloader baseline, and then updates the same injected `ConfigService` instance after successful watch reloads. Pass `onReloadError` when the application needs ownership of automatic watch reload failures from `ConfigModule`. In watch mode, the parent directory is watched for both existing and missing env files, so creating or atomically replacing the env file can trigger reload. Watch reloads compare the final env file content with the last committed watch baseline before reloading, so unchanged saves and change-then-revert bursts do not replace the in-process config snapshot.
|
|
104
108
|
|
|
105
109
|
`ConfigReloadModule` is the explicit injectable reload layer, not a standalone config source. Pair it with `ConfigModule` or another `ConfigService` provider when callers need `CONFIG_RELOADER` for manual reloads or subscriptions. Watchers created by `ConfigModule` or `ConfigReloadModule` are created only when `watch: true`, and they are closed during module shutdown. Enable `watch: true` on one layer for a given env file: use `ConfigModule` for automatic `ConfigService` updates, or `ConfigReloadModule` when callers need the injected reloader contract for subscriptions/manual reloads.
|
|
106
110
|
|
package/dist/load.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"load.d.ts","sourceRoot":"","sources":["../src/load.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAEjB,cAAc,EAKf,MAAM,YAAY,CAAC;AAisBpB;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAgC/E;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAEvE"}
|
package/dist/load.js
CHANGED
|
@@ -1,12 +1,71 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { existsSync, readFileSync, watch } from 'node:fs';
|
|
3
|
-
import { basename, dirname, join } from 'node:path';
|
|
4
1
|
import { FluoError } from '@fluojs/core';
|
|
5
|
-
import { parse as dotenvParse } from 'dotenv';
|
|
6
|
-
import { expand as dotenvExpand } from 'dotenv-expand';
|
|
7
2
|
import { cloneConfigDictionary } from './clone.js';
|
|
8
3
|
import { snapshotConfigLoadOptions } from './options.js';
|
|
9
4
|
const reloadFailureReasons = new WeakMap();
|
|
5
|
+
const nodeBuiltinRuntimeRequirement = 'Node.js 20.16.0 or newer is required when @fluojs/config loads env files or starts watch mode.';
|
|
6
|
+
let requireNodeBuiltin;
|
|
7
|
+
function resolveRequireNodeBuiltin() {
|
|
8
|
+
if (requireNodeBuiltin) {
|
|
9
|
+
return requireNodeBuiltin;
|
|
10
|
+
}
|
|
11
|
+
const nodeModule = globalThis.process?.getBuiltinModule?.('node:module');
|
|
12
|
+
if (!nodeModule?.createRequire) {
|
|
13
|
+
throw new FluoError('Node.js configuration loading is unavailable in this runtime.', {
|
|
14
|
+
code: 'CONFIG_RUNTIME_UNAVAILABLE',
|
|
15
|
+
cause: new Error(`${nodeBuiltinRuntimeRequirement} The host runtime did not expose a synchronous node:module loader. Use in-memory config options or run env-file loading on Node.js.`)
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
requireNodeBuiltin = nodeModule.createRequire(import.meta.url);
|
|
19
|
+
return requireNodeBuiltin;
|
|
20
|
+
}
|
|
21
|
+
function requireNodeBuiltinFallback(id) {
|
|
22
|
+
try {
|
|
23
|
+
return resolveRequireNodeBuiltin()(id);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
if (error instanceof FluoError) {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
throw new FluoError('Node.js configuration loading is unavailable in this runtime.', {
|
|
29
|
+
code: 'CONFIG_RUNTIME_UNAVAILABLE',
|
|
30
|
+
cause: new Error(`${nodeBuiltinRuntimeRequirement} The host runtime could not load ${id}. Use in-memory config options or run env-file loading on Node.js.`, {
|
|
31
|
+
cause: error
|
|
32
|
+
})
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function resolveNodeBuiltin(id) {
|
|
37
|
+
const getBuiltinModule = globalThis.process?.getBuiltinModule;
|
|
38
|
+
if (!getBuiltinModule) {
|
|
39
|
+
return requireNodeBuiltinFallback(id);
|
|
40
|
+
}
|
|
41
|
+
const module = getBuiltinModule(id);
|
|
42
|
+
if (!module) {
|
|
43
|
+
return requireNodeBuiltinFallback(id);
|
|
44
|
+
}
|
|
45
|
+
return module;
|
|
46
|
+
}
|
|
47
|
+
function nodeCrypto() {
|
|
48
|
+
return resolveNodeBuiltin('node:crypto');
|
|
49
|
+
}
|
|
50
|
+
function nodeFs() {
|
|
51
|
+
return resolveNodeBuiltin('node:fs');
|
|
52
|
+
}
|
|
53
|
+
function nodePath() {
|
|
54
|
+
return resolveNodeBuiltin('node:path');
|
|
55
|
+
}
|
|
56
|
+
function resolveCurrentWorkingDirectory() {
|
|
57
|
+
const cwd = globalThis.process?.cwd;
|
|
58
|
+
if (!cwd) {
|
|
59
|
+
throw new FluoError('Node.js configuration loading is unavailable in this runtime.', {
|
|
60
|
+
code: 'CONFIG_RUNTIME_UNAVAILABLE',
|
|
61
|
+
cause: new Error('The host runtime did not expose process.cwd(). Pass envFilePath or avoid env-file loading outside Node.js.')
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return cwd();
|
|
65
|
+
}
|
|
66
|
+
function isNodeFsError(error) {
|
|
67
|
+
return typeof error === 'object' && error !== null && 'code' in error;
|
|
68
|
+
}
|
|
10
69
|
function markReloadFailure(error, reason) {
|
|
11
70
|
if (typeof error === 'object' && error !== null) {
|
|
12
71
|
reloadFailureReasons.set(error, reason);
|
|
@@ -18,18 +77,139 @@ function getReloadFailureReason(error) {
|
|
|
18
77
|
}
|
|
19
78
|
return reloadFailureReasons.get(error);
|
|
20
79
|
}
|
|
80
|
+
function unquoteEnvValue(value) {
|
|
81
|
+
if (value.length < 2) {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
const quote = value[0];
|
|
85
|
+
if (quote !== '"' && quote !== "'" && quote !== '`' || value[value.length - 1] !== quote) {
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
const unquoted = value.slice(1, -1);
|
|
89
|
+
return quote === '"' ? unquoted.replace(/\\n/g, '\n').replace(/\\r/g, '\r') : unquoted;
|
|
90
|
+
}
|
|
91
|
+
function stripInlineEnvComment(value) {
|
|
92
|
+
const commentIndex = value.search(/\s#/);
|
|
93
|
+
return commentIndex === -1 ? value : value.slice(0, commentIndex);
|
|
94
|
+
}
|
|
95
|
+
function findClosingEnvQuote(value, quote) {
|
|
96
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
97
|
+
if (value[index] === quote && value[index - 1] !== '\\') {
|
|
98
|
+
return index;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return -1;
|
|
102
|
+
}
|
|
103
|
+
function collectQuotedEnvValue(lines, startIndex, valueStart, quote) {
|
|
104
|
+
let value = valueStart;
|
|
105
|
+
let currentIndex = startIndex;
|
|
106
|
+
while (currentIndex + 1 < lines.length && findClosingEnvQuote(value, quote) === -1) {
|
|
107
|
+
currentIndex += 1;
|
|
108
|
+
value += `\n${lines[currentIndex]}`;
|
|
109
|
+
}
|
|
110
|
+
const closingQuoteIndex = findClosingEnvQuote(value, quote);
|
|
111
|
+
return {
|
|
112
|
+
nextIndex: currentIndex,
|
|
113
|
+
value: closingQuoteIndex === -1 ? value : value.slice(0, closingQuoteIndex + 1)
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function parseDotenvContent(content) {
|
|
117
|
+
const parsed = {};
|
|
118
|
+
const lines = content.split(/\r?\n/);
|
|
119
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
120
|
+
const rawLine = lines[index];
|
|
121
|
+
const line = rawLine.trim();
|
|
122
|
+
if (line.length === 0 || line.startsWith('#')) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const entry = line.startsWith('export ') ? line.slice(7).trimStart() : line;
|
|
126
|
+
const separatorIndex = entry.search(/[:=]/);
|
|
127
|
+
if (separatorIndex <= 0) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const key = entry.slice(0, separatorIndex).trim();
|
|
131
|
+
const rawValue = entry.slice(separatorIndex + 1).trim();
|
|
132
|
+
if (!/^[\w.-]+$/.test(key)) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
const quote = rawValue[0];
|
|
136
|
+
const quotedValue = quote === '"' || quote === "'" || quote === '`';
|
|
137
|
+
const collected = quotedValue ? collectQuotedEnvValue(lines, index, rawValue, quote) : {
|
|
138
|
+
nextIndex: index,
|
|
139
|
+
value: stripInlineEnvComment(rawValue).trim()
|
|
140
|
+
};
|
|
141
|
+
index = collected.nextIndex;
|
|
142
|
+
parsed[key] = unquoteEnvValue(collected.value);
|
|
143
|
+
}
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
function expandEnvVariables(parsed, safeProcessEnv) {
|
|
147
|
+
const expanded = {};
|
|
148
|
+
const source = {
|
|
149
|
+
...parsed,
|
|
150
|
+
...safeProcessEnv
|
|
151
|
+
};
|
|
152
|
+
const parseBracedExpansion = expression => {
|
|
153
|
+
if (expression in source) {
|
|
154
|
+
return {
|
|
155
|
+
variableName: expression
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
const colonDefaultIndex = expression.indexOf(':-');
|
|
159
|
+
if (colonDefaultIndex !== -1) {
|
|
160
|
+
return {
|
|
161
|
+
defaultOperator: ':-',
|
|
162
|
+
defaultValue: expression.slice(colonDefaultIndex + 2),
|
|
163
|
+
variableName: expression.slice(0, colonDefaultIndex)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const defaultIndex = expression.indexOf('-');
|
|
167
|
+
if (defaultIndex !== -1) {
|
|
168
|
+
return {
|
|
169
|
+
defaultOperator: '-',
|
|
170
|
+
defaultValue: expression.slice(defaultIndex + 1),
|
|
171
|
+
variableName: expression.slice(0, defaultIndex)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
variableName: expression
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const expandValue = (value, visiting) => value.replace(/(^|[^\\])\$(?:\{([^}]*)\}|([\w.-]+))/g, (match, prefix, bracedExpression, bareVariableName) => {
|
|
179
|
+
const expansion = bracedExpression === undefined ? {
|
|
180
|
+
variableName: bareVariableName ?? ''
|
|
181
|
+
} : parseBracedExpansion(bracedExpression);
|
|
182
|
+
const {
|
|
183
|
+
defaultOperator,
|
|
184
|
+
defaultValue,
|
|
185
|
+
variableName
|
|
186
|
+
} = expansion;
|
|
187
|
+
if (!variableName) {
|
|
188
|
+
return match;
|
|
189
|
+
}
|
|
190
|
+
const hasSourceValue = variableName in source;
|
|
191
|
+
const sourceValue = hasSourceValue ? source[variableName] ?? '' : undefined;
|
|
192
|
+
const shouldUseDefaultValue = defaultValue !== undefined && (!hasSourceValue || defaultOperator === ':-' && sourceValue === '');
|
|
193
|
+
if (shouldUseDefaultValue) {
|
|
194
|
+
return `${prefix}${expandValue(defaultValue, visiting)}`;
|
|
195
|
+
}
|
|
196
|
+
if (!hasSourceValue || visiting.has(variableName)) {
|
|
197
|
+
return prefix;
|
|
198
|
+
}
|
|
199
|
+
const replacement = variableName in safeProcessEnv ? safeProcessEnv[variableName] : variableName in expanded ? expanded[variableName] : expandValue(sourceValue ?? '', new Set([...visiting, variableName]));
|
|
200
|
+
return `${prefix}${replacement}`;
|
|
201
|
+
}).replace(/\\\$/g, '$');
|
|
202
|
+
for (const [key, value] of Object.entries(parsed)) {
|
|
203
|
+
expanded[key] = expandValue(value, new Set([key]));
|
|
204
|
+
source[key] = expanded[key];
|
|
205
|
+
}
|
|
206
|
+
return expanded;
|
|
207
|
+
}
|
|
21
208
|
function parseEnvContent(content, safeProcessEnv, customParser) {
|
|
22
209
|
if (customParser) {
|
|
23
210
|
return customParser(content);
|
|
24
211
|
}
|
|
25
|
-
|
|
26
|
-
const result = dotenvExpand({
|
|
27
|
-
parsed,
|
|
28
|
-
processEnv: {
|
|
29
|
-
...safeProcessEnv
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
return result.parsed ?? {};
|
|
212
|
+
return expandEnvVariables(parseDotenvContent(content), safeProcessEnv);
|
|
33
213
|
}
|
|
34
214
|
function sanitizeProcessEnv(processEnv) {
|
|
35
215
|
return Object.fromEntries(Object.entries(processEnv).filter(entry => entry[1] !== undefined));
|
|
@@ -44,8 +224,11 @@ function rejectLegacyValidateOption(options) {
|
|
|
44
224
|
}
|
|
45
225
|
function normalizeLoadOptions(options) {
|
|
46
226
|
rejectLegacyValidateOption(options);
|
|
47
|
-
const
|
|
48
|
-
const
|
|
227
|
+
const hasExplicitEnvFile = options.envFilePath !== undefined || options.envFile !== undefined;
|
|
228
|
+
const hasExplicitInMemorySource = options.defaults !== undefined || options.processEnv !== undefined || options.runtimeOverrides !== undefined;
|
|
229
|
+
const shouldUseDefaultEnvFile = !hasExplicitEnvFile && (options.cwd !== undefined || options.watch === true || !hasExplicitInMemorySource);
|
|
230
|
+
const cwd = shouldUseDefaultEnvFile && options.envFilePath === undefined && options.envFile === undefined ? options.cwd ?? resolveCurrentWorkingDirectory() : options.cwd;
|
|
231
|
+
const envFile = options.envFilePath ?? options.envFile ?? (cwd ? nodePath().join(cwd, '.env') : undefined);
|
|
49
232
|
const defaults = options.defaults ?? {};
|
|
50
233
|
const processEnv = options.processEnv ?? {};
|
|
51
234
|
const safeProcessEnv = sanitizeProcessEnv(processEnv);
|
|
@@ -60,10 +243,13 @@ function normalizeLoadOptions(options) {
|
|
|
60
243
|
};
|
|
61
244
|
}
|
|
62
245
|
function readEnvFileValues(options) {
|
|
246
|
+
if (options.envFile === undefined) {
|
|
247
|
+
return {};
|
|
248
|
+
}
|
|
63
249
|
try {
|
|
64
|
-
return parseEnvContent(readFileSync(options.envFile, 'utf8'), options.safeProcessEnv, options.parse);
|
|
250
|
+
return parseEnvContent(nodeFs().readFileSync(options.envFile, 'utf8'), options.safeProcessEnv, options.parse);
|
|
65
251
|
} catch (error) {
|
|
66
|
-
if (
|
|
252
|
+
if (isNodeFsError(error) && error.code === 'ENOENT') {
|
|
67
253
|
return {};
|
|
68
254
|
}
|
|
69
255
|
throw error;
|
|
@@ -142,7 +328,7 @@ function createInvalidConfigError(cause, issues) {
|
|
|
142
328
|
});
|
|
143
329
|
}
|
|
144
330
|
function isInvalidConfigError(error) {
|
|
145
|
-
return error
|
|
331
|
+
return typeof error === 'object' && error !== null && 'code' in error && error.code === 'INVALID_CONFIG';
|
|
146
332
|
}
|
|
147
333
|
function readConfigSchemaResult(result) {
|
|
148
334
|
if (isConfigSchemaFailureResult(result)) {
|
|
@@ -151,7 +337,7 @@ function readConfigSchemaResult(result) {
|
|
|
151
337
|
if (!isConfigSchemaSuccessResult(result)) {
|
|
152
338
|
throw createInvalidConfigError(new Error('Standard Schema config validator returned a malformed result.'));
|
|
153
339
|
}
|
|
154
|
-
return result.value;
|
|
340
|
+
return cloneConfigDictionary(result.value);
|
|
155
341
|
}
|
|
156
342
|
function validateConfig(options, merged) {
|
|
157
343
|
if (!options.schema) {
|
|
@@ -183,9 +369,9 @@ function createSubscription(listeners, listener) {
|
|
|
183
369
|
}
|
|
184
370
|
function hashEnvFileContent(envFile) {
|
|
185
371
|
try {
|
|
186
|
-
return createHash('sha256').update(readFileSync(envFile)).digest('hex');
|
|
372
|
+
return nodeCrypto().createHash('sha256').update(nodeFs().readFileSync(envFile)).digest('hex');
|
|
187
373
|
} catch (error) {
|
|
188
|
-
if (
|
|
374
|
+
if (isNodeFsError(error) && error.code === 'ENOENT') {
|
|
189
375
|
return undefined;
|
|
190
376
|
}
|
|
191
377
|
throw error;
|
|
@@ -241,19 +427,25 @@ function startReloaderWatcher(normalized, options, state, listeners, errorListen
|
|
|
241
427
|
if (!options.watch) {
|
|
242
428
|
return undefined;
|
|
243
429
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
430
|
+
if (normalized.envFile === undefined) {
|
|
431
|
+
return undefined;
|
|
432
|
+
}
|
|
433
|
+
const path = nodePath();
|
|
434
|
+
const fs = nodeFs();
|
|
435
|
+
const envFile = normalized.envFile;
|
|
436
|
+
const watchTarget = path.dirname(envFile);
|
|
437
|
+
const watchedEnvFileName = path.basename(envFile);
|
|
438
|
+
if (!fs.existsSync(watchTarget)) {
|
|
247
439
|
return undefined;
|
|
248
440
|
}
|
|
249
|
-
return watch(watchTarget, {
|
|
441
|
+
return fs.watch(watchTarget, {
|
|
250
442
|
persistent: false
|
|
251
443
|
}, (_eventType, filename) => {
|
|
252
444
|
if (filename !== null && filename.toString() !== watchedEnvFileName) {
|
|
253
445
|
return;
|
|
254
446
|
}
|
|
255
447
|
try {
|
|
256
|
-
const nextEnvFileHash = hashEnvFileContent(
|
|
448
|
+
const nextEnvFileHash = hashEnvFileContent(envFile);
|
|
257
449
|
if (nextEnvFileHash === state.watchedEnvFileHash) {
|
|
258
450
|
return;
|
|
259
451
|
}
|
|
@@ -300,7 +492,7 @@ export function createConfigReloader(options) {
|
|
|
300
492
|
current: resolveConfig(normalized),
|
|
301
493
|
pendingReloadReason: undefined,
|
|
302
494
|
reloading: false,
|
|
303
|
-
watchedEnvFileHash: hashEnvFileContent(normalized.envFile),
|
|
495
|
+
watchedEnvFileHash: normalized.envFile === undefined ? undefined : hashEnvFileContent(normalized.envFile),
|
|
304
496
|
watcher: undefined
|
|
305
497
|
};
|
|
306
498
|
const listeners = new Set();
|
package/dist/options.d.ts
CHANGED
|
@@ -3,14 +3,14 @@ import type { ConfigLoadOptions, ConfigModuleOptions } from './types.js';
|
|
|
3
3
|
* Creates a detached snapshot of config module registration options.
|
|
4
4
|
*
|
|
5
5
|
* @param options Caller-owned module options captured at registration time.
|
|
6
|
-
* @returns Options that cannot observe later caller mutations of config dictionaries.
|
|
6
|
+
* @returns Options that cannot observe later caller mutations of config dictionaries or schema objects.
|
|
7
7
|
*/
|
|
8
8
|
export declare function snapshotConfigModuleOptions(options?: ConfigModuleOptions): ConfigModuleOptions;
|
|
9
9
|
/**
|
|
10
10
|
* Creates a detached snapshot of config load and reload options.
|
|
11
11
|
*
|
|
12
12
|
* @param options Caller-owned load options captured by loaders or reload modules.
|
|
13
|
-
* @returns Options that preserve registration-time config dictionary inputs.
|
|
13
|
+
* @returns Options that preserve registration-time config dictionary and schema inputs.
|
|
14
14
|
*/
|
|
15
15
|
export declare function snapshotConfigLoadOptions(options?: ConfigLoadOptions): ConfigLoadOptions;
|
|
16
16
|
//# sourceMappingURL=options.d.ts.map
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,mBAAmB,
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,mBAAmB,EAAgB,MAAM,YAAY,CAAC;AAwCzG;;;;;GAKG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,mBAAmB,CAY9F;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,iBAAiB,CAYxF"}
|
package/dist/options.js
CHANGED
|
@@ -14,12 +14,27 @@ function snapshotProcessEnv(processEnv) {
|
|
|
14
14
|
}
|
|
15
15
|
return Object.freeze(snapshot);
|
|
16
16
|
}
|
|
17
|
+
function snapshotConfigSchema(schema) {
|
|
18
|
+
if (schema === undefined) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
const standard = schema['~standard'];
|
|
22
|
+
const snapshot = {
|
|
23
|
+
'~standard': Object.freeze({
|
|
24
|
+
types: standard.types,
|
|
25
|
+
validate: standard.validate,
|
|
26
|
+
vendor: standard.vendor,
|
|
27
|
+
version: standard.version
|
|
28
|
+
})
|
|
29
|
+
};
|
|
30
|
+
return Object.freeze(snapshot);
|
|
31
|
+
}
|
|
17
32
|
|
|
18
33
|
/**
|
|
19
34
|
* Creates a detached snapshot of config module registration options.
|
|
20
35
|
*
|
|
21
36
|
* @param options Caller-owned module options captured at registration time.
|
|
22
|
-
* @returns Options that cannot observe later caller mutations of config dictionaries.
|
|
37
|
+
* @returns Options that cannot observe later caller mutations of config dictionaries or schema objects.
|
|
23
38
|
*/
|
|
24
39
|
export function snapshotConfigModuleOptions(options) {
|
|
25
40
|
if (options === undefined) {
|
|
@@ -28,7 +43,9 @@ export function snapshotConfigModuleOptions(options) {
|
|
|
28
43
|
return Object.freeze({
|
|
29
44
|
...options,
|
|
30
45
|
defaults: snapshotConfigDictionary(options.defaults),
|
|
31
|
-
processEnv: snapshotProcessEnv(options.processEnv)
|
|
46
|
+
processEnv: snapshotProcessEnv(options.processEnv),
|
|
47
|
+
runtimeOverrides: snapshotConfigDictionary(options.runtimeOverrides),
|
|
48
|
+
schema: snapshotConfigSchema(options.schema)
|
|
32
49
|
});
|
|
33
50
|
}
|
|
34
51
|
|
|
@@ -36,7 +53,7 @@ export function snapshotConfigModuleOptions(options) {
|
|
|
36
53
|
* Creates a detached snapshot of config load and reload options.
|
|
37
54
|
*
|
|
38
55
|
* @param options Caller-owned load options captured by loaders or reload modules.
|
|
39
|
-
* @returns Options that preserve registration-time config dictionary inputs.
|
|
56
|
+
* @returns Options that preserve registration-time config dictionary and schema inputs.
|
|
40
57
|
*/
|
|
41
58
|
export function snapshotConfigLoadOptions(options) {
|
|
42
59
|
if (options === undefined) {
|
|
@@ -46,6 +63,7 @@ export function snapshotConfigLoadOptions(options) {
|
|
|
46
63
|
...options,
|
|
47
64
|
defaults: snapshotConfigDictionary(options.defaults),
|
|
48
65
|
processEnv: snapshotProcessEnv(options.processEnv),
|
|
49
|
-
runtimeOverrides: snapshotConfigDictionary(options.runtimeOverrides)
|
|
66
|
+
runtimeOverrides: snapshotConfigDictionary(options.runtimeOverrides),
|
|
67
|
+
schema: snapshotConfigSchema(options.schema)
|
|
50
68
|
});
|
|
51
69
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface ConfigModuleOptions {
|
|
|
30
30
|
processEnv?: NodeJS.ProcessEnv;
|
|
31
31
|
schema?: ConfigSchema;
|
|
32
32
|
defaults?: ConfigDictionary;
|
|
33
|
+
/** Highest-precedence values applied after defaults, env files, and `processEnv`. */
|
|
34
|
+
runtimeOverrides?: ConfigDictionary;
|
|
33
35
|
/** Supply a custom file parser (e.g. for YAML or TOML). Receives raw file content,
|
|
34
36
|
* returns a flat key-value record. Defaults to dotenv parsing. */
|
|
35
37
|
parse?: (content: string) => Record<string, string>;
|
|
@@ -44,7 +46,6 @@ export interface ConfigModuleOptions {
|
|
|
44
46
|
*/
|
|
45
47
|
export interface ConfigLoadOptions extends ConfigModuleOptions {
|
|
46
48
|
cwd?: string;
|
|
47
|
-
runtimeOverrides?: ConfigDictionary;
|
|
48
49
|
}
|
|
49
50
|
/**
|
|
50
51
|
* Reason attached to config reload notifications.
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,SAAS,gBAAgB,GAAG,gBAAgB,IAAI,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAEhI;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnF;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,GAAG,MAAM,GAAG,CAAC,EAAE,GACf,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC;CACrC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GACnB,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GACzD,CAAC,CAAC,CAAC,CAAC,GACJ,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACrC,IAAI,SAAS,MAAM,CAAC,GAClB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GACvB,KAAK,GACP,KAAK,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;uEACmE;IACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,YAAY,CAAC,KAAK,GAAG,OAAO,EAAE,MAAM,SAAS,gBAAgB,GAAG,gBAAgB,IAAI,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAEhI;;;GAGG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACnF;KACG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM,GAClB,GAAG,MAAM,GAAG,CAAC,EAAE,GACf,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,GAAG,CAAC,GAAG,CAAC;CACrC,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,GACnB,KAAK,CAAC;AAEV;;GAEG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,MAAM,CAAC,GACzD,CAAC,CAAC,CAAC,CAAC,GACJ,CAAC,SAAS,GAAG,MAAM,IAAI,IAAI,MAAM,IAAI,EAAE,GACrC,IAAI,SAAS,MAAM,CAAC,GAClB,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,GACvB,KAAK,GACP,KAAK,CAAC;AAEZ;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC;uEACmE;IACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,wEAAwE;IACxE,aAAa,CAAC,EAAE,yBAAyB,CAAC;IAC1C,qFAAqF;IACrF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,mBAAmB;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAEpG;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAE7F;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,WAAW,IAAI,IAAI,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,gBAAgB,CAAC;IAC5B,MAAM,IAAI,gBAAgB,CAAC;IAC3B,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,wBAAwB,CAAC;IACpE,cAAc,CAAC,QAAQ,EAAE,yBAAyB,GAAG,wBAAwB,CAAC;IAC9E,KAAK,IAAI,IAAI,CAAC;CACf"}
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"environment",
|
|
9
9
|
"typed-config"
|
|
10
10
|
],
|
|
11
|
-
"version": "1.0.
|
|
11
|
+
"version": "1.0.4",
|
|
12
12
|
"private": false,
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"directory": "packages/config"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
|
-
"node": ">=20.
|
|
20
|
+
"node": ">=20.16.0"
|
|
21
21
|
},
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
|
@@ -36,13 +36,11 @@
|
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@standard-schema/spec": "^1.1.0",
|
|
39
|
-
"
|
|
40
|
-
"dotenv-expand": "^11.0.0",
|
|
41
|
-
"@fluojs/core": "^1.0.2"
|
|
39
|
+
"@fluojs/core": "^1.1.0"
|
|
42
40
|
},
|
|
43
41
|
"devDependencies": {
|
|
44
42
|
"vitest": "^3.2.4",
|
|
45
|
-
"@fluojs/di": "^
|
|
43
|
+
"@fluojs/di": "^2.0.0"
|
|
46
44
|
},
|
|
47
45
|
"scripts": {
|
|
48
46
|
"prebuild": "node ../../tooling/scripts/clean-dist.mjs",
|