@graphrefly/graphrefly 0.9.0 → 0.11.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/{chunk-WYI7YW54.js → chunk-2OTXEZQO.js} +3 -3
- package/dist/{chunk-TZLX4KIT.js → chunk-2VHNCFGG.js} +3 -3
- package/dist/{chunk-XCZPGOVP.js → chunk-3WACHRHV.js} +3 -3
- package/dist/{chunk-YWTP2XRJ.js → chunk-5WXTWOD7.js} +2 -2
- package/dist/{chunk-E7OH6ZAZ.js → chunk-FMVFRP7L.js} +62 -3
- package/dist/chunk-FMVFRP7L.js.map +1 -0
- package/dist/{chunk-LR2CLSEF.js → chunk-OHUECHWY.js} +2 -3
- package/dist/chunk-OHUECHWY.js.map +1 -0
- package/dist/{chunk-UCW3VWMN.js → chunk-PZCDQD2U.js} +4 -4
- package/dist/{chunk-A2AJJOSJ.js → chunk-U5HFZGAQ.js} +3 -3
- package/dist/{chunk-QTZSBQGJ.js → chunk-UG2QZMRN.js} +18 -1
- package/dist/chunk-UG2QZMRN.js.map +1 -0
- package/dist/compat/nestjs/index.cjs +10 -1
- package/dist/compat/nestjs/index.cjs.map +1 -1
- package/dist/compat/nestjs/index.d.cts +4 -4
- package/dist/compat/nestjs/index.d.ts +4 -4
- package/dist/compat/nestjs/index.js +7 -7
- package/dist/core/index.cjs +74 -1
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +2 -2
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +12 -3
- package/dist/extra/index.cjs +10 -0
- package/dist/extra/index.cjs.map +1 -1
- package/dist/extra/index.d.cts +4 -4
- package/dist/extra/index.d.ts +4 -4
- package/dist/extra/index.js +3 -3
- package/dist/graph/index.cjs +10 -1
- package/dist/graph/index.cjs.map +1 -1
- package/dist/graph/index.d.cts +3 -3
- package/dist/graph/index.d.ts +3 -3
- package/dist/graph/index.js +4 -4
- package/dist/{graph-DqTICAY2.d.cts → graph-BE10ujU9.d.cts} +1 -1
- package/dist/{graph-X9uwnD_z.d.ts → graph-DXT95WZ3.d.ts} +1 -1
- package/dist/{index-DLO8wnYU.d.ts → index-53cDGX7F.d.ts} +3 -3
- package/dist/{index-DMv1Etbi.d.ts → index-B10Q0sQB.d.ts} +2 -2
- package/dist/{index-a5gHmH5b.d.ts → index-Bbgvinsi.d.ts} +3 -3
- package/dist/{index-BPCeYDS4.d.ts → index-C0_7g9sj.d.ts} +1 -1
- package/dist/{index-3U0WxdD-.d.cts → index-CCvzN5GB.d.cts} +2 -2
- package/dist/{index-BP1t_38S.d.cts → index-DpZozxaJ.d.cts} +3 -3
- package/dist/{index-BVG5pjin.d.ts → index-Dzdm20sx.d.ts} +88 -3
- package/dist/{index-BYEgosAX.d.cts → index-QfbXNW1N.d.cts} +88 -3
- package/dist/{index-BYa2YMat.d.cts → index-aBZ2RoP0.d.cts} +3 -3
- package/dist/{index-DbwgQ4Cw.d.cts → index-nRulwTr-.d.cts} +1 -1
- package/dist/index.cjs +2009 -406
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +559 -14
- package/dist/index.d.ts +559 -14
- package/dist/index.js +1938 -406
- package/dist/index.js.map +1 -1
- package/dist/{meta-BJEU8fYz.d.cts → meta-BcuDhtwu.d.cts} +33 -1
- package/dist/{meta-BJEU8fYz.d.ts → meta-BcuDhtwu.d.ts} +33 -1
- package/dist/patterns/reactive-layout/index.cjs +10 -1
- package/dist/patterns/reactive-layout/index.cjs.map +1 -1
- package/dist/patterns/reactive-layout/index.d.cts +3 -3
- package/dist/patterns/reactive-layout/index.d.ts +3 -3
- package/dist/patterns/reactive-layout/index.js +4 -4
- package/dist/{reactive-log-RhgIog2Z.d.ts → reactive-log-Cu0VdqkT.d.ts} +2 -2
- package/dist/{reactive-log-BfX6bOSZ.d.cts → reactive-log-OULQssZg.d.cts} +2 -2
- package/package.json +7 -2
- package/dist/chunk-E7OH6ZAZ.js.map +0 -1
- package/dist/chunk-LR2CLSEF.js.map +0 -1
- package/dist/chunk-QTZSBQGJ.js.map +0 -1
- /package/dist/{chunk-WYI7YW54.js.map → chunk-2OTXEZQO.js.map} +0 -0
- /package/dist/{chunk-TZLX4KIT.js.map → chunk-2VHNCFGG.js.map} +0 -0
- /package/dist/{chunk-XCZPGOVP.js.map → chunk-3WACHRHV.js.map} +0 -0
- /package/dist/{chunk-YWTP2XRJ.js.map → chunk-5WXTWOD7.js.map} +0 -0
- /package/dist/{chunk-UCW3VWMN.js.map → chunk-PZCDQD2U.js.map} +0 -0
- /package/dist/{chunk-A2AJJOSJ.js.map → chunk-U5HFZGAQ.js.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -137,11 +137,16 @@ import {
|
|
|
137
137
|
workerBridge,
|
|
138
138
|
workerSelf,
|
|
139
139
|
zip
|
|
140
|
-
} from "./chunk-
|
|
140
|
+
} from "./chunk-2VHNCFGG.js";
|
|
141
141
|
import {
|
|
142
142
|
cqrs_exports,
|
|
143
143
|
nestjs_exports
|
|
144
|
-
} from "./chunk-
|
|
144
|
+
} from "./chunk-PZCDQD2U.js";
|
|
145
|
+
import {
|
|
146
|
+
DEFAULT_DOWN,
|
|
147
|
+
bridge,
|
|
148
|
+
core_exports
|
|
149
|
+
} from "./chunk-FMVFRP7L.js";
|
|
145
150
|
import {
|
|
146
151
|
JsonCodec,
|
|
147
152
|
createDagCborCodec,
|
|
@@ -149,7 +154,7 @@ import {
|
|
|
149
154
|
graph_exports,
|
|
150
155
|
negotiateCodec,
|
|
151
156
|
replayWAL
|
|
152
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-U5HFZGAQ.js";
|
|
153
158
|
import {
|
|
154
159
|
cached,
|
|
155
160
|
createWatermarkController,
|
|
@@ -182,10 +187,7 @@ import {
|
|
|
182
187
|
toArray,
|
|
183
188
|
toMessages$,
|
|
184
189
|
toObservable
|
|
185
|
-
} from "./chunk-
|
|
186
|
-
import {
|
|
187
|
-
core_exports
|
|
188
|
-
} from "./chunk-E7OH6ZAZ.js";
|
|
190
|
+
} from "./chunk-5WXTWOD7.js";
|
|
189
191
|
import {
|
|
190
192
|
ResettableTimer
|
|
191
193
|
} from "./chunk-WZ2Z2CRV.js";
|
|
@@ -193,18 +195,19 @@ import {
|
|
|
193
195
|
analyzeAndMeasure,
|
|
194
196
|
computeLineBreaks,
|
|
195
197
|
reactive_layout_exports
|
|
196
|
-
} from "./chunk-
|
|
198
|
+
} from "./chunk-3WACHRHV.js";
|
|
197
199
|
import {
|
|
198
200
|
GRAPH_META_SEGMENT,
|
|
199
201
|
Graph,
|
|
200
202
|
reachable
|
|
201
|
-
} from "./chunk-
|
|
203
|
+
} from "./chunk-2OTXEZQO.js";
|
|
202
204
|
import {
|
|
203
205
|
describeNode,
|
|
204
206
|
metaSnapshot,
|
|
205
207
|
resolveDescribeFields
|
|
206
|
-
} from "./chunk-
|
|
208
|
+
} from "./chunk-OHUECHWY.js";
|
|
207
209
|
import {
|
|
210
|
+
CLEANUP_RESULT,
|
|
208
211
|
COMPLETE,
|
|
209
212
|
DATA,
|
|
210
213
|
DEFAULT_ACTOR,
|
|
@@ -221,6 +224,7 @@ import {
|
|
|
221
224
|
accessHintForGuard,
|
|
222
225
|
advanceVersion,
|
|
223
226
|
batch,
|
|
227
|
+
cleanupResult,
|
|
224
228
|
createVersioning,
|
|
225
229
|
defaultHash,
|
|
226
230
|
derived,
|
|
@@ -245,7 +249,7 @@ import {
|
|
|
245
249
|
propagatesToMeta,
|
|
246
250
|
state,
|
|
247
251
|
wallClockNs
|
|
248
|
-
} from "./chunk-
|
|
252
|
+
} from "./chunk-UG2QZMRN.js";
|
|
249
253
|
|
|
250
254
|
// src/compat/index.ts
|
|
251
255
|
var compat_exports = {};
|
|
@@ -1045,6 +1049,8 @@ __export(patterns_exports, {
|
|
|
1045
1049
|
ai: () => ai_exports,
|
|
1046
1050
|
cqrs: () => cqrs_exports,
|
|
1047
1051
|
demoShell: () => demo_shell_exports,
|
|
1052
|
+
domainTemplates: () => domain_templates_exports,
|
|
1053
|
+
graphspec: () => graphspec_exports,
|
|
1048
1054
|
layout: () => reactive_layout_exports,
|
|
1049
1055
|
memory: () => memory_exports,
|
|
1050
1056
|
messaging: () => messaging_exports,
|
|
@@ -2457,7 +2463,7 @@ function gaugesAsContext(graph, actor, options) {
|
|
|
2457
2463
|
const ungrouped = [];
|
|
2458
2464
|
for (const entry of entries) {
|
|
2459
2465
|
const node2 = described.nodes[entry.path];
|
|
2460
|
-
const tags =
|
|
2466
|
+
const tags = node2.meta?.tags;
|
|
2461
2467
|
if (tags && tags.length > 0) {
|
|
2462
2468
|
const tag = tags[0];
|
|
2463
2469
|
let group = tagGroups.get(tag);
|
|
@@ -3024,128 +3030,1919 @@ function demoShell(opts) {
|
|
|
3024
3030
|
};
|
|
3025
3031
|
}
|
|
3026
3032
|
|
|
3027
|
-
// src/patterns/
|
|
3028
|
-
var
|
|
3029
|
-
__export(
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
TopicGraph: () => TopicGraph,
|
|
3035
|
-
jobFlow: () => jobFlow,
|
|
3036
|
-
jobQueue: () => jobQueue,
|
|
3037
|
-
subscription: () => subscription,
|
|
3038
|
-
topic: () => topic,
|
|
3039
|
-
topicBridge: () => topicBridge
|
|
3033
|
+
// src/patterns/domain-templates.ts
|
|
3034
|
+
var domain_templates_exports = {};
|
|
3035
|
+
__export(domain_templates_exports, {
|
|
3036
|
+
contentModerationGraph: () => contentModerationGraph,
|
|
3037
|
+
dataQualityGraph: () => dataQualityGraph,
|
|
3038
|
+
issueTrackerGraph: () => issueTrackerGraph,
|
|
3039
|
+
observabilityGraph: () => observabilityGraph
|
|
3040
3040
|
});
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3041
|
+
|
|
3042
|
+
// src/patterns/reduction.ts
|
|
3043
|
+
var reduction_exports = {};
|
|
3044
|
+
__export(reduction_exports, {
|
|
3045
|
+
budgetGate: () => budgetGate,
|
|
3046
|
+
feedback: () => feedback,
|
|
3047
|
+
funnel: () => funnel,
|
|
3048
|
+
scorer: () => scorer,
|
|
3049
|
+
stratify: () => stratify
|
|
3050
|
+
});
|
|
3051
|
+
function baseMeta(kind, meta) {
|
|
3052
|
+
return {
|
|
3053
|
+
reduction: true,
|
|
3054
|
+
reduction_type: kind,
|
|
3055
|
+
...meta ?? {}
|
|
3056
|
+
};
|
|
3047
3057
|
}
|
|
3048
3058
|
function keepalive2(n) {
|
|
3049
3059
|
return n.subscribe(() => {
|
|
3050
3060
|
});
|
|
3051
3061
|
}
|
|
3052
|
-
function
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
};
|
|
3062
|
+
function stratify(name, source, rules, opts) {
|
|
3063
|
+
const g = new Graph(name, opts);
|
|
3064
|
+
g.add("source", source);
|
|
3065
|
+
const rulesNode = state(rules, {
|
|
3066
|
+
meta: baseMeta("stratify_rules")
|
|
3067
|
+
});
|
|
3068
|
+
g.add("rules", rulesNode);
|
|
3069
|
+
for (const rule of rules) {
|
|
3070
|
+
_addBranch(g, source, rulesNode, rule);
|
|
3071
|
+
}
|
|
3072
|
+
return g;
|
|
3058
3073
|
}
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3074
|
+
function _addBranch(graph, source, rulesNode, rule) {
|
|
3075
|
+
const branchName = `branch/${rule.name}`;
|
|
3076
|
+
const _noValue = /* @__PURE__ */ Symbol("noValue");
|
|
3077
|
+
let sourceDirty = false;
|
|
3078
|
+
let rulesDirty = false;
|
|
3079
|
+
let sourcePhase2 = false;
|
|
3080
|
+
let sourceValue = _noValue;
|
|
3081
|
+
let pendingDirty = false;
|
|
3082
|
+
function resolve(actions) {
|
|
3083
|
+
if (sourcePhase2) {
|
|
3084
|
+
sourcePhase2 = false;
|
|
3085
|
+
const value = sourceValue;
|
|
3086
|
+
sourceValue = _noValue;
|
|
3087
|
+
if (value !== _noValue) {
|
|
3088
|
+
const currentRules = rulesNode.get();
|
|
3089
|
+
const currentRule = currentRules.find((r) => r.name === rule.name);
|
|
3090
|
+
let matches = false;
|
|
3091
|
+
try {
|
|
3092
|
+
matches = currentRule?.classify(value) ?? false;
|
|
3093
|
+
} catch {
|
|
3094
|
+
matches = false;
|
|
3095
|
+
}
|
|
3096
|
+
if (matches) {
|
|
3097
|
+
pendingDirty = false;
|
|
3098
|
+
actions.emit(value);
|
|
3099
|
+
} else {
|
|
3100
|
+
if (pendingDirty) {
|
|
3101
|
+
pendingDirty = false;
|
|
3102
|
+
actions.down([[DIRTY], [RESOLVED]]);
|
|
3103
|
+
}
|
|
3104
|
+
}
|
|
3105
|
+
} else {
|
|
3106
|
+
if (pendingDirty) {
|
|
3107
|
+
pendingDirty = false;
|
|
3108
|
+
actions.down([[DIRTY], [RESOLVED]]);
|
|
3109
|
+
} else {
|
|
3110
|
+
actions.down([[RESOLVED]]);
|
|
3111
|
+
}
|
|
3080
3112
|
}
|
|
3081
|
-
|
|
3082
|
-
this.add("latest", this.latest);
|
|
3083
|
-
this.connect("events", "latest");
|
|
3084
|
-
this._keepaliveDisposers.push(keepalive2(this.latest));
|
|
3085
|
-
}
|
|
3086
|
-
destroy() {
|
|
3087
|
-
for (const dispose of this._keepaliveDisposers) dispose();
|
|
3088
|
-
this._keepaliveDisposers.length = 0;
|
|
3089
|
-
super.destroy();
|
|
3090
|
-
}
|
|
3091
|
-
publish(value) {
|
|
3092
|
-
this._log.append(value);
|
|
3113
|
+
}
|
|
3093
3114
|
}
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3115
|
+
const filterNode = node([source, rulesNode], () => void 0, {
|
|
3116
|
+
describeKind: "operator",
|
|
3117
|
+
meta: baseMeta("stratify_branch", { branch: rule.name }),
|
|
3118
|
+
onMessage(msg, depIndex, actions) {
|
|
3119
|
+
const t = msg[0];
|
|
3120
|
+
if (t === DIRTY) {
|
|
3121
|
+
if (depIndex === 0) {
|
|
3122
|
+
sourceDirty = true;
|
|
3123
|
+
pendingDirty = true;
|
|
3124
|
+
} else {
|
|
3125
|
+
rulesDirty = true;
|
|
3126
|
+
}
|
|
3127
|
+
return true;
|
|
3128
|
+
}
|
|
3129
|
+
if (t === DATA || t === RESOLVED) {
|
|
3130
|
+
if (depIndex === 0) {
|
|
3131
|
+
sourceDirty = false;
|
|
3132
|
+
sourcePhase2 = true;
|
|
3133
|
+
sourceValue = t === DATA ? msg[1] : _noValue;
|
|
3134
|
+
} else {
|
|
3135
|
+
rulesDirty = false;
|
|
3136
|
+
}
|
|
3137
|
+
if (sourceDirty || rulesDirty) return true;
|
|
3138
|
+
resolve(actions);
|
|
3139
|
+
return true;
|
|
3140
|
+
}
|
|
3141
|
+
if (t === COMPLETE || t === ERROR || t === TEARDOWN) {
|
|
3142
|
+
sourceDirty = false;
|
|
3143
|
+
rulesDirty = false;
|
|
3144
|
+
sourcePhase2 = false;
|
|
3145
|
+
sourceValue = _noValue;
|
|
3146
|
+
pendingDirty = false;
|
|
3147
|
+
if (depIndex === 0) {
|
|
3148
|
+
actions.down([msg]);
|
|
3149
|
+
}
|
|
3150
|
+
return true;
|
|
3151
|
+
}
|
|
3152
|
+
if (depIndex === 1) return true;
|
|
3153
|
+
return false;
|
|
3154
|
+
},
|
|
3155
|
+
completeWhenDepsComplete: false
|
|
3156
|
+
});
|
|
3157
|
+
graph.add(branchName, filterNode);
|
|
3158
|
+
graph.connect("source", branchName);
|
|
3159
|
+
if (rule.ops) {
|
|
3160
|
+
const transformed = rule.ops(filterNode);
|
|
3161
|
+
const transformedName = `branch/${rule.name}/out`;
|
|
3162
|
+
graph.add(transformedName, transformed);
|
|
3163
|
+
graph.connect(branchName, transformedName);
|
|
3097
3164
|
}
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
const
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
}
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3165
|
+
}
|
|
3166
|
+
function funnel(name, sources, stages, opts) {
|
|
3167
|
+
if (sources.length === 0) throw new RangeError("funnel requires at least one source");
|
|
3168
|
+
if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
|
|
3169
|
+
const g = new Graph(name, opts);
|
|
3170
|
+
const merged = sources.length === 1 ? sources[0] : merge(...sources);
|
|
3171
|
+
g.add("merged", merged);
|
|
3172
|
+
let prevOutputPath = "merged";
|
|
3173
|
+
for (let i = 0; i < stages.length; i++) {
|
|
3174
|
+
const stage = stages[i];
|
|
3175
|
+
const sub = new Graph(stage.name);
|
|
3176
|
+
stage.build(sub);
|
|
3177
|
+
try {
|
|
3178
|
+
sub.resolve("input");
|
|
3179
|
+
} catch {
|
|
3180
|
+
throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
|
|
3181
|
+
}
|
|
3182
|
+
try {
|
|
3183
|
+
sub.resolve("output");
|
|
3184
|
+
} catch {
|
|
3185
|
+
throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
|
|
3186
|
+
}
|
|
3187
|
+
g.mount(stage.name, sub);
|
|
3188
|
+
const prevNode = g.resolve(prevOutputPath);
|
|
3189
|
+
const stageInputPath = `${stage.name}::input`;
|
|
3190
|
+
const stageInput = g.resolve(stageInputPath);
|
|
3191
|
+
const bridgeName = `__bridge_${prevOutputPath}\u2192${stage.name}_input`;
|
|
3192
|
+
const br = bridge(prevNode, stageInput, {
|
|
3193
|
+
name: bridgeName,
|
|
3194
|
+
down: DEFAULT_DOWN.filter((t) => t !== TEARDOWN)
|
|
3120
3195
|
});
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3196
|
+
g.add(bridgeName, br);
|
|
3197
|
+
g.connect(prevOutputPath, bridgeName);
|
|
3198
|
+
keepalive2(br);
|
|
3199
|
+
prevOutputPath = `${stage.name}::output`;
|
|
3200
|
+
}
|
|
3201
|
+
return g;
|
|
3202
|
+
}
|
|
3203
|
+
function feedback(graph, condition, reentry, opts) {
|
|
3204
|
+
const maxIter = opts?.maxIterations ?? 10;
|
|
3205
|
+
const counterName = `__feedback_${condition}`;
|
|
3206
|
+
const counter = state(0, {
|
|
3207
|
+
meta: baseMeta("feedback_counter", {
|
|
3208
|
+
maxIterations: maxIter,
|
|
3209
|
+
feedbackFrom: condition,
|
|
3210
|
+
feedbackTo: reentry
|
|
3211
|
+
})
|
|
3212
|
+
});
|
|
3213
|
+
graph.add(counterName, counter);
|
|
3214
|
+
const condNode = graph.resolve(condition);
|
|
3215
|
+
const reentryNode = graph.resolve(reentry);
|
|
3216
|
+
const feedbackEffectName = `__feedback_effect_${condition}`;
|
|
3217
|
+
const feedbackEffect = node([condNode], void 0, {
|
|
3218
|
+
name: feedbackEffectName,
|
|
3219
|
+
describeKind: "effect",
|
|
3220
|
+
meta: {
|
|
3221
|
+
...baseMeta("feedback_effect", {
|
|
3222
|
+
feedbackFrom: condition,
|
|
3223
|
+
feedbackTo: reentry
|
|
3224
|
+
}),
|
|
3225
|
+
_internal: true
|
|
3226
|
+
},
|
|
3227
|
+
onMessage(msg, _depIndex, _actions) {
|
|
3228
|
+
const t = msg[0];
|
|
3229
|
+
if (t === DATA) {
|
|
3230
|
+
const currentCount = counter.get();
|
|
3231
|
+
if (currentCount >= maxIter) return true;
|
|
3232
|
+
const condValue = msg[1];
|
|
3233
|
+
if (condValue == null) return true;
|
|
3234
|
+
batch(() => {
|
|
3235
|
+
counter.down([[DATA, currentCount + 1]]);
|
|
3236
|
+
reentryNode.down([[DATA, condValue]]);
|
|
3237
|
+
});
|
|
3238
|
+
return true;
|
|
3134
3239
|
}
|
|
3135
|
-
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3240
|
+
if (t === COMPLETE || t === ERROR) {
|
|
3241
|
+
const terminal = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];
|
|
3242
|
+
counter.down([terminal]);
|
|
3243
|
+
return true;
|
|
3244
|
+
}
|
|
3245
|
+
return false;
|
|
3246
|
+
}
|
|
3247
|
+
});
|
|
3248
|
+
graph.add(feedbackEffectName, feedbackEffect);
|
|
3249
|
+
graph.connect(condition, feedbackEffectName);
|
|
3250
|
+
keepalive2(feedbackEffect);
|
|
3251
|
+
return graph;
|
|
3252
|
+
}
|
|
3253
|
+
function budgetGate(source, constraints, opts) {
|
|
3254
|
+
if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
|
|
3255
|
+
const constraintNodes = constraints.map((c) => c.node);
|
|
3256
|
+
const allDeps = [source, ...constraintNodes];
|
|
3257
|
+
let buffer2 = [];
|
|
3258
|
+
let paused = false;
|
|
3259
|
+
let pendingResolved = false;
|
|
3260
|
+
const lockId = /* @__PURE__ */ Symbol("budget-gate");
|
|
3261
|
+
function checkBudget() {
|
|
3262
|
+
return constraints.every((c) => c.check(c.node.get()));
|
|
3142
3263
|
}
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3264
|
+
function flushBuffer(actions) {
|
|
3265
|
+
while (buffer2.length > 0 && checkBudget()) {
|
|
3266
|
+
const item = buffer2[0];
|
|
3267
|
+
buffer2 = buffer2.slice(1);
|
|
3268
|
+
actions.emit(item);
|
|
3269
|
+
}
|
|
3270
|
+
if (buffer2.length === 0 && pendingResolved) {
|
|
3271
|
+
pendingResolved = false;
|
|
3272
|
+
actions.down([[RESOLVED]]);
|
|
3273
|
+
}
|
|
3147
3274
|
}
|
|
3148
|
-
|
|
3275
|
+
return node(allDeps, () => void 0, {
|
|
3276
|
+
...opts,
|
|
3277
|
+
describeKind: "operator",
|
|
3278
|
+
meta: baseMeta("budget_gate", opts?.meta),
|
|
3279
|
+
onMessage(msg, depIndex, actions) {
|
|
3280
|
+
const t = msg[0];
|
|
3281
|
+
if (depIndex === 0) {
|
|
3282
|
+
if (t === DATA) {
|
|
3283
|
+
if (checkBudget() && buffer2.length === 0) {
|
|
3284
|
+
actions.emit(msg[1]);
|
|
3285
|
+
} else {
|
|
3286
|
+
buffer2.push(msg[1]);
|
|
3287
|
+
if (!paused) {
|
|
3288
|
+
paused = true;
|
|
3289
|
+
actions.up([[PAUSE, lockId]]);
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
return true;
|
|
3293
|
+
}
|
|
3294
|
+
if (t === DIRTY) {
|
|
3295
|
+
actions.down([[DIRTY]]);
|
|
3296
|
+
return true;
|
|
3297
|
+
}
|
|
3298
|
+
if (t === RESOLVED) {
|
|
3299
|
+
if (buffer2.length === 0) {
|
|
3300
|
+
actions.down([[RESOLVED]]);
|
|
3301
|
+
} else {
|
|
3302
|
+
pendingResolved = true;
|
|
3303
|
+
}
|
|
3304
|
+
return true;
|
|
3305
|
+
}
|
|
3306
|
+
if (t === COMPLETE || t === ERROR) {
|
|
3307
|
+
for (const item of buffer2) {
|
|
3308
|
+
actions.emit(item);
|
|
3309
|
+
}
|
|
3310
|
+
buffer2 = [];
|
|
3311
|
+
pendingResolved = false;
|
|
3312
|
+
if (paused) {
|
|
3313
|
+
paused = false;
|
|
3314
|
+
actions.up([[RESUME, lockId]]);
|
|
3315
|
+
}
|
|
3316
|
+
actions.down([msg]);
|
|
3317
|
+
return true;
|
|
3318
|
+
}
|
|
3319
|
+
return false;
|
|
3320
|
+
}
|
|
3321
|
+
if (t === DATA || t === RESOLVED) {
|
|
3322
|
+
if (checkBudget() && buffer2.length > 0) {
|
|
3323
|
+
flushBuffer(actions);
|
|
3324
|
+
if (buffer2.length === 0 && paused) {
|
|
3325
|
+
paused = false;
|
|
3326
|
+
actions.up([[RESUME, lockId]]);
|
|
3327
|
+
}
|
|
3328
|
+
} else if (!checkBudget() && !paused && buffer2.length > 0) {
|
|
3329
|
+
paused = true;
|
|
3330
|
+
actions.up([[PAUSE, lockId]]);
|
|
3331
|
+
}
|
|
3332
|
+
return true;
|
|
3333
|
+
}
|
|
3334
|
+
if (t === DIRTY) {
|
|
3335
|
+
return true;
|
|
3336
|
+
}
|
|
3337
|
+
if (t === ERROR) {
|
|
3338
|
+
actions.down([msg]);
|
|
3339
|
+
return true;
|
|
3340
|
+
}
|
|
3341
|
+
if (t === COMPLETE) {
|
|
3342
|
+
return true;
|
|
3343
|
+
}
|
|
3344
|
+
return false;
|
|
3345
|
+
}
|
|
3346
|
+
});
|
|
3347
|
+
}
|
|
3348
|
+
function scorer(sources, weights, opts) {
|
|
3349
|
+
if (sources.length === 0) throw new RangeError("scorer requires at least one source");
|
|
3350
|
+
if (sources.length !== weights.length) {
|
|
3351
|
+
throw new RangeError("scorer requires the same number of sources and weights");
|
|
3352
|
+
}
|
|
3353
|
+
const allDeps = [...sources, ...weights];
|
|
3354
|
+
const n = sources.length;
|
|
3355
|
+
const scoreFns = opts?.scoreFns;
|
|
3356
|
+
return derived(
|
|
3357
|
+
allDeps,
|
|
3358
|
+
(vals) => {
|
|
3359
|
+
const signals = vals.slice(0, n);
|
|
3360
|
+
const weightValues = vals.slice(n);
|
|
3361
|
+
const breakdown = [];
|
|
3362
|
+
let totalScore = 0;
|
|
3363
|
+
for (let i = 0; i < n; i++) {
|
|
3364
|
+
const sig = signals[i] ?? 0;
|
|
3365
|
+
const wt = weightValues[i] ?? 0;
|
|
3366
|
+
const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
|
|
3367
|
+
const weighted = rawScore * wt;
|
|
3368
|
+
breakdown.push(weighted);
|
|
3369
|
+
totalScore += weighted;
|
|
3370
|
+
}
|
|
3371
|
+
return {
|
|
3372
|
+
value: signals,
|
|
3373
|
+
score: totalScore,
|
|
3374
|
+
breakdown
|
|
3375
|
+
};
|
|
3376
|
+
},
|
|
3377
|
+
{
|
|
3378
|
+
...opts,
|
|
3379
|
+
describeKind: "derived",
|
|
3380
|
+
meta: baseMeta("scorer", opts?.meta)
|
|
3381
|
+
}
|
|
3382
|
+
);
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
// src/patterns/domain-templates.ts
|
|
3386
|
+
function keepalive3(n) {
|
|
3387
|
+
return n.subscribe(() => {
|
|
3388
|
+
});
|
|
3389
|
+
}
|
|
3390
|
+
function baseMeta2(kind, extra) {
|
|
3391
|
+
return { domain_template: true, template_type: kind, ...extra ?? {} };
|
|
3392
|
+
}
|
|
3393
|
+
function observabilityGraph(name, opts) {
|
|
3394
|
+
const g = new Graph(name, opts);
|
|
3395
|
+
g.add("source", opts.source);
|
|
3396
|
+
const defaultBranches = [
|
|
3397
|
+
{ name: "errors", classify: (v) => isTagged(v, "error") },
|
|
3398
|
+
{ name: "traces", classify: (v) => isTagged(v, "trace") },
|
|
3399
|
+
{ name: "metrics", classify: (v) => isTagged(v, "metric") }
|
|
3400
|
+
];
|
|
3401
|
+
const branches = opts.branches ?? defaultBranches;
|
|
3402
|
+
const rules = branches.map((b) => ({
|
|
3403
|
+
name: b.name,
|
|
3404
|
+
classify: b.classify
|
|
3405
|
+
}));
|
|
3406
|
+
const strat = stratify("stratify", opts.source, rules);
|
|
3407
|
+
g.mount("stratify", strat);
|
|
3408
|
+
const branchNodes = branches.map((b) => {
|
|
3409
|
+
try {
|
|
3410
|
+
return g.resolve(`stratify::branch/${b.name}`);
|
|
3411
|
+
} catch {
|
|
3412
|
+
return state(null);
|
|
3413
|
+
}
|
|
3414
|
+
});
|
|
3415
|
+
const correlateFn = opts.correlate ?? ((vals) => vals);
|
|
3416
|
+
const correlateNode = derived(
|
|
3417
|
+
branchNodes,
|
|
3418
|
+
(vals) => correlateFn(vals),
|
|
3419
|
+
{
|
|
3420
|
+
meta: baseMeta2("observability", { stage: "correlate" })
|
|
3421
|
+
}
|
|
3422
|
+
);
|
|
3423
|
+
g.add("correlate", correlateNode);
|
|
3424
|
+
for (const b of branches) {
|
|
3425
|
+
try {
|
|
3426
|
+
g.connect(`stratify::branch/${b.name}`, "correlate");
|
|
3427
|
+
} catch {
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
const sloCheckFn = opts.sloCheck ?? (() => ({ pass: true }));
|
|
3431
|
+
const sloValue = derived([correlateNode], (vals) => vals[0], {
|
|
3432
|
+
meta: baseMeta2("observability", { stage: "slo_value" })
|
|
3433
|
+
});
|
|
3434
|
+
const sloVerified = derived([sloValue], (vals) => sloCheckFn(vals[0]), {
|
|
3435
|
+
meta: baseMeta2("observability", { stage: "slo_verified" })
|
|
3436
|
+
});
|
|
3437
|
+
g.add("slo_value", sloValue);
|
|
3438
|
+
g.add("slo_verified", sloVerified);
|
|
3439
|
+
g.connect("correlate", "slo_value");
|
|
3440
|
+
g.connect("slo_value", "slo_verified");
|
|
3441
|
+
const weightValues = opts.weights ?? branches.map(() => 1);
|
|
3442
|
+
const signalNodes = branchNodes.map(
|
|
3443
|
+
(bn) => derived([bn], (vals) => vals[0] != null ? 1 : 0)
|
|
3444
|
+
);
|
|
3445
|
+
const weightNodes = weightValues.map((w) => state(w));
|
|
3446
|
+
for (let i = 0; i < signalNodes.length; i++) {
|
|
3447
|
+
g.add(`__signal_${i}`, signalNodes[i]);
|
|
3448
|
+
g.add(`__weight_${i}`, weightNodes[i]);
|
|
3449
|
+
}
|
|
3450
|
+
const alerts = scorer(
|
|
3451
|
+
signalNodes,
|
|
3452
|
+
weightNodes
|
|
3453
|
+
);
|
|
3454
|
+
g.add("alerts", alerts);
|
|
3455
|
+
const output = derived(
|
|
3456
|
+
[alerts, sloVerified],
|
|
3457
|
+
(vals) => ({
|
|
3458
|
+
scored: vals[0],
|
|
3459
|
+
slo: vals[1]
|
|
3460
|
+
}),
|
|
3461
|
+
{
|
|
3462
|
+
meta: baseMeta2("observability", { stage: "output" })
|
|
3463
|
+
}
|
|
3464
|
+
);
|
|
3465
|
+
g.add("output", output);
|
|
3466
|
+
g.connect("alerts", "output");
|
|
3467
|
+
g.connect("slo_verified", "output");
|
|
3468
|
+
const fbReentry = state(null, {
|
|
3469
|
+
meta: baseMeta2("observability", { stage: "feedback_reentry" })
|
|
3470
|
+
});
|
|
3471
|
+
g.add("feedback_reentry", fbReentry);
|
|
3472
|
+
const fbCondition = derived(
|
|
3473
|
+
[sloVerified],
|
|
3474
|
+
(vals) => {
|
|
3475
|
+
const result = vals[0];
|
|
3476
|
+
if (result && result.pass === false) return result;
|
|
3477
|
+
return null;
|
|
3478
|
+
},
|
|
3479
|
+
{
|
|
3480
|
+
meta: baseMeta2("observability", { stage: "feedback_condition" })
|
|
3481
|
+
}
|
|
3482
|
+
);
|
|
3483
|
+
g.add("feedback_condition", fbCondition);
|
|
3484
|
+
g.connect("slo_verified", "feedback_condition");
|
|
3485
|
+
feedback(g, "feedback_condition", "feedback_reentry", {
|
|
3486
|
+
maxIterations: opts.maxFeedbackIterations ?? 5
|
|
3487
|
+
});
|
|
3488
|
+
return g;
|
|
3489
|
+
}
|
|
3490
|
+
function issueTrackerGraph(name, opts) {
|
|
3491
|
+
const g = new Graph(name, opts);
|
|
3492
|
+
g.add("source", opts.source);
|
|
3493
|
+
let _issueCounter = 0;
|
|
3494
|
+
const defaultExtract = (raw) => ({
|
|
3495
|
+
id: `issue-${++_issueCounter}`,
|
|
3496
|
+
title: String(raw),
|
|
3497
|
+
severity: 1,
|
|
3498
|
+
source: "unknown",
|
|
3499
|
+
raw
|
|
3500
|
+
});
|
|
3501
|
+
const extractFn = opts.extract ?? defaultExtract;
|
|
3502
|
+
const extractNode = derived([opts.source], (vals) => extractFn(vals[0]), {
|
|
3503
|
+
meta: baseMeta2("issue_tracker", { stage: "extract" })
|
|
3504
|
+
});
|
|
3505
|
+
g.add("extract", extractNode);
|
|
3506
|
+
g.connect("source", "extract");
|
|
3507
|
+
const verifyFn = opts.verify ?? (() => ({ valid: true }));
|
|
3508
|
+
const verifyNode = derived(
|
|
3509
|
+
[extractNode],
|
|
3510
|
+
(vals) => {
|
|
3511
|
+
const issue = vals[0];
|
|
3512
|
+
return { issue, verification: verifyFn(issue) };
|
|
3513
|
+
},
|
|
3514
|
+
{
|
|
3515
|
+
meta: baseMeta2("issue_tracker", { stage: "verify" })
|
|
3516
|
+
}
|
|
3517
|
+
);
|
|
3518
|
+
g.add("verify", verifyNode);
|
|
3519
|
+
g.connect("extract", "verify");
|
|
3520
|
+
const knownPatterns = state([], {
|
|
3521
|
+
meta: baseMeta2("issue_tracker", { stage: "known_patterns" })
|
|
3522
|
+
});
|
|
3523
|
+
g.add("known_patterns", knownPatterns);
|
|
3524
|
+
const detectFn = opts.detectRegression ?? (() => ({ regression: false }));
|
|
3525
|
+
const regressionNode = derived(
|
|
3526
|
+
[extractNode, knownPatterns],
|
|
3527
|
+
(vals) => {
|
|
3528
|
+
const issue = vals[0];
|
|
3529
|
+
const known = vals[1];
|
|
3530
|
+
return { issue, regression: detectFn(issue, known) };
|
|
3531
|
+
},
|
|
3532
|
+
{ meta: baseMeta2("issue_tracker", { stage: "regression" }) }
|
|
3533
|
+
);
|
|
3534
|
+
g.add("regression", regressionNode);
|
|
3535
|
+
g.connect("extract", "regression");
|
|
3536
|
+
g.connect("known_patterns", "regression");
|
|
3537
|
+
const severitySignal = derived([extractNode], (vals) => {
|
|
3538
|
+
const issue = vals[0];
|
|
3539
|
+
return issue?.severity ?? 0;
|
|
3540
|
+
});
|
|
3541
|
+
const regressionSignal = derived([regressionNode], (vals) => {
|
|
3542
|
+
const r = vals[0];
|
|
3543
|
+
return r?.regression ? 2 : 0;
|
|
3544
|
+
});
|
|
3545
|
+
g.add("__severity_signal", severitySignal);
|
|
3546
|
+
g.add("__regression_signal", regressionSignal);
|
|
3547
|
+
const severityWeight = state(1);
|
|
3548
|
+
const regressionWeight = state(1.5);
|
|
3549
|
+
g.add("__severity_weight", severityWeight);
|
|
3550
|
+
g.add("__regression_weight", regressionWeight);
|
|
3551
|
+
const priority = scorer([severitySignal, regressionSignal], [severityWeight, regressionWeight]);
|
|
3552
|
+
g.add("priority", priority);
|
|
3553
|
+
const output = derived(
|
|
3554
|
+
[verifyNode, regressionNode, priority],
|
|
3555
|
+
(vals) => ({
|
|
3556
|
+
verified: vals[0],
|
|
3557
|
+
regression: vals[1],
|
|
3558
|
+
priority: vals[2]
|
|
3559
|
+
}),
|
|
3560
|
+
{ meta: baseMeta2("issue_tracker", { stage: "output" }) }
|
|
3561
|
+
);
|
|
3562
|
+
g.add("output", output);
|
|
3563
|
+
g.connect("verify", "output");
|
|
3564
|
+
g.connect("regression", "output");
|
|
3565
|
+
g.connect("priority", "output");
|
|
3566
|
+
const fbReentry = state(null, {
|
|
3567
|
+
meta: baseMeta2("issue_tracker", { stage: "feedback_reentry" })
|
|
3568
|
+
});
|
|
3569
|
+
g.add("feedback_reentry", fbReentry);
|
|
3570
|
+
const fbCondition = derived(
|
|
3571
|
+
[verifyNode],
|
|
3572
|
+
(vals) => {
|
|
3573
|
+
const result = vals[0];
|
|
3574
|
+
if (result) {
|
|
3575
|
+
const v = result.verification;
|
|
3576
|
+
if (v && v.valid === false) return result;
|
|
3577
|
+
}
|
|
3578
|
+
return null;
|
|
3579
|
+
},
|
|
3580
|
+
{
|
|
3581
|
+
meta: baseMeta2("issue_tracker", { stage: "feedback_condition" })
|
|
3582
|
+
}
|
|
3583
|
+
);
|
|
3584
|
+
g.add("feedback_condition", fbCondition);
|
|
3585
|
+
g.connect("verify", "feedback_condition");
|
|
3586
|
+
feedback(g, "feedback_condition", "feedback_reentry", {
|
|
3587
|
+
maxIterations: opts.maxFeedbackIterations ?? 3
|
|
3588
|
+
});
|
|
3589
|
+
return g;
|
|
3590
|
+
}
|
|
3591
|
+
function contentModerationGraph(name, opts) {
|
|
3592
|
+
const g = new Graph(name, opts);
|
|
3593
|
+
g.add("source", opts.source);
|
|
3594
|
+
const defaultClassify = (content) => ({
|
|
3595
|
+
label: "review",
|
|
3596
|
+
confidence: 0.5,
|
|
3597
|
+
original: content
|
|
3598
|
+
});
|
|
3599
|
+
const classifyFn = opts.classify ?? defaultClassify;
|
|
3600
|
+
const classifyNode = derived([opts.source], (vals) => classifyFn(vals[0]), {
|
|
3601
|
+
meta: baseMeta2("content_moderation", { stage: "classify" })
|
|
3602
|
+
});
|
|
3603
|
+
g.add("classify", classifyNode);
|
|
3604
|
+
g.connect("source", "classify");
|
|
3605
|
+
const strat = stratify("stratify", classifyNode, [
|
|
3606
|
+
{ name: "safe", classify: (v) => v.label === "safe" },
|
|
3607
|
+
{ name: "review", classify: (v) => v.label === "review" },
|
|
3608
|
+
{ name: "block", classify: (v) => v.label === "block" }
|
|
3609
|
+
]);
|
|
3610
|
+
g.mount("stratify", strat);
|
|
3611
|
+
const reviewLog = reactiveLog([], {
|
|
3612
|
+
name: "review_queue",
|
|
3613
|
+
maxSize: opts.maxQueueSize
|
|
3614
|
+
});
|
|
3615
|
+
g.add("review_queue", reviewLog.entries);
|
|
3616
|
+
let reviewBranch;
|
|
3617
|
+
try {
|
|
3618
|
+
reviewBranch = g.resolve("stratify::branch/review");
|
|
3619
|
+
} catch {
|
|
3620
|
+
reviewBranch = state(null);
|
|
3621
|
+
g.add("__review_fallback", reviewBranch);
|
|
3622
|
+
}
|
|
3623
|
+
const reviewAccumulator = effect([reviewBranch], (vals) => {
|
|
3624
|
+
const item = vals[0];
|
|
3625
|
+
if (item) {
|
|
3626
|
+
reviewLog.append(item);
|
|
3627
|
+
}
|
|
3628
|
+
});
|
|
3629
|
+
g.add("__review_accumulator", reviewAccumulator);
|
|
3630
|
+
keepalive3(reviewAccumulator);
|
|
3631
|
+
try {
|
|
3632
|
+
g.connect("stratify::branch/review", "__review_accumulator");
|
|
3633
|
+
} catch {
|
|
3634
|
+
}
|
|
3635
|
+
const policy2 = state(
|
|
3636
|
+
{},
|
|
3637
|
+
{
|
|
3638
|
+
meta: baseMeta2("content_moderation", {
|
|
3639
|
+
stage: "policy",
|
|
3640
|
+
access: "both",
|
|
3641
|
+
description: "Moderation policy rules \u2014 updated via feedback"
|
|
3642
|
+
})
|
|
3643
|
+
}
|
|
3644
|
+
);
|
|
3645
|
+
g.add("policy", policy2);
|
|
3646
|
+
const weights = opts.weights ?? [0.1, 1, 2];
|
|
3647
|
+
const confidenceSignal = derived([classifyNode], (vals) => {
|
|
3648
|
+
const r = vals[0];
|
|
3649
|
+
return r?.confidence ?? 0;
|
|
3650
|
+
});
|
|
3651
|
+
const severitySignal = derived([classifyNode], (vals) => {
|
|
3652
|
+
const r = vals[0];
|
|
3653
|
+
if (!r) return 0;
|
|
3654
|
+
return r.label === "block" ? weights[2] : r.label === "review" ? weights[1] : weights[0];
|
|
3655
|
+
});
|
|
3656
|
+
g.add("__confidence_signal", confidenceSignal);
|
|
3657
|
+
g.add("__severity_signal", severitySignal);
|
|
3658
|
+
const wConfidence = state(1);
|
|
3659
|
+
const wSeverity = state(1);
|
|
3660
|
+
g.add("__w_confidence", wConfidence);
|
|
3661
|
+
g.add("__w_severity", wSeverity);
|
|
3662
|
+
const priority = scorer([confidenceSignal, severitySignal], [wConfidence, wSeverity]);
|
|
3663
|
+
g.add("priority", priority);
|
|
3664
|
+
const output = derived(
|
|
3665
|
+
[classifyNode, priority],
|
|
3666
|
+
(vals) => ({
|
|
3667
|
+
classification: vals[0],
|
|
3668
|
+
priority: vals[1]
|
|
3669
|
+
}),
|
|
3670
|
+
{ meta: baseMeta2("content_moderation", { stage: "output" }) }
|
|
3671
|
+
);
|
|
3672
|
+
g.add("output", output);
|
|
3673
|
+
g.connect("classify", "output");
|
|
3674
|
+
g.connect("priority", "output");
|
|
3675
|
+
const fbCondition = derived(
|
|
3676
|
+
[reviewLog.entries, policy2],
|
|
3677
|
+
(vals) => {
|
|
3678
|
+
const snap = vals[0];
|
|
3679
|
+
const entries = snap?.value?.entries;
|
|
3680
|
+
if (entries && entries.length > 0) {
|
|
3681
|
+
const latest = entries[entries.length - 1];
|
|
3682
|
+
if (latest && latest.falsePositive) {
|
|
3683
|
+
return latest;
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
return null;
|
|
3687
|
+
},
|
|
3688
|
+
{
|
|
3689
|
+
meta: baseMeta2("content_moderation", { stage: "feedback_condition" })
|
|
3690
|
+
}
|
|
3691
|
+
);
|
|
3692
|
+
g.add("feedback_condition", fbCondition);
|
|
3693
|
+
feedback(g, "feedback_condition", "policy", {
|
|
3694
|
+
maxIterations: opts.maxFeedbackIterations ?? 5
|
|
3695
|
+
});
|
|
3696
|
+
return g;
|
|
3697
|
+
}
|
|
3698
|
+
function dataQualityGraph(name, opts) {
|
|
3699
|
+
const g = new Graph(name, opts);
|
|
3700
|
+
g.add("source", opts.source);
|
|
3701
|
+
const validateFn = opts.validate ?? ((record) => ({
|
|
3702
|
+
valid: true,
|
|
3703
|
+
errors: [],
|
|
3704
|
+
record
|
|
3705
|
+
}));
|
|
3706
|
+
const validateNode = derived(
|
|
3707
|
+
[opts.source],
|
|
3708
|
+
(vals) => vals[0] != null ? validateFn(vals[0]) : void 0,
|
|
3709
|
+
{ meta: baseMeta2("data_quality", { stage: "validate" }) }
|
|
3710
|
+
);
|
|
3711
|
+
g.add("validate", validateNode);
|
|
3712
|
+
g.connect("source", "validate");
|
|
3713
|
+
const detectAnomalyFn = opts.detectAnomaly ?? ((record) => ({
|
|
3714
|
+
anomaly: false,
|
|
3715
|
+
score: 0,
|
|
3716
|
+
record
|
|
3717
|
+
}));
|
|
3718
|
+
const anomalyNode = derived(
|
|
3719
|
+
[opts.source],
|
|
3720
|
+
(vals) => vals[0] != null ? detectAnomalyFn(vals[0]) : void 0,
|
|
3721
|
+
{ meta: baseMeta2("data_quality", { stage: "anomaly" }) }
|
|
3722
|
+
);
|
|
3723
|
+
g.add("anomaly", anomalyNode);
|
|
3724
|
+
g.connect("source", "anomaly");
|
|
3725
|
+
const baseline = state(null, {
|
|
3726
|
+
meta: baseMeta2("data_quality", {
|
|
3727
|
+
stage: "baseline",
|
|
3728
|
+
description: "Rolling baseline for drift detection"
|
|
3729
|
+
})
|
|
3730
|
+
});
|
|
3731
|
+
g.add("baseline", baseline);
|
|
3732
|
+
const baselineUpdater = effect([validateNode], (vals) => {
|
|
3733
|
+
const result = vals[0];
|
|
3734
|
+
if (result?.valid) {
|
|
3735
|
+
batch(() => {
|
|
3736
|
+
baseline.down([[DATA, result.record]]);
|
|
3737
|
+
});
|
|
3738
|
+
}
|
|
3739
|
+
});
|
|
3740
|
+
g.add("__baseline_updater", baselineUpdater);
|
|
3741
|
+
g.connect("validate", "__baseline_updater");
|
|
3742
|
+
keepalive3(baselineUpdater);
|
|
3743
|
+
const detectDriftFn = opts.detectDrift ?? (() => ({ drift: false }));
|
|
3744
|
+
const driftNode = derived(
|
|
3745
|
+
[opts.source, baseline],
|
|
3746
|
+
(vals) => detectDriftFn(vals[0], vals[1]),
|
|
3747
|
+
{ meta: baseMeta2("data_quality", { stage: "drift" }) }
|
|
3748
|
+
);
|
|
3749
|
+
g.add("drift", driftNode);
|
|
3750
|
+
g.connect("source", "drift");
|
|
3751
|
+
g.connect("baseline", "drift");
|
|
3752
|
+
const suggestFn = opts.suggest ?? (() => null);
|
|
3753
|
+
const remediateNode = derived(
|
|
3754
|
+
[validateNode, anomalyNode],
|
|
3755
|
+
(vals) => suggestFn({
|
|
3756
|
+
validation: vals[0],
|
|
3757
|
+
anomaly: vals[1]
|
|
3758
|
+
}),
|
|
3759
|
+
{ meta: baseMeta2("data_quality", { stage: "remediate" }) }
|
|
3760
|
+
);
|
|
3761
|
+
g.add("remediate", remediateNode);
|
|
3762
|
+
g.connect("validate", "remediate");
|
|
3763
|
+
g.connect("anomaly", "remediate");
|
|
3764
|
+
const output = derived(
|
|
3765
|
+
[validateNode, anomalyNode, driftNode, remediateNode],
|
|
3766
|
+
(vals) => ({
|
|
3767
|
+
validation: vals[0],
|
|
3768
|
+
anomaly: vals[1],
|
|
3769
|
+
drift: vals[2],
|
|
3770
|
+
remediation: vals[3]
|
|
3771
|
+
}),
|
|
3772
|
+
{ meta: baseMeta2("data_quality", { stage: "output" }) }
|
|
3773
|
+
);
|
|
3774
|
+
g.add("output", output);
|
|
3775
|
+
g.connect("validate", "output");
|
|
3776
|
+
g.connect("anomaly", "output");
|
|
3777
|
+
g.connect("drift", "output");
|
|
3778
|
+
g.connect("remediate", "output");
|
|
3779
|
+
const validationRules = state([], {
|
|
3780
|
+
meta: baseMeta2("data_quality", { stage: "validation_rules" })
|
|
3781
|
+
});
|
|
3782
|
+
g.add("validation_rules", validationRules);
|
|
3783
|
+
const fbCondition = derived(
|
|
3784
|
+
[anomalyNode],
|
|
3785
|
+
(vals) => {
|
|
3786
|
+
const a = vals[0];
|
|
3787
|
+
if (a?.anomaly) return a;
|
|
3788
|
+
return null;
|
|
3789
|
+
},
|
|
3790
|
+
{
|
|
3791
|
+
meta: baseMeta2("data_quality", { stage: "feedback_condition" })
|
|
3792
|
+
}
|
|
3793
|
+
);
|
|
3794
|
+
g.add("feedback_condition", fbCondition);
|
|
3795
|
+
g.connect("anomaly", "feedback_condition");
|
|
3796
|
+
feedback(g, "feedback_condition", "validation_rules", {
|
|
3797
|
+
maxIterations: opts.maxFeedbackIterations ?? 3
|
|
3798
|
+
});
|
|
3799
|
+
return g;
|
|
3800
|
+
}
|
|
3801
|
+
function isTagged(value, tag) {
|
|
3802
|
+
if (value == null || typeof value !== "object") return false;
|
|
3803
|
+
const v = value;
|
|
3804
|
+
return v.type === tag || v.kind === tag;
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3807
|
+
// src/patterns/graphspec.ts
|
|
3808
|
+
var graphspec_exports = {};
|
|
3809
|
+
__export(graphspec_exports, {
|
|
3810
|
+
compileSpec: () => compileSpec,
|
|
3811
|
+
decompileGraph: () => decompileGraph,
|
|
3812
|
+
extractFnFactory: () => extractFnFactory,
|
|
3813
|
+
extractSourceFactory: () => extractSourceFactory,
|
|
3814
|
+
generateCatalogPrompt: () => generateCatalogPrompt,
|
|
3815
|
+
isRichFnEntry: () => isRichFnEntry,
|
|
3816
|
+
isRichSourceEntry: () => isRichSourceEntry,
|
|
3817
|
+
llmCompose: () => llmCompose,
|
|
3818
|
+
llmRefine: () => llmRefine,
|
|
3819
|
+
specDiff: () => specDiff,
|
|
3820
|
+
validateSpec: () => validateSpec,
|
|
3821
|
+
validateSpecAgainstCatalog: () => validateSpecAgainstCatalog
|
|
3822
|
+
});
|
|
3823
|
+
function isRichFnEntry(entry) {
|
|
3824
|
+
return typeof entry === "object" && entry !== null && "factory" in entry;
|
|
3825
|
+
}
|
|
3826
|
+
function isRichSourceEntry(entry) {
|
|
3827
|
+
return typeof entry === "object" && entry !== null && "factory" in entry;
|
|
3828
|
+
}
|
|
3829
|
+
function extractFnFactory(entry) {
|
|
3830
|
+
return isRichFnEntry(entry) ? entry.factory : entry;
|
|
3831
|
+
}
|
|
3832
|
+
function extractSourceFactory(entry) {
|
|
3833
|
+
return isRichSourceEntry(entry) ? entry.factory : entry;
|
|
3834
|
+
}
|
|
3835
|
+
function generateCatalogPrompt(catalog) {
|
|
3836
|
+
const sections = [];
|
|
3837
|
+
if (catalog.fns) {
|
|
3838
|
+
const groups = /* @__PURE__ */ new Map();
|
|
3839
|
+
for (const [name, entry] of Object.entries(catalog.fns)) {
|
|
3840
|
+
const tag = isRichFnEntry(entry) ? entry.tags?.[0] ?? "Other" : "Other";
|
|
3841
|
+
if (!groups.has(tag)) groups.set(tag, []);
|
|
3842
|
+
groups.get(tag).push(formatFnEntry(name, entry));
|
|
3843
|
+
}
|
|
3844
|
+
for (const [tag, lines] of groups) {
|
|
3845
|
+
sections.push(`${tag}:
|
|
3846
|
+
${lines.join("\n")}`);
|
|
3847
|
+
}
|
|
3848
|
+
}
|
|
3849
|
+
if (catalog.sources) {
|
|
3850
|
+
const lines = [];
|
|
3851
|
+
for (const [name, entry] of Object.entries(catalog.sources)) {
|
|
3852
|
+
lines.push(formatSourceEntry(name, entry));
|
|
3853
|
+
}
|
|
3854
|
+
if (lines.length > 0) {
|
|
3855
|
+
sections.push(`Sources:
|
|
3856
|
+
${lines.join("\n")}`);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
return sections.join("\n\n");
|
|
3860
|
+
}
|
|
3861
|
+
function formatFnEntry(name, entry) {
|
|
3862
|
+
if (!isRichFnEntry(entry)) return `- ${name}`;
|
|
3863
|
+
let line = `- ${name}: ${entry.description}`;
|
|
3864
|
+
if (entry.configSchema) {
|
|
3865
|
+
const fields = Object.entries(entry.configSchema).map(([k, v]) => {
|
|
3866
|
+
let desc = `${k}: ${v.type}`;
|
|
3867
|
+
if (v.enum) desc += ` (${v.enum.join("|")})`;
|
|
3868
|
+
if (v.required === false) desc += "?";
|
|
3869
|
+
return desc;
|
|
3870
|
+
});
|
|
3871
|
+
line += `. Config: { ${fields.join(", ")} }`;
|
|
3872
|
+
}
|
|
3873
|
+
return line;
|
|
3874
|
+
}
|
|
3875
|
+
function formatSourceEntry(name, entry) {
|
|
3876
|
+
if (!isRichSourceEntry(entry)) return `- ${name}`;
|
|
3877
|
+
let line = `- ${name}: ${entry.description}`;
|
|
3878
|
+
if (entry.configSchema) {
|
|
3879
|
+
const fields = Object.entries(entry.configSchema).map(([k, v]) => {
|
|
3880
|
+
let desc = `${k}: ${v.type}`;
|
|
3881
|
+
if (v.required === false) desc += "?";
|
|
3882
|
+
return desc;
|
|
3883
|
+
});
|
|
3884
|
+
line += `. Config: { ${fields.join(", ")} }`;
|
|
3885
|
+
}
|
|
3886
|
+
return line;
|
|
3887
|
+
}
|
|
3888
|
+
function validateSpecAgainstCatalog(spec, catalog) {
|
|
3889
|
+
const errors = [];
|
|
3890
|
+
const fnNames = new Set(Object.keys(catalog.fns ?? {}));
|
|
3891
|
+
const sourceNames = new Set(Object.keys(catalog.sources ?? {}));
|
|
3892
|
+
for (const [nodeName, nodeRaw] of Object.entries(spec.nodes)) {
|
|
3893
|
+
if (nodeRaw.type === "template") continue;
|
|
3894
|
+
const node2 = nodeRaw;
|
|
3895
|
+
if (node2.fn && fnNames.size > 0 && !fnNames.has(node2.fn)) {
|
|
3896
|
+
if (sourceNames.has(node2.fn)) {
|
|
3897
|
+
errors.push(
|
|
3898
|
+
`Node "${nodeName}": fn "${node2.fn}" is a source, not a function. Use it as a producer source instead, or use a function from: ${[...fnNames].join(", ")}`
|
|
3899
|
+
);
|
|
3900
|
+
} else {
|
|
3901
|
+
const suggestion = findClosest(node2.fn, fnNames);
|
|
3902
|
+
errors.push(
|
|
3903
|
+
`Node "${nodeName}": fn "${node2.fn}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
}
|
|
3907
|
+
if (node2.source && sourceNames.size > 0 && !sourceNames.has(node2.source)) {
|
|
3908
|
+
if (fnNames.has(node2.source)) {
|
|
3909
|
+
errors.push(
|
|
3910
|
+
`Node "${nodeName}": source "${node2.source}" is a function, not a source. Use it as fn instead, or use a source from: ${[...sourceNames].join(", ")}`
|
|
3911
|
+
);
|
|
3912
|
+
} else {
|
|
3913
|
+
const suggestion = findClosest(node2.source, sourceNames);
|
|
3914
|
+
errors.push(
|
|
3915
|
+
`Node "${nodeName}": source "${node2.source}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
|
|
3916
|
+
);
|
|
3917
|
+
}
|
|
3918
|
+
}
|
|
3919
|
+
if (node2.fn && node2.config && catalog.fns?.[node2.fn]) {
|
|
3920
|
+
const entry = catalog.fns[node2.fn];
|
|
3921
|
+
if (isRichFnEntry(entry) && entry.configSchema) {
|
|
3922
|
+
for (const [field, schema] of Object.entries(entry.configSchema)) {
|
|
3923
|
+
if (schema.required !== false && !(field in node2.config)) {
|
|
3924
|
+
errors.push(`Node "${nodeName}": config missing required field "${field}"`);
|
|
3925
|
+
}
|
|
3926
|
+
if (field in node2.config && schema.enum) {
|
|
3927
|
+
const val = node2.config[field];
|
|
3928
|
+
if (!schema.enum.includes(val)) {
|
|
3929
|
+
errors.push(
|
|
3930
|
+
`Node "${nodeName}": config.${field} = ${JSON.stringify(val)}, expected one of: ${schema.enum.join(", ")}`
|
|
3931
|
+
);
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
if (spec.templates) {
|
|
3939
|
+
for (const [tName, template] of Object.entries(spec.templates)) {
|
|
3940
|
+
for (const [nodeName, node2] of Object.entries(template.nodes)) {
|
|
3941
|
+
if (node2.fn && fnNames.size > 0 && !fnNames.has(node2.fn)) {
|
|
3942
|
+
const suggestion = findClosest(node2.fn, fnNames);
|
|
3943
|
+
errors.push(
|
|
3944
|
+
`Template "${tName}" node "${nodeName}": fn "${node2.fn}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
|
|
3945
|
+
);
|
|
3946
|
+
}
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
}
|
|
3950
|
+
return { valid: errors.length === 0, errors };
|
|
3951
|
+
}
|
|
3952
|
+
function findClosest(input, candidates) {
|
|
3953
|
+
let best = null;
|
|
3954
|
+
let bestDist = Infinity;
|
|
3955
|
+
const lower = input.toLowerCase();
|
|
3956
|
+
for (const c of candidates) {
|
|
3957
|
+
const dist = levenshtein(lower, c.toLowerCase());
|
|
3958
|
+
if (dist < bestDist && dist <= Math.max(3, Math.floor(input.length / 2))) {
|
|
3959
|
+
bestDist = dist;
|
|
3960
|
+
best = c;
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
return best;
|
|
3964
|
+
}
|
|
3965
|
+
function levenshtein(a, b) {
|
|
3966
|
+
const m = a.length;
|
|
3967
|
+
const n = b.length;
|
|
3968
|
+
const dp = Array.from(
|
|
3969
|
+
{ length: m + 1 },
|
|
3970
|
+
(_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
|
|
3971
|
+
);
|
|
3972
|
+
for (let i = 1; i <= m; i++) {
|
|
3973
|
+
for (let j = 1; j <= n; j++) {
|
|
3974
|
+
dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
|
|
3975
|
+
}
|
|
3976
|
+
}
|
|
3977
|
+
return dp[m][n];
|
|
3978
|
+
}
|
|
3979
|
+
var VALID_NODE_TYPES2 = /* @__PURE__ */ new Set([
|
|
3980
|
+
"state",
|
|
3981
|
+
"producer",
|
|
3982
|
+
"derived",
|
|
3983
|
+
"effect",
|
|
3984
|
+
"operator",
|
|
3985
|
+
"template"
|
|
3986
|
+
]);
|
|
3987
|
+
var INNER_NODE_TYPES = /* @__PURE__ */ new Set(["state", "producer", "derived", "effect", "operator"]);
|
|
3988
|
+
function validateSpec(spec) {
|
|
3989
|
+
const errors = [];
|
|
3990
|
+
if (spec == null || typeof spec !== "object") {
|
|
3991
|
+
return { valid: false, errors: ["GraphSpec must be a non-null object"] };
|
|
3992
|
+
}
|
|
3993
|
+
const s = spec;
|
|
3994
|
+
if (typeof s.name !== "string" || s.name.length === 0) {
|
|
3995
|
+
errors.push("Missing or empty 'name' field");
|
|
3996
|
+
}
|
|
3997
|
+
if (s.nodes == null || typeof s.nodes !== "object" || Array.isArray(s.nodes)) {
|
|
3998
|
+
errors.push("Missing or invalid 'nodes' field (must be an object)");
|
|
3999
|
+
return { valid: false, errors };
|
|
4000
|
+
}
|
|
4001
|
+
const nodeNames = new Set(Object.keys(s.nodes));
|
|
4002
|
+
const nodeTypes = /* @__PURE__ */ new Map();
|
|
4003
|
+
const templateDefs = /* @__PURE__ */ new Map();
|
|
4004
|
+
if (s.templates != null && typeof s.templates === "object" && !Array.isArray(s.templates)) {
|
|
4005
|
+
for (const [tName, tRaw] of Object.entries(s.templates)) {
|
|
4006
|
+
if (tRaw != null && typeof tRaw === "object") {
|
|
4007
|
+
const t = tRaw;
|
|
4008
|
+
templateDefs.set(tName, {
|
|
4009
|
+
params: Array.isArray(t.params) ? t.params : []
|
|
4010
|
+
});
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
}
|
|
4014
|
+
if (s.templates != null) {
|
|
4015
|
+
if (typeof s.templates !== "object" || Array.isArray(s.templates)) {
|
|
4016
|
+
errors.push("'templates' must be an object");
|
|
4017
|
+
} else {
|
|
4018
|
+
for (const [tName, tRaw] of Object.entries(s.templates)) {
|
|
4019
|
+
if (tRaw == null || typeof tRaw !== "object") {
|
|
4020
|
+
errors.push(`Template "${tName}": must be an object`);
|
|
4021
|
+
continue;
|
|
4022
|
+
}
|
|
4023
|
+
const t = tRaw;
|
|
4024
|
+
if (!Array.isArray(t.params)) {
|
|
4025
|
+
errors.push(`Template "${tName}": missing 'params' array`);
|
|
4026
|
+
}
|
|
4027
|
+
if (t.nodes == null || typeof t.nodes !== "object" || Array.isArray(t.nodes)) {
|
|
4028
|
+
errors.push(`Template "${tName}": missing or invalid 'nodes' object`);
|
|
4029
|
+
} else {
|
|
4030
|
+
const paramSet = new Set(Array.isArray(t.params) ? t.params : []);
|
|
4031
|
+
const innerNames = new Set(Object.keys(t.nodes));
|
|
4032
|
+
for (const [nName, nRaw] of Object.entries(t.nodes)) {
|
|
4033
|
+
if (nRaw == null || typeof nRaw !== "object") {
|
|
4034
|
+
errors.push(`Template "${tName}" node "${nName}": must be an object`);
|
|
4035
|
+
continue;
|
|
4036
|
+
}
|
|
4037
|
+
const n = nRaw;
|
|
4038
|
+
if (typeof n.type !== "string" || !INNER_NODE_TYPES.has(n.type)) {
|
|
4039
|
+
errors.push(`Template "${tName}" node "${nName}": invalid type`);
|
|
4040
|
+
}
|
|
4041
|
+
if (Array.isArray(n.deps)) {
|
|
4042
|
+
for (const dep of n.deps) {
|
|
4043
|
+
if (!innerNames.has(dep) && !paramSet.has(dep)) {
|
|
4044
|
+
errors.push(
|
|
4045
|
+
`Template "${tName}" node "${nName}": dep "${dep}" is not an inner node or param`
|
|
4046
|
+
);
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
if (typeof t.output !== "string") {
|
|
4052
|
+
errors.push(`Template "${tName}": missing 'output' string`);
|
|
4053
|
+
} else if (!t.nodes[t.output]) {
|
|
4054
|
+
errors.push(`Template "${tName}": output "${t.output}" is not a declared node`);
|
|
4055
|
+
}
|
|
4056
|
+
}
|
|
4057
|
+
}
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
for (const [name, raw] of Object.entries(s.nodes)) {
|
|
4061
|
+
if (raw == null || typeof raw !== "object") {
|
|
4062
|
+
errors.push(`Node "${name}": must be an object`);
|
|
4063
|
+
continue;
|
|
4064
|
+
}
|
|
4065
|
+
const n = raw;
|
|
4066
|
+
if (typeof n.type !== "string" || !VALID_NODE_TYPES2.has(n.type)) {
|
|
4067
|
+
errors.push(
|
|
4068
|
+
`Node "${name}": invalid type "${String(n.type)}" (expected: ${[...VALID_NODE_TYPES2].join(", ")})`
|
|
4069
|
+
);
|
|
4070
|
+
continue;
|
|
4071
|
+
}
|
|
4072
|
+
nodeTypes.set(name, n.type);
|
|
4073
|
+
if (n.type === "template") {
|
|
4074
|
+
if (typeof n.template !== "string" || !templateDefs.has(n.template)) {
|
|
4075
|
+
errors.push(`Node "${name}": template "${String(n.template)}" not found in templates`);
|
|
4076
|
+
} else {
|
|
4077
|
+
if (n.bind == null || typeof n.bind !== "object" || Array.isArray(n.bind)) {
|
|
4078
|
+
errors.push(`Node "${name}": template ref requires 'bind' object`);
|
|
4079
|
+
} else {
|
|
4080
|
+
const tmpl = templateDefs.get(n.template);
|
|
4081
|
+
const bind = n.bind;
|
|
4082
|
+
for (const param of tmpl.params) {
|
|
4083
|
+
if (!(param in bind)) {
|
|
4084
|
+
errors.push(
|
|
4085
|
+
`Node "${name}": template param "${param}" is not bound (template "${n.template}")`
|
|
4086
|
+
);
|
|
4087
|
+
}
|
|
4088
|
+
}
|
|
4089
|
+
for (const [, target] of Object.entries(bind)) {
|
|
4090
|
+
if (typeof target === "string" && !nodeNames.has(target)) {
|
|
4091
|
+
errors.push(
|
|
4092
|
+
`Node "${name}": bind target "${target}" does not reference an existing node`
|
|
4093
|
+
);
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
} else {
|
|
4099
|
+
if (Array.isArray(n.deps)) {
|
|
4100
|
+
for (const dep of n.deps) {
|
|
4101
|
+
if (dep === name) {
|
|
4102
|
+
errors.push(`Node "${name}": self-referencing dep`);
|
|
4103
|
+
} else if (!nodeNames.has(dep)) {
|
|
4104
|
+
errors.push(`Node "${name}": dep "${dep}" does not reference an existing node`);
|
|
4105
|
+
}
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
if ((n.type === "derived" || n.type === "effect" || n.type === "operator") && !Array.isArray(n.deps)) {
|
|
4109
|
+
errors.push(`Node "${name}": ${n.type} node should have a 'deps' array`);
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
if (s.feedback != null) {
|
|
4114
|
+
if (!Array.isArray(s.feedback)) {
|
|
4115
|
+
errors.push("'feedback' must be an array");
|
|
4116
|
+
} else {
|
|
4117
|
+
for (let i = 0; i < s.feedback.length; i++) {
|
|
4118
|
+
const edge = s.feedback[i];
|
|
4119
|
+
if (edge == null || typeof edge !== "object") {
|
|
4120
|
+
errors.push(`Feedback [${i}]: must be an object`);
|
|
4121
|
+
continue;
|
|
4122
|
+
}
|
|
4123
|
+
const e = edge;
|
|
4124
|
+
if (typeof e.from !== "string" || !nodeNames.has(e.from)) {
|
|
4125
|
+
errors.push(
|
|
4126
|
+
`Feedback [${i}]: 'from' "${String(e.from)}" does not reference an existing node`
|
|
4127
|
+
);
|
|
4128
|
+
}
|
|
4129
|
+
if (typeof e.from === "string" && e.from === e.to) {
|
|
4130
|
+
errors.push(`Feedback [${i}]: 'from' and 'to' must be different nodes`);
|
|
4131
|
+
}
|
|
4132
|
+
if (typeof e.to !== "string" || !nodeNames.has(e.to)) {
|
|
4133
|
+
errors.push(
|
|
4134
|
+
`Feedback [${i}]: 'to' "${String(e.to)}" does not reference an existing node`
|
|
4135
|
+
);
|
|
4136
|
+
} else if (typeof e.to === "string" && nodeTypes.get(e.to) !== "state") {
|
|
4137
|
+
errors.push(
|
|
4138
|
+
`Feedback [${i}]: 'to' node "${e.to}" must be a state node (got "${nodeTypes.get(e.to) ?? "unknown"}")`
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4141
|
+
}
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
return { valid: errors.length === 0, errors };
|
|
4145
|
+
}
|
|
4146
|
+
function compileSpec(spec, opts) {
|
|
4147
|
+
const validation = validateSpec(spec);
|
|
4148
|
+
if (!validation.valid) {
|
|
4149
|
+
throw new Error(`compileSpec: invalid GraphSpec:
|
|
4150
|
+
${validation.errors.join("\n")}`);
|
|
4151
|
+
}
|
|
4152
|
+
const catalog = opts?.catalog ?? {};
|
|
4153
|
+
const g = new Graph(spec.name);
|
|
4154
|
+
const templates = spec.templates ?? {};
|
|
4155
|
+
const catalogValidation = validateSpecAgainstCatalog(spec, catalog);
|
|
4156
|
+
if (!catalogValidation.valid) {
|
|
4157
|
+
throw new Error(
|
|
4158
|
+
`compileSpec: catalog validation errors:
|
|
4159
|
+
${catalogValidation.errors.join("\n")}`
|
|
4160
|
+
);
|
|
4161
|
+
}
|
|
4162
|
+
const resolveFn = (fnName) => {
|
|
4163
|
+
const entry = catalog.fns?.[fnName];
|
|
4164
|
+
return entry ? extractFnFactory(entry) : void 0;
|
|
4165
|
+
};
|
|
4166
|
+
const resolveSource = (sourceName) => {
|
|
4167
|
+
const entry = catalog.sources?.[sourceName];
|
|
4168
|
+
return entry ? extractSourceFactory(entry) : void 0;
|
|
4169
|
+
};
|
|
4170
|
+
const created = /* @__PURE__ */ new Map();
|
|
4171
|
+
const deferred = [];
|
|
4172
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
4173
|
+
if (raw.type === "template") continue;
|
|
4174
|
+
const n = raw;
|
|
4175
|
+
if (n.type === "state") {
|
|
4176
|
+
const nd = state(n.initial, {
|
|
4177
|
+
name,
|
|
4178
|
+
meta: n.meta ? { ...n.meta } : void 0
|
|
4179
|
+
});
|
|
4180
|
+
g.add(name, nd);
|
|
4181
|
+
created.set(name, nd);
|
|
4182
|
+
} else if (n.type === "producer") {
|
|
4183
|
+
const sourceFactory = n.source ? resolveSource(n.source) : void 0;
|
|
4184
|
+
const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
|
|
4185
|
+
if (sourceFactory) {
|
|
4186
|
+
const nd = sourceFactory(n.config ?? {});
|
|
4187
|
+
g.add(name, nd);
|
|
4188
|
+
created.set(name, nd);
|
|
4189
|
+
} else if (fnFactory) {
|
|
4190
|
+
const nd = fnFactory([], n.config ?? {});
|
|
4191
|
+
g.add(name, nd);
|
|
4192
|
+
created.set(name, nd);
|
|
4193
|
+
} else {
|
|
4194
|
+
const nd = producer(() => {
|
|
4195
|
+
}, {
|
|
4196
|
+
name,
|
|
4197
|
+
meta: { ...n.meta, _specFn: n.fn, _specSource: n.source }
|
|
4198
|
+
});
|
|
4199
|
+
g.add(name, nd);
|
|
4200
|
+
created.set(name, nd);
|
|
4201
|
+
}
|
|
4202
|
+
} else {
|
|
4203
|
+
deferred.push([name, n]);
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
let progressed = true;
|
|
4207
|
+
const pending = new Map(deferred);
|
|
4208
|
+
while (pending.size > 0 && progressed) {
|
|
4209
|
+
progressed = false;
|
|
4210
|
+
for (const [name, n] of [...pending.entries()]) {
|
|
4211
|
+
const deps = n.deps ?? [];
|
|
4212
|
+
if (!deps.every((dep) => created.has(dep))) continue;
|
|
4213
|
+
const resolvedDeps = deps.map((dep) => created.get(dep));
|
|
4214
|
+
const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
|
|
4215
|
+
let nd;
|
|
4216
|
+
if (fnFactory) {
|
|
4217
|
+
nd = fnFactory(resolvedDeps, n.config ?? {});
|
|
4218
|
+
} else if (n.type === "effect") {
|
|
4219
|
+
nd = effect(resolvedDeps, () => {
|
|
4220
|
+
});
|
|
4221
|
+
} else {
|
|
4222
|
+
nd = derived(resolvedDeps, (vals) => vals[0]);
|
|
4223
|
+
}
|
|
4224
|
+
g.add(name, nd);
|
|
4225
|
+
created.set(name, nd);
|
|
4226
|
+
pending.delete(name);
|
|
4227
|
+
progressed = true;
|
|
4228
|
+
}
|
|
4229
|
+
}
|
|
4230
|
+
if (pending.size > 0) {
|
|
4231
|
+
const unresolved = [...pending.keys()].sort().join(", ");
|
|
4232
|
+
throw new Error(`compileSpec: unresolvable deps for nodes: ${unresolved}`);
|
|
4233
|
+
}
|
|
4234
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
4235
|
+
if (raw.type !== "template") continue;
|
|
4236
|
+
const ref = raw;
|
|
4237
|
+
const tmpl = templates[ref.template];
|
|
4238
|
+
const sub = new Graph(name);
|
|
4239
|
+
const subCreated = /* @__PURE__ */ new Map();
|
|
4240
|
+
const subDeferred = [];
|
|
4241
|
+
for (const [nName, nSpec] of Object.entries(tmpl.nodes)) {
|
|
4242
|
+
const resolvedDeps = (nSpec.deps ?? []).map((dep) => {
|
|
4243
|
+
if (dep.startsWith("$") && ref.bind[dep]) {
|
|
4244
|
+
return ref.bind[dep];
|
|
4245
|
+
}
|
|
4246
|
+
return dep;
|
|
4247
|
+
});
|
|
4248
|
+
const specWithResolvedDeps = { ...nSpec, deps: resolvedDeps };
|
|
4249
|
+
if (nSpec.type === "state") {
|
|
4250
|
+
const nd = state(nSpec.initial, {
|
|
4251
|
+
name: nName,
|
|
4252
|
+
meta: nSpec.meta ? { ...nSpec.meta } : void 0
|
|
4253
|
+
});
|
|
4254
|
+
sub.add(nName, nd);
|
|
4255
|
+
subCreated.set(nName, nd);
|
|
4256
|
+
} else if (nSpec.type === "producer") {
|
|
4257
|
+
const sourceFactory = nSpec.source ? resolveSource(nSpec.source) : void 0;
|
|
4258
|
+
const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
|
|
4259
|
+
if (sourceFactory) {
|
|
4260
|
+
const nd = sourceFactory(nSpec.config ?? {});
|
|
4261
|
+
sub.add(nName, nd);
|
|
4262
|
+
subCreated.set(nName, nd);
|
|
4263
|
+
} else if (fnFactory) {
|
|
4264
|
+
const nd = fnFactory([], nSpec.config ?? {});
|
|
4265
|
+
sub.add(nName, nd);
|
|
4266
|
+
subCreated.set(nName, nd);
|
|
4267
|
+
} else {
|
|
4268
|
+
const nd = producer(() => {
|
|
4269
|
+
}, {
|
|
4270
|
+
name: nName,
|
|
4271
|
+
meta: { ...nSpec.meta, _specFn: nSpec.fn, _specSource: nSpec.source }
|
|
4272
|
+
});
|
|
4273
|
+
sub.add(nName, nd);
|
|
4274
|
+
subCreated.set(nName, nd);
|
|
4275
|
+
}
|
|
4276
|
+
} else {
|
|
4277
|
+
subDeferred.push([nName, specWithResolvedDeps]);
|
|
4278
|
+
}
|
|
4279
|
+
}
|
|
4280
|
+
let subProgressed = true;
|
|
4281
|
+
const subPending = new Map(subDeferred);
|
|
4282
|
+
while (subPending.size > 0 && subProgressed) {
|
|
4283
|
+
subProgressed = false;
|
|
4284
|
+
for (const [nName, nSpec] of [...subPending.entries()]) {
|
|
4285
|
+
const deps = nSpec.deps ?? [];
|
|
4286
|
+
const allReady = deps.every((dep) => subCreated.has(dep) || created.has(dep));
|
|
4287
|
+
if (!allReady) continue;
|
|
4288
|
+
const resolvedDeps = deps.map((dep) => subCreated.get(dep) ?? created.get(dep));
|
|
4289
|
+
const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
|
|
4290
|
+
let nd;
|
|
4291
|
+
if (fnFactory) {
|
|
4292
|
+
nd = fnFactory(resolvedDeps, nSpec.config ?? {});
|
|
4293
|
+
} else if (nSpec.type === "effect") {
|
|
4294
|
+
nd = effect(resolvedDeps, () => {
|
|
4295
|
+
});
|
|
4296
|
+
} else {
|
|
4297
|
+
nd = derived(resolvedDeps, (vals) => vals[0]);
|
|
4298
|
+
}
|
|
4299
|
+
sub.add(nName, nd);
|
|
4300
|
+
subCreated.set(nName, nd);
|
|
4301
|
+
subPending.delete(nName);
|
|
4302
|
+
subProgressed = true;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
if (subPending.size > 0) {
|
|
4306
|
+
const unresolved = [...subPending.keys()].sort().join(", ");
|
|
4307
|
+
throw new Error(
|
|
4308
|
+
`compileSpec: template "${ref.template}" has unresolvable deps: ${unresolved}`
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4311
|
+
g.mount(name, sub);
|
|
4312
|
+
const outputPath = `${name}::${tmpl.output}`;
|
|
4313
|
+
created.set(name, g.resolve(outputPath));
|
|
4314
|
+
try {
|
|
4315
|
+
const outputNode = g.resolve(outputPath);
|
|
4316
|
+
outputNode.meta._templateName?.down([[DATA, ref.template]]);
|
|
4317
|
+
outputNode.meta._templateBind?.down([[DATA, ref.bind]]);
|
|
4318
|
+
} catch {
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
for (const [name, raw] of Object.entries(spec.nodes)) {
|
|
4322
|
+
if (raw.type === "template") continue;
|
|
4323
|
+
const n = raw;
|
|
4324
|
+
for (const dep of n.deps ?? []) {
|
|
4325
|
+
try {
|
|
4326
|
+
g.connect(dep, name);
|
|
4327
|
+
} catch (err) {
|
|
4328
|
+
const msg = err instanceof Error ? err.message : "";
|
|
4329
|
+
if (!msg.includes("constructor deps") && !msg.includes("already")) {
|
|
4330
|
+
throw err;
|
|
4331
|
+
}
|
|
4332
|
+
}
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
for (const fb of spec.feedback ?? []) {
|
|
4336
|
+
feedback(g, fb.from, fb.to, {
|
|
4337
|
+
maxIterations: fb.maxIterations
|
|
4338
|
+
});
|
|
4339
|
+
}
|
|
4340
|
+
return g;
|
|
4341
|
+
}
|
|
4342
|
+
var INTERNAL_META_KEYS = /* @__PURE__ */ new Set([
|
|
4343
|
+
"reduction",
|
|
4344
|
+
"reduction_type",
|
|
4345
|
+
"_specFn",
|
|
4346
|
+
"_specSource",
|
|
4347
|
+
"_templateName",
|
|
4348
|
+
"_templateBind",
|
|
4349
|
+
"feedbackFrom",
|
|
4350
|
+
"feedbackTo",
|
|
4351
|
+
"_internal"
|
|
4352
|
+
]);
|
|
4353
|
+
function decompileGraph(graph) {
|
|
4354
|
+
const desc = graph.describe({ detail: "standard" });
|
|
4355
|
+
const nodes = {};
|
|
4356
|
+
const feedbackEdges = [];
|
|
4357
|
+
const metaSegment = `::${GRAPH_META_SEGMENT}::`;
|
|
4358
|
+
const feedbackCounterPattern = /^__feedback_(?!effect_)(.+)$/;
|
|
4359
|
+
const feedbackConditions = /* @__PURE__ */ new Set();
|
|
4360
|
+
for (const path of Object.keys(desc.nodes)) {
|
|
4361
|
+
if (path.includes(metaSegment)) continue;
|
|
4362
|
+
const match = feedbackCounterPattern.exec(path);
|
|
4363
|
+
if (match) {
|
|
4364
|
+
feedbackConditions.add(match[1]);
|
|
4365
|
+
const meta = desc.nodes[path]?.meta;
|
|
4366
|
+
if (meta?.feedbackFrom && meta?.feedbackTo) {
|
|
4367
|
+
feedbackEdges.push({
|
|
4368
|
+
from: meta.feedbackFrom,
|
|
4369
|
+
to: meta.feedbackTo,
|
|
4370
|
+
...meta.maxIterations ? { maxIterations: meta.maxIterations } : {}
|
|
4371
|
+
});
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
4376
|
+
if (path.includes(metaSegment)) continue;
|
|
4377
|
+
if (feedbackCounterPattern.test(path)) continue;
|
|
4378
|
+
if (nodeDesc.meta?._internal) continue;
|
|
4379
|
+
if (path.startsWith("__feedback_effect_")) continue;
|
|
4380
|
+
if (path.startsWith("__bridge_")) continue;
|
|
4381
|
+
if (path.includes("::")) continue;
|
|
4382
|
+
const specNode = {
|
|
4383
|
+
type: nodeDesc.type
|
|
4384
|
+
};
|
|
4385
|
+
if (nodeDesc.deps.length > 0) {
|
|
4386
|
+
specNode.deps = nodeDesc.deps.filter((d) => !d.includes("::"));
|
|
4387
|
+
}
|
|
4388
|
+
if (nodeDesc.type === "state" && nodeDesc.value !== void 0) {
|
|
4389
|
+
specNode.initial = nodeDesc.value;
|
|
4390
|
+
}
|
|
4391
|
+
if (nodeDesc.meta && Object.keys(nodeDesc.meta).length > 0) {
|
|
4392
|
+
const meta = {};
|
|
4393
|
+
for (const [k, v] of Object.entries(nodeDesc.meta)) {
|
|
4394
|
+
if (!INTERNAL_META_KEYS.has(k)) meta[k] = v;
|
|
4395
|
+
}
|
|
4396
|
+
if (Object.keys(meta).length > 0) {
|
|
4397
|
+
specNode.meta = meta;
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
nodes[path] = specNode;
|
|
4401
|
+
}
|
|
4402
|
+
const templates = {};
|
|
4403
|
+
const templateRefs = {};
|
|
4404
|
+
const metaDetectedSubgraphs = /* @__PURE__ */ new Set();
|
|
4405
|
+
for (const subName of desc.subgraphs) {
|
|
4406
|
+
const prefix = `${subName}::`;
|
|
4407
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
4408
|
+
if (!path.startsWith(prefix)) continue;
|
|
4409
|
+
if (path.includes(metaSegment)) continue;
|
|
4410
|
+
const meta = nodeDesc.meta;
|
|
4411
|
+
if (meta?._templateName && meta?._templateBind) {
|
|
4412
|
+
const templateName = meta._templateName;
|
|
4413
|
+
const bind = meta._templateBind;
|
|
4414
|
+
if (!templates[templateName]) {
|
|
4415
|
+
const tmplNodes = {};
|
|
4416
|
+
const tmplInnerNames = /* @__PURE__ */ new Set();
|
|
4417
|
+
const tmplPrefix = `${subName}::`;
|
|
4418
|
+
for (const [p, nd] of Object.entries(desc.nodes)) {
|
|
4419
|
+
if (!p.startsWith(tmplPrefix) || p.includes(metaSegment)) continue;
|
|
4420
|
+
const localName = p.slice(tmplPrefix.length);
|
|
4421
|
+
if (localName.includes("::")) continue;
|
|
4422
|
+
tmplInnerNames.add(localName);
|
|
4423
|
+
tmplNodes[localName] = {
|
|
4424
|
+
type: nd.type,
|
|
4425
|
+
...nd.deps.length > 0 ? {
|
|
4426
|
+
deps: nd.deps.map(
|
|
4427
|
+
(d) => d.startsWith(tmplPrefix) ? d.slice(tmplPrefix.length) : d
|
|
4428
|
+
)
|
|
4429
|
+
} : {}
|
|
4430
|
+
};
|
|
4431
|
+
}
|
|
4432
|
+
const tmplParams = [];
|
|
4433
|
+
const tmplParamMap = /* @__PURE__ */ new Map();
|
|
4434
|
+
for (const n of Object.values(tmplNodes)) {
|
|
4435
|
+
for (const dep of n.deps ?? []) {
|
|
4436
|
+
if (!tmplInnerNames.has(dep) && !tmplParamMap.has(dep)) {
|
|
4437
|
+
const param = `$${dep}`;
|
|
4438
|
+
tmplParams.push(param);
|
|
4439
|
+
tmplParamMap.set(dep, param);
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
for (const n of Object.values(tmplNodes)) {
|
|
4444
|
+
if (n.deps) n.deps = n.deps.map((d) => tmplParamMap.get(d) ?? d);
|
|
4445
|
+
}
|
|
4446
|
+
const depended = /* @__PURE__ */ new Set();
|
|
4447
|
+
for (const n of Object.values(tmplNodes)) {
|
|
4448
|
+
for (const dep of n.deps ?? []) {
|
|
4449
|
+
if (tmplInnerNames.has(dep)) depended.add(dep);
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
const outputCandidates = [...tmplInnerNames].filter((n) => !depended.has(n));
|
|
4453
|
+
const tmplOutput = outputCandidates[0] ?? [...tmplInnerNames].pop();
|
|
4454
|
+
templates[templateName] = { params: tmplParams, nodes: tmplNodes, output: tmplOutput };
|
|
4455
|
+
}
|
|
4456
|
+
delete nodes[subName];
|
|
4457
|
+
templateRefs[subName] = { type: "template", template: templateName, bind };
|
|
4458
|
+
metaDetectedSubgraphs.add(subName);
|
|
4459
|
+
break;
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
const structureMap = /* @__PURE__ */ new Map();
|
|
4464
|
+
for (const subName of desc.subgraphs) {
|
|
4465
|
+
if (metaDetectedSubgraphs.has(subName)) continue;
|
|
4466
|
+
const subNodes = {};
|
|
4467
|
+
const prefix = `${subName}::`;
|
|
4468
|
+
for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
|
|
4469
|
+
if (path.includes(metaSegment)) continue;
|
|
4470
|
+
if (!path.startsWith(prefix)) continue;
|
|
4471
|
+
const localName = path.slice(prefix.length);
|
|
4472
|
+
if (localName.includes("::")) continue;
|
|
4473
|
+
subNodes[localName] = {
|
|
4474
|
+
type: nodeDesc.type,
|
|
4475
|
+
...nodeDesc.deps.length > 0 ? {
|
|
4476
|
+
deps: nodeDesc.deps.map((d) => d.startsWith(prefix) ? d.slice(prefix.length) : d)
|
|
4477
|
+
} : {}
|
|
4478
|
+
};
|
|
4479
|
+
}
|
|
4480
|
+
const fingerprint = JSON.stringify(
|
|
4481
|
+
Object.fromEntries(
|
|
4482
|
+
Object.entries(subNodes).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => [k, { type: v.type, deps: v.deps ?? [] }])
|
|
4483
|
+
)
|
|
4484
|
+
);
|
|
4485
|
+
if (!structureMap.has(fingerprint)) {
|
|
4486
|
+
structureMap.set(fingerprint, []);
|
|
4487
|
+
}
|
|
4488
|
+
structureMap.get(fingerprint).push({ name: subName, nodes: subNodes });
|
|
4489
|
+
}
|
|
4490
|
+
for (const [, group] of structureMap) {
|
|
4491
|
+
if (group.length < 2) continue;
|
|
4492
|
+
const templateName = `${group[0].name}_template`;
|
|
4493
|
+
const refNodes = group[0].nodes;
|
|
4494
|
+
const innerNames = new Set(Object.keys(refNodes));
|
|
4495
|
+
const params = [];
|
|
4496
|
+
const baseParamMap = /* @__PURE__ */ new Map();
|
|
4497
|
+
for (const n of Object.values(refNodes)) {
|
|
4498
|
+
for (const dep of n.deps ?? []) {
|
|
4499
|
+
if (!innerNames.has(dep) && !baseParamMap.has(dep)) {
|
|
4500
|
+
const param = `$${dep}`;
|
|
4501
|
+
params.push(param);
|
|
4502
|
+
baseParamMap.set(dep, param);
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
}
|
|
4506
|
+
const depended = /* @__PURE__ */ new Set();
|
|
4507
|
+
for (const n of Object.values(refNodes)) {
|
|
4508
|
+
for (const dep of n.deps ?? []) {
|
|
4509
|
+
if (innerNames.has(dep)) depended.add(dep);
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
const outputCandidates = [...innerNames].filter((n) => !depended.has(n));
|
|
4513
|
+
const output = outputCandidates[0] ?? [...innerNames].pop();
|
|
4514
|
+
const tmplNodes = {};
|
|
4515
|
+
for (const [nName, nSpec] of Object.entries(refNodes)) {
|
|
4516
|
+
tmplNodes[nName] = {
|
|
4517
|
+
...nSpec,
|
|
4518
|
+
deps: nSpec.deps?.map((d) => baseParamMap.get(d) ?? d)
|
|
4519
|
+
};
|
|
4520
|
+
}
|
|
4521
|
+
templates[templateName] = { params, nodes: tmplNodes, output };
|
|
4522
|
+
for (const member of group) {
|
|
4523
|
+
delete nodes[member.name];
|
|
4524
|
+
const memberBind = {};
|
|
4525
|
+
const memberInnerNames = new Set(Object.keys(member.nodes));
|
|
4526
|
+
for (const n of Object.values(member.nodes)) {
|
|
4527
|
+
for (const dep of n.deps ?? []) {
|
|
4528
|
+
if (!memberInnerNames.has(dep)) {
|
|
4529
|
+
const param = baseParamMap.get(dep) ?? `$${dep}`;
|
|
4530
|
+
memberBind[param] = dep;
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
templateRefs[member.name] = {
|
|
4535
|
+
type: "template",
|
|
4536
|
+
template: templateName,
|
|
4537
|
+
bind: memberBind
|
|
4538
|
+
};
|
|
4539
|
+
}
|
|
4540
|
+
}
|
|
4541
|
+
const allNodes = {
|
|
4542
|
+
...nodes,
|
|
4543
|
+
...templateRefs
|
|
4544
|
+
};
|
|
4545
|
+
const result = { name: desc.name, nodes: allNodes };
|
|
4546
|
+
if (Object.keys(templates).length > 0) result.templates = templates;
|
|
4547
|
+
if (feedbackEdges.length > 0) result.feedback = feedbackEdges;
|
|
4548
|
+
return result;
|
|
4549
|
+
}
|
|
4550
|
+
function specDiff(specA, specB) {
|
|
4551
|
+
const entries = [];
|
|
4552
|
+
if (specA.name !== specB.name) {
|
|
4553
|
+
entries.push({
|
|
4554
|
+
type: "changed",
|
|
4555
|
+
path: "name",
|
|
4556
|
+
detail: `"${specA.name}" \u2192 "${specB.name}"`
|
|
4557
|
+
});
|
|
4558
|
+
}
|
|
4559
|
+
const nodesA = new Set(Object.keys(specA.nodes));
|
|
4560
|
+
const nodesB = new Set(Object.keys(specB.nodes));
|
|
4561
|
+
for (const name of nodesB) {
|
|
4562
|
+
if (!nodesA.has(name)) {
|
|
4563
|
+
const n = specB.nodes[name];
|
|
4564
|
+
entries.push({
|
|
4565
|
+
type: "added",
|
|
4566
|
+
path: `nodes.${name}`,
|
|
4567
|
+
detail: `type: ${n.type}`
|
|
4568
|
+
});
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
for (const name of nodesA) {
|
|
4572
|
+
if (!nodesB.has(name)) {
|
|
4573
|
+
entries.push({ type: "removed", path: `nodes.${name}` });
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
for (const name of nodesA) {
|
|
4577
|
+
if (!nodesB.has(name)) continue;
|
|
4578
|
+
const a = specA.nodes[name];
|
|
4579
|
+
const b = specB.nodes[name];
|
|
4580
|
+
if (JSON.stringify(a) !== JSON.stringify(b)) {
|
|
4581
|
+
const details = [];
|
|
4582
|
+
if (a.type !== b.type) details.push(`type: ${a.type} \u2192 ${b.type}`);
|
|
4583
|
+
if (JSON.stringify(a.deps) !== JSON.stringify(b.deps)) {
|
|
4584
|
+
details.push("deps changed");
|
|
4585
|
+
}
|
|
4586
|
+
if (a.fn !== b.fn) {
|
|
4587
|
+
details.push(`fn: ${a.fn} \u2192 ${b.fn}`);
|
|
4588
|
+
}
|
|
4589
|
+
if (JSON.stringify(a.config) !== JSON.stringify(b.config)) {
|
|
4590
|
+
details.push("config changed");
|
|
4591
|
+
}
|
|
4592
|
+
entries.push({
|
|
4593
|
+
type: "changed",
|
|
4594
|
+
path: `nodes.${name}`,
|
|
4595
|
+
detail: details.join("; ") || "modified"
|
|
4596
|
+
});
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
const tmplA = specA.templates ?? {};
|
|
4600
|
+
const tmplB = specB.templates ?? {};
|
|
4601
|
+
const tmplNamesA = new Set(Object.keys(tmplA));
|
|
4602
|
+
const tmplNamesB = new Set(Object.keys(tmplB));
|
|
4603
|
+
for (const name of tmplNamesB) {
|
|
4604
|
+
if (!tmplNamesA.has(name)) {
|
|
4605
|
+
entries.push({ type: "added", path: `templates.${name}` });
|
|
4606
|
+
}
|
|
4607
|
+
}
|
|
4608
|
+
for (const name of tmplNamesA) {
|
|
4609
|
+
if (!tmplNamesB.has(name)) {
|
|
4610
|
+
entries.push({ type: "removed", path: `templates.${name}` });
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
for (const name of tmplNamesA) {
|
|
4614
|
+
if (!tmplNamesB.has(name)) continue;
|
|
4615
|
+
if (JSON.stringify(tmplA[name]) !== JSON.stringify(tmplB[name])) {
|
|
4616
|
+
entries.push({
|
|
4617
|
+
type: "changed",
|
|
4618
|
+
path: `templates.${name}`,
|
|
4619
|
+
detail: "template definition changed"
|
|
4620
|
+
});
|
|
4621
|
+
}
|
|
4622
|
+
}
|
|
4623
|
+
const fbA = specA.feedback ?? [];
|
|
4624
|
+
const fbB = specB.feedback ?? [];
|
|
4625
|
+
const fbKeyA = new Set(fbA.map((e) => `${e.from}->${e.to}`));
|
|
4626
|
+
const fbKeyB = new Set(fbB.map((e) => `${e.from}->${e.to}`));
|
|
4627
|
+
for (const fb of fbB) {
|
|
4628
|
+
const key = `${fb.from}->${fb.to}`;
|
|
4629
|
+
if (!fbKeyA.has(key)) {
|
|
4630
|
+
entries.push({
|
|
4631
|
+
type: "added",
|
|
4632
|
+
path: `feedback.${key}`,
|
|
4633
|
+
detail: `maxIterations: ${fb.maxIterations ?? 10}`
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4637
|
+
for (const fb of fbA) {
|
|
4638
|
+
const key = `${fb.from}->${fb.to}`;
|
|
4639
|
+
if (!fbKeyB.has(key)) {
|
|
4640
|
+
entries.push({ type: "removed", path: `feedback.${key}` });
|
|
4641
|
+
}
|
|
4642
|
+
}
|
|
4643
|
+
for (const fb of fbA) {
|
|
4644
|
+
const key = `${fb.from}->${fb.to}`;
|
|
4645
|
+
const counterpart = fbB.find((b) => b.from === fb.from && b.to === fb.to);
|
|
4646
|
+
if (counterpart && JSON.stringify(fb) !== JSON.stringify(counterpart)) {
|
|
4647
|
+
entries.push({
|
|
4648
|
+
type: "changed",
|
|
4649
|
+
path: `feedback.${key}`,
|
|
4650
|
+
detail: `maxIterations: ${fb.maxIterations ?? 10} \u2192 ${counterpart.maxIterations ?? 10}`
|
|
4651
|
+
});
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
const added = entries.filter((e) => e.type === "added").length;
|
|
4655
|
+
const removed = entries.filter((e) => e.type === "removed").length;
|
|
4656
|
+
const changed = entries.filter((e) => e.type === "changed").length;
|
|
4657
|
+
const parts = [];
|
|
4658
|
+
if (added) parts.push(`${added} added`);
|
|
4659
|
+
if (removed) parts.push(`${removed} removed`);
|
|
4660
|
+
if (changed) parts.push(`${changed} changed`);
|
|
4661
|
+
const summary = parts.length > 0 ? parts.join(", ") : "no changes";
|
|
4662
|
+
return { entries, summary };
|
|
4663
|
+
}
|
|
4664
|
+
var LLM_COMPOSE_SYSTEM_PROMPT = `You are a graph architect for GraphReFly, a reactive graph protocol.
|
|
4665
|
+
|
|
4666
|
+
Given a natural-language description, produce a JSON GraphSpec with this structure:
|
|
4667
|
+
|
|
4668
|
+
{
|
|
4669
|
+
"name": "<graph_name>",
|
|
4670
|
+
"nodes": {
|
|
4671
|
+
"<node_name>": {
|
|
4672
|
+
"type": "state" | "derived" | "producer" | "effect" | "operator",
|
|
4673
|
+
"deps": ["<dep_node_name>", ...],
|
|
4674
|
+
"fn": "<catalog_function_name>",
|
|
4675
|
+
"source": "<catalog_source_name>",
|
|
4676
|
+
"config": { ... },
|
|
4677
|
+
"initial": <value>,
|
|
4678
|
+
"meta": { "description": "<purpose>" }
|
|
4679
|
+
},
|
|
4680
|
+
"<template_instance>": {
|
|
4681
|
+
"type": "template",
|
|
4682
|
+
"template": "<template_name>",
|
|
4683
|
+
"bind": { "$param": "node_name" }
|
|
4684
|
+
}
|
|
4685
|
+
},
|
|
4686
|
+
"templates": {
|
|
4687
|
+
"<template_name>": {
|
|
4688
|
+
"params": ["$param1", "$param2"],
|
|
4689
|
+
"nodes": { ... },
|
|
4690
|
+
"output": "<output_node>"
|
|
4691
|
+
}
|
|
4692
|
+
},
|
|
4693
|
+
"feedback": [
|
|
4694
|
+
{ "from": "<condition_node>", "to": "<state_node>", "maxIterations": 10 }
|
|
4695
|
+
]
|
|
4696
|
+
}
|
|
4697
|
+
|
|
4698
|
+
Rules:
|
|
4699
|
+
- "state" nodes hold user/LLM-writable values (knobs). Use "initial" for default values.
|
|
4700
|
+
- "derived" nodes compute from deps using a named "fn".
|
|
4701
|
+
- "effect" nodes produce side effects from deps.
|
|
4702
|
+
- "producer" nodes generate values from a named "source".
|
|
4703
|
+
- Use "templates" when the same subgraph pattern repeats (e.g., per-source resilience).
|
|
4704
|
+
- Use "feedback" for bounded cycles where a derived value writes back to a state node.
|
|
4705
|
+
- meta.description is required for every node.
|
|
4706
|
+
- Return ONLY valid JSON, no markdown fences or commentary.`;
|
|
4707
|
+
function stripFences2(text) {
|
|
4708
|
+
const match = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```[\s\S]*$/);
|
|
4709
|
+
return match ? match[1] : text;
|
|
4710
|
+
}
|
|
4711
|
+
async function llmCompose(problem, adapter, opts) {
|
|
4712
|
+
let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
|
|
4713
|
+
const catalogPrompt = opts?.catalogDescription ?? (opts?.catalog ? generateCatalogPrompt(opts.catalog) : void 0);
|
|
4714
|
+
if (catalogPrompt) {
|
|
4715
|
+
systemPrompt += `
|
|
4716
|
+
|
|
4717
|
+
Available catalog (use ONLY these names):
|
|
4718
|
+
${catalogPrompt}`;
|
|
4719
|
+
}
|
|
4720
|
+
if (opts?.systemPromptExtra) {
|
|
4721
|
+
systemPrompt += `
|
|
4722
|
+
|
|
4723
|
+
${opts.systemPromptExtra}`;
|
|
4724
|
+
}
|
|
4725
|
+
const messages = [
|
|
4726
|
+
{ role: "system", content: systemPrompt },
|
|
4727
|
+
{ role: "user", content: problem }
|
|
4728
|
+
];
|
|
4729
|
+
const rawResult = adapter.invoke(messages, {
|
|
4730
|
+
model: opts?.model,
|
|
4731
|
+
temperature: opts?.temperature ?? 0,
|
|
4732
|
+
maxTokens: opts?.maxTokens
|
|
4733
|
+
});
|
|
4734
|
+
const response = await rawResult;
|
|
4735
|
+
let content = response.content.trim();
|
|
4736
|
+
if (content.startsWith("```")) {
|
|
4737
|
+
content = stripFences2(content);
|
|
4738
|
+
}
|
|
4739
|
+
let parsed;
|
|
4740
|
+
try {
|
|
4741
|
+
parsed = JSON.parse(content);
|
|
4742
|
+
} catch {
|
|
4743
|
+
throw new Error(`llmCompose: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
4744
|
+
}
|
|
4745
|
+
const validation = validateSpec(parsed);
|
|
4746
|
+
if (!validation.valid) {
|
|
4747
|
+
throw new Error(`llmCompose: invalid GraphSpec:
|
|
4748
|
+
${validation.errors.join("\n")}`);
|
|
4749
|
+
}
|
|
4750
|
+
let spec = parsed;
|
|
4751
|
+
if (opts?.catalog) {
|
|
4752
|
+
const maxRefine = opts.maxAutoRefine ?? 0;
|
|
4753
|
+
for (let attempt = 0; attempt <= maxRefine; attempt++) {
|
|
4754
|
+
const catalogValidation = validateSpecAgainstCatalog(spec, opts.catalog);
|
|
4755
|
+
if (catalogValidation.valid) break;
|
|
4756
|
+
if (attempt === maxRefine) {
|
|
4757
|
+
throw new Error(
|
|
4758
|
+
`llmCompose: catalog validation failed after ${maxRefine} refine attempts:
|
|
4759
|
+
${catalogValidation.errors.join("\n")}`
|
|
4760
|
+
);
|
|
4761
|
+
}
|
|
4762
|
+
spec = await llmRefine(
|
|
4763
|
+
spec,
|
|
4764
|
+
`Fix these catalog errors:
|
|
4765
|
+
${catalogValidation.errors.join("\n")}
|
|
4766
|
+
|
|
4767
|
+
Use ONLY functions and sources from the catalog.`,
|
|
4768
|
+
adapter,
|
|
4769
|
+
{ ...opts, catalogDescription: catalogPrompt }
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4773
|
+
return spec;
|
|
4774
|
+
}
|
|
4775
|
+
async function llmRefine(currentSpec, feedback2, adapter, opts) {
|
|
4776
|
+
let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
|
|
4777
|
+
if (opts?.catalogDescription) {
|
|
4778
|
+
systemPrompt += `
|
|
4779
|
+
|
|
4780
|
+
Available catalog:
|
|
4781
|
+
${opts.catalogDescription}`;
|
|
4782
|
+
}
|
|
4783
|
+
if (opts?.systemPromptExtra) {
|
|
4784
|
+
systemPrompt += `
|
|
4785
|
+
|
|
4786
|
+
${opts.systemPromptExtra}`;
|
|
4787
|
+
}
|
|
4788
|
+
const messages = [
|
|
4789
|
+
{ role: "system", content: systemPrompt },
|
|
4790
|
+
{
|
|
4791
|
+
role: "user",
|
|
4792
|
+
content: `Current GraphSpec:
|
|
4793
|
+
${JSON.stringify(currentSpec, null, 2)}
|
|
4794
|
+
|
|
4795
|
+
Modification request: ${feedback2}
|
|
4796
|
+
|
|
4797
|
+
Return the complete modified GraphSpec as JSON.`
|
|
4798
|
+
}
|
|
4799
|
+
];
|
|
4800
|
+
const rawResult = adapter.invoke(messages, {
|
|
4801
|
+
model: opts?.model,
|
|
4802
|
+
temperature: opts?.temperature ?? 0,
|
|
4803
|
+
maxTokens: opts?.maxTokens
|
|
4804
|
+
});
|
|
4805
|
+
const response = await rawResult;
|
|
4806
|
+
let content = response.content.trim();
|
|
4807
|
+
if (content.startsWith("```")) {
|
|
4808
|
+
content = stripFences2(content);
|
|
4809
|
+
}
|
|
4810
|
+
let parsed;
|
|
4811
|
+
try {
|
|
4812
|
+
parsed = JSON.parse(content);
|
|
4813
|
+
} catch {
|
|
4814
|
+
throw new Error(`llmRefine: LLM response is not valid JSON: ${content.slice(0, 200)}`);
|
|
4815
|
+
}
|
|
4816
|
+
const validation = validateSpec(parsed);
|
|
4817
|
+
if (!validation.valid) {
|
|
4818
|
+
throw new Error(`llmRefine: invalid GraphSpec:
|
|
4819
|
+
${validation.errors.join("\n")}`);
|
|
4820
|
+
}
|
|
4821
|
+
return parsed;
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4824
|
+
// src/patterns/messaging.ts
|
|
4825
|
+
var messaging_exports = {};
|
|
4826
|
+
__export(messaging_exports, {
|
|
4827
|
+
JobFlowGraph: () => JobFlowGraph,
|
|
4828
|
+
JobQueueGraph: () => JobQueueGraph,
|
|
4829
|
+
SubscriptionGraph: () => SubscriptionGraph,
|
|
4830
|
+
TopicBridgeGraph: () => TopicBridgeGraph,
|
|
4831
|
+
TopicGraph: () => TopicGraph,
|
|
4832
|
+
jobFlow: () => jobFlow,
|
|
4833
|
+
jobQueue: () => jobQueue,
|
|
4834
|
+
subscription: () => subscription,
|
|
4835
|
+
topic: () => topic,
|
|
4836
|
+
topicBridge: () => topicBridge
|
|
4837
|
+
});
|
|
4838
|
+
var DEFAULT_MAX_PER_PUMP = 2147483647;
|
|
4839
|
+
function requireNonNegativeInt(value, label) {
|
|
4840
|
+
if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
|
|
4841
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
4842
|
+
}
|
|
4843
|
+
return value;
|
|
4844
|
+
}
|
|
4845
|
+
function keepalive4(n) {
|
|
4846
|
+
return n.subscribe(() => {
|
|
4847
|
+
});
|
|
4848
|
+
}
|
|
4849
|
+
function messagingMeta(kind, extra) {
|
|
4850
|
+
return {
|
|
4851
|
+
messaging: true,
|
|
4852
|
+
messaging_type: kind,
|
|
4853
|
+
...extra ?? {}
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
4856
|
+
var TopicGraph = class extends Graph {
|
|
4857
|
+
_log;
|
|
4858
|
+
_keepaliveDisposers = [];
|
|
4859
|
+
events;
|
|
4860
|
+
latest;
|
|
4861
|
+
constructor(name, opts = {}) {
|
|
4862
|
+
super(name, opts.graph);
|
|
4863
|
+
this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
|
|
4864
|
+
this.events = this._log.entries;
|
|
4865
|
+
this.add("events", this.events);
|
|
4866
|
+
this.latest = derived(
|
|
4867
|
+
[this.events],
|
|
4868
|
+
([snapshot]) => {
|
|
4869
|
+
const entries = snapshot.value.entries;
|
|
4870
|
+
return entries.length === 0 ? void 0 : entries[entries.length - 1];
|
|
4871
|
+
},
|
|
4872
|
+
{
|
|
4873
|
+
name: "latest",
|
|
4874
|
+
describeKind: "derived",
|
|
4875
|
+
meta: messagingMeta("topic_latest"),
|
|
4876
|
+
initial: void 0
|
|
4877
|
+
}
|
|
4878
|
+
);
|
|
4879
|
+
this.add("latest", this.latest);
|
|
4880
|
+
this.connect("events", "latest");
|
|
4881
|
+
this._keepaliveDisposers.push(keepalive4(this.latest));
|
|
4882
|
+
}
|
|
4883
|
+
destroy() {
|
|
4884
|
+
for (const dispose of this._keepaliveDisposers) dispose();
|
|
4885
|
+
this._keepaliveDisposers.length = 0;
|
|
4886
|
+
super.destroy();
|
|
4887
|
+
}
|
|
4888
|
+
publish(value) {
|
|
4889
|
+
this._log.append(value);
|
|
4890
|
+
}
|
|
4891
|
+
retained() {
|
|
4892
|
+
const snapshot = this.events.get();
|
|
4893
|
+
return snapshot.value.entries;
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
var SubscriptionGraph = class extends Graph {
|
|
4897
|
+
_keepaliveDisposers = [];
|
|
4898
|
+
source;
|
|
4899
|
+
cursor;
|
|
4900
|
+
available;
|
|
4901
|
+
constructor(name, topicGraph, opts = {}) {
|
|
4902
|
+
super(name, opts.graph);
|
|
4903
|
+
const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
|
|
4904
|
+
this.mount("topic", topicGraph);
|
|
4905
|
+
const topicEvents = topicGraph.events;
|
|
4906
|
+
this.source = derived([topicEvents], ([snapshot]) => snapshot, {
|
|
4907
|
+
name: "source",
|
|
4908
|
+
describeKind: "derived",
|
|
4909
|
+
meta: messagingMeta("subscription_source"),
|
|
4910
|
+
initial: topicEvents.get()
|
|
4911
|
+
});
|
|
4912
|
+
this.add("source", this.source);
|
|
4913
|
+
this.cursor = state(initialCursor, {
|
|
4914
|
+
name: "cursor",
|
|
4915
|
+
describeKind: "state",
|
|
4916
|
+
meta: messagingMeta("subscription_cursor")
|
|
4917
|
+
});
|
|
4918
|
+
this.add("cursor", this.cursor);
|
|
4919
|
+
this.available = derived(
|
|
4920
|
+
[this.source, this.cursor],
|
|
4921
|
+
([sourceSnapshot, cursor]) => {
|
|
4922
|
+
const entries = sourceSnapshot.value.entries;
|
|
4923
|
+
const start = Math.max(0, Math.trunc(cursor ?? 0));
|
|
4924
|
+
return entries.slice(start);
|
|
4925
|
+
},
|
|
4926
|
+
{
|
|
4927
|
+
name: "available",
|
|
4928
|
+
describeKind: "derived",
|
|
4929
|
+
meta: messagingMeta("subscription_available"),
|
|
4930
|
+
initial: []
|
|
4931
|
+
}
|
|
4932
|
+
);
|
|
4933
|
+
this.add("available", this.available);
|
|
4934
|
+
this.connect("topic::events", "source");
|
|
4935
|
+
this.connect("source", "available");
|
|
4936
|
+
this.connect("cursor", "available");
|
|
4937
|
+
this._keepaliveDisposers.push(keepalive4(this.source));
|
|
4938
|
+
this._keepaliveDisposers.push(keepalive4(this.available));
|
|
4939
|
+
}
|
|
4940
|
+
destroy() {
|
|
4941
|
+
for (const dispose of this._keepaliveDisposers) dispose();
|
|
4942
|
+
this._keepaliveDisposers.length = 0;
|
|
4943
|
+
super.destroy();
|
|
4944
|
+
}
|
|
4945
|
+
ack(count) {
|
|
3149
4946
|
const available = this.available.get();
|
|
3150
4947
|
const requested = count === void 0 ? available.length : requireNonNegativeInt(count, "subscription ack count");
|
|
3151
4948
|
const step = Math.min(requested, available.length);
|
|
@@ -3190,7 +4987,7 @@ var JobQueueGraph = class extends Graph {
|
|
|
3190
4987
|
);
|
|
3191
4988
|
this.add("depth", this.depth);
|
|
3192
4989
|
this.connect("pending", "depth");
|
|
3193
|
-
this._keepaliveDisposers.push(
|
|
4990
|
+
this._keepaliveDisposers.push(keepalive4(this.depth));
|
|
3194
4991
|
}
|
|
3195
4992
|
destroy() {
|
|
3196
4993
|
for (const dispose of this._keepaliveDisposers) dispose();
|
|
@@ -3290,7 +5087,7 @@ var JobFlowGraph = class extends Graph {
|
|
|
3290
5087
|
);
|
|
3291
5088
|
this.add("completedCount", this.completedCount);
|
|
3292
5089
|
this.connect("completed", "completedCount");
|
|
3293
|
-
this._keepaliveDisposers.push(
|
|
5090
|
+
this._keepaliveDisposers.push(keepalive4(this.completedCount));
|
|
3294
5091
|
const maxPerPump = Math.max(
|
|
3295
5092
|
1,
|
|
3296
5093
|
requireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, "job flow maxPerPump")
|
|
@@ -3330,7 +5127,7 @@ var JobFlowGraph = class extends Graph {
|
|
|
3330
5127
|
);
|
|
3331
5128
|
this.add(`pump_${stage}`, pump);
|
|
3332
5129
|
this.connect(`${stage}::pending`, `pump_${stage}`);
|
|
3333
|
-
this._keepaliveDisposers.push(
|
|
5130
|
+
this._keepaliveDisposers.push(keepalive4(pump));
|
|
3334
5131
|
}
|
|
3335
5132
|
}
|
|
3336
5133
|
destroy() {
|
|
@@ -3402,7 +5199,7 @@ var TopicBridgeGraph = class extends Graph {
|
|
|
3402
5199
|
);
|
|
3403
5200
|
this.add("pump", pump);
|
|
3404
5201
|
this.connect("subscription::available", "pump");
|
|
3405
|
-
this._keepaliveDisposers.push(
|
|
5202
|
+
this._keepaliveDisposers.push(keepalive4(pump));
|
|
3406
5203
|
}
|
|
3407
5204
|
destroy() {
|
|
3408
5205
|
for (const dispose of this._keepaliveDisposers) dispose();
|
|
@@ -3472,7 +5269,7 @@ function registerStep(graph, name, step, depPaths) {
|
|
|
3472
5269
|
graph.connect(path, name);
|
|
3473
5270
|
}
|
|
3474
5271
|
}
|
|
3475
|
-
function
|
|
5272
|
+
function baseMeta3(kind, meta) {
|
|
3476
5273
|
return {
|
|
3477
5274
|
orchestration: true,
|
|
3478
5275
|
orchestration_type: kind,
|
|
@@ -3510,7 +5307,7 @@ function task(graph, name, run, opts) {
|
|
|
3510
5307
|
...nodeOpts,
|
|
3511
5308
|
name,
|
|
3512
5309
|
describeKind: "derived",
|
|
3513
|
-
meta:
|
|
5310
|
+
meta: baseMeta3("task", opts?.meta)
|
|
3514
5311
|
}
|
|
3515
5312
|
);
|
|
3516
5313
|
registerStep(
|
|
@@ -3533,7 +5330,7 @@ function branch(graph, name, source, predicate, opts) {
|
|
|
3533
5330
|
...opts,
|
|
3534
5331
|
name,
|
|
3535
5332
|
describeKind: "derived",
|
|
3536
|
-
meta:
|
|
5333
|
+
meta: baseMeta3("branch", opts?.meta)
|
|
3537
5334
|
}
|
|
3538
5335
|
);
|
|
3539
5336
|
registerStep(graph, name, step, src.path ? [src.path] : []);
|
|
@@ -3556,7 +5353,7 @@ function gate2(graph, name, source, control, opts) {
|
|
|
3556
5353
|
...opts,
|
|
3557
5354
|
name,
|
|
3558
5355
|
describeKind: "operator",
|
|
3559
|
-
meta:
|
|
5356
|
+
meta: baseMeta3("gate", opts?.meta)
|
|
3560
5357
|
}
|
|
3561
5358
|
);
|
|
3562
5359
|
registerStep(
|
|
@@ -3584,7 +5381,7 @@ function approval(graph, name, source, approver, opts) {
|
|
|
3584
5381
|
...opts,
|
|
3585
5382
|
name,
|
|
3586
5383
|
describeKind: "operator",
|
|
3587
|
-
meta:
|
|
5384
|
+
meta: baseMeta3("approval", opts?.meta)
|
|
3588
5385
|
}
|
|
3589
5386
|
);
|
|
3590
5387
|
registerStep(
|
|
@@ -3603,7 +5400,7 @@ function forEach2(graph, name, source, run, opts) {
|
|
|
3603
5400
|
name,
|
|
3604
5401
|
describeKind: "effect",
|
|
3605
5402
|
completeWhenDepsComplete: false,
|
|
3606
|
-
meta:
|
|
5403
|
+
meta: baseMeta3("forEach", opts?.meta),
|
|
3607
5404
|
onMessage(msg, depIndex, actions) {
|
|
3608
5405
|
if (terminated) return true;
|
|
3609
5406
|
if (depIndex !== 0) {
|
|
@@ -3638,7 +5435,7 @@ function join(graph, name, deps, opts) {
|
|
|
3638
5435
|
...opts,
|
|
3639
5436
|
name,
|
|
3640
5437
|
describeKind: "derived",
|
|
3641
|
-
meta:
|
|
5438
|
+
meta: baseMeta3("join", opts?.meta)
|
|
3642
5439
|
}
|
|
3643
5440
|
);
|
|
3644
5441
|
registerStep(
|
|
@@ -3669,7 +5466,7 @@ function loop(graph, name, source, iterate, opts) {
|
|
|
3669
5466
|
...opts,
|
|
3670
5467
|
name,
|
|
3671
5468
|
describeKind: "derived",
|
|
3672
|
-
meta:
|
|
5469
|
+
meta: baseMeta3("loop", opts?.meta)
|
|
3673
5470
|
}
|
|
3674
5471
|
);
|
|
3675
5472
|
registerStep(
|
|
@@ -3694,7 +5491,7 @@ function sensor(graph, name, initial, opts) {
|
|
|
3694
5491
|
name,
|
|
3695
5492
|
initial,
|
|
3696
5493
|
describeKind: "producer",
|
|
3697
|
-
meta:
|
|
5494
|
+
meta: baseMeta3("sensor", opts?.meta)
|
|
3698
5495
|
});
|
|
3699
5496
|
registerStep(graph, name, source, []);
|
|
3700
5497
|
return {
|
|
@@ -3732,7 +5529,7 @@ function wait(graph, name, source, ms, opts) {
|
|
|
3732
5529
|
initial: src.node.get(),
|
|
3733
5530
|
describeKind: "operator",
|
|
3734
5531
|
completeWhenDepsComplete: false,
|
|
3735
|
-
meta:
|
|
5532
|
+
meta: baseMeta3("wait", opts?.meta),
|
|
3736
5533
|
onMessage(msg, depIndex, actions) {
|
|
3737
5534
|
if (terminated) return true;
|
|
3738
5535
|
if (depIndex !== 0) {
|
|
@@ -3782,7 +5579,7 @@ function onFailure(graph, name, source, recover, opts) {
|
|
|
3782
5579
|
name,
|
|
3783
5580
|
describeKind: "operator",
|
|
3784
5581
|
completeWhenDepsComplete: false,
|
|
3785
|
-
meta:
|
|
5582
|
+
meta: baseMeta3("onFailure", opts?.meta),
|
|
3786
5583
|
onMessage(msg, _depIndex, actions) {
|
|
3787
5584
|
if (terminated) return true;
|
|
3788
5585
|
if (msg[0] === ERROR) {
|
|
@@ -3803,284 +5600,15 @@ function onFailure(graph, name, source, recover, opts) {
|
|
|
3803
5600
|
return step;
|
|
3804
5601
|
}
|
|
3805
5602
|
|
|
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 }),
|
|
3840
|
-
onMessage(msg, depIndex, actions) {
|
|
3841
|
-
if (depIndex !== 0) return false;
|
|
3842
|
-
const t = msg[0];
|
|
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;
|
|
3873
|
-
actions.down([msg]);
|
|
3874
|
-
return true;
|
|
3875
|
-
}
|
|
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
|
-
if (msg[0] === DATA) {
|
|
3943
|
-
const currentCount = counter.get();
|
|
3944
|
-
if (currentCount >= maxIter) continue;
|
|
3945
|
-
const condValue = msg[1];
|
|
3946
|
-
if (condValue == null) continue;
|
|
3947
|
-
counter.down([[DATA, currentCount + 1]]);
|
|
3948
|
-
reentryNode.down([[DATA, condValue]]);
|
|
3949
|
-
}
|
|
3950
|
-
}
|
|
3951
|
-
});
|
|
3952
|
-
return graph;
|
|
3953
|
-
}
|
|
3954
|
-
function budgetGate(source, constraints, opts) {
|
|
3955
|
-
if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
|
|
3956
|
-
const constraintNodes = constraints.map((c) => c.node);
|
|
3957
|
-
const allDeps = [source, ...constraintNodes];
|
|
3958
|
-
let buffer2 = [];
|
|
3959
|
-
let paused = false;
|
|
3960
|
-
const lockId = /* @__PURE__ */ Symbol("budget-gate");
|
|
3961
|
-
function checkBudget() {
|
|
3962
|
-
return constraints.every((c) => c.check(c.node.get()));
|
|
3963
|
-
}
|
|
3964
|
-
function flushBuffer(actions) {
|
|
3965
|
-
while (buffer2.length > 0 && checkBudget()) {
|
|
3966
|
-
const item = buffer2.shift();
|
|
3967
|
-
actions.emit(item);
|
|
3968
|
-
}
|
|
3969
|
-
}
|
|
3970
|
-
return node(allDeps, () => void 0, {
|
|
3971
|
-
...opts,
|
|
3972
|
-
describeKind: "operator",
|
|
3973
|
-
meta: baseMeta2("budget_gate", opts?.meta),
|
|
3974
|
-
onMessage(msg, depIndex, actions) {
|
|
3975
|
-
const t = msg[0];
|
|
3976
|
-
if (depIndex === 0) {
|
|
3977
|
-
if (t === DATA) {
|
|
3978
|
-
if (checkBudget() && buffer2.length === 0) {
|
|
3979
|
-
actions.emit(msg[1]);
|
|
3980
|
-
} else {
|
|
3981
|
-
buffer2.push(msg[1]);
|
|
3982
|
-
if (!paused) {
|
|
3983
|
-
paused = true;
|
|
3984
|
-
actions.up([[PAUSE, lockId]]);
|
|
3985
|
-
}
|
|
3986
|
-
}
|
|
3987
|
-
return true;
|
|
3988
|
-
}
|
|
3989
|
-
if (t === DIRTY) {
|
|
3990
|
-
actions.down([[DIRTY]]);
|
|
3991
|
-
return true;
|
|
3992
|
-
}
|
|
3993
|
-
if (t === RESOLVED) {
|
|
3994
|
-
if (buffer2.length === 0) {
|
|
3995
|
-
actions.down([[RESOLVED]]);
|
|
3996
|
-
}
|
|
3997
|
-
return true;
|
|
3998
|
-
}
|
|
3999
|
-
if (t === COMPLETE || t === ERROR) {
|
|
4000
|
-
for (const item of buffer2) {
|
|
4001
|
-
actions.emit(item);
|
|
4002
|
-
}
|
|
4003
|
-
buffer2 = [];
|
|
4004
|
-
if (paused) {
|
|
4005
|
-
paused = false;
|
|
4006
|
-
actions.up([[RESUME, lockId]]);
|
|
4007
|
-
}
|
|
4008
|
-
actions.down([msg]);
|
|
4009
|
-
return true;
|
|
4010
|
-
}
|
|
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
|
-
actions.down([msg]);
|
|
4031
|
-
return true;
|
|
4032
|
-
}
|
|
4033
|
-
if (t === COMPLETE) {
|
|
4034
|
-
return true;
|
|
4035
|
-
}
|
|
4036
|
-
return false;
|
|
4037
|
-
}
|
|
4038
|
-
});
|
|
4039
|
-
}
|
|
4040
|
-
function scorer(sources, weights, opts) {
|
|
4041
|
-
if (sources.length === 0) throw new RangeError("scorer requires at least one source");
|
|
4042
|
-
if (sources.length !== weights.length) {
|
|
4043
|
-
throw new RangeError("scorer requires the same number of sources and weights");
|
|
4044
|
-
}
|
|
4045
|
-
const allDeps = [...sources, ...weights];
|
|
4046
|
-
const n = sources.length;
|
|
4047
|
-
const scoreFns = opts?.scoreFns;
|
|
4048
|
-
return derived(
|
|
4049
|
-
allDeps,
|
|
4050
|
-
(vals) => {
|
|
4051
|
-
const signals = vals.slice(0, n);
|
|
4052
|
-
const weightValues = vals.slice(n);
|
|
4053
|
-
const breakdown = [];
|
|
4054
|
-
let totalScore = 0;
|
|
4055
|
-
for (let i = 0; i < n; i++) {
|
|
4056
|
-
const sig = signals[i] ?? 0;
|
|
4057
|
-
const wt = weightValues[i] ?? 0;
|
|
4058
|
-
const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
|
|
4059
|
-
const weighted = rawScore * wt;
|
|
4060
|
-
breakdown.push(weighted);
|
|
4061
|
-
totalScore += weighted;
|
|
4062
|
-
}
|
|
4063
|
-
return {
|
|
4064
|
-
value: signals,
|
|
4065
|
-
score: totalScore,
|
|
4066
|
-
breakdown
|
|
4067
|
-
};
|
|
4068
|
-
},
|
|
4069
|
-
{
|
|
4070
|
-
...opts,
|
|
4071
|
-
describeKind: "derived",
|
|
4072
|
-
meta: baseMeta2("scorer", opts?.meta)
|
|
4073
|
-
}
|
|
4074
|
-
);
|
|
4075
|
-
}
|
|
4076
|
-
|
|
4077
5603
|
// src/index.ts
|
|
4078
5604
|
var version = "0.0.0";
|
|
4079
5605
|
export {
|
|
5606
|
+
CLEANUP_RESULT,
|
|
4080
5607
|
COMPLETE,
|
|
4081
5608
|
CircuitOpenError,
|
|
4082
5609
|
DATA,
|
|
4083
5610
|
DEFAULT_ACTOR,
|
|
5611
|
+
DEFAULT_DOWN,
|
|
4084
5612
|
DIRTY,
|
|
4085
5613
|
DictCheckpointAdapter,
|
|
4086
5614
|
DynamicNodeImpl,
|
|
@@ -4106,6 +5634,7 @@ export {
|
|
|
4106
5634
|
ai_exports as ai,
|
|
4107
5635
|
audit,
|
|
4108
5636
|
batch,
|
|
5637
|
+
bridge,
|
|
4109
5638
|
buffer,
|
|
4110
5639
|
bufferCount,
|
|
4111
5640
|
bufferTime,
|
|
@@ -4117,6 +5646,7 @@ export {
|
|
|
4117
5646
|
checkpointToRedis,
|
|
4118
5647
|
checkpointToS3,
|
|
4119
5648
|
circuitBreaker,
|
|
5649
|
+
cleanupResult,
|
|
4120
5650
|
combine,
|
|
4121
5651
|
combineLatest,
|
|
4122
5652
|
compat_exports as compat,
|
|
@@ -4141,6 +5671,7 @@ export {
|
|
|
4141
5671
|
deserializeError,
|
|
4142
5672
|
distill,
|
|
4143
5673
|
distinctUntilChanged,
|
|
5674
|
+
domain_templates_exports as domainTemplates,
|
|
4144
5675
|
dynamicNode,
|
|
4145
5676
|
effect,
|
|
4146
5677
|
elementAt,
|
|
@@ -4192,6 +5723,7 @@ export {
|
|
|
4192
5723
|
gate,
|
|
4193
5724
|
globToRegExp,
|
|
4194
5725
|
graph_exports as graph,
|
|
5726
|
+
graphspec_exports as graphspec,
|
|
4195
5727
|
interval,
|
|
4196
5728
|
isBatching,
|
|
4197
5729
|
isKnownMessageType,
|