@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.
@@ -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
  type EnvOptions = {
4
4
  /**
@@ -22,6 +22,16 @@ type EnvOptions = {
22
22
  * Reading a single variable needs nothing from here — HOCON's own `${?VAR}`
23
23
  * already does that. This is for mounting a whole namespace as a subtree.
24
24
  *
25
+ * `__` is the only path separator: a single `_` stays part of its segment
26
+ * (`APP_DB__MAX_CONN` → `db.max_conn`), and a literal `.` in a variable name is
27
+ * key text rather than a boundary, so `APP_FOO.BAR` becomes the single
28
+ * top-level key `foo.bar` — addressable as the quoted path `"foo.bar"` — and
29
+ * coexists with `APP_FOO__BAR` instead of colliding with it (F1.2).
30
+ *
31
+ * Two names that do map to one path are an error, the environment having no
32
+ * order to break the tie with (F1.6). Keys named `__proto__`, `constructor` or
33
+ * `prototype` are kept like any other (F2.9).
34
+ *
25
35
  * Values are always strings, and a `${...}` inside one stays literal (F0.2, F1.4).
26
36
  */
27
37
  declare function loadEnv(opts?: EnvOptions & {
@@ -35,6 +45,11 @@ declare function loadEnv(opts?: EnvOptions & {
35
45
  * double quotes with `\n \r \t \\ \"`. Multi-line values and trailing comments
36
46
  * are not supported — an unquoted value containing ` #` is an error rather than
37
47
  * a guess about whether a comment was meant. No `${...}` expansion.
48
+ *
49
+ * Names map to paths exactly as in {@link loadEnv}: `__` is the only separator,
50
+ * so a literal `.` stays key text (`FOO.BAR` → the single key `foo.bar`). A
51
+ * file has a definite line order, so a repeated name is last-wins (F0.7)
52
+ * rather than the collision error the process environment gets.
38
53
  */
39
54
  declare function parseDotEnv(input: string, opts?: EnvOptions): Config;
40
55
 
@@ -513,36 +513,73 @@ 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
+ 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
+ }
522
+
523
+ // src/internal/depth.ts
524
+ var MAX_PATH_SEGMENTS = 64;
525
+ function tooDeep(segments) {
526
+ return segments > MAX_PATH_SEGMENTS;
527
+ }
528
+ function guardStackDepth(fn, wrap) {
529
+ try {
530
+ return fn();
531
+ } catch (e) {
532
+ if (!isStackOverflow(e)) throw e;
533
+ throw wrap(STACK_MESSAGE);
534
+ }
535
+ }
536
+ var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
537
+ function isStackOverflow(e) {
538
+ if (!(e instanceof RangeError)) return false;
539
+ const m = e.message.toLowerCase();
540
+ return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
541
+ m.includes("recursion") || // SpiderMonkey: "too much recursion"
542
+ m.includes("stack overflow");
543
+ }
544
+ function depthError(message) {
545
+ return new ConfigError(message, "");
546
+ }
516
547
 
517
548
  // src/internal/properties/properties.ts
518
549
  function nestPairs(pairs) {
519
550
  const root = /* @__PURE__ */ Object.create(null);
520
- const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
521
- for (const [key, value] of sorted) {
522
- setNested(root, key.split("."), value);
551
+ const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
552
+ for (const [segments, value] of sorted) {
553
+ setNested(root, segments, value);
523
554
  }
524
555
  return root;
525
556
  }
526
- var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
557
+ function compareSegments(a, b) {
558
+ const n = Math.min(a.length, b.length);
559
+ for (let i = 0; i < n; i++) {
560
+ const x = a[i];
561
+ const y = b[i];
562
+ if (x !== y) return x < y ? -1 : 1;
563
+ }
564
+ return a.length - b.length;
565
+ }
566
+ function pathKey(segments) {
567
+ return JSON.stringify(segments);
568
+ }
527
569
  function setNested(obj, segments, value) {
528
570
  let current = obj;
529
571
  for (let i = 0; i < segments.length - 1; i++) {
530
572
  const seg = segments[i];
531
- if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
532
573
  if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
533
574
  current[seg] = /* @__PURE__ */ Object.create(null);
534
575
  }
535
576
  current = current[seg];
536
577
  }
537
578
  const last = segments[segments.length - 1];
538
- if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
539
579
  if (typeof current[last] === "object" && current[last] !== null) return;
540
580
  current[last] = value;
541
581
  }
542
582
 
543
- // src/internal/resolver/include-loader.ts
544
- var _require = createRequire(import.meta.url);
545
-
546
583
  // src/value/numeric-array.ts
547
584
  function numericObjectToArray(value) {
548
585
  if (value.kind !== "object") return null;
@@ -1130,10 +1167,10 @@ var Config = class _Config {
1130
1167
  }
1131
1168
  if (v.kind === "object") {
1132
1169
  const converted = numericObjectToArray(v);
1133
- if (converted !== null) return converted.map(hoconToJs);
1170
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1134
1171
  }
1135
1172
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1136
- return v.items.map(hoconToJs);
1173
+ return v.items.map((item) => hoconToJs(item));
1137
1174
  }
1138
1175
  has(path) {
1139
1176
  return this.lookupNode(path) !== void 0;
@@ -1261,8 +1298,52 @@ var Config = class _Config {
1261
1298
  originDescription: this._originDescription
1262
1299
  });
1263
1300
  }
1264
- toObject() {
1265
- return hoconToJs(this.root);
1301
+ /**
1302
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1303
+ * booleans and `null`.
1304
+ *
1305
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1306
+ * `constructor` and `prototype` included: each is defined as an **own data
1307
+ * property**, so the returned object's prototype is always
1308
+ * `Object.prototype`, and this call writes nothing to the global one.
1309
+ *
1310
+ * ### Passing the result to other code
1311
+ *
1312
+ * A config file — especially a `.properties` file or an env namespace you
1313
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1314
+ * next depends entirely on how the consumer copies it:
1315
+ *
1316
+ * ```ts
1317
+ * const data = cfg.toObject()
1318
+ * { ...data } // safe — own properties are copied as data
1319
+ * structuredClone(data) // safe
1320
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1321
+ * // Object.prototype's __proto__ setter: the key
1322
+ * // vanishes and config data becomes the result's
1323
+ * // prototype
1324
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1325
+ * // target['__proto__'], gets Object.prototype,
1326
+ * // and writes to it: global pollution
1327
+ * for (const k in data) … // prefer Object.entries / Object.keys
1328
+ * ```
1329
+ *
1330
+ * Those two hazards come from the **destination** object's prototype, so no
1331
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1332
+ *
1333
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1334
+ * nothing: every object comes back with a `null` prototype, so reading
1335
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1336
+ * `undefined` rather than something from `Object.prototype`, and code that
1337
+ * merges *into* this object cannot climb out of it. Keys are all still
1338
+ * present. The trade-off is the usual one for prototype-less objects: not
1339
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1340
+ *
1341
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1342
+ * here even when the config holds it exactly — read those with
1343
+ * {@link Config.getString} instead.
1344
+ */
1345
+ toObject(opts) {
1346
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1266
1347
  }
1267
1348
  /**
1268
1349
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1374,16 +1455,25 @@ function scalarToJs(raw, valueType) {
1374
1455
  return raw;
1375
1456
  }
1376
1457
  }
1377
- function hoconToJs(v) {
1458
+ function hoconToJs(v, nullPrototype = false) {
1378
1459
  switch (v.kind) {
1379
1460
  case "scalar":
1380
1461
  return scalarToJs(v.raw, v.valueType);
1381
1462
  case "array":
1382
- return v.items.map(hoconToJs);
1463
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1383
1464
  case "object": {
1384
- const obj = /* @__PURE__ */ Object.create(null);
1385
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1386
- return Object.assign({}, obj);
1465
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1466
+ if (nullPrototype) {
1467
+ const obj = /* @__PURE__ */ Object.create(null);
1468
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1469
+ value: val,
1470
+ writable: true,
1471
+ enumerable: true,
1472
+ configurable: true
1473
+ });
1474
+ return obj;
1475
+ }
1476
+ return Object.fromEntries(entries);
1387
1477
  }
1388
1478
  }
1389
1479
  }
@@ -1468,10 +1558,15 @@ function normalizeLeadingZeroNumber(raw) {
1468
1558
 
1469
1559
  // src/value-factory.ts
1470
1560
  function fromMap(values, originDescription) {
1471
- const fields = coerceObject(values, "");
1561
+ const fields = guardStackDepth(
1562
+ () => coerceObject(values, ""),
1563
+ (msg) => depthError(`fromMap: ${msg}`)
1564
+ );
1472
1565
  const root = { kind: "object", fields };
1473
1566
  return Config._fromResolvedValue(root, { originDescription });
1474
1567
  }
1568
+ var INT64_MAX = 9223372036854775807n;
1569
+ var INT64_MIN = -9223372036854775808n;
1475
1570
  function coerceObject(obj, atPath) {
1476
1571
  const fields = /* @__PURE__ */ new Map();
1477
1572
  for (const [k, v] of Object.entries(obj)) {
@@ -1500,14 +1595,14 @@ function coerceValue(v, atPath) {
1500
1595
  atPath
1501
1596
  );
1502
1597
  }
1503
- return { kind: "scalar", raw: String(v), valueType: "number" };
1598
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1504
1599
  }
1505
1600
  case "boolean":
1506
1601
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1507
1602
  case "bigint": {
1508
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1603
+ if (v > INT64_MAX || v < INT64_MIN) {
1509
1604
  throw new ConfigError(
1510
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1605
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1511
1606
  atPath
1512
1607
  );
1513
1608
  }
@@ -1548,11 +1643,12 @@ function loadEnv(opts = {}) {
1548
1643
  const value = source[name];
1549
1644
  if (value === void 0) continue;
1550
1645
  const path = toPath(name.slice(prefix.length));
1551
- const prev = seen.get(path);
1646
+ const key = pathKey(path);
1647
+ const prev = seen.get(key);
1552
1648
  if (prev !== void 0) {
1553
- throw new ConfigError(`loadEnv: ${prev} and ${name} both map to "${path}"`, path);
1649
+ throw new ConfigError(`loadEnv: ${prev} and ${name} both map to "${path.join(".")}"`, path.join("."));
1554
1650
  }
1555
- seen.set(path, name);
1651
+ seen.set(key, name);
1556
1652
  pairs.push([path, value]);
1557
1653
  }
1558
1654
  return fromMap(nestPairs(pairs), opts.originDescription ?? "environment variables");
@@ -1561,27 +1657,37 @@ function parseDotEnv(input, opts = {}) {
1561
1657
  const origin = opts.originDescription ?? ".env";
1562
1658
  const prefix = opts.prefix ?? "";
1563
1659
  const pairs = [];
1564
- const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1660
+ const lines = stripBom(input).replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
1565
1661
  for (let i = 0; i < lines.length; i++) {
1566
1662
  const raw = (lines[i] ?? "").trim();
1567
1663
  if (raw === "" || raw.startsWith("#")) continue;
1568
- const line = raw.startsWith("export ") ? raw.slice("export ".length) : raw;
1664
+ const line = stripExport(raw);
1569
1665
  const eq = line.indexOf("=");
1570
1666
  if (eq === -1) throw new ConfigError(`${origin}:${i + 1}: expected NAME=value`, "");
1571
1667
  const name = line.slice(0, eq).trim();
1572
1668
  if (name === "") throw new ConfigError(`${origin}:${i + 1}: empty variable name`, "");
1573
- const value = dotEnvValue(line.slice(eq + 1).replace(/^[ \t]+/, ""), origin, i + 1, name);
1574
1669
  if (!name.startsWith(prefix)) continue;
1670
+ checkName(name, origin, i + 1);
1671
+ const value = dotEnvValue(line.slice(eq + 1).replace(/^[ \t]+/, ""), origin, i + 1, name);
1575
1672
  pairs.push([toPath(name.slice(prefix.length)), value]);
1576
1673
  }
1577
1674
  return fromMap(nestPairs(pairs), origin);
1578
1675
  }
1579
1676
  function toPath(name) {
1580
- const segs = name.split(SEPARATOR).map((s) => s.toLowerCase());
1677
+ const segs = name.split(SEPARATOR).map(asciiLower);
1581
1678
  if (segs.some((s) => s === "")) {
1582
1679
  throw new ConfigError(`env: "${name}" produces an empty path segment`, name);
1583
1680
  }
1584
- return segs.join(".");
1681
+ if (tooDeep(segs.length)) {
1682
+ throw new ConfigError(
1683
+ `env: "${name}" maps to a path ${segs.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
1684
+ name
1685
+ );
1686
+ }
1687
+ return segs;
1688
+ }
1689
+ function asciiLower(s) {
1690
+ return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
1585
1691
  }
1586
1692
  function dotEnvValue(v, origin, line, name) {
1587
1693
  const fail = (msg) => {
@@ -1623,6 +1729,20 @@ function dotEnvValue(v, origin, line, name) {
1623
1729
  }
1624
1730
  return trimmed;
1625
1731
  }
1732
+ function stripExport(line) {
1733
+ const m = /^export[ \t]+/.exec(line);
1734
+ return m ? line.slice(m[0].length) : line;
1735
+ }
1736
+ function checkName(name, origin, line) {
1737
+ const bad = /\p{White_Space}|#/u.exec(name);
1738
+ if (bad) {
1739
+ const what = bad[0] === "#" ? "'#'" : "whitespace";
1740
+ throw new ConfigError(
1741
+ `${origin}:${line}: variable name "${name}" contains ${what}; the line is not NAME=value (spec F1.7)`,
1742
+ name
1743
+ );
1744
+ }
1745
+ }
1626
1746
  export {
1627
1747
  loadEnv,
1628
1748
  parseDotEnv
@@ -550,8 +550,33 @@ function deepMergeHoconValues(base, overlay) {
550
550
  // src/internal/resolver/include-loader.ts
551
551
  var import_node_module = require("module");
552
552
  var nodePath = __toESM(require("path"), 1);
553
- var import_meta = {};
554
- var _require = (0, import_node_module.createRequire)(import_meta.url);
553
+ var import_node_url = require("url");
554
+
555
+ // src/internal/strip-bom.ts
556
+ function stripBom(input) {
557
+ return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
558
+ }
559
+
560
+ // src/internal/depth.ts
561
+ function guardStackDepth(fn, wrap) {
562
+ try {
563
+ return fn();
564
+ } catch (e) {
565
+ if (!isStackOverflow(e)) throw e;
566
+ throw wrap(STACK_MESSAGE);
567
+ }
568
+ }
569
+ var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
570
+ function isStackOverflow(e) {
571
+ if (!(e instanceof RangeError)) return false;
572
+ const m = e.message.toLowerCase();
573
+ return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
574
+ m.includes("recursion") || // SpiderMonkey: "too much recursion"
575
+ m.includes("stack overflow");
576
+ }
577
+ function depthError(message) {
578
+ return new ConfigError(message, "");
579
+ }
555
580
 
556
581
  // src/value/numeric-array.ts
557
582
  function numericObjectToArray(value) {
@@ -1140,10 +1165,10 @@ var Config = class _Config {
1140
1165
  }
1141
1166
  if (v.kind === "object") {
1142
1167
  const converted = numericObjectToArray(v);
1143
- if (converted !== null) return converted.map(hoconToJs);
1168
+ if (converted !== null) return converted.map((item) => hoconToJs(item));
1144
1169
  }
1145
1170
  if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
1146
- return v.items.map(hoconToJs);
1171
+ return v.items.map((item) => hoconToJs(item));
1147
1172
  }
1148
1173
  has(path) {
1149
1174
  return this.lookupNode(path) !== void 0;
@@ -1271,8 +1296,52 @@ var Config = class _Config {
1271
1296
  originDescription: this._originDescription
1272
1297
  });
1273
1298
  }
1274
- toObject() {
1275
- return hoconToJs(this.root);
1299
+ /**
1300
+ * The whole config as plain JS data: objects, arrays, strings, numbers,
1301
+ * booleans and `null`.
1302
+ *
1303
+ * Keys are reproduced exactly as the config carries them, `__proto__`,
1304
+ * `constructor` and `prototype` included: each is defined as an **own data
1305
+ * property**, so the returned object's prototype is always
1306
+ * `Object.prototype`, and this call writes nothing to the global one.
1307
+ *
1308
+ * ### Passing the result to other code
1309
+ *
1310
+ * A config file — especially a `.properties` file or an env namespace you
1311
+ * merged in — can therefore hand you an own `__proto__` key, and what happens
1312
+ * next depends entirely on how the consumer copies it:
1313
+ *
1314
+ * ```ts
1315
+ * const data = cfg.toObject()
1316
+ * { ...data } // safe — own properties are copied as data
1317
+ * structuredClone(data) // safe
1318
+ * Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
1319
+ * // Object.prototype's __proto__ setter: the key
1320
+ * // vanishes and config data becomes the result's
1321
+ * // prototype
1322
+ * deepMerge({}, data) // NOT safe — a naive recursive merge reads
1323
+ * // target['__proto__'], gets Object.prototype,
1324
+ * // and writes to it: global pollution
1325
+ * for (const k in data) … // prefer Object.entries / Object.keys
1326
+ * ```
1327
+ *
1328
+ * Those two hazards come from the **destination** object's prototype, so no
1329
+ * option here can disarm them — copy with spread or `structuredClone` instead.
1330
+ *
1331
+ * What `{ nullPrototype: true }` does give you is a result that inherits
1332
+ * nothing: every object comes back with a `null` prototype, so reading
1333
+ * `x.__proto__`, `x.constructor` or `x.toString` yields config data or
1334
+ * `undefined` rather than something from `Object.prototype`, and code that
1335
+ * merges *into* this object cannot climb out of it. Keys are all still
1336
+ * present. The trade-off is the usual one for prototype-less objects: not
1337
+ * `instanceof Object`, and no `hasOwnProperty` of their own.
1338
+ *
1339
+ * Numbers follow the JS number model, so an integer wider than 2^53 rounds
1340
+ * here even when the config holds it exactly — read those with
1341
+ * {@link Config.getString} instead.
1342
+ */
1343
+ toObject(opts) {
1344
+ return hoconToJs(this.root, opts?.nullPrototype === true);
1276
1345
  }
1277
1346
  /**
1278
1347
  * @internal Test-only: renders this resolved Config's value tree as canonical
@@ -1384,16 +1453,25 @@ function scalarToJs(raw, valueType) {
1384
1453
  return raw;
1385
1454
  }
1386
1455
  }
1387
- function hoconToJs(v) {
1456
+ function hoconToJs(v, nullPrototype = false) {
1388
1457
  switch (v.kind) {
1389
1458
  case "scalar":
1390
1459
  return scalarToJs(v.raw, v.valueType);
1391
1460
  case "array":
1392
- return v.items.map(hoconToJs);
1461
+ return v.items.map((item) => hoconToJs(item, nullPrototype));
1393
1462
  case "object": {
1394
- const obj = /* @__PURE__ */ Object.create(null);
1395
- for (const [k, val] of v.fields) obj[k] = hoconToJs(val);
1396
- return Object.assign({}, obj);
1463
+ const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
1464
+ if (nullPrototype) {
1465
+ const obj = /* @__PURE__ */ Object.create(null);
1466
+ for (const [k, val] of entries) Object.defineProperty(obj, k, {
1467
+ value: val,
1468
+ writable: true,
1469
+ enumerable: true,
1470
+ configurable: true
1471
+ });
1472
+ return obj;
1473
+ }
1474
+ return Object.fromEntries(entries);
1397
1475
  }
1398
1476
  }
1399
1477
  }
@@ -1478,10 +1556,15 @@ function normalizeLeadingZeroNumber(raw) {
1478
1556
 
1479
1557
  // src/value-factory.ts
1480
1558
  function fromMap(values, originDescription) {
1481
- const fields = coerceObject(values, "");
1559
+ const fields = guardStackDepth(
1560
+ () => coerceObject(values, ""),
1561
+ (msg) => depthError(`fromMap: ${msg}`)
1562
+ );
1482
1563
  const root = { kind: "object", fields };
1483
1564
  return Config._fromResolvedValue(root, { originDescription });
1484
1565
  }
1566
+ var INT64_MAX = 9223372036854775807n;
1567
+ var INT64_MIN = -9223372036854775808n;
1485
1568
  function coerceObject(obj, atPath) {
1486
1569
  const fields = /* @__PURE__ */ new Map();
1487
1570
  for (const [k, v] of Object.entries(obj)) {
@@ -1510,14 +1593,14 @@ function coerceValue(v, atPath) {
1510
1593
  atPath
1511
1594
  );
1512
1595
  }
1513
- return { kind: "scalar", raw: String(v), valueType: "number" };
1596
+ return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
1514
1597
  }
1515
1598
  case "boolean":
1516
1599
  return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
1517
1600
  case "bigint": {
1518
- if (v > BigInt(Number.MAX_SAFE_INTEGER) || v < BigInt(Number.MIN_SAFE_INTEGER)) {
1601
+ if (v > INT64_MAX || v < INT64_MIN) {
1519
1602
  throw new ConfigError(
1520
- `fromMap: bigint ${v} exceeds Number.MAX_SAFE_INTEGER (not representable as HOCON number) at path "${atPath}"`,
1603
+ `fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
1521
1604
  atPath
1522
1605
  );
1523
1606
  }
@@ -1541,7 +1624,10 @@ function coerceValue(v, atPath) {
1541
1624
 
1542
1625
  // src/adapters/jsonc.ts
1543
1626
  function parseJsonc(input, originDescription) {
1544
- const doc = JSON.parse(stripTrailingCommas(stripComments(input)));
1627
+ const doc = guardStackDepth(
1628
+ () => JSON.parse(stripTrailingCommas(stripComments(stripBom(input))), bigIntReviver),
1629
+ (msg) => depthError(`jsonc: ${msg}`)
1630
+ );
1545
1631
  if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
1546
1632
  throw new ConfigError(
1547
1633
  `jsonc: document root is ${Array.isArray(doc) ? "an array" : typeof doc}, but a config root must be an object (spec F0.3)`,
@@ -1550,6 +1636,50 @@ function parseJsonc(input, originDescription) {
1550
1636
  }
1551
1637
  return fromMap(doc, originDescription);
1552
1638
  }
1639
+ function isIntegerLiteral(source) {
1640
+ return !/[.eE]/.test(source);
1641
+ }
1642
+ var HAS_SOURCE_TEXT_ACCESS = (() => {
1643
+ let seen;
1644
+ JSON.parse("1", function(_k, v, context) {
1645
+ seen = context?.source;
1646
+ return v;
1647
+ });
1648
+ return seen !== void 0;
1649
+ })();
1650
+ function bigIntReviver(key, value, context) {
1651
+ if (typeof value !== "number" || Number.isSafeInteger(value)) return value;
1652
+ const source = context?.source;
1653
+ if (source === void 0) {
1654
+ if (!Number.isInteger(value)) return value;
1655
+ throw new ConfigError(
1656
+ `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)`,
1657
+ key
1658
+ );
1659
+ }
1660
+ if (!isIntegerLiteral(source)) return value;
1661
+ return BigInt(source);
1662
+ }
1663
+ function isLineBreak(c) {
1664
+ return c === "\n" || c === "\r";
1665
+ }
1666
+ function lineBreaksOf(span) {
1667
+ let out = "";
1668
+ for (let i = 0; i < span.length; i++) {
1669
+ const c = span[i];
1670
+ if (c === "\r") {
1671
+ if (span[i + 1] === "\n") {
1672
+ out += "\r\n";
1673
+ i++;
1674
+ } else {
1675
+ out += "\r";
1676
+ }
1677
+ } else if (c === "\n") {
1678
+ out += "\n";
1679
+ }
1680
+ }
1681
+ return out;
1682
+ }
1553
1683
  function stripComments(src) {
1554
1684
  let out = "";
1555
1685
  for (let i = 0; i < src.length; ) {
@@ -1561,13 +1691,13 @@ function stripComments(src) {
1561
1691
  continue;
1562
1692
  }
1563
1693
  if (c === "/" && src[i + 1] === "/") {
1564
- while (i < src.length && src[i] !== "\n") i++;
1694
+ while (i < src.length && !isLineBreak(src[i])) i++;
1565
1695
  continue;
1566
1696
  }
1567
1697
  if (c === "/" && src[i + 1] === "*") {
1568
1698
  const end = src.indexOf("*/", i + 2);
1569
1699
  if (end === -1) throw new ConfigError("jsonc: unterminated block comment", "");
1570
- for (const ch of src.slice(i, end + 2)) if (ch === "\n") out += "\n";
1700
+ out += lineBreaksOf(src.slice(i, end + 2)) || " ";
1571
1701
  i = end + 2;
1572
1702
  continue;
1573
1703
  }
@@ -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 JSON with comments and trailing commas — the dialect VS Code and
@@ -10,15 +10,46 @@ import { C as Config } from '../config-PdhVgeLS.cjs';
10
10
  * of its own.)
11
11
  *
12
12
  * Comments and trailing commas are removed and `JSON.parse` does the rest, so
13
- * the accepted grammar is otherwise exactly the platform's JSON.
13
+ * the accepted grammar is otherwise exactly the platform's JSON. A comment is
14
+ * replaced by whitespace rather than deleted, so it separates the tokens around
15
+ * it and `{"a":1/*x*\/2}` is a syntax error rather than `{"a":12}` (F3.2).
14
16
  *
15
- * See docs/specs/format-ingestion-mapping.md items F3.x in the hocon scope.
17
+ * Integers are ingested losslessly (F0.5): a literal too wide for a JS `number`
18
+ * keeps its digits — `getString` returns them exactly — and one outside int64
19
+ * is refused instead of being rounded. `getNumber` and `toObject` still apply
20
+ * the JS number model, as they do for HOCON's own literals, so read large
21
+ * identifiers with `getString`.
22
+ *
23
+ * **An unpaired `\uXXXX` surrogate is accepted here and refused by go.hocon,
24
+ * py.hocon and rs.hocon** (F3.5). That is deliberate, and the same divergence
25
+ * the properties adapter already carries under F2.8: a JavaScript string is
26
+ * UTF-16, like Java's, so it holds a lone surrogate natively and round-trips it.
27
+ * A Go or Rust string cannot hold one at all — Go silently substituted U+FFFD
28
+ * until F3.5 — and a Python `str` can hold one but cannot encode it as UTF-8,
29
+ * so all three refuse rather than defer the failure. Refusing here would mean
30
+ * this spec overriding the host language rather than protecting the user; see
31
+ * S1.2.6 for the class.
32
+ *
33
+ * See the F3.x items in the format-ingestion mapping spec:
34
+ * https://github.com/o3co/xx.hocon/blob/main/docs/format-ingestion-mapping.md
16
35
  */
17
36
  declare function parseJsonc(input: string, originDescription?: string): Config;
18
37
  /**
19
38
  * Remove `//` line comments and block comments, leaving string literals alone.
20
- * Newlines inside removed spans are kept so JSON.parse still reports useful
21
- * positions.
39
+ *
40
+ * F3.2: a comment is replaced by *whitespace*, never the empty string — it must
41
+ * keep separating the tokens around it, so a `1`, a block comment and a `2` stay
42
+ * two tokens and fail the JSON decode instead of silently reading as `12`.
43
+ * Newlines inside a removed span are kept so JSON.parse still reports useful
44
+ * positions; a span with no newline becomes a single space.
45
+ *
46
+ * A `//` comment ends at **LF or CR** (F3.2), and at nothing else. Stopping only
47
+ * at LF let a CR-terminated comment eat the rest of the line, and the
48
+ * trailing-comma pass then tidied the remains into valid JSON — so
49
+ * `{"a":1,//c\r"b":2,\n"c":3}` decoded as `{"a":1,"c":3}`, losing a key with no
50
+ * error at all. Stopping at U+2028/U+2029 would be the mirror-image mistake:
51
+ * ending a comment the dialect's own parser is still inside, so the document
52
+ * would mean something different here than in VS Code. See {@link isLineBreak}.
22
53
  */
23
54
  declare function stripComments(src: string): string;
24
55