@langchain/langgraph-checkpoint 0.0.6 → 0.0.8-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/index.cjs +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/memory.cjs +55 -49
- package/dist/memory.js +55 -49
- package/dist/serde/jsonplus.cjs +55 -83
- package/dist/serde/jsonplus.js +55 -83
- package/dist/serde/utils/fast-safe-stringify/index.cjs +222 -0
- package/dist/serde/utils/fast-safe-stringify/index.d.ts +1 -0
- package/dist/serde/utils/fast-safe-stringify/index.js +218 -0
- package/dist/store/base.cjs +98 -0
- package/dist/store/base.d.ts +174 -0
- package/dist/store/base.js +94 -0
- package/dist/store/batch.cjs +121 -0
- package/dist/store/batch.d.ts +29 -0
- package/dist/store/batch.js +117 -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 +116 -0
- package/dist/store/memory.d.ts +10 -0
- package/dist/store/memory.js +112 -0
- package/dist/types.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a stored item with metadata.
|
|
3
|
+
*/
|
|
4
|
+
export interface Item {
|
|
5
|
+
/**
|
|
6
|
+
* The stored data as an object. Keys are filterable.
|
|
7
|
+
*/
|
|
8
|
+
value: Record<string, any>;
|
|
9
|
+
/**
|
|
10
|
+
* Unique identifier within the namespace.
|
|
11
|
+
*/
|
|
12
|
+
id: string;
|
|
13
|
+
/**
|
|
14
|
+
* Hierarchical path defining the collection in which this document resides.
|
|
15
|
+
* Represented as an array of strings, allowing for nested categorization.
|
|
16
|
+
* For example: ["documents", "user123"]
|
|
17
|
+
*/
|
|
18
|
+
namespace: string[];
|
|
19
|
+
/**
|
|
20
|
+
* Timestamp of item creation.
|
|
21
|
+
*/
|
|
22
|
+
createdAt: Date;
|
|
23
|
+
/**
|
|
24
|
+
* Timestamp of last update.
|
|
25
|
+
*/
|
|
26
|
+
updatedAt: Date;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Operation to retrieve an item by namespace and ID.
|
|
30
|
+
*/
|
|
31
|
+
export interface GetOperation {
|
|
32
|
+
/**
|
|
33
|
+
* Hierarchical path for the item.
|
|
34
|
+
*/
|
|
35
|
+
namespace: string[];
|
|
36
|
+
/**
|
|
37
|
+
* Unique identifier within the namespace.
|
|
38
|
+
*/
|
|
39
|
+
id: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Operation to search for items within a namespace prefix.
|
|
43
|
+
*/
|
|
44
|
+
export interface SearchOperation {
|
|
45
|
+
/**
|
|
46
|
+
* Hierarchical path prefix to search within.
|
|
47
|
+
*/
|
|
48
|
+
namespacePrefix: string[];
|
|
49
|
+
/**
|
|
50
|
+
* Key-value pairs to filter results.
|
|
51
|
+
*/
|
|
52
|
+
filter?: Record<string, any>;
|
|
53
|
+
/**
|
|
54
|
+
* Maximum number of items to return.
|
|
55
|
+
* @default 10
|
|
56
|
+
*/
|
|
57
|
+
limit?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Number of items to skip before returning results.
|
|
60
|
+
* @default 0
|
|
61
|
+
*/
|
|
62
|
+
offset?: number;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Operation to store, update, or delete an item.
|
|
66
|
+
*/
|
|
67
|
+
export interface PutOperation {
|
|
68
|
+
/**
|
|
69
|
+
* Hierarchical path for the item.
|
|
70
|
+
* Represented as a tuple of strings, allowing for nested categorization.
|
|
71
|
+
* @example ["documents", "user123"]
|
|
72
|
+
*/
|
|
73
|
+
namespace: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Unique identifier for the document.
|
|
76
|
+
* Should be distinct within its namespace.
|
|
77
|
+
*/
|
|
78
|
+
id: string;
|
|
79
|
+
/**
|
|
80
|
+
* Data to be stored, or None to delete the item.
|
|
81
|
+
* Schema:
|
|
82
|
+
* - Should be an object where:
|
|
83
|
+
* - Keys are strings representing field names
|
|
84
|
+
* - Values can be of any serializable type
|
|
85
|
+
* - If null, it indicates that the item should be deleted
|
|
86
|
+
*/
|
|
87
|
+
value: Record<string, any> | null;
|
|
88
|
+
}
|
|
89
|
+
export type NameSpacePath = (string | "*")[];
|
|
90
|
+
export type NamespaceMatchType = "prefix" | "suffix";
|
|
91
|
+
export interface MatchCondition {
|
|
92
|
+
matchType: NamespaceMatchType;
|
|
93
|
+
path: NameSpacePath;
|
|
94
|
+
}
|
|
95
|
+
export interface ListNamespacesOperation {
|
|
96
|
+
matchConditions?: MatchCondition[];
|
|
97
|
+
maxDepth?: number;
|
|
98
|
+
limit: number;
|
|
99
|
+
offset: number;
|
|
100
|
+
}
|
|
101
|
+
export type Operation = GetOperation | SearchOperation | PutOperation | ListNamespacesOperation;
|
|
102
|
+
export type OperationResults<Tuple extends readonly Operation[]> = {
|
|
103
|
+
[K in keyof Tuple]: Tuple[K] extends PutOperation ? void : Tuple[K] extends SearchOperation ? Item[] : Tuple[K] extends GetOperation ? Item | null : Tuple[K] extends ListNamespacesOperation ? string[][] : never;
|
|
104
|
+
};
|
|
105
|
+
/**
|
|
106
|
+
* Abstract base class for key-value stores.
|
|
107
|
+
*/
|
|
108
|
+
export declare abstract class BaseStore {
|
|
109
|
+
/**
|
|
110
|
+
* Execute a batch of operations.
|
|
111
|
+
* @param _operations An array of operations to execute.
|
|
112
|
+
* @returns A promise that resolves to the results of the operations.
|
|
113
|
+
*/
|
|
114
|
+
abstract batch<Op extends Operation[]>(_operations: Op): Promise<OperationResults<Op>>;
|
|
115
|
+
/**
|
|
116
|
+
* Retrieve a single item.
|
|
117
|
+
* @param namespace Hierarchical path for the item.
|
|
118
|
+
* @param id Unique identifier within the namespace.
|
|
119
|
+
* @returns A promise that resolves to the retrieved item or null if not found.
|
|
120
|
+
*/
|
|
121
|
+
get(namespace: string[], id: string): Promise<Item | null>;
|
|
122
|
+
/**
|
|
123
|
+
* Search for items within a namespace prefix.
|
|
124
|
+
* @param namespacePrefix Hierarchical path prefix to search within.
|
|
125
|
+
* @param options Search options.
|
|
126
|
+
* @param options.filter Key-value pairs to filter results.
|
|
127
|
+
* @param options.limit Maximum number of items to return (default: 10).
|
|
128
|
+
* @param options.offset Number of items to skip before returning results (default: 0).
|
|
129
|
+
* @returns A promise that resolves to a list of items matching the search criteria.
|
|
130
|
+
*/
|
|
131
|
+
search(namespacePrefix: string[], options?: {
|
|
132
|
+
filter?: Record<string, any>;
|
|
133
|
+
limit?: number;
|
|
134
|
+
offset?: number;
|
|
135
|
+
}): Promise<Item[]>;
|
|
136
|
+
/**
|
|
137
|
+
* Store or update an item.
|
|
138
|
+
* @param namespace Hierarchical path for the item.
|
|
139
|
+
* @param id Unique identifier within the namespace.
|
|
140
|
+
* @param value Object containing the item's data.
|
|
141
|
+
*/
|
|
142
|
+
put(namespace: string[], id: string, value: Record<string, any>): Promise<void>;
|
|
143
|
+
/**
|
|
144
|
+
* Delete an item.
|
|
145
|
+
* @param namespace Hierarchical path for the item.
|
|
146
|
+
* @param id Unique identifier within the namespace.
|
|
147
|
+
*/
|
|
148
|
+
delete(namespace: string[], id: string): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* List and filter namespaces in the store.
|
|
151
|
+
* @param options Options for listing namespaces.
|
|
152
|
+
* @param options.prefix Filter namespaces that start with this path.
|
|
153
|
+
* @param options.suffix Filter namespaces that end with this path.
|
|
154
|
+
* @param options.maxDepth Return namespaces up to this depth in the hierarchy.
|
|
155
|
+
* @param options.limit Maximum number of namespaces to return (default 100).
|
|
156
|
+
* @param options.offset Number of namespaces to skip for pagination (default 0).
|
|
157
|
+
* @returns A promise that resolves to a list of namespace arrays that match the criteria.
|
|
158
|
+
*/
|
|
159
|
+
listNamespaces(options: {
|
|
160
|
+
prefix?: string[];
|
|
161
|
+
suffix?: string[];
|
|
162
|
+
maxDepth?: number;
|
|
163
|
+
limit?: number;
|
|
164
|
+
offset?: number;
|
|
165
|
+
}): Promise<string[][]>;
|
|
166
|
+
/**
|
|
167
|
+
* Stop the store. No-op if not implemented.
|
|
168
|
+
*/
|
|
169
|
+
stop(): void;
|
|
170
|
+
/**
|
|
171
|
+
* Start the store. No-op if not implemented.
|
|
172
|
+
*/
|
|
173
|
+
start(): void;
|
|
174
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
/**
|
|
3
|
+
* Abstract base class for key-value stores.
|
|
4
|
+
*/
|
|
5
|
+
export class BaseStore {
|
|
6
|
+
// convenience methods
|
|
7
|
+
/**
|
|
8
|
+
* Retrieve a single item.
|
|
9
|
+
* @param namespace Hierarchical path for the item.
|
|
10
|
+
* @param id Unique identifier within the namespace.
|
|
11
|
+
* @returns A promise that resolves to the retrieved item or null if not found.
|
|
12
|
+
*/
|
|
13
|
+
async get(namespace, id) {
|
|
14
|
+
const batchResult = await this.batch([{ namespace, id }]);
|
|
15
|
+
return batchResult[0];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Search for items within a namespace prefix.
|
|
19
|
+
* @param namespacePrefix Hierarchical path prefix to search within.
|
|
20
|
+
* @param options Search options.
|
|
21
|
+
* @param options.filter Key-value pairs to filter results.
|
|
22
|
+
* @param options.limit Maximum number of items to return (default: 10).
|
|
23
|
+
* @param options.offset Number of items to skip before returning results (default: 0).
|
|
24
|
+
* @returns A promise that resolves to a list of items matching the search criteria.
|
|
25
|
+
*/
|
|
26
|
+
async search(namespacePrefix, options) {
|
|
27
|
+
const optionsWithDefaults = {
|
|
28
|
+
limit: 10,
|
|
29
|
+
offset: 0,
|
|
30
|
+
...(options || {}),
|
|
31
|
+
};
|
|
32
|
+
const batchResults = await this.batch([
|
|
33
|
+
{ namespacePrefix, ...optionsWithDefaults },
|
|
34
|
+
]);
|
|
35
|
+
return batchResults[0];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Store or update an item.
|
|
39
|
+
* @param namespace Hierarchical path for the item.
|
|
40
|
+
* @param id Unique identifier within the namespace.
|
|
41
|
+
* @param value Object containing the item's data.
|
|
42
|
+
*/
|
|
43
|
+
async put(namespace, id, value) {
|
|
44
|
+
await this.batch([{ namespace, id, value }]);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Delete an item.
|
|
48
|
+
* @param namespace Hierarchical path for the item.
|
|
49
|
+
* @param id Unique identifier within the namespace.
|
|
50
|
+
*/
|
|
51
|
+
async delete(namespace, id) {
|
|
52
|
+
await this.batch([{ namespace, id, value: null }]);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* List and filter namespaces in the store.
|
|
56
|
+
* @param options Options for listing namespaces.
|
|
57
|
+
* @param options.prefix Filter namespaces that start with this path.
|
|
58
|
+
* @param options.suffix Filter namespaces that end with this path.
|
|
59
|
+
* @param options.maxDepth Return namespaces up to this depth in the hierarchy.
|
|
60
|
+
* @param options.limit Maximum number of namespaces to return (default 100).
|
|
61
|
+
* @param options.offset Number of namespaces to skip for pagination (default 0).
|
|
62
|
+
* @returns A promise that resolves to a list of namespace arrays that match the criteria.
|
|
63
|
+
*/
|
|
64
|
+
async listNamespaces(options) {
|
|
65
|
+
const { prefix, suffix, maxDepth, limit = 100, offset = 0 } = options;
|
|
66
|
+
const matchConditions = [];
|
|
67
|
+
if (prefix) {
|
|
68
|
+
matchConditions.push({ matchType: "prefix", path: prefix });
|
|
69
|
+
}
|
|
70
|
+
if (suffix) {
|
|
71
|
+
matchConditions.push({ matchType: "suffix", path: suffix });
|
|
72
|
+
}
|
|
73
|
+
const op = {
|
|
74
|
+
matchConditions: matchConditions.length > 0 ? matchConditions : undefined,
|
|
75
|
+
maxDepth,
|
|
76
|
+
limit,
|
|
77
|
+
offset,
|
|
78
|
+
};
|
|
79
|
+
const batchResults = await this.batch([op]);
|
|
80
|
+
return batchResults[0];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Stop the store. No-op if not implemented.
|
|
84
|
+
*/
|
|
85
|
+
stop() {
|
|
86
|
+
// no-op if not implemented.
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Start the store. No-op if not implemented.
|
|
90
|
+
*/
|
|
91
|
+
start() {
|
|
92
|
+
// no-op if not implemented.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.AsyncBatchedStore = void 0;
|
|
5
|
+
const base_js_1 = require("./base.cjs");
|
|
6
|
+
class AsyncBatchedStore extends base_js_1.BaseStore {
|
|
7
|
+
constructor(store) {
|
|
8
|
+
super();
|
|
9
|
+
Object.defineProperty(this, "store", {
|
|
10
|
+
enumerable: true,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true,
|
|
13
|
+
value: void 0
|
|
14
|
+
});
|
|
15
|
+
Object.defineProperty(this, "queue", {
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
writable: true,
|
|
19
|
+
value: new Map()
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(this, "nextKey", {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
configurable: true,
|
|
24
|
+
writable: true,
|
|
25
|
+
value: 0
|
|
26
|
+
});
|
|
27
|
+
Object.defineProperty(this, "running", {
|
|
28
|
+
enumerable: true,
|
|
29
|
+
configurable: true,
|
|
30
|
+
writable: true,
|
|
31
|
+
value: false
|
|
32
|
+
});
|
|
33
|
+
Object.defineProperty(this, "processingTask", {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: true,
|
|
36
|
+
writable: true,
|
|
37
|
+
value: null
|
|
38
|
+
});
|
|
39
|
+
this.store = store;
|
|
40
|
+
}
|
|
41
|
+
get isRunning() {
|
|
42
|
+
return this.running;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @ignore
|
|
46
|
+
* Batch is not implemented here as we're only extending `BaseStore`
|
|
47
|
+
* to allow it to be passed where `BaseStore` is expected, and implement
|
|
48
|
+
* the convenience methods (get, search, put, delete).
|
|
49
|
+
*/
|
|
50
|
+
async batch(_operations) {
|
|
51
|
+
throw new Error("The `batch` method is not implemented on `AsyncBatchedStore`." +
|
|
52
|
+
"\n Instead, it calls the `batch` method on the wrapped store." +
|
|
53
|
+
"\n If you are seeing this error, something is wrong.");
|
|
54
|
+
}
|
|
55
|
+
async get(namespace, id) {
|
|
56
|
+
return this.enqueueOperation({ namespace, id });
|
|
57
|
+
}
|
|
58
|
+
async search(namespacePrefix, options) {
|
|
59
|
+
const { filter, limit = 10, offset = 0 } = options || {};
|
|
60
|
+
return this.enqueueOperation({
|
|
61
|
+
namespacePrefix,
|
|
62
|
+
filter,
|
|
63
|
+
limit,
|
|
64
|
+
offset,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
async put(namespace, id, value) {
|
|
68
|
+
return this.enqueueOperation({ namespace, id, value });
|
|
69
|
+
}
|
|
70
|
+
async delete(namespace, id) {
|
|
71
|
+
return this.enqueueOperation({
|
|
72
|
+
namespace,
|
|
73
|
+
id,
|
|
74
|
+
value: null,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
start() {
|
|
78
|
+
if (!this.running) {
|
|
79
|
+
this.running = true;
|
|
80
|
+
this.processingTask = this.processBatchQueue();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async stop() {
|
|
84
|
+
this.running = false;
|
|
85
|
+
if (this.processingTask) {
|
|
86
|
+
await this.processingTask;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
enqueueOperation(operation) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const key = this.nextKey;
|
|
92
|
+
this.nextKey += 1;
|
|
93
|
+
this.queue.set(key, { operation, resolve, reject });
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async processBatchQueue() {
|
|
97
|
+
while (this.running) {
|
|
98
|
+
await new Promise((resolve) => {
|
|
99
|
+
setTimeout(resolve, 0);
|
|
100
|
+
});
|
|
101
|
+
if (this.queue.size === 0)
|
|
102
|
+
continue;
|
|
103
|
+
const batch = new Map(this.queue);
|
|
104
|
+
this.queue.clear();
|
|
105
|
+
try {
|
|
106
|
+
const operations = Array.from(batch.values()).map(({ operation }) => operation);
|
|
107
|
+
const results = await this.store.batch(operations);
|
|
108
|
+
batch.forEach(({ resolve }, key) => {
|
|
109
|
+
const index = Array.from(batch.keys()).indexOf(key);
|
|
110
|
+
resolve(results[index]);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
batch.forEach(({ reject }) => {
|
|
115
|
+
reject(e);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.AsyncBatchedStore = AsyncBatchedStore;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { BaseStore, type Item, type Operation, OperationResults } from "./base.js";
|
|
2
|
+
export declare class AsyncBatchedStore extends BaseStore {
|
|
3
|
+
private store;
|
|
4
|
+
private queue;
|
|
5
|
+
private nextKey;
|
|
6
|
+
private running;
|
|
7
|
+
private processingTask;
|
|
8
|
+
constructor(store: BaseStore);
|
|
9
|
+
get isRunning(): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* @ignore
|
|
12
|
+
* Batch is not implemented here as we're only extending `BaseStore`
|
|
13
|
+
* to allow it to be passed where `BaseStore` is expected, and implement
|
|
14
|
+
* the convenience methods (get, search, put, delete).
|
|
15
|
+
*/
|
|
16
|
+
batch<Op extends Operation[]>(_operations: Op): Promise<OperationResults<Op>>;
|
|
17
|
+
get(namespace: string[], id: string): Promise<Item | null>;
|
|
18
|
+
search(namespacePrefix: string[], options?: {
|
|
19
|
+
filter?: Record<string, any>;
|
|
20
|
+
limit?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
}): Promise<Item[]>;
|
|
23
|
+
put(namespace: string[], id: string, value: Record<string, any>): Promise<void>;
|
|
24
|
+
delete(namespace: string[], id: string): Promise<void>;
|
|
25
|
+
start(): void;
|
|
26
|
+
stop(): Promise<void>;
|
|
27
|
+
private enqueueOperation;
|
|
28
|
+
private processBatchQueue;
|
|
29
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { BaseStore, } from "./base.js";
|
|
3
|
+
export class AsyncBatchedStore extends BaseStore {
|
|
4
|
+
constructor(store) {
|
|
5
|
+
super();
|
|
6
|
+
Object.defineProperty(this, "store", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
configurable: true,
|
|
9
|
+
writable: true,
|
|
10
|
+
value: void 0
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(this, "queue", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
configurable: true,
|
|
15
|
+
writable: true,
|
|
16
|
+
value: new Map()
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(this, "nextKey", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
configurable: true,
|
|
21
|
+
writable: true,
|
|
22
|
+
value: 0
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(this, "running", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
configurable: true,
|
|
27
|
+
writable: true,
|
|
28
|
+
value: false
|
|
29
|
+
});
|
|
30
|
+
Object.defineProperty(this, "processingTask", {
|
|
31
|
+
enumerable: true,
|
|
32
|
+
configurable: true,
|
|
33
|
+
writable: true,
|
|
34
|
+
value: null
|
|
35
|
+
});
|
|
36
|
+
this.store = store;
|
|
37
|
+
}
|
|
38
|
+
get isRunning() {
|
|
39
|
+
return this.running;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* @ignore
|
|
43
|
+
* Batch is not implemented here as we're only extending `BaseStore`
|
|
44
|
+
* to allow it to be passed where `BaseStore` is expected, and implement
|
|
45
|
+
* the convenience methods (get, search, put, delete).
|
|
46
|
+
*/
|
|
47
|
+
async batch(_operations) {
|
|
48
|
+
throw new Error("The `batch` method is not implemented on `AsyncBatchedStore`." +
|
|
49
|
+
"\n Instead, it calls the `batch` method on the wrapped store." +
|
|
50
|
+
"\n If you are seeing this error, something is wrong.");
|
|
51
|
+
}
|
|
52
|
+
async get(namespace, id) {
|
|
53
|
+
return this.enqueueOperation({ namespace, id });
|
|
54
|
+
}
|
|
55
|
+
async search(namespacePrefix, options) {
|
|
56
|
+
const { filter, limit = 10, offset = 0 } = options || {};
|
|
57
|
+
return this.enqueueOperation({
|
|
58
|
+
namespacePrefix,
|
|
59
|
+
filter,
|
|
60
|
+
limit,
|
|
61
|
+
offset,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async put(namespace, id, value) {
|
|
65
|
+
return this.enqueueOperation({ namespace, id, value });
|
|
66
|
+
}
|
|
67
|
+
async delete(namespace, id) {
|
|
68
|
+
return this.enqueueOperation({
|
|
69
|
+
namespace,
|
|
70
|
+
id,
|
|
71
|
+
value: null,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
start() {
|
|
75
|
+
if (!this.running) {
|
|
76
|
+
this.running = true;
|
|
77
|
+
this.processingTask = this.processBatchQueue();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async stop() {
|
|
81
|
+
this.running = false;
|
|
82
|
+
if (this.processingTask) {
|
|
83
|
+
await this.processingTask;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
enqueueOperation(operation) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
const key = this.nextKey;
|
|
89
|
+
this.nextKey += 1;
|
|
90
|
+
this.queue.set(key, { operation, resolve, reject });
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async processBatchQueue() {
|
|
94
|
+
while (this.running) {
|
|
95
|
+
await new Promise((resolve) => {
|
|
96
|
+
setTimeout(resolve, 0);
|
|
97
|
+
});
|
|
98
|
+
if (this.queue.size === 0)
|
|
99
|
+
continue;
|
|
100
|
+
const batch = new Map(this.queue);
|
|
101
|
+
this.queue.clear();
|
|
102
|
+
try {
|
|
103
|
+
const operations = Array.from(batch.values()).map(({ operation }) => operation);
|
|
104
|
+
const results = await this.store.batch(operations);
|
|
105
|
+
batch.forEach(({ resolve }, key) => {
|
|
106
|
+
const index = Array.from(batch.keys()).indexOf(key);
|
|
107
|
+
resolve(results[index]);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
batch.forEach(({ reject }) => {
|
|
112
|
+
reject(e);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -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,116 @@
|
|
|
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 batch(operations) {
|
|
16
|
+
const results = [];
|
|
17
|
+
for (const op of operations) {
|
|
18
|
+
if ("id" in op && "namespace" in op && !("value" in op)) {
|
|
19
|
+
// GetOperation
|
|
20
|
+
results.push(this.getOperation(op));
|
|
21
|
+
}
|
|
22
|
+
else if ("namespacePrefix" in op) {
|
|
23
|
+
// SearchOperation
|
|
24
|
+
results.push(this.searchOperation(op));
|
|
25
|
+
}
|
|
26
|
+
else if ("value" in op) {
|
|
27
|
+
// PutOperation
|
|
28
|
+
results.push(this.putOperation(op));
|
|
29
|
+
}
|
|
30
|
+
else if ("matchConditions" in op) {
|
|
31
|
+
// ListNamespacesOperation
|
|
32
|
+
results.push(this.listNamespacesOperation(op));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return Promise.resolve(results);
|
|
36
|
+
}
|
|
37
|
+
getOperation(op) {
|
|
38
|
+
const namespaceKey = op.namespace.join(":");
|
|
39
|
+
const item = this.data.get(namespaceKey)?.get(op.id);
|
|
40
|
+
return item || null;
|
|
41
|
+
}
|
|
42
|
+
searchOperation(op) {
|
|
43
|
+
const candidates = [];
|
|
44
|
+
for (const [namespace, items] of this.data.entries()) {
|
|
45
|
+
if (namespace.startsWith(op.namespacePrefix.join(":"))) {
|
|
46
|
+
candidates.push(...items.values());
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
let filteredCandidates = candidates;
|
|
50
|
+
if (op.filter) {
|
|
51
|
+
filteredCandidates = candidates.filter((item) => Object.entries(op.filter).every(([key, value]) => item.value[key] === value));
|
|
52
|
+
}
|
|
53
|
+
const searchResults = filteredCandidates.slice(op.offset || 0, (op.offset || 0) + (op.limit || 10));
|
|
54
|
+
return searchResults;
|
|
55
|
+
}
|
|
56
|
+
putOperation(op) {
|
|
57
|
+
const namespaceKey = op.namespace.join(":");
|
|
58
|
+
if (!this.data.has(namespaceKey)) {
|
|
59
|
+
this.data.set(namespaceKey, new Map());
|
|
60
|
+
}
|
|
61
|
+
const namespaceMap = this.data.get(namespaceKey);
|
|
62
|
+
if (op.value === null) {
|
|
63
|
+
namespaceMap.delete(op.id);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const now = new Date();
|
|
67
|
+
if (namespaceMap.has(op.id)) {
|
|
68
|
+
const item = namespaceMap.get(op.id);
|
|
69
|
+
item.value = op.value;
|
|
70
|
+
item.updatedAt = now;
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
namespaceMap.set(op.id, {
|
|
74
|
+
value: op.value,
|
|
75
|
+
id: op.id,
|
|
76
|
+
namespace: op.namespace,
|
|
77
|
+
createdAt: now,
|
|
78
|
+
updatedAt: now,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
listNamespacesOperation(op) {
|
|
84
|
+
const allNamespaces = Array.from(this.data.keys()).map((ns) => ns.split(":"));
|
|
85
|
+
let namespaces = allNamespaces;
|
|
86
|
+
if (op.matchConditions && op.matchConditions.length > 0) {
|
|
87
|
+
namespaces = namespaces.filter((ns) => op.matchConditions.every((condition) => this.doesMatch(condition, ns)));
|
|
88
|
+
}
|
|
89
|
+
if (op.maxDepth !== undefined) {
|
|
90
|
+
namespaces = Array.from(new Set(namespaces.map((ns) => ns.slice(0, op.maxDepth).join(":")))).map((ns) => ns.split(":"));
|
|
91
|
+
}
|
|
92
|
+
namespaces.sort((a, b) => a.join(":").localeCompare(b.join(":")));
|
|
93
|
+
const paginatedNamespaces = namespaces.slice(op.offset, op.offset + op.limit);
|
|
94
|
+
return paginatedNamespaces;
|
|
95
|
+
}
|
|
96
|
+
doesMatch(matchCondition, key) {
|
|
97
|
+
const { matchType, path } = matchCondition;
|
|
98
|
+
if (key.length < path.length) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (matchType === "prefix") {
|
|
102
|
+
return path.every((pElem, index) => {
|
|
103
|
+
const kElem = key[index];
|
|
104
|
+
return pElem === "*" || kElem === pElem;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
else if (matchType === "suffix") {
|
|
108
|
+
return [...path].reverse().every((pElem, index) => {
|
|
109
|
+
const kElem = key[key.length - 1 - index];
|
|
110
|
+
return pElem === "*" || kElem === pElem;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
throw new Error(`Unsupported match type: ${matchType}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.MemoryStore = MemoryStore;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseStore, type OperationResults, type Operation } from "./base.js";
|
|
2
|
+
export declare class MemoryStore extends BaseStore {
|
|
3
|
+
private data;
|
|
4
|
+
batch<Op extends Operation[]>(operations: Op): Promise<OperationResults<Op>>;
|
|
5
|
+
private getOperation;
|
|
6
|
+
private searchOperation;
|
|
7
|
+
private putOperation;
|
|
8
|
+
private listNamespacesOperation;
|
|
9
|
+
private doesMatch;
|
|
10
|
+
}
|