@oomol-lab/open-flow 0.1.0-beta.75 → 0.1.0-beta.77
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 +3 -1
- package/dist/browser/{ContentIcon-FCpKiwjp.js → ContentIcon-VZQO8VZU.js} +9 -6
- package/dist/browser/{FlowCanvas-dzRBnlUT.js → FlowCanvas-BNGDMaWr.js} +75 -63
- package/dist/browser/{createResourceDialog-BeYDdUl-.js → createResourceDialog-BmtakfVg.js} +63 -57
- package/dist/browser/{dialog-ZtpsFbl4.js → dialog-BcFBfRB1.js} +126 -32
- package/dist/browser/{exportSvg-DqXw1yf-.js → exportSvg-CG8hEUNk.js} +2 -2
- package/dist/browser/{flowChanges-b_5UobFj.js → flowChanges-BGo_4Ypo.js} +6063 -11285
- package/dist/browser/{flowWorkspace-ItojZbO4.js → flowWorkspace-BFHGev7s.js} +1065 -2570
- package/dist/browser/preview.css +626 -653
- package/dist/browser/preview.js +1907 -9663
- package/dist/browser/theme.css +117 -263
- package/dist/browser/{tooltip-wZJRB9oP.js → tooltip-BAWSUexx.js} +2 -2
- package/dist/browser/{typeScriptSession-CSRwWmZm.js → typeScriptSession-CRoezJPN.js} +1 -1
- package/dist/browser/ui.css +464 -463
- package/dist/browser/ui.js +4 -3
- package/dist/browser/workbench.css +9891 -9897
- package/dist/browser/workbench.d.ts +6 -2
- package/dist/browser/workbench.js +838 -499
- package/dist/browser/{workbenchSelect-M441Lz0d.js → workbenchSelect-CjK-xdm3.js} +2 -2
- package/dist/command/index.js +48 -50
- package/dist/common/authoring.d.ts +5 -5
- package/dist/common/authoringExamples.d.ts +3 -2
- package/dist/common/authoringSchema.d.ts +26 -4
- package/dist/common/connector-proxy.d.ts +1 -1
- package/dist/common/control-api-conformance.js +352 -144
- package/dist/common/control-api-errors.d.ts +12 -0
- package/dist/common/control-api.d.ts +38 -4
- package/dist/common/control-api.js +373 -162
- package/dist/common/control-requests.d.ts +22 -3
- package/dist/common/control-requests.js +268 -28
- package/dist/common/cron-trigger.d.ts +2 -1
- package/dist/common/decision.d.ts +4 -4
- package/dist/common/decision.js +5 -2
- package/dist/common/engine-contract.d.ts +6 -1
- package/dist/common/event-source-definition.d.ts +1 -1
- package/dist/common/event-source-view.d.ts +22 -0
- package/dist/common/event-source.d.ts +1 -1
- package/dist/common/event-source.js +24 -65
- package/dist/common/event-sources.d.ts +0 -21
- package/dist/common/flow-authoring.js +90 -20
- package/dist/common/flow-change-schema.d.ts +9 -1
- package/dist/common/flow-change.d.ts +8 -202
- package/dist/common/flow-change.js +96 -24
- package/dist/common/flow-encoding.js +89 -21
- package/dist/common/flow-input-value.d.ts +1 -1
- package/dist/common/flow-model.d.ts +121 -0
- package/dist/common/flow-notifications.d.ts +1 -1
- package/dist/common/flow-retry.d.ts +2 -4
- package/dist/common/flow-semantics.js +161 -80
- package/dist/common/flow-state.d.ts +123 -0
- package/dist/common/flow-state.js +5852 -0
- package/dist/common/flow-workflowReferences.d.ts +8 -0
- package/dist/common/flowInspection.d.ts +1 -1
- package/dist/common/http-hosts.d.ts +6 -0
- package/dist/common/http-request.d.ts +13 -5
- package/dist/common/http-request.js +23 -5
- package/dist/common/integration-trigger.d.ts +2 -1
- package/dist/common/mcp.d.ts +96 -3
- package/dist/common/mcp.js +355 -142
- package/dist/common/openapi.d.ts +4 -4
- package/dist/common/openapi.js +4 -1
- package/dist/common/poll-trigger.d.ts +2 -1
- package/dist/common/run-events.js +1 -0
- package/dist/common/runtime-contract.d.ts +37 -1
- package/dist/common/runtime-contract.js +717 -72
- package/dist/common/runtime-nodejs-compat.d.ts +44 -0
- package/dist/common/runtime-nodejs-compat.js +5870 -0
- package/dist/common/scheduler-checkpoint.d.ts +1 -1
- package/dist/common/scheduler-node-execution.d.ts +2 -1
- package/dist/common/scheduler.d.ts +2 -2
- package/dist/common/scheduler.js +76 -6
- package/dist/common/state-conformance.d.ts +16 -0
- package/dist/common/tags.d.ts +4 -0
- package/dist/common/trigger-catalog.d.ts +1 -7
- package/dist/common/trigger-config-options.d.ts +1 -1
- package/dist/common/trigger-config.d.ts +1 -1
- package/dist/common/trigger-contract.d.ts +2 -1
- package/dist/common/trigger-localization.d.ts +8 -2
- package/dist/common/trigger-model.d.ts +101 -0
- package/dist/common/webhook-response.d.ts +1 -1
- package/dist/common/webhook-trigger.d.ts +3 -1
- package/dist/common/workflow.d.ts +3 -12
- package/dist/common/workflow.js +25 -23
- package/dist/index.d.ts +52 -14
- package/package.json +9 -1
- package/src/base/common/json.ts +1 -1
- package/src/connector/common/proxy.ts +1 -1
- package/src/control/common/api.ts +90 -16
- package/src/control/common/authoring.ts +6 -1
- package/src/control/common/authoringExamples.ts +8 -0
- package/src/control/common/authoringSchema.ts +26 -13
- package/src/control/common/authoringTools.ts +1 -1
- package/src/control/common/conformance.ts +1 -1
- package/src/control/common/connectorDecoders.ts +1 -1
- package/src/control/common/errors.ts +6 -0
- package/src/control/common/eventSources.ts +2 -29
- package/src/control/common/flowDecoders.ts +1 -0
- package/src/control/common/mcp.ts +16 -0
- package/src/control/common/requests.ts +10 -2
- package/src/control/common/tags.ts +20 -0
- package/src/control/common/triggerCatalog.ts +1 -8
- package/src/decision/common/decision.ts +15 -7
- package/src/event-source/common/definition.ts +1 -1
- package/src/event-source/common/index.ts +1 -1
- package/src/event-source/common/view.ts +20 -0
- package/src/execution/common/checkpoint.ts +1 -1
- package/src/execution/common/events.ts +3 -0
- package/src/execution/common/node-execution.ts +4 -1
- package/src/execution/common/scheduler.ts +3 -1
- package/src/flow/common/change.ts +60 -193
- package/src/flow/common/changeSchema.ts +39 -4
- package/src/flow/common/connectionUsage.ts +1 -1
- package/src/flow/common/errorHandling.ts +1 -1
- package/src/flow/common/inputValue.ts +1 -1
- package/src/flow/common/model.ts +130 -0
- package/src/flow/common/modules.ts +1 -1
- package/src/flow/common/portChanges.ts +1 -1
- package/src/flow/common/retry.ts +2 -4
- package/src/flow/common/semantics.ts +31 -4
- package/src/flow/common/webhookResponse.ts +1 -1
- package/src/flow/common/workflowReferences.ts +31 -0
- package/src/http/common/authBindings.ts +6 -9
- package/src/http/common/hosts.ts +13 -0
- package/src/http/common/httpRequest.ts +30 -15
- package/src/openapi/common/openapi.ts +9 -5
- package/src/{execution → runtime}/common/engineContract.ts +23 -15
- package/src/runtime/common/nodejsCompat.ts +275 -0
- package/src/runtime/common/nodejsGuest.generated.json +47 -0
- package/src/runtime/common/runtime.ts +1254 -0
- package/src/state/common/conformance.ts +86 -0
- package/src/state/common/state.ts +169 -0
- package/src/trigger/common/config.ts +1 -1
- package/src/trigger/common/configOptions.ts +1 -1
- package/src/trigger/common/contract.ts +2 -1
- package/src/trigger/common/cron.ts +2 -1
- package/src/trigger/common/integration.ts +2 -1
- package/src/trigger/common/model.ts +87 -0
- package/src/trigger/common/poll.ts +2 -1
- package/src/trigger/common/webhook.ts +3 -1
- package/src/trigger/providers/fieldDescriptions.ts +1 -1
- package/src/trigger/providers/localization.ts +9 -2
- package/src/trigger/providers/remote.ts +1 -1
- package/src/types/index.ts +55 -15
- package/src/workflow/common/workflow.ts +3 -44
- package/src/execution/common/runtime.ts +0 -563
- /package/src/{control → connector}/common/providerIconSprite.ts +0 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { JsonValue } from '../../flow/common/model.ts'
|
|
2
|
+
import type { DedupeConfig } from './state.ts'
|
|
3
|
+
|
|
4
|
+
import { dequal } from 'dequal/lite'
|
|
5
|
+
|
|
6
|
+
export interface StateConformanceHarness {
|
|
7
|
+
readonly maxIdentities: number
|
|
8
|
+
/** Executes Code on the same initially empty Flow and scope on every call; returns its `result` output. */
|
|
9
|
+
run(source: string): Promise<JsonValue>
|
|
10
|
+
/** Executes the same dedupe node across Runs, updating its business config and items. */
|
|
11
|
+
dedupe(config: DedupeConfig, items: JsonValue[]): Promise<{ items: JsonValue[]; duplicates: JsonValue[] }>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const stateConformanceCases: readonly { name: string; verify(harness: StateConformanceHarness): Promise<void> }[] = [
|
|
15
|
+
{
|
|
16
|
+
name: 'persists watermarks by instant and resets changed modes atomically',
|
|
17
|
+
async verify(harness) {
|
|
18
|
+
const config: DedupeConfig = { field: '', mode: 'increasing', space: 'node' }
|
|
19
|
+
const first = await harness.dedupe(config, ['2026-10-30T09:00:00+08:00', '2026-10-30T01:00:00Z', '2026-10-30T02:00:00Z'])
|
|
20
|
+
if (!dequal(first, { items: ['2026-10-30T09:00:00+08:00', '2026-10-30T02:00:00Z'], duplicates: ['2026-10-30T01:00:00Z'] }))
|
|
21
|
+
throw new Error('Watermarks must compare instants and preserve item order.')
|
|
22
|
+
let rejected = false
|
|
23
|
+
try {
|
|
24
|
+
await harness.dedupe(config, [1])
|
|
25
|
+
} catch {
|
|
26
|
+
rejected = true
|
|
27
|
+
}
|
|
28
|
+
if (!rejected) throw new Error('Mixed watermark types must fail.')
|
|
29
|
+
const second = await harness.dedupe(config, ['2026-10-30T02:00:00Z', '2026-10-30T03:00:00Z'])
|
|
30
|
+
if (!dequal(second, { items: ['2026-10-30T03:00:00Z'], duplicates: ['2026-10-30T02:00:00Z'] }))
|
|
31
|
+
throw new Error('A failed batch must preserve the previous watermark.')
|
|
32
|
+
await harness.dedupe({ ...config, mode: 'new' }, [])
|
|
33
|
+
const reset = await harness.dedupe(config, [5, 3, 5])
|
|
34
|
+
if (!dequal(reset, { items: [5, 3], duplicates: [5] })) throw new Error('A mode reset must discard previous watermarks, including on empty batches.')
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'round-trips independent JSON values and deletes without exposing stored references',
|
|
39
|
+
async verify(harness) {
|
|
40
|
+
const result = await harness.run(`export default async (_, context) => {
|
|
41
|
+
await context.state.set('b', { nested: [1, null, false] });
|
|
42
|
+
const copy = await context.state.get('b');
|
|
43
|
+
copy.nested.push(2);
|
|
44
|
+
const retained = await context.state.get('b');
|
|
45
|
+
const inserted = await context.state.insert('b', 'replacement');
|
|
46
|
+
await context.state.set('a', 0);
|
|
47
|
+
const keys = await context.state.list();
|
|
48
|
+
await context.state.delete('a');
|
|
49
|
+
await context.state.set('b', null);
|
|
50
|
+
return {result: {retained, inserted, keys, missing: await context.state.get('a'), remaining: await context.state.list()}};
|
|
51
|
+
}`)
|
|
52
|
+
if (!dequal(result, { retained: { nested: [1, null, false] }, inserted: false, keys: ['a', 'b'], missing: null, remaining: [] }))
|
|
53
|
+
throw new Error('State JSON round-trip, conditional insertion or deletion differs from the contract.')
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'persists explicit values and dedupe history across Runs',
|
|
58
|
+
async verify(harness) {
|
|
59
|
+
const source = `export default async (_, context) => {
|
|
60
|
+
const first = await context.state.insert('count', 0);
|
|
61
|
+
const count = await context.state.get('count');
|
|
62
|
+
await context.state.set('count', count + 1);
|
|
63
|
+
return { result: { first, count, ids: await context.dedupe.insert(['a', 'a']) } };
|
|
64
|
+
}`
|
|
65
|
+
if (
|
|
66
|
+
!dequal(await harness.run(source), { first: true, count: 0, ids: [true, false] }) ||
|
|
67
|
+
!dequal(await harness.run(source), { first: false, count: 1, ids: [false, false] })
|
|
68
|
+
)
|
|
69
|
+
throw new Error('State must persist across Runs without replacing existing values on insert.')
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
name: 'rejects a full batch atomically while permitting duplicates at capacity',
|
|
74
|
+
async verify(harness) {
|
|
75
|
+
const result = await harness.run(`export default async (_, context) => {
|
|
76
|
+
const ids = Array.from({length: ${harness.maxIdentities}}, (_, index) => index);
|
|
77
|
+
await context.dedupe.insert(ids);
|
|
78
|
+
let error;
|
|
79
|
+
try { await context.dedupe.insert([0, 'extra']); } catch (failure) { error = failure.code; }
|
|
80
|
+
return { result: { error, extra: await context.dedupe.has(['extra']), duplicate: await context.dedupe.insert([0, 0]) } };
|
|
81
|
+
}`)
|
|
82
|
+
if (!dequal(result, { error: 'dedupe.full', extra: [false], duplicate: [false, false] }))
|
|
83
|
+
throw new Error('Dedupe capacity must fail atomically and preserve duplicate membership.')
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
]
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import type { JsonValue, ManagedTaskDefinitionOf } from '../../flow/common/model.ts'
|
|
2
|
+
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
|
|
5
|
+
export { stateConformanceCases, type StateConformanceHarness } from './conformance.ts'
|
|
6
|
+
|
|
7
|
+
export type StateScope = 'draft' | 'live'
|
|
8
|
+
export const stateQuerySchema = z.strictObject({ scope: z.enum(['draft', 'live']), values: z.boolean().optional() })
|
|
9
|
+
export const stateClearSchema = z.strictObject({
|
|
10
|
+
scope: z.enum(['draft', 'live']),
|
|
11
|
+
key: z
|
|
12
|
+
.string()
|
|
13
|
+
.min(1)
|
|
14
|
+
.refine((key) => new TextEncoder().encode(key).length <= 256)
|
|
15
|
+
.optional(),
|
|
16
|
+
})
|
|
17
|
+
export const dedupeClearSchema = z.strictObject({ scope: z.enum(['draft', 'live']), space: z.string().regex(/^(flow|node:.+)$/) })
|
|
18
|
+
export type DedupeIdentity = string | number
|
|
19
|
+
export const dedupeConfigSchema = z.strictObject({
|
|
20
|
+
field: z.string().default(''),
|
|
21
|
+
mode: z.enum(['new', 'increasing']).default('new'),
|
|
22
|
+
space: z.enum(['node', 'flow']).default('node'),
|
|
23
|
+
})
|
|
24
|
+
export type DedupeConfig = z.infer<typeof dedupeConfigSchema>
|
|
25
|
+
export interface DedupeExecutor extends DedupeConfig {
|
|
26
|
+
readonly kind: 'dedupe'
|
|
27
|
+
}
|
|
28
|
+
export const dedupeExecutorSchema = dedupeConfigSchema.extend({ kind: z.literal('dedupe') })
|
|
29
|
+
|
|
30
|
+
export function dedupeTask(config: DedupeConfig = dedupeConfigSchema.parse({}), name = 'Remove Duplicates'): ManagedTaskDefinitionOf<DedupeExecutor> {
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
executor: { ...config, kind: 'dedupe' },
|
|
34
|
+
inputs: [{ handle: 'items', jsonSchema: { type: 'array' }, nullable: false }],
|
|
35
|
+
outputs: ['items', 'duplicates'].map((handle) => ({ handle, jsonSchema: { type: 'array' }, nullable: false })),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export class StateError extends Error {
|
|
40
|
+
readonly retryable = false
|
|
41
|
+
readonly catchable: boolean
|
|
42
|
+
readonly code: 'capability.invalid' | 'capability.denied' | 'dedupe.full' | 'limit-exceeded'
|
|
43
|
+
|
|
44
|
+
constructor(code: StateError['code'], message: string) {
|
|
45
|
+
super(message)
|
|
46
|
+
this.name = 'StateError'
|
|
47
|
+
this.code = code
|
|
48
|
+
this.catchable = code === 'dedupe.full'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function stateKey(key: unknown): string {
|
|
53
|
+
if (typeof key !== 'string' || key.length === 0 || new TextEncoder().encode(key).length > 256)
|
|
54
|
+
throw new StateError('capability.invalid', 'State keys require 1 to 256 UTF-8 bytes.')
|
|
55
|
+
return key
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function dedupeIdentities(value: unknown): DedupeIdentity[] {
|
|
59
|
+
if (!Array.isArray(value) || !Array.from(value).every((id) => typeof id === 'string' || (typeof id === 'number' && Number.isFinite(id))))
|
|
60
|
+
throw new StateError('capability.invalid', 'Identities must be strings or finite numbers.')
|
|
61
|
+
return value
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function hashIdentities(ids: readonly DedupeIdentity[]): Promise<Uint8Array[]> {
|
|
65
|
+
return Promise.all(
|
|
66
|
+
ids.map(async (id) => new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(`${typeof id}:${String(id)}`))).slice(0, 16)),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function watermarkValue(id: DedupeIdentity): number {
|
|
71
|
+
if (typeof id === 'number') {
|
|
72
|
+
if (!Number.isFinite(id)) throw new StateError('capability.invalid', 'Watermarks must be finite.')
|
|
73
|
+
return id
|
|
74
|
+
}
|
|
75
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?(Z|[+-]\d{2}:\d{2})$/.exec(id)
|
|
76
|
+
if (match == null) throw new StateError('capability.invalid', 'Watermarks require RFC 3339 timestamps with an explicit timezone.')
|
|
77
|
+
const [, year, month, day, hour, minute, second, , zone] = match
|
|
78
|
+
const y = Number(year)
|
|
79
|
+
const days = [31, y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][Number(month) - 1] ?? 0
|
|
80
|
+
const result = Date.parse(id)
|
|
81
|
+
if (
|
|
82
|
+
Number(month) < 1 ||
|
|
83
|
+
Number(month) > 12 ||
|
|
84
|
+
Number(day) < 1 ||
|
|
85
|
+
Number(day) > days ||
|
|
86
|
+
Number(hour) > 23 ||
|
|
87
|
+
Number(minute) > 59 ||
|
|
88
|
+
Number(second) > 59 ||
|
|
89
|
+
(zone !== 'Z' && (Number(zone!.slice(1, 3)) > 23 || Number(zone!.slice(4)) > 59)) ||
|
|
90
|
+
!Number.isFinite(result)
|
|
91
|
+
)
|
|
92
|
+
throw new StateError('capability.invalid', 'Watermarks require valid calendar timestamps.')
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function increasingIdentities(ids: readonly DedupeIdentity[], watermark: DedupeIdentity | null) {
|
|
97
|
+
const type = typeof (watermark ?? ids[0])
|
|
98
|
+
if (ids.some((id) => typeof id !== type)) throw new StateError('capability.invalid', 'Watermark types must not be mixed.')
|
|
99
|
+
const initial = watermark == null ? -Infinity : watermarkValue(watermark)
|
|
100
|
+
const seen = new Set<number>()
|
|
101
|
+
let maximum = initial
|
|
102
|
+
let next = watermark
|
|
103
|
+
const inserted = ids.map((id) => {
|
|
104
|
+
const value = watermarkValue(id)
|
|
105
|
+
const fresh = value > initial && !seen.has(value)
|
|
106
|
+
seen.add(value)
|
|
107
|
+
if (value > maximum) {
|
|
108
|
+
maximum = value
|
|
109
|
+
next = id
|
|
110
|
+
}
|
|
111
|
+
return fresh
|
|
112
|
+
})
|
|
113
|
+
return { inserted, watermark: next }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function itemIdentities(items: JsonValue, field: string): DedupeIdentity[] {
|
|
117
|
+
if (!Array.isArray(items)) throw new StateError('capability.invalid', 'Deduplication requires an array of items.')
|
|
118
|
+
const path = field === '' ? [] : field.split('.')
|
|
119
|
+
return dedupeIdentities(
|
|
120
|
+
items.map((item) =>
|
|
121
|
+
path.reduce<JsonValue | undefined>(
|
|
122
|
+
(value, key) => (value != null && typeof value === 'object' && Object.hasOwn(value, key) ? (value as Record<string, JsonValue>)[key] : undefined),
|
|
123
|
+
item,
|
|
124
|
+
),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface FlowStateView {
|
|
130
|
+
readonly version: 1
|
|
131
|
+
readonly scope: StateScope
|
|
132
|
+
readonly limits: {
|
|
133
|
+
readonly maxValueBytes: number
|
|
134
|
+
readonly maxValues: number
|
|
135
|
+
readonly maxSpaces: number
|
|
136
|
+
readonly maxIdentities: number
|
|
137
|
+
readonly maxRunWrites: number
|
|
138
|
+
}
|
|
139
|
+
readonly values: readonly { readonly key: string; readonly size: number; readonly updatedAt: number; readonly value?: JsonValue }[]
|
|
140
|
+
readonly spaces: readonly {
|
|
141
|
+
readonly space: string
|
|
142
|
+
readonly mode: 'new' | 'increasing'
|
|
143
|
+
readonly count: number
|
|
144
|
+
readonly watermark: DedupeIdentity | null
|
|
145
|
+
readonly updatedAt: number
|
|
146
|
+
}[]
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const flowStateViewSchema = z.object({
|
|
150
|
+
version: z.literal(1),
|
|
151
|
+
scope: z.enum(['draft', 'live']),
|
|
152
|
+
limits: z.object({
|
|
153
|
+
maxValueBytes: z.number().positive(),
|
|
154
|
+
maxValues: z.number().int().positive(),
|
|
155
|
+
maxSpaces: z.number().int().positive(),
|
|
156
|
+
maxIdentities: z.number().positive(),
|
|
157
|
+
maxRunWrites: z.number().positive(),
|
|
158
|
+
}),
|
|
159
|
+
values: z.array(z.object({ key: z.string(), size: z.number().nonnegative(), updatedAt: z.number(), value: z.json().optional() })),
|
|
160
|
+
spaces: z.array(
|
|
161
|
+
z.object({
|
|
162
|
+
space: z.string(),
|
|
163
|
+
mode: z.enum(['new', 'increasing']),
|
|
164
|
+
count: z.number().nonnegative(),
|
|
165
|
+
watermark: z.union([z.string(), z.number(), z.null()]),
|
|
166
|
+
updatedAt: z.number(),
|
|
167
|
+
}),
|
|
168
|
+
),
|
|
169
|
+
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Group, InputPort, InputValues, JsonValue } from '../../flow/common/
|
|
1
|
+
import type { Group, InputPort, InputValues, JsonValue } from '../../flow/common/model.ts'
|
|
2
2
|
|
|
3
3
|
import { inputValue } from '../../flow/common/inputValue.ts'
|
|
4
4
|
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { JsonValue, Port, PortDefinition
|
|
1
|
+
import type { JsonValue, Port, PortDefinition } from '../../flow/common/model.ts'
|
|
2
|
+
import type { TriggerNode } from './model.ts'
|
|
2
3
|
|
|
3
4
|
import { isJsonObject } from '../../base/common/json.ts'
|
|
4
5
|
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { resolveTriggerConfig, triggerConfigValues } from './config.ts'
|
|
2
2
|
import type { ConnectorProxy } from '../../connector/common/proxy.ts'
|
|
3
|
-
import type {
|
|
3
|
+
import type { JsonValue } from '../../flow/common/model.ts'
|
|
4
4
|
import type { TriggerConfigOption, TriggerConfigOptionsContext } from './configOptions.ts'
|
|
5
|
+
import type { IntegrationEndpointMethod, TriggerKeySnapshot } from './model.ts'
|
|
5
6
|
|
|
6
7
|
import { dequal } from 'dequal/lite'
|
|
7
8
|
import { isJsonObject, isJsonValue } from '../../base/common/json.ts'
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { Group, InputPort, InputValues, Port } from '../../flow/common/model.ts'
|
|
2
|
+
import type { WebhookMethod } from '../../flow/common/webhookMethod.ts'
|
|
3
|
+
|
|
4
|
+
/** Trigger node shapes. The graph node union and its operations belong to flow/common/change.ts. */
|
|
5
|
+
|
|
6
|
+
export type TriggerSchedule =
|
|
7
|
+
| { readonly expression: string; readonly timezone: string; readonly type: 'cron' }
|
|
8
|
+
| { readonly type: 'every'; readonly unit: 'day' | 'hour' | 'minute' | 'month' | 'week'; readonly value: number }
|
|
9
|
+
|
|
10
|
+
interface TriggerKeySnapshotBase {
|
|
11
|
+
readonly configInputs: readonly (InputPort | Group)[]
|
|
12
|
+
readonly definitionVersion: number
|
|
13
|
+
readonly description: string
|
|
14
|
+
readonly displayName: string
|
|
15
|
+
readonly key: string
|
|
16
|
+
readonly name: string
|
|
17
|
+
readonly outputs: readonly Port[]
|
|
18
|
+
readonly provider: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type IntegrationEndpointMethod = 'DELETE' | 'GET' | 'HEAD' | 'PATCH' | 'POST' | 'PUT'
|
|
22
|
+
|
|
23
|
+
export type IntegrationBodyFormat = 'form' | 'json' | 'multipart' | 'text'
|
|
24
|
+
|
|
25
|
+
export interface IntegrationEndpointDeclaration {
|
|
26
|
+
readonly body: {
|
|
27
|
+
readonly allowArray: boolean
|
|
28
|
+
readonly allowEmpty: boolean
|
|
29
|
+
readonly formats: readonly IntegrationBodyFormat[]
|
|
30
|
+
}
|
|
31
|
+
readonly methods: readonly IntegrationEndpointMethod[]
|
|
32
|
+
readonly successStatus: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type TriggerKeySnapshot =
|
|
36
|
+
| (TriggerKeySnapshotBase & { readonly type: 'poll' })
|
|
37
|
+
| (TriggerKeySnapshotBase & { readonly endpoint: IntegrationEndpointDeclaration; readonly type: 'integration' })
|
|
38
|
+
|
|
39
|
+
export interface WebhookBodyField extends InputPort {}
|
|
40
|
+
|
|
41
|
+
export interface WebhookOptions {
|
|
42
|
+
readonly allowedOrigins?: readonly string[]
|
|
43
|
+
readonly responseBody?:
|
|
44
|
+
| { readonly kind: 'workflow-outputs'; readonly output?: string; readonly timeoutMs?: number }
|
|
45
|
+
| { readonly kind: 'webhook-response'; readonly nodeId?: string; readonly timeoutMs?: number }
|
|
46
|
+
readonly responseData?: string
|
|
47
|
+
readonly responseHeaders?: Readonly<Record<string, string>>
|
|
48
|
+
readonly responseHeadersSource?: {
|
|
49
|
+
readonly kind: 'workflow-outputs'
|
|
50
|
+
readonly output?: string
|
|
51
|
+
}
|
|
52
|
+
readonly responseHeaderOutputs?: Readonly<Record<string, string>>
|
|
53
|
+
readonly responseStatusCode?: number
|
|
54
|
+
readonly responseStatusCodeOutput?: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
interface TriggerNodeBase {
|
|
58
|
+
readonly onError?: never
|
|
59
|
+
readonly description?: string
|
|
60
|
+
readonly icon?: string
|
|
61
|
+
readonly name: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type TriggerNode =
|
|
65
|
+
| (TriggerNodeBase & { readonly kind: 'manual' })
|
|
66
|
+
| (TriggerNodeBase & { readonly kind: 'start'; readonly inputDefs: readonly Port[] })
|
|
67
|
+
| (TriggerNodeBase & { readonly kind: 'error'; readonly sourceFlowIds?: readonly string[] })
|
|
68
|
+
| (TriggerNodeBase & {
|
|
69
|
+
readonly bodyFields: readonly WebhookBodyField[]
|
|
70
|
+
readonly kind: 'webhook'
|
|
71
|
+
readonly method: WebhookMethod
|
|
72
|
+
readonly options?: WebhookOptions
|
|
73
|
+
})
|
|
74
|
+
| (TriggerNodeBase & { readonly cronTimes: readonly TriggerSchedule[]; readonly kind: 'cron' })
|
|
75
|
+
| (TriggerNodeBase & {
|
|
76
|
+
readonly connectionId?: string
|
|
77
|
+
readonly config: InputValues
|
|
78
|
+
readonly definition: TriggerKeySnapshot & { readonly type: 'poll' }
|
|
79
|
+
readonly kind: 'poll'
|
|
80
|
+
readonly pollTimes: readonly TriggerSchedule[]
|
|
81
|
+
})
|
|
82
|
+
| (TriggerNodeBase & {
|
|
83
|
+
readonly connectionId?: string
|
|
84
|
+
readonly config: InputValues
|
|
85
|
+
readonly definition: TriggerKeySnapshot & { readonly type: 'integration' }
|
|
86
|
+
readonly kind: 'integration'
|
|
87
|
+
})
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
export { resolveTriggerConfig, triggerConfigValues } from './config.ts'
|
|
2
2
|
import type { ConnectorProxy } from '../../connector/common/proxy.ts'
|
|
3
|
-
import type { JsonValue
|
|
3
|
+
import type { JsonValue } from '../../flow/common/model.ts'
|
|
4
4
|
import type { TriggerConfigOption, TriggerConfigOptionsContext } from './configOptions.ts'
|
|
5
|
+
import type { TriggerKeySnapshot, TriggerSchedule } from './model.ts'
|
|
5
6
|
|
|
6
7
|
const encoder = new TextEncoder()
|
|
7
8
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { JsonValue
|
|
1
|
+
import type { JsonValue } from '../../flow/common/model.ts'
|
|
2
|
+
import type { WebhookMethod } from '../../flow/common/webhookMethod.ts'
|
|
3
|
+
import type { WebhookBodyField, WebhookOptions } from './model.ts'
|
|
2
4
|
|
|
3
5
|
import { dequal } from 'dequal/lite'
|
|
4
6
|
import { isJsonObject } from '../../base/common/json.ts'
|
|
@@ -1,10 +1,17 @@
|
|
|
1
|
-
import type { TriggerDisplay } from '../../control/common/triggerCatalog.ts'
|
|
2
|
-
import type { TriggerKeySnapshot } from '../../flow/common/change.ts'
|
|
3
1
|
import type { UiLanguage } from '../../localization/common/languages.ts'
|
|
2
|
+
import type { TriggerKeySnapshot } from '../common/model.ts'
|
|
4
3
|
|
|
5
4
|
import { englishTriggerFieldDescriptions } from './fieldDescriptions.ts'
|
|
6
5
|
import { triggerConfigFieldLabels } from './fieldLabels.ts'
|
|
7
6
|
|
|
7
|
+
export interface TriggerDisplay {
|
|
8
|
+
readonly configInputs: Readonly<Record<string, string>>
|
|
9
|
+
readonly configInputLabels: Readonly<Record<string, string>>
|
|
10
|
+
readonly displayName: string
|
|
11
|
+
readonly description: string
|
|
12
|
+
readonly outputs: Readonly<Record<string, string>>
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
type Translations = Readonly<Record<string, Partial<TriggerDisplay>>>
|
|
9
16
|
|
|
10
17
|
const loaders = {
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { ConnectorProxy, TriggerOperationRequest } from '../../connector/common/proxy.ts'
|
|
2
|
-
import type { TriggerKeySnapshot } from '../../flow/common/change.ts'
|
|
3
2
|
import type { TriggerConfigOption } from '../common/configOptions.ts'
|
|
4
3
|
import type {
|
|
5
4
|
IntegrationDefinition,
|
|
@@ -8,6 +7,7 @@ import type {
|
|
|
8
7
|
IntegrationStateContext,
|
|
9
8
|
ListenerPage,
|
|
10
9
|
} from '../common/integration.ts'
|
|
10
|
+
import type { TriggerKeySnapshot } from '../common/model.ts'
|
|
11
11
|
import type { PollDefinition, PollResult } from '../common/poll.ts'
|
|
12
12
|
|
|
13
13
|
import { isJsonObject } from '../../base/common/json.ts'
|
package/src/types/index.ts
CHANGED
|
@@ -22,21 +22,56 @@ export interface ArtifactRef {
|
|
|
22
22
|
readonly kind: 'artifact'
|
|
23
23
|
readonly id: string
|
|
24
24
|
readonly name: string
|
|
25
|
-
readonly mediaType
|
|
25
|
+
readonly mediaType?: string
|
|
26
26
|
readonly size: number
|
|
27
27
|
readonly digest: string
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export interface ArtifactPutOptions {
|
|
31
31
|
readonly name: string
|
|
32
|
-
readonly mediaType?: string
|
|
32
|
+
readonly mediaType?: string
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
/** Strings are stored as UTF-8. */
|
|
36
|
+
export type ArtifactData = string | Uint8Array
|
|
36
37
|
|
|
38
|
+
/** Artifacts belong to the current Run; an ArtifactRef is JSON and can be passed to downstream nodes. */
|
|
37
39
|
export interface ArtifactCapability {
|
|
38
40
|
put(data: ArtifactData, options: ArtifactPutOptions): Promise<ArtifactRef>
|
|
39
|
-
open(ref: ArtifactRef): Promise<
|
|
41
|
+
open(ref: ArtifactRef): Promise<Uint8Array>
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TaskFetchInit {
|
|
45
|
+
/** GET, HEAD, POST, PUT, PATCH, DELETE or OPTIONS. */
|
|
46
|
+
readonly method?: string
|
|
47
|
+
readonly headers?: Readonly<Record<string, string>> | Iterable<readonly [string, string]>
|
|
48
|
+
/** At most 1 MiB; strings are sent as UTF-8 text/plain unless a content-type header is supplied. */
|
|
49
|
+
readonly body?: string | Uint8Array | ArrayBuffer
|
|
50
|
+
readonly signal?: AbortSignal
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface TaskFetchHeaders extends Iterable<[string, string]> {
|
|
54
|
+
get(name: string): string | null
|
|
55
|
+
has(name: string): boolean
|
|
56
|
+
entries(): IterableIterator<[string, string]>
|
|
57
|
+
keys(): IterableIterator<string>
|
|
58
|
+
values(): IterableIterator<string>
|
|
59
|
+
forEach(callback: (value: string, name: string) => void): void
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** A read-only subset of the Fetch Response; the body is buffered, at most 1 MiB, and readable once. */
|
|
63
|
+
export interface TaskFetchResponse {
|
|
64
|
+
readonly status: number
|
|
65
|
+
readonly statusText: string
|
|
66
|
+
readonly ok: boolean
|
|
67
|
+
readonly redirected: false
|
|
68
|
+
readonly url: string
|
|
69
|
+
readonly headers: TaskFetchHeaders
|
|
70
|
+
readonly bodyUsed: boolean
|
|
71
|
+
bytes(): Promise<Uint8Array>
|
|
72
|
+
arrayBuffer(): Promise<ArrayBuffer>
|
|
73
|
+
text(): Promise<string>
|
|
74
|
+
json(): Promise<unknown>
|
|
40
75
|
}
|
|
41
76
|
|
|
42
77
|
export interface MediaPreviewPayload {
|
|
@@ -73,13 +108,6 @@ export interface CsvPreviewPayload {
|
|
|
73
108
|
export type PreviewPayload = MediaPreviewPayload | TextPreviewPayload | ImagePreviewPayload | TablePreviewPayload | CsvPreviewPayload
|
|
74
109
|
export type PreviewType = PreviewPayload['type']
|
|
75
110
|
|
|
76
|
-
export interface TaskLogger {
|
|
77
|
-
debug(message: string, data?: unknown): void
|
|
78
|
-
info(message: string, data?: unknown): void
|
|
79
|
-
warn(message: string, data?: unknown): void
|
|
80
|
-
error(message: string, data?: unknown): void
|
|
81
|
-
}
|
|
82
|
-
|
|
83
111
|
export interface TaskContext<
|
|
84
112
|
Actions extends object = Record<string, never>,
|
|
85
113
|
Workflows extends object = { call: (name: never, input?: never) => Promise<never> },
|
|
@@ -91,13 +119,25 @@ export interface TaskContext<
|
|
|
91
119
|
readonly inputs: Readonly<Record<string, unknown>>
|
|
92
120
|
readonly runId: string
|
|
93
121
|
readonly blockId: string
|
|
122
|
+
readonly state: {
|
|
123
|
+
get(key: string): Promise<JsonValue | null>
|
|
124
|
+
set(key: string, value: JsonValue): Promise<void>
|
|
125
|
+
insert(key: string, value: Exclude<JsonValue, null>): Promise<boolean>
|
|
126
|
+
delete(key: string): Promise<void>
|
|
127
|
+
list(): Promise<string[]>
|
|
128
|
+
}
|
|
129
|
+
readonly dedupe: {
|
|
130
|
+
insert(ids: readonly (string | number)[]): Promise<boolean[]>
|
|
131
|
+
has(ids: readonly (string | number)[]): Promise<boolean[]>
|
|
132
|
+
}
|
|
94
133
|
readonly artifact: ArtifactCapability
|
|
95
|
-
readonly fetch: typeof fetch
|
|
96
|
-
readonly logger: TaskLogger
|
|
97
134
|
/** Reads the selected direct predecessors, sorted by node ID; empty for a graph root. */
|
|
98
135
|
getPrevious(): Promise<readonly PreviousNode[]>
|
|
99
|
-
|
|
100
|
-
|
|
136
|
+
/**
|
|
137
|
+
* Calls an HTTP(S) URL whose host the node declares in its `http` Capability. Redirects are returned, not followed,
|
|
138
|
+
* and no platform credentials are added. Network failures reject with a TypeError.
|
|
139
|
+
*/
|
|
140
|
+
fetch(input: string | URL, init?: TaskFetchInit): Promise<TaskFetchResponse>
|
|
101
141
|
}
|
|
102
142
|
|
|
103
143
|
export type TaskResult<Outputs extends object> = keyof Outputs extends never ? Outputs | void : Outputs
|
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
TaskNode,
|
|
3
|
-
ManagedTaskDefinition,
|
|
4
|
-
Port,
|
|
5
|
-
JsonValue,
|
|
6
|
-
RevisionContent,
|
|
7
|
-
GraphNode,
|
|
8
|
-
WorkflowCapability,
|
|
9
|
-
AgentWorkflowTool,
|
|
10
|
-
} from '../../flow/common/change.ts'
|
|
1
|
+
import type { TaskNode, ManagedTaskDefinition, Port, JsonValue, RevisionContent, WorkflowCapability, AgentWorkflowTool } from '../../flow/common/change.ts'
|
|
11
2
|
|
|
12
3
|
import { dequal } from 'dequal/lite'
|
|
13
4
|
import { z } from 'zod'
|
|
14
5
|
import { comparePorts, matchesSchema } from '../../flow/common/schema.ts'
|
|
15
6
|
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
readonly flowId: string
|
|
19
|
-
readonly revisionId: string
|
|
20
|
-
}
|
|
7
|
+
export type { WorkflowExecutor } from '../../flow/common/model.ts'
|
|
8
|
+
export { nodeWorkflowReferences } from '../../flow/common/workflowReferences.ts'
|
|
21
9
|
|
|
22
10
|
const portSchema = z.object({
|
|
23
11
|
handle: z.string().min(1),
|
|
@@ -183,35 +171,6 @@ export const workflowReferencesSchema = z.object({
|
|
|
183
171
|
})
|
|
184
172
|
export type WorkflowReferences = z.infer<typeof workflowReferencesSchema>
|
|
185
173
|
|
|
186
|
-
/** Workflow declarations share discovery across authoring, admission and resource indexing. */
|
|
187
|
-
export function nodeWorkflowReferences(node: GraphNode): readonly {
|
|
188
|
-
readonly target: Omit<WorkflowCapability, 'kind' | 'name'>
|
|
189
|
-
readonly path: string
|
|
190
|
-
readonly tool?: string
|
|
191
|
-
}[] {
|
|
192
|
-
if (node.kind != 'task') return []
|
|
193
|
-
if ('moduleId' in node.task)
|
|
194
|
-
return (node.task.capabilities ?? []).flatMap((item, index) =>
|
|
195
|
-
item.kind == 'workflow' ? [{ target: item, path: `task/capabilities/${index}`, tool: item.name }] : [],
|
|
196
|
-
)
|
|
197
|
-
if (node.task.executor.kind == 'workflow')
|
|
198
|
-
return [
|
|
199
|
-
{
|
|
200
|
-
target: {
|
|
201
|
-
...node.task.executor,
|
|
202
|
-
inputs: node.task.inputs.filter((port) => 'handle' in port),
|
|
203
|
-
outputs: node.task.outputs.filter((port) => 'handle' in port),
|
|
204
|
-
},
|
|
205
|
-
path: 'task/executor',
|
|
206
|
-
},
|
|
207
|
-
]
|
|
208
|
-
if (node.task.executor.kind == 'agent')
|
|
209
|
-
return node.task.executor.tools.flatMap((item, index) =>
|
|
210
|
-
item.kind == 'workflow' ? [{ target: item, path: `task/executor/tools/${index}`, tool: item.name }] : [],
|
|
211
|
-
)
|
|
212
|
-
return []
|
|
213
|
-
}
|
|
214
|
-
|
|
215
174
|
export function workflowCapability(name: string, target: WorkflowContract | WorkflowCandidate): WorkflowCapability {
|
|
216
175
|
return {
|
|
217
176
|
kind: 'workflow',
|