@langchain/langgraph 0.2.3 → 0.2.5-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.
- package/dist/channels/base.cjs +16 -4
- package/dist/channels/base.d.ts +3 -0
- package/dist/channels/base.js +14 -3
- package/dist/constants.cjs +4 -1
- package/dist/constants.d.ts +3 -0
- package/dist/constants.js +3 -0
- package/dist/graph/annotation.cjs +5 -1
- package/dist/graph/annotation.d.ts +5 -4
- package/dist/graph/annotation.js +5 -1
- package/dist/graph/state.cjs +19 -7
- package/dist/graph/state.d.ts +13 -8
- package/dist/graph/state.js +18 -6
- package/dist/managed/base.cjs +163 -0
- package/dist/managed/base.d.ts +30 -0
- package/dist/managed/base.js +155 -0
- package/dist/managed/index.cjs +19 -0
- package/dist/managed/index.d.ts +3 -0
- package/dist/managed/index.js +3 -0
- package/dist/managed/is_last_step.cjs +11 -0
- package/dist/managed/is_last_step.d.ts +4 -0
- package/dist/managed/is_last_step.js +7 -0
- package/dist/managed/shared_value.cjs +109 -0
- package/dist/managed/shared_value.d.ts +23 -0
- package/dist/managed/shared_value.js +105 -0
- package/dist/prebuilt/agent_executor.d.ts +1 -1
- package/dist/pregel/algo.cjs +111 -50
- package/dist/pregel/algo.d.ts +7 -6
- package/dist/pregel/algo.js +112 -51
- package/dist/pregel/index.cjs +78 -6
- package/dist/pregel/index.d.ts +10 -2
- package/dist/pregel/index.js +80 -8
- package/dist/pregel/loop.cjs +40 -3
- package/dist/pregel/loop.d.ts +10 -0
- package/dist/pregel/loop.js +40 -3
- package/dist/pregel/types.d.ts +9 -2
- package/dist/pregel/validate.d.ts +3 -2
- package/dist/store/base.cjs +12 -0
- package/dist/store/base.d.ts +7 -0
- package/dist/store/base.js +8 -0
- package/dist/store/batch.cjs +126 -0
- package/dist/store/batch.d.ts +55 -0
- package/dist/store/batch.js +122 -0
- package/dist/store/index.cjs +19 -0
- package/dist/store/index.d.ts +3 -0
- package/dist/store/index.js +3 -0
- package/dist/store/memory.cjs +43 -0
- package/dist/store/memory.d.ts +6 -0
- package/dist/store/memory.js +39 -0
- package/dist/utils.cjs +26 -1
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +24 -0
- package/dist/web.cjs +2 -0
- package/dist/web.d.ts +2 -0
- package/dist/web.js +2 -0
- package/package.json +1 -1
package/dist/pregel/loop.js
CHANGED
|
@@ -8,6 +8,7 @@ import { mapInput, mapOutputUpdates, mapOutputValues } from "./io.js";
|
|
|
8
8
|
import { EmptyInputError, GraphInterrupt } from "../errors.js";
|
|
9
9
|
import { getNewChannelVersions } from "./utils.js";
|
|
10
10
|
import { mapDebugTasks, mapDebugCheckpoint, mapDebugTaskResults, } from "./debug.js";
|
|
11
|
+
import { AsyncBatchedStore } from "../store/batch.js";
|
|
11
12
|
const INPUT_DONE = Symbol.for("INPUT_DONE");
|
|
12
13
|
const INPUT_RESUMING = Symbol.for("INPUT_RESUMING");
|
|
13
14
|
const DEFAULT_LOOP_LIMIT = 25;
|
|
@@ -44,6 +45,12 @@ export class PregelLoop {
|
|
|
44
45
|
writable: true,
|
|
45
46
|
value: void 0
|
|
46
47
|
});
|
|
48
|
+
Object.defineProperty(this, "managed", {
|
|
49
|
+
enumerable: true,
|
|
50
|
+
configurable: true,
|
|
51
|
+
writable: true,
|
|
52
|
+
value: void 0
|
|
53
|
+
});
|
|
47
54
|
Object.defineProperty(this, "checkpoint", {
|
|
48
55
|
enumerable: true,
|
|
49
56
|
configurable: true,
|
|
@@ -148,6 +155,12 @@ export class PregelLoop {
|
|
|
148
155
|
writable: true,
|
|
149
156
|
value: Promise.resolve()
|
|
150
157
|
});
|
|
158
|
+
Object.defineProperty(this, "store", {
|
|
159
|
+
enumerable: true,
|
|
160
|
+
configurable: true,
|
|
161
|
+
writable: true,
|
|
162
|
+
value: void 0
|
|
163
|
+
});
|
|
151
164
|
this.input = params.input;
|
|
152
165
|
this.config = params.config;
|
|
153
166
|
this.checkpointer = params.checkpointer;
|
|
@@ -164,6 +177,7 @@ export class PregelLoop {
|
|
|
164
177
|
this.checkpointMetadata = params.checkpointMetadata;
|
|
165
178
|
this.checkpointPreviousVersions = params.checkpointPreviousVersions;
|
|
166
179
|
this.channels = params.channels;
|
|
180
|
+
this.managed = params.managed;
|
|
167
181
|
this.checkpointPendingWrites = params.checkpointPendingWrites;
|
|
168
182
|
this.step = params.step;
|
|
169
183
|
this.stop = params.stop;
|
|
@@ -172,6 +186,7 @@ export class PregelLoop {
|
|
|
172
186
|
this.streamKeys = params.streamKeys;
|
|
173
187
|
this.nodes = params.nodes;
|
|
174
188
|
this.skipDoneTasks = this.config.configurable?.checkpoint_id === undefined;
|
|
189
|
+
this.store = params.store;
|
|
175
190
|
}
|
|
176
191
|
static async initialize(params) {
|
|
177
192
|
const saved = (await params.checkpointer?.getTuple(params.config)) ?? {
|
|
@@ -199,6 +214,13 @@ export class PregelLoop {
|
|
|
199
214
|
const step = (checkpointMetadata.step ?? 0) + 1;
|
|
200
215
|
const stop = step + (params.config.recursionLimit ?? DEFAULT_LOOP_LIMIT) + 1;
|
|
201
216
|
const checkpointPreviousVersions = { ...checkpoint.channel_versions };
|
|
217
|
+
const store = params.store
|
|
218
|
+
? new AsyncBatchedStore(params.store)
|
|
219
|
+
: undefined;
|
|
220
|
+
if (store) {
|
|
221
|
+
// Start the store. This is a batch store, so it will run continuously
|
|
222
|
+
store.start();
|
|
223
|
+
}
|
|
202
224
|
return new PregelLoop({
|
|
203
225
|
input: params.input,
|
|
204
226
|
config: params.config,
|
|
@@ -207,6 +229,7 @@ export class PregelLoop {
|
|
|
207
229
|
checkpointMetadata,
|
|
208
230
|
checkpointConfig,
|
|
209
231
|
channels,
|
|
232
|
+
managed: params.managed,
|
|
210
233
|
step,
|
|
211
234
|
stop,
|
|
212
235
|
checkpointPreviousVersions,
|
|
@@ -214,6 +237,7 @@ export class PregelLoop {
|
|
|
214
237
|
outputKeys: params.outputKeys ?? [],
|
|
215
238
|
streamKeys: params.streamKeys ?? [],
|
|
216
239
|
nodes: params.nodes,
|
|
240
|
+
store,
|
|
217
241
|
});
|
|
218
242
|
}
|
|
219
243
|
_checkpointerPutAfterPrevious(input) {
|
|
@@ -222,6 +246,13 @@ export class PregelLoop {
|
|
|
222
246
|
});
|
|
223
247
|
this.checkpointerPromises.push(this._checkpointerChainedPromise);
|
|
224
248
|
}
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
250
|
+
async updateManagedValues(key, values) {
|
|
251
|
+
const mv = this.managed.get(key);
|
|
252
|
+
if (mv && "update" in mv && typeof mv.update === "function") {
|
|
253
|
+
await mv.update(values);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
225
256
|
/**
|
|
226
257
|
* Put writes for a task, to be read by the next tick.
|
|
227
258
|
* @param taskId
|
|
@@ -255,6 +286,9 @@ export class PregelLoop {
|
|
|
255
286
|
* @param params
|
|
256
287
|
*/
|
|
257
288
|
async tick(params) {
|
|
289
|
+
if (this.store && !this.store.isRunning) {
|
|
290
|
+
this.store?.start();
|
|
291
|
+
}
|
|
258
292
|
const { inputKeys = [], interruptAfter = [], interruptBefore = [], manager, } = params;
|
|
259
293
|
if (this.status !== "pending") {
|
|
260
294
|
throw new Error(`Cannot tick when status is no longer "pending". Current status: "${this.status}"`);
|
|
@@ -265,7 +299,10 @@ export class PregelLoop {
|
|
|
265
299
|
else if (this.tasks.every((task) => task.writes.length > 0)) {
|
|
266
300
|
const writes = this.tasks.flatMap((t) => t.writes);
|
|
267
301
|
// All tasks have finished
|
|
268
|
-
_applyWrites(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
302
|
+
const myWrites = _applyWrites(this.checkpoint, this.channels, this.tasks, this.checkpointerGetNextVersion);
|
|
303
|
+
for (const [key, values] of Object.entries(myWrites)) {
|
|
304
|
+
await this.updateManagedValues(key, values);
|
|
305
|
+
}
|
|
269
306
|
// produce values output
|
|
270
307
|
const valuesOutput = await gatherIterator(prefixGenerator(mapOutputValues(this.outputKeys, writes, this.channels), "values"));
|
|
271
308
|
this.stream.push(...valuesOutput);
|
|
@@ -293,7 +330,7 @@ export class PregelLoop {
|
|
|
293
330
|
this.status = "out_of_steps";
|
|
294
331
|
return false;
|
|
295
332
|
}
|
|
296
|
-
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.config, true, {
|
|
333
|
+
const nextTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, {
|
|
297
334
|
step: this.step,
|
|
298
335
|
checkpointer: this.checkpointer,
|
|
299
336
|
isResuming: this.input === INPUT_RESUMING,
|
|
@@ -371,7 +408,7 @@ export class PregelLoop {
|
|
|
371
408
|
if (inputWrites.length === 0) {
|
|
372
409
|
throw new EmptyInputError(`Received no input writes for ${JSON.stringify(inputKeys, null, 2)}`);
|
|
373
410
|
}
|
|
374
|
-
const discardTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.config, true, { step: this.step });
|
|
411
|
+
const discardTasks = _prepareNextTasks(this.checkpoint, this.nodes, this.channels, this.managed, this.config, true, { step: this.step });
|
|
375
412
|
_applyWrites(this.checkpoint, this.channels, discardTasks.concat([
|
|
376
413
|
{
|
|
377
414
|
name: INPUT,
|
package/dist/pregel/types.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { BaseChannel } from "../channels/base.js";
|
|
|
4
4
|
import type { PregelNode } from "./read.js";
|
|
5
5
|
import { RetryPolicy } from "./utils.js";
|
|
6
6
|
import { Interrupt } from "../constants.js";
|
|
7
|
+
import { BaseStore } from "../store/base.js";
|
|
8
|
+
import { type ManagedValueSpec } from "../managed/base.js";
|
|
7
9
|
export type StreamMode = "values" | "updates" | "debug";
|
|
8
10
|
/**
|
|
9
11
|
* Construct a type with a set of properties K of type T
|
|
@@ -11,7 +13,7 @@ export type StreamMode = "values" | "updates" | "debug";
|
|
|
11
13
|
type StrRecord<K extends string, T> = {
|
|
12
14
|
[P in K]: T;
|
|
13
15
|
};
|
|
14
|
-
export interface PregelInterface<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>> {
|
|
16
|
+
export interface PregelInterface<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel | ManagedValueSpec>> {
|
|
15
17
|
nodes: Nn;
|
|
16
18
|
channels: Cc;
|
|
17
19
|
/**
|
|
@@ -24,6 +26,7 @@ export interface PregelInterface<Nn extends StrRecord<string, PregelNode>, Cc ex
|
|
|
24
26
|
streamMode?: StreamMode | StreamMode[];
|
|
25
27
|
inputChannels: keyof Cc | Array<keyof Cc>;
|
|
26
28
|
outputChannels: keyof Cc | Array<keyof Cc>;
|
|
29
|
+
configKeys?: string[];
|
|
27
30
|
/**
|
|
28
31
|
* @default []
|
|
29
32
|
*/
|
|
@@ -44,8 +47,12 @@ export interface PregelInterface<Nn extends StrRecord<string, PregelNode>, Cc ex
|
|
|
44
47
|
debug?: boolean;
|
|
45
48
|
checkpointer?: BaseCheckpointSaver;
|
|
46
49
|
retryPolicy?: RetryPolicy;
|
|
50
|
+
/**
|
|
51
|
+
* Memory store to use for SharedValues.
|
|
52
|
+
*/
|
|
53
|
+
store?: BaseStore;
|
|
47
54
|
}
|
|
48
|
-
export type PregelParams<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel>> = Omit<PregelInterface<Nn, Cc>, "streamChannelsAsIs">;
|
|
55
|
+
export type PregelParams<Nn extends StrRecord<string, PregelNode>, Cc extends StrRecord<string, BaseChannel | ManagedValueSpec>> = Omit<PregelInterface<Nn, Cc>, "streamChannelsAsIs">;
|
|
49
56
|
export interface PregelTaskDescription {
|
|
50
57
|
readonly id: string;
|
|
51
58
|
readonly name: string;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { All } from "@langchain/langgraph-checkpoint";
|
|
2
2
|
import { BaseChannel } from "../channels/index.js";
|
|
3
3
|
import { PregelNode } from "./read.js";
|
|
4
|
+
import { type ManagedValueSpec } from "../managed/base.js";
|
|
4
5
|
export declare class GraphValidationError extends Error {
|
|
5
6
|
constructor(message?: string);
|
|
6
7
|
}
|
|
7
|
-
export declare function validateGraph<Nn extends Record<string, PregelNode>, Cc extends Record<string, BaseChannel>>({ nodes, channels, inputChannels, outputChannels, streamChannels, interruptAfterNodes, interruptBeforeNodes, }: {
|
|
8
|
+
export declare function validateGraph<Nn extends Record<string, PregelNode>, Cc extends Record<string, BaseChannel | ManagedValueSpec>>({ nodes, channels, inputChannels, outputChannels, streamChannels, interruptAfterNodes, interruptBeforeNodes, }: {
|
|
8
9
|
nodes: Nn;
|
|
9
10
|
channels: Cc;
|
|
10
11
|
inputChannels: keyof Cc | Array<keyof Cc>;
|
|
@@ -13,4 +14,4 @@ export declare function validateGraph<Nn extends Record<string, PregelNode>, Cc
|
|
|
13
14
|
interruptAfterNodes?: Array<keyof Nn> | All;
|
|
14
15
|
interruptBeforeNodes?: Array<keyof Nn> | All;
|
|
15
16
|
}): void;
|
|
16
|
-
export declare function validateKeys<Cc extends Record<string, BaseChannel>>(keys: keyof Cc | Array<keyof Cc>, channels: Cc): void;
|
|
17
|
+
export declare function validateKeys<Cc extends Record<string, BaseChannel | ManagedValueSpec>>(keys: keyof Cc | Array<keyof Cc>, channels: Cc): void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseStore = void 0;
|
|
4
|
+
class BaseStore {
|
|
5
|
+
stop() {
|
|
6
|
+
// no-op if not implemented.
|
|
7
|
+
}
|
|
8
|
+
start() {
|
|
9
|
+
// no-op if not implemented.
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
exports.BaseStore = BaseStore;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type Values = Record<string, any>;
|
|
2
|
+
export declare abstract class BaseStore {
|
|
3
|
+
abstract list(prefixes: string[]): Promise<Record<string, Record<string, Values>>>;
|
|
4
|
+
abstract put(writes: Array<[string, string, Values | null]>): Promise<void>;
|
|
5
|
+
stop(): void;
|
|
6
|
+
start(): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AsyncBatchedStore = void 0;
|
|
4
|
+
const base_js_1 = require("./base.cjs");
|
|
5
|
+
/**
|
|
6
|
+
* AsyncBatchedStore extends BaseStore to provide batched operations for list and put methods.
|
|
7
|
+
* It queues operations and processes them in batches for improved efficiency. This store is
|
|
8
|
+
* designed to run for the full duration of the process, or until `stop()` is called.
|
|
9
|
+
*/
|
|
10
|
+
class AsyncBatchedStore extends base_js_1.BaseStore {
|
|
11
|
+
get isRunning() {
|
|
12
|
+
return this.running;
|
|
13
|
+
}
|
|
14
|
+
constructor(store) {
|
|
15
|
+
super();
|
|
16
|
+
/**
|
|
17
|
+
* The store to batch operations for.
|
|
18
|
+
* @type {BaseStore}
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(this, "store", {
|
|
21
|
+
enumerable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
writable: true,
|
|
24
|
+
value: void 0
|
|
25
|
+
});
|
|
26
|
+
/**
|
|
27
|
+
* A queue of operations to be processed in batch.
|
|
28
|
+
* @type {QueueItem[]}
|
|
29
|
+
*/
|
|
30
|
+
Object.defineProperty(this, "queue", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: []
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Whether or not the batched processing is currently running.
|
|
38
|
+
* @type {boolean}
|
|
39
|
+
* @default {false}
|
|
40
|
+
*/
|
|
41
|
+
Object.defineProperty(this, "running", {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
writable: true,
|
|
45
|
+
value: false
|
|
46
|
+
});
|
|
47
|
+
this.store = store;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Queues a list operation to be processed in batch.
|
|
51
|
+
* @param {string[]} prefixes An array of prefixes to list.
|
|
52
|
+
* @returns {Promise<Record<string, Record<string, Values>>>} A promise that resolves with the list results.
|
|
53
|
+
*/
|
|
54
|
+
async list(prefixes) {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
this.queue.push({ resolve, reject, op: { prefixes } });
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Queues a put operation to be processed in batch.
|
|
61
|
+
* @param {Array<[string, string, Values | null]>} writes An array of write operations to be performed.
|
|
62
|
+
* @returns {Promise<void>} A promise that resolves when the put operation is complete.
|
|
63
|
+
*/
|
|
64
|
+
async put(writes) {
|
|
65
|
+
return new Promise((resolve, reject) => {
|
|
66
|
+
this.queue.push({ resolve, reject, op: { writes } });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Start running the batched processing of operations.
|
|
71
|
+
* This process will run continuously until the store is stopped,
|
|
72
|
+
* which can be done by calling the `stop()` method.
|
|
73
|
+
*/
|
|
74
|
+
start() {
|
|
75
|
+
this.running = true;
|
|
76
|
+
void this.processBatchQueue();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Stops the batched processing of operations.
|
|
80
|
+
*/
|
|
81
|
+
stop() {
|
|
82
|
+
this.running = false;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Runs the task that processes queued operations in batches.
|
|
86
|
+
* This method runs continuously until the store is stopped,
|
|
87
|
+
* or the process is terminated.
|
|
88
|
+
* @returns {Promise<void>} A promise that resolves when the task is complete.
|
|
89
|
+
*/
|
|
90
|
+
async processBatchQueue() {
|
|
91
|
+
while (this.running) {
|
|
92
|
+
await new Promise((resolve) => {
|
|
93
|
+
setTimeout(resolve, 0);
|
|
94
|
+
});
|
|
95
|
+
if (this.queue.length === 0)
|
|
96
|
+
continue;
|
|
97
|
+
const taken = this.queue.splice(0);
|
|
98
|
+
const lists = taken.filter((item) => "prefixes" in item.op);
|
|
99
|
+
if (lists.length > 0) {
|
|
100
|
+
try {
|
|
101
|
+
const allPrefixes = lists.flatMap((item) => item.op.prefixes);
|
|
102
|
+
const results = await this.store.list(allPrefixes);
|
|
103
|
+
lists.forEach((item) => {
|
|
104
|
+
const { prefixes } = item.op;
|
|
105
|
+
item.resolve(Object.fromEntries(prefixes.map((p) => [p, results[p] || {}])));
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (e) {
|
|
109
|
+
lists.forEach((item) => item.reject(e));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const puts = taken.filter((item) => "writes" in item.op);
|
|
113
|
+
if (puts.length > 0) {
|
|
114
|
+
try {
|
|
115
|
+
const allWrites = puts.flatMap((item) => item.op.writes);
|
|
116
|
+
await this.store.put(allWrites);
|
|
117
|
+
puts.forEach((item) => item.resolve());
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
puts.forEach((item) => item.reject(e));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
exports.AsyncBatchedStore = AsyncBatchedStore;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { BaseStore, type Values } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* AsyncBatchedStore extends BaseStore to provide batched operations for list and put methods.
|
|
4
|
+
* It queues operations and processes them in batches for improved efficiency. This store is
|
|
5
|
+
* designed to run for the full duration of the process, or until `stop()` is called.
|
|
6
|
+
*/
|
|
7
|
+
export declare class AsyncBatchedStore extends BaseStore {
|
|
8
|
+
/**
|
|
9
|
+
* The store to batch operations for.
|
|
10
|
+
* @type {BaseStore}
|
|
11
|
+
*/
|
|
12
|
+
private store;
|
|
13
|
+
/**
|
|
14
|
+
* A queue of operations to be processed in batch.
|
|
15
|
+
* @type {QueueItem[]}
|
|
16
|
+
*/
|
|
17
|
+
private queue;
|
|
18
|
+
/**
|
|
19
|
+
* Whether or not the batched processing is currently running.
|
|
20
|
+
* @type {boolean}
|
|
21
|
+
* @default {false}
|
|
22
|
+
*/
|
|
23
|
+
private running;
|
|
24
|
+
get isRunning(): boolean;
|
|
25
|
+
constructor(store: BaseStore);
|
|
26
|
+
/**
|
|
27
|
+
* Queues a list operation to be processed in batch.
|
|
28
|
+
* @param {string[]} prefixes An array of prefixes to list.
|
|
29
|
+
* @returns {Promise<Record<string, Record<string, Values>>>} A promise that resolves with the list results.
|
|
30
|
+
*/
|
|
31
|
+
list(prefixes: string[]): Promise<Record<string, Record<string, Values>>>;
|
|
32
|
+
/**
|
|
33
|
+
* Queues a put operation to be processed in batch.
|
|
34
|
+
* @param {Array<[string, string, Values | null]>} writes An array of write operations to be performed.
|
|
35
|
+
* @returns {Promise<void>} A promise that resolves when the put operation is complete.
|
|
36
|
+
*/
|
|
37
|
+
put(writes: Array<[string, string, Values | null]>): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Start running the batched processing of operations.
|
|
40
|
+
* This process will run continuously until the store is stopped,
|
|
41
|
+
* which can be done by calling the `stop()` method.
|
|
42
|
+
*/
|
|
43
|
+
start(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Stops the batched processing of operations.
|
|
46
|
+
*/
|
|
47
|
+
stop(): void;
|
|
48
|
+
/**
|
|
49
|
+
* Runs the task that processes queued operations in batches.
|
|
50
|
+
* This method runs continuously until the store is stopped,
|
|
51
|
+
* or the process is terminated.
|
|
52
|
+
* @returns {Promise<void>} A promise that resolves when the task is complete.
|
|
53
|
+
*/
|
|
54
|
+
private processBatchQueue;
|
|
55
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { BaseStore } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* AsyncBatchedStore extends BaseStore to provide batched operations for list and put methods.
|
|
4
|
+
* It queues operations and processes them in batches for improved efficiency. This store is
|
|
5
|
+
* designed to run for the full duration of the process, or until `stop()` is called.
|
|
6
|
+
*/
|
|
7
|
+
export class AsyncBatchedStore extends BaseStore {
|
|
8
|
+
get isRunning() {
|
|
9
|
+
return this.running;
|
|
10
|
+
}
|
|
11
|
+
constructor(store) {
|
|
12
|
+
super();
|
|
13
|
+
/**
|
|
14
|
+
* The store to batch operations for.
|
|
15
|
+
* @type {BaseStore}
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(this, "store", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* A queue of operations to be processed in batch.
|
|
25
|
+
* @type {QueueItem[]}
|
|
26
|
+
*/
|
|
27
|
+
Object.defineProperty(this, "queue", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: []
|
|
32
|
+
});
|
|
33
|
+
/**
|
|
34
|
+
* Whether or not the batched processing is currently running.
|
|
35
|
+
* @type {boolean}
|
|
36
|
+
* @default {false}
|
|
37
|
+
*/
|
|
38
|
+
Object.defineProperty(this, "running", {
|
|
39
|
+
enumerable: true,
|
|
40
|
+
configurable: true,
|
|
41
|
+
writable: true,
|
|
42
|
+
value: false
|
|
43
|
+
});
|
|
44
|
+
this.store = store;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Queues a list operation to be processed in batch.
|
|
48
|
+
* @param {string[]} prefixes An array of prefixes to list.
|
|
49
|
+
* @returns {Promise<Record<string, Record<string, Values>>>} A promise that resolves with the list results.
|
|
50
|
+
*/
|
|
51
|
+
async list(prefixes) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
this.queue.push({ resolve, reject, op: { prefixes } });
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Queues a put operation to be processed in batch.
|
|
58
|
+
* @param {Array<[string, string, Values | null]>} writes An array of write operations to be performed.
|
|
59
|
+
* @returns {Promise<void>} A promise that resolves when the put operation is complete.
|
|
60
|
+
*/
|
|
61
|
+
async put(writes) {
|
|
62
|
+
return new Promise((resolve, reject) => {
|
|
63
|
+
this.queue.push({ resolve, reject, op: { writes } });
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Start running the batched processing of operations.
|
|
68
|
+
* This process will run continuously until the store is stopped,
|
|
69
|
+
* which can be done by calling the `stop()` method.
|
|
70
|
+
*/
|
|
71
|
+
start() {
|
|
72
|
+
this.running = true;
|
|
73
|
+
void this.processBatchQueue();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stops the batched processing of operations.
|
|
77
|
+
*/
|
|
78
|
+
stop() {
|
|
79
|
+
this.running = false;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Runs the task that processes queued operations in batches.
|
|
83
|
+
* This method runs continuously until the store is stopped,
|
|
84
|
+
* or the process is terminated.
|
|
85
|
+
* @returns {Promise<void>} A promise that resolves when the task is complete.
|
|
86
|
+
*/
|
|
87
|
+
async processBatchQueue() {
|
|
88
|
+
while (this.running) {
|
|
89
|
+
await new Promise((resolve) => {
|
|
90
|
+
setTimeout(resolve, 0);
|
|
91
|
+
});
|
|
92
|
+
if (this.queue.length === 0)
|
|
93
|
+
continue;
|
|
94
|
+
const taken = this.queue.splice(0);
|
|
95
|
+
const lists = taken.filter((item) => "prefixes" in item.op);
|
|
96
|
+
if (lists.length > 0) {
|
|
97
|
+
try {
|
|
98
|
+
const allPrefixes = lists.flatMap((item) => item.op.prefixes);
|
|
99
|
+
const results = await this.store.list(allPrefixes);
|
|
100
|
+
lists.forEach((item) => {
|
|
101
|
+
const { prefixes } = item.op;
|
|
102
|
+
item.resolve(Object.fromEntries(prefixes.map((p) => [p, results[p] || {}])));
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (e) {
|
|
106
|
+
lists.forEach((item) => item.reject(e));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const puts = taken.filter((item) => "writes" in item.op);
|
|
110
|
+
if (puts.length > 0) {
|
|
111
|
+
try {
|
|
112
|
+
const allWrites = puts.flatMap((item) => item.op.writes);
|
|
113
|
+
await this.store.put(allWrites);
|
|
114
|
+
puts.forEach((item) => item.resolve());
|
|
115
|
+
}
|
|
116
|
+
catch (e) {
|
|
117
|
+
puts.forEach((item) => item.reject(e));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./base.cjs"), exports);
|
|
18
|
+
__exportStar(require("./batch.cjs"), exports);
|
|
19
|
+
__exportStar(require("./memory.cjs"), exports);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryStore = void 0;
|
|
4
|
+
const base_js_1 = require("./base.cjs");
|
|
5
|
+
class MemoryStore extends base_js_1.BaseStore {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
Object.defineProperty(this, "data", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
writable: true,
|
|
12
|
+
value: new Map()
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
async list(prefixes) {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
const result = {};
|
|
18
|
+
for (const prefix of prefixes) {
|
|
19
|
+
if (this.data.has(prefix)) {
|
|
20
|
+
result[prefix] = Object.fromEntries(this.data.get(prefix));
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
result[prefix] = {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return Promise.resolve(result);
|
|
27
|
+
}
|
|
28
|
+
async put(writes) {
|
|
29
|
+
for (const [namespace, key, value] of writes) {
|
|
30
|
+
if (!this.data.has(namespace)) {
|
|
31
|
+
this.data.set(namespace, new Map());
|
|
32
|
+
}
|
|
33
|
+
const namespaceMap = this.data.get(namespace);
|
|
34
|
+
if (value === null) {
|
|
35
|
+
namespaceMap.delete(key);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
namespaceMap.set(key, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.MemoryStore = MemoryStore;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BaseStore, type Values } from "./base.js";
|
|
2
|
+
export declare class MemoryStore extends BaseStore {
|
|
3
|
+
private data;
|
|
4
|
+
list(prefixes: string[]): Promise<Record<string, Record<string, Values>>>;
|
|
5
|
+
put(writes: Array<[string, string, Values | null]>): Promise<void>;
|
|
6
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { BaseStore } from "./base.js";
|
|
2
|
+
export class MemoryStore extends BaseStore {
|
|
3
|
+
constructor() {
|
|
4
|
+
super(...arguments);
|
|
5
|
+
Object.defineProperty(this, "data", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: new Map()
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async list(prefixes) {
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
14
|
+
const result = {};
|
|
15
|
+
for (const prefix of prefixes) {
|
|
16
|
+
if (this.data.has(prefix)) {
|
|
17
|
+
result[prefix] = Object.fromEntries(this.data.get(prefix));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
result[prefix] = {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return Promise.resolve(result);
|
|
24
|
+
}
|
|
25
|
+
async put(writes) {
|
|
26
|
+
for (const [namespace, key, value] of writes) {
|
|
27
|
+
if (!this.data.has(namespace)) {
|
|
28
|
+
this.data.set(namespace, new Map());
|
|
29
|
+
}
|
|
30
|
+
const namespaceMap = this.data.get(namespace);
|
|
31
|
+
if (value === null) {
|
|
32
|
+
namespaceMap.delete(key);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
namespaceMap.set(key, value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|