@o3co/ts.hocon 0.1.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/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
- package/.github/pull_request_template.md +17 -0
- package/.github/workflows/lint.yml +33 -0
- package/.github/workflows/release.yml +41 -0
- package/.github/workflows/test.yml +53 -0
- package/CHANGELOG.md +22 -0
- package/CONTRIBUTING.md +62 -0
- package/LICENSE +202 -0
- package/README.ja.md +195 -0
- package/README.md +195 -0
- package/SECURITY.md +20 -0
- package/docs/superpowers/plans/2026-03-20-ts-hocon.md +2412 -0
- package/docs/superpowers/specs/2026-03-20-ts-hocon-design.md +368 -0
- package/package.json +44 -0
- package/src/config.ts +108 -0
- package/src/errors.ts +34 -0
- package/src/index.ts +5 -0
- package/src/internal/lexer/lexer.ts +168 -0
- package/src/internal/lexer/token.ts +20 -0
- package/src/internal/parser/ast.ts +17 -0
- package/src/internal/parser/parser.ts +229 -0
- package/src/internal/resolver/resolver.ts +428 -0
- package/src/parse.ts +79 -0
- package/src/value.ts +5 -0
- package/src/zod.ts +12 -0
- package/tests/config.test.ts +100 -0
- package/tests/errors.test.ts +43 -0
- package/tests/lexer.test.ts +141 -0
- package/tests/lightbend/lightbend.test.ts +39 -0
- package/tests/lightbend/testdata/a_1.conf +1 -0
- package/tests/lightbend/testdata/b_2.conf +1 -0
- package/tests/lightbend/testdata/beanconfig/beanconfig01.conf +131 -0
- package/tests/lightbend/testdata/bom.conf +2 -0
- package/tests/lightbend/testdata/cycle.conf +1 -0
- package/tests/lightbend/testdata/env-variables.conf +16 -0
- package/tests/lightbend/testdata/equiv01/comments.conf +45 -0
- package/tests/lightbend/testdata/equiv01/equals.conf +41 -0
- package/tests/lightbend/testdata/equiv01/no-commas.conf +55 -0
- package/tests/lightbend/testdata/equiv01/no-root-braces.conf +40 -0
- package/tests/lightbend/testdata/equiv01/no-whitespace.json +1 -0
- package/tests/lightbend/testdata/equiv01/omit-colons.conf +44 -0
- package/tests/lightbend/testdata/equiv01/original.json +41 -0
- package/tests/lightbend/testdata/equiv01/path-keys.conf +29 -0
- package/tests/lightbend/testdata/equiv01/properties-style.conf +37 -0
- package/tests/lightbend/testdata/equiv01/substitutions.conf +41 -0
- package/tests/lightbend/testdata/equiv01/unquoted.conf +41 -0
- package/tests/lightbend/testdata/equiv02/original.json +17 -0
- package/tests/lightbend/testdata/equiv02/path-keys-weird-whitespace.conf +32 -0
- package/tests/lightbend/testdata/equiv02/path-keys.conf +9 -0
- package/tests/lightbend/testdata/equiv03/includes.conf +7 -0
- package/tests/lightbend/testdata/equiv03/letters/a.conf +6 -0
- package/tests/lightbend/testdata/equiv03/letters/b.json +3 -0
- package/tests/lightbend/testdata/equiv03/letters/c.conf +2 -0
- package/tests/lightbend/testdata/equiv03/letters/c.properties +1 -0
- package/tests/lightbend/testdata/equiv03/letters/numbers/1.conf +1 -0
- package/tests/lightbend/testdata/equiv03/letters/numbers/2.properties +1 -0
- package/tests/lightbend/testdata/equiv03/original.json +15 -0
- package/tests/lightbend/testdata/equiv03/root/foo.conf +1 -0
- package/tests/lightbend/testdata/equiv04/missing-substitutions.conf +4 -0
- package/tests/lightbend/testdata/equiv04/original.json +3 -0
- package/tests/lightbend/testdata/equiv05/original.json +16 -0
- package/tests/lightbend/testdata/equiv05/triple-quotes.conf +20 -0
- package/tests/lightbend/testdata/file-include.conf +5 -0
- package/tests/lightbend/testdata/include-from-list.conf +5 -0
- package/tests/lightbend/testdata/onclasspath.conf +2 -0
- package/tests/lightbend/testdata/subdir/bar-file.conf +1 -0
- package/tests/lightbend/testdata/subdir/bar.conf +1 -0
- package/tests/lightbend/testdata/subdir/baz.conf +1 -0
- package/tests/lightbend/testdata/subdir/foo.conf +5 -0
- package/tests/lightbend/testdata/test01.conf +96 -0
- package/tests/lightbend/testdata/test01.json +4 -0
- package/tests/lightbend/testdata/test01.properties +5 -0
- package/tests/lightbend/testdata/test02.conf +13 -0
- package/tests/lightbend/testdata/test03-included.conf +9 -0
- package/tests/lightbend/testdata/test03.conf +36 -0
- package/tests/lightbend/testdata/test04.conf +305 -0
- package/tests/lightbend/testdata/test05.conf +154 -0
- package/tests/lightbend/testdata/test06.conf +14 -0
- package/tests/lightbend/testdata/test07.conf +4 -0
- package/tests/lightbend/testdata/test08.conf +6 -0
- package/tests/lightbend/testdata/test09.conf +17 -0
- package/tests/lightbend/testdata/test10.conf +11 -0
- package/tests/lightbend/testdata/test11.conf +7 -0
- package/tests/lightbend/testdata/test12.conf +43 -0
- package/tests/lightbend/testdata/test13-application-override-substitutions.conf +1 -0
- package/tests/lightbend/testdata/test13-reference-bad-substitutions.conf +1 -0
- package/tests/lightbend/testdata/test13-reference-with-substitutions.conf +2 -0
- package/tests/lightbend/testdata/validate-invalid.conf +30 -0
- package/tests/lightbend/testdata/validate-reference.conf +32 -0
- package/tests/parse.test.ts +44 -0
- package/tests/parser.test.ts +154 -0
- package/tests/resolver.test.ts +159 -0
- package/tests/zod.test.ts +57 -0
- package/tsconfig.json +16 -0
- package/tsup.config.ts +12 -0
- package/vitest.config.ts +7 -0
package/README.md
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# ts.hocon
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@o3co/ts.hocon)
|
|
4
|
+
[](https://github.com/o3co/ts.hocon/actions/workflows/test.yml)
|
|
5
|
+
[](https://codecov.io/gh/o3co/ts.hocon)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
A full [Lightbend HOCON](https://github.com/lightbend/config/blob/main/HOCON.md) spec-compliant TypeScript library.
|
|
9
|
+
|
|
10
|
+
> **Implemented by [Claude](https://claude.ai/) (Anthropic)** — designed and built end-to-end with Claude Code.
|
|
11
|
+
|
|
12
|
+
[日本語](README.ja.md)
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- Full HOCON parsing: objects, arrays, scalars, substitutions (`${path}`, `${?path}`)
|
|
19
|
+
- Self-referential substitutions (`path = ${path}:/extra`)
|
|
20
|
+
- Deep-merge for duplicate keys (last definition wins)
|
|
21
|
+
- `+=` append operator
|
|
22
|
+
- `include "file.conf"` and `include file("file.conf")` directives
|
|
23
|
+
- Triple-quoted strings (`"""..."""`)
|
|
24
|
+
- Sync and async API (`parse` / `parseAsync` / `parseFile` / `parseFileAsync`)
|
|
25
|
+
- ESM + CJS dual package
|
|
26
|
+
- Optional [Zod](https://zod.dev/) integration for schema validation
|
|
27
|
+
- Browser compatible (`parse`/`parseAsync` — no Node.js required)
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @o3co/ts.hocon
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Requires Node.js 18+.
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { parse, parseFile } from '@o3co/ts.hocon'
|
|
41
|
+
|
|
42
|
+
// Parse from string
|
|
43
|
+
const cfg = parse(`
|
|
44
|
+
server {
|
|
45
|
+
host = "localhost"
|
|
46
|
+
port = 8080
|
|
47
|
+
}
|
|
48
|
+
`)
|
|
49
|
+
|
|
50
|
+
// Parse from file
|
|
51
|
+
const cfg = parseFile('application.conf')
|
|
52
|
+
|
|
53
|
+
// Scalar getters (throw ConfigError on missing/wrong type)
|
|
54
|
+
const host = cfg.getString('server.host') // "localhost"
|
|
55
|
+
const port = cfg.getNumber('server.port') // 8080
|
|
56
|
+
|
|
57
|
+
// Safe access
|
|
58
|
+
const host = cfg.get('server.host') // unknown | undefined
|
|
59
|
+
const exists = cfg.has('server.host') // true
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API
|
|
63
|
+
|
|
64
|
+
### Parse functions
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { parse, parseAsync, parseFile, parseFileAsync } from '@o3co/ts.hocon'
|
|
68
|
+
import type { ParseOptions } from '@o3co/ts.hocon'
|
|
69
|
+
|
|
70
|
+
parse(input: string, opts?: ParseOptions): Config
|
|
71
|
+
parseAsync(input: string, opts?: ParseOptions): Promise<Config>
|
|
72
|
+
parseFile(path: string, opts?: ParseOptions): Config
|
|
73
|
+
parseFileAsync(path: string, opts?: ParseOptions): Promise<Config>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`ParseOptions`:
|
|
77
|
+
| Option | Type | Description |
|
|
78
|
+
|--------|------|-------------|
|
|
79
|
+
| `baseDir` | `string` | Base directory for `include` resolution |
|
|
80
|
+
| `env` | `Record<string, string>` | Environment variables for substitution (default: `process.env`) |
|
|
81
|
+
| `readFileSync` | `(path: string) => string` | Custom file reader (sync) |
|
|
82
|
+
| `readFile` | `(path: string) => Promise<string>` | Custom file reader (async) |
|
|
83
|
+
|
|
84
|
+
### Config methods
|
|
85
|
+
|
|
86
|
+
| Method | Returns | Throws if |
|
|
87
|
+
|--------|---------|-----------|
|
|
88
|
+
| `get(path)` | `unknown \| undefined` | — |
|
|
89
|
+
| `getString(path)` | `string` | missing, wrong type |
|
|
90
|
+
| `getNumber(path)` | `number` | missing, wrong type |
|
|
91
|
+
| `getBoolean(path)` | `boolean` | missing, wrong type |
|
|
92
|
+
| `getConfig(path)` | `Config` | missing, not an object |
|
|
93
|
+
| `getList(path)` | `unknown[]` | missing, not an array |
|
|
94
|
+
| `has(path)` | `boolean` | — |
|
|
95
|
+
| `keys()` | `string[]` | — |
|
|
96
|
+
| `withFallback(fallback)` | `Config` | — |
|
|
97
|
+
| `toObject()` | `unknown` | — |
|
|
98
|
+
|
|
99
|
+
### Zod integration
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { validate, getValidated } from '@o3co/ts.hocon/zod'
|
|
103
|
+
import { z } from 'zod'
|
|
104
|
+
|
|
105
|
+
const Schema = z.object({
|
|
106
|
+
server: z.object({
|
|
107
|
+
host: z.string(),
|
|
108
|
+
port: z.number().int(),
|
|
109
|
+
}),
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Validate entire config
|
|
113
|
+
const app = validate(cfg, Schema)
|
|
114
|
+
|
|
115
|
+
// Validate a single path
|
|
116
|
+
const port = getValidated(cfg, 'server.port', z.number().int())
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Install Zod as a peer dependency:
|
|
120
|
+
```bash
|
|
121
|
+
npm install zod
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Error types
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { ParseError, ResolveError, ConfigError } from '@o3co/ts.hocon'
|
|
128
|
+
|
|
129
|
+
// ParseError — lexing/parsing failure: .line, .col, .file?
|
|
130
|
+
// ResolveError — substitution/include failure: .path, .line, .col, .file?
|
|
131
|
+
// ConfigError — wrong type or missing path: .path
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## HOCON Examples
|
|
135
|
+
|
|
136
|
+
```hocon
|
|
137
|
+
# Comments with # or //
|
|
138
|
+
database {
|
|
139
|
+
host = "db.example.com"
|
|
140
|
+
port = 5432
|
|
141
|
+
url = "jdbc:"${database.host}":"${database.port}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Duplicate keys deep-merge (last wins for scalars)
|
|
145
|
+
server { host = localhost }
|
|
146
|
+
server { port = 8080 } // result: { host: "localhost", port: 8080 }
|
|
147
|
+
|
|
148
|
+
# Self-referential append
|
|
149
|
+
path = "/usr/bin"
|
|
150
|
+
path = ${path}":/usr/local/bin"
|
|
151
|
+
|
|
152
|
+
# += shorthand
|
|
153
|
+
items = [1]
|
|
154
|
+
items += 2
|
|
155
|
+
items += 3 // [1, 2, 3]
|
|
156
|
+
|
|
157
|
+
# Include
|
|
158
|
+
include "defaults.conf"
|
|
159
|
+
include file("overrides.conf")
|
|
160
|
+
|
|
161
|
+
# Triple-quoted multiline strings
|
|
162
|
+
description = """
|
|
163
|
+
This is a
|
|
164
|
+
multiline string.
|
|
165
|
+
"""
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Spec Compliance
|
|
169
|
+
|
|
170
|
+
Tested against the [Lightbend official test suite](https://github.com/lightbend/config/tree/main/config/src/test/resources): **13/13 test groups pass**.
|
|
171
|
+
|
|
172
|
+
Not supported in v0.1.0:
|
|
173
|
+
- `include url(...)`
|
|
174
|
+
- `include classpath(...)`
|
|
175
|
+
- `.properties` file parsing
|
|
176
|
+
|
|
177
|
+
## Browser Compatibility
|
|
178
|
+
|
|
179
|
+
`parse()` and `parseAsync()` work in browsers. `parseFile()` and `parseFileAsync()` require Node.js (or a custom `readFileSync`/`readFile` option).
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
// Browser usage with custom file loader
|
|
183
|
+
const cfg = await parseAsync(hoconString, {
|
|
184
|
+
readFile: async (path) => {
|
|
185
|
+
const res = await fetch(`/config/${path}`)
|
|
186
|
+
return res.text()
|
|
187
|
+
},
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## License
|
|
192
|
+
|
|
193
|
+
Apache License 2.0 — see [LICENSE](LICENSE).
|
|
194
|
+
|
|
195
|
+
Copyright 2026 o3co Inc.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
|---------|-----------|
|
|
7
|
+
| latest | ✅ |
|
|
8
|
+
|
|
9
|
+
## Reporting a vulnerability
|
|
10
|
+
|
|
11
|
+
Please **do not** open a public GitHub issue for security vulnerabilities.
|
|
12
|
+
|
|
13
|
+
Report vulnerabilities privately via [GitHub Security Advisories](https://github.com/o3co/ts.hocon/security/advisories/new).
|
|
14
|
+
|
|
15
|
+
Include:
|
|
16
|
+
- Description of the vulnerability
|
|
17
|
+
- Steps to reproduce
|
|
18
|
+
- Potential impact
|
|
19
|
+
|
|
20
|
+
You will receive a response within 7 days.
|