@o3co/ts.hocon 1.5.0 → 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 +262 -17
- package/dist/index.js +262 -17
- package/dist/zod.cjs +262 -17
- package/dist/zod.js +262 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -338,6 +338,192 @@ function mergeUnresolved(receiver, fallback) {
|
|
|
338
338
|
return result;
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
// src/internal/resolver/fold-self-ref.ts
|
|
342
|
+
function substFullKey(s) {
|
|
343
|
+
return stringSegmentsToKey(s.segments.map((seg) => seg.text));
|
|
344
|
+
}
|
|
345
|
+
function stringSegmentsToKey(segments) {
|
|
346
|
+
return segments.map((t) => {
|
|
347
|
+
if (t === "" || /[^a-zA-Z0-9\-_]/.test(t)) {
|
|
348
|
+
return `"${t.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
349
|
+
}
|
|
350
|
+
return t;
|
|
351
|
+
}).join(".");
|
|
352
|
+
}
|
|
353
|
+
function containsSelfRef(v, fullKey) {
|
|
354
|
+
if (isSubst(v)) return substFullKey(v) === fullKey;
|
|
355
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
|
|
356
|
+
if (isAppend(v)) {
|
|
357
|
+
return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
|
|
358
|
+
}
|
|
359
|
+
if (isResObj(v)) {
|
|
360
|
+
for (const f of v.fields.values()) {
|
|
361
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
362
|
+
}
|
|
363
|
+
return false;
|
|
364
|
+
}
|
|
365
|
+
const hv = v;
|
|
366
|
+
if (hv.kind === "array") {
|
|
367
|
+
return hv.items.some((item) => containsSelfRef(item, fullKey));
|
|
368
|
+
}
|
|
369
|
+
if (hv.kind === "object") {
|
|
370
|
+
for (const f of hv.fields.values()) {
|
|
371
|
+
if (containsSelfRef(f, fullKey)) return true;
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
function foldSelfRef(v, fullKey, replacement) {
|
|
378
|
+
if (isSubst(v)) {
|
|
379
|
+
return substFullKey(v) === fullKey ? replacement : v;
|
|
380
|
+
}
|
|
381
|
+
if (isConcat(v)) {
|
|
382
|
+
return {
|
|
383
|
+
_kind: "concat-placeholder",
|
|
384
|
+
nodes: v.nodes.map((n) => foldSelfRef(n, fullKey, replacement)),
|
|
385
|
+
line: v.line,
|
|
386
|
+
col: v.col
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
if (isAppend(v)) {
|
|
390
|
+
return {
|
|
391
|
+
_kind: "append-placeholder",
|
|
392
|
+
existing: foldSelfRef(v.existing, fullKey, replacement),
|
|
393
|
+
elem: foldSelfRef(v.elem, fullKey, replacement)
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
if (isResObj(v)) {
|
|
397
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
398
|
+
for (const [k, val] of v.fields) {
|
|
399
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
400
|
+
}
|
|
401
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
402
|
+
}
|
|
403
|
+
const hv = v;
|
|
404
|
+
if (hv.kind === "array") {
|
|
405
|
+
return {
|
|
406
|
+
kind: "array",
|
|
407
|
+
items: hv.items.map((item) => foldSelfRef(item, fullKey, replacement))
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
if (hv.kind === "object") {
|
|
411
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
412
|
+
for (const [k, val] of hv.fields) {
|
|
413
|
+
newFields.set(k, foldSelfRef(val, fullKey, replacement));
|
|
414
|
+
}
|
|
415
|
+
return { kind: "object", fields: newFields };
|
|
416
|
+
}
|
|
417
|
+
return v;
|
|
418
|
+
}
|
|
419
|
+
function foldOrSkipPrior(prior, fullKey, old) {
|
|
420
|
+
if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
|
|
421
|
+
if (old === void 0) return void 0;
|
|
422
|
+
return foldSelfRef(prior, fullKey, old);
|
|
423
|
+
}
|
|
424
|
+
function cloneResolverValue(v) {
|
|
425
|
+
if (isSubst(v)) {
|
|
426
|
+
return {
|
|
427
|
+
_kind: "subst-placeholder",
|
|
428
|
+
segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
|
|
429
|
+
optional: v.optional,
|
|
430
|
+
listSuffix: v.listSuffix,
|
|
431
|
+
line: v.line,
|
|
432
|
+
col: v.col,
|
|
433
|
+
prefixLen: v.prefixLen
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
if (isConcat(v)) {
|
|
437
|
+
return {
|
|
438
|
+
_kind: "concat-placeholder",
|
|
439
|
+
nodes: v.nodes.map((n) => cloneResolverValue(n)),
|
|
440
|
+
line: v.line,
|
|
441
|
+
col: v.col
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
if (isResObj(v)) {
|
|
445
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
446
|
+
for (const [k, val] of v.fields) newFields.set(k, cloneResolverValue(val));
|
|
447
|
+
const newPriors = /* @__PURE__ */ new Map();
|
|
448
|
+
for (const [k, val] of v.priorValues) newPriors.set(k, cloneResolverValue(val));
|
|
449
|
+
return { _kind: "res-obj", fields: newFields, priorValues: newPriors };
|
|
450
|
+
}
|
|
451
|
+
if (isAppend(v)) {
|
|
452
|
+
return {
|
|
453
|
+
_kind: "append-placeholder",
|
|
454
|
+
existing: cloneResolverValue(v.existing),
|
|
455
|
+
elem: cloneResolverValue(v.elem)
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
const hv = v;
|
|
459
|
+
if (hv.kind === "array") {
|
|
460
|
+
return {
|
|
461
|
+
kind: "array",
|
|
462
|
+
items: hv.items.map((item) => cloneResolverValue(item))
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
if (hv.kind === "object") {
|
|
466
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
467
|
+
for (const [k, val] of hv.fields) {
|
|
468
|
+
newFields.set(k, cloneResolverValue(val));
|
|
469
|
+
}
|
|
470
|
+
return { kind: "object", fields: newFields };
|
|
471
|
+
}
|
|
472
|
+
return hv;
|
|
473
|
+
}
|
|
474
|
+
function foldNestedSelfRefs(v, pathPrefix) {
|
|
475
|
+
if (!isResObj(v)) return v;
|
|
476
|
+
const newFields = /* @__PURE__ */ new Map();
|
|
477
|
+
for (const [k, fieldVal] of v.fields) {
|
|
478
|
+
const childPath = [...pathPrefix, k];
|
|
479
|
+
const fullKey = stringSegmentsToKey(childPath);
|
|
480
|
+
const folded = foldNestedSelfRefs(fieldVal, childPath);
|
|
481
|
+
let finalVal = folded;
|
|
482
|
+
if (containsSelfRef(folded, fullKey)) {
|
|
483
|
+
const leafPrior = v.priorValues.get(k);
|
|
484
|
+
if (leafPrior !== void 0) {
|
|
485
|
+
const leafPriorFolded = foldNestedSelfRefs(leafPrior, childPath);
|
|
486
|
+
finalVal = foldSelfRef(folded, fullKey, leafPriorFolded);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
newFields.set(k, finalVal);
|
|
490
|
+
}
|
|
491
|
+
return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
|
|
492
|
+
}
|
|
493
|
+
function containsSubstByPath(v, target) {
|
|
494
|
+
if (isSubst(v)) return segmentsTextEqual(v.segments, target);
|
|
495
|
+
if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
|
|
496
|
+
if (isAppend(v)) {
|
|
497
|
+
return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
|
|
498
|
+
}
|
|
499
|
+
if (isResObj(v)) {
|
|
500
|
+
for (const f of v.fields.values()) {
|
|
501
|
+
if (containsSubstByPath(f, target)) return true;
|
|
502
|
+
}
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
const hv = v;
|
|
506
|
+
if (hv.kind === "array") {
|
|
507
|
+
return hv.items.some((item) => containsSubstByPath(item, target));
|
|
508
|
+
}
|
|
509
|
+
if (hv.kind === "object") {
|
|
510
|
+
for (const f of hv.fields.values()) {
|
|
511
|
+
if (containsSubstByPath(f, target)) return true;
|
|
512
|
+
}
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
function segmentsTextEqual(a, b) {
|
|
518
|
+
if (a.length !== b.length) return false;
|
|
519
|
+
for (let i = 0; i < a.length; i++) {
|
|
520
|
+
const ai = a[i];
|
|
521
|
+
const bi = b[i];
|
|
522
|
+
if (ai === void 0 || bi === void 0 || ai.text !== bi.text) return false;
|
|
523
|
+
}
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
|
|
341
527
|
// src/internal/resolver/utils.ts
|
|
342
528
|
function segmentsToKey(segments) {
|
|
343
529
|
return segments.map((s) => {
|
|
@@ -379,13 +565,22 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
379
565
|
}
|
|
380
566
|
return { kind: "object", fields: merged };
|
|
381
567
|
}
|
|
382
|
-
function deepMergeResObjInto(dst, src) {
|
|
568
|
+
function deepMergeResObjInto(dst, src, pathPrefix = []) {
|
|
383
569
|
for (const [k, srcVal] of src.fields) {
|
|
570
|
+
const childPrefix = [...pathPrefix, k];
|
|
571
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
384
572
|
const dstVal = dst.fields.get(k);
|
|
385
573
|
if (dstVal !== void 0 && isResObj(dstVal) && isResObj(srcVal)) {
|
|
386
|
-
|
|
574
|
+
const priorExisting = dst.priorValues.get(k);
|
|
575
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
576
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
577
|
+
deepMergeResObjInto(dstVal, srcVal, childPrefix);
|
|
387
578
|
} else {
|
|
388
|
-
if (dstVal !== void 0)
|
|
579
|
+
if (dstVal !== void 0) {
|
|
580
|
+
const priorExisting = dst.priorValues.get(k);
|
|
581
|
+
const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
|
|
582
|
+
if (prior !== void 0) dst.priorValues.set(k, prior);
|
|
583
|
+
}
|
|
389
584
|
dst.fields.set(k, srcVal);
|
|
390
585
|
}
|
|
391
586
|
}
|
|
@@ -1396,7 +1591,7 @@ var IncludeLoader = class {
|
|
|
1396
1591
|
for (const ext of probeExts) {
|
|
1397
1592
|
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
1398
1593
|
if (obj !== void 0) {
|
|
1399
|
-
deepMergeResObjInto(merged, obj);
|
|
1594
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1400
1595
|
foundAny = true;
|
|
1401
1596
|
}
|
|
1402
1597
|
}
|
|
@@ -1429,7 +1624,7 @@ var IncludeLoader = class {
|
|
|
1429
1624
|
for (const ext of probeExts) {
|
|
1430
1625
|
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
1431
1626
|
if (obj !== void 0) {
|
|
1432
|
-
deepMergeResObjInto(merged, obj);
|
|
1627
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1433
1628
|
foundAny = true;
|
|
1434
1629
|
}
|
|
1435
1630
|
}
|
|
@@ -1601,7 +1796,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1601
1796
|
if (pathPrefix.length > 0) {
|
|
1602
1797
|
this.relativizeResObj(included, pathPrefix);
|
|
1603
1798
|
}
|
|
1604
|
-
deepMergeResObjInto(obj, included);
|
|
1799
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1605
1800
|
return;
|
|
1606
1801
|
}
|
|
1607
1802
|
const [head, ...tail] = field.key;
|
|
@@ -1616,12 +1811,27 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1616
1811
|
return;
|
|
1617
1812
|
}
|
|
1618
1813
|
const childPrefix = [...pathPrefix, head];
|
|
1814
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1619
1815
|
if (field.append) {
|
|
1620
1816
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1621
|
-
obj.priorValues.
|
|
1817
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1818
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1819
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1622
1820
|
obj.fields.set(head, {
|
|
1623
1821
|
_kind: "append-placeholder",
|
|
1624
|
-
existing
|
|
1822
|
+
// Use the folded existing (self-ref-free) for the Append. When the
|
|
1823
|
+
// fold returned undefined (self-ref present but no old prior),
|
|
1824
|
+
// fall back to the original existing — resolveSubst will then
|
|
1825
|
+
// raise the "self-ref with no prior value" error at resolve time,
|
|
1826
|
+
// matching the pre-fix behaviour for that edge case.
|
|
1827
|
+
//
|
|
1828
|
+
// Clone `folded` again so the Append.existing and priorValues[head]
|
|
1829
|
+
// do not share the same Subst/Concat references — relativizeResObj
|
|
1830
|
+
// walks BOTH `fields` and `priorValues` and mutates Subst.segments
|
|
1831
|
+
// in place, so a shared reference would get its prefix applied
|
|
1832
|
+
// twice (resulting in e.g. `${outer.outer.base}` instead of
|
|
1833
|
+
// `${outer.base}`). Codex review on PR #131.
|
|
1834
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1625
1835
|
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1626
1836
|
});
|
|
1627
1837
|
return;
|
|
@@ -1629,10 +1839,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1629
1839
|
const existing = obj.fields.get(head);
|
|
1630
1840
|
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1631
1841
|
if (existing !== void 0) {
|
|
1632
|
-
|
|
1842
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1843
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1844
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1845
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1633
1846
|
}
|
|
1634
1847
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1635
|
-
deepMergeResObjInto(existing, newVal);
|
|
1848
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1636
1849
|
return;
|
|
1637
1850
|
}
|
|
1638
1851
|
obj.fields.set(head, newVal);
|
|
@@ -1649,7 +1862,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1649
1862
|
if (pathPrefix.length > 0) {
|
|
1650
1863
|
this.relativizeResObj(included, pathPrefix);
|
|
1651
1864
|
}
|
|
1652
|
-
deepMergeResObjInto(obj, included);
|
|
1865
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1653
1866
|
return;
|
|
1654
1867
|
}
|
|
1655
1868
|
const [head, ...tail] = field.key;
|
|
@@ -1664,12 +1877,15 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1664
1877
|
return;
|
|
1665
1878
|
}
|
|
1666
1879
|
const childPrefix = [...pathPrefix, head];
|
|
1880
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1667
1881
|
if (field.append) {
|
|
1668
1882
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1669
|
-
obj.priorValues.
|
|
1883
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1884
|
+
const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
|
|
1885
|
+
if (folded !== void 0) obj.priorValues.set(head, folded);
|
|
1670
1886
|
obj.fields.set(head, {
|
|
1671
1887
|
_kind: "append-placeholder",
|
|
1672
|
-
existing: existing2,
|
|
1888
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1673
1889
|
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1674
1890
|
});
|
|
1675
1891
|
return;
|
|
@@ -1677,10 +1893,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1677
1893
|
const existing = obj.fields.get(head);
|
|
1678
1894
|
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1679
1895
|
if (existing !== void 0) {
|
|
1680
|
-
|
|
1896
|
+
const exFolded = foldNestedSelfRefs(existing, childPrefix);
|
|
1897
|
+
const oldPrior = obj.priorValues.get(head);
|
|
1898
|
+
const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
|
|
1899
|
+
if (prior !== void 0) obj.priorValues.set(head, prior);
|
|
1681
1900
|
}
|
|
1682
1901
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1683
|
-
deepMergeResObjInto(existing, newVal);
|
|
1902
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1684
1903
|
return;
|
|
1685
1904
|
}
|
|
1686
1905
|
obj.fields.set(head, newVal);
|
|
@@ -1812,6 +2031,23 @@ var SubstitutionResolver = class {
|
|
|
1812
2031
|
// narrower than path-equality. Spec amendment deferred to a follow-up
|
|
1813
2032
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1814
2033
|
this.resolvingConcats = /* @__PURE__ */ new WeakSet();
|
|
2034
|
+
// Path stack tracking the full dotted path of the field currently being
|
|
2035
|
+
// assigned in `resolveResObj`. Leaf keys are pushed on entry to each
|
|
2036
|
+
// `(key, val)` iteration and popped on exit, so nested objects build up
|
|
2037
|
+
// the full path (e.g. `["o", "history"]` while resolving `o.history`).
|
|
2038
|
+
//
|
|
2039
|
+
// Used to tighten self-ref detection at the L192 found-lookup site:
|
|
2040
|
+
// a substitution `${X}` whose looked-up value contains a Subst pointing
|
|
2041
|
+
// at `X` is only a true self-reference when the field currently being
|
|
2042
|
+
// assigned is `X` (or a descendant of `X`). Without this guard, an
|
|
2043
|
+
// external lookup like `b = ${o}` (where o has a self-referential
|
|
2044
|
+
// field) would mis-fire and short-circuit to o's prior, returning the
|
|
2045
|
+
// wrong value for b. The is_owner gate (rfp prefix-matches s.segments)
|
|
2046
|
+
// narrows the check to the owner case only.
|
|
2047
|
+
//
|
|
2048
|
+
// Cross-impl with rs.hocon v1.5.1's resolving_field_path (same purpose,
|
|
2049
|
+
// same prefix-match semantics).
|
|
2050
|
+
this.resolvingFieldPath = [];
|
|
1815
2051
|
}
|
|
1816
2052
|
/** Returns a prefix for error messages, including origin info when set. */
|
|
1817
2053
|
originPrefix() {
|
|
@@ -1823,7 +2059,13 @@ var SubstitutionResolver = class {
|
|
|
1823
2059
|
resolveResObj(obj) {
|
|
1824
2060
|
const result = /* @__PURE__ */ new Map();
|
|
1825
2061
|
for (const [key, val] of obj.fields) {
|
|
1826
|
-
|
|
2062
|
+
this.resolvingFieldPath.push(key);
|
|
2063
|
+
let resolved;
|
|
2064
|
+
try {
|
|
2065
|
+
resolved = this.resolveVal(val, obj);
|
|
2066
|
+
} finally {
|
|
2067
|
+
this.resolvingFieldPath.pop();
|
|
2068
|
+
}
|
|
1827
2069
|
if (resolved !== void 0) {
|
|
1828
2070
|
if (resolved.kind === "object") {
|
|
1829
2071
|
const prior = obj.priorValues.get(key);
|
|
@@ -1913,7 +2155,10 @@ var SubstitutionResolver = class {
|
|
|
1913
2155
|
try {
|
|
1914
2156
|
const found = lookupPath(this.root, s.segments);
|
|
1915
2157
|
if (found !== void 0) {
|
|
1916
|
-
|
|
2158
|
+
const rfp = this.resolvingFieldPath;
|
|
2159
|
+
const isOwner = rfp.length >= s.segments.length && s.segments.every((seg, i) => rfp[i] === seg.text);
|
|
2160
|
+
const isSelfRef = isConcat(found) && this.resolvingConcats.has(found) || isOwner && containsSubstByPath(found, s.segments);
|
|
2161
|
+
if (isSelfRef) {
|
|
1917
2162
|
let prior;
|
|
1918
2163
|
if (s.segments.length > 1) {
|
|
1919
2164
|
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|