@codemation/core 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +2 -0
- package/dist/{EngineRuntimeRegistration.types-0sgV2XL2.d.ts → EngineRuntimeRegistration.types-Bjeo7Sfq.d.ts} +2 -2
- package/dist/{EngineWorkflowRunnerService-Dx7bJsJR.d.cts → EngineWorkflowRunnerService-Dd4yD31l.d.cts} +2 -2
- package/dist/{InMemoryRunDataFactory-qIYQEar7.d.cts → InMemoryRunDataFactory-OUzDmAHt.d.cts} +2 -2
- package/dist/{RunIntentService-BCvGdOSY.d.ts → RunIntentService-BAKikN8h.d.ts} +80 -12
- package/dist/{RunIntentService-CV8izV8t.d.cts → RunIntentService-Bkg4oYrM.d.cts} +74 -5
- package/dist/bootstrap/index.cjs +31 -31
- package/dist/bootstrap/index.d.cts +5 -3
- package/dist/bootstrap/index.d.ts +3 -3
- package/dist/bootstrap/index.js +2 -2
- package/dist/bootstrap-BD6CobHl.js +215 -0
- package/dist/bootstrap-BD6CobHl.js.map +1 -0
- package/dist/bootstrap-DwS5S7s9.cjs +240 -0
- package/dist/bootstrap-DwS5S7s9.cjs.map +1 -0
- package/dist/{index-CueSzHsf.d.ts → index-uCm9l0nw.d.ts} +64 -15
- package/dist/index.cjs +114 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -21
- package/dist/index.d.ts +3 -3
- package/dist/index.js +83 -2
- package/dist/index.js.map +1 -1
- package/dist/{RunIntentService-BFA48UpH.js → runtime-Cy-3FTI_.js} +1224 -94
- package/dist/runtime-Cy-3FTI_.js.map +1 -0
- package/dist/{RunIntentService-DcxXf_AM.cjs → runtime-ZJUpWmPH.cjs} +1251 -132
- package/dist/runtime-ZJUpWmPH.cjs.map +1 -0
- package/dist/testing.cjs +74 -29
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +55 -3
- package/dist/testing.d.ts +55 -3
- package/dist/testing.js +46 -3
- package/dist/testing.js.map +1 -1
- package/dist/workflowActivationPolicy-B8HzTk3o.js.map +1 -1
- package/dist/workflowActivationPolicy-BzyzXLa_.cjs.map +1 -1
- package/package.json +1 -1
- package/src/ai/AgentMessageConfigNormalizerFactory.ts +43 -0
- package/src/ai/AgentToolFactory.ts +2 -2
- package/src/ai/AiHost.ts +10 -9
- package/src/ai/NodeBackedToolConfig.ts +1 -1
- package/src/authoring/defineNode.types.ts +153 -10
- package/src/authoring/index.ts +3 -1
- package/src/contracts/runtimeTypes.ts +26 -0
- package/src/contracts/workflowTypes.ts +67 -5
- package/src/execution/ActivationEnqueueService.ts +8 -5
- package/src/execution/NodeActivationRequestInputPreparer.ts +89 -0
- package/src/execution/NodeExecutor.ts +38 -1
- package/src/execution/NodeInputContractError.ts +13 -0
- package/src/execution/index.ts +2 -0
- package/src/orchestration/RunContinuationService.ts +181 -50
- package/src/planning/RunQueuePlanner.ts +12 -1
- package/src/runtime/EngineFactory.ts +3 -0
- package/src/testing/ItemHarnessNode.ts +27 -0
- package/src/testing/ItemHarnessNodeConfig.ts +43 -0
- package/src/testing/RegistrarEngineTestKitFactory.ts +2 -0
- package/src/testing.ts +2 -0
- package/src/workflow/dsl/ChainCursorResolver.ts +1 -1
- package/src/workflow/dsl/workflowBuilderTypes.ts +8 -5
- package/dist/RunIntentService-BFA48UpH.js.map +0 -1
- package/dist/RunIntentService-DcxXf_AM.cjs.map +0 -1
- package/dist/bootstrap-D67Sf2BF.js +0 -1136
- package/dist/bootstrap-D67Sf2BF.js.map +0 -1
- package/dist/bootstrap-DoQHAEQJ.cjs +0 -1203
- package/dist/bootstrap-DoQHAEQJ.cjs.map +0 -1
|
@@ -4,10 +4,11 @@ import type {
|
|
|
4
4
|
CredentialRequirement,
|
|
5
5
|
CredentialTypeId,
|
|
6
6
|
} from "../contracts/credentialTypes";
|
|
7
|
-
import type { Node, NodeExecutionContext } from "../contracts/runtimeTypes";
|
|
8
|
-
import type { Items, NodeOutputs, RunnableNodeConfig } from "../contracts/workflowTypes";
|
|
7
|
+
import type { ItemNode, Node, NodeExecutionContext } from "../contracts/runtimeTypes";
|
|
8
|
+
import type { Item, ItemInputMapper, Items, NodeOutputs, RunnableNodeConfig } from "../contracts/workflowTypes";
|
|
9
9
|
import type { TypeToken } from "../di";
|
|
10
10
|
import { node as persistedNode } from "../runtime-types/runtimeTypeDecorators.types";
|
|
11
|
+
import type { ZodType } from "zod";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
import { DefinedNodeRegistry } from "./DefinedNodeRegistry";
|
|
13
14
|
|
|
@@ -54,27 +55,46 @@ export interface DefinedNodeRunContext<
|
|
|
54
55
|
readonly execution: NodeExecutionContext<RunnableNodeConfig<TConfig, unknown>>;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Arguments for {@link defineNode} `executeOne` (engine `ctx` matches {@link ItemNode.executeOne};
|
|
60
|
+
* the second callback parameter adds {@link DefinedNodeRunContext} for credential accessors).
|
|
61
|
+
*/
|
|
62
|
+
export type DefineNodeExecuteOneArgs<TConfig extends CredentialJsonRecord, TInputJson, TWireJson> = Readonly<{
|
|
63
|
+
input: TInputJson;
|
|
64
|
+
item: Item;
|
|
65
|
+
itemIndex: number;
|
|
66
|
+
items: Items;
|
|
67
|
+
ctx: NodeExecutionContext<RunnableNodeConfig<TInputJson, unknown, TWireJson> & Readonly<{ config: TConfig }>>;
|
|
68
|
+
}>;
|
|
69
|
+
|
|
57
70
|
export interface DefinedNode<
|
|
58
71
|
TKey extends string,
|
|
59
72
|
TConfig extends CredentialJsonRecord,
|
|
60
73
|
TInputJson,
|
|
61
74
|
TOutputJson,
|
|
62
75
|
_TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
76
|
+
TWireJson = TInputJson,
|
|
63
77
|
> {
|
|
64
78
|
readonly kind: "defined-node";
|
|
65
79
|
readonly key: TKey;
|
|
66
80
|
readonly title: string;
|
|
67
81
|
readonly description?: string;
|
|
68
|
-
create(config: TConfig, name?: string, id?: string): RunnableNodeConfig<TInputJson, TOutputJson>;
|
|
82
|
+
create(config: TConfig, name?: string, id?: string): RunnableNodeConfig<TInputJson, TOutputJson, TWireJson>;
|
|
69
83
|
register(context: { registerNode<TValue>(token: TypeToken<TValue>, implementation?: TypeToken<TValue>): void }): void;
|
|
70
84
|
}
|
|
71
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Plugin / DSL-friendly node: **one item in, one item out** per engine activation step.
|
|
88
|
+
* The engine applies {@link RunnableNodeConfig.mapInput} (if any) + {@link RunnableNodeConfig.inputSchema}
|
|
89
|
+
* before `executeOne`. Use {@link defineBatchNode} for legacy batch `run(items, …)` semantics.
|
|
90
|
+
*/
|
|
72
91
|
export interface DefineNodeOptions<
|
|
73
92
|
TKey extends string,
|
|
74
93
|
TConfig extends CredentialJsonRecord,
|
|
75
94
|
TInputJson,
|
|
76
95
|
TOutputJson,
|
|
77
96
|
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
97
|
+
TWireJson = TInputJson,
|
|
78
98
|
> {
|
|
79
99
|
readonly key: TKey;
|
|
80
100
|
readonly title: string;
|
|
@@ -84,6 +104,38 @@ export interface DefineNodeOptions<
|
|
|
84
104
|
* The Next host resolves Lucide (`lucide:…`), built-in SVGs (`builtin:…`), Simple Icons (`si:…`), and image URLs (`https:`, `data:`, `/…`).
|
|
85
105
|
*/
|
|
86
106
|
readonly icon?: string;
|
|
107
|
+
/** Default values / form hints for **static** node configuration (credentials, retry, IDs), not per-item payload. */
|
|
108
|
+
readonly input?: Readonly<Record<keyof TConfig & string, unknown>>;
|
|
109
|
+
readonly configSchema?: z.ZodType<TConfig>;
|
|
110
|
+
readonly credentials?: TBindings;
|
|
111
|
+
/**
|
|
112
|
+
* Validates **`input`** after optional {@link mapInput} (engine also accepts `inputSchema` on the node class).
|
|
113
|
+
*/
|
|
114
|
+
readonly inputSchema?: ZodType<TInputJson>;
|
|
115
|
+
/**
|
|
116
|
+
* Maps wire JSON (`item.json` from upstream) to execute input before validation.
|
|
117
|
+
*/
|
|
118
|
+
readonly mapInput?: ItemInputMapper<TWireJson, TInputJson>;
|
|
119
|
+
executeOne(
|
|
120
|
+
args: DefineNodeExecuteOneArgs<TConfig, TInputJson, TWireJson>,
|
|
121
|
+
context: DefinedNodeRunContext<TConfig, TBindings>,
|
|
122
|
+
): MaybePromise<TOutputJson>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Batch-oriented defined node (legacy): receives all items at once via `run`.
|
|
127
|
+
*/
|
|
128
|
+
export interface DefineBatchNodeOptions<
|
|
129
|
+
TKey extends string,
|
|
130
|
+
TConfig extends CredentialJsonRecord,
|
|
131
|
+
TInputJson,
|
|
132
|
+
TOutputJson,
|
|
133
|
+
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
134
|
+
> {
|
|
135
|
+
readonly key: TKey;
|
|
136
|
+
readonly title: string;
|
|
137
|
+
readonly description?: string;
|
|
138
|
+
readonly icon?: string;
|
|
87
139
|
readonly input?: Readonly<Record<keyof TConfig & string, unknown>>;
|
|
88
140
|
readonly configSchema?: z.ZodType<TConfig>;
|
|
89
141
|
readonly credentials?: TBindings;
|
|
@@ -145,7 +197,7 @@ const definedNodeCredentialRequirementFactory = {
|
|
|
145
197
|
const definedNodeCredentialAccessorFactory = {
|
|
146
198
|
create<TBindings extends DefinedNodeCredentialBindings | undefined>(
|
|
147
199
|
bindings: TBindings,
|
|
148
|
-
ctx: NodeExecutionContext<RunnableNodeConfig<any, any>>,
|
|
200
|
+
ctx: NodeExecutionContext<RunnableNodeConfig<any, any, any>>,
|
|
149
201
|
): DefinedNodeCredentialAccessors<TBindings> {
|
|
150
202
|
if (!bindings) {
|
|
151
203
|
return {} as DefinedNodeCredentialAccessors<TBindings>;
|
|
@@ -161,11 +213,100 @@ export function defineNode<
|
|
|
161
213
|
TInputJson,
|
|
162
214
|
TOutputJson,
|
|
163
215
|
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
216
|
+
TWireJson = TInputJson,
|
|
217
|
+
>(
|
|
218
|
+
options: DefineNodeOptions<TKey, TConfig, TInputJson, TOutputJson, TBindings, TWireJson>,
|
|
219
|
+
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings, TWireJson> {
|
|
220
|
+
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
221
|
+
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson, TWireJson> &
|
|
222
|
+
Readonly<{ config: TConfig }>;
|
|
223
|
+
|
|
224
|
+
const DefinedNodeRuntime = class implements ItemNode<DefinedRunnableNodeConfigShape, TInputJson, TOutputJson> {
|
|
225
|
+
readonly kind = "node" as const;
|
|
226
|
+
readonly outputPorts = ["main"] as const;
|
|
227
|
+
readonly inputSchema = options.inputSchema;
|
|
228
|
+
|
|
229
|
+
async executeOne(
|
|
230
|
+
args: Readonly<{
|
|
231
|
+
input: TInputJson;
|
|
232
|
+
item: Item;
|
|
233
|
+
itemIndex: number;
|
|
234
|
+
items: Items;
|
|
235
|
+
ctx: NodeExecutionContext<DefinedRunnableNodeConfigShape>;
|
|
236
|
+
}>,
|
|
237
|
+
): Promise<TOutputJson> {
|
|
238
|
+
const ctx = args.ctx;
|
|
239
|
+
const context: DefinedNodeRunContext<TConfig, TBindings> = {
|
|
240
|
+
config: ctx.config.config,
|
|
241
|
+
credentials: definedNodeCredentialAccessorFactory.create(
|
|
242
|
+
options.credentials,
|
|
243
|
+
ctx,
|
|
244
|
+
) as DefinedNodeCredentialAccessors<TBindings>,
|
|
245
|
+
execution: ctx as unknown as NodeExecutionContext<RunnableNodeConfig<TConfig, unknown>>,
|
|
246
|
+
};
|
|
247
|
+
const payload: DefineNodeExecuteOneArgs<TConfig, TInputJson, TWireJson> = {
|
|
248
|
+
input: args.input,
|
|
249
|
+
item: args.item,
|
|
250
|
+
itemIndex: args.itemIndex,
|
|
251
|
+
items: args.items,
|
|
252
|
+
ctx,
|
|
253
|
+
};
|
|
254
|
+
return await options.executeOne(payload, context);
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
persistedNode({ name: options.key })(DefinedNodeRuntime);
|
|
259
|
+
|
|
260
|
+
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson, TWireJson> {
|
|
261
|
+
readonly kind = "node" as const;
|
|
262
|
+
readonly type: TypeToken<unknown> = DefinedNodeRuntime;
|
|
263
|
+
readonly icon = options.icon;
|
|
264
|
+
readonly inputSchema = options.inputSchema;
|
|
265
|
+
readonly mapInput = options.mapInput;
|
|
266
|
+
|
|
267
|
+
constructor(
|
|
268
|
+
public readonly name: string,
|
|
269
|
+
public readonly config: TConfig,
|
|
270
|
+
public readonly id?: string,
|
|
271
|
+
) {}
|
|
272
|
+
|
|
273
|
+
getCredentialRequirements(): ReadonlyArray<CredentialRequirement> {
|
|
274
|
+
return credentialRequirements;
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings, TWireJson> = {
|
|
279
|
+
kind: "defined-node",
|
|
280
|
+
key: options.key,
|
|
281
|
+
title: options.title,
|
|
282
|
+
description: options.description,
|
|
283
|
+
create(config, name = options.title, id) {
|
|
284
|
+
return new DefinedRunnableNodeConfig(name, config, id);
|
|
285
|
+
},
|
|
286
|
+
register(context) {
|
|
287
|
+
context.registerNode(DefinedNodeRuntime);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
DefinedNodeRegistry.register(
|
|
292
|
+
definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined, unknown>,
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
return definition;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function defineBatchNode<
|
|
299
|
+
TKey extends string,
|
|
300
|
+
TConfig extends CredentialJsonRecord,
|
|
301
|
+
TInputJson,
|
|
302
|
+
TOutputJson,
|
|
303
|
+
TBindings extends DefinedNodeCredentialBindings | undefined = undefined,
|
|
164
304
|
>(
|
|
165
|
-
options:
|
|
166
|
-
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> {
|
|
305
|
+
options: DefineBatchNodeOptions<TKey, TConfig, TInputJson, TOutputJson, TBindings>,
|
|
306
|
+
): DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings, TInputJson> {
|
|
167
307
|
const credentialRequirements = definedNodeCredentialRequirementFactory.create(options.credentials);
|
|
168
|
-
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson> &
|
|
308
|
+
type DefinedRunnableNodeConfigShape = RunnableNodeConfig<TInputJson, TOutputJson, TInputJson> &
|
|
309
|
+
Readonly<{ config: TConfig }>;
|
|
169
310
|
|
|
170
311
|
const DefinedNodeRuntime = class implements Node<DefinedRunnableNodeConfigShape> {
|
|
171
312
|
readonly kind = "node" as const;
|
|
@@ -198,7 +339,7 @@ export function defineNode<
|
|
|
198
339
|
|
|
199
340
|
persistedNode({ name: options.key })(DefinedNodeRuntime);
|
|
200
341
|
|
|
201
|
-
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson> {
|
|
342
|
+
const DefinedRunnableNodeConfig = class implements RunnableNodeConfig<TInputJson, TOutputJson, TInputJson> {
|
|
202
343
|
readonly kind = "node" as const;
|
|
203
344
|
readonly type: TypeToken<unknown> = DefinedNodeRuntime;
|
|
204
345
|
readonly icon = options.icon;
|
|
@@ -214,7 +355,7 @@ export function defineNode<
|
|
|
214
355
|
}
|
|
215
356
|
};
|
|
216
357
|
|
|
217
|
-
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings> = {
|
|
358
|
+
const definition: DefinedNode<TKey, TConfig, TInputJson, TOutputJson, TBindings, TInputJson> = {
|
|
218
359
|
kind: "defined-node",
|
|
219
360
|
key: options.key,
|
|
220
361
|
title: options.title,
|
|
@@ -227,7 +368,9 @@ export function defineNode<
|
|
|
227
368
|
},
|
|
228
369
|
};
|
|
229
370
|
|
|
230
|
-
DefinedNodeRegistry.register(
|
|
371
|
+
DefinedNodeRegistry.register(
|
|
372
|
+
definition as DefinedNode<string, Record<string, unknown>, unknown, unknown, undefined, unknown>,
|
|
373
|
+
);
|
|
231
374
|
|
|
232
375
|
return definition;
|
|
233
376
|
}
|
package/src/authoring/index.ts
CHANGED
|
@@ -5,8 +5,10 @@ export type {
|
|
|
5
5
|
DefinedNodeCredentialBinding,
|
|
6
6
|
DefinedNodeCredentialBindings,
|
|
7
7
|
DefinedNodeRunContext,
|
|
8
|
+
DefineBatchNodeOptions,
|
|
9
|
+
DefineNodeExecuteOneArgs,
|
|
8
10
|
DefineNodeOptions,
|
|
9
11
|
} from "./defineNode.types";
|
|
10
|
-
export { defineNode } from "./defineNode.types";
|
|
12
|
+
export { defineBatchNode, defineNode } from "./defineNode.types";
|
|
11
13
|
export type { DefineCredentialOptions } from "./defineCredential.types";
|
|
12
14
|
export { defineCredential } from "./defineCredential.types";
|
|
@@ -13,6 +13,8 @@ import type {
|
|
|
13
13
|
} from "./runTypes";
|
|
14
14
|
import type { WorkflowActivationPolicy } from "./workflowActivationPolicy";
|
|
15
15
|
import type { TriggerInstanceId, WebhookTriggerMatcher } from "./webhookTypes";
|
|
16
|
+
import type { ZodType } from "zod";
|
|
17
|
+
|
|
16
18
|
import type {
|
|
17
19
|
ActivationIdFactory,
|
|
18
20
|
BinaryAttachment,
|
|
@@ -225,6 +227,30 @@ export interface Node<TConfig extends NodeConfigBase = NodeConfigBase> {
|
|
|
225
227
|
execute(items: Items, ctx: NodeExecutionContext<TConfig>): Promise<NodeOutputs>;
|
|
226
228
|
}
|
|
227
229
|
|
|
230
|
+
/**
|
|
231
|
+
* Single-input runnable node with per-item execution on `main` only (1→1 default).
|
|
232
|
+
* Engine applies {@link RunnableNodeConfig.mapInput} (if any) + `inputSchema.parse` before `executeOne`.
|
|
233
|
+
*/
|
|
234
|
+
export interface ItemNode<
|
|
235
|
+
TConfig extends NodeConfigBase = NodeConfigBase,
|
|
236
|
+
TInputJson = unknown,
|
|
237
|
+
TOutputJson = unknown,
|
|
238
|
+
> {
|
|
239
|
+
readonly kind: "node";
|
|
240
|
+
readonly outputPorts: readonly ["main"];
|
|
241
|
+
/** When omitted, engine uses {@link RunnableNodeConfig.inputSchema} or `z.unknown()`. */
|
|
242
|
+
readonly inputSchema?: ZodType<TInputJson>;
|
|
243
|
+
executeOne(
|
|
244
|
+
args: Readonly<{
|
|
245
|
+
input: TInputJson;
|
|
246
|
+
item: Item;
|
|
247
|
+
itemIndex: number;
|
|
248
|
+
items: Items;
|
|
249
|
+
ctx: NodeExecutionContext<TConfig>;
|
|
250
|
+
}>,
|
|
251
|
+
): Promise<TOutputJson> | TOutputJson;
|
|
252
|
+
}
|
|
253
|
+
|
|
228
254
|
export interface MultiInputNode<TConfig extends NodeConfigBase = NodeConfigBase> {
|
|
229
255
|
kind: "node";
|
|
230
256
|
outputPorts: ReadonlyArray<OutputPortKey>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { ZodType } from "zod";
|
|
2
|
+
|
|
1
3
|
import type { TypeToken } from "../di";
|
|
2
4
|
import type { CredentialRequirement } from "./credentialTypes";
|
|
3
5
|
import type { RetryPolicySpec } from "./retryPolicySpec.types";
|
|
@@ -84,12 +86,69 @@ export interface NodeConfigBase {
|
|
|
84
86
|
|
|
85
87
|
export declare const runnableNodeInputType: unique symbol;
|
|
86
88
|
export declare const runnableNodeOutputType: unique symbol;
|
|
89
|
+
/** Phantom: JSON shape on the wire from upstream before {@link RunnableNodeConfig.mapInput}. */
|
|
90
|
+
export declare const runnableNodeWireType: unique symbol;
|
|
87
91
|
export declare const triggerNodeOutputType: unique symbol;
|
|
88
92
|
|
|
89
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Read-only execution slice passed to {@link RunnableNodeConfig.mapInput} (aligned with the engine’s
|
|
95
|
+
* node execution context for `runId`, `data`, etc.). Use **`ctx.data`** to read **any completed** upstream
|
|
96
|
+
* node’s outputs in this run (e.g. `ctx.data.getOutputItems(nodeIdA, "main")` while mapping at D), not only
|
|
97
|
+
* the immediate predecessor’s {@link ItemInputMapperArgs.item}.
|
|
98
|
+
*/
|
|
99
|
+
export interface ItemInputMapperContext {
|
|
100
|
+
readonly runId: RunId;
|
|
101
|
+
readonly workflowId: WorkflowId;
|
|
102
|
+
/** Node whose activation is being prepared (the consumer of `mapInput`). */
|
|
103
|
+
readonly nodeId: NodeId;
|
|
104
|
+
readonly activationId: NodeActivationId;
|
|
105
|
+
readonly parent?: ParentExecutionRef;
|
|
106
|
+
readonly data: RunDataSnapshot;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Arguments for optional per-item input mapping applied by the engine before Zod validation.
|
|
111
|
+
*/
|
|
112
|
+
export interface ItemInputMapperArgs<TWireJson = unknown> {
|
|
113
|
+
readonly item: Item<TWireJson>;
|
|
114
|
+
readonly itemIndex: number;
|
|
115
|
+
readonly items: Items<TWireJson>;
|
|
116
|
+
readonly ctx: ItemInputMapperContext;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Per-item mapper before Zod validation. Uses a **bivariant** method signature so concrete
|
|
121
|
+
* `ItemInputMapper<SpecificWire, TIn>` remains assignable to `RunnableNodeConfig` fields typed as
|
|
122
|
+
* `ItemInputMapper<unknown, unknown>` (same pattern as React-style callbacks).
|
|
123
|
+
*/
|
|
124
|
+
export type ItemInputMapper<TWireJson = unknown, TInputJson = unknown> = {
|
|
125
|
+
bivarianceHack(args: ItemInputMapperArgs<TWireJson>): TInputJson | Promise<TInputJson>;
|
|
126
|
+
}["bivarianceHack"];
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Runnable node: **`TInputJson`** is the payload after `mapInput` (if any) + Zod validation — what {@link ItemNode}
|
|
130
|
+
* `executeOne` receives. **`TOutputJson`** is emitted `item.json` on outputs. **`TWireJson`** is `item.json` from
|
|
131
|
+
* upstream **before** `mapInput`; it defaults to **`TInputJson`** when there is no mapper or wire differs from execute input.
|
|
132
|
+
*/
|
|
133
|
+
export interface RunnableNodeConfig<
|
|
134
|
+
TInputJson = unknown,
|
|
135
|
+
TOutputJson = unknown,
|
|
136
|
+
TWireJson = TInputJson,
|
|
137
|
+
> extends NodeConfigBase {
|
|
90
138
|
readonly kind: "node";
|
|
91
139
|
readonly [runnableNodeInputType]?: TInputJson;
|
|
92
140
|
readonly [runnableNodeOutputType]?: TOutputJson;
|
|
141
|
+
readonly [runnableNodeWireType]?: TWireJson;
|
|
142
|
+
/**
|
|
143
|
+
* Optional Zod input contract for {@link ItemNode} when not set on the node class.
|
|
144
|
+
* Resolution order: node instance `inputSchema`, then config `inputSchema`, then `z.unknown()`.
|
|
145
|
+
*/
|
|
146
|
+
readonly inputSchema?: ZodType<TInputJson>;
|
|
147
|
+
/**
|
|
148
|
+
* Optional per-item mapper: engine applies it before validating against the node’s `inputSchema`.
|
|
149
|
+
* When omitted, the engine validates `item.json` directly.
|
|
150
|
+
*/
|
|
151
|
+
readonly mapInput?: ItemInputMapper<TWireJson, TInputJson>;
|
|
93
152
|
}
|
|
94
153
|
|
|
95
154
|
export declare const triggerNodeSetupStateType: unique symbol;
|
|
@@ -103,11 +162,14 @@ export interface TriggerNodeConfig<
|
|
|
103
162
|
readonly [triggerNodeSetupStateType]?: TSetupState;
|
|
104
163
|
}
|
|
105
164
|
|
|
106
|
-
export type RunnableNodeInputJson<TConfig extends RunnableNodeConfig<any, any>> =
|
|
107
|
-
TConfig extends RunnableNodeConfig<infer TInputJson, any> ? TInputJson : never;
|
|
165
|
+
export type RunnableNodeInputJson<TConfig extends RunnableNodeConfig<any, any, any>> =
|
|
166
|
+
TConfig extends RunnableNodeConfig<infer TInputJson, any, any> ? TInputJson : never;
|
|
167
|
+
|
|
168
|
+
export type RunnableNodeWireJson<TConfig extends RunnableNodeConfig<any, any, any>> =
|
|
169
|
+
TConfig extends RunnableNodeConfig<any, any, infer TWireJson> ? TWireJson : never;
|
|
108
170
|
|
|
109
|
-
export type RunnableNodeOutputJson<TConfig extends RunnableNodeConfig<any, any>> =
|
|
110
|
-
TConfig extends RunnableNodeConfig<any, infer TOutputJson> ? TOutputJson : never;
|
|
171
|
+
export type RunnableNodeOutputJson<TConfig extends RunnableNodeConfig<any, any, any>> =
|
|
172
|
+
TConfig extends RunnableNodeConfig<any, infer TOutputJson, any> ? TOutputJson : never;
|
|
111
173
|
|
|
112
174
|
export type TriggerNodeOutputJson<TConfig extends TriggerNodeConfig<any, any>> =
|
|
113
175
|
TConfig extends TriggerNodeConfig<infer TOutputJson, any> ? TOutputJson : never;
|
|
@@ -21,6 +21,7 @@ import type {
|
|
|
21
21
|
import { RunQueuePlanner } from "../planning/RunQueuePlanner";
|
|
22
22
|
|
|
23
23
|
import { NodeEventPublisher } from "../events/NodeEventPublisher";
|
|
24
|
+
import type { NodeActivationRequestInputPreparer } from "./NodeActivationRequestInputPreparer";
|
|
24
25
|
import { NodeExecutionSnapshotFactory } from "./NodeExecutionSnapshotFactory";
|
|
25
26
|
import { NodeInputsByPortFactory } from "./NodeInputsByPortFactory";
|
|
26
27
|
|
|
@@ -51,6 +52,7 @@ export class ActivationEnqueueService {
|
|
|
51
52
|
private readonly activationScheduler: ActivationSchedulerPort,
|
|
52
53
|
private readonly workflowExecutionRepository: WorkflowExecutionRepository,
|
|
53
54
|
private readonly nodeEventPublisher: NodeEventPublisher,
|
|
55
|
+
private readonly nodeActivationRequestInputPreparer: NodeActivationRequestInputPreparer,
|
|
54
56
|
) {}
|
|
55
57
|
|
|
56
58
|
async enqueueActivation(args: ActivationEnqueueRequest): Promise<RunResult> {
|
|
@@ -62,12 +64,13 @@ export class ActivationEnqueueService {
|
|
|
62
64
|
async enqueueActivationWithSnapshot(
|
|
63
65
|
args: ActivationEnqueueRequest,
|
|
64
66
|
): Promise<{ result: RunResult; queuedSnapshot: NodeExecutionSnapshot }> {
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
+
const preparedRequest = await this.nodeActivationRequestInputPreparer.prepare(args.request);
|
|
68
|
+
const preparedDispatch = await this.activationScheduler.prepareDispatch(preparedRequest);
|
|
69
|
+
const inputsByPort = NodeInputsByPortFactory.fromRequest(preparedRequest);
|
|
67
70
|
const itemsIn =
|
|
68
|
-
|
|
69
|
-
? args.planner.sumItemsByPort(
|
|
70
|
-
:
|
|
71
|
+
preparedRequest.kind === "multi"
|
|
72
|
+
? args.planner.sumItemsByPort(preparedRequest.inputsByPort)
|
|
73
|
+
: preparedRequest.input.length;
|
|
71
74
|
const enqueuedAt = new Date().toISOString();
|
|
72
75
|
const pending: PendingNodeExecution = {
|
|
73
76
|
runId: args.runId,
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { z, ZodError } from "zod";
|
|
2
|
+
|
|
3
|
+
import type { Item, NodeActivationRequest, RunnableNodeConfig, WorkflowNodeInstanceFactory } from "../types";
|
|
4
|
+
|
|
5
|
+
import { NodeInputContractError } from "./NodeInputContractError";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Maps and validates per-item inputs for {@link ItemNode} before enqueue persistence.
|
|
9
|
+
*/
|
|
10
|
+
export class NodeActivationRequestInputPreparer {
|
|
11
|
+
constructor(private readonly workflowNodeInstanceFactory: WorkflowNodeInstanceFactory) {}
|
|
12
|
+
|
|
13
|
+
async prepare(request: NodeActivationRequest): Promise<NodeActivationRequest> {
|
|
14
|
+
if (request.kind !== "single") {
|
|
15
|
+
return request;
|
|
16
|
+
}
|
|
17
|
+
const nodeInstance: unknown = this.workflowNodeInstanceFactory.createByType(request.ctx.config.type);
|
|
18
|
+
if (!this.hasExecuteOne(nodeInstance)) {
|
|
19
|
+
return request;
|
|
20
|
+
}
|
|
21
|
+
const inputSchema = this.resolveInputSchema(nodeInstance, request.ctx.config as RunnableNodeConfig);
|
|
22
|
+
const config = request.ctx.config as RunnableNodeConfig;
|
|
23
|
+
const mappedItems: Item[] = [];
|
|
24
|
+
for (let i = 0; i < request.input.length; i++) {
|
|
25
|
+
const item = request.input[i] as Item;
|
|
26
|
+
try {
|
|
27
|
+
const mappedRaw = config.mapInput
|
|
28
|
+
? await Promise.resolve(
|
|
29
|
+
config.mapInput({
|
|
30
|
+
item,
|
|
31
|
+
itemIndex: i,
|
|
32
|
+
items: request.input,
|
|
33
|
+
ctx: request.ctx,
|
|
34
|
+
}),
|
|
35
|
+
)
|
|
36
|
+
: item.json;
|
|
37
|
+
const parsed = inputSchema.parse(mappedRaw);
|
|
38
|
+
mappedItems.push({ ...item, json: parsed });
|
|
39
|
+
} catch (cause) {
|
|
40
|
+
const message = this.formatContractFailure(cause);
|
|
41
|
+
throw new NodeInputContractError(
|
|
42
|
+
`Node ${request.nodeId} activation ${request.activationId}: input contract failed: ${message}`,
|
|
43
|
+
request.nodeId,
|
|
44
|
+
request.activationId,
|
|
45
|
+
cause,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
...request,
|
|
51
|
+
input: mappedItems,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private hasExecuteOne(nodeInstance: unknown): boolean {
|
|
56
|
+
return (
|
|
57
|
+
typeof nodeInstance === "object" &&
|
|
58
|
+
nodeInstance !== null &&
|
|
59
|
+
typeof (nodeInstance as { executeOne?: unknown }).executeOne === "function"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private resolveInputSchema(
|
|
64
|
+
nodeInstance: unknown,
|
|
65
|
+
config: RunnableNodeConfig,
|
|
66
|
+
): {
|
|
67
|
+
parse: (data: unknown) => unknown;
|
|
68
|
+
} {
|
|
69
|
+
const fromInstance = (nodeInstance as { inputSchema?: unknown }).inputSchema;
|
|
70
|
+
if (fromInstance && typeof (fromInstance as { parse?: unknown }).parse === "function") {
|
|
71
|
+
return fromInstance as { parse: (data: unknown) => unknown };
|
|
72
|
+
}
|
|
73
|
+
const fromConfig = config.inputSchema;
|
|
74
|
+
if (fromConfig && typeof fromConfig.parse === "function") {
|
|
75
|
+
return fromConfig as { parse: (data: unknown) => unknown };
|
|
76
|
+
}
|
|
77
|
+
return z.unknown();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private formatContractFailure(cause: unknown): string {
|
|
81
|
+
if (cause instanceof ZodError) {
|
|
82
|
+
return cause.issues.map((i) => `${i.path.join(".") || "<root>"}: ${i.message}`).join("; ");
|
|
83
|
+
}
|
|
84
|
+
if (cause instanceof Error) {
|
|
85
|
+
return cause.message;
|
|
86
|
+
}
|
|
87
|
+
return String(cause);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
Item,
|
|
3
|
+
MultiInputNode,
|
|
4
|
+
Node,
|
|
5
|
+
NodeActivationRequest,
|
|
6
|
+
NodeOutputs,
|
|
7
|
+
WorkflowNodeInstanceFactory,
|
|
8
|
+
} from "../types";
|
|
2
9
|
|
|
3
10
|
import { InProcessRetryRunner } from "./InProcessRetryRunner";
|
|
4
11
|
|
|
@@ -34,10 +41,40 @@ export class NodeExecutor {
|
|
|
34
41
|
request: Extract<NodeActivationRequest, { kind: "single" }>,
|
|
35
42
|
node: unknown,
|
|
36
43
|
): Promise<NodeOutputs> {
|
|
44
|
+
if (this.hasExecuteOne(node)) {
|
|
45
|
+
return await this.executeItemNode(request, node);
|
|
46
|
+
}
|
|
37
47
|
const singleInputNode = node as Node;
|
|
38
48
|
if (typeof (singleInputNode as { execute?: unknown }).execute !== "function") {
|
|
39
49
|
throw new Error(`Node ${request.nodeId} does not support execute but received single-input activation`);
|
|
40
50
|
}
|
|
41
51
|
return await singleInputNode.execute(request.input, request.ctx as any);
|
|
42
52
|
}
|
|
53
|
+
|
|
54
|
+
private hasExecuteOne(node: unknown): node is { executeOne: (args: unknown) => unknown | Promise<unknown> } {
|
|
55
|
+
return (
|
|
56
|
+
typeof node === "object" && node !== null && typeof (node as { executeOne?: unknown }).executeOne === "function"
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async executeItemNode(
|
|
61
|
+
request: Extract<NodeActivationRequest, { kind: "single" }>,
|
|
62
|
+
node: { executeOne: (args: unknown) => unknown | Promise<unknown> },
|
|
63
|
+
): Promise<NodeOutputs> {
|
|
64
|
+
const out: Item[] = [];
|
|
65
|
+
for (let i = 0; i < request.input.length; i++) {
|
|
66
|
+
const item = request.input[i] as Item;
|
|
67
|
+
const outputJson = await Promise.resolve(
|
|
68
|
+
node.executeOne({
|
|
69
|
+
input: item.json,
|
|
70
|
+
item,
|
|
71
|
+
itemIndex: i,
|
|
72
|
+
items: request.input,
|
|
73
|
+
ctx: request.ctx,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
out.push({ ...item, json: outputJson });
|
|
77
|
+
}
|
|
78
|
+
return { main: out };
|
|
79
|
+
}
|
|
43
80
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NodeActivationId, NodeId } from "../types";
|
|
2
|
+
|
|
3
|
+
export class NodeInputContractError extends Error {
|
|
4
|
+
constructor(
|
|
5
|
+
message: string,
|
|
6
|
+
public readonly nodeId: NodeId,
|
|
7
|
+
public readonly activationId: NodeActivationId,
|
|
8
|
+
public readonly cause?: unknown,
|
|
9
|
+
) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "NodeInputContractError";
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/execution/index.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { ActivationEnqueueService } from "./ActivationEnqueueService";
|
|
2
|
+
export { NodeActivationRequestInputPreparer } from "./NodeActivationRequestInputPreparer";
|
|
3
|
+
export { NodeInputContractError } from "./NodeInputContractError";
|
|
2
4
|
export { CredentialResolverFactory } from "./CredentialResolverFactory";
|
|
3
5
|
export { DefaultAsyncSleeper } from "./DefaultAsyncSleeper";
|
|
4
6
|
export { DefaultExecutionContextFactory } from "./DefaultExecutionContextFactory";
|