@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.
- package/README.ja.md +129 -1
- package/README.md +90 -2
- package/dist/adapters/env.cjs +98 -28
- package/dist/adapters/env.d.cts +16 -1
- package/dist/adapters/env.d.ts +16 -1
- package/dist/adapters/env.js +98 -27
- package/dist/adapters/jsonc.cjs +120 -17
- package/dist/adapters/jsonc.d.cts +24 -4
- package/dist/adapters/jsonc.d.ts +24 -4
- package/dist/adapters/jsonc.js +120 -16
- package/dist/adapters/properties.cjs +87 -24
- package/dist/adapters/properties.d.cts +6 -1
- package/dist/adapters/properties.d.ts +6 -1
- package/dist/adapters/properties.js +87 -23
- package/dist/adapters/toml.cjs +79 -20
- package/dist/adapters/toml.d.cts +1 -1
- package/dist/adapters/toml.d.ts +1 -1
- package/dist/adapters/toml.js +79 -19
- package/dist/adapters/yaml.cjs +100 -24
- package/dist/adapters/yaml.d.cts +10 -2
- package/dist/adapters/yaml.d.ts +10 -2
- package/dist/adapters/yaml.js +100 -23
- package/dist/{config-PdhVgeLS.d.cts → config-Dlq6rF3w.d.cts} +47 -1
- package/dist/{config-PdhVgeLS.d.ts → config-Dlq6rF3w.d.ts} +47 -1
- package/dist/index.cjs +104 -22
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +111 -22
- package/dist/zod.cjs +100 -20
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +107 -20
- package/package.json +58 -22
package/dist/adapters/jsonc.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
1237
|
-
|
|
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
|
|
1357
|
-
|
|
1358
|
-
|
|
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 >
|
|
1540
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1481
1541
|
throw new ConfigError(
|
|
1482
|
-
`fromMap:
|
|
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]
|
|
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
|
-
|
|
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
|
|
576
|
-
for (const [
|
|
577
|
-
setNested(root,
|
|
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
|
-
|
|
1407
|
-
|
|
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
|
|
1527
|
-
|
|
1528
|
-
|
|
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 >
|
|
1713
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1651
1714
|
throw new ConfigError(
|
|
1652
|
-
`fromMap:
|
|
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-
|
|
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-
|
|
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
|
|
540
|
-
for (const [
|
|
541
|
-
setNested(root,
|
|
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
|
-
|
|
1370
|
-
|
|
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
|
|
1490
|
-
|
|
1491
|
-
|
|
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 >
|
|
1677
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1614
1678
|
throw new ConfigError(
|
|
1615
|
-
`fromMap:
|
|
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
|
}
|