@langchain/langgraph-checkpoint-mongodb 1.1.6 → 1.2.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 CHANGED
@@ -1,3 +1,4 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
1
2
  let _langchain_langgraph_checkpoint = require("@langchain/langgraph-checkpoint");
2
3
 
3
4
  //#region src/index.ts
@@ -9,13 +10,18 @@ var MongoDBSaver = class extends _langchain_langgraph_checkpoint.BaseCheckpointS
9
10
  db;
10
11
  checkpointCollectionName = "checkpoints";
11
12
  checkpointWritesCollectionName = "checkpoint_writes";
12
- constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName }, serde) {
13
+ enableTimestamps;
14
+ get timestampOp() {
15
+ return this.enableTimestamps ? { $currentDate: { upserted_at: true } } : {};
16
+ }
17
+ constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName, enableTimestamps }, serde) {
13
18
  super(serde);
14
19
  this.client = client;
15
20
  this.client.appendMetadata({ name: "langgraphjs_checkpoint_saver" });
16
21
  this.db = this.client.db(dbName);
17
22
  this.checkpointCollectionName = checkpointCollectionName ?? this.checkpointCollectionName;
18
23
  this.checkpointWritesCollectionName = checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;
24
+ this.enableTimestamps = enableTimestamps ?? false;
19
25
  }
20
26
  /**
21
27
  * Retrieves a checkpoint from the MongoDB database based on the
@@ -75,6 +81,7 @@ var MongoDBSaver = class extends _langchain_langgraph_checkpoint.BaseCheckpointS
75
81
  if (config?.configurable?.thread_id) query.thread_id = config.configurable.thread_id;
76
82
  if (config?.configurable?.checkpoint_ns !== void 0 && config?.configurable?.checkpoint_ns !== null) query.checkpoint_ns = config.configurable.checkpoint_ns;
77
83
  if (filter) Object.entries(filter).forEach(([key, value]) => {
84
+ if (value !== null && typeof value === "object") throw new Error(`Invalid filter value for key "${key}": filter values must be primitives (string, number, boolean, or null)`);
78
85
  query[`metadata.${key}`] = value;
79
86
  });
80
87
  if (before) query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };
@@ -121,7 +128,10 @@ var MongoDBSaver = class extends _langchain_langgraph_checkpoint.BaseCheckpointS
121
128
  checkpoint_ns,
122
129
  checkpoint_id
123
130
  };
124
- await this.db.collection(this.checkpointCollectionName).updateOne(upsertQuery, { $set: doc }, { upsert: true });
131
+ await this.db.collection(this.checkpointCollectionName).updateOne(upsertQuery, {
132
+ $set: doc,
133
+ ...this.timestampOp
134
+ }, { upsert: true });
125
135
  return { configurable: {
126
136
  thread_id,
127
137
  checkpoint_ns,
@@ -147,11 +157,14 @@ var MongoDBSaver = class extends _langchain_langgraph_checkpoint.BaseCheckpointS
147
157
  const [type, serializedValue] = await this.serde.dumpsTyped(value);
148
158
  return { updateOne: {
149
159
  filter: upsertQuery,
150
- update: { $set: {
151
- channel,
152
- type,
153
- value: serializedValue
154
- } },
160
+ update: {
161
+ $set: {
162
+ channel,
163
+ type,
164
+ value: serializedValue
165
+ },
166
+ ...this.timestampOp
167
+ },
155
168
  upsert: true
156
169
  } };
157
170
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["BaseCheckpointSaver"],"sources":["../src/index.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n CheckpointPendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\n\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n};\n\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n\n protected db: MongoDatabase;\n\n checkpointCollectionName = \"checkpoints\";\n\n checkpointWritesCollectionName = \"checkpoint_writes\";\n\n constructor(\n {\n client,\n dbName,\n checkpointCollectionName,\n checkpointWritesCollectionName,\n }: MongoDBSaverParams,\n serde?: SerializerProtocol\n ) {\n super(serde);\n this.client = client;\n this.client.appendMetadata({\n name: \"langgraphjs_checkpoint_saver\",\n });\n this.db = this.client.db(dbName);\n this.checkpointCollectionName =\n checkpointCollectionName ?? this.checkpointCollectionName;\n this.checkpointWritesCollectionName =\n checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;\n }\n\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const {\n thread_id,\n checkpoint_ns = \"\",\n checkpoint_id,\n } = config.configurable ?? {};\n let query;\n if (checkpoint_id) {\n query = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n } else {\n query = { thread_id, checkpoint_ns };\n }\n const result = await this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1)\n .limit(1)\n .toArray();\n if (result.length === 0) {\n return undefined;\n }\n const doc = result[0];\n const configurableValues = {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n };\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const serializedWrites = await this.db\n .collection(this.checkpointWritesCollectionName)\n .find(configurableValues)\n .toArray();\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n serializedWrites.map(async (serializedWrite) => {\n return [\n serializedWrite.task_id,\n serializedWrite.channel,\n await this.serde.loadsTyped(\n serializedWrite.type,\n serializedWrite.value.value(\"utf8\")\n ),\n ] as CheckpointPendingWrite;\n })\n );\n return {\n config: { configurable: configurableValues },\n checkpoint,\n pendingWrites,\n metadata: (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata,\n parentConfig:\n doc.parent_checkpoint_id != null\n ? {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n const { limit, before, filter } = options ?? {};\n const query: Record<string, unknown> = {};\n\n if (config?.configurable?.thread_id) {\n query.thread_id = config.configurable.thread_id;\n }\n\n if (\n config?.configurable?.checkpoint_ns !== undefined &&\n config?.configurable?.checkpoint_ns !== null\n ) {\n query.checkpoint_ns = config.configurable.checkpoint_ns;\n }\n\n if (filter) {\n Object.entries(filter).forEach(([key, value]) => {\n query[`metadata.${key}`] = value;\n });\n }\n\n if (before) {\n query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };\n }\n\n let result = this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1);\n\n if (limit !== undefined) {\n result = result.limit(limit);\n }\n\n for await (const doc of result) {\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const metadata = (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata;\n\n yield {\n config: {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: doc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n }\n\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n const checkpoint_id = checkpoint.id;\n if (thread_id === undefined) {\n throw new Error(\n `The provided config must contain a configurable field with a \"thread_id\" field.`\n );\n }\n const [\n [checkpointType, serializedCheckpoint],\n [metadataType, serializedMetadata],\n ] = await Promise.all([\n this.serde.dumpsTyped(checkpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (checkpointType !== metadataType) {\n throw new Error(\"Mismatched checkpoint and metadata types.\");\n }\n const doc = {\n parent_checkpoint_id: config.configurable?.checkpoint_id,\n type: checkpointType,\n checkpoint: serializedCheckpoint,\n metadata: serializedMetadata,\n };\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n await this.db\n .collection(this.checkpointCollectionName)\n .updateOne(upsertQuery, { $set: doc }, { upsert: true });\n\n return {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n },\n };\n }\n\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns;\n const checkpoint_id = config.configurable?.checkpoint_id;\n if (\n thread_id === undefined ||\n checkpoint_ns === undefined ||\n checkpoint_id === undefined\n ) {\n throw new Error(\n `The provided config must contain a configurable field with \"thread_id\", \"checkpoint_ns\" and \"checkpoint_id\" fields.`\n );\n }\n\n const operations = await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n task_id: taskId,\n idx,\n };\n\n const [type, serializedValue] = await this.serde.dumpsTyped(value);\n\n return {\n updateOne: {\n filter: upsertQuery,\n update: { $set: { channel, type, value: serializedValue } },\n upsert: true,\n },\n };\n })\n );\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .bulkWrite(operations);\n }\n\n async deleteThread(threadId: string) {\n await this.db\n .collection(this.checkpointCollectionName)\n .deleteMany({ thread_id: threadId });\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .deleteMany({ thread_id: threadId });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["BaseCheckpointSaver"],"sources":["../src/index.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n CheckpointPendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\n\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n /**\n * When true, writes an `upserted_at` BSON date to documents on every upsert.\n * Useful for MongoDB TTL indexes, auditing, or debugging.\n */\n enableTimestamps?: boolean;\n};\n\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n\n protected db: MongoDatabase;\n\n checkpointCollectionName = \"checkpoints\";\n\n checkpointWritesCollectionName = \"checkpoint_writes\";\n\n protected enableTimestamps: boolean;\n\n private get timestampOp() {\n return this.enableTimestamps\n ? ({ $currentDate: { upserted_at: true } } as const)\n : {};\n }\n\n constructor(\n {\n client,\n dbName,\n checkpointCollectionName,\n checkpointWritesCollectionName,\n enableTimestamps,\n }: MongoDBSaverParams,\n serde?: SerializerProtocol\n ) {\n super(serde);\n this.client = client;\n this.client.appendMetadata({\n name: \"langgraphjs_checkpoint_saver\",\n });\n this.db = this.client.db(dbName);\n this.checkpointCollectionName =\n checkpointCollectionName ?? this.checkpointCollectionName;\n this.checkpointWritesCollectionName =\n checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;\n this.enableTimestamps = enableTimestamps ?? false;\n }\n\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const {\n thread_id,\n checkpoint_ns = \"\",\n checkpoint_id,\n } = config.configurable ?? {};\n let query;\n if (checkpoint_id) {\n query = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n } else {\n query = { thread_id, checkpoint_ns };\n }\n const result = await this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1)\n .limit(1)\n .toArray();\n if (result.length === 0) {\n return undefined;\n }\n const doc = result[0];\n const configurableValues = {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n };\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const serializedWrites = await this.db\n .collection(this.checkpointWritesCollectionName)\n .find(configurableValues)\n .toArray();\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n serializedWrites.map(async (serializedWrite) => {\n return [\n serializedWrite.task_id,\n serializedWrite.channel,\n await this.serde.loadsTyped(\n serializedWrite.type,\n serializedWrite.value.value(\"utf8\")\n ),\n ] as CheckpointPendingWrite;\n })\n );\n return {\n config: { configurable: configurableValues },\n checkpoint,\n pendingWrites,\n metadata: (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata,\n parentConfig:\n doc.parent_checkpoint_id != null\n ? {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n const { limit, before, filter } = options ?? {};\n const query: Record<string, unknown> = {};\n\n if (config?.configurable?.thread_id) {\n query.thread_id = config.configurable.thread_id;\n }\n\n if (\n config?.configurable?.checkpoint_ns !== undefined &&\n config?.configurable?.checkpoint_ns !== null\n ) {\n query.checkpoint_ns = config.configurable.checkpoint_ns;\n }\n\n if (filter) {\n Object.entries(filter).forEach(([key, value]) => {\n // Prevent MongoDB operator injection - only allow primitive values\n if (value !== null && typeof value === \"object\") {\n throw new Error(\n `Invalid filter value for key \"${key}\": filter values must be primitives (string, number, boolean, or null)`\n );\n }\n query[`metadata.${key}`] = value;\n });\n }\n\n if (before) {\n query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };\n }\n\n let result = this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1);\n\n if (limit !== undefined) {\n result = result.limit(limit);\n }\n\n for await (const doc of result) {\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const metadata = (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata;\n\n yield {\n config: {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: doc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n }\n\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n const checkpoint_id = checkpoint.id;\n if (thread_id === undefined) {\n throw new Error(\n `The provided config must contain a configurable field with a \"thread_id\" field.`\n );\n }\n const [\n [checkpointType, serializedCheckpoint],\n [metadataType, serializedMetadata],\n ] = await Promise.all([\n this.serde.dumpsTyped(checkpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (checkpointType !== metadataType) {\n throw new Error(\"Mismatched checkpoint and metadata types.\");\n }\n const doc = {\n parent_checkpoint_id: config.configurable?.checkpoint_id,\n type: checkpointType,\n checkpoint: serializedCheckpoint,\n metadata: serializedMetadata,\n };\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n await this.db\n .collection(this.checkpointCollectionName)\n .updateOne(\n upsertQuery,\n { $set: doc, ...this.timestampOp },\n { upsert: true }\n );\n\n return {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n },\n };\n }\n\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns;\n const checkpoint_id = config.configurable?.checkpoint_id;\n if (\n thread_id === undefined ||\n checkpoint_ns === undefined ||\n checkpoint_id === undefined\n ) {\n throw new Error(\n `The provided config must contain a configurable field with \"thread_id\", \"checkpoint_ns\" and \"checkpoint_id\" fields.`\n );\n }\n\n const operations = await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n task_id: taskId,\n idx,\n };\n\n const [type, serializedValue] = await this.serde.dumpsTyped(value);\n\n return {\n updateOne: {\n filter: upsertQuery,\n update: {\n $set: { channel, type, value: serializedValue },\n ...this.timestampOp,\n },\n upsert: true,\n },\n };\n })\n );\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .bulkWrite(operations);\n }\n\n async deleteThread(threadId: string) {\n await this.db\n .collection(this.checkpointCollectionName)\n .deleteMany({ thread_id: threadId });\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .deleteMany({ thread_id: threadId });\n }\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -8,6 +8,11 @@ type MongoDBSaverParams = {
8
8
  dbName?: string;
9
9
  checkpointCollectionName?: string;
10
10
  checkpointWritesCollectionName?: string;
11
+ /**
12
+ * When true, writes an `upserted_at` BSON date to documents on every upsert.
13
+ * Useful for MongoDB TTL indexes, auditing, or debugging.
14
+ */
15
+ enableTimestamps?: boolean;
11
16
  };
12
17
  /**
13
18
  * A LangGraph checkpoint saver backed by a MongoDB database.
@@ -17,11 +22,14 @@ declare class MongoDBSaver extends BaseCheckpointSaver {
17
22
  protected db: Db;
18
23
  checkpointCollectionName: string;
19
24
  checkpointWritesCollectionName: string;
25
+ protected enableTimestamps: boolean;
26
+ private get timestampOp();
20
27
  constructor({
21
28
  client,
22
29
  dbName,
23
30
  checkpointCollectionName,
24
- checkpointWritesCollectionName
31
+ checkpointWritesCollectionName,
32
+ enableTimestamps
25
33
  }: MongoDBSaverParams, serde?: SerializerProtocol);
26
34
  /**
27
35
  * Retrieves a checkpoint from the MongoDB database based on the
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":["MongoClient","Db","MongoDatabase","RunnableConfig","BaseCheckpointSaver","Checkpoint","CheckpointListOptions","CheckpointTuple","SerializerProtocol","PendingWrite","CheckpointMetadata","MongoDBSaverParams","MongoDBSaver","client","dbName","checkpointCollectionName","checkpointWritesCollectionName","Promise","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { BaseCheckpointSaver, type Checkpoint, type CheckpointListOptions, type CheckpointTuple, type SerializerProtocol, type PendingWrite, type CheckpointMetadata } from \"@langchain/langgraph-checkpoint\";\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n};\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport declare class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n protected db: MongoDatabase;\n checkpointCollectionName: string;\n checkpointWritesCollectionName: string;\n constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName }: MongoDBSaverParams, serde?: SerializerProtocol);\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n}\n"],"mappings":";;;;;KAGYW,kBAAAA;UACAX;EADAW,MAAAA,CAAAA,EAAAA,MAAAA;EASSC,wBAAY,CAAA,EAAA,MAAA;EAAA,8BAAA,CAAA,EAAA,MAAA;;;;;AAKCG,cALbH,YAAAA,SAAqBR,mBAAAA,CAKRW;YAA0BC,MAAAA,EAJtChB,WAIsCgB;YAAkCL,EAAAA,EAH5ET,EAG4ES;0BAA4BH,EAAAA,MAAAA;gCAOrGL,EAAAA,MAAAA;aAAyBI,CAAAA;IAAAA,MAAAA;IAAAA,MAAAA;IAAAA,wBAAAA;IAAAA;EAAAA,CAAAA,EAPgDI,kBAOhDJ,EAAAA,KAAAA,CAAAA,EAP4EC,kBAO5ED;;;;;;;UAWFF,CAAAA,MAAAA,EAXvBF,cAWuBE,CAAAA,EAXNY,OAWMZ,CAXEE,eAWFF,GAAAA,SAAAA,CAAAA;;;;;;MAImCY,CAAAA,MAAAA,EAT9Dd,cAS8Dc,EAAAA,OAAAA,CAAAA,EATpCX,qBASoCW,CAAAA,EATZC,cASYD,CATGV,eASHU,CAAAA;;;;;cAJ/Dd,4BAA4BE,sBAAsBK,qBAAqBO,QAAQd;;;;oBAIzEA,wBAAwBM,iCAAiCQ;kCAC3CA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;KAaY,kBAAA;EACV,MAAA,EAAQ,WAAA;EACR,MAAA;EACA,wBAAA;EACA,8BAAA;EAHmB;;;;EAQnB,gBAAA;AAAA;;;;cAMW,YAAA,SAAqB,mBAAA;EAAA,UACtB,MAAA,EAAQ,WAAA;EAAA,UAER,EAAA,EAAI,EAAA;EAEd,wBAAA;EAEA,8BAAA;EAAA,UAEU,gBAAA;EAAA,YAEE,WAAA,CAAA;EAMZ,WAAA,CAAA;IAEI,MAAA;IACA,MAAA;IACA,wBAAA;IACA,8BAAA;IACA;EAAA,GACC,kBAAA,EACH,KAAA,GAAQ,kBAAA;EAHN;;;;;;EAwBE,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EA8EtC;;;;;EADH,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAgFN;;;;EAHN,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EAuDR;;;EAJG,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAgB,OAAA;AAAA"}
package/dist/index.d.ts CHANGED
@@ -8,6 +8,11 @@ type MongoDBSaverParams = {
8
8
  dbName?: string;
9
9
  checkpointCollectionName?: string;
10
10
  checkpointWritesCollectionName?: string;
11
+ /**
12
+ * When true, writes an `upserted_at` BSON date to documents on every upsert.
13
+ * Useful for MongoDB TTL indexes, auditing, or debugging.
14
+ */
15
+ enableTimestamps?: boolean;
11
16
  };
12
17
  /**
13
18
  * A LangGraph checkpoint saver backed by a MongoDB database.
@@ -17,11 +22,14 @@ declare class MongoDBSaver extends BaseCheckpointSaver {
17
22
  protected db: Db;
18
23
  checkpointCollectionName: string;
19
24
  checkpointWritesCollectionName: string;
25
+ protected enableTimestamps: boolean;
26
+ private get timestampOp();
20
27
  constructor({
21
28
  client,
22
29
  dbName,
23
30
  checkpointCollectionName,
24
- checkpointWritesCollectionName
31
+ checkpointWritesCollectionName,
32
+ enableTimestamps
25
33
  }: MongoDBSaverParams, serde?: SerializerProtocol);
26
34
  /**
27
35
  * Retrieves a checkpoint from the MongoDB database based on the
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["MongoClient","Db","MongoDatabase","RunnableConfig","BaseCheckpointSaver","Checkpoint","CheckpointListOptions","CheckpointTuple","SerializerProtocol","PendingWrite","CheckpointMetadata","MongoDBSaverParams","MongoDBSaver","client","dbName","checkpointCollectionName","checkpointWritesCollectionName","Promise","AsyncGenerator"],"sources":["../src/index.d.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport { BaseCheckpointSaver, type Checkpoint, type CheckpointListOptions, type CheckpointTuple, type SerializerProtocol, type PendingWrite, type CheckpointMetadata } from \"@langchain/langgraph-checkpoint\";\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n};\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport declare class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n protected db: MongoDatabase;\n checkpointCollectionName: string;\n checkpointWritesCollectionName: string;\n constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName }: MongoDBSaverParams, serde?: SerializerProtocol);\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;\n deleteThread(threadId: string): Promise<void>;\n}\n"],"mappings":";;;;;KAGYW,kBAAAA;UACAX;EADAW,MAAAA,CAAAA,EAAAA,MAAAA;EASSC,wBAAY,CAAA,EAAA,MAAA;EAAA,8BAAA,CAAA,EAAA,MAAA;;;;;AAKCG,cALbH,YAAAA,SAAqBR,mBAAAA,CAKRW;YAA0BC,MAAAA,EAJtChB,WAIsCgB;YAAkCL,EAAAA,EAH5ET,EAG4ES;0BAA4BH,EAAAA,MAAAA;gCAOrGL,EAAAA,MAAAA;aAAyBI,CAAAA;IAAAA,MAAAA;IAAAA,MAAAA;IAAAA,wBAAAA;IAAAA;EAAAA,CAAAA,EAPgDI,kBAOhDJ,EAAAA,KAAAA,CAAAA,EAP4EC,kBAO5ED;;;;;;;UAWFF,CAAAA,MAAAA,EAXvBF,cAWuBE,CAAAA,EAXNY,OAWMZ,CAXEE,eAWFF,GAAAA,SAAAA,CAAAA;;;;;;MAImCY,CAAAA,MAAAA,EAT9Dd,cAS8Dc,EAAAA,OAAAA,CAAAA,EATpCX,qBASoCW,CAAAA,EATZC,cASYD,CATGV,eASHU,CAAAA;;;;;cAJ/Dd,4BAA4BE,sBAAsBK,qBAAqBO,QAAQd;;;;oBAIzEA,wBAAwBM,iCAAiCQ;kCAC3CA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;KAaY,kBAAA;EACV,MAAA,EAAQ,WAAA;EACR,MAAA;EACA,wBAAA;EACA,8BAAA;EAHmB;;;;EAQnB,gBAAA;AAAA;;;;cAMW,YAAA,SAAqB,mBAAA;EAAA,UACtB,MAAA,EAAQ,WAAA;EAAA,UAER,EAAA,EAAI,EAAA;EAEd,wBAAA;EAEA,8BAAA;EAAA,UAEU,gBAAA;EAAA,YAEE,WAAA,CAAA;EAMZ,WAAA,CAAA;IAEI,MAAA;IACA,MAAA;IACA,wBAAA;IACA,8BAAA;IACA;EAAA,GACC,kBAAA,EACH,KAAA,GAAQ,kBAAA;EAHN;;;;;;EAwBE,QAAA,CAAS,MAAA,EAAQ,cAAA,GAAiB,OAAA,CAAQ,eAAA;EA8EtC;;;;;EADH,IAAA,CACL,MAAA,EAAQ,cAAA,EACR,OAAA,GAAU,qBAAA,GACT,cAAA,CAAe,eAAA;EAgFN;;;;EAHN,GAAA,CACJ,MAAA,EAAQ,cAAA,EACR,UAAA,EAAY,UAAA,EACZ,QAAA,EAAU,kBAAA,GACT,OAAA,CAAQ,cAAA;EAuDR;;;EAJG,SAAA,CACJ,MAAA,EAAQ,cAAA,EACR,MAAA,EAAQ,YAAA,IACR,MAAA,WACC,OAAA;EA4CG,YAAA,CAAa,QAAA,WAAgB,OAAA;AAAA"}
package/dist/index.js CHANGED
@@ -9,13 +9,18 @@ var MongoDBSaver = class extends BaseCheckpointSaver {
9
9
  db;
10
10
  checkpointCollectionName = "checkpoints";
11
11
  checkpointWritesCollectionName = "checkpoint_writes";
12
- constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName }, serde) {
12
+ enableTimestamps;
13
+ get timestampOp() {
14
+ return this.enableTimestamps ? { $currentDate: { upserted_at: true } } : {};
15
+ }
16
+ constructor({ client, dbName, checkpointCollectionName, checkpointWritesCollectionName, enableTimestamps }, serde) {
13
17
  super(serde);
14
18
  this.client = client;
15
19
  this.client.appendMetadata({ name: "langgraphjs_checkpoint_saver" });
16
20
  this.db = this.client.db(dbName);
17
21
  this.checkpointCollectionName = checkpointCollectionName ?? this.checkpointCollectionName;
18
22
  this.checkpointWritesCollectionName = checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;
23
+ this.enableTimestamps = enableTimestamps ?? false;
19
24
  }
20
25
  /**
21
26
  * Retrieves a checkpoint from the MongoDB database based on the
@@ -75,6 +80,7 @@ var MongoDBSaver = class extends BaseCheckpointSaver {
75
80
  if (config?.configurable?.thread_id) query.thread_id = config.configurable.thread_id;
76
81
  if (config?.configurable?.checkpoint_ns !== void 0 && config?.configurable?.checkpoint_ns !== null) query.checkpoint_ns = config.configurable.checkpoint_ns;
77
82
  if (filter) Object.entries(filter).forEach(([key, value]) => {
83
+ if (value !== null && typeof value === "object") throw new Error(`Invalid filter value for key "${key}": filter values must be primitives (string, number, boolean, or null)`);
78
84
  query[`metadata.${key}`] = value;
79
85
  });
80
86
  if (before) query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };
@@ -121,7 +127,10 @@ var MongoDBSaver = class extends BaseCheckpointSaver {
121
127
  checkpoint_ns,
122
128
  checkpoint_id
123
129
  };
124
- await this.db.collection(this.checkpointCollectionName).updateOne(upsertQuery, { $set: doc }, { upsert: true });
130
+ await this.db.collection(this.checkpointCollectionName).updateOne(upsertQuery, {
131
+ $set: doc,
132
+ ...this.timestampOp
133
+ }, { upsert: true });
125
134
  return { configurable: {
126
135
  thread_id,
127
136
  checkpoint_ns,
@@ -147,11 +156,14 @@ var MongoDBSaver = class extends BaseCheckpointSaver {
147
156
  const [type, serializedValue] = await this.serde.dumpsTyped(value);
148
157
  return { updateOne: {
149
158
  filter: upsertQuery,
150
- update: { $set: {
151
- channel,
152
- type,
153
- value: serializedValue
154
- } },
159
+ update: {
160
+ $set: {
161
+ channel,
162
+ type,
163
+ value: serializedValue
164
+ },
165
+ ...this.timestampOp
166
+ },
155
167
  upsert: true
156
168
  } };
157
169
  }));
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n CheckpointPendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\n\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n};\n\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n\n protected db: MongoDatabase;\n\n checkpointCollectionName = \"checkpoints\";\n\n checkpointWritesCollectionName = \"checkpoint_writes\";\n\n constructor(\n {\n client,\n dbName,\n checkpointCollectionName,\n checkpointWritesCollectionName,\n }: MongoDBSaverParams,\n serde?: SerializerProtocol\n ) {\n super(serde);\n this.client = client;\n this.client.appendMetadata({\n name: \"langgraphjs_checkpoint_saver\",\n });\n this.db = this.client.db(dbName);\n this.checkpointCollectionName =\n checkpointCollectionName ?? this.checkpointCollectionName;\n this.checkpointWritesCollectionName =\n checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;\n }\n\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const {\n thread_id,\n checkpoint_ns = \"\",\n checkpoint_id,\n } = config.configurable ?? {};\n let query;\n if (checkpoint_id) {\n query = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n } else {\n query = { thread_id, checkpoint_ns };\n }\n const result = await this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1)\n .limit(1)\n .toArray();\n if (result.length === 0) {\n return undefined;\n }\n const doc = result[0];\n const configurableValues = {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n };\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const serializedWrites = await this.db\n .collection(this.checkpointWritesCollectionName)\n .find(configurableValues)\n .toArray();\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n serializedWrites.map(async (serializedWrite) => {\n return [\n serializedWrite.task_id,\n serializedWrite.channel,\n await this.serde.loadsTyped(\n serializedWrite.type,\n serializedWrite.value.value(\"utf8\")\n ),\n ] as CheckpointPendingWrite;\n })\n );\n return {\n config: { configurable: configurableValues },\n checkpoint,\n pendingWrites,\n metadata: (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata,\n parentConfig:\n doc.parent_checkpoint_id != null\n ? {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n const { limit, before, filter } = options ?? {};\n const query: Record<string, unknown> = {};\n\n if (config?.configurable?.thread_id) {\n query.thread_id = config.configurable.thread_id;\n }\n\n if (\n config?.configurable?.checkpoint_ns !== undefined &&\n config?.configurable?.checkpoint_ns !== null\n ) {\n query.checkpoint_ns = config.configurable.checkpoint_ns;\n }\n\n if (filter) {\n Object.entries(filter).forEach(([key, value]) => {\n query[`metadata.${key}`] = value;\n });\n }\n\n if (before) {\n query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };\n }\n\n let result = this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1);\n\n if (limit !== undefined) {\n result = result.limit(limit);\n }\n\n for await (const doc of result) {\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const metadata = (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata;\n\n yield {\n config: {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: doc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n }\n\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n const checkpoint_id = checkpoint.id;\n if (thread_id === undefined) {\n throw new Error(\n `The provided config must contain a configurable field with a \"thread_id\" field.`\n );\n }\n const [\n [checkpointType, serializedCheckpoint],\n [metadataType, serializedMetadata],\n ] = await Promise.all([\n this.serde.dumpsTyped(checkpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (checkpointType !== metadataType) {\n throw new Error(\"Mismatched checkpoint and metadata types.\");\n }\n const doc = {\n parent_checkpoint_id: config.configurable?.checkpoint_id,\n type: checkpointType,\n checkpoint: serializedCheckpoint,\n metadata: serializedMetadata,\n };\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n await this.db\n .collection(this.checkpointCollectionName)\n .updateOne(upsertQuery, { $set: doc }, { upsert: true });\n\n return {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n },\n };\n }\n\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns;\n const checkpoint_id = config.configurable?.checkpoint_id;\n if (\n thread_id === undefined ||\n checkpoint_ns === undefined ||\n checkpoint_id === undefined\n ) {\n throw new Error(\n `The provided config must contain a configurable field with \"thread_id\", \"checkpoint_ns\" and \"checkpoint_id\" fields.`\n );\n }\n\n const operations = await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n task_id: taskId,\n idx,\n };\n\n const [type, serializedValue] = await this.serde.dumpsTyped(value);\n\n return {\n updateOne: {\n filter: upsertQuery,\n update: { $set: { channel, type, value: serializedValue } },\n upsert: true,\n },\n };\n })\n );\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .bulkWrite(operations);\n }\n\n async deleteThread(threadId: string) {\n await this.db\n .collection(this.checkpointCollectionName)\n .deleteMany({ thread_id: threadId });\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .deleteMany({ thread_id: threadId });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { type MongoClient, type Db as MongoDatabase } from \"mongodb\";\nimport type { RunnableConfig } from \"@langchain/core/runnables\";\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n CheckpointPendingWrite,\n} from \"@langchain/langgraph-checkpoint\";\n\nexport type MongoDBSaverParams = {\n client: MongoClient;\n dbName?: string;\n checkpointCollectionName?: string;\n checkpointWritesCollectionName?: string;\n /**\n * When true, writes an `upserted_at` BSON date to documents on every upsert.\n * Useful for MongoDB TTL indexes, auditing, or debugging.\n */\n enableTimestamps?: boolean;\n};\n\n/**\n * A LangGraph checkpoint saver backed by a MongoDB database.\n */\nexport class MongoDBSaver extends BaseCheckpointSaver {\n protected client: MongoClient;\n\n protected db: MongoDatabase;\n\n checkpointCollectionName = \"checkpoints\";\n\n checkpointWritesCollectionName = \"checkpoint_writes\";\n\n protected enableTimestamps: boolean;\n\n private get timestampOp() {\n return this.enableTimestamps\n ? ({ $currentDate: { upserted_at: true } } as const)\n : {};\n }\n\n constructor(\n {\n client,\n dbName,\n checkpointCollectionName,\n checkpointWritesCollectionName,\n enableTimestamps,\n }: MongoDBSaverParams,\n serde?: SerializerProtocol\n ) {\n super(serde);\n this.client = client;\n this.client.appendMetadata({\n name: \"langgraphjs_checkpoint_saver\",\n });\n this.db = this.client.db(dbName);\n this.checkpointCollectionName =\n checkpointCollectionName ?? this.checkpointCollectionName;\n this.checkpointWritesCollectionName =\n checkpointWritesCollectionName ?? this.checkpointWritesCollectionName;\n this.enableTimestamps = enableTimestamps ?? false;\n }\n\n /**\n * Retrieves a checkpoint from the MongoDB database based on the\n * provided config. If the config contains a \"checkpoint_id\" key, the checkpoint with\n * the matching thread ID and checkpoint ID is retrieved. Otherwise, the latest checkpoint\n * for the given thread ID is retrieved.\n */\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n const {\n thread_id,\n checkpoint_ns = \"\",\n checkpoint_id,\n } = config.configurable ?? {};\n let query;\n if (checkpoint_id) {\n query = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n } else {\n query = { thread_id, checkpoint_ns };\n }\n const result = await this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1)\n .limit(1)\n .toArray();\n if (result.length === 0) {\n return undefined;\n }\n const doc = result[0];\n const configurableValues = {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n };\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const serializedWrites = await this.db\n .collection(this.checkpointWritesCollectionName)\n .find(configurableValues)\n .toArray();\n const pendingWrites: CheckpointPendingWrite[] = await Promise.all(\n serializedWrites.map(async (serializedWrite) => {\n return [\n serializedWrite.task_id,\n serializedWrite.channel,\n await this.serde.loadsTyped(\n serializedWrite.type,\n serializedWrite.value.value(\"utf8\")\n ),\n ] as CheckpointPendingWrite;\n })\n );\n return {\n config: { configurable: configurableValues },\n checkpoint,\n pendingWrites,\n metadata: (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata,\n parentConfig:\n doc.parent_checkpoint_id != null\n ? {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n\n /**\n * Retrieve a list of checkpoint tuples from the MongoDB database based\n * on the provided config. The checkpoints are ordered by checkpoint ID\n * in descending order (newest first).\n */\n async *list(\n config: RunnableConfig,\n options?: CheckpointListOptions\n ): AsyncGenerator<CheckpointTuple> {\n const { limit, before, filter } = options ?? {};\n const query: Record<string, unknown> = {};\n\n if (config?.configurable?.thread_id) {\n query.thread_id = config.configurable.thread_id;\n }\n\n if (\n config?.configurable?.checkpoint_ns !== undefined &&\n config?.configurable?.checkpoint_ns !== null\n ) {\n query.checkpoint_ns = config.configurable.checkpoint_ns;\n }\n\n if (filter) {\n Object.entries(filter).forEach(([key, value]) => {\n // Prevent MongoDB operator injection - only allow primitive values\n if (value !== null && typeof value === \"object\") {\n throw new Error(\n `Invalid filter value for key \"${key}\": filter values must be primitives (string, number, boolean, or null)`\n );\n }\n query[`metadata.${key}`] = value;\n });\n }\n\n if (before) {\n query.checkpoint_id = { $lt: before.configurable?.checkpoint_id };\n }\n\n let result = this.db\n .collection(this.checkpointCollectionName)\n .find(query)\n .sort(\"checkpoint_id\", -1);\n\n if (limit !== undefined) {\n result = result.limit(limit);\n }\n\n for await (const doc of result) {\n const checkpoint = (await this.serde.loadsTyped(\n doc.type,\n doc.checkpoint.value(\"utf8\")\n )) as Checkpoint;\n const metadata = (await this.serde.loadsTyped(\n doc.type,\n doc.metadata.value(\"utf8\")\n )) as CheckpointMetadata;\n\n yield {\n config: {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: doc.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: doc.thread_id,\n checkpoint_ns: doc.checkpoint_ns,\n checkpoint_id: doc.parent_checkpoint_id,\n },\n }\n : undefined,\n };\n }\n }\n\n /**\n * Saves a checkpoint to the MongoDB database. The checkpoint is associated\n * with the provided config and its parent config (if any).\n */\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata\n ): Promise<RunnableConfig> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns ?? \"\";\n const checkpoint_id = checkpoint.id;\n if (thread_id === undefined) {\n throw new Error(\n `The provided config must contain a configurable field with a \"thread_id\" field.`\n );\n }\n const [\n [checkpointType, serializedCheckpoint],\n [metadataType, serializedMetadata],\n ] = await Promise.all([\n this.serde.dumpsTyped(checkpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (checkpointType !== metadataType) {\n throw new Error(\"Mismatched checkpoint and metadata types.\");\n }\n const doc = {\n parent_checkpoint_id: config.configurable?.checkpoint_id,\n type: checkpointType,\n checkpoint: serializedCheckpoint,\n metadata: serializedMetadata,\n };\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n };\n await this.db\n .collection(this.checkpointCollectionName)\n .updateOne(\n upsertQuery,\n { $set: doc, ...this.timestampOp },\n { upsert: true }\n );\n\n return {\n configurable: {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n },\n };\n }\n\n /**\n * Saves intermediate writes associated with a checkpoint to the MongoDB database.\n */\n async putWrites(\n config: RunnableConfig,\n writes: PendingWrite[],\n taskId: string\n ): Promise<void> {\n const thread_id = config.configurable?.thread_id;\n const checkpoint_ns = config.configurable?.checkpoint_ns;\n const checkpoint_id = config.configurable?.checkpoint_id;\n if (\n thread_id === undefined ||\n checkpoint_ns === undefined ||\n checkpoint_id === undefined\n ) {\n throw new Error(\n `The provided config must contain a configurable field with \"thread_id\", \"checkpoint_ns\" and \"checkpoint_id\" fields.`\n );\n }\n\n const operations = await Promise.all(\n writes.map(async ([channel, value], idx) => {\n const upsertQuery = {\n thread_id,\n checkpoint_ns,\n checkpoint_id,\n task_id: taskId,\n idx,\n };\n\n const [type, serializedValue] = await this.serde.dumpsTyped(value);\n\n return {\n updateOne: {\n filter: upsertQuery,\n update: {\n $set: { channel, type, value: serializedValue },\n ...this.timestampOp,\n },\n upsert: true,\n },\n };\n })\n );\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .bulkWrite(operations);\n }\n\n async deleteThread(threadId: string) {\n await this.db\n .collection(this.checkpointCollectionName)\n .deleteMany({ thread_id: threadId });\n\n await this.db\n .collection(this.checkpointWritesCollectionName)\n .deleteMany({ thread_id: threadId });\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@langchain/langgraph-checkpoint-mongodb",
3
- "version": "1.1.6",
3
+ "version": "1.2.0",
4
4
  "description": "LangGraph",
5
5
  "type": "module",
6
6
  "engines": {