@mastra/libsql 1.22.3-alpha.0 → 1.22.3-alpha.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.
package/dist/index.js CHANGED
@@ -4,6 +4,8 @@ import { AGENTS_SCHEMA, AGENT_VERSIONS_SCHEMA, AgentsStorage, BackgroundTasksSto
4
4
  import { parseFieldKey, parseSqlIdentifier } from "@mastra/core/utils";
5
5
  import { MastraVector, validateTopK, validateUpsertInput } from "@mastra/core/vector";
6
6
  import { BaseFilterTranslator } from "@mastra/core/vector/filter";
7
+ import { realpath } from "fs/promises";
8
+ import { basename, dirname, isAbsolute, join, resolve } from "path";
7
9
  import { MastraBase } from "@mastra/core/base";
8
10
  import { randomUUID } from "crypto";
9
11
  import { MessageList } from "@mastra/core/agent";
@@ -384,6 +386,31 @@ const processOperator = (key, operator, operatorValue) => {
384
386
  };
385
387
  };
386
388
  //#endregion
389
+ //#region src/vector/write-lock.ts
390
+ const databaseWriteChains = /* @__PURE__ */ new Map();
391
+ async function getLocalFileDatabaseKey({ url, syncUrl, cwd }) {
392
+ if (!url.startsWith("file:") || url.includes(":memory:") || syncUrl) return;
393
+ const uriPath = url.slice(5).split(/[?#]/, 1)[0];
394
+ const decodedPath = decodeURIComponent(uriPath);
395
+ const absolutePath = isAbsolute(decodedPath) ? decodedPath : resolve(cwd, decodedPath);
396
+ try {
397
+ return await realpath(absolutePath);
398
+ } catch (error) {
399
+ if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") throw error;
400
+ }
401
+ return join(await realpath(dirname(absolutePath)), basename(absolutePath));
402
+ }
403
+ function withLocalFileDatabaseWriteLock(key, fn) {
404
+ if (!key) return fn();
405
+ const result = (databaseWriteChains.get(key) ?? Promise.resolve()).then(fn, fn);
406
+ const tail = result.then(() => void 0, () => void 0);
407
+ databaseWriteChains.set(key, tail);
408
+ tail.then(() => {
409
+ if (databaseWriteChains.get(key) === tail) databaseWriteChains.delete(key);
410
+ });
411
+ return result;
412
+ }
413
+ //#endregion
387
414
  //#region src/vector/index.ts
388
415
  var LibSQLVector = class extends MastraVector {
389
416
  turso;
@@ -391,25 +418,57 @@ var LibSQLVector = class extends MastraVector {
391
418
  initialBackoffMs;
392
419
  overFetchMultiplier;
393
420
  isMemoryDb;
394
- vectorIndexes;
421
+ initialization;
422
+ databaseKey;
423
+ vectorIndexes = /* @__PURE__ */ new Set();
395
424
  constructor({ url, authToken, syncUrl, syncInterval, maxRetries = 5, initialBackoffMs = 100, vectorTopKOverFetchMultiplier = 10, id }) {
396
425
  super({ id });
426
+ this.isMemoryDb = url.includes(":memory:");
427
+ const isLocalDb = (url.startsWith("file:") || this.isMemoryDb) && !syncUrl;
428
+ const cwd = process.cwd();
397
429
  this.turso = createClient({
398
430
  url,
399
431
  syncUrl,
400
432
  authToken,
401
- syncInterval
433
+ syncInterval,
434
+ ...isLocalDb ? { timeout: 5e3 } : {}
402
435
  });
403
436
  this.maxRetries = maxRetries;
404
437
  this.initialBackoffMs = initialBackoffMs;
405
438
  if (!Number.isInteger(vectorTopKOverFetchMultiplier) || vectorTopKOverFetchMultiplier < 1) throw new Error("vectorTopKOverFetchMultiplier must be a positive integer");
406
439
  this.overFetchMultiplier = vectorTopKOverFetchMultiplier;
407
- this.isMemoryDb = url.includes(":memory:");
408
- if (url.includes(`file:`) || this.isMemoryDb) {
409
- this.turso.execute("PRAGMA journal_mode=WAL;").then(() => this.logger.debug("LibSQLStore: PRAGMA journal_mode=WAL set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA journal_mode=WAL.", err));
410
- this.turso.execute("PRAGMA busy_timeout = 5000;").then(() => this.logger.debug("LibSQLStore: PRAGMA busy_timeout=5000 set.")).catch((err) => this.logger.warn("LibSQLStore: Failed to set PRAGMA busy_timeout=5000.", err));
440
+ this.initialization = this.initialize({
441
+ url,
442
+ syncUrl,
443
+ cwd,
444
+ isLocalDb
445
+ });
446
+ }
447
+ async initialize({ url, syncUrl, cwd, isLocalDb }) {
448
+ if (isLocalDb) {
449
+ await this.applyLocalPragmas();
450
+ this.databaseKey = await getLocalFileDatabaseKey({
451
+ url,
452
+ syncUrl,
453
+ cwd
454
+ });
455
+ }
456
+ if (!this.isMemoryDb) this.vectorIndexes = await this.discoverVectorIndexes();
457
+ }
458
+ async applyLocalPragmas() {
459
+ for (const [label, sql] of [["journal_mode=WAL", "PRAGMA journal_mode=WAL;"], ["busy_timeout=5000", "PRAGMA busy_timeout = 5000;"]]) try {
460
+ await this.turso.execute(sql);
461
+ this.logger.debug(`LibSQLStore: PRAGMA ${label} set.`);
462
+ } catch (err) {
463
+ this.logger.warn(`LibSQLStore: Failed to set PRAGMA ${label}.`, err);
411
464
  }
412
- this.vectorIndexes = this.isMemoryDb ? Promise.resolve(/* @__PURE__ */ new Set()) : this.discoverVectorIndexes();
465
+ }
466
+ async ensureInitialized() {
467
+ await this.initialization;
468
+ }
469
+ async executeMutation(operation, isTransaction = false) {
470
+ await this.ensureInitialized();
471
+ return withLocalFileDatabaseWriteLock(this.databaseKey, () => this.executeWriteOperationWithRetry(operation, isTransaction));
413
472
  }
414
473
  /**
415
474
  * Closes the underlying libsql client, releasing this vector store's OS file handles.
@@ -417,6 +476,7 @@ var LibSQLVector = class extends MastraVector {
417
476
  * Safe to call more than once; subsequent calls are no-ops.
418
477
  */
419
478
  async close() {
479
+ await this.ensureInitialized();
420
480
  if (!this.turso.closed) this.turso.close();
421
481
  }
422
482
  async discoverVectorIndexes() {
@@ -452,8 +512,8 @@ var LibSQLVector = class extends MastraVector {
452
512
  transformFilter(filter) {
453
513
  return new LibSQLFilterTranslator().translate(filter);
454
514
  }
455
- async hasVectorIndex(parsedIndexName) {
456
- return (await this.vectorIndexes).has(`${parsedIndexName}_vector_idx`);
515
+ hasVectorIndex(parsedIndexName) {
516
+ return this.vectorIndexes.has(`${parsedIndexName}_vector_idx`);
457
517
  }
458
518
  async queryWithIndex(parsedIndexName, vectorStr, topK, filter, includeVector, minScore) {
459
519
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(this.transformFilter(filter));
@@ -508,9 +568,10 @@ var LibSQLVector = class extends MastraVector {
508
568
  details: { message: "queryVector must be an array of finite numbers" }
509
569
  });
510
570
  try {
571
+ await this.ensureInitialized();
511
572
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
512
573
  const vectorStr = `[${queryVector.join(",")}]`;
513
- if (!this.isMemoryDb && await this.hasVectorIndex(parsedIndexName)) try {
574
+ if (!this.isMemoryDb && this.hasVectorIndex(parsedIndexName)) try {
514
575
  const indexedResults = await this.queryWithIndex(parsedIndexName, vectorStr, topK, filter, includeVector, minScore);
515
576
  if (!filter || indexedResults.length >= topK) return indexedResults;
516
577
  } catch (err) {
@@ -551,9 +612,9 @@ var LibSQLVector = class extends MastraVector {
551
612
  }, error);
552
613
  }
553
614
  }
554
- upsert(args) {
615
+ async upsert(args) {
555
616
  try {
556
- return this.executeWriteOperationWithRetry(() => this.doUpsert(args), true);
617
+ return await this.executeMutation(() => this.doUpsert(args), true);
557
618
  } catch (error) {
558
619
  throw new MastraError({
559
620
  id: createVectorErrorId("LIBSQL", "UPSERT", "FAILED"),
@@ -601,9 +662,9 @@ var LibSQLVector = class extends MastraVector {
601
662
  throw error;
602
663
  }
603
664
  }
604
- createIndex(args) {
665
+ async createIndex(args) {
605
666
  try {
606
- return this.executeWriteOperationWithRetry(() => this.doCreateIndex(args));
667
+ return await this.executeMutation(() => this.doCreateIndex(args));
607
668
  } catch (error) {
608
669
  throw new MastraError({
609
670
  id: createVectorErrorId("LIBSQL", "CREATE_INDEX", "FAILED"),
@@ -637,11 +698,11 @@ var LibSQLVector = class extends MastraVector {
637
698
  `,
638
699
  args: []
639
700
  });
640
- this.vectorIndexes.then((indexes) => indexes.add(`${parsedIndexName}_vector_idx`));
701
+ this.vectorIndexes.add(`${parsedIndexName}_vector_idx`);
641
702
  }
642
- deleteIndex(args) {
703
+ async deleteIndex(args) {
643
704
  try {
644
- return this.executeWriteOperationWithRetry(() => this.doDeleteIndex(args));
705
+ return await this.executeMutation(() => this.doDeleteIndex(args));
645
706
  } catch (error) {
646
707
  throw new MastraError({
647
708
  id: createVectorErrorId("LIBSQL", "DELETE_INDEX", "FAILED"),
@@ -657,10 +718,11 @@ var LibSQLVector = class extends MastraVector {
657
718
  sql: `DROP TABLE IF EXISTS ${parsedIndexName}`,
658
719
  args: []
659
720
  });
660
- this.vectorIndexes.then((indexes) => indexes.delete(`${parsedIndexName}_vector_idx`));
721
+ this.vectorIndexes.delete(`${parsedIndexName}_vector_idx`);
661
722
  }
662
723
  async listIndexes() {
663
724
  try {
725
+ await this.ensureInitialized();
664
726
  return (await this.turso.execute({
665
727
  sql: `
666
728
  SELECT name FROM sqlite_master
@@ -685,6 +747,7 @@ var LibSQLVector = class extends MastraVector {
685
747
  */
686
748
  async describeIndex({ indexName }) {
687
749
  try {
750
+ await this.ensureInitialized();
688
751
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
689
752
  const tableInfo = await this.turso.execute({
690
753
  sql: `
@@ -730,7 +793,7 @@ var LibSQLVector = class extends MastraVector {
730
793
  * @throws Will throw an error if no updates are provided or if the update operation fails.
731
794
  */
732
795
  updateVector(args) {
733
- return this.executeWriteOperationWithRetry(() => this.doUpdateVector(args));
796
+ return this.executeMutation(() => this.doUpdateVector(args));
734
797
  }
735
798
  async doUpdateVector(params) {
736
799
  const { indexName, update } = params;
@@ -835,9 +898,9 @@ var LibSQLVector = class extends MastraVector {
835
898
  * @returns A promise that resolves when the deletion is complete.
836
899
  * @throws Will throw an error if the deletion operation fails.
837
900
  */
838
- deleteVector(args) {
901
+ async deleteVector(args) {
839
902
  try {
840
- return this.executeWriteOperationWithRetry(() => this.doDeleteVector(args));
903
+ return await this.executeMutation(() => this.doDeleteVector(args));
841
904
  } catch (error) {
842
905
  throw new MastraError({
843
906
  id: createVectorErrorId("LIBSQL", "DELETE_VECTOR", "FAILED"),
@@ -858,7 +921,7 @@ var LibSQLVector = class extends MastraVector {
858
921
  });
859
922
  }
860
923
  deleteVectors(args) {
861
- return this.executeWriteOperationWithRetry(() => this.doDeleteVectors(args));
924
+ return this.executeMutation(() => this.doDeleteVectors(args));
862
925
  }
863
926
  async doDeleteVectors({ indexName, filter, ids }) {
864
927
  const parsedIndexName = parseSqlIdentifier(indexName, "index name");
@@ -940,9 +1003,9 @@ var LibSQLVector = class extends MastraVector {
940
1003
  }, error);
941
1004
  }
942
1005
  }
943
- truncateIndex(args) {
1006
+ async truncateIndex(args) {
944
1007
  try {
945
- return this.executeWriteOperationWithRetry(() => this._doTruncateIndex(args));
1008
+ return await this.executeMutation(() => this._doTruncateIndex(args));
946
1009
  } catch (error) {
947
1010
  throw new MastraError({
948
1011
  id: createVectorErrorId("LIBSQL", "TRUNCATE_INDEX", "FAILED"),
@@ -2498,6 +2561,26 @@ var AgentsLibSQL = class extends AgentsStorage {
2498
2561
  }, error);
2499
2562
  }
2500
2563
  }
2564
+ async getVersions(ids) {
2565
+ if (ids.length === 0) return [];
2566
+ try {
2567
+ return (await this.#db.selectMany({
2568
+ tableName: TABLE_AGENT_VERSIONS,
2569
+ whereClause: {
2570
+ sql: `WHERE id IN (${ids.map(() => "?").join(", ")})`,
2571
+ args: ids
2572
+ }
2573
+ }) ?? []).map((row) => this.parseVersionRow(row));
2574
+ } catch (error) {
2575
+ if (error instanceof MastraError) throw error;
2576
+ throw new MastraError({
2577
+ id: createStorageErrorId("LIBSQL", "GET_VERSIONS", "FAILED"),
2578
+ domain: ErrorDomain.STORAGE,
2579
+ category: ErrorCategory.THIRD_PARTY,
2580
+ details: { count: ids.length }
2581
+ }, error);
2582
+ }
2583
+ }
2501
2584
  async getVersionByNumber(agentId, versionNumber) {
2502
2585
  try {
2503
2586
  const rows = await this.#db.selectMany({
@@ -12241,6 +12324,24 @@ var SkillsLibSQL = class extends SkillsStorage {
12241
12324
  }, error);
12242
12325
  }
12243
12326
  }
12327
+ async getVersions(ids) {
12328
+ if (ids.length === 0) return [];
12329
+ try {
12330
+ const placeholders = ids.map(() => "?").join(", ");
12331
+ return ((await this.#client.execute({
12332
+ sql: `SELECT ${buildSelectColumns(TABLE_SKILL_VERSIONS)} FROM "${TABLE_SKILL_VERSIONS}" WHERE id IN (${placeholders})`,
12333
+ args: ids
12334
+ })).rows ?? []).map((row) => this.#parseVersionRow(row));
12335
+ } catch (error) {
12336
+ if (error instanceof MastraError) throw error;
12337
+ throw new MastraError({
12338
+ id: createStorageErrorId("LIBSQL", "GET_SKILL_VERSIONS", "FAILED"),
12339
+ domain: ErrorDomain.STORAGE,
12340
+ category: ErrorCategory.THIRD_PARTY,
12341
+ details: { count: ids.length }
12342
+ }, error);
12343
+ }
12344
+ }
12244
12345
  async getVersionByNumber(skillId, versionNumber) {
12245
12346
  try {
12246
12347
  const row = (await this.#client.execute({
@@ -12761,6 +12862,9 @@ function parseJson(val, column, rowId) {
12761
12862
  }
12762
12863
  return val;
12763
12864
  }
12865
+ function workflowDefinitionSelectColumns() {
12866
+ return buildSelectColumns(TABLE_WORKFLOW_DEFINITIONS).replace("json(\"schedule\") as \"schedule\"", "\"schedule\"");
12867
+ }
12764
12868
  function rowToDefinition(row) {
12765
12869
  const inputSchema = parseJson(row.inputSchema, "inputSchema", row.id);
12766
12870
  const outputSchema = parseJson(row.outputSchema, "outputSchema", row.id);
@@ -12783,6 +12887,12 @@ function rowToDefinition(row) {
12783
12887
  if (stateSchema !== void 0) def.stateSchema = stateSchema;
12784
12888
  const requestContextSchema = parseJson(row.requestContextSchema, "requestContextSchema", row.id);
12785
12889
  if (requestContextSchema !== void 0) def.requestContextSchema = requestContextSchema;
12890
+ try {
12891
+ const schedule = parseJson(row.schedule, "schedule", row.id);
12892
+ if (schedule != null) def.schedule = schedule;
12893
+ } catch {
12894
+ def.schedule = row.schedule;
12895
+ }
12786
12896
  if (row.authorId != null) def.authorId = String(row.authorId);
12787
12897
  return def;
12788
12898
  }
@@ -12804,6 +12914,11 @@ var WorkflowDefinitionsLibSQL = class extends WorkflowDefinitionsStorage {
12804
12914
  tableName: TABLE_WORKFLOW_DEFINITIONS,
12805
12915
  schema: TABLE_SCHEMAS[TABLE_WORKFLOW_DEFINITIONS]
12806
12916
  });
12917
+ await this.#db.alterTable({
12918
+ tableName: TABLE_WORKFLOW_DEFINITIONS,
12919
+ schema: TABLE_SCHEMAS[TABLE_WORKFLOW_DEFINITIONS],
12920
+ ifNotExists: ["schedule"]
12921
+ });
12807
12922
  await this.#client.execute({
12808
12923
  sql: `CREATE INDEX IF NOT EXISTS idx_workflow_definitions_status ON "${TABLE_WORKFLOW_DEFINITIONS}" ("status")`,
12809
12924
  args: []
@@ -12827,6 +12942,7 @@ var WorkflowDefinitionsLibSQL = class extends WorkflowDefinitionsStorage {
12827
12942
  stateSchema: input.stateSchema ?? null,
12828
12943
  requestContextSchema: input.requestContextSchema ?? null,
12829
12944
  graph: input.graph,
12945
+ schedule: "schedule" in input ? input.schedule ?? null : null,
12830
12946
  status: "active",
12831
12947
  source: "storage",
12832
12948
  authorId: "authorId" in input ? input.authorId ?? null : null,
@@ -12857,6 +12973,7 @@ var WorkflowDefinitionsLibSQL = class extends WorkflowDefinitionsStorage {
12857
12973
  if ("stateSchema" in input && input.stateSchema !== void 0) data.stateSchema = input.stateSchema;
12858
12974
  if ("requestContextSchema" in input && input.requestContextSchema !== void 0) data.requestContextSchema = input.requestContextSchema;
12859
12975
  if ("graph" in input && input.graph !== void 0) data.graph = input.graph;
12976
+ if ("schedule" in input && input.schedule !== void 0) data.schedule = input.schedule;
12860
12977
  if ("status" in input && input.status !== void 0) data.status = input.status;
12861
12978
  if ("authorId" in input && input.authorId !== void 0) data.authorId = input.authorId;
12862
12979
  await this.#db.update({
@@ -12870,7 +12987,7 @@ var WorkflowDefinitionsLibSQL = class extends WorkflowDefinitionsStorage {
12870
12987
  }
12871
12988
  async get(id) {
12872
12989
  const row = (await this.#client.execute({
12873
- sql: `SELECT ${buildSelectColumns(TABLE_WORKFLOW_DEFINITIONS)} FROM "${TABLE_WORKFLOW_DEFINITIONS}" WHERE id = ?`,
12990
+ sql: `SELECT ${workflowDefinitionSelectColumns()} FROM "${TABLE_WORKFLOW_DEFINITIONS}" WHERE id = ?`,
12874
12991
  args: [id]
12875
12992
  })).rows[0];
12876
12993
  return row ? rowToDefinition(row) : null;
@@ -12888,7 +13005,7 @@ var WorkflowDefinitionsLibSQL = class extends WorkflowDefinitionsStorage {
12888
13005
  }
12889
13006
  const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
12890
13007
  const definitions = (await this.#client.execute({
12891
- sql: `SELECT ${buildSelectColumns(TABLE_WORKFLOW_DEFINITIONS)} FROM "${TABLE_WORKFLOW_DEFINITIONS}" ${where} ORDER BY updatedAt DESC`,
13008
+ sql: `SELECT ${workflowDefinitionSelectColumns()} FROM "${TABLE_WORKFLOW_DEFINITIONS}" ${where} ORDER BY updatedAt DESC`,
12892
13009
  args: params
12893
13010
  })).rows.map((row) => rowToDefinition(row));
12894
13011
  return {