@o3co/ts.hocon 1.6.0 → 1.7.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.
@@ -39,17 +39,20 @@ type ConcatPlaceholder = {
39
39
  /** 1-based column of the concat value in the source file (from AST Concat pos). */
40
40
  col: number;
41
41
  };
42
- type AppendPlaceholder = {
43
- _kind: 'append-placeholder';
44
- existing: ResolverValue;
45
- elem: ResolverValue;
46
- };
47
42
  type ResObj = {
48
43
  _kind: 'res-obj';
49
44
  fields: Map<string, ResolverValue>;
50
45
  priorValues: Map<string, ResolverValue>;
46
+ /**
47
+ * Keys whose net value in THIS object was established by an explicit
48
+ * non-self-referential assignment (`k = [...]`), i.e. a reset rather than a
49
+ * `+=`/self-ref append. Used by `deepMergeResObjInto` to decide whether an
50
+ * included file's `k` chains off the destination's pre-merge value (append
51
+ * origin → splice) or replaces it (reset origin → discard). See go.hocon#134.
52
+ */
53
+ resetKeys: Set<string>;
51
54
  };
52
- type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | AppendPlaceholder | ResObj;
55
+ type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | ResObj;
53
56
  /**
54
57
  * Custom resolver for `include package("id", "file")`.
55
58
  *
@@ -39,17 +39,20 @@ type ConcatPlaceholder = {
39
39
  /** 1-based column of the concat value in the source file (from AST Concat pos). */
40
40
  col: number;
41
41
  };
42
- type AppendPlaceholder = {
43
- _kind: 'append-placeholder';
44
- existing: ResolverValue;
45
- elem: ResolverValue;
46
- };
47
42
  type ResObj = {
48
43
  _kind: 'res-obj';
49
44
  fields: Map<string, ResolverValue>;
50
45
  priorValues: Map<string, ResolverValue>;
46
+ /**
47
+ * Keys whose net value in THIS object was established by an explicit
48
+ * non-self-referential assignment (`k = [...]`), i.e. a reset rather than a
49
+ * `+=`/self-ref append. Used by `deepMergeResObjInto` to decide whether an
50
+ * included file's `k` chains off the destination's pre-merge value (append
51
+ * origin → splice) or replaces it (reset origin → discard). See go.hocon#134.
52
+ */
53
+ resetKeys: Set<string>;
51
54
  };
52
- type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | AppendPlaceholder | ResObj;
55
+ type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | ResObj;
53
56
  /**
54
57
  * Custom resolver for `include package("id", "file")`.
55
58
  *
package/dist/index.cjs CHANGED
@@ -289,55 +289,6 @@ var NotResolvedError = class extends ConfigError {
289
289
  }
290
290
  };
291
291
 
292
- // src/internal/resolver/types.ts
293
- var separatorValues = /* @__PURE__ */ new WeakSet();
294
- function isSubst(v) {
295
- return v._kind === "subst-placeholder";
296
- }
297
- function isConcat(v) {
298
- return v._kind === "concat-placeholder";
299
- }
300
- function isAppend(v) {
301
- return v._kind === "append-placeholder";
302
- }
303
- function isResObj(v) {
304
- return v._kind === "res-obj";
305
- }
306
- function makeResObj() {
307
- return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map() };
308
- }
309
- function mergeUnresolved(receiver, fallback) {
310
- const result = makeResObj();
311
- for (const [k, v] of fallback.fields) {
312
- result.fields.set(k, v);
313
- }
314
- for (const [k, v] of fallback.priorValues) {
315
- result.priorValues.set(k, v);
316
- }
317
- for (const [k, rv] of receiver.fields) {
318
- const existing = result.fields.get(k);
319
- if (existing !== void 0) {
320
- const recPrior = receiver.priorValues.get(k);
321
- if (recPrior !== void 0 && !isResObj(recPrior)) {
322
- result.fields.set(k, rv);
323
- continue;
324
- }
325
- if (isResObj(rv) && isResObj(existing)) {
326
- result.fields.set(k, mergeUnresolved(rv, existing));
327
- continue;
328
- }
329
- result.priorValues.set(k, existing);
330
- }
331
- result.fields.set(k, rv);
332
- }
333
- for (const [k, v] of receiver.priorValues) {
334
- if (receiver.fields.has(k)) {
335
- result.priorValues.set(k, v);
336
- }
337
- }
338
- return result;
339
- }
340
-
341
292
  // src/internal/resolver/fold-self-ref.ts
342
293
  function substFullKey(s) {
343
294
  return stringSegmentsToKey(s.segments.map((seg) => seg.text));
@@ -353,9 +304,6 @@ function stringSegmentsToKey(segments) {
353
304
  function containsSelfRef(v, fullKey) {
354
305
  if (isSubst(v)) return !v.knownAbsent && substFullKey(v) === fullKey;
355
306
  if (isConcat(v)) return v.nodes.some((n) => containsSelfRef(n, fullKey));
356
- if (isAppend(v)) {
357
- return containsSelfRef(v.existing, fullKey) || containsSelfRef(v.elem, fullKey);
358
- }
359
307
  if (isResObj(v)) {
360
308
  for (const f of v.fields.values()) {
361
309
  if (containsSelfRef(f, fullKey)) return true;
@@ -386,19 +334,12 @@ function foldSelfRef(v, fullKey, replacement) {
386
334
  col: v.col
387
335
  };
388
336
  }
389
- if (isAppend(v)) {
390
- return {
391
- _kind: "append-placeholder",
392
- existing: foldSelfRef(v.existing, fullKey, replacement),
393
- elem: foldSelfRef(v.elem, fullKey, replacement)
394
- };
395
- }
396
337
  if (isResObj(v)) {
397
338
  const newFields = /* @__PURE__ */ new Map();
398
339
  for (const [k, val] of v.fields) {
399
340
  newFields.set(k, foldSelfRef(val, fullKey, replacement));
400
341
  }
401
- return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
342
+ return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues), resetKeys: new Set(v.resetKeys) };
402
343
  }
403
344
  const hv = v;
404
345
  if (hv.kind === "array") {
@@ -416,6 +357,41 @@ function foldSelfRef(v, fullKey, replacement) {
416
357
  }
417
358
  return v;
418
359
  }
360
+ function foldKnownAbsentSelfRef(v, fullKey, replacement) {
361
+ if (isSubst(v)) {
362
+ return v.knownAbsent && substFullKey(v) === fullKey ? replacement : v;
363
+ }
364
+ if (isConcat(v)) {
365
+ return {
366
+ _kind: "concat-placeholder",
367
+ nodes: v.nodes.map((n) => foldKnownAbsentSelfRef(n, fullKey, replacement)),
368
+ line: v.line,
369
+ col: v.col
370
+ };
371
+ }
372
+ if (isResObj(v)) {
373
+ const newFields = /* @__PURE__ */ new Map();
374
+ for (const [k, val] of v.fields) {
375
+ newFields.set(k, foldKnownAbsentSelfRef(val, fullKey, replacement));
376
+ }
377
+ return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues), resetKeys: new Set(v.resetKeys) };
378
+ }
379
+ const hv = v;
380
+ if (hv.kind === "array") {
381
+ return {
382
+ kind: "array",
383
+ items: hv.items.map((item) => foldKnownAbsentSelfRef(item, fullKey, replacement))
384
+ };
385
+ }
386
+ if (hv.kind === "object") {
387
+ const newFields = /* @__PURE__ */ new Map();
388
+ for (const [k, val] of hv.fields) {
389
+ newFields.set(k, foldKnownAbsentSelfRef(val, fullKey, replacement));
390
+ }
391
+ return { kind: "object", fields: newFields };
392
+ }
393
+ return v;
394
+ }
419
395
  function foldOrSkipPrior(prior, fullKey, old) {
420
396
  if (!containsSelfRef(prior, fullKey)) return cloneResolverValue(prior);
421
397
  if (old === void 0) return foldOptionalSelfRefAbsent(prior, fullKey);
@@ -444,12 +420,6 @@ function foldOptionalSelfRefAbsent(v, fullKey) {
444
420
  }
445
421
  return { _kind: "concat-placeholder", nodes, line: v.line, col: v.col };
446
422
  }
447
- if (isAppend(v)) {
448
- const existing = foldOptionalSelfRefAbsent(v.existing, fullKey);
449
- const elem = foldOptionalSelfRefAbsent(v.elem, fullKey);
450
- if (existing === void 0 || elem === void 0) return void 0;
451
- return { _kind: "append-placeholder", existing, elem };
452
- }
453
423
  if (isResObj(v)) {
454
424
  const fields = /* @__PURE__ */ new Map();
455
425
  for (const [key, value] of v.fields) {
@@ -457,7 +427,7 @@ function foldOptionalSelfRefAbsent(v, fullKey) {
457
427
  if (folded === void 0) return void 0;
458
428
  fields.set(key, folded);
459
429
  }
460
- return { _kind: "res-obj", fields, priorValues: new Map(v.priorValues) };
430
+ return { _kind: "res-obj", fields, priorValues: new Map(v.priorValues), resetKeys: new Set(v.resetKeys) };
461
431
  }
462
432
  const hv = v;
463
433
  if (hv.kind === "array") {
@@ -506,14 +476,7 @@ function cloneResolverValue(v) {
506
476
  for (const [k, val] of v.fields) newFields.set(k, cloneResolverValue(val));
507
477
  const newPriors = /* @__PURE__ */ new Map();
508
478
  for (const [k, val] of v.priorValues) newPriors.set(k, cloneResolverValue(val));
509
- return { _kind: "res-obj", fields: newFields, priorValues: newPriors };
510
- }
511
- if (isAppend(v)) {
512
- return {
513
- _kind: "append-placeholder",
514
- existing: cloneResolverValue(v.existing),
515
- elem: cloneResolverValue(v.elem)
516
- };
479
+ return { _kind: "res-obj", fields: newFields, priorValues: newPriors, resetKeys: new Set(v.resetKeys) };
517
480
  }
518
481
  const hv = v;
519
482
  if (hv.kind === "array") {
@@ -548,14 +511,11 @@ function foldNestedSelfRefs(v, pathPrefix) {
548
511
  }
549
512
  newFields.set(k, finalVal);
550
513
  }
551
- return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues) };
514
+ return { _kind: "res-obj", fields: newFields, priorValues: new Map(v.priorValues), resetKeys: new Set(v.resetKeys) };
552
515
  }
553
516
  function containsSubstByPath(v, target) {
554
517
  if (isSubst(v)) return !v.knownAbsent && segmentsTextEqual(v.segments, target);
555
518
  if (isConcat(v)) return v.nodes.some((n) => containsSubstByPath(n, target));
556
- if (isAppend(v)) {
557
- return containsSubstByPath(v.existing, target) || containsSubstByPath(v.elem, target);
558
- }
559
519
  if (isResObj(v)) {
560
520
  for (const f of v.fields.values()) {
561
521
  if (containsSubstByPath(f, target)) return true;
@@ -584,6 +544,53 @@ function segmentsTextEqual(a, b) {
584
544
  return true;
585
545
  }
586
546
 
547
+ // src/internal/resolver/types.ts
548
+ var separatorValues = /* @__PURE__ */ new WeakSet();
549
+ function isSubst(v) {
550
+ return v._kind === "subst-placeholder";
551
+ }
552
+ function isConcat(v) {
553
+ return v._kind === "concat-placeholder";
554
+ }
555
+ function isResObj(v) {
556
+ return v._kind === "res-obj";
557
+ }
558
+ function makeResObj() {
559
+ return { _kind: "res-obj", fields: /* @__PURE__ */ new Map(), priorValues: /* @__PURE__ */ new Map(), resetKeys: /* @__PURE__ */ new Set() };
560
+ }
561
+ function mergeUnresolved(receiver, fallback) {
562
+ const result = makeResObj();
563
+ for (const [k, v] of fallback.fields) {
564
+ result.fields.set(k, v);
565
+ }
566
+ for (const [k, v] of fallback.priorValues) {
567
+ result.priorValues.set(k, v);
568
+ }
569
+ for (const [k, rv] of receiver.fields) {
570
+ const existing = result.fields.get(k);
571
+ if (existing !== void 0) {
572
+ const recPrior = receiver.priorValues.get(k);
573
+ if (recPrior !== void 0 && !isResObj(recPrior)) {
574
+ result.fields.set(k, rv);
575
+ continue;
576
+ }
577
+ if (isResObj(rv) && isResObj(existing)) {
578
+ result.fields.set(k, mergeUnresolved(rv, existing));
579
+ continue;
580
+ }
581
+ const foldedPrior = foldOrSkipPrior(existing, k, void 0);
582
+ if (foldedPrior !== void 0) result.priorValues.set(k, foldedPrior);
583
+ }
584
+ result.fields.set(k, rv);
585
+ }
586
+ for (const [k, v] of receiver.priorValues) {
587
+ if (receiver.fields.has(k)) {
588
+ result.priorValues.set(k, v);
589
+ }
590
+ }
591
+ return result;
592
+ }
593
+
587
594
  // src/internal/resolver/utils.ts
588
595
  function segmentsToKey(segments) {
589
596
  return segments.map((s) => {
@@ -637,9 +644,19 @@ function deepMergeResObjInto(dst, src, pathPrefix = []) {
637
644
  deepMergeResObjInto(dstVal, srcVal, childPrefix);
638
645
  } else {
639
646
  if (dstVal !== void 0) {
640
- const priorExisting = dst.priorValues.get(k);
641
- const prior = foldOrSkipPrior(dstVal, fullKey, priorExisting);
642
- if (prior !== void 0) dst.priorValues.set(k, prior);
647
+ if (src.resetKeys.has(k)) {
648
+ dst.priorValues.delete(k);
649
+ } else {
650
+ const dstFolded = foldOrSkipPrior(dstVal, fullKey, dst.priorValues.get(k));
651
+ if (dstFolded !== void 0) {
652
+ const srcPrior = src.priorValues.get(k);
653
+ if (srcPrior !== void 0) {
654
+ dst.priorValues.set(k, foldKnownAbsentSelfRef(srcPrior, fullKey, dstFolded));
655
+ } else {
656
+ dst.priorValues.set(k, dstFolded);
657
+ }
658
+ }
659
+ }
643
660
  }
644
661
  dst.fields.set(k, srcVal);
645
662
  }
@@ -649,6 +666,7 @@ function deepMergeResObjInto(dst, src, pathPrefix = []) {
649
666
  dst.priorValues.set(k, srcPrior);
650
667
  }
651
668
  }
669
+ for (const k of src.resetKeys) dst.resetKeys.add(k);
652
670
  }
653
671
  function hoconValueToResObj(hv) {
654
672
  const obj = makeResObj();
@@ -1905,31 +1923,15 @@ var StructureBuilder = class _StructureBuilder {
1905
1923
  const childPrefix = [...pathPrefix, head];
1906
1924
  const fullKey = stringSegmentsToKey(childPrefix);
1907
1925
  if (field.append) {
1908
- const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
1909
- const oldPrior = obj.priorValues.get(head);
1910
- const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
1911
- if (folded !== void 0) obj.priorValues.set(head, folded);
1912
- obj.fields.set(head, {
1913
- _kind: "append-placeholder",
1914
- // Use the folded existing (self-ref-free) for the Append. When the
1915
- // fold returned undefined (self-ref present but no old prior),
1916
- // fall back to the original existing — resolveSubst will then
1917
- // raise the "self-ref with no prior value" error at resolve time,
1918
- // matching the pre-fix behaviour for that edge case.
1919
- //
1920
- // Clone `folded` again so the Append.existing and priorValues[head]
1921
- // do not share the same Subst/Concat references — relativizeResObj
1922
- // walks BOTH `fields` and `priorValues` and mutates Subst.segments
1923
- // in place, so a shared reference would get its prefix applied
1924
- // twice (resulting in e.g. `${outer.outer.base}` instead of
1925
- // `${outer.base}`). Codex review on PR #131.
1926
- existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
1927
- elem: this.astToResolverValue(field.value, childPrefix)
1928
- });
1926
+ const synthetic = this.desugarAppend(field, childPrefix);
1927
+ this.applyField(obj, synthetic, pathPrefix);
1929
1928
  return;
1930
1929
  }
1931
1930
  const existing = obj.fields.get(head);
1932
1931
  const newVal = this.astToResolverValue(field.value, childPrefix);
1932
+ if (!containsSelfRef(newVal, fullKey)) {
1933
+ obj.resetKeys.add(head);
1934
+ }
1933
1935
  if (existing !== void 0) {
1934
1936
  const oldPrior = obj.priorValues.get(head);
1935
1937
  const priorInput = isResObj(existing) ? foldNestedSelfRefs(existing, childPrefix) : existing;
@@ -1971,19 +1973,15 @@ var StructureBuilder = class _StructureBuilder {
1971
1973
  const childPrefix = [...pathPrefix, head];
1972
1974
  const fullKey = stringSegmentsToKey(childPrefix);
1973
1975
  if (field.append) {
1974
- const existing2 = obj.fields.get(head) ?? { kind: "array", items: [] };
1975
- const oldPrior = obj.priorValues.get(head);
1976
- const folded = foldOrSkipPrior(existing2, fullKey, oldPrior);
1977
- if (folded !== void 0) obj.priorValues.set(head, folded);
1978
- obj.fields.set(head, {
1979
- _kind: "append-placeholder",
1980
- existing: folded !== void 0 ? cloneResolverValue(folded) : existing2,
1981
- elem: await this.astToResolverValueAsync(field.value, childPrefix)
1982
- });
1976
+ const synthetic = this.desugarAppend(field, childPrefix);
1977
+ await this.applyFieldAsync(obj, synthetic, pathPrefix);
1983
1978
  return;
1984
1979
  }
1985
1980
  const existing = obj.fields.get(head);
1986
1981
  const newVal = await this.astToResolverValueAsync(field.value, childPrefix);
1982
+ if (!containsSelfRef(newVal, fullKey)) {
1983
+ obj.resetKeys.add(head);
1984
+ }
1987
1985
  if (existing !== void 0) {
1988
1986
  const oldPrior = obj.priorValues.get(head);
1989
1987
  const priorInput = isResObj(existing) ? foldNestedSelfRefs(existing, childPrefix) : existing;
@@ -1996,6 +1994,21 @@ var StructureBuilder = class _StructureBuilder {
1996
1994
  }
1997
1995
  obj.fields.set(head, newVal);
1998
1996
  }
1997
+ /** Builds the `key = ${?fullkey} [value]` field that `key += value` desugars
1998
+ * to (S13b.2, HOCON.md L732). `childPrefix` is the field's fully-qualified
1999
+ * path (pathPrefix + key), so a nested `srv { items += x }` references
2000
+ * `${?srv.items}`; include relativization rewrites it under a mount prefix.
2001
+ * `field.key` is already single-segment here (multi-segment keys are split
2002
+ * into nested objects upstream). Returns a NEW AstField (append=false);
2003
+ * positions inherit from the original field so resolve-time errors (the
2004
+ * S13b.2 non-array check, now via the scalar+array concat) keep a location. */
2005
+ desugarAppend(field, childPrefix) {
2006
+ const segments = childPrefix.map((text) => ({ text, line: field.pos.line, col: field.pos.col }));
2007
+ const subst = { kind: "subst", segments, optional: true, listSuffix: false, pos: field.pos };
2008
+ const elemArray = { kind: "array", items: [field.value], pos: field.pos };
2009
+ const synthetic = { kind: "concat", nodes: [subst, elemArray], pos: field.pos };
2010
+ return { key: field.key, value: synthetic, append: false, pos: field.pos };
2011
+ }
1999
2012
  astToResolverValue(ast, pathPrefix) {
2000
2013
  switch (ast.kind) {
2001
2014
  case "scalar": {
@@ -2060,11 +2073,6 @@ var StructureBuilder = class _StructureBuilder {
2060
2073
  }
2061
2074
  return;
2062
2075
  }
2063
- if (isAppend(val)) {
2064
- this.relativizeSubstPaths(val.existing, prefixSegments);
2065
- this.relativizeSubstPaths(val.elem, prefixSegments);
2066
- return;
2067
- }
2068
2076
  if (isResObj(val)) {
2069
2077
  this.relativizeResObj(val, prefixSegments);
2070
2078
  return;
@@ -2213,7 +2221,6 @@ var SubstitutionResolver = class {
2213
2221
  this.resolvingConcats.delete(v);
2214
2222
  }
2215
2223
  }
2216
- if (isAppend(v)) return this.resolveAppend(v, scope);
2217
2224
  if (isResObj(v)) return this.resolveResObj(v);
2218
2225
  const hv = v;
2219
2226
  if (hv.kind === "array") {
@@ -2498,38 +2505,6 @@ var SubstitutionResolver = class {
2498
2505
  }
2499
2506
  return folded;
2500
2507
  }
2501
- resolveAppend(a, scope) {
2502
- const existing = this.resolveVal(a.existing, scope) ?? { kind: "array", items: [] };
2503
- if (this.opts.allowUnresolved && (isSubst(existing) || isConcat(existing) || isAppend(existing))) {
2504
- return a;
2505
- }
2506
- const elem = this.resolveVal(a.elem, scope);
2507
- let priorItems;
2508
- if (existing.kind === "array") {
2509
- priorItems = existing.items;
2510
- } else if (existing.kind === "object") {
2511
- const converted = numericObjectToArray(existing);
2512
- if (converted === null) {
2513
- throw new ResolveError(
2514
- `'+=' on non-array value: prior value is object with non-numeric keys (spec L732)`,
2515
- "",
2516
- 0,
2517
- 0
2518
- );
2519
- }
2520
- priorItems = converted;
2521
- } else {
2522
- throw new ResolveError(
2523
- `'+=' on non-array value: prior value is ${existing.kind} (spec L732)`,
2524
- "",
2525
- 0,
2526
- 0
2527
- );
2528
- }
2529
- const items = [...priorItems];
2530
- if (elem !== void 0) items.push(elem);
2531
- return { kind: "array", items };
2532
- }
2533
2508
  };
2534
2509
 
2535
2510
  // src/internal/resolver/resolver.ts
@@ -2547,7 +2522,7 @@ function containsPlaceholders(tree) {
2547
2522
  }
2548
2523
  function valContainsPlaceholders(v) {
2549
2524
  const rv = v;
2550
- if (isSubst(rv) || isConcat(rv) || isAppend(rv)) return true;
2525
+ if (isSubst(rv) || isConcat(rv)) return true;
2551
2526
  if (isResObj(rv)) return containsPlaceholders(rv);
2552
2527
  const hv = v;
2553
2528
  if (hv?.kind === "array") {
@@ -2558,7 +2533,7 @@ function valContainsPlaceholders(v) {
2558
2533
  function buildPartialHoconFromResObj(tree) {
2559
2534
  const fields = /* @__PURE__ */ new Map();
2560
2535
  for (const [k, v] of tree.fields) {
2561
- if (!isSubst(v) && !isConcat(v) && !isAppend(v)) {
2536
+ if (!isSubst(v) && !isConcat(v)) {
2562
2537
  if (isResObj(v)) {
2563
2538
  fields.set(k, buildPartialHoconFromResObj(v));
2564
2539
  } else {
@@ -2951,7 +2926,7 @@ function hoconToJs(v) {
2951
2926
  function resObjToKeyShape(tree) {
2952
2927
  const fields = /* @__PURE__ */ new Map();
2953
2928
  for (const [k, v] of tree.fields) {
2954
- if (isSubst(v) || isConcat(v) || isAppend(v)) {
2929
+ if (isSubst(v) || isConcat(v)) {
2955
2930
  fields.set(k, { kind: "scalar", raw: "", valueType: "null" });
2956
2931
  } else if (isResObj(v)) {
2957
2932
  fields.set(k, resObjToKeyShape(v));
@@ -2965,7 +2940,7 @@ function stripPlaceholderFields(v) {
2965
2940
  const fields = /* @__PURE__ */ new Map();
2966
2941
  let hadPlaceholders = false;
2967
2942
  for (const [k, val] of v.fields) {
2968
- if (val._kind === "subst-placeholder" || val._kind === "concat-placeholder" || val._kind === "append-placeholder") {
2943
+ if (val._kind === "subst-placeholder" || val._kind === "concat-placeholder") {
2969
2944
  hadPlaceholders = true;
2970
2945
  } else if (val.kind === "object") {
2971
2946
  const inner = stripPlaceholderFields(val);
@@ -2999,9 +2974,8 @@ function renderHoconAsJSON(v) {
2999
2974
  case "boolean":
3000
2975
  return v.raw;
3001
2976
  case "number": {
3002
- const n = Number(v.raw);
3003
- if (Number.isFinite(n)) return v.raw;
3004
- return JSON.stringify(v.raw);
2977
+ const norm = normalizeLeadingZeroNumber(v.raw);
2978
+ return JSON_NUMBER_RE.test(norm) ? norm : JSON.stringify(v.raw);
3005
2979
  }
3006
2980
  case "string":
3007
2981
  return JSON.stringify(v.raw);
@@ -3023,6 +2997,10 @@ function renderHoconAsJSON(v) {
3023
2997
  }
3024
2998
  throw new Error(`renderHoconAsJSON: unsupported value kind (unresolved placeholder?)`);
3025
2999
  }
3000
+ var JSON_NUMBER_RE = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
3001
+ function normalizeLeadingZeroNumber(raw) {
3002
+ return raw.replace(/^([+-]?)0+(\d)/, "$1$2");
3003
+ }
3026
3004
 
3027
3005
  // src/parse.ts
3028
3006
  var fs = __toESM(require("fs"), 1);
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Config } from './config-CjSkQ4_l.cjs';
2
- export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ResolveOptions, S as ScalarValueType, d as defaultParseOptions, b as defaultResolveOptions, p as parse, c as parseAsync, e as parseFile, f as parseFileAsync, g as parseFileWithOptions, h as parseString, i as parseStringWithOptions } from './config-CjSkQ4_l.cjs';
1
+ import { C as Config } from './config-DCSLXkNY.cjs';
2
+ export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ResolveOptions, S as ScalarValueType, d as defaultParseOptions, b as defaultResolveOptions, p as parse, c as parseAsync, e as parseFile, f as parseFileAsync, g as parseFileWithOptions, h as parseString, i as parseStringWithOptions } from './config-DCSLXkNY.cjs';
3
3
 
4
4
  declare class ParseError extends Error {
5
5
  readonly line: number;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { C as Config } from './config-CjSkQ4_l.js';
2
- export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ResolveOptions, S as ScalarValueType, d as defaultParseOptions, b as defaultResolveOptions, p as parse, c as parseAsync, e as parseFile, f as parseFileAsync, g as parseFileWithOptions, h as parseString, i as parseStringWithOptions } from './config-CjSkQ4_l.js';
1
+ import { C as Config } from './config-DCSLXkNY.js';
2
+ export { B as ByteUnit, D as DurationUnit, H as HoconValue, P as PackageResolver, a as ParseOptions, R as ResolveOptions, S as ScalarValueType, d as defaultParseOptions, b as defaultResolveOptions, p as parse, c as parseAsync, e as parseFile, f as parseFileAsync, g as parseFileWithOptions, h as parseString, i as parseStringWithOptions } from './config-DCSLXkNY.js';
3
3
 
4
4
  declare class ParseError extends Error {
5
5
  readonly line: number;