@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.
- package/README.ja.md +129 -1
- package/README.md +103 -10
- package/dist/adapters/env.cjs +150 -31
- package/dist/adapters/env.d.cts +16 -1
- package/dist/adapters/env.d.ts +16 -1
- package/dist/adapters/env.js +150 -30
- package/dist/adapters/jsonc.cjs +148 -18
- package/dist/adapters/jsonc.d.cts +36 -5
- package/dist/adapters/jsonc.d.ts +36 -5
- package/dist/adapters/jsonc.js +148 -17
- package/dist/adapters/properties.cjs +124 -25
- package/dist/adapters/properties.d.cts +6 -1
- package/dist/adapters/properties.d.ts +6 -1
- package/dist/adapters/properties.js +124 -24
- package/dist/adapters/toml.cjs +111 -22
- package/dist/adapters/toml.d.cts +3 -2
- package/dist/adapters/toml.d.ts +3 -2
- package/dist/adapters/toml.js +111 -21
- package/dist/adapters/yaml.cjs +142 -27
- package/dist/adapters/yaml.d.cts +12 -3
- package/dist/adapters/yaml.d.ts +12 -3
- package/dist/adapters/yaml.js +142 -26
- 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 +162 -31
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +169 -31
- package/dist/zod.cjs +153 -23
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +160 -23
- package/package.json +58 -22
package/dist/adapters/jsonc.d.ts
CHANGED
|
@@ -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 JSON with comments and trailing commas — the dialect VS Code and
|
|
@@ -10,15 +10,46 @@ import { C as Config } from '../config-PdhVgeLS.js';
|
|
|
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
|
-
*
|
|
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
|
-
*
|
|
21
|
-
*
|
|
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
|
|
package/dist/adapters/jsonc.js
CHANGED
|
@@ -513,7 +513,33 @@ 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
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/internal/depth.ts
|
|
524
|
+
function guardStackDepth(fn, wrap) {
|
|
525
|
+
try {
|
|
526
|
+
return fn();
|
|
527
|
+
} catch (e) {
|
|
528
|
+
if (!isStackOverflow(e)) throw e;
|
|
529
|
+
throw wrap(STACK_MESSAGE);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
|
|
533
|
+
function isStackOverflow(e) {
|
|
534
|
+
if (!(e instanceof RangeError)) return false;
|
|
535
|
+
const m = e.message.toLowerCase();
|
|
536
|
+
return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
|
|
537
|
+
m.includes("recursion") || // SpiderMonkey: "too much recursion"
|
|
538
|
+
m.includes("stack overflow");
|
|
539
|
+
}
|
|
540
|
+
function depthError(message) {
|
|
541
|
+
return new ConfigError(message, "");
|
|
542
|
+
}
|
|
517
543
|
|
|
518
544
|
// src/value/numeric-array.ts
|
|
519
545
|
function numericObjectToArray(value) {
|
|
@@ -1102,10 +1128,10 @@ var Config = class _Config {
|
|
|
1102
1128
|
}
|
|
1103
1129
|
if (v.kind === "object") {
|
|
1104
1130
|
const converted = numericObjectToArray(v);
|
|
1105
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
1131
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
1106
1132
|
}
|
|
1107
1133
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
|
|
1108
|
-
return v.items.map(hoconToJs);
|
|
1134
|
+
return v.items.map((item) => hoconToJs(item));
|
|
1109
1135
|
}
|
|
1110
1136
|
has(path) {
|
|
1111
1137
|
return this.lookupNode(path) !== void 0;
|
|
@@ -1233,8 +1259,52 @@ var Config = class _Config {
|
|
|
1233
1259
|
originDescription: this._originDescription
|
|
1234
1260
|
});
|
|
1235
1261
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1262
|
+
/**
|
|
1263
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
1264
|
+
* booleans and `null`.
|
|
1265
|
+
*
|
|
1266
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
1267
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
1268
|
+
* property**, so the returned object's prototype is always
|
|
1269
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
1270
|
+
*
|
|
1271
|
+
* ### Passing the result to other code
|
|
1272
|
+
*
|
|
1273
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
1274
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
1275
|
+
* next depends entirely on how the consumer copies it:
|
|
1276
|
+
*
|
|
1277
|
+
* ```ts
|
|
1278
|
+
* const data = cfg.toObject()
|
|
1279
|
+
* { ...data } // safe — own properties are copied as data
|
|
1280
|
+
* structuredClone(data) // safe
|
|
1281
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
1282
|
+
* // Object.prototype's __proto__ setter: the key
|
|
1283
|
+
* // vanishes and config data becomes the result's
|
|
1284
|
+
* // prototype
|
|
1285
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
1286
|
+
* // target['__proto__'], gets Object.prototype,
|
|
1287
|
+
* // and writes to it: global pollution
|
|
1288
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
1289
|
+
* ```
|
|
1290
|
+
*
|
|
1291
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
1292
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
1293
|
+
*
|
|
1294
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
1295
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
1296
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
1297
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
1298
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
1299
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
1300
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
1301
|
+
*
|
|
1302
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
1303
|
+
* here even when the config holds it exactly — read those with
|
|
1304
|
+
* {@link Config.getString} instead.
|
|
1305
|
+
*/
|
|
1306
|
+
toObject(opts) {
|
|
1307
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
1238
1308
|
}
|
|
1239
1309
|
/**
|
|
1240
1310
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -1346,16 +1416,25 @@ function scalarToJs(raw, valueType) {
|
|
|
1346
1416
|
return raw;
|
|
1347
1417
|
}
|
|
1348
1418
|
}
|
|
1349
|
-
function hoconToJs(v) {
|
|
1419
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
1350
1420
|
switch (v.kind) {
|
|
1351
1421
|
case "scalar":
|
|
1352
1422
|
return scalarToJs(v.raw, v.valueType);
|
|
1353
1423
|
case "array":
|
|
1354
|
-
return v.items.map(hoconToJs);
|
|
1424
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
1355
1425
|
case "object": {
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1426
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
1427
|
+
if (nullPrototype) {
|
|
1428
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1429
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
1430
|
+
value: val,
|
|
1431
|
+
writable: true,
|
|
1432
|
+
enumerable: true,
|
|
1433
|
+
configurable: true
|
|
1434
|
+
});
|
|
1435
|
+
return obj;
|
|
1436
|
+
}
|
|
1437
|
+
return Object.fromEntries(entries);
|
|
1359
1438
|
}
|
|
1360
1439
|
}
|
|
1361
1440
|
}
|
|
@@ -1440,10 +1519,15 @@ function normalizeLeadingZeroNumber(raw) {
|
|
|
1440
1519
|
|
|
1441
1520
|
// src/value-factory.ts
|
|
1442
1521
|
function fromMap(values, originDescription) {
|
|
1443
|
-
const fields =
|
|
1522
|
+
const fields = guardStackDepth(
|
|
1523
|
+
() => coerceObject(values, ""),
|
|
1524
|
+
(msg) => depthError(`fromMap: ${msg}`)
|
|
1525
|
+
);
|
|
1444
1526
|
const root = { kind: "object", fields };
|
|
1445
1527
|
return Config._fromResolvedValue(root, { originDescription });
|
|
1446
1528
|
}
|
|
1529
|
+
var INT64_MAX = 9223372036854775807n;
|
|
1530
|
+
var INT64_MIN = -9223372036854775808n;
|
|
1447
1531
|
function coerceObject(obj, atPath) {
|
|
1448
1532
|
const fields = /* @__PURE__ */ new Map();
|
|
1449
1533
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -1472,14 +1556,14 @@ function coerceValue(v, atPath) {
|
|
|
1472
1556
|
atPath
|
|
1473
1557
|
);
|
|
1474
1558
|
}
|
|
1475
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
1559
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
1476
1560
|
}
|
|
1477
1561
|
case "boolean":
|
|
1478
1562
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
1479
1563
|
case "bigint": {
|
|
1480
|
-
if (v >
|
|
1564
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1481
1565
|
throw new ConfigError(
|
|
1482
|
-
`fromMap:
|
|
1566
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
1483
1567
|
atPath
|
|
1484
1568
|
);
|
|
1485
1569
|
}
|
|
@@ -1503,7 +1587,10 @@ function coerceValue(v, atPath) {
|
|
|
1503
1587
|
|
|
1504
1588
|
// src/adapters/jsonc.ts
|
|
1505
1589
|
function parseJsonc(input, originDescription) {
|
|
1506
|
-
const doc =
|
|
1590
|
+
const doc = guardStackDepth(
|
|
1591
|
+
() => JSON.parse(stripTrailingCommas(stripComments(stripBom(input))), bigIntReviver),
|
|
1592
|
+
(msg) => depthError(`jsonc: ${msg}`)
|
|
1593
|
+
);
|
|
1507
1594
|
if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
|
|
1508
1595
|
throw new ConfigError(
|
|
1509
1596
|
`jsonc: document root is ${Array.isArray(doc) ? "an array" : typeof doc}, but a config root must be an object (spec F0.3)`,
|
|
@@ -1512,6 +1599,50 @@ function parseJsonc(input, originDescription) {
|
|
|
1512
1599
|
}
|
|
1513
1600
|
return fromMap(doc, originDescription);
|
|
1514
1601
|
}
|
|
1602
|
+
function isIntegerLiteral(source) {
|
|
1603
|
+
return !/[.eE]/.test(source);
|
|
1604
|
+
}
|
|
1605
|
+
var HAS_SOURCE_TEXT_ACCESS = (() => {
|
|
1606
|
+
let seen;
|
|
1607
|
+
JSON.parse("1", function(_k, v, context) {
|
|
1608
|
+
seen = context?.source;
|
|
1609
|
+
return v;
|
|
1610
|
+
});
|
|
1611
|
+
return seen !== void 0;
|
|
1612
|
+
})();
|
|
1613
|
+
function bigIntReviver(key, value, context) {
|
|
1614
|
+
if (typeof value !== "number" || Number.isSafeInteger(value)) return value;
|
|
1615
|
+
const source = context?.source;
|
|
1616
|
+
if (source === void 0) {
|
|
1617
|
+
if (!Number.isInteger(value)) return value;
|
|
1618
|
+
throw new ConfigError(
|
|
1619
|
+
`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)`,
|
|
1620
|
+
key
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
if (!isIntegerLiteral(source)) return value;
|
|
1624
|
+
return BigInt(source);
|
|
1625
|
+
}
|
|
1626
|
+
function isLineBreak(c) {
|
|
1627
|
+
return c === "\n" || c === "\r";
|
|
1628
|
+
}
|
|
1629
|
+
function lineBreaksOf(span) {
|
|
1630
|
+
let out = "";
|
|
1631
|
+
for (let i = 0; i < span.length; i++) {
|
|
1632
|
+
const c = span[i];
|
|
1633
|
+
if (c === "\r") {
|
|
1634
|
+
if (span[i + 1] === "\n") {
|
|
1635
|
+
out += "\r\n";
|
|
1636
|
+
i++;
|
|
1637
|
+
} else {
|
|
1638
|
+
out += "\r";
|
|
1639
|
+
}
|
|
1640
|
+
} else if (c === "\n") {
|
|
1641
|
+
out += "\n";
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
return out;
|
|
1645
|
+
}
|
|
1515
1646
|
function stripComments(src) {
|
|
1516
1647
|
let out = "";
|
|
1517
1648
|
for (let i = 0; i < src.length; ) {
|
|
@@ -1523,13 +1654,13 @@ function stripComments(src) {
|
|
|
1523
1654
|
continue;
|
|
1524
1655
|
}
|
|
1525
1656
|
if (c === "/" && src[i + 1] === "/") {
|
|
1526
|
-
while (i < src.length && src[i]
|
|
1657
|
+
while (i < src.length && !isLineBreak(src[i])) i++;
|
|
1527
1658
|
continue;
|
|
1528
1659
|
}
|
|
1529
1660
|
if (c === "/" && src[i + 1] === "*") {
|
|
1530
1661
|
const end = src.indexOf("*/", i + 2);
|
|
1531
1662
|
if (end === -1) throw new ConfigError("jsonc: unterminated block comment", "");
|
|
1532
|
-
|
|
1663
|
+
out += lineBreaksOf(src.slice(i, end + 2)) || " ";
|
|
1533
1664
|
i = end + 2;
|
|
1534
1665
|
continue;
|
|
1535
1666
|
}
|
|
@@ -558,26 +558,74 @@ 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
|
+
}
|
|
567
|
+
|
|
568
|
+
// src/internal/depth.ts
|
|
569
|
+
var MAX_PATH_SEGMENTS = 64;
|
|
570
|
+
function tooDeep(segments) {
|
|
571
|
+
return segments > MAX_PATH_SEGMENTS;
|
|
572
|
+
}
|
|
573
|
+
function guardStackDepth(fn, wrap) {
|
|
574
|
+
try {
|
|
575
|
+
return fn();
|
|
576
|
+
} catch (e) {
|
|
577
|
+
if (!isStackOverflow(e)) throw e;
|
|
578
|
+
throw wrap(STACK_MESSAGE);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
|
|
582
|
+
function isStackOverflow(e) {
|
|
583
|
+
if (!(e instanceof RangeError)) return false;
|
|
584
|
+
const m = e.message.toLowerCase();
|
|
585
|
+
return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
|
|
586
|
+
m.includes("recursion") || // SpiderMonkey: "too much recursion"
|
|
587
|
+
m.includes("stack overflow");
|
|
588
|
+
}
|
|
589
|
+
function depthError(message) {
|
|
590
|
+
return new ConfigError(message, "");
|
|
591
|
+
}
|
|
561
592
|
|
|
562
593
|
// src/internal/properties/properties.ts
|
|
563
594
|
function parseProperties(input) {
|
|
564
595
|
const pairs = [];
|
|
565
|
-
for (const { text, line } of logicalLines(input)) {
|
|
596
|
+
for (const { text, line } of logicalLines(stripBom(input))) {
|
|
566
597
|
const [rawKey, rawValue] = splitKeyValue(text);
|
|
567
598
|
const key = unescapeProps(rawKey, line);
|
|
568
599
|
if (key === "") continue;
|
|
569
|
-
|
|
600
|
+
const segments = key.split(".");
|
|
601
|
+
if (tooDeep(segments.length)) {
|
|
602
|
+
throw new ParseError(
|
|
603
|
+
`key maps to a path ${segments.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
|
|
604
|
+
line,
|
|
605
|
+
1
|
|
606
|
+
);
|
|
607
|
+
}
|
|
608
|
+
pairs.push([segments, unescapeProps(rawValue, line)]);
|
|
570
609
|
}
|
|
571
610
|
return nestPairs(pairs);
|
|
572
611
|
}
|
|
573
612
|
function nestPairs(pairs) {
|
|
574
613
|
const root = /* @__PURE__ */ Object.create(null);
|
|
575
|
-
const sorted = [...pairs].sort(([a], [b]) => a
|
|
576
|
-
for (const [
|
|
577
|
-
setNested(root,
|
|
614
|
+
const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
|
|
615
|
+
for (const [segments, value] of sorted) {
|
|
616
|
+
setNested(root, segments, value);
|
|
578
617
|
}
|
|
579
618
|
return root;
|
|
580
619
|
}
|
|
620
|
+
function compareSegments(a, b) {
|
|
621
|
+
const n = Math.min(a.length, b.length);
|
|
622
|
+
for (let i = 0; i < n; i++) {
|
|
623
|
+
const x = a[i];
|
|
624
|
+
const y = b[i];
|
|
625
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
626
|
+
}
|
|
627
|
+
return a.length - b.length;
|
|
628
|
+
}
|
|
581
629
|
function logicalLines(input) {
|
|
582
630
|
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
583
631
|
const out = [];
|
|
@@ -664,27 +712,20 @@ function unescapeProps(s, line) {
|
|
|
664
712
|
}
|
|
665
713
|
return out;
|
|
666
714
|
}
|
|
667
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
668
715
|
function setNested(obj, segments, value) {
|
|
669
716
|
let current = obj;
|
|
670
717
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
671
718
|
const seg = segments[i];
|
|
672
|
-
if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
|
|
673
719
|
if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
|
|
674
720
|
current[seg] = /* @__PURE__ */ Object.create(null);
|
|
675
721
|
}
|
|
676
722
|
current = current[seg];
|
|
677
723
|
}
|
|
678
724
|
const last = segments[segments.length - 1];
|
|
679
|
-
if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
|
|
680
725
|
if (typeof current[last] === "object" && current[last] !== null) return;
|
|
681
726
|
current[last] = value;
|
|
682
727
|
}
|
|
683
728
|
|
|
684
|
-
// src/internal/resolver/include-loader.ts
|
|
685
|
-
var import_meta = {};
|
|
686
|
-
var _require = (0, import_node_module.createRequire)(import_meta.url);
|
|
687
|
-
|
|
688
729
|
// src/value/numeric-array.ts
|
|
689
730
|
function numericObjectToArray(value) {
|
|
690
731
|
if (value.kind !== "object") return null;
|
|
@@ -1272,10 +1313,10 @@ var Config = class _Config {
|
|
|
1272
1313
|
}
|
|
1273
1314
|
if (v.kind === "object") {
|
|
1274
1315
|
const converted = numericObjectToArray(v);
|
|
1275
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
1316
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
1276
1317
|
}
|
|
1277
1318
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
|
|
1278
|
-
return v.items.map(hoconToJs);
|
|
1319
|
+
return v.items.map((item) => hoconToJs(item));
|
|
1279
1320
|
}
|
|
1280
1321
|
has(path) {
|
|
1281
1322
|
return this.lookupNode(path) !== void 0;
|
|
@@ -1403,8 +1444,52 @@ var Config = class _Config {
|
|
|
1403
1444
|
originDescription: this._originDescription
|
|
1404
1445
|
});
|
|
1405
1446
|
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1447
|
+
/**
|
|
1448
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
1449
|
+
* booleans and `null`.
|
|
1450
|
+
*
|
|
1451
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
1452
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
1453
|
+
* property**, so the returned object's prototype is always
|
|
1454
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
1455
|
+
*
|
|
1456
|
+
* ### Passing the result to other code
|
|
1457
|
+
*
|
|
1458
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
1459
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
1460
|
+
* next depends entirely on how the consumer copies it:
|
|
1461
|
+
*
|
|
1462
|
+
* ```ts
|
|
1463
|
+
* const data = cfg.toObject()
|
|
1464
|
+
* { ...data } // safe — own properties are copied as data
|
|
1465
|
+
* structuredClone(data) // safe
|
|
1466
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
1467
|
+
* // Object.prototype's __proto__ setter: the key
|
|
1468
|
+
* // vanishes and config data becomes the result's
|
|
1469
|
+
* // prototype
|
|
1470
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
1471
|
+
* // target['__proto__'], gets Object.prototype,
|
|
1472
|
+
* // and writes to it: global pollution
|
|
1473
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
1474
|
+
* ```
|
|
1475
|
+
*
|
|
1476
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
1477
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
1478
|
+
*
|
|
1479
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
1480
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
1481
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
1482
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
1483
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
1484
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
1485
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
1486
|
+
*
|
|
1487
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
1488
|
+
* here even when the config holds it exactly — read those with
|
|
1489
|
+
* {@link Config.getString} instead.
|
|
1490
|
+
*/
|
|
1491
|
+
toObject(opts) {
|
|
1492
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
1408
1493
|
}
|
|
1409
1494
|
/**
|
|
1410
1495
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -1516,16 +1601,25 @@ function scalarToJs(raw, valueType) {
|
|
|
1516
1601
|
return raw;
|
|
1517
1602
|
}
|
|
1518
1603
|
}
|
|
1519
|
-
function hoconToJs(v) {
|
|
1604
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
1520
1605
|
switch (v.kind) {
|
|
1521
1606
|
case "scalar":
|
|
1522
1607
|
return scalarToJs(v.raw, v.valueType);
|
|
1523
1608
|
case "array":
|
|
1524
|
-
return v.items.map(hoconToJs);
|
|
1609
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
1525
1610
|
case "object": {
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
|
|
1611
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
1612
|
+
if (nullPrototype) {
|
|
1613
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1614
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
1615
|
+
value: val,
|
|
1616
|
+
writable: true,
|
|
1617
|
+
enumerable: true,
|
|
1618
|
+
configurable: true
|
|
1619
|
+
});
|
|
1620
|
+
return obj;
|
|
1621
|
+
}
|
|
1622
|
+
return Object.fromEntries(entries);
|
|
1529
1623
|
}
|
|
1530
1624
|
}
|
|
1531
1625
|
}
|
|
@@ -1610,10 +1704,15 @@ function normalizeLeadingZeroNumber(raw) {
|
|
|
1610
1704
|
|
|
1611
1705
|
// src/value-factory.ts
|
|
1612
1706
|
function fromMap(values, originDescription) {
|
|
1613
|
-
const fields =
|
|
1707
|
+
const fields = guardStackDepth(
|
|
1708
|
+
() => coerceObject(values, ""),
|
|
1709
|
+
(msg) => depthError(`fromMap: ${msg}`)
|
|
1710
|
+
);
|
|
1614
1711
|
const root = { kind: "object", fields };
|
|
1615
1712
|
return Config._fromResolvedValue(root, { originDescription });
|
|
1616
1713
|
}
|
|
1714
|
+
var INT64_MAX = 9223372036854775807n;
|
|
1715
|
+
var INT64_MIN = -9223372036854775808n;
|
|
1617
1716
|
function coerceObject(obj, atPath) {
|
|
1618
1717
|
const fields = /* @__PURE__ */ new Map();
|
|
1619
1718
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -1642,14 +1741,14 @@ function coerceValue(v, atPath) {
|
|
|
1642
1741
|
atPath
|
|
1643
1742
|
);
|
|
1644
1743
|
}
|
|
1645
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
1744
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
1646
1745
|
}
|
|
1647
1746
|
case "boolean":
|
|
1648
1747
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
1649
1748
|
case "bigint": {
|
|
1650
|
-
if (v >
|
|
1749
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1651
1750
|
throw new ConfigError(
|
|
1652
|
-
`fromMap:
|
|
1751
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
1653
1752
|
atPath
|
|
1654
1753
|
);
|
|
1655
1754
|
}
|
|
@@ -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
|
*/
|