@o3co/ts.hocon 1.1.0 → 1.2.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/README.ja.md +10 -4
- package/README.md +18 -4
- package/dist/index.cjs +325 -162
- package/dist/index.js +325 -162
- package/dist/zod.cjs +325 -162
- package/dist/zod.js +325 -162
- package/package.json +4 -4
package/dist/zod.cjs
CHANGED
|
@@ -205,6 +205,20 @@ var ConfigError = class extends Error {
|
|
|
205
205
|
};
|
|
206
206
|
|
|
207
207
|
// src/internal/lexer/lexer.ts
|
|
208
|
+
function isHoconWhitespace(ch) {
|
|
209
|
+
const cp = ch.codePointAt(0) ?? -1;
|
|
210
|
+
if (cp === 9 || cp === 10 || cp === 11 || cp === 12 || cp === 13) return true;
|
|
211
|
+
if (cp >= 28 && cp <= 31) return true;
|
|
212
|
+
if (cp === 32 || cp === 160 || cp === 65279) return true;
|
|
213
|
+
if (cp === 5760) return true;
|
|
214
|
+
if (cp >= 8192 && cp <= 8202) return true;
|
|
215
|
+
if (cp === 8232 || cp === 8233 || cp === 8239 || cp === 8287) return true;
|
|
216
|
+
if (cp === 12288) return true;
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
function isHoconNewline(ch) {
|
|
220
|
+
return ch === "\n";
|
|
221
|
+
}
|
|
208
222
|
var SINGLE_CHAR_TOKENS = {
|
|
209
223
|
"{": "lbrace",
|
|
210
224
|
"}": "rbrace",
|
|
@@ -227,18 +241,18 @@ var Lexer = class {
|
|
|
227
241
|
while (this.pos < this.input.length) {
|
|
228
242
|
const sl = this.line, sc = this.col;
|
|
229
243
|
const ch = this.peek();
|
|
230
|
-
if (ch
|
|
231
|
-
this.advance();
|
|
232
|
-
this.hadSpace = true;
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
if (ch === "\n") {
|
|
244
|
+
if (isHoconNewline(ch)) {
|
|
236
245
|
this.advance();
|
|
237
246
|
if (this.tokens[this.tokens.length - 1]?.kind !== "newline") {
|
|
238
247
|
this.push("newline", "\n", sl, sc);
|
|
239
248
|
}
|
|
240
249
|
continue;
|
|
241
250
|
}
|
|
251
|
+
if (isHoconWhitespace(ch)) {
|
|
252
|
+
this.advance();
|
|
253
|
+
this.hadSpace = true;
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
242
256
|
if (ch === "/" && this.peek(1) === "/") {
|
|
243
257
|
while (this.pos < this.input.length && this.peek() !== "\n") this.advance();
|
|
244
258
|
this.hadSpace = true;
|
|
@@ -268,16 +282,16 @@ var Lexer = class {
|
|
|
268
282
|
if (ch === "$" && this.peek(1) === "{") {
|
|
269
283
|
this.advance();
|
|
270
284
|
this.advance();
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
this.
|
|
285
|
+
const payload = this.parseSubstBody(sl, sc);
|
|
286
|
+
const value = payload.segments.map((s) => {
|
|
287
|
+
const t = s.text;
|
|
288
|
+
if (t === "" || t.includes(".") || t.includes(" ") || t.includes(" ") || t.includes('"') || t.includes("\\") || t !== t.trim()) {
|
|
289
|
+
const escaped = t.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
290
|
+
return `"${escaped}"`;
|
|
291
|
+
}
|
|
292
|
+
return t;
|
|
293
|
+
}).join(".");
|
|
294
|
+
this.pushSubst(payload, value, sl, sc);
|
|
281
295
|
continue;
|
|
282
296
|
}
|
|
283
297
|
if (ch === '"' && this.peek(1) === '"' && this.peek(2) === '"') {
|
|
@@ -312,62 +326,19 @@ var Lexer = class {
|
|
|
312
326
|
}
|
|
313
327
|
if (ch === '"') {
|
|
314
328
|
this.advance();
|
|
315
|
-
|
|
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;
|
|
357
|
-
}
|
|
358
|
-
default:
|
|
359
|
-
throw new ParseError(`Unknown escape sequence: \\${esc}`, this.line, this.col);
|
|
360
|
-
}
|
|
361
|
-
} else {
|
|
362
|
-
value += this.advance();
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
if (this.peek() !== '"') throw new ParseError("unterminated string", sl, sc);
|
|
366
|
-
this.advance();
|
|
329
|
+
const value = this.readQuotedStringBody(sl, sc);
|
|
367
330
|
this.push("string", value, sl, sc, true);
|
|
368
331
|
continue;
|
|
369
332
|
}
|
|
370
333
|
if (isUnquotedStart(ch)) {
|
|
334
|
+
if (ch === "-" && !isDecimalDigit(this.peek(1))) {
|
|
335
|
+
const after = this.peek(1) === "" ? "EOF" : JSON.stringify(this.peek(1));
|
|
336
|
+
throw new ParseError(
|
|
337
|
+
`unquoted string cannot begin with '-' unless followed by a digit (got '-' then ${after}, HOCON.md L270-276)`,
|
|
338
|
+
sl,
|
|
339
|
+
sc
|
|
340
|
+
);
|
|
341
|
+
}
|
|
371
342
|
let value = "";
|
|
372
343
|
while (this.pos < this.input.length && isUnquotedContinue(this.peek(), () => this.peek(1))) {
|
|
373
344
|
value += this.advance();
|
|
@@ -397,22 +368,201 @@ var Lexer = class {
|
|
|
397
368
|
this.tokens.push({ kind, value, line: l, col: c, isQuoted, precedingSpace: this.hadSpace });
|
|
398
369
|
this.hadSpace = false;
|
|
399
370
|
}
|
|
371
|
+
pushSubst(payload, value, l, c) {
|
|
372
|
+
this.tokens.push({ kind: "subst", value, line: l, col: c, isQuoted: false, precedingSpace: this.hadSpace, subst: payload });
|
|
373
|
+
this.hadSpace = false;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Read body of a quoted string. Opening `"` must already be consumed.
|
|
377
|
+
* Returns decoded string. Throws ParseError on unterminated/invalid-escape.
|
|
378
|
+
* openLine/openCol are the position of the opening `"` for error reporting.
|
|
379
|
+
*/
|
|
380
|
+
readQuotedStringBody(openLine, openCol) {
|
|
381
|
+
let value = "";
|
|
382
|
+
while (this.pos < this.input.length && this.peek() !== '"') {
|
|
383
|
+
if (this.peek() === "\n") throw new ParseError("unterminated string", openLine, openCol);
|
|
384
|
+
if (this.peek() === "\\") {
|
|
385
|
+
const escCol = this.col;
|
|
386
|
+
this.advance();
|
|
387
|
+
if (this.pos >= this.input.length) {
|
|
388
|
+
throw new ParseError("unterminated string", openLine, openCol);
|
|
389
|
+
}
|
|
390
|
+
const esc = this.advance();
|
|
391
|
+
switch (esc) {
|
|
392
|
+
case "n":
|
|
393
|
+
value += "\n";
|
|
394
|
+
break;
|
|
395
|
+
case "t":
|
|
396
|
+
value += " ";
|
|
397
|
+
break;
|
|
398
|
+
case "r":
|
|
399
|
+
value += "\r";
|
|
400
|
+
break;
|
|
401
|
+
case '"':
|
|
402
|
+
value += '"';
|
|
403
|
+
break;
|
|
404
|
+
case "\\":
|
|
405
|
+
value += "\\";
|
|
406
|
+
break;
|
|
407
|
+
case "/":
|
|
408
|
+
value += "/";
|
|
409
|
+
break;
|
|
410
|
+
case "b":
|
|
411
|
+
value += "\b";
|
|
412
|
+
break;
|
|
413
|
+
case "f":
|
|
414
|
+
value += "\f";
|
|
415
|
+
break;
|
|
416
|
+
case "u": {
|
|
417
|
+
if (this.pos + 4 > this.input.length) {
|
|
418
|
+
throw new ParseError("invalid unicode escape", openLine, escCol);
|
|
419
|
+
}
|
|
420
|
+
const hex = this.input.slice(this.pos, this.pos + 4);
|
|
421
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
422
|
+
throw new ParseError("invalid unicode escape", openLine, escCol);
|
|
423
|
+
}
|
|
424
|
+
const code = parseInt(hex, 16);
|
|
425
|
+
value += String.fromCharCode(code);
|
|
426
|
+
for (let i = 0; i < 4; i++) this.advance();
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
default:
|
|
430
|
+
throw new ParseError(`unknown escape sequence: \\${esc}`, openLine, escCol);
|
|
431
|
+
}
|
|
432
|
+
} else {
|
|
433
|
+
value += this.advance();
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (this.pos >= this.input.length || this.peek() !== '"') {
|
|
437
|
+
throw new ParseError("unterminated string", openLine, openCol);
|
|
438
|
+
}
|
|
439
|
+
this.advance();
|
|
440
|
+
return value;
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Parse the body of a `${...}` substitution (called after `${` has been consumed).
|
|
444
|
+
* Implements Appendix A state machine from the cross-language spec.
|
|
445
|
+
*/
|
|
446
|
+
parseSubstBody(startLine, startCol) {
|
|
447
|
+
let optional = false;
|
|
448
|
+
if (this.peek() === "?") {
|
|
449
|
+
this.advance();
|
|
450
|
+
optional = true;
|
|
451
|
+
}
|
|
452
|
+
let curText = "";
|
|
453
|
+
let curStarted = false;
|
|
454
|
+
let curLine = 0;
|
|
455
|
+
let curCol = 0;
|
|
456
|
+
let pendingWs = "";
|
|
457
|
+
const segments = [];
|
|
458
|
+
let lastDot = null;
|
|
459
|
+
while (true) {
|
|
460
|
+
if (this.pos >= this.input.length) {
|
|
461
|
+
throw new ParseError("unterminated substitution", startLine, startCol);
|
|
462
|
+
}
|
|
463
|
+
const ch = this.peek();
|
|
464
|
+
if (ch === "}") {
|
|
465
|
+
this.advance();
|
|
466
|
+
pendingWs = "";
|
|
467
|
+
break;
|
|
468
|
+
} else if (ch === '"') {
|
|
469
|
+
const qLine = startLine;
|
|
470
|
+
const qCol = this.col;
|
|
471
|
+
if (curStarted) {
|
|
472
|
+
curText += pendingWs;
|
|
473
|
+
}
|
|
474
|
+
pendingWs = "";
|
|
475
|
+
this.advance();
|
|
476
|
+
const decoded = this.readQuotedStringBody(qLine, qCol);
|
|
477
|
+
curText += decoded;
|
|
478
|
+
if (!curStarted) {
|
|
479
|
+
curLine = qLine;
|
|
480
|
+
curCol = qCol;
|
|
481
|
+
curStarted = true;
|
|
482
|
+
}
|
|
483
|
+
} else if (isUnquotedSubstChar(ch)) {
|
|
484
|
+
if (ch === "-" && !curStarted && !isDecimalDigit(this.peek(1))) {
|
|
485
|
+
const after = this.peek(1) === "" ? "EOF" : JSON.stringify(this.peek(1));
|
|
486
|
+
throw new ParseError(
|
|
487
|
+
`unquoted path segment cannot begin with '-' unless followed by a digit (got '-' then ${after}, HOCON.md L270-276)`,
|
|
488
|
+
startLine,
|
|
489
|
+
this.col
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
const uCol = this.col;
|
|
493
|
+
if (curStarted) {
|
|
494
|
+
curText += pendingWs;
|
|
495
|
+
}
|
|
496
|
+
pendingWs = "";
|
|
497
|
+
if (!curStarted) {
|
|
498
|
+
curLine = startLine;
|
|
499
|
+
curCol = uCol;
|
|
500
|
+
curStarted = true;
|
|
501
|
+
}
|
|
502
|
+
while (this.pos < this.input.length && isUnquotedSubstChar(this.peek())) {
|
|
503
|
+
curText += this.advance();
|
|
504
|
+
}
|
|
505
|
+
} else if (ch === ".") {
|
|
506
|
+
const dotCol = this.col;
|
|
507
|
+
pendingWs = "";
|
|
508
|
+
if (!curStarted) {
|
|
509
|
+
throw new ParseError("empty segment in path", startLine, dotCol);
|
|
510
|
+
}
|
|
511
|
+
segments.push({ text: curText, line: curLine, col: curCol });
|
|
512
|
+
curText = "";
|
|
513
|
+
curStarted = false;
|
|
514
|
+
curLine = 0;
|
|
515
|
+
curCol = 0;
|
|
516
|
+
lastDot = [startLine, dotCol];
|
|
517
|
+
this.advance();
|
|
518
|
+
} else if (isHoconNewline(ch)) {
|
|
519
|
+
throw new ParseError("unterminated substitution", startLine, startCol);
|
|
520
|
+
} else if (isHoconWhitespace(ch)) {
|
|
521
|
+
pendingWs += ch;
|
|
522
|
+
this.advance();
|
|
523
|
+
} else {
|
|
524
|
+
throw new ParseError(`unexpected character in substitution path: ${JSON.stringify(ch)}`, startLine, this.col);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (curStarted) {
|
|
528
|
+
segments.push({ text: curText, line: curLine, col: curCol });
|
|
529
|
+
} else if (segments.length === 0) {
|
|
530
|
+
throw new ParseError("empty substitution path", startLine, startCol);
|
|
531
|
+
} else {
|
|
532
|
+
const [errLine, errCol] = lastDot ?? [startLine, startCol];
|
|
533
|
+
throw new ParseError("empty segment in path", errLine, errCol);
|
|
534
|
+
}
|
|
535
|
+
return { segments, optional };
|
|
536
|
+
}
|
|
400
537
|
};
|
|
401
538
|
function tokenize(input) {
|
|
402
539
|
return new Lexer(input).tokenize();
|
|
403
540
|
}
|
|
541
|
+
function isUnquotedSubstChar(ch) {
|
|
542
|
+
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
543
|
+
if ('"\\'.includes(ch)) return false;
|
|
544
|
+
if ("{}[]".includes(ch)) return false;
|
|
545
|
+
if (":=,+#`^?!@*&$.".includes(ch)) return false;
|
|
546
|
+
return true;
|
|
547
|
+
}
|
|
548
|
+
function isDecimalDigit(ch) {
|
|
549
|
+
return ch >= "0" && ch <= "9";
|
|
550
|
+
}
|
|
404
551
|
function isUnquotedStart(ch) {
|
|
405
|
-
|
|
552
|
+
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
553
|
+
if ('{}[],:=+#"$?!@*&^\\'.includes(ch)) return false;
|
|
554
|
+
return true;
|
|
406
555
|
}
|
|
407
556
|
function isUnquotedContinue(ch, nextFn) {
|
|
408
|
-
if (ch === "" ||
|
|
557
|
+
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
558
|
+
if ('{}[],:=#"$?!@*&^\\'.includes(ch)) return false;
|
|
409
559
|
if (ch === "+" && nextFn() === "=") return false;
|
|
410
560
|
if (ch === "/" && nextFn() === "/") return false;
|
|
411
561
|
return true;
|
|
412
562
|
}
|
|
413
563
|
|
|
414
564
|
// src/internal/parser/parser.ts
|
|
415
|
-
var EOF_TOKEN = { kind: "eof", value: "", line: 0, col: 0, isQuoted: false, precedingSpace: false };
|
|
565
|
+
var EOF_TOKEN = { kind: "eof", value: "", line: 0, col: 0, isQuoted: false, precedingSpace: false, subst: void 0 };
|
|
416
566
|
var Parser = class {
|
|
417
567
|
constructor(tokens) {
|
|
418
568
|
this.tokens = tokens;
|
|
@@ -516,10 +666,21 @@ var Parser = class {
|
|
|
516
666
|
trailingDot = false;
|
|
517
667
|
} else if (t.kind === "unquoted") {
|
|
518
668
|
this.advance();
|
|
519
|
-
const
|
|
669
|
+
const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
|
|
670
|
+
const parts = raw.split(".");
|
|
520
671
|
const filtered = parts.filter((s) => s.length > 0);
|
|
672
|
+
for (const seg of filtered) {
|
|
673
|
+
if (seg[0] === "-" && !(seg.length >= 2 && seg[1] >= "0" && seg[1] <= "9")) {
|
|
674
|
+
const after = seg.length >= 2 ? JSON.stringify(seg[1]) : "EOF";
|
|
675
|
+
throw new ParseError(
|
|
676
|
+
`unquoted key segment cannot begin with '-' unless followed by a digit (got '-' then ${after} in ${JSON.stringify(seg)}, HOCON.md L270-276)`,
|
|
677
|
+
t.line,
|
|
678
|
+
t.col
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
521
682
|
segments.push(...filtered);
|
|
522
|
-
trailingDot =
|
|
683
|
+
trailingDot = raw.endsWith(".");
|
|
523
684
|
} else {
|
|
524
685
|
if (segments.length === 0) throw new ParseError(`expected key, got ${t.kind}`, t.line, t.col);
|
|
525
686
|
break;
|
|
@@ -531,6 +692,10 @@ var Parser = class {
|
|
|
531
692
|
trailingDot = true;
|
|
532
693
|
continue;
|
|
533
694
|
}
|
|
695
|
+
if (next.kind === "unquoted" && next.value.startsWith(".") && !next.precedingSpace) {
|
|
696
|
+
trailingDot = true;
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
534
699
|
break;
|
|
535
700
|
}
|
|
536
701
|
return segments;
|
|
@@ -606,9 +771,13 @@ var Parser = class {
|
|
|
606
771
|
} else if (t.kind === "lbracket") {
|
|
607
772
|
this.advance();
|
|
608
773
|
node = this.parseArray();
|
|
609
|
-
} else if (t.kind === "subst"
|
|
774
|
+
} else if (t.kind === "subst") {
|
|
610
775
|
this.advance();
|
|
611
|
-
|
|
776
|
+
if (!t.subst) {
|
|
777
|
+
throw new ParseError(`internal: subst token missing payload`, t.line, t.col);
|
|
778
|
+
}
|
|
779
|
+
const payload = t.subst;
|
|
780
|
+
node = { kind: "subst", segments: payload.segments, optional: payload.optional, pos: { line: t.line, col: t.col } };
|
|
612
781
|
} else if (t.kind === "string" || t.kind === "triple_string") {
|
|
613
782
|
this.advance();
|
|
614
783
|
node = { kind: "scalar", raw: t.value, valueType: "string", pos: { line: t.line, col: t.col } };
|
|
@@ -678,64 +847,20 @@ function makeResObj() {
|
|
|
678
847
|
}
|
|
679
848
|
|
|
680
849
|
// 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++;
|
|
711
|
-
}
|
|
712
|
-
segments.push(seg.trim());
|
|
713
|
-
if (i < raw.length && raw[i] === ".") i++;
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
return segments;
|
|
717
|
-
}
|
|
718
850
|
function segmentsToKey(segments) {
|
|
719
851
|
return segments.map((s) => {
|
|
720
|
-
|
|
721
|
-
|
|
852
|
+
const t = s.text;
|
|
853
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
854
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
722
855
|
}
|
|
723
|
-
return
|
|
856
|
+
return t;
|
|
724
857
|
}).join(".");
|
|
725
858
|
}
|
|
726
859
|
function lookupPath(root, segments) {
|
|
727
860
|
const [head, ...tail] = segments;
|
|
728
|
-
if (head === void 0
|
|
729
|
-
|
|
730
|
-
|
|
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);
|
|
861
|
+
if (head === void 0) return void 0;
|
|
862
|
+
const key = head.text;
|
|
863
|
+
const val = root.fields.get(key);
|
|
739
864
|
if (val === void 0) return void 0;
|
|
740
865
|
if (tail.length === 0) return val;
|
|
741
866
|
if (isResObj(val)) return lookupPath(val, tail);
|
|
@@ -744,7 +869,7 @@ function lookupPath(root, segments) {
|
|
|
744
869
|
function lookupResObj(root, segments) {
|
|
745
870
|
let cur = root;
|
|
746
871
|
for (const seg of segments) {
|
|
747
|
-
const val = cur.fields.get(seg);
|
|
872
|
+
const val = cur.fields.get(seg.text);
|
|
748
873
|
if (val === void 0 || !isResObj(val)) return void 0;
|
|
749
874
|
cur = val;
|
|
750
875
|
}
|
|
@@ -1021,7 +1146,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1021
1146
|
return;
|
|
1022
1147
|
}
|
|
1023
1148
|
const [head, ...tail] = field.key;
|
|
1024
|
-
if (
|
|
1149
|
+
if (head === void 0) return;
|
|
1025
1150
|
if (tail.length > 0) {
|
|
1026
1151
|
const syntheticAst = {
|
|
1027
1152
|
kind: "object",
|
|
@@ -1063,7 +1188,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1063
1188
|
return;
|
|
1064
1189
|
}
|
|
1065
1190
|
const [head, ...tail] = field.key;
|
|
1066
|
-
if (
|
|
1191
|
+
if (head === void 0) return;
|
|
1067
1192
|
if (tail.length > 0) {
|
|
1068
1193
|
const syntheticAst = {
|
|
1069
1194
|
kind: "object",
|
|
@@ -1109,7 +1234,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1109
1234
|
return inner;
|
|
1110
1235
|
}
|
|
1111
1236
|
case "subst":
|
|
1112
|
-
return { _kind: "subst-placeholder", segments:
|
|
1237
|
+
return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
|
|
1113
1238
|
case "concat":
|
|
1114
1239
|
return { _kind: "concat-placeholder", nodes: ast.nodes.map((n) => this.astToResolverValue(n, pathPrefix)) };
|
|
1115
1240
|
case "include":
|
|
@@ -1133,7 +1258,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1133
1258
|
case "object":
|
|
1134
1259
|
return await this.buildAsync(ast, pathPrefix);
|
|
1135
1260
|
case "subst":
|
|
1136
|
-
return { _kind: "subst-placeholder", segments:
|
|
1261
|
+
return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
|
|
1137
1262
|
case "concat": {
|
|
1138
1263
|
const nodes = [];
|
|
1139
1264
|
for (const n of ast.nodes) {
|
|
@@ -1148,7 +1273,8 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1148
1273
|
// ---- Relativize substitution paths for nested includes ----
|
|
1149
1274
|
relativizeSubstPaths(val, prefixSegments) {
|
|
1150
1275
|
if (isSubst(val)) {
|
|
1151
|
-
|
|
1276
|
+
const prefixAsSegments = prefixSegments.map((text) => ({ text, line: 0, col: 0 }));
|
|
1277
|
+
val.segments = [...prefixAsSegments, ...val.segments];
|
|
1152
1278
|
val.prefixLen += prefixSegments.length;
|
|
1153
1279
|
return;
|
|
1154
1280
|
}
|
|
@@ -1184,6 +1310,23 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1184
1310
|
}
|
|
1185
1311
|
};
|
|
1186
1312
|
|
|
1313
|
+
// src/value/numeric-array.ts
|
|
1314
|
+
function numericObjectToArray(value) {
|
|
1315
|
+
if (value.kind !== "object") return null;
|
|
1316
|
+
if (value.fields.size === 0) return null;
|
|
1317
|
+
const canonicalInt = /^(0|[1-9][0-9]*)$/;
|
|
1318
|
+
const eligible = [];
|
|
1319
|
+
for (const [key, val] of value.fields) {
|
|
1320
|
+
if (!canonicalInt.test(key)) continue;
|
|
1321
|
+
const n = Number(key);
|
|
1322
|
+
if (!Number.isInteger(n) || n > 2147483647) continue;
|
|
1323
|
+
eligible.push({ n, v: val });
|
|
1324
|
+
}
|
|
1325
|
+
if (eligible.length === 0) return null;
|
|
1326
|
+
eligible.sort((a, b) => a.n - b.n);
|
|
1327
|
+
return eligible.map((e) => e.v);
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1187
1330
|
// src/internal/resolver/substitution-resolver.ts
|
|
1188
1331
|
var SubstitutionResolver = class {
|
|
1189
1332
|
constructor(root, opts) {
|
|
@@ -1244,7 +1387,7 @@ var SubstitutionResolver = class {
|
|
|
1244
1387
|
return hv;
|
|
1245
1388
|
}
|
|
1246
1389
|
segmentsEqual(a, b) {
|
|
1247
|
-
return a.length === b.length && a.every((seg, i) => seg === b[i]);
|
|
1390
|
+
return a.length === b.length && a.every((seg, i) => seg.text === b[i]?.text);
|
|
1248
1391
|
}
|
|
1249
1392
|
resolveSubst(s, scope) {
|
|
1250
1393
|
const key = segmentsToKey(s.segments);
|
|
@@ -1252,14 +1395,14 @@ var SubstitutionResolver = class {
|
|
|
1252
1395
|
if (this.resolving.has(key)) {
|
|
1253
1396
|
let prior;
|
|
1254
1397
|
if (s.prefixLen > 0) {
|
|
1255
|
-
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1398
|
+
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
1256
1399
|
const parentScope = lookupResObj(
|
|
1257
1400
|
this.root,
|
|
1258
1401
|
s.segments.slice(0, s.segments.length - 1)
|
|
1259
1402
|
);
|
|
1260
1403
|
prior = parentScope?.priorValues.get(leafSeg);
|
|
1261
1404
|
} else {
|
|
1262
|
-
const rootSeg = s.segments[0] ?? "";
|
|
1405
|
+
const rootSeg = s.segments[0]?.text ?? "";
|
|
1263
1406
|
prior = scope.priorValues.get(rootSeg) ?? this.root.priorValues.get(rootSeg);
|
|
1264
1407
|
}
|
|
1265
1408
|
if (prior !== void 0) {
|
|
@@ -1290,14 +1433,14 @@ var SubstitutionResolver = class {
|
|
|
1290
1433
|
if (isSelfRef) {
|
|
1291
1434
|
let prior;
|
|
1292
1435
|
if (s.prefixLen > 0) {
|
|
1293
|
-
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1436
|
+
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
1294
1437
|
const parentScope = lookupResObj(
|
|
1295
1438
|
this.root,
|
|
1296
1439
|
s.segments.slice(0, s.segments.length - 1)
|
|
1297
1440
|
);
|
|
1298
1441
|
prior = parentScope?.priorValues.get(leafSeg);
|
|
1299
1442
|
} else {
|
|
1300
|
-
const rootSeg = s.segments[0] ?? "";
|
|
1443
|
+
const rootSeg = s.segments[0]?.text ?? "";
|
|
1301
1444
|
prior = scope.priorValues.get(rootSeg) ?? this.root.priorValues.get(rootSeg);
|
|
1302
1445
|
}
|
|
1303
1446
|
if (prior !== void 0) {
|
|
@@ -1310,7 +1453,7 @@ var SubstitutionResolver = class {
|
|
|
1310
1453
|
let result = this.resolveVal(found, scope);
|
|
1311
1454
|
const effectiveLen = s.segments.length - s.prefixLen;
|
|
1312
1455
|
if (effectiveLen === 1 && result !== void 0 && result.kind === "object") {
|
|
1313
|
-
const leafSeg = s.segments[s.segments.length - 1] ?? "";
|
|
1456
|
+
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
1314
1457
|
let prior;
|
|
1315
1458
|
if (s.prefixLen > 0) {
|
|
1316
1459
|
const parentScope = lookupResObj(
|
|
@@ -1334,8 +1477,8 @@ var SubstitutionResolver = class {
|
|
|
1334
1477
|
if (result !== void 0) this.cache.set(key, result);
|
|
1335
1478
|
return result;
|
|
1336
1479
|
}
|
|
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);
|
|
1480
|
+
const envKey = s.segments.map((seg) => seg.text).join(".");
|
|
1481
|
+
const envVal = this.opts.env[envKey] ?? (s.prefixLen > 0 ? this.opts.env[s.segments.slice(s.prefixLen).map((seg) => seg.text).join(".")] : void 0);
|
|
1339
1482
|
if (envVal !== void 0) {
|
|
1340
1483
|
const result = { kind: "scalar", raw: envVal, valueType: "string" };
|
|
1341
1484
|
this.cache.set(key, result);
|
|
@@ -1357,32 +1500,48 @@ var SubstitutionResolver = class {
|
|
|
1357
1500
|
if (resolved.length === 0) return { kind: "scalar", raw: "null", valueType: "null" };
|
|
1358
1501
|
if (resolved.length === 1) return resolved[0];
|
|
1359
1502
|
const nonSep = resolved.filter((v) => !separatorValues.has(v));
|
|
1360
|
-
if (nonSep.length
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}
|
|
1503
|
+
if (nonSep.length === 0) {
|
|
1504
|
+
const str = resolved.map((v) => v.kind === "scalar" ? v.raw : JSON.stringify(v)).join("");
|
|
1505
|
+
return { kind: "scalar", raw: str, valueType: "string" };
|
|
1506
|
+
}
|
|
1507
|
+
const joinPair = (left, right) => {
|
|
1508
|
+
if (left.kind === "object" && right.kind === "object") {
|
|
1509
|
+
return deepMergeHoconValues(left, right);
|
|
1510
|
+
}
|
|
1511
|
+
if (left.kind === "array" && right.kind === "object") {
|
|
1512
|
+
const converted = numericObjectToArray(right);
|
|
1513
|
+
if (converted !== null) {
|
|
1514
|
+
return { kind: "array", items: [...left.items, ...converted] };
|
|
1372
1515
|
}
|
|
1516
|
+
return { kind: "array", items: [...left.items, right] };
|
|
1373
1517
|
}
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1518
|
+
if (left.kind === "object" && right.kind === "array") {
|
|
1519
|
+
const converted = numericObjectToArray(left);
|
|
1520
|
+
if (converted !== null) {
|
|
1521
|
+
return { kind: "array", items: [...converted, ...right.items] };
|
|
1522
|
+
}
|
|
1523
|
+
return { kind: "array", items: [left, ...right.items] };
|
|
1524
|
+
}
|
|
1525
|
+
if (left.kind === "array" && right.kind === "array") {
|
|
1526
|
+
return { kind: "array", items: [...left.items, ...right.items] };
|
|
1527
|
+
}
|
|
1528
|
+
if (left.kind === "array") {
|
|
1529
|
+
return { kind: "array", items: [...left.items, right] };
|
|
1381
1530
|
}
|
|
1382
|
-
|
|
1531
|
+
if (right.kind === "array") {
|
|
1532
|
+
return { kind: "array", items: [left, ...right.items] };
|
|
1533
|
+
}
|
|
1534
|
+
const leftStr = left.kind === "scalar" ? left.raw : JSON.stringify(left);
|
|
1535
|
+
const rightStr = right.kind === "scalar" ? right.raw : JSON.stringify(right);
|
|
1536
|
+
return { kind: "scalar", raw: leftStr + rightStr, valueType: "string" };
|
|
1537
|
+
};
|
|
1538
|
+
const [head, ...tail] = nonSep;
|
|
1539
|
+
const folded = tail.reduce(joinPair, head);
|
|
1540
|
+
if (folded.kind === "scalar") {
|
|
1541
|
+
const str = resolved.map((v) => v.kind === "scalar" ? v.raw : JSON.stringify(v)).join("");
|
|
1542
|
+
return { kind: "scalar", raw: str, valueType: "string" };
|
|
1383
1543
|
}
|
|
1384
|
-
|
|
1385
|
-
return { kind: "scalar", raw: str, valueType: "string" };
|
|
1544
|
+
return folded;
|
|
1386
1545
|
}
|
|
1387
1546
|
resolveAppend(a, scope) {
|
|
1388
1547
|
const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
|
|
@@ -1452,6 +1611,10 @@ var Config = class _Config {
|
|
|
1452
1611
|
getList(path2) {
|
|
1453
1612
|
const v = this.lookupNode(path2);
|
|
1454
1613
|
if (v === void 0) throw new ConfigError(`path not found: ${path2}`, path2);
|
|
1614
|
+
if (v.kind === "object") {
|
|
1615
|
+
const converted = numericObjectToArray(v);
|
|
1616
|
+
if (converted !== null) return converted.map(hoconToJs);
|
|
1617
|
+
}
|
|
1455
1618
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
|
|
1456
1619
|
return v.items.map(hoconToJs);
|
|
1457
1620
|
}
|