@mcp-abap-adt/proxy 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +9 -1
- package/dist/lib/envInterpolation.d.ts +14 -2
- package/dist/lib/envInterpolation.d.ts.map +1 -1
- package/dist/lib/envInterpolation.js +34 -7
- package/docs/YAML_CONFIG.md +17 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.0.1] - 2026-09-24
|
|
11
|
+
|
|
12
|
+
**A variable from an env file could be silently lost, and the failure blamed the
|
|
13
|
+
variable instead of naming the file.** Both hit Windows hardest, with the same
|
|
14
|
+
config and the same `.env` that work on Linux.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **An empty environment variable no longer shadows the env file.** The lookup
|
|
19
|
+
was `process.env[key] ?? envFileMap[key]`, and `??` skips only `undefined` — so
|
|
20
|
+
a `SAP_LOGIN=` sitting in the environment beat the file the user had pointed at
|
|
21
|
+
with `--env-file`, and the header went out empty with nothing reported. Windows
|
|
22
|
+
carries variables nobody set deliberately, which is why the same files behaved
|
|
23
|
+
differently there. A real value in the environment still wins.
|
|
24
|
+
|
|
25
|
+
- **The interpolation failure now names where it looked.** It said only
|
|
26
|
+
`Config references undefined env variable: SAP_LOGIN`, so three different
|
|
27
|
+
causes produced one sentence and the cause had to be guessed:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
... looked in process.env only — no env file was given (--env-file or envFile:)
|
|
31
|
+
... looked in process.env, then /path/e19.env, which yielded NO variables
|
|
32
|
+
(0 bytes) — check its encoding, since a UTF-16 file reads as nothing here
|
|
33
|
+
... looked in process.env, then /path/e19.env (1: SAP_PASSWORD)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
An env file that yields nothing is still not an error — an empty `.env` is
|
|
37
|
+
legitimate, and a config may take every value from the environment. It is
|
|
38
|
+
reported by whoever then needs a variable, where it can be said usefully.
|
|
39
|
+
|
|
40
|
+
### Documentation
|
|
41
|
+
|
|
42
|
+
- `docs/YAML_CONFIG.md`: a Windows path in **double** quotes is a YAML escape
|
|
43
|
+
context. `envFile: "C:\Users\..."` fails to parse (`\U` is a Unicode escape)
|
|
44
|
+
and `"C:\temp\e19.env"` parses *silently wrong* (`\e` becomes 0x1B). Use
|
|
45
|
+
single quotes, no quotes, or forward slashes.
|
|
46
|
+
|
|
10
47
|
## [4.0.0] - 2026-09-24
|
|
11
48
|
|
|
12
49
|
**The proxy stops re-implementing the broker, there is one forwarding path
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGxC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGxC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CA8C3D;AAwED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,mBAAmB,CAQrB;AAkCD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAYhC;AAmJD;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAUD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG;IACnD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAoDA"}
|
package/dist/lib/config.js
CHANGED
|
@@ -68,7 +68,15 @@ function loadConfig(configPath) {
|
|
|
68
68
|
const envFilePath = resolveEnvFilePath(fileConfig, finalConfigPath);
|
|
69
69
|
const envFileMap = envFilePath ? (0, envInterpolation_js_1.loadEnvFile)(envFilePath) : {};
|
|
70
70
|
const lookup = (0, envInterpolation_js_1.buildLookup)(envFileMap);
|
|
71
|
-
const
|
|
71
|
+
const keys = Object.keys(envFileMap);
|
|
72
|
+
const sources = !envFilePath
|
|
73
|
+
? 'process.env only — no env file was given (--env-file or envFile:)'
|
|
74
|
+
: keys.length === 0
|
|
75
|
+
? `process.env, then ${envFilePath}, which yielded NO variables ` +
|
|
76
|
+
`(${fs.statSync(envFilePath).size} bytes) — check its encoding, ` +
|
|
77
|
+
`since a UTF-16 file reads as nothing here, and that lines look like KEY=value`
|
|
78
|
+
: `process.env, then ${envFilePath} (${keys.length}: ${keys.join(', ')})`;
|
|
79
|
+
const interpolated = (0, envInterpolation_js_1.interpolateConfig)(fileConfig, lookup, '', sources);
|
|
72
80
|
delete interpolated.envFile;
|
|
73
81
|
const base = applyDefaults(interpolated);
|
|
74
82
|
const cli = readCliOverrides();
|
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
* default when one is given. A ${VAR} without a default that resolves to
|
|
9
9
|
* undefined throws, naming the variable and the field it came from.
|
|
10
10
|
*/
|
|
11
|
-
export declare function interpolateString(input: string, lookup: (key: string) => string | undefined, fieldPath: string
|
|
11
|
+
export declare function interpolateString(input: string, lookup: (key: string) => string | undefined, fieldPath: string,
|
|
12
|
+
/** Where values were looked for, named in the failure. */
|
|
13
|
+
sources?: string): string;
|
|
12
14
|
/**
|
|
13
15
|
* Recursively interpolate all string values in a parsed config object.
|
|
14
16
|
* Objects/arrays are walked; non-string scalars are returned unchanged.
|
|
15
17
|
* The field path (e.g. `defaultHeaders.x-sap-password`) is threaded through for
|
|
16
18
|
* error messages.
|
|
17
19
|
*/
|
|
18
|
-
export declare function interpolateConfig(value: unknown, lookup: (key: string) => string | undefined, path?: string): unknown;
|
|
20
|
+
export declare function interpolateConfig(value: unknown, lookup: (key: string) => string | undefined, path?: string, sources?: string): unknown;
|
|
19
21
|
/**
|
|
20
22
|
* Parse a .env file into a flat map. Throws if the path is given but missing —
|
|
21
23
|
* a specified-yet-absent secret source is a configuration error, not a no-op.
|
|
@@ -23,6 +25,16 @@ export declare function interpolateConfig(value: unknown, lookup: (key: string)
|
|
|
23
25
|
export declare function loadEnvFile(envFilePath: string): Record<string, string>;
|
|
24
26
|
/**
|
|
25
27
|
* Build a lookup over process.env (highest priority) then the parsed .env map.
|
|
28
|
+
*
|
|
29
|
+
* An EMPTY environment variable does not count as a value. It used to: `??`
|
|
30
|
+
* skips only `undefined`, so a `SAP_LOGIN=` sitting in the environment won
|
|
31
|
+
* against the env file the user had explicitly pointed at with `--env-file`,
|
|
32
|
+
* and the header went out empty with nothing reported. Windows carries
|
|
33
|
+
* variables nobody set deliberately, which is why the same config and the same
|
|
34
|
+
* `.env` behaved differently there.
|
|
35
|
+
*
|
|
36
|
+
* A real value in the environment still wins — that is the documented
|
|
37
|
+
* precedence, and it is how a one-off override is meant to work.
|
|
26
38
|
*/
|
|
27
39
|
export declare function buildLookup(envFileMap: Record<string, string>): (key: string) => string | undefined;
|
|
28
40
|
//# sourceMappingURL=envInterpolation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"envInterpolation.d.ts","sourceRoot":"","sources":["../../src/lib/envInterpolation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC3C,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"envInterpolation.d.ts","sourceRoot":"","sources":["../../src/lib/envInterpolation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC3C,SAAS,EAAE,MAAM;AACjB,0DAA0D;AAC1D,OAAO,SAAgB,GACtB,MAAM,CAsBR;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,EAC3C,IAAI,SAAK,EACT,OAAO,SAAgB,GACtB,OAAO,CAsBT;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GACjC,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAQrC"}
|
|
@@ -50,7 +50,9 @@ const PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)(?::-([^}]*))?\}/g;
|
|
|
50
50
|
* default when one is given. A ${VAR} without a default that resolves to
|
|
51
51
|
* undefined throws, naming the variable and the field it came from.
|
|
52
52
|
*/
|
|
53
|
-
function interpolateString(input, lookup, fieldPath
|
|
53
|
+
function interpolateString(input, lookup, fieldPath,
|
|
54
|
+
/** Where values were looked for, named in the failure. */
|
|
55
|
+
sources = 'process.env') {
|
|
54
56
|
return input.replace(PLACEHOLDER, (_match, name, defaultVal) => {
|
|
55
57
|
const value = lookup(name);
|
|
56
58
|
const isEmpty = value === undefined || value === '';
|
|
@@ -58,7 +60,12 @@ function interpolateString(input, lookup, fieldPath) {
|
|
|
58
60
|
return isEmpty ? defaultVal : value;
|
|
59
61
|
}
|
|
60
62
|
if (value === undefined) {
|
|
61
|
-
|
|
63
|
+
// Naming where it looked, because the old message named only the
|
|
64
|
+
// variable — so a file never read, a file read empty, and a value
|
|
65
|
+
// shadowed by the environment all produced the same sentence and the
|
|
66
|
+
// cause had to be guessed.
|
|
67
|
+
throw new Error(`Config references undefined env variable: ${name} ` +
|
|
68
|
+
`(referenced in ${fieldPath}; looked in ${sources})`);
|
|
62
69
|
}
|
|
63
70
|
return value;
|
|
64
71
|
});
|
|
@@ -69,17 +76,17 @@ function interpolateString(input, lookup, fieldPath) {
|
|
|
69
76
|
* The field path (e.g. `defaultHeaders.x-sap-password`) is threaded through for
|
|
70
77
|
* error messages.
|
|
71
78
|
*/
|
|
72
|
-
function interpolateConfig(value, lookup, path = '') {
|
|
79
|
+
function interpolateConfig(value, lookup, path = '', sources = 'process.env') {
|
|
73
80
|
if (typeof value === 'string') {
|
|
74
|
-
return interpolateString(value, lookup, path || '(root)');
|
|
81
|
+
return interpolateString(value, lookup, path || '(root)', sources);
|
|
75
82
|
}
|
|
76
83
|
if (Array.isArray(value)) {
|
|
77
|
-
return value.map((item, i) => interpolateConfig(item, lookup, `${path}[${i}]
|
|
84
|
+
return value.map((item, i) => interpolateConfig(item, lookup, `${path}[${i}]`, sources));
|
|
78
85
|
}
|
|
79
86
|
if (value !== null && typeof value === 'object') {
|
|
80
87
|
const out = {};
|
|
81
88
|
for (const [key, val] of Object.entries(value)) {
|
|
82
|
-
out[key] = interpolateConfig(val, lookup, path ? `${path}.${key}` : key);
|
|
89
|
+
out[key] = interpolateConfig(val, lookup, path ? `${path}.${key}` : key, sources);
|
|
83
90
|
}
|
|
84
91
|
return out;
|
|
85
92
|
}
|
|
@@ -93,11 +100,31 @@ function loadEnvFile(envFilePath) {
|
|
|
93
100
|
if (!fs.existsSync(envFilePath)) {
|
|
94
101
|
throw new Error(`env file not found: ${envFilePath}`);
|
|
95
102
|
}
|
|
103
|
+
// Not an error when it yields nothing: an empty `.env` is legitimate, and a
|
|
104
|
+
// config may take every value from the environment. But the emptiness is
|
|
105
|
+
// reported by whoever needs a variable — see the `sources` note threaded into
|
|
106
|
+
// the interpolation failure, which names the file, its size and its key count.
|
|
96
107
|
return dotenv.parse(fs.readFileSync(envFilePath, 'utf-8'));
|
|
97
108
|
}
|
|
98
109
|
/**
|
|
99
110
|
* Build a lookup over process.env (highest priority) then the parsed .env map.
|
|
111
|
+
*
|
|
112
|
+
* An EMPTY environment variable does not count as a value. It used to: `??`
|
|
113
|
+
* skips only `undefined`, so a `SAP_LOGIN=` sitting in the environment won
|
|
114
|
+
* against the env file the user had explicitly pointed at with `--env-file`,
|
|
115
|
+
* and the header went out empty with nothing reported. Windows carries
|
|
116
|
+
* variables nobody set deliberately, which is why the same config and the same
|
|
117
|
+
* `.env` behaved differently there.
|
|
118
|
+
*
|
|
119
|
+
* A real value in the environment still wins — that is the documented
|
|
120
|
+
* precedence, and it is how a one-off override is meant to work.
|
|
100
121
|
*/
|
|
101
122
|
function buildLookup(envFileMap) {
|
|
102
|
-
return (key) =>
|
|
123
|
+
return (key) => {
|
|
124
|
+
const fromEnvironment = process.env[key];
|
|
125
|
+
if (fromEnvironment !== undefined && fromEnvironment !== '') {
|
|
126
|
+
return fromEnvironment;
|
|
127
|
+
}
|
|
128
|
+
return envFileMap[key];
|
|
129
|
+
};
|
|
103
130
|
}
|
package/docs/YAML_CONFIG.md
CHANGED
|
@@ -206,6 +206,23 @@ requestTimeout: 120000
|
|
|
206
206
|
| `maxRetries` | `number` | `3` | Maximum number of retry attempts |
|
|
207
207
|
| `retryDelay` | `number` | `1000` | Delay between retries (milliseconds) |
|
|
208
208
|
| `requestTimeout` | `number` | `60000` | Request timeout (milliseconds) |
|
|
209
|
+
### Windows paths must not go in double quotes
|
|
210
|
+
|
|
211
|
+
In YAML, a double-quoted string is an **escape context**, and a Windows path is
|
|
212
|
+
full of backslashes. Measured with the parser this package uses:
|
|
213
|
+
|
|
214
|
+
| written as | result |
|
|
215
|
+
|---|---|
|
|
216
|
+
| `envFile: "C:\Users\me\e19.env"` | **fails** — `expected hexadecimal character`, because `\U` starts a Unicode escape |
|
|
217
|
+
| `envFile: "C:\temp\e19.env"` | **silently wrong** — `\e` becomes the escape character `0x1B`, so the path points nowhere |
|
|
218
|
+
| `envFile: 'C:\Users\me\e19.env'` | correct |
|
|
219
|
+
| `envFile: C:\Users\me\e19.env` | correct |
|
|
220
|
+
| `envFile: "C:/Users/me/e19.env"` | correct |
|
|
221
|
+
|
|
222
|
+
The second row is the dangerous one: it does not fail, it resolves to a path that
|
|
223
|
+
does not exist. Forward slashes are the safest form — Node accepts them on
|
|
224
|
+
Windows, and they carry no meaning inside quotes.
|
|
225
|
+
|
|
209
226
|
| ~~`circuitBreakerThreshold`~~ | `number` | — | **No effect since 4.0.0.** Still accepted so existing files load; the circuit breaker guarded the buffered forward that release removed |
|
|
210
227
|
| ~~`circuitBreakerTimeout`~~ | `number` | — | **No effect since 4.0.0.** As above |
|
|
211
228
|
|
package/package.json
CHANGED