@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 +21 -0
- package/README.md +110 -0
- package/dist/context-api.d.ts +41 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1534 -0
- package/dist/index.js.map +7 -0
- package/dist/index.mjs +1511 -0
- package/dist/index.mjs.map +7 -0
- package/dist/platform-rules.d.ts +14 -0
- package/dist/simulate.d.ts +81 -0
- package/dist/types.d.ts +418 -0
- package/dist/validate.d.ts +46 -0
- package/package.json +49 -0
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a plugin is handed, and what it must hand back.
|
|
3
|
+
*
|
|
4
|
+
* This file is the contract between a plugin and the VisualAutomate runtime:
|
|
5
|
+
* the arguments `execute` is called with, every helper on `context`, the shape
|
|
6
|
+
* of the result, and the manifest that declares the step. When a helper is only
|
|
7
|
+
* present under some condition, that is said here rather than left for somebody
|
|
8
|
+
* to discover from a TypeError.
|
|
9
|
+
*
|
|
10
|
+
* context-api.ts describes the same runtime as data, for tools that cannot read
|
|
11
|
+
* a TypeScript interface, such as an editor's autocomplete.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* The output of the steps that led here, keyed by step name and node id.
|
|
15
|
+
*
|
|
16
|
+
* A step reads `input.download_clips.file` where `download_clips` is what the
|
|
17
|
+
* step before it is called on the canvas. Every route into this step is merged,
|
|
18
|
+
* so a step at the join of two branches sees both.
|
|
19
|
+
*/
|
|
20
|
+
export interface PluginInput {
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
/** The fields somebody filled in on the canvas, as the manifest declared them. */
|
|
24
|
+
export interface PluginConfig {
|
|
25
|
+
[key: string]: unknown;
|
|
26
|
+
}
|
|
27
|
+
/** What `storage.put` reports about the file it wrote. */
|
|
28
|
+
export interface StorageResult {
|
|
29
|
+
url: string;
|
|
30
|
+
size: number;
|
|
31
|
+
contentType: string;
|
|
32
|
+
uploadedAt: string;
|
|
33
|
+
expiresAt: string;
|
|
34
|
+
}
|
|
35
|
+
/** What `storage.list` reports. */
|
|
36
|
+
export interface StorageListResult {
|
|
37
|
+
files: Array<{
|
|
38
|
+
url: string;
|
|
39
|
+
pathname: string;
|
|
40
|
+
size: number;
|
|
41
|
+
uploadedAt: string;
|
|
42
|
+
}>;
|
|
43
|
+
totalSize: number;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The account's own files. Paths are scoped to the user; you cannot name
|
|
47
|
+
* anybody else's, and you do not have to prefix your own.
|
|
48
|
+
*
|
|
49
|
+
* The account's plan caps what it may hold at once, and a `put` past that
|
|
50
|
+
* ceiling is refused rather than silently dropped.
|
|
51
|
+
*/
|
|
52
|
+
export interface PluginStorage {
|
|
53
|
+
/** Write a file. Bytes may be a string, a Buffer or any typed array. */
|
|
54
|
+
put(path: string, data: Uint8Array | string, contentType?: string): Promise<StorageResult>;
|
|
55
|
+
/** Read a file back. `null` when it is not there. */
|
|
56
|
+
get(path: string): Promise<Uint8Array | null>;
|
|
57
|
+
delete(path: string): Promise<void>;
|
|
58
|
+
/** Everything under a prefix, or everything when no prefix is given. */
|
|
59
|
+
list(prefix?: string): Promise<StorageListResult>;
|
|
60
|
+
/** Size and content type without moving the bytes. `null` when absent. */
|
|
61
|
+
head(path: string): Promise<{
|
|
62
|
+
size: number;
|
|
63
|
+
contentType: string;
|
|
64
|
+
} | null>;
|
|
65
|
+
/**
|
|
66
|
+
* A URL somebody else can fetch this file from, for a few minutes.
|
|
67
|
+
*
|
|
68
|
+
* For the common API that takes a link and pulls the file itself. Clamped
|
|
69
|
+
* to between one minute and one hour; the default is fifteen minutes.
|
|
70
|
+
*/
|
|
71
|
+
signedUrl(path: string, expiresInSeconds?: number): Promise<string | null>;
|
|
72
|
+
/**
|
|
73
|
+
* Pull a URL straight into storage without the bytes passing through this
|
|
74
|
+
* step. A hundred-megabyte file costs the run no memory and no egress,
|
|
75
|
+
* because a Worker fetches it and writes it to storage directly.
|
|
76
|
+
*
|
|
77
|
+
* ONLY PRESENT when the download Worker is configured on the server. Guard
|
|
78
|
+
* with `if (context.storage.download)` if you want to degrade gracefully.
|
|
79
|
+
*/
|
|
80
|
+
download?(url: string, path: string, contentType?: string): Promise<StorageResult>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Values that outlive one run, scoped to this workflow and this account.
|
|
84
|
+
*
|
|
85
|
+
* For a cursor, a last-seen id, a counter — the things a workflow needs to
|
|
86
|
+
* remember between firings. Not for large data: that is what storage is for.
|
|
87
|
+
* The account's plan caps how many keys it may hold.
|
|
88
|
+
*/
|
|
89
|
+
export interface PluginState {
|
|
90
|
+
/** The stored value, or `null` when the key has never been set. */
|
|
91
|
+
get(key: string): Promise<unknown>;
|
|
92
|
+
set(key: string, value: unknown, options?: {
|
|
93
|
+
ttlSeconds?: number;
|
|
94
|
+
}): Promise<void>;
|
|
95
|
+
delete(key: string): Promise<void>;
|
|
96
|
+
list(prefix?: string): Promise<{
|
|
97
|
+
keys: string[];
|
|
98
|
+
}>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* ffmpeg, for a step that has a file in storage and wants a different one.
|
|
102
|
+
*
|
|
103
|
+
* A PLUGIN NEVER BUILDS AN FFMPEG COMMAND LINE, and cannot. You name an
|
|
104
|
+
* operation and supply values; the command line is assembled on the server, one
|
|
105
|
+
* operation at a time, and the only paths in it are placeholders the container
|
|
106
|
+
* fills from a list the server wrote. That is deliberate: `-i` takes any path
|
|
107
|
+
* and several filters open files of their own, so an ffmpeg taking its flags
|
|
108
|
+
* from plugin config would be a plugin reading the container's filesystem —
|
|
109
|
+
* including the file holding that run's own credentials.
|
|
110
|
+
*
|
|
111
|
+
* The job runs in a container of its own and talks to storage directly, so a
|
|
112
|
+
* two hundred megabyte clip never crosses this step.
|
|
113
|
+
*
|
|
114
|
+
* ONLY PRESENT when the sandbox and storage are both configured.
|
|
115
|
+
*/
|
|
116
|
+
export interface PluginMedia {
|
|
117
|
+
run(operation: string, params: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
118
|
+
}
|
|
119
|
+
/** One request through the integration proxy. */
|
|
120
|
+
export interface ConnectionRequest {
|
|
121
|
+
path: string;
|
|
122
|
+
method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
123
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
124
|
+
headers?: Record<string, string>;
|
|
125
|
+
body?: unknown;
|
|
126
|
+
}
|
|
127
|
+
/** What the proxy answers with. The provider's own reply, unwrapped. */
|
|
128
|
+
export interface ConnectionResponse {
|
|
129
|
+
ok: boolean;
|
|
130
|
+
status: number;
|
|
131
|
+
headers?: Record<string, string>;
|
|
132
|
+
body: unknown;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* One of the accounts the user connected.
|
|
136
|
+
*
|
|
137
|
+
* THERE IS NO getToken(), deliberately. A token that can be read can be logged
|
|
138
|
+
* or posted somewhere, and this is where third-party code runs. You name a path
|
|
139
|
+
* and the proxy attaches the credential on the way past.
|
|
140
|
+
*
|
|
141
|
+
* You also never name a host. The base URL belongs to the provider
|
|
142
|
+
* configuration, which is what stops a connection being pointed at an internal
|
|
143
|
+
* address or at somebody's collection server.
|
|
144
|
+
*/
|
|
145
|
+
export interface PluginConnection {
|
|
146
|
+
/** This platform's id for the connection, not the provider's. */
|
|
147
|
+
id: string;
|
|
148
|
+
request(options: ConnectionRequest): Promise<ConnectionResponse>;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The accounts this step was configured with.
|
|
152
|
+
*
|
|
153
|
+
* `get` takes the name of one of your own `connection` properties — not a
|
|
154
|
+
* connection id — so a step can only reach the accounts it was set up with.
|
|
155
|
+
*
|
|
156
|
+
* ONLY PRESENT when the step declares at least one `connection` property and
|
|
157
|
+
* the user has chosen an account for it.
|
|
158
|
+
*/
|
|
159
|
+
export interface PluginConnections {
|
|
160
|
+
get(propertyName: string): Promise<PluginConnection>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Everything a step is told about the run it is part of.
|
|
164
|
+
*
|
|
165
|
+
* The optional members are optional for a reason and not out of caution: each
|
|
166
|
+
* is absent when the thing behind it is not configured or not applicable, and
|
|
167
|
+
* a plugin that needs one should say so plainly rather than crash on a dot.
|
|
168
|
+
*/
|
|
169
|
+
export interface PluginContext {
|
|
170
|
+
/** The workflow this run belongs to. */
|
|
171
|
+
workflowId: string;
|
|
172
|
+
/** This step's id on the canvas. */
|
|
173
|
+
nodeId: string;
|
|
174
|
+
/** Whose run it is. */
|
|
175
|
+
userId: string;
|
|
176
|
+
/** This run, distinct from every other run of the same workflow. */
|
|
177
|
+
executionId: string;
|
|
178
|
+
/** When the run started, as an ISO string. */
|
|
179
|
+
startedAt: string;
|
|
180
|
+
/** Values the plugin was published with. Not the user's credentials. */
|
|
181
|
+
secrets: Record<string, string>;
|
|
182
|
+
/** The account's files. See PluginStorage. */
|
|
183
|
+
storage: PluginStorage;
|
|
184
|
+
/** Values that outlive one run. See PluginState. */
|
|
185
|
+
state: PluginState;
|
|
186
|
+
/** ffmpeg. Absent when the server has no sandbox or storage configured. */
|
|
187
|
+
media?: PluginMedia;
|
|
188
|
+
/** The accounts this step was configured with. Absent when it has none. */
|
|
189
|
+
connections?: PluginConnections;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The result of a step.
|
|
193
|
+
*
|
|
194
|
+
* `output` names which port the run leaves by, and therefore which lines on the
|
|
195
|
+
* canvas are followed. It has to be one of the `outputs` in the manifest —
|
|
196
|
+
* usually `"success"` or `"error"`.
|
|
197
|
+
*
|
|
198
|
+
* `data` is what the next steps read as `{{this_step.field}}`. Keep it flat and
|
|
199
|
+
* named for what it is; those names are what somebody picks from a dropdown.
|
|
200
|
+
*/
|
|
201
|
+
export interface PluginOutput {
|
|
202
|
+
output: string;
|
|
203
|
+
data: Record<string, unknown>;
|
|
204
|
+
metadata?: {
|
|
205
|
+
durationMs?: number;
|
|
206
|
+
memoryUsedMb?: number;
|
|
207
|
+
logs?: string[];
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* A plugin.
|
|
212
|
+
*
|
|
213
|
+
* Export `execute`, either as `module.exports.execute` or as the default export
|
|
214
|
+
* of a module with an `execute` on it. Both are accepted, and so is a bare
|
|
215
|
+
* function as the default export.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* export default {
|
|
219
|
+
* async execute(input, config, context) {
|
|
220
|
+
* const file = await context.storage.get(config.path as string)
|
|
221
|
+
* return { output: "success", data: { bytes: file?.length ?? 0 } }
|
|
222
|
+
* }
|
|
223
|
+
* }
|
|
224
|
+
*/
|
|
225
|
+
export interface PluginModule {
|
|
226
|
+
execute(input: PluginInput, config: PluginConfig, context: PluginContext): Promise<PluginOutput> | PluginOutput;
|
|
227
|
+
}
|
|
228
|
+
/** The bare function form, for a plugin that is only an execute. */
|
|
229
|
+
export type PluginExecute = PluginModule["execute"];
|
|
230
|
+
/**
|
|
231
|
+
* `skipped` is a decision rather than a fault: the filters on every line into
|
|
232
|
+
* the step said no, so it never ran. Counting it as an error turns a branch
|
|
233
|
+
* that was simply not chosen into a broken workflow.
|
|
234
|
+
*/
|
|
235
|
+
export type ExecutionStatus = "success" | "error" | "timeout" | "skipped" | "cancelled";
|
|
236
|
+
export interface PluginExecutionResult {
|
|
237
|
+
status: ExecutionStatus;
|
|
238
|
+
output: PluginOutput | null;
|
|
239
|
+
error?: string;
|
|
240
|
+
durationMs: number;
|
|
241
|
+
memoryMb?: number;
|
|
242
|
+
outputPort?: string;
|
|
243
|
+
logs?: string[];
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The declaration that sits beside the code.
|
|
247
|
+
*
|
|
248
|
+
* It says which fields the canvas renders, which account the step acts as, what
|
|
249
|
+
* it hands downstream, and whether it is a starting point that watches
|
|
250
|
+
* something. One shape wherever it is stored — `manifest.json` next to your
|
|
251
|
+
* code, or an `@schema` comment in the source.
|
|
252
|
+
*
|
|
253
|
+
* Every name below is one the platform keeps. A block it does not recognise is
|
|
254
|
+
* dropped when the plugin is saved, so a field that is not listed here will not
|
|
255
|
+
* survive an upload.
|
|
256
|
+
*/
|
|
257
|
+
export type PluginPropertyType = "string" | "number" | "boolean" | "select" | "multiselect" | "json"
|
|
258
|
+
/**
|
|
259
|
+
* A program, not a value.
|
|
260
|
+
*
|
|
261
|
+
* Stored exactly as typed and never parsed, which is the difference from
|
|
262
|
+
* `json` above: that one keeps its value structured, so `return 5` would be
|
|
263
|
+
* saved as the number five and the step would run nothing.
|
|
264
|
+
*/
|
|
265
|
+
| "code" | "color" | "date"
|
|
266
|
+
/** An account the user connected. Rendered as a picker, not a text box. */
|
|
267
|
+
| "connection"
|
|
268
|
+
/**
|
|
269
|
+
* A file the account already has, read into the config before the step
|
|
270
|
+
* runs. `{{config.attachment.name}}`, `.contentType`, `.size`,
|
|
271
|
+
* `.contentBytes`. Right for attaching a PDF; a video would be hundreds of
|
|
272
|
+
* megabytes of base64 — use `path` for those.
|
|
273
|
+
*/
|
|
274
|
+
| "file"
|
|
275
|
+
/** A file in storage, named rather than read. What ffmpeg steps take. */
|
|
276
|
+
| "path"
|
|
277
|
+
/** Several of them, one per line, in the order they were chosen. */
|
|
278
|
+
| "paths";
|
|
279
|
+
export interface PluginProperty {
|
|
280
|
+
/** Shown above the input on the canvas. Defaults to the property name. */
|
|
281
|
+
label: string;
|
|
282
|
+
type: PluginPropertyType;
|
|
283
|
+
required?: boolean;
|
|
284
|
+
default?: unknown;
|
|
285
|
+
/** Helper text under the input. */
|
|
286
|
+
description?: string;
|
|
287
|
+
/** For `select` and `multiselect`. */
|
|
288
|
+
options?: Array<{
|
|
289
|
+
label: string;
|
|
290
|
+
value: string;
|
|
291
|
+
}>;
|
|
292
|
+
/** Puts related fields in a section together. */
|
|
293
|
+
group?: string;
|
|
294
|
+
/** For `number`. */
|
|
295
|
+
min?: number;
|
|
296
|
+
max?: number;
|
|
297
|
+
/** For `string`: a regex the value must match. */
|
|
298
|
+
pattern?: string;
|
|
299
|
+
/**
|
|
300
|
+
* For `connection`: which integration the account must belong to, e.g.
|
|
301
|
+
* `outlook`. A module inherits its app's, so it does not repeat this.
|
|
302
|
+
*/
|
|
303
|
+
provider?: string;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* How a step reaches the account it acts as.
|
|
307
|
+
*
|
|
308
|
+
* `none` means the plugin holds its own key in a property. That is not the same
|
|
309
|
+
* as leaving the block out, which means "written before auth could be
|
|
310
|
+
* declared".
|
|
311
|
+
*
|
|
312
|
+
* A MANIFEST MAY NOT NAME A HOST. `url`, `baseUrl`, `base_url` and `host` are
|
|
313
|
+
* refused outright rather than ignored, so nobody writes one and believes it
|
|
314
|
+
* took effect: the base URL belongs to the provider configuration, and that is
|
|
315
|
+
* what stops a connection being pointed at an internal address.
|
|
316
|
+
*/
|
|
317
|
+
export interface PluginAuth {
|
|
318
|
+
type: "oauth2" | "api_key" | "basic" | "none";
|
|
319
|
+
provider?: string;
|
|
320
|
+
scopes?: string[];
|
|
321
|
+
}
|
|
322
|
+
/** One request the platform makes on your behalf. Relative paths only. */
|
|
323
|
+
export interface TriggerRequest {
|
|
324
|
+
method?: "GET" | "POST";
|
|
325
|
+
/** Relative to the provider's base URL. The proxy refuses anything else. */
|
|
326
|
+
path: string;
|
|
327
|
+
query?: Record<string, string>;
|
|
328
|
+
headers?: Record<string, string>;
|
|
329
|
+
body?: unknown;
|
|
330
|
+
}
|
|
331
|
+
/** Where the next poll should carry on from. */
|
|
332
|
+
export interface TriggerCursor {
|
|
333
|
+
from: "response" | "item";
|
|
334
|
+
/** Dotted path to the value. `@odata.deltaLink` is one key, not two. */
|
|
335
|
+
path: string;
|
|
336
|
+
into?: string;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* A second request per item, for providers that answer with identifiers.
|
|
340
|
+
*
|
|
341
|
+
* Gmail's message list returns `{ id, threadId }` — enough to know a mail
|
|
342
|
+
* arrived, not enough to tell anyone what it says. Without this the trigger
|
|
343
|
+
* polls correctly, dedupes correctly, and hands every workflow an empty
|
|
344
|
+
* message, which is the worst kind of working.
|
|
345
|
+
*/
|
|
346
|
+
export interface TriggerHydrate {
|
|
347
|
+
path: string;
|
|
348
|
+
method?: "GET" | "POST";
|
|
349
|
+
query?: Record<string, string>;
|
|
350
|
+
headers?: Record<string, string>;
|
|
351
|
+
}
|
|
352
|
+
/** For providers that can tell us instead of being asked. */
|
|
353
|
+
export interface TriggerPush {
|
|
354
|
+
resource: string;
|
|
355
|
+
changeType: string;
|
|
356
|
+
maxMinutes?: number;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* What makes a plugin a starting point.
|
|
360
|
+
*
|
|
361
|
+
* A declaration rather than code, because the runtime is billed by the second
|
|
362
|
+
* and a step blocking on an empty inbox would be paid for every second of doing
|
|
363
|
+
* nothing. The platform polls for you and starts the workflow when something
|
|
364
|
+
* arrives.
|
|
365
|
+
*/
|
|
366
|
+
export interface PluginTrigger {
|
|
367
|
+
/** Must name one of your own properties of type `connection`. */
|
|
368
|
+
connection: string;
|
|
369
|
+
/** Minutes between checks. The account's plan sets the floor. */
|
|
370
|
+
poll?: string | number;
|
|
371
|
+
request: TriggerRequest;
|
|
372
|
+
/** Dotted path to the array of items in the response. */
|
|
373
|
+
items: string;
|
|
374
|
+
/** Dotted path, within one item, to what makes it unique. */
|
|
375
|
+
id: string;
|
|
376
|
+
cursor?: TriggerCursor;
|
|
377
|
+
hydrate?: TriggerHydrate;
|
|
378
|
+
push?: TriggerPush;
|
|
379
|
+
/** How many items one poll may start workflows for. */
|
|
380
|
+
maxPerPoll?: number;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* A step that is a declaration rather than a program.
|
|
384
|
+
*
|
|
385
|
+
* One request built from the properties somebody filled in, or no request at
|
|
386
|
+
* all for a step that only reshapes what reached it. No container, no code, and
|
|
387
|
+
* nothing for a review to be about — which is why most steps should be one.
|
|
388
|
+
*
|
|
389
|
+
* Left loosely typed here on purpose: writing an action is a different job from
|
|
390
|
+
* writing a plugin, and the platform validates its shape when it is saved.
|
|
391
|
+
*/
|
|
392
|
+
export type PluginAction = Record<string, unknown>;
|
|
393
|
+
/**
|
|
394
|
+
* Every block the validator keeps. Anything else is dropped on save.
|
|
395
|
+
*
|
|
396
|
+
* Note what is NOT here: the hosts a plugin may contact are set on the platform
|
|
397
|
+
* rather than declared in the manifest, so a plugin cannot widen its own egress.
|
|
398
|
+
*/
|
|
399
|
+
export interface PluginManifest {
|
|
400
|
+
description?: string;
|
|
401
|
+
icon?: string;
|
|
402
|
+
color?: string;
|
|
403
|
+
/** Bumped by you when the declaration changes shape. */
|
|
404
|
+
version?: number;
|
|
405
|
+
/** How long one run may take. Capped by the account's plan. */
|
|
406
|
+
timeoutMs?: number;
|
|
407
|
+
memoryLimitMb?: number;
|
|
408
|
+
auth?: PluginAuth;
|
|
409
|
+
properties?: Record<string, PluginProperty>;
|
|
410
|
+
inputs?: string[];
|
|
411
|
+
outputs?: string[];
|
|
412
|
+
/** What downstream steps can read, described for the picker. */
|
|
413
|
+
outputFields?: Record<string, string>;
|
|
414
|
+
trigger?: PluginTrigger;
|
|
415
|
+
action?: PluginAction;
|
|
416
|
+
}
|
|
417
|
+
/** The older name for the same thing, kept because authors still say it. */
|
|
418
|
+
export type PluginSchema = PluginManifest;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the platform checks about a step's code before it will take it.
|
|
3
|
+
*
|
|
4
|
+
* The same checks, in the same order, with the same messages as the platform's
|
|
5
|
+
* upload, against the rules in ./platform-rules. A plugin that passes here is
|
|
6
|
+
* not refused on push for its code; one that fails here would have been.
|
|
7
|
+
*
|
|
8
|
+
* And a `require` that behaves like the sandbox's: a step gets the allowed
|
|
9
|
+
* packages and nothing else — no `fs`, no `http`, not even `path`.
|
|
10
|
+
*/
|
|
11
|
+
import { ALLOWED_PACKAGES, RESTRICTED_PATTERNS } from "./platform-rules";
|
|
12
|
+
export { ALLOWED_PACKAGES, RESTRICTED_PATTERNS };
|
|
13
|
+
/** The names in ALLOWED_PACKAGES, for lookups. */
|
|
14
|
+
export declare const ALLOWED_PACKAGE_NAMES: ReadonlySet<string>;
|
|
15
|
+
export type CodeCheck = {
|
|
16
|
+
valid: true;
|
|
17
|
+
packages: string[];
|
|
18
|
+
} | {
|
|
19
|
+
valid: false;
|
|
20
|
+
error: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Check a step's code the way the platform's upload does.
|
|
24
|
+
*
|
|
25
|
+
* On success, also says which packages it requires — the ones a local run has to
|
|
26
|
+
* be able to load.
|
|
27
|
+
*/
|
|
28
|
+
export declare function validatePluginCode(code: string): CodeCheck;
|
|
29
|
+
/**
|
|
30
|
+
* The `require` a step gets inside the sandbox, backed by a loader of your own.
|
|
31
|
+
*
|
|
32
|
+
* `load` is how this machine finds a package — in the CLI, Node's resolution
|
|
33
|
+
* from the project's directory. The sandbox has every allowed package installed;
|
|
34
|
+
* a laptop has whatever was installed there, so a missing one is reported with
|
|
35
|
+
* the command that installs the version the sandbox has.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sandboxRequire(load: (name: string) => unknown): (name: string) => unknown;
|
|
38
|
+
/**
|
|
39
|
+
* Evaluate a step's code the way the sandbox wraps it: as the body of a function
|
|
40
|
+
* given `module`, `exports` and `require`, in strict mode. Returns what it exported.
|
|
41
|
+
*
|
|
42
|
+
* Not a security boundary — this runs your own code on your own machine. It is
|
|
43
|
+
* here so a file the sandbox cannot load (an `export` statement, a top-level
|
|
44
|
+
* `await`) fails here too, rather than passing a looser loader.
|
|
45
|
+
*/
|
|
46
|
+
export declare function loadPluginCode(code: string, require: (name: string) => unknown): unknown;
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@visualautomate/plugin-sdk",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Types and a local runner for building VisualAutomate plugins.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.mjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "node build.mjs",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"visualautomate",
|
|
27
|
+
"plugin",
|
|
28
|
+
"sdk",
|
|
29
|
+
"automation",
|
|
30
|
+
"workflow"
|
|
31
|
+
],
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=22"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/node": "^22",
|
|
37
|
+
"esbuild": "^0.23.0",
|
|
38
|
+
"typescript": "^5"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "git+https://github.com/VisualAutomate/visualautomate-sdk.git",
|
|
43
|
+
"directory": "packages/plugin-sdk"
|
|
44
|
+
},
|
|
45
|
+
"homepage": "https://developers.visualautomate.com",
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
}
|
|
49
|
+
}
|