@langchain/langgraph 0.0.34 → 0.1.0-rc.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 (82) hide show
  1. package/README.md +11 -15
  2. package/dist/channels/any_value.cjs +3 -1
  3. package/dist/channels/any_value.d.ts +1 -1
  4. package/dist/channels/any_value.js +3 -1
  5. package/dist/channels/base.cjs +8 -0
  6. package/dist/channels/base.d.ts +12 -1
  7. package/dist/channels/base.js +8 -0
  8. package/dist/channels/binop.cjs +2 -1
  9. package/dist/channels/binop.d.ts +1 -1
  10. package/dist/channels/binop.js +2 -1
  11. package/dist/channels/dynamic_barrier_value.cjs +30 -18
  12. package/dist/channels/dynamic_barrier_value.d.ts +2 -1
  13. package/dist/channels/dynamic_barrier_value.js +30 -18
  14. package/dist/channels/ephemeral_value.cjs +3 -1
  15. package/dist/channels/ephemeral_value.d.ts +1 -1
  16. package/dist/channels/ephemeral_value.js +3 -1
  17. package/dist/channels/last_value.cjs +3 -2
  18. package/dist/channels/last_value.d.ts +1 -1
  19. package/dist/channels/last_value.js +3 -2
  20. package/dist/channels/named_barrier_value.cjs +14 -6
  21. package/dist/channels/named_barrier_value.d.ts +2 -1
  22. package/dist/channels/named_barrier_value.js +15 -7
  23. package/dist/channels/topic.cjs +10 -11
  24. package/dist/channels/topic.d.ts +1 -1
  25. package/dist/channels/topic.js +10 -11
  26. package/dist/checkpoint/base.cjs +9 -0
  27. package/dist/checkpoint/base.d.ts +23 -18
  28. package/dist/checkpoint/base.js +9 -0
  29. package/dist/checkpoint/id.cjs +13 -1
  30. package/dist/checkpoint/id.d.ts +1 -0
  31. package/dist/checkpoint/id.js +12 -1
  32. package/dist/checkpoint/index.d.ts +2 -1
  33. package/dist/checkpoint/memory.cjs +152 -39
  34. package/dist/checkpoint/memory.d.ts +6 -3
  35. package/dist/checkpoint/memory.js +152 -39
  36. package/dist/checkpoint/serde/types.cjs +2 -0
  37. package/dist/checkpoint/serde/types.d.ts +40 -0
  38. package/dist/checkpoint/serde/types.js +1 -0
  39. package/dist/checkpoint/sqlite.cjs +127 -98
  40. package/dist/checkpoint/sqlite.d.ts +5 -3
  41. package/dist/checkpoint/sqlite.js +127 -98
  42. package/dist/checkpoint/types.cjs +2 -0
  43. package/dist/checkpoint/types.d.ts +28 -0
  44. package/dist/checkpoint/types.js +1 -0
  45. package/dist/constants.cjs +17 -1
  46. package/dist/constants.d.ts +7 -0
  47. package/dist/constants.js +16 -0
  48. package/dist/errors.cjs +21 -1
  49. package/dist/errors.d.ts +8 -0
  50. package/dist/errors.js +18 -0
  51. package/dist/graph/graph.cjs +6 -3
  52. package/dist/graph/graph.d.ts +6 -2
  53. package/dist/graph/graph.js +7 -4
  54. package/dist/graph/index.d.ts +1 -1
  55. package/dist/graph/state.cjs +7 -7
  56. package/dist/graph/state.js +7 -7
  57. package/dist/pregel/algo.cjs +384 -0
  58. package/dist/pregel/algo.d.ts +32 -0
  59. package/dist/pregel/algo.js +374 -0
  60. package/dist/pregel/debug.cjs +158 -9
  61. package/dist/pregel/debug.d.ts +39 -2
  62. package/dist/pregel/debug.js +151 -7
  63. package/dist/pregel/index.cjs +217 -509
  64. package/dist/pregel/index.d.ts +29 -66
  65. package/dist/pregel/index.js +219 -506
  66. package/dist/pregel/io.cjs +2 -2
  67. package/dist/pregel/io.d.ts +5 -4
  68. package/dist/pregel/io.js +2 -2
  69. package/dist/pregel/loop.cjs +428 -0
  70. package/dist/pregel/loop.d.ts +84 -0
  71. package/dist/pregel/loop.js +421 -0
  72. package/dist/pregel/types.d.ts +53 -4
  73. package/dist/pregel/utils.cjs +33 -0
  74. package/dist/pregel/utils.d.ts +3 -0
  75. package/dist/pregel/utils.js +28 -0
  76. package/dist/pregel/write.cjs +3 -1
  77. package/dist/pregel/write.js +3 -1
  78. package/dist/utils.cjs +21 -1
  79. package/dist/utils.d.ts +4 -0
  80. package/dist/utils.js +18 -0
  81. package/dist/web.d.ts +3 -2
  82. package/package.json +4 -2
package/README.md CHANGED
@@ -60,20 +60,15 @@ import { tool } from "@langchain/core/tools";
60
60
  import { z } from "zod";
61
61
  import { ChatAnthropic } from "@langchain/anthropic";
62
62
  import { StateGraph, StateGraphArgs } from "@langchain/langgraph";
63
- import { MemorySaver } from "@langchain/langgraph";
63
+ import { MemorySaver, Annotation } from "@langchain/langgraph";
64
64
  import { ToolNode } from "@langchain/langgraph/prebuilt";
65
65
 
66
- // Define the state interface
67
- interface AgentState {
68
- messages: BaseMessage[];
69
- }
70
-
71
66
  // Define the graph state
72
- const graphState: StateGraphArgs<AgentState>["channels"] = {
73
- messages: {
74
- reducer: (x: BaseMessage[], y: BaseMessage[]) => x.concat(y),
75
- },
76
- };
67
+ const GraphState = Annotation.Root({
68
+ messages: Annotation<BaseMessage[]>({
69
+ reducer: (x, y) => x.concat(y),
70
+ })
71
+ })
77
72
 
78
73
  // Define the tools for the agent to use
79
74
  const weatherTool = tool(async ({ query }) => {
@@ -92,7 +87,8 @@ const weatherTool = tool(async ({ query }) => {
92
87
  });
93
88
 
94
89
  const tools = [weatherTool];
95
- const toolNode = new ToolNode<AgentState>(tools);
90
+ // We can extract the state typing via `GraphState.State`
91
+ const toolNode = new ToolNode<typeof GraphState.State>(tools);
96
92
 
97
93
  const model = new ChatAnthropic({
98
94
  model: "claude-3-5-sonnet-20240620",
@@ -100,7 +96,7 @@ const model = new ChatAnthropic({
100
96
  }).bindTools(tools);
101
97
 
102
98
  // Define the function that determines whether to continue or not
103
- function shouldContinue(state: AgentState) {
99
+ function shouldContinue(state: typeof GraphState.State) {
104
100
  const messages = state.messages;
105
101
  const lastMessage = messages[messages.length - 1] as AIMessage;
106
102
 
@@ -113,7 +109,7 @@ function shouldContinue(state: AgentState) {
113
109
  }
114
110
 
115
111
  // Define the function that calls the model
116
- async function callModel(state: AgentState) {
112
+ async function callModel(state: typeof GraphState.State) {
117
113
  const messages = state.messages;
118
114
  const response = await model.invoke(messages);
119
115
 
@@ -122,7 +118,7 @@ async function callModel(state: AgentState) {
122
118
  }
123
119
 
124
120
  // Define a new graph
125
- const workflow = new StateGraph<AgentState>({ channels: graphState })
121
+ const workflow = new StateGraph(GraphState)
126
122
  .addNode("agent", callModel)
127
123
  .addNode("tools", toolNode)
128
124
  .addEdge("__start__", "agent")
@@ -35,11 +35,13 @@ class AnyValue extends index_js_1.BaseChannel {
35
35
  }
36
36
  update(values) {
37
37
  if (values.length === 0) {
38
+ const updated = this.value !== undefined;
38
39
  this.value = undefined;
39
- return;
40
+ return updated;
40
41
  }
41
42
  // eslint-disable-next-line prefer-destructuring
42
43
  this.value = values[values.length - 1];
44
+ return false;
43
45
  }
44
46
  get() {
45
47
  if (this.value === undefined) {
@@ -10,7 +10,7 @@ export declare class AnyValue<Value> extends BaseChannel<Value, Value, Value> {
10
10
  value: Value | undefined;
11
11
  constructor();
12
12
  fromCheckpoint(checkpoint?: Value): this;
13
- update(values: Value[]): void;
13
+ update(values: Value[]): boolean;
14
14
  get(): Value;
15
15
  checkpoint(): Value;
16
16
  }
@@ -32,11 +32,13 @@ export class AnyValue extends BaseChannel {
32
32
  }
33
33
  update(values) {
34
34
  if (values.length === 0) {
35
+ const updated = this.value !== undefined;
35
36
  this.value = undefined;
36
- return;
37
+ return updated;
37
38
  }
38
39
  // eslint-disable-next-line prefer-destructuring
39
40
  this.value = values[values.length - 1];
41
+ return false;
40
42
  }
41
43
  get() {
42
44
  if (this.value === undefined) {
@@ -19,6 +19,14 @@ class BaseChannel {
19
19
  value: void 0
20
20
  });
21
21
  }
22
+ /**
23
+ * Mark the current value of the channel as consumed. By default, no-op.
24
+ * This is called by Pregel before the start of the next step, for all
25
+ * channels that triggered a node. If the channel was updated, return true.
26
+ */
27
+ consume() {
28
+ return false;
29
+ }
22
30
  }
23
31
  exports.BaseChannel = BaseChannel;
24
32
  function emptyChannels(channels, checkpoint) {
@@ -19,12 +19,17 @@ export declare abstract class BaseChannel<ValueType = unknown, UpdateType = unkn
19
19
  /**
20
20
  * Update the channel's value with the given sequence of updates.
21
21
  * The order of the updates in the sequence is arbitrary.
22
+ * This method is called by Pregel for all channels at the end of each step.
23
+ * If there are no updates, it is called with an empty sequence.
24
+ *
25
+ * Raises InvalidUpdateError if the sequence of updates is invalid.
26
+ * Returns True if the channel was updated, False otherwise.
22
27
  *
23
28
  * @throws {InvalidUpdateError} if the sequence of updates is invalid.
24
29
  * @param {Array<UpdateType>} values
25
30
  * @returns {void}
26
31
  */
27
- abstract update(values: UpdateType[]): void;
32
+ abstract update(values: UpdateType[]): boolean;
28
33
  /**
29
34
  * Return the current value of the channel.
30
35
  *
@@ -39,6 +44,12 @@ export declare abstract class BaseChannel<ValueType = unknown, UpdateType = unkn
39
44
  * @returns {CheckpointType | undefined}
40
45
  */
41
46
  abstract checkpoint(): CheckpointType | undefined;
47
+ /**
48
+ * Mark the current value of the channel as consumed. By default, no-op.
49
+ * This is called by Pregel before the start of the next step, for all
50
+ * channels that triggered a node. If the channel was updated, return true.
51
+ */
52
+ consume(): boolean;
42
53
  }
43
54
  export declare function emptyChannels<Cc extends Record<string, BaseChannel>>(channels: Cc, checkpoint: ReadonlyCheckpoint): Cc;
44
55
  export declare function createCheckpoint<ValueType>(checkpoint: ReadonlyCheckpoint, channels: Record<string, BaseChannel<ValueType>>, step: number): Checkpoint;
@@ -16,6 +16,14 @@ export class BaseChannel {
16
16
  value: void 0
17
17
  });
18
18
  }
19
+ /**
20
+ * Mark the current value of the channel as consumed. By default, no-op.
21
+ * This is called by Pregel before the start of the next step, for all
22
+ * channels that triggered a node. If the channel was updated, return true.
23
+ */
24
+ consume() {
25
+ return false;
26
+ }
19
27
  }
20
28
  export function emptyChannels(channels, checkpoint) {
21
29
  const newChannels = {};
@@ -47,7 +47,7 @@ class BinaryOperatorAggregate extends index_js_1.BaseChannel {
47
47
  update(values) {
48
48
  let newValues = values;
49
49
  if (!newValues.length)
50
- return;
50
+ return false;
51
51
  if (this.value === undefined) {
52
52
  [this.value] = newValues;
53
53
  newValues = newValues.slice(1);
@@ -57,6 +57,7 @@ class BinaryOperatorAggregate extends index_js_1.BaseChannel {
57
57
  this.value = this.operator(this.value, value);
58
58
  }
59
59
  }
60
+ return true;
60
61
  }
61
62
  get() {
62
63
  if (this.value === undefined) {
@@ -10,7 +10,7 @@ export declare class BinaryOperatorAggregate<ValueType, UpdateType = ValueType>
10
10
  initialValueFactory?: () => ValueType;
11
11
  constructor(operator: BinaryOperator<ValueType, UpdateType>, initialValueFactory?: () => ValueType);
12
12
  fromCheckpoint(checkpoint?: ValueType): this;
13
- update(values: UpdateType[]): void;
13
+ update(values: UpdateType[]): boolean;
14
14
  get(): ValueType;
15
15
  checkpoint(): ValueType;
16
16
  }
@@ -44,7 +44,7 @@ export class BinaryOperatorAggregate extends BaseChannel {
44
44
  update(values) {
45
45
  let newValues = values;
46
46
  if (!newValues.length)
47
- return;
47
+ return false;
48
48
  if (this.value === undefined) {
49
49
  [this.value] = newValues;
50
50
  newValues = newValues.slice(1);
@@ -54,6 +54,7 @@ export class BinaryOperatorAggregate extends BaseChannel {
54
54
  this.value = this.operator(this.value, value);
55
55
  }
56
56
  }
57
+ return true;
57
58
  }
58
59
  get() {
59
60
  if (this.value === undefined) {
@@ -4,6 +4,9 @@ exports.DynamicBarrierValue = void 0;
4
4
  const errors_js_1 = require("../errors.cjs");
5
5
  const index_js_1 = require("./index.cjs");
6
6
  const named_barrier_value_js_1 = require("./named_barrier_value.cjs");
7
+ function isWaitForNames(v) {
8
+ return v.__names !== undefined;
9
+ }
7
10
  /**
8
11
  A channel that switches between two states
9
12
 
@@ -46,32 +49,41 @@ class DynamicBarrierValue extends index_js_1.BaseChannel {
46
49
  return empty;
47
50
  }
48
51
  update(values) {
49
- // switch to priming state after reading it once
50
- if (this.names && (0, named_barrier_value_js_1.areSetsEqual)(this.names, this.seen)) {
51
- this.seen = new Set();
52
- this.names = undefined;
53
- }
54
- const newNames = values.filter((v) => typeof v === "object" &&
55
- !!v &&
56
- "__names" in v &&
57
- Object.keys(v).join(",") === "__names" &&
58
- Array.isArray(v.__names));
59
- if (newNames.length > 1) {
60
- throw new errors_js_1.InvalidUpdateError(`Expected at most one WaitForNames object, got ${newNames.length}`);
61
- }
62
- else if (newNames.length === 1) {
63
- this.names = new Set(newNames[0].__names);
52
+ const waitForNames = values.filter(isWaitForNames);
53
+ if (waitForNames.length > 0) {
54
+ if (waitForNames.length > 1) {
55
+ throw new errors_js_1.InvalidUpdateError("Received multiple WaitForNames updates in the same step.");
56
+ }
57
+ this.names = new Set(waitForNames[0].__names);
58
+ return true;
64
59
  }
65
- else if (this.names) {
60
+ else if (this.names !== undefined) {
61
+ let updated = false;
66
62
  for (const value of values) {
63
+ if (isWaitForNames(value)) {
64
+ throw new Error("Assertion Error: Received unexpected WaitForNames instance.");
65
+ }
67
66
  if (this.names.has(value)) {
68
- this.seen.add(value);
67
+ if (!this.seen.has(value)) {
68
+ this.seen.add(value);
69
+ updated = true;
70
+ }
69
71
  }
70
72
  else {
71
- throw new errors_js_1.InvalidUpdateError(`Value ${value} not in names ${this.names}`);
73
+ throw new errors_js_1.InvalidUpdateError(`Value ${value} not in ${[...this.names]}`);
72
74
  }
73
75
  }
76
+ return updated;
77
+ }
78
+ return false;
79
+ }
80
+ consume() {
81
+ if (this.seen && this.names && (0, named_barrier_value_js_1.areSetsEqual)(this.seen, this.names)) {
82
+ this.seen = new Set();
83
+ this.names = undefined;
84
+ return true;
74
85
  }
86
+ return false;
75
87
  }
76
88
  // If we have not yet seen all the node names we want to wait for,
77
89
  // throw an error to prevent continuing.
@@ -20,7 +20,8 @@ export declare class DynamicBarrierValue<Value> extends BaseChannel<void, Value
20
20
  seen: Set<Value>;
21
21
  constructor();
22
22
  fromCheckpoint(checkpoint?: [Value[] | undefined, Value[]]): this;
23
- update(values: (Value | WaitForNames<Value>)[]): void;
23
+ update(values: (Value | WaitForNames<Value>)[]): boolean;
24
+ consume(): boolean;
24
25
  get(): void;
25
26
  checkpoint(): [Value[] | undefined, Value[]];
26
27
  }
@@ -1,6 +1,9 @@
1
1
  import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
2
2
  import { BaseChannel } from "./index.js";
3
3
  import { areSetsEqual } from "./named_barrier_value.js";
4
+ function isWaitForNames(v) {
5
+ return v.__names !== undefined;
6
+ }
4
7
  /**
5
8
  A channel that switches between two states
6
9
 
@@ -43,32 +46,41 @@ export class DynamicBarrierValue extends BaseChannel {
43
46
  return empty;
44
47
  }
45
48
  update(values) {
46
- // switch to priming state after reading it once
47
- if (this.names && areSetsEqual(this.names, this.seen)) {
48
- this.seen = new Set();
49
- this.names = undefined;
50
- }
51
- const newNames = values.filter((v) => typeof v === "object" &&
52
- !!v &&
53
- "__names" in v &&
54
- Object.keys(v).join(",") === "__names" &&
55
- Array.isArray(v.__names));
56
- if (newNames.length > 1) {
57
- throw new InvalidUpdateError(`Expected at most one WaitForNames object, got ${newNames.length}`);
58
- }
59
- else if (newNames.length === 1) {
60
- this.names = new Set(newNames[0].__names);
49
+ const waitForNames = values.filter(isWaitForNames);
50
+ if (waitForNames.length > 0) {
51
+ if (waitForNames.length > 1) {
52
+ throw new InvalidUpdateError("Received multiple WaitForNames updates in the same step.");
53
+ }
54
+ this.names = new Set(waitForNames[0].__names);
55
+ return true;
61
56
  }
62
- else if (this.names) {
57
+ else if (this.names !== undefined) {
58
+ let updated = false;
63
59
  for (const value of values) {
60
+ if (isWaitForNames(value)) {
61
+ throw new Error("Assertion Error: Received unexpected WaitForNames instance.");
62
+ }
64
63
  if (this.names.has(value)) {
65
- this.seen.add(value);
64
+ if (!this.seen.has(value)) {
65
+ this.seen.add(value);
66
+ updated = true;
67
+ }
66
68
  }
67
69
  else {
68
- throw new InvalidUpdateError(`Value ${value} not in names ${this.names}`);
70
+ throw new InvalidUpdateError(`Value ${value} not in ${[...this.names]}`);
69
71
  }
70
72
  }
73
+ return updated;
74
+ }
75
+ return false;
76
+ }
77
+ consume() {
78
+ if (this.seen && this.names && areSetsEqual(this.seen, this.names)) {
79
+ this.seen = new Set();
80
+ this.names = undefined;
81
+ return true;
71
82
  }
83
+ return false;
72
84
  }
73
85
  // If we have not yet seen all the node names we want to wait for,
74
86
  // throw an error to prevent continuing.
@@ -38,15 +38,17 @@ class EphemeralValue extends index_js_1.BaseChannel {
38
38
  }
39
39
  update(values) {
40
40
  if (values.length === 0) {
41
+ const updated = this.value !== undefined;
41
42
  // If there are no updates for this specific channel at the end of the step, wipe it.
42
43
  this.value = undefined;
43
- return;
44
+ return updated;
44
45
  }
45
46
  if (values.length !== 1 && this.guard) {
46
47
  throw new errors_js_1.InvalidUpdateError("EphemeralValue can only receive one value per step.");
47
48
  }
48
49
  // eslint-disable-next-line prefer-destructuring
49
50
  this.value = values[values.length - 1];
51
+ return true;
50
52
  }
51
53
  get() {
52
54
  if (this.value === undefined) {
@@ -8,7 +8,7 @@ export declare class EphemeralValue<Value> extends BaseChannel<Value, Value, Val
8
8
  value?: Value;
9
9
  constructor(guard?: boolean);
10
10
  fromCheckpoint(checkpoint?: Value): this;
11
- update(values: Value[]): void;
11
+ update(values: Value[]): boolean;
12
12
  get(): Value;
13
13
  checkpoint(): Value;
14
14
  }
@@ -35,15 +35,17 @@ export class EphemeralValue extends BaseChannel {
35
35
  }
36
36
  update(values) {
37
37
  if (values.length === 0) {
38
+ const updated = this.value !== undefined;
38
39
  // If there are no updates for this specific channel at the end of the step, wipe it.
39
40
  this.value = undefined;
40
- return;
41
+ return updated;
41
42
  }
42
43
  if (values.length !== 1 && this.guard) {
43
44
  throw new InvalidUpdateError("EphemeralValue can only receive one value per step.");
44
45
  }
45
46
  // eslint-disable-next-line prefer-destructuring
46
47
  this.value = values[values.length - 1];
48
+ return true;
47
49
  }
48
50
  get() {
49
51
  if (this.value === undefined) {
@@ -35,13 +35,14 @@ class LastValue extends index_js_1.BaseChannel {
35
35
  }
36
36
  update(values) {
37
37
  if (values.length === 0) {
38
- return;
38
+ return false;
39
39
  }
40
40
  if (values.length !== 1) {
41
- throw new errors_js_1.InvalidUpdateError();
41
+ throw new errors_js_1.InvalidUpdateError("LastValue can only receive one value per step.");
42
42
  }
43
43
  // eslint-disable-next-line prefer-destructuring
44
44
  this.value = values[values.length - 1];
45
+ return true;
45
46
  }
46
47
  get() {
47
48
  if (this.value === undefined) {
@@ -10,7 +10,7 @@ export declare class LastValue<Value> extends BaseChannel<Value, Value, Value> {
10
10
  lc_graph_name: string;
11
11
  value?: Value;
12
12
  fromCheckpoint(checkpoint?: Value): this;
13
- update(values: Value[]): void;
13
+ update(values: Value[]): boolean;
14
14
  get(): Value;
15
15
  checkpoint(): Value;
16
16
  }
@@ -32,13 +32,14 @@ export class LastValue extends BaseChannel {
32
32
  }
33
33
  update(values) {
34
34
  if (values.length === 0) {
35
- return;
35
+ return false;
36
36
  }
37
37
  if (values.length !== 1) {
38
- throw new InvalidUpdateError();
38
+ throw new InvalidUpdateError("LastValue can only receive one value per step.");
39
39
  }
40
40
  // eslint-disable-next-line prefer-destructuring
41
41
  this.value = values[values.length - 1];
42
+ return true;
42
43
  }
43
44
  get() {
44
45
  if (this.value === undefined) {
@@ -43,18 +43,19 @@ class NamedBarrierValue extends index_js_1.BaseChannel {
43
43
  return empty;
44
44
  }
45
45
  update(values) {
46
- // We have seen all nodes, so we can reset the seen set in preparation for the next round of updates.
47
- if ((0, exports.areSetsEqual)(this.names, this.seen)) {
48
- this.seen = new Set();
49
- }
46
+ let updated = false;
50
47
  for (const nodeName of values) {
51
48
  if (this.names.has(nodeName)) {
52
- this.seen.add(nodeName);
49
+ if (!this.seen.has(nodeName)) {
50
+ this.seen.add(nodeName);
51
+ updated = true;
52
+ }
53
53
  }
54
54
  else {
55
- throw new Error(`Value ${JSON.stringify(nodeName)} not in names ${JSON.stringify(this.names)}`);
55
+ throw new errors_js_1.InvalidUpdateError(`Value ${JSON.stringify(nodeName)} not in names ${JSON.stringify(this.names)}`);
56
56
  }
57
57
  }
58
+ return updated;
58
59
  }
59
60
  // If we have not yet seen all the node names we want to wait for,
60
61
  // throw an error to prevent continuing.
@@ -67,5 +68,12 @@ class NamedBarrierValue extends index_js_1.BaseChannel {
67
68
  checkpoint() {
68
69
  return [...this.seen];
69
70
  }
71
+ consume() {
72
+ if (this.seen && this.names && (0, exports.areSetsEqual)(this.seen, this.names)) {
73
+ this.seen = new Set();
74
+ return true;
75
+ }
76
+ return false;
77
+ }
70
78
  }
71
79
  exports.NamedBarrierValue = NamedBarrierValue;
@@ -12,7 +12,8 @@ export declare class NamedBarrierValue<Value> extends BaseChannel<void, Value, V
12
12
  seen: Set<Value>;
13
13
  constructor(names: Set<Value>);
14
14
  fromCheckpoint(checkpoint?: Value[]): this;
15
- update(values: Value[]): void;
15
+ update(values: Value[]): boolean;
16
16
  get(): void;
17
17
  checkpoint(): Value[];
18
+ consume(): boolean;
18
19
  }
@@ -1,4 +1,4 @@
1
- import { EmptyChannelError } from "../errors.js";
1
+ import { EmptyChannelError, InvalidUpdateError } from "../errors.js";
2
2
  import { BaseChannel } from "./index.js";
3
3
  export const areSetsEqual = (a, b) => a.size === b.size && [...a].every((value) => b.has(value));
4
4
  /**
@@ -39,18 +39,19 @@ export class NamedBarrierValue extends BaseChannel {
39
39
  return empty;
40
40
  }
41
41
  update(values) {
42
- // We have seen all nodes, so we can reset the seen set in preparation for the next round of updates.
43
- if (areSetsEqual(this.names, this.seen)) {
44
- this.seen = new Set();
45
- }
42
+ let updated = false;
46
43
  for (const nodeName of values) {
47
44
  if (this.names.has(nodeName)) {
48
- this.seen.add(nodeName);
45
+ if (!this.seen.has(nodeName)) {
46
+ this.seen.add(nodeName);
47
+ updated = true;
48
+ }
49
49
  }
50
50
  else {
51
- throw new Error(`Value ${JSON.stringify(nodeName)} not in names ${JSON.stringify(this.names)}`);
51
+ throw new InvalidUpdateError(`Value ${JSON.stringify(nodeName)} not in names ${JSON.stringify(this.names)}`);
52
52
  }
53
53
  }
54
+ return updated;
54
55
  }
55
56
  // If we have not yet seen all the node names we want to wait for,
56
57
  // throw an error to prevent continuing.
@@ -63,4 +64,11 @@ export class NamedBarrierValue extends BaseChannel {
63
64
  checkpoint() {
64
65
  return [...this.seen];
65
66
  }
67
+ consume() {
68
+ if (this.seen && this.names && areSetsEqual(this.seen, this.names)) {
69
+ this.seen = new Set();
70
+ return true;
71
+ }
72
+ return false;
73
+ }
66
74
  }
@@ -1,16 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Topic = void 0;
4
+ const errors_js_1 = require("../errors.cjs");
4
5
  const base_js_1 = require("./base.cjs");
5
- function* flatten(values) {
6
- for (const value of values) {
7
- if (Array.isArray(value)) {
8
- yield* value;
9
- }
10
- else {
11
- yield value;
12
- }
13
- }
6
+ function arraysEqual(a, b) {
7
+ return a.length === b.length && a.every((val, index) => val === b[index]);
14
8
  }
15
9
  class Topic extends base_js_1.BaseChannel {
16
10
  constructor(fields) {
@@ -64,11 +58,12 @@ class Topic extends base_js_1.BaseChannel {
64
58
  return empty;
65
59
  }
66
60
  update(values) {
61
+ const current = [...this.values];
67
62
  if (!this.accumulate) {
68
63
  this.values = [];
69
64
  }
70
- const flatValues = flatten(values);
71
- if (flatValues) {
65
+ const flatValues = values.flat();
66
+ if (flatValues.length > 0) {
72
67
  if (this.unique) {
73
68
  for (const value of flatValues) {
74
69
  if (!this.seen.has(value)) {
@@ -81,8 +76,12 @@ class Topic extends base_js_1.BaseChannel {
81
76
  this.values.push(...flatValues);
82
77
  }
83
78
  }
79
+ return !arraysEqual(this.values, current);
84
80
  }
85
81
  get() {
82
+ if (this.values.length === 0) {
83
+ throw new errors_js_1.EmptyChannelError();
84
+ }
86
85
  return this.values;
87
86
  }
88
87
  checkpoint() {
@@ -13,7 +13,7 @@ export declare class Topic<Value> extends BaseChannel<Array<Value>, Value | Valu
13
13
  accumulate?: boolean;
14
14
  });
15
15
  fromCheckpoint(checkpoint?: [Value[], Value[]]): this;
16
- update(values: Array<Value | Value[]>): void;
16
+ update(values: Array<Value | Value[]>): boolean;
17
17
  get(): Array<Value>;
18
18
  checkpoint(): [Value[], Value[]];
19
19
  }
@@ -1,13 +1,7 @@
1
+ import { EmptyChannelError } from "../errors.js";
1
2
  import { BaseChannel } from "./base.js";
2
- function* flatten(values) {
3
- for (const value of values) {
4
- if (Array.isArray(value)) {
5
- yield* value;
6
- }
7
- else {
8
- yield value;
9
- }
10
- }
3
+ function arraysEqual(a, b) {
4
+ return a.length === b.length && a.every((val, index) => val === b[index]);
11
5
  }
12
6
  export class Topic extends BaseChannel {
13
7
  constructor(fields) {
@@ -61,11 +55,12 @@ export class Topic extends BaseChannel {
61
55
  return empty;
62
56
  }
63
57
  update(values) {
58
+ const current = [...this.values];
64
59
  if (!this.accumulate) {
65
60
  this.values = [];
66
61
  }
67
- const flatValues = flatten(values);
68
- if (flatValues) {
62
+ const flatValues = values.flat();
63
+ if (flatValues.length > 0) {
69
64
  if (this.unique) {
70
65
  for (const value of flatValues) {
71
66
  if (!this.seen.has(value)) {
@@ -78,8 +73,12 @@ export class Topic extends BaseChannel {
78
73
  this.values.push(...flatValues);
79
74
  }
80
75
  }
76
+ return !arraysEqual(this.values, current);
81
77
  }
82
78
  get() {
79
+ if (this.values.length === 0) {
80
+ throw new EmptyChannelError();
81
+ }
83
82
  return this.values;
84
83
  }
85
84
  checkpoint() {