@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.
@@ -513,7 +513,12 @@ function deepMergeHoconValues(base, overlay) {
513
513
  // src/internal/resolver/include-loader.ts
514
514
  import { createRequire } from "module";
515
515
  import * as nodePath from "path";
516
- var _require = createRequire(import.meta.url);
516
+ import { pathToFileURL } from "url";
517
+
518
+ // src/internal/strip-bom.ts
519
+ function stripBom(input) {
520
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
521
+ }
517
522
 
518
523
  // src/value/numeric-array.ts
519
524
  function numericObjectToArray(value) {
@@ -1102,10 +1107,10 @@ var Config = class _Config {
1102
1107
  }
1103
1108
  if (v.kind === "object") {
1104
1109
  const converted = numericObjectToArray(v);
1105
- if (converted !== null) return converted.map(hoconToJs);
1110
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1106
1111
  }
1107
1112
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1108
- return v.items.map(hoconToJs);
1113
+ return v.items.map((item) => hoconToJs(item));
1109
1114
  }
1110
1115
  has(path) {
1111
1116
  return this.lookupNode(path) !== void 0;
@@ -1233,8 +1238,52 @@ var Config = class _Config {
1233
1238
  originDescription: this._originDescription
1234
1239
  });
1235
1240
  }
1236
- toObject() {
1237
- return hoconToJs(this.root);
1241
+ /**
1242
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1243
+ * booleans and `null`.
1244
+ *
1245
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1246
+ * `constructor` and `prototype` included: each is defined as an **own data
1247
+ * property**, so the returned object's prototype is always
1248
+ * `Object.prototype`, and this call writes nothing to the global one.
1249
+ *
1250
+ * ### Passing the result to other code
1251
+ *
1252
+ * A config file — especially a `.properties` file or an env namespace you
1253
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1254
+ * next depends entirely on how the consumer copies it:
1255
+ *
1256
+ * ```ts
1257
+ * const data = cfg.toObject()
1258
+ * { ...data } // safe — own properties are copied as data
1259
+ * structuredClone(data) // safe
1260
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1261
+ * // Object.prototype's __proto__ setter: the key
1262
+ * // vanishes and config data becomes the result's
1263
+ * // prototype
1264
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1265
+ * // target['__proto__'], gets Object.prototype,
1266
+ * // and writes to it: global pollution
1267
+ * for (const k in data) … // prefer Object.entries / Object.keys
1268
+ * ```
1269
+ *
1270
+ * Those two hazards come from the **destination** object's prototype, so no
1271
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1272
+ *
1273
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1274
+ * nothing: every object comes back with a `null` prototype, so reading
1275
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1276
+ * `undefined` rather than something from `Object.prototype`, and code that
1277
+ * merges *into* this object cannot climb out of it. Keys are all still
1278
+ * present. The trade-off is the usual one for prototype-less objects: not
1279
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1280
+ *
1281
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1282
+ * here even when the config holds it exactly — read those with
1283
+ * {@link Config.getString} instead.
1284
+ */
1285
+ toObject(opts) {
1286
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1238
1287
  }
1239
1288
  /**
1240
1289
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1346,16 +1395,25 @@ function scalarToJs(raw, valueType) {
1346
1395
  return raw;
1347
1396
  }
1348
1397
  }
1349
- function hoconToJs(v) {
1398
+ function hoconToJs(v, nullPrototype = false) {
1350
1399
  switch (v.kind) {
1351
1400
  case "scalar":
1352
1401
  return scalarToJs(v.raw, v.valueType);
1353
1402
  case "array":
1354
- return v.items.map(hoconToJs);
1403
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1355
1404
  case "object": {
1356
- const obj = /* @__PURE__ */ Object.create(null);
1357
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1358
- return Object.assign({}, obj);
1405
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1406
+ if (nullPrototype) {
1407
+ const obj = /* @__PURE__ */ Object.create(null);
1408
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1409
+ value: val,
1410
+ writable: true,
1411
+ enumerable: true,
1412
+ configurable: true
1413
+ });
1414
+ return obj;
1415
+ }
1416
+ return Object.fromEntries(entries);
1359
1417
  }
1360
1418
  }
1361
1419
  }
@@ -1444,6 +1502,8 @@ function fromMap(values, originDescription) {
1444
1502
  const root = { kind: "object", fields };
1445
1503
  return Config._fromResolvedValue(root, { originDescription });
1446
1504
  }
1505
+ var INT64_MAX = 9223372036854775807n;
1506
+ var INT64_MIN = -9223372036854775808n;
1447
1507
  function coerceObject(obj, atPath) {
1448
1508
  const fields = /* @__PURE__ */ new Map();
1449
1509
  for (const [k, v] of Object.entries(obj)) {
@@ -1472,14 +1532,14 @@ function coerceValue(v, atPath) {
1472
1532
  atPath
1473
1533
  );
1474
1534
  }
1475
- return { kind: "scalar", raw: String(v), valueType: "number" };
1535
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1476
1536
  }
1477
1537
  case "boolean":
1478
1538
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1479
1539
  case "bigint": {
1480
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1540
+ if (v > INT64_MAX || v < INT64_MIN) {
1481
1541
  throw new ConfigError(
1482
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1542
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1483
1543
  atPath
1484
1544
  );
1485
1545
  }
@@ -1503,7 +1563,7 @@ function coerceValue(v, atPath) {
1503
1563
 
1504
1564
  // src/adapters/jsonc.ts
1505
1565
  function parseJsonc(input, originDescription) {
1506
- const doc = JSON.parse(stripTrailingCommas(stripComments(input)));
1566
+ const doc = JSON.parse(stripTrailingCommas(stripComments(stripBom(input))), bigIntReviver);
1507
1567
  if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
1508
1568
  throw new ConfigError(
1509
1569
  `jsonc: document root is ${Array.isArray(doc) ? "an array" : typeof doc}, but a config root must be an object (spec F0.3)`,
@@ -1512,6 +1572,50 @@ function parseJsonc(input, originDescription) {
1512
1572
  }
1513
1573
  return fromMap(doc, originDescription);
1514
1574
  }
1575
+ function isIntegerLiteral(source) {
1576
+ return !/[.eE]/.test(source);
1577
+ }
1578
+ var HAS_SOURCE_TEXT_ACCESS = (() => {
1579
+ let seen;
1580
+ JSON.parse("1", function(_k, v, context) {
1581
+ seen = context?.source;
1582
+ return v;
1583
+ });
1584
+ return seen !== void 0;
1585
+ })();
1586
+ function bigIntReviver(key, value, context) {
1587
+ if (typeof value !== "number" || Number.isSafeInteger(value)) return value;
1588
+ const source = context?.source;
1589
+ if (source === void 0) {
1590
+ if (!Number.isInteger(value)) return value;
1591
+ throw new ConfigError(
1592
+ `jsonc: the integer at "${key || "<root>"}" needs more precision than a JS number holds, and this runtime does not expose JSON.parse source text (${HAS_SOURCE_TEXT_ACCESS ? "unexpected \u2014 the capability probe said otherwise" : "requires Node >= 22 or an engine with JSON.parse source access"}), so it cannot be read losslessly (spec F0.5)`,
1593
+ key
1594
+ );
1595
+ }
1596
+ if (!isIntegerLiteral(source)) return value;
1597
+ return BigInt(source);
1598
+ }
1599
+ function isLineBreak(c) {
1600
+ return c === "\n" || c === "\r";
1601
+ }
1602
+ function lineBreaksOf(span) {
1603
+ let out = "";
1604
+ for (let i = 0; i < span.length; i++) {
1605
+ const c = span[i];
1606
+ if (c === "\r") {
1607
+ if (span[i + 1] === "\n") {
1608
+ out += "\r\n";
1609
+ i++;
1610
+ } else {
1611
+ out += "\r";
1612
+ }
1613
+ } else if (c === "\n") {
1614
+ out += "\n";
1615
+ }
1616
+ }
1617
+ return out;
1618
+ }
1515
1619
  function stripComments(src) {
1516
1620
  let out = "";
1517
1621
  for (let i = 0; i < src.length; ) {
@@ -1523,13 +1627,13 @@ function stripComments(src) {
1523
1627
  continue;
1524
1628
  }
1525
1629
  if (c === "/" && src[i + 1] === "/") {
1526
- while (i < src.length && src[i] !== "\n") i++;
1630
+ while (i < src.length && !isLineBreak(src[i])) i++;
1527
1631
  continue;
1528
1632
  }
1529
1633
  if (c === "/" && src[i + 1] === "*") {
1530
1634
  const end = src.indexOf("*/", i + 2);
1531
1635
  if (end === -1) throw new ConfigError("jsonc: unterminated block comment", "");
1532
- for (const ch of src.slice(i, end + 2)) if (ch === "\n") out += "\n";
1636
+ out += lineBreaksOf(src.slice(i, end + 2)) || " ";
1533
1637
  i = end + 2;
1534
1638
  continue;
1535
1639
  }
@@ -558,26 +558,41 @@ function deepMergeHoconValues(base, overlay) {
558
558
  // src/internal/resolver/include-loader.ts
559
559
  var import_node_module = require("module");
560
560
  var nodePath = __toESM(require("path"), 1);
561
+ var import_node_url = require("url");
562
+
563
+ // src/internal/strip-bom.ts
564
+ function stripBom(input) {
565
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
566
+ }
561
567
 
562
568
  // src/internal/properties/properties.ts
563
569
  function parseProperties(input) {
564
570
  const pairs = [];
565
- for (const { text, line } of logicalLines(input)) {
571
+ for (const { text, line } of logicalLines(stripBom(input))) {
566
572
  const [rawKey, rawValue] = splitKeyValue(text);
567
573
  const key = unescapeProps(rawKey, line);
568
574
  if (key === "") continue;
569
- pairs.push([key, unescapeProps(rawValue, line)]);
575
+ pairs.push([key.split("."), unescapeProps(rawValue, line)]);
570
576
  }
571
577
  return nestPairs(pairs);
572
578
  }
573
579
  function nestPairs(pairs) {
574
580
  const root = /* @__PURE__ */ Object.create(null);
575
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
576
- for (const [key, value] of sorted) {
577
- setNested(root, key.split("."), value);
581
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
582
+ for (const [segments, value] of sorted) {
583
+ setNested(root, segments, value);
578
584
  }
579
585
  return root;
580
586
  }
587
+ function compareSegments(a, b) {
588
+ const n = Math.min(a.length, b.length);
589
+ for (let i = 0; i < n; i++) {
590
+ const x = a[i];
591
+ const y = b[i];
592
+ if (x !== y) return x < y ? -1 : 1;
593
+ }
594
+ return a.length - b.length;
595
+ }
581
596
  function logicalLines(input) {
582
597
  const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
583
598
  const out = [];
@@ -664,27 +679,20 @@ function unescapeProps(s, line) {
664
679
  }
665
680
  return out;
666
681
  }
667
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
668
682
  function setNested(obj, segments, value) {
669
683
  let current = obj;
670
684
  for (let i = 0; i < segments.length - 1; i++) {
671
685
  const seg = segments[i];
672
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
673
686
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
674
687
  current[seg] = /* @__PURE__ */ Object.create(null);
675
688
  }
676
689
  current = current[seg];
677
690
  }
678
691
  const last = segments[segments.length - 1];
679
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
680
692
  if (typeof current[last] === "object" && current[last] !== null) return;
681
693
  current[last] = value;
682
694
  }
683
695
 
684
- // src/internal/resolver/include-loader.ts
685
- var import_meta = {};
686
- var _require = (0, import_node_module.createRequire)(import_meta.url);
687
-
688
696
  // src/value/numeric-array.ts
689
697
  function numericObjectToArray(value) {
690
698
  if (value.kind !== "object") return null;
@@ -1272,10 +1280,10 @@ var Config = class _Config {
1272
1280
  }
1273
1281
  if (v.kind === "object") {
1274
1282
  const converted = numericObjectToArray(v);
1275
- if (converted !== null) return converted.map(hoconToJs);
1283
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1276
1284
  }
1277
1285
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1278
- return v.items.map(hoconToJs);
1286
+ return v.items.map((item) => hoconToJs(item));
1279
1287
  }
1280
1288
  has(path) {
1281
1289
  return this.lookupNode(path) !== void 0;
@@ -1403,8 +1411,52 @@ var Config = class _Config {
1403
1411
  originDescription: this._originDescription
1404
1412
  });
1405
1413
  }
1406
- toObject() {
1407
- return hoconToJs(this.root);
1414
+ /**
1415
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1416
+ * booleans and `null`.
1417
+ *
1418
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1419
+ * `constructor` and `prototype` included: each is defined as an **own data
1420
+ * property**, so the returned object's prototype is always
1421
+ * `Object.prototype`, and this call writes nothing to the global one.
1422
+ *
1423
+ * ### Passing the result to other code
1424
+ *
1425
+ * A config file — especially a `.properties` file or an env namespace you
1426
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1427
+ * next depends entirely on how the consumer copies it:
1428
+ *
1429
+ * ```ts
1430
+ * const data = cfg.toObject()
1431
+ * { ...data } // safe — own properties are copied as data
1432
+ * structuredClone(data) // safe
1433
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1434
+ * // Object.prototype's __proto__ setter: the key
1435
+ * // vanishes and config data becomes the result's
1436
+ * // prototype
1437
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1438
+ * // target['__proto__'], gets Object.prototype,
1439
+ * // and writes to it: global pollution
1440
+ * for (const k in data) … // prefer Object.entries / Object.keys
1441
+ * ```
1442
+ *
1443
+ * Those two hazards come from the **destination** object's prototype, so no
1444
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1445
+ *
1446
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1447
+ * nothing: every object comes back with a `null` prototype, so reading
1448
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1449
+ * `undefined` rather than something from `Object.prototype`, and code that
1450
+ * merges *into* this object cannot climb out of it. Keys are all still
1451
+ * present. The trade-off is the usual one for prototype-less objects: not
1452
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1453
+ *
1454
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1455
+ * here even when the config holds it exactly — read those with
1456
+ * {@link Config.getString} instead.
1457
+ */
1458
+ toObject(opts) {
1459
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1408
1460
  }
1409
1461
  /**
1410
1462
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1516,16 +1568,25 @@ function scalarToJs(raw, valueType) {
1516
1568
  return raw;
1517
1569
  }
1518
1570
  }
1519
- function hoconToJs(v) {
1571
+ function hoconToJs(v, nullPrototype = false) {
1520
1572
  switch (v.kind) {
1521
1573
  case "scalar":
1522
1574
  return scalarToJs(v.raw, v.valueType);
1523
1575
  case "array":
1524
- return v.items.map(hoconToJs);
1576
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1525
1577
  case "object": {
1526
- const obj = /* @__PURE__ */ Object.create(null);
1527
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1528
- return Object.assign({}, obj);
1578
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1579
+ if (nullPrototype) {
1580
+ const obj = /* @__PURE__ */ Object.create(null);
1581
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1582
+ value: val,
1583
+ writable: true,
1584
+ enumerable: true,
1585
+ configurable: true
1586
+ });
1587
+ return obj;
1588
+ }
1589
+ return Object.fromEntries(entries);
1529
1590
  }
1530
1591
  }
1531
1592
  }
@@ -1614,6 +1675,8 @@ function fromMap(values, originDescription) {
1614
1675
  const root = { kind: "object", fields };
1615
1676
  return Config._fromResolvedValue(root, { originDescription });
1616
1677
  }
1678
+ var INT64_MAX = 9223372036854775807n;
1679
+ var INT64_MIN = -9223372036854775808n;
1617
1680
  function coerceObject(obj, atPath) {
1618
1681
  const fields = /* @__PURE__ */ new Map();
1619
1682
  for (const [k, v] of Object.entries(obj)) {
@@ -1642,14 +1705,14 @@ function coerceValue(v, atPath) {
1642
1705
  atPath
1643
1706
  );
1644
1707
  }
1645
- return { kind: "scalar", raw: String(v), valueType: "number" };
1708
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1646
1709
  }
1647
1710
  case "boolean":
1648
1711
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1649
1712
  case "bigint": {
1650
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1713
+ if (v > INT64_MAX || v < INT64_MIN) {
1651
1714
  throw new ConfigError(
1652
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1715
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1653
1716
  atPath
1654
1717
  );
1655
1718
  }
@@ -1,4 +1,4 @@
1
- import { C as Config } from '../config-PdhVgeLS.cjs';
1
+ import { C as Config } from '../config-Dlq6rF3w.cjs';
2
2
 
3
3
  /**
4
4
  * Read a `java.util.Properties` file as HOCON config.
@@ -19,6 +19,11 @@ import { C as Config } from '../config-PdhVgeLS.cjs';
19
19
  * A `${a.b}` in a value stays that literal text — the file belongs to another
20
20
  * program that never agreed to HOCON's syntax (spec F0.2).
21
21
  *
22
+ * Dotted keys nest (F2.1), and every key is preserved — `__proto__`,
23
+ * `constructor` and `prototype` included (F2.9). They arrive as ordinary keys:
24
+ * the tree is built on null-prototype objects and `toObject()` defines own
25
+ * properties, so nothing can reach `Object.prototype`.
26
+ *
22
27
  * Shares its syntax layer with `include "x.properties"`, so the two cannot
23
28
  * drift apart.
24
29
  */
@@ -1,4 +1,4 @@
1
- import { C as Config } from '../config-PdhVgeLS.js';
1
+ import { C as Config } from '../config-Dlq6rF3w.js';
2
2
 
3
3
  /**
4
4
  * Read a `java.util.Properties` file as HOCON config.
@@ -19,6 +19,11 @@ import { C as Config } from '../config-PdhVgeLS.js';
19
19
  * A `${a.b}` in a value stays that literal text — the file belongs to another
20
20
  * program that never agreed to HOCON's syntax (spec F0.2).
21
21
  *
22
+ * Dotted keys nest (F2.1), and every key is preserved — `__proto__`,
23
+ * `constructor` and `prototype` included (F2.9). They arrive as ordinary keys:
24
+ * the tree is built on null-prototype objects and `toObject()` defines own
25
+ * properties, so nothing can reach `Object.prototype`.
26
+ *
22
27
  * Shares its syntax layer with `include "x.properties"`, so the two cannot
23
28
  * drift apart.
24
29
  */
@@ -522,26 +522,41 @@ function deepMergeHoconValues(base, overlay) {
522
522
  // src/internal/resolver/include-loader.ts
523
523
  import { createRequire } from "module";
524
524
  import * as nodePath from "path";
525
+ import { pathToFileURL } from "url";
526
+
527
+ // src/internal/strip-bom.ts
528
+ function stripBom(input) {
529
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
530
+ }
525
531
 
526
532
  // src/internal/properties/properties.ts
527
533
  function parseProperties(input) {
528
534
  const pairs = [];
529
- for (const { text, line } of logicalLines(input)) {
535
+ for (const { text, line } of logicalLines(stripBom(input))) {
530
536
  const [rawKey, rawValue] = splitKeyValue(text);
531
537
  const key = unescapeProps(rawKey, line);
532
538
  if (key === "") continue;
533
- pairs.push([key, unescapeProps(rawValue, line)]);
539
+ pairs.push([key.split("."), unescapeProps(rawValue, line)]);
534
540
  }
535
541
  return nestPairs(pairs);
536
542
  }
537
543
  function nestPairs(pairs) {
538
544
  const root = /* @__PURE__ */ Object.create(null);
539
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
540
- for (const [key, value] of sorted) {
541
- setNested(root, key.split("."), value);
545
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
546
+ for (const [segments, value] of sorted) {
547
+ setNested(root, segments, value);
542
548
  }
543
549
  return root;
544
550
  }
551
+ function compareSegments(a, b) {
552
+ const n = Math.min(a.length, b.length);
553
+ for (let i = 0; i < n; i++) {
554
+ const x = a[i];
555
+ const y = b[i];
556
+ if (x !== y) return x < y ? -1 : 1;
557
+ }
558
+ return a.length - b.length;
559
+ }
545
560
  function logicalLines(input) {
546
561
  const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
547
562
  const out = [];
@@ -628,26 +643,20 @@ function unescapeProps(s, line) {
628
643
  }
629
644
  return out;
630
645
  }
631
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
632
646
  function setNested(obj, segments, value) {
633
647
  let current = obj;
634
648
  for (let i = 0; i < segments.length - 1; i++) {
635
649
  const seg = segments[i];
636
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
637
650
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
638
651
  current[seg] = /* @__PURE__ */ Object.create(null);
639
652
  }
640
653
  current = current[seg];
641
654
  }
642
655
  const last = segments[segments.length - 1];
643
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
644
656
  if (typeof current[last] === "object" && current[last] !== null) return;
645
657
  current[last] = value;
646
658
  }
647
659
 
648
- // src/internal/resolver/include-loader.ts
649
- var _require = createRequire(import.meta.url);
650
-
651
660
  // src/value/numeric-array.ts
652
661
  function numericObjectToArray(value) {
653
662
  if (value.kind !== "object") return null;
@@ -1235,10 +1244,10 @@ var Config = class _Config {
1235
1244
  }
1236
1245
  if (v.kind === "object") {
1237
1246
  const converted = numericObjectToArray(v);
1238
- if (converted !== null) return converted.map(hoconToJs);
1247
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1239
1248
  }
1240
1249
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1241
- return v.items.map(hoconToJs);
1250
+ return v.items.map((item) => hoconToJs(item));
1242
1251
  }
1243
1252
  has(path) {
1244
1253
  return this.lookupNode(path) !== void 0;
@@ -1366,8 +1375,52 @@ var Config = class _Config {
1366
1375
  originDescription: this._originDescription
1367
1376
  });
1368
1377
  }
1369
- toObject() {
1370
- return hoconToJs(this.root);
1378
+ /**
1379
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1380
+ * booleans and `null`.
1381
+ *
1382
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1383
+ * `constructor` and `prototype` included: each is defined as an **own data
1384
+ * property**, so the returned object's prototype is always
1385
+ * `Object.prototype`, and this call writes nothing to the global one.
1386
+ *
1387
+ * ### Passing the result to other code
1388
+ *
1389
+ * A config file — especially a `.properties` file or an env namespace you
1390
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1391
+ * next depends entirely on how the consumer copies it:
1392
+ *
1393
+ * ```ts
1394
+ * const data = cfg.toObject()
1395
+ * { ...data } // safe — own properties are copied as data
1396
+ * structuredClone(data) // safe
1397
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1398
+ * // Object.prototype's __proto__ setter: the key
1399
+ * // vanishes and config data becomes the result's
1400
+ * // prototype
1401
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1402
+ * // target['__proto__'], gets Object.prototype,
1403
+ * // and writes to it: global pollution
1404
+ * for (const k in data) … // prefer Object.entries / Object.keys
1405
+ * ```
1406
+ *
1407
+ * Those two hazards come from the **destination** object's prototype, so no
1408
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1409
+ *
1410
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1411
+ * nothing: every object comes back with a `null` prototype, so reading
1412
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1413
+ * `undefined` rather than something from `Object.prototype`, and code that
1414
+ * merges *into* this object cannot climb out of it. Keys are all still
1415
+ * present. The trade-off is the usual one for prototype-less objects: not
1416
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1417
+ *
1418
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1419
+ * here even when the config holds it exactly — read those with
1420
+ * {@link Config.getString} instead.
1421
+ */
1422
+ toObject(opts) {
1423
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1371
1424
  }
1372
1425
  /**
1373
1426
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1479,16 +1532,25 @@ function scalarToJs(raw, valueType) {
1479
1532
  return raw;
1480
1533
  }
1481
1534
  }
1482
- function hoconToJs(v) {
1535
+ function hoconToJs(v, nullPrototype = false) {
1483
1536
  switch (v.kind) {
1484
1537
  case "scalar":
1485
1538
  return scalarToJs(v.raw, v.valueType);
1486
1539
  case "array":
1487
- return v.items.map(hoconToJs);
1540
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1488
1541
  case "object": {
1489
- const obj = /* @__PURE__ */ Object.create(null);
1490
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1491
- return Object.assign({}, obj);
1542
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1543
+ if (nullPrototype) {
1544
+ const obj = /* @__PURE__ */ Object.create(null);
1545
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1546
+ value: val,
1547
+ writable: true,
1548
+ enumerable: true,
1549
+ configurable: true
1550
+ });
1551
+ return obj;
1552
+ }
1553
+ return Object.fromEntries(entries);
1492
1554
  }
1493
1555
  }
1494
1556
  }
@@ -1577,6 +1639,8 @@ function fromMap(values, originDescription) {
1577
1639
  const root = { kind: "object", fields };
1578
1640
  return Config._fromResolvedValue(root, { originDescription });
1579
1641
  }
1642
+ var INT64_MAX = 9223372036854775807n;
1643
+ var INT64_MIN = -9223372036854775808n;
1580
1644
  function coerceObject(obj, atPath) {
1581
1645
  const fields = /* @__PURE__ */ new Map();
1582
1646
  for (const [k, v] of Object.entries(obj)) {
@@ -1605,14 +1669,14 @@ function coerceValue(v, atPath) {
1605
1669
  atPath
1606
1670
  );
1607
1671
  }
1608
- return { kind: "scalar", raw: String(v), valueType: "number" };
1672
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1609
1673
  }
1610
1674
  case "boolean":
1611
1675
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1612
1676
  case "bigint": {
1613
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1677
+ if (v > INT64_MAX || v < INT64_MIN) {
1614
1678
  throw new ConfigError(
1615
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1679
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1616
1680
  atPath
1617
1681
  );
1618
1682
  }