@o3co/ts.hocon 1.8.0 → 1.10.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.md +38 -5
- package/dist/adapters/env.cjs +1668 -0
- package/dist/adapters/env.d.cts +41 -0
- package/dist/adapters/env.d.ts +41 -0
- package/dist/adapters/env.js +1629 -0
- package/dist/adapters/jsonc.cjs +1613 -0
- package/dist/adapters/jsonc.d.cts +25 -0
- package/dist/adapters/jsonc.d.ts +25 -0
- package/dist/adapters/jsonc.js +1574 -0
- package/dist/adapters/properties.cjs +1681 -0
- package/dist/adapters/properties.d.cts +27 -0
- package/dist/adapters/properties.d.ts +27 -0
- package/dist/adapters/properties.js +1643 -0
- package/dist/adapters/toml.cjs +1570 -0
- package/dist/adapters/toml.d.cts +18 -0
- package/dist/adapters/toml.d.ts +18 -0
- package/dist/adapters/toml.js +1534 -0
- package/dist/adapters/yaml.cjs +1610 -0
- package/dist/adapters/yaml.d.cts +43 -0
- package/dist/adapters/yaml.d.ts +43 -0
- package/dist/adapters/yaml.js +1573 -0
- package/dist/index.cjs +139 -43
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +139 -43
- package/dist/zod.cjs +139 -43
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +139 -43
- package/package.json +44 -9
- package/dist/{config-D27s3_aG.d.cts → config-PdhVgeLS.d.cts} +3 -3
- package/dist/{config-D27s3_aG.d.ts → config-PdhVgeLS.d.ts} +3 -3
package/dist/zod.cjs
CHANGED
|
@@ -119,7 +119,7 @@ function parseDuration(value, outputUnit = "ms") {
|
|
|
119
119
|
if (i === 0) return NaN;
|
|
120
120
|
const num = Number(trimmed.slice(0, i));
|
|
121
121
|
if (Number.isNaN(num)) return NaN;
|
|
122
|
-
const unit = trimHoconWs(trimmed.slice(i))
|
|
122
|
+
const unit = trimHoconWs(trimmed.slice(i));
|
|
123
123
|
const divisor = OUTPUT_DURATION_UNITS[outputUnit];
|
|
124
124
|
if (divisor === void 0) return NaN;
|
|
125
125
|
if (unit === "") {
|
|
@@ -683,20 +683,6 @@ function isFileNotFoundError(e) {
|
|
|
683
683
|
var import_node_module = require("module");
|
|
684
684
|
var nodePath = __toESM(require("path"), 1);
|
|
685
685
|
|
|
686
|
-
// src/internal/parser/empty-check.ts
|
|
687
|
-
function hasContentTokens(tokens) {
|
|
688
|
-
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
689
|
-
}
|
|
690
|
-
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
691
|
-
if (!hasContentTokens(tokens)) {
|
|
692
|
-
throw new ParseError(
|
|
693
|
-
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
694
|
-
1,
|
|
695
|
-
1
|
|
696
|
-
);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
|
|
700
686
|
// src/internal/lexer/lexer.ts
|
|
701
687
|
function isHoconWhitespace(ch) {
|
|
702
688
|
const cp = ch.codePointAt(0) ?? -1;
|
|
@@ -1097,12 +1083,12 @@ function isDecimalDigit(ch) {
|
|
|
1097
1083
|
}
|
|
1098
1084
|
function isUnquotedStart(ch) {
|
|
1099
1085
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1100
|
-
if ('{}[],:=+#"
|
|
1086
|
+
if ('{}[],:=+#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1101
1087
|
return true;
|
|
1102
1088
|
}
|
|
1103
1089
|
function isUnquotedContinue(ch, nextFn) {
|
|
1104
1090
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1105
|
-
if ('{}[],:=#"
|
|
1091
|
+
if ('{}[],:=#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1106
1092
|
if (ch === "+" && nextFn() === "=") return false;
|
|
1107
1093
|
if (ch === "/" && nextFn() === "/") return false;
|
|
1108
1094
|
return true;
|
|
@@ -1142,6 +1128,16 @@ var Parser = class {
|
|
|
1142
1128
|
}
|
|
1143
1129
|
return { kind: "object", fields: allFields, pos: first.pos };
|
|
1144
1130
|
}
|
|
1131
|
+
if (t.kind === "lbracket") {
|
|
1132
|
+
this.advance();
|
|
1133
|
+
const arr = this.parseArray();
|
|
1134
|
+
this.skip("newline");
|
|
1135
|
+
const remaining = this.peek();
|
|
1136
|
+
if (remaining.kind !== "eof") {
|
|
1137
|
+
throw new ParseError(`Unexpected token '${remaining.value}' after root array`, remaining.line, remaining.col);
|
|
1138
|
+
}
|
|
1139
|
+
return { ...arr, pos: { line: t.line, col: t.col } };
|
|
1140
|
+
}
|
|
1145
1141
|
return this.parseObject(false);
|
|
1146
1142
|
}
|
|
1147
1143
|
peek(offset = 0) {
|
|
@@ -1241,6 +1237,17 @@ var Parser = class {
|
|
|
1241
1237
|
this.advance();
|
|
1242
1238
|
const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
|
|
1243
1239
|
const parts = raw.split(".");
|
|
1240
|
+
const leadingDotIsSeparator = spaceConcat || postDotPrefix !== "";
|
|
1241
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1242
|
+
if (parts[i] !== "") continue;
|
|
1243
|
+
if (i === parts.length - 1) continue;
|
|
1244
|
+
if (i === 0 && leadingDotIsSeparator) continue;
|
|
1245
|
+
throw new ParseError(
|
|
1246
|
+
`path has ${i === 0 ? "a leading period '.'" : "two adjacent periods '.'"} \u2014 empty key segment not allowed, use quoted "" (HOCON.md path rules)`,
|
|
1247
|
+
t.line,
|
|
1248
|
+
t.col
|
|
1249
|
+
);
|
|
1250
|
+
}
|
|
1244
1251
|
const filtered = parts.filter((s) => s.length > 0);
|
|
1245
1252
|
if (spaceConcat) {
|
|
1246
1253
|
segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}`;
|
|
@@ -1519,29 +1526,108 @@ function parseTokens(tokens) {
|
|
|
1519
1526
|
|
|
1520
1527
|
// src/internal/properties/properties.ts
|
|
1521
1528
|
function parseProperties(input) {
|
|
1522
|
-
const root = /* @__PURE__ */ Object.create(null);
|
|
1523
1529
|
const pairs = [];
|
|
1524
|
-
for (const line of input
|
|
1525
|
-
const
|
|
1526
|
-
|
|
1527
|
-
const sepIdx = findSeparator(trimmed);
|
|
1528
|
-
if (sepIdx === -1) continue;
|
|
1529
|
-
const key = trimmed.slice(0, sepIdx).trim();
|
|
1530
|
-
const value = trimmed.slice(sepIdx + 1).trim();
|
|
1530
|
+
for (const { text, line } of logicalLines(input)) {
|
|
1531
|
+
const [rawKey, rawValue] = splitKeyValue(text);
|
|
1532
|
+
const key = unescapeProps(rawKey, line);
|
|
1531
1533
|
if (key === "") continue;
|
|
1532
|
-
pairs.push([key,
|
|
1534
|
+
pairs.push([key, unescapeProps(rawValue, line)]);
|
|
1533
1535
|
}
|
|
1534
|
-
pairs
|
|
1535
|
-
|
|
1536
|
+
return nestPairs(pairs);
|
|
1537
|
+
}
|
|
1538
|
+
function nestPairs(pairs) {
|
|
1539
|
+
const root = /* @__PURE__ */ Object.create(null);
|
|
1540
|
+
const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
1541
|
+
for (const [key, value] of sorted) {
|
|
1536
1542
|
setNested(root, key.split("."), value);
|
|
1537
1543
|
}
|
|
1538
1544
|
return root;
|
|
1539
1545
|
}
|
|
1540
|
-
function
|
|
1541
|
-
|
|
1542
|
-
|
|
1546
|
+
function logicalLines(input) {
|
|
1547
|
+
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1548
|
+
const out = [];
|
|
1549
|
+
for (let i = 0; i < natural.length; i++) {
|
|
1550
|
+
let text = trimLeadingSpace(natural[i] ?? "");
|
|
1551
|
+
if (text === "" || text.startsWith("#") || text.startsWith("!")) continue;
|
|
1552
|
+
const start = i + 1;
|
|
1553
|
+
while (endsWithContinuation(text)) {
|
|
1554
|
+
text = text.slice(0, -1);
|
|
1555
|
+
if (i + 1 >= natural.length) break;
|
|
1556
|
+
i++;
|
|
1557
|
+
text += trimLeadingSpace(natural[i] ?? "");
|
|
1558
|
+
}
|
|
1559
|
+
out.push({ text, line: start });
|
|
1543
1560
|
}
|
|
1544
|
-
return
|
|
1561
|
+
return out;
|
|
1562
|
+
}
|
|
1563
|
+
function trimLeadingSpace(s) {
|
|
1564
|
+
return s.replace(/^[ \t\f]+/, "");
|
|
1565
|
+
}
|
|
1566
|
+
function endsWithContinuation(line) {
|
|
1567
|
+
let n = 0;
|
|
1568
|
+
for (let i = line.length - 1; i >= 0 && line[i] === "\\"; i--) n++;
|
|
1569
|
+
return n % 2 === 1;
|
|
1570
|
+
}
|
|
1571
|
+
function splitKeyValue(line) {
|
|
1572
|
+
let key = "";
|
|
1573
|
+
let i = 0;
|
|
1574
|
+
for (; i < line.length; i++) {
|
|
1575
|
+
const c = line[i];
|
|
1576
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
1577
|
+
key += c + line[i + 1];
|
|
1578
|
+
i++;
|
|
1579
|
+
continue;
|
|
1580
|
+
}
|
|
1581
|
+
if (c === "=" || c === ":" || isPropsSpace(c)) break;
|
|
1582
|
+
key += c;
|
|
1583
|
+
}
|
|
1584
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1585
|
+
if (i < line.length && (line[i] === "=" || line[i] === ":")) {
|
|
1586
|
+
i++;
|
|
1587
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1588
|
+
}
|
|
1589
|
+
return [key, line.slice(i)];
|
|
1590
|
+
}
|
|
1591
|
+
function isPropsSpace(c) {
|
|
1592
|
+
return c === " " || c === " " || c === "\f";
|
|
1593
|
+
}
|
|
1594
|
+
function unescapeProps(s, line) {
|
|
1595
|
+
let out = "";
|
|
1596
|
+
for (let i = 0; i < s.length; i++) {
|
|
1597
|
+
if (s[i] !== "\\") {
|
|
1598
|
+
out += s[i];
|
|
1599
|
+
continue;
|
|
1600
|
+
}
|
|
1601
|
+
i++;
|
|
1602
|
+
if (i >= s.length) break;
|
|
1603
|
+
const e = s[i];
|
|
1604
|
+
switch (e) {
|
|
1605
|
+
case "t":
|
|
1606
|
+
out += " ";
|
|
1607
|
+
break;
|
|
1608
|
+
case "n":
|
|
1609
|
+
out += "\n";
|
|
1610
|
+
break;
|
|
1611
|
+
case "r":
|
|
1612
|
+
out += "\r";
|
|
1613
|
+
break;
|
|
1614
|
+
case "f":
|
|
1615
|
+
out += "\f";
|
|
1616
|
+
break;
|
|
1617
|
+
case "u": {
|
|
1618
|
+
const hex = s.slice(i + 1, i + 5);
|
|
1619
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1620
|
+
throw new ParseError(`malformed \\u escape in .properties: \\u${hex}`, line, i + 1);
|
|
1621
|
+
}
|
|
1622
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
1623
|
+
i += 4;
|
|
1624
|
+
break;
|
|
1625
|
+
}
|
|
1626
|
+
default:
|
|
1627
|
+
out += e;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return out;
|
|
1545
1631
|
}
|
|
1546
1632
|
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1547
1633
|
function setNested(obj, segments, value) {
|
|
@@ -1581,6 +1667,16 @@ var _require = (0, import_node_module.createRequire)(import_meta.url);
|
|
|
1581
1667
|
function isPnpPath(p) {
|
|
1582
1668
|
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1583
1669
|
}
|
|
1670
|
+
function assertObjectRootedInclude(ast, sourcePath) {
|
|
1671
|
+
if (ast.kind === "array") {
|
|
1672
|
+
throw new ResolveError(
|
|
1673
|
+
`included file has array at file root \u2014 an included file must contain an object, not an array (HOCON.md L993-994): ${sourcePath}`,
|
|
1674
|
+
sourcePath,
|
|
1675
|
+
ast.pos.line,
|
|
1676
|
+
ast.pos.col
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1584
1680
|
function validatePackageFile(file, identifier) {
|
|
1585
1681
|
if (file.length === 0) {
|
|
1586
1682
|
throw new ParseError(`include package("${identifier}", ...): file argument must be non-empty`, 0, 0);
|
|
@@ -1760,10 +1856,9 @@ var IncludeLoader = class {
|
|
|
1760
1856
|
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1761
1857
|
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1762
1858
|
const content = readFileSync2(resolvedPath);
|
|
1763
|
-
if (content.length === 0) return makeResObj();
|
|
1764
1859
|
const tokens = tokenize(content);
|
|
1765
|
-
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1766
1860
|
const ast = parseTokens(tokens);
|
|
1861
|
+
assertObjectRootedInclude(ast, resolvedPath);
|
|
1767
1862
|
return this.onBuildResObj(ast, {
|
|
1768
1863
|
...this.opts,
|
|
1769
1864
|
baseDir: nodePath.dirname(resolvedPath),
|
|
@@ -1793,10 +1888,9 @@ var IncludeLoader = class {
|
|
|
1793
1888
|
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1794
1889
|
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1795
1890
|
const content = await read(resolvedPath);
|
|
1796
|
-
if (content.length === 0) return makeResObj();
|
|
1797
1891
|
const tokens = tokenize(content);
|
|
1798
|
-
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1799
1892
|
const ast = parseTokens(tokens);
|
|
1893
|
+
assertObjectRootedInclude(ast, resolvedPath);
|
|
1800
1894
|
return this.onBuildResObjAsync(ast, {
|
|
1801
1895
|
...this.opts,
|
|
1802
1896
|
baseDir: nodePath.dirname(resolvedPath),
|
|
@@ -1819,10 +1913,8 @@ var IncludeLoader = class {
|
|
|
1819
1913
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1820
1914
|
}
|
|
1821
1915
|
const tokens = tokenize(content);
|
|
1822
|
-
if (!hasContentTokens(tokens)) {
|
|
1823
|
-
return makeResObj();
|
|
1824
|
-
}
|
|
1825
1916
|
const ast = parseTokens(tokens);
|
|
1917
|
+
assertObjectRootedInclude(ast, candidate);
|
|
1826
1918
|
return this.onBuildResObj(ast, {
|
|
1827
1919
|
...this.opts,
|
|
1828
1920
|
baseDir: nodePath.dirname(candidate),
|
|
@@ -1846,10 +1938,8 @@ var IncludeLoader = class {
|
|
|
1846
1938
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1847
1939
|
}
|
|
1848
1940
|
const tokens = tokenize(content);
|
|
1849
|
-
if (!hasContentTokens(tokens)) {
|
|
1850
|
-
return makeResObj();
|
|
1851
|
-
}
|
|
1852
1941
|
const ast = parseTokens(tokens);
|
|
1942
|
+
assertObjectRootedInclude(ast, candidate);
|
|
1853
1943
|
return this.onBuildResObjAsync(ast, {
|
|
1854
1944
|
...this.opts,
|
|
1855
1945
|
baseDir: nodePath.dirname(candidate),
|
|
@@ -3026,8 +3116,14 @@ function defaultReadFileSync(filePath) {
|
|
|
3026
3116
|
}
|
|
3027
3117
|
function buildResolveContext(input, opts) {
|
|
3028
3118
|
const tokens = tokenize(input);
|
|
3029
|
-
assertNonEmptyDocument(tokens, "input");
|
|
3030
3119
|
const ast = parseTokens(tokens);
|
|
3120
|
+
if (ast.kind === "array") {
|
|
3121
|
+
const origin = opts.originDescription ?? "input";
|
|
3122
|
+
throw new ConfigError(
|
|
3123
|
+
`${origin}: ${ast.pos.line}:${ast.pos.col}: document has type array rather than object at file root (HOCON.md L989-991); the Config API requires an object root`,
|
|
3124
|
+
""
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
3031
3127
|
const resolveOpts = {
|
|
3032
3128
|
env: getEnv(opts),
|
|
3033
3129
|
baseDir: opts.baseDir,
|
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-PdhVgeLS.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-PdhVgeLS.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;
|
package/dist/zod.js
CHANGED
|
@@ -80,7 +80,7 @@ function parseDuration(value, outputUnit = "ms") {
|
|
|
80
80
|
if (i === 0) return NaN;
|
|
81
81
|
const num = Number(trimmed.slice(0, i));
|
|
82
82
|
if (Number.isNaN(num)) return NaN;
|
|
83
|
-
const unit = trimHoconWs(trimmed.slice(i))
|
|
83
|
+
const unit = trimHoconWs(trimmed.slice(i));
|
|
84
84
|
const divisor = OUTPUT_DURATION_UNITS[outputUnit];
|
|
85
85
|
if (divisor === void 0) return NaN;
|
|
86
86
|
if (unit === "") {
|
|
@@ -644,20 +644,6 @@ function isFileNotFoundError(e) {
|
|
|
644
644
|
import { createRequire } from "module";
|
|
645
645
|
import * as nodePath from "path";
|
|
646
646
|
|
|
647
|
-
// src/internal/parser/empty-check.ts
|
|
648
|
-
function hasContentTokens(tokens) {
|
|
649
|
-
return tokens.some((t) => t.kind !== "eof" && t.kind !== "newline");
|
|
650
|
-
}
|
|
651
|
-
function assertNonEmptyDocument(tokens, sourceDescriptor) {
|
|
652
|
-
if (!hasContentTokens(tokens)) {
|
|
653
|
-
throw new ParseError(
|
|
654
|
-
`empty file is not a valid HOCON document (HOCON.md L130): ${sourceDescriptor}`,
|
|
655
|
-
1,
|
|
656
|
-
1
|
|
657
|
-
);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
|
|
661
647
|
// src/internal/lexer/lexer.ts
|
|
662
648
|
function isHoconWhitespace(ch) {
|
|
663
649
|
const cp = ch.codePointAt(0) ?? -1;
|
|
@@ -1058,12 +1044,12 @@ function isDecimalDigit(ch) {
|
|
|
1058
1044
|
}
|
|
1059
1045
|
function isUnquotedStart(ch) {
|
|
1060
1046
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1061
|
-
if ('{}[],:=+#"
|
|
1047
|
+
if ('{}[],:=+#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1062
1048
|
return true;
|
|
1063
1049
|
}
|
|
1064
1050
|
function isUnquotedContinue(ch, nextFn) {
|
|
1065
1051
|
if (ch === "" || isHoconWhitespace(ch)) return false;
|
|
1066
|
-
if ('{}[],:=#"
|
|
1052
|
+
if ('{}[],:=#"$?!@*&^`\\'.includes(ch)) return false;
|
|
1067
1053
|
if (ch === "+" && nextFn() === "=") return false;
|
|
1068
1054
|
if (ch === "/" && nextFn() === "/") return false;
|
|
1069
1055
|
return true;
|
|
@@ -1103,6 +1089,16 @@ var Parser = class {
|
|
|
1103
1089
|
}
|
|
1104
1090
|
return { kind: "object", fields: allFields, pos: first.pos };
|
|
1105
1091
|
}
|
|
1092
|
+
if (t.kind === "lbracket") {
|
|
1093
|
+
this.advance();
|
|
1094
|
+
const arr = this.parseArray();
|
|
1095
|
+
this.skip("newline");
|
|
1096
|
+
const remaining = this.peek();
|
|
1097
|
+
if (remaining.kind !== "eof") {
|
|
1098
|
+
throw new ParseError(`Unexpected token '${remaining.value}' after root array`, remaining.line, remaining.col);
|
|
1099
|
+
}
|
|
1100
|
+
return { ...arr, pos: { line: t.line, col: t.col } };
|
|
1101
|
+
}
|
|
1106
1102
|
return this.parseObject(false);
|
|
1107
1103
|
}
|
|
1108
1104
|
peek(offset = 0) {
|
|
@@ -1202,6 +1198,17 @@ var Parser = class {
|
|
|
1202
1198
|
this.advance();
|
|
1203
1199
|
const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
|
|
1204
1200
|
const parts = raw.split(".");
|
|
1201
|
+
const leadingDotIsSeparator = spaceConcat || postDotPrefix !== "";
|
|
1202
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1203
|
+
if (parts[i] !== "") continue;
|
|
1204
|
+
if (i === parts.length - 1) continue;
|
|
1205
|
+
if (i === 0 && leadingDotIsSeparator) continue;
|
|
1206
|
+
throw new ParseError(
|
|
1207
|
+
`path has ${i === 0 ? "a leading period '.'" : "two adjacent periods '.'"} \u2014 empty key segment not allowed, use quoted "" (HOCON.md path rules)`,
|
|
1208
|
+
t.line,
|
|
1209
|
+
t.col
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1205
1212
|
const filtered = parts.filter((s) => s.length > 0);
|
|
1206
1213
|
if (spaceConcat) {
|
|
1207
1214
|
segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}`;
|
|
@@ -1480,29 +1487,108 @@ function parseTokens(tokens) {
|
|
|
1480
1487
|
|
|
1481
1488
|
// src/internal/properties/properties.ts
|
|
1482
1489
|
function parseProperties(input) {
|
|
1483
|
-
const root = /* @__PURE__ */ Object.create(null);
|
|
1484
1490
|
const pairs = [];
|
|
1485
|
-
for (const line of input
|
|
1486
|
-
const
|
|
1487
|
-
|
|
1488
|
-
const sepIdx = findSeparator(trimmed);
|
|
1489
|
-
if (sepIdx === -1) continue;
|
|
1490
|
-
const key = trimmed.slice(0, sepIdx).trim();
|
|
1491
|
-
const value = trimmed.slice(sepIdx + 1).trim();
|
|
1491
|
+
for (const { text, line } of logicalLines(input)) {
|
|
1492
|
+
const [rawKey, rawValue] = splitKeyValue(text);
|
|
1493
|
+
const key = unescapeProps(rawKey, line);
|
|
1492
1494
|
if (key === "") continue;
|
|
1493
|
-
pairs.push([key,
|
|
1495
|
+
pairs.push([key, unescapeProps(rawValue, line)]);
|
|
1494
1496
|
}
|
|
1495
|
-
pairs
|
|
1496
|
-
|
|
1497
|
+
return nestPairs(pairs);
|
|
1498
|
+
}
|
|
1499
|
+
function nestPairs(pairs) {
|
|
1500
|
+
const root = /* @__PURE__ */ Object.create(null);
|
|
1501
|
+
const sorted = [...pairs].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0);
|
|
1502
|
+
for (const [key, value] of sorted) {
|
|
1497
1503
|
setNested(root, key.split("."), value);
|
|
1498
1504
|
}
|
|
1499
1505
|
return root;
|
|
1500
1506
|
}
|
|
1501
|
-
function
|
|
1502
|
-
|
|
1503
|
-
|
|
1507
|
+
function logicalLines(input) {
|
|
1508
|
+
const natural = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1509
|
+
const out = [];
|
|
1510
|
+
for (let i = 0; i < natural.length; i++) {
|
|
1511
|
+
let text = trimLeadingSpace(natural[i] ?? "");
|
|
1512
|
+
if (text === "" || text.startsWith("#") || text.startsWith("!")) continue;
|
|
1513
|
+
const start = i + 1;
|
|
1514
|
+
while (endsWithContinuation(text)) {
|
|
1515
|
+
text = text.slice(0, -1);
|
|
1516
|
+
if (i + 1 >= natural.length) break;
|
|
1517
|
+
i++;
|
|
1518
|
+
text += trimLeadingSpace(natural[i] ?? "");
|
|
1519
|
+
}
|
|
1520
|
+
out.push({ text, line: start });
|
|
1504
1521
|
}
|
|
1505
|
-
return
|
|
1522
|
+
return out;
|
|
1523
|
+
}
|
|
1524
|
+
function trimLeadingSpace(s) {
|
|
1525
|
+
return s.replace(/^[ \t\f]+/, "");
|
|
1526
|
+
}
|
|
1527
|
+
function endsWithContinuation(line) {
|
|
1528
|
+
let n = 0;
|
|
1529
|
+
for (let i = line.length - 1; i >= 0 && line[i] === "\\"; i--) n++;
|
|
1530
|
+
return n % 2 === 1;
|
|
1531
|
+
}
|
|
1532
|
+
function splitKeyValue(line) {
|
|
1533
|
+
let key = "";
|
|
1534
|
+
let i = 0;
|
|
1535
|
+
for (; i < line.length; i++) {
|
|
1536
|
+
const c = line[i];
|
|
1537
|
+
if (c === "\\" && i + 1 < line.length) {
|
|
1538
|
+
key += c + line[i + 1];
|
|
1539
|
+
i++;
|
|
1540
|
+
continue;
|
|
1541
|
+
}
|
|
1542
|
+
if (c === "=" || c === ":" || isPropsSpace(c)) break;
|
|
1543
|
+
key += c;
|
|
1544
|
+
}
|
|
1545
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1546
|
+
if (i < line.length && (line[i] === "=" || line[i] === ":")) {
|
|
1547
|
+
i++;
|
|
1548
|
+
while (i < line.length && isPropsSpace(line[i])) i++;
|
|
1549
|
+
}
|
|
1550
|
+
return [key, line.slice(i)];
|
|
1551
|
+
}
|
|
1552
|
+
function isPropsSpace(c) {
|
|
1553
|
+
return c === " " || c === " " || c === "\f";
|
|
1554
|
+
}
|
|
1555
|
+
function unescapeProps(s, line) {
|
|
1556
|
+
let out = "";
|
|
1557
|
+
for (let i = 0; i < s.length; i++) {
|
|
1558
|
+
if (s[i] !== "\\") {
|
|
1559
|
+
out += s[i];
|
|
1560
|
+
continue;
|
|
1561
|
+
}
|
|
1562
|
+
i++;
|
|
1563
|
+
if (i >= s.length) break;
|
|
1564
|
+
const e = s[i];
|
|
1565
|
+
switch (e) {
|
|
1566
|
+
case "t":
|
|
1567
|
+
out += " ";
|
|
1568
|
+
break;
|
|
1569
|
+
case "n":
|
|
1570
|
+
out += "\n";
|
|
1571
|
+
break;
|
|
1572
|
+
case "r":
|
|
1573
|
+
out += "\r";
|
|
1574
|
+
break;
|
|
1575
|
+
case "f":
|
|
1576
|
+
out += "\f";
|
|
1577
|
+
break;
|
|
1578
|
+
case "u": {
|
|
1579
|
+
const hex = s.slice(i + 1, i + 5);
|
|
1580
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
1581
|
+
throw new ParseError(`malformed \\u escape in .properties: \\u${hex}`, line, i + 1);
|
|
1582
|
+
}
|
|
1583
|
+
out += String.fromCharCode(parseInt(hex, 16));
|
|
1584
|
+
i += 4;
|
|
1585
|
+
break;
|
|
1586
|
+
}
|
|
1587
|
+
default:
|
|
1588
|
+
out += e;
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1591
|
+
return out;
|
|
1506
1592
|
}
|
|
1507
1593
|
var DANGEROUS_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1508
1594
|
function setNested(obj, segments, value) {
|
|
@@ -1541,6 +1627,16 @@ var _require = createRequire(import.meta.url);
|
|
|
1541
1627
|
function isPnpPath(p) {
|
|
1542
1628
|
return p.includes(".yarn/cache") || p.includes(".pnp.cjs") || p.includes(".pnp.loader");
|
|
1543
1629
|
}
|
|
1630
|
+
function assertObjectRootedInclude(ast, sourcePath) {
|
|
1631
|
+
if (ast.kind === "array") {
|
|
1632
|
+
throw new ResolveError(
|
|
1633
|
+
`included file has array at file root \u2014 an included file must contain an object, not an array (HOCON.md L993-994): ${sourcePath}`,
|
|
1634
|
+
sourcePath,
|
|
1635
|
+
ast.pos.line,
|
|
1636
|
+
ast.pos.col
|
|
1637
|
+
);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1544
1640
|
function validatePackageFile(file, identifier) {
|
|
1545
1641
|
if (file.length === 0) {
|
|
1546
1642
|
throw new ParseError(`include package("${identifier}", ...): file argument must be non-empty`, 0, 0);
|
|
@@ -1720,10 +1816,9 @@ var IncludeLoader = class {
|
|
|
1720
1816
|
const resolver = packageResolver ?? ((id, f, includingFile, bDir) => defaultPackageResolver(id, f, includingFile, bDir, resolveFrom));
|
|
1721
1817
|
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1722
1818
|
const content = readFileSync2(resolvedPath);
|
|
1723
|
-
if (content.length === 0) return makeResObj();
|
|
1724
1819
|
const tokens = tokenize(content);
|
|
1725
|
-
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1726
1820
|
const ast = parseTokens(tokens);
|
|
1821
|
+
assertObjectRootedInclude(ast, resolvedPath);
|
|
1727
1822
|
return this.onBuildResObj(ast, {
|
|
1728
1823
|
...this.opts,
|
|
1729
1824
|
baseDir: nodePath.dirname(resolvedPath),
|
|
@@ -1753,10 +1848,9 @@ var IncludeLoader = class {
|
|
|
1753
1848
|
const resolvedPath = resolver(identifier, file, void 0, baseDir);
|
|
1754
1849
|
const read = readFile ? async (p) => readFile(p) : async (p) => readFileSync2(p);
|
|
1755
1850
|
const content = await read(resolvedPath);
|
|
1756
|
-
if (content.length === 0) return makeResObj();
|
|
1757
1851
|
const tokens = tokenize(content);
|
|
1758
|
-
assertNonEmptyDocument(tokens, resolvedPath);
|
|
1759
1852
|
const ast = parseTokens(tokens);
|
|
1853
|
+
assertObjectRootedInclude(ast, resolvedPath);
|
|
1760
1854
|
return this.onBuildResObjAsync(ast, {
|
|
1761
1855
|
...this.opts,
|
|
1762
1856
|
baseDir: nodePath.dirname(resolvedPath),
|
|
@@ -1779,10 +1873,8 @@ var IncludeLoader = class {
|
|
|
1779
1873
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1780
1874
|
}
|
|
1781
1875
|
const tokens = tokenize(content);
|
|
1782
|
-
if (!hasContentTokens(tokens)) {
|
|
1783
|
-
return makeResObj();
|
|
1784
|
-
}
|
|
1785
1876
|
const ast = parseTokens(tokens);
|
|
1877
|
+
assertObjectRootedInclude(ast, candidate);
|
|
1786
1878
|
return this.onBuildResObj(ast, {
|
|
1787
1879
|
...this.opts,
|
|
1788
1880
|
baseDir: nodePath.dirname(candidate),
|
|
@@ -1806,10 +1898,8 @@ var IncludeLoader = class {
|
|
|
1806
1898
|
return hoconValueToResObj(propertiesToHoconValue(content));
|
|
1807
1899
|
}
|
|
1808
1900
|
const tokens = tokenize(content);
|
|
1809
|
-
if (!hasContentTokens(tokens)) {
|
|
1810
|
-
return makeResObj();
|
|
1811
|
-
}
|
|
1812
1901
|
const ast = parseTokens(tokens);
|
|
1902
|
+
assertObjectRootedInclude(ast, candidate);
|
|
1813
1903
|
return this.onBuildResObjAsync(ast, {
|
|
1814
1904
|
...this.opts,
|
|
1815
1905
|
baseDir: nodePath.dirname(candidate),
|
|
@@ -2986,8 +3076,14 @@ function defaultReadFileSync(filePath) {
|
|
|
2986
3076
|
}
|
|
2987
3077
|
function buildResolveContext(input, opts) {
|
|
2988
3078
|
const tokens = tokenize(input);
|
|
2989
|
-
assertNonEmptyDocument(tokens, "input");
|
|
2990
3079
|
const ast = parseTokens(tokens);
|
|
3080
|
+
if (ast.kind === "array") {
|
|
3081
|
+
const origin = opts.originDescription ?? "input";
|
|
3082
|
+
throw new ConfigError(
|
|
3083
|
+
`${origin}: ${ast.pos.line}:${ast.pos.col}: document has type array rather than object at file root (HOCON.md L989-991); the Config API requires an object root`,
|
|
3084
|
+
""
|
|
3085
|
+
);
|
|
3086
|
+
}
|
|
2991
3087
|
const resolveOpts = {
|
|
2992
3088
|
env: getEnv(opts),
|
|
2993
3089
|
baseDir: opts.baseDir,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@o3co/ts.hocon",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Full Lightbend HOCON spec-compliant parser and config library for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"hocon",
|
|
@@ -21,6 +21,31 @@
|
|
|
21
21
|
"types": "./dist/zod.d.ts",
|
|
22
22
|
"import": "./dist/zod.js",
|
|
23
23
|
"require": "./dist/zod.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./adapters/properties": {
|
|
26
|
+
"types": "./dist/adapters/properties.d.ts",
|
|
27
|
+
"import": "./dist/adapters/properties.js",
|
|
28
|
+
"require": "./dist/adapters/properties.cjs"
|
|
29
|
+
},
|
|
30
|
+
"./adapters/env": {
|
|
31
|
+
"types": "./dist/adapters/env.d.ts",
|
|
32
|
+
"import": "./dist/adapters/env.js",
|
|
33
|
+
"require": "./dist/adapters/env.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./adapters/jsonc": {
|
|
36
|
+
"types": "./dist/adapters/jsonc.d.ts",
|
|
37
|
+
"import": "./dist/adapters/jsonc.js",
|
|
38
|
+
"require": "./dist/adapters/jsonc.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./adapters/toml": {
|
|
41
|
+
"types": "./dist/adapters/toml.d.ts",
|
|
42
|
+
"import": "./dist/adapters/toml.js",
|
|
43
|
+
"require": "./dist/adapters/toml.cjs"
|
|
44
|
+
},
|
|
45
|
+
"./adapters/yaml": {
|
|
46
|
+
"types": "./dist/adapters/yaml.d.ts",
|
|
47
|
+
"import": "./dist/adapters/yaml.js",
|
|
48
|
+
"require": "./dist/adapters/yaml.cjs"
|
|
24
49
|
}
|
|
25
50
|
},
|
|
26
51
|
"main": "./dist/index.cjs",
|
|
@@ -30,27 +55,37 @@
|
|
|
30
55
|
"dist"
|
|
31
56
|
],
|
|
32
57
|
"peerDependencies": {
|
|
33
|
-
"zod": ">=4.0.0"
|
|
58
|
+
"zod": ">=4.0.0",
|
|
59
|
+
"smol-toml": ">=1.0.0",
|
|
60
|
+
"yaml": ">=2.0.0"
|
|
34
61
|
},
|
|
35
62
|
"peerDependenciesMeta": {
|
|
36
63
|
"zod": {
|
|
37
64
|
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"smol-toml": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"yaml": {
|
|
70
|
+
"optional": true
|
|
38
71
|
}
|
|
39
72
|
},
|
|
40
73
|
"devDependencies": {
|
|
41
74
|
"@types/js-yaml": "^4.0.9",
|
|
42
|
-
"@types/node": "^25.5
|
|
43
|
-
"@vitest/coverage-v8": "4.1.
|
|
44
|
-
"js-yaml": "^4.
|
|
75
|
+
"@types/node": "^25.9.5",
|
|
76
|
+
"@vitest/coverage-v8": "4.1.10",
|
|
77
|
+
"js-yaml": "^4.3.0",
|
|
78
|
+
"smol-toml": "^1.7.0",
|
|
45
79
|
"tsup": "^8.5.1",
|
|
46
|
-
"typedoc": "^0.28.
|
|
80
|
+
"typedoc": "^0.28.20",
|
|
47
81
|
"typescript": "^5.9.3",
|
|
48
|
-
"vitest": "4.1.
|
|
49
|
-
"
|
|
82
|
+
"vitest": "4.1.10",
|
|
83
|
+
"yaml": "^2.9.0",
|
|
84
|
+
"zod": "^4.4.3"
|
|
50
85
|
},
|
|
51
86
|
"repository": {
|
|
52
87
|
"type": "git",
|
|
53
|
-
"url": "https://github.com/o3co/ts.hocon"
|
|
88
|
+
"url": "git+https://github.com/o3co/ts.hocon.git"
|
|
54
89
|
},
|
|
55
90
|
"license": "Apache-2.0",
|
|
56
91
|
"engines": {
|
|
@@ -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. */
|