@o3co/ts.hocon 1.2.0 → 1.4.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@o3co/ts.hocon.svg)](https://www.npmjs.com/package/@o3co/ts.hocon)
4
4
  [![CI](https://github.com/o3co/ts.hocon/actions/workflows/test.yml/badge.svg)](https://github.com/o3co/ts.hocon/actions/workflows/test.yml)
5
- [![codecov](https://codecov.io/gh/o3co/ts.hocon/branch/main/graph/badge.svg)](https://codecov.io/gh/o3co/ts.hocon)
5
+ [![codecov](https://codecov.io/gh/o3co/ts.hocon/branch/develop/graph/badge.svg)](https://codecov.io/gh/o3co/ts.hocon)
6
6
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
7
7
 
8
8
  [Lightbend HOCON](https://github.com/lightbend/config/blob/main/HOCON.md) 仕様の TypeScript パーサー。現在の準拠率は [仕様準拠](#仕様準拠) を参照。
@@ -54,7 +54,9 @@ cfg.has('server.host') // true
54
54
  | Trailing commas | N/A | No | N/A | Yes |
55
55
  | Unquoted strings | Yes | No | Yes | Yes |
56
56
 
57
- HOCON は YAML の可読性と JSON の構造性を兼ね備え、どちらにもない機能 — 変数参照、インクルード、ディープマージ — を提供します。設定がフラットなキーバリューペア以上のものであれば、HOCON を検討する価値があります。
57
+ HOCON は単なるシリアライズ形式ではなく、**プログラムに注入するための設定言語** です。JSON / YAML / TOML はデータ構造の表現に徹しており、ファイルの重ね合わせ・環境変数・参照解決はアプリ側(Pydantic、Serde、Zod 等)の責務になります。HOCON はそれらを仕様そのものに内包しているため、プログラムが設定を受け取る時点で、フォールバックは合成済み・`${VAR}` 参照は解決済みの「1 枚の設定」になっています。「このレイヤーに値があるか?」に由来する条件分岐は、コードではなくフォーマット境界で消えます。
58
+
59
+ 加えて HOCON は YAML の可読性と JSON の構造性を兼ね備えるため、フラットなキーバリュー設定を超えるユースケースには強い選択肢になります。
58
60
 
59
61
  ## 特徴
60
62
 
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/@o3co/ts.hocon.svg)](https://www.npmjs.com/package/@o3co/ts.hocon)
4
4
  [![CI](https://github.com/o3co/ts.hocon/actions/workflows/test.yml/badge.svg)](https://github.com/o3co/ts.hocon/actions/workflows/test.yml)
5
- [![codecov](https://codecov.io/gh/o3co/ts.hocon/branch/main/graph/badge.svg)](https://codecov.io/gh/o3co/ts.hocon)
5
+ [![codecov](https://codecov.io/gh/o3co/ts.hocon/branch/develop/graph/badge.svg)](https://codecov.io/gh/o3co/ts.hocon)
6
6
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
7
7
 
8
8
  A [Lightbend HOCON](https://github.com/lightbend/config/blob/main/HOCON.md) parser for TypeScript. See [Spec Compliance](#spec-compliance) for the current conformance rate.
@@ -58,7 +58,9 @@ cfg.has('server.host') // true
58
58
  | Trailing commas | N/A | No | N/A | Yes |
59
59
  | Unquoted strings | Yes | No | Yes | Yes |
60
60
 
61
- HOCON gives you the readability of YAML, the structure of JSON, and features that neither has — substitutions, includes, and deep merge. If your config is more than a few flat key-value pairs, HOCON is worth considering.
61
+ HOCON isn't just a serialization format — it's a **config-injection language**. JSON, YAML, and TOML describe data structures and leave file layering, environment variables, and reference resolution to your code (Pydantic, Serde, Zod, etc.). HOCON bakes those into the spec itself: by the time your program reads the config, fallback files are merged and `${VAR}` references resolved into a single composed object. Conditional branching from "is this value present in this layer?" disappears at the format boundary.
62
+
63
+ On top of that, HOCON combines the readability of YAML with the structure of JSON — making it a strong fit for anything beyond flat key-value config.
62
64
 
63
65
  ## Features
64
66
 
@@ -103,18 +105,60 @@ parseFileAsync(path: string, opts?: ParseOptions): Promise<Config>
103
105
  | Method | Returns | Throws if |
104
106
  |--------|---------|-----------|
105
107
  | `get(path)` | `unknown \| undefined` | — |
106
- | `getString(path)` | `string` | missing, wrong type |
107
- | `getNumber(path)` | `number` | missing, wrong type |
108
- | `getBoolean(path)` | `boolean` | missing, wrong type |
109
- | `getConfig(path)` | `Config` | missing, not an object |
110
- | `getList(path)` | `unknown[]` | missing, not an array |
108
+ | `getString(path)` | `string` | missing, wrong type, or unresolved |
109
+ | `getNumber(path)` | `number` | missing, wrong type, or unresolved |
110
+ | `getBoolean(path)` | `boolean` | missing, wrong type, or unresolved |
111
+ | `getConfig(path)` | `Config` | missing, not an object, or unresolved |
112
+ | `getList(path)` | `unknown[]` | missing, not an array, or unresolved |
111
113
  | `getDuration(path, unit?)` | `number` | missing, not a string, or invalid duration format |
112
114
  | `getBytes(path, unit?)` | `number` | missing, not a string, or invalid byte size format |
113
115
  | `has(path)` | `boolean` | — |
114
116
  | `keys()` | `string[]` | — |
115
117
  | `withFallback(fallback)` | `Config` | — |
118
+ | `resolve(opts?)` | `Config` | unresolvable substitution (unless `allowUnresolved: true`) |
119
+ | `resolveWith(source, opts?)` | `Config` | source unresolved, or unresolvable substitution |
120
+ | `isResolved()` | `boolean` | — |
116
121
  | `toObject()` | `unknown` | — |
117
122
 
123
+ ### Deferred resolution API (E12)
124
+
125
+ Separate the parse, fallback-layering, and resolve steps for runtime config injection.
126
+
127
+ ```ts
128
+ import { parseStringWithOptions, fromMap, empty } from '@o3co/ts.hocon'
129
+ import type { ResolveOptions } from '@o3co/ts.hocon'
130
+
131
+ // 1. Parse without resolving — substitutions deferred
132
+ const cfg = parseStringWithOptions(
133
+ 'version = ${shortversion}-${CI_RUN_NUMBER}\nvariables { shortversion = "1.2.3" }',
134
+ { resolveSubstitutions: false }
135
+ )
136
+ cfg.isResolved() // false — ${CI_RUN_NUMBER} still pending
137
+
138
+ // 2. Layer runtime fallbacks
139
+ const runtime = fromMap({ CI_RUN_NUMBER: '42' })
140
+ const vars = cfg.getConfig('variables') // already available (not a substitution)
141
+ const merged = cfg.withFallback(runtime).withFallback(vars)
142
+
143
+ // 3. Resolve the full fallback stack
144
+ const resolved = merged.resolve({ useSystemEnvironment: false })
145
+ resolved.getString('version') // "1.2.3-42"
146
+
147
+ // resolveWith: resolve receiver using source for lookup, source keys NOT in result
148
+ const receiver = parseStringWithOptions('r = ${key}', { resolveSubstitutions: false })
149
+ const source = fromMap({ key: 'val' })
150
+ const result = receiver.resolveWith(source)
151
+ result.has('key') // false — source keys excluded
152
+ result.getString('r') // "val"
153
+ ```
154
+
155
+ `ResolveOptions`:
156
+
157
+ | Option | Type | Default | Description |
158
+ |--------|------|---------|-------------|
159
+ | `useSystemEnvironment` | `boolean` | `true` | Consult `process.env` for substitution fallback |
160
+ | `allowUnresolved` | `boolean` | `false` | Leave unresolvable substitutions in place (no error) |
161
+
118
162
  ### Zod integration
119
163
 
120
164
  ```ts
@@ -214,7 +258,14 @@ Conformance against the [Lightbend HOCON specification](https://github.com/light
214
258
  | In-scope only | **83.3%** |
215
259
  | Lightbend `test01`–`test13` suite | 13/13 passing |
216
260
 
217
- Not supported in v0.1.0:
261
+ **Extra-spec conventions (E-series) — implementation status:**
262
+
263
+ | Item | Description | Status |
264
+ |------|-------------|--------|
265
+ | E11 | `include package(...)` service-locator includes | ✅ v1.3.0 |
266
+ | E12 | Deferred substitution resolution (`parse → withFallback → resolve()` lifecycle) | ✅ v1.4.0 |
267
+
268
+ Not supported:
218
269
 
219
270
  - `include url(...)`
220
271
  - `include classpath(...)`
@@ -0,0 +1,271 @@
1
+ type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
2
+ type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
3
+
4
+ type ScalarValueType = 'string' | 'number' | 'boolean' | 'null';
5
+ type HoconValue = {
6
+ kind: 'object';
7
+ fields: Map<string, HoconValue>;
8
+ } | {
9
+ kind: 'array';
10
+ items: HoconValue[];
11
+ } | {
12
+ kind: 'scalar';
13
+ raw: string;
14
+ valueType: ScalarValueType;
15
+ };
16
+
17
+ interface Segment {
18
+ readonly text: string;
19
+ readonly line: number;
20
+ readonly col: number;
21
+ }
22
+
23
+ type SubstPlaceholder = {
24
+ _kind: 'subst-placeholder';
25
+ segments: Segment[];
26
+ optional: boolean;
27
+ listSuffix: boolean;
28
+ line: number;
29
+ col: number;
30
+ prefixLen: number;
31
+ };
32
+ type ConcatPlaceholder = {
33
+ _kind: 'concat-placeholder';
34
+ nodes: ResolverValue[];
35
+ /** 1-based line of the concat value in the source file (from AST Concat pos). */
36
+ line: number;
37
+ /** 1-based column of the concat value in the source file (from AST Concat pos). */
38
+ col: number;
39
+ };
40
+ type AppendPlaceholder = {
41
+ _kind: 'append-placeholder';
42
+ existing: ResolverValue;
43
+ elem: ResolverValue;
44
+ };
45
+ type ResObj = {
46
+ _kind: 'res-obj';
47
+ fields: Map<string, ResolverValue>;
48
+ priorValues: Map<string, ResolverValue>;
49
+ };
50
+ type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | AppendPlaceholder | ResObj;
51
+ /**
52
+ * Custom resolver for `include package("id", "file")`.
53
+ *
54
+ * Receives:
55
+ * - `identifier`, `file`: the qualifier arguments (post HOCON unescape, byte-exact)
56
+ * - `includingFile`: absolute path of the including `.conf` file when known
57
+ * (currently always `undefined`; reserved for future threading from a future
58
+ * `parseFile` integration)
59
+ * - `baseDir`: the directory context active when the include is resolved
60
+ * (typically `path.dirname(includingFile)` of the parent, or the caller-set
61
+ * `baseDir` parse option). Set when known, `undefined` for parses of literal
62
+ * input strings without a directory context.
63
+ *
64
+ * Must return an absolute path or throw.
65
+ *
66
+ * When not provided, the default resolver uses `createRequire(import.meta.url)`
67
+ * which works in both CJS and ESM Node contexts. The default resolver picks the
68
+ * lookup starting paths in priority order:
69
+ * `resolveFrom` > `baseDir` > `path.dirname(includingFile)` > `process.cwd()`
70
+ */
71
+ type PackageResolver = (identifier: string, file: string, includingFile: string | undefined, baseDir: string | undefined) => string;
72
+ type ResolveOptions$1 = {
73
+ env: Record<string, string>;
74
+ baseDir: string | undefined;
75
+ readFileSync: (filePath: string) => string;
76
+ readFile?: (filePath: string) => Promise<string>;
77
+ includeStack?: string[];
78
+ /** Override the starting directory for `require.resolve` used by the default package resolver. */
79
+ resolveFrom?: string | string[];
80
+ /** Custom resolver for `include package(...)`. When provided, takes full control; `resolveFrom` is ignored. */
81
+ packageResolver?: PackageResolver;
82
+ /**
83
+ * When false (default), env var lookups inside SubstitutionResolver are
84
+ * suppressed. Set to true to preserve the current fused-resolve behaviour
85
+ * where process.env is consulted as fallback. Phase-2 callers (resolveTree)
86
+ * receive this flag explicitly; the fused resolve() path passes true for
87
+ * backward compat.
88
+ *
89
+ * E12 ResolveOptions.useSystemEnvironment.
90
+ */
91
+ useSystemEnvironment?: boolean;
92
+ /**
93
+ * When true, leaves unresolved (non-optional) substitution placeholders in
94
+ * place instead of throwing ResolveError. Phase 2 only; ignored by buildTree.
95
+ *
96
+ * E12 ResolveOptions.allowUnresolved.
97
+ */
98
+ allowUnresolved?: boolean;
99
+ /**
100
+ * Human-readable origin for error messages (e.g. file name or description).
101
+ * When set, ResolveError messages include this as a prefix to help callers
102
+ * identify which config source caused the error.
103
+ *
104
+ * E12 origin preservation.
105
+ */
106
+ originDescription?: string;
107
+ };
108
+
109
+ type ParseOptions = {
110
+ baseDir?: string;
111
+ env?: Record<string, string>;
112
+ readFile?: (filePath: string) => Promise<string>;
113
+ readFileSync?: (filePath: string) => string;
114
+ /** When true (default), run substitution resolution after parse. When false,
115
+ * return an unresolved Config with placeholders intact. E12 §"ParseOptions". */
116
+ resolveSubstitutions?: boolean;
117
+ /** Source name surfaced in error messages when no file path is available. E12. */
118
+ originDescription?: string;
119
+ /**
120
+ * Override the starting directory (or directories) used when resolving
121
+ * `include package("id", "file")` via Node module resolution.
122
+ *
123
+ * Default: `path.dirname(includingConfFile)` when known, `process.cwd()` otherwise.
124
+ * Ignored when `packageResolver` is also provided.
125
+ */
126
+ resolveFrom?: string | string[];
127
+ /**
128
+ * Custom resolver for `include package("id", "file")`.
129
+ * See {@link PackageResolver} for the full callback signature and contract
130
+ * (parameters, lookup-starting-path priority, error semantics).
131
+ *
132
+ * When provided, takes full control of resolution; `resolveFrom` is ignored.
133
+ * Use this for Yarn Berry PnP, bundler contexts, edge runtimes, or test isolation.
134
+ *
135
+ * Note: on macOS/Windows (case-insensitive filesystems), the default resolver may
136
+ * resolve case-insensitively for intermediate path segments. Use a custom
137
+ * `packageResolver` for strict cross-platform enforcement.
138
+ */
139
+ packageResolver?: PackageResolver;
140
+ };
141
+ type ResolveOptions = {
142
+ /** When true (default), env var lookups are available during resolve. E12. */
143
+ useSystemEnvironment?: boolean;
144
+ /** When true, leaves unresolved non-optional substitutions in place instead
145
+ * of throwing ResolveError. Default false. E12. */
146
+ allowUnresolved?: boolean;
147
+ };
148
+ /** Returns a ParseOptions object with Lightbend-aligned defaults:
149
+ * resolveSubstitutions=true, originDescription=undefined. */
150
+ declare function defaultParseOptions(): Required<Pick<ParseOptions, 'resolveSubstitutions'>> & ParseOptions;
151
+ /** Returns a ResolveOptions object with Lightbend-aligned defaults:
152
+ * useSystemEnvironment=true, allowUnresolved=false. */
153
+ declare function defaultResolveOptions(): Required<ResolveOptions>;
154
+ declare function parse(input: string, opts?: ParseOptions): Config;
155
+ /** Lightbend-aligned alias for parse(). Produces a fully resolved Config. */
156
+ declare const parseString: typeof parse;
157
+ /** Parse a HOCON string with explicit options. When opts.resolveSubstitutions
158
+ * is false, returns an unresolved Config with substitution placeholders intact.
159
+ * Use Config.resolve() or Config.resolveWith() to complete resolution later. */
160
+ declare function parseStringWithOptions(input: string, opts?: ParseOptions): Config;
161
+ /** Parse a HOCON file with explicit options. */
162
+ declare function parseFileWithOptions(filePath: string, opts?: ParseOptions): Config;
163
+ /**
164
+ * Truly async version of `parse()`. Include directives are resolved
165
+ * asynchronously via `readFile` when provided.
166
+ */
167
+ declare function parseAsync(input: string, opts?: ParseOptions): Promise<Config>;
168
+ declare function parseFile(filePath: string, opts?: ParseOptions): Config;
169
+ declare function parseFileAsync(filePath: string, opts?: ParseOptions): Promise<Config>;
170
+
171
+ declare class Config {
172
+ private readonly root;
173
+ /** @internal resolved flag: true when no substitution placeholders remain. */
174
+ private readonly _resolved;
175
+ /** @internal base directory for re-runs (file-parsed configs). */
176
+ readonly _parseBaseDir: string | undefined;
177
+ /** @internal origin description for error messages. */
178
+ readonly _originDescription: string | undefined;
179
+ /** @internal ResObj tree for deferred-resolution path. Set when resolved=false. */
180
+ readonly _resObjRoot: ResObj | undefined;
181
+ /** @internal ResolveOptions stored for the resolve() call. */
182
+ private readonly _resolveOpts;
183
+ constructor(root: HoconValue & {
184
+ kind: 'object';
185
+ }, opts?: {
186
+ resolved?: boolean;
187
+ parseBaseDir?: string;
188
+ originDescription?: string;
189
+ resObjRoot?: ResObj;
190
+ resolveOpts?: ResolveOptions$1;
191
+ });
192
+ /** Returns true when the Config's value tree contains no unresolved
193
+ * substitution placeholders. Whole-config granularity (E12 decision 11). */
194
+ isResolved(): boolean;
195
+ /** @internal Test-only: construct a resolved Config from a HoconValue. */
196
+ static _fromResolvedValue(root: HoconValue & {
197
+ kind: 'object';
198
+ }, opts?: {
199
+ originDescription?: string;
200
+ }): Config;
201
+ /** @internal Construct an unresolved Config carrying a ResObj tree.
202
+ * Used by parseStringWithOptions (deferred path) in T6. */
203
+ static _fromUnresolvedResObj(tree: ResObj, opts: {
204
+ parseBaseDir?: string;
205
+ originDescription?: string;
206
+ resolved: boolean;
207
+ resolveOpts: ResolveOptions$1;
208
+ }): Config;
209
+ get(path: string): unknown;
210
+ getString(path: string): string;
211
+ getNumber(path: string): number;
212
+ getBoolean(path: string): boolean;
213
+ getDuration(path: string, unit?: DurationUnit): number;
214
+ getBytes(path: string, unit?: ByteUnit): number;
215
+ getConfig(path: string): Config;
216
+ getList(path: string): unknown[];
217
+ has(path: string): boolean;
218
+ keys(): string[];
219
+ withFallback(fallback: Config | undefined): Config;
220
+ /**
221
+ * Runs substitution resolution (phase 2) on the stored unresolved tree.
222
+ * Idempotent: calling resolve() on an already-resolved Config returns an
223
+ * equivalent resolved Config.
224
+ *
225
+ * opts.allowUnresolved (default false) — when true, leaves unresolved
226
+ * non-optional placeholders in place rather than throwing.
227
+ * opts.useSystemEnvironment (default true) — when false, env var lookups
228
+ * are suppressed (hermetic resolution).
229
+ *
230
+ * E12 decision 3.
231
+ */
232
+ resolve(opts?: ResolveOptions): Config;
233
+ /**
234
+ * Resolves receiver substitutions using source as lookup context.
235
+ * Source's keys are NOT merged into the result — differs from
236
+ * `this.withFallback(source).resolve()` which includes source keys.
237
+ *
238
+ * Precondition (E12 decision 10): source must be resolved. If
239
+ * source.isResolved() is false, throws NotResolvedError.
240
+ *
241
+ * On an already-resolved receiver, resolveWith is a no-op (returns an
242
+ * equivalent resolved Config, source keys not included).
243
+ *
244
+ * E12 decisions 9, 10.
245
+ */
246
+ resolveWith(source: Config, opts?: ResolveOptions): Config;
247
+ toObject(): unknown;
248
+ /**
249
+ * @internal Test-only: renders this resolved Config's value tree as canonical
250
+ * JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
251
+ * against Lightbend ground truth. NOT part of the public API.
252
+ *
253
+ * Throws if the Config is unresolved (contains placeholders).
254
+ */
255
+ _renderJSONForTest(): string;
256
+ private lookupNode;
257
+ /**
258
+ * _resObjRootSubtreeHasPlaceholders — walk _resObjRoot to the given path and
259
+ * return true if the subtree there contains any unresolved placeholder.
260
+ * Used by getConfig / getList (T2) to detect transitive unresolved placeholders
261
+ * in object / array subtrees that would otherwise appear to be "resolved" in the
262
+ * partial HoconValue root (because individual leaf fields are omitted rather than
263
+ * the whole subtree when their value is a direct placeholder scalar, but object
264
+ * sub-containers are retained in the partial root with the placeholder-keyed
265
+ * fields omitted from them — so the object appears "present but empty-ish").
266
+ */
267
+ private _resObjRootSubtreeHasPlaceholders;
268
+ private requireScalar;
269
+ }
270
+
271
+ export { type ByteUnit as B, Config as C, type DurationUnit as D, type HoconValue as H, type PackageResolver as P, type ResolveOptions as R, type ScalarValueType as S, type ParseOptions as a, defaultResolveOptions as b, parseAsync as c, defaultParseOptions as d, parseFile as e, parseFileAsync as f, parseFileWithOptions as g, parseString as h, parseStringWithOptions as i, parse as p };
@@ -0,0 +1,271 @@
1
+ type DurationUnit = 'ns' | 'us' | 'ms' | 's' | 'm' | 'h' | 'd';
2
+ type ByteUnit = 'B' | 'KB' | 'KiB' | 'MB' | 'MiB' | 'GB' | 'GiB' | 'TB' | 'TiB';
3
+
4
+ type ScalarValueType = 'string' | 'number' | 'boolean' | 'null';
5
+ type HoconValue = {
6
+ kind: 'object';
7
+ fields: Map<string, HoconValue>;
8
+ } | {
9
+ kind: 'array';
10
+ items: HoconValue[];
11
+ } | {
12
+ kind: 'scalar';
13
+ raw: string;
14
+ valueType: ScalarValueType;
15
+ };
16
+
17
+ interface Segment {
18
+ readonly text: string;
19
+ readonly line: number;
20
+ readonly col: number;
21
+ }
22
+
23
+ type SubstPlaceholder = {
24
+ _kind: 'subst-placeholder';
25
+ segments: Segment[];
26
+ optional: boolean;
27
+ listSuffix: boolean;
28
+ line: number;
29
+ col: number;
30
+ prefixLen: number;
31
+ };
32
+ type ConcatPlaceholder = {
33
+ _kind: 'concat-placeholder';
34
+ nodes: ResolverValue[];
35
+ /** 1-based line of the concat value in the source file (from AST Concat pos). */
36
+ line: number;
37
+ /** 1-based column of the concat value in the source file (from AST Concat pos). */
38
+ col: number;
39
+ };
40
+ type AppendPlaceholder = {
41
+ _kind: 'append-placeholder';
42
+ existing: ResolverValue;
43
+ elem: ResolverValue;
44
+ };
45
+ type ResObj = {
46
+ _kind: 'res-obj';
47
+ fields: Map<string, ResolverValue>;
48
+ priorValues: Map<string, ResolverValue>;
49
+ };
50
+ type ResolverValue = HoconValue | SubstPlaceholder | ConcatPlaceholder | AppendPlaceholder | ResObj;
51
+ /**
52
+ * Custom resolver for `include package("id", "file")`.
53
+ *
54
+ * Receives:
55
+ * - `identifier`, `file`: the qualifier arguments (post HOCON unescape, byte-exact)
56
+ * - `includingFile`: absolute path of the including `.conf` file when known
57
+ * (currently always `undefined`; reserved for future threading from a future
58
+ * `parseFile` integration)
59
+ * - `baseDir`: the directory context active when the include is resolved
60
+ * (typically `path.dirname(includingFile)` of the parent, or the caller-set
61
+ * `baseDir` parse option). Set when known, `undefined` for parses of literal
62
+ * input strings without a directory context.
63
+ *
64
+ * Must return an absolute path or throw.
65
+ *
66
+ * When not provided, the default resolver uses `createRequire(import.meta.url)`
67
+ * which works in both CJS and ESM Node contexts. The default resolver picks the
68
+ * lookup starting paths in priority order:
69
+ * `resolveFrom` > `baseDir` > `path.dirname(includingFile)` > `process.cwd()`
70
+ */
71
+ type PackageResolver = (identifier: string, file: string, includingFile: string | undefined, baseDir: string | undefined) => string;
72
+ type ResolveOptions$1 = {
73
+ env: Record<string, string>;
74
+ baseDir: string | undefined;
75
+ readFileSync: (filePath: string) => string;
76
+ readFile?: (filePath: string) => Promise<string>;
77
+ includeStack?: string[];
78
+ /** Override the starting directory for `require.resolve` used by the default package resolver. */
79
+ resolveFrom?: string | string[];
80
+ /** Custom resolver for `include package(...)`. When provided, takes full control; `resolveFrom` is ignored. */
81
+ packageResolver?: PackageResolver;
82
+ /**
83
+ * When false (default), env var lookups inside SubstitutionResolver are
84
+ * suppressed. Set to true to preserve the current fused-resolve behaviour
85
+ * where process.env is consulted as fallback. Phase-2 callers (resolveTree)
86
+ * receive this flag explicitly; the fused resolve() path passes true for
87
+ * backward compat.
88
+ *
89
+ * E12 ResolveOptions.useSystemEnvironment.
90
+ */
91
+ useSystemEnvironment?: boolean;
92
+ /**
93
+ * When true, leaves unresolved (non-optional) substitution placeholders in
94
+ * place instead of throwing ResolveError. Phase 2 only; ignored by buildTree.
95
+ *
96
+ * E12 ResolveOptions.allowUnresolved.
97
+ */
98
+ allowUnresolved?: boolean;
99
+ /**
100
+ * Human-readable origin for error messages (e.g. file name or description).
101
+ * When set, ResolveError messages include this as a prefix to help callers
102
+ * identify which config source caused the error.
103
+ *
104
+ * E12 origin preservation.
105
+ */
106
+ originDescription?: string;
107
+ };
108
+
109
+ type ParseOptions = {
110
+ baseDir?: string;
111
+ env?: Record<string, string>;
112
+ readFile?: (filePath: string) => Promise<string>;
113
+ readFileSync?: (filePath: string) => string;
114
+ /** When true (default), run substitution resolution after parse. When false,
115
+ * return an unresolved Config with placeholders intact. E12 §"ParseOptions". */
116
+ resolveSubstitutions?: boolean;
117
+ /** Source name surfaced in error messages when no file path is available. E12. */
118
+ originDescription?: string;
119
+ /**
120
+ * Override the starting directory (or directories) used when resolving
121
+ * `include package("id", "file")` via Node module resolution.
122
+ *
123
+ * Default: `path.dirname(includingConfFile)` when known, `process.cwd()` otherwise.
124
+ * Ignored when `packageResolver` is also provided.
125
+ */
126
+ resolveFrom?: string | string[];
127
+ /**
128
+ * Custom resolver for `include package("id", "file")`.
129
+ * See {@link PackageResolver} for the full callback signature and contract
130
+ * (parameters, lookup-starting-path priority, error semantics).
131
+ *
132
+ * When provided, takes full control of resolution; `resolveFrom` is ignored.
133
+ * Use this for Yarn Berry PnP, bundler contexts, edge runtimes, or test isolation.
134
+ *
135
+ * Note: on macOS/Windows (case-insensitive filesystems), the default resolver may
136
+ * resolve case-insensitively for intermediate path segments. Use a custom
137
+ * `packageResolver` for strict cross-platform enforcement.
138
+ */
139
+ packageResolver?: PackageResolver;
140
+ };
141
+ type ResolveOptions = {
142
+ /** When true (default), env var lookups are available during resolve. E12. */
143
+ useSystemEnvironment?: boolean;
144
+ /** When true, leaves unresolved non-optional substitutions in place instead
145
+ * of throwing ResolveError. Default false. E12. */
146
+ allowUnresolved?: boolean;
147
+ };
148
+ /** Returns a ParseOptions object with Lightbend-aligned defaults:
149
+ * resolveSubstitutions=true, originDescription=undefined. */
150
+ declare function defaultParseOptions(): Required<Pick<ParseOptions, 'resolveSubstitutions'>> & ParseOptions;
151
+ /** Returns a ResolveOptions object with Lightbend-aligned defaults:
152
+ * useSystemEnvironment=true, allowUnresolved=false. */
153
+ declare function defaultResolveOptions(): Required<ResolveOptions>;
154
+ declare function parse(input: string, opts?: ParseOptions): Config;
155
+ /** Lightbend-aligned alias for parse(). Produces a fully resolved Config. */
156
+ declare const parseString: typeof parse;
157
+ /** Parse a HOCON string with explicit options. When opts.resolveSubstitutions
158
+ * is false, returns an unresolved Config with substitution placeholders intact.
159
+ * Use Config.resolve() or Config.resolveWith() to complete resolution later. */
160
+ declare function parseStringWithOptions(input: string, opts?: ParseOptions): Config;
161
+ /** Parse a HOCON file with explicit options. */
162
+ declare function parseFileWithOptions(filePath: string, opts?: ParseOptions): Config;
163
+ /**
164
+ * Truly async version of `parse()`. Include directives are resolved
165
+ * asynchronously via `readFile` when provided.
166
+ */
167
+ declare function parseAsync(input: string, opts?: ParseOptions): Promise<Config>;
168
+ declare function parseFile(filePath: string, opts?: ParseOptions): Config;
169
+ declare function parseFileAsync(filePath: string, opts?: ParseOptions): Promise<Config>;
170
+
171
+ declare class Config {
172
+ private readonly root;
173
+ /** @internal resolved flag: true when no substitution placeholders remain. */
174
+ private readonly _resolved;
175
+ /** @internal base directory for re-runs (file-parsed configs). */
176
+ readonly _parseBaseDir: string | undefined;
177
+ /** @internal origin description for error messages. */
178
+ readonly _originDescription: string | undefined;
179
+ /** @internal ResObj tree for deferred-resolution path. Set when resolved=false. */
180
+ readonly _resObjRoot: ResObj | undefined;
181
+ /** @internal ResolveOptions stored for the resolve() call. */
182
+ private readonly _resolveOpts;
183
+ constructor(root: HoconValue & {
184
+ kind: 'object';
185
+ }, opts?: {
186
+ resolved?: boolean;
187
+ parseBaseDir?: string;
188
+ originDescription?: string;
189
+ resObjRoot?: ResObj;
190
+ resolveOpts?: ResolveOptions$1;
191
+ });
192
+ /** Returns true when the Config's value tree contains no unresolved
193
+ * substitution placeholders. Whole-config granularity (E12 decision 11). */
194
+ isResolved(): boolean;
195
+ /** @internal Test-only: construct a resolved Config from a HoconValue. */
196
+ static _fromResolvedValue(root: HoconValue & {
197
+ kind: 'object';
198
+ }, opts?: {
199
+ originDescription?: string;
200
+ }): Config;
201
+ /** @internal Construct an unresolved Config carrying a ResObj tree.
202
+ * Used by parseStringWithOptions (deferred path) in T6. */
203
+ static _fromUnresolvedResObj(tree: ResObj, opts: {
204
+ parseBaseDir?: string;
205
+ originDescription?: string;
206
+ resolved: boolean;
207
+ resolveOpts: ResolveOptions$1;
208
+ }): Config;
209
+ get(path: string): unknown;
210
+ getString(path: string): string;
211
+ getNumber(path: string): number;
212
+ getBoolean(path: string): boolean;
213
+ getDuration(path: string, unit?: DurationUnit): number;
214
+ getBytes(path: string, unit?: ByteUnit): number;
215
+ getConfig(path: string): Config;
216
+ getList(path: string): unknown[];
217
+ has(path: string): boolean;
218
+ keys(): string[];
219
+ withFallback(fallback: Config | undefined): Config;
220
+ /**
221
+ * Runs substitution resolution (phase 2) on the stored unresolved tree.
222
+ * Idempotent: calling resolve() on an already-resolved Config returns an
223
+ * equivalent resolved Config.
224
+ *
225
+ * opts.allowUnresolved (default false) — when true, leaves unresolved
226
+ * non-optional placeholders in place rather than throwing.
227
+ * opts.useSystemEnvironment (default true) — when false, env var lookups
228
+ * are suppressed (hermetic resolution).
229
+ *
230
+ * E12 decision 3.
231
+ */
232
+ resolve(opts?: ResolveOptions): Config;
233
+ /**
234
+ * Resolves receiver substitutions using source as lookup context.
235
+ * Source's keys are NOT merged into the result — differs from
236
+ * `this.withFallback(source).resolve()` which includes source keys.
237
+ *
238
+ * Precondition (E12 decision 10): source must be resolved. If
239
+ * source.isResolved() is false, throws NotResolvedError.
240
+ *
241
+ * On an already-resolved receiver, resolveWith is a no-op (returns an
242
+ * equivalent resolved Config, source keys not included).
243
+ *
244
+ * E12 decisions 9, 10.
245
+ */
246
+ resolveWith(source: Config, opts?: ResolveOptions): Config;
247
+ toObject(): unknown;
248
+ /**
249
+ * @internal Test-only: renders this resolved Config's value tree as canonical
250
+ * JSON (sorted keys, no whitespace). Used by Layer-2 fixture tests to compare
251
+ * against Lightbend ground truth. NOT part of the public API.
252
+ *
253
+ * Throws if the Config is unresolved (contains placeholders).
254
+ */
255
+ _renderJSONForTest(): string;
256
+ private lookupNode;
257
+ /**
258
+ * _resObjRootSubtreeHasPlaceholders — walk _resObjRoot to the given path and
259
+ * return true if the subtree there contains any unresolved placeholder.
260
+ * Used by getConfig / getList (T2) to detect transitive unresolved placeholders
261
+ * in object / array subtrees that would otherwise appear to be "resolved" in the
262
+ * partial HoconValue root (because individual leaf fields are omitted rather than
263
+ * the whole subtree when their value is a direct placeholder scalar, but object
264
+ * sub-containers are retained in the partial root with the placeholder-keyed
265
+ * fields omitted from them — so the object appears "present but empty-ish").
266
+ */
267
+ private _resObjRootSubtreeHasPlaceholders;
268
+ private requireScalar;
269
+ }
270
+
271
+ export { type ByteUnit as B, Config as C, type DurationUnit as D, type HoconValue as H, type PackageResolver as P, type ResolveOptions as R, type ScalarValueType as S, type ParseOptions as a, defaultResolveOptions as b, parseAsync as c, defaultParseOptions as d, parseFile as e, parseFileAsync as f, parseFileWithOptions as g, parseString as h, parseStringWithOptions as i, parse as p };