@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.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
|
}
|
|
@@ -1343,7 +1538,7 @@ var IncludeLoader = class {
|
|
|
1343
1538
|
for (const ext of probeExts) {
|
|
1344
1539
|
const obj = this.loadSingle(`${absPath}${ext}`);
|
|
1345
1540
|
if (obj !== void 0) {
|
|
1346
|
-
deepMergeResObjInto(merged, obj);
|
|
1541
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1347
1542
|
foundAny = true;
|
|
1348
1543
|
}
|
|
1349
1544
|
}
|
|
@@ -1376,7 +1571,7 @@ var IncludeLoader = class {
|
|
|
1376
1571
|
for (const ext of probeExts) {
|
|
1377
1572
|
const obj = await this.loadSingleAsync(`${absPath}${ext}`);
|
|
1378
1573
|
if (obj !== void 0) {
|
|
1379
|
-
deepMergeResObjInto(merged, obj);
|
|
1574
|
+
deepMergeResObjInto(merged, obj, []);
|
|
1380
1575
|
foundAny = true;
|
|
1381
1576
|
}
|
|
1382
1577
|
}
|
|
@@ -1548,7 +1743,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1548
1743
|
if (pathPrefix.length > 0) {
|
|
1549
1744
|
this.relativizeResObj(included, pathPrefix);
|
|
1550
1745
|
}
|
|
1551
|
-
deepMergeResObjInto(obj, included);
|
|
1746
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1552
1747
|
return;
|
|
1553
1748
|
}
|
|
1554
1749
|
const [head, ...tail] = field.key;
|
|
@@ -1563,12 +1758,27 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1563
1758
|
return;
|
|
1564
1759
|
}
|
|
1565
1760
|
const childPrefix = [...pathPrefix, head];
|
|
1761
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1566
1762
|
if (field.append) {
|
|
1567
1763
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1568
|
-
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);
|
|
1569
1767
|
obj.fields.set(head, {
|
|
1570
1768
|
_kind: "append-placeholder",
|
|
1571
|
-
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,
|
|
1572
1782
|
elem: this.astToResolverValue(field.value, childPrefix)
|
|
1573
1783
|
});
|
|
1574
1784
|
return;
|
|
@@ -1576,10 +1786,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1576
1786
|
const existing = obj.fields.get(head);
|
|
1577
1787
|
const newVal = this.astToResolverValue(field.value, childPrefix);
|
|
1578
1788
|
if (existing !== void 0) {
|
|
1579
|
-
|
|
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);
|
|
1580
1793
|
}
|
|
1581
1794
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1582
|
-
deepMergeResObjInto(existing, newVal);
|
|
1795
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1583
1796
|
return;
|
|
1584
1797
|
}
|
|
1585
1798
|
obj.fields.set(head, newVal);
|
|
@@ -1596,7 +1809,7 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1596
1809
|
if (pathPrefix.length > 0) {
|
|
1597
1810
|
this.relativizeResObj(included, pathPrefix);
|
|
1598
1811
|
}
|
|
1599
|
-
deepMergeResObjInto(obj, included);
|
|
1812
|
+
deepMergeResObjInto(obj, included, pathPrefix);
|
|
1600
1813
|
return;
|
|
1601
1814
|
}
|
|
1602
1815
|
const [head, ...tail] = field.key;
|
|
@@ -1611,12 +1824,15 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1611
1824
|
return;
|
|
1612
1825
|
}
|
|
1613
1826
|
const childPrefix = [...pathPrefix, head];
|
|
1827
|
+
const fullKey = stringSegmentsToKey(childPrefix);
|
|
1614
1828
|
if (field.append) {
|
|
1615
1829
|
const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
|
|
1616
|
-
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);
|
|
1617
1833
|
obj.fields.set(head, {
|
|
1618
1834
|
_kind: "append-placeholder",
|
|
1619
|
-
existing: existing2,
|
|
1835
|
+
existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
|
|
1620
1836
|
elem: await this.astToResolverValueAsync(field.value, childPrefix)
|
|
1621
1837
|
});
|
|
1622
1838
|
return;
|
|
@@ -1624,10 +1840,13 @@ var StructureBuilder = class _StructureBuilder {
|
|
|
1624
1840
|
const existing = obj.fields.get(head);
|
|
1625
1841
|
const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
|
|
1626
1842
|
if (existing !== void 0) {
|
|
1627
|
-
|
|
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);
|
|
1628
1847
|
}
|
|
1629
1848
|
if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
|
|
1630
|
-
deepMergeResObjInto(existing, newVal);
|
|
1849
|
+
deepMergeResObjInto(existing, newVal, childPrefix);
|
|
1631
1850
|
return;
|
|
1632
1851
|
}
|
|
1633
1852
|
obj.fields.set(head, newVal);
|
|
@@ -1759,6 +1978,23 @@ var SubstitutionResolver = class {
|
|
|
1759
1978
|
// narrower than path-equality. Spec amendment deferred to a follow-up
|
|
1760
1979
|
// xx.hocon PR (see Phase 6 #3f close-out notes).
|
|
1761
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 = [];
|
|
1762
1998
|
}
|
|
1763
1999
|
/** Returns a prefix for error messages, including origin info when set. */
|
|
1764
2000
|
originPrefix() {
|
|
@@ -1770,7 +2006,13 @@ var SubstitutionResolver = class {
|
|
|
1770
2006
|
resolveResObj(obj) {
|
|
1771
2007
|
const result = /* @__PURE__ */ new Map();
|
|
1772
2008
|
for (const [key, val] of obj.fields) {
|
|
1773
|
-
|
|
2009
|
+
this.resolvingFieldPath.push(key);
|
|
2010
|
+
let resolved;
|
|
2011
|
+
try {
|
|
2012
|
+
resolved = this.resolveVal(val, obj);
|
|
2013
|
+
} finally {
|
|
2014
|
+
this.resolvingFieldPath.pop();
|
|
2015
|
+
}
|
|
1774
2016
|
if (resolved !== void 0) {
|
|
1775
2017
|
if (resolved.kind === "object") {
|
|
1776
2018
|
const prior = obj.priorValues.get(key);
|
|
@@ -1860,7 +2102,10 @@ var SubstitutionResolver = class {
|
|
|
1860
2102
|
try {
|
|
1861
2103
|
const found = lookupPath(this.root, s.segments);
|
|
1862
2104
|
if (found !== void 0) {
|
|
1863
|
-
|
|
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) {
|
|
1864
2109
|
let prior;
|
|
1865
2110
|
if (s.segments.length > 1) {
|
|
1866
2111
|
const leafSeg = s.segments[s.segments.length - 1]?.text ?? "";
|