@graphrefly/graphrefly 0.10.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 +855 -101
  46. package/dist/index.cjs.map +1 -1
  47. package/dist/index.d.cts +349 -19
  48. package/dist/index.d.ts +349 -19
  49. package/dist/index.js +800 -116
  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,14 +137,24 @@ 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
145
  import {
146
+ DEFAULT_DOWN,
147
+ bridge,
146
148
  core_exports
147
- } from "./chunk-E7OH6ZAZ.js";
149
+ } from "./chunk-FMVFRP7L.js";
150
+ import {
151
+ JsonCodec,
152
+ createDagCborCodec,
153
+ createDagCborZstdCodec,
154
+ graph_exports,
155
+ negotiateCodec,
156
+ replayWAL
157
+ } from "./chunk-U5HFZGAQ.js";
148
158
  import {
149
159
  cached,
150
160
  createWatermarkController,
@@ -177,34 +187,27 @@ import {
177
187
  toArray,
178
188
  toMessages$,
179
189
  toObservable
180
- } from "./chunk-YWTP2XRJ.js";
190
+ } from "./chunk-5WXTWOD7.js";
181
191
  import {
182
192
  ResettableTimer
183
193
  } from "./chunk-WZ2Z2CRV.js";
184
- import {
185
- JsonCodec,
186
- createDagCborCodec,
187
- createDagCborZstdCodec,
188
- graph_exports,
189
- negotiateCodec,
190
- replayWAL
191
- } from "./chunk-A2AJJOSJ.js";
192
194
  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,7 @@ __export(patterns_exports, {
1045
1049
  ai: () => ai_exports,
1046
1050
  cqrs: () => cqrs_exports,
1047
1051
  demoShell: () => demo_shell_exports,
1052
+ domainTemplates: () => domain_templates_exports,
1048
1053
  graphspec: () => graphspec_exports,
1049
1054
  layout: () => reactive_layout_exports,
1050
1055
  memory: () => memory_exports,
@@ -2458,7 +2463,7 @@ function gaugesAsContext(graph, actor, options) {
2458
2463
  const ungrouped = [];
2459
2464
  for (const entry of entries) {
2460
2465
  const node2 = described.nodes[entry.path];
2461
- const tags = (node2.meta ?? {}).tags;
2466
+ const tags = node2.meta?.tags;
2462
2467
  if (tags && tags.length > 0) {
2463
2468
  const tag = tags[0];
2464
2469
  let group = tagGroups.get(tag);
@@ -3025,15 +3030,13 @@ function demoShell(opts) {
3025
3030
  };
3026
3031
  }
3027
3032
 
3028
- // src/patterns/graphspec.ts
3029
- var graphspec_exports = {};
3030
- __export(graphspec_exports, {
3031
- compileSpec: () => compileSpec,
3032
- decompileGraph: () => decompileGraph,
3033
- llmCompose: () => llmCompose,
3034
- llmRefine: () => llmRefine,
3035
- specDiff: () => specDiff,
3036
- validateSpec: () => validateSpec
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
3037
3040
  });
3038
3041
 
3039
3042
  // src/patterns/reduction.ts
@@ -3052,6 +3055,10 @@ function baseMeta(kind, meta) {
3052
3055
  ...meta ?? {}
3053
3056
  };
3054
3057
  }
3058
+ function keepalive2(n) {
3059
+ return n.subscribe(() => {
3060
+ });
3061
+ }
3055
3062
  function stratify(name, source, rules, opts) {
3056
3063
  const g = new Graph(name, opts);
3057
3064
  g.add("source", source);
@@ -3066,18 +3073,27 @@ function stratify(name, source, rules, opts) {
3066
3073
  }
3067
3074
  function _addBranch(graph, source, rulesNode, rule) {
3068
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;
3069
3081
  let pendingDirty = false;
3070
- const filterNode = node([source, rulesNode], () => void 0, {
3071
- describeKind: "operator",
3072
- meta: baseMeta("stratify_branch", { branch: rule.name }),
3073
- onMessage(msg, depIndex, actions) {
3074
- if (depIndex !== 0) return false;
3075
- const t = msg[0];
3076
- if (t === DATA) {
3077
- const value = msg[1];
3082
+ function resolve(actions) {
3083
+ if (sourcePhase2) {
3084
+ sourcePhase2 = false;
3085
+ const value = sourceValue;
3086
+ sourceValue = _noValue;
3087
+ if (value !== _noValue) {
3078
3088
  const currentRules = rulesNode.get();
3079
3089
  const currentRule = currentRules.find((r) => r.name === rule.name);
3080
- if (currentRule && currentRule.classify(value)) {
3090
+ let matches = false;
3091
+ try {
3092
+ matches = currentRule?.classify(value) ?? false;
3093
+ } catch {
3094
+ matches = false;
3095
+ }
3096
+ if (matches) {
3081
3097
  pendingDirty = false;
3082
3098
  actions.emit(value);
3083
3099
  } else {
@@ -3086,28 +3102,57 @@ function _addBranch(graph, source, rulesNode, rule) {
3086
3102
  actions.down([[DIRTY], [RESOLVED]]);
3087
3103
  }
3088
3104
  }
3089
- return true;
3090
- }
3091
- if (t === DIRTY) {
3092
- pendingDirty = true;
3093
- return true;
3094
- }
3095
- if (t === RESOLVED) {
3105
+ } else {
3096
3106
  if (pendingDirty) {
3097
3107
  pendingDirty = false;
3098
3108
  actions.down([[DIRTY], [RESOLVED]]);
3099
3109
  } else {
3100
3110
  actions.down([[RESOLVED]]);
3101
3111
  }
3112
+ }
3113
+ }
3114
+ }
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
+ }
3102
3127
  return true;
3103
3128
  }
3104
- if (t === COMPLETE || t === ERROR) {
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;
3105
3146
  pendingDirty = false;
3106
- actions.down([msg]);
3147
+ if (depIndex === 0) {
3148
+ actions.down([msg]);
3149
+ }
3107
3150
  return true;
3108
3151
  }
3152
+ if (depIndex === 1) return true;
3109
3153
  return false;
3110
- }
3154
+ },
3155
+ completeWhenDepsComplete: false
3111
3156
  });
3112
3157
  graph.add(branchName, filterNode);
3113
3158
  graph.connect("source", branchName);
@@ -3143,20 +3188,14 @@ function funnel(name, sources, stages, opts) {
3143
3188
  const prevNode = g.resolve(prevOutputPath);
3144
3189
  const stageInputPath = `${stage.name}::input`;
3145
3190
  const stageInput = g.resolve(stageInputPath);
3146
- prevNode.subscribe((msgs) => {
3147
- for (const msg of msgs) {
3148
- const t = msg[0];
3149
- if (t === DATA) {
3150
- stageInput.down([[DATA, msg[1]]]);
3151
- } else if (t === DIRTY) {
3152
- stageInput.down([[DIRTY]]);
3153
- } else if (t === RESOLVED) {
3154
- stageInput.down([[RESOLVED]]);
3155
- } else if (t === COMPLETE || t === ERROR) {
3156
- stageInput.down([msg]);
3157
- }
3158
- }
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)
3159
3195
  });
3196
+ g.add(bridgeName, br);
3197
+ g.connect(prevOutputPath, bridgeName);
3198
+ keepalive2(br);
3160
3199
  prevOutputPath = `${stage.name}::output`;
3161
3200
  }
3162
3201
  return g;
@@ -3174,38 +3213,41 @@ function feedback(graph, condition, reentry, opts) {
3174
3213
  graph.add(counterName, counter);
3175
3214
  const condNode = graph.resolve(condition);
3176
3215
  const reentryNode = graph.resolve(reentry);
3177
- let tornDown = false;
3178
- let unsubCounter = null;
3179
- const safeUnsub = () => {
3180
- if (tornDown) return;
3181
- tornDown = true;
3182
- unsub();
3183
- unsubCounter?.();
3184
- };
3185
- const unsub = condNode.subscribe((msgs) => {
3186
- for (const msg of msgs) {
3187
- if (msg[0] === DATA) {
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) {
3188
3230
  const currentCount = counter.get();
3189
- if (currentCount >= maxIter) continue;
3231
+ if (currentCount >= maxIter) return true;
3190
3232
  const condValue = msg[1];
3191
- if (condValue == null) continue;
3192
- counter.down([[DATA, currentCount + 1]]);
3193
- reentryNode.down([[DATA, condValue]]);
3194
- } else if (msg[0] === COMPLETE || msg[0] === ERROR) {
3195
- const terminal = msg[0] === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [msg[0]];
3196
- counter.down([terminal]);
3197
- safeUnsub();
3233
+ if (condValue == null) return true;
3234
+ batch(() => {
3235
+ counter.down([[DATA, currentCount + 1]]);
3236
+ reentryNode.down([[DATA, condValue]]);
3237
+ });
3238
+ return true;
3198
3239
  }
3199
- }
3200
- });
3201
- unsubCounter = counter.subscribe((msgs) => {
3202
- for (const msg of msgs) {
3203
- if (msg[0] === COMPLETE || msg[0] === ERROR) {
3204
- safeUnsub();
3205
- return;
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;
3206
3244
  }
3245
+ return false;
3207
3246
  }
3208
3247
  });
3248
+ graph.add(feedbackEffectName, feedbackEffect);
3249
+ graph.connect(condition, feedbackEffectName);
3250
+ keepalive2(feedbackEffect);
3209
3251
  return graph;
3210
3252
  }
3211
3253
  function budgetGate(source, constraints, opts) {
@@ -3221,7 +3263,8 @@ function budgetGate(source, constraints, opts) {
3221
3263
  }
3222
3264
  function flushBuffer(actions) {
3223
3265
  while (buffer2.length > 0 && checkBudget()) {
3224
- const item = buffer2.shift();
3266
+ const item = buffer2[0];
3267
+ buffer2 = buffer2.slice(1);
3225
3268
  actions.emit(item);
3226
3269
  }
3227
3270
  if (buffer2.length === 0 && pendingResolved) {
@@ -3339,7 +3382,600 @@ function scorer(sources, weights, opts) {
3339
3382
  );
3340
3383
  }
3341
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
+
3342
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
+ }
3343
3979
  var VALID_NODE_TYPES2 = /* @__PURE__ */ new Set([
3344
3980
  "state",
3345
3981
  "producer",
@@ -3516,6 +4152,21 @@ ${validation.errors.join("\n")}`);
3516
4152
  const catalog = opts?.catalog ?? {};
3517
4153
  const g = new Graph(spec.name);
3518
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
+ };
3519
4170
  const created = /* @__PURE__ */ new Map();
3520
4171
  const deferred = [];
3521
4172
  for (const [name, raw] of Object.entries(spec.nodes)) {
@@ -3529,8 +4180,8 @@ ${validation.errors.join("\n")}`);
3529
4180
  g.add(name, nd);
3530
4181
  created.set(name, nd);
3531
4182
  } else if (n.type === "producer") {
3532
- const sourceFactory = n.source ? catalog.sources?.[n.source] : void 0;
3533
- const fnFactory = n.fn ? catalog.fns?.[n.fn] : void 0;
4183
+ const sourceFactory = n.source ? resolveSource(n.source) : void 0;
4184
+ const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
3534
4185
  if (sourceFactory) {
3535
4186
  const nd = sourceFactory(n.config ?? {});
3536
4187
  g.add(name, nd);
@@ -3560,7 +4211,7 @@ ${validation.errors.join("\n")}`);
3560
4211
  const deps = n.deps ?? [];
3561
4212
  if (!deps.every((dep) => created.has(dep))) continue;
3562
4213
  const resolvedDeps = deps.map((dep) => created.get(dep));
3563
- const fnFactory = n.fn ? catalog.fns?.[n.fn] : void 0;
4214
+ const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
3564
4215
  let nd;
3565
4216
  if (fnFactory) {
3566
4217
  nd = fnFactory(resolvedDeps, n.config ?? {});
@@ -3603,8 +4254,8 @@ ${validation.errors.join("\n")}`);
3603
4254
  sub.add(nName, nd);
3604
4255
  subCreated.set(nName, nd);
3605
4256
  } else if (nSpec.type === "producer") {
3606
- const sourceFactory = nSpec.source ? catalog.sources?.[nSpec.source] : void 0;
3607
- const fnFactory = nSpec.fn ? catalog.fns?.[nSpec.fn] : void 0;
4257
+ const sourceFactory = nSpec.source ? resolveSource(nSpec.source) : void 0;
4258
+ const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
3608
4259
  if (sourceFactory) {
3609
4260
  const nd = sourceFactory(nSpec.config ?? {});
3610
4261
  sub.add(nName, nd);
@@ -3635,7 +4286,7 @@ ${validation.errors.join("\n")}`);
3635
4286
  const allReady = deps.every((dep) => subCreated.has(dep) || created.has(dep));
3636
4287
  if (!allReady) continue;
3637
4288
  const resolvedDeps = deps.map((dep) => subCreated.get(dep) ?? created.get(dep));
3638
- const fnFactory = nSpec.fn ? catalog.fns?.[nSpec.fn] : void 0;
4289
+ const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
3639
4290
  let nd;
3640
4291
  if (fnFactory) {
3641
4292
  nd = fnFactory(resolvedDeps, nSpec.config ?? {});
@@ -3696,14 +4347,15 @@ var INTERNAL_META_KEYS = /* @__PURE__ */ new Set([
3696
4347
  "_templateName",
3697
4348
  "_templateBind",
3698
4349
  "feedbackFrom",
3699
- "feedbackTo"
4350
+ "feedbackTo",
4351
+ "_internal"
3700
4352
  ]);
3701
4353
  function decompileGraph(graph) {
3702
4354
  const desc = graph.describe({ detail: "standard" });
3703
4355
  const nodes = {};
3704
4356
  const feedbackEdges = [];
3705
4357
  const metaSegment = `::${GRAPH_META_SEGMENT}::`;
3706
- const feedbackCounterPattern = /^__feedback_(.+)$/;
4358
+ const feedbackCounterPattern = /^__feedback_(?!effect_)(.+)$/;
3707
4359
  const feedbackConditions = /* @__PURE__ */ new Set();
3708
4360
  for (const path of Object.keys(desc.nodes)) {
3709
4361
  if (path.includes(metaSegment)) continue;
@@ -3723,6 +4375,9 @@ function decompileGraph(graph) {
3723
4375
  for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
3724
4376
  if (path.includes(metaSegment)) continue;
3725
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;
3726
4381
  if (path.includes("::")) continue;
3727
4382
  const specNode = {
3728
4383
  type: nodeDesc.type
@@ -4055,11 +4710,12 @@ function stripFences2(text) {
4055
4710
  }
4056
4711
  async function llmCompose(problem, adapter, opts) {
4057
4712
  let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
4058
- if (opts?.catalogDescription) {
4713
+ const catalogPrompt = opts?.catalogDescription ?? (opts?.catalog ? generateCatalogPrompt(opts.catalog) : void 0);
4714
+ if (catalogPrompt) {
4059
4715
  systemPrompt += `
4060
4716
 
4061
- Available catalog:
4062
- ${opts.catalogDescription}`;
4717
+ Available catalog (use ONLY these names):
4718
+ ${catalogPrompt}`;
4063
4719
  }
4064
4720
  if (opts?.systemPromptExtra) {
4065
4721
  systemPrompt += `
@@ -4091,7 +4747,30 @@ ${opts.systemPromptExtra}`;
4091
4747
  throw new Error(`llmCompose: invalid GraphSpec:
4092
4748
  ${validation.errors.join("\n")}`);
4093
4749
  }
4094
- return parsed;
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;
4095
4774
  }
4096
4775
  async function llmRefine(currentSpec, feedback2, adapter, opts) {
4097
4776
  let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
@@ -4163,7 +4842,7 @@ function requireNonNegativeInt(value, label) {
4163
4842
  }
4164
4843
  return value;
4165
4844
  }
4166
- function keepalive2(n) {
4845
+ function keepalive4(n) {
4167
4846
  return n.subscribe(() => {
4168
4847
  });
4169
4848
  }
@@ -4199,7 +4878,7 @@ var TopicGraph = class extends Graph {
4199
4878
  );
4200
4879
  this.add("latest", this.latest);
4201
4880
  this.connect("events", "latest");
4202
- this._keepaliveDisposers.push(keepalive2(this.latest));
4881
+ this._keepaliveDisposers.push(keepalive4(this.latest));
4203
4882
  }
4204
4883
  destroy() {
4205
4884
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -4255,8 +4934,8 @@ var SubscriptionGraph = class extends Graph {
4255
4934
  this.connect("topic::events", "source");
4256
4935
  this.connect("source", "available");
4257
4936
  this.connect("cursor", "available");
4258
- this._keepaliveDisposers.push(keepalive2(this.source));
4259
- this._keepaliveDisposers.push(keepalive2(this.available));
4937
+ this._keepaliveDisposers.push(keepalive4(this.source));
4938
+ this._keepaliveDisposers.push(keepalive4(this.available));
4260
4939
  }
4261
4940
  destroy() {
4262
4941
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -4308,7 +4987,7 @@ var JobQueueGraph = class extends Graph {
4308
4987
  );
4309
4988
  this.add("depth", this.depth);
4310
4989
  this.connect("pending", "depth");
4311
- this._keepaliveDisposers.push(keepalive2(this.depth));
4990
+ this._keepaliveDisposers.push(keepalive4(this.depth));
4312
4991
  }
4313
4992
  destroy() {
4314
4993
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -4408,7 +5087,7 @@ var JobFlowGraph = class extends Graph {
4408
5087
  );
4409
5088
  this.add("completedCount", this.completedCount);
4410
5089
  this.connect("completed", "completedCount");
4411
- this._keepaliveDisposers.push(keepalive2(this.completedCount));
5090
+ this._keepaliveDisposers.push(keepalive4(this.completedCount));
4412
5091
  const maxPerPump = Math.max(
4413
5092
  1,
4414
5093
  requireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, "job flow maxPerPump")
@@ -4448,7 +5127,7 @@ var JobFlowGraph = class extends Graph {
4448
5127
  );
4449
5128
  this.add(`pump_${stage}`, pump);
4450
5129
  this.connect(`${stage}::pending`, `pump_${stage}`);
4451
- this._keepaliveDisposers.push(keepalive2(pump));
5130
+ this._keepaliveDisposers.push(keepalive4(pump));
4452
5131
  }
4453
5132
  }
4454
5133
  destroy() {
@@ -4520,7 +5199,7 @@ var TopicBridgeGraph = class extends Graph {
4520
5199
  );
4521
5200
  this.add("pump", pump);
4522
5201
  this.connect("subscription::available", "pump");
4523
- this._keepaliveDisposers.push(keepalive2(pump));
5202
+ this._keepaliveDisposers.push(keepalive4(pump));
4524
5203
  }
4525
5204
  destroy() {
4526
5205
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -4590,7 +5269,7 @@ function registerStep(graph, name, step, depPaths) {
4590
5269
  graph.connect(path, name);
4591
5270
  }
4592
5271
  }
4593
- function baseMeta2(kind, meta) {
5272
+ function baseMeta3(kind, meta) {
4594
5273
  return {
4595
5274
  orchestration: true,
4596
5275
  orchestration_type: kind,
@@ -4628,7 +5307,7 @@ function task(graph, name, run, opts) {
4628
5307
  ...nodeOpts,
4629
5308
  name,
4630
5309
  describeKind: "derived",
4631
- meta: baseMeta2("task", opts?.meta)
5310
+ meta: baseMeta3("task", opts?.meta)
4632
5311
  }
4633
5312
  );
4634
5313
  registerStep(
@@ -4651,7 +5330,7 @@ function branch(graph, name, source, predicate, opts) {
4651
5330
  ...opts,
4652
5331
  name,
4653
5332
  describeKind: "derived",
4654
- meta: baseMeta2("branch", opts?.meta)
5333
+ meta: baseMeta3("branch", opts?.meta)
4655
5334
  }
4656
5335
  );
4657
5336
  registerStep(graph, name, step, src.path ? [src.path] : []);
@@ -4674,7 +5353,7 @@ function gate2(graph, name, source, control, opts) {
4674
5353
  ...opts,
4675
5354
  name,
4676
5355
  describeKind: "operator",
4677
- meta: baseMeta2("gate", opts?.meta)
5356
+ meta: baseMeta3("gate", opts?.meta)
4678
5357
  }
4679
5358
  );
4680
5359
  registerStep(
@@ -4702,7 +5381,7 @@ function approval(graph, name, source, approver, opts) {
4702
5381
  ...opts,
4703
5382
  name,
4704
5383
  describeKind: "operator",
4705
- meta: baseMeta2("approval", opts?.meta)
5384
+ meta: baseMeta3("approval", opts?.meta)
4706
5385
  }
4707
5386
  );
4708
5387
  registerStep(
@@ -4721,7 +5400,7 @@ function forEach2(graph, name, source, run, opts) {
4721
5400
  name,
4722
5401
  describeKind: "effect",
4723
5402
  completeWhenDepsComplete: false,
4724
- meta: baseMeta2("forEach", opts?.meta),
5403
+ meta: baseMeta3("forEach", opts?.meta),
4725
5404
  onMessage(msg, depIndex, actions) {
4726
5405
  if (terminated) return true;
4727
5406
  if (depIndex !== 0) {
@@ -4756,7 +5435,7 @@ function join(graph, name, deps, opts) {
4756
5435
  ...opts,
4757
5436
  name,
4758
5437
  describeKind: "derived",
4759
- meta: baseMeta2("join", opts?.meta)
5438
+ meta: baseMeta3("join", opts?.meta)
4760
5439
  }
4761
5440
  );
4762
5441
  registerStep(
@@ -4787,7 +5466,7 @@ function loop(graph, name, source, iterate, opts) {
4787
5466
  ...opts,
4788
5467
  name,
4789
5468
  describeKind: "derived",
4790
- meta: baseMeta2("loop", opts?.meta)
5469
+ meta: baseMeta3("loop", opts?.meta)
4791
5470
  }
4792
5471
  );
4793
5472
  registerStep(
@@ -4812,7 +5491,7 @@ function sensor(graph, name, initial, opts) {
4812
5491
  name,
4813
5492
  initial,
4814
5493
  describeKind: "producer",
4815
- meta: baseMeta2("sensor", opts?.meta)
5494
+ meta: baseMeta3("sensor", opts?.meta)
4816
5495
  });
4817
5496
  registerStep(graph, name, source, []);
4818
5497
  return {
@@ -4850,7 +5529,7 @@ function wait(graph, name, source, ms, opts) {
4850
5529
  initial: src.node.get(),
4851
5530
  describeKind: "operator",
4852
5531
  completeWhenDepsComplete: false,
4853
- meta: baseMeta2("wait", opts?.meta),
5532
+ meta: baseMeta3("wait", opts?.meta),
4854
5533
  onMessage(msg, depIndex, actions) {
4855
5534
  if (terminated) return true;
4856
5535
  if (depIndex !== 0) {
@@ -4900,7 +5579,7 @@ function onFailure(graph, name, source, recover, opts) {
4900
5579
  name,
4901
5580
  describeKind: "operator",
4902
5581
  completeWhenDepsComplete: false,
4903
- meta: baseMeta2("onFailure", opts?.meta),
5582
+ meta: baseMeta3("onFailure", opts?.meta),
4904
5583
  onMessage(msg, _depIndex, actions) {
4905
5584
  if (terminated) return true;
4906
5585
  if (msg[0] === ERROR) {
@@ -4924,10 +5603,12 @@ function onFailure(graph, name, source, recover, opts) {
4924
5603
  // src/index.ts
4925
5604
  var version = "0.0.0";
4926
5605
  export {
5606
+ CLEANUP_RESULT,
4927
5607
  COMPLETE,
4928
5608
  CircuitOpenError,
4929
5609
  DATA,
4930
5610
  DEFAULT_ACTOR,
5611
+ DEFAULT_DOWN,
4931
5612
  DIRTY,
4932
5613
  DictCheckpointAdapter,
4933
5614
  DynamicNodeImpl,
@@ -4953,6 +5634,7 @@ export {
4953
5634
  ai_exports as ai,
4954
5635
  audit,
4955
5636
  batch,
5637
+ bridge,
4956
5638
  buffer,
4957
5639
  bufferCount,
4958
5640
  bufferTime,
@@ -4964,6 +5646,7 @@ export {
4964
5646
  checkpointToRedis,
4965
5647
  checkpointToS3,
4966
5648
  circuitBreaker,
5649
+ cleanupResult,
4967
5650
  combine,
4968
5651
  combineLatest,
4969
5652
  compat_exports as compat,
@@ -4988,6 +5671,7 @@ export {
4988
5671
  deserializeError,
4989
5672
  distill,
4990
5673
  distinctUntilChanged,
5674
+ domain_templates_exports as domainTemplates,
4991
5675
  dynamicNode,
4992
5676
  effect,
4993
5677
  elementAt,