@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/index.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/coerce.ts
|
|
2
9
|
var DECIMAL_NUMBER_RE = /^-?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
3
10
|
function isHoconWs(cp) {
|
|
@@ -639,6 +646,7 @@ function isFileNotFoundError(e) {
|
|
|
639
646
|
// src/internal/resolver/include-loader.ts
|
|
640
647
|
import { createRequire } from "module";
|
|
641
648
|
import * as nodePath from "path";
|
|
649
|
+
import { pathToFileURL } from "url";
|
|
642
650
|
|
|
643
651
|
// src/internal/lexer/lexer.ts
|
|
644
652
|
function isHoconWhitespace(ch) {
|
|
@@ -1481,25 +1489,80 @@ function parseTokens(tokens) {
|
|
|
1481
1489
|
return new Parser(tokens).parse();
|
|
1482
1490
|
}
|
|
1483
1491
|
|
|
1492
|
+
// src/internal/strip-bom.ts
|
|
1493
|
+
function stripBom(input) {
|
|
1494
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/internal/depth.ts
|
|
1498
|
+
var MAX_PATH_SEGMENTS = 64;
|
|
1499
|
+
function tooDeep(segments) {
|
|
1500
|
+
return segments > MAX_PATH_SEGMENTS;
|
|
1501
|
+
}
|
|
1502
|
+
function guardStackDepth(fn, wrap) {
|
|
1503
|
+
try {
|
|
1504
|
+
return fn();
|
|
1505
|
+
} catch (e) {
|
|
1506
|
+
if (!isStackOverflow(e)) throw e;
|
|
1507
|
+
throw wrap(STACK_MESSAGE);
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
|
|
1511
|
+
function isStackOverflow(e) {
|
|
1512
|
+
if (!(e instanceof RangeError)) return false;
|
|
1513
|
+
const m = e.message.toLowerCase();
|
|
1514
|
+
return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
|
|
1515
|
+
m.includes("recursion") || // SpiderMonkey: "too much recursion"
|
|
1516
|
+
m.includes("stack overflow");
|
|
1517
|
+
}
|
|
1518
|
+
function depthError(message) {
|
|
1519
|
+
return new ConfigError(message, "");
|
|
1520
|
+
}
|
|
1521
|
+
async function guardStackDepthAsync(fn, wrap) {
|
|
1522
|
+
try {
|
|
1523
|
+
return await fn();
|
|
1524
|
+
} catch (e) {
|
|
1525
|
+
if (!isStackOverflow(e)) throw e;
|
|
1526
|
+
throw wrap(STACK_MESSAGE);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1484
1530
|
// src/internal/properties/properties.ts
|
|
1485
1531
|
function parseProperties(input) {
|
|
1486
1532
|
const pairs = [];
|
|
1487
|
-
for (const { text, line } of logicalLines(input)) {
|
|
1533
|
+
for (const { text, line } of logicalLines(stripBom(input))) {
|
|
1488
1534
|
const [rawKey, rawValue] = splitKeyValue(text);
|
|
1489
1535
|
const key = unescapeProps(rawKey, line);
|
|
1490
1536
|
if (key === "") continue;
|
|
1491
|
-
|
|
1537
|
+
const segments = key.split(".");
|
|
1538
|
+
if (tooDeep(segments.length)) {
|
|
1539
|
+
throw new ParseError(
|
|
1540
|
+
`key maps to a path ${segments.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
|
|
1541
|
+
line,
|
|
1542
|
+
1
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
pairs.push([segments, unescapeProps(rawValue, line)]);
|
|
1492
1546
|
}
|
|
1493
1547
|
return nestPairs(pairs);
|
|
1494
1548
|
}
|
|
1495
1549
|
function nestPairs(pairs) {
|
|
1496
1550
|
const root = /* @__PURE__ */ Object.create(null);
|
|
1497
|
-
const sorted = [...pairs].sort(([a], [b]) => a
|
|
1498
|
-
for (const [
|
|
1499
|
-
setNested(root,
|
|
1551
|
+
const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
|
|
1552
|
+
for (const [segments, value] of sorted) {
|
|
1553
|
+
setNested(root, segments, value);
|
|
1500
1554
|
}
|
|
1501
1555
|
return root;
|
|
1502
1556
|
}
|
|
1557
|
+
function compareSegments(a, b) {
|
|
1558
|
+
const n = Math.min(a.length, b.length);
|
|
1559
|
+
for (let i = 0; i < n; i++) {
|
|
1560
|
+
const x = a[i];
|
|
1561
|
+
const y = b[i];
|
|
1562
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
1563
|
+
}
|
|
1564
|
+
return a.length - b.length;
|
|
1565
|
+
}
|
|
1503
1566
|
function logicalLines(input) {
|
|
1504
1567
|
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1505
1568
|
const out = [];
|
|
@@ -1586,19 +1649,16 @@ function unescapeProps(s, line) {
|
|
|
1586
1649
|
}
|
|
1587
1650
|
return out;
|
|
1588
1651
|
}
|
|
1589
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1590
1652
|
function setNested(obj, segments, value) {
|
|
1591
1653
|
let current = obj;
|
|
1592
1654
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1593
1655
|
const seg = segments[i];
|
|
1594
|
-
if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
|
|
1595
1656
|
if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
|
|
1596
1657
|
current[seg] = /* @__PURE__ */ Object.create(null);
|
|
1597
1658
|
}
|
|
1598
1659
|
current = current[seg];
|
|
1599
1660
|
}
|
|
1600
1661
|
const last = segments[segments.length - 1];
|
|
1601
|
-
if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
|
|
1602
1662
|
if (typeof current[last] === "object" && current[last] !== null) return;
|
|
1603
1663
|
current[last] = value;
|
|
1604
1664
|
}
|
|
@@ -1619,7 +1679,22 @@ function recordToHoconValue(obj) {
|
|
|
1619
1679
|
}
|
|
1620
1680
|
|
|
1621
1681
|
// src/internal/resolver/include-loader.ts
|
|
1622
|
-
var _require
|
|
1682
|
+
var _require;
|
|
1683
|
+
function getRequire() {
|
|
1684
|
+
if (_require === void 0) {
|
|
1685
|
+
_require = pickRequire(
|
|
1686
|
+
typeof __require === "function" ? __require : void 0,
|
|
1687
|
+
import.meta.url
|
|
1688
|
+
);
|
|
1689
|
+
}
|
|
1690
|
+
return _require;
|
|
1691
|
+
}
|
|
1692
|
+
function pickRequire(candidate, metaUrl) {
|
|
1693
|
+
if (typeof candidate?.resolve === "function") {
|
|
1694
|
+
return candidate;
|
|
1695
|
+
}
|
|
1696
|
+
return createRequire(metaUrl ?? pathToFileURL(nodePath.join(process.cwd(), "noop.js")));
|
|
1697
|
+
}
|
|
1623
1698
|
function isPnpPath(p) {
|
|
1624
1699
|
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1625
1700
|
}
|
|
@@ -1675,7 +1750,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
|
|
|
1675
1750
|
const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
|
|
1676
1751
|
let resolved;
|
|
1677
1752
|
try {
|
|
1678
|
-
resolved =
|
|
1753
|
+
resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
|
|
1679
1754
|
} catch {
|
|
1680
1755
|
throw new PackageLookupError(
|
|
1681
1756
|
`include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
|
|
@@ -2729,10 +2804,10 @@ var Config = class _Config {
|
|
|
2729
2804
|
}
|
|
2730
2805
|
if (v.kind === "object") {
|
|
2731
2806
|
const converted = numericObjectToArray(v);
|
|
2732
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
2807
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
2733
2808
|
}
|
|
2734
2809
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
|
|
2735
|
-
return v.items.map(hoconToJs);
|
|
2810
|
+
return v.items.map((item) => hoconToJs(item));
|
|
2736
2811
|
}
|
|
2737
2812
|
has(path2) {
|
|
2738
2813
|
return this.lookupNode(path2) !== void 0;
|
|
@@ -2860,8 +2935,52 @@ var Config = class _Config {
|
|
|
2860
2935
|
originDescription: this._originDescription
|
|
2861
2936
|
});
|
|
2862
2937
|
}
|
|
2863
|
-
|
|
2864
|
-
|
|
2938
|
+
/**
|
|
2939
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
2940
|
+
* booleans and `null`.
|
|
2941
|
+
*
|
|
2942
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
2943
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
2944
|
+
* property**, so the returned object's prototype is always
|
|
2945
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
2946
|
+
*
|
|
2947
|
+
* ### Passing the result to other code
|
|
2948
|
+
*
|
|
2949
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
2950
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
2951
|
+
* next depends entirely on how the consumer copies it:
|
|
2952
|
+
*
|
|
2953
|
+
* ```ts
|
|
2954
|
+
* const data = cfg.toObject()
|
|
2955
|
+
* { ...data } // safe — own properties are copied as data
|
|
2956
|
+
* structuredClone(data) // safe
|
|
2957
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
2958
|
+
* // Object.prototype's __proto__ setter: the key
|
|
2959
|
+
* // vanishes and config data becomes the result's
|
|
2960
|
+
* // prototype
|
|
2961
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
2962
|
+
* // target['__proto__'], gets Object.prototype,
|
|
2963
|
+
* // and writes to it: global pollution
|
|
2964
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
2965
|
+
* ```
|
|
2966
|
+
*
|
|
2967
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
2968
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
2969
|
+
*
|
|
2970
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
2971
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
2972
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
2973
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
2974
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
2975
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
2976
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
2977
|
+
*
|
|
2978
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
2979
|
+
* here even when the config holds it exactly — read those with
|
|
2980
|
+
* {@link Config.getString} instead.
|
|
2981
|
+
*/
|
|
2982
|
+
toObject(opts) {
|
|
2983
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
2865
2984
|
}
|
|
2866
2985
|
/**
|
|
2867
2986
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -2973,16 +3092,25 @@ function scalarToJs(raw, valueType) {
|
|
|
2973
3092
|
return raw;
|
|
2974
3093
|
}
|
|
2975
3094
|
}
|
|
2976
|
-
function hoconToJs(v) {
|
|
3095
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
2977
3096
|
switch (v.kind) {
|
|
2978
3097
|
case "scalar":
|
|
2979
3098
|
return scalarToJs(v.raw, v.valueType);
|
|
2980
3099
|
case "array":
|
|
2981
|
-
return v.items.map(hoconToJs);
|
|
3100
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
2982
3101
|
case "object": {
|
|
2983
|
-
const
|
|
2984
|
-
|
|
2985
|
-
|
|
3102
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
3103
|
+
if (nullPrototype) {
|
|
3104
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
3105
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
3106
|
+
value: val,
|
|
3107
|
+
writable: true,
|
|
3108
|
+
enumerable: true,
|
|
3109
|
+
configurable: true
|
|
3110
|
+
});
|
|
3111
|
+
return obj;
|
|
3112
|
+
}
|
|
3113
|
+
return Object.fromEntries(entries);
|
|
2986
3114
|
}
|
|
2987
3115
|
}
|
|
2988
3116
|
}
|
|
@@ -3107,13 +3235,16 @@ function buildResolveContext(input, opts) {
|
|
|
3107
3235
|
return { ast, resolveOpts };
|
|
3108
3236
|
}
|
|
3109
3237
|
function parse(input, opts = {}) {
|
|
3110
|
-
|
|
3111
|
-
const value = resolve2(ast, resolveOpts);
|
|
3112
|
-
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
3113
|
-
return new Config(value, { resolved: true, originDescription: opts.originDescription });
|
|
3238
|
+
return parseStringWithOptions(input, opts);
|
|
3114
3239
|
}
|
|
3115
3240
|
var parseString = parse;
|
|
3116
3241
|
function parseStringWithOptions(input, opts = {}) {
|
|
3242
|
+
return guardStackDepth(
|
|
3243
|
+
() => parseStringInner(input, opts),
|
|
3244
|
+
(msg) => new ParseError(msg, 1, 1, opts.originDescription)
|
|
3245
|
+
);
|
|
3246
|
+
}
|
|
3247
|
+
function parseStringInner(input, opts) {
|
|
3117
3248
|
const resolveSubstitutions = opts.resolveSubstitutions ?? true;
|
|
3118
3249
|
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
3119
3250
|
if (resolveSubstitutions) {
|
|
@@ -3144,10 +3275,12 @@ function parseFileWithOptions(filePath, opts = {}) {
|
|
|
3144
3275
|
});
|
|
3145
3276
|
}
|
|
3146
3277
|
async function parseAsync(input, opts = {}) {
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3278
|
+
return guardStackDepthAsync(async () => {
|
|
3279
|
+
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
3280
|
+
const value = await resolveAsync(ast, resolveOpts);
|
|
3281
|
+
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
3282
|
+
return new Config(value);
|
|
3283
|
+
}, (msg) => new ParseError(msg, 1, 1, opts.originDescription));
|
|
3151
3284
|
}
|
|
3152
3285
|
function parseFile(filePath, opts = {}) {
|
|
3153
3286
|
if (typeof process === "undefined" && !opts.readFileSync) {
|
|
@@ -3208,7 +3341,10 @@ function isNull(v) {
|
|
|
3208
3341
|
|
|
3209
3342
|
// src/value-factory.ts
|
|
3210
3343
|
function fromMap(values, originDescription) {
|
|
3211
|
-
const fields =
|
|
3344
|
+
const fields = guardStackDepth(
|
|
3345
|
+
() => coerceObject(values, ""),
|
|
3346
|
+
(msg) => depthError(`fromMap: ${msg}`)
|
|
3347
|
+
);
|
|
3212
3348
|
const root = { kind: "object", fields };
|
|
3213
3349
|
return Config._fromResolvedValue(root, { originDescription });
|
|
3214
3350
|
}
|
|
@@ -3216,6 +3352,8 @@ function empty(originDescription) {
|
|
|
3216
3352
|
const root = { kind: "object", fields: /* @__PURE__ */ new Map() };
|
|
3217
3353
|
return Config._fromResolvedValue(root, { originDescription });
|
|
3218
3354
|
}
|
|
3355
|
+
var INT64_MAX = 9223372036854775807n;
|
|
3356
|
+
var INT64_MIN = -9223372036854775808n;
|
|
3219
3357
|
function coerceObject(obj, atPath) {
|
|
3220
3358
|
const fields = /* @__PURE__ */ new Map();
|
|
3221
3359
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -3244,14 +3382,14 @@ function coerceValue(v, atPath) {
|
|
|
3244
3382
|
atPath
|
|
3245
3383
|
);
|
|
3246
3384
|
}
|
|
3247
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
3385
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
3248
3386
|
}
|
|
3249
3387
|
case "boolean":
|
|
3250
3388
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
3251
3389
|
case "bigint": {
|
|
3252
|
-
if (v >
|
|
3390
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
3253
3391
|
throw new ConfigError(
|
|
3254
|
-
`fromMap:
|
|
3392
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
3255
3393
|
atPath
|
|
3256
3394
|
);
|
|
3257
3395
|
}
|
package/dist/zod.cjs
CHANGED
|
@@ -682,6 +682,7 @@ function isFileNotFoundError(e) {
|
|
|
682
682
|
// src/internal/resolver/include-loader.ts
|
|
683
683
|
var import_node_module = require("module");
|
|
684
684
|
var nodePath = __toESM(require("path"), 1);
|
|
685
|
+
var import_node_url = require("url");
|
|
685
686
|
|
|
686
687
|
// src/internal/lexer/lexer.ts
|
|
687
688
|
function isHoconWhitespace(ch) {
|
|
@@ -1524,25 +1525,69 @@ function parseTokens(tokens) {
|
|
|
1524
1525
|
return new Parser(tokens).parse();
|
|
1525
1526
|
}
|
|
1526
1527
|
|
|
1528
|
+
// src/internal/strip-bom.ts
|
|
1529
|
+
function stripBom(input) {
|
|
1530
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// src/internal/depth.ts
|
|
1534
|
+
var MAX_PATH_SEGMENTS = 64;
|
|
1535
|
+
function tooDeep(segments) {
|
|
1536
|
+
return segments > MAX_PATH_SEGMENTS;
|
|
1537
|
+
}
|
|
1538
|
+
function guardStackDepth(fn, wrap) {
|
|
1539
|
+
try {
|
|
1540
|
+
return fn();
|
|
1541
|
+
} catch (e) {
|
|
1542
|
+
if (!isStackOverflow(e)) throw e;
|
|
1543
|
+
throw wrap(STACK_MESSAGE);
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
var STACK_MESSAGE = "input is nested too deeply for this engine's stack, or contains a cycle (flatten the document, or break the cycle)";
|
|
1547
|
+
function isStackOverflow(e) {
|
|
1548
|
+
if (!(e instanceof RangeError)) return false;
|
|
1549
|
+
const m = e.message.toLowerCase();
|
|
1550
|
+
return m.includes("call stack") || // V8: "Maximum call stack size exceeded"
|
|
1551
|
+
m.includes("recursion") || // SpiderMonkey: "too much recursion"
|
|
1552
|
+
m.includes("stack overflow");
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1527
1555
|
// src/internal/properties/properties.ts
|
|
1528
1556
|
function parseProperties(input) {
|
|
1529
1557
|
const pairs = [];
|
|
1530
|
-
for (const { text, line } of logicalLines(input)) {
|
|
1558
|
+
for (const { text, line } of logicalLines(stripBom(input))) {
|
|
1531
1559
|
const [rawKey, rawValue] = splitKeyValue(text);
|
|
1532
1560
|
const key = unescapeProps(rawKey, line);
|
|
1533
1561
|
if (key === "") continue;
|
|
1534
|
-
|
|
1562
|
+
const segments = key.split(".");
|
|
1563
|
+
if (tooDeep(segments.length)) {
|
|
1564
|
+
throw new ParseError(
|
|
1565
|
+
`key maps to a path ${segments.length} segments deep, over the limit of ${MAX_PATH_SEGMENTS}`,
|
|
1566
|
+
line,
|
|
1567
|
+
1
|
|
1568
|
+
);
|
|
1569
|
+
}
|
|
1570
|
+
pairs.push([segments, unescapeProps(rawValue, line)]);
|
|
1535
1571
|
}
|
|
1536
1572
|
return nestPairs(pairs);
|
|
1537
1573
|
}
|
|
1538
1574
|
function nestPairs(pairs) {
|
|
1539
1575
|
const root = /* @__PURE__ */ Object.create(null);
|
|
1540
|
-
const sorted = [...pairs].sort(([a], [b]) => a
|
|
1541
|
-
for (const [
|
|
1542
|
-
setNested(root,
|
|
1576
|
+
const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
|
|
1577
|
+
for (const [segments, value] of sorted) {
|
|
1578
|
+
setNested(root, segments, value);
|
|
1543
1579
|
}
|
|
1544
1580
|
return root;
|
|
1545
1581
|
}
|
|
1582
|
+
function compareSegments(a, b) {
|
|
1583
|
+
const n = Math.min(a.length, b.length);
|
|
1584
|
+
for (let i = 0; i < n; i++) {
|
|
1585
|
+
const x = a[i];
|
|
1586
|
+
const y = b[i];
|
|
1587
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
1588
|
+
}
|
|
1589
|
+
return a.length - b.length;
|
|
1590
|
+
}
|
|
1546
1591
|
function logicalLines(input) {
|
|
1547
1592
|
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1548
1593
|
const out = [];
|
|
@@ -1629,19 +1674,16 @@ function unescapeProps(s, line) {
|
|
|
1629
1674
|
}
|
|
1630
1675
|
return out;
|
|
1631
1676
|
}
|
|
1632
|
-
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1633
1677
|
function setNested(obj, segments, value) {
|
|
1634
1678
|
let current = obj;
|
|
1635
1679
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1636
1680
|
const seg = segments[i];
|
|
1637
|
-
if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
|
|
1638
1681
|
if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
|
|
1639
1682
|
current[seg] = /* @__PURE__ */ Object.create(null);
|
|
1640
1683
|
}
|
|
1641
1684
|
current = current[seg];
|
|
1642
1685
|
}
|
|
1643
1686
|
const last = segments[segments.length - 1];
|
|
1644
|
-
if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
|
|
1645
1687
|
if (typeof current[last] === "object" && current[last] !== null) return;
|
|
1646
1688
|
current[last] = value;
|
|
1647
1689
|
}
|
|
@@ -1663,7 +1705,22 @@ function recordToHoconValue(obj) {
|
|
|
1663
1705
|
|
|
1664
1706
|
// src/internal/resolver/include-loader.ts
|
|
1665
1707
|
var import_meta = {};
|
|
1666
|
-
var _require
|
|
1708
|
+
var _require;
|
|
1709
|
+
function getRequire() {
|
|
1710
|
+
if (_require === void 0) {
|
|
1711
|
+
_require = pickRequire(
|
|
1712
|
+
typeof require === "function" ? require : void 0,
|
|
1713
|
+
import_meta.url
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
return _require;
|
|
1717
|
+
}
|
|
1718
|
+
function pickRequire(candidate, metaUrl) {
|
|
1719
|
+
if (typeof candidate?.resolve === "function") {
|
|
1720
|
+
return candidate;
|
|
1721
|
+
}
|
|
1722
|
+
return (0, import_node_module.createRequire)(metaUrl ?? (0, import_node_url.pathToFileURL)(nodePath.join(process.cwd(), "noop.js")));
|
|
1723
|
+
}
|
|
1667
1724
|
function isPnpPath(p) {
|
|
1668
1725
|
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1669
1726
|
}
|
|
@@ -1719,7 +1776,7 @@ function defaultPackageResolver(identifier, file, includingFile, baseDir, resolv
|
|
|
1719
1776
|
const from = resolveFrom ? Array.isArray(resolveFrom) ? resolveFrom : [resolveFrom] : baseDir ? [baseDir] : includingFile ? [nodePath.dirname(includingFile)] : [process.cwd()];
|
|
1720
1777
|
let resolved;
|
|
1721
1778
|
try {
|
|
1722
|
-
resolved =
|
|
1779
|
+
resolved = getRequire().resolve(`${identifier}/${file}`, { paths: from });
|
|
1723
1780
|
} catch {
|
|
1724
1781
|
throw new PackageLookupError(
|
|
1725
1782
|
`include package("${identifier}", "${file}"): module not found (starting from: ${from.join(", ")})`,
|
|
@@ -2769,10 +2826,10 @@ var Config = class _Config {
|
|
|
2769
2826
|
}
|
|
2770
2827
|
if (v.kind === "object") {
|
|
2771
2828
|
const converted = numericObjectToArray(v);
|
|
2772
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
2829
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
2773
2830
|
}
|
|
2774
2831
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path2}`, path2);
|
|
2775
|
-
return v.items.map(hoconToJs);
|
|
2832
|
+
return v.items.map((item) => hoconToJs(item));
|
|
2776
2833
|
}
|
|
2777
2834
|
has(path2) {
|
|
2778
2835
|
return this.lookupNode(path2) !== void 0;
|
|
@@ -2900,8 +2957,52 @@ var Config = class _Config {
|
|
|
2900
2957
|
originDescription: this._originDescription
|
|
2901
2958
|
});
|
|
2902
2959
|
}
|
|
2903
|
-
|
|
2904
|
-
|
|
2960
|
+
/**
|
|
2961
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
2962
|
+
* booleans and `null`.
|
|
2963
|
+
*
|
|
2964
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
2965
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
2966
|
+
* property**, so the returned object's prototype is always
|
|
2967
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
2968
|
+
*
|
|
2969
|
+
* ### Passing the result to other code
|
|
2970
|
+
*
|
|
2971
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
2972
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
2973
|
+
* next depends entirely on how the consumer copies it:
|
|
2974
|
+
*
|
|
2975
|
+
* ```ts
|
|
2976
|
+
* const data = cfg.toObject()
|
|
2977
|
+
* { ...data } // safe — own properties are copied as data
|
|
2978
|
+
* structuredClone(data) // safe
|
|
2979
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
2980
|
+
* // Object.prototype's __proto__ setter: the key
|
|
2981
|
+
* // vanishes and config data becomes the result's
|
|
2982
|
+
* // prototype
|
|
2983
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
2984
|
+
* // target['__proto__'], gets Object.prototype,
|
|
2985
|
+
* // and writes to it: global pollution
|
|
2986
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
2987
|
+
* ```
|
|
2988
|
+
*
|
|
2989
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
2990
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
2991
|
+
*
|
|
2992
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
2993
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
2994
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
2995
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
2996
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
2997
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
2998
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
2999
|
+
*
|
|
3000
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
3001
|
+
* here even when the config holds it exactly — read those with
|
|
3002
|
+
* {@link Config.getString} instead.
|
|
3003
|
+
*/
|
|
3004
|
+
toObject(opts) {
|
|
3005
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
2905
3006
|
}
|
|
2906
3007
|
/**
|
|
2907
3008
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -3013,16 +3114,25 @@ function scalarToJs(raw, valueType) {
|
|
|
3013
3114
|
return raw;
|
|
3014
3115
|
}
|
|
3015
3116
|
}
|
|
3016
|
-
function hoconToJs(v) {
|
|
3117
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
3017
3118
|
switch (v.kind) {
|
|
3018
3119
|
case "scalar":
|
|
3019
3120
|
return scalarToJs(v.raw, v.valueType);
|
|
3020
3121
|
case "array":
|
|
3021
|
-
return v.items.map(hoconToJs);
|
|
3122
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
3022
3123
|
case "object": {
|
|
3023
|
-
const
|
|
3024
|
-
|
|
3025
|
-
|
|
3124
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
3125
|
+
if (nullPrototype) {
|
|
3126
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
3127
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
3128
|
+
value: val,
|
|
3129
|
+
writable: true,
|
|
3130
|
+
enumerable: true,
|
|
3131
|
+
configurable: true
|
|
3132
|
+
});
|
|
3133
|
+
return obj;
|
|
3134
|
+
}
|
|
3135
|
+
return Object.fromEntries(entries);
|
|
3026
3136
|
}
|
|
3027
3137
|
}
|
|
3028
3138
|
}
|
|
@@ -3136,10 +3246,30 @@ function buildResolveContext(input, opts) {
|
|
|
3136
3246
|
return { ast, resolveOpts };
|
|
3137
3247
|
}
|
|
3138
3248
|
function parse(input, opts = {}) {
|
|
3249
|
+
return parseStringWithOptions(input, opts);
|
|
3250
|
+
}
|
|
3251
|
+
function parseStringWithOptions(input, opts = {}) {
|
|
3252
|
+
return guardStackDepth(
|
|
3253
|
+
() => parseStringInner(input, opts),
|
|
3254
|
+
(msg) => new ParseError(msg, 1, 1, opts.originDescription)
|
|
3255
|
+
);
|
|
3256
|
+
}
|
|
3257
|
+
function parseStringInner(input, opts) {
|
|
3258
|
+
const resolveSubstitutions = opts.resolveSubstitutions ?? true;
|
|
3139
3259
|
const { ast, resolveOpts } = buildResolveContext(input, opts);
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3260
|
+
if (resolveSubstitutions) {
|
|
3261
|
+
const value = resolve2(ast, resolveOpts);
|
|
3262
|
+
if (value.kind !== "object") throw new Error("resolved value is not an object");
|
|
3263
|
+
return new Config(value, { resolved: true, originDescription: opts.originDescription });
|
|
3264
|
+
}
|
|
3265
|
+
const tree = buildTree(ast, resolveOpts);
|
|
3266
|
+
const hasPlaceholders = containsPlaceholders(tree);
|
|
3267
|
+
return Config._fromUnresolvedResObj(tree, {
|
|
3268
|
+
parseBaseDir: opts.baseDir,
|
|
3269
|
+
originDescription: opts.originDescription,
|
|
3270
|
+
resolved: !hasPlaceholders,
|
|
3271
|
+
resolveOpts
|
|
3272
|
+
});
|
|
3143
3273
|
}
|
|
3144
3274
|
function parseFile(filePath, opts = {}) {
|
|
3145
3275
|
if (typeof process === "undefined" && !opts.readFileSync) {
|
|
@@ -3213,7 +3343,7 @@ function coerceValue(value, schema) {
|
|
|
3213
3343
|
const obj = value;
|
|
3214
3344
|
const result = { ...obj };
|
|
3215
3345
|
for (const key of Object.keys(shape)) {
|
|
3216
|
-
if (key
|
|
3346
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
3217
3347
|
result[key] = coerceValue(obj[key], shape[key]);
|
|
3218
3348
|
}
|
|
3219
3349
|
}
|
package/dist/zod.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ZodType } from 'zod';
|
|
2
|
-
import { C as Config, a as ParseOptions } from './config-
|
|
2
|
+
import { C as Config, a as ParseOptions } from './config-Dlq6rF3w.cjs';
|
|
3
3
|
|
|
4
4
|
declare function validate<T>(config: Config, schema: ZodType<T>): T;
|
|
5
5
|
declare function getValidated<T>(config: Config, path: string, schema: ZodType<T>): T;
|
package/dist/zod.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ZodType } from 'zod';
|
|
2
|
-
import { C as Config, a as ParseOptions } from './config-
|
|
2
|
+
import { C as Config, a as ParseOptions } from './config-Dlq6rF3w.js';
|
|
3
3
|
|
|
4
4
|
declare function validate<T>(config: Config, schema: ZodType<T>): T;
|
|
5
5
|
declare function getValidated<T>(config: Config, path: string, schema: ZodType<T>): T;
|