@o3co/ts.hocon 1.9.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 +122 -2
- package/dist/adapters/env.cjs +1738 -0
- package/dist/adapters/env.d.cts +56 -0
- package/dist/adapters/env.d.ts +56 -0
- package/dist/adapters/env.js +1700 -0
- package/dist/adapters/jsonc.cjs +1716 -0
- package/dist/adapters/jsonc.d.cts +45 -0
- package/dist/adapters/jsonc.d.ts +45 -0
- package/dist/adapters/jsonc.js +1678 -0
- package/dist/adapters/properties.cjs +1744 -0
- package/dist/adapters/properties.d.cts +32 -0
- package/dist/adapters/properties.d.ts +32 -0
- package/dist/adapters/properties.js +1707 -0
- package/dist/adapters/toml.cjs +1629 -0
- package/dist/adapters/toml.d.cts +18 -0
- package/dist/adapters/toml.d.ts +18 -0
- package/dist/adapters/toml.js +1594 -0
- package/dist/adapters/yaml.cjs +1686 -0
- package/dist/adapters/yaml.d.cts +51 -0
- package/dist/adapters/yaml.d.ts +51 -0
- package/dist/adapters/yaml.js +1650 -0
- package/dist/{config-D27s3_aG.d.cts → config-Dlq6rF3w.d.cts} +50 -4
- package/dist/{config-D27s3_aG.d.ts → config-Dlq6rF3w.d.ts} +50 -4
- package/dist/index.cjs +207 -35
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +214 -35
- package/dist/zod.cjs +203 -33
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +210 -33
- package/package.json +89 -19
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
|
|
2
|
-
type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
|
|
3
|
-
|
|
4
1
|
type ScalarValueType = 'string' | 'number' | 'boolean' | 'null';
|
|
5
2
|
type HoconValue = {
|
|
6
3
|
kind: 'object';
|
|
@@ -226,6 +223,9 @@ declare function parseAsync(input: string, opts?: ParseOptions): Promise<Config>
|
|
|
226
223
|
declare function parseFile(filePath: string, opts?: ParseOptions): Config;
|
|
227
224
|
declare function parseFileAsync(filePath: string, opts?: ParseOptions): Promise<Config>;
|
|
228
225
|
|
|
226
|
+
type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
|
|
227
|
+
type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
|
|
228
|
+
|
|
229
229
|
declare class Config {
|
|
230
230
|
private readonly root;
|
|
231
231
|
/** @internal resolved flag: true when no substitution placeholders remain. */
|
|
@@ -316,7 +316,53 @@ declare class Config {
|
|
|
316
316
|
* E12 decisions 9, 10.
|
|
317
317
|
*/
|
|
318
318
|
resolveWith(source: Config, opts?: ResolveOptions): Config;
|
|
319
|
-
|
|
319
|
+
/**
|
|
320
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
321
|
+
* booleans and `null`.
|
|
322
|
+
*
|
|
323
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
324
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
325
|
+
* property**, so the returned object's prototype is always
|
|
326
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
327
|
+
*
|
|
328
|
+
* ### Passing the result to other code
|
|
329
|
+
*
|
|
330
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
331
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
332
|
+
* next depends entirely on how the consumer copies it:
|
|
333
|
+
*
|
|
334
|
+
* ```ts
|
|
335
|
+
* const data = cfg.toObject()
|
|
336
|
+
* { ...data } // safe — own properties are copied as data
|
|
337
|
+
* structuredClone(data) // safe
|
|
338
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
339
|
+
* // Object.prototype's __proto__ setter: the key
|
|
340
|
+
* // vanishes and config data becomes the result's
|
|
341
|
+
* // prototype
|
|
342
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
343
|
+
* // target['__proto__'], gets Object.prototype,
|
|
344
|
+
* // and writes to it: global pollution
|
|
345
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
346
|
+
* ```
|
|
347
|
+
*
|
|
348
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
349
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
350
|
+
*
|
|
351
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
352
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
353
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
354
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
355
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
356
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
357
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
358
|
+
*
|
|
359
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
360
|
+
* here even when the config holds it exactly — read those with
|
|
361
|
+
* {@link Config.getString} instead.
|
|
362
|
+
*/
|
|
363
|
+
toObject(opts?: {
|
|
364
|
+
nullPrototype?: boolean;
|
|
365
|
+
}): unknown;
|
|
320
366
|
/**
|
|
321
367
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
322
368
|
* JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
|
|
2
|
-
type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
|
|
3
|
-
|
|
4
1
|
type ScalarValueType = 'string' | 'number' | 'boolean' | 'null';
|
|
5
2
|
type HoconValue = {
|
|
6
3
|
kind: 'object';
|
|
@@ -226,6 +223,9 @@ declare function parseAsync(input: string, opts?: ParseOptions): Promise<Config>
|
|
|
226
223
|
declare function parseFile(filePath: string, opts?: ParseOptions): Config;
|
|
227
224
|
declare function parseFileAsync(filePath: string, opts?: ParseOptions): Promise<Config>;
|
|
228
225
|
|
|
226
|
+
type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
|
|
227
|
+
type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
|
|
228
|
+
|
|
229
229
|
declare class Config {
|
|
230
230
|
private readonly root;
|
|
231
231
|
/** @internal resolved flag: true when no substitution placeholders remain. */
|
|
@@ -316,7 +316,53 @@ declare class Config {
|
|
|
316
316
|
* E12 decisions 9, 10.
|
|
317
317
|
*/
|
|
318
318
|
resolveWith(source: Config, opts?: ResolveOptions): Config;
|
|
319
|
-
|
|
319
|
+
/**
|
|
320
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
321
|
+
* booleans and `null`.
|
|
322
|
+
*
|
|
323
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
324
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
325
|
+
* property**, so the returned object's prototype is always
|
|
326
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
327
|
+
*
|
|
328
|
+
* ### Passing the result to other code
|
|
329
|
+
*
|
|
330
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
331
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
332
|
+
* next depends entirely on how the consumer copies it:
|
|
333
|
+
*
|
|
334
|
+
* ```ts
|
|
335
|
+
* const data = cfg.toObject()
|
|
336
|
+
* { ...data } // safe — own properties are copied as data
|
|
337
|
+
* structuredClone(data) // safe
|
|
338
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
339
|
+
* // Object.prototype's __proto__ setter: the key
|
|
340
|
+
* // vanishes and config data becomes the result's
|
|
341
|
+
* // prototype
|
|
342
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
343
|
+
* // target['__proto__'], gets Object.prototype,
|
|
344
|
+
* // and writes to it: global pollution
|
|
345
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
346
|
+
* ```
|
|
347
|
+
*
|
|
348
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
349
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
350
|
+
*
|
|
351
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
352
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
353
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
354
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
355
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
356
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
357
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
358
|
+
*
|
|
359
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
360
|
+
* here even when the config holds it exactly — read those with
|
|
361
|
+
* {@link Config.getString} instead.
|
|
362
|
+
*/
|
|
363
|
+
toObject(opts?: {
|
|
364
|
+
nullPrototype?: boolean;
|
|
365
|
+
}): unknown;
|
|
320
366
|
/**
|
|
321
367
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
322
368
|
* JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
|
package/dist/index.cjs
CHANGED
|
@@ -700,6 +700,7 @@ function isFileNotFoundError(e) {
|
|
|
700
700
|
// src/internal/resolver/include-loader.ts
|
|
701
701
|
var import_node_module = require("module");
|
|
702
702
|
var nodePath = __toESM(require("path"), 1);
|
|
703
|
+
var import_node_url = require("url");
|
|
703
704
|
|
|
704
705
|
// src/internal/lexer/lexer.ts
|
|
705
706
|
function isHoconWhitespace(ch) {
|
|
@@ -1101,12 +1102,12 @@ function isDecimalDigit(ch) {
|
|
|
1101
1102
|
}
|
|
1102
1103
|
function isUnquotedStart(ch) {
|
|
1103
1104
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1104
|
-
if ('{}[],:=+#"
|
|
1105
|
+
if ('{}[],:=+#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1105
1106
|
return true;
|
|
1106
1107
|
}
|
|
1107
1108
|
function isUnquotedContinue(ch, nextFn) {
|
|
1108
1109
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1109
|
-
if ('{}[],:=#"
|
|
1110
|
+
if ('{}[],:=#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1110
1111
|
if (ch === "+" && nextFn() === "=") return false;
|
|
1111
1112
|
if (ch === "/" && nextFn() === "/") return false;
|
|
1112
1113
|
return true;
|
|
@@ -1255,6 +1256,17 @@ var Parser = class {
|
|
|
1255
1256
|
this.advance();
|
|
1256
1257
|
const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
|
|
1257
1258
|
const parts = raw.split(".");
|
|
1259
|
+
const leadingDotIsSeparator = spaceConcat || postDotPrefix !== "";
|
|
1260
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1261
|
+
if (parts[i] !== "") continue;
|
|
1262
|
+
if (i === parts.length - 1) continue;
|
|
1263
|
+
if (i === 0 && leadingDotIsSeparator) continue;
|
|
1264
|
+
throw new ParseError(
|
|
1265
|
+
`path has ${i === 0 ? "a leading period '.'" : "two adjacent periods '.'"} \u2014 empty key segment not allowed, use quoted "" (HOCON.md path rules)`,
|
|
1266
|
+
t.line,
|
|
1267
|
+
t.col
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1258
1270
|
const filtered = parts.filter((s) => s.length > 0);
|
|
1259
1271
|
if (spaceConcat) {
|
|
1260
1272
|
segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}`;
|
|
@@ -1531,45 +1543,135 @@ function parseTokens(tokens) {
|
|
|
1531
1543
|
return new Parser(tokens).parse();
|
|
1532
1544
|
}
|
|
1533
1545
|
|
|
1546
|
+
// src/internal/strip-bom.ts
|
|
1547
|
+
function stripBom(input) {
|
|
1548
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1534
1551
|
// src/internal/properties/properties.ts
|
|
1535
1552
|
function parseProperties(input) {
|
|
1536
|
-
const root = /* @__PURE__ */ Object.create(null);
|
|
1537
1553
|
const pairs = [];
|
|
1538
|
-
for (const line of input
|
|
1539
|
-
const
|
|
1540
|
-
|
|
1541
|
-
const sepIdx = findSeparator(trimmed);
|
|
1542
|
-
if (sepIdx === -1) continue;
|
|
1543
|
-
const key = trimmed.slice(0, sepIdx).trim();
|
|
1544
|
-
const value = trimmed.slice(sepIdx + 1).trim();
|
|
1554
|
+
for (const { text, line } of logicalLines(stripBom(input))) {
|
|
1555
|
+
const [rawKey, rawValue] = splitKeyValue(text);
|
|
1556
|
+
const key = unescapeProps(rawKey, line);
|
|
1545
1557
|
if (key === "") continue;
|
|
1546
|
-
pairs.push([key,
|
|
1558
|
+
pairs.push([key.split("."), unescapeProps(rawValue, line)]);
|
|
1547
1559
|
}
|
|
1548
|
-
pairs
|
|
1549
|
-
|
|
1550
|
-
|
|
1560
|
+
return nestPairs(pairs);
|
|
1561
|
+
}
|
|
1562
|
+
function nestPairs(pairs) {
|
|
1563
|
+
const root = /* @__PURE__ */ Object.create(null);
|
|
1564
|
+
const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
|
|
1565
|
+
for (const [segments, value] of sorted) {
|
|
1566
|
+
setNested(root, segments, value);
|
|
1551
1567
|
}
|
|
1552
1568
|
return root;
|
|
1553
1569
|
}
|
|
1554
|
-
function
|
|
1555
|
-
|
|
1556
|
-
|
|
1570
|
+
function compareSegments(a, b) {
|
|
1571
|
+
const n = Math.min(a.length, b.length);
|
|
1572
|
+
for (let i = 0; i < n; i++) {
|
|
1573
|
+
const x = a[i];
|
|
1574
|
+
const y = b[i];
|
|
1575
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
1576
|
+
}
|
|
1577
|
+
return a.length - b.length;
|
|
1578
|
+
}
|
|
1579
|
+
function logicalLines(input) {
|
|
1580
|
+
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1581
|
+
const out = [];
|
|
1582
|
+
for (let i = 0; i < natural.length; i++) {
|
|
1583
|
+
let text = trimLeadingSpace(natural[i] ?? "");
|
|
1584
|
+
if (text === "" || text.startsWith("#") || text.startsWith("!")) continue;
|
|
1585
|
+
const start = i + 1;
|
|
1586
|
+
while (endsWithContinuation(text)) {
|
|
1587
|
+
text = text.slice(0, -1);
|
|
1588
|
+
if (i + 1 >= natural.length) break;
|
|
1589
|
+
i++;
|
|
1590
|
+
text += trimLeadingSpace(natural[i] ?? "");
|
|
1591
|
+
}
|
|
1592
|
+
out.push({ text, line: start });
|
|
1593
|
+
}
|
|
1594
|
+
return out;
|
|
1595
|
+
}
|
|
1596
|
+
function trimLeadingSpace(s) {
|
|
1597
|
+
return s.replace(/^[ \t\f]+/, "");
|
|
1598
|
+
}
|
|
1599
|
+
function endsWithContinuation(line) {
|
|
1600
|
+
let n = 0;
|
|
1601
|
+
for (let i = line.length - 1; i >= 0 && line[i] === "\\"; i--) n++;
|
|
1602
|
+
return n % 2 === 1;
|
|
1603
|
+
}
|
|
1604
|
+
function splitKeyValue(line) {
|
|
1605
|
+
let key = "";
|
|
1606
|
+
let i = 0;
|
|
1607
|
+
for (; i < line.length; i++) {
|
|
1608
|
+
const c = line[i];
|
|
1609
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
1610
|
+
key += c + line[i + 1];
|
|
1611
|
+
i++;
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
if (c === "=" || c === ":" || isPropsSpace(c)) break;
|
|
1615
|
+
key += c;
|
|
1616
|
+
}
|
|
1617
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1618
|
+
if (i < line.length && (line[i] === "=" || line[i] === ":")) {
|
|
1619
|
+
i++;
|
|
1620
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1621
|
+
}
|
|
1622
|
+
return [key, line.slice(i)];
|
|
1623
|
+
}
|
|
1624
|
+
function isPropsSpace(c) {
|
|
1625
|
+
return c === " " || c === " " || c === "\f";
|
|
1626
|
+
}
|
|
1627
|
+
function unescapeProps(s, line) {
|
|
1628
|
+
let out = "";
|
|
1629
|
+
for (let i = 0; i < s.length; i++) {
|
|
1630
|
+
if (s[i] !== "\\") {
|
|
1631
|
+
out += s[i];
|
|
1632
|
+
continue;
|
|
1633
|
+
}
|
|
1634
|
+
i++;
|
|
1635
|
+
if (i >= s.length) break;
|
|
1636
|
+
const e = s[i];
|
|
1637
|
+
switch (e) {
|
|
1638
|
+
case "t":
|
|
1639
|
+
out += " ";
|
|
1640
|
+
break;
|
|
1641
|
+
case "n":
|
|
1642
|
+
out += "\n";
|
|
1643
|
+
break;
|
|
1644
|
+
case "r":
|
|
1645
|
+
out += "\r";
|
|
1646
|
+
break;
|
|
1647
|
+
case "f":
|
|
1648
|
+
out += "\f";
|
|
1649
|
+
break;
|
|
1650
|
+
case "u": {
|
|
1651
|
+
const hex = s.slice(i + 1, i + 5);
|
|
1652
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1653
|
+
throw new ParseError(`malformed \\u escape in .properties: \\u${hex}`, line, i + 1);
|
|
1654
|
+
}
|
|
1655
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
1656
|
+
i += 4;
|
|
1657
|
+
break;
|
|
1658
|
+
}
|
|
1659
|
+
default:
|
|
1660
|
+
out += e;
|
|
1661
|
+
}
|
|
1557
1662
|
}
|
|
1558
|
-
return
|
|
1663
|
+
return out;
|
|
1559
1664
|
}
|
|
1560
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1561
1665
|
function setNested(obj, segments, value) {
|
|
1562
1666
|
let current = obj;
|
|
1563
1667
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1564
1668
|
const seg = segments[i];
|
|
1565
|
-
if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
|
|
1566
1669
|
if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
|
|
1567
1670
|
current[seg] = /* @__PURE__ */ Object.create(null);
|
|
1568
1671
|
}
|
|
1569
1672
|
current = current[seg];
|
|
1570
1673
|
}
|
|
1571
1674
|
const last = segments[segments.length - 1];
|
|
1572
|
-
if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
|
|
1573
1675
|
if (typeof current[last] === "object" && current[last] !== null) return;
|
|
1574
1676
|
current[last] = value;
|
|
1575
1677
|
}
|
|
@@ -1591,7 +1693,22 @@ function recordToHoconValue(obj) {
|
|
|
1591
1693
|
|
|
1592
1694
|
// src/internal/resolver/include-loader.ts
|
|
1593
1695
|
var import_meta = {};
|
|
1594
|
-
var _require
|
|
1696
|
+
var _require;
|
|
1697
|
+
function getRequire() {
|
|
1698
|
+
if (_require === void 0) {
|
|
1699
|
+
_require = pickRequire(
|
|
1700
|
+
typeof require === "function" ? require : void 0,
|
|
1701
|
+
import_meta.url
|
|
1702
|
+
);
|
|
1703
|
+
}
|
|
1704
|
+
return _require;
|
|
1705
|
+
}
|
|
1706
|
+
function pickRequire(candidate, metaUrl) {
|
|
1707
|
+
if (typeof candidate?.resolve === "function") {
|
|
1708
|
+
return candidate;
|
|
1709
|
+
}
|
|
1710
|
+
return (0, import_node_module.createRequire)(metaUrl ?? (0, import_node_url.pathToFileURL)(nodePath.join(process.cwd(), "noop.js")));
|
|
1711
|
+
}
|
|
1595
1712
|
function isPnpPath(p) {
|
|
1596
1713
|
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1597
1714
|
}
|
|
@@ -1647,7 +1764,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
|
|
|
1647
1764
|
const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
|
|
1648
1765
|
let resolved;
|
|
1649
1766
|
try {
|
|
1650
|
-
resolved =
|
|
1767
|
+
resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
|
|
1651
1768
|
} catch {
|
|
1652
1769
|
throw new PackageLookupError(
|
|
1653
1770
|
`include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
|
|
@@ -2701,10 +2818,10 @@ var Config = class _Config {
|
|
|
2701
2818
|
}
|
|
2702
2819
|
if (v.kind === "object") {
|
|
2703
2820
|
const converted = numericObjectToArray(v);
|
|
2704
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
2821
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
2705
2822
|
}
|
|
2706
2823
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
|
|
2707
|
-
return v.items.map(hoconToJs);
|
|
2824
|
+
return v.items.map((item) => hoconToJs(item));
|
|
2708
2825
|
}
|
|
2709
2826
|
has(path2) {
|
|
2710
2827
|
return this.lookupNode(path2) !== void 0;
|
|
@@ -2832,8 +2949,52 @@ var Config = class _Config {
|
|
|
2832
2949
|
originDescription: this._originDescription
|
|
2833
2950
|
});
|
|
2834
2951
|
}
|
|
2835
|
-
|
|
2836
|
-
|
|
2952
|
+
/**
|
|
2953
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
2954
|
+
* booleans and `null`.
|
|
2955
|
+
*
|
|
2956
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
2957
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
2958
|
+
* property**, so the returned object's prototype is always
|
|
2959
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
2960
|
+
*
|
|
2961
|
+
* ### Passing the result to other code
|
|
2962
|
+
*
|
|
2963
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
2964
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
2965
|
+
* next depends entirely on how the consumer copies it:
|
|
2966
|
+
*
|
|
2967
|
+
* ```ts
|
|
2968
|
+
* const data = cfg.toObject()
|
|
2969
|
+
* { ...data } // safe — own properties are copied as data
|
|
2970
|
+
* structuredClone(data) // safe
|
|
2971
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
2972
|
+
* // Object.prototype's __proto__ setter: the key
|
|
2973
|
+
* // vanishes and config data becomes the result's
|
|
2974
|
+
* // prototype
|
|
2975
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
2976
|
+
* // target['__proto__'], gets Object.prototype,
|
|
2977
|
+
* // and writes to it: global pollution
|
|
2978
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
2979
|
+
* ```
|
|
2980
|
+
*
|
|
2981
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
2982
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
2983
|
+
*
|
|
2984
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
2985
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
2986
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
2987
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
2988
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
2989
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
2990
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
2991
|
+
*
|
|
2992
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
2993
|
+
* here even when the config holds it exactly — read those with
|
|
2994
|
+
* {@link Config.getString} instead.
|
|
2995
|
+
*/
|
|
2996
|
+
toObject(opts) {
|
|
2997
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
2837
2998
|
}
|
|
2838
2999
|
/**
|
|
2839
3000
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -2945,16 +3106,25 @@ function scalarToJs(raw, valueType) {
|
|
|
2945
3106
|
return raw;
|
|
2946
3107
|
}
|
|
2947
3108
|
}
|
|
2948
|
-
function hoconToJs(v) {
|
|
3109
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
2949
3110
|
switch (v.kind) {
|
|
2950
3111
|
case "scalar":
|
|
2951
3112
|
return scalarToJs(v.raw, v.valueType);
|
|
2952
3113
|
case "array":
|
|
2953
|
-
return v.items.map(hoconToJs);
|
|
3114
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
2954
3115
|
case "object": {
|
|
2955
|
-
const
|
|
2956
|
-
|
|
2957
|
-
|
|
3116
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
3117
|
+
if (nullPrototype) {
|
|
3118
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
3119
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
3120
|
+
value: val,
|
|
3121
|
+
writable: true,
|
|
3122
|
+
enumerable: true,
|
|
3123
|
+
configurable: true
|
|
3124
|
+
});
|
|
3125
|
+
return obj;
|
|
3126
|
+
}
|
|
3127
|
+
return Object.fromEntries(entries);
|
|
2958
3128
|
}
|
|
2959
3129
|
}
|
|
2960
3130
|
}
|
|
@@ -3188,6 +3358,8 @@ function empty(originDescription) {
|
|
|
3188
3358
|
const root = { kind: "object", fields: /* @__PURE__ */ new Map() };
|
|
3189
3359
|
return Config._fromResolvedValue(root, { originDescription });
|
|
3190
3360
|
}
|
|
3361
|
+
var INT64_MAX = 9223372036854775807n;
|
|
3362
|
+
var INT64_MIN = -9223372036854775808n;
|
|
3191
3363
|
function coerceObject(obj, atPath) {
|
|
3192
3364
|
const fields = /* @__PURE__ */ new Map();
|
|
3193
3365
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -3216,14 +3388,14 @@ function coerceValue(v, atPath) {
|
|
|
3216
3388
|
atPath
|
|
3217
3389
|
);
|
|
3218
3390
|
}
|
|
3219
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
3391
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
3220
3392
|
}
|
|
3221
3393
|
case "boolean":
|
|
3222
3394
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
3223
3395
|
case "bigint": {
|
|
3224
|
-
if (v >
|
|
3396
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
3225
3397
|
throw new ConfigError(
|
|
3226
|
-
`fromMap:
|
|
3398
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
3227
3399
|
atPath
|
|
3228
3400
|
);
|
|
3229
3401
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as Config } from './config-
|
|
2
|
-
export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-
|
|
1
|
+
import { C as Config } from './config-Dlq6rF3w.cjs';
|
|
2
|
+
export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-Dlq6rF3w.cjs';
|
|
3
3
|
|
|
4
4
|
declare class ParseError extends Error {
|
|
5
5
|
readonly line: number;
|
|
@@ -48,7 +48,14 @@ declare class NotResolvedError extends ConfigError {
|
|
|
48
48
|
* null → scalar/null
|
|
49
49
|
* Array<unknown> → array (elements recursively coerced)
|
|
50
50
|
* Record<string,unknown> → object (values recursively coerced)
|
|
51
|
-
*
|
|
51
|
+
* bigint → scalar/number, digits kept verbatim (out of int64 = error)
|
|
52
|
+
* undefined / function / symbol → ConfigError
|
|
53
|
+
*
|
|
54
|
+
* A `bigint` is how a caller (or a format adapter) hands over an integer that a
|
|
55
|
+
* JS `number` cannot hold exactly: its digits are kept verbatim in the value's
|
|
56
|
+
* raw text, so `getString` returns them intact, while `getNumber` / `toObject`
|
|
57
|
+
* apply the JS number model as they do for any other value. Integers outside
|
|
58
|
+
* the int64 range are rejected (spec F0.5).
|
|
52
59
|
*
|
|
53
60
|
* originDescription (optional) provides a source name for error messages.
|
|
54
61
|
* E12 decision 13.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as Config } from './config-
|
|
2
|
-
export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-
|
|
1
|
+
import { C as Config } from './config-Dlq6rF3w.js';
|
|
2
|
+
export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ReadonlyHoconValue, b as ResolveOptions, S as ScalarValueType, c as asArray, d as asBoolean, e as asNumber, f as asObject, g as asString, h as defaultParseOptions, i as defaultResolveOptions, j as isArray, k as isNull, l as isObject, m as isScalar, p as parse, n as parseAsync, o as parseFile, q as parseFileAsync, r as parseFileWithOptions, s as parseString, t as parseStringWithOptions } from './config-Dlq6rF3w.js';
|
|
3
3
|
|
|
4
4
|
declare class ParseError extends Error {
|
|
5
5
|
readonly line: number;
|
|
@@ -48,7 +48,14 @@ declare class NotResolvedError extends ConfigError {
|
|
|
48
48
|
* null → scalar/null
|
|
49
49
|
* Array<unknown> → array (elements recursively coerced)
|
|
50
50
|
* Record<string,unknown> → object (values recursively coerced)
|
|
51
|
-
*
|
|
51
|
+
* bigint → scalar/number, digits kept verbatim (out of int64 = error)
|
|
52
|
+
* undefined / function / symbol → ConfigError
|
|
53
|
+
*
|
|
54
|
+
* A `bigint` is how a caller (or a format adapter) hands over an integer that a
|
|
55
|
+
* JS `number` cannot hold exactly: its digits are kept verbatim in the value's
|
|
56
|
+
* raw text, so `getString` returns them intact, while `getNumber` / `toObject`
|
|
57
|
+
* apply the JS number model as they do for any other value. Integers outside
|
|
58
|
+
* the int64 range are rejected (spec F0.5).
|
|
52
59
|
*
|
|
53
60
|
* originDescription (optional) provides a source name for error messages.
|
|
54
61
|
* E12 decision 13.
|