@venn-lang/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +284 -0
- package/dist/bin/venn.mjs +16 -0
- package/dist/cli.mjs +56378 -0
- package/dist/index.d.mts +98 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +916 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -0
- package/src/bin/venn.ts +14 -0
- package/src/cli.ts +299 -0
- package/src/commands/build.ts +97 -0
- package/src/commands/check.ts +143 -0
- package/src/commands/deps.ts +228 -0
- package/src/commands/fmt.ts +55 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/inside-workspace.ts +20 -0
- package/src/commands/list.ts +53 -0
- package/src/commands/new.ts +66 -0
- package/src/commands/run.ts +149 -0
- package/src/commands/script.ts +127 -0
- package/src/commands/upgrade.ts +86 -0
- package/src/commands/verify-plugin.ts +35 -0
- package/src/index.ts +7 -0
- package/src/manifest/index.ts +2 -0
- package/src/manifest/load-env.ts +70 -0
- package/src/manifest/load-manifest.ts +42 -0
- package/src/project/command-targets.ts +53 -0
- package/src/project/index.ts +9 -0
- package/src/project/resolve-targets.ts +70 -0
- package/src/project/select-packages.ts +37 -0
- package/src/project/targets-or-exit.ts +28 -0
- package/src/reporters/colors.ts +17 -0
- package/src/reporters/dot-sink.ts +18 -0
- package/src/reporters/error-line.ts +14 -0
- package/src/reporters/index.ts +9 -0
- package/src/reporters/junit-sink.ts +39 -0
- package/src/reporters/ndjson-stdout.ts +13 -0
- package/src/reporters/pick-reporter.ts +28 -0
- package/src/reporters/pretty/diff-lines.ts +53 -0
- package/src/reporters/pretty/index.ts +1 -0
- package/src/reporters/pretty/pretty-reporter.ts +122 -0
- package/src/reporters/pretty/pretty.types.ts +29 -0
- package/src/reporters/pretty/render.ts +88 -0
- package/src/reporters/problem-reporter.ts +13 -0
- package/src/reporters/problem-sink.ts +20 -0
- package/src/reporters/reporter.types.ts +21 -0
- package/src/run/collect-files.ts +43 -0
- package/src/run/ending.types.ts +17 -0
- package/src/run/exit-code.ts +15 -0
- package/src/run/node-io.ts +27 -0
- package/src/run/npm-loader.ts +41 -0
- package/src/run/package-types.ts +66 -0
- package/src/run/run-file.ts +106 -0
- package/src/run/should-leave.ts +11 -0
- package/src/run/step-titles.ts +61 -0
- package/src/shutdown/create-leave.ts +42 -0
- package/src/shutdown/create-shutdown.ts +34 -0
- package/src/shutdown/index.ts +7 -0
- package/src/shutdown/install-exit-hook.ts +21 -0
- package/src/shutdown/install-fault-hooks.ts +30 -0
- package/src/shutdown/install-hooks.ts +36 -0
- package/src/shutdown/install-signal-hooks.ts +26 -0
- package/src/shutdown/shutdown.types.ts +20 -0
- package/src/title/index.ts +2 -0
- package/src/title/program-title.ts +15 -0
- package/src/title/set-program-title.ts +15 -0
- package/src/upgrade/index.ts +6 -0
- package/src/upgrade/install-site.ts +70 -0
- package/src/upgrade/latest-version.ts +50 -0
- package/src/upgrade/upgrade-command.ts +32 -0
- package/src/upgrade/upgrade-plan.ts +36 -0
- package/src/upgrade/upgrade.types.ts +17 -0
- package/src/upgrade/version.ts +21 -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,284 @@
|
|
|
1
|
+
# @venn-lang/cli
|
|
2
|
+
|
|
3
|
+
> The `venn` binary: one command for starting, checking, running and testing a Venn project.
|
|
4
|
+
|
|
5
|
+
This is the only package that touches `node:*`. It builds the `Host`, binds the real
|
|
6
|
+
implementations behind every port (filesystem, HTTP client, HTTP server, console, spawn) and hands
|
|
7
|
+
them to the runtime. Everything below it (`@venn-lang/core`, `@venn-lang/runtime`, `@venn-lang/sdk`) stays
|
|
8
|
+
platform-neutral, which is why the same compiler runs in a Web Worker for the editor.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
# tests/hello.vn
|
|
14
|
+
module demo.hello
|
|
15
|
+
|
|
16
|
+
use "venn/http"
|
|
17
|
+
use "venn/assert"
|
|
18
|
+
|
|
19
|
+
flow "Hello" {
|
|
20
|
+
step "Ping" {
|
|
21
|
+
let res = http.get "https://example.com"
|
|
22
|
+
expect res.status == 200
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
venn test tests/hello.vn
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The repository is not published, so from a source checkout the binary is:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pnpm --filter @venn-lang/cli build
|
|
35
|
+
node packages/cli/dist/bin/venn.mjs test examples/
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
| Command | What it does |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `venn new <name>` | Start a project in a new directory |
|
|
43
|
+
| `venn init` | Start a project in the directory you are in |
|
|
44
|
+
| `venn add <pkg…>` | Add a dependency to `venn.toml` and install it |
|
|
45
|
+
| `venn remove <pkg…>` | Remove a dependency and install without it |
|
|
46
|
+
| `venn update` | Update what is installed, within the ranges asked for |
|
|
47
|
+
| `venn install` | Install what the manifest asks for |
|
|
48
|
+
| `venn build` | Check every target and record the build under `target/` |
|
|
49
|
+
| `venn run [target]` | Run a file as a program: its statements, top to bottom |
|
|
50
|
+
| `venn test [target]` | Run every flow in a file or folder as a test suite |
|
|
51
|
+
| `venn list [target]` | Print the flows and steps that would run |
|
|
52
|
+
| `venn fmt [target]` | Format `.vn` files in place |
|
|
53
|
+
| `venn check [target]` | Statically check without running |
|
|
54
|
+
| `venn verify-plugin <path>` | Inspect a plugin module and check its shape |
|
|
55
|
+
| `venn upgrade` | Move a global install to the latest published version |
|
|
56
|
+
|
|
57
|
+
### Starting a project
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
venn new my-suite # a program: venn.toml, src/main.vn, .gitignore
|
|
61
|
+
venn new my-lib --lib # a library: venn.toml, src/lib.vn
|
|
62
|
+
venn new monorepo --workspace # a root that owns members
|
|
63
|
+
venn init --name api # the same, in the current directory
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Flag | Effect |
|
|
67
|
+
| --- | --- |
|
|
68
|
+
| `--bin` | A program with a `main`. This is the default. |
|
|
69
|
+
| `--lib` | A library: other packages use what it marks `pub`. |
|
|
70
|
+
| `--workspace` | A root that owns members, one lock and one `target/`. |
|
|
71
|
+
| `--dry-run` | Print what would be written and write nothing. |
|
|
72
|
+
| `--name <name>` | `init` only. Defaults to the directory's own name. |
|
|
73
|
+
|
|
74
|
+
A `.gitignore` is written only when the new package is not already inside a workspace, because one
|
|
75
|
+
`target/` per workspace means one line ignoring it, at the root that owns it. An existing
|
|
76
|
+
`venn.toml` is never overwritten: the command stops with `VN2102`.
|
|
77
|
+
|
|
78
|
+
### Dependencies
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
venn add zod # newest, pinned back into venn.toml as ^x.y.z
|
|
82
|
+
venn add hono@^4 -D # a development dependency
|
|
83
|
+
venn remove zod
|
|
84
|
+
venn install --frozen # refuse anything the lock did not record
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
| Flag | Effect |
|
|
88
|
+
| --- | --- |
|
|
89
|
+
| `-p, --package <name>` | Act on one workspace member. |
|
|
90
|
+
| `-D, --dev` | `add` and `remove`: the `[dev-dependencies]` table. |
|
|
91
|
+
| `--frozen` | `install`: check what is installed against `venn.lock` and fail on drift. |
|
|
92
|
+
|
|
93
|
+
`venn.toml` is edited first, then the package manager runs against a `package.json` generated into
|
|
94
|
+
`target/`. That ordering is what makes the manifest the source rather than a copy. Which manager
|
|
95
|
+
runs is `[tooling] manager` in the root manifest: `pnpm` (the default), `npm`, `bun` or `yarn`.
|
|
96
|
+
|
|
97
|
+
After a successful install the lockfile is written and the types each package publishes are derived
|
|
98
|
+
into `target/types/`. Each one reports a measured coverage line, `<name>: <pct>% of <n> exports
|
|
99
|
+
typed`, rather than a claim. Those files are what `venn check` reads to type an
|
|
100
|
+
`import { z } from "zod"`.
|
|
101
|
+
|
|
102
|
+
Names are validated before anything runs (`VN2105`). Under `--frozen` the lock is the input, not the
|
|
103
|
+
output: a missing lock is `VN2106` and any drift is `VN2107`, listed file by file.
|
|
104
|
+
|
|
105
|
+
### Running
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
venn run src/main.vn arg1 arg2
|
|
109
|
+
venn run --bin worker --env staging
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`run` executes the file as a program. `test` runs its flows as a suite. Arguments after the file
|
|
113
|
+
reach the program as `io.args`.
|
|
114
|
+
|
|
115
|
+
A program that reached its last line is not asked to stop: a server that bound a port keeps
|
|
116
|
+
serving, and the event loop decides when the process ends. A program that said `exit N`, or that
|
|
117
|
+
ended badly, leaves at once, and whatever it opened is closed on the way out. The same closing runs
|
|
118
|
+
for a signal, an uncaught fault and an ordinary exit.
|
|
119
|
+
|
|
120
|
+
| Flag | Command | Effect |
|
|
121
|
+
| --- | --- | --- |
|
|
122
|
+
| `--bin <name>` | `run` | Which program, when the package has several. |
|
|
123
|
+
| `--env <name>` | `run`, `test` | The environment from `venn.toml`. Default `local`. |
|
|
124
|
+
| `-p, --package <name>` | all | Act on one workspace member. |
|
|
125
|
+
| `--reporter <name>` | `test` | `pretty`, `ndjson`, `dot` or `junit`. |
|
|
126
|
+
| `--flow <text>` | `test`, `list` | Only flows whose title contains this. |
|
|
127
|
+
| `--step <text>` | `test`, `list` | Only steps whose title contains this. |
|
|
128
|
+
| `--tags <a,b>` | `test` | Comma-separated `@tag` filter. |
|
|
129
|
+
| `--bail` | `test` | Stop after the first failing flow. |
|
|
130
|
+
|
|
131
|
+
### Checking
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
venn check . # resolve actions, matchers, imports and types
|
|
135
|
+
venn fmt src/ # format in place
|
|
136
|
+
venn fmt --check . # report what would change and fail, for CI
|
|
137
|
+
venn build --release
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`check` parses each file, resolves its imports, builds the plugin registry from the whole stdlib and
|
|
141
|
+
runs the type checker. Problems from every file are gathered and reported once. `fmt --check` exits
|
|
142
|
+
1 when anything would change.
|
|
143
|
+
|
|
144
|
+
`build` checks every target of the selected packages and writes `target/debug/build.json`, or
|
|
145
|
+
`target/release/build.json` with `--release`. There is no code generation yet and the record claims
|
|
146
|
+
none: it lists the targets covered, the files read and the number of problems. A build with problems
|
|
147
|
+
always fails; what the profile decides is whether the record is written anyway. `[profile.dev]` is
|
|
148
|
+
lenient by default and `[profile.release]` is strict, and either can set `strict` explicitly.
|
|
149
|
+
|
|
150
|
+
### verify-plugin
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
venn verify-plugin ./dist/index.mjs
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Imports the module, takes its default export (or the first export that looks like a plugin) and
|
|
157
|
+
prints the name, the namespace and how many actions, matchers and resources it declares. Exits 1
|
|
158
|
+
when the shape is wrong.
|
|
159
|
+
|
|
160
|
+
### upgrade
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
venn upgrade # ask first, then run the install
|
|
164
|
+
venn upgrade --dry-run # print the command that would run, change nothing
|
|
165
|
+
venn upgrade --yes # skip the question, for a script
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Finds which manager installed this copy by reading the path it is running from, then runs that
|
|
169
|
+
manager's own global install. It does not rewrite its own files: the CLI cannot replace itself while
|
|
170
|
+
it is executing, and on Windows the running executable is locked.
|
|
171
|
+
|
|
172
|
+
The path is the signal because `npm_config_user_agent`, the obvious alternative, is only set while a
|
|
173
|
+
package script runs and is empty when the binary is invoked directly, which is every real use.
|
|
174
|
+
|
|
175
|
+
Two cases are refused rather than guessed at. A copy the project owns is left alone, since its
|
|
176
|
+
version is pinned in the manifest and the next install would undo the upgrade; the message says to
|
|
177
|
+
update it there instead. A path that matches no manager is refused outright, with the install command
|
|
178
|
+
to run by hand. Both exit 1.
|
|
179
|
+
|
|
180
|
+
A prerelease is never offered to someone on a stable version, since opting into one is a decision
|
|
181
|
+
rather than an update.
|
|
182
|
+
|
|
183
|
+
## Targets: what a bare command means
|
|
184
|
+
|
|
185
|
+
A path given outright always wins and is never second-guessed. With no path, the nearest `venn.toml`
|
|
186
|
+
answers, walking up from the current directory; a workspace answers with its `default-members`, or
|
|
187
|
+
with all of them when it named none.
|
|
188
|
+
|
|
189
|
+
| Command | With no path |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `venn test`, `venn list` | The `tests/` directory of each selected package |
|
|
192
|
+
| `venn run` | The package's single `bin` target, conventionally `src/main.vn` |
|
|
193
|
+
| `venn check`, `venn fmt` | Every `.vn` file the selected packages own |
|
|
194
|
+
|
|
195
|
+
Directories are walked recursively, sorted so runs are reproducible, skipping `node_modules`, `dist`
|
|
196
|
+
and `.git`. Nothing silently succeeds: no project is `VN2101`, an unknown `-p` name is `VN2103`, and
|
|
197
|
+
several `bin` targets with no `--bin` prints the names to pick from.
|
|
198
|
+
|
|
199
|
+
## Reporters
|
|
200
|
+
|
|
201
|
+
| Name | Output |
|
|
202
|
+
| --- | --- |
|
|
203
|
+
| `pretty` | A live tree: a banner per file, a branch per flow, a verdict per step, then every failure repeated at the end with its `VNxxxx` code and source location. |
|
|
204
|
+
| `ndjson` | One event envelope per line on stdout. |
|
|
205
|
+
| `dot` | One character per assertion, then a summary line. |
|
|
206
|
+
| `junit` | A JUnit XML document, emitted on `run.finished`. |
|
|
207
|
+
|
|
208
|
+
With no `--reporter`, a terminal gets `pretty` and anything piped gets `ndjson`, so scripts and CI
|
|
209
|
+
keep a stream they can parse.
|
|
210
|
+
|
|
211
|
+
## Environments
|
|
212
|
+
|
|
213
|
+
`--env <name>` selects an environment, `local` unless told otherwise. Variables come from three
|
|
214
|
+
places, lowest precedence first:
|
|
215
|
+
|
|
216
|
+
1. `[env.<name>]` in `venn.toml`, the documented default, committed.
|
|
217
|
+
2. The dotenv files, in order: `.env`, `.env.<name>`, `.env.local`, `.env.<name>.local`, or whatever
|
|
218
|
+
`[env] files` lists instead.
|
|
219
|
+
3. The environment the process was started with.
|
|
220
|
+
|
|
221
|
+
The real environment wins, because that is how CI passes a token in. It overrides rather than adds:
|
|
222
|
+
a name has to be declared in one of the first two places for the third to fill it, which keeps
|
|
223
|
+
`PATH` and `TEMP` out of the editor's completion. A value that exists only in CI is read through
|
|
224
|
+
`secrets.*`, which needs no declaration and redacts what it returns.
|
|
225
|
+
|
|
226
|
+
The manifest that governs a file is found by walking up to the project the file belongs to, not by
|
|
227
|
+
reading whatever sits in the same folder, so `venn test packages/api/tests/login.vn` sees the same
|
|
228
|
+
environments and `[paths]` aliases as running it from inside `packages/api`.
|
|
229
|
+
|
|
230
|
+
## API
|
|
231
|
+
|
|
232
|
+
The package also exports the seam the commands are built on, for embedding a run in another program.
|
|
233
|
+
|
|
234
|
+
| Export | What it is |
|
|
235
|
+
| --- | --- |
|
|
236
|
+
| `runFile(args)` | Parse and run one `.vn` source with the full stdlib loaded. Returns `{ problems, result }`. |
|
|
237
|
+
| `RunFileOutcome` | The result type: `Problem[]` plus the runtime's `RunResult`. |
|
|
238
|
+
| `runCommand(options)` | What `venn test` does for one file: collect, run, report, return an exit code. |
|
|
239
|
+
| `verifyPluginCommand({ path })` | What `venn verify-plugin` does. |
|
|
240
|
+
| `createStdoutSink()` | An NDJSON `EventSink` that writes each envelope to stdout. |
|
|
241
|
+
| `reportProblems(problems)` | Print `VNxxxx` problems to stderr with their source location. |
|
|
242
|
+
|
|
243
|
+
`runFile` takes the ports it should use, so a test can drive it entirely offline:
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
import { createTestHost } from "@venn-lang/contracts";
|
|
247
|
+
import { createFakeClient, okResponse } from "@venn-lang/http";
|
|
248
|
+
import { createMemorySink } from "@venn-lang/runtime";
|
|
249
|
+
import { runFile } from "@venn-lang/cli";
|
|
250
|
+
|
|
251
|
+
const source = `module demo.hello
|
|
252
|
+
use "venn/http"
|
|
253
|
+
use "venn/assert"
|
|
254
|
+
|
|
255
|
+
flow "Hello" {
|
|
256
|
+
step "Ping" {
|
|
257
|
+
const res = http.get "https://example.com"
|
|
258
|
+
expect res.status == 200
|
|
259
|
+
}
|
|
260
|
+
}`;
|
|
261
|
+
|
|
262
|
+
const outcome = await runFile({
|
|
263
|
+
source,
|
|
264
|
+
uri: "memory://hello.vn",
|
|
265
|
+
host: createTestHost(),
|
|
266
|
+
sink: createMemorySink(),
|
|
267
|
+
httpClient: createFakeClient({
|
|
268
|
+
responses: { "https://example.com": okResponse({ status: 200 }) },
|
|
269
|
+
}),
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
outcome.problems; // []
|
|
273
|
+
outcome.result?.passed; // 1
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
`mode: "script"` runs the file top to bottom instead of running its flows. `filter`, `bail`, `env`,
|
|
277
|
+
`io`, `npm` and `cleanup` are the rest of what the commands pass in.
|
|
278
|
+
|
|
279
|
+
## See also
|
|
280
|
+
|
|
281
|
+
- [`@venn-lang/runtime`](../runtime) for the scheduler, the plugin registry and the event stream.
|
|
282
|
+
- [`@venn-lang/project`](../project) for manifests, workspaces, lockfiles and build profiles.
|
|
283
|
+
- [`@venn-lang/contracts`](../contracts) for the ports and the Node implementations bound here.
|
|
284
|
+
- [`@venn-lang/lsp`](../lsp) for the same compiler behind an editor.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { enableCompileCache } from "node:module";
|
|
3
|
+
//#region src/bin/venn.ts
|
|
4
|
+
/**
|
|
5
|
+
* The launcher, deliberately tiny.
|
|
6
|
+
*
|
|
7
|
+
* `enableCompileCache` keeps V8's compilation of the bundle on disk between
|
|
8
|
+
* runs, but only for modules loaded after it is called, so the engine has to
|
|
9
|
+
* arrive through a dynamic import rather than a static one. The URL is computed
|
|
10
|
+
* so the bundler leaves it alone: this file must stay separate from the file it
|
|
11
|
+
* loads, or there is nothing left to cache.
|
|
12
|
+
*/
|
|
13
|
+
enableCompileCache?.();
|
|
14
|
+
await import(new URL("../cli.mjs", import.meta.url).href);
|
|
15
|
+
//#endregion
|
|
16
|
+
export {};
|