@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.
Files changed (69) hide show
  1. package/dist/{chunk-WYI7YW54.js → chunk-2OTXEZQO.js} +3 -3
  2. package/dist/{chunk-TZLX4KIT.js → chunk-2VHNCFGG.js} +3 -3
  3. package/dist/{chunk-XCZPGOVP.js → chunk-3WACHRHV.js} +3 -3
  4. package/dist/{chunk-YWTP2XRJ.js → chunk-5WXTWOD7.js} +2 -2
  5. package/dist/{chunk-E7OH6ZAZ.js → chunk-FMVFRP7L.js} +62 -3
  6. package/dist/chunk-FMVFRP7L.js.map +1 -0
  7. package/dist/{chunk-LR2CLSEF.js → chunk-OHUECHWY.js} +2 -3
  8. package/dist/chunk-OHUECHWY.js.map +1 -0
  9. package/dist/{chunk-UCW3VWMN.js → chunk-PZCDQD2U.js} +4 -4
  10. package/dist/{chunk-A2AJJOSJ.js → chunk-U5HFZGAQ.js} +3 -3
  11. package/dist/{chunk-QTZSBQGJ.js → chunk-UG2QZMRN.js} +18 -1
  12. package/dist/chunk-UG2QZMRN.js.map +1 -0
  13. package/dist/compat/nestjs/index.cjs +10 -1
  14. package/dist/compat/nestjs/index.cjs.map +1 -1
  15. package/dist/compat/nestjs/index.d.cts +4 -4
  16. package/dist/compat/nestjs/index.d.ts +4 -4
  17. package/dist/compat/nestjs/index.js +7 -7
  18. package/dist/core/index.cjs +74 -1
  19. package/dist/core/index.cjs.map +1 -1
  20. package/dist/core/index.d.cts +2 -2
  21. package/dist/core/index.d.ts +2 -2
  22. package/dist/core/index.js +12 -3
  23. package/dist/extra/index.cjs +10 -0
  24. package/dist/extra/index.cjs.map +1 -1
  25. package/dist/extra/index.d.cts +4 -4
  26. package/dist/extra/index.d.ts +4 -4
  27. package/dist/extra/index.js +3 -3
  28. package/dist/graph/index.cjs +10 -1
  29. package/dist/graph/index.cjs.map +1 -1
  30. package/dist/graph/index.d.cts +3 -3
  31. package/dist/graph/index.d.ts +3 -3
  32. package/dist/graph/index.js +4 -4
  33. package/dist/{graph-DqTICAY2.d.cts → graph-BE10ujU9.d.cts} +1 -1
  34. package/dist/{graph-X9uwnD_z.d.ts → graph-DXT95WZ3.d.ts} +1 -1
  35. package/dist/{index-DLO8wnYU.d.ts → index-53cDGX7F.d.ts} +3 -3
  36. package/dist/{index-DMv1Etbi.d.ts → index-B10Q0sQB.d.ts} +2 -2
  37. package/dist/{index-a5gHmH5b.d.ts → index-Bbgvinsi.d.ts} +3 -3
  38. package/dist/{index-BPCeYDS4.d.ts → index-C0_7g9sj.d.ts} +1 -1
  39. package/dist/{index-3U0WxdD-.d.cts → index-CCvzN5GB.d.cts} +2 -2
  40. package/dist/{index-BP1t_38S.d.cts → index-DpZozxaJ.d.cts} +3 -3
  41. package/dist/{index-BVG5pjin.d.ts → index-Dzdm20sx.d.ts} +88 -3
  42. package/dist/{index-BYEgosAX.d.cts → index-QfbXNW1N.d.cts} +88 -3
  43. package/dist/{index-BYa2YMat.d.cts → index-aBZ2RoP0.d.cts} +3 -3
  44. package/dist/{index-DbwgQ4Cw.d.cts → index-nRulwTr-.d.cts} +1 -1
  45. package/dist/index.cjs +2009 -406
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +559 -14
  48. package/dist/index.d.ts +559 -14
  49. package/dist/index.js +1938 -406
  50. package/dist/index.js.map +1 -1
  51. package/dist/{meta-BJEU8fYz.d.cts → meta-BcuDhtwu.d.cts} +33 -1
  52. package/dist/{meta-BJEU8fYz.d.ts → meta-BcuDhtwu.d.ts} +33 -1
  53. package/dist/patterns/reactive-layout/index.cjs +10 -1
  54. package/dist/patterns/reactive-layout/index.cjs.map +1 -1
  55. package/dist/patterns/reactive-layout/index.d.cts +3 -3
  56. package/dist/patterns/reactive-layout/index.d.ts +3 -3
  57. package/dist/patterns/reactive-layout/index.js +4 -4
  58. package/dist/{reactive-log-RhgIog2Z.d.ts → reactive-log-Cu0VdqkT.d.ts} +2 -2
  59. package/dist/{reactive-log-BfX6bOSZ.d.cts → reactive-log-OULQssZg.d.cts} +2 -2
  60. package/package.json +7 -2
  61. package/dist/chunk-E7OH6ZAZ.js.map +0 -1
  62. package/dist/chunk-LR2CLSEF.js.map +0 -1
  63. package/dist/chunk-QTZSBQGJ.js.map +0 -1
  64. /package/dist/{chunk-WYI7YW54.js.map → chunk-2OTXEZQO.js.map} +0 -0
  65. /package/dist/{chunk-TZLX4KIT.js.map → chunk-2VHNCFGG.js.map} +0 -0
  66. /package/dist/{chunk-XCZPGOVP.js.map → chunk-3WACHRHV.js.map} +0 -0
  67. /package/dist/{chunk-YWTP2XRJ.js.map → chunk-5WXTWOD7.js.map} +0 -0
  68. /package/dist/{chunk-UCW3VWMN.js.map → chunk-PZCDQD2U.js.map} +0 -0
  69. /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-TZLX4KIT.js";
140
+ } from "./chunk-2VHNCFGG.js";
141
141
  import {
142
142
  cqrs_exports,
143
143
  nestjs_exports
144
- } from "./chunk-UCW3VWMN.js";
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-A2AJJOSJ.js";
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-YWTP2XRJ.js";
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-XCZPGOVP.js";
198
+ } from "./chunk-3WACHRHV.js";
197
199
  import {
198
200
  GRAPH_META_SEGMENT,
199
201
  Graph,
200
202
  reachable
201
- } from "./chunk-WYI7YW54.js";
203
+ } from "./chunk-2OTXEZQO.js";
202
204
  import {
203
205
  describeNode,
204
206
  metaSnapshot,
205
207
  resolveDescribeFields
206
- } from "./chunk-LR2CLSEF.js";
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-QTZSBQGJ.js";
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 = (node2.meta ?? {}).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/messaging.ts
3028
- var messaging_exports = {};
3029
- __export(messaging_exports, {
3030
- JobFlowGraph: () => JobFlowGraph,
3031
- JobQueueGraph: () => JobQueueGraph,
3032
- SubscriptionGraph: () => SubscriptionGraph,
3033
- TopicBridgeGraph: () => TopicBridgeGraph,
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
- var DEFAULT_MAX_PER_PUMP = 2147483647;
3042
- function requireNonNegativeInt(value, label) {
3043
- if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
3044
- throw new Error(`${label} must be a non-negative integer`);
3045
- }
3046
- return value;
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 messagingMeta(kind, extra) {
3053
- return {
3054
- messaging: true,
3055
- messaging_type: kind,
3056
- ...extra ?? {}
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
- var TopicGraph = class extends Graph {
3060
- _log;
3061
- _keepaliveDisposers = [];
3062
- events;
3063
- latest;
3064
- constructor(name, opts = {}) {
3065
- super(name, opts.graph);
3066
- this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
3067
- this.events = this._log.entries;
3068
- this.add("events", this.events);
3069
- this.latest = derived(
3070
- [this.events],
3071
- ([snapshot]) => {
3072
- const entries = snapshot.value.entries;
3073
- return entries.length === 0 ? void 0 : entries[entries.length - 1];
3074
- },
3075
- {
3076
- name: "latest",
3077
- describeKind: "derived",
3078
- meta: messagingMeta("topic_latest"),
3079
- initial: void 0
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
- retained() {
3095
- const snapshot = this.events.get();
3096
- return snapshot.value.entries;
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
- var SubscriptionGraph = class extends Graph {
3100
- _keepaliveDisposers = [];
3101
- source;
3102
- cursor;
3103
- available;
3104
- constructor(name, topicGraph, opts = {}) {
3105
- super(name, opts.graph);
3106
- const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
3107
- this.mount("topic", topicGraph);
3108
- const topicEvents = topicGraph.events;
3109
- this.source = derived([topicEvents], ([snapshot]) => snapshot, {
3110
- name: "source",
3111
- describeKind: "derived",
3112
- meta: messagingMeta("subscription_source"),
3113
- initial: topicEvents.get()
3114
- });
3115
- this.add("source", this.source);
3116
- this.cursor = state(initialCursor, {
3117
- name: "cursor",
3118
- describeKind: "state",
3119
- meta: messagingMeta("subscription_cursor")
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
- this.add("cursor", this.cursor);
3122
- this.available = derived(
3123
- [this.source, this.cursor],
3124
- ([sourceSnapshot, cursor]) => {
3125
- const entries = sourceSnapshot.value.entries;
3126
- const start = Math.max(0, Math.trunc(cursor ?? 0));
3127
- return entries.slice(start);
3128
- },
3129
- {
3130
- name: "available",
3131
- describeKind: "derived",
3132
- meta: messagingMeta("subscription_available"),
3133
- initial: []
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
- this.add("available", this.available);
3137
- this.connect("topic::events", "source");
3138
- this.connect("source", "available");
3139
- this.connect("cursor", "available");
3140
- this._keepaliveDisposers.push(keepalive2(this.source));
3141
- this._keepaliveDisposers.push(keepalive2(this.available));
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
- destroy() {
3144
- for (const dispose of this._keepaliveDisposers) dispose();
3145
- this._keepaliveDisposers.length = 0;
3146
- super.destroy();
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
- ack(count) {
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(keepalive2(this.depth));
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(keepalive2(this.completedCount));
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(keepalive2(pump));
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(keepalive2(pump));
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 baseMeta(kind, meta) {
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: baseMeta("task", opts?.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: baseMeta("branch", opts?.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: baseMeta("gate", opts?.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: baseMeta("approval", opts?.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: baseMeta("forEach", opts?.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: baseMeta("join", opts?.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: baseMeta("loop", opts?.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: baseMeta("sensor", opts?.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: baseMeta("wait", opts?.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: baseMeta("onFailure", opts?.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,