@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.
Files changed (97) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +23 -0
  3. package/.github/pull_request_template.md +17 -0
  4. package/.github/workflows/lint.yml +33 -0
  5. package/.github/workflows/release.yml +41 -0
  6. package/.github/workflows/test.yml +53 -0
  7. package/CHANGELOG.md +22 -0
  8. package/CONTRIBUTING.md +62 -0
  9. package/LICENSE +202 -0
  10. package/README.ja.md +195 -0
  11. package/README.md +195 -0
  12. package/SECURITY.md +20 -0
  13. package/docs/superpowers/plans/2026-03-20-ts-hocon.md +2412 -0
  14. package/docs/superpowers/specs/2026-03-20-ts-hocon-design.md +368 -0
  15. package/package.json +44 -0
  16. package/src/config.ts +108 -0
  17. package/src/errors.ts +34 -0
  18. package/src/index.ts +5 -0
  19. package/src/internal/lexer/lexer.ts +168 -0
  20. package/src/internal/lexer/token.ts +20 -0
  21. package/src/internal/parser/ast.ts +17 -0
  22. package/src/internal/parser/parser.ts +229 -0
  23. package/src/internal/resolver/resolver.ts +428 -0
  24. package/src/parse.ts +79 -0
  25. package/src/value.ts +5 -0
  26. package/src/zod.ts +12 -0
  27. package/tests/config.test.ts +100 -0
  28. package/tests/errors.test.ts +43 -0
  29. package/tests/lexer.test.ts +141 -0
  30. package/tests/lightbend/lightbend.test.ts +39 -0
  31. package/tests/lightbend/testdata/a_1.conf +1 -0
  32. package/tests/lightbend/testdata/b_2.conf +1 -0
  33. package/tests/lightbend/testdata/beanconfig/beanconfig01.conf +131 -0
  34. package/tests/lightbend/testdata/bom.conf +2 -0
  35. package/tests/lightbend/testdata/cycle.conf +1 -0
  36. package/tests/lightbend/testdata/env-variables.conf +16 -0
  37. package/tests/lightbend/testdata/equiv01/comments.conf +45 -0
  38. package/tests/lightbend/testdata/equiv01/equals.conf +41 -0
  39. package/tests/lightbend/testdata/equiv01/no-commas.conf +55 -0
  40. package/tests/lightbend/testdata/equiv01/no-root-braces.conf +40 -0
  41. package/tests/lightbend/testdata/equiv01/no-whitespace.json +1 -0
  42. package/tests/lightbend/testdata/equiv01/omit-colons.conf +44 -0
  43. package/tests/lightbend/testdata/equiv01/original.json +41 -0
  44. package/tests/lightbend/testdata/equiv01/path-keys.conf +29 -0
  45. package/tests/lightbend/testdata/equiv01/properties-style.conf +37 -0
  46. package/tests/lightbend/testdata/equiv01/substitutions.conf +41 -0
  47. package/tests/lightbend/testdata/equiv01/unquoted.conf +41 -0
  48. package/tests/lightbend/testdata/equiv02/original.json +17 -0
  49. package/tests/lightbend/testdata/equiv02/path-keys-weird-whitespace.conf +32 -0
  50. package/tests/lightbend/testdata/equiv02/path-keys.conf +9 -0
  51. package/tests/lightbend/testdata/equiv03/includes.conf +7 -0
  52. package/tests/lightbend/testdata/equiv03/letters/a.conf +6 -0
  53. package/tests/lightbend/testdata/equiv03/letters/b.json +3 -0
  54. package/tests/lightbend/testdata/equiv03/letters/c.conf +2 -0
  55. package/tests/lightbend/testdata/equiv03/letters/c.properties +1 -0
  56. package/tests/lightbend/testdata/equiv03/letters/numbers/1.conf +1 -0
  57. package/tests/lightbend/testdata/equiv03/letters/numbers/2.properties +1 -0
  58. package/tests/lightbend/testdata/equiv03/original.json +15 -0
  59. package/tests/lightbend/testdata/equiv03/root/foo.conf +1 -0
  60. package/tests/lightbend/testdata/equiv04/missing-substitutions.conf +4 -0
  61. package/tests/lightbend/testdata/equiv04/original.json +3 -0
  62. package/tests/lightbend/testdata/equiv05/original.json +16 -0
  63. package/tests/lightbend/testdata/equiv05/triple-quotes.conf +20 -0
  64. package/tests/lightbend/testdata/file-include.conf +5 -0
  65. package/tests/lightbend/testdata/include-from-list.conf +5 -0
  66. package/tests/lightbend/testdata/onclasspath.conf +2 -0
  67. package/tests/lightbend/testdata/subdir/bar-file.conf +1 -0
  68. package/tests/lightbend/testdata/subdir/bar.conf +1 -0
  69. package/tests/lightbend/testdata/subdir/baz.conf +1 -0
  70. package/tests/lightbend/testdata/subdir/foo.conf +5 -0
  71. package/tests/lightbend/testdata/test01.conf +96 -0
  72. package/tests/lightbend/testdata/test01.json +4 -0
  73. package/tests/lightbend/testdata/test01.properties +5 -0
  74. package/tests/lightbend/testdata/test02.conf +13 -0
  75. package/tests/lightbend/testdata/test03-included.conf +9 -0
  76. package/tests/lightbend/testdata/test03.conf +36 -0
  77. package/tests/lightbend/testdata/test04.conf +305 -0
  78. package/tests/lightbend/testdata/test05.conf +154 -0
  79. package/tests/lightbend/testdata/test06.conf +14 -0
  80. package/tests/lightbend/testdata/test07.conf +4 -0
  81. package/tests/lightbend/testdata/test08.conf +6 -0
  82. package/tests/lightbend/testdata/test09.conf +17 -0
  83. package/tests/lightbend/testdata/test10.conf +11 -0
  84. package/tests/lightbend/testdata/test11.conf +7 -0
  85. package/tests/lightbend/testdata/test12.conf +43 -0
  86. package/tests/lightbend/testdata/test13-application-override-substitutions.conf +1 -0
  87. package/tests/lightbend/testdata/test13-reference-bad-substitutions.conf +1 -0
  88. package/tests/lightbend/testdata/test13-reference-with-substitutions.conf +2 -0
  89. package/tests/lightbend/testdata/validate-invalid.conf +30 -0
  90. package/tests/lightbend/testdata/validate-reference.conf +32 -0
  91. package/tests/parse.test.ts +44 -0
  92. package/tests/parser.test.ts +154 -0
  93. package/tests/resolver.test.ts +159 -0
  94. package/tests/zod.test.ts +57 -0
  95. package/tsconfig.json +16 -0
  96. package/tsup.config.ts +12 -0
  97. package/vitest.config.ts +7 -0
@@ -0,0 +1,41 @@
1
+ {
2
+ "ints" : {
3
+ "fortyTwo" : 42,
4
+ "fortyTwoAgain" : ${ints.fortyTwo}
5
+ },
6
+
7
+ "floats" : {
8
+ "fortyTwoPointOne" : 42.1,
9
+ "fortyTwoPointOneAgain" : ${floats.fortyTwoPointOne}
10
+ },
11
+
12
+ "strings" : {
13
+ "abcd" : "abcd",
14
+ "abcdAgain" : ${strings.a}${strings.b}${strings.c}${strings.d},
15
+ "a" : "a",
16
+ "b" : "b",
17
+ "c" : "c",
18
+ "d" : "d",
19
+ "concatenated" : "null bar 42 baz true 3.14 hi"
20
+ },
21
+
22
+ "arrays" : {
23
+ "empty" : [],
24
+ "1" : [ 1 ],
25
+ "12" : [1, 2],
26
+ "123" : [1, 2, 3],
27
+ "ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ]
28
+ },
29
+
30
+ "booleans" : {
31
+ "true" : true,
32
+ "trueAgain" : ${booleans.true},
33
+ "false" : false,
34
+ "falseAgain" : ${booleans.false}
35
+ },
36
+
37
+ "nulls" : {
38
+ "null" : null,
39
+ "nullAgain" : ${nulls.null}
40
+ }
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "ints" : {
3
+ "fortyTwo" : 42,
4
+ "fortyTwoAgain" : 42
5
+ },
6
+
7
+ "floats" : {
8
+ "fortyTwoPointOne" : 42.1,
9
+ "fortyTwoPointOneAgain" : 42.1
10
+ },
11
+
12
+ "strings" : {
13
+ "abcd" : abcd,
14
+ "abcdAgain" : abcd,
15
+ "a" : a,
16
+ "b" : b,
17
+ "c" : c,
18
+ "d" : d,
19
+ "concatenated" : null bar 42 "baz" true 3.14 hi
20
+ },
21
+
22
+ "arrays" : {
23
+ "empty" : [],
24
+ "1" : [ 1 ],
25
+ "12" : [1, 2],
26
+ "123" : [1, 2, 3],
27
+ "ofString" : [a, b, c]
28
+ },
29
+
30
+ "booleans" : {
31
+ "true" : true,
32
+ "trueAgain" : true,
33
+ "false" : false,
34
+ "falseAgain" : false
35
+ },
36
+
37
+ "nulls" : {
38
+ "null" : null,
39
+ "nullAgain" : null
40
+ }
41
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "a" : {
3
+ "s" : 303,
4
+ "b" : {
5
+ "r" : 302,
6
+ "c" : {
7
+ "q" : 301,
8
+ "d" : {
9
+ "e" : 401,
10
+ "a" : 1,
11
+ "b" : 2,
12
+ "z" : 102
13
+ }
14
+ }
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ a.b.c.d
3
+ : { "a" : 1, "z" : 101 },
4
+ a.b.c
5
+ : { "q" : 301 },
6
+ a.b :
7
+
8
+ { "r" : 302 },
9
+
10
+ a
11
+
12
+ : { "s" : 303 },
13
+
14
+ a.b.c.d
15
+
16
+
17
+ : { "b" : 2, "z" : 102 },
18
+
19
+
20
+ a.b.c.d.e
21
+
22
+ : 400,
23
+
24
+
25
+ a.b.c.d.e
26
+
27
+ :
28
+
29
+
30
+ 401
31
+
32
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ a.b.c.d : { "a" : 1, "z" : 101 },
3
+ a.b.c : { "q" : 301 },
4
+ a.b : { "r" : 302 },
5
+ a : { "s" : 303 },
6
+ a.b.c.d : { "b" : 2, "z" : 102 },
7
+ a.b.c.d.e : 400,
8
+ a.b.c.d.e : 401
9
+ }
@@ -0,0 +1,7 @@
1
+ letters {
2
+ include "letters/a.conf"
3
+ include "letters/b.json"
4
+ include "letters/c"
5
+ }
6
+
7
+ include "root/foo.conf"
@@ -0,0 +1,6 @@
1
+ numbers {
2
+ include "numbers/1.conf"
3
+ include "numbers/2"
4
+ }
5
+
6
+ a=ok
@@ -0,0 +1,3 @@
1
+ {
2
+ "b" : 507
3
+ }
@@ -0,0 +1,2 @@
1
+ c.fromConf=89
2
+
@@ -0,0 +1 @@
1
+ c.fromProp=true
@@ -0,0 +1,15 @@
1
+ {
2
+ "letters" : {
3
+ "numbers" : {
4
+ "2" : "abcd",
5
+ "1" : 1
6
+ },
7
+ "a" : "ok",
8
+ "b" : 507,
9
+ "c" : {
10
+ "fromConf" : 89,
11
+ "fromProp" : "true"
12
+ }
13
+ },
14
+ "root" : "this gets included at the root"
15
+ }
@@ -0,0 +1 @@
1
+ root="this gets included at the root"
@@ -0,0 +1,4 @@
1
+ a=${?NOT_DEFINED_ANYWHERE}
2
+ b=${?also.not.defined.anywhere}
3
+ c=${?a}
4
+
@@ -0,0 +1,16 @@
1
+ {
2
+ "a" : "hello",
3
+ "b" : "hello\nworld",
4
+ "b1" : "hello\\nworld",
5
+ "c" : " \n hello \n world \n ",
6
+ "d" : " \"hello\" ",
7
+ "e" : "\"hello\"",
8
+ "f1" : "hello\"",
9
+ "f2" : "hello\"\"",
10
+ "f3" : "hello\"\"\"",
11
+ "f4" : "hello\"\"\"\"",
12
+ "g1" : "\"hello",
13
+ "g2" : "\"\"hello",
14
+ "h" : "\"\"",
15
+ "i" : "foo\"\"bar"
16
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ a : """hello""",
3
+ b : """hello
4
+ world""",
5
+ b1 : """hello\nworld""",
6
+ c : """
7
+ hello
8
+ world
9
+ """,
10
+ d : """ "hello" """,
11
+ e : """"hello"""",
12
+ f1 : """hello"""",
13
+ f2 : """hello""""",
14
+ f3 : """hello"""""",
15
+ f4 : """hello""""""",
16
+ g1 : """"hello""",
17
+ g2 : """""hello""",
18
+ h : """""""",
19
+ i : """foo""bar"""
20
+ }
@@ -0,0 +1,5 @@
1
+ base=41
2
+ # included without file() in a subdir
3
+ include "subdir/foo.conf"
4
+ # included using file() in a subdir
5
+ include file("subdir/baz.conf")
@@ -0,0 +1,5 @@
1
+ // The {} inside the [] is needed because
2
+ // just [ include ] means an array with the
3
+ // string "include" in it.
4
+ a = [ { include "test01.conf" } ]
5
+
@@ -0,0 +1,2 @@
1
+ # this is just a file on the classpath that we ensure we can load
2
+ onclasspath=42
@@ -0,0 +1 @@
1
+ bar-file=44
@@ -0,0 +1 @@
1
+ bar=43
@@ -0,0 +1 @@
1
+ baz=45
@@ -0,0 +1,5 @@
1
+ foo=42
2
+ # included without file()
3
+ include "bar.conf"
4
+ # included using file()
5
+ include file("bar-file.conf")
@@ -0,0 +1,96 @@
1
+ {
2
+ "ints" : {
3
+ "fortyTwo" : 42,
4
+ "fortyTwoAgain" : ${ints.fortyTwo}
5
+ },
6
+
7
+ "floats" : {
8
+ "fortyTwoPointOne" : 42.1,
9
+ "fortyTwoPointOneAgain" : ${floats.fortyTwoPointOne},
10
+ "pointThirtyThree": .33
11
+ "pointThirtyThreeAgain": ${floats.pointThirtyThree}
12
+ },
13
+
14
+ "strings" : {
15
+ "abcd" : "abcd",
16
+ "abcdAgain" : ${strings.a}${strings.b}${strings.c}${strings.d},
17
+ "a" : "a",
18
+ "b" : "b",
19
+ "c" : "c",
20
+ "d" : "d",
21
+ "concatenated" : null bar 42 baz true 3.14 hi,
22
+ "double" : "3.14",
23
+ "doubleStartingWithDot": ".33",
24
+ "number" : "57",
25
+ "null" : "null",
26
+ "true" : "true",
27
+ "yes" : "yes",
28
+ "false" : "false",
29
+ "no" : "no"
30
+ },
31
+
32
+ "arrays" : {
33
+ "empty" : [],
34
+ "ofInt" : [1, 2, 3],
35
+ "ofString" : [ ${strings.a}, ${strings.b}, ${strings.c} ],
36
+ "ofDouble" : [3.14, 4.14, 5.14],
37
+ "ofNull" : [null, null, null],
38
+ "ofBoolean" : [true, false],
39
+ "ofArray" : [${arrays.ofString}, ${arrays.ofString}, ${arrays.ofString}],
40
+ "ofObject" : [${ints}, ${booleans}, ${strings}],
41
+ "firstElementNotASubst" : [ "a", ${strings.b} ]
42
+ },
43
+
44
+ "booleans" : {
45
+ "true" : true,
46
+ "trueAgain" : ${booleans.true},
47
+ "false" : false,
48
+ "falseAgain" : ${booleans.false}
49
+ },
50
+
51
+ "nulls" : {
52
+ "null" : null,
53
+ "nullAgain" : ${nulls.null}
54
+ },
55
+
56
+ "durations" : {
57
+ "second" : 1s,
58
+ "secondsList" : [1s,2seconds,3 s, 4000],
59
+ "secondAsNumber" : 1000,
60
+ "halfSecond" : 0.5s,
61
+ "millis" : 1 milli,
62
+ "micros" : 2000 micros,
63
+ "largeNanos" : 4878955355435272204ns,
64
+ "plusLargeNanos" : "+4878955355435272204ns",
65
+ "minusLargeNanos" : -4878955355435272204ns
66
+ },
67
+
68
+ "periods" : {
69
+ "day" : 1d,
70
+ "dayAsNumber": 2,
71
+ "week": 3 weeks,
72
+ "month": 5 mo,
73
+ "year": 8y
74
+ },
75
+
76
+ "memsizes" : {
77
+ "meg" : 1M,
78
+ "megsList" : [1M, 1024K, 1048576],
79
+ "megAsNumber" : 1048576,
80
+ "halfMeg" : 0.5M
81
+ "yottabyte" : 1YB
82
+ "yottabyteList" : [1YB, 0.5YB]
83
+ },
84
+
85
+ "system" : {
86
+ "javaversion" : ${?java.version},
87
+ "userhome" : ${?user.home},
88
+ "home" : ${?HOME},
89
+ "pwd" : ${?PWD},
90
+ "shell" : ${?SHELL},
91
+ "lang" : ${?LANG},
92
+ "path" : ${?PATH},
93
+ "not_here" : ${?NOT_HERE},
94
+ "concatenated" : Your Java version is ${?system.javaversion} and your user.home is ${?system.userhome}
95
+ }
96
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "fromJson1" : 1,
3
+ "fromJsonA" : "A"
4
+ }
@@ -0,0 +1,5 @@
1
+ # .properties file
2
+ fromProps.abc=abc
3
+ fromProps.one=1
4
+ fromProps.bool=true
5
+ fromProps.specialChars=hello^^
@@ -0,0 +1,13 @@
1
+ {
2
+ "" : { "" : { "" : 42 } },
3
+ "42_a" : ${""."".""},
4
+ "42_b" : ${ "".""."" },
5
+ "a" : { "b" : { "c" : 57 } },
6
+ "57_a" : ${a.b.c},
7
+ "57_b" : ${"a"."b"."c"},
8
+ "a.b.c" : 103,
9
+ "103_a" : ${"a.b.c"},
10
+ "a-c" : 259,
11
+ "a_c" : 260,
12
+ "-" : 261
13
+ }
@@ -0,0 +1,9 @@
1
+ # this file is used to include in test03.conf
2
+
3
+ foo="This is in the included file"
4
+
5
+ # foo is in this file, so "a" should always be our local ${foo}
6
+ a=${foo}
7
+
8
+ # bar is only in the parent file, so "b" should always be the root ${bar}
9
+ b=${bar}
@@ -0,0 +1,36 @@
1
+ {
2
+ "test01" : {
3
+ "ints" : 12,
4
+ include "test01",
5
+ "booleans" : 42
6
+ },
7
+
8
+ "test02" : {
9
+ include
10
+
11
+ "test02.conf"
12
+ },
13
+
14
+ "equiv01" : {
15
+ include "equiv01/original.json"
16
+ },
17
+
18
+ # missing includes are supposed to be silently ignored
19
+ nonexistent {
20
+ include "nothere"
21
+ include "nothere.conf"
22
+ include "nothere.json"
23
+ include "nothere.properties"
24
+ }
25
+
26
+ # make sure included file substitutions fall back to parent file,
27
+ # both when the include is at the root (so doesn't need to have
28
+ # substitutions adjusted) and when it is not.
29
+ foo="This is in the including file"
30
+ bar="This is in the including file"
31
+ include "test03-included.conf"
32
+
33
+ subtree {
34
+ include "test03-included.conf"
35
+ }
36
+ }