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