@bian-womp/spark-graph 0.3.73 → 0.3.75
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/lib/cjs/index.cjs +161 -83
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/types.d.ts +2 -0
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +2 -2
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/RunContextManager.d.ts +6 -0
- package/lib/cjs/src/runtime/components/RunContextManager.d.ts.map +1 -1
- package/lib/esm/index.js +161 -83
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/types.d.ts +2 -0
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +2 -2
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/RunContextManager.d.ts +6 -0
- package/lib/esm/src/runtime/components/RunContextManager.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -1684,6 +1684,7 @@ class RunContextManager {
|
|
|
1684
1684
|
id,
|
|
1685
1685
|
startNodes: new Set([startNodeId]),
|
|
1686
1686
|
cancelledNodes: new Set(),
|
|
1687
|
+
pendingScheduling: 1,
|
|
1687
1688
|
pendingNodes: 0,
|
|
1688
1689
|
pendingEdges: 0,
|
|
1689
1690
|
pendingResolvers: 0,
|
|
@@ -1719,6 +1720,33 @@ class RunContextManager {
|
|
|
1719
1720
|
hasActiveRunContexts() {
|
|
1720
1721
|
return this.runContexts.size > 0;
|
|
1721
1722
|
}
|
|
1723
|
+
/**
|
|
1724
|
+
* Release one scheduling hold from a run-context.
|
|
1725
|
+
* Must be called once by the creator when scheduling decisions are complete.
|
|
1726
|
+
*/
|
|
1727
|
+
releaseScheduling(id) {
|
|
1728
|
+
const ctx = this.runContexts.get(id);
|
|
1729
|
+
if (!ctx) {
|
|
1730
|
+
this.logger.debug("release-scheduling-context-not-found", {
|
|
1731
|
+
runContextId: id,
|
|
1732
|
+
});
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
if (ctx.pendingScheduling > 0) {
|
|
1736
|
+
ctx.pendingScheduling--;
|
|
1737
|
+
}
|
|
1738
|
+
else {
|
|
1739
|
+
this.logger.warn("release-scheduling-underflow", {
|
|
1740
|
+
runContextId: id,
|
|
1741
|
+
pendingScheduling: ctx.pendingScheduling,
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
this.logger.debug("release-scheduling", {
|
|
1745
|
+
runContextId: id,
|
|
1746
|
+
pendingScheduling: ctx.pendingScheduling,
|
|
1747
|
+
});
|
|
1748
|
+
this.finishRunContextIfPossible(id);
|
|
1749
|
+
}
|
|
1722
1750
|
/**
|
|
1723
1751
|
* Increment queued work count for a run-context.
|
|
1724
1752
|
* Used by asyncConcurrency: "queue" to keep contexts alive while work is queued.
|
|
@@ -1870,7 +1898,11 @@ class RunContextManager {
|
|
|
1870
1898
|
});
|
|
1871
1899
|
return;
|
|
1872
1900
|
}
|
|
1873
|
-
if (ctx.
|
|
1901
|
+
if (ctx.pendingScheduling > 0 ||
|
|
1902
|
+
ctx.pendingNodes > 0 ||
|
|
1903
|
+
ctx.pendingEdges > 0 ||
|
|
1904
|
+
ctx.pendingResolvers > 0 ||
|
|
1905
|
+
ctx.pendingQueued > 0) {
|
|
1874
1906
|
return; // Still has pending work
|
|
1875
1907
|
}
|
|
1876
1908
|
this.logger.info("finish-run-context", {
|
|
@@ -2789,7 +2821,10 @@ class EdgePropagator {
|
|
|
2789
2821
|
*/
|
|
2790
2822
|
shouldPropagateExecution(effectiveRunContexts) {
|
|
2791
2823
|
if (!effectiveRunContexts) {
|
|
2792
|
-
|
|
2824
|
+
// Without run-context IDs, only auto mode should schedule downstream execution.
|
|
2825
|
+
// In manual mode this path is used for value refresh/re-emits (e.g. handle updates),
|
|
2826
|
+
// and should not implicitly trigger node runs.
|
|
2827
|
+
return this.runtime.getRunMode() === "auto";
|
|
2793
2828
|
}
|
|
2794
2829
|
// Check propagate flag (only in run-context mode)
|
|
2795
2830
|
for (const id of effectiveRunContexts) {
|
|
@@ -2980,13 +3015,22 @@ class NodeExecutor {
|
|
|
2980
3015
|
execute: (opts) => {
|
|
2981
3016
|
if (this.graph.allInboundHaveValue(nodeId)) {
|
|
2982
3017
|
let runContextIdsToUse = this.runtime.getRunMode() === "auto" ? undefined : runContextIds;
|
|
3018
|
+
let runContextIdToRelease;
|
|
2983
3019
|
if (this.runtime.getRunMode() === "manual" && (!runContextIds || runContextIds.size === 0)) {
|
|
2984
|
-
|
|
3020
|
+
runContextIdToRelease = this.runContextManager.createRunContext(nodeId, opts);
|
|
3021
|
+
runContextIdsToUse = new Set([runContextIdToRelease]);
|
|
3022
|
+
}
|
|
3023
|
+
try {
|
|
3024
|
+
this.execute(nodeId, {
|
|
3025
|
+
runContextIds: runContextIdsToUse,
|
|
3026
|
+
reason: opts?.reason ?? "executeFromContext",
|
|
3027
|
+
});
|
|
3028
|
+
}
|
|
3029
|
+
finally {
|
|
3030
|
+
if (runContextIdToRelease) {
|
|
3031
|
+
this.runContextManager.releaseScheduling(runContextIdToRelease);
|
|
3032
|
+
}
|
|
2985
3033
|
}
|
|
2986
|
-
this.execute(nodeId, {
|
|
2987
|
-
runContextIds: runContextIdsToUse,
|
|
2988
|
-
reason: opts?.reason ?? "executeFromContext",
|
|
2989
|
-
});
|
|
2990
3034
|
}
|
|
2991
3035
|
},
|
|
2992
3036
|
getInput: (handle) => inputs[handle],
|
|
@@ -3012,6 +3056,7 @@ class NodeExecutor {
|
|
|
3012
3056
|
*/
|
|
3013
3057
|
execute(nodeId, opts) {
|
|
3014
3058
|
let { runContextIds, canSkipHandleResolution, reason = "" } = opts ?? {};
|
|
3059
|
+
let autoCreatedRunContextId;
|
|
3015
3060
|
const node = this.graph.getNode(nodeId);
|
|
3016
3061
|
if (!node)
|
|
3017
3062
|
return;
|
|
@@ -3024,81 +3069,89 @@ class NodeExecutor {
|
|
|
3024
3069
|
if (runMode === "manual" && (!runContextIds || runContextIds.size === 0)) {
|
|
3025
3070
|
// If autoRun is true, auto-generate a run context (similar to createExecutionContext pattern)
|
|
3026
3071
|
if (node.policy?.autoRun === true) {
|
|
3027
|
-
|
|
3072
|
+
autoCreatedRunContextId = this.runContextManager.createRunContext(nodeId, { propagate: false });
|
|
3073
|
+
runContextIds = new Set([autoCreatedRunContextId]);
|
|
3028
3074
|
}
|
|
3029
3075
|
else {
|
|
3030
3076
|
console.trace(`NodeExecutor.execute[${formatNodeRef(this.graph, nodeId)}:${reason}]: no runContextIds provided in manual mode, skipping execution`);
|
|
3031
3077
|
return;
|
|
3032
3078
|
}
|
|
3033
3079
|
}
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
// Early validation for auto-mode paused state
|
|
3039
|
-
if (this.runtime.isPaused())
|
|
3040
|
-
return;
|
|
3041
|
-
// Check runtime validators (check current state, not just graph definition)
|
|
3042
|
-
const runtimeValidationError = this.runtime.hasRuntimeValidationBlock(nodeId);
|
|
3043
|
-
if (runtimeValidationError) {
|
|
3044
|
-
this.eventEmitter.emit("error", {
|
|
3045
|
-
kind: "system",
|
|
3046
|
-
message: runtimeValidationError.message,
|
|
3047
|
-
code: runtimeValidationError.code || "RUNTIME_VALIDATION_BLOCKED",
|
|
3048
|
-
details: {
|
|
3049
|
-
nodeId,
|
|
3050
|
-
nodeTypeId: node?.typeId,
|
|
3051
|
-
...runtimeValidationError.details,
|
|
3052
|
-
},
|
|
3053
|
-
});
|
|
3054
|
-
return;
|
|
3055
|
-
}
|
|
3056
|
-
// Attach run-context IDs if provided - do this BEFORE checking for pending resolution
|
|
3057
|
-
// so that handle resolution can track these run contexts
|
|
3058
|
-
if (runContextIds) {
|
|
3059
|
-
this.graph.addNodeRunContextIds(nodeId, runContextIds);
|
|
3060
|
-
}
|
|
3061
|
-
if (!canSkipHandleResolution && !this.handleResolver.getPendingResolution(nodeId)) {
|
|
3062
|
-
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
3063
|
-
}
|
|
3064
|
-
// Check if handles are being resolved - wait for resolution before executing
|
|
3065
|
-
// Do this AFTER setting up run contexts so handle resolution can track them
|
|
3066
|
-
const pendingResolution = this.handleResolver.getPendingResolution(nodeId);
|
|
3067
|
-
if (pendingResolution) {
|
|
3068
|
-
if (runContextIds && runContextIds.size > 0) {
|
|
3069
|
-
for (const id of runContextIds) {
|
|
3070
|
-
this.runContextManager.startHandleResolution(id, nodeId);
|
|
3071
|
-
}
|
|
3080
|
+
try {
|
|
3081
|
+
if (runMode === "auto" && runContextIds && runContextIds.size > 0) {
|
|
3082
|
+
console.trace(`NodeExecutor.execute[${formatNodeRef(this.graph, nodeId)}:${reason}]: runContextIds provided in auto mode, ignoring`);
|
|
3083
|
+
runContextIds = undefined;
|
|
3072
3084
|
}
|
|
3073
|
-
//
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3085
|
+
// Early validation for auto-mode paused state
|
|
3086
|
+
if (this.runtime.isPaused())
|
|
3087
|
+
return;
|
|
3088
|
+
// Check runtime validators (check current state, not just graph definition)
|
|
3089
|
+
const runtimeValidationError = this.runtime.hasRuntimeValidationBlock(nodeId);
|
|
3090
|
+
if (runtimeValidationError) {
|
|
3091
|
+
this.eventEmitter.emit("error", {
|
|
3092
|
+
kind: "system",
|
|
3093
|
+
message: runtimeValidationError.message,
|
|
3094
|
+
code: runtimeValidationError.code || "RUNTIME_VALIDATION_BLOCKED",
|
|
3095
|
+
details: {
|
|
3096
|
+
nodeId,
|
|
3097
|
+
nodeTypeId: node?.typeId,
|
|
3098
|
+
...runtimeValidationError.details,
|
|
3099
|
+
},
|
|
3100
|
+
});
|
|
3101
|
+
return;
|
|
3102
|
+
}
|
|
3103
|
+
// Attach run-context IDs if provided - do this BEFORE checking for pending resolution
|
|
3104
|
+
// so that handle resolution can track these run contexts
|
|
3105
|
+
if (runContextIds) {
|
|
3106
|
+
this.graph.addNodeRunContextIds(nodeId, runContextIds);
|
|
3107
|
+
}
|
|
3108
|
+
if (!canSkipHandleResolution && !this.handleResolver.getPendingResolution(nodeId)) {
|
|
3109
|
+
this.handleResolver.scheduleRecomputeHandles(nodeId);
|
|
3110
|
+
}
|
|
3111
|
+
// Check if handles are being resolved - wait for resolution before executing
|
|
3112
|
+
// Do this AFTER setting up run contexts so handle resolution can track them
|
|
3113
|
+
const pendingResolution = this.handleResolver.getPendingResolution(nodeId);
|
|
3114
|
+
if (pendingResolution) {
|
|
3084
3115
|
if (runContextIds && runContextIds.size > 0) {
|
|
3085
3116
|
for (const id of runContextIds) {
|
|
3086
|
-
this.runContextManager.
|
|
3117
|
+
this.runContextManager.startHandleResolution(id, nodeId);
|
|
3087
3118
|
}
|
|
3088
3119
|
}
|
|
3089
|
-
|
|
3090
|
-
|
|
3120
|
+
// Wait for resolution to complete, then re-execute
|
|
3121
|
+
pendingResolution.then(() => {
|
|
3122
|
+
// Re-check node still exists and conditions
|
|
3123
|
+
const nodeAfter = this.graph.getNode(nodeId);
|
|
3124
|
+
if (nodeAfter) {
|
|
3125
|
+
this.execute(nodeId, {
|
|
3126
|
+
runContextIds,
|
|
3127
|
+
canSkipHandleResolution: true,
|
|
3128
|
+
reason: opts?.reason,
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
if (runContextIds && runContextIds.size > 0) {
|
|
3132
|
+
for (const id of runContextIds) {
|
|
3133
|
+
this.runContextManager.finishHandleResolution(id, nodeId);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
});
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
3139
|
+
// Handle debouncing
|
|
3140
|
+
const now = Date.now();
|
|
3141
|
+
if (this.shouldDebounce(nodeId, now)) {
|
|
3142
|
+
this.handleDebouncedSchedule(nodeId, now, runContextIds, reason);
|
|
3143
|
+
return;
|
|
3144
|
+
}
|
|
3145
|
+
// Prepare execution plan
|
|
3146
|
+
const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
|
|
3147
|
+
// Route to appropriate concurrency handler
|
|
3148
|
+
this.routeToConcurrencyHandler(nodeId, executionPlan);
|
|
3091
3149
|
}
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
return;
|
|
3150
|
+
finally {
|
|
3151
|
+
if (autoCreatedRunContextId) {
|
|
3152
|
+
this.runContextManager.releaseScheduling(autoCreatedRunContextId);
|
|
3153
|
+
}
|
|
3097
3154
|
}
|
|
3098
|
-
// Prepare execution plan
|
|
3099
|
-
const executionPlan = this.prepareExecutionPlan(nodeId, runContextIds, now, reason);
|
|
3100
|
-
// Route to appropriate concurrency handler
|
|
3101
|
-
this.routeToConcurrencyHandler(nodeId, executionPlan);
|
|
3102
3155
|
}
|
|
3103
3156
|
/**
|
|
3104
3157
|
* Check if execution should be debounced
|
|
@@ -3716,16 +3769,26 @@ class GraphRuntime {
|
|
|
3716
3769
|
executeNodeAutoRun(nodeId, opts) {
|
|
3717
3770
|
const node = this.graph.getNode(nodeId);
|
|
3718
3771
|
const shouldAutoRun = this.runMode === "auto" || node?.policy?.autoRun === true;
|
|
3772
|
+
const canExecute = this.graph.allInboundHaveValue(nodeId);
|
|
3773
|
+
if (!shouldAutoRun || !canExecute)
|
|
3774
|
+
return;
|
|
3719
3775
|
let runContextIdsToUse = undefined;
|
|
3776
|
+
let runContextIdToRelease;
|
|
3720
3777
|
if (this.runMode === "manual") {
|
|
3721
|
-
|
|
3778
|
+
runContextIdToRelease = this.runContextManager.createRunContext(nodeId, { propagate: false });
|
|
3779
|
+
runContextIdsToUse = new Set([runContextIdToRelease]);
|
|
3722
3780
|
}
|
|
3723
|
-
|
|
3781
|
+
try {
|
|
3724
3782
|
this.execute(nodeId, {
|
|
3725
3783
|
runContextIds: runContextIdsToUse,
|
|
3726
3784
|
reason: opts?.reason ?? "executeNodeAutoRun",
|
|
3727
3785
|
});
|
|
3728
3786
|
}
|
|
3787
|
+
finally {
|
|
3788
|
+
if (runContextIdToRelease) {
|
|
3789
|
+
this.runContextManager.releaseScheduling(runContextIdToRelease);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3729
3792
|
}
|
|
3730
3793
|
setInputs(nodeId, inputs) {
|
|
3731
3794
|
const node = this.graph.getNode(nodeId);
|
|
@@ -3810,8 +3873,9 @@ class GraphRuntime {
|
|
|
3810
3873
|
});
|
|
3811
3874
|
if (this.runMode === "auto" && invalidate) {
|
|
3812
3875
|
for (const nodeId of this.graph.getNodeIds()) {
|
|
3813
|
-
if (this.graph.allInboundHaveValue(nodeId))
|
|
3876
|
+
if (this.graph.allInboundHaveValue(nodeId)) {
|
|
3814
3877
|
this.execute(nodeId, { reason: "launch" });
|
|
3878
|
+
}
|
|
3815
3879
|
}
|
|
3816
3880
|
}
|
|
3817
3881
|
if (startPaused) {
|
|
@@ -3958,10 +4022,15 @@ class GraphRuntime {
|
|
|
3958
4022
|
...opts,
|
|
3959
4023
|
});
|
|
3960
4024
|
this.graph.addNodeRunContextId(startNodeId, id);
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
4025
|
+
try {
|
|
4026
|
+
this.execute(startNodeId, {
|
|
4027
|
+
runContextIds: new Set([id]),
|
|
4028
|
+
reason: opts?.reason ?? "runFromHereContext",
|
|
4029
|
+
});
|
|
4030
|
+
}
|
|
4031
|
+
finally {
|
|
4032
|
+
this.runContextManager.releaseScheduling(id);
|
|
4033
|
+
}
|
|
3965
4034
|
});
|
|
3966
4035
|
}
|
|
3967
4036
|
setRunMode(runMode) {
|
|
@@ -4084,6 +4153,7 @@ class GraphRuntime {
|
|
|
4084
4153
|
node.lifecycle?.dispose?.({
|
|
4085
4154
|
state: node.state,
|
|
4086
4155
|
setState: (next) => this.graph.updateNodeState(node.nodeId, next),
|
|
4156
|
+
disposeReason: "node-removed",
|
|
4087
4157
|
});
|
|
4088
4158
|
this.graph.deleteNode(nodeId);
|
|
4089
4159
|
this.edgePropagator.clearArrayBuckets(nodeId);
|
|
@@ -4282,14 +4352,21 @@ class GraphRuntime {
|
|
|
4282
4352
|
const val = this.getOutput(nodeId, handle);
|
|
4283
4353
|
if (val !== undefined) {
|
|
4284
4354
|
let runContextIdsToUse = undefined;
|
|
4355
|
+
let runContextIdToRelease;
|
|
4285
4356
|
if (this.runMode === "manual") {
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4357
|
+
runContextIdToRelease = this.runContextManager.createRunContext(nodeId, {
|
|
4358
|
+
propagate: false,
|
|
4359
|
+
});
|
|
4360
|
+
runContextIdsToUse = new Set([runContextIdToRelease]);
|
|
4361
|
+
}
|
|
4362
|
+
try {
|
|
4363
|
+
this.propagate(nodeId, handle, val, runContextIdsToUse);
|
|
4364
|
+
}
|
|
4365
|
+
finally {
|
|
4366
|
+
if (runContextIdToRelease) {
|
|
4367
|
+
this.runContextManager.releaseScheduling(runContextIdToRelease);
|
|
4368
|
+
}
|
|
4291
4369
|
}
|
|
4292
|
-
this.propagate(nodeId, handle, val, runContextIdsToUse);
|
|
4293
4370
|
}
|
|
4294
4371
|
}
|
|
4295
4372
|
}
|
|
@@ -4297,7 +4374,7 @@ class GraphRuntime {
|
|
|
4297
4374
|
}
|
|
4298
4375
|
}
|
|
4299
4376
|
}
|
|
4300
|
-
dispose() {
|
|
4377
|
+
dispose(reason = "runtime-dispose") {
|
|
4301
4378
|
this.runContextManager.resolveAll();
|
|
4302
4379
|
this.graph.forEachNode((node) => {
|
|
4303
4380
|
node.runtime.onDeactivated?.();
|
|
@@ -4305,6 +4382,7 @@ class GraphRuntime {
|
|
|
4305
4382
|
node.lifecycle?.dispose?.({
|
|
4306
4383
|
state: node.state,
|
|
4307
4384
|
setState: (next) => this.graph.updateNodeState(node.nodeId, next),
|
|
4385
|
+
disposeReason: reason,
|
|
4308
4386
|
});
|
|
4309
4387
|
});
|
|
4310
4388
|
this.graph.clear();
|