@frontera-sdk/workflow 1.50.84 → 1.51.1
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/README.md +18 -0
- package/package.json +3 -3
- package/src/definition.ts +27 -0
- package/src/types.ts +2 -1
- package/src/workflow-client.ts +17 -10
package/README.md
CHANGED
|
@@ -71,6 +71,24 @@ await workflows.run('supplier-onboarding', {
|
|
|
71
71
|
})
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
An input of type `files` takes several files. Put a list of `File` values under
|
|
75
|
+
that input and each file goes as its own part under the input's name, in list
|
|
76
|
+
order.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
await workflows.run('delivery-intake', {
|
|
80
|
+
input: {
|
|
81
|
+
carrier: 'globex-logistics',
|
|
82
|
+
scans: [new File([page1], 'page-1.pdf'), new File([page2], 'page-2.pdf')],
|
|
83
|
+
},
|
|
84
|
+
})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The list must hold only `File` values. A list that mixes `File` values with
|
|
88
|
+
references to files that are already uploaded is not sent as file parts, and
|
|
89
|
+
the service refuses it. An input takes 20 files unless the workflow sets another limit, and
|
|
90
|
+
50 at most.
|
|
91
|
+
|
|
74
92
|
## Author a workflow
|
|
75
93
|
|
|
76
94
|
A definition is a plain object typed as `WorkflowDefinition`, so the step kinds,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frontera-sdk/workflow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.1",
|
|
4
4
|
"description": "Typed headless client for starting, inspecting, waiting on and cancelling Frontera workflow runs from a script or backend.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"frontera",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"typecheck": "bunx tsc --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@frontera-sdk/core": "1.
|
|
42
|
-
"@frontera-sdk/functions": "1.
|
|
41
|
+
"@frontera-sdk/core": "1.51.1",
|
|
42
|
+
"@frontera-sdk/functions": "1.51.1"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/bun": "^1.3.14",
|
package/src/definition.ts
CHANGED
|
@@ -140,12 +140,39 @@ export type AgentTrigger = {
|
|
|
140
140
|
description?: string
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/** Started by something that happened in a connected Plugin account, such as a new email. */
|
|
144
|
+
export type PluginTrigger = {
|
|
145
|
+
kind: 'plugin'
|
|
146
|
+
description?: string
|
|
147
|
+
/** The Plugin install, the same reference a `plugin` node uses. */
|
|
148
|
+
resourceId: string
|
|
149
|
+
/** Event name the install's Plugin kind offers, e.g. `new_email`. */
|
|
150
|
+
event: string
|
|
151
|
+
/** Whose connection the trigger reads: the shared workspace account, or one named person's own account. */
|
|
152
|
+
account: { kind: 'workspace' } | { kind: 'user'; userId: string }
|
|
153
|
+
/** Provider-side selection, validated by the event's own schema. Gmail `new_email`: `{ labelId?: string }`. */
|
|
154
|
+
config?: Record<string, unknown>
|
|
155
|
+
/** JSONata over `$event`. A run starts only when the result is truthy. Absent: every event. */
|
|
156
|
+
filter?: string
|
|
157
|
+
/** Skip automatic replies and delivery failures. Default true. */
|
|
158
|
+
ignoreAutoReplies?: boolean
|
|
159
|
+
/** JSONata over `$event`, giving the run input object. Absent: the keys of `$event.data` that are declared inputs. */
|
|
160
|
+
input?: string
|
|
161
|
+
/** JSONata over `$event`, giving the subject pk. Required iff the definition has a subject. */
|
|
162
|
+
subject?: string
|
|
163
|
+
/** The declared list-of-files input that receives the event's attachments. Absent: attachments are not downloaded. */
|
|
164
|
+
attachmentsInput?: string
|
|
165
|
+
/** Most runs this trigger may start in any 24 hours, 1 to 5000. Events over it wait. Default 200. */
|
|
166
|
+
maxRunsPerDay?: number
|
|
167
|
+
}
|
|
168
|
+
|
|
143
169
|
export type WorkflowTrigger =
|
|
144
170
|
| ManualTrigger
|
|
145
171
|
| CronTrigger
|
|
146
172
|
| WorkflowCallTrigger
|
|
147
173
|
| AgentTrigger
|
|
148
174
|
| WebhookTrigger
|
|
175
|
+
| PluginTrigger
|
|
149
176
|
|
|
150
177
|
// ─── Shared node settings ────────────────────────────────────────────────────
|
|
151
178
|
|
package/src/types.ts
CHANGED
|
@@ -211,7 +211,8 @@ export interface VersionLineage {
|
|
|
211
211
|
export interface RunRequest {
|
|
212
212
|
/**
|
|
213
213
|
* A `File` value here is sent as its own multipart part, named after the
|
|
214
|
-
* `file` input it fills
|
|
214
|
+
* `file` input it fills. A list of `File`s fills a `files` input: one part
|
|
215
|
+
* per file, all under that input's name. Everything else travels as JSON.
|
|
215
216
|
*/
|
|
216
217
|
input?: Record<string, unknown>
|
|
217
218
|
subject?: { pk: string }
|
package/src/workflow-client.ts
CHANGED
|
@@ -307,24 +307,31 @@ export class WorkflowClient {
|
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
310
|
+
/** The `File`s one input value carries: itself, or a non-empty list of nothing but `File`s. */
|
|
311
|
+
function filesOf(value: unknown): File[] {
|
|
312
|
+
if (value instanceof File) return [value]
|
|
313
|
+
return Array.isArray(value) && value.length > 0 && value.every((item) => item instanceof File) ? value : []
|
|
314
|
+
}
|
|
315
|
+
|
|
310
316
|
/**
|
|
311
|
-
* The run body, JSON unless a `file` input carries an actual `File`.
|
|
317
|
+
* The run body, JSON unless a `file` or `files` input carries an actual `File`.
|
|
312
318
|
*
|
|
313
|
-
* A `
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
319
|
+
* A `File` cannot travel as JSON, so one turns the whole request multipart:
|
|
320
|
+
* the non-file fields go in an `input` part as JSON and each file gets a part
|
|
321
|
+
* named after its field, which is the shape the service's `readMultipartRun`
|
|
322
|
+
* expects. A list of `File`s under one key, for a `files` input, is sent as
|
|
323
|
+
* that part name repeated, in list order. `subject` and `version` are
|
|
324
|
+
* serialised because a multipart part is text — the route parses both back.
|
|
325
|
+
* The content type is left alone deliberately: only `FormData` can generate
|
|
326
|
+
* the boundary, and naming the type here would lose it.
|
|
320
327
|
*/
|
|
321
328
|
function runBody(request: RunRequest): RunRequest | FormData {
|
|
322
329
|
const input = request.input ?? {}
|
|
323
|
-
const files = Object.entries(input).
|
|
330
|
+
const files = Object.entries(input).flatMap(([field, value]) => filesOf(value).map((file) => [field, file] as const))
|
|
324
331
|
if (files.length === 0) return request
|
|
325
332
|
|
|
326
333
|
const form = new FormData()
|
|
327
|
-
const rest = Object.fromEntries(Object.entries(input).filter(([, value]) =>
|
|
334
|
+
const rest = Object.fromEntries(Object.entries(input).filter(([, value]) => filesOf(value).length === 0))
|
|
328
335
|
form.append('input', JSON.stringify(rest))
|
|
329
336
|
for (const [field, file] of files) form.append(field, file)
|
|
330
337
|
if (request.subject) form.append('subject', JSON.stringify(request.subject))
|