@o3co/ts.hocon 1.4.1 → 1.5.2
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 +313 -20
- package/dist/index.js +313 -20
- package/dist/zod.cjs +313 -20
- package/dist/zod.js +313 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -286,6 +286,192 @@ function mergeUnresolved(receiver, fallback) {
|
|
|
286
286
|
return result;
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
// src/internal/resolver/fold-self-ref.ts
|
|
290
|
+
function substFullKey(s) {
|
|
291
|
+
return stringSegmentsToKey(s.segments.map((seg) => seg.text));
|
|
292
|
+
}
|
|
293
|
+
function stringSegmentsToKey(segments) {
|
|
294
|
+
return segments.map((t) => {
|
|
295
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
296
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
297
|
+
}
|
|
298
|
+
return t;
|
|
299
|
+
}).join(".");
|
|
300
|
+
}
|
|
301
|
+
function containsSelfRef(v, fullKey) {
|
|
302
|
+
if (isSubst(v)) return substFullKey(v) === fullKey;
|
|
303
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
|
|
304
|
+
if (isAppend(v)) {
|
|
305
|
+
return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
|
|
306
|
+
}
|
|
307
|
+
if (isResObj(v)) {
|
|
308
|
+
for (const f of v.fields.values()) {
|
|
309
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
const hv = v;
|
|
314
|
+
if (hv.kind === "array") {
|
|
315
|
+
return hv.items.some((item) => containsSelfRef(item, fullKey));
|
|
316
|
+
}
|
|
317
|
+
if (hv.kind === "object") {
|
|
318
|
+
for (const f of hv.fields.values()) {
|
|
319
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
320
|
+
}
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
function foldSelfRef(v, fullKey, replacement) {
|
|
326
|
+
if (isSubst(v)) {
|
|
327
|
+
return substFullKey(v) === fullKey ? replacement : v;
|
|
328
|
+
}
|
|
329
|
+
if (isConcat(v)) {
|
|
330
|
+
return {
|
|
331
|
+
_kind: "concat-placeholder",
|
|
332
|
+
nodes: v.nodes.map((n) => foldSelfRef(n, fullKey, replacement)),
|
|
333
|
+
line: v.line,
|
|
334
|
+
col: v.col
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
if (isAppend(v)) {
|
|
338
|
+
return {
|
|
339
|
+
_kind: "append-placeholder",
|
|
340
|
+
existing: foldSelfRef(v.existing, fullKey, replacement),
|
|
341
|
+
elem: foldSelfRef(v.elem, fullKey, replacement)
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
if (isResObj(v)) {
|
|
345
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
346
|
+
for (const [k, val] of v.fields) {
|
|
347
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
348
|
+
}
|
|
349
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
350
|
+
}
|
|
351
|
+
const hv = v;
|
|
352
|
+
if (hv.kind === "array") {
|
|
353
|
+
return {
|
|
354
|
+
kind: "array",
|
|
355
|
+
items: hv.items.map((item) => foldSelfRef(item, fullKey, replacement))
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
if (hv.kind === "object") {
|
|
359
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
360
|
+
for (const [k, val] of hv.fields) {
|
|
361
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
362
|
+
}
|
|
363
|
+
return { kind: "object", fields: newFields };
|
|
364
|
+
}
|
|
365
|
+
return v;
|
|
366
|
+
}
|
|
367
|
+
function foldOrSkipPrior(prior, fullKey, old) {
|
|
368
|
+
if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
|
|
369
|
+
if (old === void 0) return void 0;
|
|
370
|
+
return foldSelfRef(prior, fullKey, old);
|
|
371
|
+
}
|
|
372
|
+
function cloneResolverValue(v) {
|
|
373
|
+
if (isSubst(v)) {
|
|
374
|
+
return {
|
|
375
|
+
_kind: "subst-placeholder",
|
|
376
|
+
segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
|
|
377
|
+
optional: v.optional,
|
|
378
|
+
listSuffix: v.listSuffix,
|
|
379
|
+
line: v.line,
|
|
380
|
+
col: v.col,
|
|
381
|
+
prefixLen: v.prefixLen
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
if (isConcat(v)) {
|
|
385
|
+
return {
|
|
386
|
+
_kind: "concat-placeholder",
|
|
387
|
+
nodes: v.nodes.map((n) => cloneResolverValue(n)),
|
|
388
|
+
line: v.line,
|
|
389
|
+
col: v.col
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
if (isResObj(v)) {
|
|
393
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
394
|
+
for (const [k, val] of v.fields) newFields.set(k, cloneResolverValue(val));
|
|
395
|
+
const newPriors = /* @__PURE__ */ new Map();
|
|
396
|
+
for (const [k, val] of v.priorValues) newPriors.set(k, cloneResolverValue(val));
|
|
397
|
+
return { _kind: "res-obj", fields: newFields, priorValues: newPriors };
|
|
398
|
+
}
|
|
399
|
+
if (isAppend(v)) {
|
|
400
|
+
return {
|
|
401
|
+
_kind: "append-placeholder",
|
|
402
|
+
existing: cloneResolverValue(v.existing),
|
|
403
|
+
elem: cloneResolverValue(v.elem)
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
const hv = v;
|
|
407
|
+
if (hv.kind === "array") {
|
|
408
|
+
return {
|
|
409
|
+
kind: "array",
|
|
410
|
+
items: hv.items.map((item) => cloneResolverValue(item))
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
if (hv.kind === "object") {
|
|
414
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
415
|
+
for (const [k, val] of hv.fields) {
|
|
416
|
+
newFields.set(k, cloneResolverValue(val));
|
|
417
|
+
}
|
|
418
|
+
return { kind: "object", fields: newFields };
|
|
419
|
+
}
|
|
420
|
+
return hv;
|
|
421
|
+
}
|
|
422
|
+
function foldNestedSelfRefs(v, pathPrefix) {
|
|
423
|
+
if (!isResObj(v)) return v;
|
|
424
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
425
|
+
for (const [k, fieldVal] of v.fields) {
|
|
426
|
+
const childPath = [...pathPrefix, k];
|
|
427
|
+
const fullKey = stringSegmentsToKey(childPath);
|
|
428
|
+
const folded = foldNestedSelfRefs(fieldVal, childPath);
|
|
429
|
+
let finalVal = folded;
|
|
430
|
+
if (containsSelfRef(folded, fullKey)) {
|
|
431
|
+
const leafPrior = v.priorValues.get(k);
|
|
432
|
+
if (leafPrior !== void 0) {
|
|
433
|
+
const leafPriorFolded = foldNestedSelfRefs(leafPrior, childPath);
|
|
434
|
+
finalVal = foldSelfRef(folded, fullKey, leafPriorFolded);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
newFields.set(k, finalVal);
|
|
438
|
+
}
|
|
439
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
440
|
+
}
|
|
441
|
+
function containsSubstByPath(v, target) {
|
|
442
|
+
if (isSubst(v)) return segmentsTextEqual(v.segments, target);
|
|
443
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
|
|
444
|
+
if (isAppend(v)) {
|
|
445
|
+
return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
|
|
446
|
+
}
|
|
447
|
+
if (isResObj(v)) {
|
|
448
|
+
for (const f of v.fields.values()) {
|
|
449
|
+
if (containsSubstByPath(f, target)) return true;
|
|
450
|
+
}
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const hv = v;
|
|
454
|
+
if (hv.kind === "array") {
|
|
455
|
+
return hv.items.some((item) => containsSubstByPath(item, target));
|
|
456
|
+
}
|
|
457
|
+
if (hv.kind === "object") {
|
|
458
|
+
for (const f of hv.fields.values()) {
|
|
459
|
+
if (containsSubstByPath(f, target)) return true;
|
|
460
|
+
}
|
|
461
|
+
return false;
|
|
462
|
+
}
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
function segmentsTextEqual(a, b) {
|
|
466
|
+
if (a.length !== b.length) return false;
|
|
467
|
+
for (let i = 0; i < a.length; i++) {
|
|
468
|
+
const ai = a[i];
|
|
469
|
+
const bi = b[i];
|
|
470
|
+
if (ai === void 0 || bi === void 0 || ai.text !== bi.text) return false;
|
|
471
|
+
}
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
|
|
289
475
|
// src/internal/resolver/utils.ts
|
|
290
476
|
function segmentsToKey(segments) {
|
|
291
477
|
return segments.map((s) => {
|
|
@@ -327,13 +513,22 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
327
513
|
}
|
|
328
514
|
return { kind: "object", fields: merged };
|
|
329
515
|
}
|
|
330
|
-
function deepMergeResObjInto(dst, src) {
|
|
516
|
+
function deepMergeResObjInto(dst, src, pathPrefix = []) {
|
|
331
517
|
for (const [k, srcVal] of src.fields) {
|
|
518
|
+
const childPrefix = [...pathPrefix, k];
|
|
519
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
332
520
|
const dstVal = dst.fields.get(k);
|
|
333
521
|
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
334
|
-
|
|
522
|
+
const priorExisting = dst.priorValues.get(k);
|
|
523
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
524
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
525
|
+
deepMergeResObjInto(dstVal, srcVal, childPrefix);
|
|
335
526
|
} else {
|
|
336
|
-
if (dstVal !== void 0)
|
|
527
|
+
if (dstVal !== void 0) {
|
|
528
|
+
const priorExisting = dst.priorValues.get(k);
|
|
529
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
530
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
531
|
+
}
|
|
337
532
|
dst.fields.set(k, srcVal);
|
|
338
533
|
}
|
|
339
534
|
}
|
|
@@ -897,12 +1092,17 @@ var Parser = class {
|
|
|
897
1092
|
const segments = [];
|
|
898
1093
|
let trailingDot = false;
|
|
899
1094
|
let firstWasQuoted = false;
|
|
1095
|
+
let spaceConcat = false;
|
|
900
1096
|
while (true) {
|
|
901
1097
|
const t = this.peek();
|
|
902
1098
|
if (t.kind === "string") {
|
|
903
1099
|
this.advance();
|
|
904
1100
|
if (segments.length === 0) firstWasQuoted = true;
|
|
905
|
-
|
|
1101
|
+
if (spaceConcat) {
|
|
1102
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${t.value}`;
|
|
1103
|
+
} else {
|
|
1104
|
+
segments.push(t.value);
|
|
1105
|
+
}
|
|
906
1106
|
trailingDot = false;
|
|
907
1107
|
} else if (t.kind === "unquoted") {
|
|
908
1108
|
this.advance();
|
|
@@ -919,12 +1119,23 @@ var Parser = class {
|
|
|
919
1119
|
);
|
|
920
1120
|
}
|
|
921
1121
|
}
|
|
922
|
-
|
|
1122
|
+
if (spaceConcat && filtered.length > 0) {
|
|
1123
|
+
if (raw.startsWith(".")) {
|
|
1124
|
+
segments.push(...filtered);
|
|
1125
|
+
} else {
|
|
1126
|
+
const [head, ...tail] = filtered;
|
|
1127
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${head}`;
|
|
1128
|
+
segments.push(...tail);
|
|
1129
|
+
}
|
|
1130
|
+
} else {
|
|
1131
|
+
segments.push(...filtered);
|
|
1132
|
+
}
|
|
923
1133
|
trailingDot = raw.endsWith(".");
|
|
924
1134
|
} else {
|
|
925
1135
|
if (segments.length === 0) throw new ParseError(`expected key, got ${t.kind}`, t.line, t.col);
|
|
926
1136
|
break;
|
|
927
1137
|
}
|
|
1138
|
+
spaceConcat = false;
|
|
928
1139
|
if (trailingDot) continue;
|
|
929
1140
|
const next = this.peek();
|
|
930
1141
|
if (next.kind === "unquoted" && next.value === "." && !next.precedingSpace) {
|
|
@@ -936,6 +1147,10 @@ var Parser = class {
|
|
|
936
1147
|
trailingDot = true;
|
|
937
1148
|
continue;
|
|
938
1149
|
}
|
|
1150
|
+
if ((next.kind === "unquoted" || next.kind === "string") && next.precedingSpace) {
|
|
1151
|
+
spaceConcat = true;
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
939
1154
|
break;
|
|
940
1155
|
}
|
|
941
1156
|
return { segments, firstWasQuoted };
|
|
@@ -1323,7 +1538,7 @@ var IncludeLoader = class {
|
|
|
1323
1538
|
for (const ext of probeExts) {
|
|
1324
1539
|
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
1325
1540
|
if (obj !== void 0) {
|
|
1326
|
-
deepMergeResObjInto(merged, obj);
|
|
1541
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1327
1542
|
foundAny = true;
|
|
1328
1543
|
}
|
|
1329
1544
|
}
|
|
@@ -1356,7 +1571,7 @@ var IncludeLoader = class {
|
|
|
1356
1571
|
for (const ext of probeExts) {
|
|
1357
1572
|
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
1358
1573
|
if (obj !== void 0) {
|
|
1359
|
-
deepMergeResObjInto(merged, obj);
|
|
1574
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1360
1575
|
foundAny = true;
|
|
1361
1576
|
}
|
|
1362
1577
|
}
|
|
@@ -1528,7 +1743,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1528
1743
|
if (pathPrefix.length > 0) {
|
|
1529
1744
|
this.relativizeResObj(included, pathPrefix);
|
|
1530
1745
|
}
|
|
1531
|
-
deepMergeResObjInto(obj, included);
|
|
1746
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1532
1747
|
return;
|
|
1533
1748
|
}
|
|
1534
1749
|
const [head, ...tail] = field.key;
|
|
@@ -1543,12 +1758,27 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1543
1758
|
return;
|
|
1544
1759
|
}
|
|
1545
1760
|
const childPrefix = [...pathPrefix, head];
|
|
1761
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1546
1762
|
if (field.append) {
|
|
1547
1763
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1548
|
-
obj.priorValues.
|
|
1764
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1765
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1766
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1549
1767
|
obj.fields.set(head, {
|
|
1550
1768
|
_kind: "append-placeholder",
|
|
1551
|
-
existing
|
|
1769
|
+
// Use the folded existing (self-ref-free) for the Append. When the
|
|
1770
|
+
// fold returned undefined (self-ref present but no old prior),
|
|
1771
|
+
// fall back to the original existing — resolveSubst will then
|
|
1772
|
+
// raise the "self-ref with no prior value" error at resolve time,
|
|
1773
|
+
// matching the pre-fix behaviour for that edge case.
|
|
1774
|
+
//
|
|
1775
|
+
// Clone `folded` again so the Append.existing and priorValues[head]
|
|
1776
|
+
// do not share the same Subst/Concat references — relativizeResObj
|
|
1777
|
+
// walks BOTH `fields` and `priorValues` and mutates Subst.segments
|
|
1778
|
+
// in place, so a shared reference would get its prefix applied
|
|
1779
|
+
// twice (resulting in e.g. `${outer.outer.base}` instead of
|
|
1780
|
+
// `${outer.base}`). Codex review on PR #131.
|
|
1781
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1552
1782
|
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1553
1783
|
});
|
|
1554
1784
|
return;
|
|
@@ -1556,10 +1786,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1556
1786
|
const existing = obj.fields.get(head);
|
|
1557
1787
|
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1558
1788
|
if (existing !== void 0) {
|
|
1559
|
-
|
|
1789
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1790
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1791
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1792
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1560
1793
|
}
|
|
1561
1794
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1562
|
-
deepMergeResObjInto(existing, newVal);
|
|
1795
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1563
1796
|
return;
|
|
1564
1797
|
}
|
|
1565
1798
|
obj.fields.set(head, newVal);
|
|
@@ -1576,7 +1809,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1576
1809
|
if (pathPrefix.length > 0) {
|
|
1577
1810
|
this.relativizeResObj(included, pathPrefix);
|
|
1578
1811
|
}
|
|
1579
|
-
deepMergeResObjInto(obj, included);
|
|
1812
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1580
1813
|
return;
|
|
1581
1814
|
}
|
|
1582
1815
|
const [head, ...tail] = field.key;
|
|
@@ -1591,12 +1824,15 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1591
1824
|
return;
|
|
1592
1825
|
}
|
|
1593
1826
|
const childPrefix = [...pathPrefix, head];
|
|
1827
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1594
1828
|
if (field.append) {
|
|
1595
1829
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1596
|
-
obj.priorValues.
|
|
1830
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1831
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1832
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1597
1833
|
obj.fields.set(head, {
|
|
1598
1834
|
_kind: "append-placeholder",
|
|
1599
|
-
existing: existing2,
|
|
1835
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1600
1836
|
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1601
1837
|
});
|
|
1602
1838
|
return;
|
|
@@ -1604,10 +1840,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1604
1840
|
const existing = obj.fields.get(head);
|
|
1605
1841
|
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1606
1842
|
if (existing !== void 0) {
|
|
1607
|
-
|
|
1843
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1844
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1845
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1846
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1608
1847
|
}
|
|
1609
1848
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1610
|
-
deepMergeResObjInto(existing, newVal);
|
|
1849
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1611
1850
|
return;
|
|
1612
1851
|
}
|
|
1613
1852
|
obj.fields.set(head, newVal);
|
|
@@ -1739,6 +1978,23 @@ var SubstitutionResolver = class {
|
|
|
1739
1978
|
// narrower than path-equality. Spec amendment deferred to a follow-up
|
|
1740
1979
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1741
1980
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
1981
|
+
// Path stack tracking the full dotted path of the field currently being
|
|
1982
|
+
// assigned in `resolveResObj`. Leaf keys are pushed on entry to each
|
|
1983
|
+
// `(key, val)` iteration and popped on exit, so nested objects build up
|
|
1984
|
+
// the full path (e.g. `["o", "history"]` while resolving `o.history`).
|
|
1985
|
+
//
|
|
1986
|
+
// Used to tighten self-ref detection at the L192 found-lookup site:
|
|
1987
|
+
// a substitution `${X}` whose looked-up value contains a Subst pointing
|
|
1988
|
+
// at `X` is only a true self-reference when the field currently being
|
|
1989
|
+
// assigned is `X` (or a descendant of `X`). Without this guard, an
|
|
1990
|
+
// external lookup like `b = ${o}` (where o has a self-referential
|
|
1991
|
+
// field) would mis-fire and short-circuit to o's prior, returning the
|
|
1992
|
+
// wrong value for b. The is_owner gate (rfp prefix-matches s.segments)
|
|
1993
|
+
// narrows the check to the owner case only.
|
|
1994
|
+
//
|
|
1995
|
+
// Cross-impl with rs.hocon v1.5.1's resolving_field_path (same purpose,
|
|
1996
|
+
// same prefix-match semantics).
|
|
1997
|
+
this.resolvingFieldPath = [];
|
|
1742
1998
|
}
|
|
1743
1999
|
/** Returns a prefix for error messages, including origin info when set. */
|
|
1744
2000
|
originPrefix() {
|
|
@@ -1750,7 +2006,13 @@ var SubstitutionResolver = class {
|
|
|
1750
2006
|
resolveResObj(obj) {
|
|
1751
2007
|
const result = /* @__PURE__ */ new Map();
|
|
1752
2008
|
for (const [key, val] of obj.fields) {
|
|
1753
|
-
|
|
2009
|
+
this.resolvingFieldPath.push(key);
|
|
2010
|
+
let resolved;
|
|
2011
|
+
try {
|
|
2012
|
+
resolved = this.resolveVal(val, obj);
|
|
2013
|
+
} finally {
|
|
2014
|
+
this.resolvingFieldPath.pop();
|
|
2015
|
+
}
|
|
1754
2016
|
if (resolved !== void 0) {
|
|
1755
2017
|
if (resolved.kind === "object") {
|
|
1756
2018
|
const prior = obj.priorValues.get(key);
|
|
@@ -1840,7 +2102,10 @@ var SubstitutionResolver = class {
|
|
|
1840
2102
|
try {
|
|
1841
2103
|
const found = lookupPath(this.root, s.segments);
|
|
1842
2104
|
if (found !== void 0) {
|
|
1843
|
-
|
|
2105
|
+
const rfp = this.resolvingFieldPath;
|
|
2106
|
+
const isOwner = rfp.length >= s.segments.length && s.segments.every((seg, i) => rfp[i] === seg.text);
|
|
2107
|
+
const isSelfRef = isConcat(found) && this.resolvingConcats.has(found) || isOwner && containsSubstByPath(found, s.segments);
|
|
2108
|
+
if (isSelfRef) {
|
|
1844
2109
|
let prior;
|
|
1845
2110
|
if (s.segments.length > 1) {
|
|
1846
2111
|
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
@@ -2051,8 +2316,33 @@ var SubstitutionResolver = class {
|
|
|
2051
2316
|
}
|
|
2052
2317
|
resolveAppend(a, scope) {
|
|
2053
2318
|
const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
|
|
2319
|
+
if (this.opts.allowUnresolved && (isSubst(existing) || isConcat(existing) || isAppend(existing))) {
|
|
2320
|
+
return a;
|
|
2321
|
+
}
|
|
2054
2322
|
const elem = this.resolveVal(a.elem, scope);
|
|
2055
|
-
|
|
2323
|
+
let priorItems;
|
|
2324
|
+
if (existing.kind === "array") {
|
|
2325
|
+
priorItems = existing.items;
|
|
2326
|
+
} else if (existing.kind === "object") {
|
|
2327
|
+
const converted = numericObjectToArray(existing);
|
|
2328
|
+
if (converted === null) {
|
|
2329
|
+
throw new ResolveError(
|
|
2330
|
+
`'+=' on non-array value: prior value is object with non-numeric keys (spec L732)`,
|
|
2331
|
+
"",
|
|
2332
|
+
0,
|
|
2333
|
+
0
|
|
2334
|
+
);
|
|
2335
|
+
}
|
|
2336
|
+
priorItems = converted;
|
|
2337
|
+
} else {
|
|
2338
|
+
throw new ResolveError(
|
|
2339
|
+
`'+=' on non-array value: prior value is ${existing.kind} (spec L732)`,
|
|
2340
|
+
"",
|
|
2341
|
+
0,
|
|
2342
|
+
0
|
|
2343
|
+
);
|
|
2344
|
+
}
|
|
2345
|
+
const items = [...priorItems];
|
|
2056
2346
|
if (elem !== void 0) items.push(elem);
|
|
2057
2347
|
return { kind: "array", items };
|
|
2058
2348
|
}
|
|
@@ -2154,6 +2444,9 @@ var Config = class _Config {
|
|
|
2154
2444
|
}
|
|
2155
2445
|
getString(path2) {
|
|
2156
2446
|
const v = this.requireScalar(path2);
|
|
2447
|
+
if (v.valueType === "null") {
|
|
2448
|
+
throw new ConfigError(`expected string at ${path2}, got ${v.valueType}`, path2);
|
|
2449
|
+
}
|
|
2157
2450
|
return v.raw;
|
|
2158
2451
|
}
|
|
2159
2452
|
getNumber(path2) {
|