@o3co/ts.hocon 1.10.0 → 1.12.0

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.js CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/coerce.ts
2
9
  var DECIMAL_NUMBER_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
3
10
  function isHoconWs(cp) {
@@ -639,6 +646,7 @@ function isFileNotFoundError(e) {
639
646
  // src/internal/resolver/include-loader.ts
640
647
  import { createRequire } from "module";
641
648
  import * as nodePath from "path";
649
+ import { pathToFileURL } from "url";
642
650
 
643
651
  // src/internal/lexer/lexer.ts
644
652
  function isHoconWhitespace(ch) {
@@ -1481,25 +1489,80 @@ function parseTokens(tokens) {
1481
1489
  return new Parser(tokens).parse();
1482
1490
  }
1483
1491
 
1492
+ // src/internal/strip-bom.ts
1493
+ function stripBom(input) {
1494
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
1495
+ }
1496
+
1497
+ // src/internal/depth.ts
1498
+ var MAX_PATH_SEGMENTS = 64;
1499
+ function tooDeep(segments) {
1500
+ return segments > MAX_PATH_SEGMENTS;
1501
+ }
1502
+ function guardStackDepth(fn, wrap) {
1503
+ try {
1504
+ return fn();
1505
+ } catch (e) {
1506
+ if (!isStackOverflow(e)) throw e;
1507
+ throw wrap(STACK_MESSAGE);
1508
+ }
1509
+ }
1510
+ var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
1511
+ function isStackOverflow(e) {
1512
+ if (!(e instanceof RangeError)) return false;
1513
+ const m = e.message.toLowerCase();
1514
+ return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
1515
+ m.includes("recursion") || // SpiderMonkey: "too much recursion"
1516
+ m.includes("stack overflow");
1517
+ }
1518
+ function depthError(message) {
1519
+ return new ConfigError(message, "");
1520
+ }
1521
+ async function guardStackDepthAsync(fn, wrap) {
1522
+ try {
1523
+ return await fn();
1524
+ } catch (e) {
1525
+ if (!isStackOverflow(e)) throw e;
1526
+ throw wrap(STACK_MESSAGE);
1527
+ }
1528
+ }
1529
+
1484
1530
  // src/internal/properties/properties.ts
1485
1531
  function parseProperties(input) {
1486
1532
  const pairs = [];
1487
- for (const { text, line } of logicalLines(input)) {
1533
+ for (const { text, line } of logicalLines(stripBom(input))) {
1488
1534
  const [rawKey, rawValue] = splitKeyValue(text);
1489
1535
  const key = unescapeProps(rawKey, line);
1490
1536
  if (key === "") continue;
1491
- pairs.push([key, unescapeProps(rawValue, line)]);
1537
+ const segments = key.split(".");
1538
+ if (tooDeep(segments.length)) {
1539
+ throw new ParseError(
1540
+ `key maps to a path ${segments.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
1541
+ line,
1542
+ 1
1543
+ );
1544
+ }
1545
+ pairs.push([segments, unescapeProps(rawValue, line)]);
1492
1546
  }
1493
1547
  return nestPairs(pairs);
1494
1548
  }
1495
1549
  function nestPairs(pairs) {
1496
1550
  const root = /* @__PURE__ */ Object.create(null);
1497
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1498
- for (const [key, value] of sorted) {
1499
- setNested(root, key.split("."), value);
1551
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
1552
+ for (const [segments, value] of sorted) {
1553
+ setNested(root, segments, value);
1500
1554
  }
1501
1555
  return root;
1502
1556
  }
1557
+ function compareSegments(a, b) {
1558
+ const n = Math.min(a.length, b.length);
1559
+ for (let i = 0; i < n; i++) {
1560
+ const x = a[i];
1561
+ const y = b[i];
1562
+ if (x !== y) return x < y ? -1 : 1;
1563
+ }
1564
+ return a.length - b.length;
1565
+ }
1503
1566
  function logicalLines(input) {
1504
1567
  const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1505
1568
  const out = [];
@@ -1586,19 +1649,16 @@ function unescapeProps(s, line) {
1586
1649
  }
1587
1650
  return out;
1588
1651
  }
1589
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1590
1652
  function setNested(obj, segments, value) {
1591
1653
  let current = obj;
1592
1654
  for (let i = 0; i < segments.length - 1; i++) {
1593
1655
  const seg = segments[i];
1594
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
1595
1656
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
1596
1657
  current[seg] = /* @__PURE__ */ Object.create(null);
1597
1658
  }
1598
1659
  current = current[seg];
1599
1660
  }
1600
1661
  const last = segments[segments.length - 1];
1601
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
1602
1662
  if (typeof current[last] === "object" && current[last] !== null) return;
1603
1663
  current[last] = value;
1604
1664
  }
@@ -1619,7 +1679,22 @@ function recordToHoconValue(obj) {
1619
1679
  }
1620
1680
 
1621
1681
  // src/internal/resolver/include-loader.ts
1622
- var _require = createRequire(import.meta.url);
1682
+ var _require;
1683
+ function getRequire() {
1684
+ if (_require === void 0) {
1685
+ _require = pickRequire(
1686
+ typeof __require === "function" ? __require : void 0,
1687
+ import.meta.url
1688
+ );
1689
+ }
1690
+ return _require;
1691
+ }
1692
+ function pickRequire(candidate, metaUrl) {
1693
+ if (typeof candidate?.resolve === "function") {
1694
+ return candidate;
1695
+ }
1696
+ return createRequire(metaUrl ?? pathToFileURL(nodePath.join(process.cwd(), "noop.js")));
1697
+ }
1623
1698
  function isPnpPath(p) {
1624
1699
  return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
1625
1700
  }
@@ -1675,7 +1750,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
1675
1750
  const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
1676
1751
  let resolved;
1677
1752
  try {
1678
- resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
1753
+ resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
1679
1754
  } catch {
1680
1755
  throw new PackageLookupError(
1681
1756
  `include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
@@ -2729,10 +2804,10 @@ var Config = class _Config {
2729
2804
  }
2730
2805
  if (v.kind === "object") {
2731
2806
  const converted = numericObjectToArray(v);
2732
- if (converted !== null) return converted.map(hoconToJs);
2807
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
2733
2808
  }
2734
2809
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
2735
- return v.items.map(hoconToJs);
2810
+ return v.items.map((item) => hoconToJs(item));
2736
2811
  }
2737
2812
  has(path2) {
2738
2813
  return this.lookupNode(path2) !== void 0;
@@ -2860,8 +2935,52 @@ var Config = class _Config {
2860
2935
  originDescription: this._originDescription
2861
2936
  });
2862
2937
  }
2863
- toObject() {
2864
- return hoconToJs(this.root);
2938
+ /**
2939
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
2940
+ * booleans and `null`.
2941
+ *
2942
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
2943
+ * `constructor` and `prototype` included: each is defined as an **own data
2944
+ * property**, so the returned object's prototype is always
2945
+ * `Object.prototype`, and this call writes nothing to the global one.
2946
+ *
2947
+ * ### Passing the result to other code
2948
+ *
2949
+ * A config file — especially a `.properties` file or an env namespace you
2950
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
2951
+ * next depends entirely on how the consumer copies it:
2952
+ *
2953
+ * ```ts
2954
+ * const data = cfg.toObject()
2955
+ * { ...data } // safe — own properties are copied as data
2956
+ * structuredClone(data) // safe
2957
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
2958
+ * // Object.prototype's __proto__ setter: the key
2959
+ * // vanishes and config data becomes the result's
2960
+ * // prototype
2961
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
2962
+ * // target['__proto__'], gets Object.prototype,
2963
+ * // and writes to it: global pollution
2964
+ * for (const k in data) … // prefer Object.entries / Object.keys
2965
+ * ```
2966
+ *
2967
+ * Those two hazards come from the **destination** object's prototype, so no
2968
+ * option here can disarm them — copy with spread or `structuredClone` instead.
2969
+ *
2970
+ * What `{ nullPrototype: true }` does give you is a result that inherits
2971
+ * nothing: every object comes back with a `null` prototype, so reading
2972
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
2973
+ * `undefined` rather than something from `Object.prototype`, and code that
2974
+ * merges *into* this object cannot climb out of it. Keys are all still
2975
+ * present. The trade-off is the usual one for prototype-less objects: not
2976
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
2977
+ *
2978
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
2979
+ * here even when the config holds it exactly — read those with
2980
+ * {@link Config.getString} instead.
2981
+ */
2982
+ toObject(opts) {
2983
+ return hoconToJs(this.root, opts?.nullPrototype === true);
2865
2984
  }
2866
2985
  /**
2867
2986
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -2973,16 +3092,25 @@ function scalarToJs(raw, valueType) {
2973
3092
  return raw;
2974
3093
  }
2975
3094
  }
2976
- function hoconToJs(v) {
3095
+ function hoconToJs(v, nullPrototype = false) {
2977
3096
  switch (v.kind) {
2978
3097
  case "scalar":
2979
3098
  return scalarToJs(v.raw, v.valueType);
2980
3099
  case "array":
2981
- return v.items.map(hoconToJs);
3100
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
2982
3101
  case "object": {
2983
- const obj = /* @__PURE__ */ Object.create(null);
2984
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
2985
- return Object.assign({}, obj);
3102
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
3103
+ if (nullPrototype) {
3104
+ const obj = /* @__PURE__ */ Object.create(null);
3105
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
3106
+ value: val,
3107
+ writable: true,
3108
+ enumerable: true,
3109
+ configurable: true
3110
+ });
3111
+ return obj;
3112
+ }
3113
+ return Object.fromEntries(entries);
2986
3114
  }
2987
3115
  }
2988
3116
  }
@@ -3107,13 +3235,16 @@ function buildResolveContext(input, opts) {
3107
3235
  return { ast, resolveOpts };
3108
3236
  }
3109
3237
  function parse(input, opts = {}) {
3110
- const { ast, resolveOpts } = buildResolveContext(input, opts);
3111
- const value = resolve2(ast, resolveOpts);
3112
- if (value.kind !== "object") throw new Error("resolved value is not an object");
3113
- return new Config(value, { resolved: true, originDescription: opts.originDescription });
3238
+ return parseStringWithOptions(input, opts);
3114
3239
  }
3115
3240
  var parseString = parse;
3116
3241
  function parseStringWithOptions(input, opts = {}) {
3242
+ return guardStackDepth(
3243
+ () => parseStringInner(input, opts),
3244
+ (msg) => new ParseError(msg, 1, 1, opts.originDescription)
3245
+ );
3246
+ }
3247
+ function parseStringInner(input, opts) {
3117
3248
  const resolveSubstitutions = opts.resolveSubstitutions ?? true;
3118
3249
  const { ast, resolveOpts } = buildResolveContext(input, opts);
3119
3250
  if (resolveSubstitutions) {
@@ -3144,10 +3275,12 @@ function parseFileWithOptions(filePath, opts = {}) {
3144
3275
  });
3145
3276
  }
3146
3277
  async function parseAsync(input, opts = {}) {
3147
- const { ast, resolveOpts } = buildResolveContext(input, opts);
3148
- const value = await resolveAsync(ast, resolveOpts);
3149
- if (value.kind !== "object") throw new Error("resolved value is not an object");
3150
- return new Config(value);
3278
+ return guardStackDepthAsync(async () => {
3279
+ const { ast, resolveOpts } = buildResolveContext(input, opts);
3280
+ const value = await resolveAsync(ast, resolveOpts);
3281
+ if (value.kind !== "object") throw new Error("resolved value is not an object");
3282
+ return new Config(value);
3283
+ }, (msg) => new ParseError(msg, 1, 1, opts.originDescription));
3151
3284
  }
3152
3285
  function parseFile(filePath, opts = {}) {
3153
3286
  if (typeof process === "undefined" && !opts.readFileSync) {
@@ -3208,7 +3341,10 @@ function isNull(v) {
3208
3341
 
3209
3342
  // src/value-factory.ts
3210
3343
  function fromMap(values, originDescription) {
3211
- const fields = coerceObject(values, "");
3344
+ const fields = guardStackDepth(
3345
+ () => coerceObject(values, ""),
3346
+ (msg) => depthError(`fromMap: ${msg}`)
3347
+ );
3212
3348
  const root = { kind: "object", fields };
3213
3349
  return Config._fromResolvedValue(root, { originDescription });
3214
3350
  }
@@ -3216,6 +3352,8 @@ function empty(originDescription) {
3216
3352
  const root = { kind: "object", fields: /* @__PURE__ */ new Map() };
3217
3353
  return Config._fromResolvedValue(root, { originDescription });
3218
3354
  }
3355
+ var INT64_MAX = 9223372036854775807n;
3356
+ var INT64_MIN = -9223372036854775808n;
3219
3357
  function coerceObject(obj, atPath) {
3220
3358
  const fields = /* @__PURE__ */ new Map();
3221
3359
  for (const [k, v] of Object.entries(obj)) {
@@ -3244,14 +3382,14 @@ function coerceValue(v, atPath) {
3244
3382
  atPath
3245
3383
  );
3246
3384
  }
3247
- return { kind: "scalar", raw: String(v), valueType: "number" };
3385
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
3248
3386
  }
3249
3387
  case "boolean":
3250
3388
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
3251
3389
  case "bigint": {
3252
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
3390
+ if (v > INT64_MAX || v < INT64_MIN) {
3253
3391
  throw new ConfigError(
3254
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
3392
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
3255
3393
  atPath
3256
3394
  );
3257
3395
  }
package/dist/zod.cjs CHANGED
@@ -682,6 +682,7 @@ function isFileNotFoundError(e) {
682
682
  // src/internal/resolver/include-loader.ts
683
683
  var import_node_module = require("module");
684
684
  var nodePath = __toESM(require("path"), 1);
685
+ var import_node_url = require("url");
685
686
 
686
687
  // src/internal/lexer/lexer.ts
687
688
  function isHoconWhitespace(ch) {
@@ -1524,25 +1525,69 @@ function parseTokens(tokens) {
1524
1525
  return new Parser(tokens).parse();
1525
1526
  }
1526
1527
 
1528
+ // src/internal/strip-bom.ts
1529
+ function stripBom(input) {
1530
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
1531
+ }
1532
+
1533
+ // src/internal/depth.ts
1534
+ var MAX_PATH_SEGMENTS = 64;
1535
+ function tooDeep(segments) {
1536
+ return segments > MAX_PATH_SEGMENTS;
1537
+ }
1538
+ function guardStackDepth(fn, wrap) {
1539
+ try {
1540
+ return fn();
1541
+ } catch (e) {
1542
+ if (!isStackOverflow(e)) throw e;
1543
+ throw wrap(STACK_MESSAGE);
1544
+ }
1545
+ }
1546
+ var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
1547
+ function isStackOverflow(e) {
1548
+ if (!(e instanceof RangeError)) return false;
1549
+ const m = e.message.toLowerCase();
1550
+ return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
1551
+ m.includes("recursion") || // SpiderMonkey: "too much recursion"
1552
+ m.includes("stack overflow");
1553
+ }
1554
+
1527
1555
  // src/internal/properties/properties.ts
1528
1556
  function parseProperties(input) {
1529
1557
  const pairs = [];
1530
- for (const { text, line } of logicalLines(input)) {
1558
+ for (const { text, line } of logicalLines(stripBom(input))) {
1531
1559
  const [rawKey, rawValue] = splitKeyValue(text);
1532
1560
  const key = unescapeProps(rawKey, line);
1533
1561
  if (key === "") continue;
1534
- pairs.push([key, unescapeProps(rawValue, line)]);
1562
+ const segments = key.split(".");
1563
+ if (tooDeep(segments.length)) {
1564
+ throw new ParseError(
1565
+ `key maps to a path ${segments.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
1566
+ line,
1567
+ 1
1568
+ );
1569
+ }
1570
+ pairs.push([segments, unescapeProps(rawValue, line)]);
1535
1571
  }
1536
1572
  return nestPairs(pairs);
1537
1573
  }
1538
1574
  function nestPairs(pairs) {
1539
1575
  const root = /* @__PURE__ */ Object.create(null);
1540
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1541
- for (const [key, value] of sorted) {
1542
- setNested(root, key.split("."), value);
1576
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
1577
+ for (const [segments, value] of sorted) {
1578
+ setNested(root, segments, value);
1543
1579
  }
1544
1580
  return root;
1545
1581
  }
1582
+ function compareSegments(a, b) {
1583
+ const n = Math.min(a.length, b.length);
1584
+ for (let i = 0; i < n; i++) {
1585
+ const x = a[i];
1586
+ const y = b[i];
1587
+ if (x !== y) return x < y ? -1 : 1;
1588
+ }
1589
+ return a.length - b.length;
1590
+ }
1546
1591
  function logicalLines(input) {
1547
1592
  const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1548
1593
  const out = [];
@@ -1629,19 +1674,16 @@ function unescapeProps(s, line) {
1629
1674
  }
1630
1675
  return out;
1631
1676
  }
1632
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1633
1677
  function setNested(obj, segments, value) {
1634
1678
  let current = obj;
1635
1679
  for (let i = 0; i < segments.length - 1; i++) {
1636
1680
  const seg = segments[i];
1637
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
1638
1681
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
1639
1682
  current[seg] = /* @__PURE__ */ Object.create(null);
1640
1683
  }
1641
1684
  current = current[seg];
1642
1685
  }
1643
1686
  const last = segments[segments.length - 1];
1644
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
1645
1687
  if (typeof current[last] === "object" && current[last] !== null) return;
1646
1688
  current[last] = value;
1647
1689
  }
@@ -1663,7 +1705,22 @@ function recordToHoconValue(obj) {
1663
1705
 
1664
1706
  // src/internal/resolver/include-loader.ts
1665
1707
  var import_meta = {};
1666
- var _require = (0, import_node_module.createRequire)(import_meta.url);
1708
+ var _require;
1709
+ function getRequire() {
1710
+ if (_require === void 0) {
1711
+ _require = pickRequire(
1712
+ typeof require === "function" ? require : void 0,
1713
+ import_meta.url
1714
+ );
1715
+ }
1716
+ return _require;
1717
+ }
1718
+ function pickRequire(candidate, metaUrl) {
1719
+ if (typeof candidate?.resolve === "function") {
1720
+ return candidate;
1721
+ }
1722
+ return (0, import_node_module.createRequire)(metaUrl ?? (0, import_node_url.pathToFileURL)(nodePath.join(process.cwd(), "noop.js")));
1723
+ }
1667
1724
  function isPnpPath(p) {
1668
1725
  return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
1669
1726
  }
@@ -1719,7 +1776,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
1719
1776
  const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
1720
1777
  let resolved;
1721
1778
  try {
1722
- resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
1779
+ resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
1723
1780
  } catch {
1724
1781
  throw new PackageLookupError(
1725
1782
  `include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
@@ -2769,10 +2826,10 @@ var Config = class _Config {
2769
2826
  }
2770
2827
  if (v.kind === "object") {
2771
2828
  const converted = numericObjectToArray(v);
2772
- if (converted !== null) return converted.map(hoconToJs);
2829
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
2773
2830
  }
2774
2831
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
2775
- return v.items.map(hoconToJs);
2832
+ return v.items.map((item) => hoconToJs(item));
2776
2833
  }
2777
2834
  has(path2) {
2778
2835
  return this.lookupNode(path2) !== void 0;
@@ -2900,8 +2957,52 @@ var Config = class _Config {
2900
2957
  originDescription: this._originDescription
2901
2958
  });
2902
2959
  }
2903
- toObject() {
2904
- return hoconToJs(this.root);
2960
+ /**
2961
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
2962
+ * booleans and `null`.
2963
+ *
2964
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
2965
+ * `constructor` and `prototype` included: each is defined as an **own data
2966
+ * property**, so the returned object's prototype is always
2967
+ * `Object.prototype`, and this call writes nothing to the global one.
2968
+ *
2969
+ * ### Passing the result to other code
2970
+ *
2971
+ * A config file — especially a `.properties` file or an env namespace you
2972
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
2973
+ * next depends entirely on how the consumer copies it:
2974
+ *
2975
+ * ```ts
2976
+ * const data = cfg.toObject()
2977
+ * { ...data } // safe — own properties are copied as data
2978
+ * structuredClone(data) // safe
2979
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
2980
+ * // Object.prototype's __proto__ setter: the key
2981
+ * // vanishes and config data becomes the result's
2982
+ * // prototype
2983
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
2984
+ * // target['__proto__'], gets Object.prototype,
2985
+ * // and writes to it: global pollution
2986
+ * for (const k in data) … // prefer Object.entries / Object.keys
2987
+ * ```
2988
+ *
2989
+ * Those two hazards come from the **destination** object's prototype, so no
2990
+ * option here can disarm them — copy with spread or `structuredClone` instead.
2991
+ *
2992
+ * What `{ nullPrototype: true }` does give you is a result that inherits
2993
+ * nothing: every object comes back with a `null` prototype, so reading
2994
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
2995
+ * `undefined` rather than something from `Object.prototype`, and code that
2996
+ * merges *into* this object cannot climb out of it. Keys are all still
2997
+ * present. The trade-off is the usual one for prototype-less objects: not
2998
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
2999
+ *
3000
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
3001
+ * here even when the config holds it exactly — read those with
3002
+ * {@link Config.getString} instead.
3003
+ */
3004
+ toObject(opts) {
3005
+ return hoconToJs(this.root, opts?.nullPrototype === true);
2905
3006
  }
2906
3007
  /**
2907
3008
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -3013,16 +3114,25 @@ function scalarToJs(raw, valueType) {
3013
3114
  return raw;
3014
3115
  }
3015
3116
  }
3016
- function hoconToJs(v) {
3117
+ function hoconToJs(v, nullPrototype = false) {
3017
3118
  switch (v.kind) {
3018
3119
  case "scalar":
3019
3120
  return scalarToJs(v.raw, v.valueType);
3020
3121
  case "array":
3021
- return v.items.map(hoconToJs);
3122
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
3022
3123
  case "object": {
3023
- const obj = /* @__PURE__ */ Object.create(null);
3024
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
3025
- return Object.assign({}, obj);
3124
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
3125
+ if (nullPrototype) {
3126
+ const obj = /* @__PURE__ */ Object.create(null);
3127
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
3128
+ value: val,
3129
+ writable: true,
3130
+ enumerable: true,
3131
+ configurable: true
3132
+ });
3133
+ return obj;
3134
+ }
3135
+ return Object.fromEntries(entries);
3026
3136
  }
3027
3137
  }
3028
3138
  }
@@ -3136,10 +3246,30 @@ function buildResolveContext(input, opts) {
3136
3246
  return { ast, resolveOpts };
3137
3247
  }
3138
3248
  function parse(input, opts = {}) {
3249
+ return parseStringWithOptions(input, opts);
3250
+ }
3251
+ function parseStringWithOptions(input, opts = {}) {
3252
+ return guardStackDepth(
3253
+ () => parseStringInner(input, opts),
3254
+ (msg) => new ParseError(msg, 1, 1, opts.originDescription)
3255
+ );
3256
+ }
3257
+ function parseStringInner(input, opts) {
3258
+ const resolveSubstitutions = opts.resolveSubstitutions ?? true;
3139
3259
  const { ast, resolveOpts } = buildResolveContext(input, opts);
3140
- const value = resolve2(ast, resolveOpts);
3141
- if (value.kind !== "object") throw new Error("resolved value is not an object");
3142
- return new Config(value, { resolved: true, originDescription: opts.originDescription });
3260
+ if (resolveSubstitutions) {
3261
+ const value = resolve2(ast, resolveOpts);
3262
+ if (value.kind !== "object") throw new Error("resolved value is not an object");
3263
+ return new Config(value, { resolved: true, originDescription: opts.originDescription });
3264
+ }
3265
+ const tree = buildTree(ast, resolveOpts);
3266
+ const hasPlaceholders = containsPlaceholders(tree);
3267
+ return Config._fromUnresolvedResObj(tree, {
3268
+ parseBaseDir: opts.baseDir,
3269
+ originDescription: opts.originDescription,
3270
+ resolved: !hasPlaceholders,
3271
+ resolveOpts
3272
+ });
3143
3273
  }
3144
3274
  function parseFile(filePath, opts = {}) {
3145
3275
  if (typeof process === "undefined" && !opts.readFileSync) {
@@ -3213,7 +3343,7 @@ function coerceValue(value, schema) {
3213
3343
  const obj = value;
3214
3344
  const result = { ...obj };
3215
3345
  for (const key of Object.keys(shape)) {
3216
- if (key in obj) {
3346
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
3217
3347
  result[key] = coerceValue(obj[key], shape[key]);
3218
3348
  }
3219
3349
  }
package/dist/zod.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ZodType } from 'zod';
2
- import { C as Config, a as ParseOptions } from './config-PdhVgeLS.cjs';
2
+ import { C as Config, a as ParseOptions } from './config-Dlq6rF3w.cjs';
3
3
 
4
4
  declare function validate<T>(config: Config, schema: ZodType<T>): T;
5
5
  declare function getValidated<T>(config: Config, path: string, schema: ZodType<T>): T;
package/dist/zod.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ZodType } from 'zod';
2
- import { C as Config, a as ParseOptions } from './config-PdhVgeLS.js';
2
+ import { C as Config, a as ParseOptions } from './config-Dlq6rF3w.js';
3
3
 
4
4
  declare function validate<T>(config: Config, schema: ZodType<T>): T;
5
5
  declare function getValidated<T>(config: Config, path: string, schema: ZodType<T>): T;