@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.cjs
CHANGED
|
@@ -329,6 +329,192 @@ function mergeUnresolved(receiver, fallback) {
|
|
|
329
329
|
return result;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
// src/internal/resolver/fold-self-ref.ts
|
|
333
|
+
function substFullKey(s) {
|
|
334
|
+
return stringSegmentsToKey(s.segments.map((seg) => seg.text));
|
|
335
|
+
}
|
|
336
|
+
function stringSegmentsToKey(segments) {
|
|
337
|
+
return segments.map((t) => {
|
|
338
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
339
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
340
|
+
}
|
|
341
|
+
return t;
|
|
342
|
+
}).join(".");
|
|
343
|
+
}
|
|
344
|
+
function containsSelfRef(v, fullKey) {
|
|
345
|
+
if (isSubst(v)) return substFullKey(v) === fullKey;
|
|
346
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
|
|
347
|
+
if (isAppend(v)) {
|
|
348
|
+
return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
|
|
349
|
+
}
|
|
350
|
+
if (isResObj(v)) {
|
|
351
|
+
for (const f of v.fields.values()) {
|
|
352
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
const hv = v;
|
|
357
|
+
if (hv.kind === "array") {
|
|
358
|
+
return hv.items.some((item) => containsSelfRef(item, fullKey));
|
|
359
|
+
}
|
|
360
|
+
if (hv.kind === "object") {
|
|
361
|
+
for (const f of hv.fields.values()) {
|
|
362
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
363
|
+
}
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
function foldSelfRef(v, fullKey, replacement) {
|
|
369
|
+
if (isSubst(v)) {
|
|
370
|
+
return substFullKey(v) === fullKey ? replacement : v;
|
|
371
|
+
}
|
|
372
|
+
if (isConcat(v)) {
|
|
373
|
+
return {
|
|
374
|
+
_kind: "concat-placeholder",
|
|
375
|
+
nodes: v.nodes.map((n) => foldSelfRef(n, fullKey, replacement)),
|
|
376
|
+
line: v.line,
|
|
377
|
+
col: v.col
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (isAppend(v)) {
|
|
381
|
+
return {
|
|
382
|
+
_kind: "append-placeholder",
|
|
383
|
+
existing: foldSelfRef(v.existing, fullKey, replacement),
|
|
384
|
+
elem: foldSelfRef(v.elem, fullKey, replacement)
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (isResObj(v)) {
|
|
388
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
389
|
+
for (const [k, val] of v.fields) {
|
|
390
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
391
|
+
}
|
|
392
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
393
|
+
}
|
|
394
|
+
const hv = v;
|
|
395
|
+
if (hv.kind === "array") {
|
|
396
|
+
return {
|
|
397
|
+
kind: "array",
|
|
398
|
+
items: hv.items.map((item) => foldSelfRef(item, fullKey, replacement))
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (hv.kind === "object") {
|
|
402
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
403
|
+
for (const [k, val] of hv.fields) {
|
|
404
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
405
|
+
}
|
|
406
|
+
return { kind: "object", fields: newFields };
|
|
407
|
+
}
|
|
408
|
+
return v;
|
|
409
|
+
}
|
|
410
|
+
function foldOrSkipPrior(prior, fullKey, old) {
|
|
411
|
+
if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
|
|
412
|
+
if (old === void 0) return void 0;
|
|
413
|
+
return foldSelfRef(prior, fullKey, old);
|
|
414
|
+
}
|
|
415
|
+
function cloneResolverValue(v) {
|
|
416
|
+
if (isSubst(v)) {
|
|
417
|
+
return {
|
|
418
|
+
_kind: "subst-placeholder",
|
|
419
|
+
segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
|
|
420
|
+
optional: v.optional,
|
|
421
|
+
listSuffix: v.listSuffix,
|
|
422
|
+
line: v.line,
|
|
423
|
+
col: v.col,
|
|
424
|
+
prefixLen: v.prefixLen
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
if (isConcat(v)) {
|
|
428
|
+
return {
|
|
429
|
+
_kind: "concat-placeholder",
|
|
430
|
+
nodes: v.nodes.map((n) => cloneResolverValue(n)),
|
|
431
|
+
line: v.line,
|
|
432
|
+
col: v.col
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (isResObj(v)) {
|
|
436
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
437
|
+
for (const [k, val] of v.fields) newFields.set(k, cloneResolverValue(val));
|
|
438
|
+
const newPriors = /* @__PURE__ */ new Map();
|
|
439
|
+
for (const [k, val] of v.priorValues) newPriors.set(k, cloneResolverValue(val));
|
|
440
|
+
return { _kind: "res-obj", fields: newFields, priorValues: newPriors };
|
|
441
|
+
}
|
|
442
|
+
if (isAppend(v)) {
|
|
443
|
+
return {
|
|
444
|
+
_kind: "append-placeholder",
|
|
445
|
+
existing: cloneResolverValue(v.existing),
|
|
446
|
+
elem: cloneResolverValue(v.elem)
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
const hv = v;
|
|
450
|
+
if (hv.kind === "array") {
|
|
451
|
+
return {
|
|
452
|
+
kind: "array",
|
|
453
|
+
items: hv.items.map((item) => cloneResolverValue(item))
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (hv.kind === "object") {
|
|
457
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
458
|
+
for (const [k, val] of hv.fields) {
|
|
459
|
+
newFields.set(k, cloneResolverValue(val));
|
|
460
|
+
}
|
|
461
|
+
return { kind: "object", fields: newFields };
|
|
462
|
+
}
|
|
463
|
+
return hv;
|
|
464
|
+
}
|
|
465
|
+
function foldNestedSelfRefs(v, pathPrefix) {
|
|
466
|
+
if (!isResObj(v)) return v;
|
|
467
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
468
|
+
for (const [k, fieldVal] of v.fields) {
|
|
469
|
+
const childPath = [...pathPrefix, k];
|
|
470
|
+
const fullKey = stringSegmentsToKey(childPath);
|
|
471
|
+
const folded = foldNestedSelfRefs(fieldVal, childPath);
|
|
472
|
+
let finalVal = folded;
|
|
473
|
+
if (containsSelfRef(folded, fullKey)) {
|
|
474
|
+
const leafPrior = v.priorValues.get(k);
|
|
475
|
+
if (leafPrior !== void 0) {
|
|
476
|
+
const leafPriorFolded = foldNestedSelfRefs(leafPrior, childPath);
|
|
477
|
+
finalVal = foldSelfRef(folded, fullKey, leafPriorFolded);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
newFields.set(k, finalVal);
|
|
481
|
+
}
|
|
482
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
483
|
+
}
|
|
484
|
+
function containsSubstByPath(v, target) {
|
|
485
|
+
if (isSubst(v)) return segmentsTextEqual(v.segments, target);
|
|
486
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
|
|
487
|
+
if (isAppend(v)) {
|
|
488
|
+
return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
|
|
489
|
+
}
|
|
490
|
+
if (isResObj(v)) {
|
|
491
|
+
for (const f of v.fields.values()) {
|
|
492
|
+
if (containsSubstByPath(f, target)) return true;
|
|
493
|
+
}
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
const hv = v;
|
|
497
|
+
if (hv.kind === "array") {
|
|
498
|
+
return hv.items.some((item) => containsSubstByPath(item, target));
|
|
499
|
+
}
|
|
500
|
+
if (hv.kind === "object") {
|
|
501
|
+
for (const f of hv.fields.values()) {
|
|
502
|
+
if (containsSubstByPath(f, target)) return true;
|
|
503
|
+
}
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
508
|
+
function segmentsTextEqual(a, b) {
|
|
509
|
+
if (a.length !== b.length) return false;
|
|
510
|
+
for (let i = 0; i < a.length; i++) {
|
|
511
|
+
const ai = a[i];
|
|
512
|
+
const bi = b[i];
|
|
513
|
+
if (ai === void 0 || bi === void 0 || ai.text !== bi.text) return false;
|
|
514
|
+
}
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
|
|
332
518
|
// src/internal/resolver/utils.ts
|
|
333
519
|
function segmentsToKey(segments) {
|
|
334
520
|
return segments.map((s) => {
|
|
@@ -370,13 +556,22 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
370
556
|
}
|
|
371
557
|
return { kind: "object", fields: merged };
|
|
372
558
|
}
|
|
373
|
-
function deepMergeResObjInto(dst, src) {
|
|
559
|
+
function deepMergeResObjInto(dst, src, pathPrefix = []) {
|
|
374
560
|
for (const [k, srcVal] of src.fields) {
|
|
561
|
+
const childPrefix = [...pathPrefix, k];
|
|
562
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
375
563
|
const dstVal = dst.fields.get(k);
|
|
376
564
|
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
377
|
-
|
|
565
|
+
const priorExisting = dst.priorValues.get(k);
|
|
566
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
567
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
568
|
+
deepMergeResObjInto(dstVal, srcVal, childPrefix);
|
|
378
569
|
} else {
|
|
379
|
-
if (dstVal !== void 0)
|
|
570
|
+
if (dstVal !== void 0) {
|
|
571
|
+
const priorExisting = dst.priorValues.get(k);
|
|
572
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
573
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
574
|
+
}
|
|
380
575
|
dst.fields.set(k, srcVal);
|
|
381
576
|
}
|
|
382
577
|
}
|
|
@@ -940,12 +1135,17 @@ var Parser = class {
|
|
|
940
1135
|
const segments = [];
|
|
941
1136
|
let trailingDot = false;
|
|
942
1137
|
let firstWasQuoted = false;
|
|
1138
|
+
let spaceConcat = false;
|
|
943
1139
|
while (true) {
|
|
944
1140
|
const t = this.peek();
|
|
945
1141
|
if (t.kind === "string") {
|
|
946
1142
|
this.advance();
|
|
947
1143
|
if (segments.length === 0) firstWasQuoted = true;
|
|
948
|
-
|
|
1144
|
+
if (spaceConcat) {
|
|
1145
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${t.value}`;
|
|
1146
|
+
} else {
|
|
1147
|
+
segments.push(t.value);
|
|
1148
|
+
}
|
|
949
1149
|
trailingDot = false;
|
|
950
1150
|
} else if (t.kind === "unquoted") {
|
|
951
1151
|
this.advance();
|
|
@@ -962,12 +1162,23 @@ var Parser = class {
|
|
|
962
1162
|
);
|
|
963
1163
|
}
|
|
964
1164
|
}
|
|
965
|
-
|
|
1165
|
+
if (spaceConcat && filtered.length > 0) {
|
|
1166
|
+
if (raw.startsWith(".")) {
|
|
1167
|
+
segments.push(...filtered);
|
|
1168
|
+
} else {
|
|
1169
|
+
const [head, ...tail] = filtered;
|
|
1170
|
+
segments[segments.length - 1] = `${segments[segments.length - 1]} ${head}`;
|
|
1171
|
+
segments.push(...tail);
|
|
1172
|
+
}
|
|
1173
|
+
} else {
|
|
1174
|
+
segments.push(...filtered);
|
|
1175
|
+
}
|
|
966
1176
|
trailingDot = raw.endsWith(".");
|
|
967
1177
|
} else {
|
|
968
1178
|
if (segments.length === 0) throw new ParseError(`expected key, got ${t.kind}`, t.line, t.col);
|
|
969
1179
|
break;
|
|
970
1180
|
}
|
|
1181
|
+
spaceConcat = false;
|
|
971
1182
|
if (trailingDot) continue;
|
|
972
1183
|
const next = this.peek();
|
|
973
1184
|
if (next.kind === "unquoted" && next.value === "." && !next.precedingSpace) {
|
|
@@ -979,6 +1190,10 @@ var Parser = class {
|
|
|
979
1190
|
trailingDot = true;
|
|
980
1191
|
continue;
|
|
981
1192
|
}
|
|
1193
|
+
if ((next.kind === "unquoted" || next.kind === "string") && next.precedingSpace) {
|
|
1194
|
+
spaceConcat = true;
|
|
1195
|
+
continue;
|
|
1196
|
+
}
|
|
982
1197
|
break;
|
|
983
1198
|
}
|
|
984
1199
|
return { segments, firstWasQuoted };
|
|
@@ -1367,7 +1582,7 @@ var IncludeLoader = class {
|
|
|
1367
1582
|
for (const ext of probeExts) {
|
|
1368
1583
|
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
1369
1584
|
if (obj !== void 0) {
|
|
1370
|
-
deepMergeResObjInto(merged, obj);
|
|
1585
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1371
1586
|
foundAny = true;
|
|
1372
1587
|
}
|
|
1373
1588
|
}
|
|
@@ -1400,7 +1615,7 @@ var IncludeLoader = class {
|
|
|
1400
1615
|
for (const ext of probeExts) {
|
|
1401
1616
|
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
1402
1617
|
if (obj !== void 0) {
|
|
1403
|
-
deepMergeResObjInto(merged, obj);
|
|
1618
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1404
1619
|
foundAny = true;
|
|
1405
1620
|
}
|
|
1406
1621
|
}
|
|
@@ -1572,7 +1787,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1572
1787
|
if (pathPrefix.length > 0) {
|
|
1573
1788
|
this.relativizeResObj(included, pathPrefix);
|
|
1574
1789
|
}
|
|
1575
|
-
deepMergeResObjInto(obj, included);
|
|
1790
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1576
1791
|
return;
|
|
1577
1792
|
}
|
|
1578
1793
|
const [head, ...tail] = field.key;
|
|
@@ -1587,12 +1802,27 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1587
1802
|
return;
|
|
1588
1803
|
}
|
|
1589
1804
|
const childPrefix = [...pathPrefix, head];
|
|
1805
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1590
1806
|
if (field.append) {
|
|
1591
1807
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1592
|
-
obj.priorValues.
|
|
1808
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1809
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1810
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1593
1811
|
obj.fields.set(head, {
|
|
1594
1812
|
_kind: "append-placeholder",
|
|
1595
|
-
existing
|
|
1813
|
+
// Use the folded existing (self-ref-free) for the Append. When the
|
|
1814
|
+
// fold returned undefined (self-ref present but no old prior),
|
|
1815
|
+
// fall back to the original existing — resolveSubst will then
|
|
1816
|
+
// raise the "self-ref with no prior value" error at resolve time,
|
|
1817
|
+
// matching the pre-fix behaviour for that edge case.
|
|
1818
|
+
//
|
|
1819
|
+
// Clone `folded` again so the Append.existing and priorValues[head]
|
|
1820
|
+
// do not share the same Subst/Concat references — relativizeResObj
|
|
1821
|
+
// walks BOTH `fields` and `priorValues` and mutates Subst.segments
|
|
1822
|
+
// in place, so a shared reference would get its prefix applied
|
|
1823
|
+
// twice (resulting in e.g. `${outer.outer.base}` instead of
|
|
1824
|
+
// `${outer.base}`). Codex review on PR #131.
|
|
1825
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1596
1826
|
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1597
1827
|
});
|
|
1598
1828
|
return;
|
|
@@ -1600,10 +1830,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1600
1830
|
const existing = obj.fields.get(head);
|
|
1601
1831
|
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1602
1832
|
if (existing !== void 0) {
|
|
1603
|
-
|
|
1833
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1834
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1835
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1836
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1604
1837
|
}
|
|
1605
1838
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1606
|
-
deepMergeResObjInto(existing, newVal);
|
|
1839
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1607
1840
|
return;
|
|
1608
1841
|
}
|
|
1609
1842
|
obj.fields.set(head, newVal);
|
|
@@ -1620,7 +1853,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1620
1853
|
if (pathPrefix.length > 0) {
|
|
1621
1854
|
this.relativizeResObj(included, pathPrefix);
|
|
1622
1855
|
}
|
|
1623
|
-
deepMergeResObjInto(obj, included);
|
|
1856
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1624
1857
|
return;
|
|
1625
1858
|
}
|
|
1626
1859
|
const [head, ...tail] = field.key;
|
|
@@ -1635,12 +1868,15 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1635
1868
|
return;
|
|
1636
1869
|
}
|
|
1637
1870
|
const childPrefix = [...pathPrefix, head];
|
|
1871
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1638
1872
|
if (field.append) {
|
|
1639
1873
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1640
|
-
obj.priorValues.
|
|
1874
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1875
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1876
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1641
1877
|
obj.fields.set(head, {
|
|
1642
1878
|
_kind: "append-placeholder",
|
|
1643
|
-
existing: existing2,
|
|
1879
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1644
1880
|
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1645
1881
|
});
|
|
1646
1882
|
return;
|
|
@@ -1648,10 +1884,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1648
1884
|
const existing = obj.fields.get(head);
|
|
1649
1885
|
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1650
1886
|
if (existing !== void 0) {
|
|
1651
|
-
|
|
1887
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1888
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1889
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1890
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1652
1891
|
}
|
|
1653
1892
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1654
|
-
deepMergeResObjInto(existing, newVal);
|
|
1893
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1655
1894
|
return;
|
|
1656
1895
|
}
|
|
1657
1896
|
obj.fields.set(head, newVal);
|
|
@@ -1783,6 +2022,23 @@ var SubstitutionResolver = class {
|
|
|
1783
2022
|
// narrower than path-equality. Spec amendment deferred to a follow-up
|
|
1784
2023
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1785
2024
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
2025
|
+
// Path stack tracking the full dotted path of the field currently being
|
|
2026
|
+
// assigned in `resolveResObj`. Leaf keys are pushed on entry to each
|
|
2027
|
+
// `(key, val)` iteration and popped on exit, so nested objects build up
|
|
2028
|
+
// the full path (e.g. `["o", "history"]` while resolving `o.history`).
|
|
2029
|
+
//
|
|
2030
|
+
// Used to tighten self-ref detection at the L192 found-lookup site:
|
|
2031
|
+
// a substitution `${X}` whose looked-up value contains a Subst pointing
|
|
2032
|
+
// at `X` is only a true self-reference when the field currently being
|
|
2033
|
+
// assigned is `X` (or a descendant of `X`). Without this guard, an
|
|
2034
|
+
// external lookup like `b = ${o}` (where o has a self-referential
|
|
2035
|
+
// field) would mis-fire and short-circuit to o's prior, returning the
|
|
2036
|
+
// wrong value for b. The is_owner gate (rfp prefix-matches s.segments)
|
|
2037
|
+
// narrows the check to the owner case only.
|
|
2038
|
+
//
|
|
2039
|
+
// Cross-impl with rs.hocon v1.5.1's resolving_field_path (same purpose,
|
|
2040
|
+
// same prefix-match semantics).
|
|
2041
|
+
this.resolvingFieldPath = [];
|
|
1786
2042
|
}
|
|
1787
2043
|
/** Returns a prefix for error messages, including origin info when set. */
|
|
1788
2044
|
originPrefix() {
|
|
@@ -1794,7 +2050,13 @@ var SubstitutionResolver = class {
|
|
|
1794
2050
|
resolveResObj(obj) {
|
|
1795
2051
|
const result = /* @__PURE__ */ new Map();
|
|
1796
2052
|
for (const [key, val] of obj.fields) {
|
|
1797
|
-
|
|
2053
|
+
this.resolvingFieldPath.push(key);
|
|
2054
|
+
let resolved;
|
|
2055
|
+
try {
|
|
2056
|
+
resolved = this.resolveVal(val, obj);
|
|
2057
|
+
} finally {
|
|
2058
|
+
this.resolvingFieldPath.pop();
|
|
2059
|
+
}
|
|
1798
2060
|
if (resolved !== void 0) {
|
|
1799
2061
|
if (resolved.kind === "object") {
|
|
1800
2062
|
const prior = obj.priorValues.get(key);
|
|
@@ -1884,7 +2146,10 @@ var SubstitutionResolver = class {
|
|
|
1884
2146
|
try {
|
|
1885
2147
|
const found = lookupPath(this.root, s.segments);
|
|
1886
2148
|
if (found !== void 0) {
|
|
1887
|
-
|
|
2149
|
+
const rfp = this.resolvingFieldPath;
|
|
2150
|
+
const isOwner = rfp.length >= s.segments.length && s.segments.every((seg, i) => rfp[i] === seg.text);
|
|
2151
|
+
const isSelfRef = isConcat(found) && this.resolvingConcats.has(found) || isOwner && containsSubstByPath(found, s.segments);
|
|
2152
|
+
if (isSelfRef) {
|
|
1888
2153
|
let prior;
|
|
1889
2154
|
if (s.segments.length > 1) {
|
|
1890
2155
|
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|
|
@@ -2095,8 +2360,33 @@ var SubstitutionResolver = class {
|
|
|
2095
2360
|
}
|
|
2096
2361
|
resolveAppend(a, scope) {
|
|
2097
2362
|
const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
|
|
2363
|
+
if (this.opts.allowUnresolved && (isSubst(existing) || isConcat(existing) || isAppend(existing))) {
|
|
2364
|
+
return a;
|
|
2365
|
+
}
|
|
2098
2366
|
const elem = this.resolveVal(a.elem, scope);
|
|
2099
|
-
|
|
2367
|
+
let priorItems;
|
|
2368
|
+
if (existing.kind === "array") {
|
|
2369
|
+
priorItems = existing.items;
|
|
2370
|
+
} else if (existing.kind === "object") {
|
|
2371
|
+
const converted = numericObjectToArray(existing);
|
|
2372
|
+
if (converted === null) {
|
|
2373
|
+
throw new ResolveError(
|
|
2374
|
+
`'+=' on non-array value: prior value is object with non-numeric keys (spec L732)`,
|
|
2375
|
+
"",
|
|
2376
|
+
0,
|
|
2377
|
+
0
|
|
2378
|
+
);
|
|
2379
|
+
}
|
|
2380
|
+
priorItems = converted;
|
|
2381
|
+
} else {
|
|
2382
|
+
throw new ResolveError(
|
|
2383
|
+
`'+=' on non-array value: prior value is ${existing.kind} (spec L732)`,
|
|
2384
|
+
"",
|
|
2385
|
+
0,
|
|
2386
|
+
0
|
|
2387
|
+
);
|
|
2388
|
+
}
|
|
2389
|
+
const items = [...priorItems];
|
|
2100
2390
|
if (elem !== void 0) items.push(elem);
|
|
2101
2391
|
return { kind: "array", items };
|
|
2102
2392
|
}
|
|
@@ -2194,6 +2484,9 @@ var Config = class _Config {
|
|
|
2194
2484
|
}
|
|
2195
2485
|
getString(path2) {
|
|
2196
2486
|
const v = this.requireScalar(path2);
|
|
2487
|
+
if (v.valueType === "null") {
|
|
2488
|
+
throw new ConfigError(`expected string at ${path2}, got ${v.valueType}`, path2);
|
|
2489
|
+
}
|
|
2197
2490
|
return v.raw;
|
|
2198
2491
|
}
|
|
2199
2492
|
getNumber(path2) {
|