@mastra/libsql 1.22.4-alpha.1 → 1.22.5-alpha.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/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-message-history.md +1 -1
- package/dist/docs/references/docs-memory-multi-user-threads.md +1 -1
- package/dist/docs/references/docs-memory-overview.md +4 -4
- package/dist/index.cjs +98 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +98 -13
- package/dist/index.js.map +1 -1
- package/dist/shared/single-connection-client.d.ts +29 -0
- package/dist/shared/single-connection-client.d.ts.map +1 -0
- package/dist/storage/db/write-lock.d.ts.map +1 -1
- package/dist/storage/factory-storage.d.ts.map +1 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +8 -7
package/dist/index.js
CHANGED
|
@@ -11,6 +11,84 @@ import { randomUUID } from "crypto";
|
|
|
11
11
|
import { MessageList } from "@mastra/core/agent";
|
|
12
12
|
import { saveScorePayloadSchema } from "@mastra/core/evals";
|
|
13
13
|
import { skillSnapshotFieldValuesEqual } from "@mastra/core/storage/domains/skills";
|
|
14
|
+
//#region src/shared/single-connection-client.ts
|
|
15
|
+
/**
|
|
16
|
+
* Whether `@libsql/client` backs this database with exactly one connection.
|
|
17
|
+
*
|
|
18
|
+
* An in-memory database exists only on the connection that opened it, and each
|
|
19
|
+
* embedded-replica connection carries its own sync state, so `@libsql/client`
|
|
20
|
+
* (>= 0.18.0) gives both a pool of one. Any `execute`/`batch` issued while an
|
|
21
|
+
* interactive `transaction()` holds that connection is rejected immediately
|
|
22
|
+
* with `TRANSACTION_ACTIVE` instead of waiting for the transaction to settle.
|
|
23
|
+
*/
|
|
24
|
+
function isSingleConnectionDatabase({ url, syncUrl }) {
|
|
25
|
+
return url.includes(":memory:") || Boolean(syncUrl);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Wraps a single-connection client so client calls queue behind open
|
|
29
|
+
* transactions rather than failing with `TRANSACTION_ACTIVE`.
|
|
30
|
+
*
|
|
31
|
+
* `transaction()` takes the gate and releases it when the transaction commits,
|
|
32
|
+
* rolls back, or closes. `execute`, `batch`, `executeMultiple`, and `migrate`
|
|
33
|
+
* wait for the gate to be free before running but do not hold it — the driver
|
|
34
|
+
* executes them synchronously on the connection, so they cannot interleave
|
|
35
|
+
* with each other. Every other member passes through untouched.
|
|
36
|
+
*
|
|
37
|
+
* Callers must not issue client calls from inside their own open transaction
|
|
38
|
+
* (use `tx.execute`); such a call would wait for the transaction it is part of.
|
|
39
|
+
*/
|
|
40
|
+
function gateSingleConnectionClient(client) {
|
|
41
|
+
let gate = Promise.resolve();
|
|
42
|
+
const waitForGate = (run) => gate.then(run, run);
|
|
43
|
+
const transaction = async (mode) => {
|
|
44
|
+
let release;
|
|
45
|
+
const held = new Promise((resolve) => {
|
|
46
|
+
release = resolve;
|
|
47
|
+
});
|
|
48
|
+
const previous = gate;
|
|
49
|
+
gate = previous.then(() => held, () => held);
|
|
50
|
+
await previous.then(() => void 0, () => void 0);
|
|
51
|
+
let tx;
|
|
52
|
+
try {
|
|
53
|
+
tx = await client.transaction(mode);
|
|
54
|
+
} catch (error) {
|
|
55
|
+
release();
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
return new Proxy(tx, { get(target, prop) {
|
|
59
|
+
if (prop === "commit" || prop === "rollback") return async () => {
|
|
60
|
+
try {
|
|
61
|
+
await target[prop].call(target);
|
|
62
|
+
} finally {
|
|
63
|
+
release();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
if (prop === "close") return () => {
|
|
67
|
+
try {
|
|
68
|
+
target.close();
|
|
69
|
+
} finally {
|
|
70
|
+
release();
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const value = Reflect.get(target, prop);
|
|
74
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
75
|
+
} });
|
|
76
|
+
};
|
|
77
|
+
return new Proxy(client, { get(target, prop) {
|
|
78
|
+
switch (prop) {
|
|
79
|
+
case "transaction": return transaction;
|
|
80
|
+
case "execute":
|
|
81
|
+
case "batch":
|
|
82
|
+
case "executeMultiple":
|
|
83
|
+
case "migrate": return (...args) => waitForGate(() => target[prop].apply(target, args));
|
|
84
|
+
default: {
|
|
85
|
+
const value = Reflect.get(target, prop);
|
|
86
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} });
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
14
92
|
//#region src/vector/filter.ts
|
|
15
93
|
/**
|
|
16
94
|
* Translates MongoDB-style filters to LibSQL compatible filters.
|
|
@@ -426,13 +504,17 @@ var LibSQLVector = class extends MastraVector {
|
|
|
426
504
|
this.isMemoryDb = url.includes(":memory:");
|
|
427
505
|
const isLocalDb = (url.startsWith("file:") || this.isMemoryDb) && !syncUrl;
|
|
428
506
|
const cwd = process.cwd();
|
|
429
|
-
|
|
507
|
+
const client = createClient({
|
|
430
508
|
url,
|
|
431
509
|
syncUrl,
|
|
432
510
|
authToken,
|
|
433
511
|
syncInterval,
|
|
434
512
|
...isLocalDb ? { timeout: 5e3 } : {}
|
|
435
513
|
});
|
|
514
|
+
this.turso = isSingleConnectionDatabase({
|
|
515
|
+
url,
|
|
516
|
+
syncUrl
|
|
517
|
+
}) ? gateSingleConnectionClient(client) : client;
|
|
436
518
|
this.maxRetries = maxRetries;
|
|
437
519
|
this.initialBackoffMs = initialBackoffMs;
|
|
438
520
|
if (!Number.isInteger(vectorTopKOverFetchMultiplier) || vectorTopKOverFetchMultiplier < 1) throw new Error("vectorTopKOverFetchMultiplier must be a positive integer");
|
|
@@ -1279,24 +1361,26 @@ function transformFromSqlRow({ tableName, sqlRow }) {
|
|
|
1279
1361
|
/**
|
|
1280
1362
|
* Per-client write serialization.
|
|
1281
1363
|
*
|
|
1282
|
-
* `@libsql/client`
|
|
1283
|
-
*
|
|
1284
|
-
* `
|
|
1285
|
-
*
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
1288
|
-
* of as its own statement. Two concurrent interactive transactions collide the
|
|
1289
|
-
* same way ("cannot start a transaction within a transaction").
|
|
1364
|
+
* `@libsql/client` >= 0.18.0 pools connections for local `file:` databases, but
|
|
1365
|
+
* SQLite still admits one writer at a time: an interactive
|
|
1366
|
+
* `client.transaction('write')` holds `BEGIN` open across every
|
|
1367
|
+
* `await tx.execute(...)`, and any other write on the same database in that
|
|
1368
|
+
* window contends on the file lock and can fail with `SQLITE_BUSY` once
|
|
1369
|
+
* `busy_timeout` expires.
|
|
1290
1370
|
*
|
|
1291
1371
|
* This is dormant under the default engine but the evented engine runs many
|
|
1292
1372
|
* concurrent workflow snapshot writes per agent run, so a write issued by an
|
|
1293
|
-
* unrelated domain (e.g. creating a dataset experiment) can
|
|
1373
|
+
* unrelated domain (e.g. creating a dataset experiment) can fail spuriously.
|
|
1294
1374
|
*
|
|
1295
1375
|
* Serializing every write on a given client closes that window: writes — both
|
|
1296
1376
|
* autocommit statements and full interactive transactions — run one at a time,
|
|
1297
1377
|
* so none can interleave with an open transaction. Reads are intentionally not
|
|
1298
1378
|
* gated; WAL readers never observe a partial write and must not queue behind a
|
|
1299
1379
|
* long-running writer.
|
|
1380
|
+
*
|
|
1381
|
+
* `:memory:` databases and embedded replicas get a single pooled connection
|
|
1382
|
+
* instead; see `shared/single-connection-client.ts`, which gates *all* calls
|
|
1383
|
+
* (reads included) behind open transactions for those clients.
|
|
1300
1384
|
*/
|
|
1301
1385
|
const clientWriteChains = /* @__PURE__ */ new WeakMap();
|
|
1302
1386
|
/**
|
|
@@ -14221,11 +14305,12 @@ var LibSQLFactoryStorage = class extends FactoryStorage {
|
|
|
14221
14305
|
super();
|
|
14222
14306
|
this.#config = config;
|
|
14223
14307
|
const isLocalDb = config.url.startsWith("file:") || config.url.includes(":memory:");
|
|
14224
|
-
|
|
14308
|
+
const client = createClient({
|
|
14225
14309
|
url: config.url,
|
|
14226
14310
|
...config.authToken ? { authToken: config.authToken } : {},
|
|
14227
14311
|
...isLocalDb ? { timeout: DEFAULT_CONNECTION_TIMEOUT_MS } : {}
|
|
14228
14312
|
});
|
|
14313
|
+
this.#client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : client;
|
|
14229
14314
|
this.ops = new LibSQLFactoryStorageOps(this.#client, this.#schemas, (fn) => withClientWriteLock(this.#client, fn));
|
|
14230
14315
|
}
|
|
14231
14316
|
getMastraStorage() {
|
|
@@ -14383,15 +14468,15 @@ var LibSQLStore = class extends MastraCompositeStore {
|
|
|
14383
14468
|
mmapSize: config.localPragmas?.mmapSize ?? DEFAULT_LOCAL_MMAP_SIZE
|
|
14384
14469
|
};
|
|
14385
14470
|
if ("url" in config) {
|
|
14386
|
-
if (config.url.includes(":memory:")) this.shouldCacheInit = false;
|
|
14387
14471
|
this.isLocalDb = (config.url.startsWith("file:") || config.url.includes(":memory:")) && !config.syncUrl;
|
|
14388
|
-
|
|
14472
|
+
const client = createClient({
|
|
14389
14473
|
url: config.url,
|
|
14390
14474
|
...config.authToken ? { authToken: config.authToken } : {},
|
|
14391
14475
|
...config.syncUrl ? { syncUrl: config.syncUrl } : {},
|
|
14392
14476
|
...config.syncInterval !== void 0 ? { syncInterval: config.syncInterval } : {},
|
|
14393
14477
|
...this.isLocalDb ? { timeout: this.connectionTimeoutMs } : {}
|
|
14394
14478
|
});
|
|
14479
|
+
this.client = isSingleConnectionDatabase(config) ? gateSingleConnectionClient(client) : client;
|
|
14395
14480
|
this.pragmasReady = this.isLocalDb ? this.applyLocalPragmas() : Promise.resolve();
|
|
14396
14481
|
} else {
|
|
14397
14482
|
this.client = config.client;
|