@langgraph-js/pure-graph 3.1.2 → 3.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/adapter/fetch/index.js +128 -30
  2. package/dist/adapter/fetch/index.js.map +1 -1
  3. package/dist/adapter/fetch/utils.d.ts +13 -3
  4. package/dist/adapter/nextjs/index.js +1 -1
  5. package/dist/{checkpoint-1sAx_j1E.js → checkpoint-CY59Lr2q.js} +79 -36
  6. package/dist/checkpoint-CY59Lr2q.js.map +1 -0
  7. package/dist/{createEndpoint-D1nfguxg.js → createEndpoint-vMmFiMSz.js} +20 -5
  8. package/dist/createEndpoint-vMmFiMSz.js.map +1 -0
  9. package/dist/createEndpoint.d.ts +2 -1
  10. package/dist/index.js +2 -2
  11. package/dist/queue/stream_queue.d.ts +3 -1
  12. package/dist/{queue-Cu_nO_wt.js → queue-CUe5TDP1.js} +120 -16
  13. package/dist/queue-CUe5TDP1.js.map +1 -0
  14. package/dist/remote/index.js +1 -1
  15. package/dist/shallow-checkpoint-ImbLxYNR.js +466 -0
  16. package/dist/shallow-checkpoint-ImbLxYNR.js.map +1 -0
  17. package/dist/{sqlite-adapter-BKOLSdoL.js → sqlite-adapter-CJXgit1j.js} +25 -12
  18. package/dist/sqlite-adapter-CJXgit1j.js.map +1 -0
  19. package/dist/storage/index.d.ts +4 -2
  20. package/dist/storage/kysely/sqlite-adapter.d.ts +7 -0
  21. package/dist/storage/memory/queue.d.ts +6 -0
  22. package/dist/storage/redis/queue.d.ts +14 -1
  23. package/dist/storage/sqlite/shallow-checkpoint.d.ts +59 -0
  24. package/dist/{stream-CsqWsCjt.js → stream-jYlUzTZO.js} +199 -64
  25. package/dist/stream-jYlUzTZO.js.map +1 -0
  26. package/package.json +2 -1
  27. package/dist/checkpoint-1sAx_j1E.js.map +0 -1
  28. package/dist/createEndpoint-D1nfguxg.js.map +0 -1
  29. package/dist/queue-Cu_nO_wt.js.map +0 -1
  30. package/dist/sqlite-adapter-BKOLSdoL.js.map +0 -1
  31. package/dist/stream-CsqWsCjt.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-CUe5TDP1.js","sources":["../src/storage/redis/queue.ts"],"sourcesContent":["import { CancelEventMessage, EventMessage } from '../../queue/event_message.js';\nimport { BaseStreamQueue } from '../../queue/stream_queue.js';\nimport { BaseStreamQueueInterface } from '../../queue/stream_queue.js';\nimport { createClient, RedisClientType } from 'redis';\n\n// 全局共享的 Redis 连接池\nlet sharedRedisClient: RedisClientType | null = null;\nlet connectionRefCount = 0;\nlet connectionPromise: Promise<void> | null = null;\nlet releaseTimeoutId: ReturnType<typeof setTimeout> | null = null;\n\n/**\n * 获取共享的 Redis 连接(线程安全)\n */\nasync function getSharedRedisClient(): Promise<RedisClientType> {\n // 如果已有连接,直接返回\n if (sharedRedisClient && sharedRedisClient.isOpen) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n\n // 如果正在建立连接,等待现有 Promise\n if (connectionPromise) {\n await connectionPromise;\n if (sharedRedisClient) {\n connectionRefCount++;\n return sharedRedisClient;\n }\n // 连接失败,继续尝试创建\n }\n\n // 创建新的连接 Promise,防止并发创建多个连接\n connectionPromise = (async () => {\n const client = createClient({\n url: process.env.REDIS_URL,\n });\n await client.connect();\n sharedRedisClient = client as RedisClientType;\n })();\n\n try {\n await connectionPromise;\n connectionRefCount++;\n return sharedRedisClient!;\n } catch (error) {\n // 连接失败时清理状态\n connectionPromise = null;\n throw error;\n } finally {\n connectionPromise = null;\n }\n}\n\n/**\n * 释放 Redis 连接引用\n */\nasync function releaseRedisClient(): Promise<void> {\n if (connectionRefCount > 0) {\n connectionRefCount--;\n }\n\n // 引用计数为 0 且超过一定时间没有新连接时才关闭\n // 避免频繁开关连接\n if (connectionRefCount <= 0 && sharedRedisClient) {\n // 清理之前的延迟关闭定时器\n if (releaseTimeoutId) {\n clearTimeout(releaseTimeoutId);\n releaseTimeoutId = null;\n }\n\n // 延迟关闭,给其他队列复用连接的机会\n releaseTimeoutId = setTimeout(async () => {\n if (connectionRefCount <= 0 && sharedRedisClient) {\n try {\n await sharedRedisClient.quit();\n } catch (e) {\n // 忽略关闭错误\n }\n sharedRedisClient = null;\n connectionRefCount = 0;\n releaseTimeoutId = null;\n }\n }, 5000);\n }\n}\n\n/**\n * Redis Stream 实现的消息队列,用于存储消息\n * 使用 Redis Streams 替代 pub/sub,支持集群模式\n */\nexport class RedisStreamQueue extends BaseStreamQueue implements BaseStreamQueueInterface {\n private redis: RedisClientType | null = null;\n private streamKey: string;\n private listKey: string;\n private isConnected = false;\n public cancelSignal: AbortController;\n private lastStreamId: string = '0'; // 最后读取的 Stream ID\n private pollInterval: number = 100; // 轮询间隔(毫秒)\n private connectionReady: Promise<void>;\n\n constructor(readonly id: string, readonly compressMessages: boolean = true, readonly ttl: number = 300) {\n super(id, true, ttl);\n this.streamKey = `stream:${this.id}`;\n this.listKey = `queue:${this.id}`;\n this.cancelSignal = new AbortController();\n\n // 异步初始化 Redis 连接\n this.connectionReady = this.initConnection();\n }\n\n /**\n * 初始化 Redis 连接(使用共享连接池)\n */\n private async initConnection(): Promise<void> {\n try {\n this.redis = await getSharedRedisClient();\n this.isConnected = true;\n } catch (error) {\n console.error('Failed to connect to Redis:', error);\n throw error;\n }\n }\n\n /**\n * 确保连接已建立\n */\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected || !this.redis) {\n await this.connectionReady;\n }\n }\n\n /**\n * 推送消息到 Redis Stream 和 List\n * - Stream: 用于实时推送(集群友好)\n * - List: 用于 getAll() 批量获取历史数据\n */\n async push(item: EventMessage): Promise<void> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const encodedData = await this.encodeData(item);\n // 将 Uint8Array 转换为 base64 字符串,以便存储到 Redis Stream\n const dataString = Buffer.from(encodedData).toString('base64');\n const serializedData = Buffer.from(encodedData);\n\n // 推送到 Stream(实时推送)\n // 注意:xAdd 的第三个参数必须是简单的键值对对象,值必须是字符串\n await this.redis.xAdd(this.streamKey, '*', { data: dataString });\n\n // 设置 Stream TTL\n await this.redis.expire(this.streamKey, this.ttl);\n\n // 同时推送到 List(用于 getAll)\n await this.redis.rPush(this.listKey, serializedData);\n await this.redis.expire(this.listKey, this.ttl);\n\n this.emit('dataChange', dataString);\n }\n\n /**\n * 异步生成器:使用 Redis Streams XREAD 轮询消费队列数据\n */\n async *onDataReceive(): AsyncGenerator<EventMessage, void, unknown> {\n let isStreamEnded = false;\n let isCleanupDone = false;\n\n // 等待连接建立\n await this.ensureConnected();\n if (!this.redis) {\n throw new Error('Redis connection not available');\n }\n\n // 检查是否已取消\n if (this.cancelSignal.signal.aborted) {\n return;\n }\n\n // 监听取消信号\n const abortHandler = () => {\n isStreamEnded = true;\n };\n this.cancelSignal.signal.addEventListener('abort', abortHandler);\n\n const cleanup = () => {\n if (isCleanupDone) return;\n isCleanupDone = true;\n try {\n this.cancelSignal.signal.removeEventListener('abort', abortHandler);\n } catch (e) {\n console.error('Error removing abort listener:', e);\n }\n };\n\n try {\n while (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n // 检查 Redis 连接是否仍然有效\n if (!this.redis || !this.isConnected) {\n break;\n }\n\n // 从 Stream 读取新消息(XREAD 阻塞读取)\n const streams = await this.redis.xRead([{ key: this.streamKey, id: this.lastStreamId }], {\n BLOCK: this.pollInterval,\n COUNT: 10,\n });\n\n if (streams && streams.length > 0) {\n for (const stream of streams) {\n for (const message of stream.messages) {\n // 更新最后读取的 ID\n this.lastStreamId = message.id;\n\n // 解析消息:从 base64 字符串转换回 Uint8Array\n const dataString = message.message.data as string;\n const data = Buffer.from(dataString, 'base64');\n const item = (await this.decodeData(data)) as EventMessage;\n\n // 检查是否为流结束或错误信号\n if (\n item.event === '__stream_end__' ||\n item.event === '__stream_error__' ||\n item.event === '__stream_cancel__'\n ) {\n // 延迟 300ms 后结束,确保消息被消费\n await new Promise((resolve) => setTimeout(resolve, 300));\n isStreamEnded = true;\n\n if (item.event === '__stream_cancel__') {\n await this.cancel();\n }\n }\n\n yield item;\n\n if (isStreamEnded) {\n break;\n }\n }\n if (isStreamEnded) {\n break;\n }\n }\n }\n\n // 轮询间隔\n if (!isStreamEnded && !this.cancelSignal.signal.aborted) {\n await new Promise((resolve) => setTimeout(resolve, this.pollInterval));\n }\n }\n } finally {\n cleanup();\n }\n }\n\n /**\n * 获取队列中的所有数据(从 List 获取历史数据)\n */\n async getAll(): Promise<EventMessage[]> {\n await this.ensureConnected();\n if (!this.redis) return [];\n\n const data = await this.redis.lRange(this.listKey, 0, -1);\n\n if (!data || data.length === 0) {\n return [];\n }\n\n if (this.compressMessages) {\n return (await Promise.all(\n data.map((item: Buffer | string) => {\n // 处理 Buffer 或字符串类型\n const buffer = typeof item === 'string' ? Buffer.from(item, 'binary') : item;\n return this.decodeData(buffer);\n }),\n )) as EventMessage[];\n } else {\n return data.map((item: string) => JSON.parse(item) as EventMessage);\n }\n }\n\n /**\n * 清空队列\n */\n async clear(): Promise<void> {\n if (this.isConnected && this.redis) {\n // 同时清空 Stream 和 List\n await Promise.all([this.redis.del(this.streamKey), this.redis.del(this.listKey)]);\n }\n }\n\n /**\n * 销毁队列实例,释放 Redis 连接引用\n */\n async destroy(): Promise<void> {\n await this.clear();\n this.isConnected = false;\n this.redis = null;\n await releaseRedisClient();\n }\n\n /**\n * 取消操作\n */\n async cancel(): Promise<void> {\n // First abort to stop any waiting generators\n this.cancelSignal.abort('user cancel this run');\n // Then push the cancel message to signal other consumers\n await this.push(new CancelEventMessage());\n }\n\n /**\n * 复制队列到另一个队列\n */\n async copyToQueue(toId: string, ttl?: number): Promise<RedisStreamQueue> {\n await this.ensureConnected();\n if (!this.redis) throw new Error('Redis connection not available');\n\n const queue = new RedisStreamQueue(toId, this.compressMessages, ttl ?? this.ttl);\n await queue.ensureConnected();\n\n if (!queue.redis) throw new Error('Target Redis connection not available');\n\n // 复制 List\n await this.redis.copy(this.listKey, queue.listKey);\n await this.redis.expire(queue.listKey, ttl ?? this.ttl);\n\n // 复制 Stream(需要遍历并重新添加)\n const allStreamData = await this.redis.xRange(this.streamKey, '-', '+');\n if (allStreamData && allStreamData.length > 0) {\n for (const message of allStreamData) {\n // 确保所有值都是字符串,Redis Streams 只支持 string 值\n const fields: Record<string, string> = {};\n for (const [key, value] of Object.entries(message.message)) {\n fields[key] = String(value);\n }\n await this.redis.xAdd(queue.streamKey, '*', fields);\n }\n await this.redis.expire(queue.streamKey, ttl ?? this.ttl);\n }\n\n return queue;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Hono } from 'hono';
2
2
  import { PostgresAdapter } from '../pg-adapter-BFtir1GE.js';
3
- import { K as KyselyThreadsManager } from '../stream-CsqWsCjt.js';
3
+ import { K as KyselyThreadsManager } from '../stream-jYlUzTZO.js';
4
4
  import { Pool } from 'pg';
5
5
 
6
6
  class RemoteServer {
@@ -0,0 +1,466 @@
1
+ import { Kysely, sql } from 'kysely';
2
+ import { BaseCheckpointSaver, getCheckpointId, TASKS, uuid6, copyCheckpoint, WRITES_IDX_MAP, maxChannelVersion } from '@langchain/langgraph-checkpoint';
3
+
4
+ const SQLITE_RETRY_CONFIG = {
5
+ maxRetries: 3,
6
+ baseDelayMs: 100,
7
+ isRetryableError: (error) => {
8
+ const msg = error?.message?.toLowerCase() || "";
9
+ return msg.includes("sqlite_busy") || msg.includes("database is locked") || msg.includes("database disk image is malformed") || msg === "sqlite_busy" || msg === "database is locked";
10
+ }
11
+ };
12
+ async function withRetry(operation, context) {
13
+ let lastError = null;
14
+ for (let attempt = 0; attempt < SQLITE_RETRY_CONFIG.maxRetries; attempt++) {
15
+ try {
16
+ return await operation();
17
+ } catch (error) {
18
+ lastError = error;
19
+ if (!SQLITE_RETRY_CONFIG.isRetryableError(error)) {
20
+ throw error;
21
+ }
22
+ if (attempt < SQLITE_RETRY_CONFIG.maxRetries - 1) {
23
+ const delay = SQLITE_RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);
24
+ console.warn(
25
+ `SQLite lock detected${context ? ` (${context})` : ""}, retrying in ${delay}ms (attempt ${attempt + 1}/${SQLITE_RETRY_CONFIG.maxRetries})`
26
+ );
27
+ await new Promise((resolve) => setTimeout(resolve, delay));
28
+ }
29
+ }
30
+ }
31
+ throw lastError;
32
+ }
33
+ function deterministicStringify(obj) {
34
+ if (obj === null || typeof obj !== "object") {
35
+ return JSON.stringify(obj);
36
+ }
37
+ if (Array.isArray(obj)) {
38
+ return JSON.stringify(obj.map((item) => deterministicStringify(item)));
39
+ }
40
+ const sortedObj = {};
41
+ const sortedKeys = Object.keys(obj).sort();
42
+ for (const key of sortedKeys) {
43
+ sortedObj[key] = obj[key];
44
+ }
45
+ return JSON.stringify(sortedObj, (_, value) => {
46
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
47
+ const sorted = {};
48
+ const keys = Object.keys(value).sort();
49
+ for (const k of keys) {
50
+ sorted[k] = value[k];
51
+ }
52
+ return sorted;
53
+ }
54
+ return value;
55
+ });
56
+ }
57
+ class SqliteShallowSaver extends BaseCheckpointSaver {
58
+ db;
59
+ isSetup;
60
+ constructor(dialect, serde) {
61
+ super(serde);
62
+ this.db = new Kysely({
63
+ dialect
64
+ });
65
+ this.isSetup = false;
66
+ }
67
+ static async fromConnStringAsync(connStringOrLocalPath) {
68
+ let saver;
69
+ if (globalThis.Bun) {
70
+ console.log("LG | Using BunSqliteDialect " + connStringOrLocalPath);
71
+ const { BunSqliteDialect } = await import('kysely-bun-worker/normal');
72
+ saver = new SqliteShallowSaver(new BunSqliteDialect({ url: connStringOrLocalPath }));
73
+ } else {
74
+ console.log("LG | Using NodeWasmDialect");
75
+ const { default: SqliteDatabase } = await import('node-sqlite3-wasm');
76
+ const { NodeWasmDialect } = await import('kysely-wasm');
77
+ console.log(connStringOrLocalPath);
78
+ const wasm = new NodeWasmDialect({
79
+ database: new SqliteDatabase.Database(connStringOrLocalPath)
80
+ });
81
+ saver = new SqliteShallowSaver(wasm);
82
+ }
83
+ await saver.setup();
84
+ return saver;
85
+ }
86
+ async setup() {
87
+ if (this.isSetup) {
88
+ return;
89
+ }
90
+ await sql`PRAGMA busy_timeout = 5000`.execute(this.db);
91
+ await sql`PRAGMA journal_mode = WAL`.execute(this.db);
92
+ await sql`PRAGMA synchronous = NORMAL`.execute(this.db);
93
+ await sql`PRAGMA wal_autocheckpoint = 1000`.execute(this.db);
94
+ await sql`
95
+ CREATE TABLE IF NOT EXISTS shallow_checkpoints (
96
+ thread_id TEXT NOT NULL,
97
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
98
+ checkpoint_id TEXT NOT NULL,
99
+ parent_checkpoint_id TEXT,
100
+ type TEXT,
101
+ checkpoint BLOB,
102
+ metadata BLOB,
103
+ checkpoint_ts INTEGER NOT NULL DEFAULT 0,
104
+ PRIMARY KEY (thread_id, checkpoint_ns)
105
+ )`.execute(this.db);
106
+ await sql`
107
+ CREATE INDEX IF NOT EXISTS idx_shallow_checkpoints_ts
108
+ ON shallow_checkpoints(checkpoint_ts DESC)`.execute(this.db);
109
+ await sql`
110
+ CREATE TABLE IF NOT EXISTS writes (
111
+ thread_id TEXT NOT NULL,
112
+ checkpoint_ns TEXT NOT NULL DEFAULT '',
113
+ checkpoint_id TEXT NOT NULL,
114
+ task_id TEXT NOT NULL,
115
+ idx INTEGER NOT NULL,
116
+ channel TEXT NOT NULL,
117
+ type TEXT,
118
+ value BLOB,
119
+ PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)
120
+ )`.execute(this.db);
121
+ this.isSetup = true;
122
+ }
123
+ /**
124
+ * 获取 checkpoint(便捷方法)
125
+ */
126
+ async get(config) {
127
+ const tuple = await this.getTuple(config);
128
+ return tuple?.checkpoint;
129
+ }
130
+ async getTuple(config) {
131
+ await this.setup();
132
+ const threadId = config.configurable?.thread_id;
133
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
134
+ const requestedCheckpointId = getCheckpointId(config);
135
+ if (threadId === void 0) {
136
+ return void 0;
137
+ }
138
+ let query = this.db.selectFrom("shallow_checkpoints").select([
139
+ "thread_id",
140
+ "checkpoint_ns",
141
+ "checkpoint_id",
142
+ "parent_checkpoint_id",
143
+ "type",
144
+ "checkpoint",
145
+ "metadata",
146
+ "checkpoint_ts",
147
+ sql`(
148
+ SELECT json_group_array(
149
+ json_object(
150
+ 'task_id', pw.task_id,
151
+ 'channel', pw.channel,
152
+ 'type', pw.type,
153
+ 'value', CAST(pw.value AS TEXT)
154
+ )
155
+ )
156
+ FROM writes as pw
157
+ WHERE pw.thread_id = shallow_checkpoints.thread_id
158
+ AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns
159
+ AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id
160
+ )`.as("pending_writes"),
161
+ sql`(
162
+ SELECT json_group_array(
163
+ json_object(
164
+ 'type', ps.type,
165
+ 'value', CAST(ps.value AS TEXT)
166
+ )
167
+ )
168
+ FROM writes as ps
169
+ WHERE ps.thread_id = shallow_checkpoints.thread_id
170
+ AND ps.checkpoint_ns = shallow_checkpoints.checkpoint_ns
171
+ AND ps.checkpoint_id = shallow_checkpoints.parent_checkpoint_id
172
+ AND ps.channel = ${TASKS}
173
+ ORDER BY ps.idx
174
+ )`.as("pending_sends")
175
+ ]).where("thread_id", "=", threadId).where("checkpoint_ns", "=", checkpointNs);
176
+ if (requestedCheckpointId) {
177
+ query = query.where("checkpoint_id", "=", requestedCheckpointId);
178
+ }
179
+ const row = await query.executeTakeFirst();
180
+ if (!row) return void 0;
181
+ if (requestedCheckpointId && row.checkpoint_id !== requestedCheckpointId) {
182
+ return void 0;
183
+ }
184
+ const pendingWrites = await Promise.all(
185
+ JSON.parse(row.pending_writes || "[]").map(async (write) => {
186
+ return [
187
+ write.task_id,
188
+ write.channel,
189
+ await this.serde.loadsTyped(write.type ?? "json", write.value ?? "")
190
+ ];
191
+ })
192
+ );
193
+ const checkpoint = await this.serde.loadsTyped(
194
+ row.type ?? "json",
195
+ new TextDecoder().decode(row.checkpoint)
196
+ );
197
+ if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {
198
+ await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);
199
+ }
200
+ const finalConfig = {
201
+ configurable: {
202
+ thread_id: row.thread_id,
203
+ checkpoint_ns: row.checkpoint_ns,
204
+ checkpoint_id: row.checkpoint_id
205
+ }
206
+ };
207
+ return {
208
+ checkpoint,
209
+ config: finalConfig,
210
+ metadata: await this.serde.loadsTyped(
211
+ row.type ?? "json",
212
+ new TextDecoder().decode(row.metadata)
213
+ ),
214
+ parentConfig: row.parent_checkpoint_id ? {
215
+ configurable: {
216
+ thread_id: row.thread_id,
217
+ checkpoint_ns: row.checkpoint_ns,
218
+ checkpoint_id: row.parent_checkpoint_id
219
+ }
220
+ } : void 0,
221
+ pendingWrites
222
+ };
223
+ }
224
+ async *list(config, options) {
225
+ await this.setup();
226
+ const { limit, before, filter } = options ?? {};
227
+ const threadId = config.configurable?.thread_id;
228
+ const checkpointNs = config.configurable?.checkpoint_ns;
229
+ let query = this.db.selectFrom("shallow_checkpoints").select([
230
+ "thread_id",
231
+ "checkpoint_ns",
232
+ "checkpoint_id",
233
+ "parent_checkpoint_id",
234
+ "type",
235
+ "checkpoint",
236
+ "metadata",
237
+ "checkpoint_ts",
238
+ sql`(
239
+ SELECT json_group_array(
240
+ json_object(
241
+ 'task_id', pw.task_id,
242
+ 'channel', pw.channel,
243
+ 'type', pw.type,
244
+ 'value', CAST(pw.value AS TEXT)
245
+ )
246
+ )
247
+ FROM writes as pw
248
+ WHERE pw.thread_id = shallow_checkpoints.thread_id
249
+ AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns
250
+ AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id
251
+ )`.as("pending_writes")
252
+ ]);
253
+ if (threadId) {
254
+ query = query.where("thread_id", "=", threadId);
255
+ }
256
+ if (checkpointNs !== void 0 && checkpointNs !== null) {
257
+ query = query.where("checkpoint_ns", "=", checkpointNs);
258
+ }
259
+ if (before?.configurable?.checkpoint_id !== void 0) {
260
+ query = query.where("checkpoint_id", "<", before.configurable.checkpoint_id);
261
+ }
262
+ query = query.orderBy("checkpoint_ts", "desc");
263
+ const rows = await query.execute();
264
+ let count = 0;
265
+ for (const row of rows) {
266
+ const metadata = await this.serde.loadsTyped(
267
+ row.type ?? "json",
268
+ new TextDecoder().decode(row.metadata)
269
+ );
270
+ if (filter && !this._checkMetadataFilterMatch(metadata, filter)) {
271
+ continue;
272
+ }
273
+ if (limit !== void 0 && count >= limit) {
274
+ return;
275
+ }
276
+ const pendingWrites = await Promise.all(
277
+ JSON.parse(row.pending_writes || "[]").map(async (write) => {
278
+ return [
279
+ write.task_id,
280
+ write.channel,
281
+ await this.serde.loadsTyped(write.type ?? "json", write.value ?? "")
282
+ ];
283
+ })
284
+ );
285
+ const checkpoint = await this.serde.loadsTyped(
286
+ row.type ?? "json",
287
+ new TextDecoder().decode(row.checkpoint)
288
+ );
289
+ if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {
290
+ await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);
291
+ }
292
+ count++;
293
+ yield {
294
+ config: {
295
+ configurable: {
296
+ thread_id: row.thread_id,
297
+ checkpoint_ns: row.checkpoint_ns,
298
+ checkpoint_id: row.checkpoint_id
299
+ }
300
+ },
301
+ checkpoint,
302
+ metadata,
303
+ parentConfig: row.parent_checkpoint_id ? {
304
+ configurable: {
305
+ thread_id: row.thread_id,
306
+ checkpoint_ns: row.checkpoint_ns,
307
+ checkpoint_id: row.parent_checkpoint_id
308
+ }
309
+ } : void 0,
310
+ pendingWrites
311
+ };
312
+ }
313
+ }
314
+ /**
315
+ * Check metadata filter matches (with deep comparison support)
316
+ * Matches ShallowMemorySaver behavior
317
+ */
318
+ _checkMetadataFilterMatch(metadata, filter) {
319
+ for (const [key, value] of Object.entries(filter)) {
320
+ const metadataValue = metadata?.[key];
321
+ if (value === null) {
322
+ if (!(key in (metadata || {})) || metadataValue !== null) {
323
+ return false;
324
+ }
325
+ } else if (typeof value === "object" && !Array.isArray(value)) {
326
+ if (typeof metadataValue !== "object" || metadataValue === null) {
327
+ return false;
328
+ }
329
+ if (deterministicStringify(value) !== deterministicStringify(metadataValue)) {
330
+ return false;
331
+ }
332
+ } else if (metadataValue !== value) {
333
+ return false;
334
+ }
335
+ }
336
+ return true;
337
+ }
338
+ async put(config, checkpoint, metadata, _newVersions) {
339
+ await this.setup();
340
+ const threadId = config.configurable?.thread_id;
341
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
342
+ const parentCheckpointId = config.configurable?.checkpoint_id;
343
+ if (!threadId) {
344
+ throw new Error("thread_id is required");
345
+ }
346
+ const checkpointId = checkpoint.id || uuid6(0);
347
+ const preparedCheckpoint = copyCheckpoint(checkpoint);
348
+ const [[type1, serializedCheckpoint], [type2, serializedMetadata]] = await Promise.all([
349
+ this.serde.dumpsTyped(preparedCheckpoint),
350
+ this.serde.dumpsTyped(metadata)
351
+ ]);
352
+ if (type1 !== type2) {
353
+ throw new Error("Failed to serialize checkpoint and metadata to the same type.");
354
+ }
355
+ const threadId_ = threadId;
356
+ const checkpointNs_ = checkpointNs;
357
+ const checkpointId_ = checkpointId;
358
+ await withRetry(
359
+ async () => {
360
+ await this.db.transaction().execute(async (trx) => {
361
+ const oldCheckpoint = await trx.selectFrom("shallow_checkpoints").select(["checkpoint_id"]).where("thread_id", "=", threadId_).where("checkpoint_ns", "=", checkpointNs_).executeTakeFirst();
362
+ if (oldCheckpoint && oldCheckpoint.checkpoint_id !== checkpointId_) {
363
+ await trx.deleteFrom("writes").where("thread_id", "=", threadId_).where("checkpoint_ns", "=", checkpointNs_).where("checkpoint_id", "=", oldCheckpoint.checkpoint_id).execute();
364
+ }
365
+ await trx.insertInto("shallow_checkpoints").values({
366
+ thread_id: threadId_,
367
+ checkpoint_ns: checkpointNs_,
368
+ checkpoint_id: checkpointId_,
369
+ parent_checkpoint_id: parentCheckpointId ?? null,
370
+ type: type1,
371
+ checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),
372
+ metadata: new Uint8Array(Buffer.from(serializedMetadata)),
373
+ checkpoint_ts: Date.now()
374
+ }).onConflict(
375
+ (oc) => oc.columns(["thread_id", "checkpoint_ns"]).doUpdateSet({
376
+ checkpoint_id: checkpointId_,
377
+ parent_checkpoint_id: parentCheckpointId ?? null,
378
+ type: type1,
379
+ checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),
380
+ metadata: new Uint8Array(Buffer.from(serializedMetadata)),
381
+ checkpoint_ts: Date.now()
382
+ })
383
+ ).execute();
384
+ });
385
+ },
386
+ `put(${threadId}/${checkpointId})`
387
+ );
388
+ return {
389
+ configurable: {
390
+ thread_id: threadId,
391
+ checkpoint_ns: checkpointNs,
392
+ checkpoint_id: checkpointId
393
+ }
394
+ };
395
+ }
396
+ async putWrites(config, writes, taskId) {
397
+ await this.setup();
398
+ const threadId = config.configurable?.thread_id;
399
+ const checkpointNs = config.configurable?.checkpoint_ns ?? "";
400
+ const checkpointId = config.configurable?.checkpoint_id;
401
+ if (!threadId || !checkpointId) {
402
+ throw new Error("thread_id and checkpoint_id are required");
403
+ }
404
+ const values = await Promise.all(
405
+ writes.map(async (write, idx) => {
406
+ const [type, serializedWrite] = await this.serde.dumpsTyped(write[1]);
407
+ return {
408
+ thread_id: threadId,
409
+ checkpoint_ns: checkpointNs,
410
+ checkpoint_id: checkpointId,
411
+ task_id: taskId,
412
+ idx: WRITES_IDX_MAP[write[0]] ?? idx,
413
+ channel: write[0],
414
+ type,
415
+ value: new Uint8Array(Buffer.from(serializedWrite))
416
+ };
417
+ })
418
+ );
419
+ if (values.length === 0) return;
420
+ await withRetry(
421
+ async () => {
422
+ await this.db.transaction().execute(async (trx) => {
423
+ await trx.deleteFrom("writes").where("thread_id", "=", threadId).where("checkpoint_ns", "=", checkpointNs).where("checkpoint_id", "=", checkpointId).where("task_id", "=", taskId).execute();
424
+ for (const value of values) {
425
+ await trx.insertInto("writes").values(value).execute();
426
+ }
427
+ });
428
+ },
429
+ `putWrites(${threadId}/${checkpointId}/${taskId})`
430
+ );
431
+ }
432
+ async deleteThread(threadId) {
433
+ await withRetry(
434
+ async () => {
435
+ await this.db.transaction().execute(async (trx) => {
436
+ await trx.deleteFrom("shallow_checkpoints").where("thread_id", "=", threadId).execute();
437
+ await trx.deleteFrom("writes").where("thread_id", "=", threadId).execute();
438
+ });
439
+ },
440
+ `deleteThread(${threadId})`
441
+ );
442
+ }
443
+ async migratePendingSends(checkpoint, threadId, parentCheckpointId) {
444
+ const result = await this.db.selectFrom("writes as ps").select([
445
+ "ps.checkpoint_id",
446
+ sql`json_group_array(
447
+ json_object(
448
+ 'type', ps.type,
449
+ 'value', CAST(ps.value AS TEXT)
450
+ )
451
+ )`.as("pending_sends")
452
+ ]).where("ps.thread_id", "=", threadId).where("ps.checkpoint_id", "=", parentCheckpointId).where("ps.channel", "=", TASKS).orderBy("ps.idx").executeTakeFirst();
453
+ if (!result) return;
454
+ const mutableCheckpoint = checkpoint;
455
+ mutableCheckpoint.channel_values ??= {};
456
+ mutableCheckpoint.channel_values[TASKS] = await Promise.all(
457
+ JSON.parse(result.pending_sends || "[]").map(
458
+ ({ type, value }) => this.serde.loadsTyped(type, value)
459
+ )
460
+ );
461
+ mutableCheckpoint.channel_versions[TASKS] = Object.keys(checkpoint.channel_versions).length > 0 ? maxChannelVersion(...Object.values(checkpoint.channel_versions)) : this.getNextVersion(void 0);
462
+ }
463
+ }
464
+
465
+ export { SqliteShallowSaver };
466
+ //# sourceMappingURL=shallow-checkpoint-ImbLxYNR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shallow-checkpoint-ImbLxYNR.js","sources":["../src/storage/sqlite/shallow-checkpoint.ts"],"sourcesContent":["import { Dialect, Kysely, sql } from 'kysely';\nimport type { RunnableConfig } from '@langchain/core/runnables';\n\nimport {\n BaseCheckpointSaver,\n type Checkpoint,\n type CheckpointListOptions,\n type CheckpointTuple,\n type SerializerProtocol,\n type PendingWrite,\n type CheckpointMetadata,\n TASKS,\n copyCheckpoint,\n maxChannelVersion,\n getCheckpointId,\n WRITES_IDX_MAP,\n uuid6,\n} from '@langchain/langgraph-checkpoint';\n\n/**\n * SQLite 重试配置\n */\nconst SQLITE_RETRY_CONFIG = {\n maxRetries: 3,\n baseDelayMs: 100,\n isRetryableError: (error: any): boolean => {\n const msg = error?.message?.toLowerCase() || '';\n // 精确匹配 SQLITE_BUSY 和 database is locked\n // 注意:不重试事务状态错误(如 cannot rollback),这些可能是结构性问题\n return (\n msg.includes('sqlite_busy') ||\n msg.includes('database is locked') ||\n msg.includes('database disk image is malformed') ||\n msg === 'sqlite_busy' ||\n msg === 'database is locked'\n );\n },\n};\n\n/**\n * 带重试的数据库操作包装器\n */\nasync function withRetry<T>(operation: () => Promise<T>, context?: string): Promise<T> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < SQLITE_RETRY_CONFIG.maxRetries; attempt++) {\n try {\n return await operation();\n } catch (error: any) {\n lastError = error;\n\n if (!SQLITE_RETRY_CONFIG.isRetryableError(error)) {\n throw error;\n }\n\n if (attempt < SQLITE_RETRY_CONFIG.maxRetries - 1) {\n const delay = SQLITE_RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);\n console.warn(\n `SQLite lock detected${context ? ` (${context})` : ''}, retrying in ${delay}ms (attempt ${attempt + 1}/${SQLITE_RETRY_CONFIG.maxRetries})`,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n\n throw lastError;\n}\n\n/**\n * Helper function for deterministic object comparison\n * Used for deep metadata filtering\n */\nfunction deterministicStringify(obj: any): string {\n if (obj === null || typeof obj !== 'object') {\n return JSON.stringify(obj);\n }\n if (Array.isArray(obj)) {\n return JSON.stringify(obj.map((item) => deterministicStringify(item)));\n }\n const sortedObj: Record<string, any> = {};\n const sortedKeys = Object.keys(obj).sort();\n for (const key of sortedKeys) {\n sortedObj[key] = obj[key];\n }\n return JSON.stringify(sortedObj, (_, value) => {\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n const sorted: Record<string, any> = {};\n const keys = Object.keys(value).sort();\n for (const k of keys) {\n sorted[k] = value[k];\n }\n return sorted;\n }\n return value;\n });\n}\n\n// Kysely 数据库表类型定义 - 浅层模式\ninterface ShallowCheckpointsTable {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n parent_checkpoint_id: string | null;\n type: string | null;\n checkpoint: Uint8Array;\n metadata: Uint8Array;\n checkpoint_ts: number; // 时间戳用于排序\n}\n\ninterface WritesTable {\n thread_id: string;\n checkpoint_ns: string;\n checkpoint_id: string;\n task_id: string;\n idx: number;\n channel: string;\n type: string | null;\n value: Uint8Array | null;\n}\n\ninterface ShallowCheckpointDatabase {\n shallow_checkpoints: ShallowCheckpointsTable;\n writes: WritesTable;\n}\n\n// Valid metadata keys for filtering\nconst checkpointMetadataKeys = ['source', 'step', 'parents'] as const;\n\ntype CheckKeys<T, K extends readonly (keyof T)[]> = [K[number]] extends [keyof T]\n ? [keyof T] extends [K[number]]\n ? K\n : never\n : never;\n\nfunction validateKeys<T, K extends readonly (keyof T)[]>(keys: CheckKeys<T, K>): K {\n return keys;\n}\n\nconst validCheckpointMetadataKeys = validateKeys<CheckpointMetadata, typeof checkpointMetadataKeys>(\n checkpointMetadataKeys,\n);\n\n/**\n * SqliteShallowSaver - SQLite 浅层检查点存储器\n *\n * 特性:\n * - 每个 thread_id + checkpoint_ns 组合只保留最新的 checkpoint\n * - 新 checkpoint 写入时自动清理旧 checkpoint 的 writes\n * - 使用 checkpoint_ts 时间戳排序\n * - 大幅减少存储数据量\n */\nexport class SqliteShallowSaver extends BaseCheckpointSaver {\n db: Kysely<ShallowCheckpointDatabase>;\n protected isSetup: boolean;\n\n constructor(dialect: Dialect, serde?: SerializerProtocol) {\n super(serde);\n this.db = new Kysely<ShallowCheckpointDatabase>({\n dialect,\n });\n this.isSetup = false;\n }\n\n static async fromConnStringAsync(connStringOrLocalPath: string): Promise<SqliteShallowSaver> {\n let saver: SqliteShallowSaver;\n /** @ts-ignore */\n if (globalThis.Bun) {\n console.log('LG | Using BunSqliteDialect ' + connStringOrLocalPath);\n const { BunSqliteDialect } = await import('kysely-bun-worker/normal');\n // 使用 BunSqliteDialect(非 Worker 模式)避免 Worker 事务状态同步问题\n // BunWorkerDialect 在高并发下可能出现 \"cannot rollback - no transaction is active\"\n saver = new SqliteShallowSaver(new BunSqliteDialect({ url: connStringOrLocalPath }));\n } else {\n /** @ts-ignore */\n console.log('LG | Using NodeWasmDialect');\n const { default: SqliteDatabase } = await import('node-sqlite3-wasm');\n const { NodeWasmDialect } = await import('kysely-wasm');\n console.log(connStringOrLocalPath);\n const wasm = new NodeWasmDialect({\n database: new SqliteDatabase.Database(connStringOrLocalPath),\n });\n saver = new SqliteShallowSaver(wasm);\n }\n await saver.setup();\n return saver;\n }\n\n protected async setup(): Promise<void> {\n if (this.isSetup) {\n return;\n }\n\n // 锁等待超时 5 秒\n await sql`PRAGMA busy_timeout = 5000`.execute(this.db as any);\n\n // WAL 模式 - 允许读写并发\n await sql`PRAGMA journal_mode = WAL`.execute(this.db as any);\n\n // NORMAL 模式 - 平衡数据安全与性能\n await sql`PRAGMA synchronous = NORMAL`.execute(this.db as any);\n\n // WAL 自动检查点\n await sql`PRAGMA wal_autocheckpoint = 1000`.execute(this.db as any);\n\n // 创建浅层 checkpoints 表 - 使用复合主键 (thread_id, checkpoint_ns)\n await sql`\nCREATE TABLE IF NOT EXISTS shallow_checkpoints (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n parent_checkpoint_id TEXT,\n type TEXT,\n checkpoint BLOB,\n metadata BLOB,\n checkpoint_ts INTEGER NOT NULL DEFAULT 0,\n PRIMARY KEY (thread_id, checkpoint_ns)\n)`.execute(this.db as any);\n\n // 创建 checkpoint_ts 索引用于排序\n await sql`\nCREATE INDEX IF NOT EXISTS idx_shallow_checkpoints_ts \nON shallow_checkpoints(checkpoint_ts DESC)`.execute(this.db as any);\n\n // 创建 writes 表\n await sql`\nCREATE TABLE IF NOT EXISTS writes (\n thread_id TEXT NOT NULL,\n checkpoint_ns TEXT NOT NULL DEFAULT '',\n checkpoint_id TEXT NOT NULL,\n task_id TEXT NOT NULL,\n idx INTEGER NOT NULL,\n channel TEXT NOT NULL,\n type TEXT,\n value BLOB,\n PRIMARY KEY (thread_id, checkpoint_ns, checkpoint_id, task_id, idx)\n)`.execute(this.db as any);\n\n this.isSetup = true;\n }\n\n /**\n * 获取 checkpoint(便捷方法)\n */\n async get(config: RunnableConfig): Promise<Checkpoint | undefined> {\n const tuple = await this.getTuple(config);\n return tuple?.checkpoint;\n }\n\n async getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined> {\n await this.setup();\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const requestedCheckpointId = getCheckpointId(config);\n\n if (threadId === undefined) {\n return undefined;\n }\n\n let query = this.db\n .selectFrom('shallow_checkpoints')\n .select([\n 'thread_id',\n 'checkpoint_ns',\n 'checkpoint_id',\n 'parent_checkpoint_id',\n 'type',\n 'checkpoint',\n 'metadata',\n 'checkpoint_ts',\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = shallow_checkpoints.thread_id\n AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id\n )`.as('pending_writes'),\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )\n FROM writes as ps\n WHERE ps.thread_id = shallow_checkpoints.thread_id\n AND ps.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND ps.checkpoint_id = shallow_checkpoints.parent_checkpoint_id\n AND ps.channel = ${TASKS}\n ORDER BY ps.idx\n )`.as('pending_sends'),\n ])\n .where('thread_id', '=', threadId)\n .where('checkpoint_ns', '=', checkpointNs);\n\n // 如果指定了 checkpoint_id,需要验证是否匹配\n if (requestedCheckpointId) {\n query = query.where('checkpoint_id', '=', requestedCheckpointId);\n }\n\n const row = await query.executeTakeFirst();\n if (!row) return undefined;\n\n // 如果请求了特定的 checkpoint_id 但不匹配(在 shallow 模式下只有一条记录)\n if (requestedCheckpointId && row.checkpoint_id !== requestedCheckpointId) {\n return undefined;\n }\n\n // 反序列化 pending writes\n const pendingWrites = await Promise.all(\n (JSON.parse(row.pending_writes || '[]') as Array<{\n task_id: string;\n channel: string;\n type: string;\n value: string;\n }>).map(async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),\n ] as [string, string, unknown];\n }),\n );\n\n // 反序列化 checkpoint\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.checkpoint),\n )) as Checkpoint;\n\n // 处理 v < 4 迁移\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);\n }\n\n const finalConfig: RunnableConfig = {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n };\n\n return {\n checkpoint,\n config: finalConfig,\n metadata: (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.metadata),\n )) as CheckpointMetadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n\n async *list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple> {\n await this.setup();\n const { limit, before, filter } = options ?? {};\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns;\n\n let query = this.db.selectFrom('shallow_checkpoints').select([\n 'thread_id',\n 'checkpoint_ns',\n 'checkpoint_id',\n 'parent_checkpoint_id',\n 'type',\n 'checkpoint',\n 'metadata',\n 'checkpoint_ts',\n sql<string>`(\n SELECT json_group_array(\n json_object(\n 'task_id', pw.task_id,\n 'channel', pw.channel,\n 'type', pw.type,\n 'value', CAST(pw.value AS TEXT)\n )\n )\n FROM writes as pw\n WHERE pw.thread_id = shallow_checkpoints.thread_id\n AND pw.checkpoint_ns = shallow_checkpoints.checkpoint_ns\n AND pw.checkpoint_id = shallow_checkpoints.checkpoint_id\n )`.as('pending_writes'),\n ]);\n\n if (threadId) {\n query = query.where('thread_id', '=', threadId);\n }\n\n if (checkpointNs !== undefined && checkpointNs !== null) {\n query = query.where('checkpoint_ns', '=', checkpointNs);\n }\n\n if (before?.configurable?.checkpoint_id !== undefined) {\n query = query.where('checkpoint_id', '<', before.configurable.checkpoint_id);\n }\n\n // 按时间戳降序(最新的在前)\n query = query.orderBy('checkpoint_ts', 'desc');\n\n const rows = await query.execute();\n let count = 0;\n\n for (const row of rows) {\n // 先反序列化 metadata 以便在应用层过滤\n const metadata = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.metadata),\n )) as CheckpointMetadata;\n\n // 应用层 metadata 过滤(与 ShallowMemorySaver 保持一致)\n if (filter && !this._checkMetadataFilterMatch(metadata, filter as CheckpointMetadata)) {\n continue;\n }\n\n // 应用 limit\n if (limit !== undefined && count >= limit) {\n return;\n }\n\n const pendingWrites = await Promise.all(\n (JSON.parse(row.pending_writes || '[]') as Array<{\n task_id: string;\n channel: string;\n type: string;\n value: string;\n }>).map(async (write) => {\n return [\n write.task_id,\n write.channel,\n await this.serde.loadsTyped(write.type ?? 'json', write.value ?? ''),\n ] as [string, string, unknown];\n }),\n );\n\n const checkpoint = (await this.serde.loadsTyped(\n row.type ?? 'json',\n new TextDecoder().decode(row.checkpoint),\n )) as Checkpoint;\n\n if (checkpoint.v < 4 && row.parent_checkpoint_id != null) {\n await this.migratePendingSends(checkpoint, row.thread_id, row.parent_checkpoint_id);\n }\n\n count++;\n yield {\n config: {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.checkpoint_id,\n },\n },\n checkpoint,\n metadata,\n parentConfig: row.parent_checkpoint_id\n ? {\n configurable: {\n thread_id: row.thread_id,\n checkpoint_ns: row.checkpoint_ns,\n checkpoint_id: row.parent_checkpoint_id,\n },\n }\n : undefined,\n pendingWrites,\n };\n }\n }\n\n /**\n * Check metadata filter matches (with deep comparison support)\n * Matches ShallowMemorySaver behavior\n */\n private _checkMetadataFilterMatch(metadata: any, filter: CheckpointMetadata): boolean {\n for (const [key, value] of Object.entries(filter)) {\n const metadataValue = metadata?.[key];\n\n if (value === null) {\n // For null filter value, check if key doesn't exist or value is null\n if (!(key in (metadata || {})) || metadataValue !== null) {\n return false;\n }\n } else if (typeof value === 'object' && !Array.isArray(value)) {\n // Deep comparison for objects with deterministic key ordering\n if (typeof metadataValue !== 'object' || metadataValue === null) {\n return false;\n }\n if (deterministicStringify(value) !== deterministicStringify(metadataValue)) {\n return false;\n }\n } else if (metadataValue !== value) {\n return false;\n }\n }\n return true;\n }\n\n async put(\n config: RunnableConfig,\n checkpoint: Checkpoint,\n metadata: CheckpointMetadata,\n _newVersions?: Record<string, string | number>,\n ): Promise<RunnableConfig> {\n await this.setup();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const parentCheckpointId = config.configurable?.checkpoint_id;\n\n if (!threadId) {\n throw new Error('thread_id is required');\n }\n\n // 使用 checkpoint.id 或生成新的\n const checkpointId = checkpoint.id || uuid6(0);\n\n const preparedCheckpoint: Partial<Checkpoint> = copyCheckpoint(checkpoint);\n\n // 序列化\n const [[type1, serializedCheckpoint], [type2, serializedMetadata]] = await Promise.all([\n this.serde.dumpsTyped(preparedCheckpoint),\n this.serde.dumpsTyped(metadata),\n ]);\n\n if (type1 !== type2) {\n throw new Error('Failed to serialize checkpoint and metadata to the same type.');\n }\n\n const threadId_ = threadId;\n const checkpointNs_ = checkpointNs;\n const checkpointId_ = checkpointId;\n\n // 带重试的数据库操作\n await withRetry(\n async () => {\n await this.db.transaction().execute(async (trx) => {\n // 获取旧的 checkpoint_id 用于清理 writes\n const oldCheckpoint = await trx\n .selectFrom('shallow_checkpoints')\n .select(['checkpoint_id'])\n .where('thread_id', '=', threadId_)\n .where('checkpoint_ns', '=', checkpointNs_)\n .executeTakeFirst();\n\n // 如果存在旧的 checkpoint 且 id 不同,清理旧的 writes\n if (oldCheckpoint && oldCheckpoint.checkpoint_id !== checkpointId_) {\n await trx\n .deleteFrom('writes')\n .where('thread_id', '=', threadId_)\n .where('checkpoint_ns', '=', checkpointNs_)\n .where('checkpoint_id', '=', oldCheckpoint.checkpoint_id)\n .execute();\n }\n\n // 使用 INSERT OR REPLACE (UPSERT) 覆盖写入\n await trx\n .insertInto('shallow_checkpoints')\n .values({\n thread_id: threadId_,\n checkpoint_ns: checkpointNs_,\n checkpoint_id: checkpointId_,\n parent_checkpoint_id: parentCheckpointId ?? null,\n type: type1,\n checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),\n metadata: new Uint8Array(Buffer.from(serializedMetadata)),\n checkpoint_ts: Date.now(),\n })\n .onConflict((oc) =>\n oc.columns(['thread_id', 'checkpoint_ns']).doUpdateSet({\n checkpoint_id: checkpointId_,\n parent_checkpoint_id: parentCheckpointId ?? null,\n type: type1,\n checkpoint: new Uint8Array(Buffer.from(serializedCheckpoint)),\n metadata: new Uint8Array(Buffer.from(serializedMetadata)),\n checkpoint_ts: Date.now(),\n }),\n )\n .execute();\n });\n },\n `put(${threadId}/${checkpointId})`,\n );\n\n return {\n configurable: {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n },\n };\n }\n\n async putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void> {\n await this.setup();\n\n const threadId = config.configurable?.thread_id;\n const checkpointNs = config.configurable?.checkpoint_ns ?? '';\n const checkpointId = config.configurable?.checkpoint_id;\n\n if (!threadId || !checkpointId) {\n throw new Error('thread_id and checkpoint_id are required');\n }\n\n // 预先序列化\n const values = await Promise.all(\n writes.map(async (write, idx) => {\n const [type, serializedWrite] = await this.serde.dumpsTyped(write[1]);\n return {\n thread_id: threadId,\n checkpoint_ns: checkpointNs,\n checkpoint_id: checkpointId,\n task_id: taskId,\n idx: WRITES_IDX_MAP[write[0]] ?? idx,\n channel: write[0],\n type,\n value: new Uint8Array(Buffer.from(serializedWrite)),\n };\n }),\n );\n\n if (values.length === 0) return;\n\n // 带重试的批量插入\n await withRetry(\n async () => {\n await this.db.transaction().execute(async (trx) => {\n // 先删除已存在的记录\n await trx\n .deleteFrom('writes')\n .where('thread_id', '=', threadId)\n .where('checkpoint_ns', '=', checkpointNs)\n .where('checkpoint_id', '=', checkpointId)\n .where('task_id', '=', taskId)\n .execute();\n\n // 批量插入\n for (const value of values) {\n await trx.insertInto('writes').values(value).execute();\n }\n });\n },\n `putWrites(${threadId}/${checkpointId}/${taskId})`,\n );\n }\n\n async deleteThread(threadId: string): Promise<void> {\n await withRetry(\n async () => {\n await this.db.transaction().execute(async (trx) => {\n await trx.deleteFrom('shallow_checkpoints').where('thread_id', '=', threadId).execute();\n await trx.deleteFrom('writes').where('thread_id', '=', threadId).execute();\n });\n },\n `deleteThread(${threadId})`,\n );\n }\n\n protected async migratePendingSends(checkpoint: Checkpoint, threadId: string, parentCheckpointId: string) {\n const result = await this.db\n .selectFrom('writes as ps')\n .select([\n 'ps.checkpoint_id',\n sql<string>`json_group_array(\n json_object(\n 'type', ps.type,\n 'value', CAST(ps.value AS TEXT)\n )\n )`.as('pending_sends'),\n ])\n .where('ps.thread_id', '=', threadId)\n .where('ps.checkpoint_id', '=', parentCheckpointId)\n .where('ps.channel', '=', TASKS)\n .orderBy('ps.idx')\n .executeTakeFirst();\n\n if (!result) return;\n\n const mutableCheckpoint = checkpoint;\n\n mutableCheckpoint.channel_values ??= {};\n mutableCheckpoint.channel_values[TASKS] = await Promise.all(\n JSON.parse(result.pending_sends || '[]').map(({ type, value }: { type: string; value: string }) =>\n this.serde.loadsTyped(type, value),\n ),\n );\n\n mutableCheckpoint.channel_versions[TASKS] =\n Object.keys(checkpoint.channel_versions).length > 0\n ? maxChannelVersion(...Object.values(checkpoint.channel_versions))\n : this.getNextVersion(undefined);\n }\n}\n"],"names":[],"mappings":";;;AAsBA,MAAM,mBAAA,GAAsB;AAAA,EACxB,UAAA,EAAY,CAAA;AAAA,EACZ,WAAA,EAAa,GAAA;AAAA,EACb,gBAAA,EAAkB,CAAC,KAAA,KAAwB;AACvC,IAAA,MAAM,GAAA,GAAM,KAAA,EAAO,OAAA,EAAS,WAAA,EAAY,IAAK,EAAA;AAG7C,IAAA,OACI,GAAA,CAAI,QAAA,CAAS,aAAa,CAAA,IAC1B,IAAI,QAAA,CAAS,oBAAoB,CAAA,IACjC,GAAA,CAAI,QAAA,CAAS,kCAAkC,CAAA,IAC/C,GAAA,KAAQ,iBACR,GAAA,KAAQ,oBAAA;AAAA,EAEhB;AACJ,CAAA;AAKA,eAAe,SAAA,CAAa,WAA6B,OAAA,EAA8B;AACnF,EAAA,IAAI,SAAA,GAA0B,IAAA;AAE9B,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,GAAU,mBAAA,CAAoB,YAAY,OAAA,EAAA,EAAW;AACvE,IAAA,IAAI;AACA,MAAA,OAAO,MAAM,SAAA,EAAU;AAAA,IAC3B,SAAS,KAAA,EAAY;AACjB,MAAA,SAAA,GAAY,KAAA;AAEZ,MAAA,IAAI,CAAC,mBAAA,CAAoB,gBAAA,CAAiB,KAAK,CAAA,EAAG;AAC9C,QAAA,MAAM,KAAA;AAAA,MACV;AAEA,MAAA,IAAI,OAAA,GAAU,mBAAA,CAAoB,UAAA,GAAa,CAAA,EAAG;AAC9C,QAAA,MAAM,QAAQ,mBAAA,CAAoB,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,GAAG,OAAO,CAAA;AACnE,QAAA,OAAA,CAAQ,IAAA;AAAA,UACJ,CAAA,oBAAA,EAAuB,OAAA,GAAU,CAAA,EAAA,EAAK,OAAO,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,cAAA,EAAiB,KAAK,CAAA,YAAA,EAAe,OAAA,GAAU,CAAC,CAAA,CAAA,EAAI,oBAAoB,UAAU,CAAA,CAAA;AAAA,SAC3I;AACA,QAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,KAAK,CAAC,CAAA;AAAA,MAC7D;AAAA,IACJ;AAAA,EACJ;AAEA,EAAA,MAAM,SAAA;AACV;AAMA,SAAS,uBAAuB,GAAA,EAAkB;AAC9C,EAAA,IAAI,GAAA,KAAQ,IAAA,IAAQ,OAAO,GAAA,KAAQ,QAAA,EAAU;AACzC,IAAA,OAAO,IAAA,CAAK,UAAU,GAAG,CAAA;AAAA,EAC7B;AACA,EAAA,IAAI,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,EAAG;AACpB,IAAA,OAAO,IAAA,CAAK,UAAU,GAAA,CAAI,GAAA,CAAI,CAAC,IAAA,KAAS,sBAAA,CAAuB,IAAI,CAAC,CAAC,CAAA;AAAA,EACzE;AACA,EAAA,MAAM,YAAiC,EAAC;AACxC,EAAA,MAAM,UAAA,GAAa,MAAA,CAAO,IAAA,CAAK,GAAG,EAAE,IAAA,EAAK;AACzC,EAAA,KAAA,MAAW,OAAO,UAAA,EAAY;AAC1B,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,GAAA,CAAI,GAAG,CAAA;AAAA,EAC5B;AACA,EAAA,OAAO,IAAA,CAAK,SAAA,CAAU,SAAA,EAAW,CAAC,GAAG,KAAA,KAAU;AAC3C,IAAA,IAAI,KAAA,KAAU,QAAQ,OAAO,KAAA,KAAU,YAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtE,MAAA,MAAM,SAA8B,EAAC;AACrC,MAAA,MAAM,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,KAAK,EAAE,IAAA,EAAK;AACrC,MAAA,KAAA,MAAW,KAAK,IAAA,EAAM;AAClB,QAAA,MAAA,CAAO,CAAC,CAAA,GAAI,KAAA,CAAM,CAAC,CAAA;AAAA,MACvB;AACA,MAAA,OAAO,MAAA;AAAA,IACX;AACA,IAAA,OAAO,KAAA;AAAA,EACX,CAAC,CAAA;AACL;AAwDO,MAAM,2BAA2B,mBAAA,CAAoB;AAAA,EACxD,EAAA;AAAA,EACU,OAAA;AAAA,EAEV,WAAA,CAAY,SAAkB,KAAA,EAA4B;AACtD,IAAA,KAAA,CAAM,KAAK,CAAA;AACX,IAAA,IAAA,CAAK,EAAA,GAAK,IAAI,MAAA,CAAkC;AAAA,MAC5C;AAAA,KACH,CAAA;AACD,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AAAA,EACnB;AAAA,EAEA,aAAa,oBAAoB,qBAAA,EAA4D;AACzF,IAAA,IAAI,KAAA;AAEJ,IAAA,IAAI,WAAW,GAAA,EAAK;AAChB,MAAA,OAAA,CAAQ,GAAA,CAAI,iCAAiC,qBAAqB,CAAA;AAClE,MAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,MAAM,OAAO,0BAA0B,CAAA;AAGpE,MAAA,KAAA,GAAQ,IAAI,mBAAmB,IAAI,gBAAA,CAAiB,EAAE,GAAA,EAAK,qBAAA,EAAuB,CAAC,CAAA;AAAA,IACvF,CAAA,MAAO;AAEH,MAAA,OAAA,CAAQ,IAAI,4BAA4B,CAAA;AACxC,MAAA,MAAM,EAAE,OAAA,EAAS,cAAA,EAAe,GAAI,MAAM,OAAO,mBAAmB,CAAA;AACpE,MAAA,MAAM,EAAE,eAAA,EAAgB,GAAI,MAAM,OAAO,aAAa,CAAA;AACtD,MAAA,OAAA,CAAQ,IAAI,qBAAqB,CAAA;AACjC,MAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB;AAAA,QAC7B,QAAA,EAAU,IAAI,cAAA,CAAe,QAAA,CAAS,qBAAqB;AAAA,OAC9D,CAAA;AACD,MAAA,KAAA,GAAQ,IAAI,mBAAmB,IAAI,CAAA;AAAA,IACvC;AACA,IAAA,MAAM,MAAM,KAAA,EAAM;AAClB,IAAA,OAAO,KAAA;AAAA,EACX;AAAA,EAEA,MAAgB,KAAA,GAAuB;AACnC,IAAA,IAAI,KAAK,OAAA,EAAS;AACd,MAAA;AAAA,IACJ;AAGA,IAAA,MAAM,GAAA,CAAA,0BAAA,CAAA,CAAgC,OAAA,CAAQ,IAAA,CAAK,EAAS,CAAA;AAG5D,IAAA,MAAM,GAAA,CAAA,yBAAA,CAAA,CAA+B,OAAA,CAAQ,IAAA,CAAK,EAAS,CAAA;AAG3D,IAAA,MAAM,GAAA,CAAA,2BAAA,CAAA,CAAiC,OAAA,CAAQ,IAAA,CAAK,EAAS,CAAA;AAG7D,IAAA,MAAM,GAAA,CAAA,gCAAA,CAAA,CAAsC,OAAA,CAAQ,IAAA,CAAK,EAAS,CAAA;AAGlE,IAAA,MAAM,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA,CAWX,OAAA,CAAQ,KAAK,EAAS,CAAA;AAGjB,IAAA,MAAM,GAAA;AAAA;AAAA,0CAAA,CAAA,CAE8B,OAAA,CAAQ,KAAK,EAAS,CAAA;AAG1D,IAAA,MAAM,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA,CAAA,CAWX,OAAA,CAAQ,KAAK,EAAS,CAAA;AAEjB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,IAAI,MAAA,EAAyD;AAC/D,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AACxC,IAAA,OAAO,KAAA,EAAO,UAAA;AAAA,EAClB;AAAA,EAEA,MAAM,SAAS,MAAA,EAA8D;AACzE,IAAA,MAAM,KAAK,KAAA,EAAM;AACjB,IAAA,MAAM,QAAA,GAAW,OAAO,YAAA,EAAc,SAAA;AACtC,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,YAAA,EAAc,aAAA,IAAiB,EAAA;AAC3D,IAAA,MAAM,qBAAA,GAAwB,gBAAgB,MAAM,CAAA;AAEpD,IAAA,IAAI,aAAa,MAAA,EAAW;AACxB,MAAA,OAAO,MAAA;AAAA,IACX;AAEA,IAAA,IAAI,QAAQ,IAAA,CAAK,EAAA,CACZ,UAAA,CAAW,qBAAqB,EAChC,MAAA,CAAO;AAAA,MACJ,WAAA;AAAA,MACA,eAAA;AAAA,MACA,eAAA;AAAA,MACA,sBAAA;AAAA,MACA,MAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAA;AAAA,MACA,eAAA;AAAA,MACA,GAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAAA,CAaG,GAAG,gBAAgB,CAAA;AAAA,MACtB,GAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAAA,EAW2B,KAAK;AAAA;AAAA,iBAAA,CAAA,CAE7B,GAAG,eAAe;AAAA,KACxB,CAAA,CACA,KAAA,CAAM,WAAA,EAAa,GAAA,EAAK,QAAQ,CAAA,CAChC,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,YAAY,CAAA;AAG7C,IAAA,IAAI,qBAAA,EAAuB;AACvB,MAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,qBAAqB,CAAA;AAAA,IACnE;AAEA,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,gBAAA,EAAiB;AACzC,IAAA,IAAI,CAAC,KAAK,OAAO,MAAA;AAGjB,IAAA,IAAI,qBAAA,IAAyB,GAAA,CAAI,aAAA,KAAkB,qBAAA,EAAuB;AACtE,MAAA,OAAO,MAAA;AAAA,IACX;AAGA,IAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC/B,IAAA,CAAK,MAAM,GAAA,CAAI,cAAA,IAAkB,IAAI,CAAA,CAKlC,GAAA,CAAI,OAAO,KAAA,KAAU;AACrB,QAAA,OAAO;AAAA,UACH,KAAA,CAAM,OAAA;AAAA,UACN,KAAA,CAAM,OAAA;AAAA,UACN,MAAM,KAAK,KAAA,CAAM,UAAA,CAAW,MAAM,IAAA,IAAQ,MAAA,EAAQ,KAAA,CAAM,KAAA,IAAS,EAAE;AAAA,SACvE;AAAA,MACJ,CAAC;AAAA,KACL;AAGA,IAAA,MAAM,UAAA,GAAc,MAAM,IAAA,CAAK,KAAA,CAAM,UAAA;AAAA,MACjC,IAAI,IAAA,IAAQ,MAAA;AAAA,MACZ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,IAAI,UAAU;AAAA,KAC3C;AAGA,IAAA,IAAI,UAAA,CAAW,CAAA,GAAI,CAAA,IAAK,GAAA,CAAI,wBAAwB,IAAA,EAAM;AACtD,MAAA,MAAM,KAAK,mBAAA,CAAoB,UAAA,EAAY,GAAA,CAAI,SAAA,EAAW,IAAI,oBAAoB,CAAA;AAAA,IACtF;AAEA,IAAA,MAAM,WAAA,GAA8B;AAAA,MAChC,YAAA,EAAc;AAAA,QACV,WAAW,GAAA,CAAI,SAAA;AAAA,QACf,eAAe,GAAA,CAAI,aAAA;AAAA,QACnB,eAAe,GAAA,CAAI;AAAA;AACvB,KACJ;AAEA,IAAA,OAAO;AAAA,MACH,UAAA;AAAA,MACA,MAAA,EAAQ,WAAA;AAAA,MACR,QAAA,EAAW,MAAM,IAAA,CAAK,KAAA,CAAM,UAAA;AAAA,QACxB,IAAI,IAAA,IAAQ,MAAA;AAAA,QACZ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,IAAI,QAAQ;AAAA,OACzC;AAAA,MACA,YAAA,EAAc,IAAI,oBAAA,GACZ;AAAA,QACI,YAAA,EAAc;AAAA,UACV,WAAW,GAAA,CAAI,SAAA;AAAA,UACf,eAAe,GAAA,CAAI,aAAA;AAAA,UACnB,eAAe,GAAA,CAAI;AAAA;AACvB,OACJ,GACA,MAAA;AAAA,MACN;AAAA,KACJ;AAAA,EACJ;AAAA,EAEA,OAAO,IAAA,CAAK,MAAA,EAAwB,OAAA,EAAkE;AAClG,IAAA,MAAM,KAAK,KAAA,EAAM;AACjB,IAAA,MAAM,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAO,GAAI,WAAW,EAAC;AAC9C,IAAA,MAAM,QAAA,GAAW,OAAO,YAAA,EAAc,SAAA;AACtC,IAAA,MAAM,YAAA,GAAe,OAAO,YAAA,EAAc,aAAA;AAE1C,IAAA,IAAI,QAAQ,IAAA,CAAK,EAAA,CAAG,UAAA,CAAW,qBAAqB,EAAE,MAAA,CAAO;AAAA,MACzD,WAAA;AAAA,MACA,eAAA;AAAA,MACA,eAAA;AAAA,MACA,sBAAA;AAAA,MACA,MAAA;AAAA,MACA,YAAA;AAAA,MACA,UAAA;AAAA,MACA,eAAA;AAAA,MACA,GAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAAA,CAaO,GAAG,gBAAgB;AAAA,KAC7B,CAAA;AAED,IAAA,IAAI,QAAA,EAAU;AACV,MAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,WAAA,EAAa,GAAA,EAAK,QAAQ,CAAA;AAAA,IAClD;AAEA,IAAA,IAAI,YAAA,KAAiB,MAAA,IAAa,YAAA,KAAiB,IAAA,EAAM;AACrD,MAAA,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,YAAY,CAAA;AAAA,IAC1D;AAEA,IAAA,IAAI,MAAA,EAAQ,YAAA,EAAc,aAAA,KAAkB,MAAA,EAAW;AACnD,MAAA,KAAA,GAAQ,MAAM,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,MAAA,CAAO,aAAa,aAAa,CAAA;AAAA,IAC/E;AAGA,IAAA,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,eAAA,EAAiB,MAAM,CAAA;AAE7C,IAAA,MAAM,IAAA,GAAO,MAAM,KAAA,CAAM,OAAA,EAAQ;AACjC,IAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,IAAA,KAAA,MAAW,OAAO,IAAA,EAAM;AAEpB,MAAA,MAAM,QAAA,GAAY,MAAM,IAAA,CAAK,KAAA,CAAM,UAAA;AAAA,QAC/B,IAAI,IAAA,IAAQ,MAAA;AAAA,QACZ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,IAAI,QAAQ;AAAA,OACzC;AAGA,MAAA,IAAI,UAAU,CAAC,IAAA,CAAK,yBAAA,CAA0B,QAAA,EAAU,MAA4B,CAAA,EAAG;AACnF,QAAA;AAAA,MACJ;AAGA,MAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,IAAS,KAAA,EAAO;AACvC,QAAA;AAAA,MACJ;AAEA,MAAA,MAAM,aAAA,GAAgB,MAAM,OAAA,CAAQ,GAAA;AAAA,QAC/B,IAAA,CAAK,MAAM,GAAA,CAAI,cAAA,IAAkB,IAAI,CAAA,CAKlC,GAAA,CAAI,OAAO,KAAA,KAAU;AACrB,UAAA,OAAO;AAAA,YACH,KAAA,CAAM,OAAA;AAAA,YACN,KAAA,CAAM,OAAA;AAAA,YACN,MAAM,KAAK,KAAA,CAAM,UAAA,CAAW,MAAM,IAAA,IAAQ,MAAA,EAAQ,KAAA,CAAM,KAAA,IAAS,EAAE;AAAA,WACvE;AAAA,QACJ,CAAC;AAAA,OACL;AAEA,MAAA,MAAM,UAAA,GAAc,MAAM,IAAA,CAAK,KAAA,CAAM,UAAA;AAAA,QACjC,IAAI,IAAA,IAAQ,MAAA;AAAA,QACZ,IAAI,WAAA,EAAY,CAAE,MAAA,CAAO,IAAI,UAAU;AAAA,OAC3C;AAEA,MAAA,IAAI,UAAA,CAAW,CAAA,GAAI,CAAA,IAAK,GAAA,CAAI,wBAAwB,IAAA,EAAM;AACtD,QAAA,MAAM,KAAK,mBAAA,CAAoB,UAAA,EAAY,GAAA,CAAI,SAAA,EAAW,IAAI,oBAAoB,CAAA;AAAA,MACtF;AAEA,MAAA,KAAA,EAAA;AACA,MAAA,MAAM;AAAA,QACF,MAAA,EAAQ;AAAA,UACJ,YAAA,EAAc;AAAA,YACV,WAAW,GAAA,CAAI,SAAA;AAAA,YACf,eAAe,GAAA,CAAI,aAAA;AAAA,YACnB,eAAe,GAAA,CAAI;AAAA;AACvB,SACJ;AAAA,QACA,UAAA;AAAA,QACA,QAAA;AAAA,QACA,YAAA,EAAc,IAAI,oBAAA,GACZ;AAAA,UACI,YAAA,EAAc;AAAA,YACV,WAAW,GAAA,CAAI,SAAA;AAAA,YACf,eAAe,GAAA,CAAI,aAAA;AAAA,YACnB,eAAe,GAAA,CAAI;AAAA;AACvB,SACJ,GACA,MAAA;AAAA,QACN;AAAA,OACJ;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAAA,CAA0B,UAAe,MAAA,EAAqC;AAClF,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC/C,MAAA,MAAM,aAAA,GAAgB,WAAW,GAAG,CAAA;AAEpC,MAAA,IAAI,UAAU,IAAA,EAAM;AAEhB,QAAA,IAAI,EAAE,GAAA,KAAQ,QAAA,IAAY,EAAC,CAAA,CAAA,IAAO,kBAAkB,IAAA,EAAM;AACtD,UAAA,OAAO,KAAA;AAAA,QACX;AAAA,MACJ,CAAA,MAAA,IAAW,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AAE3D,QAAA,IAAI,OAAO,aAAA,KAAkB,QAAA,IAAY,aAAA,KAAkB,IAAA,EAAM;AAC7D,UAAA,OAAO,KAAA;AAAA,QACX;AACA,QAAA,IAAI,sBAAA,CAAuB,KAAK,CAAA,KAAM,sBAAA,CAAuB,aAAa,CAAA,EAAG;AACzE,UAAA,OAAO,KAAA;AAAA,QACX;AAAA,MACJ,CAAA,MAAA,IAAW,kBAAkB,KAAA,EAAO;AAChC,QAAA,OAAO,KAAA;AAAA,MACX;AAAA,IACJ;AACA,IAAA,OAAO,IAAA;AAAA,EACX;AAAA,EAEA,MAAM,GAAA,CACF,MAAA,EACA,UAAA,EACA,UACA,YAAA,EACuB;AACvB,IAAA,MAAM,KAAK,KAAA,EAAM;AAEjB,IAAA,MAAM,QAAA,GAAW,OAAO,YAAA,EAAc,SAAA;AACtC,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,YAAA,EAAc,aAAA,IAAiB,EAAA;AAC3D,IAAA,MAAM,kBAAA,GAAqB,OAAO,YAAA,EAAc,aAAA;AAEhD,IAAA,IAAI,CAAC,QAAA,EAAU;AACX,MAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA,IAC3C;AAGA,IAAA,MAAM,YAAA,GAAe,UAAA,CAAW,EAAA,IAAM,KAAA,CAAM,CAAC,CAAA;AAE7C,IAAA,MAAM,kBAAA,GAA0C,eAAe,UAAU,CAAA;AAGzE,IAAA,MAAM,CAAC,CAAC,KAAA,EAAO,oBAAoB,CAAA,EAAG,CAAC,KAAA,EAAO,kBAAkB,CAAC,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,MACnF,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,kBAAkB,CAAA;AAAA,MACxC,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,QAAQ;AAAA,KACjC,CAAA;AAED,IAAA,IAAI,UAAU,KAAA,EAAO;AACjB,MAAA,MAAM,IAAI,MAAM,+DAA+D,CAAA;AAAA,IACnF;AAEA,IAAA,MAAM,SAAA,GAAY,QAAA;AAClB,IAAA,MAAM,aAAA,GAAgB,YAAA;AACtB,IAAA,MAAM,aAAA,GAAgB,YAAA;AAGtB,IAAA,MAAM,SAAA;AAAA,MACF,YAAY;AACR,QAAA,MAAM,KAAK,EAAA,CAAG,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,GAAA,KAAQ;AAE/C,UAAA,MAAM,aAAA,GAAgB,MAAM,GAAA,CACvB,UAAA,CAAW,qBAAqB,CAAA,CAChC,MAAA,CAAO,CAAC,eAAe,CAAC,EACxB,KAAA,CAAM,WAAA,EAAa,KAAK,SAAS,CAAA,CACjC,MAAM,eAAA,EAAiB,GAAA,EAAK,aAAa,CAAA,CACzC,gBAAA,EAAiB;AAGtB,UAAA,IAAI,aAAA,IAAiB,aAAA,CAAc,aAAA,KAAkB,aAAA,EAAe;AAChE,YAAA,MAAM,GAAA,CACD,WAAW,QAAQ,CAAA,CACnB,MAAM,WAAA,EAAa,GAAA,EAAK,SAAS,CAAA,CACjC,KAAA,CAAM,iBAAiB,GAAA,EAAK,aAAa,EACzC,KAAA,CAAM,eAAA,EAAiB,KAAK,aAAA,CAAc,aAAa,EACvD,OAAA,EAAQ;AAAA,UACjB;AAGA,UAAA,MAAM,GAAA,CACD,UAAA,CAAW,qBAAqB,CAAA,CAChC,MAAA,CAAO;AAAA,YACJ,SAAA,EAAW,SAAA;AAAA,YACX,aAAA,EAAe,aAAA;AAAA,YACf,aAAA,EAAe,aAAA;AAAA,YACf,sBAAsB,kBAAA,IAAsB,IAAA;AAAA,YAC5C,IAAA,EAAM,KAAA;AAAA,YACN,YAAY,IAAI,UAAA,CAAW,MAAA,CAAO,IAAA,CAAK,oBAAoB,CAAC,CAAA;AAAA,YAC5D,UAAU,IAAI,UAAA,CAAW,MAAA,CAAO,IAAA,CAAK,kBAAkB,CAAC,CAAA;AAAA,YACxD,aAAA,EAAe,KAAK,GAAA;AAAI,WAC3B,CAAA,CACA,UAAA;AAAA,YAAW,CAAC,OACT,EAAA,CAAG,OAAA,CAAQ,CAAC,WAAA,EAAa,eAAe,CAAC,CAAA,CAAE,WAAA,CAAY;AAAA,cACnD,aAAA,EAAe,aAAA;AAAA,cACf,sBAAsB,kBAAA,IAAsB,IAAA;AAAA,cAC5C,IAAA,EAAM,KAAA;AAAA,cACN,YAAY,IAAI,UAAA,CAAW,MAAA,CAAO,IAAA,CAAK,oBAAoB,CAAC,CAAA;AAAA,cAC5D,UAAU,IAAI,UAAA,CAAW,MAAA,CAAO,IAAA,CAAK,kBAAkB,CAAC,CAAA;AAAA,cACxD,aAAA,EAAe,KAAK,GAAA;AAAI,aAC3B;AAAA,YAEJ,OAAA,EAAQ;AAAA,QACjB,CAAC,CAAA;AAAA,MACL,CAAA;AAAA,MACA,CAAA,IAAA,EAAO,QAAQ,CAAA,CAAA,EAAI,YAAY,CAAA,CAAA;AAAA,KACnC;AAEA,IAAA,OAAO;AAAA,MACH,YAAA,EAAc;AAAA,QACV,SAAA,EAAW,QAAA;AAAA,QACX,aAAA,EAAe,YAAA;AAAA,QACf,aAAA,EAAe;AAAA;AACnB,KACJ;AAAA,EACJ;AAAA,EAEA,MAAM,SAAA,CAAU,MAAA,EAAwB,MAAA,EAAwB,MAAA,EAA+B;AAC3F,IAAA,MAAM,KAAK,KAAA,EAAM;AAEjB,IAAA,MAAM,QAAA,GAAW,OAAO,YAAA,EAAc,SAAA;AACtC,IAAA,MAAM,YAAA,GAAe,MAAA,CAAO,YAAA,EAAc,aAAA,IAAiB,EAAA;AAC3D,IAAA,MAAM,YAAA,GAAe,OAAO,YAAA,EAAc,aAAA;AAE1C,IAAA,IAAI,CAAC,QAAA,IAAY,CAAC,YAAA,EAAc;AAC5B,MAAA,MAAM,IAAI,MAAM,0CAA0C,CAAA;AAAA,IAC9D;AAGA,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,GAAA;AAAA,MACzB,MAAA,CAAO,GAAA,CAAI,OAAO,KAAA,EAAO,GAAA,KAAQ;AAC7B,QAAA,MAAM,CAAC,IAAA,EAAM,eAAe,CAAA,GAAI,MAAM,KAAK,KAAA,CAAM,UAAA,CAAW,KAAA,CAAM,CAAC,CAAC,CAAA;AACpE,QAAA,OAAO;AAAA,UACH,SAAA,EAAW,QAAA;AAAA,UACX,aAAA,EAAe,YAAA;AAAA,UACf,aAAA,EAAe,YAAA;AAAA,UACf,OAAA,EAAS,MAAA;AAAA,UACT,GAAA,EAAK,cAAA,CAAe,KAAA,CAAM,CAAC,CAAC,CAAA,IAAK,GAAA;AAAA,UACjC,OAAA,EAAS,MAAM,CAAC,CAAA;AAAA,UAChB,IAAA;AAAA,UACA,OAAO,IAAI,UAAA,CAAW,MAAA,CAAO,IAAA,CAAK,eAAe,CAAC;AAAA,SACtD;AAAA,MACJ,CAAC;AAAA,KACL;AAEA,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,EAAG;AAGzB,IAAA,MAAM,SAAA;AAAA,MACF,YAAY;AACR,QAAA,MAAM,KAAK,EAAA,CAAG,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,GAAA,KAAQ;AAE/C,UAAA,MAAM,GAAA,CACD,UAAA,CAAW,QAAQ,CAAA,CACnB,KAAA,CAAM,aAAa,GAAA,EAAK,QAAQ,CAAA,CAChC,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,YAAY,CAAA,CACxC,KAAA,CAAM,eAAA,EAAiB,GAAA,EAAK,YAAY,CAAA,CACxC,MAAM,SAAA,EAAW,GAAA,EAAK,MAAM,CAAA,CAC5B,OAAA,EAAQ;AAGb,UAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AACxB,YAAA,MAAM,IAAI,UAAA,CAAW,QAAQ,EAAE,MAAA,CAAO,KAAK,EAAE,OAAA,EAAQ;AAAA,UACzD;AAAA,QACJ,CAAC,CAAA;AAAA,MACL,CAAA;AAAA,MACA,CAAA,UAAA,EAAa,QAAQ,CAAA,CAAA,EAAI,YAAY,IAAI,MAAM,CAAA,CAAA;AAAA,KACnD;AAAA,EACJ;AAAA,EAEA,MAAM,aAAa,QAAA,EAAiC;AAChD,IAAA,MAAM,SAAA;AAAA,MACF,YAAY;AACR,QAAA,MAAM,KAAK,EAAA,CAAG,WAAA,EAAY,CAAE,OAAA,CAAQ,OAAO,GAAA,KAAQ;AAC/C,UAAA,MAAM,GAAA,CAAI,WAAW,qBAAqB,CAAA,CAAE,MAAM,WAAA,EAAa,GAAA,EAAK,QAAQ,CAAA,CAAE,OAAA,EAAQ;AACtF,UAAA,MAAM,GAAA,CAAI,WAAW,QAAQ,CAAA,CAAE,MAAM,WAAA,EAAa,GAAA,EAAK,QAAQ,CAAA,CAAE,OAAA,EAAQ;AAAA,QAC7E,CAAC,CAAA;AAAA,MACL,CAAA;AAAA,MACA,gBAAgB,QAAQ,CAAA,CAAA;AAAA,KAC5B;AAAA,EACJ;AAAA,EAEA,MAAgB,mBAAA,CAAoB,UAAA,EAAwB,QAAA,EAAkB,kBAAA,EAA4B;AACtG,IAAA,MAAM,SAAS,MAAM,IAAA,CAAK,GACrB,UAAA,CAAW,cAAc,EACzB,MAAA,CAAO;AAAA,MACJ,kBAAA;AAAA,MACA,GAAA,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAA,CAAA,CAKG,GAAG,eAAe;AAAA,KACxB,EACA,KAAA,CAAM,cAAA,EAAgB,KAAK,QAAQ,CAAA,CACnC,MAAM,kBAAA,EAAoB,GAAA,EAAK,kBAAkB,CAAA,CACjD,KAAA,CAAM,cAAc,GAAA,EAAK,KAAK,EAC9B,OAAA,CAAQ,QAAQ,EAChB,gBAAA,EAAiB;AAEtB,IAAA,IAAI,CAAC,MAAA,EAAQ;AAEb,IAAA,MAAM,iBAAA,GAAoB,UAAA;AAE1B,IAAA,iBAAA,CAAkB,mBAAmB,EAAC;AACtC,IAAA,iBAAA,CAAkB,cAAA,CAAe,KAAK,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA;AAAA,MACpD,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,aAAA,IAAiB,IAAI,CAAA,CAAE,GAAA;AAAA,QAAI,CAAC,EAAE,IAAA,EAAM,KAAA,OAClD,IAAA,CAAK,KAAA,CAAM,UAAA,CAAW,IAAA,EAAM,KAAK;AAAA;AACrC,KACJ;AAEA,IAAA,iBAAA,CAAkB,gBAAA,CAAiB,KAAK,CAAA,GACpC,MAAA,CAAO,KAAK,UAAA,CAAW,gBAAgB,EAAE,MAAA,GAAS,CAAA,GAC5C,kBAAkB,GAAG,MAAA,CAAO,OAAO,UAAA,CAAW,gBAAgB,CAAC,CAAA,GAC/D,IAAA,CAAK,eAAe,MAAS,CAAA;AAAA,EAC3C;AACJ;;;;"}