@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.
Files changed (146) hide show
  1. package/README.md +3 -1
  2. package/dist/browser/{ContentIcon-FCpKiwjp.js → ContentIcon-VZQO8VZU.js} +9 -6
  3. package/dist/browser/{FlowCanvas-dzRBnlUT.js → FlowCanvas-BNGDMaWr.js} +75 -63
  4. package/dist/browser/{createResourceDialog-BeYDdUl-.js → createResourceDialog-BmtakfVg.js} +63 -57
  5. package/dist/browser/{dialog-ZtpsFbl4.js → dialog-BcFBfRB1.js} +126 -32
  6. package/dist/browser/{exportSvg-DqXw1yf-.js → exportSvg-CG8hEUNk.js} +2 -2
  7. package/dist/browser/{flowChanges-b_5UobFj.js → flowChanges-BGo_4Ypo.js} +6063 -11285
  8. package/dist/browser/{flowWorkspace-ItojZbO4.js → flowWorkspace-BFHGev7s.js} +1065 -2570
  9. package/dist/browser/preview.css +626 -653
  10. package/dist/browser/preview.js +1907 -9663
  11. package/dist/browser/theme.css +117 -263
  12. package/dist/browser/{tooltip-wZJRB9oP.js → tooltip-BAWSUexx.js} +2 -2
  13. package/dist/browser/{typeScriptSession-CSRwWmZm.js → typeScriptSession-CRoezJPN.js} +1 -1
  14. package/dist/browser/ui.css +464 -463
  15. package/dist/browser/ui.js +4 -3
  16. package/dist/browser/workbench.css +9891 -9897
  17. package/dist/browser/workbench.d.ts +6 -2
  18. package/dist/browser/workbench.js +838 -499
  19. package/dist/browser/{workbenchSelect-M441Lz0d.js → workbenchSelect-CjK-xdm3.js} +2 -2
  20. package/dist/command/index.js +48 -50
  21. package/dist/common/authoring.d.ts +5 -5
  22. package/dist/common/authoringExamples.d.ts +3 -2
  23. package/dist/common/authoringSchema.d.ts +26 -4
  24. package/dist/common/connector-proxy.d.ts +1 -1
  25. package/dist/common/control-api-conformance.js +352 -144
  26. package/dist/common/control-api-errors.d.ts +12 -0
  27. package/dist/common/control-api.d.ts +38 -4
  28. package/dist/common/control-api.js +373 -162
  29. package/dist/common/control-requests.d.ts +22 -3
  30. package/dist/common/control-requests.js +268 -28
  31. package/dist/common/cron-trigger.d.ts +2 -1
  32. package/dist/common/decision.d.ts +4 -4
  33. package/dist/common/decision.js +5 -2
  34. package/dist/common/engine-contract.d.ts +6 -1
  35. package/dist/common/event-source-definition.d.ts +1 -1
  36. package/dist/common/event-source-view.d.ts +22 -0
  37. package/dist/common/event-source.d.ts +1 -1
  38. package/dist/common/event-source.js +24 -65
  39. package/dist/common/event-sources.d.ts +0 -21
  40. package/dist/common/flow-authoring.js +90 -20
  41. package/dist/common/flow-change-schema.d.ts +9 -1
  42. package/dist/common/flow-change.d.ts +8 -202
  43. package/dist/common/flow-change.js +96 -24
  44. package/dist/common/flow-encoding.js +89 -21
  45. package/dist/common/flow-input-value.d.ts +1 -1
  46. package/dist/common/flow-model.d.ts +121 -0
  47. package/dist/common/flow-notifications.d.ts +1 -1
  48. package/dist/common/flow-retry.d.ts +2 -4
  49. package/dist/common/flow-semantics.js +161 -80
  50. package/dist/common/flow-state.d.ts +123 -0
  51. package/dist/common/flow-state.js +5852 -0
  52. package/dist/common/flow-workflowReferences.d.ts +8 -0
  53. package/dist/common/flowInspection.d.ts +1 -1
  54. package/dist/common/http-hosts.d.ts +6 -0
  55. package/dist/common/http-request.d.ts +13 -5
  56. package/dist/common/http-request.js +23 -5
  57. package/dist/common/integration-trigger.d.ts +2 -1
  58. package/dist/common/mcp.d.ts +96 -3
  59. package/dist/common/mcp.js +355 -142
  60. package/dist/common/openapi.d.ts +4 -4
  61. package/dist/common/openapi.js +4 -1
  62. package/dist/common/poll-trigger.d.ts +2 -1
  63. package/dist/common/run-events.js +1 -0
  64. package/dist/common/runtime-contract.d.ts +37 -1
  65. package/dist/common/runtime-contract.js +717 -72
  66. package/dist/common/runtime-nodejs-compat.d.ts +44 -0
  67. package/dist/common/runtime-nodejs-compat.js +5870 -0
  68. package/dist/common/scheduler-checkpoint.d.ts +1 -1
  69. package/dist/common/scheduler-node-execution.d.ts +2 -1
  70. package/dist/common/scheduler.d.ts +2 -2
  71. package/dist/common/scheduler.js +76 -6
  72. package/dist/common/state-conformance.d.ts +16 -0
  73. package/dist/common/tags.d.ts +4 -0
  74. package/dist/common/trigger-catalog.d.ts +1 -7
  75. package/dist/common/trigger-config-options.d.ts +1 -1
  76. package/dist/common/trigger-config.d.ts +1 -1
  77. package/dist/common/trigger-contract.d.ts +2 -1
  78. package/dist/common/trigger-localization.d.ts +8 -2
  79. package/dist/common/trigger-model.d.ts +101 -0
  80. package/dist/common/webhook-response.d.ts +1 -1
  81. package/dist/common/webhook-trigger.d.ts +3 -1
  82. package/dist/common/workflow.d.ts +3 -12
  83. package/dist/common/workflow.js +25 -23
  84. package/dist/index.d.ts +52 -14
  85. package/package.json +9 -1
  86. package/src/base/common/json.ts +1 -1
  87. package/src/connector/common/proxy.ts +1 -1
  88. package/src/control/common/api.ts +90 -16
  89. package/src/control/common/authoring.ts +6 -1
  90. package/src/control/common/authoringExamples.ts +8 -0
  91. package/src/control/common/authoringSchema.ts +26 -13
  92. package/src/control/common/authoringTools.ts +1 -1
  93. package/src/control/common/conformance.ts +1 -1
  94. package/src/control/common/connectorDecoders.ts +1 -1
  95. package/src/control/common/errors.ts +6 -0
  96. package/src/control/common/eventSources.ts +2 -29
  97. package/src/control/common/flowDecoders.ts +1 -0
  98. package/src/control/common/mcp.ts +16 -0
  99. package/src/control/common/requests.ts +10 -2
  100. package/src/control/common/tags.ts +20 -0
  101. package/src/control/common/triggerCatalog.ts +1 -8
  102. package/src/decision/common/decision.ts +15 -7
  103. package/src/event-source/common/definition.ts +1 -1
  104. package/src/event-source/common/index.ts +1 -1
  105. package/src/event-source/common/view.ts +20 -0
  106. package/src/execution/common/checkpoint.ts +1 -1
  107. package/src/execution/common/events.ts +3 -0
  108. package/src/execution/common/node-execution.ts +4 -1
  109. package/src/execution/common/scheduler.ts +3 -1
  110. package/src/flow/common/change.ts +60 -193
  111. package/src/flow/common/changeSchema.ts +39 -4
  112. package/src/flow/common/connectionUsage.ts +1 -1
  113. package/src/flow/common/errorHandling.ts +1 -1
  114. package/src/flow/common/inputValue.ts +1 -1
  115. package/src/flow/common/model.ts +130 -0
  116. package/src/flow/common/modules.ts +1 -1
  117. package/src/flow/common/portChanges.ts +1 -1
  118. package/src/flow/common/retry.ts +2 -4
  119. package/src/flow/common/semantics.ts +31 -4
  120. package/src/flow/common/webhookResponse.ts +1 -1
  121. package/src/flow/common/workflowReferences.ts +31 -0
  122. package/src/http/common/authBindings.ts +6 -9
  123. package/src/http/common/hosts.ts +13 -0
  124. package/src/http/common/httpRequest.ts +30 -15
  125. package/src/openapi/common/openapi.ts +9 -5
  126. package/src/{execution → runtime}/common/engineContract.ts +23 -15
  127. package/src/runtime/common/nodejsCompat.ts +275 -0
  128. package/src/runtime/common/nodejsGuest.generated.json +47 -0
  129. package/src/runtime/common/runtime.ts +1254 -0
  130. package/src/state/common/conformance.ts +86 -0
  131. package/src/state/common/state.ts +169 -0
  132. package/src/trigger/common/config.ts +1 -1
  133. package/src/trigger/common/configOptions.ts +1 -1
  134. package/src/trigger/common/contract.ts +2 -1
  135. package/src/trigger/common/cron.ts +2 -1
  136. package/src/trigger/common/integration.ts +2 -1
  137. package/src/trigger/common/model.ts +87 -0
  138. package/src/trigger/common/poll.ts +2 -1
  139. package/src/trigger/common/webhook.ts +3 -1
  140. package/src/trigger/providers/fieldDescriptions.ts +1 -1
  141. package/src/trigger/providers/localization.ts +9 -2
  142. package/src/trigger/providers/remote.ts +1 -1
  143. package/src/types/index.ts +55 -15
  144. package/src/workflow/common/workflow.ts +3 -44
  145. package/src/execution/common/runtime.ts +0 -563
  146. /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/change.ts'
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,5 +1,5 @@
1
1
  import type { ConnectorProxy } from '../../connector/common/proxy.ts'
2
- import type { JsonValue } from '../../flow/common/change.ts'
2
+ import type { JsonValue } from '../../flow/common/model.ts'
3
3
 
4
4
  export interface TriggerConfigOption {
5
5
  readonly value: string
@@ -1,4 +1,5 @@
1
- import type { JsonValue, Port, PortDefinition, TriggerNode } from '../../flow/common/change.ts'
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,4 +1,5 @@
1
- import type { JsonValue, TriggerSchedule } from '../../flow/common/change.ts'
1
+ import type { JsonValue } from '../../flow/common/model.ts'
2
+ import type { TriggerSchedule } from './model.ts'
2
3
 
3
4
  import { Cron } from 'croner'
4
5
 
@@ -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 { IntegrationEndpointMethod, JsonValue, TriggerKeySnapshot } from '../../flow/common/change.ts'
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, TriggerKeySnapshot, TriggerSchedule } from '../../flow/common/change.ts'
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, WebhookBodyField, WebhookMethod, WebhookOptions } from '../../flow/common/change.ts'
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,4 +1,4 @@
1
- import type { TriggerDisplay } from '../../control/common/triggerCatalog.ts'
1
+ import type { TriggerDisplay } from './localization.ts'
2
2
 
3
3
  type FieldDescriptions = Pick<TriggerDisplay, 'configInputs' | 'outputs'>
4
4
 
@@ -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'
@@ -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: string | undefined
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 | undefined
32
+ readonly mediaType?: string
33
33
  }
34
34
 
35
- export type ArtifactData = Uint8Array | Blob | ReadableStream<Uint8Array>
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<ReadableStream<Uint8Array>>
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
- preview(payload: PreviewPayload, id?: string): Promise<void>
100
- reportProgress(progress: number): Promise<void>
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 interface WorkflowExecutor {
17
- readonly kind: 'workflow'
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',