@venn-lang/core 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 (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +303 -0
  3. package/dist/index.d.ts +3654 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +13137 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +61 -0
  8. package/src/ast/call-args.ts +15 -0
  9. package/src/ast/dotted-path.ts +22 -0
  10. package/src/ast/index.ts +7 -0
  11. package/src/ast/is-statement.ts +40 -0
  12. package/src/ast/split-call.ts +28 -0
  13. package/src/ast/walk.ts +6 -0
  14. package/src/codes/build-problem.ts +24 -0
  15. package/src/codes/catalog.ts +35 -0
  16. package/src/codes/code.types.ts +7 -0
  17. package/src/codes/index.ts +3 -0
  18. package/src/compile/compile.ts +169 -0
  19. package/src/compile/compile.types.ts +40 -0
  20. package/src/compile/index.ts +3 -0
  21. package/src/compile/lex-scope.ts +78 -0
  22. package/src/compile/nodes/binary.ts +56 -0
  23. package/src/compile/nodes/call.ts +50 -0
  24. package/src/compile/nodes/collection.ts +83 -0
  25. package/src/compile/nodes/const-lit.ts +30 -0
  26. package/src/compile/nodes/fast-binary.ts +71 -0
  27. package/src/compile/nodes/fn.ts +87 -0
  28. package/src/compile/nodes/index.ts +8 -0
  29. package/src/compile/nodes/literal.ts +86 -0
  30. package/src/compile/nodes/map-fields.ts +79 -0
  31. package/src/compile/nodes/member.ts +32 -0
  32. package/src/events/envelope.types.ts +17 -0
  33. package/src/events/event-data.types.ts +25 -0
  34. package/src/events/ids.types.ts +10 -0
  35. package/src/events/index.ts +5 -0
  36. package/src/events/run-plan.types.ts +18 -0
  37. package/src/events/status.types.ts +2 -0
  38. package/src/expand/accepted-kinds.ts +23 -0
  39. package/src/expand/deco/deco-decorator.ts +77 -0
  40. package/src/expand/deco/deco-env.ts +24 -0
  41. package/src/expand/deco/deco.types.ts +43 -0
  42. package/src/expand/deco/document-decos.ts +64 -0
  43. package/src/expand/deco/index.ts +7 -0
  44. package/src/expand/deco/read-signature.ts +57 -0
  45. package/src/expand/deco/run-body.ts +119 -0
  46. package/src/expand/decorate-callable.ts +49 -0
  47. package/src/expand/decorations.ts +41 -0
  48. package/src/expand/expand.ts +108 -0
  49. package/src/expand/expand.types.ts +78 -0
  50. package/src/expand/handles/around-verbs.ts +35 -0
  51. package/src/expand/handles/binding-verbs.ts +27 -0
  52. package/src/expand/handles/common-verbs.ts +40 -0
  53. package/src/expand/handles/fn-verbs.ts +58 -0
  54. package/src/expand/handles/handle.types.ts +33 -0
  55. package/src/expand/handles/index.ts +4 -0
  56. package/src/expand/handles/kind-of.ts +36 -0
  57. package/src/expand/handles/make-handle.ts +100 -0
  58. package/src/expand/handles/missing-verb.ts +27 -0
  59. package/src/expand/handles/runnable-verbs.ts +13 -0
  60. package/src/expand/handles/type-verbs.ts +56 -0
  61. package/src/expand/index.ts +40 -0
  62. package/src/expand/make-context.ts +69 -0
  63. package/src/expand/node-meta.ts +30 -0
  64. package/src/expand/node-span.ts +17 -0
  65. package/src/expand/swap-node.ts +28 -0
  66. package/src/expand/wrong-kind.ts +102 -0
  67. package/src/expr/cell.types.ts +25 -0
  68. package/src/expr/closure.ts +53 -0
  69. package/src/expr/closure.types.ts +31 -0
  70. package/src/expr/eval-env.types.ts +9 -0
  71. package/src/expr/evaluate.ts +18 -0
  72. package/src/expr/frame.ts +60 -0
  73. package/src/expr/index.ts +14 -0
  74. package/src/expr/invoke.ts +140 -0
  75. package/src/expr/member-value.ts +91 -0
  76. package/src/expr/methods/index.ts +65 -0
  77. package/src/expr/methods/list-grouping.ts +107 -0
  78. package/src/expr/methods/list-methods.ts +57 -0
  79. package/src/expr/methods/list-selection.ts +142 -0
  80. package/src/expr/methods/map-extras.ts +87 -0
  81. package/src/expr/methods/map-methods.ts +17 -0
  82. package/src/expr/methods/number-methods.ts +29 -0
  83. package/src/expr/methods/string-extras.ts +66 -0
  84. package/src/expr/methods/string-methods.ts +32 -0
  85. package/src/expr/methods/unit-methods.ts +54 -0
  86. package/src/expr/namespace.ts +20 -0
  87. package/src/expr/native.types.ts +40 -0
  88. package/src/expr/operators.ts +91 -0
  89. package/src/expr/pending.ts +47 -0
  90. package/src/expr/prelude.ts +75 -0
  91. package/src/expr/task.ts +71 -0
  92. package/src/format/format-text.ts +22 -0
  93. package/src/format/format.types.ts +18 -0
  94. package/src/format/from-settings.ts +40 -0
  95. package/src/format/index.ts +5 -0
  96. package/src/format/organize-header.ts +66 -0
  97. package/src/format/reindent.ts +35 -0
  98. package/src/generated/ast.ts +2383 -0
  99. package/src/generated/grammar.ts +5236 -0
  100. package/src/generated/module.ts +25 -0
  101. package/src/grammar/venn.langium +291 -0
  102. package/src/graph/graph.types.ts +20 -0
  103. package/src/graph/index.ts +2 -0
  104. package/src/graph/to-graph.ts +87 -0
  105. package/src/index.ts +93 -0
  106. package/src/interpolation/compile-template.ts +40 -0
  107. package/src/interpolation/index.ts +4 -0
  108. package/src/interpolation/interpolation.types.ts +18 -0
  109. package/src/interpolation/scan-interpolations.ts +47 -0
  110. package/src/interpolation/template.types.ts +19 -0
  111. package/src/lang/default-services.ts +14 -0
  112. package/src/lang/index.ts +3 -0
  113. package/src/lang/venn-lexer.ts +34 -0
  114. package/src/lang/venn-module.ts +51 -0
  115. package/src/module/index.ts +5 -0
  116. package/src/module/specifier.ts +21 -0
  117. package/src/parse/error-to-problem.ts +43 -0
  118. package/src/parse/index.ts +3 -0
  119. package/src/parse/parse-expression.ts +27 -0
  120. package/src/parse/parse-output.types.ts +8 -0
  121. package/src/parse/parse.ts +26 -0
  122. package/src/problem/build-diff.ts +116 -0
  123. package/src/problem/diff.types.ts +20 -0
  124. package/src/problem/format-value.ts +40 -0
  125. package/src/problem/index.ts +8 -0
  126. package/src/problem/problem-error.ts +15 -0
  127. package/src/problem/problem.types.ts +21 -0
  128. package/src/problem/related.types.ts +7 -0
  129. package/src/problem/severity.types.ts +2 -0
  130. package/src/problem/span.types.ts +8 -0
  131. package/src/typecheck/action-signature.ts +53 -0
  132. package/src/typecheck/builtin-types.ts +63 -0
  133. package/src/typecheck/builtins.ts +267 -0
  134. package/src/typecheck/catalog.types.ts +16 -0
  135. package/src/typecheck/check-deco.ts +174 -0
  136. package/src/typecheck/check-stmts.ts +151 -0
  137. package/src/typecheck/check-types.ts +232 -0
  138. package/src/typecheck/context.ts +31 -0
  139. package/src/typecheck/imported-types.ts +127 -0
  140. package/src/typecheck/index.ts +26 -0
  141. package/src/typecheck/infer.ts +416 -0
  142. package/src/typecheck/kind-types.ts +79 -0
  143. package/src/typecheck/member-docs.ts +211 -0
  144. package/src/typecheck/named-types.ts +57 -0
  145. package/src/typecheck/prelude-types.ts +152 -0
  146. package/src/typecheck/reshaped-fns.ts +58 -0
  147. package/src/typecheck/scheme.ts +75 -0
  148. package/src/typecheck/seed-params.ts +72 -0
  149. package/src/typecheck/seed-values.ts +64 -0
  150. package/src/typecheck/show.ts +64 -0
  151. package/src/typecheck/solidify.ts +60 -0
  152. package/src/typecheck/spec-to-type.ts +88 -0
  153. package/src/typecheck/type-env.ts +46 -0
  154. package/src/typecheck/type-ref.ts +62 -0
  155. package/src/typecheck/type.types.ts +168 -0
  156. package/src/typecheck/unify.ts +192 -0
  157. package/src/typecheck/unit-types.ts +49 -0
  158. package/src/units/combine.ts +97 -0
  159. package/src/units/index.ts +6 -0
  160. package/src/units/parse-instant.ts +12 -0
  161. package/src/units/parse-number.ts +48 -0
  162. package/src/units/unit-guards.ts +16 -0
  163. package/src/units/unit.types.ts +38 -0
  164. package/src/value/equals.ts +4 -0
  165. package/src/value/index.ts +4 -0
  166. package/src/value/is-numeric.ts +6 -0
  167. package/src/value/truthiness.ts +9 -0
  168. package/src/value/value.types.ts +12 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vinicius Borges
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # @venn-lang/core
2
+
3
+ > The Venn compiler: grammar, parser, type checker, expression compiler, decorator expansion, and the `Problem` model.
4
+
5
+ Everything that turns `.vn` text into something another package can run, check or draw lives here.
6
+ The kernel is deliberately small: it knows flows, steps, expressions, types and decorators, and it
7
+ knows nothing about HTTP, browsers or files. Protocols arrive as plugins built on
8
+ [`@venn-lang/sdk`](../sdk); the core only ever sees the tree they decorate.
9
+
10
+ `@venn-lang/core` never imports `node:*`. It is built with `platform: "neutral"` so the same bundle runs
11
+ in Node, in the CLI, and inside the Web Worker that hosts the language server. Its runtime
12
+ dependencies are Langium (parsing) and [`@venn-lang/types`](../types) (the published type vocabulary).
13
+
14
+ ## Usage
15
+
16
+ ```ts
17
+ import { checkTypes, parse, toGraph } from "@venn-lang/core";
18
+
19
+ const uri = "file:///hello.vn";
20
+ const source = `flow "Hello" {
21
+ step "Ping" {
22
+ http.get "https://example.com/health"
23
+ expect res.status == 200
24
+ }
25
+ }`;
26
+
27
+ const { ast, problems } = parse(source, { uri });
28
+ const checked = checkTypes(ast, { uri });
29
+
30
+ for (const problem of [...problems, ...checked.problems]) {
31
+ console.log(`${problem.code} ${problem.title} (${problem.span.line}:${problem.span.column})`);
32
+ }
33
+
34
+ const graph = toGraph(ast); // flow -> step -> action/expect, for the node editor
35
+ ```
36
+
37
+ `parse` is synchronous and touches no filesystem. Error recovery keeps a partial AST, so a file with
38
+ a syntax error still produces a tree the editor can work with.
39
+
40
+ ## Module map
41
+
42
+ | Module | Responsibility |
43
+ | --- | --- |
44
+ | `grammar/` | `venn.langium`, the whole grammar of the language. The single source the parser is generated from. |
45
+ | `generated/` | Langium output: the AST interfaces, the `isX` type guards, the grammar object and the services module. Regenerated by `pnpm --filter @venn-lang/core langium:generate`. |
46
+ | `lang/` | The Langium services `parse` uses, plus `VennLexer`, which makes newlines significant between statements and suppresses them inside `( )` and `[ ]`. |
47
+ | `parse/` | `parse` and `parseExpression`. Turns text into a `Document` plus VN1xxx problems. |
48
+ | `ast/` | Small readers over the generated tree: `walkAst`, `dottedPath`, `callArgs`, `splitCall`, `isRunnable`. |
49
+ | `problem/` | The `Problem` shape shared by compile diagnostics and runtime failures, with `Span`, `Diff`, `Severity`, `ProblemError` and `buildDiff`. |
50
+ | `codes/` | `CODES`, the catalog of every VNxxxx the kernel can raise, and `buildProblem`. |
51
+ | `expand/` | The expansion phase. Runs every `@decorator` over the tree once, before anything else reads it. |
52
+ | `typecheck/` | Hindley-Milner inference with generics, the `Type` algebra, the plugin `TypeCatalog`, cross-file `importedTypes`, and the prose the editor shows on hover. |
53
+ | `compile/` | Compiles each `Expr` into a `Thunk`, a closure over the environment. Memoised per node, so the tree is walked once and not once per evaluation. |
54
+ | `expr/` | The evaluator surface: `evaluate`, closures and frames, `invoke`, `memberValue`, the built-in method tables, and the prelude values. |
55
+ | `value/` | What a runtime value is, and the three questions asked of one: `truthy`, `strictEquals`, `isNumeric`. |
56
+ | `units/` | Unit-typed values (`300ms`, `2mb`, `50%`, ISO instants) and the arithmetic that keeps units honest. |
57
+ | `interpolation/` | `${…}` placeholders, described once for both the evaluator and the editor. |
58
+ | `format/` | `formatText`, shared by `venn fmt` and the language server so both agree on the result. |
59
+ | `graph/` | `toGraph`: a pure AST transform into nodes and edges for the visual editor. |
60
+ | `events/` | The event envelope the runner emits and the UI consumes. Types only, no runtime. |
61
+ | `module/` | Which of the three kinds an import specifier is: relative, alias or package. |
62
+
63
+ ## API
64
+
65
+ ### Parsing
66
+
67
+ | Export | What it gives you |
68
+ | --- | --- |
69
+ | `parse(text, { uri? })` | `ParseOutput`: the `Document` AST and the VN1xxx problems. |
70
+ | `parseExpression(source)` | An `Expr` for a standalone expression, or `undefined` when it is not one. |
71
+ | `EXPRESSION_OFFSET` | How far `parseExpression` shifts CST offsets, so a caller can map back onto the `${…}` it came from. |
72
+ | `vennServices()` / `createVennServices()` | The Langium services, cached and fresh respectively. |
73
+ | `VennLexer` | The lexer that suppresses newlines inside brackets. |
74
+ | `VennGeneratedModule`, `VennGeneratedSharedModule`, `VennLanguageMetaData` | The generated Langium modules, for a host that builds its own services. |
75
+
76
+ Everything from `generated/ast.ts` is re-exported: `Document`, `FlowDecl`, `StepDecl`, `ActionCall`,
77
+ `ExpectStmt`, `FnDecl`, `DecoDecl`, `Expr` and the rest, each with its `isX` guard.
78
+
79
+ The AST readers exist because the grammar spells some things two ways:
80
+
81
+ | Export | What it answers |
82
+ | --- | --- |
83
+ | `callArgs(call)` | The arguments of a call as one list, whether it was written `http.get "url"` or `conn.close()`. |
84
+ | `splitCall(args, takes)` | Which trailing `{ … }` is an options map and which is a real argument. |
85
+ | `dottedPath(expr)` | The dotted name an expression spells (`http.get`), or `undefined` when it is not a plain chain. |
86
+ | `isRunnable(decl)` | Whether a top-level node runs or merely defines. |
87
+ | `walkAst(root)` | Every descendant, depth first. |
88
+
89
+ ### Problems and codes
90
+
91
+ Every failure, at compile time or at run time, is a `Problem`: a stable `code`, a `severity`, a
92
+ one-line `title` in the user's vocabulary, a `span`, and optional `help`, `related`, `diff`, `note`
93
+ and `docs`. One shape means one renderer serves the terminal, the editor and the UI.
94
+
95
+ ```ts
96
+ import { buildProblem, CODES, ProblemError } from "@venn-lang/core";
97
+
98
+ throw new ProblemError(
99
+ buildProblem({
100
+ spec: CODES.VN6001_ASSERTION_FAILED,
101
+ span: { uri, offset: 0, length: 0, line: 1, column: 1 },
102
+ title: "The response did not arrive before the deadline",
103
+ }),
104
+ );
105
+ ```
106
+
107
+ | Family | Covers |
108
+ | --- | --- |
109
+ | `VN1xxx` | Lexical and syntactic. |
110
+ | `VN2xxx` | Name resolution, imports, decorators, capability negotiation. |
111
+ | `VN3xxx` | Types and units. |
112
+ | `VN4xxx` | Concurrency and isolation. |
113
+ | `VN5xxx` | Lint. |
114
+ | `VN6xxx` | Assertions, at run time. |
115
+ | `VN7xxx` | Actions and protocols, at run time. |
116
+ | `VN8xxx` | Timeouts and resource limits. |
117
+
118
+ `CODES` holds the codes the kernel itself raises, from `VN1001_LEX` to `VN8002_LOOP_LIMIT`. Plugins
119
+ add their own in the same families. `buildDiff` turns two compared values into a structured `Diff`,
120
+ walking them field by field when they line up, so a failure names the field that moved instead of
121
+ printing two renderings side by side. `formatValue` renders a single value for a report.
122
+
123
+ ### Type checking
124
+
125
+ ```ts
126
+ import { checkTypes, showType } from "@venn-lang/core";
127
+
128
+ const { problems, types, slots } = checkTypes(ast, { uri, catalog, decos, imports });
129
+ ```
130
+
131
+ `checkTypes` runs Hindley-Milner inference over the whole document. Functions are generalised, so one
132
+ `fn id(x) => x` serves every type; `dynamic` unifies with everything and never errors, so a plugin
133
+ result or a parsed HTTP response places no annotation burden on the pure parts of a file. It returns
134
+ the problems, every expression's inferred type keyed by node (for hover), and the expressions parsed
135
+ out of each string literal's `${…}` slots.
136
+
137
+ `CheckTypesOptions` is how the checker learns about the world outside the file: `catalog` (what the
138
+ loaded plugins publish), `decos` (the `pub deco`s the imports reach) and `imports` (what each
139
+ imported name turned out to be).
140
+
141
+ | Export | Purpose |
142
+ | --- | --- |
143
+ | `Type`, `FnType`, `RecordType`, `UnionType`, `OpaqueType`, `LiteralType` | The type algebra. |
144
+ | `DYNAMIC`, `literal`, `opaque`, `union` | Constructors for the ones a caller builds by hand. |
145
+ | `prune` | Follow a type variable to what it has been solved to. |
146
+ | `showType`, `showTypes` | Render types for a hover or a diagnostic. `showTypes` names variables across a group, so two unrelated parameters are not both called `a`. |
147
+ | `TypeCatalog` | The two questions the core asks about plugins: `typeOf(name)` and `signatureOf(target)`. |
148
+ | `specToType`, `ResolveRef` | Read a published `TypeSpec` from [`@venn-lang/types`](../types) into the checker's own type. |
149
+ | `importedTypes`, `ImportedTypes` | The types of the names a document imports, worked out from the files it names. Cycles answer with nothing rather than looping. |
150
+ | `memberType`, `resolveMember` | The type of `xs.map`, `s.length`, `p.name`. |
151
+ | `createContext` | Fresh per-check inference state, so two checks never share variable ids. |
152
+ | `BUILTIN_TYPES`, `isBuiltinType` | `string`, `number`, `bool`, `duration`, `size`, `percent`, `instant` and friends, each with a line of documentation and an example. |
153
+ | `PRELUDE_SPECS`, `isPrelude`, `PreludeSpec`, `PreludeArg` | The prelude, described once: types for the checker, prose for the editor. |
154
+ | `MEMBER_DOCS`, `MemberDoc`, `memberKind` | What each built-in member does, for completion and hover. |
155
+ | `KIND_SPECS`, `KIND_TYPES` | What a decorator target handle offers, published as types. |
156
+
157
+ ### Compiling and evaluating expressions
158
+
159
+ An expression is compiled once into a `Thunk`, a function of the environment. Everything the source
160
+ settles (which operator, which literal, which slot a name lives in) is decided at compile time.
161
+
162
+ ```ts
163
+ import { evaluate, type EvalEnv } from "@venn-lang/core";
164
+
165
+ const env: EvalEnv = { lookup: (name) => (name === "res" ? { status: 200 } : undefined) };
166
+ evaluate(subject, env); // true, for `res.status == 200`
167
+ ```
168
+
169
+ | Export | Purpose |
170
+ | --- | --- |
171
+ | `compileExpr(expr)` | The compiled `Thunk`. Memoised per node. |
172
+ | `closureOfDecl(decl, env)` | A top-level `fn` as a callable value bound into an environment. |
173
+ | `evaluate(expr, env)` | Compile if needed, then run. The seam every caller uses. |
174
+ | `EvalEnv` | One method, `lookup(name)`. Actions and matchers are not here: they belong to the runtime registry. |
175
+ | `childEnv(parent, bindings)` | A nested scope. |
176
+ | `Cell`, `CellEnv`, `hasCells` | Bindings addressed by cell, which is what lets a recursive `fn` capture itself. |
177
+ | `invoke`, `invoke1`, `callClosure`, `isCallable` | Calling a Venn callable, with fixed arities for the hot paths. |
178
+ | `Closure`, `isClosure`, `NativeFn`, `nativeFn`, `isNativeFn` | The two kinds of callable. |
179
+ | `memberValue(receiver, member)` | What `.x` means on any value: a map's own data first, then the built-in member tables. |
180
+ | `namespaceValue`, `isNamespaceValue` | A plugin namespace as a value, so `fmt.table(rows)` works inside any expression. |
181
+ | `PRELUDE_VALUES` | `range`, `str`, `typeOf`, `pretty`, `spawn`. |
182
+ | `display(value)`, `typeName(value)` | How `print` renders a value, and what the language calls its type. |
183
+
184
+ `Value` (from `value/`) is the shape a runtime value can take, with `truthy`, `strictEquals` and
185
+ `isNumeric` alongside it. There is no coercion: `"99.00"` never equals `99`.
186
+
187
+ ### Decorator expansion
188
+
189
+ `expand` is the phase between parsing and everything else. It is the only place a `@name` means
190
+ anything. The kernel does not know what `@retry` is; it knows that a name written with an `@` is
191
+ looked up in a `DecoratorSource`, handed the node it sits on, and allowed to rewrite it. Decorators
192
+ written in TypeScript by a plugin and decorators written in Venn with `deco` go through the same
193
+ door.
194
+
195
+ ```ts
196
+ import { expand } from "@venn-lang/core";
197
+
198
+ const { problems } = expand({ document: ast, decorators, uri, imported });
199
+ ```
200
+
201
+ Applied innermost first, so a decorator that rewrites a body finds a body its own decorators have
202
+ already finished with. An `ExpandContext` gives a decorator the node, its evaluated `args`, the same
203
+ arguments as syntax in `written`, the `parent`, and four verbs: `replace`, `remove`, `meta` and
204
+ `reject`.
205
+
206
+ | Export | Purpose |
207
+ | --- | --- |
208
+ | `expand`, `ExpandResult`, `ExpandContext`, `DecoratorDefinition`, `DecoratorSource`, `DecoratedNode`, `NodeMeta` | The mechanism and its contracts. |
209
+ | `TargetKind`, `TARGET_KINDS`, `isTargetKind`, `kindOf` | The words a `deco` uses about what it decorates: `Fn`, `Flow`, `Step`, `Binding`, `Type`, `Node`. `kindOf` is the one place that vocabulary meets the compiler's `$type` names. |
210
+ | `makeHandle`, `handleSurface`, `TargetHandle`, `HandleSurface` | The value a `deco` body holds for its target, and what it publishes. |
211
+ | `readSignature`, `DecoSignature`, `SignatureResult`, `acceptedKinds`, `decoTarget` | Reading what a `deco` decorates off the type of its first parameter. |
212
+ | `decoDecorator`, `withDocumentDecos`, `ImportedDeco`, `DocumentDecoArgs`, `decoCannotCall`, `DecoBodyArgs` | Turning a `deco` declaration, local or imported, into a definition expansion can run. |
213
+ | `metaOf`, `readMeta`, `writeMeta` | Facts a decorator leaves on a node for the runtime. Non-enumerable, so they never land in a serialised AST. |
214
+ | `readDecorations`, `addDecoration`, `AROUND_KEYS`, `Decorations`, `decorateCallable` | Where `.wrap`, `.before` and `.after` leave their closures, and how a callable picks them up. |
215
+ | `swapNode`, `spanOf` | Replacing a node in its container, and locating one. |
216
+ | `wrongKind`, `wrongKindTitle`, `wrongTargetTitle`, `kindWords`, `nodeWord`, `everyKindWritten` | The prose behind VN2014, phrased in the author's words rather than in node type names. |
217
+
218
+ ### Units
219
+
220
+ A number literal may carry a unit, and the unit survives arithmetic.
221
+
222
+ ```ruby
223
+ expect res.duration < 300ms
224
+ expect res.size <= 2mb
225
+ const rate = 99.9%
226
+ ```
227
+
228
+ `parseNumber` turns a NUMBER lexeme into a plain number or a `UnitValue`; `parseInstant` turns an
229
+ ISO-8601 lexeme into an `Instant` that keeps its source text. `combine({ op, left, right })` does the
230
+ arithmetic and comparison: `300ms + 1s` succeeds, `300ms + 2mb` returns the mismatch behind VN3012.
231
+ `isUnitValue` and `isInstant` are the guards. Canonical bases are milliseconds, bytes, a ratio from 0
232
+ to 1, and epoch milliseconds.
233
+
234
+ ### Interpolation
235
+
236
+ `scanInterpolations(text)` locates every `${…}` precisely enough to highlight, hover and jump to.
237
+ `compileTemplate(text)` splits a literal once into its constant `chunks` and its `holes`, each hole
238
+ carrying both its source and its parsed `Expr`. There is always exactly one more chunk than holes, so
239
+ rejoining them cannot drop text. Both are cached by text, because a `.vn` file holds a fixed set of
240
+ string literals.
241
+
242
+ ### Formatting
243
+
244
+ ```ts
245
+ import { DEFAULT_FORMAT, formatOptionsFrom, formatText } from "@venn-lang/core";
246
+
247
+ formatText(source, { indentWidth: 4 });
248
+ formatText(source, formatOptionsFrom(manifest.format)); // the [format] table of venn.toml
249
+ ```
250
+
251
+ `formatText` re-indents by bracket depth and, by default, moves every `use` above every `import`. It
252
+ never joins or splits lines, and it is idempotent. `organizeHeader` and `reindent` are the two steps
253
+ on their own.
254
+
255
+ ### Graph and events
256
+
257
+ `toGraph(document)` derives the node graph from the AST: a `flow` contains its steps, a step contains
258
+ its actions and expectations, and sequential edges run between siblings. Pure, with no runtime state.
259
+
260
+ The `events/` module is types only: `Envelope` is the single contract between runner, host and UI,
261
+ carrying a monotonic `seq`, a timestamp, a `RunId`, a kind and its payload. `EventKind` is derived
262
+ from the keys of `EventData`, so adding an event is one edit. `RunPlan` is what the UI draws in grey
263
+ before anything executes.
264
+
265
+ ### Modules
266
+
267
+ `specifierKind(spec)` classifies an import specifier the way Node does: `./util.vn` is relative,
268
+ `#shared/auth.vn` is an alias resolved through `[paths]`, and a bare name is an installed package.
269
+ `isPackageSpecifier` is the common question asked of it.
270
+
271
+ ## Grammar
272
+
273
+ The grammar lives in [`src/grammar/venn.langium`](src/grammar/venn.langium) and is the fixed part of
274
+ the language. It knows `flow`, `step`, `group`, `fragment`, `fn`, `deco`, `type`, `config`, `matrix`,
275
+ control flow, lifecycle hooks, `expect`, and one rule that absorbs every protocol present and future:
276
+
277
+ ```
278
+ ActionCall:
279
+ target=QualifiedName (called?='(' (call=ArgList)? ')')?
280
+ (args+=ActionArg)* (opts=MapLit)?;
281
+ ```
282
+
283
+ That is why `http.get "url" { headers }` needs no grammar change to exist. Whether a dotted target is
284
+ a plugin verb or a method on something in scope is decided by what the name resolves to, not by how
285
+ the call is written.
286
+
287
+ Statements are terminated by a newline or `;`. The lexer drops those inside `( )` and `[ ]`, so an
288
+ argument list or a list literal may still span lines, while blocks and maps keep their newlines.
289
+
290
+ Regenerate the parser after editing the grammar:
291
+
292
+ ```bash
293
+ pnpm --filter @venn-lang/core langium:generate
294
+ pnpm --filter @venn-lang/core test
295
+ ```
296
+
297
+ ## See also
298
+
299
+ - [`@venn-lang/runtime`](../runtime) walks the checked document and executes it.
300
+ - [`@venn-lang/types`](../types) is the type vocabulary as plain data, shared with plugins.
301
+ - [`@venn-lang/lsp`](../lsp) reuses the parser, checker and formatter to serve editors.
302
+ - [`docs/venn-language.md`](../../docs/venn-language.md) is the language specification;
303
+ [`docs/type-system.md`](../../docs/type-system.md) covers the types.