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