@venn-lang/io 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 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,105 @@
1
+ # @venn-lang/io
2
+
3
+ > The `io` namespace: standard output, standard error, standard input and the process arguments.
4
+
5
+ A Venn file is a program as often as it is a suite, and a program talks to the outside world through
6
+ its console. This plugin names the parts of it that need naming, on top of the `Console` port. Plain
7
+ `print` is in the prelude and needs no import; everything else is here.
8
+
9
+ ## Install
10
+
11
+ `@venn-lang/io` is part of the stdlib the `venn` CLI and the language server load, so there is nothing to
12
+ install. A file that uses it says so:
13
+
14
+ ```ruby
15
+ use "venn/io"
16
+ ```
17
+
18
+ The plugin declares `requires: ["io"]`. A host that offers no console (the Web Worker host behind
19
+ the editor offers `fs`, `clock`, `random`, `secrets` and `log`) refuses it at load time with
20
+ `VN2010`, rather than failing with a `TypeError` halfway through a run.
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ # greet.vn, run with `venn run greet.vn Ada`
26
+ use "venn/io"
27
+
28
+ const argv = io.args
29
+ io.print "arguments: ${argv}"
30
+
31
+ io.write "name? "
32
+ const name = io.readLine
33
+ if name == null {
34
+ io.eprint "no name on standard input"
35
+ exit 1
36
+ }
37
+
38
+ io.print "hello, ${name}"
39
+ ```
40
+
41
+ ## Verbs
42
+
43
+ | Verb | Signature | What it does |
44
+ | --- | --- | --- |
45
+ | `io.print` | `(...dynamic) -> void` | Writes to standard output with a newline. The same as the prelude's `print`. |
46
+ | `io.write` | `(dynamic) -> void` | Writes to standard output with nothing added after it. |
47
+ | `io.eprint` | `(dynamic) -> void` | Writes to standard error, followed by a newline. |
48
+ | `io.readLine` | `() -> string \| null` | The next line of standard input, or `null` at end of input. |
49
+ | `io.args` | `() -> list<string>` | The command-line arguments passed to the script. |
50
+
51
+ `io.print` takes as many values as you like and joins them with a space. Strings are written as
52
+ they are; anything else is rendered as JSON, so a map never prints as `[object Object]`.
53
+
54
+ `readLine` returns a union with `null` in its signature, not a bare `string`: end of input is a real
55
+ answer and a caller has to face it. `args` returns a `list<string>` rather than a bare list, so
56
+ `argv.len` and the rest of the list methods type correctly.
57
+
58
+ Arguments reach `io.args` from the command line: `venn run greet.vn Ada` gives `["Ada"]`.
59
+
60
+ ## The Console port
61
+
62
+ The verbs above are a thin layer over one port, which lives in
63
+ [`@venn-lang/contracts`](../contracts) because a console is a host capability like the filesystem or the
64
+ clock, not something this plugin owns.
65
+
66
+ | | |
67
+ | --- | --- |
68
+ | Id | `venn.port.console` |
69
+ | Version | `1` |
70
+ | Requires | `io` |
71
+ | Methods | `write`, `writeError`, `readLine`, `args` |
72
+
73
+ Two implementations ship with it, and both pass the same conformance suite: `createNodeConsole` from
74
+ `@venn-lang/contracts/node`, which writes to Node's real streams and opens stdin lazily on the first
75
+ `readLine`, and `createMemoryConsole`, which records instead of printing and reads from a scripted
76
+ input. `venn run` binds the real one, with the command line's arguments; a test binds the recorder
77
+ and reads back exactly what the program wrote:
78
+
79
+ ```ts
80
+ import { createMemoryConsole } from "@venn-lang/io";
81
+
82
+ const console = createMemoryConsole({ input: ["Ada"], argv: ["--name", "ada"] });
83
+
84
+ console.write(">");
85
+ console.out; // ">"
86
+ await console.readLine(); // "Ada"
87
+ await console.readLine(); // null
88
+ console.args(); // ["--name", "ada"]
89
+ ```
90
+
91
+ ## API
92
+
93
+ | Export | What it is |
94
+ | --- | --- |
95
+ | `ioPlugin` (also the default export) | The `PluginDefinition`: namespace `io`, requires the `io` capability, five actions, no types. |
96
+ | `consoleActions` | The five `ActionDefinition`s, in the order listed above. |
97
+ | `ConsolePort` | The port descriptor, re-exported from `@venn-lang/contracts`. |
98
+ | `Console` | The port's interface (type only). |
99
+ | `createMemoryConsole` | The recording implementation, for tests. |
100
+
101
+ ## See also
102
+
103
+ - [`@venn-lang/contracts`](../contracts) for the port, its Node implementation and the host capabilities.
104
+ - [`@venn-lang/fmt`](../std-fmt) for turning a value into the text you print.
105
+ - [`@venn-lang/cli`](../cli) for `venn run`, which binds the real console and passes the arguments in.
@@ -0,0 +1,25 @@
1
+ import { ActionDefinition, PluginDefinition } from "@venn-lang/sdk";
2
+ import { Console, ConsolePort, createMemoryConsole } from "@venn-lang/contracts";
3
+ //#region src/actions/console-actions.d.ts
4
+ /**
5
+ * The verbs of the `io` namespace: standard output, standard error, standard
6
+ * input and the process arguments.
7
+ *
8
+ * Plain `print` is in the prelude and needs no import. `io.print` is the same
9
+ * verb under its full name, so a script that imports the namespace reads the
10
+ * same either way.
11
+ */
12
+ declare const consoleActions: ActionDefinition[];
13
+ //#endregion
14
+ //#region src/plugin.d.ts
15
+ /**
16
+ * The `io` plugin: a script's standard input, standard output and arguments.
17
+ *
18
+ * Requires the host capability `io`. Where the host has no real console streams
19
+ * the plugin refuses to load with a readable diagnostic, rather than failing
20
+ * mid-run.
21
+ */
22
+ declare const ioPlugin: PluginDefinition;
23
+ //#endregion
24
+ export { type Console, ConsolePort, consoleActions, createMemoryConsole, ioPlugin as default, ioPlugin };
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/actions/console-actions.ts","../src/plugin.ts"],"mappings":";;;;;;;;;;;cA2Ba,gBAAgB;;;;;;;;;;cCjBhB,UAAU"}
package/dist/index.js ADDED
@@ -0,0 +1,80 @@
1
+ import { arg, defineAction, definePlugin, restArg } from "@venn-lang/sdk";
2
+ import { t } from "@venn-lang/types";
3
+ import { ConsolePort, createMemoryConsole } from "@venn-lang/contracts";
4
+ //#region src/actions/console-actions.ts
5
+ /** How a printed value becomes text: strings as-is, everything else as JSON. */
6
+ function display(value) {
7
+ if (typeof value === "string") return value;
8
+ if (value === null || typeof value !== "object") return String(value);
9
+ try {
10
+ return JSON.stringify(value);
11
+ } catch {
12
+ return String(value);
13
+ }
14
+ }
15
+ function line(args) {
16
+ return args.map(display).join(" ");
17
+ }
18
+ /**
19
+ * The verbs of the `io` namespace: standard output, standard error, standard
20
+ * input and the process arguments.
21
+ *
22
+ * Plain `print` is in the prelude and needs no import. `io.print` is the same
23
+ * verb under its full name, so a script that imports the namespace reads the
24
+ * same either way.
25
+ */
26
+ const consoleActions = [
27
+ defineAction({
28
+ name: "print",
29
+ doc: "Write to standard output with a newline. Same as the prelude's `print`.",
30
+ args: [restArg("values", t.dynamic, "Anything, as many as you like.")],
31
+ result: t.void,
32
+ run: (ctx, input) => ctx.port(ConsolePort).write(`${line(input.args)}\n`)
33
+ }),
34
+ defineAction({
35
+ name: "write",
36
+ doc: "Write to standard output with no trailing newline.",
37
+ args: [arg("value", t.dynamic, "What to write. Nothing is added after it.")],
38
+ result: t.void,
39
+ run: (ctx, input) => ctx.port(ConsolePort).write(line(input.args))
40
+ }),
41
+ defineAction({
42
+ name: "eprint",
43
+ doc: "Write to standard error, followed by a newline.",
44
+ args: [arg("value", t.dynamic, "What to write to stderr.")],
45
+ result: t.void,
46
+ run: (ctx, input) => ctx.port(ConsolePort).writeError(`${line(input.args)}\n`)
47
+ }),
48
+ defineAction({
49
+ name: "readLine",
50
+ doc: "Read the next line from standard input, or null at end of input.",
51
+ result: t.union(t.string, t.null),
52
+ run: (ctx) => ctx.port(ConsolePort).readLine()
53
+ }),
54
+ defineAction({
55
+ name: "args",
56
+ doc: "The command-line arguments passed to the script.",
57
+ result: t.list(t.string),
58
+ run: (ctx) => [...ctx.port(ConsolePort).args()]
59
+ })
60
+ ];
61
+ //#endregion
62
+ //#region src/plugin.ts
63
+ /**
64
+ * The `io` plugin: a script's standard input, standard output and arguments.
65
+ *
66
+ * Requires the host capability `io`. Where the host has no real console streams
67
+ * the plugin refuses to load with a readable diagnostic, rather than failing
68
+ * mid-run.
69
+ */
70
+ const ioPlugin = definePlugin({
71
+ name: "venn/io",
72
+ version: "0.0.0",
73
+ namespace: "io",
74
+ requires: ["io"],
75
+ actions: consoleActions
76
+ });
77
+ //#endregion
78
+ export { ConsolePort, consoleActions, createMemoryConsole, ioPlugin as default, ioPlugin };
79
+
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/actions/console-actions.ts","../src/plugin.ts"],"sourcesContent":["import { type ActionDefinition, arg, defineAction, restArg } from \"@venn-lang/sdk\";\nimport { t } from \"@venn-lang/types\";\nimport { ConsolePort } from \"../port/index.js\";\n\n/** How a printed value becomes text: strings as-is, everything else as JSON. */\nfunction display(value: unknown): string {\n if (typeof value === \"string\") return value;\n if (value === null || typeof value !== \"object\") return String(value);\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n\nfunction line(args: readonly unknown[]): string {\n return args.map(display).join(\" \");\n}\n\n/**\n * The verbs of the `io` namespace: standard output, standard error, standard\n * input and the process arguments.\n *\n * Plain `print` is in the prelude and needs no import. `io.print` is the same\n * verb under its full name, so a script that imports the namespace reads the\n * same either way.\n */\nexport const consoleActions: ActionDefinition[] = [\n defineAction({\n name: \"print\",\n doc: \"Write to standard output with a newline. Same as the prelude's `print`.\",\n args: [restArg(\"values\", t.dynamic, \"Anything, as many as you like.\")],\n result: t.void,\n run: (ctx, input) => ctx.port(ConsolePort).write(`${line(input.args)}\\n`),\n }),\n defineAction({\n name: \"write\",\n doc: \"Write to standard output with no trailing newline.\",\n args: [arg(\"value\", t.dynamic, \"What to write. Nothing is added after it.\")],\n result: t.void,\n run: (ctx, input) => ctx.port(ConsolePort).write(line(input.args)),\n }),\n defineAction({\n name: \"eprint\",\n doc: \"Write to standard error, followed by a newline.\",\n args: [arg(\"value\", t.dynamic, \"What to write to stderr.\")],\n result: t.void,\n run: (ctx, input) => ctx.port(ConsolePort).writeError(`${line(input.args)}\\n`),\n }),\n defineAction({\n name: \"readLine\",\n doc: \"Read the next line from standard input, or null at end of input.\",\n result: t.union(t.string, t.null),\n run: (ctx) => ctx.port(ConsolePort).readLine(),\n }),\n defineAction({\n name: \"args\",\n doc: \"The command-line arguments passed to the script.\",\n result: t.list(t.string),\n run: (ctx) => [...ctx.port(ConsolePort).args()],\n }),\n];\n","import { definePlugin, type PluginDefinition } from \"@venn-lang/sdk\";\nimport { consoleActions } from \"./actions/console-actions.js\";\n\n/**\n * The `io` plugin: a script's standard input, standard output and arguments.\n *\n * Requires the host capability `io`. Where the host has no real console streams\n * the plugin refuses to load with a readable diagnostic, rather than failing\n * mid-run.\n */\nexport const ioPlugin: PluginDefinition = definePlugin({\n name: \"venn/io\",\n version: \"0.0.0\",\n namespace: \"io\",\n requires: [\"io\"],\n actions: consoleActions,\n});\n"],"mappings":";;;;;AAKA,SAAS,QAAQ,OAAwB;CACvC,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO,OAAO,KAAK;CACpE,IAAI;EACF,OAAO,KAAK,UAAU,KAAK;CAC7B,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF;AAEA,SAAS,KAAK,MAAkC;CAC9C,OAAO,KAAK,IAAI,OAAO,CAAC,CAAC,KAAK,GAAG;AACnC;;;;;;;;;AAUA,MAAa,iBAAqC;CAChD,aAAa;EACX,MAAM;EACN,KAAK;EACL,MAAM,CAAC,QAAQ,UAAU,EAAE,SAAS,gCAAgC,CAAC;EACrE,QAAQ,EAAE;EACV,MAAM,KAAK,UAAU,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM,GAAG,KAAK,MAAM,IAAI,EAAE,GAAG;CAC1E,CAAC;CACD,aAAa;EACX,MAAM;EACN,KAAK;EACL,MAAM,CAAC,IAAI,SAAS,EAAE,SAAS,2CAA2C,CAAC;EAC3E,QAAQ,EAAE;EACV,MAAM,KAAK,UAAU,IAAI,KAAK,WAAW,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC;CACnE,CAAC;CACD,aAAa;EACX,MAAM;EACN,KAAK;EACL,MAAM,CAAC,IAAI,SAAS,EAAE,SAAS,0BAA0B,CAAC;EAC1D,QAAQ,EAAE;EACV,MAAM,KAAK,UAAU,IAAI,KAAK,WAAW,CAAC,CAAC,WAAW,GAAG,KAAK,MAAM,IAAI,EAAE,GAAG;CAC/E,CAAC;CACD,aAAa;EACX,MAAM;EACN,KAAK;EACL,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI;EAChC,MAAM,QAAQ,IAAI,KAAK,WAAW,CAAC,CAAC,SAAS;CAC/C,CAAC;CACD,aAAa;EACX,MAAM;EACN,KAAK;EACL,QAAQ,EAAE,KAAK,EAAE,MAAM;EACvB,MAAM,QAAQ,CAAC,GAAG,IAAI,KAAK,WAAW,CAAC,CAAC,KAAK,CAAC;CAChD,CAAC;AACH;;;;;;;;;;ACnDA,MAAa,WAA6B,aAAa;CACrD,MAAM;CACN,SAAS;CACT,WAAW;CACX,UAAU,CAAC,IAAI;CACf,SAAS;AACX,CAAC"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@venn-lang/io",
3
+ "version": "0.1.0",
4
+ "description": "The io namespace: standard output, standard error, standard input and the process arguments.",
5
+ "keywords": [
6
+ "venn",
7
+ "testing",
8
+ "e2e",
9
+ "io"
10
+ ],
11
+ "homepage": "https://github.com/venn-lang/venn/tree/main/packages/std-io#readme",
12
+ "bugs": "https://github.com/venn-lang/venn/issues",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://github.com/venn-lang/venn.git",
16
+ "directory": "packages/std-io"
17
+ },
18
+ "license": "MIT",
19
+ "author": "Vinicius Borges",
20
+ "type": "module",
21
+ "sideEffects": false,
22
+ "exports": {
23
+ ".": {
24
+ "development": "./src/index.ts",
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js",
27
+ "default": "./dist/index.js"
28
+ }
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "src",
33
+ "!src/**/*.test.ts",
34
+ "!src/**/*.suite.ts"
35
+ ],
36
+ "publishConfig": {
37
+ "access": "public"
38
+ },
39
+ "dependencies": {
40
+ "@venn-lang/contracts": "0.1.0",
41
+ "@venn-lang/sdk": "0.1.0",
42
+ "@venn-lang/types": "0.1.0"
43
+ },
44
+ "devDependencies": {
45
+ "@fast-check/vitest": "^0.4.1",
46
+ "tsdown": "^0.22.14",
47
+ "typescript": "^7.0.2",
48
+ "vitest": "^4.1.10"
49
+ },
50
+ "scripts": {
51
+ "build": "tsdown",
52
+ "test": "vitest run",
53
+ "typecheck": "tsc --noEmit"
54
+ }
55
+ }
@@ -0,0 +1,62 @@
1
+ import { type ActionDefinition, arg, defineAction, restArg } from "@venn-lang/sdk";
2
+ import { t } from "@venn-lang/types";
3
+ import { ConsolePort } from "../port/index.js";
4
+
5
+ /** How a printed value becomes text: strings as-is, everything else as JSON. */
6
+ function display(value: unknown): string {
7
+ if (typeof value === "string") return value;
8
+ if (value === null || typeof value !== "object") return String(value);
9
+ try {
10
+ return JSON.stringify(value);
11
+ } catch {
12
+ return String(value);
13
+ }
14
+ }
15
+
16
+ function line(args: readonly unknown[]): string {
17
+ return args.map(display).join(" ");
18
+ }
19
+
20
+ /**
21
+ * The verbs of the `io` namespace: standard output, standard error, standard
22
+ * input and the process arguments.
23
+ *
24
+ * Plain `print` is in the prelude and needs no import. `io.print` is the same
25
+ * verb under its full name, so a script that imports the namespace reads the
26
+ * same either way.
27
+ */
28
+ export const consoleActions: ActionDefinition[] = [
29
+ defineAction({
30
+ name: "print",
31
+ doc: "Write to standard output with a newline. Same as the prelude's `print`.",
32
+ args: [restArg("values", t.dynamic, "Anything, as many as you like.")],
33
+ result: t.void,
34
+ run: (ctx, input) => ctx.port(ConsolePort).write(`${line(input.args)}\n`),
35
+ }),
36
+ defineAction({
37
+ name: "write",
38
+ doc: "Write to standard output with no trailing newline.",
39
+ args: [arg("value", t.dynamic, "What to write. Nothing is added after it.")],
40
+ result: t.void,
41
+ run: (ctx, input) => ctx.port(ConsolePort).write(line(input.args)),
42
+ }),
43
+ defineAction({
44
+ name: "eprint",
45
+ doc: "Write to standard error, followed by a newline.",
46
+ args: [arg("value", t.dynamic, "What to write to stderr.")],
47
+ result: t.void,
48
+ run: (ctx, input) => ctx.port(ConsolePort).writeError(`${line(input.args)}\n`),
49
+ }),
50
+ defineAction({
51
+ name: "readLine",
52
+ doc: "Read the next line from standard input, or null at end of input.",
53
+ result: t.union(t.string, t.null),
54
+ run: (ctx) => ctx.port(ConsolePort).readLine(),
55
+ }),
56
+ defineAction({
57
+ name: "args",
58
+ doc: "The command-line arguments passed to the script.",
59
+ result: t.list(t.string),
60
+ run: (ctx) => [...ctx.port(ConsolePort).args()],
61
+ }),
62
+ ];
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ // The `io` namespace: standard input, standard error and process arguments.
2
+ // Plain `print` needs no import: it is in the prelude.
3
+ export { consoleActions } from "./actions/console-actions.js";
4
+ export { ioPlugin, ioPlugin as default } from "./plugin.js";
5
+ export { type Console, ConsolePort, createMemoryConsole } from "./port/index.js";
package/src/plugin.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { definePlugin, type PluginDefinition } from "@venn-lang/sdk";
2
+ import { consoleActions } from "./actions/console-actions.js";
3
+
4
+ /**
5
+ * The `io` plugin: a script's standard input, standard output and arguments.
6
+ *
7
+ * Requires the host capability `io`. Where the host has no real console streams
8
+ * the plugin refuses to load with a readable diagnostic, rather than failing
9
+ * mid-run.
10
+ */
11
+ export const ioPlugin: PluginDefinition = definePlugin({
12
+ name: "venn/io",
13
+ version: "0.0.0",
14
+ namespace: "io",
15
+ requires: ["io"],
16
+ actions: consoleActions,
17
+ });
@@ -0,0 +1,3 @@
1
+ // The Console port lives in @venn-lang/contracts: it is a host capability like the
2
+ // file system or the clock, not something this plugin owns.
3
+ export { type Console, ConsolePort, createMemoryConsole } from "@venn-lang/contracts";