@graphrefly/graphrefly 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -142,9 +142,6 @@ import {
142
142
  cqrs_exports,
143
143
  nestjs_exports
144
144
  } from "./chunk-UCW3VWMN.js";
145
- import {
146
- core_exports
147
- } from "./chunk-E7OH6ZAZ.js";
148
145
  import {
149
146
  JsonCodec,
150
147
  createDagCborCodec,
@@ -186,6 +183,9 @@ import {
186
183
  toMessages$,
187
184
  toObservable
188
185
  } from "./chunk-YWTP2XRJ.js";
186
+ import {
187
+ core_exports
188
+ } from "./chunk-E7OH6ZAZ.js";
189
189
  import {
190
190
  ResettableTimer
191
191
  } from "./chunk-WZ2Z2CRV.js";
@@ -1048,7 +1048,8 @@ __export(patterns_exports, {
1048
1048
  layout: () => reactive_layout_exports,
1049
1049
  memory: () => memory_exports,
1050
1050
  messaging: () => messaging_exports,
1051
- orchestration: () => orchestration_exports
1051
+ orchestration: () => orchestration_exports,
1052
+ reduction: () => reduction_exports
1052
1053
  });
1053
1054
 
1054
1055
  // src/patterns/ai.ts
@@ -3802,6 +3803,277 @@ function onFailure(graph, name, source, recover, opts) {
3802
3803
  return step;
3803
3804
  }
3804
3805
 
3806
+ // src/patterns/reduction.ts
3807
+ var reduction_exports = {};
3808
+ __export(reduction_exports, {
3809
+ budgetGate: () => budgetGate,
3810
+ feedback: () => feedback,
3811
+ funnel: () => funnel,
3812
+ scorer: () => scorer,
3813
+ stratify: () => stratify
3814
+ });
3815
+ function baseMeta2(kind, meta) {
3816
+ return {
3817
+ reduction: true,
3818
+ reduction_type: kind,
3819
+ ...meta ?? {}
3820
+ };
3821
+ }
3822
+ function stratify(name, source, rules, opts) {
3823
+ const g = new Graph(name, opts);
3824
+ g.add("source", source);
3825
+ const rulesNode = state(rules, {
3826
+ meta: baseMeta2("stratify_rules")
3827
+ });
3828
+ g.add("rules", rulesNode);
3829
+ for (const rule of rules) {
3830
+ _addBranch(g, source, rulesNode, rule);
3831
+ }
3832
+ return g;
3833
+ }
3834
+ function _addBranch(graph, source, rulesNode, rule) {
3835
+ const branchName = `branch/${rule.name}`;
3836
+ let pendingDirty = false;
3837
+ const filterNode = node([source, rulesNode], () => void 0, {
3838
+ describeKind: "operator",
3839
+ meta: baseMeta2("stratify_branch", { branch: rule.name }),
3840
+ onMessage(msg, depIndex, actions) {
3841
+ if (depIndex !== 0) return false;
3842
+ const t = msg[0];
3843
+ if (t === DATA) {
3844
+ const value = msg[1];
3845
+ const currentRules = rulesNode.get();
3846
+ const currentRule = currentRules.find((r) => r.name === rule.name);
3847
+ if (currentRule && currentRule.classify(value)) {
3848
+ pendingDirty = false;
3849
+ actions.emit(value);
3850
+ } else {
3851
+ if (pendingDirty) {
3852
+ pendingDirty = false;
3853
+ actions.down([[DIRTY], [RESOLVED]]);
3854
+ }
3855
+ }
3856
+ return true;
3857
+ }
3858
+ if (t === DIRTY) {
3859
+ pendingDirty = true;
3860
+ return true;
3861
+ }
3862
+ if (t === RESOLVED) {
3863
+ if (pendingDirty) {
3864
+ pendingDirty = false;
3865
+ actions.down([[DIRTY], [RESOLVED]]);
3866
+ } else {
3867
+ actions.down([[RESOLVED]]);
3868
+ }
3869
+ return true;
3870
+ }
3871
+ if (t === COMPLETE || t === ERROR) {
3872
+ pendingDirty = false;
3873
+ actions.down([msg]);
3874
+ return true;
3875
+ }
3876
+ return false;
3877
+ }
3878
+ });
3879
+ graph.add(branchName, filterNode);
3880
+ graph.connect("source", branchName);
3881
+ if (rule.ops) {
3882
+ const transformed = rule.ops(filterNode);
3883
+ const transformedName = `branch/${rule.name}/out`;
3884
+ graph.add(transformedName, transformed);
3885
+ graph.connect(branchName, transformedName);
3886
+ }
3887
+ }
3888
+ function funnel(name, sources, stages, opts) {
3889
+ if (sources.length === 0) throw new RangeError("funnel requires at least one source");
3890
+ if (stages.length === 0) throw new RangeError("funnel requires at least one stage");
3891
+ const g = new Graph(name, opts);
3892
+ const merged = sources.length === 1 ? sources[0] : merge(...sources);
3893
+ g.add("merged", merged);
3894
+ let prevOutputPath = "merged";
3895
+ for (let i = 0; i < stages.length; i++) {
3896
+ const stage = stages[i];
3897
+ const sub = new Graph(stage.name);
3898
+ stage.build(sub);
3899
+ try {
3900
+ sub.resolve("input");
3901
+ } catch {
3902
+ throw new Error(`funnel stage "${stage.name}" must define an "input" node`);
3903
+ }
3904
+ try {
3905
+ sub.resolve("output");
3906
+ } catch {
3907
+ throw new Error(`funnel stage "${stage.name}" must define an "output" node`);
3908
+ }
3909
+ g.mount(stage.name, sub);
3910
+ const prevNode = g.resolve(prevOutputPath);
3911
+ const stageInputPath = `${stage.name}::input`;
3912
+ const stageInput = g.resolve(stageInputPath);
3913
+ prevNode.subscribe((msgs) => {
3914
+ for (const msg of msgs) {
3915
+ const t = msg[0];
3916
+ if (t === DATA) {
3917
+ stageInput.down([[DATA, msg[1]]]);
3918
+ } else if (t === DIRTY) {
3919
+ stageInput.down([[DIRTY]]);
3920
+ } else if (t === RESOLVED) {
3921
+ stageInput.down([[RESOLVED]]);
3922
+ } else if (t === COMPLETE || t === ERROR) {
3923
+ stageInput.down([msg]);
3924
+ }
3925
+ }
3926
+ });
3927
+ prevOutputPath = `${stage.name}::output`;
3928
+ }
3929
+ return g;
3930
+ }
3931
+ function feedback(graph, condition, reentry, opts) {
3932
+ const maxIter = opts?.maxIterations ?? 10;
3933
+ const counterName = `__feedback_${condition}`;
3934
+ const counter = state(0, {
3935
+ meta: baseMeta2("feedback_counter", { maxIterations: maxIter })
3936
+ });
3937
+ graph.add(counterName, counter);
3938
+ const condNode = graph.resolve(condition);
3939
+ const reentryNode = graph.resolve(reentry);
3940
+ condNode.subscribe((msgs) => {
3941
+ for (const msg of msgs) {
3942
+ if (msg[0] === DATA) {
3943
+ const currentCount = counter.get();
3944
+ if (currentCount >= maxIter) continue;
3945
+ const condValue = msg[1];
3946
+ if (condValue == null) continue;
3947
+ counter.down([[DATA, currentCount + 1]]);
3948
+ reentryNode.down([[DATA, condValue]]);
3949
+ }
3950
+ }
3951
+ });
3952
+ return graph;
3953
+ }
3954
+ function budgetGate(source, constraints, opts) {
3955
+ if (constraints.length === 0) throw new RangeError("budgetGate requires at least one constraint");
3956
+ const constraintNodes = constraints.map((c) => c.node);
3957
+ const allDeps = [source, ...constraintNodes];
3958
+ let buffer2 = [];
3959
+ let paused = false;
3960
+ const lockId = /* @__PURE__ */ Symbol("budget-gate");
3961
+ function checkBudget() {
3962
+ return constraints.every((c) => c.check(c.node.get()));
3963
+ }
3964
+ function flushBuffer(actions) {
3965
+ while (buffer2.length > 0 && checkBudget()) {
3966
+ const item = buffer2.shift();
3967
+ actions.emit(item);
3968
+ }
3969
+ }
3970
+ return node(allDeps, () => void 0, {
3971
+ ...opts,
3972
+ describeKind: "operator",
3973
+ meta: baseMeta2("budget_gate", opts?.meta),
3974
+ onMessage(msg, depIndex, actions) {
3975
+ const t = msg[0];
3976
+ if (depIndex === 0) {
3977
+ if (t === DATA) {
3978
+ if (checkBudget() && buffer2.length === 0) {
3979
+ actions.emit(msg[1]);
3980
+ } else {
3981
+ buffer2.push(msg[1]);
3982
+ if (!paused) {
3983
+ paused = true;
3984
+ actions.up([[PAUSE, lockId]]);
3985
+ }
3986
+ }
3987
+ return true;
3988
+ }
3989
+ if (t === DIRTY) {
3990
+ actions.down([[DIRTY]]);
3991
+ return true;
3992
+ }
3993
+ if (t === RESOLVED) {
3994
+ if (buffer2.length === 0) {
3995
+ actions.down([[RESOLVED]]);
3996
+ }
3997
+ return true;
3998
+ }
3999
+ if (t === COMPLETE || t === ERROR) {
4000
+ for (const item of buffer2) {
4001
+ actions.emit(item);
4002
+ }
4003
+ buffer2 = [];
4004
+ if (paused) {
4005
+ paused = false;
4006
+ actions.up([[RESUME, lockId]]);
4007
+ }
4008
+ actions.down([msg]);
4009
+ return true;
4010
+ }
4011
+ return false;
4012
+ }
4013
+ if (t === DATA || t === RESOLVED) {
4014
+ if (checkBudget() && buffer2.length > 0) {
4015
+ flushBuffer(actions);
4016
+ if (buffer2.length === 0 && paused) {
4017
+ paused = false;
4018
+ actions.up([[RESUME, lockId]]);
4019
+ }
4020
+ } else if (!checkBudget() && !paused && buffer2.length > 0) {
4021
+ paused = true;
4022
+ actions.up([[PAUSE, lockId]]);
4023
+ }
4024
+ return true;
4025
+ }
4026
+ if (t === DIRTY) {
4027
+ return true;
4028
+ }
4029
+ if (t === ERROR) {
4030
+ actions.down([msg]);
4031
+ return true;
4032
+ }
4033
+ if (t === COMPLETE) {
4034
+ return true;
4035
+ }
4036
+ return false;
4037
+ }
4038
+ });
4039
+ }
4040
+ function scorer(sources, weights, opts) {
4041
+ if (sources.length === 0) throw new RangeError("scorer requires at least one source");
4042
+ if (sources.length !== weights.length) {
4043
+ throw new RangeError("scorer requires the same number of sources and weights");
4044
+ }
4045
+ const allDeps = [...sources, ...weights];
4046
+ const n = sources.length;
4047
+ const scoreFns = opts?.scoreFns;
4048
+ return derived(
4049
+ allDeps,
4050
+ (vals) => {
4051
+ const signals = vals.slice(0, n);
4052
+ const weightValues = vals.slice(n);
4053
+ const breakdown = [];
4054
+ let totalScore = 0;
4055
+ for (let i = 0; i < n; i++) {
4056
+ const sig = signals[i] ?? 0;
4057
+ const wt = weightValues[i] ?? 0;
4058
+ const rawScore = scoreFns?.[i] ? scoreFns[i](sig) : sig;
4059
+ const weighted = rawScore * wt;
4060
+ breakdown.push(weighted);
4061
+ totalScore += weighted;
4062
+ }
4063
+ return {
4064
+ value: signals,
4065
+ score: totalScore,
4066
+ breakdown
4067
+ };
4068
+ },
4069
+ {
4070
+ ...opts,
4071
+ describeKind: "derived",
4072
+ meta: baseMeta2("scorer", opts?.meta)
4073
+ }
4074
+ );
4075
+ }
4076
+
3805
4077
  // src/index.ts
3806
4078
  var version = "0.0.0";
3807
4079
  export {
@@ -3977,6 +4249,7 @@ export {
3977
4249
  reactiveLog,
3978
4250
  reactiveMap,
3979
4251
  reduce,
4252
+ reduction_exports as reduction,
3980
4253
  repeat,
3981
4254
  replay,
3982
4255
  replayWAL,