@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
package/README.md ADDED
@@ -0,0 +1,365 @@
1
+ # @intentius/chant
2
+
3
+ > Part of the [chant](../../README.md) monorepo. Not yet published to npm.
4
+
5
+ Core functionality for chant - a lexicon-agnostic declarative infrastructure specification system.
6
+
7
+ ## Overview
8
+
9
+ This package provides the foundational types, interfaces, and utilities that power chant's declarative infrastructure system. It includes:
10
+
11
+ - **Type system**: Declarables, Intrinsics, Parameters, and Outputs
12
+ - **Discovery system**: File scanning, module loading, and entity collection
13
+ - **Build pipeline**: Dependency resolution, topological sorting, and serialization
14
+ - **Error handling**: Structured errors for discovery, build, and lint phases
15
+ - **Lint system**: Infrastructure code validation framework
16
+ - **Template import**: Convert external templates to TypeScript
17
+ - **Codegen infrastructure**: Reusable pipelines for generating, naming, packaging, and fetching schemas
18
+ - **LSP providers**: Generic lexicon-based completion and hover helpers
19
+ - **Runtime factories**: `createResource` and `createProperty` for Declarable-marked constructors
20
+
21
+ ## Key Concepts
22
+
23
+ ### Declarables
24
+
25
+ A `Declarable` is any entity that can be declared in an infrastructure specification:
26
+
27
+ ```typescript
28
+ import { isDeclarable, DECLARABLE_MARKER } from "@intentius/chant";
29
+
30
+ if (isDeclarable(value)) {
31
+ console.log(value.entityType);
32
+ }
33
+ ```
34
+
35
+ ### Intrinsics
36
+
37
+ An `Intrinsic` represents a lexicon-provided function resolved at build time:
38
+
39
+ ```typescript
40
+ import { isIntrinsic, INTRINSIC_MARKER } from "@intentius/chant";
41
+
42
+ if (isIntrinsic(value)) {
43
+ const serialized = value.toJSON();
44
+ }
45
+ ```
46
+
47
+ ### AttrRef
48
+
49
+ Reference attributes of other entities with deferred resolution:
50
+
51
+ ```typescript
52
+ import { AttrRef } from "@intentius/chant";
53
+
54
+ const bucket = {};
55
+ const arnRef = new AttrRef(bucket, "arn");
56
+
57
+ // Later, during discovery
58
+ arnRef._setLogicalName("MyBucket");
59
+ arnRef.toJSON(); // { "Fn::GetAttr": ["MyBucket", "arn"] }
60
+ ```
61
+
62
+ ## Discovery System
63
+
64
+ Discover and collect infrastructure entities from TypeScript files:
65
+
66
+ ```typescript
67
+ import { discover } from "@intentius/chant";
68
+
69
+ const result = await discover("./src/infra");
70
+ // result.entities: Map of entity name to Declarable
71
+ // result.dependencies: Dependency graph
72
+ // result.sourceFiles: Discovered files
73
+ // result.errors: Any errors encountered
74
+ ```
75
+
76
+ ### Individual Discovery Functions
77
+
78
+ ```typescript
79
+ import {
80
+ findInfraFiles,
81
+ importModule,
82
+ collectEntities,
83
+ resolveAttrRefs,
84
+ buildDependencyGraph,
85
+ detectCycles,
86
+ topologicalSort,
87
+ } from "@intentius/chant";
88
+
89
+ // Find all .ts files (excluding tests and node_modules)
90
+ const files = await findInfraFiles("./src");
91
+
92
+ // Import a module
93
+ const exports = await importModule("./src/resources.ts");
94
+
95
+ // Collect declarables from modules
96
+ const entities = collectEntities([
97
+ { file: "resources.ts", exports }
98
+ ]);
99
+
100
+ // Resolve attribute references
101
+ resolveAttrRefs(entities);
102
+
103
+ // Build dependency graph
104
+ const deps = buildDependencyGraph(entities);
105
+
106
+ // Check for cycles
107
+ const cycles = detectCycles(deps);
108
+
109
+ // Topological sort
110
+ const order = topologicalSort(deps);
111
+ ```
112
+
113
+ ## Build Pipeline
114
+
115
+ Build infrastructure specifications with lexicon-specific serialization:
116
+
117
+ ```typescript
118
+ import { build } from "@intentius/chant";
119
+ import { myLexicon } from "@intentius/chant-lexicon-myplatform";
120
+
121
+ const result = await build("./src/infra", [myLexicon]);
122
+ // result.outputs: Map of lexicon name to serialized output
123
+ // result.entities: Discovered entities
124
+ // result.warnings: Build warnings
125
+ // result.errors: Any errors
126
+ ```
127
+
128
+ ## Error Handling
129
+
130
+ Structured error types for different phases:
131
+
132
+ ```typescript
133
+ import { DiscoveryError, BuildError, LintError } from "@intentius/chant";
134
+
135
+ // Discovery phase errors
136
+ throw new DiscoveryError("config.ts", "Module not found", "import");
137
+
138
+ // Build phase errors
139
+ throw new BuildError("MyResource", "Invalid configuration");
140
+
141
+ // Lint errors with location
142
+ const error = new LintError(
143
+ "config.ts",
144
+ 10,
145
+ 5,
146
+ "no-unused",
147
+ "Variable is declared but never used"
148
+ );
149
+
150
+ // All errors support JSON serialization
151
+ const json = error.toJSON();
152
+ ```
153
+
154
+ ## Lint System
155
+
156
+ Validate infrastructure code with custom rules:
157
+
158
+ ```typescript
159
+ import { parseFile } from "@intentius/chant";
160
+ import type { LintRule, LintContext } from "@intentius/chant";
161
+
162
+ // Parse a file for linting
163
+ const sourceFile = parseFile("./src/infra/main.ts");
164
+
165
+ // Define a lint rule
166
+ const myRule: LintRule = {
167
+ id: "my-rule",
168
+ severity: "error",
169
+ category: "correctness",
170
+ check(context: LintContext): LintDiagnostic[] {
171
+ // Analyze context.sourceFile
172
+ return [];
173
+ },
174
+ fix(context: LintContext): LintFix[] {
175
+ // Optional: provide auto-fixes
176
+ return [];
177
+ }
178
+ };
179
+ ```
180
+
181
+ ## Serializer Interface
182
+
183
+ Implement lexicon-specific serialization:
184
+
185
+ ```typescript
186
+ import type { Serializer, Declarable } from "@intentius/chant";
187
+
188
+ const mySerializer: Serializer = {
189
+ name: "my-lexicon",
190
+ rulePrefix: "ML",
191
+ serialize(entities: Map<string, Declarable>): string {
192
+ // Serialize entities to lexicon-specific format
193
+ return JSON.stringify({ entities: Array.from(entities.keys()) });
194
+ }
195
+ };
196
+ ```
197
+
198
+ ## Utilities
199
+
200
+ Helper functions for working with declarables:
201
+
202
+ ```typescript
203
+ import {
204
+ getAttributes,
205
+ getLogicalName,
206
+ LOGICAL_NAME_SYMBOL
207
+ } from "@intentius/chant";
208
+
209
+ // Get attribute names that have AttrRef values
210
+ const attrs = getAttributes(myEntity);
211
+ // ["arn", "endpoint"]
212
+
213
+ // Get the logical name assigned during discovery
214
+ (entity as any)[LOGICAL_NAME_SYMBOL] = "MyResource";
215
+ const name = getLogicalName(entity);
216
+ // "MyResource"
217
+ ```
218
+
219
+ ## Lexicon Detection
220
+
221
+ Automatically detect which lexicon is being used:
222
+
223
+ ```typescript
224
+ import { detectLexicon } from "@intentius/chant";
225
+
226
+ const lexicon = await detectLexicon(["./src/infra/main.ts"]);
227
+ // "aws" (currently the only supported lexicon)
228
+ ```
229
+
230
+ ## Template Import System
231
+
232
+ Convert external templates to TypeScript:
233
+
234
+ ```typescript
235
+ import type {
236
+ TemplateIR,
237
+ TemplateParser,
238
+ TypeScriptGenerator
239
+ } from "@intentius/chant";
240
+
241
+ // Parser converts external format to IR
242
+ const parser: TemplateParser = {
243
+ parse(content: string): TemplateIR {
244
+ return { resources: [], parameters: [] };
245
+ }
246
+ };
247
+
248
+ // Generator converts IR to TypeScript
249
+ const generator: TypeScriptGenerator = {
250
+ generate(ir: TemplateIR): GeneratedFile[] {
251
+ return [{ path: "resources.ts", content: "..." }];
252
+ }
253
+ };
254
+ ```
255
+
256
+ ## Codegen Infrastructure
257
+
258
+ Core provides reusable infrastructure for lexicon code generation pipelines. Lexicons supply provider-specific callbacks; core handles orchestration.
259
+
260
+ ### Runtime Factories
261
+
262
+ Create Declarable-marked constructors for generated resource and property types:
263
+
264
+ ```typescript
265
+ import { createResource, createProperty } from "@intentius/chant/runtime";
266
+
267
+ // Creates a constructor that stamps out Declarable objects
268
+ const MyResource = createResource("Provider::Service::Type", "my-lexicon", { arn: "Arn" });
269
+ const MyProperty = createProperty("Provider::Service::Type.PropType", "my-lexicon");
270
+ ```
271
+
272
+ ### Naming Strategy
273
+
274
+ Collision-free TypeScript class name generation, parameterized by data tables:
275
+
276
+ ```typescript
277
+ import { NamingStrategy, type NamingConfig } from "@intentius/chant/codegen/naming";
278
+
279
+ const config: NamingConfig = {
280
+ priorityNames: { "Provider::S3::Bucket": "Bucket" },
281
+ priorityAliases: {},
282
+ priorityPropertyAliases: {},
283
+ serviceAbbreviations: { "ElasticLoadBalancingV2": "Elbv2" },
284
+ shortName: (t) => t.split("::").pop()!,
285
+ serviceName: (t) => t.split("::")[1],
286
+ };
287
+
288
+ const naming = new NamingStrategy(inputs, config);
289
+ naming.resolve("Provider::S3::Bucket"); // "Bucket"
290
+ ```
291
+
292
+ ### Generation Pipeline
293
+
294
+ Generic pipeline orchestration — step sequencing, logging, warning collection, stats counting:
295
+
296
+ ```typescript
297
+ import { generatePipeline, type GeneratePipelineConfig } from "@intentius/chant/codegen/generate";
298
+
299
+ const config: GeneratePipelineConfig<MyParsedResult> = {
300
+ fetchSchemas: async (opts) => { /* ... */ },
301
+ parseSchema: (name, data) => { /* ... */ },
302
+ createNaming: (results) => new MyNamingStrategy(results),
303
+ generateRegistry: (results, naming) => { /* ... */ },
304
+ generateTypes: (results, naming) => { /* ... */ },
305
+ generateRuntimeIndex: (results, naming) => { /* ... */ },
306
+ };
307
+
308
+ const result = await generatePipeline(config, { verbose: true });
309
+ ```
310
+
311
+ ### Packaging Pipeline
312
+
313
+ Bundles generation output into a distributable `BundleSpec`:
314
+
315
+ ```typescript
316
+ import { packagePipeline, type PackagePipelineConfig } from "@intentius/chant/codegen/package";
317
+
318
+ const result = await packagePipeline({
319
+ generate: (opts) => myGenerate(opts),
320
+ buildManifest: (genResult) => ({ name: "my-lexicon", version: "1.0.0", /* ... */ }),
321
+ srcDir: __dirname,
322
+ collectSkills: () => new Map(),
323
+ });
324
+ ```
325
+
326
+ ### Fetch Utilities
327
+
328
+ HTTP fetch with local file caching and zip extraction:
329
+
330
+ ```typescript
331
+ import { fetchWithCache, extractFromZip } from "@intentius/chant/codegen/fetch";
332
+
333
+ const data = await fetchWithCache({ url: "https://...", cacheFile: "/tmp/cache.zip" });
334
+ const files = await extractFromZip(data, (name) => name.endsWith(".json"));
335
+ ```
336
+
337
+ ### LSP Providers
338
+
339
+ Generic lexicon-based completion and hover:
340
+
341
+ ```typescript
342
+ import { LexiconIndex, lexiconCompletions, lexiconHover } from "@intentius/chant/lsp/lexicon-providers";
343
+
344
+ const index = new LexiconIndex(lexiconData);
345
+ const completions = lexiconCompletions(ctx, index, "My resource");
346
+ const hover = lexiconHover(ctx, index);
347
+ ```
348
+
349
+ ## TypeScript Support
350
+
351
+ This package is written in TypeScript and provides full type definitions.
352
+
353
+ ## Documentation
354
+
355
+ - [Core Concepts](../../docs/src/content/docs/guides/core-concepts.md) - Detailed guide on declarables, intrinsics, and the type system
356
+ - [Testing Guide](../../TESTING.md) - Testing patterns and utilities
357
+
358
+ ## Related Packages
359
+
360
+ - `@intentius/chant-lexicon-aws` - AWS CloudFormation lexicon
361
+ - `@intentius/chant-test-utils` - Testing utilities
362
+
363
+ ## License
364
+
365
+ See the main project LICENSE file.
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@intentius/chant",
3
+ "version": "0.0.1",
4
+ "type": "module",
5
+ "files": ["src/"],
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "bin": {
10
+ "chant": "./src/cli/main.ts"
11
+ },
12
+ "exports": {
13
+ ".": "./src/index.ts",
14
+ "./cli": "./src/cli/index.ts",
15
+ "./cli/*": "./src/cli/*",
16
+ "./*": "./src/*"
17
+ },
18
+ "dependencies": {
19
+ "fflate": "^0.8.2",
20
+ "zod": "^4.3.6"
21
+ }
22
+ }
@@ -0,0 +1,148 @@
1
+ import { describe, test, expect } from "bun:test";
2
+ import { AttrRef } from "./attrref";
3
+ import { INTRINSIC_MARKER, isIntrinsic } from "./intrinsic";
4
+
5
+ describe("AttrRef", () => {
6
+ test("creates AttrRef with parent and attribute", () => {
7
+ const parent = { id: "test" };
8
+ const attrRef = new AttrRef(parent, "Arn");
9
+
10
+ expect(attrRef).toBeInstanceOf(AttrRef);
11
+ expect(attrRef.attribute).toBe("Arn");
12
+ expect(attrRef.parent.deref()).toBe(parent);
13
+ });
14
+
15
+ test("implements Intrinsic interface", () => {
16
+ const parent = {};
17
+ const attrRef = new AttrRef(parent, "Name");
18
+
19
+ expect(attrRef[INTRINSIC_MARKER]).toBe(true);
20
+ expect(isIntrinsic(attrRef)).toBe(true);
21
+ });
22
+
23
+ test("stores parent as WeakRef", () => {
24
+ const parent = { id: "test" };
25
+ const attrRef = new AttrRef(parent, "Id");
26
+
27
+ expect(attrRef.parent).toBeInstanceOf(WeakRef);
28
+ expect(attrRef.parent.deref()).toBe(parent);
29
+ });
30
+
31
+ test("getLogicalName returns undefined before set", () => {
32
+ const parent = {};
33
+ const attrRef = new AttrRef(parent, "Arn");
34
+
35
+ expect(attrRef.getLogicalName()).toBeUndefined();
36
+ });
37
+
38
+ test("getLogicalName returns the logical name after set", () => {
39
+ const parent = {};
40
+ const attrRef = new AttrRef(parent, "Arn");
41
+
42
+ attrRef._setLogicalName("MyResource");
43
+ expect(attrRef.getLogicalName()).toBe("MyResource");
44
+ });
45
+
46
+ test("_setLogicalName sets the logical name", () => {
47
+ const parent = {};
48
+ const attrRef = new AttrRef(parent, "Arn");
49
+
50
+ attrRef._setLogicalName("MyResource");
51
+
52
+ const json = attrRef.toJSON();
53
+ expect(json).toEqual({
54
+ __attrRef: { entity: "MyResource", attribute: "Arn" },
55
+ });
56
+ });
57
+
58
+ test("toJSON returns __attrRef when logical name is set", () => {
59
+ const parent = {};
60
+ const attrRef = new AttrRef(parent, "DomainName");
61
+
62
+ attrRef._setLogicalName("MyBucket");
63
+
64
+ const json = attrRef.toJSON();
65
+ expect(json).toEqual({
66
+ __attrRef: { entity: "MyBucket", attribute: "DomainName" },
67
+ });
68
+ });
69
+
70
+ test("toJSON throws when logical name is not set", () => {
71
+ const parent = {};
72
+ const attrRef = new AttrRef(parent, "Arn");
73
+
74
+ expect(() => attrRef.toJSON()).toThrow(
75
+ 'Cannot serialize AttrRef for attribute "Arn": logical name not set'
76
+ );
77
+ });
78
+
79
+ test("toJSON throws with correct attribute name in error message", () => {
80
+ const parent = {};
81
+ const attrRef = new AttrRef(parent, "CustomAttribute");
82
+
83
+ expect(() => attrRef.toJSON()).toThrow(
84
+ 'Cannot serialize AttrRef for attribute "CustomAttribute": logical name not set'
85
+ );
86
+ });
87
+
88
+ test("handles different attribute names", () => {
89
+ const parent = {};
90
+ const attributes = ["Arn", "Name", "Id", "DomainName", "PhysicalResourceId"];
91
+
92
+ for (const attr of attributes) {
93
+ const attrRef = new AttrRef(parent, attr);
94
+ attrRef._setLogicalName("Resource");
95
+
96
+ const json = attrRef.toJSON();
97
+ expect(json).toEqual({
98
+ __attrRef: { entity: "Resource", attribute: attr },
99
+ });
100
+ }
101
+ });
102
+
103
+ test("handles different logical names", () => {
104
+ const parent = {};
105
+ const logicalNames = ["MyBucket", "MyTable", "MyFunction", "My-Resource-123"];
106
+
107
+ for (const name of logicalNames) {
108
+ const attrRef = new AttrRef(parent, "Arn");
109
+ attrRef._setLogicalName(name);
110
+
111
+ const json = attrRef.toJSON();
112
+ expect(json).toEqual({
113
+ __attrRef: { entity: name, attribute: "Arn" },
114
+ });
115
+ }
116
+ });
117
+
118
+ test("_setLogicalName can be called multiple times", () => {
119
+ const parent = {};
120
+ const attrRef = new AttrRef(parent, "Arn");
121
+
122
+ attrRef._setLogicalName("FirstName");
123
+ expect(attrRef.toJSON()).toEqual({
124
+ __attrRef: { entity: "FirstName", attribute: "Arn" },
125
+ });
126
+
127
+ attrRef._setLogicalName("SecondName");
128
+ expect(attrRef.toJSON()).toEqual({
129
+ __attrRef: { entity: "SecondName", attribute: "Arn" },
130
+ });
131
+ });
132
+
133
+ test("maintains separate logical names for different instances", () => {
134
+ const parent = {};
135
+ const attrRef1 = new AttrRef(parent, "Arn");
136
+ const attrRef2 = new AttrRef(parent, "Name");
137
+
138
+ attrRef1._setLogicalName("Resource1");
139
+ attrRef2._setLogicalName("Resource2");
140
+
141
+ expect(attrRef1.toJSON()).toEqual({
142
+ __attrRef: { entity: "Resource1", attribute: "Arn" },
143
+ });
144
+ expect(attrRef2.toJSON()).toEqual({
145
+ __attrRef: { entity: "Resource2", attribute: "Name" },
146
+ });
147
+ });
148
+ });
package/src/attrref.ts ADDED
@@ -0,0 +1,50 @@
1
+ import { INTRINSIC_MARKER, type Intrinsic } from "./intrinsic";
2
+
3
+ /**
4
+ * Reference to an attribute of a parent entity.
5
+ * Lexicon serializers read `getLogicalName()` and `attribute` to produce
6
+ * their own output format (e.g. CloudFormation `Fn::GetAttr`).
7
+ */
8
+ export class AttrRef implements Intrinsic {
9
+ readonly [INTRINSIC_MARKER] = true as const;
10
+ readonly parent: WeakRef<object>;
11
+ readonly attribute: string;
12
+ private logicalName?: string;
13
+
14
+ constructor(parent: object, attribute: string) {
15
+ this.parent = new WeakRef(parent);
16
+ this.attribute = attribute;
17
+ }
18
+
19
+ /**
20
+ * Set the logical name for this attribute reference
21
+ * @internal
22
+ */
23
+ _setLogicalName(name: string): void {
24
+ this.logicalName = name;
25
+ }
26
+
27
+ /**
28
+ * Get the logical name assigned to this attribute reference.
29
+ */
30
+ getLogicalName(): string | undefined {
31
+ return this.logicalName;
32
+ }
33
+
34
+ /**
35
+ * Serialize to a generic envelope. Lexicon-specific serializers should
36
+ * read `getLogicalName()` and `attribute` directly instead of relying
37
+ * on this format.
38
+ * @throws {Error} If logical name has not been set
39
+ */
40
+ toJSON(): { __attrRef: { entity: string; attribute: string } } {
41
+ if (!this.logicalName) {
42
+ throw new Error(
43
+ `Cannot serialize AttrRef for attribute "${this.attribute}": logical name not set`
44
+ );
45
+ }
46
+ return {
47
+ __attrRef: { entity: this.logicalName, attribute: this.attribute },
48
+ };
49
+ }
50
+ }