flowcraft 2.1.1 → 2.2.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/LICENSE +21 -0
- package/README.md +1 -1
- package/dist/analysis.d.ts +3 -2
- package/dist/analysis.js +1 -1
- package/dist/{chunk-HN72TZY5.js → chunk-233SESC2.js} +34 -23
- package/dist/chunk-233SESC2.js.map +1 -0
- package/dist/{chunk-UETC63DP.js → chunk-EUJWJWFA.js} +24 -4
- package/dist/chunk-EUJWJWFA.js.map +1 -0
- package/dist/{chunk-NBIRTKZ7.js → chunk-GEKDR2SS.js} +14 -5
- package/dist/chunk-GEKDR2SS.js.map +1 -0
- package/dist/{chunk-3XVVR2SR.js → chunk-OTS5YJ3S.js} +97 -20
- package/dist/chunk-OTS5YJ3S.js.map +1 -0
- package/dist/{chunk-O3XD45IL.js → chunk-QLGJUDQF.js} +42 -15
- package/dist/chunk-QLGJUDQF.js.map +1 -0
- package/dist/{chunk-KWQHFT7E.js → chunk-R3HQXIEL.js} +6 -6
- package/dist/chunk-R3HQXIEL.js.map +1 -0
- package/dist/{chunk-CSZ6EOWG.js → chunk-VSGQDLBF.js} +3 -3
- package/dist/chunk-VSGQDLBF.js.map +1 -0
- package/dist/{chunk-4A627Q6L.js → chunk-ZCHFZBGL.js} +24 -7
- package/dist/chunk-ZCHFZBGL.js.map +1 -0
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/evaluator.d.ts +1 -1
- package/dist/flow.d.ts +18 -7
- package/dist/flow.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +8 -8
- package/dist/linter.d.ts +3 -3
- package/dist/linter.js +2 -2
- package/dist/logger.d.ts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/runtime/adapter.d.ts +5 -2
- package/dist/runtime/adapter.js +6 -6
- package/dist/runtime/builtin-keys.d.ts +38 -0
- package/dist/runtime/builtin-keys.js +10 -0
- package/dist/runtime/builtin-keys.js.map +1 -0
- package/dist/runtime/executors.d.ts +1 -1
- package/dist/runtime/index.d.ts +2 -1
- package/dist/runtime/index.js +6 -6
- package/dist/runtime/runtime.d.ts +9 -3
- package/dist/runtime/runtime.js +5 -5
- package/dist/runtime/state.d.ts +1 -1
- package/dist/runtime/state.js +2 -2
- package/dist/runtime/traverser.d.ts +1 -1
- package/dist/runtime/traverser.js +2 -2
- package/dist/runtime/types.d.ts +2 -2
- package/dist/sanitizer.d.ts +1 -1
- package/dist/serializer.d.ts +1 -1
- package/dist/{types-CQCe_nBM.d.ts → types-CsTeXTiA.d.ts} +16 -8
- package/dist/types.d.ts +1 -1
- package/package.json +51 -51
- package/dist/chunk-3XVVR2SR.js.map +0 -1
- package/dist/chunk-4A627Q6L.js.map +0 -1
- package/dist/chunk-CSZ6EOWG.js.map +0 -1
- package/dist/chunk-HN72TZY5.js.map +0 -1
- package/dist/chunk-KWQHFT7E.js.map +0 -1
- package/dist/chunk-NBIRTKZ7.js.map +0 -1
- package/dist/chunk-O3XD45IL.js.map +0 -1
- package/dist/chunk-UETC63DP.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { FlowRuntime } from './chunk-
|
|
1
|
+
import { FlowRuntime } from './chunk-OTS5YJ3S.js';
|
|
2
2
|
import { JsonSerializer } from './chunk-CYHZ2YVH.js';
|
|
3
|
-
import { analyzeBlueprint } from './chunk-
|
|
3
|
+
import { analyzeBlueprint } from './chunk-233SESC2.js';
|
|
4
4
|
|
|
5
5
|
// src/runtime/adapter.ts
|
|
6
6
|
var BaseDistributedAdapter = class {
|
|
@@ -43,6 +43,8 @@ var BaseDistributedAdapter = class {
|
|
|
43
43
|
const hasBlueprintId = await context.has("blueprintId");
|
|
44
44
|
if (!hasBlueprintId) {
|
|
45
45
|
await context.set("blueprintId", blueprintId);
|
|
46
|
+
const blueprintKey = `flowcraft:blueprint:${runId}`;
|
|
47
|
+
await this.store.setIfNotExist(blueprintKey, blueprintId, 3600);
|
|
46
48
|
}
|
|
47
49
|
const workerState = {
|
|
48
50
|
getContext: () => context,
|
|
@@ -81,7 +83,7 @@ var BaseDistributedAdapter = class {
|
|
|
81
83
|
);
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
|
-
const nextNodes = await this.runtime.determineNextNodes(blueprint, nodeId, result, context);
|
|
86
|
+
const nextNodes = await this.runtime.determineNextNodes(blueprint, nodeId, result, context, runId);
|
|
85
87
|
if (nextNodes.length === 0 && !isTerminalNode) {
|
|
86
88
|
console.log(
|
|
87
89
|
`[Adapter] Non-terminal node '${nodeId}' reached end of branch for Run ID '${runId}'. This branch will now terminate.`
|
|
@@ -126,7 +128,17 @@ var BaseDistributedAdapter = class {
|
|
|
126
128
|
}
|
|
127
129
|
if (joinStrategy === "any") {
|
|
128
130
|
const lockKey = `flowcraft:joinlock:${runId}:${targetNodeId}`;
|
|
129
|
-
|
|
131
|
+
const isLocked = await this.store.setIfNotExist(lockKey, "locked", 3600);
|
|
132
|
+
if (!isLocked) {
|
|
133
|
+
const cancelKey = `flowcraft:fanin:cancel:${runId}:${targetNodeId}`;
|
|
134
|
+
const isCancelled = !await this.store.setIfNotExist(cancelKey, "cancelled", 3600);
|
|
135
|
+
if (isCancelled) {
|
|
136
|
+
console.log(`[Adapter] Node '${targetNodeId}' is cancelled due to failed predecessor. Failing immediately.`);
|
|
137
|
+
throw new Error(`Node '${targetNodeId}' failed due to cancelled predecessor in run '${runId}'`);
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
return true;
|
|
130
142
|
} else {
|
|
131
143
|
const fanInKey = `flowcraft:fanin:${runId}:${targetNodeId}`;
|
|
132
144
|
const readyCount = await this.store.increment(fanInKey, 3600);
|
|
@@ -148,9 +160,15 @@ var BaseDistributedAdapter = class {
|
|
|
148
160
|
*/
|
|
149
161
|
async reconcile(runId) {
|
|
150
162
|
const context = this.createContext(runId);
|
|
151
|
-
|
|
163
|
+
let blueprintId = await context.get("blueprintId");
|
|
152
164
|
if (!blueprintId) {
|
|
153
|
-
|
|
165
|
+
const blueprintKey = `flowcraft:blueprint:${runId}`;
|
|
166
|
+
blueprintId = await this.store.get(blueprintKey);
|
|
167
|
+
if (blueprintId) {
|
|
168
|
+
await context.set("blueprintId", blueprintId);
|
|
169
|
+
} else {
|
|
170
|
+
throw new Error(`Cannot reconcile runId '${runId}': blueprintId not found in context or coordination store.`);
|
|
171
|
+
}
|
|
154
172
|
}
|
|
155
173
|
const blueprint = this.runtime.options.blueprints?.[blueprintId];
|
|
156
174
|
if (!blueprint) {
|
|
@@ -215,22 +233,31 @@ var BaseDistributedAdapter = class {
|
|
|
215
233
|
return newFrontier;
|
|
216
234
|
}
|
|
217
235
|
/**
|
|
218
|
-
* Writes a poison pill for 'all' join successors of a failed node to prevent stalling.
|
|
236
|
+
* Writes a poison pill for 'all' join successors and a cancellation pill for 'any' join successors of a failed node to prevent stalling or ambiguous states.
|
|
219
237
|
*/
|
|
220
238
|
async writePoisonPillForSuccessors(runId, blueprint, failedNodeId) {
|
|
221
|
-
const successors = blueprint.edges.filter((edge) => edge.source === failedNodeId).map((edge) => edge.target).map((targetId) => blueprint.nodes.find((node) => node.id === targetId)).filter((node) => node
|
|
239
|
+
const successors = blueprint.edges.filter((edge) => edge.source === failedNodeId).map((edge) => edge.target).map((targetId) => blueprint.nodes.find((node) => node.id === targetId)).filter((node) => node);
|
|
222
240
|
for (const successor of successors) {
|
|
223
241
|
if (successor) {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
242
|
+
const joinStrategy = successor.config?.joinStrategy || "all";
|
|
243
|
+
if (joinStrategy === "all") {
|
|
244
|
+
const poisonKey = `flowcraft:fanin:poison:${runId}:${successor.id}`;
|
|
245
|
+
await this.store.setIfNotExist(poisonKey, "poisoned", 3600);
|
|
246
|
+
console.log(
|
|
247
|
+
`[Adapter] Wrote poison pill for 'all' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`
|
|
248
|
+
);
|
|
249
|
+
} else if (joinStrategy === "any") {
|
|
250
|
+
const cancelKey = `flowcraft:fanin:cancel:${runId}:${successor.id}`;
|
|
251
|
+
await this.store.setIfNotExist(cancelKey, "cancelled", 3600);
|
|
252
|
+
console.log(
|
|
253
|
+
`[Adapter] Wrote cancellation pill for 'any' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`
|
|
254
|
+
);
|
|
255
|
+
}
|
|
229
256
|
}
|
|
230
257
|
}
|
|
231
258
|
}
|
|
232
259
|
};
|
|
233
260
|
|
|
234
261
|
export { BaseDistributedAdapter };
|
|
235
|
-
//# sourceMappingURL=chunk-
|
|
236
|
-
//# sourceMappingURL=chunk-
|
|
262
|
+
//# sourceMappingURL=chunk-QLGJUDQF.js.map
|
|
263
|
+
//# sourceMappingURL=chunk-QLGJUDQF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/adapter.ts"],"names":["nodeId"],"mappings":"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","file":"chunk-QLGJUDQF.js","sourcesContent":["import { analyzeBlueprint } from '../analysis'\nimport { JsonSerializer } from '../serializer'\nimport type {\n\tIAsyncContext,\n\tISerializer,\n\tNodeResult,\n\tRuntimeOptions,\n\tWorkflowBlueprint,\n\tWorkflowResult,\n} from '../types'\nimport { FlowRuntime } from './runtime'\n\n/**\n * Defines the contract for an atomic, distributed key-value store required by\n * the adapter for coordination tasks like fan-in joins and locking.\n */\nexport interface ICoordinationStore {\n\t/** Atomically increments a key and returns the new value. Ideal for 'all' joins. */\n\tincrement: (key: string, ttlSeconds: number) => Promise<number>\n\t/** Sets a key only if it does not already exist. Ideal for 'any' joins (locking). */\n\tsetIfNotExist: (key: string, value: string, ttlSeconds: number) => Promise<boolean>\n\t/** Deletes a key. Used for cleanup. */\n\tdelete: (key: string) => Promise<void>\n\t/** Gets the value of a key. */\n\tget: (key: string) => Promise<string | undefined>\n}\n\n/** Configuration options for constructing a BaseDistributedAdapter. */\nexport interface AdapterOptions {\n\truntimeOptions: RuntimeOptions<any>\n\tcoordinationStore: ICoordinationStore\n}\n\n/** The data payload expected for a job in the queue. */\nexport interface JobPayload {\n\trunId: string\n\tblueprintId: string\n\tnodeId: string\n}\n\n/**\n * The base class for all distributed adapters. It handles the technology-agnostic\n * orchestration logic and leaves queue-specific implementation to subclasses.\n */\nexport abstract class BaseDistributedAdapter {\n\tprotected readonly runtime: FlowRuntime<any, any>\n\tprotected readonly store: ICoordinationStore\n\tprotected readonly serializer: ISerializer\n\n\tconstructor(options: AdapterOptions) {\n\t\tthis.runtime = new FlowRuntime(options.runtimeOptions)\n\t\tthis.store = options.coordinationStore\n\t\tthis.serializer = options.runtimeOptions.serializer || new JsonSerializer()\n\t\tconsole.log('[Adapter] BaseDistributedAdapter initialized.')\n\t}\n\n\t/**\n\t * Starts the worker, which begins listening for and processing jobs from the queue.\n\t */\n\tpublic start(): void {\n\t\tconsole.log('[Adapter] Starting worker...')\n\t\tthis.processJobs(this.handleJob.bind(this))\n\t}\n\n\t/**\n\t * Creates a technology-specific distributed context for a given workflow run.\n\t * @param runId The unique ID for the workflow execution.\n\t */\n\tprotected abstract createContext(runId: string): IAsyncContext<Record<string, any>>\n\t/**\n\t * Sets up the listener for the message queue. The implementation should call the\n\t * provided `handler` function for each new job received.\n\t * @param handler The core logic to execute for each job.\n\t */\n\tprotected abstract processJobs(handler: (job: JobPayload) => Promise<void>): void\n\n\t/**\n\t * Enqueues a new job onto the message queue.\n\t * @param job The payload for the job to be enqueued.\n\t */\n\tprotected abstract enqueueJob(job: JobPayload): Promise<void>\n\n\t/**\n\t * Publishes the final result of a completed or failed workflow run.\n\t * @param runId The unique ID of the workflow run.\n\t * @param result The final status and payload of the workflow.\n\t */\n\tprotected abstract publishFinalResult(\n\t\trunId: string,\n\t\tresult: {\n\t\t\tstatus: 'completed' | 'failed'\n\t\t\tpayload?: WorkflowResult\n\t\t\treason?: string\n\t\t},\n\t): Promise<void>\n\n\t/**\n\t * Hook called at the start of job processing. Subclasses can override this\n\t * to perform additional setup (e.g., timestamp tracking for reconciliation).\n\t */\n\tprotected async onJobStart(_runId: string, _blueprintId: string, _nodeId: string): Promise<void> {\n\t\t// default implementation does nothing\n\t}\n\n\t/**\n\t * The main handler for processing a single job from the queue.\n\t */\n\tprotected async handleJob(job: JobPayload): Promise<void> {\n\t\tconst { runId, blueprintId, nodeId } = job\n\n\t\tawait this.onJobStart(runId, blueprintId, nodeId)\n\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (!blueprint) {\n\t\t\tconst reason = `Blueprint with ID '${blueprintId}' not found in the worker's runtime registry.`\n\t\t\tconsole.error(`[Adapter] FATAL: ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\treturn\n\t\t}\n\n\t\tconst context = this.createContext(runId)\n\n\t\t// persist the blueprintId for the reconcile method to find later\n\t\tconst hasBlueprintId = await context.has('blueprintId' as any)\n\t\tif (!hasBlueprintId) {\n\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\t// also store in coordination store as fallback\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tawait this.store.setIfNotExist(blueprintKey, blueprintId, 3600)\n\t\t}\n\t\tconst workerState = {\n\t\t\tgetContext: () => context,\n\t\t\tmarkFallbackExecuted: () => {},\n\t\t\taddError: (nodeId: string, error: Error) => {\n\t\t\t\tconsole.error(`[Adapter] Error in node ${nodeId}:`, error)\n\t\t\t},\n\t\t} as any\n\n\t\ttry {\n\t\t\tconst result: NodeResult<any, any> = await this.runtime.executeNode(blueprint, nodeId, workerState)\n\t\t\tawait context.set(nodeId as any, result.output)\n\n\t\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\t\tconst isTerminalNode = analysis.terminalNodeIds.includes(nodeId)\n\n\t\t\tif (isTerminalNode) {\n\t\t\t\tconst completedNodes = new Set(\n\t\t\t\t\tObject.keys(await context.toJSON()).filter((k) => blueprint.nodes.some((n) => n.id === k)),\n\t\t\t\t)\n\t\t\t\tconst allTerminalNodesCompleted = analysis.terminalNodeIds.every((terminalId) => completedNodes.has(terminalId))\n\n\t\t\t\tif (allTerminalNodesCompleted) {\n\t\t\t\t\tconsole.log(`[Adapter] ✅ All terminal nodes completed for Run ID: ${runId}. Declaring workflow complete.`)\n\t\t\t\t\tconst finalContext = await context.toJSON()\n\t\t\t\t\tconst finalResult: WorkflowResult = {\n\t\t\t\t\t\tcontext: finalContext,\n\t\t\t\t\t\tserializedContext: this.serializer.serialize(finalContext),\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t}\n\t\t\t\t\tawait this.publishFinalResult(runId, {\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t\tpayload: finalResult,\n\t\t\t\t\t})\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[Adapter] Terminal node '${nodeId}' completed for Run ID '${runId}', but other terminal nodes are still running.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst nextNodes = await this.runtime.determineNextNodes(blueprint, nodeId, result, context, runId)\n\n\t\t\t// stop if a branch terminates but it wasn't a terminal node\n\t\t\tif (nextNodes.length === 0 && !isTerminalNode) {\n\t\t\t\tconsole.log(\n\t\t\t\t\t`[Adapter] Non-terminal node '${nodeId}' reached end of branch for Run ID '${runId}'. This branch will now terminate.`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor (const { node: nextNodeDef, edge } of nextNodes) {\n\t\t\t\tawait this.runtime.applyEdgeTransform(edge, result, nextNodeDef, context)\n\t\t\t\tconst isReady = await this.isReadyForFanIn(runId, blueprint, nextNodeDef.id)\n\t\t\t\tif (isReady) {\n\t\t\t\t\tconsole.log(`[Adapter] Node '${nextNodeDef.id}' is ready. Enqueuing job.`)\n\t\t\t\t\tawait this.enqueueJob({ runId, blueprintId, nodeId: nextNodeDef.id })\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`[Adapter] Node '${nextNodeDef.id}' is waiting for other predecessors to complete.`)\n\t\t\t\t}\n\t\t\t}\n\t\t} catch (error: any) {\n\t\t\tconst reason = error.message || 'Unknown execution error'\n\t\t\tconsole.error(`[Adapter] FATAL: Job for node '${nodeId}' failed for Run ID '${runId}': ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\tawait this.writePoisonPillForSuccessors(runId, blueprint, nodeId)\n\t\t}\n\t}\n\n\t/**\n\t * Encapsulates the fan-in join logic using the coordination store.\n\t */\n\tprotected async isReadyForFanIn(runId: string, blueprint: WorkflowBlueprint, targetNodeId: string): Promise<boolean> {\n\t\tconst targetNode = blueprint.nodes.find((n) => n.id === targetNodeId)\n\t\tif (!targetNode) {\n\t\t\tthrow new Error(`Node '${targetNodeId}' not found in blueprint`)\n\t\t}\n\t\tconst joinStrategy = targetNode.config?.joinStrategy || 'all'\n\t\tconst predecessors = blueprint.edges.filter((e) => e.target === targetNodeId)\n\n\t\tif (predecessors.length <= 1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${targetNodeId}`\n\t\tconst isPoisoned = !(await this.store.setIfNotExist(poisonKey, 'poisoned', 3600))\n\t\tif (isPoisoned) {\n\t\t\tconsole.log(`[Adapter] Node '${targetNodeId}' is poisoned due to failed predecessor. Failing immediately.`)\n\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to poisoned predecessor in run '${runId}'`)\n\t\t}\n\n\t\tif (joinStrategy === 'any') {\n\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${targetNodeId}`\n\t\t\tconst isLocked = await this.store.setIfNotExist(lockKey, 'locked', 3600)\n\t\t\tif (!isLocked) {\n\t\t\t\t// Check if it's cancelled\n\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${targetNodeId}`\n\t\t\t\tconst isCancelled = !(await this.store.setIfNotExist(cancelKey, 'cancelled', 3600))\n\t\t\t\tif (isCancelled) {\n\t\t\t\t\tconsole.log(`[Adapter] Node '${targetNodeId}' is cancelled due to failed predecessor. Failing immediately.`)\n\t\t\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to cancelled predecessor in run '${runId}'`)\n\t\t\t\t}\n\t\t\t\treturn false // Already locked by another predecessor\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tconst fanInKey = `flowcraft:fanin:${runId}:${targetNodeId}`\n\t\t\tconst readyCount = await this.store.increment(fanInKey, 3600)\n\t\t\tif (readyCount >= predecessors.length) {\n\t\t\t\tawait this.store.delete(fanInKey)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles the state of a workflow run. It inspects the persisted\n\t * context to find completed nodes, determines the next set of executable\n\t * nodes (the frontier), and enqueues jobs for them if they aren't\n\t * already running. This is the core of the resume functionality.\n\t *\n\t * @param runId The unique ID of the workflow execution to reconcile.\n\t * @returns The set of node IDs that were enqueued for execution.\n\t */\n\tpublic async reconcile(runId: string): Promise<Set<string>> {\n\t\tconst context = this.createContext(runId)\n\t\tlet blueprintId = (await context.get('blueprintId' as any)) as string | undefined\n\n\t\tif (!blueprintId) {\n\t\t\t// fallback to coordination store\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tblueprintId = await this.store.get(blueprintKey)\n\t\t\tif (blueprintId) {\n\t\t\t\t// set it back in context for future use\n\t\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': blueprintId not found in context or coordination store.`)\n\t\t\t}\n\t\t}\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (!blueprint) {\n\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': Blueprint with ID '${blueprintId}' not found.`)\n\t\t}\n\n\t\tconst state = await context.toJSON()\n\t\t// filter out internal keys\n\t\tconst completedNodes = new Set(Object.keys(state).filter((k) => blueprint.nodes.some((n) => n.id === k)))\n\n\t\tconst frontier = this.calculateResumedFrontier(blueprint, completedNodes)\n\n\t\tconst enqueuedNodes = new Set<string>()\n\t\tfor (const nodeId of frontier) {\n\t\t\tconst nodeDef = blueprint.nodes.find((n) => n.id === nodeId)\n\t\t\tconst joinStrategy = nodeDef?.config?.joinStrategy || 'all'\n\n\t\t\tlet shouldEnqueue = false\n\n\t\t\tif (joinStrategy === 'any') {\n\t\t\t\t// acquire the permanent join lock\n\t\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked-by-reconcile', 3600)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`[Adapter] Reconciling: Node '${nodeId}' is an 'any' join and is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// 'all' joins and single-predecessor nodes use a temporary lock\n\t\t\t\tconst lockKey = `flowcraft:nodelock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked', 120)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tconsole.log(`[Adapter] Reconciling: Node '${nodeId}' is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (shouldEnqueue) {\n\t\t\t\tconsole.log(`[Adapter] Reconciling: Enqueuing ready job for node '${nodeId}'`, { runId })\n\t\t\t\tawait this.enqueueJob({ runId, blueprintId: blueprint.id, nodeId })\n\t\t\t\tenqueuedNodes.add(nodeId)\n\t\t\t}\n\t\t}\n\n\t\treturn enqueuedNodes\n\t}\n\n\tprivate calculateResumedFrontier(blueprint: WorkflowBlueprint, completedNodes: Set<string>): Set<string> {\n\t\tconst newFrontier = new Set<string>()\n\t\tconst allPredecessors = new Map<string, Set<string>>()\n\t\t// (logic extracted from the GraphTraverser)\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tallPredecessors.set(node.id, new Set())\n\t\t}\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tallPredecessors.get(edge.target)?.add(edge.source)\n\t\t}\n\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (completedNodes.has(node.id)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst predecessors = allPredecessors.get(node.id) ?? new Set()\n\t\t\tif (predecessors.size === 0 && !completedNodes.has(node.id)) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst joinStrategy = node.config?.joinStrategy || 'all'\n\t\t\tconst completedPredecessors = [...predecessors].filter((p) => completedNodes.has(p))\n\n\t\t\tconst isReady =\n\t\t\t\tjoinStrategy === 'any' ? completedPredecessors.length > 0 : completedPredecessors.length === predecessors.size\n\n\t\t\tif (isReady) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t}\n\t\t}\n\t\treturn newFrontier\n\t}\n\n\t/**\n\t * Writes a poison pill for 'all' join successors and a cancellation pill for 'any' join successors of a failed node to prevent stalling or ambiguous states.\n\t */\n\tprivate async writePoisonPillForSuccessors(\n\t\trunId: string,\n\t\tblueprint: WorkflowBlueprint,\n\t\tfailedNodeId: string,\n\t): Promise<void> {\n\t\tconst successors = blueprint.edges\n\t\t\t.filter((edge) => edge.source === failedNodeId)\n\t\t\t.map((edge) => edge.target)\n\t\t\t.map((targetId) => blueprint.nodes.find((node) => node.id === targetId))\n\t\t\t.filter((node) => node)\n\n\t\tfor (const successor of successors) {\n\t\t\tif (successor) {\n\t\t\t\tconst joinStrategy = successor.config?.joinStrategy || 'all'\n\t\t\t\tif (joinStrategy === 'all') {\n\t\t\t\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(poisonKey, 'poisoned', 3600)\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[Adapter] Wrote poison pill for 'all' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t} else if (joinStrategy === 'any') {\n\t\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(cancelKey, 'cancelled', 3600)\n\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t`[Adapter] Wrote cancellation pill for 'any' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -6,16 +6,16 @@ var Context = class {
|
|
|
6
6
|
this.data = new Map(Object.entries(initialData));
|
|
7
7
|
}
|
|
8
8
|
get(key) {
|
|
9
|
-
return this.data.get(
|
|
9
|
+
return this.data.get(key);
|
|
10
10
|
}
|
|
11
11
|
set(key, value) {
|
|
12
|
-
this.data.set(
|
|
12
|
+
this.data.set(key, value);
|
|
13
13
|
}
|
|
14
14
|
has(key) {
|
|
15
|
-
return this.data.has(
|
|
15
|
+
return this.data.has(key);
|
|
16
16
|
}
|
|
17
17
|
delete(key) {
|
|
18
|
-
return this.data.delete(
|
|
18
|
+
return this.data.delete(key);
|
|
19
19
|
}
|
|
20
20
|
toJSON() {
|
|
21
21
|
return Object.fromEntries(this.data);
|
|
@@ -45,5 +45,5 @@ var AsyncContextView = class {
|
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
export { AsyncContextView, Context };
|
|
48
|
-
//# sourceMappingURL=chunk-
|
|
49
|
-
//# sourceMappingURL=chunk-
|
|
48
|
+
//# sourceMappingURL=chunk-R3HQXIEL.js.map
|
|
49
|
+
//# sourceMappingURL=chunk-R3HQXIEL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/context.ts"],"names":[],"mappings":";AAKO,IAAM,UAAN,MAAsF;AAAA,EAC5E,IAAA,GAAO,MAAA;AAAA,EACf,IAAA;AAAA,EAER,WAAA,CAAY,WAAA,GAAiC,EAAC,EAAG;AAChD,IAAA,IAAA,CAAK,OAAO,IAAI,GAAA,CAAI,MAAA,CAAO,OAAA,CAAQ,WAAW,CAAC,CAAA;AAAA,EAChD;AAAA,EAGA,IAAI,GAAA,EAA8B;AACjC,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AAAA,EACzB;AAAA,EAGA,GAAA,CAAI,KAAa,KAAA,EAAkB;AAClC,IAAA,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,EACzB;AAAA,EAGA,IAAI,GAAA,EAAsB;AACzB,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,GAAA,CAAI,GAAG,CAAA;AAAA,EACzB;AAAA,EAGA,OAAO,GAAA,EAAsB;AAC5B,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,MAAA,CAAO,GAAG,CAAA;AAAA,EAC5B;AAAA,EAEA,MAAA,GAA8B;AAC7B,IAAA,OAAO,MAAA,CAAO,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA;AAAA,EACpC;AACD;AAMO,IAAM,mBAAN,MAAgG;AAAA,EAGtG,YAAoB,WAAA,EAAqC;AAArC,IAAA,IAAA,CAAA,WAAA,GAAA,WAAA;AAAA,EAAsC;AAAA,EAF1C,IAAA,GAAO,OAAA;AAAA,EAKvB,IAAI,GAAA,EAAuC;AAC1C,IAAA,OAAO,QAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,EACjD;AAAA,EAGA,GAAA,CAAI,KAAa,KAAA,EAA2B;AAC3C,IAAA,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAC/B,IAAA,OAAO,QAAQ,OAAA,EAAQ;AAAA,EACxB;AAAA,EAGA,IAAI,GAAA,EAA+B;AAClC,IAAA,OAAO,QAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,EACjD;AAAA,EAGA,OAAO,GAAA,EAA+B;AACrC,IAAA,OAAO,QAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,MAAA,CAAO,GAAG,CAAC,CAAA;AAAA,EACpD;AAAA,EAEA,MAAA,GAAuC;AACtC,IAAA,OAAO,OAAA,CAAQ,OAAA,CAAQ,IAAA,CAAK,WAAA,CAAY,QAAQ,CAAA;AAAA,EACjD;AACD","file":"chunk-R3HQXIEL.js","sourcesContent":["import type { IAsyncContext, ISyncContext } from './types'\n\n/**\n * A default, high-performance, in-memory implementation of ISyncContext using a Map.\n */\nexport class Context<TContext extends Record<string, any>> implements ISyncContext<TContext> {\n\tpublic readonly type = 'sync' as const\n\tprivate data: Map<string, any>\n\n\tconstructor(initialData: Partial<TContext> = {}) {\n\t\tthis.data = new Map(Object.entries(initialData))\n\t}\n\n\tget<K extends keyof TContext>(key: K): TContext[K] | undefined\n\tget(key: string): any | undefined {\n\t\treturn this.data.get(key)\n\t}\n\n\tset<K extends keyof TContext>(key: K, value: TContext[K]): void\n\tset(key: string, value: any): void {\n\t\tthis.data.set(key, value)\n\t}\n\n\thas<K extends keyof TContext>(key: K): boolean\n\thas(key: string): boolean {\n\t\treturn this.data.has(key)\n\t}\n\n\tdelete<K extends keyof TContext>(key: K): boolean\n\tdelete(key: string): boolean {\n\t\treturn this.data.delete(key)\n\t}\n\n\ttoJSON(): Record<string, any> {\n\t\treturn Object.fromEntries(this.data)\n\t}\n}\n\n/**\n * An adapter that provides a consistent, Promise-based view of a synchronous context.\n * This is created by the runtime and is transparent to the node author.\n */\nexport class AsyncContextView<TContext extends Record<string, any>> implements IAsyncContext<TContext> {\n\tpublic readonly type = 'async' as const\n\n\tconstructor(private syncContext: ISyncContext<TContext>) {}\n\n\tget<K extends keyof TContext>(key: K): Promise<TContext[K] | undefined>\n\tget(key: string): Promise<any | undefined> {\n\t\treturn Promise.resolve(this.syncContext.get(key))\n\t}\n\n\tset<K extends keyof TContext>(key: K, value: TContext[K]): Promise<void>\n\tset(key: string, value: any): Promise<void> {\n\t\tthis.syncContext.set(key, value)\n\t\treturn Promise.resolve()\n\t}\n\n\thas<K extends keyof TContext>(key: K): Promise<boolean>\n\thas(key: string): Promise<boolean> {\n\t\treturn Promise.resolve(this.syncContext.has(key))\n\t}\n\n\tdelete<K extends keyof TContext>(key: K): Promise<boolean>\n\tdelete(key: string): Promise<boolean> {\n\t\treturn Promise.resolve(this.syncContext.delete(key))\n\t}\n\n\ttoJSON(): Promise<Record<string, any>> {\n\t\treturn Promise.resolve(this.syncContext.toJSON())\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Context } from './chunk-
|
|
1
|
+
import { Context } from './chunk-R3HQXIEL.js';
|
|
2
2
|
|
|
3
3
|
// src/runtime/state.ts
|
|
4
4
|
var WorkflowState = class {
|
|
@@ -57,5 +57,5 @@ var WorkflowState = class {
|
|
|
57
57
|
};
|
|
58
58
|
|
|
59
59
|
export { WorkflowState };
|
|
60
|
-
//# sourceMappingURL=chunk-
|
|
61
|
-
//# sourceMappingURL=chunk-
|
|
60
|
+
//# sourceMappingURL=chunk-VSGQDLBF.js.map
|
|
61
|
+
//# sourceMappingURL=chunk-VSGQDLBF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/runtime/state.ts"],"names":[],"mappings":";;;AAGO,IAAM,gBAAN,MAA0D;AAAA,EACxD,eAAA,uBAAsB,GAAA,EAAY;AAAA,EAClC,SAA0B,EAAC;AAAA,EAC3B,mBAAA,GAAsB,KAAA;AAAA,EACtB,OAAA;AAAA,EAER,YAAY,WAAA,EAAgC;AAC3C,IAAA,IAAA,CAAK,OAAA,GAAU,IAAI,OAAA,CAAkB,WAAW,CAAA;AAAA,EACjD;AAAA,EAEA,gBAAA,CAAiB,QAAgB,MAAA,EAAa;AAC7C,IAAA,IAAA,CAAK,eAAA,CAAgB,IAAI,MAAM,CAAA;AAC/B,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,MAAA,EAAQ,MAAM,CAAA;AAAA,EAChC;AAAA,EAEA,QAAA,CAAS,QAAgB,KAAA,EAAc;AACtC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK;AAAA,MAChB,MAAA;AAAA,MACA,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,aAAA,EAAe,KAAA;AAAA,MACf,SAAA,EAAA,iBAAW,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AAAA,MAClC,OAAO,KAAA,CAAM;AAAA,KACb,CAAA;AAAA,EACF;AAAA,EAEA,WAAW,MAAA,EAAgB;AAC1B,IAAA,IAAA,CAAK,MAAA,GAAS,KAAK,MAAA,CAAO,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,CAAI,WAAW,MAAM,CAAA;AAAA,EAChE;AAAA,EAEA,oBAAA,GAAuB;AACtB,IAAA,IAAA,CAAK,mBAAA,GAAsB,IAAA;AAAA,EAC5B;AAAA,EAEA,UAAA,GAA8C;AAC7C,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACb;AAAA,EAEA,iBAAA,GAAiC;AAChC,IAAA,OAAO,IAAI,GAAA,CAAI,IAAA,CAAK,eAAe,CAAA;AAAA,EACpC;AAAA,EAEA,SAAA,GAA6B;AAC5B,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACb;AAAA,EAEA,sBAAA,GAAkC;AACjC,IAAA,OAAO,IAAA,CAAK,mBAAA;AAAA,EACb;AAAA,EAEA,SAAA,CAAU,YAAyB,gBAAA,EAAyD;AAC3F,IAAA,IAAI,IAAA,CAAK,qBAAqB,OAAO,WAAA;AACrC,IAAA,IAAI,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,OAAO,QAAA;AAEnC,IAAA,OAAO,IAAA,CAAK,eAAA,CAAgB,IAAA,GAAO,UAAA,CAAW,OAAO,SAAA,GAAY,WAAA;AAAA,EAClE;AAAA,EAEA,SAAS,UAAA,EAAmD;AAC3D,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAO;AACxC,IAAA,OAAO;AAAA,MACN,OAAA,EAAS,WAAA;AAAA,MACT,iBAAA,EAAmB,UAAA,CAAW,SAAA,CAAU,WAAW,CAAA;AAAA,MACnD,MAAA,EAAQ,KAAK,SAAA,iBAAU,IAAI,KAAI,kBAAG,IAAI,KAAK,CAAA;AAAA,MAC3C,QAAQ,IAAA,CAAK,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,KAAK,MAAA,GAAS;AAAA,KAChD;AAAA,EACD;AACD","file":"chunk-VSGQDLBF.js","sourcesContent":["import { Context } from '../context'\nimport type { ContextImplementation, ISerializer, WorkflowError, WorkflowResult } from '../types'\n\nexport class WorkflowState<TContext extends Record<string, any>> {\n\tprivate _completedNodes = new Set<string>()\n\tprivate errors: WorkflowError[] = []\n\tprivate anyFallbackExecuted = false\n\tprivate context: ContextImplementation<TContext>\n\n\tconstructor(initialData: Partial<TContext>) {\n\t\tthis.context = new Context<TContext>(initialData)\n\t}\n\n\taddCompletedNode(nodeId: string, output: any) {\n\t\tthis._completedNodes.add(nodeId)\n\t\tthis.context.set(nodeId, output)\n\t}\n\n\taddError(nodeId: string, error: Error) {\n\t\tthis.errors.push({\n\t\t\tnodeId,\n\t\t\tmessage: error.message,\n\t\t\toriginalError: error,\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t\tstack: error.stack,\n\t\t})\n\t}\n\n\tclearError(nodeId: string) {\n\t\tthis.errors = this.errors.filter((err) => err.nodeId !== nodeId)\n\t}\n\n\tmarkFallbackExecuted() {\n\t\tthis.anyFallbackExecuted = true\n\t}\n\n\tgetContext(): ContextImplementation<TContext> {\n\t\treturn this.context\n\t}\n\n\tgetCompletedNodes(): Set<string> {\n\t\treturn new Set(this._completedNodes)\n\t}\n\n\tgetErrors(): WorkflowError[] {\n\t\treturn this.errors\n\t}\n\n\tgetAnyFallbackExecuted(): boolean {\n\t\treturn this.anyFallbackExecuted\n\t}\n\n\tgetStatus(allNodeIds: Set<string>, _fallbackNodeIds: Set<string>): WorkflowResult['status'] {\n\t\tif (this.anyFallbackExecuted) return 'completed'\n\t\tif (this.errors.length > 0) return 'failed'\n\t\t// const _remainingNodes = [...allNodeIds].filter((id) => !this._completedNodes.has(id) && !fallbackNodeIds.has(id))\n\t\treturn this._completedNodes.size < allNodeIds.size ? 'stalled' : 'completed'\n\t}\n\n\ttoResult(serializer: ISerializer): WorkflowResult<TContext> {\n\t\tconst contextJSON = this.context.toJSON() as TContext\n\t\treturn {\n\t\t\tcontext: contextJSON,\n\t\t\tserializedContext: serializer.serialize(contextJSON),\n\t\t\tstatus: this.getStatus(new Set(), new Set()),\n\t\t\terrors: this.errors.length > 0 ? this.errors : undefined,\n\t\t}\n\t}\n}\n"]}
|
|
@@ -16,11 +16,13 @@ var Flow = class {
|
|
|
16
16
|
functionRegistry;
|
|
17
17
|
loopControllerIds;
|
|
18
18
|
loopDefinitions;
|
|
19
|
+
cycleEntryPoints;
|
|
19
20
|
constructor(id) {
|
|
20
21
|
this.blueprint = { id, nodes: [], edges: [] };
|
|
21
22
|
this.functionRegistry = /* @__PURE__ */ new Map();
|
|
22
23
|
this.loopControllerIds = /* @__PURE__ */ new Map();
|
|
23
24
|
this.loopDefinitions = [];
|
|
25
|
+
this.cycleEntryPoints = /* @__PURE__ */ new Map();
|
|
24
26
|
}
|
|
25
27
|
node(id, implementation, options) {
|
|
26
28
|
let usesKey;
|
|
@@ -43,12 +45,12 @@ var Flow = class {
|
|
|
43
45
|
/**
|
|
44
46
|
* Creates a batch processing pattern.
|
|
45
47
|
* It takes an input array, runs a worker node on each item in parallel, and gathers the results.
|
|
48
|
+
* This method augments the Flow's TContext with a new key for the output array.
|
|
49
|
+
*
|
|
46
50
|
* @param id The base ID for this batch operation.
|
|
47
51
|
* @param worker The node implementation to run on each item.
|
|
48
52
|
* @param options Configuration for the batch operation.
|
|
49
|
-
* @
|
|
50
|
-
* @param options.outputKey The key in the context where the array of results will be stored.
|
|
51
|
-
* @returns The Flow instance for chaining.
|
|
53
|
+
* @returns The Flow instance with an updated context type for chaining.
|
|
52
54
|
*/
|
|
53
55
|
batch(id, worker, options) {
|
|
54
56
|
const { inputKey, outputKey } = options;
|
|
@@ -67,13 +69,13 @@ var Flow = class {
|
|
|
67
69
|
uses: "batch-scatter",
|
|
68
70
|
// built-in
|
|
69
71
|
inputs: inputKey,
|
|
70
|
-
params: { workerUsesKey, outputKey, gatherNodeId: gatherId }
|
|
72
|
+
params: { workerUsesKey, outputKey, gatherNodeId: gatherId, chunkSize: options.chunkSize }
|
|
71
73
|
});
|
|
72
74
|
this.blueprint.nodes?.push({
|
|
73
75
|
id: gatherId,
|
|
74
76
|
uses: "batch-gather",
|
|
75
77
|
// built-in
|
|
76
|
-
params: { outputKey },
|
|
78
|
+
params: { outputKey, gatherNodeId: gatherId },
|
|
77
79
|
config: { joinStrategy: "all" }
|
|
78
80
|
// important: must wait for all scattered jobs
|
|
79
81
|
});
|
|
@@ -116,6 +118,15 @@ var Flow = class {
|
|
|
116
118
|
}
|
|
117
119
|
return controllerId;
|
|
118
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Sets the preferred entry point for a cycle in non-DAG workflows.
|
|
123
|
+
* This helps remove ambiguity when the runtime needs to choose a starting node for cycles.
|
|
124
|
+
* @param nodeId The ID of the node to use as the entry point for cycles containing this node.
|
|
125
|
+
*/
|
|
126
|
+
setCycleEntryPoint(nodeId) {
|
|
127
|
+
this.cycleEntryPoints.set(nodeId, nodeId);
|
|
128
|
+
return this;
|
|
129
|
+
}
|
|
119
130
|
toBlueprint() {
|
|
120
131
|
if (!this.blueprint.nodes || this.blueprint.nodes.length === 0) {
|
|
121
132
|
throw new Error("Cannot build a blueprint with no nodes.");
|
|
@@ -132,6 +143,12 @@ var Flow = class {
|
|
|
132
143
|
startNode.config = { ...startNode.config, joinStrategy: "any" };
|
|
133
144
|
endNode.config = { ...endNode.config, joinStrategy: "any" };
|
|
134
145
|
}
|
|
146
|
+
if (this.cycleEntryPoints.size > 0) {
|
|
147
|
+
this.blueprint.metadata = {
|
|
148
|
+
...this.blueprint.metadata,
|
|
149
|
+
cycleEntryPoints: Array.from(this.cycleEntryPoints.keys())
|
|
150
|
+
};
|
|
151
|
+
}
|
|
135
152
|
return this.blueprint;
|
|
136
153
|
}
|
|
137
154
|
getFunctionRegistry() {
|
|
@@ -143,5 +160,5 @@ function createFlow(id) {
|
|
|
143
160
|
}
|
|
144
161
|
|
|
145
162
|
export { Flow, createFlow };
|
|
146
|
-
//# sourceMappingURL=chunk-
|
|
147
|
-
//# sourceMappingURL=chunk-
|
|
163
|
+
//# sourceMappingURL=chunk-ZCHFZBGL.js.map
|
|
164
|
+
//# sourceMappingURL=chunk-ZCHFZBGL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/flow.ts"],"names":[],"mappings":"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","file":"chunk-ZCHFZBGL.js","sourcesContent":["import { isNodeClass } from './node'\nimport type { EdgeDefinition, NodeClass, NodeDefinition, NodeFunction, WorkflowBlueprint } from './types'\n\n/**\n * Generates a deterministic hash for a function based on its source code.\n */\nfunction _hashFunction(fn: NodeFunction<any, any, any, any, any> | NodeClass<any, any, any, any, any>): string {\n\tconst source = fn.toString()\n\tlet hash = 0\n\tfor (let i = 0; i < source.length; i++) {\n\t\tconst char = source.charCodeAt(i)\n\t\thash = (hash << 5) - hash + char\n\t\thash = hash & hash // Convert to 32-bit integer\n\t}\n\treturn Math.abs(hash).toString(16)\n}\n\n/**\n * A fluent API for programmatically constructing a WorkflowBlueprint.\n */\nexport class Flow<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n> {\n\tprivate blueprint: Partial<WorkflowBlueprint>\n\tprivate functionRegistry: Map<string, NodeFunction | NodeClass>\n\tprivate loopControllerIds: Map<string, string>\n\tprivate loopDefinitions: Array<{\n\t\tid: string\n\t\tstartNodeId: string\n\t\tendNodeId: string\n\t}>\n\tprivate cycleEntryPoints: Map<string, string>\n\n\tconstructor(id: string) {\n\t\tthis.blueprint = { id, nodes: [], edges: [] }\n\t\tthis.functionRegistry = new Map()\n\t\tthis.loopControllerIds = new Map()\n\t\tthis.loopDefinitions = []\n\t\tthis.cycleEntryPoints = new Map()\n\t}\n\n\tnode<TInput = any, TOutput = any, TAction extends string = string>(\n\t\tid: string,\n\t\timplementation:\n\t\t\t| NodeFunction<TContext, TDependencies, TInput, TOutput, TAction>\n\t\t\t| NodeClass<TContext, TDependencies, TInput, TOutput, TAction>,\n\t\toptions?: Omit<NodeDefinition, 'id' | 'uses'>,\n\t): this {\n\t\tlet usesKey: string\n\n\t\tif (isNodeClass(implementation)) {\n\t\t\tusesKey =\n\t\t\t\timplementation.name && implementation.name !== 'BaseNode'\n\t\t\t\t\t? implementation.name\n\t\t\t\t\t: `class_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation)\n\t\t} else {\n\t\t\tusesKey = `fn_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation as unknown as NodeFunction)\n\t\t}\n\n\t\tconst nodeDef: NodeDefinition = { id, uses: usesKey, ...options }\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\tedge(source: string, target: string, options?: Omit<EdgeDefinition, 'source' | 'target'>): this {\n\t\tconst edgeDef: EdgeDefinition = { source, target, ...options }\n\t\tthis.blueprint.edges?.push(edgeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a batch processing pattern.\n\t * It takes an input array, runs a worker node on each item in parallel, and gathers the results.\n\t * This method augments the Flow's TContext with a new key for the output array.\n\t *\n\t * @param id The base ID for this batch operation.\n\t * @param worker The node implementation to run on each item.\n\t * @param options Configuration for the batch operation.\n\t * @returns The Flow instance with an updated context type for chaining.\n\t */\n\tbatch<TWorkerInput, TWorkerOutput, TWorkerAction extends string, TOutputKey extends string>(\n\t\tid: string,\n\t\tworker:\n\t\t\t| NodeFunction<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>\n\t\t\t| NodeClass<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>,\n\t\toptions: {\n\t\t\t/** The key in the context that holds the input array for the batch. */\n\t\t\tinputKey: keyof TContext\n\t\t\t/** The key in the context where the array of results will be stored. */\n\t\t\toutputKey: TOutputKey\n\t\t\t/** The number of items to process in each chunk to limit memory usage. */\n\t\t\tchunkSize?: number\n\t\t},\n\t): Flow<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies> {\n\t\tconst { inputKey, outputKey } = options\n\t\tconst scatterId = `${id}_scatter`\n\t\tconst gatherId = `${id}_gather`\n\n\t\t// register worker implementation under a unique key.\n\t\tlet workerUsesKey: string\n\t\tif (isNodeClass(worker)) {\n\t\t\tworkerUsesKey =\n\t\t\t\tworker.name && worker.name !== 'BaseNode' ? worker.name : `class_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker)\n\t\t} else {\n\t\t\tworkerUsesKey = `fn_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker as unknown as NodeFunction)\n\t\t}\n\n\t\t// scatter node: takes an array and dynamically schedules worker nodes\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: scatterId,\n\t\t\tuses: 'batch-scatter', // built-in\n\t\t\tinputs: inputKey as string,\n\t\t\tparams: { workerUsesKey, outputKey: outputKey as string, gatherNodeId: gatherId, chunkSize: options.chunkSize },\n\t\t})\n\n\t\t// gather node: waits for all workers to finish and collects the results\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: gatherId,\n\t\t\tuses: 'batch-gather', // built-in\n\t\t\tparams: { outputKey, gatherNodeId: gatherId },\n\t\t\tconfig: { joinStrategy: 'all' }, // important: must wait for all scattered jobs\n\t\t})\n\n\t\t// edge to connect scatter and gather nodes, orchestrator will manage dynamic workers\n\t\tthis.edge(scatterId, gatherId)\n\n\t\treturn this as unknown as Flow<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies>\n\t}\n\n\t/**\n\t * Creates a loop pattern in the workflow graph.\n\t * @param id A unique identifier for the loop construct.\n\t * @param options Defines the start, end, and continuation condition of the loop.\n\t * @param options.startNodeId The ID of the first node inside the loop body.\n\t * @param options.endNodeId The ID of the last node inside the loop body.\n\t * @param options.condition An expression that, if true, causes the loop to run again.\n\t */\n\tloop(\n\t\tid: string,\n\t\toptions: {\n\t\t\t/** The ID of the first node inside the loop body. */\n\t\t\tstartNodeId: string\n\t\t\t/** The ID of the last node inside the loop body. */\n\t\t\tendNodeId: string\n\t\t\t/** An expression that, if true, causes the loop to run again. */\n\t\t\tcondition: string\n\t\t},\n\t): this {\n\t\tconst { startNodeId, endNodeId, condition } = options\n\t\tconst controllerId = `${id}-loop`\n\n\t\tthis.loopControllerIds.set(id, controllerId)\n\n\t\tthis.loopDefinitions.push({ id, startNodeId, endNodeId })\n\n\t\t// controller node: evaluates the loop condition\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: controllerId,\n\t\t\tuses: 'loop-controller', // built-in\n\t\t\tparams: { condition },\n\t\t\tconfig: { joinStrategy: 'any' }, // to allow re-execution on each loop iteration\n\t\t})\n\n\t\tthis.edge(endNodeId, controllerId)\n\n\t\tthis.edge(controllerId, startNodeId, {\n\t\t\taction: 'continue',\n\t\t\ttransform: `context.${endNodeId}`, // pass the end node's value to the start node\n\t\t})\n\n\t\treturn this\n\t}\n\n\tgetLoopControllerId(id: string): string {\n\t\tconst controllerId = this.loopControllerIds.get(id)\n\t\tif (!controllerId) {\n\t\t\tthrow new Error(`Loop with id '${id}' not found. Ensure you have defined it using the .loop() method.`)\n\t\t}\n\t\treturn controllerId\n\t}\n\n\t/**\n\t * Sets the preferred entry point for a cycle in non-DAG workflows.\n\t * This helps remove ambiguity when the runtime needs to choose a starting node for cycles.\n\t * @param nodeId The ID of the node to use as the entry point for cycles containing this node.\n\t */\n\tsetCycleEntryPoint(nodeId: string): this {\n\t\tthis.cycleEntryPoints.set(nodeId, nodeId)\n\t\treturn this\n\t}\n\n\ttoBlueprint(): WorkflowBlueprint {\n\t\tif (!this.blueprint.nodes || this.blueprint.nodes.length === 0) {\n\t\t\tthrow new Error('Cannot build a blueprint with no nodes.')\n\t\t}\n\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst startNode = this.blueprint.nodes?.find((n) => n.id === loopDef.startNodeId)\n\t\t\tconst endNode = this.blueprint.nodes?.find((n) => n.id === loopDef.endNodeId)\n\n\t\t\tif (!startNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent start node '${loopDef.startNodeId}'.`)\n\t\t\t}\n\t\t\tif (!endNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent end node '${loopDef.endNodeId}'.`)\n\t\t\t}\n\n\t\t\tstartNode.config = { ...startNode.config, joinStrategy: 'any' }\n\t\t\tendNode.config = { ...endNode.config, joinStrategy: 'any' }\n\t\t}\n\n\t\tif (this.cycleEntryPoints.size > 0) {\n\t\t\tthis.blueprint.metadata = {\n\t\t\t\t...this.blueprint.metadata,\n\t\t\t\tcycleEntryPoints: Array.from(this.cycleEntryPoints.keys()),\n\t\t\t}\n\t\t}\n\n\t\treturn this.blueprint as WorkflowBlueprint\n\t}\n\n\tgetFunctionRegistry() {\n\t\treturn this.functionRegistry\n\t}\n}\n\n/**\n * Helper function to create a new Flow builder instance.\n */\nexport function createFlow<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n>(id: string): Flow<TContext, TDependencies> {\n\treturn new Flow(id)\n}\n"]}
|
package/dist/context.d.ts
CHANGED
package/dist/context.js
CHANGED
package/dist/evaluator.d.ts
CHANGED
package/dist/flow.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as NodeFunction, e as NodeClass, N as NodeDefinition, E as EdgeDefinition, W as WorkflowBlueprint } from './types-
|
|
1
|
+
import { d as NodeFunction, e as NodeClass, N as NodeDefinition, E as EdgeDefinition, W as WorkflowBlueprint } from './types-CsTeXTiA.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* A fluent API for programmatically constructing a WorkflowBlueprint.
|
|
@@ -8,25 +8,30 @@ declare class Flow<TContext extends Record<string, any> = Record<string, any>, T
|
|
|
8
8
|
private functionRegistry;
|
|
9
9
|
private loopControllerIds;
|
|
10
10
|
private loopDefinitions;
|
|
11
|
+
private cycleEntryPoints;
|
|
11
12
|
constructor(id: string);
|
|
12
13
|
node<TInput = any, TOutput = any, TAction extends string = string>(id: string, implementation: NodeFunction<TContext, TDependencies, TInput, TOutput, TAction> | NodeClass<TContext, TDependencies, TInput, TOutput, TAction>, options?: Omit<NodeDefinition, 'id' | 'uses'>): this;
|
|
13
14
|
edge(source: string, target: string, options?: Omit<EdgeDefinition, 'source' | 'target'>): this;
|
|
14
15
|
/**
|
|
15
16
|
* Creates a batch processing pattern.
|
|
16
17
|
* It takes an input array, runs a worker node on each item in parallel, and gathers the results.
|
|
18
|
+
* This method augments the Flow's TContext with a new key for the output array.
|
|
19
|
+
*
|
|
17
20
|
* @param id The base ID for this batch operation.
|
|
18
21
|
* @param worker The node implementation to run on each item.
|
|
19
22
|
* @param options Configuration for the batch operation.
|
|
20
|
-
* @
|
|
21
|
-
* @param options.outputKey The key in the context where the array of results will be stored.
|
|
22
|
-
* @returns The Flow instance for chaining.
|
|
23
|
+
* @returns The Flow instance with an updated context type for chaining.
|
|
23
24
|
*/
|
|
24
|
-
batch<
|
|
25
|
+
batch<TWorkerInput, TWorkerOutput, TWorkerAction extends string, TOutputKey extends string>(id: string, worker: NodeFunction<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction> | NodeClass<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>, options: {
|
|
25
26
|
/** The key in the context that holds the input array for the batch. */
|
|
26
27
|
inputKey: keyof TContext;
|
|
27
28
|
/** The key in the context where the array of results will be stored. */
|
|
28
|
-
outputKey:
|
|
29
|
-
|
|
29
|
+
outputKey: TOutputKey;
|
|
30
|
+
/** The number of items to process in each chunk to limit memory usage. */
|
|
31
|
+
chunkSize?: number;
|
|
32
|
+
}): Flow<TContext & {
|
|
33
|
+
[K in TOutputKey]: TWorkerOutput[];
|
|
34
|
+
}, TDependencies>;
|
|
30
35
|
/**
|
|
31
36
|
* Creates a loop pattern in the workflow graph.
|
|
32
37
|
* @param id A unique identifier for the loop construct.
|
|
@@ -44,6 +49,12 @@ declare class Flow<TContext extends Record<string, any> = Record<string, any>, T
|
|
|
44
49
|
condition: string;
|
|
45
50
|
}): this;
|
|
46
51
|
getLoopControllerId(id: string): string;
|
|
52
|
+
/**
|
|
53
|
+
* Sets the preferred entry point for a cycle in non-DAG workflows.
|
|
54
|
+
* This helps remove ambiguity when the runtime needs to choose a starting node for cycles.
|
|
55
|
+
* @param nodeId The ID of the node to use as the entry point for cycles containing this node.
|
|
56
|
+
*/
|
|
57
|
+
setCycleEntryPoint(nodeId: string): this;
|
|
47
58
|
toBlueprint(): WorkflowBlueprint;
|
|
48
59
|
getFunctionRegistry(): Map<string, NodeClass | NodeFunction>;
|
|
49
60
|
}
|
package/dist/flow.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -5,11 +5,12 @@ export { PropertyEvaluator, UnsafeEvaluator } from './evaluator.js';
|
|
|
5
5
|
export { Flow, createFlow } from './flow.js';
|
|
6
6
|
export { LinterIssue, LinterIssueCode, LinterResult, lintBlueprint } from './linter.js';
|
|
7
7
|
export { ConsoleLogger, NullLogger } from './logger.js';
|
|
8
|
-
export { B as BaseNode, C as ContextImplementation, E as EdgeDefinition, h as IAsyncContext, k as IEvaluator, m as IEventBus, l as ILogger, n as ISerializer, I as ISyncContext, M as Middleware, e as NodeClass, a as NodeConfig, c as NodeContext, N as NodeDefinition, d as NodeFunction, f as NodeImplementation, g as NodeRegistry, b as NodeResult, R as RuntimeDependencies, j as RuntimeOptions, W as WorkflowBlueprint, o as WorkflowError, p as WorkflowResult, i as isNodeClass } from './types-
|
|
8
|
+
export { B as BaseNode, C as ContextImplementation, E as EdgeDefinition, h as IAsyncContext, k as IEvaluator, m as IEventBus, l as ILogger, n as ISerializer, I as ISyncContext, M as Middleware, e as NodeClass, a as NodeConfig, c as NodeContext, N as NodeDefinition, d as NodeFunction, f as NodeImplementation, g as NodeRegistry, b as NodeResult, R as RuntimeDependencies, j as RuntimeOptions, W as WorkflowBlueprint, o as WorkflowError, p as WorkflowResult, i as isNodeClass } from './types-CsTeXTiA.js';
|
|
9
9
|
export { AdapterOptions, BaseDistributedAdapter, ICoordinationStore, JobPayload } from './runtime/adapter.js';
|
|
10
10
|
export { BuiltInNodeExecutor, ClassNodeExecutor, ExecutionStrategy, FunctionNodeExecutor } from './runtime/executors.js';
|
|
11
11
|
export { FlowRuntime } from './runtime/runtime.js';
|
|
12
12
|
export { WorkflowState } from './runtime/state.js';
|
|
13
13
|
export { GraphTraverser } from './runtime/traverser.js';
|
|
14
14
|
export { sanitizeBlueprint } from './sanitizer.js';
|
|
15
|
+
import './runtime/builtin-keys.js';
|
|
15
16
|
import './runtime/types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import './chunk-55J6XMHW.js';
|
|
2
2
|
import './chunk-HMR2GEGE.js';
|
|
3
|
-
export { BaseDistributedAdapter } from './chunk-
|
|
4
|
-
export { FlowRuntime } from './chunk-
|
|
5
|
-
export { WorkflowState } from './chunk-
|
|
6
|
-
export { GraphTraverser } from './chunk-
|
|
3
|
+
export { BaseDistributedAdapter } from './chunk-QLGJUDQF.js';
|
|
4
|
+
export { FlowRuntime } from './chunk-OTS5YJ3S.js';
|
|
5
|
+
export { WorkflowState } from './chunk-VSGQDLBF.js';
|
|
6
|
+
export { GraphTraverser } from './chunk-GEKDR2SS.js';
|
|
7
7
|
export { sanitizeBlueprint } from './chunk-DSYAC4WB.js';
|
|
8
8
|
import './chunk-CYHZ2YVH.js';
|
|
9
9
|
export { BuiltInNodeExecutor, ClassNodeExecutor, FunctionNodeExecutor } from './chunk-M2FRTT2K.js';
|
|
10
|
-
export { AsyncContextView, Context } from './chunk-
|
|
10
|
+
export { AsyncContextView, Context } from './chunk-R3HQXIEL.js';
|
|
11
11
|
export { CancelledWorkflowError, FatalNodeExecutionError, NodeExecutionError } from './chunk-5ZXV3R5D.js';
|
|
12
12
|
export { PropertyEvaluator, UnsafeEvaluator } from './chunk-PH2IYZHV.js';
|
|
13
|
-
export { Flow, createFlow } from './chunk-
|
|
13
|
+
export { Flow, createFlow } from './chunk-ZCHFZBGL.js';
|
|
14
14
|
export { BaseNode, isNodeClass } from './chunk-U5V5O5MN.js';
|
|
15
|
-
export { lintBlueprint } from './chunk-
|
|
16
|
-
export { analyzeBlueprint, checkForCycles, generateMermaid } from './chunk-
|
|
15
|
+
export { lintBlueprint } from './chunk-EUJWJWFA.js';
|
|
16
|
+
export { analyzeBlueprint, checkForCycles, generateMermaid } from './chunk-233SESC2.js';
|
|
17
17
|
export { ConsoleLogger, NullLogger } from './chunk-4PELJWF7.js';
|
|
18
18
|
//# sourceMappingURL=index.js.map
|
|
19
19
|
//# sourceMappingURL=index.js.map
|
package/dist/linter.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { W as WorkflowBlueprint, d as NodeFunction, e as NodeClass } from './types-
|
|
1
|
+
import { W as WorkflowBlueprint, d as NodeFunction, e as NodeClass } from './types-CsTeXTiA.js';
|
|
2
2
|
|
|
3
|
-
type LinterIssueCode = 'INVALID_EDGE_SOURCE' | 'INVALID_EDGE_TARGET' | 'MISSING_NODE_IMPLEMENTATION' | 'ORPHAN_NODE';
|
|
3
|
+
type LinterIssueCode = 'INVALID_EDGE_SOURCE' | 'INVALID_EDGE_TARGET' | 'MISSING_NODE_IMPLEMENTATION' | 'ORPHAN_NODE' | 'INVALID_BATCH_WORKER_KEY' | 'INVALID_SUBFLOW_BLUEPRINT_ID';
|
|
4
4
|
interface LinterIssue {
|
|
5
5
|
code: LinterIssueCode;
|
|
6
6
|
message: string;
|
|
@@ -19,6 +19,6 @@ interface LinterResult {
|
|
|
19
19
|
* @param registry A map of node implementations (functions or classes) to check against.
|
|
20
20
|
* @returns A LinterResult object containing any issues found.
|
|
21
21
|
*/
|
|
22
|
-
declare function lintBlueprint(blueprint: WorkflowBlueprint, registry: Map<string, NodeFunction | NodeClass> | Record<string, NodeFunction | NodeClass>): LinterResult;
|
|
22
|
+
declare function lintBlueprint(blueprint: WorkflowBlueprint, registry: Map<string, NodeFunction | NodeClass> | Record<string, NodeFunction | NodeClass>, blueprints?: Record<string, WorkflowBlueprint>): LinterResult;
|
|
23
23
|
|
|
24
24
|
export { type LinterIssue, type LinterIssueCode, type LinterResult, lintBlueprint };
|
package/dist/linter.js
CHANGED
package/dist/logger.d.ts
CHANGED
package/dist/node.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { B as BaseNode, i as isNodeClass } from './types-
|
|
1
|
+
export { B as BaseNode, i as isNodeClass } from './types-CsTeXTiA.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { j as RuntimeOptions, n as ISerializer, h as IAsyncContext, p as WorkflowResult, W as WorkflowBlueprint } from '../types-
|
|
1
|
+
import { j as RuntimeOptions, n as ISerializer, h as IAsyncContext, p as WorkflowResult, W as WorkflowBlueprint } from '../types-CsTeXTiA.js';
|
|
2
2
|
import { FlowRuntime } from './runtime.js';
|
|
3
|
+
import './builtin-keys.js';
|
|
3
4
|
import './state.js';
|
|
4
5
|
import './types.js';
|
|
5
6
|
|
|
@@ -14,6 +15,8 @@ interface ICoordinationStore {
|
|
|
14
15
|
setIfNotExist: (key: string, value: string, ttlSeconds: number) => Promise<boolean>;
|
|
15
16
|
/** Deletes a key. Used for cleanup. */
|
|
16
17
|
delete: (key: string) => Promise<void>;
|
|
18
|
+
/** Gets the value of a key. */
|
|
19
|
+
get: (key: string) => Promise<string | undefined>;
|
|
17
20
|
}
|
|
18
21
|
/** Configuration options for constructing a BaseDistributedAdapter. */
|
|
19
22
|
interface AdapterOptions {
|
|
@@ -90,7 +93,7 @@ declare abstract class BaseDistributedAdapter {
|
|
|
90
93
|
reconcile(runId: string): Promise<Set<string>>;
|
|
91
94
|
private calculateResumedFrontier;
|
|
92
95
|
/**
|
|
93
|
-
* Writes a poison pill for 'all' join successors of a failed node to prevent stalling.
|
|
96
|
+
* Writes a poison pill for 'all' join successors and a cancellation pill for 'any' join successors of a failed node to prevent stalling or ambiguous states.
|
|
94
97
|
*/
|
|
95
98
|
private writePoisonPillForSuccessors;
|
|
96
99
|
}
|
package/dist/runtime/adapter.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
export { BaseDistributedAdapter } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
4
|
-
import '../chunk-
|
|
1
|
+
export { BaseDistributedAdapter } from '../chunk-QLGJUDQF.js';
|
|
2
|
+
import '../chunk-OTS5YJ3S.js';
|
|
3
|
+
import '../chunk-VSGQDLBF.js';
|
|
4
|
+
import '../chunk-GEKDR2SS.js';
|
|
5
5
|
import '../chunk-DSYAC4WB.js';
|
|
6
6
|
import '../chunk-CYHZ2YVH.js';
|
|
7
7
|
import '../chunk-M2FRTT2K.js';
|
|
8
|
-
import '../chunk-
|
|
8
|
+
import '../chunk-R3HQXIEL.js';
|
|
9
9
|
import '../chunk-5ZXV3R5D.js';
|
|
10
10
|
import '../chunk-PH2IYZHV.js';
|
|
11
11
|
import '../chunk-U5V5O5MN.js';
|
|
12
|
-
import '../chunk-
|
|
12
|
+
import '../chunk-233SESC2.js';
|
|
13
13
|
import '../chunk-4PELJWF7.js';
|
|
14
14
|
//# sourceMappingURL=adapter.js.map
|
|
15
15
|
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A registry defining the dynamic context keys set by each built-in node.
|
|
3
|
+
* This object is the single source of truth.
|
|
4
|
+
*/
|
|
5
|
+
declare const BUILTIN_KEYS: {
|
|
6
|
+
readonly 'batch-scatter': readonly ["currentIndex", "hasMore"];
|
|
7
|
+
readonly 'batch-gather': readonly ["allWorkerIds", "hasMore"];
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* A comprehensive map of all possible dynamic keys to their corresponding TypeScript types.
|
|
11
|
+
* The compiler will enforce that any key used in `BUILTIN_KEYS` must have an entry here.
|
|
12
|
+
*/
|
|
13
|
+
type DynamicKeyTypeMap = {
|
|
14
|
+
currentIndex: number;
|
|
15
|
+
hasMore: boolean;
|
|
16
|
+
allWorkerIds: string[];
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A utility type that creates a union of all possible dynamic key strings
|
|
20
|
+
* by flattening the values of the `BUILTIN_KEYS` object.
|
|
21
|
+
*
|
|
22
|
+
* Example: 'currentIndex' | 'hasMore' | 'allWorkerIds'
|
|
23
|
+
*/
|
|
24
|
+
type AllDynamicKeyStrings = (typeof BUILTIN_KEYS)[keyof typeof BUILTIN_KEYS][number];
|
|
25
|
+
/**
|
|
26
|
+
* The final, dynamically generated `DynamicKeys` type.
|
|
27
|
+
*
|
|
28
|
+
* It uses a mapped type to construct an object where:
|
|
29
|
+
* - The keys are the union of all strings from `AllDynamicKeyStrings`.
|
|
30
|
+
* - The value for each key is looked up from our central `DynamicKeyTypeMap`.
|
|
31
|
+
*
|
|
32
|
+
* This ensures that `DynamicKeys` is always perfectly in sync with `BUILTIN_KEYS`.
|
|
33
|
+
*/
|
|
34
|
+
type DynamicKeys = {
|
|
35
|
+
[K in AllDynamicKeyStrings]: DynamicKeyTypeMap[K];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { BUILTIN_KEYS, type DynamicKeys };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/runtime/builtin-keys.ts
|
|
2
|
+
var BUILTIN_KEYS = {
|
|
3
|
+
"batch-scatter": ["currentIndex", "hasMore"],
|
|
4
|
+
"batch-gather": ["allWorkerIds", "hasMore"]
|
|
5
|
+
// 'loop-controller': ['loopCounter']
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export { BUILTIN_KEYS };
|
|
9
|
+
//# sourceMappingURL=builtin-keys.js.map
|
|
10
|
+
//# sourceMappingURL=builtin-keys.js.map
|