@intentius/chant 0.0.1

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 (271) hide show
  1. package/README.md +365 -0
  2. package/package.json +22 -0
  3. package/src/attrref.test.ts +148 -0
  4. package/src/attrref.ts +50 -0
  5. package/src/barrel.test.ts +157 -0
  6. package/src/barrel.ts +101 -0
  7. package/src/bench.test.ts +227 -0
  8. package/src/build.test.ts +437 -0
  9. package/src/build.ts +425 -0
  10. package/src/builder.test.ts +312 -0
  11. package/src/builder.ts +56 -0
  12. package/src/child-project.ts +44 -0
  13. package/src/cli/commands/__fixtures__/init-lexicon-output/README.md +26 -0
  14. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/astro.config.mjs +14 -0
  15. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/package.json +16 -0
  16. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content/docs/index.mdx +8 -0
  17. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/src/content.config.ts +7 -0
  18. package/src/cli/commands/__fixtures__/init-lexicon-output/docs/tsconfig.json +10 -0
  19. package/src/cli/commands/__fixtures__/init-lexicon-output/examples/getting-started/.gitkeep +0 -0
  20. package/src/cli/commands/__fixtures__/init-lexicon-output/justfile +26 -0
  21. package/src/cli/commands/__fixtures__/init-lexicon-output/package.json +29 -0
  22. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/docs.ts +25 -0
  23. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate-cli.ts +8 -0
  24. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/generate.ts +74 -0
  25. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/naming.ts +33 -0
  26. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/package.ts +25 -0
  27. package/src/cli/commands/__fixtures__/init-lexicon-output/src/codegen/rollback.ts +45 -0
  28. package/src/cli/commands/__fixtures__/init-lexicon-output/src/coverage.ts +11 -0
  29. package/src/cli/commands/__fixtures__/init-lexicon-output/src/generated/.gitkeep +0 -0
  30. package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/generator.ts +10 -0
  31. package/src/cli/commands/__fixtures__/init-lexicon-output/src/import/parser.ts +10 -0
  32. package/src/cli/commands/__fixtures__/init-lexicon-output/src/index.ts +9 -0
  33. package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/index.ts +1 -0
  34. package/src/cli/commands/__fixtures__/init-lexicon-output/src/lint/rules/sample.ts +18 -0
  35. package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/completions.ts +14 -0
  36. package/src/cli/commands/__fixtures__/init-lexicon-output/src/lsp/hover.ts +14 -0
  37. package/src/cli/commands/__fixtures__/init-lexicon-output/src/plugin.ts +110 -0
  38. package/src/cli/commands/__fixtures__/init-lexicon-output/src/serializer.ts +24 -0
  39. package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/fetch.ts +21 -0
  40. package/src/cli/commands/__fixtures__/init-lexicon-output/src/spec/parse.ts +25 -0
  41. package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate-cli.ts +4 -0
  42. package/src/cli/commands/__fixtures__/init-lexicon-output/src/validate.ts +24 -0
  43. package/src/cli/commands/__fixtures__/init-lexicon-output/tsconfig.json +10 -0
  44. package/src/cli/commands/__fixtures__/sample-rule.ts +11 -0
  45. package/src/cli/commands/__snapshots__/init-lexicon.test.ts.snap +222 -0
  46. package/src/cli/commands/build.test.ts +149 -0
  47. package/src/cli/commands/build.ts +344 -0
  48. package/src/cli/commands/diff.test.ts +148 -0
  49. package/src/cli/commands/diff.ts +221 -0
  50. package/src/cli/commands/doctor.test.ts +239 -0
  51. package/src/cli/commands/doctor.ts +224 -0
  52. package/src/cli/commands/import.test.ts +379 -0
  53. package/src/cli/commands/import.ts +335 -0
  54. package/src/cli/commands/init-lexicon.test.ts +297 -0
  55. package/src/cli/commands/init-lexicon.ts +993 -0
  56. package/src/cli/commands/init.test.ts +317 -0
  57. package/src/cli/commands/init.ts +505 -0
  58. package/src/cli/commands/licenses.ts +165 -0
  59. package/src/cli/commands/lint.test.ts +332 -0
  60. package/src/cli/commands/lint.ts +408 -0
  61. package/src/cli/commands/list.test.ts +100 -0
  62. package/src/cli/commands/list.ts +108 -0
  63. package/src/cli/commands/update.test.ts +38 -0
  64. package/src/cli/commands/update.ts +207 -0
  65. package/src/cli/conflict-check.test.ts +255 -0
  66. package/src/cli/conflict-check.ts +89 -0
  67. package/src/cli/debug.ts +8 -0
  68. package/src/cli/format.test.ts +140 -0
  69. package/src/cli/format.ts +133 -0
  70. package/src/cli/handlers/build.ts +58 -0
  71. package/src/cli/handlers/dev.ts +38 -0
  72. package/src/cli/handlers/init.ts +46 -0
  73. package/src/cli/handlers/lint.ts +36 -0
  74. package/src/cli/handlers/misc.ts +57 -0
  75. package/src/cli/handlers/serve.ts +26 -0
  76. package/src/cli/index.ts +3 -0
  77. package/src/cli/lsp/capabilities.ts +46 -0
  78. package/src/cli/lsp/diagnostics.ts +52 -0
  79. package/src/cli/lsp/server.test.ts +618 -0
  80. package/src/cli/lsp/server.ts +393 -0
  81. package/src/cli/main.test.ts +257 -0
  82. package/src/cli/main.ts +224 -0
  83. package/src/cli/mcp/resources/context.ts +59 -0
  84. package/src/cli/mcp/server.test.ts +747 -0
  85. package/src/cli/mcp/server.ts +402 -0
  86. package/src/cli/mcp/tools/build.ts +117 -0
  87. package/src/cli/mcp/tools/import.ts +48 -0
  88. package/src/cli/mcp/tools/lint.ts +45 -0
  89. package/src/cli/plugins.test.ts +31 -0
  90. package/src/cli/plugins.ts +94 -0
  91. package/src/cli/registry.ts +73 -0
  92. package/src/cli/reporters/stylish.test.ts +282 -0
  93. package/src/cli/reporters/stylish.ts +186 -0
  94. package/src/cli/watch.test.ts +81 -0
  95. package/src/cli/watch.ts +101 -0
  96. package/src/codegen/case.test.ts +30 -0
  97. package/src/codegen/case.ts +11 -0
  98. package/src/codegen/coverage.ts +167 -0
  99. package/src/codegen/docs.ts +634 -0
  100. package/src/codegen/fetch.test.ts +119 -0
  101. package/src/codegen/fetch.ts +261 -0
  102. package/src/codegen/generate-registry.test.ts +118 -0
  103. package/src/codegen/generate-registry.ts +107 -0
  104. package/src/codegen/generate-runtime-index.test.ts +81 -0
  105. package/src/codegen/generate-runtime-index.ts +99 -0
  106. package/src/codegen/generate-typescript.test.ts +146 -0
  107. package/src/codegen/generate-typescript.ts +161 -0
  108. package/src/codegen/generate.ts +206 -0
  109. package/src/codegen/json-patch.test.ts +113 -0
  110. package/src/codegen/json-patch.ts +151 -0
  111. package/src/codegen/json-schema.test.ts +196 -0
  112. package/src/codegen/json-schema.ts +209 -0
  113. package/src/codegen/naming.ts +201 -0
  114. package/src/codegen/package.ts +161 -0
  115. package/src/codegen/rollback.test.ts +92 -0
  116. package/src/codegen/rollback.ts +115 -0
  117. package/src/codegen/topo-sort.test.ts +69 -0
  118. package/src/codegen/topo-sort.ts +46 -0
  119. package/src/codegen/typecheck.test.ts +37 -0
  120. package/src/codegen/typecheck.ts +74 -0
  121. package/src/codegen/validate.test.ts +86 -0
  122. package/src/codegen/validate.ts +143 -0
  123. package/src/composite.test.ts +426 -0
  124. package/src/composite.ts +243 -0
  125. package/src/config.test.ts +91 -0
  126. package/src/config.ts +87 -0
  127. package/src/declarable.test.ts +160 -0
  128. package/src/declarable.ts +47 -0
  129. package/src/detectLexicon.test.ts +236 -0
  130. package/src/detectLexicon.ts +37 -0
  131. package/src/discovery/cache.test.ts +78 -0
  132. package/src/discovery/cache.ts +86 -0
  133. package/src/discovery/collect.test.ts +269 -0
  134. package/src/discovery/collect.ts +51 -0
  135. package/src/discovery/cycles.test.ts +238 -0
  136. package/src/discovery/cycles.ts +107 -0
  137. package/src/discovery/files.test.ts +154 -0
  138. package/src/discovery/files.ts +61 -0
  139. package/src/discovery/graph.test.ts +476 -0
  140. package/src/discovery/graph.ts +150 -0
  141. package/src/discovery/import.test.ts +199 -0
  142. package/src/discovery/import.ts +20 -0
  143. package/src/discovery/index.test.ts +272 -0
  144. package/src/discovery/index.ts +132 -0
  145. package/src/discovery/resolve.test.ts +267 -0
  146. package/src/discovery/resolve.ts +54 -0
  147. package/src/errors.test.ts +138 -0
  148. package/src/errors.ts +86 -0
  149. package/src/import/base-parser.test.ts +67 -0
  150. package/src/import/base-parser.ts +48 -0
  151. package/src/import/generator.ts +21 -0
  152. package/src/import/ir-utils.test.ts +103 -0
  153. package/src/import/ir-utils.ts +87 -0
  154. package/src/import/parser.ts +41 -0
  155. package/src/index.ts +60 -0
  156. package/src/intrinsic-interpolation.test.ts +91 -0
  157. package/src/intrinsic-interpolation.ts +89 -0
  158. package/src/intrinsic.test.ts +69 -0
  159. package/src/intrinsic.ts +43 -0
  160. package/src/lexicon-integrity.test.ts +94 -0
  161. package/src/lexicon-integrity.ts +69 -0
  162. package/src/lexicon-manifest.test.ts +101 -0
  163. package/src/lexicon-manifest.ts +71 -0
  164. package/src/lexicon-output.test.ts +182 -0
  165. package/src/lexicon-output.ts +82 -0
  166. package/src/lexicon-schema.test.ts +239 -0
  167. package/src/lexicon-schema.ts +144 -0
  168. package/src/lexicon.ts +212 -0
  169. package/src/lint/config-overrides.test.ts +254 -0
  170. package/src/lint/config.test.ts +644 -0
  171. package/src/lint/config.ts +375 -0
  172. package/src/lint/declarative.test.ts +256 -0
  173. package/src/lint/declarative.ts +187 -0
  174. package/src/lint/engine.test.ts +465 -0
  175. package/src/lint/engine.ts +172 -0
  176. package/src/lint/named-checks.test.ts +37 -0
  177. package/src/lint/named-checks.ts +33 -0
  178. package/src/lint/parser.test.ts +129 -0
  179. package/src/lint/parser.ts +42 -0
  180. package/src/lint/post-synth.test.ts +113 -0
  181. package/src/lint/post-synth.ts +76 -0
  182. package/src/lint/presets/relaxed.json +19 -0
  183. package/src/lint/presets/strict.json +19 -0
  184. package/src/lint/rule-loader.test.ts +67 -0
  185. package/src/lint/rule-loader.ts +67 -0
  186. package/src/lint/rule-options.test.ts +141 -0
  187. package/src/lint/rule.test.ts +196 -0
  188. package/src/lint/rule.ts +98 -0
  189. package/src/lint/rules/barrel-import-style.test.ts +80 -0
  190. package/src/lint/rules/barrel-import-style.ts +59 -0
  191. package/src/lint/rules/composite-scope.ts +55 -0
  192. package/src/lint/rules/cor017-composite-name-match.test.ts +107 -0
  193. package/src/lint/rules/cor017-composite-name-match.ts +108 -0
  194. package/src/lint/rules/cor018-composite-prefer-lexicon-type.test.ts +172 -0
  195. package/src/lint/rules/cor018-composite-prefer-lexicon-type.ts +167 -0
  196. package/src/lint/rules/declarable-naming-convention.test.ts +69 -0
  197. package/src/lint/rules/declarable-naming-convention.ts +70 -0
  198. package/src/lint/rules/enforce-barrel-import.test.ts +169 -0
  199. package/src/lint/rules/enforce-barrel-import.ts +81 -0
  200. package/src/lint/rules/enforce-barrel-ref.test.ts +114 -0
  201. package/src/lint/rules/enforce-barrel-ref.ts +75 -0
  202. package/src/lint/rules/evl001-non-literal-expression.test.ts +158 -0
  203. package/src/lint/rules/evl001-non-literal-expression.ts +149 -0
  204. package/src/lint/rules/evl002-control-flow-resource.test.ts +110 -0
  205. package/src/lint/rules/evl002-control-flow-resource.ts +61 -0
  206. package/src/lint/rules/evl003-dynamic-property-access.test.ts +63 -0
  207. package/src/lint/rules/evl003-dynamic-property-access.ts +41 -0
  208. package/src/lint/rules/evl004-spread-non-const.test.ts +130 -0
  209. package/src/lint/rules/evl004-spread-non-const.ts +111 -0
  210. package/src/lint/rules/evl005-resource-block-body.test.ts +59 -0
  211. package/src/lint/rules/evl005-resource-block-body.ts +49 -0
  212. package/src/lint/rules/evl006-barrel-usage.test.ts +63 -0
  213. package/src/lint/rules/evl006-barrel-usage.ts +95 -0
  214. package/src/lint/rules/evl007-invalid-siblings.test.ts +87 -0
  215. package/src/lint/rules/evl007-invalid-siblings.ts +139 -0
  216. package/src/lint/rules/evl008-unresolvable-barrel-ref.test.ts +118 -0
  217. package/src/lint/rules/evl008-unresolvable-barrel-ref.ts +140 -0
  218. package/src/lint/rules/evl009-composite-no-constant.test.ts +162 -0
  219. package/src/lint/rules/evl009-composite-no-constant.ts +171 -0
  220. package/src/lint/rules/evl010-composite-no-transform.test.ts +121 -0
  221. package/src/lint/rules/evl010-composite-no-transform.ts +69 -0
  222. package/src/lint/rules/export-required.test.ts +213 -0
  223. package/src/lint/rules/export-required.ts +158 -0
  224. package/src/lint/rules/file-declarable-limit.test.ts +148 -0
  225. package/src/lint/rules/file-declarable-limit.ts +96 -0
  226. package/src/lint/rules/flat-declarations.test.ts +210 -0
  227. package/src/lint/rules/flat-declarations.ts +70 -0
  228. package/src/lint/rules/index.ts +99 -0
  229. package/src/lint/rules/no-cyclic-declarable-ref.test.ts +135 -0
  230. package/src/lint/rules/no-cyclic-declarable-ref.ts +178 -0
  231. package/src/lint/rules/no-redundant-type-import.test.ts +129 -0
  232. package/src/lint/rules/no-redundant-type-import.ts +85 -0
  233. package/src/lint/rules/no-redundant-value-cast.test.ts +51 -0
  234. package/src/lint/rules/no-redundant-value-cast.ts +46 -0
  235. package/src/lint/rules/no-string-ref.test.ts +100 -0
  236. package/src/lint/rules/no-string-ref.ts +66 -0
  237. package/src/lint/rules/no-unused-declarable-import.test.ts +74 -0
  238. package/src/lint/rules/no-unused-declarable-import.ts +103 -0
  239. package/src/lint/rules/no-unused-declarable.test.ts +134 -0
  240. package/src/lint/rules/no-unused-declarable.ts +118 -0
  241. package/src/lint/rules/prefer-namespace-import.test.ts +102 -0
  242. package/src/lint/rules/prefer-namespace-import.ts +63 -0
  243. package/src/lint/rules/single-concern-file.test.ts +156 -0
  244. package/src/lint/rules/single-concern-file.ts +98 -0
  245. package/src/lint/rules/stale-barrel-types.ts +60 -0
  246. package/src/lint/selectors.test.ts +113 -0
  247. package/src/lint/selectors.ts +188 -0
  248. package/src/lsp/lexicon-providers.ts +191 -0
  249. package/src/lsp/types.ts +79 -0
  250. package/src/mcp/types.ts +22 -0
  251. package/src/project/scan.test.ts +178 -0
  252. package/src/project/scan.ts +182 -0
  253. package/src/project/sync.test.ts +87 -0
  254. package/src/project/sync.ts +46 -0
  255. package/src/project-validation.test.ts +64 -0
  256. package/src/project-validation.ts +79 -0
  257. package/src/pseudo-parameter.test.ts +39 -0
  258. package/src/pseudo-parameter.ts +47 -0
  259. package/src/runtime.ts +68 -0
  260. package/src/serializer-walker.test.ts +124 -0
  261. package/src/serializer-walker.ts +83 -0
  262. package/src/serializer.ts +42 -0
  263. package/src/sort.test.ts +290 -0
  264. package/src/sort.ts +58 -0
  265. package/src/stack-output.ts +82 -0
  266. package/src/types.test.ts +307 -0
  267. package/src/types.ts +46 -0
  268. package/src/utils.test.ts +195 -0
  269. package/src/utils.ts +46 -0
  270. package/src/validation.test.ts +308 -0
  271. package/src/validation.ts +50 -0
@@ -0,0 +1,312 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { Builder } from "./builder";
3
+ import type { Declarable } from "./declarable";
4
+ import { DECLARABLE_MARKER } from "./declarable";
5
+
6
+ // Test implementation of Declarable for testing
7
+ interface TestResource extends Declarable {
8
+ name: string;
9
+ type: string;
10
+ description?: string;
11
+ }
12
+
13
+ // Simple builder implementation
14
+ class SimpleResourceBuilder extends Builder<TestResource> {
15
+ private name?: string;
16
+ private type?: string;
17
+ private description?: string;
18
+
19
+ withName(name: string): this {
20
+ this.name = name;
21
+ return this;
22
+ }
23
+
24
+ withType(type: string): this {
25
+ this.type = type;
26
+ return this;
27
+ }
28
+
29
+ withDescription(description: string): this {
30
+ this.description = description;
31
+ return this;
32
+ }
33
+
34
+ build(): TestResource {
35
+ if (!this.name || !this.type) {
36
+ throw new Error("Name and type are required");
37
+ }
38
+ return {
39
+ entityType: "TestResource",
40
+ [DECLARABLE_MARKER]: true,
41
+ name: this.name,
42
+ type: this.type,
43
+ description: this.description,
44
+ };
45
+ }
46
+ }
47
+
48
+ // Complex builder with validation
49
+ class ValidatedResourceBuilder extends Builder<TestResource> {
50
+ private name?: string;
51
+ private type?: string;
52
+
53
+ withName(name: string): this {
54
+ if (!name || name.trim() === "") {
55
+ throw new Error("Name cannot be empty");
56
+ }
57
+ this.name = name;
58
+ return this;
59
+ }
60
+
61
+ withType(type: string): this {
62
+ const validTypes = ["A", "B", "C"];
63
+ if (!validTypes.includes(type)) {
64
+ throw new Error(`Type must be one of: ${validTypes.join(", ")}`);
65
+ }
66
+ this.type = type;
67
+ return this;
68
+ }
69
+
70
+ build(): TestResource {
71
+ if (!this.name || !this.type) {
72
+ throw new Error("Name and type are required");
73
+ }
74
+ return {
75
+ entityType: "TestResource",
76
+ [DECLARABLE_MARKER]: true,
77
+ name: this.name,
78
+ type: this.type,
79
+ };
80
+ }
81
+ }
82
+
83
+ // Builder with default values
84
+ class DefaultsResourceBuilder extends Builder<TestResource> {
85
+ private name = "default-name";
86
+ private type = "default-type";
87
+ private description = "default-description";
88
+
89
+ withName(name: string): this {
90
+ this.name = name;
91
+ return this;
92
+ }
93
+
94
+ withType(type: string): this {
95
+ this.type = type;
96
+ return this;
97
+ }
98
+
99
+ withDescription(description: string): this {
100
+ this.description = description;
101
+ return this;
102
+ }
103
+
104
+ build(): TestResource {
105
+ return {
106
+ entityType: "TestResource",
107
+ [DECLARABLE_MARKER]: true,
108
+ name: this.name,
109
+ type: this.type,
110
+ description: this.description,
111
+ };
112
+ }
113
+ }
114
+
115
+ describe("Builder", () => {
116
+ describe("fluent API", () => {
117
+ test("allows chaining multiple method calls", () => {
118
+ const builder = new SimpleResourceBuilder()
119
+ .withName("test-resource")
120
+ .withType("test-type")
121
+ .withDescription("test-description");
122
+
123
+ const resource = builder.build();
124
+ expect(resource.name).toBe("test-resource");
125
+ expect(resource.type).toBe("test-type");
126
+ expect(resource.description).toBe("test-description");
127
+ });
128
+
129
+ test("returns 'this' for chaining", () => {
130
+ const builder = new SimpleResourceBuilder();
131
+ const result1 = builder.withName("test");
132
+ const result2 = result1.withType("type");
133
+
134
+ expect(result1).toBe(builder);
135
+ expect(result2).toBe(builder);
136
+ });
137
+
138
+ test("allows arbitrary method call order", () => {
139
+ const resource1 = new SimpleResourceBuilder()
140
+ .withName("test")
141
+ .withType("type")
142
+ .build();
143
+
144
+ const resource2 = new SimpleResourceBuilder()
145
+ .withType("type")
146
+ .withName("test")
147
+ .build();
148
+
149
+ expect(resource1.name).toBe(resource2.name);
150
+ expect(resource1.type).toBe(resource2.type);
151
+ });
152
+ });
153
+
154
+ describe("build method", () => {
155
+ test("returns a valid Declarable entity", () => {
156
+ const resource = new SimpleResourceBuilder()
157
+ .withName("test")
158
+ .withType("type")
159
+ .build();
160
+
161
+ expect(resource.entityType).toBe("TestResource");
162
+ expect(resource[DECLARABLE_MARKER]).toBe(true);
163
+ expect(resource.name).toBe("test");
164
+ expect(resource.type).toBe("type");
165
+ });
166
+
167
+ test("throws when required properties are missing", () => {
168
+ const builder = new SimpleResourceBuilder().withName("test");
169
+
170
+ expect(() => builder.build()).toThrow("Name and type are required");
171
+ });
172
+
173
+ test("includes optional properties when set", () => {
174
+ const resource = new SimpleResourceBuilder()
175
+ .withName("test")
176
+ .withType("type")
177
+ .withDescription("A test resource")
178
+ .build();
179
+
180
+ expect(resource.description).toBe("A test resource");
181
+ });
182
+
183
+ test("omits optional properties when not set", () => {
184
+ const resource = new SimpleResourceBuilder()
185
+ .withName("test")
186
+ .withType("type")
187
+ .build();
188
+
189
+ expect(resource.description).toBeUndefined();
190
+ });
191
+ });
192
+
193
+ describe("extension patterns", () => {
194
+ test("supports custom validation in setter methods", () => {
195
+ const builder = new ValidatedResourceBuilder();
196
+
197
+ expect(() => builder.withName("")).toThrow("Name cannot be empty");
198
+ expect(() => builder.withName(" ")).toThrow("Name cannot be empty");
199
+ expect(() => builder.withType("Invalid")).toThrow(
200
+ "Type must be one of: A, B, C"
201
+ );
202
+ });
203
+
204
+ test("allows valid values through validation", () => {
205
+ const resource = new ValidatedResourceBuilder()
206
+ .withName("valid-name")
207
+ .withType("A")
208
+ .build();
209
+
210
+ expect(resource.name).toBe("valid-name");
211
+ expect(resource.type).toBe("A");
212
+ });
213
+
214
+ test("supports builders with default values", () => {
215
+ const resource = new DefaultsResourceBuilder().build();
216
+
217
+ expect(resource.name).toBe("default-name");
218
+ expect(resource.type).toBe("default-type");
219
+ expect(resource.description).toBe("default-description");
220
+ });
221
+
222
+ test("allows overriding default values", () => {
223
+ const resource = new DefaultsResourceBuilder()
224
+ .withName("custom-name")
225
+ .build();
226
+
227
+ expect(resource.name).toBe("custom-name");
228
+ expect(resource.type).toBe("default-type");
229
+ expect(resource.description).toBe("default-description");
230
+ });
231
+ });
232
+
233
+ describe("reusability", () => {
234
+ test("builder can be reused to create multiple instances", () => {
235
+ const builder = new DefaultsResourceBuilder();
236
+
237
+ const resource1 = builder.build();
238
+ const resource2 = builder.build();
239
+
240
+ // Should create separate instances
241
+ expect(resource1).not.toBe(resource2);
242
+ // But with same values
243
+ expect(resource1.name).toBe(resource2.name);
244
+ expect(resource1.type).toBe(resource2.type);
245
+ });
246
+
247
+ test("modifying builder after build affects next build", () => {
248
+ const builder = new SimpleResourceBuilder()
249
+ .withName("initial")
250
+ .withType("type");
251
+
252
+ const resource1 = builder.build();
253
+ expect(resource1.name).toBe("initial");
254
+
255
+ builder.withName("modified");
256
+ const resource2 = builder.build();
257
+ expect(resource2.name).toBe("modified");
258
+ });
259
+ });
260
+
261
+ describe("type safety", () => {
262
+ test("build method returns correct type", () => {
263
+ const builder = new SimpleResourceBuilder()
264
+ .withName("test")
265
+ .withType("type");
266
+
267
+ const resource = builder.build();
268
+
269
+ // TypeScript should recognize this as TestResource
270
+ expect(resource.entityType).toBe("TestResource");
271
+ expect(resource.name).toBe("test");
272
+ expect(resource.type).toBe("type");
273
+ });
274
+
275
+ test("fluent methods maintain builder type", () => {
276
+ const builder = new SimpleResourceBuilder();
277
+
278
+ // Each method should return the same builder type
279
+ const step1 = builder.withName("test");
280
+ const step2 = step1.withType("type");
281
+ const step3 = step2.withDescription("desc");
282
+
283
+ expect(step1).toBeInstanceOf(SimpleResourceBuilder);
284
+ expect(step2).toBeInstanceOf(SimpleResourceBuilder);
285
+ expect(step3).toBeInstanceOf(SimpleResourceBuilder);
286
+ });
287
+ });
288
+
289
+ describe("error handling", () => {
290
+ test("preserves validation errors", () => {
291
+ const builder = new ValidatedResourceBuilder();
292
+
293
+ try {
294
+ builder.withType("Invalid");
295
+ } catch (error) {
296
+ expect(error).toBeInstanceOf(Error);
297
+ expect((error as Error).message).toBe("Type must be one of: A, B, C");
298
+ }
299
+ });
300
+
301
+ test("preserves build errors", () => {
302
+ const builder = new SimpleResourceBuilder();
303
+
304
+ try {
305
+ builder.build();
306
+ } catch (error) {
307
+ expect(error).toBeInstanceOf(Error);
308
+ expect((error as Error).message).toBe("Name and type are required");
309
+ }
310
+ });
311
+ });
312
+ });
package/src/builder.ts ADDED
@@ -0,0 +1,56 @@
1
+ import type { Declarable } from "./declarable";
2
+
3
+ /**
4
+ * Base class for builder pattern implementations
5
+ *
6
+ * Provides a fluent API for constructing Declarable entities.
7
+ * Subclasses should implement the build() method to return
8
+ * a fully constructed Declarable entity.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * class ResourceBuilder extends Builder<MyResource> {
13
+ * private name?: string;
14
+ * private type?: string;
15
+ *
16
+ * withName(name: string): this {
17
+ * this.name = name;
18
+ * return this;
19
+ * }
20
+ *
21
+ * withType(type: string): this {
22
+ * this.type = type;
23
+ * return this;
24
+ * }
25
+ *
26
+ * build(): MyResource {
27
+ * if (!this.name || !this.type) {
28
+ * throw new Error("Name and type are required");
29
+ * }
30
+ * return {
31
+ * entityType: "MyResource",
32
+ * [DECLARABLE_MARKER]: true,
33
+ * name: this.name,
34
+ * type: this.type,
35
+ * };
36
+ * }
37
+ * }
38
+ *
39
+ * const resource = new ResourceBuilder()
40
+ * .withName("myResource")
41
+ * .withType("custom")
42
+ * .build();
43
+ * ```
44
+ */
45
+ export abstract class Builder<T extends Declarable> {
46
+ /**
47
+ * Builds and returns the final Declarable entity
48
+ *
49
+ * Subclasses must implement this method to construct
50
+ * and return the entity with all configured properties.
51
+ *
52
+ * @returns The constructed Declarable entity
53
+ * @throws Error if required properties are not set
54
+ */
55
+ abstract build(): T;
56
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Child Project — represents a nested project directory that gets built
3
+ * independently and referenced from a parent as a single deployment unit.
4
+ *
5
+ * Lexicons create ChildProjectInstance via their own factory (e.g. `nestedStack()`).
6
+ * The core build pipeline detects these and recursively builds children.
7
+ */
8
+
9
+ import { DECLARABLE_MARKER, type Declarable } from "./declarable";
10
+ import type { BuildResult } from "./build";
11
+
12
+ /**
13
+ * Marker symbol for child project identification.
14
+ */
15
+ export const CHILD_PROJECT_MARKER = Symbol.for("chant.childProject");
16
+
17
+ /**
18
+ * A child project instance — a Declarable representing a separately-built
19
+ * project directory referenced from a parent.
20
+ */
21
+ export interface ChildProjectInstance extends Declarable {
22
+ readonly [CHILD_PROJECT_MARKER]: true;
23
+ readonly [DECLARABLE_MARKER]: true;
24
+ readonly lexicon: string;
25
+ readonly entityType: string;
26
+ readonly kind: "resource";
27
+ readonly projectPath: string;
28
+ readonly logicalName: string;
29
+ readonly outputs: Record<string, unknown>;
30
+ readonly options: Record<string, unknown>;
31
+ buildResult?: BuildResult;
32
+ }
33
+
34
+ /**
35
+ * Type guard for ChildProjectInstance.
36
+ */
37
+ export function isChildProject(value: unknown): value is ChildProjectInstance {
38
+ return (
39
+ typeof value === "object" &&
40
+ value !== null &&
41
+ CHILD_PROJECT_MARKER in value &&
42
+ (value as Record<symbol, unknown>)[CHILD_PROJECT_MARKER] === true
43
+ );
44
+ }
@@ -0,0 +1,26 @@
1
+ # @intentius/chant-lexicon-fixture
2
+
3
+ fixture lexicon plugin for [chant](https://github.com/intentius/chant).
4
+
5
+ ## Getting started
6
+
7
+ ```bash
8
+ # Generate types from upstream spec
9
+ just generate
10
+
11
+ # Validate generated artifacts
12
+ just validate
13
+
14
+ # Generate documentation
15
+ just docs
16
+ ```
17
+
18
+ ## Project structure
19
+
20
+ - `src/plugin.ts` — LexiconPlugin with all lifecycle methods
21
+ - `src/serializer.ts` — Build output serializer
22
+ - `src/codegen/` — Code generation pipeline
23
+ - `src/spec/` — Upstream schema fetching and parsing
24
+ - `src/lint/rules/` — Lint rules
25
+ - `src/lsp/` — LSP completions and hover
26
+ - `src/generated/` — Generated artifacts (do not edit)
@@ -0,0 +1,14 @@
1
+ // @ts-check
2
+ import { defineConfig } from 'astro/config';
3
+ import starlight from '@astrojs/starlight';
4
+
5
+ export default defineConfig({
6
+ integrations: [
7
+ starlight({
8
+ title: 'Fixture',
9
+ sidebar: [
10
+ { label: 'Overview', slug: '' },
11
+ ],
12
+ }),
13
+ ],
14
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@intentius/chant-lexicon-fixture-docs",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "astro dev",
8
+ "build": "astro build",
9
+ "preview": "astro preview"
10
+ },
11
+ "dependencies": {
12
+ "@astrojs/starlight": "^0.37.6",
13
+ "astro": "^5.6.1",
14
+ "sharp": "^0.34.2"
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ ---
2
+ title: Overview
3
+ description: Fixture lexicon for chant
4
+ ---
5
+
6
+ Welcome to the Fixture lexicon documentation.
7
+
8
+ Run `just generate` to populate this site with generated reference pages.
@@ -0,0 +1,7 @@
1
+ import { defineCollection } from 'astro:content';
2
+ import { docsLoader } from '@astrojs/starlight/loaders';
3
+ import { docsSchema } from '@astrojs/starlight/schema';
4
+
5
+ export const collections = {
6
+ docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }),
7
+ };
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "astro/tsconfigs/strict",
3
+ "include": [
4
+ ".astro/types.d.ts",
5
+ "**/*"
6
+ ],
7
+ "exclude": [
8
+ "dist"
9
+ ]
10
+ }
@@ -0,0 +1,26 @@
1
+ # Default recipe - list all available commands
2
+ default:
3
+ @just --list
4
+
5
+ # Generate types and metadata from upstream schemas
6
+ generate:
7
+ bun run src/codegen/generate-cli.ts
8
+
9
+ # Validate generated artifacts
10
+ validate:
11
+ bun run src/validate-cli.ts
12
+
13
+ # Generate docs site, install deps, and start dev server
14
+ docs:
15
+ bun run src/codegen/docs-cli.ts
16
+ bun install --cwd docs
17
+ bun --cwd docs dev
18
+
19
+ # Build docs site for production
20
+ docs-build:
21
+ bun run src/codegen/docs-cli.ts
22
+ bun install --cwd docs
23
+ bun --cwd docs build
24
+
25
+ # Package the lexicon (generate + validate)
26
+ package: generate validate
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@intentius/chant-lexicon-fixture",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "private": true,
6
+ "files": [
7
+ "src/",
8
+ "dist/"
9
+ ],
10
+ "exports": {
11
+ ".": "./src/index.ts",
12
+ "./*": "./src/*",
13
+ "./manifest": "./dist/manifest.json",
14
+ "./meta": "./dist/meta.json",
15
+ "./types": "./dist/types/index.d.ts"
16
+ },
17
+ "scripts": {
18
+ "generate": "bun run src/codegen/generate-cli.ts",
19
+ "validate": "bun run src/validate-cli.ts",
20
+ "docs": "bun src/codegen/docs-cli.ts",
21
+ "prepack": "bun run generate && bun run validate"
22
+ },
23
+ "dependencies": {
24
+ "@intentius/chant": "workspace:*"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.9.3"
28
+ }
29
+ }
@@ -0,0 +1,25 @@
1
+ import { docsPipeline, writeDocsSite } from "@intentius/chant/codegen/docs";
2
+
3
+ /**
4
+ * Generate documentation site for the fixture lexicon.
5
+ */
6
+ export async function generateDocs(options?: { verbose?: boolean }): Promise<void> {
7
+ const config = {
8
+ name: "fixture",
9
+ displayName: "Fixture",
10
+ description: "fixture lexicon documentation",
11
+ distDir: "./dist",
12
+ outDir: "./docs",
13
+ // TODO: Implement service grouping for your provider
14
+ serviceFromType: (type: string) => type.split("::")[1] ?? type,
15
+ // TODO: Implement resource documentation URLs
16
+ resourceTypeUrl: (type: string) => `#${type}`,
17
+ };
18
+
19
+ const result = docsPipeline(config);
20
+ writeDocsSite(config, result);
21
+
22
+ if (options?.verbose) {
23
+ console.error("Documentation generated");
24
+ }
25
+ }
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bun
2
+ import { generate, writeGeneratedFiles } from "./generate";
3
+ import { dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ const pkgDir = dirname(dirname(fileURLToPath(import.meta.url)));
7
+ const result = await generate({ verbose: true });
8
+ writeGeneratedFiles(result, pkgDir);
@@ -0,0 +1,74 @@
1
+ import { generatePipeline, writeGeneratedArtifacts } from "@intentius/chant/codegen/generate";
2
+ import type { GenerateResult } from "@intentius/chant/codegen/generate";
3
+ import { dirname } from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ /**
7
+ * Run the code generation pipeline.
8
+ *
9
+ * Each callback has a TODO describing what to implement.
10
+ */
11
+ export async function generate(options?: { verbose?: boolean }): Promise<GenerateResult> {
12
+ const result = await generatePipeline({
13
+ // Must return Map<typeName, Buffer> — each entry is one schema file.
14
+ // Example: fetch a zip, extract JSON files, key by type name.
15
+ // See lexicons/aws/src/spec/fetch.ts for a working example.
16
+ fetchSchemas: async (opts) => {
17
+ throw new Error("TODO: implement fetchSchemas — download your upstream spec");
18
+ },
19
+
20
+ // Must return a ParsedResult (with propertyTypes[] and enums[] at minimum).
21
+ // Return null to skip a schema file.
22
+ // See lexicons/aws/src/spec/parse.ts for a working example.
23
+ parseSchema: (name, data) => {
24
+ throw new Error("TODO: implement parseSchema — parse a single schema file");
25
+ },
26
+
27
+ // Must return a NamingStrategy instance.
28
+ // See lexicons/aws/src/codegen/naming.ts and ./naming.ts for setup.
29
+ createNaming: (results) => {
30
+ throw new Error("TODO: implement createNaming — return a NamingStrategy instance");
31
+ },
32
+
33
+ // Must return a string of JSON (the lexicon registry).
34
+ // Use buildRegistry + serializeRegistry from @intentius/chant/codegen/generate-registry.
35
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
36
+ generateRegistry: (results, naming) => {
37
+ throw new Error("TODO: implement generateRegistry — produce lexicon JSON");
38
+ },
39
+
40
+ // Must return a string of TypeScript declarations (.d.ts content).
41
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
42
+ generateTypes: (results, naming) => {
43
+ throw new Error("TODO: implement generateTypes — produce .d.ts content");
44
+ },
45
+
46
+ // Must return a string of TypeScript (runtime index with factory exports).
47
+ // Use generateRuntimeIndex from @intentius/chant/codegen/generate-runtime-index.
48
+ // See lexicons/aws/src/codegen/generate.ts for a working example.
49
+ generateRuntimeIndex: (results, naming) => {
50
+ throw new Error("TODO: implement generateRuntimeIndex — produce index.ts content");
51
+ },
52
+ });
53
+
54
+ if (options?.verbose) {
55
+ console.error(`Generated ${result.resources} resources, ${result.properties} property types`);
56
+ }
57
+
58
+ return result;
59
+ }
60
+
61
+ /**
62
+ * Write generated files to the package directory.
63
+ */
64
+ export function writeGeneratedFiles(result: GenerateResult, pkgDir?: string): void {
65
+ const dir = pkgDir ?? dirname(dirname(fileURLToPath(import.meta.url)));
66
+ writeGeneratedArtifacts({
67
+ baseDir: dir,
68
+ files: {
69
+ "lexicon.json": result.lexiconJSON,
70
+ "index.d.ts": result.typesDTS,
71
+ "index.ts": result.indexTS,
72
+ },
73
+ });
74
+ }