@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/zod.js
CHANGED
|
@@ -290,6 +290,192 @@ function mergeUnresolved(receiver, fallback) {
|
|
|
290
290
|
return result;
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
+
// src/internal/resolver/fold-self-ref.ts
|
|
294
|
+
function substFullKey(s) {
|
|
295
|
+
return stringSegmentsToKey(s.segments.map((seg) => seg.text));
|
|
296
|
+
}
|
|
297
|
+
function stringSegmentsToKey(segments) {
|
|
298
|
+
return segments.map((t) => {
|
|
299
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
300
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
301
|
+
}
|
|
302
|
+
return t;
|
|
303
|
+
}).join(".");
|
|
304
|
+
}
|
|
305
|
+
function containsSelfRef(v, fullKey) {
|
|
306
|
+
if (isSubst(v)) return substFullKey(v) === fullKey;
|
|
307
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
|
|
308
|
+
if (isAppend(v)) {
|
|
309
|
+
return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
|
|
310
|
+
}
|
|
311
|
+
if (isResObj(v)) {
|
|
312
|
+
for (const f of v.fields.values()) {
|
|
313
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
314
|
+
}
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
const hv = v;
|
|
318
|
+
if (hv.kind === "array") {
|
|
319
|
+
return hv.items.some((item) => containsSelfRef(item, fullKey));
|
|
320
|
+
}
|
|
321
|
+
if (hv.kind === "object") {
|
|
322
|
+
for (const f of hv.fields.values()) {
|
|
323
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
function foldSelfRef(v, fullKey, replacement) {
|
|
330
|
+
if (isSubst(v)) {
|
|
331
|
+
return substFullKey(v) === fullKey ? replacement : v;
|
|
332
|
+
}
|
|
333
|
+
if (isConcat(v)) {
|
|
334
|
+
return {
|
|
335
|
+
_kind: "concat-placeholder",
|
|
336
|
+
nodes: v.nodes.map((n) => foldSelfRef(n, fullKey, replacement)),
|
|
337
|
+
line: v.line,
|
|
338
|
+
col: v.col
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
if (isAppend(v)) {
|
|
342
|
+
return {
|
|
343
|
+
_kind: "append-placeholder",
|
|
344
|
+
existing: foldSelfRef(v.existing, fullKey, replacement),
|
|
345
|
+
elem: foldSelfRef(v.elem, fullKey, replacement)
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (isResObj(v)) {
|
|
349
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
350
|
+
for (const [k, val] of v.fields) {
|
|
351
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
352
|
+
}
|
|
353
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
354
|
+
}
|
|
355
|
+
const hv = v;
|
|
356
|
+
if (hv.kind === "array") {
|
|
357
|
+
return {
|
|
358
|
+
kind: "array",
|
|
359
|
+
items: hv.items.map((item) => foldSelfRef(item, fullKey, replacement))
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
if (hv.kind === "object") {
|
|
363
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
364
|
+
for (const [k, val] of hv.fields) {
|
|
365
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
366
|
+
}
|
|
367
|
+
return { kind: "object", fields: newFields };
|
|
368
|
+
}
|
|
369
|
+
return v;
|
|
370
|
+
}
|
|
371
|
+
function foldOrSkipPrior(prior, fullKey, old) {
|
|
372
|
+
if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
|
|
373
|
+
if (old === void 0) return void 0;
|
|
374
|
+
return foldSelfRef(prior, fullKey, old);
|
|
375
|
+
}
|
|
376
|
+
function cloneResolverValue(v) {
|
|
377
|
+
if (isSubst(v)) {
|
|
378
|
+
return {
|
|
379
|
+
_kind: "subst-placeholder",
|
|
380
|
+
segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
|
|
381
|
+
optional: v.optional,
|
|
382
|
+
listSuffix: v.listSuffix,
|
|
383
|
+
line: v.line,
|
|
384
|
+
col: v.col,
|
|
385
|
+
prefixLen: v.prefixLen
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
if (isConcat(v)) {
|
|
389
|
+
return {
|
|
390
|
+
_kind: "concat-placeholder",
|
|
391
|
+
nodes: v.nodes.map((n) => cloneResolverValue(n)),
|
|
392
|
+
line: v.line,
|
|
393
|
+
col: v.col
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (isResObj(v)) {
|
|
397
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
398
|
+
for (const [k, val] of v.fields) newFields.set(k, cloneResolverValue(val));
|
|
399
|
+
const newPriors = /* @__PURE__ */ new Map();
|
|
400
|
+
for (const [k, val] of v.priorValues) newPriors.set(k, cloneResolverValue(val));
|
|
401
|
+
return { _kind: "res-obj", fields: newFields, priorValues: newPriors };
|
|
402
|
+
}
|
|
403
|
+
if (isAppend(v)) {
|
|
404
|
+
return {
|
|
405
|
+
_kind: "append-placeholder",
|
|
406
|
+
existing: cloneResolverValue(v.existing),
|
|
407
|
+
elem: cloneResolverValue(v.elem)
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const hv = v;
|
|
411
|
+
if (hv.kind === "array") {
|
|
412
|
+
return {
|
|
413
|
+
kind: "array",
|
|
414
|
+
items: hv.items.map((item) => cloneResolverValue(item))
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (hv.kind === "object") {
|
|
418
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
419
|
+
for (const [k, val] of hv.fields) {
|
|
420
|
+
newFields.set(k, cloneResolverValue(val));
|
|
421
|
+
}
|
|
422
|
+
return { kind: "object", fields: newFields };
|
|
423
|
+
}
|
|
424
|
+
return hv;
|
|
425
|
+
}
|
|
426
|
+
function foldNestedSelfRefs(v, pathPrefix) {
|
|
427
|
+
if (!isResObj(v)) return v;
|
|
428
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
429
|
+
for (const [k, fieldVal] of v.fields) {
|
|
430
|
+
const childPath = [...pathPrefix, k];
|
|
431
|
+
const fullKey = stringSegmentsToKey(childPath);
|
|
432
|
+
const folded = foldNestedSelfRefs(fieldVal, childPath);
|
|
433
|
+
let finalVal = folded;
|
|
434
|
+
if (containsSelfRef(folded, fullKey)) {
|
|
435
|
+
const leafPrior = v.priorValues.get(k);
|
|
436
|
+
if (leafPrior !== void 0) {
|
|
437
|
+
const leafPriorFolded = foldNestedSelfRefs(leafPrior, childPath);
|
|
438
|
+
finalVal = foldSelfRef(folded, fullKey, leafPriorFolded);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
newFields.set(k, finalVal);
|
|
442
|
+
}
|
|
443
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
444
|
+
}
|
|
445
|
+
function containsSubstByPath(v, target) {
|
|
446
|
+
if (isSubst(v)) return segmentsTextEqual(v.segments, target);
|
|
447
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
|
|
448
|
+
if (isAppend(v)) {
|
|
449
|
+
return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
|
|
450
|
+
}
|
|
451
|
+
if (isResObj(v)) {
|
|
452
|
+
for (const f of v.fields.values()) {
|
|
453
|
+
if (containsSubstByPath(f, target)) return true;
|
|
454
|
+
}
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
const hv = v;
|
|
458
|
+
if (hv.kind === "array") {
|
|
459
|
+
return hv.items.some((item) => containsSubstByPath(item, target));
|
|
460
|
+
}
|
|
461
|
+
if (hv.kind === "object") {
|
|
462
|
+
for (const f of hv.fields.values()) {
|
|
463
|
+
if (containsSubstByPath(f, target)) return true;
|
|
464
|
+
}
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
return false;
|
|
468
|
+
}
|
|
469
|
+
function segmentsTextEqual(a, b) {
|
|
470
|
+
if (a.length !== b.length) return false;
|
|
471
|
+
for (let i = 0; i < a.length; i++) {
|
|
472
|
+
const ai = a[i];
|
|
473
|
+
const bi = b[i];
|
|
474
|
+
if (ai === void 0 || bi === void 0 || ai.text !== bi.text) return false;
|
|
475
|
+
}
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
293
479
|
// src/internal/resolver/utils.ts
|
|
294
480
|
function segmentsToKey(segments) {
|
|
295
481
|
return segments.map((s) => {
|
|
@@ -331,13 +517,22 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
331
517
|
}
|
|
332
518
|
return { kind: "object", fields: merged };
|
|
333
519
|
}
|
|
334
|
-
function deepMergeResObjInto(dst, src) {
|
|
520
|
+
function deepMergeResObjInto(dst, src, pathPrefix = []) {
|
|
335
521
|
for (const [k, srcVal] of src.fields) {
|
|
522
|
+
const childPrefix = [...pathPrefix, k];
|
|
523
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
336
524
|
const dstVal = dst.fields.get(k);
|
|
337
525
|
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
338
|
-
|
|
526
|
+
const priorExisting = dst.priorValues.get(k);
|
|
527
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
528
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
529
|
+
deepMergeResObjInto(dstVal, srcVal, childPrefix);
|
|
339
530
|
} else {
|
|
340
|
-
if (dstVal !== void 0)
|
|
531
|
+
if (dstVal !== void 0) {
|
|
532
|
+
const priorExisting = dst.priorValues.get(k);
|
|
533
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
534
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
535
|
+
}
|
|
341
536
|
dst.fields.set(k, srcVal);
|
|
342
537
|
}
|
|
343
538
|
}
|
|
@@ -901,12 +1096,17 @@ var Parser = class {
|
|
|
901
1096
|
const segments = [];
|
|
902
1097
|
let trailingDot = false;
|
|
903
1098
|
let firstWasQuoted = false;
|
|
1099
|
+
let spaceConcat = false;
|
|
904
1100
|
while (true) {
|
|
905
1101
|
const t = this.peek();
|
|
906
1102
|
if (t.kind === "string") {
|
|
907
1103
|
this.advance();
|
|
908
1104
|
if (segments.length === 0) firstWasQuoted = true;
|
|
909
|
-
|
|
1105
|
+
if (spaceConcat) {
|
|
1106
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${t.value}`;
|
|
1107
|
+
} else {
|
|
1108
|
+
segments.push(t.value);
|
|
1109
|
+
}
|
|
910
1110
|
trailingDot = false;
|
|
911
1111
|
} else if (t.kind === "unquoted") {
|
|
912
1112
|
this.advance();
|
|
@@ -923,12 +1123,23 @@ var Parser = class {
|
|
|
923
1123
|
);
|
|
924
1124
|
}
|
|
925
1125
|
}
|
|
926
|
-
|
|
1126
|
+
if (spaceConcat && filtered.length > 0) {
|
|
1127
|
+
if (raw.startsWith(".")) {
|
|
1128
|
+
segments.push(...filtered);
|
|
1129
|
+
} else {
|
|
1130
|
+
const [head, ...tail] = filtered;
|
|
1131
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${head}`;
|
|
1132
|
+
segments.push(...tail);
|
|
1133
|
+
}
|
|
1134
|
+
} else {
|
|
1135
|
+
segments.push(...filtered);
|
|
1136
|
+
}
|
|
927
1137
|
trailingDot = raw.endsWith(".");
|
|
928
1138
|
} else {
|
|
929
1139
|
if (segments.length === 0) throw new ParseError(`expected key, got ${t.kind}`, t.line, t.col);
|
|
930
1140
|
break;
|
|
931
1141
|
}
|
|
1142
|
+
spaceConcat = false;
|
|
932
1143
|
if (trailingDot) continue;
|
|
933
1144
|
const next = this.peek();
|
|
934
1145
|
if (next.kind === "unquoted" && next.value === "." && !next.precedingSpace) {
|
|
@@ -940,6 +1151,10 @@ var Parser = class {
|
|
|
940
1151
|
trailingDot = true;
|
|
941
1152
|
continue;
|
|
942
1153
|
}
|
|
1154
|
+
if ((next.kind === "unquoted" || next.kind === "string") && next.precedingSpace) {
|
|
1155
|
+
spaceConcat = true;
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
943
1158
|
break;
|
|
944
1159
|
}
|
|
945
1160
|
return { segments, firstWasQuoted };
|
|
@@ -1327,7 +1542,7 @@ var IncludeLoader = class {
|
|
|
1327
1542
|
for (const ext of probeExts) {
|
|
1328
1543
|
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
1329
1544
|
if (obj !== void 0) {
|
|
1330
|
-
deepMergeResObjInto(merged, obj);
|
|
1545
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1331
1546
|
foundAny = true;
|
|
1332
1547
|
}
|
|
1333
1548
|
}
|
|
@@ -1360,7 +1575,7 @@ var IncludeLoader = class {
|
|
|
1360
1575
|
for (const ext of probeExts) {
|
|
1361
1576
|
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
1362
1577
|
if (obj !== void 0) {
|
|
1363
|
-
deepMergeResObjInto(merged, obj);
|
|
1578
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1364
1579
|
foundAny = true;
|
|
1365
1580
|
}
|
|
1366
1581
|
}
|
|
@@ -1532,7 +1747,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1532
1747
|
if (pathPrefix.length > 0) {
|
|
1533
1748
|
this.relativizeResObj(included, pathPrefix);
|
|
1534
1749
|
}
|
|
1535
|
-
deepMergeResObjInto(obj, included);
|
|
1750
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1536
1751
|
return;
|
|
1537
1752
|
}
|
|
1538
1753
|
const [head, ...tail] = field.key;
|
|
@@ -1547,12 +1762,27 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1547
1762
|
return;
|
|
1548
1763
|
}
|
|
1549
1764
|
const childPrefix = [...pathPrefix, head];
|
|
1765
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1550
1766
|
if (field.append) {
|
|
1551
1767
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1552
|
-
obj.priorValues.
|
|
1768
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1769
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1770
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1553
1771
|
obj.fields.set(head, {
|
|
1554
1772
|
_kind: "append-placeholder",
|
|
1555
|
-
existing
|
|
1773
|
+
// Use the folded existing (self-ref-free) for the Append. When the
|
|
1774
|
+
// fold returned undefined (self-ref present but no old prior),
|
|
1775
|
+
// fall back to the original existing — resolveSubst will then
|
|
1776
|
+
// raise the "self-ref with no prior value" error at resolve time,
|
|
1777
|
+
// matching the pre-fix behaviour for that edge case.
|
|
1778
|
+
//
|
|
1779
|
+
// Clone `folded` again so the Append.existing and priorValues[head]
|
|
1780
|
+
// do not share the same Subst/Concat references — relativizeResObj
|
|
1781
|
+
// walks BOTH `fields` and `priorValues` and mutates Subst.segments
|
|
1782
|
+
// in place, so a shared reference would get its prefix applied
|
|
1783
|
+
// twice (resulting in e.g. `${outer.outer.base}` instead of
|
|
1784
|
+
// `${outer.base}`). Codex review on PR #131.
|
|
1785
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1556
1786
|
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1557
1787
|
});
|
|
1558
1788
|
return;
|
|
@@ -1560,10 +1790,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1560
1790
|
const existing = obj.fields.get(head);
|
|
1561
1791
|
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1562
1792
|
if (existing !== void 0) {
|
|
1563
|
-
|
|
1793
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1794
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1795
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1796
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1564
1797
|
}
|
|
1565
1798
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1566
|
-
deepMergeResObjInto(existing, newVal);
|
|
1799
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1567
1800
|
return;
|
|
1568
1801
|
}
|
|
1569
1802
|
obj.fields.set(head, newVal);
|
|
@@ -1580,7 +1813,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1580
1813
|
if (pathPrefix.length > 0) {
|
|
1581
1814
|
this.relativizeResObj(included, pathPrefix);
|
|
1582
1815
|
}
|
|
1583
|
-
deepMergeResObjInto(obj, included);
|
|
1816
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1584
1817
|
return;
|
|
1585
1818
|
}
|
|
1586
1819
|
const [head, ...tail] = field.key;
|
|
@@ -1595,12 +1828,15 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1595
1828
|
return;
|
|
1596
1829
|
}
|
|
1597
1830
|
const childPrefix = [...pathPrefix, head];
|
|
1831
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1598
1832
|
if (field.append) {
|
|
1599
1833
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1600
|
-
obj.priorValues.
|
|
1834
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1835
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1836
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1601
1837
|
obj.fields.set(head, {
|
|
1602
1838
|
_kind: "append-placeholder",
|
|
1603
|
-
existing: existing2,
|
|
1839
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1604
1840
|
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1605
1841
|
});
|
|
1606
1842
|
return;
|
|
@@ -1608,10 +1844,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1608
1844
|
const existing = obj.fields.get(head);
|
|
1609
1845
|
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1610
1846
|
if (existing !== void 0) {
|
|
1611
|
-
|
|
1847
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1848
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1849
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1850
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1612
1851
|
}
|
|
1613
1852
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1614
|
-
deepMergeResObjInto(existing, newVal);
|
|
1853
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1615
1854
|
return;
|
|
1616
1855
|
}
|
|
1617
1856
|
obj.fields.set(head, newVal);
|
|
@@ -1743,6 +1982,23 @@ var SubstitutionResolver = class {
|
|
|
1743
1982
|
// narrower than path-equality. Spec amendment deferred to a follow-up
|
|
1744
1983
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1745
1984
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
1985
|
+
// Path stack tracking the full dotted path of the field currently being
|
|
1986
|
+
// assigned in `resolveResObj`. Leaf keys are pushed on entry to each
|
|
1987
|
+
// `(key, val)` iteration and popped on exit, so nested objects build up
|
|
1988
|
+
// the full path (e.g. `["o", "history"]` while resolving `o.history`).
|
|
1989
|
+
//
|
|
1990
|
+
// Used to tighten self-ref detection at the L192 found-lookup site:
|
|
1991
|
+
// a substitution `${X}` whose looked-up value contains a Subst pointing
|
|
1992
|
+
// at `X` is only a true self-reference when the field currently being
|
|
1993
|
+
// assigned is `X` (or a descendant of `X`). Without this guard, an
|
|
1994
|
+
// external lookup like `b = ${o}` (where o has a self-referential
|
|
1995
|
+
// field) would mis-fire and short-circuit to o's prior, returning the
|
|
1996
|
+
// wrong value for b. The is_owner gate (rfp prefix-matches s.segments)
|
|
1997
|
+
// narrows the check to the owner case only.
|
|
1998
|
+
//
|
|
1999
|
+
// Cross-impl with rs.hocon v1.5.1's resolving_field_path (same purpose,
|
|
2000
|
+
// same prefix-match semantics).
|
|
2001
|
+
this.resolvingFieldPath = [];
|
|
1746
2002
|
}
|
|
1747
2003
|
/** Returns a prefix for error messages, including origin info when set. */
|
|
1748
2004
|
originPrefix() {
|
|
@@ -1754,7 +2010,13 @@ var SubstitutionResolver = class {
|
|
|
1754
2010
|
resolveResObj(obj) {
|
|
1755
2011
|
const result = /* @__PURE__ */ new Map();
|
|
1756
2012
|
for (const [key, val] of obj.fields) {
|
|
1757
|
-
|
|
2013
|
+
this.resolvingFieldPath.push(key);
|
|
2014
|
+
let resolved;
|
|
2015
|
+
try {
|
|
2016
|
+
resolved = this.resolveVal(val, obj);
|
|
2017
|
+
} finally {
|
|
2018
|
+
this.resolvingFieldPath.pop();
|
|
2019
|
+
}
|
|
1758
2020
|
if (resolved !== void 0) {
|
|
1759
2021
|
if (resolved.kind === "object") {
|
|
1760
2022
|
const prior = obj.priorValues.get(key);
|
|
@@ -1844,7 +2106,10 @@ var SubstitutionResolver = class {
|
|
|
1844
2106
|
try {
|
|
1845
2107
|
const found = lookupPath(this.root, s.segments);
|
|
1846
2108
|
if (found !== void 0) {
|
|
1847
|
-
|
|
2109
|
+
const rfp = this.resolvingFieldPath;
|
|
2110
|
+
const isOwner = rfp.length >= s.segments.length && s.segments.every((seg, i) => rfp[i] === seg.text);
|
|
2111
|
+
const isSelfRef = isConcat(found) && this.resolvingConcats.has(found) || isOwner && containsSubstByPath(found, s.segments);
|
|
2112
|
+
if (isSelfRef) {
|
|
1848
2113
|
let prior;
|
|
1849
2114
|
if (s.segments.length > 1) {
|
|
1850
2115
|
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
@@ -2055,8 +2320,33 @@ var SubstitutionResolver = class {
|
|
|
2055
2320
|
}
|
|
2056
2321
|
resolveAppend(a, scope) {
|
|
2057
2322
|
const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
|
|
2323
|
+
if (this.opts.allowUnresolved && (isSubst(existing) || isConcat(existing) || isAppend(existing))) {
|
|
2324
|
+
return a;
|
|
2325
|
+
}
|
|
2058
2326
|
const elem = this.resolveVal(a.elem, scope);
|
|
2059
|
-
|
|
2327
|
+
let priorItems;
|
|
2328
|
+
if (existing.kind === "array") {
|
|
2329
|
+
priorItems = existing.items;
|
|
2330
|
+
} else if (existing.kind === "object") {
|
|
2331
|
+
const converted = numericObjectToArray(existing);
|
|
2332
|
+
if (converted === null) {
|
|
2333
|
+
throw new ResolveError(
|
|
2334
|
+
`'+=' on non-array value: prior value is object with non-numeric keys (spec L732)`,
|
|
2335
|
+
"",
|
|
2336
|
+
0,
|
|
2337
|
+
0
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
priorItems = converted;
|
|
2341
|
+
} else {
|
|
2342
|
+
throw new ResolveError(
|
|
2343
|
+
`'+=' on non-array value: prior value is ${existing.kind} (spec L732)`,
|
|
2344
|
+
"",
|
|
2345
|
+
0,
|
|
2346
|
+
0
|
|
2347
|
+
);
|
|
2348
|
+
}
|
|
2349
|
+
const items = [...priorItems];
|
|
2060
2350
|
if (elem !== void 0) items.push(elem);
|
|
2061
2351
|
return { kind: "array", items };
|
|
2062
2352
|
}
|
|
@@ -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) {
|