@o3co/ts.hocon 1.5.2 → 1.6.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/dist/zod.cjs CHANGED
@@ -342,7 +342,7 @@ function stringSegmentsToKey(segments) {
342
342
  }).join(".");
343
343
  }
344
344
  function containsSelfRef(v, fullKey) {
345
- if (isSubst(v)) return substFullKey(v) === fullKey;
345
+ if (isSubst(v)) return !v.knownAbsent && substFullKey(v) === fullKey;
346
346
  if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
347
347
  if (isAppend(v)) {
348
348
  return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
@@ -409,15 +409,75 @@ function foldSelfRef(v, fullKey, replacement) {
409
409
  }
410
410
  function foldOrSkipPrior(prior, fullKey, old) {
411
411
  if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
412
- if (old === void 0) return void 0;
412
+ if (old === void 0) return foldOptionalSelfRefAbsent(prior, fullKey);
413
413
  return foldSelfRef(prior, fullKey, old);
414
414
  }
415
+ function foldOptionalSelfRefAbsent(v, fullKey) {
416
+ if (isSubst(v) && substFullKey(v) === fullKey) {
417
+ if (!v.optional) return void 0;
418
+ return {
419
+ _kind: "subst-placeholder",
420
+ segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
421
+ optional: v.optional,
422
+ knownAbsent: true,
423
+ listSuffix: v.listSuffix,
424
+ line: v.line,
425
+ col: v.col,
426
+ prefixLen: v.prefixLen
427
+ };
428
+ }
429
+ if (isConcat(v)) {
430
+ const nodes = [];
431
+ for (const node of v.nodes) {
432
+ const folded = foldOptionalSelfRefAbsent(node, fullKey);
433
+ if (folded === void 0) return void 0;
434
+ nodes.push(folded);
435
+ }
436
+ return { _kind: "concat-placeholder", nodes, line: v.line, col: v.col };
437
+ }
438
+ if (isAppend(v)) {
439
+ const existing = foldOptionalSelfRefAbsent(v.existing, fullKey);
440
+ const elem = foldOptionalSelfRefAbsent(v.elem, fullKey);
441
+ if (existing === void 0 || elem === void 0) return void 0;
442
+ return { _kind: "append-placeholder", existing, elem };
443
+ }
444
+ if (isResObj(v)) {
445
+ const fields = /* @__PURE__ */ new Map();
446
+ for (const [key, value] of v.fields) {
447
+ const folded = foldOptionalSelfRefAbsent(value, fullKey);
448
+ if (folded === void 0) return void 0;
449
+ fields.set(key, folded);
450
+ }
451
+ return { _kind: "res-obj", fields, priorValues: new Map(v.priorValues) };
452
+ }
453
+ const hv = v;
454
+ if (hv.kind === "array") {
455
+ const items = [];
456
+ for (const item of hv.items) {
457
+ const folded = foldOptionalSelfRefAbsent(item, fullKey);
458
+ if (folded === void 0) return void 0;
459
+ items.push(folded);
460
+ }
461
+ return { kind: "array", items };
462
+ }
463
+ if (hv.kind === "object") {
464
+ const fields = /* @__PURE__ */ new Map();
465
+ for (const [key, value] of hv.fields) {
466
+ const folded = foldOptionalSelfRefAbsent(value, fullKey);
467
+ if (folded === void 0) return void 0;
468
+ fields.set(key, folded);
469
+ }
470
+ return { kind: "object", fields };
471
+ }
472
+ return cloneResolverValue(v);
473
+ }
415
474
  function cloneResolverValue(v) {
416
475
  if (isSubst(v)) {
417
476
  return {
418
477
  _kind: "subst-placeholder",
419
478
  segments: v.segments.map((seg) => ({ text: seg.text, line: seg.line, col: seg.col })),
420
479
  optional: v.optional,
480
+ knownAbsent: v.knownAbsent,
421
481
  listSuffix: v.listSuffix,
422
482
  line: v.line,
423
483
  col: v.col,
@@ -482,7 +542,7 @@ function foldNestedSelfRefs(v, pathPrefix) {
482
542
  return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
483
543
  }
484
544
  function containsSubstByPath(v, target) {
485
- if (isSubst(v)) return segmentsTextEqual(v.segments, target);
545
+ if (isSubst(v)) return !v.knownAbsent && segmentsTextEqual(v.segments, target);
486
546
  if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
487
547
  if (isAppend(v)) {
488
548
  return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
@@ -649,6 +709,11 @@ var Lexer = class {
649
709
  this.line = 1;
650
710
  this.col = 1;
651
711
  this.hadSpace = false;
712
+ // Accumulates literal whitespace chars consumed between tokens. Reset on every
713
+ // token push. Used so the parser can preserve path-expression whitespace
714
+ // (xx.hocon#42, E13) — e.g. tab vs space adjacent to dots in key paths.
715
+ // Comment text is NOT accumulated; only the actual WS chars.
716
+ this.whitespaceBuffer = "";
652
717
  this.tokens = [];
653
718
  if (input.charCodeAt(0) === 65279) this.input = input.slice(1);
654
719
  }
@@ -664,6 +729,7 @@ var Lexer = class {
664
729
  continue;
665
730
  }
666
731
  if (isHoconWhitespace(ch)) {
732
+ this.whitespaceBuffer += ch;
667
733
  this.advance();
668
734
  this.hadSpace = true;
669
735
  continue;
@@ -755,7 +821,7 @@ var Lexer = class {
755
821
  }
756
822
  throw new ParseError(`unexpected character: ${JSON.stringify(ch)}`, sl, sc);
757
823
  }
758
- this.tokens.push({ kind: "eof", value: "", line: this.line, col: this.col, isQuoted: false, precedingSpace: false });
824
+ this.tokens.push({ kind: "eof", value: "", line: this.line, col: this.col, isQuoted: false, precedingSpace: false, precedingWhitespace: "" });
759
825
  return this.tokens;
760
826
  }
761
827
  peek(offset = 0) {
@@ -772,12 +838,14 @@ var Lexer = class {
772
838
  return ch;
773
839
  }
774
840
  push(kind, value, l, c, isQuoted = false) {
775
- this.tokens.push({ kind, value, line: l, col: c, isQuoted, precedingSpace: this.hadSpace });
841
+ this.tokens.push({ kind, value, line: l, col: c, isQuoted, precedingSpace: this.hadSpace, precedingWhitespace: this.whitespaceBuffer });
776
842
  this.hadSpace = false;
843
+ this.whitespaceBuffer = "";
777
844
  }
778
845
  pushSubst(payload, value, l, c) {
779
- this.tokens.push({ kind: "subst", value, line: l, col: c, isQuoted: false, precedingSpace: this.hadSpace, subst: payload });
846
+ this.tokens.push({ kind: "subst", value, line: l, col: c, isQuoted: false, precedingSpace: this.hadSpace, precedingWhitespace: this.whitespaceBuffer, subst: payload });
780
847
  this.hadSpace = false;
848
+ this.whitespaceBuffer = "";
781
849
  }
782
850
  /**
783
851
  * Read body of a quoted string. Opening `"` must already be consumed.
@@ -1023,7 +1091,7 @@ function isUnquotedContinue(ch, nextFn) {
1023
1091
  }
1024
1092
 
1025
1093
  // src/internal/parser/parser.ts
1026
- var EOF_TOKEN = { kind: "eof", value: "", line: 0, col: 0, isQuoted: false, precedingSpace: false, subst: void 0 };
1094
+ var EOF_TOKEN = { kind: "eof", value: "", line: 0, col: 0, isQuoted: false, precedingSpace: false, precedingWhitespace: "", subst: void 0 };
1027
1095
  var Parser = class {
1028
1096
  constructor(tokens) {
1029
1097
  this.tokens = tokens;
@@ -1136,13 +1204,17 @@ var Parser = class {
1136
1204
  let trailingDot = false;
1137
1205
  let firstWasQuoted = false;
1138
1206
  let spaceConcat = false;
1207
+ let postDotPrefix = "";
1139
1208
  while (true) {
1140
1209
  const t = this.peek();
1141
1210
  if (t.kind === "string") {
1142
1211
  this.advance();
1143
1212
  if (segments.length === 0) firstWasQuoted = true;
1144
1213
  if (spaceConcat) {
1145
- segments[segments.length - 1] = `${segments[segments.length - 1]} ${t.value}`;
1214
+ segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}${t.value}`;
1215
+ } else if (postDotPrefix !== "") {
1216
+ segments.push(`${postDotPrefix}${t.value}`);
1217
+ postDotPrefix = "";
1146
1218
  } else {
1147
1219
  segments.push(t.value);
1148
1220
  }
@@ -1152,24 +1224,23 @@ var Parser = class {
1152
1224
  const raw = t.value.startsWith(".") && segments.length > 0 && !t.precedingSpace ? t.value.slice(1) : t.value;
1153
1225
  const parts = raw.split(".");
1154
1226
  const filtered = parts.filter((s) => s.length > 0);
1155
- for (const seg of filtered) {
1156
- if (seg[0] === "-" && !(seg.length >= 2 && seg[1] >= "0" && seg[1] <= "9")) {
1157
- const after = seg.length >= 2 ? JSON.stringify(seg[1]) : "EOF";
1158
- throw new ParseError(
1159
- `unquoted key segment cannot begin with '-' unless followed by a digit (got '-' then ${after} in ${JSON.stringify(seg)}, HOCON.md L270-276)`,
1160
- t.line,
1161
- t.col
1162
- );
1163
- }
1164
- }
1165
- if (spaceConcat && filtered.length > 0) {
1227
+ if (spaceConcat) {
1228
+ segments[segments.length - 1] = `${segments[segments.length - 1]}${t.precedingWhitespace}`;
1166
1229
  if (raw.startsWith(".")) {
1167
1230
  segments.push(...filtered);
1168
- } else {
1231
+ } else if (filtered.length > 0) {
1169
1232
  const [head, ...tail] = filtered;
1170
- segments[segments.length - 1] = `${segments[segments.length - 1]} ${head}`;
1233
+ segments[segments.length - 1] = `${segments[segments.length - 1]}${head}`;
1171
1234
  segments.push(...tail);
1172
1235
  }
1236
+ } else if (postDotPrefix !== "" && raw.startsWith(".")) {
1237
+ segments.push(postDotPrefix);
1238
+ postDotPrefix = "";
1239
+ segments.push(...filtered);
1240
+ } else if (postDotPrefix !== "" && filtered.length > 0) {
1241
+ const [head, ...tail] = filtered;
1242
+ segments.push(`${postDotPrefix}${head}`, ...tail);
1243
+ postDotPrefix = "";
1173
1244
  } else {
1174
1245
  segments.push(...filtered);
1175
1246
  }
@@ -1179,11 +1250,23 @@ var Parser = class {
1179
1250
  break;
1180
1251
  }
1181
1252
  spaceConcat = false;
1182
- if (trailingDot) continue;
1253
+ if (trailingDot) {
1254
+ const next2 = this.peek();
1255
+ if ((next2.kind === "unquoted" || next2.kind === "string") && next2.precedingWhitespace.length > 0) {
1256
+ postDotPrefix = next2.precedingWhitespace;
1257
+ } else {
1258
+ postDotPrefix = "";
1259
+ }
1260
+ continue;
1261
+ }
1183
1262
  const next = this.peek();
1184
1263
  if (next.kind === "unquoted" && next.value === "." && !next.precedingSpace) {
1185
1264
  this.advance();
1186
1265
  trailingDot = true;
1266
+ const afterDot = this.peek();
1267
+ if ((afterDot.kind === "unquoted" || afterDot.kind === "string") && afterDot.precedingWhitespace.length > 0) {
1268
+ postDotPrefix = afterDot.precedingWhitespace;
1269
+ }
1187
1270
  continue;
1188
1271
  }
1189
1272
  if (next.kind === "unquoted" && next.value.startsWith(".") && !next.precedingSpace) {
@@ -1196,6 +1279,14 @@ var Parser = class {
1196
1279
  }
1197
1280
  break;
1198
1281
  }
1282
+ if (trailingDot) {
1283
+ const here = this.peek();
1284
+ throw new ParseError(
1285
+ `path has a trailing period '.' \u2014 empty key segment not allowed (HOCON.md path rules)`,
1286
+ here.line,
1287
+ here.col
1288
+ );
1289
+ }
1199
1290
  return { segments, firstWasQuoted };
1200
1291
  }
1201
1292
  parseInclude() {
@@ -1368,7 +1459,8 @@ var Parser = class {
1368
1459
  break;
1369
1460
  }
1370
1461
  if (hadSpace) {
1371
- parts.push({ kind: "scalar", raw: " ", valueType: "string", pos: { line: t.line, col: t.col }, _separator: true });
1462
+ const sep = t.precedingWhitespace.length > 0 ? t.precedingWhitespace : " ";
1463
+ parts.push({ kind: "scalar", raw: sep, valueType: "string", pos: { line: t.line, col: t.col }, _separator: true });
1372
1464
  }
1373
1465
  parts.push(node);
1374
1466
  }
@@ -1830,9 +1922,9 @@ var StructureBuilder = class _StructureBuilder {
1830
1922
  const existing = obj.fields.get(head);
1831
1923
  const newVal = this.astToResolverValue(field.value, childPrefix);
1832
1924
  if (existing !== void 0) {
1833
- const exFolded = foldNestedSelfRefs(existing, childPrefix);
1834
1925
  const oldPrior = obj.priorValues.get(head);
1835
- const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
1926
+ const priorInput = isResObj(existing) ? foldNestedSelfRefs(existing, childPrefix) : existing;
1927
+ const prior = foldOrSkipPrior(priorInput, fullKey, oldPrior);
1836
1928
  if (prior !== void 0) obj.priorValues.set(head, prior);
1837
1929
  }
1838
1930
  if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
@@ -1884,9 +1976,9 @@ var StructureBuilder = class _StructureBuilder {
1884
1976
  const existing = obj.fields.get(head);
1885
1977
  const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
1886
1978
  if (existing !== void 0) {
1887
- const exFolded = foldNestedSelfRefs(existing, childPrefix);
1888
1979
  const oldPrior = obj.priorValues.get(head);
1889
- const prior = foldOrSkipPrior(exFolded, fullKey, oldPrior);
1980
+ const priorInput = isResObj(existing) ? foldNestedSelfRefs(existing, childPrefix) : existing;
1981
+ const prior = foldOrSkipPrior(priorInput, fullKey, oldPrior);
1890
1982
  if (prior !== void 0) obj.priorValues.set(head, prior);
1891
1983
  }
1892
1984
  if (existing !== void 0 && isResObj(existing) && isResObj(newVal)) {
@@ -1909,7 +2001,7 @@ var StructureBuilder = class _StructureBuilder {
1909
2001
  return inner;
1910
2002
  }
1911
2003
  case "subst":
1912
- return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, listSuffix: ast.listSuffix, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
2004
+ return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, knownAbsent: false, listSuffix: ast.listSuffix, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
1913
2005
  case "concat":
1914
2006
  return { _kind: "concat-placeholder", nodes: ast.nodes.map((n) => this.astToResolverValue(n, pathPrefix)), line: ast.pos.line, col: ast.pos.col };
1915
2007
  case "include":
@@ -1933,7 +2025,7 @@ var StructureBuilder = class _StructureBuilder {
1933
2025
  case "object":
1934
2026
  return await this.buildAsync(ast, pathPrefix);
1935
2027
  case "subst":
1936
- return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, listSuffix: ast.listSuffix, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
2028
+ return { _kind: "subst-placeholder", segments: ast.segments, optional: ast.optional, knownAbsent: false, listSuffix: ast.listSuffix, line: ast.pos.line, col: ast.pos.col, prefixLen: 0 };
1937
2029
  case "concat": {
1938
2030
  const nodes = [];
1939
2031
  for (const n of ast.nodes) {
@@ -2051,6 +2143,8 @@ var SubstitutionResolver = class {
2051
2143
  const result = /* @__PURE__ */ new Map();
2052
2144
  for (const [key, val] of obj.fields) {
2053
2145
  this.resolvingFieldPath.push(key);
2146
+ const fullCacheKey = stringSegmentsToKey(this.resolvingFieldPath);
2147
+ this.cache.delete(fullCacheKey);
2054
2148
  let resolved;
2055
2149
  try {
2056
2150
  resolved = this.resolveVal(val, obj);
@@ -2063,28 +2157,42 @@ var SubstitutionResolver = class {
2063
2157
  if (prior !== void 0) {
2064
2158
  const priorResolved = this.resolveVal(prior, obj);
2065
2159
  if (priorResolved !== void 0 && priorResolved.kind === "object") {
2066
- result.set(
2067
- key,
2068
- deepMergeHoconValues(
2069
- priorResolved,
2070
- resolved
2071
- )
2160
+ const finalValue = deepMergeHoconValues(
2161
+ priorResolved,
2162
+ resolved
2072
2163
  );
2164
+ result.set(key, finalValue);
2165
+ this.cache.set(fullCacheKey, finalValue);
2166
+ this.cacheDescendants(fullCacheKey, finalValue);
2073
2167
  continue;
2074
2168
  }
2075
2169
  }
2076
2170
  }
2077
2171
  result.set(key, resolved);
2172
+ this.cache.set(fullCacheKey, resolved);
2173
+ this.cacheDescendants(fullCacheKey, resolved);
2078
2174
  } else {
2079
2175
  const prior = obj.priorValues.get(key);
2080
2176
  if (prior !== void 0) {
2081
2177
  const priorResolved = this.resolveVal(prior, obj);
2082
- if (priorResolved !== void 0) result.set(key, priorResolved);
2178
+ if (priorResolved !== void 0) {
2179
+ result.set(key, priorResolved);
2180
+ this.cache.set(fullCacheKey, priorResolved);
2181
+ this.cacheDescendants(fullCacheKey, priorResolved);
2182
+ }
2083
2183
  }
2084
2184
  }
2085
2185
  }
2086
2186
  return { kind: "object", fields: result };
2087
2187
  }
2188
+ cacheDescendants(prefix, value) {
2189
+ if (value.kind !== "object") return;
2190
+ for (const [key, child] of value.fields) {
2191
+ const childKey = `${prefix}.${stringSegmentsToKey([key])}`;
2192
+ this.cache.set(childKey, child);
2193
+ this.cacheDescendants(childKey, child);
2194
+ }
2195
+ }
2088
2196
  resolveVal(v, scope) {
2089
2197
  if (isSubst(v)) return this.resolveSubst(v, scope);
2090
2198
  if (isConcat(v)) {
@@ -2110,6 +2218,7 @@ var SubstitutionResolver = class {
2110
2218
  return hv;
2111
2219
  }
2112
2220
  resolveSubst(s, scope) {
2221
+ if (s.knownAbsent) return void 0;
2113
2222
  const key = s.listSuffix ? `${segmentsToKey(s.segments)}[]` : segmentsToKey(s.segments);
2114
2223
  if (this.cache.has(key)) return this.cache.get(key);
2115
2224
  if (this.resolving.has(key)) {
@@ -2202,6 +2311,28 @@ var SubstitutionResolver = class {
2202
2311
  if (result !== void 0) this.cache.set(key, result);
2203
2312
  return result;
2204
2313
  }
2314
+ if (s.prefixLen > 0 && s.segments.length > s.prefixLen) {
2315
+ const originalSegments = s.segments.slice(s.prefixLen);
2316
+ const fallbackFound = lookupPath(this.root, originalSegments);
2317
+ if (fallbackFound !== void 0) {
2318
+ let result = this.resolveVal(fallbackFound, scope);
2319
+ if (originalSegments.length === 1 && result !== void 0 && result.kind === "object") {
2320
+ const rootSeg = originalSegments[0]?.text ?? "";
2321
+ const prior = this.root.priorValues.get(rootSeg);
2322
+ if (prior !== void 0) {
2323
+ const priorResolved = this.resolveVal(prior, scope);
2324
+ if (priorResolved !== void 0 && priorResolved.kind === "object") {
2325
+ result = deepMergeHoconValues(
2326
+ priorResolved,
2327
+ result
2328
+ );
2329
+ }
2330
+ }
2331
+ }
2332
+ if (result !== void 0) this.cache.set(key, result);
2333
+ return result;
2334
+ }
2335
+ }
2205
2336
  if (s.listSuffix && this.opts.useSystemEnvironment !== false) {
2206
2337
  const result = this.resolveEnvList(s);
2207
2338
  if (result !== void 0) {
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-BwjvKeZ5.cjs';
2
+ import { C as Config, a as ParseOptions } from './config-CjSkQ4_l.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-BwjvKeZ5.js';
2
+ import { C as Config, a as ParseOptions } from './config-CjSkQ4_l.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;