@langchain/langgraph-checkpoint 0.0.12 → 0.0.14

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/base.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WRITES_IDX_MAP = exports.maxChannelVersion = exports.compareChannelVersions = exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = void 0;
3
+ exports.getCheckpointId = exports.WRITES_IDX_MAP = exports.maxChannelVersion = exports.compareChannelVersions = exports.BaseCheckpointSaver = exports.copyCheckpoint = exports.emptyCheckpoint = exports.deepCopy = void 0;
4
4
  const id_js_1 = require("./id.cjs");
5
5
  const types_js_1 = require("./serde/types.cjs");
6
6
  const jsonplus_js_1 = require("./serde/jsonplus.cjs");
@@ -96,4 +96,10 @@ exports.maxChannelVersion = maxChannelVersion;
96
96
  exports.WRITES_IDX_MAP = {
97
97
  [types_js_1.ERROR]: -1,
98
98
  [types_js_1.SCHEDULED]: -2,
99
+ [types_js_1.INTERRUPT]: -3,
100
+ [types_js_1.RESUME]: -4,
99
101
  };
102
+ function getCheckpointId(config) {
103
+ return (config.configurable?.checkpoint_id || config.configurable?.thread_ts || "");
104
+ }
105
+ exports.getCheckpointId = getCheckpointId;
package/dist/base.d.ts CHANGED
@@ -86,4 +86,5 @@ export declare function maxChannelVersion(...versions: ChannelVersion[]): Channe
86
86
  * Each Checkpointer implementation should use this mapping in put_writes.
87
87
  */
88
88
  export declare const WRITES_IDX_MAP: Record<string, number>;
89
+ export declare function getCheckpointId(config: RunnableConfig): string;
89
90
  export {};
package/dist/base.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { uuid6 } from "./id.js";
2
- import { ERROR, SCHEDULED, } from "./serde/types.js";
2
+ import { ERROR, INTERRUPT, RESUME, SCHEDULED, } from "./serde/types.js";
3
3
  import { JsonPlusSerializer } from "./serde/jsonplus.js";
4
4
  export function deepCopy(obj) {
5
5
  if (typeof obj !== "object" || obj === null) {
@@ -87,4 +87,9 @@ export function maxChannelVersion(...versions) {
87
87
  export const WRITES_IDX_MAP = {
88
88
  [ERROR]: -1,
89
89
  [SCHEDULED]: -2,
90
+ [INTERRUPT]: -3,
91
+ [RESUME]: -4,
90
92
  };
93
+ export function getCheckpointId(config) {
94
+ return (config.configurable?.checkpoint_id || config.configurable?.thread_ts || "");
95
+ }
package/dist/memory.cjs CHANGED
@@ -26,7 +26,8 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
26
26
  async _getPendingSends(threadId, checkpointNs, parentCheckpointId) {
27
27
  let pendingSends = [];
28
28
  if (parentCheckpointId !== undefined) {
29
- pendingSends = await Promise.all(this.writes[_generateKey(threadId, checkpointNs, parentCheckpointId)]
29
+ const key = _generateKey(threadId, checkpointNs, parentCheckpointId);
30
+ pendingSends = await Promise.all(Object.values(this.writes[key] || {})
30
31
  ?.filter(([_taskId, channel]) => {
31
32
  return channel === types_js_1.TASKS;
32
33
  })
@@ -39,19 +40,18 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
39
40
  async getTuple(config) {
40
41
  const thread_id = config.configurable?.thread_id;
41
42
  const checkpoint_ns = config.configurable?.checkpoint_ns ?? "";
42
- let checkpoint_id = config.configurable?.checkpoint_id;
43
+ let checkpoint_id = (0, base_js_1.getCheckpointId)(config);
43
44
  if (checkpoint_id) {
44
45
  const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];
45
46
  if (saved !== undefined) {
46
47
  const [checkpoint, metadata, parentCheckpointId] = saved;
47
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
48
- [];
48
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
49
49
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
50
50
  const deserializedCheckpoint = {
51
51
  ...(await this.serde.loadsTyped("json", checkpoint)),
52
52
  pending_sends,
53
53
  };
54
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
54
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
55
55
  return [
56
56
  taskId,
57
57
  channel,
@@ -83,14 +83,13 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
83
83
  checkpoint_id = Object.keys(checkpoints).sort((a, b) => b.localeCompare(a))[0];
84
84
  const saved = checkpoints[checkpoint_id];
85
85
  const [checkpoint, metadata, parentCheckpointId] = saved;
86
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
87
- [];
86
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
88
87
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
89
88
  const deserializedCheckpoint = {
90
89
  ...(await this.serde.loadsTyped("json", checkpoint)),
91
90
  pending_sends,
92
91
  };
93
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
92
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
94
93
  return [
95
94
  taskId,
96
95
  channel,
@@ -130,6 +129,7 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
130
129
  ? [config.configurable?.thread_id]
131
130
  : Object.keys(this.storage);
132
131
  const configCheckpointNamespace = config.configurable?.checkpoint_ns;
132
+ const configCheckpointId = config.configurable?.checkpoint_id;
133
133
  for (const threadId of threadIds) {
134
134
  for (const checkpointNamespace of Object.keys(this.storage[threadId] ?? {})) {
135
135
  if (configCheckpointNamespace !== undefined &&
@@ -139,7 +139,11 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
139
139
  const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};
140
140
  const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) => b[0].localeCompare(a[0]));
141
141
  for (const [checkpointId, [checkpoint, metadataStr, parentCheckpointId],] of sortedCheckpoints) {
142
- // Filter by checkpoint ID
142
+ // Filter by checkpoint ID from config
143
+ if (configCheckpointId && checkpointId !== configCheckpointId) {
144
+ continue;
145
+ }
146
+ // Filter by checkpoint ID from before config
143
147
  if (before &&
144
148
  before.configurable?.checkpoint_id &&
145
149
  checkpointId >= before.configurable.checkpoint_id) {
@@ -155,10 +159,10 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
155
159
  if (limit !== undefined) {
156
160
  if (limit <= 0)
157
161
  break;
158
- // eslint-disable-next-line no-param-reassign
159
162
  limit -= 1;
160
163
  }
161
- const writes = this.writes[_generateKey(threadId, checkpointNamespace, checkpointId)] ?? [];
164
+ const key = _generateKey(threadId, checkpointNamespace, checkpointId);
165
+ const writes = Object.values(this.writes[key] || {});
162
166
  const pending_sends = await this._getPendingSends(threadId, checkpointNamespace, parentCheckpointId);
163
167
  const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
164
168
  return [
@@ -236,15 +240,23 @@ class MemorySaver extends base_js_1.BaseCheckpointSaver {
236
240
  if (checkpointId === undefined) {
237
241
  throw new Error(`Failed to put writes. The passed RunnableConfig is missing a required "checkpoint_id" field in its "configurable" property.`);
238
242
  }
239
- const key = _generateKey(threadId, checkpointNamespace, checkpointId);
240
- if (this.writes[key] === undefined) {
241
- this.writes[key] = [];
243
+ const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);
244
+ const outerWrites_ = this.writes[outerKey];
245
+ if (this.writes[outerKey] === undefined) {
246
+ this.writes[outerKey] = {};
242
247
  }
243
- const pendingWrites = writes.map(([channel, value]) => {
248
+ writes.forEach(([channel, value], idx) => {
244
249
  const [, serializedValue] = this.serde.dumpsTyped(value);
245
- return [taskId, channel, serializedValue];
250
+ const innerKey = [
251
+ taskId,
252
+ base_js_1.WRITES_IDX_MAP[channel] || idx,
253
+ ];
254
+ const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;
255
+ if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {
256
+ return;
257
+ }
258
+ this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];
246
259
  });
247
- this.writes[key].push(...pendingWrites);
248
260
  }
249
261
  }
250
262
  exports.MemorySaver = MemorySaver;
package/dist/memory.d.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import type { RunnableConfig } from "@langchain/core/runnables";
2
2
  import { BaseCheckpointSaver, Checkpoint, CheckpointListOptions, CheckpointTuple } from "./base.js";
3
3
  import { SerializerProtocol } from "./serde/base.js";
4
- import { CheckpointMetadata, CheckpointPendingWrite, PendingWrite } from "./types.js";
4
+ import { CheckpointMetadata, PendingWrite } from "./types.js";
5
5
  import { SendProtocol } from "./serde/types.js";
6
6
  export declare class MemorySaver extends BaseCheckpointSaver {
7
7
  storage: Record<string, Record<string, Record<string, [Uint8Array, Uint8Array, string | undefined]>>>;
8
- writes: Record<string, CheckpointPendingWrite[]>;
8
+ writes: Record<string, Record<string, [string, string, Uint8Array]>>;
9
9
  constructor(serde?: SerializerProtocol);
10
10
  _getPendingSends(threadId: string, checkpointNs: string, parentCheckpointId?: string): Promise<SendProtocol[]>;
11
11
  getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
package/dist/memory.js CHANGED
@@ -1,4 +1,4 @@
1
- import { BaseCheckpointSaver, copyCheckpoint, } from "./base.js";
1
+ import { BaseCheckpointSaver, copyCheckpoint, getCheckpointId, WRITES_IDX_MAP, } from "./base.js";
2
2
  import { TASKS } from "./serde/types.js";
3
3
  function _generateKey(threadId, checkpointNamespace, checkpointId) {
4
4
  return JSON.stringify([threadId, checkpointNamespace, checkpointId]);
@@ -23,7 +23,8 @@ export class MemorySaver extends BaseCheckpointSaver {
23
23
  async _getPendingSends(threadId, checkpointNs, parentCheckpointId) {
24
24
  let pendingSends = [];
25
25
  if (parentCheckpointId !== undefined) {
26
- pendingSends = await Promise.all(this.writes[_generateKey(threadId, checkpointNs, parentCheckpointId)]
26
+ const key = _generateKey(threadId, checkpointNs, parentCheckpointId);
27
+ pendingSends = await Promise.all(Object.values(this.writes[key] || {})
27
28
  ?.filter(([_taskId, channel]) => {
28
29
  return channel === TASKS;
29
30
  })
@@ -36,19 +37,18 @@ export class MemorySaver extends BaseCheckpointSaver {
36
37
  async getTuple(config) {
37
38
  const thread_id = config.configurable?.thread_id;
38
39
  const checkpoint_ns = config.configurable?.checkpoint_ns ?? "";
39
- let checkpoint_id = config.configurable?.checkpoint_id;
40
+ let checkpoint_id = getCheckpointId(config);
40
41
  if (checkpoint_id) {
41
42
  const saved = this.storage[thread_id]?.[checkpoint_ns]?.[checkpoint_id];
42
43
  if (saved !== undefined) {
43
44
  const [checkpoint, metadata, parentCheckpointId] = saved;
44
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
45
- [];
45
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
46
46
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
47
47
  const deserializedCheckpoint = {
48
48
  ...(await this.serde.loadsTyped("json", checkpoint)),
49
49
  pending_sends,
50
50
  };
51
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
51
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
52
52
  return [
53
53
  taskId,
54
54
  channel,
@@ -80,14 +80,13 @@ export class MemorySaver extends BaseCheckpointSaver {
80
80
  checkpoint_id = Object.keys(checkpoints).sort((a, b) => b.localeCompare(a))[0];
81
81
  const saved = checkpoints[checkpoint_id];
82
82
  const [checkpoint, metadata, parentCheckpointId] = saved;
83
- const writes = this.writes[_generateKey(thread_id, checkpoint_ns, checkpoint_id)] ??
84
- [];
83
+ const key = _generateKey(thread_id, checkpoint_ns, checkpoint_id);
85
84
  const pending_sends = await this._getPendingSends(thread_id, checkpoint_ns, parentCheckpointId);
86
85
  const deserializedCheckpoint = {
87
86
  ...(await this.serde.loadsTyped("json", checkpoint)),
88
87
  pending_sends,
89
88
  };
90
- const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
89
+ const pendingWrites = await Promise.all(Object.values(this.writes[key] || {}).map(async ([taskId, channel, value]) => {
91
90
  return [
92
91
  taskId,
93
92
  channel,
@@ -127,6 +126,7 @@ export class MemorySaver extends BaseCheckpointSaver {
127
126
  ? [config.configurable?.thread_id]
128
127
  : Object.keys(this.storage);
129
128
  const configCheckpointNamespace = config.configurable?.checkpoint_ns;
129
+ const configCheckpointId = config.configurable?.checkpoint_id;
130
130
  for (const threadId of threadIds) {
131
131
  for (const checkpointNamespace of Object.keys(this.storage[threadId] ?? {})) {
132
132
  if (configCheckpointNamespace !== undefined &&
@@ -136,7 +136,11 @@ export class MemorySaver extends BaseCheckpointSaver {
136
136
  const checkpoints = this.storage[threadId]?.[checkpointNamespace] ?? {};
137
137
  const sortedCheckpoints = Object.entries(checkpoints).sort((a, b) => b[0].localeCompare(a[0]));
138
138
  for (const [checkpointId, [checkpoint, metadataStr, parentCheckpointId],] of sortedCheckpoints) {
139
- // Filter by checkpoint ID
139
+ // Filter by checkpoint ID from config
140
+ if (configCheckpointId && checkpointId !== configCheckpointId) {
141
+ continue;
142
+ }
143
+ // Filter by checkpoint ID from before config
140
144
  if (before &&
141
145
  before.configurable?.checkpoint_id &&
142
146
  checkpointId >= before.configurable.checkpoint_id) {
@@ -152,10 +156,10 @@ export class MemorySaver extends BaseCheckpointSaver {
152
156
  if (limit !== undefined) {
153
157
  if (limit <= 0)
154
158
  break;
155
- // eslint-disable-next-line no-param-reassign
156
159
  limit -= 1;
157
160
  }
158
- const writes = this.writes[_generateKey(threadId, checkpointNamespace, checkpointId)] ?? [];
161
+ const key = _generateKey(threadId, checkpointNamespace, checkpointId);
162
+ const writes = Object.values(this.writes[key] || {});
159
163
  const pending_sends = await this._getPendingSends(threadId, checkpointNamespace, parentCheckpointId);
160
164
  const pendingWrites = await Promise.all(writes.map(async ([taskId, channel, value]) => {
161
165
  return [
@@ -233,14 +237,22 @@ export class MemorySaver extends BaseCheckpointSaver {
233
237
  if (checkpointId === undefined) {
234
238
  throw new Error(`Failed to put writes. The passed RunnableConfig is missing a required "checkpoint_id" field in its "configurable" property.`);
235
239
  }
236
- const key = _generateKey(threadId, checkpointNamespace, checkpointId);
237
- if (this.writes[key] === undefined) {
238
- this.writes[key] = [];
240
+ const outerKey = _generateKey(threadId, checkpointNamespace, checkpointId);
241
+ const outerWrites_ = this.writes[outerKey];
242
+ if (this.writes[outerKey] === undefined) {
243
+ this.writes[outerKey] = {};
239
244
  }
240
- const pendingWrites = writes.map(([channel, value]) => {
245
+ writes.forEach(([channel, value], idx) => {
241
246
  const [, serializedValue] = this.serde.dumpsTyped(value);
242
- return [taskId, channel, serializedValue];
247
+ const innerKey = [
248
+ taskId,
249
+ WRITES_IDX_MAP[channel] || idx,
250
+ ];
251
+ const innerKeyStr = `${innerKey[0]},${innerKey[1]}`;
252
+ if (innerKey[1] >= 0 && outerWrites_ && innerKeyStr in outerWrites_) {
253
+ return;
254
+ }
255
+ this.writes[outerKey][innerKeyStr] = [taskId, channel, serializedValue];
243
256
  });
244
- this.writes[key].push(...pendingWrites);
245
257
  }
246
258
  }
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SCHEDULED = exports.ERROR = exports.TASKS = void 0;
3
+ exports.RESUME = exports.INTERRUPT = exports.SCHEDULED = exports.ERROR = exports.TASKS = void 0;
4
4
  exports.TASKS = "__pregel_tasks";
5
5
  exports.ERROR = "__error__";
6
6
  exports.SCHEDULED = "__scheduled__";
7
+ exports.INTERRUPT = "__interrupt__";
8
+ exports.RESUME = "__resume__";
@@ -1,6 +1,8 @@
1
1
  export declare const TASKS = "__pregel_tasks";
2
2
  export declare const ERROR = "__error__";
3
3
  export declare const SCHEDULED = "__scheduled__";
4
+ export declare const INTERRUPT = "__interrupt__";
5
+ export declare const RESUME = "__resume__";
4
6
  export interface ChannelProtocol<ValueType = unknown, UpdateType = unknown, CheckpointType = unknown> {
5
7
  ValueType: ValueType;
6
8
  UpdateType: UpdateType;
@@ -1,3 +1,5 @@
1
1
  export const TASKS = "__pregel_tasks";
2
2
  export const ERROR = "__error__";
3
3
  export const SCHEDULED = "__scheduled__";
4
+ export const INTERRUPT = "__interrupt__";
5
+ export const RESUME = "__resume__";
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.BaseStore = exports.InvalidNamespaceError = void 0;
3
+ exports.BaseStore = exports.tokenizePath = exports.getTextAtPath = exports.InvalidNamespaceError = void 0;
5
4
  /**
6
5
  * Error thrown when an invalid namespace is provided.
7
6
  */
@@ -38,69 +37,160 @@ function validateNamespace(namespace) {
38
37
  }
39
38
  }
40
39
  /**
41
- * Abstract base class for key-value stores.
40
+ * Utility function to get text at a specific JSON path
41
+ */
42
+ function getTextAtPath(obj, path) {
43
+ const parts = path.split(".");
44
+ let current = obj;
45
+ for (const part of parts) {
46
+ if (part.includes("[")) {
47
+ const [arrayName, indexStr] = part.split("[");
48
+ const index = indexStr.replace("]", "");
49
+ if (!current[arrayName])
50
+ return [];
51
+ if (index === "*") {
52
+ const results = [];
53
+ for (const item of current[arrayName]) {
54
+ if (typeof item === "string")
55
+ results.push(item);
56
+ }
57
+ return results;
58
+ }
59
+ const idx = parseInt(index, 10);
60
+ if (Number.isNaN(idx))
61
+ return [];
62
+ current = current[arrayName][idx];
63
+ }
64
+ else {
65
+ current = current[part];
66
+ }
67
+ if (current === undefined)
68
+ return [];
69
+ }
70
+ return typeof current === "string" ? [current] : [];
71
+ }
72
+ exports.getTextAtPath = getTextAtPath;
73
+ /**
74
+ * Tokenizes a JSON path into parts
75
+ */
76
+ function tokenizePath(path) {
77
+ return path.split(".");
78
+ }
79
+ exports.tokenizePath = tokenizePath;
80
+ /**
81
+ * Abstract base class for persistent key-value stores.
82
+ *
83
+ * Stores enable persistence and memory that can be shared across threads,
84
+ * scoped to user IDs, assistant IDs, or other arbitrary namespaces.
85
+ *
86
+ * Features:
87
+ * - Hierarchical namespaces for organization
88
+ * - Key-value storage with metadata
89
+ * - Vector similarity search (if configured)
90
+ * - Filtering and pagination
42
91
  */
43
92
  class BaseStore {
44
- // convenience methods
45
93
  /**
46
- * Retrieve a single item.
47
- * @param namespace Hierarchical path for the item.
48
- * @param key Unique identifier within the namespace.
49
- * @returns A promise that resolves to the retrieved item or null if not found.
94
+ * Retrieve a single item by its namespace and key.
95
+ *
96
+ * @param namespace Hierarchical path for the item
97
+ * @param key Unique identifier within the namespace
98
+ * @returns Promise resolving to the item or null if not found
50
99
  */
51
100
  async get(namespace, key) {
52
- const batchResult = await this.batch([{ namespace, key }]);
53
- return batchResult[0];
101
+ return (await this.batch([{ namespace, key }]))[0];
54
102
  }
55
103
  /**
56
104
  * Search for items within a namespace prefix.
57
- * @param namespacePrefix Hierarchical path prefix to search within.
58
- * @param options Search options.
59
- * @param options.filter Key-value pairs to filter results.
60
- * @param options.limit Maximum number of items to return (default: 10).
61
- * @param options.offset Number of items to skip before returning results (default: 0).
62
- * @returns A promise that resolves to a list of items matching the search criteria.
105
+ * Supports both metadata filtering and vector similarity search.
106
+ *
107
+ * @param namespacePrefix Hierarchical path prefix to search within
108
+ * @param options Search options for filtering and pagination
109
+ * @returns Promise resolving to list of matching items with relevance scores
110
+ *
111
+ * @example
112
+ * // Search with filters
113
+ * await store.search(["documents"], {
114
+ * filter: { type: "report", status: "active" },
115
+ * limit: 5,
116
+ * offset: 10
117
+ * });
118
+ *
119
+ * // Vector similarity search
120
+ * await store.search(["users", "content"], {
121
+ * query: "technical documentation about APIs",
122
+ * limit: 20
123
+ * });
63
124
  */
64
- async search(namespacePrefix, options) {
65
- const optionsWithDefaults = {
66
- limit: 10,
67
- offset: 0,
68
- ...(options || {}),
69
- };
70
- const batchResults = await this.batch([
71
- { namespacePrefix, ...optionsWithDefaults },
72
- ]);
73
- return batchResults[0];
125
+ async search(namespacePrefix, options = {}) {
126
+ const { filter, limit = 10, offset = 0, query } = options;
127
+ return (await this.batch([
128
+ {
129
+ namespacePrefix,
130
+ filter,
131
+ limit,
132
+ offset,
133
+ query,
134
+ },
135
+ ]))[0];
74
136
  }
75
137
  /**
76
138
  * Store or update an item.
77
- * @param namespace Hierarchical path for the item.
78
- * @param key Unique identifier within the namespace.
79
- * @param value Object containing the item's data.
139
+ *
140
+ * @param namespace Hierarchical path for the item
141
+ * @param key Unique identifier within the namespace
142
+ * @param value Object containing the item's data
143
+ * @param index Optional indexing configuration
144
+ *
145
+ * @example
146
+ * // Simple storage
147
+ * await store.put(["docs"], "report", { title: "Annual Report" });
148
+ *
149
+ * // With specific field indexing
150
+ * await store.put(
151
+ * ["docs"],
152
+ * "report",
153
+ * {
154
+ * title: "Q4 Report",
155
+ * chapters: [{ content: "..." }, { content: "..." }]
156
+ * },
157
+ * ["title", "chapters[*].content"]
158
+ * );
80
159
  */
81
- async put(namespace, key, value) {
160
+ async put(namespace, key, value, index) {
82
161
  validateNamespace(namespace);
83
- await this.batch([{ namespace, key, value }]);
162
+ await this.batch([{ namespace, key, value, index }]);
84
163
  }
85
164
  /**
86
- * Delete an item.
87
- * @param namespace Hierarchical path for the item.
88
- * @param key Unique identifier within the namespace.
165
+ * Delete an item from the store.
166
+ *
167
+ * @param namespace Hierarchical path for the item
168
+ * @param key Unique identifier within the namespace
89
169
  */
90
170
  async delete(namespace, key) {
91
171
  await this.batch([{ namespace, key, value: null }]);
92
172
  }
93
173
  /**
94
174
  * List and filter namespaces in the store.
95
- * @param options Options for listing namespaces.
96
- * @param options.prefix Filter namespaces that start with this path.
97
- * @param options.suffix Filter namespaces that end with this path.
98
- * @param options.maxDepth Return namespaces up to this depth in the hierarchy.
99
- * @param options.limit Maximum number of namespaces to return (default 100).
100
- * @param options.offset Number of namespaces to skip for pagination (default 0).
101
- * @returns A promise that resolves to a list of namespace arrays that match the criteria.
175
+ * Used to explore data organization and navigate the namespace hierarchy.
176
+ *
177
+ * @param options Options for listing namespaces
178
+ * @returns Promise resolving to list of namespace paths
179
+ *
180
+ * @example
181
+ * // List all namespaces under "documents"
182
+ * await store.listNamespaces({
183
+ * prefix: ["documents"],
184
+ * maxDepth: 2
185
+ * });
186
+ *
187
+ * // List namespaces ending with "v1"
188
+ * await store.listNamespaces({
189
+ * suffix: ["v1"],
190
+ * limit: 50
191
+ * });
102
192
  */
103
- async listNamespaces(options) {
193
+ async listNamespaces(options = {}) {
104
194
  const { prefix, suffix, maxDepth, limit = 100, offset = 0 } = options;
105
195
  const matchConditions = [];
106
196
  if (prefix) {
@@ -109,26 +199,22 @@ class BaseStore {
109
199
  if (suffix) {
110
200
  matchConditions.push({ matchType: "suffix", path: suffix });
111
201
  }
112
- const op = {
113
- matchConditions: matchConditions.length > 0 ? matchConditions : undefined,
114
- maxDepth,
115
- limit,
116
- offset,
117
- };
118
- const batchResults = await this.batch([op]);
119
- return batchResults[0];
202
+ return (await this.batch([
203
+ {
204
+ matchConditions: matchConditions.length ? matchConditions : undefined,
205
+ maxDepth,
206
+ limit,
207
+ offset,
208
+ },
209
+ ]))[0];
120
210
  }
121
211
  /**
122
- * Stop the store. No-op if not implemented.
212
+ * Start the store. Override if initialization is needed.
123
213
  */
124
- stop() {
125
- // no-op if not implemented.
126
- }
214
+ start() { }
127
215
  /**
128
- * Start the store. No-op if not implemented.
216
+ * Stop the store. Override if cleanup is needed.
129
217
  */
130
- start() {
131
- // no-op if not implemented.
132
- }
218
+ stop() { }
133
219
  }
134
220
  exports.BaseStore = BaseStore;