@o3co/ts.hocon 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +129 -1
- package/README.md +90 -2
- package/dist/adapters/env.cjs +98 -28
- package/dist/adapters/env.d.cts +16 -1
- package/dist/adapters/env.d.ts +16 -1
- package/dist/adapters/env.js +98 -27
- package/dist/adapters/jsonc.cjs +120 -17
- package/dist/adapters/jsonc.d.cts +24 -4
- package/dist/adapters/jsonc.d.ts +24 -4
- package/dist/adapters/jsonc.js +120 -16
- package/dist/adapters/properties.cjs +87 -24
- package/dist/adapters/properties.d.cts +6 -1
- package/dist/adapters/properties.d.ts +6 -1
- package/dist/adapters/properties.js +87 -23
- package/dist/adapters/toml.cjs +79 -20
- package/dist/adapters/toml.d.cts +1 -1
- package/dist/adapters/toml.d.ts +1 -1
- package/dist/adapters/toml.js +79 -19
- package/dist/adapters/yaml.cjs +100 -24
- package/dist/adapters/yaml.d.cts +10 -2
- package/dist/adapters/yaml.d.ts +10 -2
- package/dist/adapters/yaml.js +100 -23
- package/dist/{config-PdhVgeLS.d.cts → config-Dlq6rF3w.d.cts} +47 -1
- package/dist/{config-PdhVgeLS.d.ts → config-Dlq6rF3w.d.ts} +47 -1
- package/dist/index.cjs +104 -22
- package/dist/index.d.cts +10 -3
- package/dist/index.d.ts +10 -3
- package/dist/index.js +111 -22
- package/dist/zod.cjs +100 -20
- package/dist/zod.d.cts +1 -1
- package/dist/zod.d.ts +1 -1
- package/dist/zod.js +107 -20
- package/package.json +58 -22
package/dist/adapters/env.js
CHANGED
|
@@ -513,36 +513,48 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
513
513
|
// src/internal/resolver/include-loader.ts
|
|
514
514
|
import { createRequire } from "module";
|
|
515
515
|
import * as nodePath from "path";
|
|
516
|
+
import { pathToFileURL } from "url";
|
|
517
|
+
|
|
518
|
+
// src/internal/strip-bom.ts
|
|
519
|
+
function stripBom(input) {
|
|
520
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
521
|
+
}
|
|
516
522
|
|
|
517
523
|
// src/internal/properties/properties.ts
|
|
518
524
|
function nestPairs(pairs) {
|
|
519
525
|
const root = /* @__PURE__ */ Object.create(null);
|
|
520
|
-
const sorted = [...pairs].sort(([a], [b]) => a
|
|
521
|
-
for (const [
|
|
522
|
-
setNested(root,
|
|
526
|
+
const sorted = [...pairs].sort(([a], [b]) => compareSegments(a, b));
|
|
527
|
+
for (const [segments, value] of sorted) {
|
|
528
|
+
setNested(root, segments, value);
|
|
523
529
|
}
|
|
524
530
|
return root;
|
|
525
531
|
}
|
|
526
|
-
|
|
532
|
+
function compareSegments(a, b) {
|
|
533
|
+
const n = Math.min(a.length, b.length);
|
|
534
|
+
for (let i = 0; i < n; i++) {
|
|
535
|
+
const x = a[i];
|
|
536
|
+
const y = b[i];
|
|
537
|
+
if (x !== y) return x < y ? -1 : 1;
|
|
538
|
+
}
|
|
539
|
+
return a.length - b.length;
|
|
540
|
+
}
|
|
541
|
+
function pathKey(segments) {
|
|
542
|
+
return JSON.stringify(segments);
|
|
543
|
+
}
|
|
527
544
|
function setNested(obj, segments, value) {
|
|
528
545
|
let current = obj;
|
|
529
546
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
530
547
|
const seg = segments[i];
|
|
531
|
-
if (seg === void 0 || DANGEROUS_KEYS.has(seg)) return;
|
|
532
548
|
if (!(seg in current) || typeof current[seg] !== "object" || current[seg] === null) {
|
|
533
549
|
current[seg] = /* @__PURE__ */ Object.create(null);
|
|
534
550
|
}
|
|
535
551
|
current = current[seg];
|
|
536
552
|
}
|
|
537
553
|
const last = segments[segments.length - 1];
|
|
538
|
-
if (last === void 0 || DANGEROUS_KEYS.has(last)) return;
|
|
539
554
|
if (typeof current[last] === "object" && current[last] !== null) return;
|
|
540
555
|
current[last] = value;
|
|
541
556
|
}
|
|
542
557
|
|
|
543
|
-
// src/internal/resolver/include-loader.ts
|
|
544
|
-
var _require = createRequire(import.meta.url);
|
|
545
|
-
|
|
546
558
|
// src/value/numeric-array.ts
|
|
547
559
|
function numericObjectToArray(value) {
|
|
548
560
|
if (value.kind !== "object") return null;
|
|
@@ -1130,10 +1142,10 @@ var Config = class _Config {
|
|
|
1130
1142
|
}
|
|
1131
1143
|
if (v.kind === "object") {
|
|
1132
1144
|
const converted = numericObjectToArray(v);
|
|
1133
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
1145
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
1134
1146
|
}
|
|
1135
1147
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
|
|
1136
|
-
return v.items.map(hoconToJs);
|
|
1148
|
+
return v.items.map((item) => hoconToJs(item));
|
|
1137
1149
|
}
|
|
1138
1150
|
has(path) {
|
|
1139
1151
|
return this.lookupNode(path) !== void 0;
|
|
@@ -1261,8 +1273,52 @@ var Config = class _Config {
|
|
|
1261
1273
|
originDescription: this._originDescription
|
|
1262
1274
|
});
|
|
1263
1275
|
}
|
|
1264
|
-
|
|
1265
|
-
|
|
1276
|
+
/**
|
|
1277
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
1278
|
+
* booleans and `null`.
|
|
1279
|
+
*
|
|
1280
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
1281
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
1282
|
+
* property**, so the returned object's prototype is always
|
|
1283
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
1284
|
+
*
|
|
1285
|
+
* ### Passing the result to other code
|
|
1286
|
+
*
|
|
1287
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
1288
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
1289
|
+
* next depends entirely on how the consumer copies it:
|
|
1290
|
+
*
|
|
1291
|
+
* ```ts
|
|
1292
|
+
* const data = cfg.toObject()
|
|
1293
|
+
* { ...data } // safe — own properties are copied as data
|
|
1294
|
+
* structuredClone(data) // safe
|
|
1295
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
1296
|
+
* // Object.prototype's __proto__ setter: the key
|
|
1297
|
+
* // vanishes and config data becomes the result's
|
|
1298
|
+
* // prototype
|
|
1299
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
1300
|
+
* // target['__proto__'], gets Object.prototype,
|
|
1301
|
+
* // and writes to it: global pollution
|
|
1302
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
1303
|
+
* ```
|
|
1304
|
+
*
|
|
1305
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
1306
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
1307
|
+
*
|
|
1308
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
1309
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
1310
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
1311
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
1312
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
1313
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
1314
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
1315
|
+
*
|
|
1316
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
1317
|
+
* here even when the config holds it exactly — read those with
|
|
1318
|
+
* {@link Config.getString} instead.
|
|
1319
|
+
*/
|
|
1320
|
+
toObject(opts) {
|
|
1321
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
1266
1322
|
}
|
|
1267
1323
|
/**
|
|
1268
1324
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -1374,16 +1430,25 @@ function scalarToJs(raw, valueType) {
|
|
|
1374
1430
|
return raw;
|
|
1375
1431
|
}
|
|
1376
1432
|
}
|
|
1377
|
-
function hoconToJs(v) {
|
|
1433
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
1378
1434
|
switch (v.kind) {
|
|
1379
1435
|
case "scalar":
|
|
1380
1436
|
return scalarToJs(v.raw, v.valueType);
|
|
1381
1437
|
case "array":
|
|
1382
|
-
return v.items.map(hoconToJs);
|
|
1438
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
1383
1439
|
case "object": {
|
|
1384
|
-
const
|
|
1385
|
-
|
|
1386
|
-
|
|
1440
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
1441
|
+
if (nullPrototype) {
|
|
1442
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1443
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
1444
|
+
value: val,
|
|
1445
|
+
writable: true,
|
|
1446
|
+
enumerable: true,
|
|
1447
|
+
configurable: true
|
|
1448
|
+
});
|
|
1449
|
+
return obj;
|
|
1450
|
+
}
|
|
1451
|
+
return Object.fromEntries(entries);
|
|
1387
1452
|
}
|
|
1388
1453
|
}
|
|
1389
1454
|
}
|
|
@@ -1472,6 +1537,8 @@ function fromMap(values, originDescription) {
|
|
|
1472
1537
|
const root = { kind: "object", fields };
|
|
1473
1538
|
return Config._fromResolvedValue(root, { originDescription });
|
|
1474
1539
|
}
|
|
1540
|
+
var INT64_MAX = 9223372036854775807n;
|
|
1541
|
+
var INT64_MIN = -9223372036854775808n;
|
|
1475
1542
|
function coerceObject(obj, atPath) {
|
|
1476
1543
|
const fields = /* @__PURE__ */ new Map();
|
|
1477
1544
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -1500,14 +1567,14 @@ function coerceValue(v, atPath) {
|
|
|
1500
1567
|
atPath
|
|
1501
1568
|
);
|
|
1502
1569
|
}
|
|
1503
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
1570
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
1504
1571
|
}
|
|
1505
1572
|
case "boolean":
|
|
1506
1573
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
1507
1574
|
case "bigint": {
|
|
1508
|
-
if (v >
|
|
1575
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1509
1576
|
throw new ConfigError(
|
|
1510
|
-
`fromMap:
|
|
1577
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
1511
1578
|
atPath
|
|
1512
1579
|
);
|
|
1513
1580
|
}
|
|
@@ -1548,11 +1615,12 @@ function loadEnv(opts = {}) {
|
|
|
1548
1615
|
const value = source[name];
|
|
1549
1616
|
if (value === void 0) continue;
|
|
1550
1617
|
const path = toPath(name.slice(prefix.length));
|
|
1551
|
-
const
|
|
1618
|
+
const key = pathKey(path);
|
|
1619
|
+
const prev = seen.get(key);
|
|
1552
1620
|
if (prev !== void 0) {
|
|
1553
|
-
throw new ConfigError(`loadEnv: ${prev} and ${name} both map to "${path}"`, path);
|
|
1621
|
+
throw new ConfigError(`loadEnv: ${prev} and ${name} both map to "${path.join(".")}"`, path.join("."));
|
|
1554
1622
|
}
|
|
1555
|
-
seen.set(
|
|
1623
|
+
seen.set(key, name);
|
|
1556
1624
|
pairs.push([path, value]);
|
|
1557
1625
|
}
|
|
1558
1626
|
return fromMap(nestPairs(pairs), opts.originDescription ?? "environment variables");
|
|
@@ -1561,7 +1629,7 @@ function parseDotEnv(input, opts = {}) {
|
|
|
1561
1629
|
const origin = opts.originDescription ?? ".env";
|
|
1562
1630
|
const prefix = opts.prefix ?? "";
|
|
1563
1631
|
const pairs = [];
|
|
1564
|
-
const lines = input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1632
|
+
const lines = stripBom(input).replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
1565
1633
|
for (let i = 0; i < lines.length; i++) {
|
|
1566
1634
|
const raw = (lines[i] ?? "").trim();
|
|
1567
1635
|
if (raw === "" || raw.startsWith("#")) continue;
|
|
@@ -1577,11 +1645,14 @@ function parseDotEnv(input, opts = {}) {
|
|
|
1577
1645
|
return fromMap(nestPairs(pairs), origin);
|
|
1578
1646
|
}
|
|
1579
1647
|
function toPath(name) {
|
|
1580
|
-
const segs = name.split(SEPARATOR).map(
|
|
1648
|
+
const segs = name.split(SEPARATOR).map(asciiLower);
|
|
1581
1649
|
if (segs.some((s) => s === "")) {
|
|
1582
1650
|
throw new ConfigError(`env: "${name}" produces an empty path segment`, name);
|
|
1583
1651
|
}
|
|
1584
|
-
return segs
|
|
1652
|
+
return segs;
|
|
1653
|
+
}
|
|
1654
|
+
function asciiLower(s) {
|
|
1655
|
+
return s.replace(/[A-Z]/g, (c) => String.fromCharCode(c.charCodeAt(0) + 32));
|
|
1585
1656
|
}
|
|
1586
1657
|
function dotEnvValue(v, origin, line, name) {
|
|
1587
1658
|
const fail = (msg) => {
|
package/dist/adapters/jsonc.cjs
CHANGED
|
@@ -550,8 +550,12 @@ function deepMergeHoconValues(base, overlay) {
|
|
|
550
550
|
// src/internal/resolver/include-loader.ts
|
|
551
551
|
var import_node_module = require("module");
|
|
552
552
|
var nodePath = __toESM(require("path"), 1);
|
|
553
|
-
var
|
|
554
|
-
|
|
553
|
+
var import_node_url = require("url");
|
|
554
|
+
|
|
555
|
+
// src/internal/strip-bom.ts
|
|
556
|
+
function stripBom(input) {
|
|
557
|
+
return input.charCodeAt(0) === 65279 ? input.slice(1) : input;
|
|
558
|
+
}
|
|
555
559
|
|
|
556
560
|
// src/value/numeric-array.ts
|
|
557
561
|
function numericObjectToArray(value) {
|
|
@@ -1140,10 +1144,10 @@ var Config = class _Config {
|
|
|
1140
1144
|
}
|
|
1141
1145
|
if (v.kind === "object") {
|
|
1142
1146
|
const converted = numericObjectToArray(v);
|
|
1143
|
-
if (converted !== null) return converted.map(hoconToJs);
|
|
1147
|
+
if (converted !== null) return converted.map((item) => hoconToJs(item));
|
|
1144
1148
|
}
|
|
1145
1149
|
if (v.kind !== "array") throw new ConfigError(`expected array at ${path}`, path);
|
|
1146
|
-
return v.items.map(hoconToJs);
|
|
1150
|
+
return v.items.map((item) => hoconToJs(item));
|
|
1147
1151
|
}
|
|
1148
1152
|
has(path) {
|
|
1149
1153
|
return this.lookupNode(path) !== void 0;
|
|
@@ -1271,8 +1275,52 @@ var Config = class _Config {
|
|
|
1271
1275
|
originDescription: this._originDescription
|
|
1272
1276
|
});
|
|
1273
1277
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1278
|
+
/**
|
|
1279
|
+
* The whole config as plain JS data: objects, arrays, strings, numbers,
|
|
1280
|
+
* booleans and `null`.
|
|
1281
|
+
*
|
|
1282
|
+
* Keys are reproduced exactly as the config carries them, `__proto__`,
|
|
1283
|
+
* `constructor` and `prototype` included: each is defined as an **own data
|
|
1284
|
+
* property**, so the returned object's prototype is always
|
|
1285
|
+
* `Object.prototype`, and this call writes nothing to the global one.
|
|
1286
|
+
*
|
|
1287
|
+
* ### Passing the result to other code
|
|
1288
|
+
*
|
|
1289
|
+
* A config file — especially a `.properties` file or an env namespace you
|
|
1290
|
+
* merged in — can therefore hand you an own `__proto__` key, and what happens
|
|
1291
|
+
* next depends entirely on how the consumer copies it:
|
|
1292
|
+
*
|
|
1293
|
+
* ```ts
|
|
1294
|
+
* const data = cfg.toObject()
|
|
1295
|
+
* { ...data } // safe — own properties are copied as data
|
|
1296
|
+
* structuredClone(data) // safe
|
|
1297
|
+
* Object.assign({}, data) // NOT safe — [[Set]] on the {} target hits
|
|
1298
|
+
* // Object.prototype's __proto__ setter: the key
|
|
1299
|
+
* // vanishes and config data becomes the result's
|
|
1300
|
+
* // prototype
|
|
1301
|
+
* deepMerge({}, data) // NOT safe — a naive recursive merge reads
|
|
1302
|
+
* // target['__proto__'], gets Object.prototype,
|
|
1303
|
+
* // and writes to it: global pollution
|
|
1304
|
+
* for (const k in data) … // prefer Object.entries / Object.keys
|
|
1305
|
+
* ```
|
|
1306
|
+
*
|
|
1307
|
+
* Those two hazards come from the **destination** object's prototype, so no
|
|
1308
|
+
* option here can disarm them — copy with spread or `structuredClone` instead.
|
|
1309
|
+
*
|
|
1310
|
+
* What `{ nullPrototype: true }` does give you is a result that inherits
|
|
1311
|
+
* nothing: every object comes back with a `null` prototype, so reading
|
|
1312
|
+
* `x.__proto__`, `x.constructor` or `x.toString` yields config data or
|
|
1313
|
+
* `undefined` rather than something from `Object.prototype`, and code that
|
|
1314
|
+
* merges *into* this object cannot climb out of it. Keys are all still
|
|
1315
|
+
* present. The trade-off is the usual one for prototype-less objects: not
|
|
1316
|
+
* `instanceof Object`, and no `hasOwnProperty` of their own.
|
|
1317
|
+
*
|
|
1318
|
+
* Numbers follow the JS number model, so an integer wider than 2^53 rounds
|
|
1319
|
+
* here even when the config holds it exactly — read those with
|
|
1320
|
+
* {@link Config.getString} instead.
|
|
1321
|
+
*/
|
|
1322
|
+
toObject(opts) {
|
|
1323
|
+
return hoconToJs(this.root, opts?.nullPrototype === true);
|
|
1276
1324
|
}
|
|
1277
1325
|
/**
|
|
1278
1326
|
* @internal Test-only: renders this resolved Config's value tree as canonical
|
|
@@ -1384,16 +1432,25 @@ function scalarToJs(raw, valueType) {
|
|
|
1384
1432
|
return raw;
|
|
1385
1433
|
}
|
|
1386
1434
|
}
|
|
1387
|
-
function hoconToJs(v) {
|
|
1435
|
+
function hoconToJs(v, nullPrototype = false) {
|
|
1388
1436
|
switch (v.kind) {
|
|
1389
1437
|
case "scalar":
|
|
1390
1438
|
return scalarToJs(v.raw, v.valueType);
|
|
1391
1439
|
case "array":
|
|
1392
|
-
return v.items.map(hoconToJs);
|
|
1440
|
+
return v.items.map((item) => hoconToJs(item, nullPrototype));
|
|
1393
1441
|
case "object": {
|
|
1394
|
-
const
|
|
1395
|
-
|
|
1396
|
-
|
|
1442
|
+
const entries = Array.from(v.fields, ([k, val]) => [k, hoconToJs(val, nullPrototype)]);
|
|
1443
|
+
if (nullPrototype) {
|
|
1444
|
+
const obj = /* @__PURE__ */ Object.create(null);
|
|
1445
|
+
for (const [k, val] of entries) Object.defineProperty(obj, k, {
|
|
1446
|
+
value: val,
|
|
1447
|
+
writable: true,
|
|
1448
|
+
enumerable: true,
|
|
1449
|
+
configurable: true
|
|
1450
|
+
});
|
|
1451
|
+
return obj;
|
|
1452
|
+
}
|
|
1453
|
+
return Object.fromEntries(entries);
|
|
1397
1454
|
}
|
|
1398
1455
|
}
|
|
1399
1456
|
}
|
|
@@ -1482,6 +1539,8 @@ function fromMap(values, originDescription) {
|
|
|
1482
1539
|
const root = { kind: "object", fields };
|
|
1483
1540
|
return Config._fromResolvedValue(root, { originDescription });
|
|
1484
1541
|
}
|
|
1542
|
+
var INT64_MAX = 9223372036854775807n;
|
|
1543
|
+
var INT64_MIN = -9223372036854775808n;
|
|
1485
1544
|
function coerceObject(obj, atPath) {
|
|
1486
1545
|
const fields = /* @__PURE__ */ new Map();
|
|
1487
1546
|
for (const [k, v] of Object.entries(obj)) {
|
|
@@ -1510,14 +1569,14 @@ function coerceValue(v, atPath) {
|
|
|
1510
1569
|
atPath
|
|
1511
1570
|
);
|
|
1512
1571
|
}
|
|
1513
|
-
return { kind: "scalar", raw: String(v), valueType: "number" };
|
|
1572
|
+
return { kind: "scalar", raw: Object.is(v, -0) ? "-0" : String(v), valueType: "number" };
|
|
1514
1573
|
}
|
|
1515
1574
|
case "boolean":
|
|
1516
1575
|
return { kind: "scalar", raw: v ? "true" : "false", valueType: "boolean" };
|
|
1517
1576
|
case "bigint": {
|
|
1518
|
-
if (v >
|
|
1577
|
+
if (v > INT64_MAX || v < INT64_MIN) {
|
|
1519
1578
|
throw new ConfigError(
|
|
1520
|
-
`fromMap:
|
|
1579
|
+
`fromMap: integer ${v} is out of int64 range (HOCON integers are int64; spec F0.5) at path "${atPath}"`,
|
|
1521
1580
|
atPath
|
|
1522
1581
|
);
|
|
1523
1582
|
}
|
|
@@ -1541,7 +1600,7 @@ function coerceValue(v, atPath) {
|
|
|
1541
1600
|
|
|
1542
1601
|
// src/adapters/jsonc.ts
|
|
1543
1602
|
function parseJsonc(input, originDescription) {
|
|
1544
|
-
const doc = JSON.parse(stripTrailingCommas(stripComments(input)));
|
|
1603
|
+
const doc = JSON.parse(stripTrailingCommas(stripComments(stripBom(input))), bigIntReviver);
|
|
1545
1604
|
if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
|
|
1546
1605
|
throw new ConfigError(
|
|
1547
1606
|
`jsonc: document root is ${Array.isArray(doc) ? "an array" : typeof doc}, but a config root must be an object (spec F0.3)`,
|
|
@@ -1550,6 +1609,50 @@ function parseJsonc(input, originDescription) {
|
|
|
1550
1609
|
}
|
|
1551
1610
|
return fromMap(doc, originDescription);
|
|
1552
1611
|
}
|
|
1612
|
+
function isIntegerLiteral(source) {
|
|
1613
|
+
return !/[.eE]/.test(source);
|
|
1614
|
+
}
|
|
1615
|
+
var HAS_SOURCE_TEXT_ACCESS = (() => {
|
|
1616
|
+
let seen;
|
|
1617
|
+
JSON.parse("1", function(_k, v, context) {
|
|
1618
|
+
seen = context?.source;
|
|
1619
|
+
return v;
|
|
1620
|
+
});
|
|
1621
|
+
return seen !== void 0;
|
|
1622
|
+
})();
|
|
1623
|
+
function bigIntReviver(key, value, context) {
|
|
1624
|
+
if (typeof value !== "number" || Number.isSafeInteger(value)) return value;
|
|
1625
|
+
const source = context?.source;
|
|
1626
|
+
if (source === void 0) {
|
|
1627
|
+
if (!Number.isInteger(value)) return value;
|
|
1628
|
+
throw new ConfigError(
|
|
1629
|
+
`jsonc: the integer at "${key || "<root>"}" needs more precision than a JS number holds, and this runtime does not expose JSON.parse source text (${HAS_SOURCE_TEXT_ACCESS ? "unexpected \u2014 the capability probe said otherwise" : "requires Node >= 22 or an engine with JSON.parse source access"}), so it cannot be read losslessly (spec F0.5)`,
|
|
1630
|
+
key
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
if (!isIntegerLiteral(source)) return value;
|
|
1634
|
+
return BigInt(source);
|
|
1635
|
+
}
|
|
1636
|
+
function isLineBreak(c) {
|
|
1637
|
+
return c === "\n" || c === "\r";
|
|
1638
|
+
}
|
|
1639
|
+
function lineBreaksOf(span) {
|
|
1640
|
+
let out = "";
|
|
1641
|
+
for (let i = 0; i < span.length; i++) {
|
|
1642
|
+
const c = span[i];
|
|
1643
|
+
if (c === "\r") {
|
|
1644
|
+
if (span[i + 1] === "\n") {
|
|
1645
|
+
out += "\r\n";
|
|
1646
|
+
i++;
|
|
1647
|
+
} else {
|
|
1648
|
+
out += "\r";
|
|
1649
|
+
}
|
|
1650
|
+
} else if (c === "\n") {
|
|
1651
|
+
out += "\n";
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return out;
|
|
1655
|
+
}
|
|
1553
1656
|
function stripComments(src) {
|
|
1554
1657
|
let out = "";
|
|
1555
1658
|
for (let i = 0; i < src.length; ) {
|
|
@@ -1561,13 +1664,13 @@ function stripComments(src) {
|
|
|
1561
1664
|
continue;
|
|
1562
1665
|
}
|
|
1563
1666
|
if (c === "/" && src[i + 1] === "/") {
|
|
1564
|
-
while (i < src.length && src[i]
|
|
1667
|
+
while (i < src.length && !isLineBreak(src[i])) i++;
|
|
1565
1668
|
continue;
|
|
1566
1669
|
}
|
|
1567
1670
|
if (c === "/" && src[i + 1] === "*") {
|
|
1568
1671
|
const end = src.indexOf("*/", i + 2);
|
|
1569
1672
|
if (end === -1) throw new ConfigError("jsonc: unterminated block comment", "");
|
|
1570
|
-
|
|
1673
|
+
out += lineBreaksOf(src.slice(i, end + 2)) || " ";
|
|
1571
1674
|
i = end + 2;
|
|
1572
1675
|
continue;
|
|
1573
1676
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Config } from '../config-
|
|
1
|
+
import { C as Config } from '../config-Dlq6rF3w.cjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Read JSON with comments and trailing commas — the dialect VS Code and
|
|
@@ -10,15 +10,35 @@ import { C as Config } from '../config-PdhVgeLS.cjs';
|
|
|
10
10
|
* of its own.)
|
|
11
11
|
*
|
|
12
12
|
* Comments and trailing commas are removed and `JSON.parse` does the rest, so
|
|
13
|
-
* the accepted grammar is otherwise exactly the platform's JSON.
|
|
13
|
+
* the accepted grammar is otherwise exactly the platform's JSON. A comment is
|
|
14
|
+
* replaced by whitespace rather than deleted, so it separates the tokens around
|
|
15
|
+
* it and `{"a":1/*x*\/2}` is a syntax error rather than `{"a":12}` (F3.2).
|
|
16
|
+
*
|
|
17
|
+
* Integers are ingested losslessly (F0.5): a literal too wide for a JS `number`
|
|
18
|
+
* keeps its digits — `getString` returns them exactly — and one outside int64
|
|
19
|
+
* is refused instead of being rounded. `getNumber` and `toObject` still apply
|
|
20
|
+
* the JS number model, as they do for HOCON's own literals, so read large
|
|
21
|
+
* identifiers with `getString`.
|
|
14
22
|
*
|
|
15
23
|
* See docs/specs/format-ingestion-mapping.md items F3.x in the hocon scope.
|
|
16
24
|
*/
|
|
17
25
|
declare function parseJsonc(input: string, originDescription?: string): Config;
|
|
18
26
|
/**
|
|
19
27
|
* Remove `//` line comments and block comments, leaving string literals alone.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
28
|
+
*
|
|
29
|
+
* F3.2: a comment is replaced by *whitespace*, never the empty string — it must
|
|
30
|
+
* keep separating the tokens around it, so a `1`, a block comment and a `2` stay
|
|
31
|
+
* two tokens and fail the JSON decode instead of silently reading as `12`.
|
|
32
|
+
* Newlines inside a removed span are kept so JSON.parse still reports useful
|
|
33
|
+
* positions; a span with no newline becomes a single space.
|
|
34
|
+
*
|
|
35
|
+
* A `//` comment ends at **LF or CR** (F3.2), and at nothing else. Stopping only
|
|
36
|
+
* at LF let a CR-terminated comment eat the rest of the line, and the
|
|
37
|
+
* trailing-comma pass then tidied the remains into valid JSON — so
|
|
38
|
+
* `{"a":1,//c\r"b":2,\n"c":3}` decoded as `{"a":1,"c":3}`, losing a key with no
|
|
39
|
+
* error at all. Stopping at U+2028/U+2029 would be the mirror-image mistake:
|
|
40
|
+
* ending a comment the dialect's own parser is still inside, so the document
|
|
41
|
+
* would mean something different here than in VS Code. See {@link isLineBreak}.
|
|
22
42
|
*/
|
|
23
43
|
declare function stripComments(src: string): string;
|
|
24
44
|
|
package/dist/adapters/jsonc.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as Config } from '../config-
|
|
1
|
+
import { C as Config } from '../config-Dlq6rF3w.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Read JSON with comments and trailing commas — the dialect VS Code and
|
|
@@ -10,15 +10,35 @@ import { C as Config } from '../config-PdhVgeLS.js';
|
|
|
10
10
|
* of its own.)
|
|
11
11
|
*
|
|
12
12
|
* Comments and trailing commas are removed and `JSON.parse` does the rest, so
|
|
13
|
-
* the accepted grammar is otherwise exactly the platform's JSON.
|
|
13
|
+
* the accepted grammar is otherwise exactly the platform's JSON. A comment is
|
|
14
|
+
* replaced by whitespace rather than deleted, so it separates the tokens around
|
|
15
|
+
* it and `{"a":1/*x*\/2}` is a syntax error rather than `{"a":12}` (F3.2).
|
|
16
|
+
*
|
|
17
|
+
* Integers are ingested losslessly (F0.5): a literal too wide for a JS `number`
|
|
18
|
+
* keeps its digits — `getString` returns them exactly — and one outside int64
|
|
19
|
+
* is refused instead of being rounded. `getNumber` and `toObject` still apply
|
|
20
|
+
* the JS number model, as they do for HOCON's own literals, so read large
|
|
21
|
+
* identifiers with `getString`.
|
|
14
22
|
*
|
|
15
23
|
* See docs/specs/format-ingestion-mapping.md items F3.x in the hocon scope.
|
|
16
24
|
*/
|
|
17
25
|
declare function parseJsonc(input: string, originDescription?: string): Config;
|
|
18
26
|
/**
|
|
19
27
|
* Remove `//` line comments and block comments, leaving string literals alone.
|
|
20
|
-
*
|
|
21
|
-
*
|
|
28
|
+
*
|
|
29
|
+
* F3.2: a comment is replaced by *whitespace*, never the empty string — it must
|
|
30
|
+
* keep separating the tokens around it, so a `1`, a block comment and a `2` stay
|
|
31
|
+
* two tokens and fail the JSON decode instead of silently reading as `12`.
|
|
32
|
+
* Newlines inside a removed span are kept so JSON.parse still reports useful
|
|
33
|
+
* positions; a span with no newline becomes a single space.
|
|
34
|
+
*
|
|
35
|
+
* A `//` comment ends at **LF or CR** (F3.2), and at nothing else. Stopping only
|
|
36
|
+
* at LF let a CR-terminated comment eat the rest of the line, and the
|
|
37
|
+
* trailing-comma pass then tidied the remains into valid JSON — so
|
|
38
|
+
* `{"a":1,//c\r"b":2,\n"c":3}` decoded as `{"a":1,"c":3}`, losing a key with no
|
|
39
|
+
* error at all. Stopping at U+2028/U+2029 would be the mirror-image mistake:
|
|
40
|
+
* ending a comment the dialect's own parser is still inside, so the document
|
|
41
|
+
* would mean something different here than in VS Code. See {@link isLineBreak}.
|
|
22
42
|
*/
|
|
23
43
|
declare function stripComments(src: string): string;
|
|
24
44
|
|