@o3co/ts.hocon 1.9.0 → 1.11.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/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) {
@@ -1083,12 +1084,12 @@ function isDecimalDigit(ch) {
1083
1084
  }
1084
1085
  function isUnquotedStart(ch) {
1085
1086
  if (ch === "" || isHoconWhitespace(ch)) return false;
1086
- if ('{}[],:=+#"$?!@*&^\\'.includes(ch)) return false;
1087
+ if ('{}[],:=+#"$?!@*&^`\\'.includes(ch)) return false;
1087
1088
  return true;
1088
1089
  }
1089
1090
  function isUnquotedContinue(ch, nextFn) {
1090
1091
  if (ch === "" || isHoconWhitespace(ch)) return false;
1091
- if ('{}[],:=#"$?!@*&^\\'.includes(ch)) return false;
1092
+ if ('{}[],:=#"$?!@*&^`\\'.includes(ch)) return false;
1092
1093
  if (ch === "+" && nextFn() === "=") return false;
1093
1094
  if (ch === "/" && nextFn() === "/") return false;
1094
1095
  return true;
@@ -1237,6 +1238,17 @@ var Parser = class {
1237
1238
  this.advance();
1238
1239
  const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
1239
1240
  const parts = raw.split(".");
1241
+ const leadingDotIsSeparator = spaceConcat || postDotPrefix !== "";
1242
+ for (let i = 0; i < parts.length; i++) {
1243
+ if (parts[i] !== "") continue;
1244
+ if (i === parts.length - 1) continue;
1245
+ if (i === 0 && leadingDotIsSeparator) continue;
1246
+ throw new ParseError(
1247
+ `path has ${i === 0 ? "a leading period '.'" : "two adjacent periods '.'"} \u2014 empty key segment not allowed, use quoted "" (HOCON.md path rules)`,
1248
+ t.line,
1249
+ t.col
1250
+ );
1251
+ }
1240
1252
  const filtered = parts.filter((s) => s.length > 0);
1241
1253
  if (spaceConcat) {
1242
1254
  segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}`;
@@ -1513,45 +1525,135 @@ function parseTokens(tokens) {
1513
1525
  return new Parser(tokens).parse();
1514
1526
  }
1515
1527
 
1528
+ // src/internal/strip-bom.ts
1529
+ function stripBom(input) {
1530
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
1531
+ }
1532
+
1516
1533
  // src/internal/properties/properties.ts
1517
1534
  function parseProperties(input) {
1518
- const root = /* @__PURE__ */ Object.create(null);
1519
1535
  const pairs = [];
1520
- for (const line of input.split("\n")) {
1521
- const trimmed = line.trim();
1522
- if (trimmed === "" || trimmed.startsWith("#") || trimmed.startsWith("!")) continue;
1523
- const sepIdx = findSeparator(trimmed);
1524
- if (sepIdx === -1) continue;
1525
- const key = trimmed.slice(0, sepIdx).trim();
1526
- const value = trimmed.slice(sepIdx + 1).trim();
1536
+ for (const { text, line } of logicalLines(stripBom(input))) {
1537
+ const [rawKey, rawValue] = splitKeyValue(text);
1538
+ const key = unescapeProps(rawKey, line);
1527
1539
  if (key === "") continue;
1528
- pairs.push([key, value]);
1540
+ pairs.push([key.split("."), unescapeProps(rawValue, line)]);
1529
1541
  }
1530
- pairs.sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1531
- for (const [key, value] of pairs) {
1532
- setNested(root, key.split("."), value);
1542
+ return nestPairs(pairs);
1543
+ }
1544
+ function nestPairs(pairs) {
1545
+ const root = /* @__PURE__ */ Object.create(null);
1546
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
1547
+ for (const [segments, value] of sorted) {
1548
+ setNested(root, segments, value);
1533
1549
  }
1534
1550
  return root;
1535
1551
  }
1536
- function findSeparator(line) {
1537
- for (let i = 0; i < line.length; i++) {
1538
- if (line[i] === "=" || line[i] === ":") return i;
1552
+ function compareSegments(a, b) {
1553
+ const n = Math.min(a.length, b.length);
1554
+ for (let i = 0; i < n; i++) {
1555
+ const x = a[i];
1556
+ const y = b[i];
1557
+ if (x !== y) return x < y ? -1 : 1;
1558
+ }
1559
+ return a.length - b.length;
1560
+ }
1561
+ function logicalLines(input) {
1562
+ const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1563
+ const out = [];
1564
+ for (let i = 0; i < natural.length; i++) {
1565
+ let text = trimLeadingSpace(natural[i] ?? "");
1566
+ if (text === "" || text.startsWith("#") || text.startsWith("!")) continue;
1567
+ const start = i + 1;
1568
+ while (endsWithContinuation(text)) {
1569
+ text = text.slice(0, -1);
1570
+ if (i + 1 >= natural.length) break;
1571
+ i++;
1572
+ text += trimLeadingSpace(natural[i] ?? "");
1573
+ }
1574
+ out.push({ text, line: start });
1575
+ }
1576
+ return out;
1577
+ }
1578
+ function trimLeadingSpace(s) {
1579
+ return s.replace(/^[ \t\f]+/, "");
1580
+ }
1581
+ function endsWithContinuation(line) {
1582
+ let n = 0;
1583
+ for (let i = line.length - 1; i >= 0 && line[i] === "\\"; i--) n++;
1584
+ return n % 2 === 1;
1585
+ }
1586
+ function splitKeyValue(line) {
1587
+ let key = "";
1588
+ let i = 0;
1589
+ for (; i < line.length; i++) {
1590
+ const c = line[i];
1591
+ if (c === "\\" && i + 1 < line.length) {
1592
+ key += c + line[i + 1];
1593
+ i++;
1594
+ continue;
1595
+ }
1596
+ if (c === "=" || c === ":" || isPropsSpace(c)) break;
1597
+ key += c;
1539
1598
  }
1540
- return -1;
1599
+ while (i < line.length && isPropsSpace(line[i])) i++;
1600
+ if (i < line.length && (line[i] === "=" || line[i] === ":")) {
1601
+ i++;
1602
+ while (i < line.length && isPropsSpace(line[i])) i++;
1603
+ }
1604
+ return [key, line.slice(i)];
1605
+ }
1606
+ function isPropsSpace(c) {
1607
+ return c === " " || c === " " || c === "\f";
1608
+ }
1609
+ function unescapeProps(s, line) {
1610
+ let out = "";
1611
+ for (let i = 0; i < s.length; i++) {
1612
+ if (s[i] !== "\\") {
1613
+ out += s[i];
1614
+ continue;
1615
+ }
1616
+ i++;
1617
+ if (i >= s.length) break;
1618
+ const e = s[i];
1619
+ switch (e) {
1620
+ case "t":
1621
+ out += " ";
1622
+ break;
1623
+ case "n":
1624
+ out += "\n";
1625
+ break;
1626
+ case "r":
1627
+ out += "\r";
1628
+ break;
1629
+ case "f":
1630
+ out += "\f";
1631
+ break;
1632
+ case "u": {
1633
+ const hex = s.slice(i + 1, i + 5);
1634
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
1635
+ throw new ParseError(`malformed \\u escape in .properties: \\u${hex}`, line, i + 1);
1636
+ }
1637
+ out += String.fromCharCode(parseInt(hex, 16));
1638
+ i += 4;
1639
+ break;
1640
+ }
1641
+ default:
1642
+ out += e;
1643
+ }
1644
+ }
1645
+ return out;
1541
1646
  }
1542
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1543
1647
  function setNested(obj, segments, value) {
1544
1648
  let current = obj;
1545
1649
  for (let i = 0; i < segments.length - 1; i++) {
1546
1650
  const seg = segments[i];
1547
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
1548
1651
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
1549
1652
  current[seg] = /* @__PURE__ */ Object.create(null);
1550
1653
  }
1551
1654
  current = current[seg];
1552
1655
  }
1553
1656
  const last = segments[segments.length - 1];
1554
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
1555
1657
  if (typeof current[last] === "object" && current[last] !== null) return;
1556
1658
  current[last] = value;
1557
1659
  }
@@ -1573,7 +1675,22 @@ function recordToHoconValue(obj) {
1573
1675
 
1574
1676
  // src/internal/resolver/include-loader.ts
1575
1677
  var import_meta = {};
1576
- var _require = (0, import_node_module.createRequire)(import_meta.url);
1678
+ var _require;
1679
+ function getRequire() {
1680
+ if (_require === void 0) {
1681
+ _require = pickRequire(
1682
+ typeof require === "function" ? require : void 0,
1683
+ import_meta.url
1684
+ );
1685
+ }
1686
+ return _require;
1687
+ }
1688
+ function pickRequire(candidate, metaUrl) {
1689
+ if (typeof candidate?.resolve === "function") {
1690
+ return candidate;
1691
+ }
1692
+ return (0, import_node_module.createRequire)(metaUrl ?? (0, import_node_url.pathToFileURL)(nodePath.join(process.cwd(), "noop.js")));
1693
+ }
1577
1694
  function isPnpPath(p) {
1578
1695
  return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
1579
1696
  }
@@ -1629,7 +1746,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
1629
1746
  const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
1630
1747
  let resolved;
1631
1748
  try {
1632
- resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
1749
+ resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
1633
1750
  } catch {
1634
1751
  throw new PackageLookupError(
1635
1752
  `include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
@@ -2679,10 +2796,10 @@ var Config = class _Config {
2679
2796
  }
2680
2797
  if (v.kind === "object") {
2681
2798
  const converted = numericObjectToArray(v);
2682
- if (converted !== null) return converted.map(hoconToJs);
2799
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
2683
2800
  }
2684
2801
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
2685
- return v.items.map(hoconToJs);
2802
+ return v.items.map((item) => hoconToJs(item));
2686
2803
  }
2687
2804
  has(path2) {
2688
2805
  return this.lookupNode(path2) !== void 0;
@@ -2810,8 +2927,52 @@ var Config = class _Config {
2810
2927
  originDescription: this._originDescription
2811
2928
  });
2812
2929
  }
2813
- toObject() {
2814
- return hoconToJs(this.root);
2930
+ /**
2931
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
2932
+ * booleans and `null`.
2933
+ *
2934
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
2935
+ * `constructor` and `prototype` included: each is defined as an **own data
2936
+ * property**, so the returned object's prototype is always
2937
+ * `Object.prototype`, and this call writes nothing to the global one.
2938
+ *
2939
+ * ### Passing the result to other code
2940
+ *
2941
+ * A config file — especially a `.properties` file or an env namespace you
2942
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
2943
+ * next depends entirely on how the consumer copies it:
2944
+ *
2945
+ * ```ts
2946
+ * const data = cfg.toObject()
2947
+ * { ...data } // safe — own properties are copied as data
2948
+ * structuredClone(data) // safe
2949
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
2950
+ * // Object.prototype's __proto__ setter: the key
2951
+ * // vanishes and config data becomes the result's
2952
+ * // prototype
2953
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
2954
+ * // target['__proto__'], gets Object.prototype,
2955
+ * // and writes to it: global pollution
2956
+ * for (const k in data) … // prefer Object.entries / Object.keys
2957
+ * ```
2958
+ *
2959
+ * Those two hazards come from the **destination** object's prototype, so no
2960
+ * option here can disarm them — copy with spread or `structuredClone` instead.
2961
+ *
2962
+ * What `{ nullPrototype: true }` does give you is a result that inherits
2963
+ * nothing: every object comes back with a `null` prototype, so reading
2964
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
2965
+ * `undefined` rather than something from `Object.prototype`, and code that
2966
+ * merges *into* this object cannot climb out of it. Keys are all still
2967
+ * present. The trade-off is the usual one for prototype-less objects: not
2968
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
2969
+ *
2970
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
2971
+ * here even when the config holds it exactly — read those with
2972
+ * {@link Config.getString} instead.
2973
+ */
2974
+ toObject(opts) {
2975
+ return hoconToJs(this.root, opts?.nullPrototype === true);
2815
2976
  }
2816
2977
  /**
2817
2978
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -2923,16 +3084,25 @@ function scalarToJs(raw, valueType) {
2923
3084
  return raw;
2924
3085
  }
2925
3086
  }
2926
- function hoconToJs(v) {
3087
+ function hoconToJs(v, nullPrototype = false) {
2927
3088
  switch (v.kind) {
2928
3089
  case "scalar":
2929
3090
  return scalarToJs(v.raw, v.valueType);
2930
3091
  case "array":
2931
- return v.items.map(hoconToJs);
3092
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
2932
3093
  case "object": {
2933
- const obj = /* @__PURE__ */ Object.create(null);
2934
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
2935
- return Object.assign({}, obj);
3094
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
3095
+ if (nullPrototype) {
3096
+ const obj = /* @__PURE__ */ Object.create(null);
3097
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
3098
+ value: val,
3099
+ writable: true,
3100
+ enumerable: true,
3101
+ configurable: true
3102
+ });
3103
+ return obj;
3104
+ }
3105
+ return Object.fromEntries(entries);
2936
3106
  }
2937
3107
  }
2938
3108
  }
@@ -3123,7 +3293,7 @@ function coerceValue(value, schema) {
3123
3293
  const obj = value;
3124
3294
  const result = { ...obj };
3125
3295
  for (const key of Object.keys(shape)) {
3126
- if (key in obj) {
3296
+ if (Object.prototype.hasOwnProperty.call(obj, key)) {
3127
3297
  result[key] = coerceValue(obj[key], shape[key]);
3128
3298
  }
3129
3299
  }
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-D27s3_aG.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-D27s3_aG.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;