@visualautomate/plugin-sdk 0.3.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 WvB Solutions
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,110 @@
1
+ # @visualautomate/plugin-sdk
2
+
3
+ The types a VisualAutomate plugin is written against, and a way to run one on
4
+ your own machine.
5
+
6
+ Most people do not need to install this. `@visualautomate/cli` carries it and
7
+ writes the types into your project, so a scaffolded plugin has no dependencies
8
+ at all. Install it directly if you want the simulator in your own test suite.
9
+
10
+ ```bash
11
+ npm install --save-dev @visualautomate/plugin-sdk
12
+ ```
13
+
14
+ ## A plugin
15
+
16
+ ```ts
17
+ import type { PluginModule } from "@visualautomate/plugin-sdk"
18
+
19
+ const plugin: PluginModule = {
20
+ async execute(input, config, context) {
21
+ const bytes = await context.storage.get(config.path as string)
22
+ if (!bytes) return { output: "error", data: { error: "No such file." } }
23
+
24
+ return {
25
+ output: "success",
26
+ data: { size: bytes.length },
27
+ }
28
+ },
29
+ }
30
+
31
+ export default plugin
32
+ ```
33
+
34
+ `output` names which port the run leaves by, and has to be one of the `outputs`
35
+ in your manifest. `data` is what the next steps read.
36
+
37
+ ## What `context` has
38
+
39
+ | | |
40
+ |---|---|
41
+ | `storage` | the account's files — `put`, `get`, `delete`, `list`, `head`, `signedUrl`, `download` |
42
+ | `state` | values that outlive one run, scoped to the workflow — `get`, `set`, `delete`, `list` |
43
+ | `media` | ffmpeg, by operation name. Absent when the server has no sandbox configured |
44
+ | `connections` | the accounts this step was configured with. Absent when it declares none |
45
+ | `workflowId`, `nodeId`, `userId`, `executionId`, `startedAt`, `secrets` | about the run |
46
+
47
+ You never see a credential and you never name a host. `connections.get("account")`
48
+ takes the name of one of *your* properties, and the platform attaches the token
49
+ on the way past — which is what stops a plugin pointing somebody's account
50
+ somewhere it chose.
51
+
52
+ ## `simulate`
53
+
54
+ ```ts
55
+ import { simulate } from "@visualautomate/plugin-sdk"
56
+ import plugin from "./plugin"
57
+
58
+ const result = await simulate(plugin, {
59
+ config: { path: "notes/a.txt" },
60
+ storage: { "notes/a.txt": "hello" },
61
+ state: { runs: 0 },
62
+ })
63
+
64
+ result.status // "success" | "error" | "timeout" | ...
65
+ result.output?.data
66
+ result.files // what storage holds now
67
+ result.state // what state holds now
68
+ ```
69
+
70
+ Storage and state are real, in memory, with the same edge cases the platform's
71
+ have — `get` on a missing key is `null`, `put` takes a string or bytes, `list`
72
+ is prefix-filtered.
73
+
74
+ `connections` and `media` are not simulated. Pass a stub if you want to test the
75
+ code around them:
76
+
77
+ ```ts
78
+ await simulate(plugin, {
79
+ connections: {
80
+ async get() {
81
+ return { id: "test", async request() { return { ok: true, status: 200, body: {} } } }
82
+ },
83
+ },
84
+ })
85
+ ```
86
+
87
+ Asserting on what your plugin *asked* the provider for is a better test than any
88
+ imitation of the provider would be.
89
+
90
+ ## The platform's code checks
91
+
92
+ The rules the platform applies to a plugin's code on upload, so a test can
93
+ refuse what the platform would refuse:
94
+
95
+ ```ts
96
+ import { validatePluginCode, ALLOWED_PACKAGES } from "@visualautomate/plugin-sdk"
97
+
98
+ const check = validatePluginCode(code)
99
+ if (!check.valid) throw new Error(check.error)
100
+ check.packages // the allowed packages this code requires
101
+ ```
102
+
103
+ `ALLOWED_PACKAGES` lists every npm package a plugin may `require`, with the
104
+ version the sandbox has installed. Nothing else is available: no `fs`, no
105
+ `http`, and no subpaths like `lodash/get`.
106
+
107
+ `sandboxRequire(load)` and `loadPluginCode(code, require)` load code the way
108
+ the sandbox does, as the body of a function given `module`, `exports` and a
109
+ `require` that only hands out allowed packages. `visualautomate test` uses
110
+ them; they are here for your own test runner.
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The runtime API, as data.
3
+ *
4
+ * The same things types.ts describes, in a form a tool can read without a
5
+ * TypeScript compiler: an editor's autocomplete, a documentation page, a lint
6
+ * rule. Each entry is a name an author types, what it is, and a one-line
7
+ * description.
8
+ *
9
+ * Plain data with no imports, so it can go into a browser bundle without the
10
+ * rest of the SDK.
11
+ */
12
+ export interface ContextApiEntry {
13
+ /** What an author types, e.g. `context.storage.put`. */
14
+ path: string;
15
+ /** `property` for a value, `method` for something you call. */
16
+ kind: "variable" | "property" | "method";
17
+ /** The signature, for a method. Shown beside the name. */
18
+ signature?: string;
19
+ /** One line, in the hover card. */
20
+ info: string;
21
+ /**
22
+ * Set when the helper is not always there.
23
+ *
24
+ * The editor says so, because the alternative is an author writing
25
+ * `context.media.run(...)` against a completion that promised it existed
26
+ * and meeting `undefined` on somebody's canvas.
27
+ */
28
+ conditional?: string;
29
+ }
30
+ /** Everything a plugin is called with, and everything it can reach. */
31
+ export declare const CONTEXT_API: ContextApiEntry[];
32
+ /**
33
+ * The globals a plugin does NOT get, and what to reach for instead.
34
+ *
35
+ * Worth offering in an editor as much as the API is: an author who types
36
+ * `require('fs')` should be told at the keystroke rather than at the push.
37
+ */
38
+ export declare const BLOCKED_GLOBALS: Array<{
39
+ name: string;
40
+ instead: string;
41
+ }>;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @visualautomate/plugin-sdk
3
+ *
4
+ * Three things: the types a plugin is written against, a way to run one on
5
+ * your own machine before it goes anywhere near a canvas, and the checks the
6
+ * platform applies to a plugin's code before it will accept it.
7
+ *
8
+ * Nothing here runs inside the sandbox. Plugin code is plain JavaScript that
9
+ * the platform loads; this package exists so an editor can tell you what
10
+ * `context` has on it, and so `visualautomate test` can call your execute with
11
+ * a context that behaves like the real one.
12
+ */
13
+ export * from "./types";
14
+ export * from "./simulate";
15
+ export * from "./validate";
16
+ export { CONTEXT_API, BLOCKED_GLOBALS, type ContextApiEntry } from "./context-api";