@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.cjs CHANGED
@@ -65,10 +65,12 @@ var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "acce
65
65
  // src/index.ts
66
66
  var index_exports = {};
67
67
  __export(index_exports, {
68
+ CLEANUP_RESULT: () => CLEANUP_RESULT,
68
69
  COMPLETE: () => COMPLETE,
69
70
  CircuitOpenError: () => CircuitOpenError,
70
71
  DATA: () => DATA,
71
72
  DEFAULT_ACTOR: () => DEFAULT_ACTOR,
73
+ DEFAULT_DOWN: () => DEFAULT_DOWN,
72
74
  DIRTY: () => DIRTY,
73
75
  DictCheckpointAdapter: () => DictCheckpointAdapter,
74
76
  DynamicNodeImpl: () => DynamicNodeImpl,
@@ -94,6 +96,7 @@ __export(index_exports, {
94
96
  ai: () => ai_exports,
95
97
  audit: () => audit,
96
98
  batch: () => batch,
99
+ bridge: () => bridge,
97
100
  buffer: () => buffer,
98
101
  bufferCount: () => bufferCount,
99
102
  bufferTime: () => bufferTime,
@@ -105,6 +108,7 @@ __export(index_exports, {
105
108
  checkpointToRedis: () => checkpointToRedis,
106
109
  checkpointToS3: () => checkpointToS3,
107
110
  circuitBreaker: () => circuitBreaker,
111
+ cleanupResult: () => cleanupResult,
108
112
  combine: () => combine,
109
113
  combineLatest: () => combineLatest,
110
114
  compat: () => compat_exports,
@@ -129,6 +133,7 @@ __export(index_exports, {
129
133
  deserializeError: () => deserializeError,
130
134
  distill: () => distill,
131
135
  distinctUntilChanged: () => distinctUntilChanged,
136
+ domainTemplates: () => domain_templates_exports,
132
137
  dynamicNode: () => dynamicNode,
133
138
  effect: () => effect,
134
139
  elementAt: () => elementAt,
@@ -180,6 +185,7 @@ __export(index_exports, {
180
185
  gate: () => gate,
181
186
  globToRegExp: () => globToRegExp,
182
187
  graph: () => graph_exports,
188
+ graphspec: () => graphspec_exports,
183
189
  interval: () => interval,
184
190
  isBatching: () => isBatching,
185
191
  isKnownMessageType: () => isKnownMessageType,
@@ -728,6 +734,7 @@ function isV1(info) {
728
734
 
729
735
  // src/core/node.ts
730
736
  var NO_VALUE = /* @__PURE__ */ Symbol.for("graphrefly/NO_VALUE");
737
+ var CLEANUP_RESULT = /* @__PURE__ */ Symbol.for("graphrefly/CLEANUP_RESULT");
731
738
  function createIntBitSet() {
732
739
  let bits = 0;
733
740
  return {
@@ -790,6 +797,12 @@ function createBitSet(size) {
790
797
  }
791
798
  var isNodeArray = (value) => Array.isArray(value);
792
799
  var isNodeOptions = (value) => typeof value === "object" && value != null && !Array.isArray(value);
800
+ function cleanupResult(cleanup, ...args) {
801
+ const r = { [CLEANUP_RESULT]: true, cleanup };
802
+ if (args.length > 0) r.value = args[0];
803
+ return r;
804
+ }
805
+ var isCleanupResult = (value) => typeof value === "object" && value !== null && CLEANUP_RESULT in value;
793
806
  var isCleanupFn = (value) => typeof value === "function";
794
807
  var statusAfterMessage = (status, msg) => {
795
808
  const t = msg[0];
@@ -1205,6 +1218,14 @@ var NodeImpl = class {
1205
1218
  this._lastDepValues = depValues;
1206
1219
  this._inspectorHook?.({ kind: "run", depValues });
1207
1220
  const out = this._fn(depValues, this._actions);
1221
+ if (isCleanupResult(out)) {
1222
+ this._cleanup = out.cleanup;
1223
+ if (this._manualEmitUsed) return;
1224
+ if ("value" in out) {
1225
+ this._emitAutoValue(out.value);
1226
+ }
1227
+ return;
1228
+ }
1208
1229
  if (isCleanupFn(out)) {
1209
1230
  this._cleanup = out;
1210
1231
  return;
@@ -3543,7 +3564,6 @@ function resolveDescribeFields(detail, fields) {
3543
3564
  case "full":
3544
3565
  return null;
3545
3566
  // null = include everything
3546
- case "minimal":
3547
3567
  default:
3548
3568
  return /* @__PURE__ */ new Set(["type", "deps"]);
3549
3569
  }
@@ -5518,9 +5538,11 @@ __export(cqrs_exports, {
5518
5538
  // src/core/index.ts
5519
5539
  var core_exports = {};
5520
5540
  __export(core_exports, {
5541
+ CLEANUP_RESULT: () => CLEANUP_RESULT,
5521
5542
  COMPLETE: () => COMPLETE,
5522
5543
  DATA: () => DATA,
5523
5544
  DEFAULT_ACTOR: () => DEFAULT_ACTOR,
5545
+ DEFAULT_DOWN: () => DEFAULT_DOWN,
5524
5546
  DIRTY: () => DIRTY,
5525
5547
  DynamicNodeImpl: () => DynamicNodeImpl,
5526
5548
  ERROR: () => ERROR,
@@ -5534,6 +5556,8 @@ __export(core_exports, {
5534
5556
  accessHintForGuard: () => accessHintForGuard,
5535
5557
  advanceVersion: () => advanceVersion,
5536
5558
  batch: () => batch,
5559
+ bridge: () => bridge,
5560
+ cleanupResult: () => cleanupResult,
5537
5561
  createVersioning: () => createVersioning,
5538
5562
  defaultHash: () => defaultHash,
5539
5563
  derived: () => derived,
@@ -5563,6 +5587,57 @@ __export(core_exports, {
5563
5587
  wallClockNs: () => wallClockNs
5564
5588
  });
5565
5589
 
5590
+ // src/core/bridge.ts
5591
+ var DEFAULT_DOWN = [
5592
+ DATA,
5593
+ DIRTY,
5594
+ RESOLVED,
5595
+ COMPLETE,
5596
+ ERROR,
5597
+ TEARDOWN,
5598
+ PAUSE,
5599
+ RESUME,
5600
+ INVALIDATE
5601
+ ];
5602
+ var STANDARD_TYPES = /* @__PURE__ */ new Set([
5603
+ DATA,
5604
+ DIRTY,
5605
+ RESOLVED,
5606
+ COMPLETE,
5607
+ ERROR,
5608
+ TEARDOWN,
5609
+ PAUSE,
5610
+ RESUME,
5611
+ INVALIDATE
5612
+ ]);
5613
+ function bridge(from, to, opts) {
5614
+ const allowedDown = new Set(opts?.down ?? DEFAULT_DOWN);
5615
+ const onMessage = (msg, _depIndex, _actions) => {
5616
+ const type = msg[0];
5617
+ if (!STANDARD_TYPES.has(type)) {
5618
+ to.down([msg]);
5619
+ return true;
5620
+ }
5621
+ if (type === COMPLETE || type === ERROR) {
5622
+ if (allowedDown.has(type)) {
5623
+ to.down([msg]);
5624
+ }
5625
+ return false;
5626
+ }
5627
+ if (!allowedDown.has(type)) {
5628
+ return true;
5629
+ }
5630
+ to.down([msg]);
5631
+ return true;
5632
+ };
5633
+ return node([from], void 0, {
5634
+ name: opts?.name,
5635
+ describeKind: "effect",
5636
+ onMessage,
5637
+ meta: { _internal: true }
5638
+ });
5639
+ }
5640
+
5566
5641
  // src/core/timer.ts
5567
5642
  var ResettableTimer = class {
5568
5643
  _timer;
@@ -12862,6 +12937,8 @@ __export(patterns_exports, {
12862
12937
  ai: () => ai_exports,
12863
12938
  cqrs: () => cqrs_exports,
12864
12939
  demoShell: () => demo_shell_exports,
12940
+ domainTemplates: () => domain_templates_exports,
12941
+ graphspec: () => graphspec_exports,
12865
12942
  layout: () => reactive_layout_exports,
12866
12943
  memory: () => memory_exports,
12867
12944
  messaging: () => messaging_exports,
@@ -14274,7 +14351,7 @@ function gaugesAsContext(graph, actor, options) {
14274
14351
  const ungrouped = [];
14275
14352
  for (const entry of entries) {
14276
14353
  const node2 = described.nodes[entry.path];
14277
- const tags = (node2.meta ?? {}).tags;
14354
+ const tags = node2.meta?.tags;
14278
14355
  if (tags && tags.length > 0) {
14279
14356
  const tag = tags[0];
14280
14357
  let group = tagGroups.get(tag);
@@ -15417,135 +15494,1926 @@ function demoShell(opts) {
15417
15494
  };
15418
15495
  }
15419
15496
 
15420
- // src/patterns/messaging.ts
15421
- var messaging_exports = {};
15422
- __export(messaging_exports, {
15423
- JobFlowGraph: () => JobFlowGraph,
15424
- JobQueueGraph: () => JobQueueGraph,
15425
- SubscriptionGraph: () => SubscriptionGraph,
15426
- TopicBridgeGraph: () => TopicBridgeGraph,
15427
- TopicGraph: () => TopicGraph,
15428
- jobFlow: () => jobFlow,
15429
- jobQueue: () => jobQueue,
15430
- subscription: () => subscription,
15431
- topic: () => topic,
15432
- topicBridge: () => topicBridge
15497
+ // src/patterns/domain-templates.ts
15498
+ var domain_templates_exports = {};
15499
+ __export(domain_templates_exports, {
15500
+ contentModerationGraph: () => contentModerationGraph,
15501
+ dataQualityGraph: () => dataQualityGraph,
15502
+ issueTrackerGraph: () => issueTrackerGraph,
15503
+ observabilityGraph: () => observabilityGraph
15433
15504
  });
15434
- var DEFAULT_MAX_PER_PUMP = 2147483647;
15435
- function requireNonNegativeInt(value, label) {
15436
- if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
15437
- throw new Error(`${label} must be a non-negative integer`);
15438
- }
15439
- return value;
15505
+
15506
+ // src/patterns/reduction.ts
15507
+ var reduction_exports = {};
15508
+ __export(reduction_exports, {
15509
+ budgetGate: () => budgetGate,
15510
+ feedback: () => feedback,
15511
+ funnel: () => funnel,
15512
+ scorer: () => scorer,
15513
+ stratify: () => stratify
15514
+ });
15515
+ function baseMeta(kind, meta) {
15516
+ return {
15517
+ reduction: true,
15518
+ reduction_type: kind,
15519
+ ...meta ?? {}
15520
+ };
15440
15521
  }
15441
15522
  function keepalive4(n) {
15442
15523
  return n.subscribe(() => {
15443
15524
  });
15444
15525
  }
15445
- function messagingMeta(kind, extra) {
15446
- return {
15447
- messaging: true,
15448
- messaging_type: kind,
15449
- ...extra ?? {}
15450
- };
15526
+ function stratify(name, source, rules, opts) {
15527
+ const g = new Graph(name, opts);
15528
+ g.add("source", source);
15529
+ const rulesNode = state(rules, {
15530
+ meta: baseMeta("stratify_rules")
15531
+ });
15532
+ g.add("rules", rulesNode);
15533
+ for (const rule of rules) {
15534
+ _addBranch(g, source, rulesNode, rule);
15535
+ }
15536
+ return g;
15451
15537
  }
15452
- var TopicGraph = class extends Graph {
15453
- _log;
15454
- _keepaliveDisposers = [];
15455
- events;
15456
- latest;
15457
- constructor(name, opts = {}) {
15458
- super(name, opts.graph);
15459
- this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
15460
- this.events = this._log.entries;
15461
- this.add("events", this.events);
15462
- this.latest = derived(
15463
- [this.events],
15464
- ([snapshot]) => {
15465
- const entries = snapshot.value.entries;
15466
- return entries.length === 0 ? void 0 : entries[entries.length - 1];
15467
- },
15468
- {
15469
- name: "latest",
15470
- describeKind: "derived",
15471
- meta: messagingMeta("topic_latest"),
15472
- initial: void 0
15538
+ function _addBranch(graph, source, rulesNode, rule) {
15539
+ const branchName = `branch/${rule.name}`;
15540
+ const _noValue = /* @__PURE__ */ Symbol("noValue");
15541
+ let sourceDirty = false;
15542
+ let rulesDirty = false;
15543
+ let sourcePhase2 = false;
15544
+ let sourceValue = _noValue;
15545
+ let pendingDirty = false;
15546
+ function resolve(actions) {
15547
+ if (sourcePhase2) {
15548
+ sourcePhase2 = false;
15549
+ const value = sourceValue;
15550
+ sourceValue = _noValue;
15551
+ if (value !== _noValue) {
15552
+ const currentRules = rulesNode.get();
15553
+ const currentRule = currentRules.find((r) => r.name === rule.name);
15554
+ let matches = false;
15555
+ try {
15556
+ matches = currentRule?.classify(value) ?? false;
15557
+ } catch {
15558
+ matches = false;
15559
+ }
15560
+ if (matches) {
15561
+ pendingDirty = false;
15562
+ actions.emit(value);
15563
+ } else {
15564
+ if (pendingDirty) {
15565
+ pendingDirty = false;
15566
+ actions.down([[DIRTY], [RESOLVED]]);
15567
+ }
15568
+ }
15569
+ } else {
15570
+ if (pendingDirty) {
15571
+ pendingDirty = false;
15572
+ actions.down([[DIRTY], [RESOLVED]]);
15573
+ } else {
15574
+ actions.down([[RESOLVED]]);
15575
+ }
15473
15576
  }
15474
- );
15475
- this.add("latest", this.latest);
15476
- this.connect("events", "latest");
15477
- this._keepaliveDisposers.push(keepalive4(this.latest));
15478
- }
15479
- destroy() {
15480
- for (const dispose of this._keepaliveDisposers) dispose();
15481
- this._keepaliveDisposers.length = 0;
15482
- super.destroy();
15483
- }
15484
- publish(value) {
15485
- this._log.append(value);
15486
- }
15487
- retained() {
15488
- const snapshot = this.events.get();
15489
- return snapshot.value.entries;
15577
+ }
15490
15578
  }
15491
- };
15492
- var SubscriptionGraph = class extends Graph {
15493
- _keepaliveDisposers = [];
15494
- source;
15495
- cursor;
15496
- available;
15497
- constructor(name, topicGraph, opts = {}) {
15498
- super(name, opts.graph);
15499
- const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
15500
- this.mount("topic", topicGraph);
15501
- const topicEvents = topicGraph.events;
15502
- this.source = derived([topicEvents], ([snapshot]) => snapshot, {
15503
- name: "source",
15504
- describeKind: "derived",
15505
- meta: messagingMeta("subscription_source"),
15506
- initial: topicEvents.get()
15507
- });
15508
- this.add("source", this.source);
15509
- this.cursor = state(initialCursor, {
15510
- name: "cursor",
15511
- describeKind: "state",
15512
- meta: messagingMeta("subscription_cursor")
15513
- });
15514
- this.add("cursor", this.cursor);
15515
- this.available = derived(
15516
- [this.source, this.cursor],
15517
- ([sourceSnapshot, cursor]) => {
15518
- const entries = sourceSnapshot.value.entries;
15519
- const start = Math.max(0, Math.trunc(cursor ?? 0));
15520
- return entries.slice(start);
15521
- },
15522
- {
15523
- name: "available",
15524
- describeKind: "derived",
15525
- meta: messagingMeta("subscription_available"),
15526
- initial: []
15579
+ const filterNode = node([source, rulesNode], () => void 0, {
15580
+ describeKind: "operator",
15581
+ meta: baseMeta("stratify_branch", { branch: rule.name }),
15582
+ onMessage(msg, depIndex, actions) {
15583
+ const t = msg[0];
15584
+ if (t === DIRTY) {
15585
+ if (depIndex === 0) {
15586
+ sourceDirty = true;
15587
+ pendingDirty = true;
15588
+ } else {
15589
+ rulesDirty = true;
15590
+ }
15591
+ return true;
15527
15592
  }
15528
- );
15529
- this.add("available", this.available);
15530
- this.connect("topic::events", "source");
15531
- this.connect("source", "available");
15532
- this.connect("cursor", "available");
15533
- this._keepaliveDisposers.push(keepalive4(this.source));
15534
- this._keepaliveDisposers.push(keepalive4(this.available));
15535
- }
15536
- destroy() {
15537
- for (const dispose of this._keepaliveDisposers) dispose();
15538
- this._keepaliveDisposers.length = 0;
15539
- super.destroy();
15593
+ if (t === DATA || t === RESOLVED) {
15594
+ if (depIndex === 0) {
15595
+ sourceDirty = false;
15596
+ sourcePhase2 = true;
15597
+ sourceValue = t === DATA ? msg[1] : _noValue;
15598
+ } else {
15599
+ rulesDirty = false;
15600
+ }
15601
+ if (sourceDirty || rulesDirty) return true;
15602
+ resolve(actions);
15603
+ return true;
15604
+ }
15605
+ if (t === COMPLETE || t === ERROR || t === TEARDOWN) {
15606
+ sourceDirty = false;
15607
+ rulesDirty = false;
15608
+ sourcePhase2 = false;
15609
+ sourceValue = _noValue;
15610
+ pendingDirty = false;
15611
+ if (depIndex === 0) {
15612
+ actions.down([msg]);
15613
+ }
15614
+ return true;
15615
+ }
15616
+ if (depIndex === 1) return true;
15617
+ return false;
15618
+ },
15619
+ completeWhenDepsComplete: false
15620
+ });
15621
+ graph.add(branchName, filterNode);
15622
+ graph.connect("source", branchName);
15623
+ if (rule.ops) {
15624
+ const transformed = rule.ops(filterNode);
15625
+ const transformedName = `branch/${rule.name}/out`;
15626
+ graph.add(transformedName, transformed);
15627
+ graph.connect(branchName, transformedName);
15540
15628
  }
15541
- ack(count) {
15542
- const available = this.available.get();
15543
- const requested = count === void 0 ? available.length : requireNonNegativeInt(count, "subscription ack count");
15544
- const step = Math.min(requested, available.length);
15545
- if (step <= 0) return this.cursor.get();
15546
- const next = this.cursor.get() + step;
15547
- this.cursor.down([[DATA, next]]);
15548
- return next;
15629
+ }
15630
+ function funnel(name, sources, stages, opts) {
15631
+ if (sources.length === 0) throw new RangeError("funnel requires at least one source");
15632
+ if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
15633
+ const g = new Graph(name, opts);
15634
+ const merged = sources.length === 1 ? sources[0] : merge(...sources);
15635
+ g.add("merged", merged);
15636
+ let prevOutputPath = "merged";
15637
+ for (let i = 0; i < stages.length; i++) {
15638
+ const stage = stages[i];
15639
+ const sub = new Graph(stage.name);
15640
+ stage.build(sub);
15641
+ try {
15642
+ sub.resolve("input");
15643
+ } catch {
15644
+ throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
15645
+ }
15646
+ try {
15647
+ sub.resolve("output");
15648
+ } catch {
15649
+ throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
15650
+ }
15651
+ g.mount(stage.name, sub);
15652
+ const prevNode = g.resolve(prevOutputPath);
15653
+ const stageInputPath = `${stage.name}::input`;
15654
+ const stageInput = g.resolve(stageInputPath);
15655
+ const bridgeName = `__bridge_${prevOutputPath}\u2192${stage.name}_input`;
15656
+ const br = bridge(prevNode, stageInput, {
15657
+ name: bridgeName,
15658
+ down: DEFAULT_DOWN.filter((t) => t !== TEARDOWN)
15659
+ });
15660
+ g.add(bridgeName, br);
15661
+ g.connect(prevOutputPath, bridgeName);
15662
+ keepalive4(br);
15663
+ prevOutputPath = `${stage.name}::output`;
15664
+ }
15665
+ return g;
15666
+ }
15667
+ function feedback(graph, condition, reentry, opts) {
15668
+ const maxIter = opts?.maxIterations ?? 10;
15669
+ const counterName = `__feedback_${condition}`;
15670
+ const counter = state(0, {
15671
+ meta: baseMeta("feedback_counter", {
15672
+ maxIterations: maxIter,
15673
+ feedbackFrom: condition,
15674
+ feedbackTo: reentry
15675
+ })
15676
+ });
15677
+ graph.add(counterName, counter);
15678
+ const condNode = graph.resolve(condition);
15679
+ const reentryNode = graph.resolve(reentry);
15680
+ const feedbackEffectName = `__feedback_effect_${condition}`;
15681
+ const feedbackEffect = node([condNode], void 0, {
15682
+ name: feedbackEffectName,
15683
+ describeKind: "effect",
15684
+ meta: {
15685
+ ...baseMeta("feedback_effect", {
15686
+ feedbackFrom: condition,
15687
+ feedbackTo: reentry
15688
+ }),
15689
+ _internal: true
15690
+ },
15691
+ onMessage(msg, _depIndex, _actions) {
15692
+ const t = msg[0];
15693
+ if (t === DATA) {
15694
+ const currentCount = counter.get();
15695
+ if (currentCount >= maxIter) return true;
15696
+ const condValue = msg[1];
15697
+ if (condValue == null) return true;
15698
+ batch(() => {
15699
+ counter.down([[DATA, currentCount + 1]]);
15700
+ reentryNode.down([[DATA, condValue]]);
15701
+ });
15702
+ return true;
15703
+ }
15704
+ if (t === COMPLETE || t === ERROR) {
15705
+ const terminal = t === ERROR && msg.length > 1 ? [ERROR, msg[1]] : [t];
15706
+ counter.down([terminal]);
15707
+ return true;
15708
+ }
15709
+ return false;
15710
+ }
15711
+ });
15712
+ graph.add(feedbackEffectName, feedbackEffect);
15713
+ graph.connect(condition, feedbackEffectName);
15714
+ keepalive4(feedbackEffect);
15715
+ return graph;
15716
+ }
15717
+ function budgetGate(source, constraints, opts) {
15718
+ if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
15719
+ const constraintNodes = constraints.map((c) => c.node);
15720
+ const allDeps = [source, ...constraintNodes];
15721
+ let buffer2 = [];
15722
+ let paused = false;
15723
+ let pendingResolved = false;
15724
+ const lockId = /* @__PURE__ */ Symbol("budget-gate");
15725
+ function checkBudget() {
15726
+ return constraints.every((c) => c.check(c.node.get()));
15727
+ }
15728
+ function flushBuffer(actions) {
15729
+ while (buffer2.length > 0 && checkBudget()) {
15730
+ const item = buffer2[0];
15731
+ buffer2 = buffer2.slice(1);
15732
+ actions.emit(item);
15733
+ }
15734
+ if (buffer2.length === 0 && pendingResolved) {
15735
+ pendingResolved = false;
15736
+ actions.down([[RESOLVED]]);
15737
+ }
15738
+ }
15739
+ return node(allDeps, () => void 0, {
15740
+ ...opts,
15741
+ describeKind: "operator",
15742
+ meta: baseMeta("budget_gate", opts?.meta),
15743
+ onMessage(msg, depIndex, actions) {
15744
+ const t = msg[0];
15745
+ if (depIndex === 0) {
15746
+ if (t === DATA) {
15747
+ if (checkBudget() && buffer2.length === 0) {
15748
+ actions.emit(msg[1]);
15749
+ } else {
15750
+ buffer2.push(msg[1]);
15751
+ if (!paused) {
15752
+ paused = true;
15753
+ actions.up([[PAUSE, lockId]]);
15754
+ }
15755
+ }
15756
+ return true;
15757
+ }
15758
+ if (t === DIRTY) {
15759
+ actions.down([[DIRTY]]);
15760
+ return true;
15761
+ }
15762
+ if (t === RESOLVED) {
15763
+ if (buffer2.length === 0) {
15764
+ actions.down([[RESOLVED]]);
15765
+ } else {
15766
+ pendingResolved = true;
15767
+ }
15768
+ return true;
15769
+ }
15770
+ if (t === COMPLETE || t === ERROR) {
15771
+ for (const item of buffer2) {
15772
+ actions.emit(item);
15773
+ }
15774
+ buffer2 = [];
15775
+ pendingResolved = false;
15776
+ if (paused) {
15777
+ paused = false;
15778
+ actions.up([[RESUME, lockId]]);
15779
+ }
15780
+ actions.down([msg]);
15781
+ return true;
15782
+ }
15783
+ return false;
15784
+ }
15785
+ if (t === DATA || t === RESOLVED) {
15786
+ if (checkBudget() && buffer2.length > 0) {
15787
+ flushBuffer(actions);
15788
+ if (buffer2.length === 0 && paused) {
15789
+ paused = false;
15790
+ actions.up([[RESUME, lockId]]);
15791
+ }
15792
+ } else if (!checkBudget() && !paused && buffer2.length > 0) {
15793
+ paused = true;
15794
+ actions.up([[PAUSE, lockId]]);
15795
+ }
15796
+ return true;
15797
+ }
15798
+ if (t === DIRTY) {
15799
+ return true;
15800
+ }
15801
+ if (t === ERROR) {
15802
+ actions.down([msg]);
15803
+ return true;
15804
+ }
15805
+ if (t === COMPLETE) {
15806
+ return true;
15807
+ }
15808
+ return false;
15809
+ }
15810
+ });
15811
+ }
15812
+ function scorer(sources, weights, opts) {
15813
+ if (sources.length === 0) throw new RangeError("scorer requires at least one source");
15814
+ if (sources.length !== weights.length) {
15815
+ throw new RangeError("scorer requires the same number of sources and weights");
15816
+ }
15817
+ const allDeps = [...sources, ...weights];
15818
+ const n = sources.length;
15819
+ const scoreFns = opts?.scoreFns;
15820
+ return derived(
15821
+ allDeps,
15822
+ (vals) => {
15823
+ const signals = vals.slice(0, n);
15824
+ const weightValues = vals.slice(n);
15825
+ const breakdown = [];
15826
+ let totalScore = 0;
15827
+ for (let i = 0; i < n; i++) {
15828
+ const sig = signals[i] ?? 0;
15829
+ const wt = weightValues[i] ?? 0;
15830
+ const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
15831
+ const weighted = rawScore * wt;
15832
+ breakdown.push(weighted);
15833
+ totalScore += weighted;
15834
+ }
15835
+ return {
15836
+ value: signals,
15837
+ score: totalScore,
15838
+ breakdown
15839
+ };
15840
+ },
15841
+ {
15842
+ ...opts,
15843
+ describeKind: "derived",
15844
+ meta: baseMeta("scorer", opts?.meta)
15845
+ }
15846
+ );
15847
+ }
15848
+
15849
+ // src/patterns/domain-templates.ts
15850
+ function keepalive5(n) {
15851
+ return n.subscribe(() => {
15852
+ });
15853
+ }
15854
+ function baseMeta2(kind, extra) {
15855
+ return { domain_template: true, template_type: kind, ...extra ?? {} };
15856
+ }
15857
+ function observabilityGraph(name, opts) {
15858
+ const g = new Graph(name, opts);
15859
+ g.add("source", opts.source);
15860
+ const defaultBranches = [
15861
+ { name: "errors", classify: (v) => isTagged(v, "error") },
15862
+ { name: "traces", classify: (v) => isTagged(v, "trace") },
15863
+ { name: "metrics", classify: (v) => isTagged(v, "metric") }
15864
+ ];
15865
+ const branches = opts.branches ?? defaultBranches;
15866
+ const rules = branches.map((b) => ({
15867
+ name: b.name,
15868
+ classify: b.classify
15869
+ }));
15870
+ const strat = stratify("stratify", opts.source, rules);
15871
+ g.mount("stratify", strat);
15872
+ const branchNodes = branches.map((b) => {
15873
+ try {
15874
+ return g.resolve(`stratify::branch/${b.name}`);
15875
+ } catch {
15876
+ return state(null);
15877
+ }
15878
+ });
15879
+ const correlateFn = opts.correlate ?? ((vals) => vals);
15880
+ const correlateNode = derived(
15881
+ branchNodes,
15882
+ (vals) => correlateFn(vals),
15883
+ {
15884
+ meta: baseMeta2("observability", { stage: "correlate" })
15885
+ }
15886
+ );
15887
+ g.add("correlate", correlateNode);
15888
+ for (const b of branches) {
15889
+ try {
15890
+ g.connect(`stratify::branch/${b.name}`, "correlate");
15891
+ } catch {
15892
+ }
15893
+ }
15894
+ const sloCheckFn = opts.sloCheck ?? (() => ({ pass: true }));
15895
+ const sloValue = derived([correlateNode], (vals) => vals[0], {
15896
+ meta: baseMeta2("observability", { stage: "slo_value" })
15897
+ });
15898
+ const sloVerified = derived([sloValue], (vals) => sloCheckFn(vals[0]), {
15899
+ meta: baseMeta2("observability", { stage: "slo_verified" })
15900
+ });
15901
+ g.add("slo_value", sloValue);
15902
+ g.add("slo_verified", sloVerified);
15903
+ g.connect("correlate", "slo_value");
15904
+ g.connect("slo_value", "slo_verified");
15905
+ const weightValues = opts.weights ?? branches.map(() => 1);
15906
+ const signalNodes = branchNodes.map(
15907
+ (bn) => derived([bn], (vals) => vals[0] != null ? 1 : 0)
15908
+ );
15909
+ const weightNodes = weightValues.map((w) => state(w));
15910
+ for (let i = 0; i < signalNodes.length; i++) {
15911
+ g.add(`__signal_${i}`, signalNodes[i]);
15912
+ g.add(`__weight_${i}`, weightNodes[i]);
15913
+ }
15914
+ const alerts = scorer(
15915
+ signalNodes,
15916
+ weightNodes
15917
+ );
15918
+ g.add("alerts", alerts);
15919
+ const output = derived(
15920
+ [alerts, sloVerified],
15921
+ (vals) => ({
15922
+ scored: vals[0],
15923
+ slo: vals[1]
15924
+ }),
15925
+ {
15926
+ meta: baseMeta2("observability", { stage: "output" })
15927
+ }
15928
+ );
15929
+ g.add("output", output);
15930
+ g.connect("alerts", "output");
15931
+ g.connect("slo_verified", "output");
15932
+ const fbReentry = state(null, {
15933
+ meta: baseMeta2("observability", { stage: "feedback_reentry" })
15934
+ });
15935
+ g.add("feedback_reentry", fbReentry);
15936
+ const fbCondition = derived(
15937
+ [sloVerified],
15938
+ (vals) => {
15939
+ const result = vals[0];
15940
+ if (result && result.pass === false) return result;
15941
+ return null;
15942
+ },
15943
+ {
15944
+ meta: baseMeta2("observability", { stage: "feedback_condition" })
15945
+ }
15946
+ );
15947
+ g.add("feedback_condition", fbCondition);
15948
+ g.connect("slo_verified", "feedback_condition");
15949
+ feedback(g, "feedback_condition", "feedback_reentry", {
15950
+ maxIterations: opts.maxFeedbackIterations ?? 5
15951
+ });
15952
+ return g;
15953
+ }
15954
+ function issueTrackerGraph(name, opts) {
15955
+ const g = new Graph(name, opts);
15956
+ g.add("source", opts.source);
15957
+ let _issueCounter = 0;
15958
+ const defaultExtract = (raw) => ({
15959
+ id: `issue-${++_issueCounter}`,
15960
+ title: String(raw),
15961
+ severity: 1,
15962
+ source: "unknown",
15963
+ raw
15964
+ });
15965
+ const extractFn = opts.extract ?? defaultExtract;
15966
+ const extractNode = derived([opts.source], (vals) => extractFn(vals[0]), {
15967
+ meta: baseMeta2("issue_tracker", { stage: "extract" })
15968
+ });
15969
+ g.add("extract", extractNode);
15970
+ g.connect("source", "extract");
15971
+ const verifyFn = opts.verify ?? (() => ({ valid: true }));
15972
+ const verifyNode = derived(
15973
+ [extractNode],
15974
+ (vals) => {
15975
+ const issue = vals[0];
15976
+ return { issue, verification: verifyFn(issue) };
15977
+ },
15978
+ {
15979
+ meta: baseMeta2("issue_tracker", { stage: "verify" })
15980
+ }
15981
+ );
15982
+ g.add("verify", verifyNode);
15983
+ g.connect("extract", "verify");
15984
+ const knownPatterns = state([], {
15985
+ meta: baseMeta2("issue_tracker", { stage: "known_patterns" })
15986
+ });
15987
+ g.add("known_patterns", knownPatterns);
15988
+ const detectFn = opts.detectRegression ?? (() => ({ regression: false }));
15989
+ const regressionNode = derived(
15990
+ [extractNode, knownPatterns],
15991
+ (vals) => {
15992
+ const issue = vals[0];
15993
+ const known = vals[1];
15994
+ return { issue, regression: detectFn(issue, known) };
15995
+ },
15996
+ { meta: baseMeta2("issue_tracker", { stage: "regression" }) }
15997
+ );
15998
+ g.add("regression", regressionNode);
15999
+ g.connect("extract", "regression");
16000
+ g.connect("known_patterns", "regression");
16001
+ const severitySignal = derived([extractNode], (vals) => {
16002
+ const issue = vals[0];
16003
+ return issue?.severity ?? 0;
16004
+ });
16005
+ const regressionSignal = derived([regressionNode], (vals) => {
16006
+ const r = vals[0];
16007
+ return r?.regression ? 2 : 0;
16008
+ });
16009
+ g.add("__severity_signal", severitySignal);
16010
+ g.add("__regression_signal", regressionSignal);
16011
+ const severityWeight = state(1);
16012
+ const regressionWeight = state(1.5);
16013
+ g.add("__severity_weight", severityWeight);
16014
+ g.add("__regression_weight", regressionWeight);
16015
+ const priority = scorer([severitySignal, regressionSignal], [severityWeight, regressionWeight]);
16016
+ g.add("priority", priority);
16017
+ const output = derived(
16018
+ [verifyNode, regressionNode, priority],
16019
+ (vals) => ({
16020
+ verified: vals[0],
16021
+ regression: vals[1],
16022
+ priority: vals[2]
16023
+ }),
16024
+ { meta: baseMeta2("issue_tracker", { stage: "output" }) }
16025
+ );
16026
+ g.add("output", output);
16027
+ g.connect("verify", "output");
16028
+ g.connect("regression", "output");
16029
+ g.connect("priority", "output");
16030
+ const fbReentry = state(null, {
16031
+ meta: baseMeta2("issue_tracker", { stage: "feedback_reentry" })
16032
+ });
16033
+ g.add("feedback_reentry", fbReentry);
16034
+ const fbCondition = derived(
16035
+ [verifyNode],
16036
+ (vals) => {
16037
+ const result = vals[0];
16038
+ if (result) {
16039
+ const v = result.verification;
16040
+ if (v && v.valid === false) return result;
16041
+ }
16042
+ return null;
16043
+ },
16044
+ {
16045
+ meta: baseMeta2("issue_tracker", { stage: "feedback_condition" })
16046
+ }
16047
+ );
16048
+ g.add("feedback_condition", fbCondition);
16049
+ g.connect("verify", "feedback_condition");
16050
+ feedback(g, "feedback_condition", "feedback_reentry", {
16051
+ maxIterations: opts.maxFeedbackIterations ?? 3
16052
+ });
16053
+ return g;
16054
+ }
16055
+ function contentModerationGraph(name, opts) {
16056
+ const g = new Graph(name, opts);
16057
+ g.add("source", opts.source);
16058
+ const defaultClassify = (content) => ({
16059
+ label: "review",
16060
+ confidence: 0.5,
16061
+ original: content
16062
+ });
16063
+ const classifyFn = opts.classify ?? defaultClassify;
16064
+ const classifyNode = derived([opts.source], (vals) => classifyFn(vals[0]), {
16065
+ meta: baseMeta2("content_moderation", { stage: "classify" })
16066
+ });
16067
+ g.add("classify", classifyNode);
16068
+ g.connect("source", "classify");
16069
+ const strat = stratify("stratify", classifyNode, [
16070
+ { name: "safe", classify: (v) => v.label === "safe" },
16071
+ { name: "review", classify: (v) => v.label === "review" },
16072
+ { name: "block", classify: (v) => v.label === "block" }
16073
+ ]);
16074
+ g.mount("stratify", strat);
16075
+ const reviewLog = reactiveLog([], {
16076
+ name: "review_queue",
16077
+ maxSize: opts.maxQueueSize
16078
+ });
16079
+ g.add("review_queue", reviewLog.entries);
16080
+ let reviewBranch;
16081
+ try {
16082
+ reviewBranch = g.resolve("stratify::branch/review");
16083
+ } catch {
16084
+ reviewBranch = state(null);
16085
+ g.add("__review_fallback", reviewBranch);
16086
+ }
16087
+ const reviewAccumulator = effect([reviewBranch], (vals) => {
16088
+ const item = vals[0];
16089
+ if (item) {
16090
+ reviewLog.append(item);
16091
+ }
16092
+ });
16093
+ g.add("__review_accumulator", reviewAccumulator);
16094
+ keepalive5(reviewAccumulator);
16095
+ try {
16096
+ g.connect("stratify::branch/review", "__review_accumulator");
16097
+ } catch {
16098
+ }
16099
+ const policy2 = state(
16100
+ {},
16101
+ {
16102
+ meta: baseMeta2("content_moderation", {
16103
+ stage: "policy",
16104
+ access: "both",
16105
+ description: "Moderation policy rules \u2014 updated via feedback"
16106
+ })
16107
+ }
16108
+ );
16109
+ g.add("policy", policy2);
16110
+ const weights = opts.weights ?? [0.1, 1, 2];
16111
+ const confidenceSignal = derived([classifyNode], (vals) => {
16112
+ const r = vals[0];
16113
+ return r?.confidence ?? 0;
16114
+ });
16115
+ const severitySignal = derived([classifyNode], (vals) => {
16116
+ const r = vals[0];
16117
+ if (!r) return 0;
16118
+ return r.label === "block" ? weights[2] : r.label === "review" ? weights[1] : weights[0];
16119
+ });
16120
+ g.add("__confidence_signal", confidenceSignal);
16121
+ g.add("__severity_signal", severitySignal);
16122
+ const wConfidence = state(1);
16123
+ const wSeverity = state(1);
16124
+ g.add("__w_confidence", wConfidence);
16125
+ g.add("__w_severity", wSeverity);
16126
+ const priority = scorer([confidenceSignal, severitySignal], [wConfidence, wSeverity]);
16127
+ g.add("priority", priority);
16128
+ const output = derived(
16129
+ [classifyNode, priority],
16130
+ (vals) => ({
16131
+ classification: vals[0],
16132
+ priority: vals[1]
16133
+ }),
16134
+ { meta: baseMeta2("content_moderation", { stage: "output" }) }
16135
+ );
16136
+ g.add("output", output);
16137
+ g.connect("classify", "output");
16138
+ g.connect("priority", "output");
16139
+ const fbCondition = derived(
16140
+ [reviewLog.entries, policy2],
16141
+ (vals) => {
16142
+ const snap = vals[0];
16143
+ const entries = snap?.value?.entries;
16144
+ if (entries && entries.length > 0) {
16145
+ const latest = entries[entries.length - 1];
16146
+ if (latest && latest.falsePositive) {
16147
+ return latest;
16148
+ }
16149
+ }
16150
+ return null;
16151
+ },
16152
+ {
16153
+ meta: baseMeta2("content_moderation", { stage: "feedback_condition" })
16154
+ }
16155
+ );
16156
+ g.add("feedback_condition", fbCondition);
16157
+ feedback(g, "feedback_condition", "policy", {
16158
+ maxIterations: opts.maxFeedbackIterations ?? 5
16159
+ });
16160
+ return g;
16161
+ }
16162
+ function dataQualityGraph(name, opts) {
16163
+ const g = new Graph(name, opts);
16164
+ g.add("source", opts.source);
16165
+ const validateFn = opts.validate ?? ((record) => ({
16166
+ valid: true,
16167
+ errors: [],
16168
+ record
16169
+ }));
16170
+ const validateNode = derived(
16171
+ [opts.source],
16172
+ (vals) => vals[0] != null ? validateFn(vals[0]) : void 0,
16173
+ { meta: baseMeta2("data_quality", { stage: "validate" }) }
16174
+ );
16175
+ g.add("validate", validateNode);
16176
+ g.connect("source", "validate");
16177
+ const detectAnomalyFn = opts.detectAnomaly ?? ((record) => ({
16178
+ anomaly: false,
16179
+ score: 0,
16180
+ record
16181
+ }));
16182
+ const anomalyNode = derived(
16183
+ [opts.source],
16184
+ (vals) => vals[0] != null ? detectAnomalyFn(vals[0]) : void 0,
16185
+ { meta: baseMeta2("data_quality", { stage: "anomaly" }) }
16186
+ );
16187
+ g.add("anomaly", anomalyNode);
16188
+ g.connect("source", "anomaly");
16189
+ const baseline = state(null, {
16190
+ meta: baseMeta2("data_quality", {
16191
+ stage: "baseline",
16192
+ description: "Rolling baseline for drift detection"
16193
+ })
16194
+ });
16195
+ g.add("baseline", baseline);
16196
+ const baselineUpdater = effect([validateNode], (vals) => {
16197
+ const result = vals[0];
16198
+ if (result?.valid) {
16199
+ batch(() => {
16200
+ baseline.down([[DATA, result.record]]);
16201
+ });
16202
+ }
16203
+ });
16204
+ g.add("__baseline_updater", baselineUpdater);
16205
+ g.connect("validate", "__baseline_updater");
16206
+ keepalive5(baselineUpdater);
16207
+ const detectDriftFn = opts.detectDrift ?? (() => ({ drift: false }));
16208
+ const driftNode = derived(
16209
+ [opts.source, baseline],
16210
+ (vals) => detectDriftFn(vals[0], vals[1]),
16211
+ { meta: baseMeta2("data_quality", { stage: "drift" }) }
16212
+ );
16213
+ g.add("drift", driftNode);
16214
+ g.connect("source", "drift");
16215
+ g.connect("baseline", "drift");
16216
+ const suggestFn = opts.suggest ?? (() => null);
16217
+ const remediateNode = derived(
16218
+ [validateNode, anomalyNode],
16219
+ (vals) => suggestFn({
16220
+ validation: vals[0],
16221
+ anomaly: vals[1]
16222
+ }),
16223
+ { meta: baseMeta2("data_quality", { stage: "remediate" }) }
16224
+ );
16225
+ g.add("remediate", remediateNode);
16226
+ g.connect("validate", "remediate");
16227
+ g.connect("anomaly", "remediate");
16228
+ const output = derived(
16229
+ [validateNode, anomalyNode, driftNode, remediateNode],
16230
+ (vals) => ({
16231
+ validation: vals[0],
16232
+ anomaly: vals[1],
16233
+ drift: vals[2],
16234
+ remediation: vals[3]
16235
+ }),
16236
+ { meta: baseMeta2("data_quality", { stage: "output" }) }
16237
+ );
16238
+ g.add("output", output);
16239
+ g.connect("validate", "output");
16240
+ g.connect("anomaly", "output");
16241
+ g.connect("drift", "output");
16242
+ g.connect("remediate", "output");
16243
+ const validationRules = state([], {
16244
+ meta: baseMeta2("data_quality", { stage: "validation_rules" })
16245
+ });
16246
+ g.add("validation_rules", validationRules);
16247
+ const fbCondition = derived(
16248
+ [anomalyNode],
16249
+ (vals) => {
16250
+ const a = vals[0];
16251
+ if (a?.anomaly) return a;
16252
+ return null;
16253
+ },
16254
+ {
16255
+ meta: baseMeta2("data_quality", { stage: "feedback_condition" })
16256
+ }
16257
+ );
16258
+ g.add("feedback_condition", fbCondition);
16259
+ g.connect("anomaly", "feedback_condition");
16260
+ feedback(g, "feedback_condition", "validation_rules", {
16261
+ maxIterations: opts.maxFeedbackIterations ?? 3
16262
+ });
16263
+ return g;
16264
+ }
16265
+ function isTagged(value, tag) {
16266
+ if (value == null || typeof value !== "object") return false;
16267
+ const v = value;
16268
+ return v.type === tag || v.kind === tag;
16269
+ }
16270
+
16271
+ // src/patterns/graphspec.ts
16272
+ var graphspec_exports = {};
16273
+ __export(graphspec_exports, {
16274
+ compileSpec: () => compileSpec,
16275
+ decompileGraph: () => decompileGraph,
16276
+ extractFnFactory: () => extractFnFactory,
16277
+ extractSourceFactory: () => extractSourceFactory,
16278
+ generateCatalogPrompt: () => generateCatalogPrompt,
16279
+ isRichFnEntry: () => isRichFnEntry,
16280
+ isRichSourceEntry: () => isRichSourceEntry,
16281
+ llmCompose: () => llmCompose,
16282
+ llmRefine: () => llmRefine,
16283
+ specDiff: () => specDiff,
16284
+ validateSpec: () => validateSpec,
16285
+ validateSpecAgainstCatalog: () => validateSpecAgainstCatalog
16286
+ });
16287
+ function isRichFnEntry(entry) {
16288
+ return typeof entry === "object" && entry !== null && "factory" in entry;
16289
+ }
16290
+ function isRichSourceEntry(entry) {
16291
+ return typeof entry === "object" && entry !== null && "factory" in entry;
16292
+ }
16293
+ function extractFnFactory(entry) {
16294
+ return isRichFnEntry(entry) ? entry.factory : entry;
16295
+ }
16296
+ function extractSourceFactory(entry) {
16297
+ return isRichSourceEntry(entry) ? entry.factory : entry;
16298
+ }
16299
+ function generateCatalogPrompt(catalog) {
16300
+ const sections = [];
16301
+ if (catalog.fns) {
16302
+ const groups = /* @__PURE__ */ new Map();
16303
+ for (const [name, entry] of Object.entries(catalog.fns)) {
16304
+ const tag = isRichFnEntry(entry) ? entry.tags?.[0] ?? "Other" : "Other";
16305
+ if (!groups.has(tag)) groups.set(tag, []);
16306
+ groups.get(tag).push(formatFnEntry(name, entry));
16307
+ }
16308
+ for (const [tag, lines] of groups) {
16309
+ sections.push(`${tag}:
16310
+ ${lines.join("\n")}`);
16311
+ }
16312
+ }
16313
+ if (catalog.sources) {
16314
+ const lines = [];
16315
+ for (const [name, entry] of Object.entries(catalog.sources)) {
16316
+ lines.push(formatSourceEntry(name, entry));
16317
+ }
16318
+ if (lines.length > 0) {
16319
+ sections.push(`Sources:
16320
+ ${lines.join("\n")}`);
16321
+ }
16322
+ }
16323
+ return sections.join("\n\n");
16324
+ }
16325
+ function formatFnEntry(name, entry) {
16326
+ if (!isRichFnEntry(entry)) return `- ${name}`;
16327
+ let line = `- ${name}: ${entry.description}`;
16328
+ if (entry.configSchema) {
16329
+ const fields = Object.entries(entry.configSchema).map(([k, v]) => {
16330
+ let desc = `${k}: ${v.type}`;
16331
+ if (v.enum) desc += ` (${v.enum.join("|")})`;
16332
+ if (v.required === false) desc += "?";
16333
+ return desc;
16334
+ });
16335
+ line += `. Config: { ${fields.join(", ")} }`;
16336
+ }
16337
+ return line;
16338
+ }
16339
+ function formatSourceEntry(name, entry) {
16340
+ if (!isRichSourceEntry(entry)) return `- ${name}`;
16341
+ let line = `- ${name}: ${entry.description}`;
16342
+ if (entry.configSchema) {
16343
+ const fields = Object.entries(entry.configSchema).map(([k, v]) => {
16344
+ let desc = `${k}: ${v.type}`;
16345
+ if (v.required === false) desc += "?";
16346
+ return desc;
16347
+ });
16348
+ line += `. Config: { ${fields.join(", ")} }`;
16349
+ }
16350
+ return line;
16351
+ }
16352
+ function validateSpecAgainstCatalog(spec, catalog) {
16353
+ const errors = [];
16354
+ const fnNames = new Set(Object.keys(catalog.fns ?? {}));
16355
+ const sourceNames = new Set(Object.keys(catalog.sources ?? {}));
16356
+ for (const [nodeName, nodeRaw] of Object.entries(spec.nodes)) {
16357
+ if (nodeRaw.type === "template") continue;
16358
+ const node2 = nodeRaw;
16359
+ if (node2.fn && fnNames.size > 0 && !fnNames.has(node2.fn)) {
16360
+ if (sourceNames.has(node2.fn)) {
16361
+ errors.push(
16362
+ `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(", ")}`
16363
+ );
16364
+ } else {
16365
+ const suggestion = findClosest(node2.fn, fnNames);
16366
+ errors.push(
16367
+ `Node "${nodeName}": fn "${node2.fn}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
16368
+ );
16369
+ }
16370
+ }
16371
+ if (node2.source && sourceNames.size > 0 && !sourceNames.has(node2.source)) {
16372
+ if (fnNames.has(node2.source)) {
16373
+ errors.push(
16374
+ `Node "${nodeName}": source "${node2.source}" is a function, not a source. Use it as fn instead, or use a source from: ${[...sourceNames].join(", ")}`
16375
+ );
16376
+ } else {
16377
+ const suggestion = findClosest(node2.source, sourceNames);
16378
+ errors.push(
16379
+ `Node "${nodeName}": source "${node2.source}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
16380
+ );
16381
+ }
16382
+ }
16383
+ if (node2.fn && node2.config && catalog.fns?.[node2.fn]) {
16384
+ const entry = catalog.fns[node2.fn];
16385
+ if (isRichFnEntry(entry) && entry.configSchema) {
16386
+ for (const [field, schema] of Object.entries(entry.configSchema)) {
16387
+ if (schema.required !== false && !(field in node2.config)) {
16388
+ errors.push(`Node "${nodeName}": config missing required field "${field}"`);
16389
+ }
16390
+ if (field in node2.config && schema.enum) {
16391
+ const val = node2.config[field];
16392
+ if (!schema.enum.includes(val)) {
16393
+ errors.push(
16394
+ `Node "${nodeName}": config.${field} = ${JSON.stringify(val)}, expected one of: ${schema.enum.join(", ")}`
16395
+ );
16396
+ }
16397
+ }
16398
+ }
16399
+ }
16400
+ }
16401
+ }
16402
+ if (spec.templates) {
16403
+ for (const [tName, template] of Object.entries(spec.templates)) {
16404
+ for (const [nodeName, node2] of Object.entries(template.nodes)) {
16405
+ if (node2.fn && fnNames.size > 0 && !fnNames.has(node2.fn)) {
16406
+ const suggestion = findClosest(node2.fn, fnNames);
16407
+ errors.push(
16408
+ `Template "${tName}" node "${nodeName}": fn "${node2.fn}" not found in catalog` + (suggestion ? `. Did you mean "${suggestion}"?` : "")
16409
+ );
16410
+ }
16411
+ }
16412
+ }
16413
+ }
16414
+ return { valid: errors.length === 0, errors };
16415
+ }
16416
+ function findClosest(input, candidates) {
16417
+ let best = null;
16418
+ let bestDist = Infinity;
16419
+ const lower = input.toLowerCase();
16420
+ for (const c of candidates) {
16421
+ const dist = levenshtein(lower, c.toLowerCase());
16422
+ if (dist < bestDist && dist <= Math.max(3, Math.floor(input.length / 2))) {
16423
+ bestDist = dist;
16424
+ best = c;
16425
+ }
16426
+ }
16427
+ return best;
16428
+ }
16429
+ function levenshtein(a, b) {
16430
+ const m = a.length;
16431
+ const n = b.length;
16432
+ const dp = Array.from(
16433
+ { length: m + 1 },
16434
+ (_, i) => Array.from({ length: n + 1 }, (_2, j) => i === 0 ? j : j === 0 ? i : 0)
16435
+ );
16436
+ for (let i = 1; i <= m; i++) {
16437
+ for (let j = 1; j <= n; j++) {
16438
+ 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]);
16439
+ }
16440
+ }
16441
+ return dp[m][n];
16442
+ }
16443
+ var VALID_NODE_TYPES2 = /* @__PURE__ */ new Set([
16444
+ "state",
16445
+ "producer",
16446
+ "derived",
16447
+ "effect",
16448
+ "operator",
16449
+ "template"
16450
+ ]);
16451
+ var INNER_NODE_TYPES = /* @__PURE__ */ new Set(["state", "producer", "derived", "effect", "operator"]);
16452
+ function validateSpec(spec) {
16453
+ const errors = [];
16454
+ if (spec == null || typeof spec !== "object") {
16455
+ return { valid: false, errors: ["GraphSpec must be a non-null object"] };
16456
+ }
16457
+ const s = spec;
16458
+ if (typeof s.name !== "string" || s.name.length === 0) {
16459
+ errors.push("Missing or empty 'name' field");
16460
+ }
16461
+ if (s.nodes == null || typeof s.nodes !== "object" || Array.isArray(s.nodes)) {
16462
+ errors.push("Missing or invalid 'nodes' field (must be an object)");
16463
+ return { valid: false, errors };
16464
+ }
16465
+ const nodeNames = new Set(Object.keys(s.nodes));
16466
+ const nodeTypes = /* @__PURE__ */ new Map();
16467
+ const templateDefs = /* @__PURE__ */ new Map();
16468
+ if (s.templates != null && typeof s.templates === "object" && !Array.isArray(s.templates)) {
16469
+ for (const [tName, tRaw] of Object.entries(s.templates)) {
16470
+ if (tRaw != null && typeof tRaw === "object") {
16471
+ const t = tRaw;
16472
+ templateDefs.set(tName, {
16473
+ params: Array.isArray(t.params) ? t.params : []
16474
+ });
16475
+ }
16476
+ }
16477
+ }
16478
+ if (s.templates != null) {
16479
+ if (typeof s.templates !== "object" || Array.isArray(s.templates)) {
16480
+ errors.push("'templates' must be an object");
16481
+ } else {
16482
+ for (const [tName, tRaw] of Object.entries(s.templates)) {
16483
+ if (tRaw == null || typeof tRaw !== "object") {
16484
+ errors.push(`Template "${tName}": must be an object`);
16485
+ continue;
16486
+ }
16487
+ const t = tRaw;
16488
+ if (!Array.isArray(t.params)) {
16489
+ errors.push(`Template "${tName}": missing 'params' array`);
16490
+ }
16491
+ if (t.nodes == null || typeof t.nodes !== "object" || Array.isArray(t.nodes)) {
16492
+ errors.push(`Template "${tName}": missing or invalid 'nodes' object`);
16493
+ } else {
16494
+ const paramSet = new Set(Array.isArray(t.params) ? t.params : []);
16495
+ const innerNames = new Set(Object.keys(t.nodes));
16496
+ for (const [nName, nRaw] of Object.entries(t.nodes)) {
16497
+ if (nRaw == null || typeof nRaw !== "object") {
16498
+ errors.push(`Template "${tName}" node "${nName}": must be an object`);
16499
+ continue;
16500
+ }
16501
+ const n = nRaw;
16502
+ if (typeof n.type !== "string" || !INNER_NODE_TYPES.has(n.type)) {
16503
+ errors.push(`Template "${tName}" node "${nName}": invalid type`);
16504
+ }
16505
+ if (Array.isArray(n.deps)) {
16506
+ for (const dep of n.deps) {
16507
+ if (!innerNames.has(dep) && !paramSet.has(dep)) {
16508
+ errors.push(
16509
+ `Template "${tName}" node "${nName}": dep "${dep}" is not an inner node or param`
16510
+ );
16511
+ }
16512
+ }
16513
+ }
16514
+ }
16515
+ if (typeof t.output !== "string") {
16516
+ errors.push(`Template "${tName}": missing 'output' string`);
16517
+ } else if (!t.nodes[t.output]) {
16518
+ errors.push(`Template "${tName}": output "${t.output}" is not a declared node`);
16519
+ }
16520
+ }
16521
+ }
16522
+ }
16523
+ }
16524
+ for (const [name, raw] of Object.entries(s.nodes)) {
16525
+ if (raw == null || typeof raw !== "object") {
16526
+ errors.push(`Node "${name}": must be an object`);
16527
+ continue;
16528
+ }
16529
+ const n = raw;
16530
+ if (typeof n.type !== "string" || !VALID_NODE_TYPES2.has(n.type)) {
16531
+ errors.push(
16532
+ `Node "${name}": invalid type "${String(n.type)}" (expected: ${[...VALID_NODE_TYPES2].join(", ")})`
16533
+ );
16534
+ continue;
16535
+ }
16536
+ nodeTypes.set(name, n.type);
16537
+ if (n.type === "template") {
16538
+ if (typeof n.template !== "string" || !templateDefs.has(n.template)) {
16539
+ errors.push(`Node "${name}": template "${String(n.template)}" not found in templates`);
16540
+ } else {
16541
+ if (n.bind == null || typeof n.bind !== "object" || Array.isArray(n.bind)) {
16542
+ errors.push(`Node "${name}": template ref requires 'bind' object`);
16543
+ } else {
16544
+ const tmpl = templateDefs.get(n.template);
16545
+ const bind = n.bind;
16546
+ for (const param of tmpl.params) {
16547
+ if (!(param in bind)) {
16548
+ errors.push(
16549
+ `Node "${name}": template param "${param}" is not bound (template "${n.template}")`
16550
+ );
16551
+ }
16552
+ }
16553
+ for (const [, target] of Object.entries(bind)) {
16554
+ if (typeof target === "string" && !nodeNames.has(target)) {
16555
+ errors.push(
16556
+ `Node "${name}": bind target "${target}" does not reference an existing node`
16557
+ );
16558
+ }
16559
+ }
16560
+ }
16561
+ }
16562
+ } else {
16563
+ if (Array.isArray(n.deps)) {
16564
+ for (const dep of n.deps) {
16565
+ if (dep === name) {
16566
+ errors.push(`Node "${name}": self-referencing dep`);
16567
+ } else if (!nodeNames.has(dep)) {
16568
+ errors.push(`Node "${name}": dep "${dep}" does not reference an existing node`);
16569
+ }
16570
+ }
16571
+ }
16572
+ if ((n.type === "derived" || n.type === "effect" || n.type === "operator") && !Array.isArray(n.deps)) {
16573
+ errors.push(`Node "${name}": ${n.type} node should have a 'deps' array`);
16574
+ }
16575
+ }
16576
+ }
16577
+ if (s.feedback != null) {
16578
+ if (!Array.isArray(s.feedback)) {
16579
+ errors.push("'feedback' must be an array");
16580
+ } else {
16581
+ for (let i = 0; i < s.feedback.length; i++) {
16582
+ const edge = s.feedback[i];
16583
+ if (edge == null || typeof edge !== "object") {
16584
+ errors.push(`Feedback [${i}]: must be an object`);
16585
+ continue;
16586
+ }
16587
+ const e = edge;
16588
+ if (typeof e.from !== "string" || !nodeNames.has(e.from)) {
16589
+ errors.push(
16590
+ `Feedback [${i}]: 'from' "${String(e.from)}" does not reference an existing node`
16591
+ );
16592
+ }
16593
+ if (typeof e.from === "string" && e.from === e.to) {
16594
+ errors.push(`Feedback [${i}]: 'from' and 'to' must be different nodes`);
16595
+ }
16596
+ if (typeof e.to !== "string" || !nodeNames.has(e.to)) {
16597
+ errors.push(
16598
+ `Feedback [${i}]: 'to' "${String(e.to)}" does not reference an existing node`
16599
+ );
16600
+ } else if (typeof e.to === "string" && nodeTypes.get(e.to) !== "state") {
16601
+ errors.push(
16602
+ `Feedback [${i}]: 'to' node "${e.to}" must be a state node (got "${nodeTypes.get(e.to) ?? "unknown"}")`
16603
+ );
16604
+ }
16605
+ }
16606
+ }
16607
+ }
16608
+ return { valid: errors.length === 0, errors };
16609
+ }
16610
+ function compileSpec(spec, opts) {
16611
+ const validation = validateSpec(spec);
16612
+ if (!validation.valid) {
16613
+ throw new Error(`compileSpec: invalid GraphSpec:
16614
+ ${validation.errors.join("\n")}`);
16615
+ }
16616
+ const catalog = opts?.catalog ?? {};
16617
+ const g = new Graph(spec.name);
16618
+ const templates = spec.templates ?? {};
16619
+ const catalogValidation = validateSpecAgainstCatalog(spec, catalog);
16620
+ if (!catalogValidation.valid) {
16621
+ throw new Error(
16622
+ `compileSpec: catalog validation errors:
16623
+ ${catalogValidation.errors.join("\n")}`
16624
+ );
16625
+ }
16626
+ const resolveFn = (fnName) => {
16627
+ const entry = catalog.fns?.[fnName];
16628
+ return entry ? extractFnFactory(entry) : void 0;
16629
+ };
16630
+ const resolveSource = (sourceName) => {
16631
+ const entry = catalog.sources?.[sourceName];
16632
+ return entry ? extractSourceFactory(entry) : void 0;
16633
+ };
16634
+ const created = /* @__PURE__ */ new Map();
16635
+ const deferred = [];
16636
+ for (const [name, raw] of Object.entries(spec.nodes)) {
16637
+ if (raw.type === "template") continue;
16638
+ const n = raw;
16639
+ if (n.type === "state") {
16640
+ const nd = state(n.initial, {
16641
+ name,
16642
+ meta: n.meta ? { ...n.meta } : void 0
16643
+ });
16644
+ g.add(name, nd);
16645
+ created.set(name, nd);
16646
+ } else if (n.type === "producer") {
16647
+ const sourceFactory = n.source ? resolveSource(n.source) : void 0;
16648
+ const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
16649
+ if (sourceFactory) {
16650
+ const nd = sourceFactory(n.config ?? {});
16651
+ g.add(name, nd);
16652
+ created.set(name, nd);
16653
+ } else if (fnFactory) {
16654
+ const nd = fnFactory([], n.config ?? {});
16655
+ g.add(name, nd);
16656
+ created.set(name, nd);
16657
+ } else {
16658
+ const nd = producer(() => {
16659
+ }, {
16660
+ name,
16661
+ meta: { ...n.meta, _specFn: n.fn, _specSource: n.source }
16662
+ });
16663
+ g.add(name, nd);
16664
+ created.set(name, nd);
16665
+ }
16666
+ } else {
16667
+ deferred.push([name, n]);
16668
+ }
16669
+ }
16670
+ let progressed = true;
16671
+ const pending = new Map(deferred);
16672
+ while (pending.size > 0 && progressed) {
16673
+ progressed = false;
16674
+ for (const [name, n] of [...pending.entries()]) {
16675
+ const deps = n.deps ?? [];
16676
+ if (!deps.every((dep) => created.has(dep))) continue;
16677
+ const resolvedDeps = deps.map((dep) => created.get(dep));
16678
+ const fnFactory = n.fn ? resolveFn(n.fn) : void 0;
16679
+ let nd;
16680
+ if (fnFactory) {
16681
+ nd = fnFactory(resolvedDeps, n.config ?? {});
16682
+ } else if (n.type === "effect") {
16683
+ nd = effect(resolvedDeps, () => {
16684
+ });
16685
+ } else {
16686
+ nd = derived(resolvedDeps, (vals) => vals[0]);
16687
+ }
16688
+ g.add(name, nd);
16689
+ created.set(name, nd);
16690
+ pending.delete(name);
16691
+ progressed = true;
16692
+ }
16693
+ }
16694
+ if (pending.size > 0) {
16695
+ const unresolved = [...pending.keys()].sort().join(", ");
16696
+ throw new Error(`compileSpec: unresolvable deps for nodes: ${unresolved}`);
16697
+ }
16698
+ for (const [name, raw] of Object.entries(spec.nodes)) {
16699
+ if (raw.type !== "template") continue;
16700
+ const ref = raw;
16701
+ const tmpl = templates[ref.template];
16702
+ const sub = new Graph(name);
16703
+ const subCreated = /* @__PURE__ */ new Map();
16704
+ const subDeferred = [];
16705
+ for (const [nName, nSpec] of Object.entries(tmpl.nodes)) {
16706
+ const resolvedDeps = (nSpec.deps ?? []).map((dep) => {
16707
+ if (dep.startsWith("$") && ref.bind[dep]) {
16708
+ return ref.bind[dep];
16709
+ }
16710
+ return dep;
16711
+ });
16712
+ const specWithResolvedDeps = { ...nSpec, deps: resolvedDeps };
16713
+ if (nSpec.type === "state") {
16714
+ const nd = state(nSpec.initial, {
16715
+ name: nName,
16716
+ meta: nSpec.meta ? { ...nSpec.meta } : void 0
16717
+ });
16718
+ sub.add(nName, nd);
16719
+ subCreated.set(nName, nd);
16720
+ } else if (nSpec.type === "producer") {
16721
+ const sourceFactory = nSpec.source ? resolveSource(nSpec.source) : void 0;
16722
+ const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
16723
+ if (sourceFactory) {
16724
+ const nd = sourceFactory(nSpec.config ?? {});
16725
+ sub.add(nName, nd);
16726
+ subCreated.set(nName, nd);
16727
+ } else if (fnFactory) {
16728
+ const nd = fnFactory([], nSpec.config ?? {});
16729
+ sub.add(nName, nd);
16730
+ subCreated.set(nName, nd);
16731
+ } else {
16732
+ const nd = producer(() => {
16733
+ }, {
16734
+ name: nName,
16735
+ meta: { ...nSpec.meta, _specFn: nSpec.fn, _specSource: nSpec.source }
16736
+ });
16737
+ sub.add(nName, nd);
16738
+ subCreated.set(nName, nd);
16739
+ }
16740
+ } else {
16741
+ subDeferred.push([nName, specWithResolvedDeps]);
16742
+ }
16743
+ }
16744
+ let subProgressed = true;
16745
+ const subPending = new Map(subDeferred);
16746
+ while (subPending.size > 0 && subProgressed) {
16747
+ subProgressed = false;
16748
+ for (const [nName, nSpec] of [...subPending.entries()]) {
16749
+ const deps = nSpec.deps ?? [];
16750
+ const allReady = deps.every((dep) => subCreated.has(dep) || created.has(dep));
16751
+ if (!allReady) continue;
16752
+ const resolvedDeps = deps.map((dep) => subCreated.get(dep) ?? created.get(dep));
16753
+ const fnFactory = nSpec.fn ? resolveFn(nSpec.fn) : void 0;
16754
+ let nd;
16755
+ if (fnFactory) {
16756
+ nd = fnFactory(resolvedDeps, nSpec.config ?? {});
16757
+ } else if (nSpec.type === "effect") {
16758
+ nd = effect(resolvedDeps, () => {
16759
+ });
16760
+ } else {
16761
+ nd = derived(resolvedDeps, (vals) => vals[0]);
16762
+ }
16763
+ sub.add(nName, nd);
16764
+ subCreated.set(nName, nd);
16765
+ subPending.delete(nName);
16766
+ subProgressed = true;
16767
+ }
16768
+ }
16769
+ if (subPending.size > 0) {
16770
+ const unresolved = [...subPending.keys()].sort().join(", ");
16771
+ throw new Error(
16772
+ `compileSpec: template "${ref.template}" has unresolvable deps: ${unresolved}`
16773
+ );
16774
+ }
16775
+ g.mount(name, sub);
16776
+ const outputPath = `${name}::${tmpl.output}`;
16777
+ created.set(name, g.resolve(outputPath));
16778
+ try {
16779
+ const outputNode = g.resolve(outputPath);
16780
+ outputNode.meta._templateName?.down([[DATA, ref.template]]);
16781
+ outputNode.meta._templateBind?.down([[DATA, ref.bind]]);
16782
+ } catch {
16783
+ }
16784
+ }
16785
+ for (const [name, raw] of Object.entries(spec.nodes)) {
16786
+ if (raw.type === "template") continue;
16787
+ const n = raw;
16788
+ for (const dep of n.deps ?? []) {
16789
+ try {
16790
+ g.connect(dep, name);
16791
+ } catch (err) {
16792
+ const msg = err instanceof Error ? err.message : "";
16793
+ if (!msg.includes("constructor deps") && !msg.includes("already")) {
16794
+ throw err;
16795
+ }
16796
+ }
16797
+ }
16798
+ }
16799
+ for (const fb of spec.feedback ?? []) {
16800
+ feedback(g, fb.from, fb.to, {
16801
+ maxIterations: fb.maxIterations
16802
+ });
16803
+ }
16804
+ return g;
16805
+ }
16806
+ var INTERNAL_META_KEYS = /* @__PURE__ */ new Set([
16807
+ "reduction",
16808
+ "reduction_type",
16809
+ "_specFn",
16810
+ "_specSource",
16811
+ "_templateName",
16812
+ "_templateBind",
16813
+ "feedbackFrom",
16814
+ "feedbackTo",
16815
+ "_internal"
16816
+ ]);
16817
+ function decompileGraph(graph) {
16818
+ const desc = graph.describe({ detail: "standard" });
16819
+ const nodes = {};
16820
+ const feedbackEdges = [];
16821
+ const metaSegment = `::${GRAPH_META_SEGMENT}::`;
16822
+ const feedbackCounterPattern = /^__feedback_(?!effect_)(.+)$/;
16823
+ const feedbackConditions = /* @__PURE__ */ new Set();
16824
+ for (const path of Object.keys(desc.nodes)) {
16825
+ if (path.includes(metaSegment)) continue;
16826
+ const match = feedbackCounterPattern.exec(path);
16827
+ if (match) {
16828
+ feedbackConditions.add(match[1]);
16829
+ const meta = desc.nodes[path]?.meta;
16830
+ if (meta?.feedbackFrom && meta?.feedbackTo) {
16831
+ feedbackEdges.push({
16832
+ from: meta.feedbackFrom,
16833
+ to: meta.feedbackTo,
16834
+ ...meta.maxIterations ? { maxIterations: meta.maxIterations } : {}
16835
+ });
16836
+ }
16837
+ }
16838
+ }
16839
+ for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
16840
+ if (path.includes(metaSegment)) continue;
16841
+ if (feedbackCounterPattern.test(path)) continue;
16842
+ if (nodeDesc.meta?._internal) continue;
16843
+ if (path.startsWith("__feedback_effect_")) continue;
16844
+ if (path.startsWith("__bridge_")) continue;
16845
+ if (path.includes("::")) continue;
16846
+ const specNode = {
16847
+ type: nodeDesc.type
16848
+ };
16849
+ if (nodeDesc.deps.length > 0) {
16850
+ specNode.deps = nodeDesc.deps.filter((d) => !d.includes("::"));
16851
+ }
16852
+ if (nodeDesc.type === "state" && nodeDesc.value !== void 0) {
16853
+ specNode.initial = nodeDesc.value;
16854
+ }
16855
+ if (nodeDesc.meta && Object.keys(nodeDesc.meta).length > 0) {
16856
+ const meta = {};
16857
+ for (const [k, v] of Object.entries(nodeDesc.meta)) {
16858
+ if (!INTERNAL_META_KEYS.has(k)) meta[k] = v;
16859
+ }
16860
+ if (Object.keys(meta).length > 0) {
16861
+ specNode.meta = meta;
16862
+ }
16863
+ }
16864
+ nodes[path] = specNode;
16865
+ }
16866
+ const templates = {};
16867
+ const templateRefs = {};
16868
+ const metaDetectedSubgraphs = /* @__PURE__ */ new Set();
16869
+ for (const subName of desc.subgraphs) {
16870
+ const prefix = `${subName}::`;
16871
+ for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
16872
+ if (!path.startsWith(prefix)) continue;
16873
+ if (path.includes(metaSegment)) continue;
16874
+ const meta = nodeDesc.meta;
16875
+ if (meta?._templateName && meta?._templateBind) {
16876
+ const templateName = meta._templateName;
16877
+ const bind = meta._templateBind;
16878
+ if (!templates[templateName]) {
16879
+ const tmplNodes = {};
16880
+ const tmplInnerNames = /* @__PURE__ */ new Set();
16881
+ const tmplPrefix = `${subName}::`;
16882
+ for (const [p, nd] of Object.entries(desc.nodes)) {
16883
+ if (!p.startsWith(tmplPrefix) || p.includes(metaSegment)) continue;
16884
+ const localName = p.slice(tmplPrefix.length);
16885
+ if (localName.includes("::")) continue;
16886
+ tmplInnerNames.add(localName);
16887
+ tmplNodes[localName] = {
16888
+ type: nd.type,
16889
+ ...nd.deps.length > 0 ? {
16890
+ deps: nd.deps.map(
16891
+ (d) => d.startsWith(tmplPrefix) ? d.slice(tmplPrefix.length) : d
16892
+ )
16893
+ } : {}
16894
+ };
16895
+ }
16896
+ const tmplParams = [];
16897
+ const tmplParamMap = /* @__PURE__ */ new Map();
16898
+ for (const n of Object.values(tmplNodes)) {
16899
+ for (const dep of n.deps ?? []) {
16900
+ if (!tmplInnerNames.has(dep) && !tmplParamMap.has(dep)) {
16901
+ const param = `$${dep}`;
16902
+ tmplParams.push(param);
16903
+ tmplParamMap.set(dep, param);
16904
+ }
16905
+ }
16906
+ }
16907
+ for (const n of Object.values(tmplNodes)) {
16908
+ if (n.deps) n.deps = n.deps.map((d) => tmplParamMap.get(d) ?? d);
16909
+ }
16910
+ const depended = /* @__PURE__ */ new Set();
16911
+ for (const n of Object.values(tmplNodes)) {
16912
+ for (const dep of n.deps ?? []) {
16913
+ if (tmplInnerNames.has(dep)) depended.add(dep);
16914
+ }
16915
+ }
16916
+ const outputCandidates = [...tmplInnerNames].filter((n) => !depended.has(n));
16917
+ const tmplOutput = outputCandidates[0] ?? [...tmplInnerNames].pop();
16918
+ templates[templateName] = { params: tmplParams, nodes: tmplNodes, output: tmplOutput };
16919
+ }
16920
+ delete nodes[subName];
16921
+ templateRefs[subName] = { type: "template", template: templateName, bind };
16922
+ metaDetectedSubgraphs.add(subName);
16923
+ break;
16924
+ }
16925
+ }
16926
+ }
16927
+ const structureMap = /* @__PURE__ */ new Map();
16928
+ for (const subName of desc.subgraphs) {
16929
+ if (metaDetectedSubgraphs.has(subName)) continue;
16930
+ const subNodes = {};
16931
+ const prefix = `${subName}::`;
16932
+ for (const [path, nodeDesc] of Object.entries(desc.nodes)) {
16933
+ if (path.includes(metaSegment)) continue;
16934
+ if (!path.startsWith(prefix)) continue;
16935
+ const localName = path.slice(prefix.length);
16936
+ if (localName.includes("::")) continue;
16937
+ subNodes[localName] = {
16938
+ type: nodeDesc.type,
16939
+ ...nodeDesc.deps.length > 0 ? {
16940
+ deps: nodeDesc.deps.map((d) => d.startsWith(prefix) ? d.slice(prefix.length) : d)
16941
+ } : {}
16942
+ };
16943
+ }
16944
+ const fingerprint = JSON.stringify(
16945
+ Object.fromEntries(
16946
+ Object.entries(subNodes).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => [k, { type: v.type, deps: v.deps ?? [] }])
16947
+ )
16948
+ );
16949
+ if (!structureMap.has(fingerprint)) {
16950
+ structureMap.set(fingerprint, []);
16951
+ }
16952
+ structureMap.get(fingerprint).push({ name: subName, nodes: subNodes });
16953
+ }
16954
+ for (const [, group] of structureMap) {
16955
+ if (group.length < 2) continue;
16956
+ const templateName = `${group[0].name}_template`;
16957
+ const refNodes = group[0].nodes;
16958
+ const innerNames = new Set(Object.keys(refNodes));
16959
+ const params = [];
16960
+ const baseParamMap = /* @__PURE__ */ new Map();
16961
+ for (const n of Object.values(refNodes)) {
16962
+ for (const dep of n.deps ?? []) {
16963
+ if (!innerNames.has(dep) && !baseParamMap.has(dep)) {
16964
+ const param = `$${dep}`;
16965
+ params.push(param);
16966
+ baseParamMap.set(dep, param);
16967
+ }
16968
+ }
16969
+ }
16970
+ const depended = /* @__PURE__ */ new Set();
16971
+ for (const n of Object.values(refNodes)) {
16972
+ for (const dep of n.deps ?? []) {
16973
+ if (innerNames.has(dep)) depended.add(dep);
16974
+ }
16975
+ }
16976
+ const outputCandidates = [...innerNames].filter((n) => !depended.has(n));
16977
+ const output = outputCandidates[0] ?? [...innerNames].pop();
16978
+ const tmplNodes = {};
16979
+ for (const [nName, nSpec] of Object.entries(refNodes)) {
16980
+ tmplNodes[nName] = {
16981
+ ...nSpec,
16982
+ deps: nSpec.deps?.map((d) => baseParamMap.get(d) ?? d)
16983
+ };
16984
+ }
16985
+ templates[templateName] = { params, nodes: tmplNodes, output };
16986
+ for (const member of group) {
16987
+ delete nodes[member.name];
16988
+ const memberBind = {};
16989
+ const memberInnerNames = new Set(Object.keys(member.nodes));
16990
+ for (const n of Object.values(member.nodes)) {
16991
+ for (const dep of n.deps ?? []) {
16992
+ if (!memberInnerNames.has(dep)) {
16993
+ const param = baseParamMap.get(dep) ?? `$${dep}`;
16994
+ memberBind[param] = dep;
16995
+ }
16996
+ }
16997
+ }
16998
+ templateRefs[member.name] = {
16999
+ type: "template",
17000
+ template: templateName,
17001
+ bind: memberBind
17002
+ };
17003
+ }
17004
+ }
17005
+ const allNodes = {
17006
+ ...nodes,
17007
+ ...templateRefs
17008
+ };
17009
+ const result = { name: desc.name, nodes: allNodes };
17010
+ if (Object.keys(templates).length > 0) result.templates = templates;
17011
+ if (feedbackEdges.length > 0) result.feedback = feedbackEdges;
17012
+ return result;
17013
+ }
17014
+ function specDiff(specA, specB) {
17015
+ const entries = [];
17016
+ if (specA.name !== specB.name) {
17017
+ entries.push({
17018
+ type: "changed",
17019
+ path: "name",
17020
+ detail: `"${specA.name}" \u2192 "${specB.name}"`
17021
+ });
17022
+ }
17023
+ const nodesA = new Set(Object.keys(specA.nodes));
17024
+ const nodesB = new Set(Object.keys(specB.nodes));
17025
+ for (const name of nodesB) {
17026
+ if (!nodesA.has(name)) {
17027
+ const n = specB.nodes[name];
17028
+ entries.push({
17029
+ type: "added",
17030
+ path: `nodes.${name}`,
17031
+ detail: `type: ${n.type}`
17032
+ });
17033
+ }
17034
+ }
17035
+ for (const name of nodesA) {
17036
+ if (!nodesB.has(name)) {
17037
+ entries.push({ type: "removed", path: `nodes.${name}` });
17038
+ }
17039
+ }
17040
+ for (const name of nodesA) {
17041
+ if (!nodesB.has(name)) continue;
17042
+ const a = specA.nodes[name];
17043
+ const b = specB.nodes[name];
17044
+ if (JSON.stringify(a) !== JSON.stringify(b)) {
17045
+ const details = [];
17046
+ if (a.type !== b.type) details.push(`type: ${a.type} \u2192 ${b.type}`);
17047
+ if (JSON.stringify(a.deps) !== JSON.stringify(b.deps)) {
17048
+ details.push("deps changed");
17049
+ }
17050
+ if (a.fn !== b.fn) {
17051
+ details.push(`fn: ${a.fn} \u2192 ${b.fn}`);
17052
+ }
17053
+ if (JSON.stringify(a.config) !== JSON.stringify(b.config)) {
17054
+ details.push("config changed");
17055
+ }
17056
+ entries.push({
17057
+ type: "changed",
17058
+ path: `nodes.${name}`,
17059
+ detail: details.join("; ") || "modified"
17060
+ });
17061
+ }
17062
+ }
17063
+ const tmplA = specA.templates ?? {};
17064
+ const tmplB = specB.templates ?? {};
17065
+ const tmplNamesA = new Set(Object.keys(tmplA));
17066
+ const tmplNamesB = new Set(Object.keys(tmplB));
17067
+ for (const name of tmplNamesB) {
17068
+ if (!tmplNamesA.has(name)) {
17069
+ entries.push({ type: "added", path: `templates.${name}` });
17070
+ }
17071
+ }
17072
+ for (const name of tmplNamesA) {
17073
+ if (!tmplNamesB.has(name)) {
17074
+ entries.push({ type: "removed", path: `templates.${name}` });
17075
+ }
17076
+ }
17077
+ for (const name of tmplNamesA) {
17078
+ if (!tmplNamesB.has(name)) continue;
17079
+ if (JSON.stringify(tmplA[name]) !== JSON.stringify(tmplB[name])) {
17080
+ entries.push({
17081
+ type: "changed",
17082
+ path: `templates.${name}`,
17083
+ detail: "template definition changed"
17084
+ });
17085
+ }
17086
+ }
17087
+ const fbA = specA.feedback ?? [];
17088
+ const fbB = specB.feedback ?? [];
17089
+ const fbKeyA = new Set(fbA.map((e) => `${e.from}->${e.to}`));
17090
+ const fbKeyB = new Set(fbB.map((e) => `${e.from}->${e.to}`));
17091
+ for (const fb of fbB) {
17092
+ const key = `${fb.from}->${fb.to}`;
17093
+ if (!fbKeyA.has(key)) {
17094
+ entries.push({
17095
+ type: "added",
17096
+ path: `feedback.${key}`,
17097
+ detail: `maxIterations: ${fb.maxIterations ?? 10}`
17098
+ });
17099
+ }
17100
+ }
17101
+ for (const fb of fbA) {
17102
+ const key = `${fb.from}->${fb.to}`;
17103
+ if (!fbKeyB.has(key)) {
17104
+ entries.push({ type: "removed", path: `feedback.${key}` });
17105
+ }
17106
+ }
17107
+ for (const fb of fbA) {
17108
+ const key = `${fb.from}->${fb.to}`;
17109
+ const counterpart = fbB.find((b) => b.from === fb.from && b.to === fb.to);
17110
+ if (counterpart && JSON.stringify(fb) !== JSON.stringify(counterpart)) {
17111
+ entries.push({
17112
+ type: "changed",
17113
+ path: `feedback.${key}`,
17114
+ detail: `maxIterations: ${fb.maxIterations ?? 10} \u2192 ${counterpart.maxIterations ?? 10}`
17115
+ });
17116
+ }
17117
+ }
17118
+ const added = entries.filter((e) => e.type === "added").length;
17119
+ const removed = entries.filter((e) => e.type === "removed").length;
17120
+ const changed = entries.filter((e) => e.type === "changed").length;
17121
+ const parts = [];
17122
+ if (added) parts.push(`${added} added`);
17123
+ if (removed) parts.push(`${removed} removed`);
17124
+ if (changed) parts.push(`${changed} changed`);
17125
+ const summary = parts.length > 0 ? parts.join(", ") : "no changes";
17126
+ return { entries, summary };
17127
+ }
17128
+ var LLM_COMPOSE_SYSTEM_PROMPT = `You are a graph architect for GraphReFly, a reactive graph protocol.
17129
+
17130
+ Given a natural-language description, produce a JSON GraphSpec with this structure:
17131
+
17132
+ {
17133
+ "name": "<graph_name>",
17134
+ "nodes": {
17135
+ "<node_name>": {
17136
+ "type": "state" | "derived" | "producer" | "effect" | "operator",
17137
+ "deps": ["<dep_node_name>", ...],
17138
+ "fn": "<catalog_function_name>",
17139
+ "source": "<catalog_source_name>",
17140
+ "config": { ... },
17141
+ "initial": <value>,
17142
+ "meta": { "description": "<purpose>" }
17143
+ },
17144
+ "<template_instance>": {
17145
+ "type": "template",
17146
+ "template": "<template_name>",
17147
+ "bind": { "$param": "node_name" }
17148
+ }
17149
+ },
17150
+ "templates": {
17151
+ "<template_name>": {
17152
+ "params": ["$param1", "$param2"],
17153
+ "nodes": { ... },
17154
+ "output": "<output_node>"
17155
+ }
17156
+ },
17157
+ "feedback": [
17158
+ { "from": "<condition_node>", "to": "<state_node>", "maxIterations": 10 }
17159
+ ]
17160
+ }
17161
+
17162
+ Rules:
17163
+ - "state" nodes hold user/LLM-writable values (knobs). Use "initial" for default values.
17164
+ - "derived" nodes compute from deps using a named "fn".
17165
+ - "effect" nodes produce side effects from deps.
17166
+ - "producer" nodes generate values from a named "source".
17167
+ - Use "templates" when the same subgraph pattern repeats (e.g., per-source resilience).
17168
+ - Use "feedback" for bounded cycles where a derived value writes back to a state node.
17169
+ - meta.description is required for every node.
17170
+ - Return ONLY valid JSON, no markdown fences or commentary.`;
17171
+ function stripFences2(text) {
17172
+ const match = text.match(/^```(?:json)?\s*([\s\S]*?)\s*```[\s\S]*$/);
17173
+ return match ? match[1] : text;
17174
+ }
17175
+ async function llmCompose(problem, adapter, opts) {
17176
+ let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
17177
+ const catalogPrompt = opts?.catalogDescription ?? (opts?.catalog ? generateCatalogPrompt(opts.catalog) : void 0);
17178
+ if (catalogPrompt) {
17179
+ systemPrompt += `
17180
+
17181
+ Available catalog (use ONLY these names):
17182
+ ${catalogPrompt}`;
17183
+ }
17184
+ if (opts?.systemPromptExtra) {
17185
+ systemPrompt += `
17186
+
17187
+ ${opts.systemPromptExtra}`;
17188
+ }
17189
+ const messages = [
17190
+ { role: "system", content: systemPrompt },
17191
+ { role: "user", content: problem }
17192
+ ];
17193
+ const rawResult = adapter.invoke(messages, {
17194
+ model: opts?.model,
17195
+ temperature: opts?.temperature ?? 0,
17196
+ maxTokens: opts?.maxTokens
17197
+ });
17198
+ const response = await rawResult;
17199
+ let content = response.content.trim();
17200
+ if (content.startsWith("```")) {
17201
+ content = stripFences2(content);
17202
+ }
17203
+ let parsed;
17204
+ try {
17205
+ parsed = JSON.parse(content);
17206
+ } catch {
17207
+ throw new Error(`llmCompose: LLM response is not valid JSON: ${content.slice(0, 200)}`);
17208
+ }
17209
+ const validation = validateSpec(parsed);
17210
+ if (!validation.valid) {
17211
+ throw new Error(`llmCompose: invalid GraphSpec:
17212
+ ${validation.errors.join("\n")}`);
17213
+ }
17214
+ let spec = parsed;
17215
+ if (opts?.catalog) {
17216
+ const maxRefine = opts.maxAutoRefine ?? 0;
17217
+ for (let attempt = 0; attempt <= maxRefine; attempt++) {
17218
+ const catalogValidation = validateSpecAgainstCatalog(spec, opts.catalog);
17219
+ if (catalogValidation.valid) break;
17220
+ if (attempt === maxRefine) {
17221
+ throw new Error(
17222
+ `llmCompose: catalog validation failed after ${maxRefine} refine attempts:
17223
+ ${catalogValidation.errors.join("\n")}`
17224
+ );
17225
+ }
17226
+ spec = await llmRefine(
17227
+ spec,
17228
+ `Fix these catalog errors:
17229
+ ${catalogValidation.errors.join("\n")}
17230
+
17231
+ Use ONLY functions and sources from the catalog.`,
17232
+ adapter,
17233
+ { ...opts, catalogDescription: catalogPrompt }
17234
+ );
17235
+ }
17236
+ }
17237
+ return spec;
17238
+ }
17239
+ async function llmRefine(currentSpec, feedback2, adapter, opts) {
17240
+ let systemPrompt = LLM_COMPOSE_SYSTEM_PROMPT;
17241
+ if (opts?.catalogDescription) {
17242
+ systemPrompt += `
17243
+
17244
+ Available catalog:
17245
+ ${opts.catalogDescription}`;
17246
+ }
17247
+ if (opts?.systemPromptExtra) {
17248
+ systemPrompt += `
17249
+
17250
+ ${opts.systemPromptExtra}`;
17251
+ }
17252
+ const messages = [
17253
+ { role: "system", content: systemPrompt },
17254
+ {
17255
+ role: "user",
17256
+ content: `Current GraphSpec:
17257
+ ${JSON.stringify(currentSpec, null, 2)}
17258
+
17259
+ Modification request: ${feedback2}
17260
+
17261
+ Return the complete modified GraphSpec as JSON.`
17262
+ }
17263
+ ];
17264
+ const rawResult = adapter.invoke(messages, {
17265
+ model: opts?.model,
17266
+ temperature: opts?.temperature ?? 0,
17267
+ maxTokens: opts?.maxTokens
17268
+ });
17269
+ const response = await rawResult;
17270
+ let content = response.content.trim();
17271
+ if (content.startsWith("```")) {
17272
+ content = stripFences2(content);
17273
+ }
17274
+ let parsed;
17275
+ try {
17276
+ parsed = JSON.parse(content);
17277
+ } catch {
17278
+ throw new Error(`llmRefine: LLM response is not valid JSON: ${content.slice(0, 200)}`);
17279
+ }
17280
+ const validation = validateSpec(parsed);
17281
+ if (!validation.valid) {
17282
+ throw new Error(`llmRefine: invalid GraphSpec:
17283
+ ${validation.errors.join("\n")}`);
17284
+ }
17285
+ return parsed;
17286
+ }
17287
+
17288
+ // src/patterns/messaging.ts
17289
+ var messaging_exports = {};
17290
+ __export(messaging_exports, {
17291
+ JobFlowGraph: () => JobFlowGraph,
17292
+ JobQueueGraph: () => JobQueueGraph,
17293
+ SubscriptionGraph: () => SubscriptionGraph,
17294
+ TopicBridgeGraph: () => TopicBridgeGraph,
17295
+ TopicGraph: () => TopicGraph,
17296
+ jobFlow: () => jobFlow,
17297
+ jobQueue: () => jobQueue,
17298
+ subscription: () => subscription,
17299
+ topic: () => topic,
17300
+ topicBridge: () => topicBridge
17301
+ });
17302
+ var DEFAULT_MAX_PER_PUMP = 2147483647;
17303
+ function requireNonNegativeInt(value, label) {
17304
+ if (!Number.isFinite(value) || !Number.isInteger(value) || value < 0) {
17305
+ throw new Error(`${label} must be a non-negative integer`);
17306
+ }
17307
+ return value;
17308
+ }
17309
+ function keepalive6(n) {
17310
+ return n.subscribe(() => {
17311
+ });
17312
+ }
17313
+ function messagingMeta(kind, extra) {
17314
+ return {
17315
+ messaging: true,
17316
+ messaging_type: kind,
17317
+ ...extra ?? {}
17318
+ };
17319
+ }
17320
+ var TopicGraph = class extends Graph {
17321
+ _log;
17322
+ _keepaliveDisposers = [];
17323
+ events;
17324
+ latest;
17325
+ constructor(name, opts = {}) {
17326
+ super(name, opts.graph);
17327
+ this._log = reactiveLog([], { name: "events", maxSize: opts.retainedLimit });
17328
+ this.events = this._log.entries;
17329
+ this.add("events", this.events);
17330
+ this.latest = derived(
17331
+ [this.events],
17332
+ ([snapshot]) => {
17333
+ const entries = snapshot.value.entries;
17334
+ return entries.length === 0 ? void 0 : entries[entries.length - 1];
17335
+ },
17336
+ {
17337
+ name: "latest",
17338
+ describeKind: "derived",
17339
+ meta: messagingMeta("topic_latest"),
17340
+ initial: void 0
17341
+ }
17342
+ );
17343
+ this.add("latest", this.latest);
17344
+ this.connect("events", "latest");
17345
+ this._keepaliveDisposers.push(keepalive6(this.latest));
17346
+ }
17347
+ destroy() {
17348
+ for (const dispose of this._keepaliveDisposers) dispose();
17349
+ this._keepaliveDisposers.length = 0;
17350
+ super.destroy();
17351
+ }
17352
+ publish(value) {
17353
+ this._log.append(value);
17354
+ }
17355
+ retained() {
17356
+ const snapshot = this.events.get();
17357
+ return snapshot.value.entries;
17358
+ }
17359
+ };
17360
+ var SubscriptionGraph = class extends Graph {
17361
+ _keepaliveDisposers = [];
17362
+ source;
17363
+ cursor;
17364
+ available;
17365
+ constructor(name, topicGraph, opts = {}) {
17366
+ super(name, opts.graph);
17367
+ const initialCursor = requireNonNegativeInt(opts.cursor ?? 0, "subscription cursor");
17368
+ this.mount("topic", topicGraph);
17369
+ const topicEvents = topicGraph.events;
17370
+ this.source = derived([topicEvents], ([snapshot]) => snapshot, {
17371
+ name: "source",
17372
+ describeKind: "derived",
17373
+ meta: messagingMeta("subscription_source"),
17374
+ initial: topicEvents.get()
17375
+ });
17376
+ this.add("source", this.source);
17377
+ this.cursor = state(initialCursor, {
17378
+ name: "cursor",
17379
+ describeKind: "state",
17380
+ meta: messagingMeta("subscription_cursor")
17381
+ });
17382
+ this.add("cursor", this.cursor);
17383
+ this.available = derived(
17384
+ [this.source, this.cursor],
17385
+ ([sourceSnapshot, cursor]) => {
17386
+ const entries = sourceSnapshot.value.entries;
17387
+ const start = Math.max(0, Math.trunc(cursor ?? 0));
17388
+ return entries.slice(start);
17389
+ },
17390
+ {
17391
+ name: "available",
17392
+ describeKind: "derived",
17393
+ meta: messagingMeta("subscription_available"),
17394
+ initial: []
17395
+ }
17396
+ );
17397
+ this.add("available", this.available);
17398
+ this.connect("topic::events", "source");
17399
+ this.connect("source", "available");
17400
+ this.connect("cursor", "available");
17401
+ this._keepaliveDisposers.push(keepalive6(this.source));
17402
+ this._keepaliveDisposers.push(keepalive6(this.available));
17403
+ }
17404
+ destroy() {
17405
+ for (const dispose of this._keepaliveDisposers) dispose();
17406
+ this._keepaliveDisposers.length = 0;
17407
+ super.destroy();
17408
+ }
17409
+ ack(count) {
17410
+ const available = this.available.get();
17411
+ const requested = count === void 0 ? available.length : requireNonNegativeInt(count, "subscription ack count");
17412
+ const step = Math.min(requested, available.length);
17413
+ if (step <= 0) return this.cursor.get();
17414
+ const next = this.cursor.get() + step;
17415
+ this.cursor.down([[DATA, next]]);
17416
+ return next;
15549
17417
  }
15550
17418
  pull(limit, opts = {}) {
15551
17419
  const available = this.available.get();
@@ -15583,7 +17451,7 @@ var JobQueueGraph = class extends Graph {
15583
17451
  );
15584
17452
  this.add("depth", this.depth);
15585
17453
  this.connect("pending", "depth");
15586
- this._keepaliveDisposers.push(keepalive4(this.depth));
17454
+ this._keepaliveDisposers.push(keepalive6(this.depth));
15587
17455
  }
15588
17456
  destroy() {
15589
17457
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -15683,7 +17551,7 @@ var JobFlowGraph = class extends Graph {
15683
17551
  );
15684
17552
  this.add("completedCount", this.completedCount);
15685
17553
  this.connect("completed", "completedCount");
15686
- this._keepaliveDisposers.push(keepalive4(this.completedCount));
17554
+ this._keepaliveDisposers.push(keepalive6(this.completedCount));
15687
17555
  const maxPerPump = Math.max(
15688
17556
  1,
15689
17557
  requireNonNegativeInt(opts.maxPerPump ?? DEFAULT_MAX_PER_PUMP, "job flow maxPerPump")
@@ -15723,7 +17591,7 @@ var JobFlowGraph = class extends Graph {
15723
17591
  );
15724
17592
  this.add(`pump_${stage}`, pump);
15725
17593
  this.connect(`${stage}::pending`, `pump_${stage}`);
15726
- this._keepaliveDisposers.push(keepalive4(pump));
17594
+ this._keepaliveDisposers.push(keepalive6(pump));
15727
17595
  }
15728
17596
  }
15729
17597
  destroy() {
@@ -15795,7 +17663,7 @@ var TopicBridgeGraph = class extends Graph {
15795
17663
  );
15796
17664
  this.add("pump", pump);
15797
17665
  this.connect("subscription::available", "pump");
15798
- this._keepaliveDisposers.push(keepalive4(pump));
17666
+ this._keepaliveDisposers.push(keepalive6(pump));
15799
17667
  }
15800
17668
  destroy() {
15801
17669
  for (const dispose of this._keepaliveDisposers) dispose();
@@ -15865,7 +17733,7 @@ function registerStep(graph, name, step, depPaths) {
15865
17733
  graph.connect(path, name);
15866
17734
  }
15867
17735
  }
15868
- function baseMeta(kind, meta) {
17736
+ function baseMeta3(kind, meta) {
15869
17737
  return {
15870
17738
  orchestration: true,
15871
17739
  orchestration_type: kind,
@@ -15903,7 +17771,7 @@ function task(graph, name, run, opts) {
15903
17771
  ...nodeOpts,
15904
17772
  name,
15905
17773
  describeKind: "derived",
15906
- meta: baseMeta("task", opts?.meta)
17774
+ meta: baseMeta3("task", opts?.meta)
15907
17775
  }
15908
17776
  );
15909
17777
  registerStep(
@@ -15926,7 +17794,7 @@ function branch(graph, name, source, predicate, opts) {
15926
17794
  ...opts,
15927
17795
  name,
15928
17796
  describeKind: "derived",
15929
- meta: baseMeta("branch", opts?.meta)
17797
+ meta: baseMeta3("branch", opts?.meta)
15930
17798
  }
15931
17799
  );
15932
17800
  registerStep(graph, name, step, src.path ? [src.path] : []);
@@ -15949,7 +17817,7 @@ function gate2(graph, name, source, control, opts) {
15949
17817
  ...opts,
15950
17818
  name,
15951
17819
  describeKind: "operator",
15952
- meta: baseMeta("gate", opts?.meta)
17820
+ meta: baseMeta3("gate", opts?.meta)
15953
17821
  }
15954
17822
  );
15955
17823
  registerStep(
@@ -15977,7 +17845,7 @@ function approval(graph, name, source, approver, opts) {
15977
17845
  ...opts,
15978
17846
  name,
15979
17847
  describeKind: "operator",
15980
- meta: baseMeta("approval", opts?.meta)
17848
+ meta: baseMeta3("approval", opts?.meta)
15981
17849
  }
15982
17850
  );
15983
17851
  registerStep(
@@ -15996,7 +17864,7 @@ function forEach2(graph, name, source, run, opts) {
15996
17864
  name,
15997
17865
  describeKind: "effect",
15998
17866
  completeWhenDepsComplete: false,
15999
- meta: baseMeta("forEach", opts?.meta),
17867
+ meta: baseMeta3("forEach", opts?.meta),
16000
17868
  onMessage(msg, depIndex, actions) {
16001
17869
  if (terminated) return true;
16002
17870
  if (depIndex !== 0) {
@@ -16031,7 +17899,7 @@ function join2(graph, name, deps, opts) {
16031
17899
  ...opts,
16032
17900
  name,
16033
17901
  describeKind: "derived",
16034
- meta: baseMeta("join", opts?.meta)
17902
+ meta: baseMeta3("join", opts?.meta)
16035
17903
  }
16036
17904
  );
16037
17905
  registerStep(
@@ -16062,7 +17930,7 @@ function loop(graph, name, source, iterate, opts) {
16062
17930
  ...opts,
16063
17931
  name,
16064
17932
  describeKind: "derived",
16065
- meta: baseMeta("loop", opts?.meta)
17933
+ meta: baseMeta3("loop", opts?.meta)
16066
17934
  }
16067
17935
  );
16068
17936
  registerStep(
@@ -16087,7 +17955,7 @@ function sensor(graph, name, initial, opts) {
16087
17955
  name,
16088
17956
  initial,
16089
17957
  describeKind: "producer",
16090
- meta: baseMeta("sensor", opts?.meta)
17958
+ meta: baseMeta3("sensor", opts?.meta)
16091
17959
  });
16092
17960
  registerStep(graph, name, source, []);
16093
17961
  return {
@@ -16125,7 +17993,7 @@ function wait(graph, name, source, ms, opts) {
16125
17993
  initial: src.node.get(),
16126
17994
  describeKind: "operator",
16127
17995
  completeWhenDepsComplete: false,
16128
- meta: baseMeta("wait", opts?.meta),
17996
+ meta: baseMeta3("wait", opts?.meta),
16129
17997
  onMessage(msg, depIndex, actions) {
16130
17998
  if (terminated) return true;
16131
17999
  if (depIndex !== 0) {
@@ -16175,7 +18043,7 @@ function onFailure(graph, name, source, recover, opts) {
16175
18043
  name,
16176
18044
  describeKind: "operator",
16177
18045
  completeWhenDepsComplete: false,
16178
- meta: baseMeta("onFailure", opts?.meta),
18046
+ meta: baseMeta3("onFailure", opts?.meta),
16179
18047
  onMessage(msg, _depIndex, actions) {
16180
18048
  if (terminated) return true;
16181
18049
  if (msg[0] === ERROR) {
@@ -16686,285 +18554,16 @@ function reactiveBlockLayout(opts) {
16686
18554
  };
16687
18555
  }
16688
18556
 
16689
- // src/patterns/reduction.ts
16690
- var reduction_exports = {};
16691
- __export(reduction_exports, {
16692
- budgetGate: () => budgetGate,
16693
- feedback: () => feedback,
16694
- funnel: () => funnel,
16695
- scorer: () => scorer,
16696
- stratify: () => stratify
16697
- });
16698
- function baseMeta2(kind, meta) {
16699
- return {
16700
- reduction: true,
16701
- reduction_type: kind,
16702
- ...meta ?? {}
16703
- };
16704
- }
16705
- function stratify(name, source, rules, opts) {
16706
- const g = new Graph(name, opts);
16707
- g.add("source", source);
16708
- const rulesNode = state(rules, {
16709
- meta: baseMeta2("stratify_rules")
16710
- });
16711
- g.add("rules", rulesNode);
16712
- for (const rule of rules) {
16713
- _addBranch(g, source, rulesNode, rule);
16714
- }
16715
- return g;
16716
- }
16717
- function _addBranch(graph, source, rulesNode, rule) {
16718
- const branchName = `branch/${rule.name}`;
16719
- let pendingDirty = false;
16720
- const filterNode = node([source, rulesNode], () => void 0, {
16721
- describeKind: "operator",
16722
- meta: baseMeta2("stratify_branch", { branch: rule.name }),
16723
- onMessage(msg, depIndex, actions) {
16724
- if (depIndex !== 0) return false;
16725
- const t = msg[0];
16726
- if (t === DATA) {
16727
- const value = msg[1];
16728
- const currentRules = rulesNode.get();
16729
- const currentRule = currentRules.find((r) => r.name === rule.name);
16730
- if (currentRule && currentRule.classify(value)) {
16731
- pendingDirty = false;
16732
- actions.emit(value);
16733
- } else {
16734
- if (pendingDirty) {
16735
- pendingDirty = false;
16736
- actions.down([[DIRTY], [RESOLVED]]);
16737
- }
16738
- }
16739
- return true;
16740
- }
16741
- if (t === DIRTY) {
16742
- pendingDirty = true;
16743
- return true;
16744
- }
16745
- if (t === RESOLVED) {
16746
- if (pendingDirty) {
16747
- pendingDirty = false;
16748
- actions.down([[DIRTY], [RESOLVED]]);
16749
- } else {
16750
- actions.down([[RESOLVED]]);
16751
- }
16752
- return true;
16753
- }
16754
- if (t === COMPLETE || t === ERROR) {
16755
- pendingDirty = false;
16756
- actions.down([msg]);
16757
- return true;
16758
- }
16759
- return false;
16760
- }
16761
- });
16762
- graph.add(branchName, filterNode);
16763
- graph.connect("source", branchName);
16764
- if (rule.ops) {
16765
- const transformed = rule.ops(filterNode);
16766
- const transformedName = `branch/${rule.name}/out`;
16767
- graph.add(transformedName, transformed);
16768
- graph.connect(branchName, transformedName);
16769
- }
16770
- }
16771
- function funnel(name, sources, stages, opts) {
16772
- if (sources.length === 0) throw new RangeError("funnel requires at least one source");
16773
- if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
16774
- const g = new Graph(name, opts);
16775
- const merged = sources.length === 1 ? sources[0] : merge(...sources);
16776
- g.add("merged", merged);
16777
- let prevOutputPath = "merged";
16778
- for (let i = 0; i < stages.length; i++) {
16779
- const stage = stages[i];
16780
- const sub = new Graph(stage.name);
16781
- stage.build(sub);
16782
- try {
16783
- sub.resolve("input");
16784
- } catch {
16785
- throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
16786
- }
16787
- try {
16788
- sub.resolve("output");
16789
- } catch {
16790
- throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
16791
- }
16792
- g.mount(stage.name, sub);
16793
- const prevNode = g.resolve(prevOutputPath);
16794
- const stageInputPath = `${stage.name}::input`;
16795
- const stageInput = g.resolve(stageInputPath);
16796
- prevNode.subscribe((msgs) => {
16797
- for (const msg of msgs) {
16798
- const t = msg[0];
16799
- if (t === DATA) {
16800
- stageInput.down([[DATA, msg[1]]]);
16801
- } else if (t === DIRTY) {
16802
- stageInput.down([[DIRTY]]);
16803
- } else if (t === RESOLVED) {
16804
- stageInput.down([[RESOLVED]]);
16805
- } else if (t === COMPLETE || t === ERROR) {
16806
- stageInput.down([msg]);
16807
- }
16808
- }
16809
- });
16810
- prevOutputPath = `${stage.name}::output`;
16811
- }
16812
- return g;
16813
- }
16814
- function feedback(graph, condition, reentry, opts) {
16815
- const maxIter = opts?.maxIterations ?? 10;
16816
- const counterName = `__feedback_${condition}`;
16817
- const counter = state(0, {
16818
- meta: baseMeta2("feedback_counter", { maxIterations: maxIter })
16819
- });
16820
- graph.add(counterName, counter);
16821
- const condNode = graph.resolve(condition);
16822
- const reentryNode = graph.resolve(reentry);
16823
- condNode.subscribe((msgs) => {
16824
- for (const msg of msgs) {
16825
- if (msg[0] === DATA) {
16826
- const currentCount = counter.get();
16827
- if (currentCount >= maxIter) continue;
16828
- const condValue = msg[1];
16829
- if (condValue == null) continue;
16830
- counter.down([[DATA, currentCount + 1]]);
16831
- reentryNode.down([[DATA, condValue]]);
16832
- }
16833
- }
16834
- });
16835
- return graph;
16836
- }
16837
- function budgetGate(source, constraints, opts) {
16838
- if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
16839
- const constraintNodes = constraints.map((c) => c.node);
16840
- const allDeps = [source, ...constraintNodes];
16841
- let buffer2 = [];
16842
- let paused = false;
16843
- const lockId = /* @__PURE__ */ Symbol("budget-gate");
16844
- function checkBudget() {
16845
- return constraints.every((c) => c.check(c.node.get()));
16846
- }
16847
- function flushBuffer(actions) {
16848
- while (buffer2.length > 0 && checkBudget()) {
16849
- const item = buffer2.shift();
16850
- actions.emit(item);
16851
- }
16852
- }
16853
- return node(allDeps, () => void 0, {
16854
- ...opts,
16855
- describeKind: "operator",
16856
- meta: baseMeta2("budget_gate", opts?.meta),
16857
- onMessage(msg, depIndex, actions) {
16858
- const t = msg[0];
16859
- if (depIndex === 0) {
16860
- if (t === DATA) {
16861
- if (checkBudget() && buffer2.length === 0) {
16862
- actions.emit(msg[1]);
16863
- } else {
16864
- buffer2.push(msg[1]);
16865
- if (!paused) {
16866
- paused = true;
16867
- actions.up([[PAUSE, lockId]]);
16868
- }
16869
- }
16870
- return true;
16871
- }
16872
- if (t === DIRTY) {
16873
- actions.down([[DIRTY]]);
16874
- return true;
16875
- }
16876
- if (t === RESOLVED) {
16877
- if (buffer2.length === 0) {
16878
- actions.down([[RESOLVED]]);
16879
- }
16880
- return true;
16881
- }
16882
- if (t === COMPLETE || t === ERROR) {
16883
- for (const item of buffer2) {
16884
- actions.emit(item);
16885
- }
16886
- buffer2 = [];
16887
- if (paused) {
16888
- paused = false;
16889
- actions.up([[RESUME, lockId]]);
16890
- }
16891
- actions.down([msg]);
16892
- return true;
16893
- }
16894
- return false;
16895
- }
16896
- if (t === DATA || t === RESOLVED) {
16897
- if (checkBudget() && buffer2.length > 0) {
16898
- flushBuffer(actions);
16899
- if (buffer2.length === 0 && paused) {
16900
- paused = false;
16901
- actions.up([[RESUME, lockId]]);
16902
- }
16903
- } else if (!checkBudget() && !paused && buffer2.length > 0) {
16904
- paused = true;
16905
- actions.up([[PAUSE, lockId]]);
16906
- }
16907
- return true;
16908
- }
16909
- if (t === DIRTY) {
16910
- return true;
16911
- }
16912
- if (t === ERROR) {
16913
- actions.down([msg]);
16914
- return true;
16915
- }
16916
- if (t === COMPLETE) {
16917
- return true;
16918
- }
16919
- return false;
16920
- }
16921
- });
16922
- }
16923
- function scorer(sources, weights, opts) {
16924
- if (sources.length === 0) throw new RangeError("scorer requires at least one source");
16925
- if (sources.length !== weights.length) {
16926
- throw new RangeError("scorer requires the same number of sources and weights");
16927
- }
16928
- const allDeps = [...sources, ...weights];
16929
- const n = sources.length;
16930
- const scoreFns = opts?.scoreFns;
16931
- return derived(
16932
- allDeps,
16933
- (vals) => {
16934
- const signals = vals.slice(0, n);
16935
- const weightValues = vals.slice(n);
16936
- const breakdown = [];
16937
- let totalScore = 0;
16938
- for (let i = 0; i < n; i++) {
16939
- const sig = signals[i] ?? 0;
16940
- const wt = weightValues[i] ?? 0;
16941
- const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
16942
- const weighted = rawScore * wt;
16943
- breakdown.push(weighted);
16944
- totalScore += weighted;
16945
- }
16946
- return {
16947
- value: signals,
16948
- score: totalScore,
16949
- breakdown
16950
- };
16951
- },
16952
- {
16953
- ...opts,
16954
- describeKind: "derived",
16955
- meta: baseMeta2("scorer", opts?.meta)
16956
- }
16957
- );
16958
- }
16959
-
16960
18557
  // src/index.ts
16961
18558
  var version = "0.0.0";
16962
18559
  // Annotate the CommonJS export names for ESM import in node:
16963
18560
  0 && (module.exports = {
18561
+ CLEANUP_RESULT,
16964
18562
  COMPLETE,
16965
18563
  CircuitOpenError,
16966
18564
  DATA,
16967
18565
  DEFAULT_ACTOR,
18566
+ DEFAULT_DOWN,
16968
18567
  DIRTY,
16969
18568
  DictCheckpointAdapter,
16970
18569
  DynamicNodeImpl,
@@ -16990,6 +18589,7 @@ var version = "0.0.0";
16990
18589
  ai,
16991
18590
  audit,
16992
18591
  batch,
18592
+ bridge,
16993
18593
  buffer,
16994
18594
  bufferCount,
16995
18595
  bufferTime,
@@ -17001,6 +18601,7 @@ var version = "0.0.0";
17001
18601
  checkpointToRedis,
17002
18602
  checkpointToS3,
17003
18603
  circuitBreaker,
18604
+ cleanupResult,
17004
18605
  combine,
17005
18606
  combineLatest,
17006
18607
  compat,
@@ -17025,6 +18626,7 @@ var version = "0.0.0";
17025
18626
  deserializeError,
17026
18627
  distill,
17027
18628
  distinctUntilChanged,
18629
+ domainTemplates,
17028
18630
  dynamicNode,
17029
18631
  effect,
17030
18632
  elementAt,
@@ -17076,6 +18678,7 @@ var version = "0.0.0";
17076
18678
  gate,
17077
18679
  globToRegExp,
17078
18680
  graph,
18681
+ graphspec,
17079
18682
  interval,
17080
18683
  isBatching,
17081
18684
  isKnownMessageType,