@graphrefly/graphrefly 0.9.0 → 0.10.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/dist/compat/nestjs/index.js +2 -2
- package/dist/index.cjs +2246 -1397
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +217 -2
- package/dist/index.d.ts +217 -2
- package/dist/index.js +1418 -570
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -143,13 +143,8 @@ import {
|
|
|
143
143
|
nestjs_exports
|
|
144
144
|
} from "./chunk-UCW3VWMN.js";
|
|
145
145
|
import {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
createDagCborZstdCodec,
|
|
149
|
-
graph_exports,
|
|
150
|
-
negotiateCodec,
|
|
151
|
-
replayWAL
|
|
152
|
-
} from "./chunk-A2AJJOSJ.js";
|
|
146
|
+
core_exports
|
|
147
|
+
} from "./chunk-E7OH6ZAZ.js";
|
|
153
148
|
import {
|
|
154
149
|
cached,
|
|
155
150
|
createWatermarkController,
|
|
@@ -183,12 +178,17 @@ import {
|
|
|
183
178
|
toMessages$,
|
|
184
179
|
toObservable
|
|
185
180
|
} from "./chunk-YWTP2XRJ.js";
|
|
186
|
-
import {
|
|
187
|
-
core_exports
|
|
188
|
-
} from "./chunk-E7OH6ZAZ.js";
|
|
189
181
|
import {
|
|
190
182
|
ResettableTimer
|
|
191
183
|
} from "./chunk-WZ2Z2CRV.js";
|
|
184
|
+
import {
|
|
185
|
+
JsonCodec,
|
|
186
|
+
createDagCborCodec,
|
|
187
|
+
createDagCborZstdCodec,
|
|
188
|
+
graph_exports,
|
|
189
|
+
negotiateCodec,
|
|
190
|
+
replayWAL
|
|
191
|
+
} from "./chunk-A2AJJOSJ.js";
|
|
192
192
|
import {
|
|
193
193
|
analyzeAndMeasure,
|
|
194
194
|
computeLineBreaks,
|
|
@@ -1045,6 +1045,7 @@ __export(patterns_exports, {
|
|
|
1045
1045
|
ai: () => ai_exports,
|
|
1046
1046
|
cqrs: () => cqrs_exports,
|
|
1047
1047
|
demoShell: () => demo_shell_exports,
|
|
1048
|
+
graphspec: () => graphspec_exports,
|
|
1048
1049
|
layout: () => reactive_layout_exports,
|
|
1049
1050
|
memory: () => memory_exports,
|
|
1050
1051
|
messaging: () => messaging_exports,
|
|
@@ -3024,92 +3025,1209 @@ function demoShell(opts) {
|
|
|
3024
3025
|
};
|
|
3025
3026
|
}
|
|
3026
3027
|
|
|
3027
|
-
// src/patterns/
|
|
3028
|
-
var
|
|
3029
|
-
__export(
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
jobQueue: () => jobQueue,
|
|
3037
|
-
subscription: () => subscription,
|
|
3038
|
-
topic: () => topic,
|
|
3039
|
-
topicBridge: () => topicBridge
|
|
3028
|
+
// src/patterns/graphspec.ts
|
|
3029
|
+
var graphspec_exports = {};
|
|
3030
|
+
__export(graphspec_exports, {
|
|
3031
|
+
compileSpec: () => compileSpec,
|
|
3032
|
+
decompileGraph: () => decompileGraph,
|
|
3033
|
+
llmCompose: () => llmCompose,
|
|
3034
|
+
llmRefine: () => llmRefine,
|
|
3035
|
+
specDiff: () => specDiff,
|
|
3036
|
+
validateSpec: () => validateSpec
|
|
3040
3037
|
});
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
function messagingMeta(kind, extra) {
|
|
3038
|
+
|
|
3039
|
+
// src/patterns/reduction.ts
|
|
3040
|
+
var reduction_exports = {};
|
|
3041
|
+
__export(reduction_exports, {
|
|
3042
|
+
budgetGate: () => budgetGate,
|
|
3043
|
+
feedback: () => feedback,
|
|
3044
|
+
funnel: () => funnel,
|
|
3045
|
+
scorer: () => scorer,
|
|
3046
|
+
stratify: () => stratify
|
|
3047
|
+
});
|
|
3048
|
+
function baseMeta(kind, meta) {
|
|
3053
3049
|
return {
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
...
|
|
3050
|
+
reduction: true,
|
|
3051
|
+
reduction_type: kind,
|
|
3052
|
+
...meta ?? {}
|
|
3057
3053
|
};
|
|
3058
3054
|
}
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3055
|
+
function stratify(name, source, rules, opts) {
|
|
3056
|
+
const g = new Graph(name, opts);
|
|
3057
|
+
g.add("source", source);
|
|
3058
|
+
const rulesNode = state(rules, {
|
|
3059
|
+
meta: baseMeta("stratify_rules")
|
|
3060
|
+
});
|
|
3061
|
+
g.add("rules", rulesNode);
|
|
3062
|
+
for (const rule of rules) {
|
|
3063
|
+
_addBranch(g, source, rulesNode, rule);
|
|
3064
|
+
}
|
|
3065
|
+
return g;
|
|
3066
|
+
}
|
|
3067
|
+
function _addBranch(graph, source, rulesNode, rule) {
|
|
3068
|
+
const branchName = `branch/${rule.name}`;
|
|
3069
|
+
let pendingDirty = false;
|
|
3070
|
+
const filterNode = node([source, rulesNode], () => void 0, {
|
|
3071
|
+
describeKind: "operator",
|
|
3072
|
+
meta: baseMeta("stratify_branch", { branch: rule.name }),
|
|
3073
|
+
onMessage(msg, depIndex, actions) {
|
|
3074
|
+
if (depIndex !== 0) return false;
|
|
3075
|
+
const t = msg[0];
|
|
3076
|
+
if (t === DATA) {
|
|
3077
|
+
const value = msg[1];
|
|
3078
|
+
const currentRules = rulesNode.get();
|
|
3079
|
+
const currentRule = currentRules.find((r) => r.name === rule.name);
|
|
3080
|
+
if (currentRule && currentRule.classify(value)) {
|
|
3081
|
+
pendingDirty = false;
|
|
3082
|
+
actions.emit(value);
|
|
3083
|
+
} else {
|
|
3084
|
+
if (pendingDirty) {
|
|
3085
|
+
pendingDirty = false;
|
|
3086
|
+
actions.down([[DIRTY], [RESOLVED]]);
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
return true;
|
|
3080
3090
|
}
|
|
3081
|
-
|
|
3082
|
-
|
|
3083
|
-
|
|
3084
|
-
|
|
3091
|
+
if (t === DIRTY) {
|
|
3092
|
+
pendingDirty = true;
|
|
3093
|
+
return true;
|
|
3094
|
+
}
|
|
3095
|
+
if (t === RESOLVED) {
|
|
3096
|
+
if (pendingDirty) {
|
|
3097
|
+
pendingDirty = false;
|
|
3098
|
+
actions.down([[DIRTY], [RESOLVED]]);
|
|
3099
|
+
} else {
|
|
3100
|
+
actions.down([[RESOLVED]]);
|
|
3101
|
+
}
|
|
3102
|
+
return true;
|
|
3103
|
+
}
|
|
3104
|
+
if (t === COMPLETE || t === ERROR) {
|
|
3105
|
+
pendingDirty = false;
|
|
3106
|
+
actions.down([msg]);
|
|
3107
|
+
return true;
|
|
3108
|
+
}
|
|
3109
|
+
return false;
|
|
3110
|
+
}
|
|
3111
|
+
});
|
|
3112
|
+
graph.add(branchName, filterNode);
|
|
3113
|
+
graph.connect("source", branchName);
|
|
3114
|
+
if (rule.ops) {
|
|
3115
|
+
const transformed = rule.ops(filterNode);
|
|
3116
|
+
const transformedName = `branch/${rule.name}/out`;
|
|
3117
|
+
graph.add(transformedName, transformed);
|
|
3118
|
+
graph.connect(branchName, transformedName);
|
|
3085
3119
|
}
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3120
|
+
}
|
|
3121
|
+
function funnel(name, sources, stages, opts) {
|
|
3122
|
+
if (sources.length === 0) throw new RangeError("funnel requires at least one source");
|
|
3123
|
+
if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
|
|
3124
|
+
const g = new Graph(name, opts);
|
|
3125
|
+
const merged = sources.length === 1 ? sources[0] : merge(...sources);
|
|
3126
|
+
g.add("merged", merged);
|
|
3127
|
+
let prevOutputPath = "merged";
|
|
3128
|
+
for (let i = 0; i < stages.length; i++) {
|
|
3129
|
+
const stage = stages[i];
|
|
3130
|
+
const sub = new Graph(stage.name);
|
|
3131
|
+
stage.build(sub);
|
|
3132
|
+
try {
|
|
3133
|
+
sub.resolve("input");
|
|
3134
|
+
} catch {
|
|
3135
|
+
throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
|
|
3136
|
+
}
|
|
3137
|
+
try {
|
|
3138
|
+
sub.resolve("output");
|
|
3139
|
+
} catch {
|
|
3140
|
+
throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
|
|
3141
|
+
}
|
|
3142
|
+
g.mount(stage.name, sub);
|
|
3143
|
+
const prevNode = g.resolve(prevOutputPath);
|
|
3144
|
+
const stageInputPath = `${stage.name}::input`;
|
|
3145
|
+
const stageInput = g.resolve(stageInputPath);
|
|
3146
|
+
prevNode.subscribe((msgs) => {
|
|
3147
|
+
for (const msg of msgs) {
|
|
3148
|
+
const t = msg[0];
|
|
3149
|
+
if (t === DATA) {
|
|
3150
|
+
stageInput.down([[DATA, msg[1]]]);
|
|
3151
|
+
} else if (t === DIRTY) {
|
|
3152
|
+
stageInput.down([[DIRTY]]);
|
|
3153
|
+
} else if (t === RESOLVED) {
|
|
3154
|
+
stageInput.down([[RESOLVED]]);
|
|
3155
|
+
} else if (t === COMPLETE || t === ERROR) {
|
|
3156
|
+
stageInput.down([msg]);
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
});
|
|
3160
|
+
prevOutputPath = `${stage.name}::output`;
|
|
3090
3161
|
}
|
|
3091
|
-
|
|
3092
|
-
|
|
3162
|
+
return g;
|
|
3163
|
+
}
|
|
3164
|
+
function feedback(graph, condition, reentry, opts) {
|
|
3165
|
+
const maxIter = opts?.maxIterations ?? 10;
|
|
3166
|
+
const counterName = `__feedback_${condition}`;
|
|
3167
|
+
const counter = state(0, {
|
|
3168
|
+
meta: baseMeta("feedback_counter", {
|
|
3169
|
+
maxIterations: maxIter,
|
|
3170
|
+
feedbackFrom: condition,
|
|
3171
|
+
feedbackTo: reentry
|
|
3172
|
+
})
|
|
3173
|
+
});
|
|
3174
|
+
graph.add(counterName, counter);
|
|
3175
|
+
const condNode = graph.resolve(condition);
|
|
3176
|
+
const reentryNode = graph.resolve(reentry);
|
|
3177
|
+
let tornDown = false;
|
|
3178
|
+
let unsubCounter = null;
|
|
3179
|
+
const safeUnsub = () => {
|
|
3180
|
+
if (tornDown) return;
|
|
3181
|
+
tornDown = true;
|
|
3182
|
+
unsub();
|
|
3183
|
+
unsubCounter?.();
|
|
3184
|
+
};
|
|
3185
|
+
const unsub = condNode.subscribe((msgs) => {
|
|
3186
|
+
for (const msg of msgs) {
|
|
3187
|
+
if (msg[0] === DATA) {
|
|
3188
|
+
const currentCount = counter.get();
|
|
3189
|
+
if (currentCount >= maxIter) continue;
|
|
3190
|
+
const condValue = msg[1];
|
|
3191
|
+
if (condValue == null) continue;
|
|
3192
|
+
counter.down([[DATA, currentCount + 1]]);
|
|
3193
|
+
reentryNode.down([[DATA, condValue]]);
|
|
3194
|
+
} else if (msg[0] === COMPLETE || msg[0] === ERROR) {
|
|
3195
|
+
const terminal = msg[0] === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [msg[0]];
|
|
3196
|
+
counter.down([terminal]);
|
|
3197
|
+
safeUnsub();
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
});
|
|
3201
|
+
unsubCounter = counter.subscribe((msgs) => {
|
|
3202
|
+
for (const msg of msgs) {
|
|
3203
|
+
if (msg[0] === COMPLETE || msg[0] === ERROR) {
|
|
3204
|
+
safeUnsub();
|
|
3205
|
+
return;
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
});
|
|
3209
|
+
return graph;
|
|
3210
|
+
}
|
|
3211
|
+
function budgetGate(source, constraints, opts) {
|
|
3212
|
+
if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
|
|
3213
|
+
const constraintNodes = constraints.map((c) => c.node);
|
|
3214
|
+
const allDeps = [source, ...constraintNodes];
|
|
3215
|
+
let buffer2 = [];
|
|
3216
|
+
let paused = false;
|
|
3217
|
+
let pendingResolved = false;
|
|
3218
|
+
const lockId = /* @__PURE__ */ Symbol("budget-gate");
|
|
3219
|
+
function checkBudget() {
|
|
3220
|
+
return constraints.every((c) => c.check(c.node.get()));
|
|
3093
3221
|
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3222
|
+
function flushBuffer(actions) {
|
|
3223
|
+
while (buffer2.length > 0 && checkBudget()) {
|
|
3224
|
+
const item = buffer2.shift();
|
|
3225
|
+
actions.emit(item);
|
|
3226
|
+
}
|
|
3227
|
+
if (buffer2.length === 0 && pendingResolved) {
|
|
3228
|
+
pendingResolved = false;
|
|
3229
|
+
actions.down([[RESOLVED]]);
|
|
3230
|
+
}
|
|
3097
3231
|
}
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3232
|
+
return node(allDeps, () => void 0, {
|
|
3233
|
+
...opts,
|
|
3234
|
+
describeKind: "operator",
|
|
3235
|
+
meta: baseMeta("budget_gate", opts?.meta),
|
|
3236
|
+
onMessage(msg, depIndex, actions) {
|
|
3237
|
+
const t = msg[0];
|
|
3238
|
+
if (depIndex === 0) {
|
|
3239
|
+
if (t === DATA) {
|
|
3240
|
+
if (checkBudget() && buffer2.length === 0) {
|
|
3241
|
+
actions.emit(msg[1]);
|
|
3242
|
+
} else {
|
|
3243
|
+
buffer2.push(msg[1]);
|
|
3244
|
+
if (!paused) {
|
|
3245
|
+
paused = true;
|
|
3246
|
+
actions.up([[PAUSE, lockId]]);
|
|
3247
|
+
}
|
|
3248
|
+
}
|
|
3249
|
+
return true;
|
|
3250
|
+
}
|
|
3251
|
+
if (t === DIRTY) {
|
|
3252
|
+
actions.down([[DIRTY]]);
|
|
3253
|
+
return true;
|
|
3254
|
+
}
|
|
3255
|
+
if (t === RESOLVED) {
|
|
3256
|
+
if (buffer2.length === 0) {
|
|
3257
|
+
actions.down([[RESOLVED]]);
|
|
3258
|
+
} else {
|
|
3259
|
+
pendingResolved = true;
|
|
3260
|
+
}
|
|
3261
|
+
return true;
|
|
3262
|
+
}
|
|
3263
|
+
if (t === COMPLETE || t === ERROR) {
|
|
3264
|
+
for (const item of buffer2) {
|
|
3265
|
+
actions.emit(item);
|
|
3266
|
+
}
|
|
3267
|
+
buffer2 = [];
|
|
3268
|
+
pendingResolved = false;
|
|
3269
|
+
if (paused) {
|
|
3270
|
+
paused = false;
|
|
3271
|
+
actions.up([[RESUME, lockId]]);
|
|
3272
|
+
}
|
|
3273
|
+
actions.down([msg]);
|
|
3274
|
+
return true;
|
|
3275
|
+
}
|
|
3276
|
+
return false;
|
|
3277
|
+
}
|
|
3278
|
+
if (t === DATA || t === RESOLVED) {
|
|
3279
|
+
if (checkBudget() && buffer2.length > 0) {
|
|
3280
|
+
flushBuffer(actions);
|
|
3281
|
+
if (buffer2.length === 0 && paused) {
|
|
3282
|
+
paused = false;
|
|
3283
|
+
actions.up([[RESUME, lockId]]);
|
|
3284
|
+
}
|
|
3285
|
+
} else if (!checkBudget() && !paused && buffer2.length > 0) {
|
|
3286
|
+
paused = true;
|
|
3287
|
+
actions.up([[PAUSE, lockId]]);
|
|
3288
|
+
}
|
|
3289
|
+
return true;
|
|
3290
|
+
}
|
|
3291
|
+
if (t === DIRTY) {
|
|
3292
|
+
return true;
|
|
3293
|
+
}
|
|
3294
|
+
if (t === ERROR) {
|
|
3295
|
+
actions.down([msg]);
|
|
3296
|
+
return true;
|
|
3297
|
+
}
|
|
3298
|
+
if (t === COMPLETE) {
|
|
3299
|
+
return true;
|
|
3300
|
+
}
|
|
3301
|
+
return false;
|
|
3302
|
+
}
|
|
3303
|
+
});
|
|
3304
|
+
}
|
|
3305
|
+
function scorer(sources, weights, opts) {
|
|
3306
|
+
if (sources.length === 0) throw new RangeError("scorer requires at least one source");
|
|
3307
|
+
if (sources.length !== weights.length) {
|
|
3308
|
+
throw new RangeError("scorer requires the same number of sources and weights");
|
|
3309
|
+
}
|
|
3310
|
+
const allDeps = [...sources, ...weights];
|
|
3311
|
+
const n = sources.length;
|
|
3312
|
+
const scoreFns = opts?.scoreFns;
|
|
3313
|
+
return derived(
|
|
3314
|
+
allDeps,
|
|
3315
|
+
(vals) => {
|
|
3316
|
+
const signals = vals.slice(0, n);
|
|
3317
|
+
const weightValues = vals.slice(n);
|
|
3318
|
+
const breakdown = [];
|
|
3319
|
+
let totalScore = 0;
|
|
3320
|
+
for (let i = 0; i < n; i++) {
|
|
3321
|
+
const sig = signals[i] ?? 0;
|
|
3322
|
+
const wt = weightValues[i] ?? 0;
|
|
3323
|
+
const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
|
|
3324
|
+
const weighted = rawScore * wt;
|
|
3325
|
+
breakdown.push(weighted);
|
|
3326
|
+
totalScore += weighted;
|
|
3327
|
+
}
|
|
3328
|
+
return {
|
|
3329
|
+
value: signals,
|
|
3330
|
+
score: totalScore,
|
|
3331
|
+
breakdown
|
|
3332
|
+
};
|
|
3333
|
+
},
|
|
3334
|
+
{
|
|
3335
|
+
...opts,
|
|
3336
|
+
describeKind: "derived",
|
|
3337
|
+
meta: baseMeta("scorer", opts?.meta)
|
|
3338
|
+
}
|
|
3339
|
+
);
|
|
3340
|
+
}
|
|
3341
|
+
|
|
3342
|
+
// src/patterns/graphspec.ts
|
|
3343
|
+
var VALID_NODE_TYPES2 = /* @__PURE__ */ new Set([
|
|
3344
|
+
"state",
|
|
3345
|
+
"producer",
|
|
3346
|
+
"derived",
|
|
3347
|
+
"effect",
|
|
3348
|
+
"operator",
|
|
3349
|
+
"template"
|
|
3350
|
+
]);
|
|
3351
|
+
var INNER_NODE_TYPES = /* @__PURE__ */ new Set(["state", "producer", "derived", "effect", "operator"]);
|
|
3352
|
+
function validateSpec(spec) {
|
|
3353
|
+
const errors = [];
|
|
3354
|
+
if (spec == null || typeof spec !== "object") {
|
|
3355
|
+
return { valid: false, errors: ["GraphSpec must be a non-null object"] };
|
|
3356
|
+
}
|
|
3357
|
+
const s = spec;
|
|
3358
|
+
if (typeof s.name !== "string" || s.name.length === 0) {
|
|
3359
|
+
errors.push("Missing or empty 'name' field");
|
|
3360
|
+
}
|
|
3361
|
+
if (s.nodes == null || typeof s.nodes !== "object" || Array.isArray(s.nodes)) {
|
|
3362
|
+
errors.push("Missing or invalid 'nodes' field (must be an object)");
|
|
3363
|
+
return { valid: false, errors };
|
|
3364
|
+
}
|
|
3365
|
+
const nodeNames = new Set(Object.keys(s.nodes));
|
|
3366
|
+
const nodeTypes = /* @__PURE__ */ new Map();
|
|
3367
|
+
const templateDefs = /* @__PURE__ */ new Map();
|
|
3368
|
+
if (s.templates != null && typeof s.templates === "object" && !Array.isArray(s.templates)) {
|
|
3369
|
+
for (const [tName, tRaw] of Object.entries(s.templates)) {
|
|
3370
|
+
if (tRaw != null && typeof tRaw === "object") {
|
|
3371
|
+
const t = tRaw;
|
|
3372
|
+
templateDefs.set(tName, {
|
|
3373
|
+
params: Array.isArray(t.params) ? t.params : []
|
|
3374
|
+
});
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
if (s.templates != null) {
|
|
3379
|
+
if (typeof s.templates !== "object" || Array.isArray(s.templates)) {
|
|
3380
|
+
errors.push("'templates' must be an object");
|
|
3381
|
+
} else {
|
|
3382
|
+
for (const [tName, tRaw] of Object.entries(s.templates)) {
|
|
3383
|
+
if (tRaw == null || typeof tRaw !== "object") {
|
|
3384
|
+
errors.push(`Template "${tName}": must be an object`);
|
|
3385
|
+
continue;
|
|
3386
|
+
}
|
|
3387
|
+
const t = tRaw;
|
|
3388
|
+
if (!Array.isArray(t.params)) {
|
|
3389
|
+
errors.push(`Template "${tName}": missing 'params' array`);
|
|
3390
|
+
}
|
|
3391
|
+
if (t.nodes == null || typeof t.nodes !== "object" || Array.isArray(t.nodes)) {
|
|
3392
|
+
errors.push(`Template "${tName}": missing or invalid 'nodes' object`);
|
|
3393
|
+
} else {
|
|
3394
|
+
const paramSet = new Set(Array.isArray(t.params) ? t.params : []);
|
|
3395
|
+
const innerNames = new Set(Object.keys(t.nodes));
|
|
3396
|
+
for (const [nName, nRaw] of Object.entries(t.nodes)) {
|
|
3397
|
+
if (nRaw == null || typeof nRaw !== "object") {
|
|
3398
|
+
errors.push(`Template "${tName}" node "${nName}": must be an object`);
|
|
3399
|
+
continue;
|
|
3400
|
+
}
|
|
3401
|
+
const n = nRaw;
|
|
3402
|
+
if (typeof n.type !== "string" || !INNER_NODE_TYPES.has(n.type)) {
|
|
3403
|
+
errors.push(`Template "${tName}" node "${nName}": invalid type`);
|
|
3404
|
+
}
|
|
3405
|
+
if (Array.isArray(n.deps)) {
|
|
3406
|
+
for (const dep of n.deps) {
|
|
3407
|
+
if (!innerNames.has(dep) && !paramSet.has(dep)) {
|
|
3408
|
+
errors.push(
|
|
3409
|
+
`Template "${tName}" node "${nName}": dep "${dep}" is not an inner node or param`
|
|
3410
|
+
);
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
}
|
|
3414
|
+
}
|
|
3415
|
+
if (typeof t.output !== "string") {
|
|
3416
|
+
errors.push(`Template "${tName}": missing 'output' string`);
|
|
3417
|
+
} else if (!t.nodes[t.output]) {
|
|
3418
|
+
errors.push(`Template "${tName}": output "${t.output}" is not a declared node`);
|
|
3419
|
+
}
|
|
3420
|
+
}
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
}
|
|
3424
|
+
for (const [name, raw] of Object.entries(s.nodes)) {
|
|
3425
|
+
if (raw == null || typeof raw !== "object") {
|
|
3426
|
+
errors.push(`Node "${name}": must be an object`);
|
|
3427
|
+
continue;
|
|
3428
|
+
}
|
|
3429
|
+
const n = raw;
|
|
3430
|
+
if (typeof n.type !== "string" || !VALID_NODE_TYPES2.has(n.type)) {
|
|
3431
|
+
errors.push(
|
|
3432
|
+
`Node "${name}": invalid type "${String(n.type)}" (expected: ${[...VALID_NODE_TYPES2].join(", ")})`
|
|
3433
|
+
);
|
|
3434
|
+
continue;
|
|
3435
|
+
}
|
|
3436
|
+
nodeTypes.set(name, n.type);
|
|
3437
|
+
if (n.type === "template") {
|
|
3438
|
+
if (typeof n.template !== "string" || !templateDefs.has(n.template)) {
|
|
3439
|
+
errors.push(`Node "${name}": template "${String(n.template)}" not found in templates`);
|
|
3440
|
+
} else {
|
|
3441
|
+
if (n.bind == null || typeof n.bind !== "object" || Array.isArray(n.bind)) {
|
|
3442
|
+
errors.push(`Node "${name}": template ref requires 'bind' object`);
|
|
3443
|
+
} else {
|
|
3444
|
+
const tmpl = templateDefs.get(n.template);
|
|
3445
|
+
const bind = n.bind;
|
|
3446
|
+
for (const param of tmpl.params) {
|
|
3447
|
+
if (!(param in bind)) {
|
|
3448
|
+
errors.push(
|
|
3449
|
+
`Node "${name}": template param "${param}" is not bound (template "${n.template}")`
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
for (const [, target] of Object.entries(bind)) {
|
|
3454
|
+
if (typeof target === "string" && !nodeNames.has(target)) {
|
|
3455
|
+
errors.push(
|
|
3456
|
+
`Node "${name}": bind target "${target}" does not reference an existing node`
|
|
3457
|
+
);
|
|
3458
|
+
}
|
|
3459
|
+
}
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
} else {
|
|
3463
|
+
if (Array.isArray(n.deps)) {
|
|
3464
|
+
for (const dep of n.deps) {
|
|
3465
|
+
if (dep === name) {
|
|
3466
|
+
errors.push(`Node "${name}": self-referencing dep`);
|
|
3467
|
+
} else if (!nodeNames.has(dep)) {
|
|
3468
|
+
errors.push(`Node "${name}": dep "${dep}" does not reference an existing node`);
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3471
|
+
}
|
|
3472
|
+
if ((n.type === "derived" || n.type === "effect" || n.type === "operator") && !Array.isArray(n.deps)) {
|
|
3473
|
+
errors.push(`Node "${name}": ${n.type} node should have a 'deps' array`);
|
|
3474
|
+
}
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
if (s.feedback != null) {
|
|
3478
|
+
if (!Array.isArray(s.feedback)) {
|
|
3479
|
+
errors.push("'feedback' must be an array");
|
|
3480
|
+
} else {
|
|
3481
|
+
for (let i = 0; i < s.feedback.length; i++) {
|
|
3482
|
+
const edge = s.feedback[i];
|
|
3483
|
+
if (edge == null || typeof edge !== "object") {
|
|
3484
|
+
errors.push(`Feedback [${i}]: must be an object`);
|
|
3485
|
+
continue;
|
|
3486
|
+
}
|
|
3487
|
+
const e = edge;
|
|
3488
|
+
if (typeof e.from !== "string" || !nodeNames.has(e.from)) {
|
|
3489
|
+
errors.push(
|
|
3490
|
+
`Feedback [${i}]: 'from' "${String(e.from)}" does not reference an existing node`
|
|
3491
|
+
);
|
|
3492
|
+
}
|
|
3493
|
+
if (typeof e.from === "string" && e.from === e.to) {
|
|
3494
|
+
errors.push(`Feedback [${i}]: 'from' and 'to' must be different nodes`);
|
|
3495
|
+
}
|
|
3496
|
+
if (typeof e.to !== "string" || !nodeNames.has(e.to)) {
|
|
3497
|
+
errors.push(
|
|
3498
|
+
`Feedback [${i}]: 'to' "${String(e.to)}" does not reference an existing node`
|
|
3499
|
+
);
|
|
3500
|
+
} else if (typeof e.to === "string" && nodeTypes.get(e.to) !== "state") {
|
|
3501
|
+
errors.push(
|
|
3502
|
+
`Feedback [${i}]: 'to' node "${e.to}" must be a state node (got "${nodeTypes.get(e.to) ?? "unknown"}")`
|
|
3503
|
+
);
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
return { valid: errors.length === 0, errors };
|
|
3509
|
+
}
|
|
3510
|
+
function compileSpec(spec, opts) {
|
|
3511
|
+
const validation = validateSpec(spec);
|
|
3512
|
+
if (!validation.valid) {
|
|
3513
|
+
throw new Error(`compileSpec: invalid GraphSpec:
|
|
3514
|
+
${validation.errors.join("\n")}`);
|
|
3515
|
+
}
|
|
3516
|
+
const catalog = opts?.catalog ?? {};
|
|
3517
|
+
const g = new Graph(spec.name);
|
|
3518
|
+
const templates = spec.templates ?? {};
|
|
3519
|
+
const created = /* @__PURE__ */ new Map();
|
|
3520
|
+
const deferred = [];
|
|
3521
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
3522
|
+
if (raw.type === "template") continue;
|
|
3523
|
+
const n = raw;
|
|
3524
|
+
if (n.type === "state") {
|
|
3525
|
+
const nd = state(n.initial, {
|
|
3526
|
+
name,
|
|
3527
|
+
meta: n.meta ? { ...n.meta } : void 0
|
|
3528
|
+
});
|
|
3529
|
+
g.add(name, nd);
|
|
3530
|
+
created.set(name, nd);
|
|
3531
|
+
} else if (n.type === "producer") {
|
|
3532
|
+
const sourceFactory = n.source ? catalog.sources?.[n.source] : void 0;
|
|
3533
|
+
const fnFactory = n.fn ? catalog.fns?.[n.fn] : void 0;
|
|
3534
|
+
if (sourceFactory) {
|
|
3535
|
+
const nd = sourceFactory(n.config ?? {});
|
|
3536
|
+
g.add(name, nd);
|
|
3537
|
+
created.set(name, nd);
|
|
3538
|
+
} else if (fnFactory) {
|
|
3539
|
+
const nd = fnFactory([], n.config ?? {});
|
|
3540
|
+
g.add(name, nd);
|
|
3541
|
+
created.set(name, nd);
|
|
3542
|
+
} else {
|
|
3543
|
+
const nd = producer(() => {
|
|
3544
|
+
}, {
|
|
3545
|
+
name,
|
|
3546
|
+
meta: { ...n.meta, _specFn: n.fn, _specSource: n.source }
|
|
3547
|
+
});
|
|
3548
|
+
g.add(name, nd);
|
|
3549
|
+
created.set(name, nd);
|
|
3550
|
+
}
|
|
3551
|
+
} else {
|
|
3552
|
+
deferred.push([name, n]);
|
|
3553
|
+
}
|
|
3554
|
+
}
|
|
3555
|
+
let progressed = true;
|
|
3556
|
+
const pending = new Map(deferred);
|
|
3557
|
+
while (pending.size > 0 && progressed) {
|
|
3558
|
+
progressed = false;
|
|
3559
|
+
for (const [name, n] of [...pending.entries()]) {
|
|
3560
|
+
const deps = n.deps ?? [];
|
|
3561
|
+
if (!deps.every((dep) => created.has(dep))) continue;
|
|
3562
|
+
const resolvedDeps = deps.map((dep) => created.get(dep));
|
|
3563
|
+
const fnFactory = n.fn ? catalog.fns?.[n.fn] : void 0;
|
|
3564
|
+
let nd;
|
|
3565
|
+
if (fnFactory) {
|
|
3566
|
+
nd = fnFactory(resolvedDeps, n.config ?? {});
|
|
3567
|
+
} else if (n.type === "effect") {
|
|
3568
|
+
nd = effect(resolvedDeps, () => {
|
|
3569
|
+
});
|
|
3570
|
+
} else {
|
|
3571
|
+
nd = derived(resolvedDeps, (vals) => vals[0]);
|
|
3572
|
+
}
|
|
3573
|
+
g.add(name, nd);
|
|
3574
|
+
created.set(name, nd);
|
|
3575
|
+
pending.delete(name);
|
|
3576
|
+
progressed = true;
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
if (pending.size > 0) {
|
|
3580
|
+
const unresolved = [...pending.keys()].sort().join(", ");
|
|
3581
|
+
throw new Error(`compileSpec: unresolvable deps for nodes: ${unresolved}`);
|
|
3582
|
+
}
|
|
3583
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
3584
|
+
if (raw.type !== "template") continue;
|
|
3585
|
+
const ref = raw;
|
|
3586
|
+
const tmpl = templates[ref.template];
|
|
3587
|
+
const sub = new Graph(name);
|
|
3588
|
+
const subCreated = /* @__PURE__ */ new Map();
|
|
3589
|
+
const subDeferred = [];
|
|
3590
|
+
for (const [nName, nSpec] of Object.entries(tmpl.nodes)) {
|
|
3591
|
+
const resolvedDeps = (nSpec.deps ?? []).map((dep) => {
|
|
3592
|
+
if (dep.startsWith("$") && ref.bind[dep]) {
|
|
3593
|
+
return ref.bind[dep];
|
|
3594
|
+
}
|
|
3595
|
+
return dep;
|
|
3596
|
+
});
|
|
3597
|
+
const specWithResolvedDeps = { ...nSpec, deps: resolvedDeps };
|
|
3598
|
+
if (nSpec.type === "state") {
|
|
3599
|
+
const nd = state(nSpec.initial, {
|
|
3600
|
+
name: nName,
|
|
3601
|
+
meta: nSpec.meta ? { ...nSpec.meta } : void 0
|
|
3602
|
+
});
|
|
3603
|
+
sub.add(nName, nd);
|
|
3604
|
+
subCreated.set(nName, nd);
|
|
3605
|
+
} else if (nSpec.type === "producer") {
|
|
3606
|
+
const sourceFactory = nSpec.source ? catalog.sources?.[nSpec.source] : void 0;
|
|
3607
|
+
const fnFactory = nSpec.fn ? catalog.fns?.[nSpec.fn] : void 0;
|
|
3608
|
+
if (sourceFactory) {
|
|
3609
|
+
const nd = sourceFactory(nSpec.config ?? {});
|
|
3610
|
+
sub.add(nName, nd);
|
|
3611
|
+
subCreated.set(nName, nd);
|
|
3612
|
+
} else if (fnFactory) {
|
|
3613
|
+
const nd = fnFactory([], nSpec.config ?? {});
|
|
3614
|
+
sub.add(nName, nd);
|
|
3615
|
+
subCreated.set(nName, nd);
|
|
3616
|
+
} else {
|
|
3617
|
+
const nd = producer(() => {
|
|
3618
|
+
}, {
|
|
3619
|
+
name: nName,
|
|
3620
|
+
meta: { ...nSpec.meta, _specFn: nSpec.fn, _specSource: nSpec.source }
|
|
3621
|
+
});
|
|
3622
|
+
sub.add(nName, nd);
|
|
3623
|
+
subCreated.set(nName, nd);
|
|
3624
|
+
}
|
|
3625
|
+
} else {
|
|
3626
|
+
subDeferred.push([nName, specWithResolvedDeps]);
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
let subProgressed = true;
|
|
3630
|
+
const subPending = new Map(subDeferred);
|
|
3631
|
+
while (subPending.size > 0 && subProgressed) {
|
|
3632
|
+
subProgressed = false;
|
|
3633
|
+
for (const [nName, nSpec] of [...subPending.entries()]) {
|
|
3634
|
+
const deps = nSpec.deps ?? [];
|
|
3635
|
+
const allReady = deps.every((dep) => subCreated.has(dep) || created.has(dep));
|
|
3636
|
+
if (!allReady) continue;
|
|
3637
|
+
const resolvedDeps = deps.map((dep) => subCreated.get(dep) ?? created.get(dep));
|
|
3638
|
+
const fnFactory = nSpec.fn ? catalog.fns?.[nSpec.fn] : void 0;
|
|
3639
|
+
let nd;
|
|
3640
|
+
if (fnFactory) {
|
|
3641
|
+
nd = fnFactory(resolvedDeps, nSpec.config ?? {});
|
|
3642
|
+
} else if (nSpec.type === "effect") {
|
|
3643
|
+
nd = effect(resolvedDeps, () => {
|
|
3644
|
+
});
|
|
3645
|
+
} else {
|
|
3646
|
+
nd = derived(resolvedDeps, (vals) => vals[0]);
|
|
3647
|
+
}
|
|
3648
|
+
sub.add(nName, nd);
|
|
3649
|
+
subCreated.set(nName, nd);
|
|
3650
|
+
subPending.delete(nName);
|
|
3651
|
+
subProgressed = true;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
if (subPending.size > 0) {
|
|
3655
|
+
const unresolved = [...subPending.keys()].sort().join(", ");
|
|
3656
|
+
throw new Error(
|
|
3657
|
+
`compileSpec: template "${ref.template}" has unresolvable deps: ${unresolved}`
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3660
|
+
g.mount(name, sub);
|
|
3661
|
+
const outputPath = `${name}::${tmpl.output}`;
|
|
3662
|
+
created.set(name, g.resolve(outputPath));
|
|
3663
|
+
try {
|
|
3664
|
+
const outputNode = g.resolve(outputPath);
|
|
3665
|
+
outputNode.meta._templateName?.down([[DATA, ref.template]]);
|
|
3666
|
+
outputNode.meta._templateBind?.down([[DATA, ref.bind]]);
|
|
3667
|
+
} catch {
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
3671
|
+
if (raw.type === "template") continue;
|
|
3672
|
+
const n = raw;
|
|
3673
|
+
for (const dep of n.deps ?? []) {
|
|
3674
|
+
try {
|
|
3675
|
+
g.connect(dep, name);
|
|
3676
|
+
} catch (err) {
|
|
3677
|
+
const msg = err instanceof Error ? err.message : "";
|
|
3678
|
+
if (!msg.includes("constructor deps") && !msg.includes("already")) {
|
|
3679
|
+
throw err;
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
for (const fb of spec.feedback ?? []) {
|
|
3685
|
+
feedback(g, fb.from, fb.to, {
|
|
3686
|
+
maxIterations: fb.maxIterations
|
|
3687
|
+
});
|
|
3688
|
+
}
|
|
3689
|
+
return g;
|
|
3690
|
+
}
|
|
3691
|
+
var INTERNAL_META_KEYS = /* @__PURE__ */ new Set([
|
|
3692
|
+
"reduction",
|
|
3693
|
+
"reduction_type",
|
|
3694
|
+
"_specFn",
|
|
3695
|
+
"_specSource",
|
|
3696
|
+
"_templateName",
|
|
3697
|
+
"_templateBind",
|
|
3698
|
+
"feedbackFrom",
|
|
3699
|
+
"feedbackTo"
|
|
3700
|
+
]);
|
|
3701
|
+
function decompileGraph(graph) {
|
|
3702
|
+
const desc = graph.describe({ detail: "standard" });
|
|
3703
|
+
const nodes = {};
|
|
3704
|
+
const feedbackEdges = [];
|
|
3705
|
+
const metaSegment = `::${GRAPH_META_SEGMENT}::`;
|
|
3706
|
+
const feedbackCounterPattern = /^__feedback_(.+)$/;
|
|
3707
|
+
const feedbackConditions = /* @__PURE__ */ new Set();
|
|
3708
|
+
for (const path of Object.keys(desc.nodes)) {
|
|
3709
|
+
if (path.includes(metaSegment)) continue;
|
|
3710
|
+
const match = feedbackCounterPattern.exec(path);
|
|
3711
|
+
if (match) {
|
|
3712
|
+
feedbackConditions.add(match[1]);
|
|
3713
|
+
const meta = desc.nodes[path]?.meta;
|
|
3714
|
+
if (meta?.feedbackFrom && meta?.feedbackTo) {
|
|
3715
|
+
feedbackEdges.push({
|
|
3716
|
+
from: meta.feedbackFrom,
|
|
3717
|
+
to: meta.feedbackTo,
|
|
3718
|
+
...meta.maxIterations ? { maxIterations: meta.maxIterations } : {}
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
}
|
|
3722
|
+
}
|
|
3723
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
3724
|
+
if (path.includes(metaSegment)) continue;
|
|
3725
|
+
if (feedbackCounterPattern.test(path)) continue;
|
|
3726
|
+
if (path.includes("::")) continue;
|
|
3727
|
+
const specNode = {
|
|
3728
|
+
type: nodeDesc.type
|
|
3729
|
+
};
|
|
3730
|
+
if (nodeDesc.deps.length > 0) {
|
|
3731
|
+
specNode.deps = nodeDesc.deps.filter((d) => !d.includes("::"));
|
|
3732
|
+
}
|
|
3733
|
+
if (nodeDesc.type === "state" && nodeDesc.value !== void 0) {
|
|
3734
|
+
specNode.initial = nodeDesc.value;
|
|
3735
|
+
}
|
|
3736
|
+
if (nodeDesc.meta && Object.keys(nodeDesc.meta).length > 0) {
|
|
3737
|
+
const meta = {};
|
|
3738
|
+
for (const [k, v] of Object.entries(nodeDesc.meta)) {
|
|
3739
|
+
if (!INTERNAL_META_KEYS.has(k)) meta[k] = v;
|
|
3740
|
+
}
|
|
3741
|
+
if (Object.keys(meta).length > 0) {
|
|
3742
|
+
specNode.meta = meta;
|
|
3743
|
+
}
|
|
3744
|
+
}
|
|
3745
|
+
nodes[path] = specNode;
|
|
3746
|
+
}
|
|
3747
|
+
const templates = {};
|
|
3748
|
+
const templateRefs = {};
|
|
3749
|
+
const metaDetectedSubgraphs = /* @__PURE__ */ new Set();
|
|
3750
|
+
for (const subName of desc.subgraphs) {
|
|
3751
|
+
const prefix = `${subName}::`;
|
|
3752
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
3753
|
+
if (!path.startsWith(prefix)) continue;
|
|
3754
|
+
if (path.includes(metaSegment)) continue;
|
|
3755
|
+
const meta = nodeDesc.meta;
|
|
3756
|
+
if (meta?._templateName && meta?._templateBind) {
|
|
3757
|
+
const templateName = meta._templateName;
|
|
3758
|
+
const bind = meta._templateBind;
|
|
3759
|
+
if (!templates[templateName]) {
|
|
3760
|
+
const tmplNodes = {};
|
|
3761
|
+
const tmplInnerNames = /* @__PURE__ */ new Set();
|
|
3762
|
+
const tmplPrefix = `${subName}::`;
|
|
3763
|
+
for (const [p, nd] of Object.entries(desc.nodes)) {
|
|
3764
|
+
if (!p.startsWith(tmplPrefix) || p.includes(metaSegment)) continue;
|
|
3765
|
+
const localName = p.slice(tmplPrefix.length);
|
|
3766
|
+
if (localName.includes("::")) continue;
|
|
3767
|
+
tmplInnerNames.add(localName);
|
|
3768
|
+
tmplNodes[localName] = {
|
|
3769
|
+
type: nd.type,
|
|
3770
|
+
...nd.deps.length > 0 ? {
|
|
3771
|
+
deps: nd.deps.map(
|
|
3772
|
+
(d) => d.startsWith(tmplPrefix) ? d.slice(tmplPrefix.length) : d
|
|
3773
|
+
)
|
|
3774
|
+
} : {}
|
|
3775
|
+
};
|
|
3776
|
+
}
|
|
3777
|
+
const tmplParams = [];
|
|
3778
|
+
const tmplParamMap = /* @__PURE__ */ new Map();
|
|
3779
|
+
for (const n of Object.values(tmplNodes)) {
|
|
3780
|
+
for (const dep of n.deps ?? []) {
|
|
3781
|
+
if (!tmplInnerNames.has(dep) && !tmplParamMap.has(dep)) {
|
|
3782
|
+
const param = `$${dep}`;
|
|
3783
|
+
tmplParams.push(param);
|
|
3784
|
+
tmplParamMap.set(dep, param);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
}
|
|
3788
|
+
for (const n of Object.values(tmplNodes)) {
|
|
3789
|
+
if (n.deps) n.deps = n.deps.map((d) => tmplParamMap.get(d) ?? d);
|
|
3790
|
+
}
|
|
3791
|
+
const depended = /* @__PURE__ */ new Set();
|
|
3792
|
+
for (const n of Object.values(tmplNodes)) {
|
|
3793
|
+
for (const dep of n.deps ?? []) {
|
|
3794
|
+
if (tmplInnerNames.has(dep)) depended.add(dep);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
const outputCandidates = [...tmplInnerNames].filter((n) => !depended.has(n));
|
|
3798
|
+
const tmplOutput = outputCandidates[0] ?? [...tmplInnerNames].pop();
|
|
3799
|
+
templates[templateName] = { params: tmplParams, nodes: tmplNodes, output: tmplOutput };
|
|
3800
|
+
}
|
|
3801
|
+
delete nodes[subName];
|
|
3802
|
+
templateRefs[subName] = { type: "template", template: templateName, bind };
|
|
3803
|
+
metaDetectedSubgraphs.add(subName);
|
|
3804
|
+
break;
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
const structureMap = /* @__PURE__ */ new Map();
|
|
3809
|
+
for (const subName of desc.subgraphs) {
|
|
3810
|
+
if (metaDetectedSubgraphs.has(subName)) continue;
|
|
3811
|
+
const subNodes = {};
|
|
3812
|
+
const prefix = `${subName}::`;
|
|
3813
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
3814
|
+
if (path.includes(metaSegment)) continue;
|
|
3815
|
+
if (!path.startsWith(prefix)) continue;
|
|
3816
|
+
const localName = path.slice(prefix.length);
|
|
3817
|
+
if (localName.includes("::")) continue;
|
|
3818
|
+
subNodes[localName] = {
|
|
3819
|
+
type: nodeDesc.type,
|
|
3820
|
+
...nodeDesc.deps.length > 0 ? {
|
|
3821
|
+
deps: nodeDesc.deps.map((d) => d.startsWith(prefix) ? d.slice(prefix.length) : d)
|
|
3822
|
+
} : {}
|
|
3823
|
+
};
|
|
3824
|
+
}
|
|
3825
|
+
const fingerprint = JSON.stringify(
|
|
3826
|
+
Object.fromEntries(
|
|
3827
|
+
Object.entries(subNodes).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => [k, { type: v.type, deps: v.deps ?? [] }])
|
|
3828
|
+
)
|
|
3829
|
+
);
|
|
3830
|
+
if (!structureMap.has(fingerprint)) {
|
|
3831
|
+
structureMap.set(fingerprint, []);
|
|
3832
|
+
}
|
|
3833
|
+
structureMap.get(fingerprint).push({ name: subName, nodes: subNodes });
|
|
3834
|
+
}
|
|
3835
|
+
for (const [, group] of structureMap) {
|
|
3836
|
+
if (group.length < 2) continue;
|
|
3837
|
+
const templateName = `${group[0].name}_template`;
|
|
3838
|
+
const refNodes = group[0].nodes;
|
|
3839
|
+
const innerNames = new Set(Object.keys(refNodes));
|
|
3840
|
+
const params = [];
|
|
3841
|
+
const baseParamMap = /* @__PURE__ */ new Map();
|
|
3842
|
+
for (const n of Object.values(refNodes)) {
|
|
3843
|
+
for (const dep of n.deps ?? []) {
|
|
3844
|
+
if (!innerNames.has(dep) && !baseParamMap.has(dep)) {
|
|
3845
|
+
const param = `$${dep}`;
|
|
3846
|
+
params.push(param);
|
|
3847
|
+
baseParamMap.set(dep, param);
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
const depended = /* @__PURE__ */ new Set();
|
|
3852
|
+
for (const n of Object.values(refNodes)) {
|
|
3853
|
+
for (const dep of n.deps ?? []) {
|
|
3854
|
+
if (innerNames.has(dep)) depended.add(dep);
|
|
3855
|
+
}
|
|
3856
|
+
}
|
|
3857
|
+
const outputCandidates = [...innerNames].filter((n) => !depended.has(n));
|
|
3858
|
+
const output = outputCandidates[0] ?? [...innerNames].pop();
|
|
3859
|
+
const tmplNodes = {};
|
|
3860
|
+
for (const [nName, nSpec] of Object.entries(refNodes)) {
|
|
3861
|
+
tmplNodes[nName] = {
|
|
3862
|
+
...nSpec,
|
|
3863
|
+
deps: nSpec.deps?.map((d) => baseParamMap.get(d) ?? d)
|
|
3864
|
+
};
|
|
3865
|
+
}
|
|
3866
|
+
templates[templateName] = { params, nodes: tmplNodes, output };
|
|
3867
|
+
for (const member of group) {
|
|
3868
|
+
delete nodes[member.name];
|
|
3869
|
+
const memberBind = {};
|
|
3870
|
+
const memberInnerNames = new Set(Object.keys(member.nodes));
|
|
3871
|
+
for (const n of Object.values(member.nodes)) {
|
|
3872
|
+
for (const dep of n.deps ?? []) {
|
|
3873
|
+
if (!memberInnerNames.has(dep)) {
|
|
3874
|
+
const param = baseParamMap.get(dep) ?? `$${dep}`;
|
|
3875
|
+
memberBind[param] = dep;
|
|
3876
|
+
}
|
|
3877
|
+
}
|
|
3878
|
+
}
|
|
3879
|
+
templateRefs[member.name] = {
|
|
3880
|
+
type: "template",
|
|
3881
|
+
template: templateName,
|
|
3882
|
+
bind: memberBind
|
|
3883
|
+
};
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
const allNodes = {
|
|
3887
|
+
...nodes,
|
|
3888
|
+
...templateRefs
|
|
3889
|
+
};
|
|
3890
|
+
const result = { name: desc.name, nodes: allNodes };
|
|
3891
|
+
if (Object.keys(templates).length > 0) result.templates = templates;
|
|
3892
|
+
if (feedbackEdges.length > 0) result.feedback = feedbackEdges;
|
|
3893
|
+
return result;
|
|
3894
|
+
}
|
|
3895
|
+
function specDiff(specA, specB) {
|
|
3896
|
+
const entries = [];
|
|
3897
|
+
if (specA.name !== specB.name) {
|
|
3898
|
+
entries.push({
|
|
3899
|
+
type: "changed",
|
|
3900
|
+
path: "name",
|
|
3901
|
+
detail: `"${specA.name}" \u2192 "${specB.name}"`
|
|
3902
|
+
});
|
|
3903
|
+
}
|
|
3904
|
+
const nodesA = new Set(Object.keys(specA.nodes));
|
|
3905
|
+
const nodesB = new Set(Object.keys(specB.nodes));
|
|
3906
|
+
for (const name of nodesB) {
|
|
3907
|
+
if (!nodesA.has(name)) {
|
|
3908
|
+
const n = specB.nodes[name];
|
|
3909
|
+
entries.push({
|
|
3910
|
+
type: "added",
|
|
3911
|
+
path: `nodes.${name}`,
|
|
3912
|
+
detail: `type: ${n.type}`
|
|
3913
|
+
});
|
|
3914
|
+
}
|
|
3915
|
+
}
|
|
3916
|
+
for (const name of nodesA) {
|
|
3917
|
+
if (!nodesB.has(name)) {
|
|
3918
|
+
entries.push({ type: "removed", path: `nodes.${name}` });
|
|
3919
|
+
}
|
|
3920
|
+
}
|
|
3921
|
+
for (const name of nodesA) {
|
|
3922
|
+
if (!nodesB.has(name)) continue;
|
|
3923
|
+
const a = specA.nodes[name];
|
|
3924
|
+
const b = specB.nodes[name];
|
|
3925
|
+
if (JSON.stringify(a) !== JSON.stringify(b)) {
|
|
3926
|
+
const details = [];
|
|
3927
|
+
if (a.type !== b.type) details.push(`type: ${a.type} \u2192 ${b.type}`);
|
|
3928
|
+
if (JSON.stringify(a.deps) !== JSON.stringify(b.deps)) {
|
|
3929
|
+
details.push("deps changed");
|
|
3930
|
+
}
|
|
3931
|
+
if (a.fn !== b.fn) {
|
|
3932
|
+
details.push(`fn: ${a.fn} \u2192 ${b.fn}`);
|
|
3933
|
+
}
|
|
3934
|
+
if (JSON.stringify(a.config) !== JSON.stringify(b.config)) {
|
|
3935
|
+
details.push("config changed");
|
|
3936
|
+
}
|
|
3937
|
+
entries.push({
|
|
3938
|
+
type: "changed",
|
|
3939
|
+
path: `nodes.${name}`,
|
|
3940
|
+
detail: details.join("; ") || "modified"
|
|
3941
|
+
});
|
|
3942
|
+
}
|
|
3943
|
+
}
|
|
3944
|
+
const tmplA = specA.templates ?? {};
|
|
3945
|
+
const tmplB = specB.templates ?? {};
|
|
3946
|
+
const tmplNamesA = new Set(Object.keys(tmplA));
|
|
3947
|
+
const tmplNamesB = new Set(Object.keys(tmplB));
|
|
3948
|
+
for (const name of tmplNamesB) {
|
|
3949
|
+
if (!tmplNamesA.has(name)) {
|
|
3950
|
+
entries.push({ type: "added", path: `templates.${name}` });
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
for (const name of tmplNamesA) {
|
|
3954
|
+
if (!tmplNamesB.has(name)) {
|
|
3955
|
+
entries.push({ type: "removed", path: `templates.${name}` });
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
for (const name of tmplNamesA) {
|
|
3959
|
+
if (!tmplNamesB.has(name)) continue;
|
|
3960
|
+
if (JSON.stringify(tmplA[name]) !== JSON.stringify(tmplB[name])) {
|
|
3961
|
+
entries.push({
|
|
3962
|
+
type: "changed",
|
|
3963
|
+
path: `templates.${name}`,
|
|
3964
|
+
detail: "template definition changed"
|
|
3965
|
+
});
|
|
3966
|
+
}
|
|
3967
|
+
}
|
|
3968
|
+
const fbA = specA.feedback ?? [];
|
|
3969
|
+
const fbB = specB.feedback ?? [];
|
|
3970
|
+
const fbKeyA = new Set(fbA.map((e) => `${e.from}->${e.to}`));
|
|
3971
|
+
const fbKeyB = new Set(fbB.map((e) => `${e.from}->${e.to}`));
|
|
3972
|
+
for (const fb of fbB) {
|
|
3973
|
+
const key = `${fb.from}->${fb.to}`;
|
|
3974
|
+
if (!fbKeyA.has(key)) {
|
|
3975
|
+
entries.push({
|
|
3976
|
+
type: "added",
|
|
3977
|
+
path: `feedback.${key}`,
|
|
3978
|
+
detail: `maxIterations: ${fb.maxIterations ?? 10}`
|
|
3979
|
+
});
|
|
3980
|
+
}
|
|
3981
|
+
}
|
|
3982
|
+
for (const fb of fbA) {
|
|
3983
|
+
const key = `${fb.from}->${fb.to}`;
|
|
3984
|
+
if (!fbKeyB.has(key)) {
|
|
3985
|
+
entries.push({ type: "removed", path: `feedback.${key}` });
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
for (const fb of fbA) {
|
|
3989
|
+
const key = `${fb.from}->${fb.to}`;
|
|
3990
|
+
const counterpart = fbB.find((b) => b.from === fb.from && b.to === fb.to);
|
|
3991
|
+
if (counterpart && JSON.stringify(fb) !== JSON.stringify(counterpart)) {
|
|
3992
|
+
entries.push({
|
|
3993
|
+
type: "changed",
|
|
3994
|
+
path: `feedback.${key}`,
|
|
3995
|
+
detail: `maxIterations: ${fb.maxIterations ?? 10} \u2192 ${counterpart.maxIterations ?? 10}`
|
|
3996
|
+
});
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
const added = entries.filter((e) => e.type === "added").length;
|
|
4000
|
+
const removed = entries.filter((e) => e.type === "removed").length;
|
|
4001
|
+
const changed = entries.filter((e) => e.type === "changed").length;
|
|
4002
|
+
const parts = [];
|
|
4003
|
+
if (added) parts.push(`${added} added`);
|
|
4004
|
+
if (removed) parts.push(`${removed} removed`);
|
|
4005
|
+
if (changed) parts.push(`${changed} changed`);
|
|
4006
|
+
const summary = parts.length > 0 ? parts.join(", ") : "no changes";
|
|
4007
|
+
return { entries, summary };
|
|
4008
|
+
}
|
|
4009
|
+
var LLM_COMPOSE_SYSTEM_PROMPT = `You are a graph architect for GraphReFly, a reactive graph protocol.
|
|
4010
|
+
|
|
4011
|
+
Given a natural-language description, produce a JSON GraphSpec with this structure:
|
|
4012
|
+
|
|
4013
|
+
{
|
|
4014
|
+
"name": "<graph_name>",
|
|
4015
|
+
"nodes": {
|
|
4016
|
+
"<node_name>": {
|
|
4017
|
+
"type": "state" | "derived" | "producer" | "effect" | "operator",
|
|
4018
|
+
"deps": ["<dep_node_name>", ...],
|
|
4019
|
+
"fn": "<catalog_function_name>",
|
|
4020
|
+
"source": "<catalog_source_name>",
|
|
4021
|
+
"config": { ... },
|
|
4022
|
+
"initial": <value>,
|
|
4023
|
+
"meta": { "description": "<purpose>" }
|
|
4024
|
+
},
|
|
4025
|
+
"<template_instance>": {
|
|
4026
|
+
"type": "template",
|
|
4027
|
+
"template": "<template_name>",
|
|
4028
|
+
"bind": { "$param": "node_name" }
|
|
4029
|
+
}
|
|
4030
|
+
},
|
|
4031
|
+
"templates": {
|
|
4032
|
+
"<template_name>": {
|
|
4033
|
+
"params": ["$param1", "$param2"],
|
|
4034
|
+
"nodes": { ... },
|
|
4035
|
+
"output": "<output_node>"
|
|
4036
|
+
}
|
|
4037
|
+
},
|
|
4038
|
+
"feedback": [
|
|
4039
|
+
{ "from": "<condition_node>", "to": "<state_node>", "maxIterations": 10 }
|
|
4040
|
+
]
|
|
4041
|
+
}
|
|
4042
|
+
|
|
4043
|
+
Rules:
|
|
4044
|
+
- "state" nodes hold user/LLM-writable values (knobs). Use "initial" for default values.
|
|
4045
|
+
- "derived" nodes compute from deps using a named "fn".
|
|
4046
|
+
- "effect" nodes produce side effects from deps.
|
|
4047
|
+
- "producer" nodes generate values from a named "source".
|
|
4048
|
+
- Use "templates" when the same subgraph pattern repeats (e.g., per-source resilience).
|
|
4049
|
+
- Use "feedback" for bounded cycles where a derived value writes back to a state node.
|
|
4050
|
+
- meta.description is required for every node.
|
|
4051
|
+
- Return ONLY valid JSON, no markdown fences or commentary.`;
|
|
4052
|
+
function stripFences2(text) {
|
|
4053
|
+
const match = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```[\s\S]*$/);
|
|
4054
|
+
return match ? match[1] : text;
|
|
4055
|
+
}
|
|
4056
|
+
async function llmCompose(problem, adapter, opts) {
|
|
4057
|
+
let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
|
|
4058
|
+
if (opts?.catalogDescription) {
|
|
4059
|
+
systemPrompt += `
|
|
4060
|
+
|
|
4061
|
+
Available catalog:
|
|
4062
|
+
${opts.catalogDescription}`;
|
|
4063
|
+
}
|
|
4064
|
+
if (opts?.systemPromptExtra) {
|
|
4065
|
+
systemPrompt += `
|
|
4066
|
+
|
|
4067
|
+
${opts.systemPromptExtra}`;
|
|
4068
|
+
}
|
|
4069
|
+
const messages = [
|
|
4070
|
+
{ role: "system", content: systemPrompt },
|
|
4071
|
+
{ role: "user", content: problem }
|
|
4072
|
+
];
|
|
4073
|
+
const rawResult = adapter.invoke(messages, {
|
|
4074
|
+
model: opts?.model,
|
|
4075
|
+
temperature: opts?.temperature ?? 0,
|
|
4076
|
+
maxTokens: opts?.maxTokens
|
|
4077
|
+
});
|
|
4078
|
+
const response = await rawResult;
|
|
4079
|
+
let content = response.content.trim();
|
|
4080
|
+
if (content.startsWith("```")) {
|
|
4081
|
+
content = stripFences2(content);
|
|
4082
|
+
}
|
|
4083
|
+
let parsed;
|
|
4084
|
+
try {
|
|
4085
|
+
parsed = JSON.parse(content);
|
|
4086
|
+
} catch {
|
|
4087
|
+
throw new Error(`llmCompose: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
4088
|
+
}
|
|
4089
|
+
const validation = validateSpec(parsed);
|
|
4090
|
+
if (!validation.valid) {
|
|
4091
|
+
throw new Error(`llmCompose: invalid GraphSpec:
|
|
4092
|
+
${validation.errors.join("\n")}`);
|
|
4093
|
+
}
|
|
4094
|
+
return parsed;
|
|
4095
|
+
}
|
|
4096
|
+
async function llmRefine(currentSpec, feedback2, adapter, opts) {
|
|
4097
|
+
let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
|
|
4098
|
+
if (opts?.catalogDescription) {
|
|
4099
|
+
systemPrompt += `
|
|
4100
|
+
|
|
4101
|
+
Available catalog:
|
|
4102
|
+
${opts.catalogDescription}`;
|
|
4103
|
+
}
|
|
4104
|
+
if (opts?.systemPromptExtra) {
|
|
4105
|
+
systemPrompt += `
|
|
4106
|
+
|
|
4107
|
+
${opts.systemPromptExtra}`;
|
|
4108
|
+
}
|
|
4109
|
+
const messages = [
|
|
4110
|
+
{ role: "system", content: systemPrompt },
|
|
4111
|
+
{
|
|
4112
|
+
role: "user",
|
|
4113
|
+
content: `Current GraphSpec:
|
|
4114
|
+
${JSON.stringify(currentSpec, null, 2)}
|
|
4115
|
+
|
|
4116
|
+
Modification request: ${feedback2}
|
|
4117
|
+
|
|
4118
|
+
Return the complete modified GraphSpec as JSON.`
|
|
4119
|
+
}
|
|
4120
|
+
];
|
|
4121
|
+
const rawResult = adapter.invoke(messages, {
|
|
4122
|
+
model: opts?.model,
|
|
4123
|
+
temperature: opts?.temperature ?? 0,
|
|
4124
|
+
maxTokens: opts?.maxTokens
|
|
4125
|
+
});
|
|
4126
|
+
const response = await rawResult;
|
|
4127
|
+
let content = response.content.trim();
|
|
4128
|
+
if (content.startsWith("```")) {
|
|
4129
|
+
content = stripFences2(content);
|
|
4130
|
+
}
|
|
4131
|
+
let parsed;
|
|
4132
|
+
try {
|
|
4133
|
+
parsed = JSON.parse(content);
|
|
4134
|
+
} catch {
|
|
4135
|
+
throw new Error(`llmRefine: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
4136
|
+
}
|
|
4137
|
+
const validation = validateSpec(parsed);
|
|
4138
|
+
if (!validation.valid) {
|
|
4139
|
+
throw new Error(`llmRefine: invalid GraphSpec:
|
|
4140
|
+
${validation.errors.join("\n")}`);
|
|
4141
|
+
}
|
|
4142
|
+
return parsed;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
// src/patterns/messaging.ts
|
|
4146
|
+
var messaging_exports = {};
|
|
4147
|
+
__export(messaging_exports, {
|
|
4148
|
+
JobFlowGraph: () => JobFlowGraph,
|
|
4149
|
+
JobQueueGraph: () => JobQueueGraph,
|
|
4150
|
+
SubscriptionGraph: () => SubscriptionGraph,
|
|
4151
|
+
TopicBridgeGraph: () => TopicBridgeGraph,
|
|
4152
|
+
TopicGraph: () => TopicGraph,
|
|
4153
|
+
jobFlow: () => jobFlow,
|
|
4154
|
+
jobQueue: () => jobQueue,
|
|
4155
|
+
subscription: () => subscription,
|
|
4156
|
+
topic: () => topic,
|
|
4157
|
+
topicBridge: () => topicBridge
|
|
4158
|
+
});
|
|
4159
|
+
var DEFAULT_MAX_PER_PUMP = 2147483647;
|
|
4160
|
+
function requireNonNegativeInt(value, label) {
|
|
4161
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
4162
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
4163
|
+
}
|
|
4164
|
+
return value;
|
|
4165
|
+
}
|
|
4166
|
+
function keepalive2(n) {
|
|
4167
|
+
return n.subscribe(() => {
|
|
4168
|
+
});
|
|
4169
|
+
}
|
|
4170
|
+
function messagingMeta(kind, extra) {
|
|
4171
|
+
return {
|
|
4172
|
+
messaging: true,
|
|
4173
|
+
messaging_type: kind,
|
|
4174
|
+
...extra ?? {}
|
|
4175
|
+
};
|
|
4176
|
+
}
|
|
4177
|
+
var TopicGraph = class extends Graph {
|
|
4178
|
+
_log;
|
|
4179
|
+
_keepaliveDisposers = [];
|
|
4180
|
+
events;
|
|
4181
|
+
latest;
|
|
4182
|
+
constructor(name, opts = {}) {
|
|
4183
|
+
super(name, opts.graph);
|
|
4184
|
+
this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
|
|
4185
|
+
this.events = this._log.entries;
|
|
4186
|
+
this.add("events", this.events);
|
|
4187
|
+
this.latest = derived(
|
|
4188
|
+
[this.events],
|
|
4189
|
+
([snapshot]) => {
|
|
4190
|
+
const entries = snapshot.value.entries;
|
|
4191
|
+
return entries.length === 0 ? void 0 : entries[entries.length - 1];
|
|
4192
|
+
},
|
|
4193
|
+
{
|
|
4194
|
+
name: "latest",
|
|
4195
|
+
describeKind: "derived",
|
|
4196
|
+
meta: messagingMeta("topic_latest"),
|
|
4197
|
+
initial: void 0
|
|
4198
|
+
}
|
|
4199
|
+
);
|
|
4200
|
+
this.add("latest", this.latest);
|
|
4201
|
+
this.connect("events", "latest");
|
|
4202
|
+
this._keepaliveDisposers.push(keepalive2(this.latest));
|
|
4203
|
+
}
|
|
4204
|
+
destroy() {
|
|
4205
|
+
for (const dispose of this._keepaliveDisposers) dispose();
|
|
4206
|
+
this._keepaliveDisposers.length = 0;
|
|
4207
|
+
super.destroy();
|
|
4208
|
+
}
|
|
4209
|
+
publish(value) {
|
|
4210
|
+
this._log.append(value);
|
|
4211
|
+
}
|
|
4212
|
+
retained() {
|
|
4213
|
+
const snapshot = this.events.get();
|
|
4214
|
+
return snapshot.value.entries;
|
|
4215
|
+
}
|
|
4216
|
+
};
|
|
4217
|
+
var SubscriptionGraph = class extends Graph {
|
|
4218
|
+
_keepaliveDisposers = [];
|
|
4219
|
+
source;
|
|
4220
|
+
cursor;
|
|
4221
|
+
available;
|
|
4222
|
+
constructor(name, topicGraph, opts = {}) {
|
|
4223
|
+
super(name, opts.graph);
|
|
4224
|
+
const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
|
|
4225
|
+
this.mount("topic", topicGraph);
|
|
4226
|
+
const topicEvents = topicGraph.events;
|
|
4227
|
+
this.source = derived([topicEvents], ([snapshot]) => snapshot, {
|
|
4228
|
+
name: "source",
|
|
4229
|
+
describeKind: "derived",
|
|
4230
|
+
meta: messagingMeta("subscription_source"),
|
|
3113
4231
|
initial: topicEvents.get()
|
|
3114
4232
|
});
|
|
3115
4233
|
this.add("source", this.source);
|
|
@@ -3472,7 +4590,7 @@ function registerStep(graph, name, step, depPaths) {
|
|
|
3472
4590
|
graph.connect(path, name);
|
|
3473
4591
|
}
|
|
3474
4592
|
}
|
|
3475
|
-
function
|
|
4593
|
+
function baseMeta2(kind, meta) {
|
|
3476
4594
|
return {
|
|
3477
4595
|
orchestration: true,
|
|
3478
4596
|
orchestration_type: kind,
|
|
@@ -3510,7 +4628,7 @@ function task(graph, name, run, opts) {
|
|
|
3510
4628
|
...nodeOpts,
|
|
3511
4629
|
name,
|
|
3512
4630
|
describeKind: "derived",
|
|
3513
|
-
meta:
|
|
4631
|
+
meta: baseMeta2("task", opts?.meta)
|
|
3514
4632
|
}
|
|
3515
4633
|
);
|
|
3516
4634
|
registerStep(
|
|
@@ -3533,7 +4651,7 @@ function branch(graph, name, source, predicate, opts) {
|
|
|
3533
4651
|
...opts,
|
|
3534
4652
|
name,
|
|
3535
4653
|
describeKind: "derived",
|
|
3536
|
-
meta:
|
|
4654
|
+
meta: baseMeta2("branch", opts?.meta)
|
|
3537
4655
|
}
|
|
3538
4656
|
);
|
|
3539
4657
|
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
@@ -3543,535 +4661,264 @@ function gate2(graph, name, source, control, opts) {
|
|
|
3543
4661
|
const src = resolveDep(graph, source);
|
|
3544
4662
|
const ctrl = resolveDep(graph, control);
|
|
3545
4663
|
const step = node(
|
|
3546
|
-
[src.node, ctrl.node],
|
|
3547
|
-
(_deps, actions) => {
|
|
3548
|
-
const opened = ctrl.node.get();
|
|
3549
|
-
if (!opened) {
|
|
3550
|
-
actions.down([[RESOLVED]]);
|
|
3551
|
-
return void 0;
|
|
3552
|
-
}
|
|
3553
|
-
return src.node.get();
|
|
3554
|
-
},
|
|
3555
|
-
{
|
|
3556
|
-
...opts,
|
|
3557
|
-
name,
|
|
3558
|
-
describeKind: "operator",
|
|
3559
|
-
meta: baseMeta("gate", opts?.meta)
|
|
3560
|
-
}
|
|
3561
|
-
);
|
|
3562
|
-
registerStep(
|
|
3563
|
-
graph,
|
|
3564
|
-
name,
|
|
3565
|
-
step,
|
|
3566
|
-
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
3567
|
-
);
|
|
3568
|
-
return step;
|
|
3569
|
-
}
|
|
3570
|
-
function approval(graph, name, source, approver, opts) {
|
|
3571
|
-
const src = resolveDep(graph, source);
|
|
3572
|
-
const ctrl = resolveDep(graph, approver);
|
|
3573
|
-
const isApproved = opts?.isApproved ?? ((value) => Boolean(value));
|
|
3574
|
-
const step = node(
|
|
3575
|
-
[src.node, ctrl.node],
|
|
3576
|
-
(_deps, actions) => {
|
|
3577
|
-
if (!isApproved(ctrl.node.get())) {
|
|
3578
|
-
actions.down([[RESOLVED]]);
|
|
3579
|
-
return void 0;
|
|
3580
|
-
}
|
|
3581
|
-
return src.node.get();
|
|
3582
|
-
},
|
|
3583
|
-
{
|
|
3584
|
-
...opts,
|
|
3585
|
-
name,
|
|
3586
|
-
describeKind: "operator",
|
|
3587
|
-
meta: baseMeta("approval", opts?.meta)
|
|
3588
|
-
}
|
|
3589
|
-
);
|
|
3590
|
-
registerStep(
|
|
3591
|
-
graph,
|
|
3592
|
-
name,
|
|
3593
|
-
step,
|
|
3594
|
-
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
3595
|
-
);
|
|
3596
|
-
return step;
|
|
3597
|
-
}
|
|
3598
|
-
function forEach2(graph, name, source, run, opts) {
|
|
3599
|
-
const src = resolveDep(graph, source);
|
|
3600
|
-
let terminated = false;
|
|
3601
|
-
const step = node([src.node], () => void 0, {
|
|
3602
|
-
...opts,
|
|
3603
|
-
name,
|
|
3604
|
-
describeKind: "effect",
|
|
3605
|
-
completeWhenDepsComplete: false,
|
|
3606
|
-
meta: baseMeta("forEach", opts?.meta),
|
|
3607
|
-
onMessage(msg, depIndex, actions) {
|
|
3608
|
-
if (terminated) return true;
|
|
3609
|
-
if (depIndex !== 0) {
|
|
3610
|
-
actions.down([msg]);
|
|
3611
|
-
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
3612
|
-
return true;
|
|
3613
|
-
}
|
|
3614
|
-
if (msg[0] === DATA) {
|
|
3615
|
-
try {
|
|
3616
|
-
run(msg[1], actions);
|
|
3617
|
-
actions.down([msg]);
|
|
3618
|
-
} catch (err) {
|
|
3619
|
-
terminated = true;
|
|
3620
|
-
actions.down([[ERROR, err]]);
|
|
3621
|
-
}
|
|
3622
|
-
return true;
|
|
3623
|
-
}
|
|
3624
|
-
actions.down([msg]);
|
|
3625
|
-
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
3626
|
-
return true;
|
|
3627
|
-
}
|
|
3628
|
-
});
|
|
3629
|
-
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
3630
|
-
return step;
|
|
3631
|
-
}
|
|
3632
|
-
function join(graph, name, deps, opts) {
|
|
3633
|
-
const resolved = deps.map((dep) => resolveDep(graph, dep));
|
|
3634
|
-
const step = node(
|
|
3635
|
-
resolved.map((d) => d.node),
|
|
3636
|
-
(values) => values,
|
|
3637
|
-
{
|
|
3638
|
-
...opts,
|
|
3639
|
-
name,
|
|
3640
|
-
describeKind: "derived",
|
|
3641
|
-
meta: baseMeta("join", opts?.meta)
|
|
3642
|
-
}
|
|
3643
|
-
);
|
|
3644
|
-
registerStep(
|
|
3645
|
-
graph,
|
|
3646
|
-
name,
|
|
3647
|
-
step,
|
|
3648
|
-
resolved.flatMap((d) => d.path ? [d.path] : [])
|
|
3649
|
-
);
|
|
3650
|
-
return step;
|
|
3651
|
-
}
|
|
3652
|
-
function loop(graph, name, source, iterate, opts) {
|
|
3653
|
-
const src = resolveDep(graph, source);
|
|
3654
|
-
const iterRef = opts?.iterations;
|
|
3655
|
-
const iterDep = typeof iterRef === "number" || iterRef === void 0 ? void 0 : resolveDep(graph, iterRef);
|
|
3656
|
-
const staticIterations = typeof iterRef === "number" ? iterRef : void 0;
|
|
3657
|
-
const step = node(
|
|
3658
|
-
iterDep ? [src.node, iterDep.node] : [src.node],
|
|
3659
|
-
(_deps, actions) => {
|
|
3660
|
-
let current = src.node.get();
|
|
3661
|
-
const rawCount = staticIterations ?? iterDep?.node.get() ?? 1;
|
|
3662
|
-
const count = coerceLoopIterations(rawCount);
|
|
3663
|
-
for (let i = 0; i < count; i += 1) {
|
|
3664
|
-
current = iterate(current, i, actions);
|
|
3665
|
-
}
|
|
3666
|
-
return current;
|
|
3667
|
-
},
|
|
3668
|
-
{
|
|
3669
|
-
...opts,
|
|
3670
|
-
name,
|
|
3671
|
-
describeKind: "derived",
|
|
3672
|
-
meta: baseMeta("loop", opts?.meta)
|
|
3673
|
-
}
|
|
3674
|
-
);
|
|
3675
|
-
registerStep(
|
|
3676
|
-
graph,
|
|
3677
|
-
name,
|
|
3678
|
-
step,
|
|
3679
|
-
[src.path, iterDep?.path].filter((v) => typeof v === "string")
|
|
3680
|
-
);
|
|
3681
|
-
return step;
|
|
3682
|
-
}
|
|
3683
|
-
function subPipeline(graph, name, childOrBuild, opts) {
|
|
3684
|
-
const child = childOrBuild instanceof Graph ? childOrBuild : pipeline(name, opts);
|
|
3685
|
-
if (typeof childOrBuild === "function") {
|
|
3686
|
-
childOrBuild(child);
|
|
3687
|
-
}
|
|
3688
|
-
graph.mount(name, child);
|
|
3689
|
-
return child;
|
|
3690
|
-
}
|
|
3691
|
-
function sensor(graph, name, initial, opts) {
|
|
3692
|
-
const source = node([], () => void 0, {
|
|
3693
|
-
...opts,
|
|
3694
|
-
name,
|
|
3695
|
-
initial,
|
|
3696
|
-
describeKind: "producer",
|
|
3697
|
-
meta: baseMeta("sensor", opts?.meta)
|
|
3698
|
-
});
|
|
3699
|
-
registerStep(graph, name, source, []);
|
|
3700
|
-
return {
|
|
3701
|
-
node: source,
|
|
3702
|
-
push(value) {
|
|
3703
|
-
source.down([[DATA, value]]);
|
|
3704
|
-
},
|
|
3705
|
-
error(err) {
|
|
3706
|
-
source.down([[ERROR, err]]);
|
|
4664
|
+
[src.node, ctrl.node],
|
|
4665
|
+
(_deps, actions) => {
|
|
4666
|
+
const opened = ctrl.node.get();
|
|
4667
|
+
if (!opened) {
|
|
4668
|
+
actions.down([[RESOLVED]]);
|
|
4669
|
+
return void 0;
|
|
4670
|
+
}
|
|
4671
|
+
return src.node.get();
|
|
3707
4672
|
},
|
|
3708
|
-
|
|
3709
|
-
|
|
4673
|
+
{
|
|
4674
|
+
...opts,
|
|
4675
|
+
name,
|
|
4676
|
+
describeKind: "operator",
|
|
4677
|
+
meta: baseMeta2("gate", opts?.meta)
|
|
3710
4678
|
}
|
|
3711
|
-
|
|
4679
|
+
);
|
|
4680
|
+
registerStep(
|
|
4681
|
+
graph,
|
|
4682
|
+
name,
|
|
4683
|
+
step,
|
|
4684
|
+
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
4685
|
+
);
|
|
4686
|
+
return step;
|
|
3712
4687
|
}
|
|
3713
|
-
function
|
|
4688
|
+
function approval(graph, name, source, approver, opts) {
|
|
3714
4689
|
const src = resolveDep(graph, source);
|
|
3715
|
-
const
|
|
3716
|
-
|
|
3717
|
-
let completed = false;
|
|
4690
|
+
const ctrl = resolveDep(graph, approver);
|
|
4691
|
+
const isApproved = opts?.isApproved ?? ((value) => Boolean(value));
|
|
3718
4692
|
const step = node(
|
|
3719
|
-
[src.node],
|
|
3720
|
-
() => {
|
|
3721
|
-
|
|
3722
|
-
|
|
3723
|
-
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
terminated = true;
|
|
3727
|
-
};
|
|
4693
|
+
[src.node, ctrl.node],
|
|
4694
|
+
(_deps, actions) => {
|
|
4695
|
+
if (!isApproved(ctrl.node.get())) {
|
|
4696
|
+
actions.down([[RESOLVED]]);
|
|
4697
|
+
return void 0;
|
|
4698
|
+
}
|
|
4699
|
+
return src.node.get();
|
|
3728
4700
|
},
|
|
3729
4701
|
{
|
|
3730
4702
|
...opts,
|
|
3731
4703
|
name,
|
|
3732
|
-
initial: src.node.get(),
|
|
3733
4704
|
describeKind: "operator",
|
|
3734
|
-
|
|
3735
|
-
meta: baseMeta("wait", opts?.meta),
|
|
3736
|
-
onMessage(msg, depIndex, actions) {
|
|
3737
|
-
if (terminated) return true;
|
|
3738
|
-
if (depIndex !== 0) {
|
|
3739
|
-
actions.down([msg]);
|
|
3740
|
-
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
3741
|
-
return true;
|
|
3742
|
-
}
|
|
3743
|
-
if (msg[0] === DATA) {
|
|
3744
|
-
const id = setTimeout(() => {
|
|
3745
|
-
timers.delete(id);
|
|
3746
|
-
actions.down([msg]);
|
|
3747
|
-
if (completed && timers.size === 0) {
|
|
3748
|
-
actions.down([[COMPLETE]]);
|
|
3749
|
-
}
|
|
3750
|
-
}, ms);
|
|
3751
|
-
timers.add(id);
|
|
3752
|
-
return true;
|
|
3753
|
-
}
|
|
3754
|
-
if (msg[0] === COMPLETE) {
|
|
3755
|
-
terminated = true;
|
|
3756
|
-
completed = true;
|
|
3757
|
-
if (timers.size === 0) {
|
|
3758
|
-
actions.down([[COMPLETE]]);
|
|
3759
|
-
}
|
|
3760
|
-
return true;
|
|
3761
|
-
}
|
|
3762
|
-
if (msg[0] === ERROR) {
|
|
3763
|
-
terminated = true;
|
|
3764
|
-
for (const id of timers) clearTimeout(id);
|
|
3765
|
-
timers.clear();
|
|
3766
|
-
actions.down([msg]);
|
|
3767
|
-
return true;
|
|
3768
|
-
}
|
|
3769
|
-
actions.down([msg]);
|
|
3770
|
-
return true;
|
|
3771
|
-
}
|
|
4705
|
+
meta: baseMeta2("approval", opts?.meta)
|
|
3772
4706
|
}
|
|
3773
4707
|
);
|
|
3774
|
-
registerStep(
|
|
4708
|
+
registerStep(
|
|
4709
|
+
graph,
|
|
4710
|
+
name,
|
|
4711
|
+
step,
|
|
4712
|
+
[src.path, ctrl.path].filter((v) => typeof v === "string")
|
|
4713
|
+
);
|
|
3775
4714
|
return step;
|
|
3776
4715
|
}
|
|
3777
|
-
function
|
|
4716
|
+
function forEach2(graph, name, source, run, opts) {
|
|
3778
4717
|
const src = resolveDep(graph, source);
|
|
3779
4718
|
let terminated = false;
|
|
3780
4719
|
const step = node([src.node], () => void 0, {
|
|
3781
4720
|
...opts,
|
|
3782
4721
|
name,
|
|
3783
|
-
describeKind: "
|
|
4722
|
+
describeKind: "effect",
|
|
3784
4723
|
completeWhenDepsComplete: false,
|
|
3785
|
-
meta:
|
|
3786
|
-
onMessage(msg, _depIndex, actions) {
|
|
3787
|
-
if (terminated) return true;
|
|
3788
|
-
if (msg[0] === ERROR) {
|
|
3789
|
-
try {
|
|
3790
|
-
actions.emit(recover(msg[1], actions));
|
|
3791
|
-
} catch (err) {
|
|
3792
|
-
terminated = true;
|
|
3793
|
-
actions.down([[ERROR, err]]);
|
|
3794
|
-
}
|
|
3795
|
-
return true;
|
|
3796
|
-
}
|
|
3797
|
-
actions.down([msg]);
|
|
3798
|
-
if (msg[0] === COMPLETE) terminated = true;
|
|
3799
|
-
return true;
|
|
3800
|
-
}
|
|
3801
|
-
});
|
|
3802
|
-
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
3803
|
-
return step;
|
|
3804
|
-
}
|
|
3805
|
-
|
|
3806
|
-
// src/patterns/reduction.ts
|
|
3807
|
-
var reduction_exports = {};
|
|
3808
|
-
__export(reduction_exports, {
|
|
3809
|
-
budgetGate: () => budgetGate,
|
|
3810
|
-
feedback: () => feedback,
|
|
3811
|
-
funnel: () => funnel,
|
|
3812
|
-
scorer: () => scorer,
|
|
3813
|
-
stratify: () => stratify
|
|
3814
|
-
});
|
|
3815
|
-
function baseMeta2(kind, meta) {
|
|
3816
|
-
return {
|
|
3817
|
-
reduction: true,
|
|
3818
|
-
reduction_type: kind,
|
|
3819
|
-
...meta ?? {}
|
|
3820
|
-
};
|
|
3821
|
-
}
|
|
3822
|
-
function stratify(name, source, rules, opts) {
|
|
3823
|
-
const g = new Graph(name, opts);
|
|
3824
|
-
g.add("source", source);
|
|
3825
|
-
const rulesNode = state(rules, {
|
|
3826
|
-
meta: baseMeta2("stratify_rules")
|
|
3827
|
-
});
|
|
3828
|
-
g.add("rules", rulesNode);
|
|
3829
|
-
for (const rule of rules) {
|
|
3830
|
-
_addBranch(g, source, rulesNode, rule);
|
|
3831
|
-
}
|
|
3832
|
-
return g;
|
|
3833
|
-
}
|
|
3834
|
-
function _addBranch(graph, source, rulesNode, rule) {
|
|
3835
|
-
const branchName = `branch/${rule.name}`;
|
|
3836
|
-
let pendingDirty = false;
|
|
3837
|
-
const filterNode = node([source, rulesNode], () => void 0, {
|
|
3838
|
-
describeKind: "operator",
|
|
3839
|
-
meta: baseMeta2("stratify_branch", { branch: rule.name }),
|
|
4724
|
+
meta: baseMeta2("forEach", opts?.meta),
|
|
3840
4725
|
onMessage(msg, depIndex, actions) {
|
|
3841
|
-
if (
|
|
3842
|
-
|
|
3843
|
-
if (t === DATA) {
|
|
3844
|
-
const value = msg[1];
|
|
3845
|
-
const currentRules = rulesNode.get();
|
|
3846
|
-
const currentRule = currentRules.find((r) => r.name === rule.name);
|
|
3847
|
-
if (currentRule && currentRule.classify(value)) {
|
|
3848
|
-
pendingDirty = false;
|
|
3849
|
-
actions.emit(value);
|
|
3850
|
-
} else {
|
|
3851
|
-
if (pendingDirty) {
|
|
3852
|
-
pendingDirty = false;
|
|
3853
|
-
actions.down([[DIRTY], [RESOLVED]]);
|
|
3854
|
-
}
|
|
3855
|
-
}
|
|
3856
|
-
return true;
|
|
3857
|
-
}
|
|
3858
|
-
if (t === DIRTY) {
|
|
3859
|
-
pendingDirty = true;
|
|
3860
|
-
return true;
|
|
3861
|
-
}
|
|
3862
|
-
if (t === RESOLVED) {
|
|
3863
|
-
if (pendingDirty) {
|
|
3864
|
-
pendingDirty = false;
|
|
3865
|
-
actions.down([[DIRTY], [RESOLVED]]);
|
|
3866
|
-
} else {
|
|
3867
|
-
actions.down([[RESOLVED]]);
|
|
3868
|
-
}
|
|
3869
|
-
return true;
|
|
3870
|
-
}
|
|
3871
|
-
if (t === COMPLETE || t === ERROR) {
|
|
3872
|
-
pendingDirty = false;
|
|
4726
|
+
if (terminated) return true;
|
|
4727
|
+
if (depIndex !== 0) {
|
|
3873
4728
|
actions.down([msg]);
|
|
4729
|
+
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
3874
4730
|
return true;
|
|
3875
4731
|
}
|
|
3876
|
-
return false;
|
|
3877
|
-
}
|
|
3878
|
-
});
|
|
3879
|
-
graph.add(branchName, filterNode);
|
|
3880
|
-
graph.connect("source", branchName);
|
|
3881
|
-
if (rule.ops) {
|
|
3882
|
-
const transformed = rule.ops(filterNode);
|
|
3883
|
-
const transformedName = `branch/${rule.name}/out`;
|
|
3884
|
-
graph.add(transformedName, transformed);
|
|
3885
|
-
graph.connect(branchName, transformedName);
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
function funnel(name, sources, stages, opts) {
|
|
3889
|
-
if (sources.length === 0) throw new RangeError("funnel requires at least one source");
|
|
3890
|
-
if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
|
|
3891
|
-
const g = new Graph(name, opts);
|
|
3892
|
-
const merged = sources.length === 1 ? sources[0] : merge(...sources);
|
|
3893
|
-
g.add("merged", merged);
|
|
3894
|
-
let prevOutputPath = "merged";
|
|
3895
|
-
for (let i = 0; i < stages.length; i++) {
|
|
3896
|
-
const stage = stages[i];
|
|
3897
|
-
const sub = new Graph(stage.name);
|
|
3898
|
-
stage.build(sub);
|
|
3899
|
-
try {
|
|
3900
|
-
sub.resolve("input");
|
|
3901
|
-
} catch {
|
|
3902
|
-
throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
|
|
3903
|
-
}
|
|
3904
|
-
try {
|
|
3905
|
-
sub.resolve("output");
|
|
3906
|
-
} catch {
|
|
3907
|
-
throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
|
|
3908
|
-
}
|
|
3909
|
-
g.mount(stage.name, sub);
|
|
3910
|
-
const prevNode = g.resolve(prevOutputPath);
|
|
3911
|
-
const stageInputPath = `${stage.name}::input`;
|
|
3912
|
-
const stageInput = g.resolve(stageInputPath);
|
|
3913
|
-
prevNode.subscribe((msgs) => {
|
|
3914
|
-
for (const msg of msgs) {
|
|
3915
|
-
const t = msg[0];
|
|
3916
|
-
if (t === DATA) {
|
|
3917
|
-
stageInput.down([[DATA, msg[1]]]);
|
|
3918
|
-
} else if (t === DIRTY) {
|
|
3919
|
-
stageInput.down([[DIRTY]]);
|
|
3920
|
-
} else if (t === RESOLVED) {
|
|
3921
|
-
stageInput.down([[RESOLVED]]);
|
|
3922
|
-
} else if (t === COMPLETE || t === ERROR) {
|
|
3923
|
-
stageInput.down([msg]);
|
|
3924
|
-
}
|
|
3925
|
-
}
|
|
3926
|
-
});
|
|
3927
|
-
prevOutputPath = `${stage.name}::output`;
|
|
3928
|
-
}
|
|
3929
|
-
return g;
|
|
3930
|
-
}
|
|
3931
|
-
function feedback(graph, condition, reentry, opts) {
|
|
3932
|
-
const maxIter = opts?.maxIterations ?? 10;
|
|
3933
|
-
const counterName = `__feedback_${condition}`;
|
|
3934
|
-
const counter = state(0, {
|
|
3935
|
-
meta: baseMeta2("feedback_counter", { maxIterations: maxIter })
|
|
3936
|
-
});
|
|
3937
|
-
graph.add(counterName, counter);
|
|
3938
|
-
const condNode = graph.resolve(condition);
|
|
3939
|
-
const reentryNode = graph.resolve(reentry);
|
|
3940
|
-
condNode.subscribe((msgs) => {
|
|
3941
|
-
for (const msg of msgs) {
|
|
3942
4732
|
if (msg[0] === DATA) {
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
4733
|
+
try {
|
|
4734
|
+
run(msg[1], actions);
|
|
4735
|
+
actions.down([msg]);
|
|
4736
|
+
} catch (err) {
|
|
4737
|
+
terminated = true;
|
|
4738
|
+
actions.down([[ERROR, err]]);
|
|
4739
|
+
}
|
|
4740
|
+
return true;
|
|
3949
4741
|
}
|
|
4742
|
+
actions.down([msg]);
|
|
4743
|
+
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
4744
|
+
return true;
|
|
3950
4745
|
}
|
|
3951
4746
|
});
|
|
3952
|
-
|
|
4747
|
+
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
4748
|
+
return step;
|
|
3953
4749
|
}
|
|
3954
|
-
function
|
|
3955
|
-
|
|
3956
|
-
const
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
4750
|
+
function join(graph, name, deps, opts) {
|
|
4751
|
+
const resolved = deps.map((dep) => resolveDep(graph, dep));
|
|
4752
|
+
const step = node(
|
|
4753
|
+
resolved.map((d) => d.node),
|
|
4754
|
+
(values) => values,
|
|
4755
|
+
{
|
|
4756
|
+
...opts,
|
|
4757
|
+
name,
|
|
4758
|
+
describeKind: "derived",
|
|
4759
|
+
meta: baseMeta2("join", opts?.meta)
|
|
4760
|
+
}
|
|
4761
|
+
);
|
|
4762
|
+
registerStep(
|
|
4763
|
+
graph,
|
|
4764
|
+
name,
|
|
4765
|
+
step,
|
|
4766
|
+
resolved.flatMap((d) => d.path ? [d.path] : [])
|
|
4767
|
+
);
|
|
4768
|
+
return step;
|
|
4769
|
+
}
|
|
4770
|
+
function loop(graph, name, source, iterate, opts) {
|
|
4771
|
+
const src = resolveDep(graph, source);
|
|
4772
|
+
const iterRef = opts?.iterations;
|
|
4773
|
+
const iterDep = typeof iterRef === "number" || iterRef === void 0 ? void 0 : resolveDep(graph, iterRef);
|
|
4774
|
+
const staticIterations = typeof iterRef === "number" ? iterRef : void 0;
|
|
4775
|
+
const step = node(
|
|
4776
|
+
iterDep ? [src.node, iterDep.node] : [src.node],
|
|
4777
|
+
(_deps, actions) => {
|
|
4778
|
+
let current = src.node.get();
|
|
4779
|
+
const rawCount = staticIterations ?? iterDep?.node.get() ?? 1;
|
|
4780
|
+
const count = coerceLoopIterations(rawCount);
|
|
4781
|
+
for (let i = 0; i < count; i += 1) {
|
|
4782
|
+
current = iterate(current, i, actions);
|
|
4783
|
+
}
|
|
4784
|
+
return current;
|
|
4785
|
+
},
|
|
4786
|
+
{
|
|
4787
|
+
...opts,
|
|
4788
|
+
name,
|
|
4789
|
+
describeKind: "derived",
|
|
4790
|
+
meta: baseMeta2("loop", opts?.meta)
|
|
3968
4791
|
}
|
|
4792
|
+
);
|
|
4793
|
+
registerStep(
|
|
4794
|
+
graph,
|
|
4795
|
+
name,
|
|
4796
|
+
step,
|
|
4797
|
+
[src.path, iterDep?.path].filter((v) => typeof v === "string")
|
|
4798
|
+
);
|
|
4799
|
+
return step;
|
|
4800
|
+
}
|
|
4801
|
+
function subPipeline(graph, name, childOrBuild, opts) {
|
|
4802
|
+
const child = childOrBuild instanceof Graph ? childOrBuild : pipeline(name, opts);
|
|
4803
|
+
if (typeof childOrBuild === "function") {
|
|
4804
|
+
childOrBuild(child);
|
|
3969
4805
|
}
|
|
3970
|
-
|
|
4806
|
+
graph.mount(name, child);
|
|
4807
|
+
return child;
|
|
4808
|
+
}
|
|
4809
|
+
function sensor(graph, name, initial, opts) {
|
|
4810
|
+
const source = node([], () => void 0, {
|
|
3971
4811
|
...opts,
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
4812
|
+
name,
|
|
4813
|
+
initial,
|
|
4814
|
+
describeKind: "producer",
|
|
4815
|
+
meta: baseMeta2("sensor", opts?.meta)
|
|
4816
|
+
});
|
|
4817
|
+
registerStep(graph, name, source, []);
|
|
4818
|
+
return {
|
|
4819
|
+
node: source,
|
|
4820
|
+
push(value) {
|
|
4821
|
+
source.down([[DATA, value]]);
|
|
4822
|
+
},
|
|
4823
|
+
error(err) {
|
|
4824
|
+
source.down([[ERROR, err]]);
|
|
4825
|
+
},
|
|
4826
|
+
complete() {
|
|
4827
|
+
source.down([[COMPLETE]]);
|
|
4828
|
+
}
|
|
4829
|
+
};
|
|
4830
|
+
}
|
|
4831
|
+
function wait(graph, name, source, ms, opts) {
|
|
4832
|
+
const src = resolveDep(graph, source);
|
|
4833
|
+
const timers = /* @__PURE__ */ new Set();
|
|
4834
|
+
let terminated = false;
|
|
4835
|
+
let completed = false;
|
|
4836
|
+
const step = node(
|
|
4837
|
+
[src.node],
|
|
4838
|
+
() => {
|
|
4839
|
+
for (const id of timers) clearTimeout(id);
|
|
4840
|
+
timers.clear();
|
|
4841
|
+
return () => {
|
|
4842
|
+
for (const id of timers) clearTimeout(id);
|
|
4843
|
+
timers.clear();
|
|
4844
|
+
terminated = true;
|
|
4845
|
+
};
|
|
4846
|
+
},
|
|
4847
|
+
{
|
|
4848
|
+
...opts,
|
|
4849
|
+
name,
|
|
4850
|
+
initial: src.node.get(),
|
|
4851
|
+
describeKind: "operator",
|
|
4852
|
+
completeWhenDepsComplete: false,
|
|
4853
|
+
meta: baseMeta2("wait", opts?.meta),
|
|
4854
|
+
onMessage(msg, depIndex, actions) {
|
|
4855
|
+
if (terminated) return true;
|
|
4856
|
+
if (depIndex !== 0) {
|
|
4857
|
+
actions.down([msg]);
|
|
4858
|
+
if (msg[0] === COMPLETE || msg[0] === ERROR) terminated = true;
|
|
3987
4859
|
return true;
|
|
3988
4860
|
}
|
|
3989
|
-
if (
|
|
3990
|
-
|
|
4861
|
+
if (msg[0] === DATA) {
|
|
4862
|
+
const id = setTimeout(() => {
|
|
4863
|
+
timers.delete(id);
|
|
4864
|
+
actions.down([msg]);
|
|
4865
|
+
if (completed && timers.size === 0) {
|
|
4866
|
+
actions.down([[COMPLETE]]);
|
|
4867
|
+
}
|
|
4868
|
+
}, ms);
|
|
4869
|
+
timers.add(id);
|
|
3991
4870
|
return true;
|
|
3992
4871
|
}
|
|
3993
|
-
if (
|
|
3994
|
-
|
|
3995
|
-
|
|
4872
|
+
if (msg[0] === COMPLETE) {
|
|
4873
|
+
terminated = true;
|
|
4874
|
+
completed = true;
|
|
4875
|
+
if (timers.size === 0) {
|
|
4876
|
+
actions.down([[COMPLETE]]);
|
|
3996
4877
|
}
|
|
3997
4878
|
return true;
|
|
3998
4879
|
}
|
|
3999
|
-
if (
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
buffer2 = [];
|
|
4004
|
-
if (paused) {
|
|
4005
|
-
paused = false;
|
|
4006
|
-
actions.up([[RESUME, lockId]]);
|
|
4007
|
-
}
|
|
4880
|
+
if (msg[0] === ERROR) {
|
|
4881
|
+
terminated = true;
|
|
4882
|
+
for (const id of timers) clearTimeout(id);
|
|
4883
|
+
timers.clear();
|
|
4008
4884
|
actions.down([msg]);
|
|
4009
4885
|
return true;
|
|
4010
4886
|
}
|
|
4011
|
-
return false;
|
|
4012
|
-
}
|
|
4013
|
-
if (t === DATA || t === RESOLVED) {
|
|
4014
|
-
if (checkBudget() && buffer2.length > 0) {
|
|
4015
|
-
flushBuffer(actions);
|
|
4016
|
-
if (buffer2.length === 0 && paused) {
|
|
4017
|
-
paused = false;
|
|
4018
|
-
actions.up([[RESUME, lockId]]);
|
|
4019
|
-
}
|
|
4020
|
-
} else if (!checkBudget() && !paused && buffer2.length > 0) {
|
|
4021
|
-
paused = true;
|
|
4022
|
-
actions.up([[PAUSE, lockId]]);
|
|
4023
|
-
}
|
|
4024
|
-
return true;
|
|
4025
|
-
}
|
|
4026
|
-
if (t === DIRTY) {
|
|
4027
|
-
return true;
|
|
4028
|
-
}
|
|
4029
|
-
if (t === ERROR) {
|
|
4030
4887
|
actions.down([msg]);
|
|
4031
4888
|
return true;
|
|
4032
4889
|
}
|
|
4033
|
-
if (t === COMPLETE) {
|
|
4034
|
-
return true;
|
|
4035
|
-
}
|
|
4036
|
-
return false;
|
|
4037
4890
|
}
|
|
4038
|
-
|
|
4891
|
+
);
|
|
4892
|
+
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
4893
|
+
return step;
|
|
4039
4894
|
}
|
|
4040
|
-
function
|
|
4041
|
-
|
|
4042
|
-
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
const weighted = rawScore * wt;
|
|
4060
|
-
breakdown.push(weighted);
|
|
4061
|
-
totalScore += weighted;
|
|
4895
|
+
function onFailure(graph, name, source, recover, opts) {
|
|
4896
|
+
const src = resolveDep(graph, source);
|
|
4897
|
+
let terminated = false;
|
|
4898
|
+
const step = node([src.node], () => void 0, {
|
|
4899
|
+
...opts,
|
|
4900
|
+
name,
|
|
4901
|
+
describeKind: "operator",
|
|
4902
|
+
completeWhenDepsComplete: false,
|
|
4903
|
+
meta: baseMeta2("onFailure", opts?.meta),
|
|
4904
|
+
onMessage(msg, _depIndex, actions) {
|
|
4905
|
+
if (terminated) return true;
|
|
4906
|
+
if (msg[0] === ERROR) {
|
|
4907
|
+
try {
|
|
4908
|
+
actions.emit(recover(msg[1], actions));
|
|
4909
|
+
} catch (err) {
|
|
4910
|
+
terminated = true;
|
|
4911
|
+
actions.down([[ERROR, err]]);
|
|
4912
|
+
}
|
|
4913
|
+
return true;
|
|
4062
4914
|
}
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
breakdown
|
|
4067
|
-
};
|
|
4068
|
-
},
|
|
4069
|
-
{
|
|
4070
|
-
...opts,
|
|
4071
|
-
describeKind: "derived",
|
|
4072
|
-
meta: baseMeta2("scorer", opts?.meta)
|
|
4915
|
+
actions.down([msg]);
|
|
4916
|
+
if (msg[0] === COMPLETE) terminated = true;
|
|
4917
|
+
return true;
|
|
4073
4918
|
}
|
|
4074
|
-
);
|
|
4919
|
+
});
|
|
4920
|
+
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
4921
|
+
return step;
|
|
4075
4922
|
}
|
|
4076
4923
|
|
|
4077
4924
|
// src/index.ts
|
|
@@ -4192,6 +5039,7 @@ export {
|
|
|
4192
5039
|
gate,
|
|
4193
5040
|
globToRegExp,
|
|
4194
5041
|
graph_exports as graph,
|
|
5042
|
+
graphspec_exports as graphspec,
|
|
4195
5043
|
interval,
|
|
4196
5044
|
isBatching,
|
|
4197
5045
|
isKnownMessageType,
|