@o3co/ts.hocon 1.0.0 → 1.1.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/dist/index.cjs +1031 -793
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1031 -793
- package/dist/{parse-C4VOYuEk.d.cts → parse-nNbyqnOh.d.cts} +4 -2
- package/dist/{parse-C4VOYuEk.d.ts → parse-nNbyqnOh.d.ts} +4 -2
- package/dist/zod.cjs +1032 -657
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +1032 -657
- package/package.json +1 -1
package/dist/zod.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/coerce.ts
|
|
2
|
+
var DECIMAL_NUMBER_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
2
3
|
var TRUTHY = /* @__PURE__ */ new Set(["true", "yes", "on"]);
|
|
3
4
|
var FALSY = /* @__PURE__ */ new Set(["false", "no", "off"]);
|
|
4
5
|
function coerceBoolean(value) {
|
|
@@ -8,7 +9,7 @@ function coerceBoolean(value) {
|
|
|
8
9
|
return void 0;
|
|
9
10
|
}
|
|
10
11
|
function coerceNumber(value) {
|
|
11
|
-
if (
|
|
12
|
+
if (!DECIMAL_NUMBER_RE.test(value)) return void 0;
|
|
12
13
|
return Number(value);
|
|
13
14
|
}
|
|
14
15
|
var DURATION_UNITS = {
|
|
@@ -165,186 +166,201 @@ var ConfigError = class extends Error {
|
|
|
165
166
|
};
|
|
166
167
|
|
|
167
168
|
// src/internal/lexer/lexer.ts
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
const ch = peek();
|
|
195
|
-
if (ch === " " || ch === " " || ch === "\r") {
|
|
196
|
-
advance();
|
|
197
|
-
hadSpace = true;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (ch === "\n") {
|
|
201
|
-
advance();
|
|
202
|
-
if (tokens[tokens.length - 1]?.kind !== "newline") {
|
|
203
|
-
push("newline", "\n", sl, sc);
|
|
204
|
-
}
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (ch === "/" && peek(1) === "/") {
|
|
208
|
-
while (pos < input.length && peek() !== "\n") advance();
|
|
209
|
-
hadSpace = true;
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
if (ch === "#") {
|
|
213
|
-
while (pos < input.length && peek() !== "\n") advance();
|
|
214
|
-
hadSpace = true;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
const single = { "{": "lbrace", "}": "rbrace", "[": "lbracket", "]": "rbracket", ",": "comma", ":": "colon" };
|
|
218
|
-
if (ch in single) {
|
|
219
|
-
advance();
|
|
220
|
-
push(single[ch], ch, sl, sc);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (ch === "=") {
|
|
224
|
-
advance();
|
|
225
|
-
push("equals", "=", sl, sc);
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (ch === "+" && peek(1) === "=") {
|
|
229
|
-
advance();
|
|
230
|
-
advance();
|
|
231
|
-
push("plus_equals", "+=", sl, sc);
|
|
232
|
-
continue;
|
|
233
|
-
}
|
|
234
|
-
if (ch === "$" && peek(1) === "{") {
|
|
235
|
-
advance();
|
|
236
|
-
advance();
|
|
237
|
-
const optional = peek() === "?";
|
|
238
|
-
if (optional) advance();
|
|
239
|
-
let path2 = "";
|
|
240
|
-
while (pos < input.length && peek() !== "}") {
|
|
241
|
-
if (peek() === "\n") throw new ParseError("unterminated substitution", sl, sc);
|
|
242
|
-
path2 += advance();
|
|
169
|
+
var SINGLE_CHAR_TOKENS = {
|
|
170
|
+
"{": "lbrace",
|
|
171
|
+
"}": "rbrace",
|
|
172
|
+
"[": "lbracket",
|
|
173
|
+
"]": "rbracket",
|
|
174
|
+
",": "comma",
|
|
175
|
+
":": "colon"
|
|
176
|
+
};
|
|
177
|
+
var Lexer = class {
|
|
178
|
+
constructor(input) {
|
|
179
|
+
this.input = input;
|
|
180
|
+
this.pos = 0;
|
|
181
|
+
this.line = 1;
|
|
182
|
+
this.col = 1;
|
|
183
|
+
this.hadSpace = false;
|
|
184
|
+
this.tokens = [];
|
|
185
|
+
if (input.charCodeAt(0) === 65279) this.input = input.slice(1);
|
|
186
|
+
}
|
|
187
|
+
tokenize() {
|
|
188
|
+
while (this.pos < this.input.length) {
|
|
189
|
+
const sl = this.line, sc = this.col;
|
|
190
|
+
const ch = this.peek();
|
|
191
|
+
if (ch === " " || ch === " " || ch === "\r") {
|
|
192
|
+
this.advance();
|
|
193
|
+
this.hadSpace = true;
|
|
194
|
+
continue;
|
|
243
195
|
}
|
|
244
|
-
if (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
}
|
|
249
|
-
if (ch === '"' && peek(1) === '"' && peek(2) === '"') {
|
|
250
|
-
advance();
|
|
251
|
-
advance();
|
|
252
|
-
advance();
|
|
253
|
-
let value = "";
|
|
254
|
-
let closed = false;
|
|
255
|
-
while (pos < input.length) {
|
|
256
|
-
if (peek() === '"') {
|
|
257
|
-
let quoteCount = 0;
|
|
258
|
-
while (pos < input.length && peek() === '"') {
|
|
259
|
-
quoteCount++;
|
|
260
|
-
advance();
|
|
261
|
-
}
|
|
262
|
-
if (quoteCount >= 3) {
|
|
263
|
-
for (let i = 0; i < quoteCount - 3; i++) value += '"';
|
|
264
|
-
closed = true;
|
|
265
|
-
break;
|
|
266
|
-
}
|
|
267
|
-
for (let i = 0; i < quoteCount; i++) value += '"';
|
|
268
|
-
continue;
|
|
196
|
+
if (ch === "\n") {
|
|
197
|
+
this.advance();
|
|
198
|
+
if (this.tokens[this.tokens.length - 1]?.kind !== "newline") {
|
|
199
|
+
this.push("newline", "\n", sl, sc);
|
|
269
200
|
}
|
|
270
|
-
|
|
201
|
+
continue;
|
|
271
202
|
}
|
|
272
|
-
if (
|
|
273
|
-
|
|
203
|
+
if (ch === "/" && this.peek(1) === "/") {
|
|
204
|
+
while (this.pos < this.input.length && this.peek() !== "\n") this.advance();
|
|
205
|
+
this.hadSpace = true;
|
|
206
|
+
continue;
|
|
274
207
|
}
|
|
275
|
-
if (
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
208
|
+
if (ch === "#") {
|
|
209
|
+
while (this.pos < this.input.length && this.peek() !== "\n") this.advance();
|
|
210
|
+
this.hadSpace = true;
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (ch in SINGLE_CHAR_TOKENS) {
|
|
214
|
+
this.advance();
|
|
215
|
+
this.push(SINGLE_CHAR_TOKENS[ch], ch, sl, sc);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (ch === "=") {
|
|
219
|
+
this.advance();
|
|
220
|
+
this.push("equals", "=", sl, sc);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (ch === "+" && this.peek(1) === "=") {
|
|
224
|
+
this.advance();
|
|
225
|
+
this.advance();
|
|
226
|
+
this.push("plus_equals", "+=", sl, sc);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (ch === "$" && this.peek(1) === "{") {
|
|
230
|
+
this.advance();
|
|
231
|
+
this.advance();
|
|
232
|
+
const optional = this.peek() === "?";
|
|
233
|
+
if (optional) this.advance();
|
|
234
|
+
let path2 = "";
|
|
235
|
+
while (this.pos < this.input.length && this.peek() !== "}") {
|
|
236
|
+
if (this.peek() === "\n") throw new ParseError("unterminated substitution", sl, sc);
|
|
237
|
+
path2 += this.advance();
|
|
238
|
+
}
|
|
239
|
+
if (this.peek() !== "}") throw new ParseError("unterminated substitution", sl, sc);
|
|
240
|
+
this.advance();
|
|
241
|
+
this.push(optional ? "opt_subst" : "subst", path2.trim(), sl, sc);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (ch === '"' && this.peek(1) === '"' && this.peek(2) === '"') {
|
|
245
|
+
this.advance();
|
|
246
|
+
this.advance();
|
|
247
|
+
this.advance();
|
|
248
|
+
let value = "";
|
|
249
|
+
let closed = false;
|
|
250
|
+
while (this.pos < this.input.length) {
|
|
251
|
+
if (this.peek() === '"') {
|
|
252
|
+
let quoteCount = 0;
|
|
253
|
+
while (this.pos < this.input.length && this.peek() === '"') {
|
|
254
|
+
quoteCount++;
|
|
255
|
+
this.advance();
|
|
256
|
+
}
|
|
257
|
+
if (quoteCount >= 3) {
|
|
258
|
+
for (let i = 0; i < quoteCount - 3; i++) value += '"';
|
|
259
|
+
closed = true;
|
|
311
260
|
break;
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
261
|
+
}
|
|
262
|
+
for (let i = 0; i < quoteCount; i++) value += '"';
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
value += this.advance();
|
|
266
|
+
}
|
|
267
|
+
if (!closed) {
|
|
268
|
+
throw new ParseError("unterminated triple-quoted string", sl, sc);
|
|
269
|
+
}
|
|
270
|
+
if (value.startsWith("\n")) value = value.slice(1);
|
|
271
|
+
this.push("triple_string", value, sl, sc, true);
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (ch === '"') {
|
|
275
|
+
this.advance();
|
|
276
|
+
let value = "";
|
|
277
|
+
while (this.pos < this.input.length && this.peek() !== '"') {
|
|
278
|
+
if (this.peek() === "\n") throw new ParseError("unterminated string", sl, sc);
|
|
279
|
+
if (this.peek() === "\\") {
|
|
280
|
+
this.advance();
|
|
281
|
+
const esc = this.advance();
|
|
282
|
+
switch (esc) {
|
|
283
|
+
case "n":
|
|
284
|
+
value += "\n";
|
|
285
|
+
break;
|
|
286
|
+
case "t":
|
|
287
|
+
value += " ";
|
|
288
|
+
break;
|
|
289
|
+
case "r":
|
|
290
|
+
value += "\r";
|
|
291
|
+
break;
|
|
292
|
+
case '"':
|
|
293
|
+
value += '"';
|
|
294
|
+
break;
|
|
295
|
+
case "\\":
|
|
296
|
+
value += "\\";
|
|
297
|
+
break;
|
|
298
|
+
case "/":
|
|
299
|
+
value += "/";
|
|
300
|
+
break;
|
|
301
|
+
case "b":
|
|
302
|
+
value += "\b";
|
|
303
|
+
break;
|
|
304
|
+
case "f":
|
|
305
|
+
value += "\f";
|
|
306
|
+
break;
|
|
307
|
+
case "u": {
|
|
308
|
+
if (this.pos + 4 > this.input.length) {
|
|
309
|
+
throw new ParseError("Invalid unicode escape: not enough characters", this.line, this.col);
|
|
310
|
+
}
|
|
311
|
+
const hex = this.input.slice(this.pos, this.pos + 4);
|
|
312
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
313
|
+
throw new ParseError(`Invalid unicode escape: \\u${hex}`, this.line, this.col);
|
|
314
|
+
}
|
|
315
|
+
value += String.fromCharCode(parseInt(hex, 16));
|
|
316
|
+
for (let i = 0; i < 4; i++) this.advance();
|
|
317
|
+
break;
|
|
319
318
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
break;
|
|
319
|
+
default:
|
|
320
|
+
throw new ParseError(`Unknown escape sequence: \\${esc}`, this.line, this.col);
|
|
323
321
|
}
|
|
324
|
-
|
|
325
|
-
|
|
322
|
+
} else {
|
|
323
|
+
value += this.advance();
|
|
326
324
|
}
|
|
327
|
-
} else {
|
|
328
|
-
value += advance();
|
|
329
325
|
}
|
|
326
|
+
if (this.peek() !== '"') throw new ParseError("unterminated string", sl, sc);
|
|
327
|
+
this.advance();
|
|
328
|
+
this.push("string", value, sl, sc, true);
|
|
329
|
+
continue;
|
|
330
330
|
}
|
|
331
|
-
if (
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
while (pos < input.length && isUnquotedContinue(peek(), () => peek(1))) {
|
|
339
|
-
value += advance();
|
|
331
|
+
if (isUnquotedStart(ch)) {
|
|
332
|
+
let value = "";
|
|
333
|
+
while (this.pos < this.input.length && isUnquotedContinue(this.peek(), () => this.peek(1))) {
|
|
334
|
+
value += this.advance();
|
|
335
|
+
}
|
|
336
|
+
this.push("unquoted", value.trimEnd(), sl, sc);
|
|
337
|
+
continue;
|
|
340
338
|
}
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
throw new ParseError(`unexpected character: ${JSON.stringify(ch)}`, sl, sc);
|
|
340
|
+
}
|
|
341
|
+
this.tokens.push({ kind: "eof", value: "", line: this.line, col: this.col, isQuoted: false, precedingSpace: false });
|
|
342
|
+
return this.tokens;
|
|
343
|
+
}
|
|
344
|
+
peek(offset = 0) {
|
|
345
|
+
return this.input[this.pos + offset] ?? "";
|
|
346
|
+
}
|
|
347
|
+
advance() {
|
|
348
|
+
const ch = this.input[this.pos++] ?? "";
|
|
349
|
+
if (ch === "\n") {
|
|
350
|
+
this.line++;
|
|
351
|
+
this.col = 1;
|
|
352
|
+
} else {
|
|
353
|
+
this.col++;
|
|
343
354
|
}
|
|
344
|
-
|
|
355
|
+
return ch;
|
|
345
356
|
}
|
|
346
|
-
|
|
347
|
-
|
|
357
|
+
push(kind, value, l, c, isQuoted = false) {
|
|
358
|
+
this.tokens.push({ kind, value, line: l, col: c, isQuoted, precedingSpace: this.hadSpace });
|
|
359
|
+
this.hadSpace = false;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
function tokenize(input) {
|
|
363
|
+
return new Lexer(input).tokenize();
|
|
348
364
|
}
|
|
349
365
|
function isUnquotedStart(ch) {
|
|
350
366
|
return ch !== "" && !'{}[],:=+#\n\r "$?!@*&^\\'.includes(ch);
|
|
@@ -357,89 +373,122 @@ function isUnquotedContinue(ch, nextFn) {
|
|
|
357
373
|
}
|
|
358
374
|
|
|
359
375
|
// src/internal/parser/parser.ts
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
376
|
+
var EOF_TOKEN = { kind: "eof", value: "", line: 0, col: 0, isQuoted: false, precedingSpace: false };
|
|
377
|
+
var Parser = class {
|
|
378
|
+
constructor(tokens) {
|
|
379
|
+
this.tokens = tokens;
|
|
380
|
+
this.pos = 0;
|
|
381
|
+
}
|
|
382
|
+
parse() {
|
|
383
|
+
this.skip("newline");
|
|
384
|
+
const t = this.peek();
|
|
385
|
+
if (t.kind === "lbrace") {
|
|
386
|
+
this.advance();
|
|
387
|
+
const first = this.parseObject(true);
|
|
388
|
+
const allFields = first.kind === "object" ? [...first.fields] : [];
|
|
389
|
+
while (true) {
|
|
390
|
+
this.skip("newline");
|
|
391
|
+
if (this.peek().kind === "eof") break;
|
|
392
|
+
if (this.peek().kind === "lbrace") {
|
|
393
|
+
this.advance();
|
|
394
|
+
const extra = this.parseObject(true);
|
|
395
|
+
if (extra.kind === "object") allFields.push(...extra.fields);
|
|
396
|
+
} else {
|
|
397
|
+
const rest = this.parseObject(false);
|
|
398
|
+
if (rest.kind === "object") allFields.push(...rest.fields);
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
this.skip("newline");
|
|
403
|
+
const remaining = this.peek();
|
|
404
|
+
if (remaining.kind !== "eof") {
|
|
405
|
+
throw new ParseError(`Unexpected token '${remaining.value}' after closing brace`, remaining.line, remaining.col);
|
|
406
|
+
}
|
|
407
|
+
return { kind: "object", fields: allFields, pos: first.pos };
|
|
408
|
+
}
|
|
409
|
+
return this.parseObject(false);
|
|
410
|
+
}
|
|
411
|
+
peek(offset = 0) {
|
|
412
|
+
return this.tokens[this.pos + offset] ?? EOF_TOKEN;
|
|
364
413
|
}
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
if (pos < tokens.length) pos++;
|
|
368
|
-
return
|
|
414
|
+
advance() {
|
|
415
|
+
const t = this.tokens[this.pos];
|
|
416
|
+
if (this.pos < this.tokens.length) this.pos++;
|
|
417
|
+
return t ?? EOF_TOKEN;
|
|
369
418
|
}
|
|
370
|
-
|
|
371
|
-
while (kinds.includes(peek().kind)) advance();
|
|
419
|
+
skip(...kinds) {
|
|
420
|
+
while (kinds.includes(this.peek().kind)) this.advance();
|
|
372
421
|
}
|
|
373
|
-
|
|
374
|
-
const
|
|
375
|
-
return { line:
|
|
422
|
+
currentPos() {
|
|
423
|
+
const t = this.peek();
|
|
424
|
+
return { line: t.line, col: t.col };
|
|
376
425
|
}
|
|
377
|
-
|
|
378
|
-
const p = currentPos();
|
|
426
|
+
parseObject(expectClosingBrace) {
|
|
427
|
+
const p = this.currentPos();
|
|
379
428
|
const fields = [];
|
|
380
429
|
while (true) {
|
|
381
|
-
skip("newline");
|
|
382
|
-
const
|
|
383
|
-
if (
|
|
384
|
-
if (
|
|
385
|
-
advance();
|
|
386
|
-
fields.push(parseInclude());
|
|
387
|
-
skip("newline");
|
|
388
|
-
if (peek().kind === "comma") advance();
|
|
389
|
-
skip("newline");
|
|
430
|
+
this.skip("newline");
|
|
431
|
+
const t = this.peek();
|
|
432
|
+
if (t.kind === "eof" || t.kind === "rbrace") break;
|
|
433
|
+
if (t.kind === "unquoted" && t.value === "include") {
|
|
434
|
+
this.advance();
|
|
435
|
+
fields.push(this.parseInclude());
|
|
436
|
+
this.skip("newline");
|
|
437
|
+
if (this.peek().kind === "comma") this.advance();
|
|
438
|
+
this.skip("newline");
|
|
390
439
|
continue;
|
|
391
440
|
}
|
|
392
|
-
const keyPos = currentPos();
|
|
393
|
-
const key = parseKey();
|
|
394
|
-
skip("newline");
|
|
441
|
+
const keyPos = this.currentPos();
|
|
442
|
+
const key = this.parseKey();
|
|
443
|
+
this.skip("newline");
|
|
395
444
|
let append = false;
|
|
396
|
-
const sep = peek();
|
|
397
|
-
if (sep.kind === "equals") advance();
|
|
445
|
+
const sep = this.peek();
|
|
446
|
+
if (sep.kind === "equals") this.advance();
|
|
398
447
|
else if (sep.kind === "plus_equals") {
|
|
399
|
-
advance();
|
|
448
|
+
this.advance();
|
|
400
449
|
append = true;
|
|
401
|
-
} else if (sep.kind === "colon") advance();
|
|
450
|
+
} else if (sep.kind === "colon") this.advance();
|
|
402
451
|
else if (sep.kind === "lbrace") {
|
|
403
452
|
} else if (sep.kind !== "newline" && sep.kind !== "eof") {
|
|
404
453
|
throw new ParseError(`unexpected token after key: ${sep.kind}`, sep.line, sep.col);
|
|
405
454
|
}
|
|
406
|
-
skip("newline");
|
|
407
|
-
const value = parseValue();
|
|
455
|
+
this.skip("newline");
|
|
456
|
+
const value = this.parseValue();
|
|
408
457
|
fields.push({ key, value, append, pos: keyPos });
|
|
409
|
-
skip("newline");
|
|
410
|
-
if (peek().kind === "comma") advance();
|
|
411
|
-
skip("newline");
|
|
458
|
+
this.skip("newline");
|
|
459
|
+
if (this.peek().kind === "comma") this.advance();
|
|
460
|
+
this.skip("newline");
|
|
412
461
|
}
|
|
413
462
|
if (expectClosingBrace) {
|
|
414
|
-
const
|
|
415
|
-
if (
|
|
416
|
-
advance();
|
|
463
|
+
const t = this.peek();
|
|
464
|
+
if (t.kind !== "rbrace") throw new ParseError("expected }", t.line, t.col);
|
|
465
|
+
this.advance();
|
|
417
466
|
}
|
|
418
467
|
return { kind: "object", fields, pos: p };
|
|
419
468
|
}
|
|
420
|
-
|
|
469
|
+
parseKey() {
|
|
421
470
|
const segments = [];
|
|
422
471
|
let trailingDot = false;
|
|
423
472
|
while (true) {
|
|
424
|
-
const
|
|
425
|
-
if (
|
|
426
|
-
advance();
|
|
427
|
-
segments.push(
|
|
473
|
+
const t = this.peek();
|
|
474
|
+
if (t.kind === "string") {
|
|
475
|
+
this.advance();
|
|
476
|
+
segments.push(t.value);
|
|
428
477
|
trailingDot = false;
|
|
429
|
-
} else if (
|
|
430
|
-
advance();
|
|
431
|
-
const parts =
|
|
478
|
+
} else if (t.kind === "unquoted") {
|
|
479
|
+
this.advance();
|
|
480
|
+
const parts = t.value.split(".");
|
|
432
481
|
const filtered = parts.filter((s) => s.length > 0);
|
|
433
482
|
segments.push(...filtered);
|
|
434
|
-
trailingDot =
|
|
483
|
+
trailingDot = t.value.endsWith(".");
|
|
435
484
|
} else {
|
|
436
|
-
if (segments.length === 0) throw new ParseError(`expected key, got ${
|
|
485
|
+
if (segments.length === 0) throw new ParseError(`expected key, got ${t.kind}`, t.line, t.col);
|
|
437
486
|
break;
|
|
438
487
|
}
|
|
439
488
|
if (trailingDot) continue;
|
|
440
|
-
const next = peek();
|
|
489
|
+
const next = this.peek();
|
|
441
490
|
if (next.kind === "unquoted" && next.value === "." && !next.precedingSpace) {
|
|
442
|
-
advance();
|
|
491
|
+
this.advance();
|
|
443
492
|
trailingDot = true;
|
|
444
493
|
continue;
|
|
445
494
|
}
|
|
@@ -447,147 +496,270 @@ function parseTokens(tokens) {
|
|
|
447
496
|
}
|
|
448
497
|
return segments;
|
|
449
498
|
}
|
|
450
|
-
|
|
451
|
-
const p = currentPos();
|
|
452
|
-
skip("newline");
|
|
453
|
-
const
|
|
499
|
+
parseInclude() {
|
|
500
|
+
const p = this.currentPos();
|
|
501
|
+
this.skip("newline");
|
|
502
|
+
const t = this.peek();
|
|
454
503
|
let path2;
|
|
455
504
|
let required = false;
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
505
|
+
let isFile = false;
|
|
506
|
+
if (t.kind === "unquoted" && (t.value === "required(" || t.value === "required" || t.value.startsWith("required("))) {
|
|
507
|
+
if (t.value === "required") {
|
|
508
|
+
const next2 = this.peek(1);
|
|
459
509
|
if (next2.kind !== "unquoted" || !next2.value.startsWith("(")) {
|
|
460
|
-
throw new ParseError("include required must be followed by (",
|
|
510
|
+
throw new ParseError("include required must be followed by (", t.line, t.col);
|
|
461
511
|
}
|
|
462
512
|
}
|
|
463
513
|
required = true;
|
|
464
|
-
advance();
|
|
465
|
-
const innerPrefix =
|
|
514
|
+
this.advance();
|
|
515
|
+
const innerPrefix = t.value.startsWith("required(") ? t.value.slice("required(".length) : "";
|
|
466
516
|
if (innerPrefix.startsWith("url") || innerPrefix.startsWith("classpath")) {
|
|
467
|
-
throw new ParseError("include url(...) and classpath(...) are not supported",
|
|
517
|
+
throw new ParseError("include url(...) and classpath(...) are not supported", t.line, t.col);
|
|
468
518
|
}
|
|
469
|
-
|
|
519
|
+
if (innerPrefix.startsWith("file")) {
|
|
520
|
+
isFile = true;
|
|
521
|
+
}
|
|
522
|
+
const next = this.peek();
|
|
470
523
|
if (next.kind === "unquoted" && (next.value === "url" || next.value.startsWith("url(") || next.value === "classpath" || next.value.startsWith("classpath("))) {
|
|
471
524
|
throw new ParseError("include url(...) and classpath(...) are not supported", next.line, next.col);
|
|
472
525
|
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
while (peek().kind !== "
|
|
477
|
-
|
|
478
|
-
path2 = advance().value;
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
throw new ParseError("include
|
|
487
|
-
|
|
488
|
-
|
|
526
|
+
if (next.kind === "unquoted" && (next.value === "file(" || next.value === "file" || next.value.startsWith("file("))) {
|
|
527
|
+
isFile = true;
|
|
528
|
+
}
|
|
529
|
+
while (this.peek().kind !== "string" && this.peek().kind !== "eof") this.advance();
|
|
530
|
+
if (this.peek().kind === "eof") throw new ParseError("expected include path", t.line, t.col);
|
|
531
|
+
path2 = this.advance().value;
|
|
532
|
+
while (this.peek().kind !== "newline" && this.peek().kind !== "rbrace" && this.peek().kind !== "eof" && this.peek().kind !== "comma") this.advance();
|
|
533
|
+
} else if (t.kind === "string") {
|
|
534
|
+
path2 = this.advance().value;
|
|
535
|
+
} else if (t.kind === "unquoted" && (t.value === "file(" || t.value === "file")) {
|
|
536
|
+
isFile = true;
|
|
537
|
+
this.advance();
|
|
538
|
+
while (this.peek().kind !== "string" && this.peek().kind !== "eof") this.advance();
|
|
539
|
+
if (this.peek().kind === "eof") throw new ParseError("expected include path", t.line, t.col);
|
|
540
|
+
path2 = this.advance().value;
|
|
541
|
+
while (this.peek().kind !== "newline" && this.peek().kind !== "rbrace" && this.peek().kind !== "eof" && this.peek().kind !== "comma") this.advance();
|
|
542
|
+
} else if (t.kind === "unquoted" && (t.value === "url" || t.value.startsWith("url("))) {
|
|
543
|
+
throw new ParseError("include url(...) is not supported", t.line, t.col);
|
|
544
|
+
} else if (t.kind === "unquoted" && (t.value === "classpath" || t.value.startsWith("classpath("))) {
|
|
545
|
+
throw new ParseError("include classpath(...) is not supported", t.line, t.col);
|
|
489
546
|
} else {
|
|
490
|
-
throw new ParseError(`expected include path, got ${
|
|
547
|
+
throw new ParseError(`expected include path, got ${t.kind}`, t.line, t.col);
|
|
491
548
|
}
|
|
492
549
|
return {
|
|
493
550
|
key: [],
|
|
494
|
-
value: { kind: "include", path: path2, required, pos: p },
|
|
551
|
+
value: { kind: "include", path: path2, required, ...isFile ? { isFile: true } : {}, pos: p },
|
|
495
552
|
append: false,
|
|
496
553
|
pos: p
|
|
497
554
|
};
|
|
498
555
|
}
|
|
499
|
-
|
|
500
|
-
const p = currentPos();
|
|
556
|
+
parseValue() {
|
|
557
|
+
const p = this.currentPos();
|
|
501
558
|
const parts = [];
|
|
502
559
|
while (true) {
|
|
503
|
-
const
|
|
504
|
-
if (
|
|
505
|
-
const hadSpace =
|
|
560
|
+
const t = this.peek();
|
|
561
|
+
if (t.kind === "eof" || t.kind === "newline" || t.kind === "rbrace" || t.kind === "rbracket" || t.kind === "comma") break;
|
|
562
|
+
const hadSpace = t.precedingSpace && parts.length > 0;
|
|
506
563
|
let node;
|
|
507
|
-
if (
|
|
508
|
-
advance();
|
|
509
|
-
node = parseObject(true);
|
|
510
|
-
} else if (
|
|
511
|
-
advance();
|
|
512
|
-
node = parseArray();
|
|
513
|
-
} else if (
|
|
514
|
-
advance();
|
|
515
|
-
node = { kind: "subst", path:
|
|
516
|
-
} else if (
|
|
517
|
-
advance();
|
|
518
|
-
node = { kind: "scalar",
|
|
519
|
-
} else if (
|
|
520
|
-
advance();
|
|
521
|
-
node = { kind: "scalar", value:
|
|
522
|
-
} else if ((
|
|
523
|
-
advance();
|
|
524
|
-
node = { kind: "scalar",
|
|
564
|
+
if (t.kind === "lbrace") {
|
|
565
|
+
this.advance();
|
|
566
|
+
node = this.parseObject(true);
|
|
567
|
+
} else if (t.kind === "lbracket") {
|
|
568
|
+
this.advance();
|
|
569
|
+
node = this.parseArray();
|
|
570
|
+
} else if (t.kind === "subst" || t.kind === "opt_subst") {
|
|
571
|
+
this.advance();
|
|
572
|
+
node = { kind: "subst", path: t.value, optional: t.kind === "opt_subst", pos: { line: t.line, col: t.col } };
|
|
573
|
+
} else if (t.kind === "string" || t.kind === "triple_string") {
|
|
574
|
+
this.advance();
|
|
575
|
+
node = { kind: "scalar", raw: t.value, valueType: "string", pos: { line: t.line, col: t.col } };
|
|
576
|
+
} else if (t.kind === "unquoted") {
|
|
577
|
+
this.advance();
|
|
578
|
+
node = { kind: "scalar", raw: t.value, valueType: this.scalarValueType(t.value), pos: { line: t.line, col: t.col } };
|
|
579
|
+
} else if ((t.kind === "colon" || t.kind === "equals") && parts.length > 0) {
|
|
580
|
+
this.advance();
|
|
581
|
+
node = { kind: "scalar", raw: t.value, valueType: "string", pos: { line: t.line, col: t.col } };
|
|
525
582
|
} else {
|
|
526
583
|
break;
|
|
527
584
|
}
|
|
528
585
|
if (hadSpace) {
|
|
529
|
-
parts.push({ kind: "scalar",
|
|
586
|
+
parts.push({ kind: "scalar", raw: " ", valueType: "string", pos: { line: t.line, col: t.col }, _separator: true });
|
|
530
587
|
}
|
|
531
588
|
parts.push(node);
|
|
532
589
|
}
|
|
533
|
-
if (parts.length === 0) throw new ParseError("expected value", peek().line, peek().col);
|
|
590
|
+
if (parts.length === 0) throw new ParseError("expected value", this.peek().line, this.peek().col);
|
|
534
591
|
if (parts.length === 1) return parts[0];
|
|
535
592
|
return { kind: "concat", nodes: parts, pos: p };
|
|
536
593
|
}
|
|
537
|
-
|
|
538
|
-
const p = currentPos();
|
|
594
|
+
parseArray() {
|
|
595
|
+
const p = this.currentPos();
|
|
539
596
|
const items = [];
|
|
540
597
|
while (true) {
|
|
541
|
-
skip("newline");
|
|
542
|
-
if (peek().kind === "rbracket" || peek().kind === "eof") break;
|
|
543
|
-
items.push(parseValue());
|
|
544
|
-
skip("newline");
|
|
545
|
-
if (peek().kind === "comma") advance();
|
|
546
|
-
skip("newline");
|
|
547
|
-
}
|
|
548
|
-
const
|
|
549
|
-
if (
|
|
550
|
-
advance();
|
|
598
|
+
this.skip("newline");
|
|
599
|
+
if (this.peek().kind === "rbracket" || this.peek().kind === "eof") break;
|
|
600
|
+
items.push(this.parseValue());
|
|
601
|
+
this.skip("newline");
|
|
602
|
+
if (this.peek().kind === "comma") this.advance();
|
|
603
|
+
this.skip("newline");
|
|
604
|
+
}
|
|
605
|
+
const t = this.peek();
|
|
606
|
+
if (t.kind !== "rbracket") throw new ParseError("expected ]", t.line, t.col);
|
|
607
|
+
this.advance();
|
|
551
608
|
return { kind: "array", items, pos: p };
|
|
552
609
|
}
|
|
553
|
-
|
|
554
|
-
if (raw === "true") return
|
|
555
|
-
if (raw === "
|
|
556
|
-
if (raw ===
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
return
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
610
|
+
scalarValueType(raw) {
|
|
611
|
+
if (raw === "true" || raw === "false") return "boolean";
|
|
612
|
+
if (raw === "null") return "null";
|
|
613
|
+
if (raw.length > 0 && (raw.charCodeAt(0) >= 48 && raw.charCodeAt(0) <= 57 || raw.charCodeAt(0) === 45)) {
|
|
614
|
+
if (DECIMAL_NUMBER_RE.test(raw)) return "number";
|
|
615
|
+
}
|
|
616
|
+
return "string";
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
function parseTokens(tokens) {
|
|
620
|
+
return new Parser(tokens).parse();
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// src/internal/resolver/types.ts
|
|
624
|
+
var separatorValues = /* @__PURE__ */ new WeakSet();
|
|
625
|
+
function isSubst(v) {
|
|
626
|
+
return v._kind === "subst-placeholder";
|
|
627
|
+
}
|
|
628
|
+
function isConcat(v) {
|
|
629
|
+
return v._kind === "concat-placeholder";
|
|
630
|
+
}
|
|
631
|
+
function isAppend(v) {
|
|
632
|
+
return v._kind === "append-placeholder";
|
|
633
|
+
}
|
|
634
|
+
function isResObj(v) {
|
|
635
|
+
return v._kind === "res-obj";
|
|
636
|
+
}
|
|
637
|
+
function makeResObj() {
|
|
638
|
+
return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// src/internal/resolver/utils.ts
|
|
642
|
+
function parseSubstPath(raw) {
|
|
643
|
+
const segments = [];
|
|
644
|
+
let i = 0;
|
|
645
|
+
while (i < raw.length) {
|
|
646
|
+
while (i < raw.length && (raw[i] === " " || raw[i] === " ")) i++;
|
|
647
|
+
if (i >= raw.length) break;
|
|
648
|
+
if (raw[i] === '"') {
|
|
649
|
+
i++;
|
|
650
|
+
let seg = "";
|
|
651
|
+
while (i < raw.length && raw[i] !== '"') {
|
|
652
|
+
if (raw[i] === "\\" && i + 1 < raw.length && (raw[i + 1] === '"' || raw[i + 1] === "\\")) {
|
|
653
|
+
seg += raw[i + 1];
|
|
654
|
+
i += 2;
|
|
655
|
+
} else {
|
|
656
|
+
seg += raw[i];
|
|
657
|
+
i++;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
if (i < raw.length) i++;
|
|
661
|
+
segments.push(seg);
|
|
662
|
+
while (i < raw.length && (raw[i] === " " || raw[i] === " ")) i++;
|
|
663
|
+
if (i < raw.length && raw[i] === ".") i++;
|
|
664
|
+
} else if (raw[i] === ".") {
|
|
665
|
+
segments.push("");
|
|
666
|
+
i++;
|
|
667
|
+
} else {
|
|
668
|
+
let seg = "";
|
|
669
|
+
while (i < raw.length && raw[i] !== ".") {
|
|
670
|
+
seg += raw[i];
|
|
671
|
+
i++;
|
|
578
672
|
}
|
|
673
|
+
segments.push(seg.trim());
|
|
674
|
+
if (i < raw.length && raw[i] === ".") i++;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
return segments;
|
|
678
|
+
}
|
|
679
|
+
function segmentsToKey(segments) {
|
|
680
|
+
return segments.map((s) => {
|
|
681
|
+
if (s === "" || /[^a-zA-Z0-9\-_]/.test(s)) {
|
|
682
|
+
return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
683
|
+
}
|
|
684
|
+
return s;
|
|
685
|
+
}).join(".");
|
|
686
|
+
}
|
|
687
|
+
function lookupPath(root, segments) {
|
|
688
|
+
const [head, ...tail] = segments;
|
|
689
|
+
if (head === void 0 || head === "") {
|
|
690
|
+
if (segments.length > 0) {
|
|
691
|
+
const val2 = root.fields.get("");
|
|
692
|
+
if (val2 === void 0) return void 0;
|
|
693
|
+
if (tail.length === 0) return val2;
|
|
694
|
+
if (isResObj(val2)) return lookupPath(val2, tail);
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
return void 0;
|
|
698
|
+
}
|
|
699
|
+
const val = root.fields.get(head);
|
|
700
|
+
if (val === void 0) return void 0;
|
|
701
|
+
if (tail.length === 0) return val;
|
|
702
|
+
if (isResObj(val)) return lookupPath(val, tail);
|
|
703
|
+
return void 0;
|
|
704
|
+
}
|
|
705
|
+
function lookupResObj(root, segments) {
|
|
706
|
+
let cur = root;
|
|
707
|
+
for (const seg of segments) {
|
|
708
|
+
const val = cur.fields.get(seg);
|
|
709
|
+
if (val === void 0 || !isResObj(val)) return void 0;
|
|
710
|
+
cur = val;
|
|
711
|
+
}
|
|
712
|
+
return cur;
|
|
713
|
+
}
|
|
714
|
+
function deepMergeHoconValues(base, overlay) {
|
|
715
|
+
const merged = new Map(base.fields);
|
|
716
|
+
for (const [k, v] of overlay.fields) {
|
|
717
|
+
const existing = merged.get(k);
|
|
718
|
+
if (existing?.kind === "object" && v.kind === "object") {
|
|
719
|
+
merged.set(k, deepMergeHoconValues(existing, v));
|
|
720
|
+
} else {
|
|
721
|
+
merged.set(k, v);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
return { kind: "object", fields: merged };
|
|
725
|
+
}
|
|
726
|
+
function deepMergeResObjInto(dst, src) {
|
|
727
|
+
for (const [k, srcVal] of src.fields) {
|
|
728
|
+
const dstVal = dst.fields.get(k);
|
|
729
|
+
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
730
|
+
deepMergeResObjInto(dstVal, srcVal);
|
|
731
|
+
} else {
|
|
732
|
+
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
733
|
+
dst.fields.set(k, srcVal);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
for (const [k, srcPrior] of src.priorValues) {
|
|
737
|
+
if (!dst.priorValues.has(k)) {
|
|
738
|
+
dst.priorValues.set(k, srcPrior);
|
|
579
739
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
function hoconValueToResObj(hv) {
|
|
743
|
+
const obj = makeResObj();
|
|
744
|
+
if (hv.kind !== "object") return obj;
|
|
745
|
+
for (const [key, val] of hv.fields) {
|
|
746
|
+
if (val.kind === "object") {
|
|
747
|
+
obj.fields.set(key, hoconValueToResObj(val));
|
|
748
|
+
} else {
|
|
749
|
+
obj.fields.set(key, val);
|
|
584
750
|
}
|
|
585
|
-
return { kind: "object", fields: allFields, pos: first.pos };
|
|
586
751
|
}
|
|
587
|
-
return
|
|
752
|
+
return obj;
|
|
753
|
+
}
|
|
754
|
+
function isFileNotFoundError(e) {
|
|
755
|
+
if (!(e instanceof Error)) return false;
|
|
756
|
+
const code = e.code;
|
|
757
|
+
if (code === "ENOENT" || code === "MODULE_NOT_FOUND") return true;
|
|
758
|
+
const msg = e.message.toLowerCase();
|
|
759
|
+
return msg.includes("not found") || msg.includes("no such file") || msg.includes("enoent");
|
|
588
760
|
}
|
|
589
761
|
|
|
590
|
-
// src/internal/resolver/
|
|
762
|
+
// src/internal/resolver/include-loader.ts
|
|
591
763
|
import * as nodePath from "path";
|
|
592
764
|
|
|
593
765
|
// src/internal/properties/properties.ts
|
|
@@ -634,7 +806,7 @@ function recordToHoconValue(obj) {
|
|
|
634
806
|
const fields = /* @__PURE__ */ new Map();
|
|
635
807
|
for (const [key, val] of Object.entries(obj)) {
|
|
636
808
|
if (typeof val === "string") {
|
|
637
|
-
fields.set(key, { kind: "scalar",
|
|
809
|
+
fields.set(key, { kind: "scalar", raw: val, valueType: "string" });
|
|
638
810
|
} else if (val !== null && typeof val === "object") {
|
|
639
811
|
fields.set(key, recordToHoconValue(val));
|
|
640
812
|
}
|
|
@@ -642,356 +814,550 @@ function recordToHoconValue(obj) {
|
|
|
642
814
|
return { kind: "object", fields };
|
|
643
815
|
}
|
|
644
816
|
|
|
645
|
-
// src/internal/resolver/
|
|
646
|
-
var
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
817
|
+
// src/internal/resolver/include-loader.ts
|
|
818
|
+
var IncludeLoader = class {
|
|
819
|
+
constructor(opts) {
|
|
820
|
+
this.opts = opts;
|
|
821
|
+
}
|
|
822
|
+
/**
|
|
823
|
+
* Resolve an include path to an absolute path.
|
|
824
|
+
* - file() includes resolve relative to CWD (or as absolute paths),
|
|
825
|
+
* NOT relative to the including file's directory.
|
|
826
|
+
* - Bare includes resolve relative to the including file's directory (baseDir).
|
|
827
|
+
*/
|
|
828
|
+
resolveIncludePath(includePath, baseDir, isFile) {
|
|
829
|
+
return isFile ? nodePath.resolve(includePath) : baseDir ? nodePath.resolve(baseDir, includePath) : nodePath.resolve(includePath);
|
|
830
|
+
}
|
|
831
|
+
load(includePath, required, isFile) {
|
|
832
|
+
const { baseDir, includeStack = [] } = this.opts;
|
|
833
|
+
const absPath = this.resolveIncludePath(includePath, baseDir, !!isFile);
|
|
834
|
+
if (includeStack.includes(absPath)) {
|
|
835
|
+
throw new ResolveError(`circular include: ${absPath}`, absPath, 0, 0);
|
|
836
|
+
}
|
|
837
|
+
if (includeStack.length >= 50) {
|
|
838
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, includePath, 0, 0);
|
|
839
|
+
}
|
|
840
|
+
const hasExplicitExt = absPath.endsWith(".conf") || absPath.endsWith(".json") || absPath.endsWith(".properties");
|
|
841
|
+
if (hasExplicitExt) {
|
|
842
|
+
const result = this.loadSingle(absPath);
|
|
843
|
+
if (result !== void 0) return result;
|
|
844
|
+
if (required) {
|
|
845
|
+
throw new ResolveError(`required include file not found: ${includePath}`, includePath, 0, 0);
|
|
846
|
+
}
|
|
847
|
+
return makeResObj();
|
|
848
|
+
}
|
|
849
|
+
const merged = makeResObj();
|
|
850
|
+
let foundAny = false;
|
|
851
|
+
const probeExts = [".properties", ".json", ".conf"];
|
|
852
|
+
for (const ext of probeExts) {
|
|
853
|
+
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
854
|
+
if (obj !== void 0) {
|
|
855
|
+
deepMergeResObjInto(merged, obj);
|
|
856
|
+
foundAny = true;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (!foundAny && required) {
|
|
860
|
+
throw new ResolveError(`required include file not found: ${includePath}`, includePath, 0, 0);
|
|
861
|
+
}
|
|
862
|
+
return merged;
|
|
671
863
|
}
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
864
|
+
async loadAsync(includePath, required, isFile) {
|
|
865
|
+
const { baseDir, includeStack = [] } = this.opts;
|
|
866
|
+
const absPath = this.resolveIncludePath(includePath, baseDir, !!isFile);
|
|
867
|
+
if (includeStack.includes(absPath)) {
|
|
868
|
+
throw new ResolveError(`circular include: ${absPath}`, absPath, 0, 0);
|
|
869
|
+
}
|
|
870
|
+
if (includeStack.length >= 50) {
|
|
871
|
+
throw new ResolveError(`include depth limit exceeded (max 50)`, includePath, 0, 0);
|
|
872
|
+
}
|
|
873
|
+
const hasExplicitExt = absPath.endsWith(".conf") || absPath.endsWith(".json") || absPath.endsWith(".properties");
|
|
874
|
+
if (hasExplicitExt) {
|
|
875
|
+
const result = await this.loadSingleAsync(absPath);
|
|
876
|
+
if (result !== void 0) return result;
|
|
877
|
+
if (required) {
|
|
878
|
+
throw new ResolveError(`required include file not found: ${includePath}`, includePath, 0, 0);
|
|
879
|
+
}
|
|
880
|
+
return makeResObj();
|
|
881
|
+
}
|
|
882
|
+
const merged = makeResObj();
|
|
883
|
+
let foundAny = false;
|
|
884
|
+
const probeExts = [".properties", ".json", ".conf"];
|
|
885
|
+
for (const ext of probeExts) {
|
|
886
|
+
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
887
|
+
if (obj !== void 0) {
|
|
888
|
+
deepMergeResObjInto(merged, obj);
|
|
889
|
+
foundAny = true;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
if (!foundAny && required) {
|
|
893
|
+
throw new ResolveError(`required include file not found: ${includePath}`, includePath, 0, 0);
|
|
894
|
+
}
|
|
895
|
+
return merged;
|
|
675
896
|
}
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
obj.priorValues.set(head, existing2);
|
|
698
|
-
obj.fields.set(head, {
|
|
699
|
-
_kind: "append-placeholder",
|
|
700
|
-
existing: existing2,
|
|
701
|
-
elem: astToResolverValue(field.value, opts)
|
|
897
|
+
loadSingle(candidate) {
|
|
898
|
+
const { readFileSync: readFileSync2, includeStack = [], env } = this.opts;
|
|
899
|
+
if (includeStack.includes(candidate)) {
|
|
900
|
+
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
901
|
+
}
|
|
902
|
+
let content;
|
|
903
|
+
try {
|
|
904
|
+
content = readFileSync2(candidate);
|
|
905
|
+
} catch (e) {
|
|
906
|
+
if (isFileNotFoundError(e)) return void 0;
|
|
907
|
+
throw e;
|
|
908
|
+
}
|
|
909
|
+
if (candidate.endsWith(".properties")) {
|
|
910
|
+
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
911
|
+
}
|
|
912
|
+
const ast = parseTokens(tokenize(content));
|
|
913
|
+
return this.onBuildResObj(ast, {
|
|
914
|
+
env,
|
|
915
|
+
baseDir: nodePath.dirname(candidate),
|
|
916
|
+
readFileSync: readFileSync2,
|
|
917
|
+
includeStack: [...includeStack, candidate]
|
|
702
918
|
});
|
|
703
|
-
return;
|
|
704
|
-
}
|
|
705
|
-
const existing = obj.fields.get(head);
|
|
706
|
-
const newVal = astToResolverValue(field.value, opts);
|
|
707
|
-
if (existing !== void 0) {
|
|
708
|
-
obj.priorValues.set(head, existing);
|
|
709
919
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
}
|
|
716
|
-
function astToResolverValue(ast, opts) {
|
|
717
|
-
switch (ast.kind) {
|
|
718
|
-
case "scalar": {
|
|
719
|
-
const sv = { kind: "scalar", value: ast.value };
|
|
720
|
-
if (ast._separator) separatorValues.add(sv);
|
|
721
|
-
return sv;
|
|
920
|
+
async loadSingleAsync(candidate) {
|
|
921
|
+
const { readFile, readFileSync: readFileSync2, includeStack = [], env } = this.opts;
|
|
922
|
+
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
923
|
+
if (includeStack.includes(candidate)) {
|
|
924
|
+
throw new ResolveError(`circular include: ${candidate}`, candidate, 0, 0);
|
|
722
925
|
}
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
return
|
|
926
|
+
let content;
|
|
927
|
+
try {
|
|
928
|
+
content = await read(candidate);
|
|
929
|
+
} catch (e) {
|
|
930
|
+
if (isFileNotFoundError(e)) return void 0;
|
|
931
|
+
throw e;
|
|
728
932
|
}
|
|
729
|
-
|
|
730
|
-
return
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
933
|
+
if (candidate.endsWith(".properties")) {
|
|
934
|
+
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
935
|
+
}
|
|
936
|
+
const ast = parseTokens(tokenize(content));
|
|
937
|
+
return this.onBuildResObjAsync(ast, {
|
|
938
|
+
env,
|
|
939
|
+
baseDir: nodePath.dirname(candidate),
|
|
940
|
+
readFileSync: readFileSync2,
|
|
941
|
+
readFile,
|
|
942
|
+
includeStack: [...includeStack, candidate]
|
|
943
|
+
});
|
|
735
944
|
}
|
|
736
|
-
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// src/internal/resolver/structure-builder.ts
|
|
948
|
+
var StructureBuilder = class _StructureBuilder {
|
|
949
|
+
constructor(opts) {
|
|
950
|
+
this.opts = opts;
|
|
951
|
+
this.loader = new IncludeLoader(opts);
|
|
952
|
+
this.loader.onBuildResObj = (a, o) => new _StructureBuilder(o).build(a);
|
|
953
|
+
this.loader.onBuildResObjAsync = async (a, o) => new _StructureBuilder(o).buildAsync(a);
|
|
954
|
+
}
|
|
955
|
+
build(ast, pathPrefix = []) {
|
|
956
|
+
if (ast.kind !== "object") {
|
|
957
|
+
throw new ResolveError("root AST must be an object", "", ast.pos.line, ast.pos.col);
|
|
745
958
|
}
|
|
959
|
+
const obj = makeResObj();
|
|
960
|
+
for (const field of ast.fields) {
|
|
961
|
+
this.applyField(obj, field, pathPrefix);
|
|
962
|
+
}
|
|
963
|
+
return obj;
|
|
746
964
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
for (const [k, srcVal] of src.fields) {
|
|
751
|
-
const dstVal = dst.fields.get(k);
|
|
752
|
-
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
753
|
-
deepMergeResObjInto(dstVal, srcVal);
|
|
754
|
-
} else {
|
|
755
|
-
if (dstVal !== void 0) dst.priorValues.set(k, dstVal);
|
|
756
|
-
dst.fields.set(k, srcVal);
|
|
965
|
+
async buildAsync(ast, pathPrefix = []) {
|
|
966
|
+
if (ast.kind !== "object") {
|
|
967
|
+
throw new ResolveError("root AST must be an object", "", ast.pos.line, ast.pos.col);
|
|
757
968
|
}
|
|
969
|
+
const obj = makeResObj();
|
|
970
|
+
for (const field of ast.fields) {
|
|
971
|
+
await this.applyFieldAsync(obj, field, pathPrefix);
|
|
972
|
+
}
|
|
973
|
+
return obj;
|
|
758
974
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
if (resolved !== void 0) {
|
|
765
|
-
result.set(key, resolved);
|
|
766
|
-
} else {
|
|
767
|
-
const prior = obj.priorValues.get(key);
|
|
768
|
-
if (prior !== void 0) {
|
|
769
|
-
const priorResolved = resolveVal(prior, obj, root, resolving, resolvedCache, opts);
|
|
770
|
-
if (priorResolved !== void 0) result.set(key, priorResolved);
|
|
975
|
+
applyField(obj, field, pathPrefix) {
|
|
976
|
+
if (field.key.length === 0 && field.value.kind === "include") {
|
|
977
|
+
const included = this.loader.load(field.value.path, field.value.required, field.value.isFile);
|
|
978
|
+
if (pathPrefix.length > 0) {
|
|
979
|
+
this.relativizeResObj(included, pathPrefix);
|
|
771
980
|
}
|
|
981
|
+
deepMergeResObjInto(obj, included);
|
|
982
|
+
return;
|
|
772
983
|
}
|
|
984
|
+
const [head, ...tail] = field.key;
|
|
985
|
+
if (!head) return;
|
|
986
|
+
if (tail.length > 0) {
|
|
987
|
+
const syntheticAst = {
|
|
988
|
+
kind: "object",
|
|
989
|
+
fields: [{ key: tail, value: field.value, append: field.append, pos: field.pos }],
|
|
990
|
+
pos: field.pos
|
|
991
|
+
};
|
|
992
|
+
this.applyField(obj, { key: [head], value: syntheticAst, append: false, pos: field.pos }, pathPrefix);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
const childPrefix = [...pathPrefix, head];
|
|
996
|
+
if (field.append) {
|
|
997
|
+
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
998
|
+
obj.priorValues.set(head, existing2);
|
|
999
|
+
obj.fields.set(head, {
|
|
1000
|
+
_kind: "append-placeholder",
|
|
1001
|
+
existing: existing2,
|
|
1002
|
+
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1003
|
+
});
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const existing = obj.fields.get(head);
|
|
1007
|
+
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1008
|
+
if (existing !== void 0) {
|
|
1009
|
+
obj.priorValues.set(head, existing);
|
|
1010
|
+
}
|
|
1011
|
+
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1012
|
+
deepMergeResObjInto(existing, newVal);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
obj.fields.set(head, newVal);
|
|
773
1016
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
if (isAppend(v)) return resolveAppend(v, scope, root, resolving, resolvedCache, opts);
|
|
780
|
-
if (isResObj(v)) return resolveResObj(v, root, resolving, resolvedCache, opts);
|
|
781
|
-
const hv = v;
|
|
782
|
-
if (hv.kind === "array") {
|
|
783
|
-
return {
|
|
784
|
-
kind: "array",
|
|
785
|
-
items: hv.items.map(
|
|
786
|
-
(item) => resolveVal(item, scope, root, resolving, resolvedCache, opts) ?? { kind: "scalar", value: null }
|
|
787
|
-
)
|
|
788
|
-
};
|
|
789
|
-
}
|
|
790
|
-
return hv;
|
|
791
|
-
}
|
|
792
|
-
function resolveSubst(s, scope, root, resolving, resolvedCache, opts) {
|
|
793
|
-
if (resolvedCache.has(s.path)) return resolvedCache.get(s.path);
|
|
794
|
-
if (resolving.has(s.path)) {
|
|
795
|
-
const rootSeg = parseSubstPath(s.path)[0] ?? "";
|
|
796
|
-
const prior = scope.priorValues.get(rootSeg) ?? root.priorValues.get(rootSeg);
|
|
797
|
-
if (prior !== void 0) {
|
|
798
|
-
return resolveVal(prior, scope, root, new Set(resolving), resolvedCache, opts);
|
|
799
|
-
}
|
|
800
|
-
if (s.optional) return void 0;
|
|
801
|
-
throw new ResolveError(`circular substitution: ${s.path}`, s.path, s.line, s.col);
|
|
802
|
-
}
|
|
803
|
-
resolving.add(s.path);
|
|
804
|
-
try {
|
|
805
|
-
const found = lookupPath(root, parseSubstPath(s.path));
|
|
806
|
-
if (found !== void 0) {
|
|
807
|
-
if (isSubst(found) || isConcat(found)) {
|
|
808
|
-
const rootSeg = parseSubstPath(s.path)[0] ?? "";
|
|
809
|
-
const prior = scope.priorValues.get(rootSeg) ?? root.priorValues.get(rootSeg);
|
|
810
|
-
if (prior !== void 0) {
|
|
811
|
-
const result2 = resolveVal(prior, scope, root, resolving, resolvedCache, opts);
|
|
812
|
-
if (result2 !== void 0) resolvedCache.set(s.path, result2);
|
|
813
|
-
return result2;
|
|
814
|
-
}
|
|
1017
|
+
async applyFieldAsync(obj, field, pathPrefix) {
|
|
1018
|
+
if (field.key.length === 0 && field.value.kind === "include") {
|
|
1019
|
+
const included = await this.loader.loadAsync(field.value.path, field.value.required, field.value.isFile);
|
|
1020
|
+
if (pathPrefix.length > 0) {
|
|
1021
|
+
this.relativizeResObj(included, pathPrefix);
|
|
815
1022
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
return result;
|
|
1023
|
+
deepMergeResObjInto(obj, included);
|
|
1024
|
+
return;
|
|
819
1025
|
}
|
|
820
|
-
const
|
|
821
|
-
if (
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
1026
|
+
const [head, ...tail] = field.key;
|
|
1027
|
+
if (!head) return;
|
|
1028
|
+
if (tail.length > 0) {
|
|
1029
|
+
const syntheticAst = {
|
|
1030
|
+
kind: "object",
|
|
1031
|
+
fields: [{ key: tail, value: field.value, append: field.append, pos: field.pos }],
|
|
1032
|
+
pos: field.pos
|
|
1033
|
+
};
|
|
1034
|
+
await this.applyFieldAsync(obj, { key: [head], value: syntheticAst, append: false, pos: field.pos }, pathPrefix);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const childPrefix = [...pathPrefix, head];
|
|
1038
|
+
if (field.append) {
|
|
1039
|
+
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1040
|
+
obj.priorValues.set(head, existing2);
|
|
1041
|
+
obj.fields.set(head, {
|
|
1042
|
+
_kind: "append-placeholder",
|
|
1043
|
+
existing: existing2,
|
|
1044
|
+
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1045
|
+
});
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
const existing = obj.fields.get(head);
|
|
1049
|
+
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1050
|
+
if (existing !== void 0) {
|
|
1051
|
+
obj.priorValues.set(head, existing);
|
|
1052
|
+
}
|
|
1053
|
+
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1054
|
+
deepMergeResObjInto(existing, newVal);
|
|
1055
|
+
return;
|
|
1056
|
+
}
|
|
1057
|
+
obj.fields.set(head, newVal);
|
|
1058
|
+
}
|
|
1059
|
+
astToResolverValue(ast, pathPrefix) {
|
|
1060
|
+
switch (ast.kind) {
|
|
1061
|
+
case "scalar": {
|
|
1062
|
+
const sv = { kind: "scalar", raw: ast.raw, valueType: ast.valueType };
|
|
1063
|
+
if (ast._separator) separatorValues.add(sv);
|
|
1064
|
+
return sv;
|
|
1065
|
+
}
|
|
1066
|
+
case "array":
|
|
1067
|
+
return { kind: "array", items: ast.items.map((i) => this.astToResolverValue(i, pathPrefix)) };
|
|
1068
|
+
case "object": {
|
|
1069
|
+
const inner = this.build(ast, pathPrefix);
|
|
1070
|
+
return inner;
|
|
1071
|
+
}
|
|
1072
|
+
case "subst":
|
|
1073
|
+
return { _kind: "subst-placeholder", segments: parseSubstPath(ast.path), optional: ast.optional, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
|
|
1074
|
+
case "concat":
|
|
1075
|
+
return { _kind: "concat-placeholder", nodes: ast.nodes.map((n) => this.astToResolverValue(n, pathPrefix)) };
|
|
1076
|
+
case "include":
|
|
1077
|
+
return { kind: "scalar", raw: "null", valueType: "null" };
|
|
825
1078
|
}
|
|
826
|
-
if (s.optional) return void 0;
|
|
827
|
-
throw new ResolveError(`could not resolve substitution: \${${s.path}}`, s.path, s.line, s.col);
|
|
828
|
-
} finally {
|
|
829
|
-
resolving.delete(s.path);
|
|
830
1079
|
}
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
const existing = merged.get(k);
|
|
843
|
-
if (existing?.kind === "object" && val.kind === "object") {
|
|
844
|
-
merged.set(k, deepMergeHoconValues(existing, val));
|
|
845
|
-
} else {
|
|
846
|
-
merged.set(k, val);
|
|
847
|
-
}
|
|
1080
|
+
async astToResolverValueAsync(ast, pathPrefix) {
|
|
1081
|
+
switch (ast.kind) {
|
|
1082
|
+
case "scalar": {
|
|
1083
|
+
const sv = { kind: "scalar", raw: ast.raw, valueType: ast.valueType };
|
|
1084
|
+
if (ast._separator) separatorValues.add(sv);
|
|
1085
|
+
return sv;
|
|
1086
|
+
}
|
|
1087
|
+
case "array": {
|
|
1088
|
+
const items = [];
|
|
1089
|
+
for (const i of ast.items) {
|
|
1090
|
+
items.push(await this.astToResolverValueAsync(i, pathPrefix));
|
|
848
1091
|
}
|
|
1092
|
+
return { kind: "array", items };
|
|
849
1093
|
}
|
|
1094
|
+
case "object":
|
|
1095
|
+
return await this.buildAsync(ast, pathPrefix);
|
|
1096
|
+
case "subst":
|
|
1097
|
+
return { _kind: "subst-placeholder", segments: parseSubstPath(ast.path), optional: ast.optional, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
|
|
1098
|
+
case "concat": {
|
|
1099
|
+
const nodes = [];
|
|
1100
|
+
for (const n of ast.nodes) {
|
|
1101
|
+
nodes.push(await this.astToResolverValueAsync(n, pathPrefix));
|
|
1102
|
+
}
|
|
1103
|
+
return { _kind: "concat-placeholder", nodes };
|
|
1104
|
+
}
|
|
1105
|
+
case "include":
|
|
1106
|
+
return { kind: "scalar", raw: "null", valueType: "null" };
|
|
850
1107
|
}
|
|
851
|
-
return { kind: "object", fields: merged };
|
|
852
1108
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1109
|
+
// ---- Relativize substitution paths for nested includes ----
|
|
1110
|
+
relativizeSubstPaths(val, prefixSegments) {
|
|
1111
|
+
if (isSubst(val)) {
|
|
1112
|
+
val.segments = [...prefixSegments, ...val.segments];
|
|
1113
|
+
val.prefixLen += prefixSegments.length;
|
|
1114
|
+
return;
|
|
858
1115
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
1116
|
+
if (isConcat(val)) {
|
|
1117
|
+
for (const node of val.nodes) {
|
|
1118
|
+
this.relativizeSubstPaths(node, prefixSegments);
|
|
1119
|
+
}
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (isAppend(val)) {
|
|
1123
|
+
this.relativizeSubstPaths(val.existing, prefixSegments);
|
|
1124
|
+
this.relativizeSubstPaths(val.elem, prefixSegments);
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
if (isResObj(val)) {
|
|
1128
|
+
this.relativizeResObj(val, prefixSegments);
|
|
1129
|
+
return;
|
|
1130
|
+
}
|
|
1131
|
+
const hv = val;
|
|
1132
|
+
if (hv.kind === "array") {
|
|
1133
|
+
for (const item of hv.items) {
|
|
1134
|
+
this.relativizeSubstPaths(item, prefixSegments);
|
|
1135
|
+
}
|
|
872
1136
|
}
|
|
873
1137
|
}
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
if (elem !== void 0) items.push(elem);
|
|
881
|
-
return { kind: "array", items };
|
|
882
|
-
}
|
|
883
|
-
function lookupPath(root, segments) {
|
|
884
|
-
const [head, ...tail] = segments;
|
|
885
|
-
if (head === void 0 || head === "") {
|
|
886
|
-
if (segments.length > 0) {
|
|
887
|
-
const val2 = root.fields.get("");
|
|
888
|
-
if (val2 === void 0) return void 0;
|
|
889
|
-
if (tail.length === 0) return val2;
|
|
890
|
-
if (isResObj(val2)) return lookupPath(val2, tail);
|
|
891
|
-
return void 0;
|
|
1138
|
+
relativizeResObj(obj, prefixSegments) {
|
|
1139
|
+
for (const val of obj.fields.values()) {
|
|
1140
|
+
this.relativizeSubstPaths(val, prefixSegments);
|
|
1141
|
+
}
|
|
1142
|
+
for (const val of obj.priorValues.values()) {
|
|
1143
|
+
this.relativizeSubstPaths(val, prefixSegments);
|
|
892
1144
|
}
|
|
893
|
-
return void 0;
|
|
894
1145
|
}
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1146
|
+
};
|
|
1147
|
+
|
|
1148
|
+
// src/internal/resolver/substitution-resolver.ts
|
|
1149
|
+
var SubstitutionResolver = class {
|
|
1150
|
+
constructor(root, opts) {
|
|
1151
|
+
this.root = root;
|
|
1152
|
+
this.opts = opts;
|
|
1153
|
+
this.resolving = /* @__PURE__ */ new Set();
|
|
1154
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
1155
|
+
}
|
|
1156
|
+
resolve() {
|
|
1157
|
+
return this.resolveResObj(this.root);
|
|
1158
|
+
}
|
|
1159
|
+
resolveResObj(obj) {
|
|
1160
|
+
const result = /* @__PURE__ */ new Map();
|
|
1161
|
+
for (const [key, val] of obj.fields) {
|
|
1162
|
+
const resolved = this.resolveVal(val, obj);
|
|
1163
|
+
if (resolved !== void 0) {
|
|
1164
|
+
if (resolved.kind === "object") {
|
|
1165
|
+
const prior = obj.priorValues.get(key);
|
|
1166
|
+
if (prior !== void 0) {
|
|
1167
|
+
const priorResolved = this.resolveVal(prior, obj);
|
|
1168
|
+
if (priorResolved !== void 0 && priorResolved.kind === "object") {
|
|
1169
|
+
result.set(
|
|
1170
|
+
key,
|
|
1171
|
+
deepMergeHoconValues(
|
|
1172
|
+
priorResolved,
|
|
1173
|
+
resolved
|
|
1174
|
+
)
|
|
1175
|
+
);
|
|
1176
|
+
continue;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
result.set(key, resolved);
|
|
1181
|
+
} else {
|
|
1182
|
+
const prior = obj.priorValues.get(key);
|
|
1183
|
+
if (prior !== void 0) {
|
|
1184
|
+
const priorResolved = this.resolveVal(prior, obj);
|
|
1185
|
+
if (priorResolved !== void 0) result.set(key, priorResolved);
|
|
1186
|
+
}
|
|
913
1187
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1188
|
+
}
|
|
1189
|
+
return { kind: "object", fields: result };
|
|
1190
|
+
}
|
|
1191
|
+
resolveVal(v, scope) {
|
|
1192
|
+
if (isSubst(v)) return this.resolveSubst(v, scope);
|
|
1193
|
+
if (isConcat(v)) return this.resolveConcat(v.nodes, scope);
|
|
1194
|
+
if (isAppend(v)) return this.resolveAppend(v, scope);
|
|
1195
|
+
if (isResObj(v)) return this.resolveResObj(v);
|
|
1196
|
+
const hv = v;
|
|
1197
|
+
if (hv.kind === "array") {
|
|
1198
|
+
return {
|
|
1199
|
+
kind: "array",
|
|
1200
|
+
items: hv.items.map(
|
|
1201
|
+
(item) => this.resolveVal(item, scope) ?? { kind: "scalar", raw: "null", valueType: "null" }
|
|
1202
|
+
)
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
return hv;
|
|
1206
|
+
}
|
|
1207
|
+
segmentsEqual(a, b) {
|
|
1208
|
+
return a.length === b.length && a.every((seg, i) => seg === b[i]);
|
|
1209
|
+
}
|
|
1210
|
+
resolveSubst(s, scope) {
|
|
1211
|
+
const key = segmentsToKey(s.segments);
|
|
1212
|
+
if (this.cache.has(key)) return this.cache.get(key);
|
|
1213
|
+
if (this.resolving.has(key)) {
|
|
1214
|
+
let prior;
|
|
1215
|
+
if (s.prefixLen > 0) {
|
|
1216
|
+
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1217
|
+
const parentScope = lookupResObj(
|
|
1218
|
+
this.root,
|
|
1219
|
+
s.segments.slice(0, s.segments.length - 1)
|
|
1220
|
+
);
|
|
1221
|
+
prior = parentScope?.priorValues.get(leafSeg);
|
|
1222
|
+
} else {
|
|
1223
|
+
const rootSeg = s.segments[0] ?? "";
|
|
1224
|
+
prior = scope.priorValues.get(rootSeg) ?? this.root.priorValues.get(rootSeg);
|
|
926
1225
|
}
|
|
927
|
-
|
|
928
|
-
|
|
1226
|
+
if (prior !== void 0) {
|
|
1227
|
+
const saved = this.resolving;
|
|
1228
|
+
this.resolving = new Set(saved);
|
|
1229
|
+
try {
|
|
1230
|
+
return this.resolveVal(prior, scope);
|
|
1231
|
+
} finally {
|
|
1232
|
+
this.resolving = saved;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (s.optional) return void 0;
|
|
1236
|
+
throw new ResolveError(
|
|
1237
|
+
`circular substitution: ${key}`,
|
|
1238
|
+
key,
|
|
1239
|
+
s.line,
|
|
1240
|
+
s.col
|
|
1241
|
+
);
|
|
929
1242
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1243
|
+
this.resolving.add(key);
|
|
1244
|
+
try {
|
|
1245
|
+
const found = lookupPath(this.root, s.segments);
|
|
1246
|
+
if (found !== void 0) {
|
|
1247
|
+
if (isSubst(found) || isConcat(found)) {
|
|
1248
|
+
const isSelfRef = isSubst(found) ? this.segmentsEqual(found.segments, s.segments) : isConcat(found) && found.nodes.some(
|
|
1249
|
+
(n) => isSubst(n) && this.segmentsEqual(n.segments, s.segments)
|
|
1250
|
+
);
|
|
1251
|
+
if (isSelfRef) {
|
|
1252
|
+
let prior;
|
|
1253
|
+
if (s.prefixLen > 0) {
|
|
1254
|
+
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1255
|
+
const parentScope = lookupResObj(
|
|
1256
|
+
this.root,
|
|
1257
|
+
s.segments.slice(0, s.segments.length - 1)
|
|
1258
|
+
);
|
|
1259
|
+
prior = parentScope?.priorValues.get(leafSeg);
|
|
1260
|
+
} else {
|
|
1261
|
+
const rootSeg = s.segments[0] ?? "";
|
|
1262
|
+
prior = scope.priorValues.get(rootSeg) ?? this.root.priorValues.get(rootSeg);
|
|
1263
|
+
}
|
|
1264
|
+
if (prior !== void 0) {
|
|
1265
|
+
const result2 = this.resolveVal(prior, scope);
|
|
1266
|
+
if (result2 !== void 0) this.cache.set(key, result2);
|
|
1267
|
+
return result2;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
let result = this.resolveVal(found, scope);
|
|
1272
|
+
const effectiveLen = s.segments.length - s.prefixLen;
|
|
1273
|
+
if (effectiveLen === 1 && result !== void 0 && result.kind === "object") {
|
|
1274
|
+
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1275
|
+
let prior;
|
|
1276
|
+
if (s.prefixLen > 0) {
|
|
1277
|
+
const parentScope = lookupResObj(
|
|
1278
|
+
this.root,
|
|
1279
|
+
s.segments.slice(0, s.segments.length - 1)
|
|
1280
|
+
);
|
|
1281
|
+
prior = parentScope?.priorValues.get(leafSeg);
|
|
1282
|
+
} else {
|
|
1283
|
+
prior = scope.priorValues.get(leafSeg) ?? this.root.priorValues.get(leafSeg);
|
|
1284
|
+
}
|
|
1285
|
+
if (prior !== void 0) {
|
|
1286
|
+
const priorResolved = this.resolveVal(prior, scope);
|
|
1287
|
+
if (priorResolved !== void 0 && priorResolved.kind === "object") {
|
|
1288
|
+
result = deepMergeHoconValues(
|
|
1289
|
+
priorResolved,
|
|
1290
|
+
result
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (result !== void 0) this.cache.set(key, result);
|
|
1296
|
+
return result;
|
|
1297
|
+
}
|
|
1298
|
+
const envKey = s.segments.join(".");
|
|
1299
|
+
const envVal = this.opts.env[envKey] ?? (s.prefixLen > 0 ? this.opts.env[s.segments.slice(s.prefixLen).join(".")] : void 0);
|
|
1300
|
+
if (envVal !== void 0) {
|
|
1301
|
+
const result = { kind: "scalar", raw: envVal, valueType: "string" };
|
|
1302
|
+
this.cache.set(key, result);
|
|
1303
|
+
return result;
|
|
1304
|
+
}
|
|
1305
|
+
if (s.optional) return void 0;
|
|
1306
|
+
throw new ResolveError(
|
|
1307
|
+
`could not resolve substitution: \${${key}}`,
|
|
1308
|
+
key,
|
|
1309
|
+
s.line,
|
|
1310
|
+
s.col
|
|
1311
|
+
);
|
|
1312
|
+
} finally {
|
|
1313
|
+
this.resolving.delete(key);
|
|
978
1314
|
}
|
|
979
|
-
return makeResObj();
|
|
980
1315
|
}
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
const
|
|
986
|
-
if (
|
|
987
|
-
|
|
988
|
-
|
|
1316
|
+
resolveConcat(nodes, scope) {
|
|
1317
|
+
const resolved = nodes.map((n) => this.resolveVal(n, scope)).filter((v) => v !== void 0);
|
|
1318
|
+
if (resolved.length === 0) return { kind: "scalar", raw: "null", valueType: "null" };
|
|
1319
|
+
if (resolved.length === 1) return resolved[0];
|
|
1320
|
+
const nonSep = resolved.filter((v) => !separatorValues.has(v));
|
|
1321
|
+
if (nonSep.length > 0 && nonSep.every((v) => v.kind === "object")) {
|
|
1322
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1323
|
+
for (const v of nonSep) {
|
|
1324
|
+
if (v.kind === "object") {
|
|
1325
|
+
for (const [k, val] of v.fields) {
|
|
1326
|
+
const existing = merged.get(k);
|
|
1327
|
+
if (existing?.kind === "object" && val.kind === "object") {
|
|
1328
|
+
merged.set(k, deepMergeHoconValues(existing, val));
|
|
1329
|
+
} else {
|
|
1330
|
+
merged.set(k, val);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
return { kind: "object", fields: merged };
|
|
989
1336
|
}
|
|
1337
|
+
if (resolved.some((v) => v.kind === "array")) {
|
|
1338
|
+
const items = [];
|
|
1339
|
+
for (const v of resolved) {
|
|
1340
|
+
if (v.kind === "array") items.push(...v.items);
|
|
1341
|
+
else items.push(v);
|
|
1342
|
+
}
|
|
1343
|
+
return { kind: "array", items };
|
|
1344
|
+
}
|
|
1345
|
+
const str = resolved.map((v) => v.kind === "scalar" ? v.raw : JSON.stringify(v)).join("");
|
|
1346
|
+
return { kind: "scalar", raw: str, valueType: "string" };
|
|
1347
|
+
}
|
|
1348
|
+
resolveAppend(a, scope) {
|
|
1349
|
+
const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
|
|
1350
|
+
const elem = this.resolveVal(a.elem, scope);
|
|
1351
|
+
const items = existing.kind === "array" ? [...existing.items] : [existing];
|
|
1352
|
+
if (elem !== void 0) items.push(elem);
|
|
1353
|
+
return { kind: "array", items };
|
|
990
1354
|
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
1355
|
+
};
|
|
1356
|
+
|
|
1357
|
+
// src/internal/resolver/resolver.ts
|
|
1358
|
+
function resolve2(ast, opts) {
|
|
1359
|
+
const root = new StructureBuilder(opts).build(ast);
|
|
1360
|
+
return new SubstitutionResolver(root, opts).resolve();
|
|
995
1361
|
}
|
|
996
1362
|
|
|
997
1363
|
// src/config.ts
|
|
@@ -1006,39 +1372,36 @@ var Config = class _Config {
|
|
|
1006
1372
|
}
|
|
1007
1373
|
getString(path2) {
|
|
1008
1374
|
const v = this.requireScalar(path2);
|
|
1009
|
-
|
|
1010
|
-
return v;
|
|
1375
|
+
return v.raw;
|
|
1011
1376
|
}
|
|
1012
1377
|
getNumber(path2) {
|
|
1013
1378
|
const v = this.requireScalar(path2);
|
|
1014
|
-
|
|
1015
|
-
if (
|
|
1016
|
-
|
|
1017
|
-
if (coerced !== void 0) return coerced;
|
|
1018
|
-
}
|
|
1019
|
-
throw new ConfigError(`expected number at ${path2}, got ${typeof v}`, path2);
|
|
1379
|
+
const coerced = coerceNumber(v.raw);
|
|
1380
|
+
if (coerced !== void 0) return coerced;
|
|
1381
|
+
throw new ConfigError(`expected number at ${path2}, got ${v.valueType}`, path2);
|
|
1020
1382
|
}
|
|
1021
1383
|
getBoolean(path2) {
|
|
1022
1384
|
const v = this.requireScalar(path2);
|
|
1023
|
-
|
|
1024
|
-
if (
|
|
1025
|
-
|
|
1026
|
-
if (coerced !== void 0) return coerced;
|
|
1027
|
-
}
|
|
1028
|
-
throw new ConfigError(`expected boolean at ${path2}, got ${typeof v}`, path2);
|
|
1385
|
+
const coerced = coerceBoolean(v.raw);
|
|
1386
|
+
if (coerced !== void 0) return coerced;
|
|
1387
|
+
throw new ConfigError(`expected boolean at ${path2}, got ${v.valueType}`, path2);
|
|
1029
1388
|
}
|
|
1030
1389
|
getDuration(path2, unit) {
|
|
1031
1390
|
const v = this.requireScalar(path2);
|
|
1032
|
-
if (
|
|
1033
|
-
|
|
1034
|
-
|
|
1391
|
+
if (v.valueType !== "string" && v.valueType !== "number") {
|
|
1392
|
+
throw new ConfigError(`expected duration at ${path2}, got ${v.valueType}`, path2);
|
|
1393
|
+
}
|
|
1394
|
+
const result = parseDuration(v.raw, unit);
|
|
1395
|
+
if (Number.isNaN(result)) throw new ConfigError(`invalid duration at ${path2}: ${JSON.stringify(v.raw)}`, path2);
|
|
1035
1396
|
return result;
|
|
1036
1397
|
}
|
|
1037
1398
|
getBytes(path2, unit) {
|
|
1038
1399
|
const v = this.requireScalar(path2);
|
|
1039
|
-
if (
|
|
1040
|
-
|
|
1041
|
-
|
|
1400
|
+
if (v.valueType !== "string" && v.valueType !== "number") {
|
|
1401
|
+
throw new ConfigError(`expected byte size at ${path2}, got ${v.valueType}`, path2);
|
|
1402
|
+
}
|
|
1403
|
+
const result = parseBytes(v.raw, unit);
|
|
1404
|
+
if (Number.isNaN(result)) throw new ConfigError(`invalid byte size at ${path2}: ${JSON.stringify(v.raw)}`, path2);
|
|
1042
1405
|
return result;
|
|
1043
1406
|
}
|
|
1044
1407
|
getConfig(path2) {
|
|
@@ -1081,7 +1444,7 @@ var Config = class _Config {
|
|
|
1081
1444
|
const v = this.lookupNode(path2);
|
|
1082
1445
|
if (v === void 0) throw new ConfigError(`path not found: ${path2}`, path2);
|
|
1083
1446
|
if (v.kind !== "scalar") throw new ConfigError(`expected scalar at ${path2}, got ${v.kind}`, path2);
|
|
1084
|
-
return v
|
|
1447
|
+
return v;
|
|
1085
1448
|
}
|
|
1086
1449
|
};
|
|
1087
1450
|
function splitConfigPath(path2) {
|
|
@@ -1123,10 +1486,22 @@ function splitConfigPath(path2) {
|
|
|
1123
1486
|
}
|
|
1124
1487
|
return segments;
|
|
1125
1488
|
}
|
|
1489
|
+
function scalarToJs(raw, valueType) {
|
|
1490
|
+
switch (valueType) {
|
|
1491
|
+
case "null":
|
|
1492
|
+
return null;
|
|
1493
|
+
case "boolean":
|
|
1494
|
+
return raw === "true";
|
|
1495
|
+
case "number":
|
|
1496
|
+
return Number(raw);
|
|
1497
|
+
case "string":
|
|
1498
|
+
return raw;
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1126
1501
|
function hoconToJs(v) {
|
|
1127
1502
|
switch (v.kind) {
|
|
1128
1503
|
case "scalar":
|
|
1129
|
-
return v.
|
|
1504
|
+
return scalarToJs(v.raw, v.valueType);
|
|
1130
1505
|
case "array":
|
|
1131
1506
|
return v.items.map(hoconToJs);
|
|
1132
1507
|
case "object": {
|