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