@mastra/libsql 0.0.4-alpha.4 → 0.0.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/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var client = require('@libsql/client');
4
+ var utils = require('@mastra/core/utils');
4
5
  var vector = require('@mastra/core/vector');
5
6
  var filter = require('@mastra/core/vector/filter');
6
7
  var storage = require('@mastra/core/storage');
@@ -72,28 +73,53 @@ var LibSQLFilterTranslator = class extends filter.BaseFilterTranslator {
72
73
  // };
73
74
  // }
74
75
  };
75
-
76
- // src/vector/sql-builder.ts
77
76
  var createBasicOperator = (symbol) => {
78
- return (key) => ({
79
- sql: `CASE
80
- WHEN ? IS NULL THEN json_extract(metadata, '$."${handleKey(key)}"') IS ${symbol === "=" ? "" : "NOT"} NULL
81
- ELSE json_extract(metadata, '$."${handleKey(key)}"') ${symbol} ?
82
- END`,
83
- needsValue: true,
84
- transformValue: (value) => {
85
- return [value, value];
86
- }
87
- });
77
+ return (key, value) => {
78
+ const jsonPathKey = parseJsonPathKey(key);
79
+ return {
80
+ sql: `CASE
81
+ WHEN ? IS NULL THEN json_extract(metadata, '$."${jsonPathKey}"') IS ${symbol === "=" ? "" : "NOT"} NULL
82
+ ELSE json_extract(metadata, '$."${jsonPathKey}"') ${symbol} ?
83
+ END`,
84
+ needsValue: true,
85
+ transformValue: () => {
86
+ return [value, value];
87
+ }
88
+ };
89
+ };
88
90
  };
89
91
  var createNumericOperator = (symbol) => {
90
- return (key) => ({
91
- sql: `CAST(json_extract(metadata, '$."${handleKey(key)}"') AS NUMERIC) ${symbol} ?`,
92
- needsValue: true
93
- });
92
+ return (key) => {
93
+ const jsonPathKey = parseJsonPathKey(key);
94
+ return {
95
+ sql: `CAST(json_extract(metadata, '$."${jsonPathKey}"') AS NUMERIC) ${symbol} ?`,
96
+ needsValue: true
97
+ };
98
+ };
94
99
  };
95
- var validateJsonArray = (key) => `json_valid(json_extract(metadata, '$."${handleKey(key)}"'))
96
- AND json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array'`;
100
+ var validateJsonArray = (key) => `json_valid(json_extract(metadata, '$."${key}"'))
101
+ AND json_type(json_extract(metadata, '$."${key}"')) = 'array'`;
102
+ var pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
103
+ function buildElemMatchConditions(value) {
104
+ const conditions = Object.entries(value).map(([field, fieldValue]) => {
105
+ if (field.startsWith("$")) {
106
+ const { sql, values } = buildCondition("elem.value", { [field]: fieldValue });
107
+ const elemSql = sql.replace(pattern, "elem.value");
108
+ return { sql: elemSql, values };
109
+ } else if (typeof fieldValue === "object" && !Array.isArray(fieldValue)) {
110
+ const { sql, values } = buildCondition(field, fieldValue);
111
+ const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
112
+ return { sql: elemSql, values };
113
+ } else {
114
+ const parsedFieldKey = utils.parseFieldKey(field);
115
+ return {
116
+ sql: `json_extract(elem.value, '$."${parsedFieldKey}"') = ?`,
117
+ values: [fieldValue]
118
+ };
119
+ }
120
+ });
121
+ return conditions;
122
+ }
97
123
  var FILTER_OPERATORS = {
98
124
  $eq: createBasicOperator("="),
99
125
  $ne: createBasicOperator("!="),
@@ -102,90 +128,113 @@ var FILTER_OPERATORS = {
102
128
  $lt: createNumericOperator("<"),
103
129
  $lte: createNumericOperator("<="),
104
130
  // Array Operators
105
- $in: (key, value) => ({
106
- sql: `json_extract(metadata, '$."${handleKey(key)}"') IN (${value.map(() => "?").join(",")})`,
107
- needsValue: true
108
- }),
109
- $nin: (key, value) => ({
110
- sql: `json_extract(metadata, '$."${handleKey(key)}"') NOT IN (${value.map(() => "?").join(",")})`,
111
- needsValue: true
112
- }),
113
- $all: (key) => ({
114
- sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
115
- needsValue: true,
116
- transformValue: (value) => {
117
- const arrayValue = Array.isArray(value) ? value : [value];
118
- if (arrayValue.length === 0) {
119
- return {
120
- sql: "1 = 0",
121
- values: []
122
- };
123
- }
124
- return {
125
- sql: `(
126
- CASE
127
- WHEN ${validateJsonArray(key)} THEN
128
- NOT EXISTS (
129
- SELECT value
130
- FROM json_each(?)
131
- WHERE value NOT IN (
132
- SELECT value
133
- FROM json_each(json_extract(metadata, '$."${handleKey(key)}"'))
134
- )
131
+ $in: (key, value) => {
132
+ const jsonPathKey = parseJsonPathKey(key);
133
+ const arr = Array.isArray(value) ? value : [value];
134
+ if (arr.length === 0) {
135
+ return { sql: "1 = 0", needsValue: true, transformValue: () => [] };
136
+ }
137
+ const paramPlaceholders = arr.map(() => "?").join(",");
138
+ return {
139
+ sql: `(
140
+ CASE
141
+ WHEN ${validateJsonArray(jsonPathKey)} THEN
142
+ EXISTS (
143
+ SELECT 1 FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
144
+ WHERE elem.value IN (SELECT value FROM json_each(?))
145
+ )
146
+ ELSE json_extract(metadata, '$."${jsonPathKey}"') IN (${paramPlaceholders})
147
+ END
148
+ )`,
149
+ needsValue: true,
150
+ transformValue: () => [JSON.stringify(arr), ...arr]
151
+ };
152
+ },
153
+ $nin: (key, value) => {
154
+ const jsonPathKey = parseJsonPathKey(key);
155
+ const arr = Array.isArray(value) ? value : [value];
156
+ if (arr.length === 0) {
157
+ return { sql: "1 = 1", needsValue: true, transformValue: () => [] };
158
+ }
159
+ const paramPlaceholders = arr.map(() => "?").join(",");
160
+ return {
161
+ sql: `(
162
+ CASE
163
+ WHEN ${validateJsonArray(jsonPathKey)} THEN
164
+ NOT EXISTS (
165
+ SELECT 1 FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
166
+ WHERE elem.value IN (SELECT value FROM json_each(?))
167
+ )
168
+ ELSE json_extract(metadata, '$."${jsonPathKey}"') NOT IN (${paramPlaceholders})
169
+ END
170
+ )`,
171
+ needsValue: true,
172
+ transformValue: () => [JSON.stringify(arr), ...arr]
173
+ };
174
+ },
175
+ $all: (key, value) => {
176
+ const jsonPathKey = parseJsonPathKey(key);
177
+ let sql;
178
+ const arrayValue = Array.isArray(value) ? value : [value];
179
+ if (arrayValue.length === 0) {
180
+ sql = "1 = 0";
181
+ } else {
182
+ sql = `(
183
+ CASE
184
+ WHEN ${validateJsonArray(jsonPathKey)} THEN
185
+ NOT EXISTS (
186
+ SELECT value
187
+ FROM json_each(?)
188
+ WHERE value NOT IN (
189
+ SELECT value
190
+ FROM json_each(json_extract(metadata, '$."${jsonPathKey}"'))
135
191
  )
136
- ELSE FALSE
137
- END
138
- )`,
139
- values: [JSON.stringify(arrayValue)]
140
- };
192
+ )
193
+ ELSE FALSE
194
+ END
195
+ )`;
141
196
  }
142
- }),
143
- $elemMatch: (key) => ({
144
- sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
145
- needsValue: true,
146
- transformValue: (value) => {
147
- if (typeof value !== "object" || Array.isArray(value)) {
148
- throw new Error("$elemMatch requires an object with conditions");
149
- }
150
- const conditions = Object.entries(value).map(([field, fieldValue]) => {
151
- if (field.startsWith("$")) {
152
- const { sql, values } = buildCondition("elem.value", { [field]: fieldValue });
153
- const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
154
- const elemSql = sql.replace(pattern, "elem.value");
155
- return { sql: elemSql, values };
156
- } else if (typeof fieldValue === "object" && !Array.isArray(fieldValue)) {
157
- const { sql, values } = buildCondition(field, fieldValue);
158
- const pattern = /json_extract\(metadata, '\$\."[^"]*"(\."[^"]*")*'\)/g;
159
- const elemSql = sql.replace(pattern, `json_extract(elem.value, '$."${field}"')`);
160
- return { sql: elemSql, values };
161
- } else {
162
- return {
163
- sql: `json_extract(elem.value, '$."${field}"') = ?`,
164
- values: [fieldValue]
165
- };
197
+ return {
198
+ sql,
199
+ needsValue: true,
200
+ transformValue: () => {
201
+ if (arrayValue.length === 0) {
202
+ return [];
166
203
  }
167
- });
168
- return {
169
- sql: `(
170
- CASE
171
- WHEN ${validateJsonArray(key)} THEN
172
- EXISTS (
173
- SELECT 1
174
- FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as elem
175
- WHERE ${conditions.map((c) => c.sql).join(" AND ")}
176
- )
177
- ELSE FALSE
178
- END
179
- )`,
180
- values: conditions.flatMap((c) => c.values)
181
- };
204
+ return [JSON.stringify(arrayValue)];
205
+ }
206
+ };
207
+ },
208
+ $elemMatch: (key, value) => {
209
+ const jsonPathKey = parseJsonPathKey(key);
210
+ if (typeof value !== "object" || Array.isArray(value)) {
211
+ throw new Error("$elemMatch requires an object with conditions");
182
212
  }
183
- }),
213
+ const conditions = buildElemMatchConditions(value);
214
+ return {
215
+ sql: `(
216
+ CASE
217
+ WHEN ${validateJsonArray(jsonPathKey)} THEN
218
+ EXISTS (
219
+ SELECT 1
220
+ FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as elem
221
+ WHERE ${conditions.map((c) => c.sql).join(" AND ")}
222
+ )
223
+ ELSE FALSE
224
+ END
225
+ )`,
226
+ needsValue: true,
227
+ transformValue: () => conditions.flatMap((c) => c.values)
228
+ };
229
+ },
184
230
  // Element Operators
185
- $exists: (key) => ({
186
- sql: `json_extract(metadata, '$."${handleKey(key)}"') IS NOT NULL`,
187
- needsValue: false
188
- }),
231
+ $exists: (key) => {
232
+ const jsonPathKey = parseJsonPathKey(key);
233
+ return {
234
+ sql: `json_extract(metadata, '$."${jsonPathKey}"') IS NOT NULL`,
235
+ needsValue: false
236
+ };
237
+ },
189
238
  // Logical Operators
190
239
  $and: (key) => ({
191
240
  sql: `(${key})`,
@@ -200,27 +249,30 @@ var FILTER_OPERATORS = {
200
249
  sql: `NOT (${key})`,
201
250
  needsValue: false
202
251
  }),
203
- $size: (key, paramIndex) => ({
204
- sql: `(
252
+ $size: (key, paramIndex) => {
253
+ const jsonPathKey = parseJsonPathKey(key);
254
+ return {
255
+ sql: `(
205
256
  CASE
206
- WHEN json_type(json_extract(metadata, '$."${handleKey(key)}"')) = 'array' THEN
207
- json_array_length(json_extract(metadata, '$."${handleKey(key)}"')) = $${paramIndex}
257
+ WHEN json_type(json_extract(metadata, '$."${jsonPathKey}"')) = 'array' THEN
258
+ json_array_length(json_extract(metadata, '$."${jsonPathKey}"')) = $${paramIndex}
208
259
  ELSE FALSE
209
260
  END
210
261
  )`,
211
- needsValue: true
212
- }),
262
+ needsValue: true
263
+ };
264
+ },
213
265
  // /**
214
266
  // * Regex Operators
215
267
  // * Supports case insensitive and multiline
216
268
  // */
217
269
  // $regex: (key: string): FilterOperator => ({
218
- // sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
270
+ // sql: `json_extract(metadata, '$."${toJsonPathKey(key)}"') = ?`,
219
271
  // needsValue: true,
220
272
  // transformValue: (value: any) => {
221
273
  // const pattern = typeof value === 'object' ? value.$regex : value;
222
274
  // const options = typeof value === 'object' ? value.$options || '' : '';
223
- // let sql = `json_extract(metadata, '$."${handleKey(key)}"')`;
275
+ // let sql = `json_extract(metadata, '$."${toJsonPathKey(key)}"')`;
224
276
  // // Handle multiline
225
277
  // // if (options.includes('m')) {
226
278
  // // sql = `REPLACE(${sql}, CHAR(10), '\n')`;
@@ -269,50 +321,64 @@ var FILTER_OPERATORS = {
269
321
  // };
270
322
  // },
271
323
  // }),
272
- $contains: (key) => ({
273
- sql: `json_extract(metadata, '$."${handleKey(key)}"') = ?`,
274
- needsValue: true,
275
- transformValue: (value) => {
276
- if (Array.isArray(value)) {
277
- return {
278
- sql: `(
279
- SELECT ${validateJsonArray(key)}
280
- AND EXISTS (
281
- SELECT 1
282
- FROM json_each(json_extract(metadata, '$."${handleKey(key)}"')) as m
283
- WHERE m.value IN (SELECT value FROM json_each(?))
284
- )
285
- )`,
286
- values: [JSON.stringify(value)]
287
- };
288
- }
289
- if (value && typeof value === "object") {
290
- let traverse2 = function(obj, path = []) {
291
- for (const [k, v] of Object.entries(obj)) {
292
- const currentPath = [...path, k];
293
- if (v && typeof v === "object" && !Array.isArray(v)) {
294
- traverse2(v, currentPath);
295
- } else {
296
- paths.push(currentPath.join("."));
297
- values.push(v);
298
- }
299
- }
300
- };
301
- const paths = [];
302
- const values = [];
303
- traverse2(value);
304
- return {
305
- sql: `(${paths.map((path) => `json_extract(metadata, '$."${handleKey(key)}"."${path}"') = ?`).join(" AND ")})`,
306
- values
307
- };
308
- }
309
- return value;
324
+ $contains: (key, value) => {
325
+ const jsonPathKey = parseJsonPathKey(key);
326
+ let sql;
327
+ if (Array.isArray(value)) {
328
+ sql = `(
329
+ SELECT ${validateJsonArray(jsonPathKey)}
330
+ AND EXISTS (
331
+ SELECT 1
332
+ FROM json_each(json_extract(metadata, '$."${jsonPathKey}"')) as m
333
+ WHERE m.value IN (SELECT value FROM json_each(?))
334
+ )
335
+ )`;
336
+ } else if (typeof value === "string") {
337
+ sql = `lower(json_extract(metadata, '$."${jsonPathKey}"')) LIKE '%' || lower(?) || '%' ESCAPE '\\'`;
338
+ } else {
339
+ sql = `json_extract(metadata, '$."${jsonPathKey}"') = ?`;
310
340
  }
311
- })
341
+ return {
342
+ sql,
343
+ needsValue: true,
344
+ transformValue: () => {
345
+ if (Array.isArray(value)) {
346
+ return [JSON.stringify(value)];
347
+ }
348
+ if (typeof value === "object" && value !== null) {
349
+ return [JSON.stringify(value)];
350
+ }
351
+ if (typeof value === "string") {
352
+ return [escapeLikePattern(value)];
353
+ }
354
+ return [value];
355
+ }
356
+ };
357
+ }
358
+ /**
359
+ * $objectContains: True JSON containment for advanced use (deep sub-object match).
360
+ * Usage: { field: { $objectContains: { ...subobject } } }
361
+ */
362
+ // $objectContains: (key: string) => ({
363
+ // sql: '', // Will be overridden by transformValue
364
+ // needsValue: true,
365
+ // transformValue: (value: any) => ({
366
+ // sql: `json_type(json_extract(metadata, '$."${toJsonPathKey(key)}"')) = 'object'
367
+ // AND json_patch(json_extract(metadata, '$."${toJsonPathKey(key)}"'), ?) = json_extract(metadata, '$."${toJsonPathKey(key)}"')`,
368
+ // values: [JSON.stringify(value)],
369
+ // }),
370
+ // }),
312
371
  };
313
- var handleKey = (key) => {
314
- return key.replace(/\./g, '"."');
372
+ function isFilterResult(obj) {
373
+ return obj && typeof obj === "object" && typeof obj.sql === "string" && Array.isArray(obj.values);
374
+ }
375
+ var parseJsonPathKey = (key) => {
376
+ const parsedKey = utils.parseFieldKey(key);
377
+ return parsedKey.replace(/\./g, '"."');
315
378
  };
379
+ function escapeLikePattern(str) {
380
+ return str.replace(/([%_\\])/g, "\\$1");
381
+ }
316
382
  function buildFilterQuery(filter) {
317
383
  if (!filter) {
318
384
  return { sql: "", values: [] };
@@ -405,8 +471,8 @@ var processOperator = (key, operator, operatorValue) => {
405
471
  if (!operatorResult.needsValue) {
406
472
  return { sql: operatorResult.sql, values: [] };
407
473
  }
408
- const transformed = operatorResult.transformValue ? operatorResult.transformValue(operatorValue) : operatorValue;
409
- if (transformed && typeof transformed === "object" && "sql" in transformed) {
474
+ const transformed = operatorResult.transformValue ? operatorResult.transformValue() : operatorValue;
475
+ if (isFilterResult(transformed)) {
410
476
  return transformed;
411
477
  }
412
478
  return {
@@ -446,10 +512,18 @@ var LibSQLVector = class extends vector.MastraVector {
446
512
  const params = this.normalizeArgs("query", args, ["minScore"]);
447
513
  try {
448
514
  const { indexName, queryVector, topK = 10, filter, includeVector = false, minScore = 0 } = params;
515
+ if (!Number.isInteger(topK) || topK <= 0) {
516
+ throw new Error("topK must be a positive integer");
517
+ }
518
+ if (!Array.isArray(queryVector) || !queryVector.every((x) => typeof x === "number" && Number.isFinite(x))) {
519
+ throw new Error("queryVector must be an array of finite numbers");
520
+ }
521
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
449
522
  const vectorStr = `[${queryVector.join(",")}]`;
450
523
  const translatedFilter = this.transformFilter(filter);
451
524
  const { sql: filterQuery, values: filterValues } = buildFilterQuery(translatedFilter);
452
525
  filterValues.push(minScore);
526
+ filterValues.push(topK);
453
527
  const query = `
454
528
  WITH vector_scores AS (
455
529
  SELECT
@@ -457,14 +531,14 @@ var LibSQLVector = class extends vector.MastraVector {
457
531
  (1-vector_distance_cos(embedding, '${vectorStr}')) as score,
458
532
  metadata
459
533
  ${includeVector ? ", vector_extract(embedding) as embedding" : ""}
460
- FROM ${indexName}
534
+ FROM ${parsedIndexName}
461
535
  ${filterQuery}
462
536
  )
463
537
  SELECT *
464
538
  FROM vector_scores
465
539
  WHERE score > ?
466
540
  ORDER BY score DESC
467
- LIMIT ${topK}`;
541
+ LIMIT ?`;
468
542
  const result = await this.turso.execute({
469
543
  sql: query,
470
544
  args: filterValues
@@ -483,10 +557,11 @@ var LibSQLVector = class extends vector.MastraVector {
483
557
  const { indexName, vectors, metadata, ids } = params;
484
558
  const tx = await this.turso.transaction("write");
485
559
  try {
560
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
486
561
  const vectorIds = ids || vectors.map(() => crypto.randomUUID());
487
562
  for (let i = 0; i < vectors.length; i++) {
488
563
  const query = `
489
- INSERT INTO ${indexName} (vector_id, embedding, metadata)
564
+ INSERT INTO ${parsedIndexName} (vector_id, embedding, metadata)
490
565
  VALUES (?, vector32(?), ?)
491
566
  ON CONFLICT(vector_id) DO UPDATE SET
492
567
  embedding = vector32(?),
@@ -524,15 +599,13 @@ var LibSQLVector = class extends vector.MastraVector {
524
599
  const params = this.normalizeArgs("createIndex", args);
525
600
  const { indexName, dimension } = params;
526
601
  try {
527
- if (!indexName.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)) {
528
- throw new Error("Invalid index name format");
529
- }
530
602
  if (!Number.isInteger(dimension) || dimension <= 0) {
531
603
  throw new Error("Dimension must be a positive integer");
532
604
  }
605
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
533
606
  await this.turso.execute({
534
607
  sql: `
535
- CREATE TABLE IF NOT EXISTS ${indexName} (
608
+ CREATE TABLE IF NOT EXISTS ${parsedIndexName} (
536
609
  id SERIAL PRIMARY KEY,
537
610
  vector_id TEXT UNIQUE NOT NULL,
538
611
  embedding F32_BLOB(${dimension}),
@@ -543,8 +616,8 @@ var LibSQLVector = class extends vector.MastraVector {
543
616
  });
544
617
  await this.turso.execute({
545
618
  sql: `
546
- CREATE INDEX IF NOT EXISTS ${indexName}_vector_idx
547
- ON ${indexName} (libsql_vector_idx(embedding))
619
+ CREATE INDEX IF NOT EXISTS ${parsedIndexName}_vector_idx
620
+ ON ${parsedIndexName} (libsql_vector_idx(embedding))
548
621
  `,
549
622
  args: []
550
623
  });
@@ -554,10 +627,13 @@ var LibSQLVector = class extends vector.MastraVector {
554
627
  } finally {
555
628
  }
556
629
  }
557
- async deleteIndex(indexName) {
630
+ async deleteIndex(...args) {
631
+ const params = this.normalizeArgs("deleteIndex", args);
632
+ const { indexName } = params;
558
633
  try {
634
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
559
635
  await this.turso.execute({
560
- sql: `DROP TABLE IF EXISTS ${indexName}`,
636
+ sql: `DROP TABLE IF EXISTS ${parsedIndexName}`,
561
637
  args: []
562
638
  });
563
639
  } catch (error) {
@@ -582,8 +658,18 @@ var LibSQLVector = class extends vector.MastraVector {
582
658
  throw new Error(`Failed to list vector tables: ${error.message}`);
583
659
  }
584
660
  }
585
- async describeIndex(indexName) {
661
+ /**
662
+ * Retrieves statistics about a vector index.
663
+ *
664
+ * @param params - The parameters for describing an index
665
+ * @param params.indexName - The name of the index to describe
666
+ * @returns A promise that resolves to the index statistics including dimension, count and metric
667
+ */
668
+ async describeIndex(...args) {
669
+ const params = this.normalizeArgs("describeIndex", args);
670
+ const { indexName } = params;
586
671
  try {
672
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
587
673
  const tableInfoQuery = `
588
674
  SELECT sql
589
675
  FROM sqlite_master
@@ -592,15 +678,15 @@ var LibSQLVector = class extends vector.MastraVector {
592
678
  `;
593
679
  const tableInfo = await this.turso.execute({
594
680
  sql: tableInfoQuery,
595
- args: [indexName]
681
+ args: [parsedIndexName]
596
682
  });
597
683
  if (!tableInfo.rows[0]?.sql) {
598
- throw new Error(`Table ${indexName} not found`);
684
+ throw new Error(`Table ${parsedIndexName} not found`);
599
685
  }
600
686
  const dimension = parseInt(tableInfo.rows[0].sql.match(/F32_BLOB\((\d+)\)/)?.[1] || "0");
601
687
  const countQuery = `
602
688
  SELECT COUNT(*) as count
603
- FROM ${indexName};
689
+ FROM ${parsedIndexName};
604
690
  `;
605
691
  const countResult = await this.turso.execute({
606
692
  sql: countQuery,
@@ -634,10 +720,11 @@ var LibSQLVector = class extends vector.MastraVector {
634
720
  Please use updateVector() instead.
635
721
  updateIndexById() will be removed on May 20th, 2025.`
636
722
  );
637
- await this.updateVector(indexName, id, update);
723
+ await this.updateVector({ indexName, id, update });
638
724
  }
639
725
  /**
640
726
  * Updates a vector by its ID with the provided vector and/or metadata.
727
+ *
641
728
  * @param indexName - The name of the index containing the vector.
642
729
  * @param id - The ID of the vector to update.
643
730
  * @param update - An object containing the vector and/or metadata to update.
@@ -646,30 +733,33 @@ var LibSQLVector = class extends vector.MastraVector {
646
733
  * @returns A promise that resolves when the update is complete.
647
734
  * @throws Will throw an error if no updates are provided or if the update operation fails.
648
735
  */
649
- async updateVector(indexName, id, update) {
736
+ async updateVector(...args) {
737
+ const params = this.normalizeArgs("updateVector", args);
738
+ const { indexName, id, update } = params;
650
739
  try {
740
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
651
741
  const updates = [];
652
- const args = [];
742
+ const args2 = [];
653
743
  if (update.vector) {
654
744
  updates.push("embedding = vector32(?)");
655
- args.push(JSON.stringify(update.vector));
745
+ args2.push(JSON.stringify(update.vector));
656
746
  }
657
747
  if (update.metadata) {
658
748
  updates.push("metadata = ?");
659
- args.push(JSON.stringify(update.metadata));
749
+ args2.push(JSON.stringify(update.metadata));
660
750
  }
661
751
  if (updates.length === 0) {
662
752
  throw new Error("No updates provided");
663
753
  }
664
- args.push(id);
754
+ args2.push(id);
665
755
  const query = `
666
- UPDATE ${indexName}
756
+ UPDATE ${parsedIndexName}
667
757
  SET ${updates.join(", ")}
668
758
  WHERE vector_id = ?;
669
759
  `;
670
760
  await this.turso.execute({
671
761
  sql: query,
672
- args
762
+ args: args2
673
763
  });
674
764
  } catch (error) {
675
765
  throw new Error(`Failed to update vector by id: ${id} for index: ${indexName}: ${error.message}`);
@@ -690,7 +780,7 @@ var LibSQLVector = class extends vector.MastraVector {
690
780
  Please use deleteVector() instead.
691
781
  deleteIndexById() will be removed on May 20th, 2025.`
692
782
  );
693
- await this.deleteVector(indexName, id);
783
+ await this.deleteVector({ indexName, id });
694
784
  }
695
785
  /**
696
786
  * Deletes a vector by its ID.
@@ -699,19 +789,24 @@ var LibSQLVector = class extends vector.MastraVector {
699
789
  * @returns A promise that resolves when the deletion is complete.
700
790
  * @throws Will throw an error if the deletion operation fails.
701
791
  */
702
- async deleteVector(indexName, id) {
792
+ async deleteVector(...args) {
793
+ const params = this.normalizeArgs("deleteVector", args);
794
+ const { indexName, id } = params;
703
795
  try {
796
+ const parsedIndexName = utils.parseSqlIdentifier(indexName, "index name");
704
797
  await this.turso.execute({
705
- sql: `DELETE FROM ${indexName} WHERE vector_id = ?`,
798
+ sql: `DELETE FROM ${parsedIndexName} WHERE vector_id = ?`,
706
799
  args: [id]
707
800
  });
708
801
  } catch (error) {
709
802
  throw new Error(`Failed to delete vector by id: ${id} for index: ${indexName}: ${error.message}`);
710
803
  }
711
804
  }
712
- async truncateIndex(indexName) {
805
+ async truncateIndex(...args) {
806
+ const params = this.normalizeArgs("truncateIndex", args);
807
+ const { indexName } = params;
713
808
  await this.turso.execute({
714
- sql: `DELETE FROM ${indexName}`,
809
+ sql: `DELETE FROM ${utils.parseSqlIdentifier(indexName, "index name")}`,
715
810
  args: []
716
811
  });
717
812
  }
@@ -733,22 +828,24 @@ var LibSQLStore = class extends storage.MastraStorage {
733
828
  this.client = client.createClient(config);
734
829
  }
735
830
  getCreateTableSQL(tableName, schema) {
831
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
736
832
  const columns = Object.entries(schema).map(([name, col]) => {
833
+ const parsedColumnName = utils.parseSqlIdentifier(name, "column name");
737
834
  let type = col.type.toUpperCase();
738
835
  if (type === "TEXT") type = "TEXT";
739
836
  if (type === "TIMESTAMP") type = "TEXT";
740
837
  const nullable = col.nullable ? "" : "NOT NULL";
741
838
  const primaryKey = col.primaryKey ? "PRIMARY KEY" : "";
742
- return `${name} ${type} ${nullable} ${primaryKey}`.trim();
839
+ return `${parsedColumnName} ${type} ${nullable} ${primaryKey}`.trim();
743
840
  });
744
841
  if (tableName === storage.TABLE_WORKFLOW_SNAPSHOT) {
745
- const stmnt = `CREATE TABLE IF NOT EXISTS ${tableName} (
842
+ const stmnt = `CREATE TABLE IF NOT EXISTS ${parsedTableName} (
746
843
  ${columns.join(",\n")},
747
844
  PRIMARY KEY (workflow_name, run_id)
748
845
  )`;
749
846
  return stmnt;
750
847
  }
751
- return `CREATE TABLE IF NOT EXISTS ${tableName} (${columns.join(", ")})`;
848
+ return `CREATE TABLE IF NOT EXISTS ${parsedTableName} (${columns.join(", ")})`;
752
849
  }
753
850
  async createTable({
754
851
  tableName,
@@ -764,8 +861,9 @@ var LibSQLStore = class extends storage.MastraStorage {
764
861
  }
765
862
  }
766
863
  async clearTable({ tableName }) {
864
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
767
865
  try {
768
- await this.client.execute(`DELETE FROM ${tableName}`);
866
+ await this.client.execute(`DELETE FROM ${parsedTableName}`);
769
867
  } catch (e) {
770
868
  if (e instanceof Error) {
771
869
  this.logger.error(e.message);
@@ -773,7 +871,8 @@ var LibSQLStore = class extends storage.MastraStorage {
773
871
  }
774
872
  }
775
873
  prepareStatement({ tableName, record }) {
776
- const columns = Object.keys(record);
874
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
875
+ const columns = Object.keys(record).map((col) => utils.parseSqlIdentifier(col, "column name"));
777
876
  const values = Object.values(record).map((v) => {
778
877
  if (typeof v === `undefined`) {
779
878
  return null;
@@ -785,7 +884,7 @@ var LibSQLStore = class extends storage.MastraStorage {
785
884
  });
786
885
  const placeholders = values.map(() => "?").join(", ");
787
886
  return {
788
- sql: `INSERT OR REPLACE INTO ${tableName} (${columns.join(", ")}) VALUES (${placeholders})`,
887
+ sql: `INSERT OR REPLACE INTO ${parsedTableName} (${columns.join(", ")}) VALUES (${placeholders})`,
789
888
  args: values
790
889
  };
791
890
  }
@@ -813,10 +912,12 @@ var LibSQLStore = class extends storage.MastraStorage {
813
912
  }
814
913
  }
815
914
  async load({ tableName, keys }) {
816
- const conditions = Object.entries(keys).map(([key]) => `${key} = ?`).join(" AND ");
915
+ const parsedTableName = utils.parseSqlIdentifier(tableName, "table name");
916
+ const parsedKeys = Object.keys(keys).map((key) => utils.parseSqlIdentifier(key, "column name"));
917
+ const conditions = parsedKeys.map((key) => `${key} = ?`).join(" AND ");
817
918
  const values = Object.values(keys);
818
919
  const result = await this.client.execute({
819
- sql: `SELECT * FROM ${tableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
920
+ sql: `SELECT * FROM ${parsedTableName} WHERE ${conditions} ORDER BY createdAt DESC LIMIT 1`,
820
921
  args: values
821
922
  });
822
923
  if (!result.rows || result.rows.length === 0) {