@o3co/ts.hocon 1.10.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.
@@ -516,7 +516,12 @@ function deepMergeHoconValues(base, overlay) {
516
516
  // src/internal/resolver/include-loader.ts
517
517
  import { createRequire } from "module";
518
518
  import * as nodePath from "path";
519
- var _require = createRequire(import.meta.url);
519
+ import { pathToFileURL } from "url";
520
+
521
+ // src/internal/strip-bom.ts
522
+ function stripBom(input) {
523
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
524
+ }
520
525
 
521
526
  // src/value/numeric-array.ts
522
527
  function numericObjectToArray(value) {
@@ -1105,10 +1110,10 @@ var Config = class _Config {
1105
1110
  }
1106
1111
  if (v.kind === "object") {
1107
1112
  const converted = numericObjectToArray(v);
1108
- if (converted !== null) return converted.map(hoconToJs);
1113
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1109
1114
  }
1110
1115
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1111
- return v.items.map(hoconToJs);
1116
+ return v.items.map((item) => hoconToJs(item));
1112
1117
  }
1113
1118
  has(path) {
1114
1119
  return this.lookupNode(path) !== void 0;
@@ -1236,8 +1241,52 @@ var Config = class _Config {
1236
1241
  originDescription: this._originDescription
1237
1242
  });
1238
1243
  }
1239
- toObject() {
1240
- return hoconToJs(this.root);
1244
+ /**
1245
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1246
+ * booleans and `null`.
1247
+ *
1248
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1249
+ * `constructor` and `prototype` included: each is defined as an **own data
1250
+ * property**, so the returned object's prototype is always
1251
+ * `Object.prototype`, and this call writes nothing to the global one.
1252
+ *
1253
+ * ### Passing the result to other code
1254
+ *
1255
+ * A config file — especially a `.properties` file or an env namespace you
1256
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1257
+ * next depends entirely on how the consumer copies it:
1258
+ *
1259
+ * ```ts
1260
+ * const data = cfg.toObject()
1261
+ * { ...data } // safe — own properties are copied as data
1262
+ * structuredClone(data) // safe
1263
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1264
+ * // Object.prototype's __proto__ setter: the key
1265
+ * // vanishes and config data becomes the result's
1266
+ * // prototype
1267
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1268
+ * // target['__proto__'], gets Object.prototype,
1269
+ * // and writes to it: global pollution
1270
+ * for (const k in data) … // prefer Object.entries / Object.keys
1271
+ * ```
1272
+ *
1273
+ * Those two hazards come from the **destination** object's prototype, so no
1274
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1275
+ *
1276
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1277
+ * nothing: every object comes back with a `null` prototype, so reading
1278
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1279
+ * `undefined` rather than something from `Object.prototype`, and code that
1280
+ * merges *into* this object cannot climb out of it. Keys are all still
1281
+ * present. The trade-off is the usual one for prototype-less objects: not
1282
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1283
+ *
1284
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1285
+ * here even when the config holds it exactly — read those with
1286
+ * {@link Config.getString} instead.
1287
+ */
1288
+ toObject(opts) {
1289
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1241
1290
  }
1242
1291
  /**
1243
1292
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1349,16 +1398,25 @@ function scalarToJs(raw, valueType) {
1349
1398
  return raw;
1350
1399
  }
1351
1400
  }
1352
- function hoconToJs(v) {
1401
+ function hoconToJs(v, nullPrototype = false) {
1353
1402
  switch (v.kind) {
1354
1403
  case "scalar":
1355
1404
  return scalarToJs(v.raw, v.valueType);
1356
1405
  case "array":
1357
- return v.items.map(hoconToJs);
1406
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1358
1407
  case "object": {
1359
- const obj = /* @__PURE__ */ Object.create(null);
1360
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1361
- return Object.assign({}, obj);
1408
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1409
+ if (nullPrototype) {
1410
+ const obj = /* @__PURE__ */ Object.create(null);
1411
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1412
+ value: val,
1413
+ writable: true,
1414
+ enumerable: true,
1415
+ configurable: true
1416
+ });
1417
+ return obj;
1418
+ }
1419
+ return Object.fromEntries(entries);
1362
1420
  }
1363
1421
  }
1364
1422
  }
@@ -1447,6 +1505,8 @@ function fromMap(values, originDescription) {
1447
1505
  const root = { kind: "object", fields };
1448
1506
  return Config._fromResolvedValue(root, { originDescription });
1449
1507
  }
1508
+ var INT64_MAX = 9223372036854775807n;
1509
+ var INT64_MIN = -9223372036854775808n;
1450
1510
  function coerceObject(obj, atPath) {
1451
1511
  const fields = /* @__PURE__ */ new Map();
1452
1512
  for (const [k, v] of Object.entries(obj)) {
@@ -1475,14 +1535,14 @@ function coerceValue(v, atPath) {
1475
1535
  atPath
1476
1536
  );
1477
1537
  }
1478
- return { kind: "scalar", raw: String(v), valueType: "number" };
1538
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1479
1539
  }
1480
1540
  case "boolean":
1481
1541
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1482
1542
  case "bigint": {
1483
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1543
+ if (v > INT64_MAX || v < INT64_MIN) {
1484
1544
  throw new ConfigError(
1485
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1545
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1486
1546
  atPath
1487
1547
  );
1488
1548
  }
@@ -1506,7 +1566,7 @@ function coerceValue(v, atPath) {
1506
1566
 
1507
1567
  // src/adapters/yaml.ts
1508
1568
  function parseYaml(input, originDescription) {
1509
- const docs = YAML.parseAllDocuments(input, { version: "1.2", merge: true });
1569
+ const docs = YAML.parseAllDocuments(stripBom(input), { version: "1.2", merge: true, intAsBigInt: true });
1510
1570
  if (docs.length > 1) {
1511
1571
  throw new ConfigError(
1512
1572
  "yaml: multi-document streams are not supported (spec F5.7); a config is one document",
@@ -1537,7 +1597,7 @@ function convert(v, atPath) {
1537
1597
  return v.toISOString();
1538
1598
  }
1539
1599
  if (v instanceof Map) {
1540
- const out = {};
1600
+ const entries = [];
1541
1601
  for (const [k, e] of v) {
1542
1602
  if (k !== null && typeof k === "object") {
1543
1603
  throw new ConfigError(
@@ -1545,19 +1605,20 @@ function convert(v, atPath) {
1545
1605
  atPath
1546
1606
  );
1547
1607
  }
1548
- out[String(k)] = convert(e, atPath === "" ? String(k) : `${atPath}.${String(k)}`);
1608
+ const key = String(k);
1609
+ entries.push([key, convert(e, atPath === "" ? key : `${atPath}.${key}`)]);
1549
1610
  }
1550
- return out;
1611
+ return Object.fromEntries(entries);
1551
1612
  }
1552
1613
  if (v instanceof Uint8Array) {
1553
- return btoa(String.fromCharCode(...v));
1614
+ return bytesToBase64(v);
1554
1615
  }
1555
1616
  if (v !== null && typeof v === "object") {
1556
- const out = {};
1557
- for (const [k, e] of Object.entries(v)) {
1558
- out[k] = convert(e, atPath === "" ? k : `${atPath}.${k}`);
1559
- }
1560
- return out;
1617
+ return Object.fromEntries(
1618
+ Object.entries(v).map(
1619
+ ([k, e]) => [k, convert(e, atPath === "" ? k : `${atPath}.${k}`)]
1620
+ )
1621
+ );
1561
1622
  }
1562
1623
  if (typeof v === "number" && !Number.isFinite(v)) {
1563
1624
  throw new ConfigError(
@@ -1565,8 +1626,24 @@ function convert(v, atPath) {
1565
1626
  atPath
1566
1627
  );
1567
1628
  }
1629
+ if (typeof v === "bigint") {
1630
+ return isSafeIntegerBigInt(v) ? Number(v) : v;
1631
+ }
1568
1632
  return v;
1569
1633
  }
1634
+ function bytesToBase64(bytes) {
1635
+ const CHUNK = 32768;
1636
+ let binary = "";
1637
+ for (let i = 0; i < bytes.length; i += CHUNK) {
1638
+ binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
1639
+ }
1640
+ return btoa(binary);
1641
+ }
1642
+ var MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
1643
+ var MIN_SAFE = BigInt(Number.MIN_SAFE_INTEGER);
1644
+ function isSafeIntegerBigInt(v) {
1645
+ return v <= MAX_SAFE && v >= MIN_SAFE;
1646
+ }
1570
1647
  export {
1571
1648
  fromYamlValue,
1572
1649
  parseYaml
@@ -316,7 +316,53 @@ declare class Config {
316
316
  * E12 decisions 9, 10.
317
317
  */
318
318
  resolveWith(source: Config, opts?: ResolveOptions): Config;
319
- toObject(): unknown;
319
+ /**
320
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
321
+ * booleans and `null`.
322
+ *
323
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
324
+ * `constructor` and `prototype` included: each is defined as an **own data
325
+ * property**, so the returned object's prototype is always
326
+ * `Object.prototype`, and this call writes nothing to the global one.
327
+ *
328
+ * ### Passing the result to other code
329
+ *
330
+ * A config file — especially a `.properties` file or an env namespace you
331
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
332
+ * next depends entirely on how the consumer copies it:
333
+ *
334
+ * ```ts
335
+ * const data = cfg.toObject()
336
+ * { ...data } // safe — own properties are copied as data
337
+ * structuredClone(data) // safe
338
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
339
+ * // Object.prototype's __proto__ setter: the key
340
+ * // vanishes and config data becomes the result's
341
+ * // prototype
342
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
343
+ * // target['__proto__'], gets Object.prototype,
344
+ * // and writes to it: global pollution
345
+ * for (const k in data) … // prefer Object.entries / Object.keys
346
+ * ```
347
+ *
348
+ * Those two hazards come from the **destination** object's prototype, so no
349
+ * option here can disarm them — copy with spread or `structuredClone` instead.
350
+ *
351
+ * What `{ nullPrototype: true }` does give you is a result that inherits
352
+ * nothing: every object comes back with a `null` prototype, so reading
353
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
354
+ * `undefined` rather than something from `Object.prototype`, and code that
355
+ * merges *into* this object cannot climb out of it. Keys are all still
356
+ * present. The trade-off is the usual one for prototype-less objects: not
357
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
358
+ *
359
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
360
+ * here even when the config holds it exactly — read those with
361
+ * {@link Config.getString} instead.
362
+ */
363
+ toObject(opts?: {
364
+ nullPrototype?: boolean;
365
+ }): unknown;
320
366
  /**
321
367
  * @internal Test-only: renders this resolved Config's value tree as canonical
322
368
  * JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
@@ -316,7 +316,53 @@ declare class Config {
316
316
  * E12 decisions 9, 10.
317
317
  */
318
318
  resolveWith(source: Config, opts?: ResolveOptions): Config;
319
- toObject(): unknown;
319
+ /**
320
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
321
+ * booleans and `null`.
322
+ *
323
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
324
+ * `constructor` and `prototype` included: each is defined as an **own data
325
+ * property**, so the returned object's prototype is always
326
+ * `Object.prototype`, and this call writes nothing to the global one.
327
+ *
328
+ * ### Passing the result to other code
329
+ *
330
+ * A config file — especially a `.properties` file or an env namespace you
331
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
332
+ * next depends entirely on how the consumer copies it:
333
+ *
334
+ * ```ts
335
+ * const data = cfg.toObject()
336
+ * { ...data } // safe — own properties are copied as data
337
+ * structuredClone(data) // safe
338
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
339
+ * // Object.prototype's __proto__ setter: the key
340
+ * // vanishes and config data becomes the result's
341
+ * // prototype
342
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
343
+ * // target['__proto__'], gets Object.prototype,
344
+ * // and writes to it: global pollution
345
+ * for (const k in data) … // prefer Object.entries / Object.keys
346
+ * ```
347
+ *
348
+ * Those two hazards come from the **destination** object's prototype, so no
349
+ * option here can disarm them — copy with spread or `structuredClone` instead.
350
+ *
351
+ * What `{ nullPrototype: true }` does give you is a result that inherits
352
+ * nothing: every object comes back with a `null` prototype, so reading
353
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
354
+ * `undefined` rather than something from `Object.prototype`, and code that
355
+ * merges *into* this object cannot climb out of it. Keys are all still
356
+ * present. The trade-off is the usual one for prototype-less objects: not
357
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
358
+ *
359
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
360
+ * here even when the config holds it exactly — read those with
361
+ * {@link Config.getString} instead.
362
+ */
363
+ toObject(opts?: {
364
+ nullPrototype?: boolean;
365
+ }): unknown;
320
366
  /**
321
367
  * @internal Test-only: renders this resolved Config's value tree as canonical
322
368
  * JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
package/dist/index.cjs CHANGED
@@ -700,6 +700,7 @@ function isFileNotFoundError(e) {
700
700
  // src/internal/resolver/include-loader.ts
701
701
  var import_node_module = require("module");
702
702
  var nodePath = __toESM(require("path"), 1);
703
+ var import_node_url = require("url");
703
704
 
704
705
  // src/internal/lexer/lexer.ts
705
706
  function isHoconWhitespace(ch) {
@@ -1542,25 +1543,39 @@ function parseTokens(tokens) {
1542
1543
  return new Parser(tokens).parse();
1543
1544
  }
1544
1545
 
1546
+ // src/internal/strip-bom.ts
1547
+ function stripBom(input) {
1548
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
1549
+ }
1550
+
1545
1551
  // src/internal/properties/properties.ts
1546
1552
  function parseProperties(input) {
1547
1553
  const pairs = [];
1548
- for (const { text, line } of logicalLines(input)) {
1554
+ for (const { text, line } of logicalLines(stripBom(input))) {
1549
1555
  const [rawKey, rawValue] = splitKeyValue(text);
1550
1556
  const key = unescapeProps(rawKey, line);
1551
1557
  if (key === "") continue;
1552
- pairs.push([key, unescapeProps(rawValue, line)]);
1558
+ pairs.push([key.split("."), unescapeProps(rawValue, line)]);
1553
1559
  }
1554
1560
  return nestPairs(pairs);
1555
1561
  }
1556
1562
  function nestPairs(pairs) {
1557
1563
  const root = /* @__PURE__ */ Object.create(null);
1558
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
1559
- for (const [key, value] of sorted) {
1560
- setNested(root, key.split("."), value);
1564
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
1565
+ for (const [segments, value] of sorted) {
1566
+ setNested(root, segments, value);
1561
1567
  }
1562
1568
  return root;
1563
1569
  }
1570
+ function compareSegments(a, b) {
1571
+ const n = Math.min(a.length, b.length);
1572
+ for (let i = 0; i < n; i++) {
1573
+ const x = a[i];
1574
+ const y = b[i];
1575
+ if (x !== y) return x < y ? -1 : 1;
1576
+ }
1577
+ return a.length - b.length;
1578
+ }
1564
1579
  function logicalLines(input) {
1565
1580
  const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1566
1581
  const out = [];
@@ -1647,19 +1662,16 @@ function unescapeProps(s, line) {
1647
1662
  }
1648
1663
  return out;
1649
1664
  }
1650
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
1651
1665
  function setNested(obj, segments, value) {
1652
1666
  let current = obj;
1653
1667
  for (let i = 0; i < segments.length - 1; i++) {
1654
1668
  const seg = segments[i];
1655
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
1656
1669
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
1657
1670
  current[seg] = /* @__PURE__ */ Object.create(null);
1658
1671
  }
1659
1672
  current = current[seg];
1660
1673
  }
1661
1674
  const last = segments[segments.length - 1];
1662
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
1663
1675
  if (typeof current[last] === "object" && current[last] !== null) return;
1664
1676
  current[last] = value;
1665
1677
  }
@@ -1681,7 +1693,22 @@ function recordToHoconValue(obj) {
1681
1693
 
1682
1694
  // src/internal/resolver/include-loader.ts
1683
1695
  var import_meta = {};
1684
- var _require = (0, import_node_module.createRequire)(import_meta.url);
1696
+ var _require;
1697
+ function getRequire() {
1698
+ if (_require === void 0) {
1699
+ _require = pickRequire(
1700
+ typeof require === "function" ? require : void 0,
1701
+ import_meta.url
1702
+ );
1703
+ }
1704
+ return _require;
1705
+ }
1706
+ function pickRequire(candidate, metaUrl) {
1707
+ if (typeof candidate?.resolve === "function") {
1708
+ return candidate;
1709
+ }
1710
+ return (0, import_node_module.createRequire)(metaUrl ?? (0, import_node_url.pathToFileURL)(nodePath.join(process.cwd(), "noop.js")));
1711
+ }
1685
1712
  function isPnpPath(p) {
1686
1713
  return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
1687
1714
  }
@@ -1737,7 +1764,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
1737
1764
  const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
1738
1765
  let resolved;
1739
1766
  try {
1740
- resolved = _require.resolve(`${identifier}/${file}`, { paths: from });
1767
+ resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
1741
1768
  } catch {
1742
1769
  throw new PackageLookupError(
1743
1770
  `include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
@@ -2791,10 +2818,10 @@ var Config = class _Config {
2791
2818
  }
2792
2819
  if (v.kind === "object") {
2793
2820
  const converted = numericObjectToArray(v);
2794
- if (converted !== null) return converted.map(hoconToJs);
2821
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
2795
2822
  }
2796
2823
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
2797
- return v.items.map(hoconToJs);
2824
+ return v.items.map((item) => hoconToJs(item));
2798
2825
  }
2799
2826
  has(path2) {
2800
2827
  return this.lookupNode(path2) !== void 0;
@@ -2922,8 +2949,52 @@ var Config = class _Config {
2922
2949
  originDescription: this._originDescription
2923
2950
  });
2924
2951
  }
2925
- toObject() {
2926
- return hoconToJs(this.root);
2952
+ /**
2953
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
2954
+ * booleans and `null`.
2955
+ *
2956
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
2957
+ * `constructor` and `prototype` included: each is defined as an **own data
2958
+ * property**, so the returned object's prototype is always
2959
+ * `Object.prototype`, and this call writes nothing to the global one.
2960
+ *
2961
+ * ### Passing the result to other code
2962
+ *
2963
+ * A config file — especially a `.properties` file or an env namespace you
2964
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
2965
+ * next depends entirely on how the consumer copies it:
2966
+ *
2967
+ * ```ts
2968
+ * const data = cfg.toObject()
2969
+ * { ...data } // safe — own properties are copied as data
2970
+ * structuredClone(data) // safe
2971
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
2972
+ * // Object.prototype's __proto__ setter: the key
2973
+ * // vanishes and config data becomes the result's
2974
+ * // prototype
2975
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
2976
+ * // target['__proto__'], gets Object.prototype,
2977
+ * // and writes to it: global pollution
2978
+ * for (const k in data) … // prefer Object.entries / Object.keys
2979
+ * ```
2980
+ *
2981
+ * Those two hazards come from the **destination** object's prototype, so no
2982
+ * option here can disarm them — copy with spread or `structuredClone` instead.
2983
+ *
2984
+ * What `{ nullPrototype: true }` does give you is a result that inherits
2985
+ * nothing: every object comes back with a `null` prototype, so reading
2986
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
2987
+ * `undefined` rather than something from `Object.prototype`, and code that
2988
+ * merges *into* this object cannot climb out of it. Keys are all still
2989
+ * present. The trade-off is the usual one for prototype-less objects: not
2990
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
2991
+ *
2992
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
2993
+ * here even when the config holds it exactly — read those with
2994
+ * {@link Config.getString} instead.
2995
+ */
2996
+ toObject(opts) {
2997
+ return hoconToJs(this.root, opts?.nullPrototype === true);
2927
2998
  }
2928
2999
  /**
2929
3000
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -3035,16 +3106,25 @@ function scalarToJs(raw, valueType) {
3035
3106
  return raw;
3036
3107
  }
3037
3108
  }
3038
- function hoconToJs(v) {
3109
+ function hoconToJs(v, nullPrototype = false) {
3039
3110
  switch (v.kind) {
3040
3111
  case "scalar":
3041
3112
  return scalarToJs(v.raw, v.valueType);
3042
3113
  case "array":
3043
- return v.items.map(hoconToJs);
3114
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
3044
3115
  case "object": {
3045
- const obj = /* @__PURE__ */ Object.create(null);
3046
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
3047
- return Object.assign({}, obj);
3116
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
3117
+ if (nullPrototype) {
3118
+ const obj = /* @__PURE__ */ Object.create(null);
3119
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
3120
+ value: val,
3121
+ writable: true,
3122
+ enumerable: true,
3123
+ configurable: true
3124
+ });
3125
+ return obj;
3126
+ }
3127
+ return Object.fromEntries(entries);
3048
3128
  }
3049
3129
  }
3050
3130
  }
@@ -3278,6 +3358,8 @@ function empty(originDescription) {
3278
3358
  const root = { kind: "object", fields: /* @__PURE__ */ new Map() };
3279
3359
  return Config._fromResolvedValue(root, { originDescription });
3280
3360
  }
3361
+ var INT64_MAX = 9223372036854775807n;
3362
+ var INT64_MIN = -9223372036854775808n;
3281
3363
  function coerceObject(obj, atPath) {
3282
3364
  const fields = /* @__PURE__ */ new Map();
3283
3365
  for (const [k, v] of Object.entries(obj)) {
@@ -3306,14 +3388,14 @@ function coerceValue(v, atPath) {
3306
3388
  atPath
3307
3389
  );
3308
3390
  }
3309
- return { kind: "scalar", raw: String(v), valueType: "number" };
3391
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
3310
3392
  }
3311
3393
  case "boolean":
3312
3394
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
3313
3395
  case "bigint": {
3314
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
3396
+ if (v > INT64_MAX || v < INT64_MIN) {
3315
3397
  throw new ConfigError(
3316
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
3398
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
3317
3399
  atPath
3318
3400
  );
3319
3401
  }
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Config } from './config-PdhVgeLS.cjs';
2
- export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-PdhVgeLS.cjs';
1
+ import { C as Config } from './config-Dlq6rF3w.cjs';
2
+ export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-Dlq6rF3w.cjs';
3
3
 
4
4
  declare class ParseError extends Error {
5
5
  readonly line: number;
@@ -48,7 +48,14 @@ declare class NotResolvedError extends ConfigError {
48
48
  * null → scalar/null
49
49
  * Array<unknown> → array (elements recursively coerced)
50
50
  * Record<string,unknown> → object (values recursively coerced)
51
- * undefined / function / symbol / bigint (> MAX_SAFE_INTEGER) → ConfigError
51
+ * bigint → scalar/number, digits kept verbatim (out of int64 = error)
52
+ * undefined / function / symbol → ConfigError
53
+ *
54
+ * A `bigint` is how a caller (or a format adapter) hands over an integer that a
55
+ * JS `number` cannot hold exactly: its digits are kept verbatim in the value's
56
+ * raw text, so `getString` returns them intact, while `getNumber` / `toObject`
57
+ * apply the JS number model as they do for any other value. Integers outside
58
+ * the int64 range are rejected (spec F0.5).
52
59
  *
53
60
  * originDescription (optional) provides a source name for error messages.
54
61
  * E12 decision 13.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Config } from './config-PdhVgeLS.js';
2
- export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-PdhVgeLS.js';
1
+ import { C as Config } from './config-Dlq6rF3w.js';
2
+ export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-Dlq6rF3w.js';
3
3
 
4
4
  declare class ParseError extends Error {
5
5
  readonly line: number;
@@ -48,7 +48,14 @@ declare class NotResolvedError extends ConfigError {
48
48
  * null → scalar/null
49
49
  * Array<unknown> → array (elements recursively coerced)
50
50
  * Record<string,unknown> → object (values recursively coerced)
51
- * undefined / function / symbol / bigint (> MAX_SAFE_INTEGER) → ConfigError
51
+ * bigint → scalar/number, digits kept verbatim (out of int64 = error)
52
+ * undefined / function / symbol → ConfigError
53
+ *
54
+ * A `bigint` is how a caller (or a format adapter) hands over an integer that a
55
+ * JS `number` cannot hold exactly: its digits are kept verbatim in the value's
56
+ * raw text, so `getString` returns them intact, while `getNumber` / `toObject`
57
+ * apply the JS number model as they do for any other value. Integers outside
58
+ * the int64 range are rejected (spec F0.5).
52
59
  *
53
60
  * originDescription (optional) provides a source name for error messages.
54
61
  * E12 decision 13.