@oomol-lab/open-flow 0.1.0-beta.75 → 0.1.0-beta.76
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 +2 -1
- package/dist/browser/{ContentIcon-FCpKiwjp.js → ContentIcon-Cm1hbVzW.js} +2 -2
- package/dist/browser/{FlowCanvas-dzRBnlUT.js → FlowCanvas-NzKtla_c.js} +62 -62
- package/dist/browser/{createResourceDialog-BeYDdUl-.js → createResourceDialog-BgdedQqu.js} +3 -3
- package/dist/browser/{dialog-ZtpsFbl4.js → dialog-aSElFFw2.js} +49 -24
- package/dist/browser/{exportSvg-DqXw1yf-.js → exportSvg-Dcz4HTo_.js} +2 -2
- package/dist/browser/{flowChanges-b_5UobFj.js → flowChanges-NZyS7HSp.js} +207 -396
- package/dist/browser/{flowWorkspace-ItojZbO4.js → flowWorkspace-Bl0SDehy.js} +90 -13
- package/dist/browser/preview.css +515 -649
- package/dist/browser/preview.js +454 -545
- package/dist/browser/theme.css +114 -262
- package/dist/browser/{tooltip-wZJRB9oP.js → tooltip-3XMK9jkr.js} +1 -1
- package/dist/browser/{typeScriptSession-CSRwWmZm.js → typeScriptSession-C9sBGOKZ.js} +1 -1
- package/dist/browser/ui.css +354 -460
- package/dist/browser/ui.js +1 -1
- package/dist/browser/workbench.css +515 -649
- package/dist/browser/workbench.js +7 -7
- package/dist/browser/{workbenchSelect-M441Lz0d.js → workbenchSelect-DBGP7Y4Z.js} +2 -2
- package/dist/command/index.js +33 -35
- package/dist/common/authoring.d.ts +3 -3
- package/dist/common/authoringSchema.d.ts +3 -0
- package/dist/common/connector-proxy.d.ts +1 -1
- package/dist/common/control-api-conformance.js +197 -136
- package/dist/common/control-api.d.ts +3 -2
- package/dist/common/control-api.js +192 -131
- package/dist/common/control-requests.js +126 -24
- 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 +5 -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 +40 -18
- package/dist/common/flow-change-schema.d.ts +9 -1
- package/dist/common/flow-change.d.ts +7 -201
- package/dist/common/flow-change.js +46 -22
- package/dist/common/flow-encoding.js +40 -20
- 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 +83 -86
- 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.js +195 -134
- 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/runtime-contract.d.ts +23 -1
- package/dist/common/runtime-contract.js +604 -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 +1 -1
- package/dist/common/scheduler.d.ts +1 -1
- package/dist/common/scheduler.js +25 -4
- 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 +41 -14
- package/package.json +5 -1
- package/src/base/common/json.ts +1 -1
- package/src/connector/common/proxy.ts +1 -1
- package/src/control/common/api.ts +6 -5
- package/src/control/common/authoringSchema.ts +9 -2
- 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/eventSources.ts +2 -29
- 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/node-execution.ts +1 -1
- package/src/execution/common/scheduler.ts +1 -1
- package/src/flow/common/change.ts +58 -192
- package/src/flow/common/changeSchema.ts +29 -4
- package/src/flow/common/connectionUsage.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 +11 -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 +20 -15
- package/src/runtime/common/nodejsCompat.ts +275 -0
- package/src/runtime/common/nodejsGuest.generated.json +47 -0
- package/src/runtime/common/runtime.ts +1138 -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 +44 -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,130 @@
|
|
|
1
|
+
import type { JsonValue, Port, PortDefinition } from '../../types/index.ts'
|
|
2
|
+
|
|
3
|
+
export type { JsonValue, Port, PortDefinition } from '../../types/index.ts'
|
|
4
|
+
|
|
5
|
+
/** Primitives shared by every node family. Node unions and graph operations belong to change.ts. */
|
|
6
|
+
|
|
7
|
+
export interface RetryConfiguration {
|
|
8
|
+
readonly maxRetries: number
|
|
9
|
+
readonly intervalMs: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface InputPortDefinition extends PortDefinition {
|
|
13
|
+
readonly value?: JsonValue
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface InputPort extends InputPortDefinition {
|
|
17
|
+
readonly handle: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Group {
|
|
21
|
+
readonly collapsed?: boolean
|
|
22
|
+
readonly group: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function portsByHandle<Value extends Port>(ports: readonly (Value | Group)[]): Readonly<Record<string, Value>> {
|
|
26
|
+
return Object.fromEntries(ports.flatMap((port) => ('handle' in port ? [[port.handle, port]] : [])))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface NodeSource {
|
|
30
|
+
readonly system?: true
|
|
31
|
+
readonly field?: string
|
|
32
|
+
readonly kind: 'node'
|
|
33
|
+
readonly nodeId: string
|
|
34
|
+
readonly output: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface BindingSource {
|
|
38
|
+
readonly bindingId: string
|
|
39
|
+
readonly kind: 'binding'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export type FixedInputValue = { readonly kind: 'unset' } | { readonly kind: 'value'; readonly value: JsonValue }
|
|
43
|
+
export type InputValues = Readonly<Record<string, FixedInputValue>>
|
|
44
|
+
|
|
45
|
+
export type InputMapping = FixedInputValue | { readonly kind: 'sources'; readonly sources: readonly (BindingSource | NodeSource)[] }
|
|
46
|
+
|
|
47
|
+
export interface GraphNodeBase {
|
|
48
|
+
readonly description?: string
|
|
49
|
+
readonly icon?: string
|
|
50
|
+
readonly inputs: Readonly<Record<string, InputMapping>>
|
|
51
|
+
readonly name?: string
|
|
52
|
+
readonly maxExecutions?: number
|
|
53
|
+
readonly timeoutMs?: number
|
|
54
|
+
readonly retry?: RetryConfiguration
|
|
55
|
+
readonly onError?: 'stop' | 'error'
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export type Source = BindingSource | NodeSource
|
|
59
|
+
|
|
60
|
+
export interface WorkflowCapability {
|
|
61
|
+
readonly kind: 'workflow'
|
|
62
|
+
readonly name: string
|
|
63
|
+
readonly flowId: string
|
|
64
|
+
readonly revisionId: string
|
|
65
|
+
readonly inputs: readonly Port[]
|
|
66
|
+
readonly outputs: readonly Port[] | null
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Hosts that Code may call through `context.fetch`; requires Flow model 12 and Engine v9. */
|
|
70
|
+
export interface HttpCapability {
|
|
71
|
+
readonly kind: 'http'
|
|
72
|
+
readonly hosts: readonly string[]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type TaskCapability = ConnectorCapability | WorkflowCapability | HttpCapability
|
|
76
|
+
|
|
77
|
+
export interface ConnectorAccessCapability {
|
|
78
|
+
readonly actionHints?: readonly string[]
|
|
79
|
+
readonly connectionHints?: readonly {
|
|
80
|
+
readonly action: string
|
|
81
|
+
readonly connectionId: string
|
|
82
|
+
readonly alias?: string
|
|
83
|
+
}[]
|
|
84
|
+
readonly kind: 'connector'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface ConnectorActionCapability {
|
|
88
|
+
readonly action: string
|
|
89
|
+
readonly connections: readonly { readonly connectionId: string; readonly alias?: string }[]
|
|
90
|
+
readonly connectionId?: string
|
|
91
|
+
readonly kind: 'connector'
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type ConnectorPermissionCapability =
|
|
95
|
+
| { readonly kind: 'connector'; readonly mode: 'shared' }
|
|
96
|
+
| { readonly kind: 'connector'; readonly mode: 'independent'; readonly actions: readonly { readonly action: string; readonly connectionId?: string }[] }
|
|
97
|
+
|
|
98
|
+
export type ConnectorCapability = ConnectorAccessCapability | ConnectorActionCapability | ConnectorPermissionCapability
|
|
99
|
+
|
|
100
|
+
export interface TaskDefinitionBase {
|
|
101
|
+
readonly inputs: readonly (InputPort | Group)[]
|
|
102
|
+
readonly name: string
|
|
103
|
+
readonly outputs: readonly (Port | Group)[]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface InlineTaskDefinition extends TaskDefinitionBase {
|
|
107
|
+
readonly capabilities?: readonly TaskCapability[]
|
|
108
|
+
readonly moduleId: string
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The executor shape every managed Task shares; each node family narrows it with its own executor. */
|
|
112
|
+
export interface TaskExecutor {
|
|
113
|
+
readonly kind: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ManagedTaskDefinitionOf<Executor extends TaskExecutor> extends TaskDefinitionBase {
|
|
117
|
+
readonly executor: Executor
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface WorkflowExecutor {
|
|
121
|
+
readonly kind: 'workflow'
|
|
122
|
+
readonly flowId: string
|
|
123
|
+
readonly revisionId: string
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export interface CodeModule {
|
|
127
|
+
readonly imports: readonly string[]
|
|
128
|
+
readonly name: string
|
|
129
|
+
readonly source: string
|
|
130
|
+
}
|
package/src/flow/common/retry.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import type { GraphNode } from './change.ts'
|
|
2
|
+
import type { RetryConfiguration } from './model.ts'
|
|
2
3
|
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
readonly maxRetries: number
|
|
7
|
-
readonly intervalMs: number
|
|
8
|
-
}
|
|
6
|
+
export type { RetryConfiguration } from './model.ts'
|
|
9
7
|
|
|
10
8
|
export const retryNodeTypes = ['code', 'connector', 'openapi', 'http-request', 'llm', 'decision'] as const
|
|
11
9
|
export const nodeRetryLimits = { maxRetries: 5, minIntervalMs: 1000, maxIntervalMs: 60000, intervalStepMs: 1000 } as const
|
|
@@ -2,19 +2,19 @@ import { dequal } from 'dequal/lite'
|
|
|
2
2
|
import { decisionTaskIssues } from '../../decision/common/decision.ts'
|
|
3
3
|
import { httpRequestIssues, httpAuthHandles, httpRequestTask, httpRequestUrl } from '../../http/common/httpRequest.ts'
|
|
4
4
|
import { openApiIssues, authHandles } from '../../openapi/common/openapi.ts'
|
|
5
|
-
import { nodeWorkflowReferences } from '../../workflow/common/workflow.ts'
|
|
6
5
|
import { isTriggerNode } from './change.ts'
|
|
6
|
+
import { nodeWorkflowReferences } from './workflowReferences.ts'
|
|
7
7
|
export { renderPrompt } from './promptTemplate.ts'
|
|
8
8
|
export { connectionUsage, removeConnectionUsage } from './connectionUsage.ts'
|
|
9
9
|
import { matchesTriggerOutputs, normalizeTriggerOutputs } from '../../trigger/common/contract.ts'
|
|
10
10
|
import { nodeInputMappings } from './condition.ts'
|
|
11
11
|
import { connectionUsage } from './connectionUsage.ts'
|
|
12
12
|
export { matchesTriggerOutputs, normalizeTriggerOutputs, triggerOutputDefinitions, triggerOutputPorts } from '../../trigger/common/contract.ts'
|
|
13
|
-
import type { EngineContract } from '../../
|
|
14
|
-
import type { RuntimeProgram } from '../../
|
|
13
|
+
import type { EngineContract } from '../../runtime/common/engineContract.ts'
|
|
14
|
+
import type { RuntimeProgram } from '../../runtime/common/runtime.ts'
|
|
15
15
|
import type { ConnectorActionCapability, ConnectorCapability, Graph, InputMapping, JsonValue, Port, RevisionContent, SchemaMismatch } from './change.ts'
|
|
16
16
|
|
|
17
|
-
import { findEngineContract } from '../../
|
|
17
|
+
import { findEngineContract } from '../../runtime/common/engineContract.ts'
|
|
18
18
|
import { agentConfigIssues } from './agent.ts'
|
|
19
19
|
import { decodeTaskCapabilities } from './change.ts'
|
|
20
20
|
import { canonicalJsonBytes, canonicalModule, canonicalRevisionGraph, digestBytes } from './encoding.ts'
|
|
@@ -445,6 +445,13 @@ export async function prepareFlow(revision: RevisionContent, engineContract: str
|
|
|
445
445
|
Object.values(revision.document.graph.nodes).some((node) => node.kind == 'task' && 'executor' in node.task && node.task.executor.kind == 'http-request')
|
|
446
446
|
)
|
|
447
447
|
return { kind: 'engine-unsupported' }
|
|
448
|
+
if (
|
|
449
|
+
!engine.codeFetch &&
|
|
450
|
+
Object.values(revision.document.graph.nodes).some(
|
|
451
|
+
(node) => node.kind == 'task' && 'moduleId' in node.task && node.task.capabilities?.some((item) => item.kind == 'http'),
|
|
452
|
+
)
|
|
453
|
+
)
|
|
454
|
+
return { kind: 'engine-unsupported' }
|
|
448
455
|
if (!engine.workflowTools && Object.values(revision.document.graph.nodes).some((node) => nodeWorkflowReferences(node).some((ref) => ref.tool != null)))
|
|
449
456
|
return { kind: 'engine-unsupported' }
|
|
450
457
|
if (triggerId != null) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { GraphNode } from './change.ts'
|
|
2
|
+
import type { WorkflowCapability } from './model.ts'
|
|
3
|
+
|
|
4
|
+
/** Workflow declarations share discovery across authoring, admission and resource indexing. */
|
|
5
|
+
export function nodeWorkflowReferences(node: GraphNode): readonly {
|
|
6
|
+
readonly target: Omit<WorkflowCapability, 'kind' | 'name'>
|
|
7
|
+
readonly path: string
|
|
8
|
+
readonly tool?: string
|
|
9
|
+
}[] {
|
|
10
|
+
if (node.kind != 'task') return []
|
|
11
|
+
if ('moduleId' in node.task)
|
|
12
|
+
return (node.task.capabilities ?? []).flatMap((item, index) =>
|
|
13
|
+
item.kind == 'workflow' ? [{ target: item, path: `task/capabilities/${index}`, tool: item.name }] : [],
|
|
14
|
+
)
|
|
15
|
+
if (node.task.executor.kind == 'workflow')
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
target: {
|
|
19
|
+
...node.task.executor,
|
|
20
|
+
inputs: node.task.inputs.filter((port) => 'handle' in port),
|
|
21
|
+
outputs: node.task.outputs.filter((port) => 'handle' in port),
|
|
22
|
+
},
|
|
23
|
+
path: 'task/executor',
|
|
24
|
+
},
|
|
25
|
+
]
|
|
26
|
+
if (node.task.executor.kind == 'agent')
|
|
27
|
+
return node.task.executor.tools.flatMap((item, index) =>
|
|
28
|
+
item.kind == 'workflow' ? [{ target: item, path: `task/executor/tools/${index}`, tool: item.name }] : [],
|
|
29
|
+
)
|
|
30
|
+
return []
|
|
31
|
+
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InputMapping } from '../../flow/common/model.ts'
|
|
2
2
|
|
|
3
|
-
/** Never allow credentials to enter a Revision as literal node
|
|
4
|
-
export function assertHttpAuthBindings(
|
|
5
|
-
for (const
|
|
6
|
-
if (
|
|
7
|
-
|
|
8
|
-
if (handle.startsWith('auth.') && mapping.kind == 'value')
|
|
9
|
-
throw new TypeError('HTTP authentication accepts deployment variables and upstream outputs only.')
|
|
10
|
-
}
|
|
3
|
+
/** Never allow credentials to enter a Revision as literal inputs of an OpenAPI or HTTP request node. */
|
|
4
|
+
export function assertHttpAuthBindings(inputs: Readonly<Record<string, InputMapping>>): void {
|
|
5
|
+
for (const [handle, mapping] of Object.entries(inputs)) {
|
|
6
|
+
if (handle.startsWith('auth.') && mapping.kind == 'value')
|
|
7
|
+
throw new TypeError('HTTP authentication accepts deployment variables and upstream outputs only.')
|
|
11
8
|
}
|
|
12
9
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Hosts of a Code `http` Capability: lowercase DNS names or IPv4 addresses; `*.example.com` matches subdomains only. */
|
|
2
|
+
export const httpHostPattern = /^(?:\*\.)?(?=.{1,253}$)(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)*[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/
|
|
3
|
+
export const maxHttpHosts = 20
|
|
4
|
+
|
|
5
|
+
/** Wildcards must name a registrable suffix, so `*.com` is rejected. */
|
|
6
|
+
export function isHttpHost(value: string): boolean {
|
|
7
|
+
return httpHostPattern.test(value) && (!value.startsWith('*.') || value.slice(2).includes('.'))
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function httpHostAllowed(hosts: readonly string[], hostname: string): boolean {
|
|
11
|
+
const host = hostname.toLowerCase()
|
|
12
|
+
return hosts.some((pattern) => (pattern.startsWith('*.') ? host.endsWith(pattern.slice(1)) : host == pattern))
|
|
13
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import type { InputMapping, InputPort, JsonValue,
|
|
1
|
+
import type { InputMapping, InputPort, JsonValue, ManagedTaskDefinitionOf, TaskDefinitionBase, TaskExecutor } from '../../flow/common/model.ts'
|
|
2
2
|
|
|
3
3
|
import { dequal } from 'dequal/lite'
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
import { remapInputMappings, renamedPort } from '../../flow/common/portChanges.ts'
|
|
6
6
|
import { httpHeaderNameIssue } from './headers.ts'
|
|
7
7
|
|
|
8
|
+
export { httpHostAllowed, isHttpHost, maxHttpHosts } from './hosts.ts'
|
|
9
|
+
|
|
8
10
|
const parameterSchema = z.strictObject({ mode: z.enum(['fields', 'object']).default('fields'), names: z.array(z.string()).default([]) })
|
|
9
11
|
export const httpRequestConfigSchema = z.strictObject({
|
|
10
12
|
method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']).default('GET'),
|
|
@@ -35,10 +37,10 @@ export function httpAuthHandles(config: HttpRequestConfig): string[] {
|
|
|
35
37
|
return config.authentication.type == 'none' ? [] : config.authentication.type == 'basic' ? ['auth.username', 'auth.password'] : ['auth.token']
|
|
36
38
|
}
|
|
37
39
|
// Preserve metadata on ports whose contracts did not change.
|
|
38
|
-
function retain(ports: readonly InputPort[], old:
|
|
40
|
+
function retain(ports: readonly InputPort[], old: TaskDefinitionBase['inputs']): TaskDefinitionBase['inputs'] {
|
|
39
41
|
const remaining = new Map(ports.map((port) => [port.handle, port]))
|
|
40
42
|
return [
|
|
41
|
-
...old.flatMap((item):
|
|
43
|
+
...old.flatMap((item): TaskDefinitionBase['inputs'] => {
|
|
42
44
|
if ('group' in item) return [item]
|
|
43
45
|
const next = remaining.get(item.handle)
|
|
44
46
|
if (next == null) return []
|
|
@@ -51,8 +53,8 @@ function retain(ports: readonly InputPort[], old: ManagedTaskDefinition['inputs'
|
|
|
51
53
|
export function httpRequestTask(
|
|
52
54
|
config: HttpRequestConfig = httpRequestConfigSchema.parse({}),
|
|
53
55
|
name = 'HTTP Request',
|
|
54
|
-
previous?:
|
|
55
|
-
):
|
|
56
|
+
previous?: TaskDefinitionBase,
|
|
57
|
+
): ManagedTaskDefinitionOf<HttpRequestExecutor> {
|
|
56
58
|
const inputs: InputPort[] = [{ handle: 'url', jsonSchema: { type: 'string' }, nullable: false }]
|
|
57
59
|
for (const group of ['query', 'headers'] as const) {
|
|
58
60
|
if (config[group].mode == 'object') inputs.push({ handle: group, jsonSchema: group == 'query' ? httpQuerySchema : httpHeaderSchema, nullable: true })
|
|
@@ -218,18 +220,29 @@ export function parseHttpResponse(config: HttpRequestConfig, status: number, hea
|
|
|
218
220
|
/** Only a named parameter rename carries a value; changing source modes is not a rename. */
|
|
219
221
|
export function httpRequestInputs(
|
|
220
222
|
mappings: Readonly<Record<string, InputMapping>>,
|
|
221
|
-
previous:
|
|
222
|
-
next:
|
|
223
|
+
previous: TaskDefinitionBase,
|
|
224
|
+
next: TaskDefinitionBase,
|
|
223
225
|
): Record<string, InputMapping> {
|
|
224
226
|
const candidate = renamedPort(previous.inputs, next.inputs)
|
|
225
227
|
const rename = candidate != null && ['query.', 'header.'].some((prefix) => candidate.every((name) => name.startsWith(prefix))) ? candidate : undefined
|
|
226
228
|
return remapInputMappings(mappings, next.inputs, rename)
|
|
227
229
|
}
|
|
228
230
|
|
|
231
|
+
/** A Task node as HTTP helpers read it; inline Tasks have no executor. */
|
|
232
|
+
interface HttpTaskNode {
|
|
233
|
+
readonly inputs: Readonly<Record<string, InputMapping>>
|
|
234
|
+
readonly task: TaskDefinitionBase & { readonly executor?: TaskExecutor }
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function httpRequestExecutor(node: HttpTaskNode): HttpRequestExecutor | undefined {
|
|
238
|
+
const executor = node.task.executor
|
|
239
|
+
return executor?.kind === 'http-request' ? (executor as HttpRequestExecutor) : undefined
|
|
240
|
+
}
|
|
241
|
+
|
|
229
242
|
/** The object editor assembles named inputs without changing unrelated request settings. */
|
|
230
|
-
export function withHttpParameterFields(node:
|
|
231
|
-
|
|
232
|
-
|
|
243
|
+
export function withHttpParameterFields<Node extends HttpTaskNode>(node: Node, group: 'query' | 'headers', values: Record<string, JsonValue>): Node {
|
|
244
|
+
const config = httpRequestExecutor(node)
|
|
245
|
+
if (config == null) return node
|
|
233
246
|
const task = httpRequestTask({ ...config, [group]: { mode: 'fields', names: Object.keys(values) } }, node.task.name, node.task)
|
|
234
247
|
const inputs = httpRequestInputs(node.inputs, node.task, task)
|
|
235
248
|
const rename = renamedPort(node.task.inputs, task.inputs)
|
|
@@ -238,14 +251,16 @@ export function withHttpParameterFields(node: TaskNode, group: 'query' | 'header
|
|
|
238
251
|
const existing = config[group].mode === 'fields' && (config[group].names.includes(name) || rename?.[1] === handle)
|
|
239
252
|
if (inputs[handle] == null && !existing) inputs[handle] = { kind: 'value', value }
|
|
240
253
|
}
|
|
241
|
-
|
|
254
|
+
// Only the HTTP Task and its input mappings change, so the node keeps its own type.
|
|
255
|
+
return { ...node, task, inputs } as Node
|
|
242
256
|
}
|
|
243
257
|
|
|
244
|
-
export function withHttpParameterSource(node:
|
|
245
|
-
|
|
246
|
-
|
|
258
|
+
export function withHttpParameterSource<Node extends HttpTaskNode>(node: Node, group: 'query' | 'headers', mapping?: InputMapping): Node {
|
|
259
|
+
const config = httpRequestExecutor(node)
|
|
260
|
+
if (config == null) return node
|
|
261
|
+
const task = httpRequestTask({ ...config, [group]: { mode: 'object', names: [] } }, node.task.name, node.task)
|
|
247
262
|
const inputs = httpRequestInputs(node.inputs, node.task, task)
|
|
248
263
|
if (mapping == null) delete inputs[group]
|
|
249
264
|
else inputs[group] = mapping
|
|
250
|
-
return { ...node, task, inputs }
|
|
265
|
+
return { ...node, task, inputs } as Node
|
|
251
266
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { InputPort, JsonValue,
|
|
1
|
+
import type { InputPort, JsonValue, ManagedTaskDefinitionOf, Port, TaskExecutor } from '../../flow/common/model.ts'
|
|
2
2
|
|
|
3
3
|
import { dequal } from 'dequal/lite'
|
|
4
4
|
import { matchesSchema } from '../../flow/common/schema.ts'
|
|
@@ -224,6 +224,10 @@ function schema(doc: ObjectValue, schemaValue: JsonValue): JsonValue {
|
|
|
224
224
|
if (typeof converted == 'boolean') return converted
|
|
225
225
|
return { $schema: 'https://json-schema.org/draft/2020-12/schema', ...object(converted), ...(Object.keys(defs).length ? { $defs: defs } : {}) }
|
|
226
226
|
}
|
|
227
|
+
function isOpenApiExecutor(executor: TaskExecutor): executor is OpenApiExecutor {
|
|
228
|
+
return executor.kind == 'openapi'
|
|
229
|
+
}
|
|
230
|
+
|
|
227
231
|
function parameters(config: OpenApiExecutor): ObjectValue[] {
|
|
228
232
|
const doc = parseOpenApi(config.document)
|
|
229
233
|
const { item, op } = operation(doc, config.path, config.method)
|
|
@@ -250,7 +254,7 @@ function jsonContent(doc: ObjectValue, value: JsonValue): { schema: JsonValue; r
|
|
|
250
254
|
export function authHandles(auth: readonly OpenApiAuth[]): string[] {
|
|
251
255
|
return auth.flatMap((scheme) => (scheme.type == 'basic' ? [`auth.${scheme.id}.username`, `auth.${scheme.id}.password`] : [`auth.${scheme.id}.token`]))
|
|
252
256
|
}
|
|
253
|
-
export function openApiTask(config: OpenApiExecutor, name = 'OpenAPI'):
|
|
257
|
+
export function openApiTask(config: OpenApiExecutor, name = 'OpenAPI'): ManagedTaskDefinitionOf<OpenApiExecutor> {
|
|
254
258
|
if (!config.path) return { name, executor: config, inputs: [], outputs: [] }
|
|
255
259
|
const doc = parseOpenApi(config.document)
|
|
256
260
|
const { op } = operation(doc, config.path, config.method)
|
|
@@ -318,8 +322,8 @@ export interface OpenApiFieldIssue {
|
|
|
318
322
|
}
|
|
319
323
|
|
|
320
324
|
/** The canvas validator and property panel share the same configuration contract. */
|
|
321
|
-
export function openApiFieldIssues(task:
|
|
322
|
-
if (task.executor
|
|
325
|
+
export function openApiFieldIssues<Executor extends TaskExecutor>(task: ManagedTaskDefinitionOf<Executor>): OpenApiFieldIssue[] {
|
|
326
|
+
if (!isOpenApiExecutor(task.executor)) return []
|
|
323
327
|
const config = task.executor
|
|
324
328
|
let field: OpenApiFieldIssue['field'] = 'sourceUrl'
|
|
325
329
|
try {
|
|
@@ -342,7 +346,7 @@ export function openApiFieldIssues(task: ManagedTaskDefinition): OpenApiFieldIss
|
|
|
342
346
|
}
|
|
343
347
|
}
|
|
344
348
|
|
|
345
|
-
export function openApiIssues(task:
|
|
349
|
+
export function openApiIssues(task: ManagedTaskDefinitionOf<TaskExecutor>): string[] {
|
|
346
350
|
return openApiFieldIssues(task).map((issue) => issue.message)
|
|
347
351
|
}
|
|
348
352
|
export function selectOperation(value: JsonValue, sourceUrl: string, path: string, method: string): OpenApiExecutor {
|
|
@@ -7,25 +7,31 @@ export interface EngineContract {
|
|
|
7
7
|
readonly webhookResponses?: boolean
|
|
8
8
|
readonly workflowTools?: boolean
|
|
9
9
|
readonly httpRequests?: boolean
|
|
10
|
+
/** Code may call `context.fetch` for hosts declared by its `http` Capability. */
|
|
11
|
+
readonly codeFetch?: boolean
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
|
|
14
|
+
/** v9 is a single contract: the Node compatibility builtins, `context.fetch`, and no `context.egress`. */
|
|
15
|
+
export const currentEngineContract = 'open-flow-engine/v9'
|
|
16
|
+
/** The published v8 Node compatibility contract, retained for existing Publications and Runs. */
|
|
13
17
|
export const nodejsEngineContract = 'open-flow-engine/v8/nodejs-compat-v1'
|
|
14
18
|
|
|
15
|
-
const
|
|
19
|
+
const platformSource = (contract: string) => `export const engineContract = ${JSON.stringify(contract)}
|
|
20
|
+
export function identity(value) { return value }
|
|
21
|
+
`
|
|
22
|
+
|
|
23
|
+
const v8Contract: EngineContract = {
|
|
16
24
|
workflowTools: true,
|
|
17
25
|
httpRequests: true,
|
|
18
26
|
waitAutoContinue: true,
|
|
19
27
|
webhookResponses: true,
|
|
20
28
|
platformExports: new Set(['engineContract', 'identity']),
|
|
21
29
|
platformModule: 'open-flow:platform',
|
|
22
|
-
platformSource:
|
|
23
|
-
export function identity(value) { return value }
|
|
24
|
-
`,
|
|
30
|
+
platformSource: platformSource('open-flow-engine/v8'),
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
const nodejsContract: EngineContract = {
|
|
28
|
-
...
|
|
34
|
+
...v8Contract,
|
|
29
35
|
builtinModules: new Set(
|
|
30
36
|
[
|
|
31
37
|
'assert',
|
|
@@ -54,31 +60,30 @@ const nodejsContract: EngineContract = {
|
|
|
54
60
|
'zlib',
|
|
55
61
|
].flatMap((name) => [name, `node:${name}`]),
|
|
56
62
|
),
|
|
57
|
-
platformSource:
|
|
58
|
-
export function identity(value) { return value }
|
|
59
|
-
`,
|
|
63
|
+
platformSource: platformSource(nodejsEngineContract),
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
const currentContract: EngineContract = { ...nodejsContract, codeFetch: true, platformSource: platformSource(currentEngineContract) }
|
|
67
|
+
|
|
62
68
|
export function findEngineContract(contract: string): EngineContract | undefined {
|
|
63
69
|
if (contract == currentEngineContract) return currentContract
|
|
70
|
+
if (contract == 'open-flow-engine/v8') return v8Contract
|
|
64
71
|
if (contract == nodejsEngineContract) return nodejsContract
|
|
65
72
|
if (contract == 'open-flow-engine/v7' || contract == 'open-flow-engine/v7/nodejs-compat-v1')
|
|
66
73
|
return {
|
|
67
|
-
...(contract.endsWith('/nodejs-compat-v1') ? nodejsContract :
|
|
74
|
+
...(contract.endsWith('/nodejs-compat-v1') ? nodejsContract : v8Contract),
|
|
68
75
|
workflowTools: false,
|
|
69
76
|
httpRequests: false,
|
|
70
|
-
platformSource:
|
|
77
|
+
platformSource: platformSource(contract),
|
|
71
78
|
}
|
|
72
79
|
if (contract == 'open-flow-engine/v6' || contract == 'open-flow-engine/v6/nodejs-compat-v1')
|
|
73
80
|
return {
|
|
74
|
-
...(contract == 'open-flow-engine/v6' ?
|
|
81
|
+
...(contract == 'open-flow-engine/v6' ? v8Contract : nodejsContract),
|
|
75
82
|
workflowTools: false,
|
|
76
83
|
httpRequests: false,
|
|
77
84
|
waitAutoContinue: false,
|
|
78
85
|
webhookResponses: false,
|
|
79
|
-
platformSource:
|
|
80
|
-
export function identity(value) { return value }
|
|
81
|
-
`,
|
|
86
|
+
platformSource: platformSource(contract),
|
|
82
87
|
}
|
|
83
88
|
return undefined
|
|
84
89
|
}
|