@mastra/libsql 1.27.1-alpha.0 → 1.27.1

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
@@ -500,7 +500,7 @@ const createNumericOperator = (symbol) => {
500
500
  sql: `CAST(json_extract(metadata, ${jsonPath}) AS NUMERIC) ${symbol} ?`,
501
501
  needsValue: true
502
502
  };
503
- else return {
503
+ return {
504
504
  sql: `CAST(json_extract(metadata, ${jsonPath}) AS TEXT) ${symbol} ?`,
505
505
  needsValue: true
506
506
  };
@@ -527,7 +527,8 @@ function buildElemMatchConditions(value) {
527
527
  sql: sql.replace(pattern, `json_extract(elem.value, '$.${jsonPath}')`),
528
528
  values
529
529
  };
530
- } else return {
530
+ }
531
+ return {
531
532
  sql: `json_extract(elem.value, '$.${parseJsonPathKey(field)}') = ?`,
532
533
  values: [fieldValue]
533
534
  };
@@ -2058,6 +2059,41 @@ var LibSQLDB = class extends MastraBase {
2058
2059
  });
2059
2060
  }
2060
2061
  /**
2062
+ * Updates multiple records in one write batch, with the same retry behavior as
2063
+ * {@link batchUpdate}. Returns, in input order, whether each update changed a row,
2064
+ * so callers can put extra conditions in `keys` (for example `{ endedAt: null }`).
2065
+ */
2066
+ async batchUpdateReportingChanges({ tableName, updates }) {
2067
+ return this.executeWriteOperationWithRetry(async () => {
2068
+ const changed = updates.map(() => false);
2069
+ const statements = [];
2070
+ const statementIndexes = [];
2071
+ for (const [index, { keys, data }] of updates.entries()) {
2072
+ const filteredData = await this.filterRecordToKnownColumns(tableName, data);
2073
+ if (Object.keys(filteredData).length === 0) continue;
2074
+ statements.push(prepareUpdateStatement({
2075
+ tableName,
2076
+ updates: filteredData,
2077
+ keys
2078
+ }));
2079
+ statementIndexes.push(index);
2080
+ }
2081
+ if (statements.length === 0) return changed;
2082
+ (await withClientWriteLock(this.client, () => this.client.batch(statements, "write"))).forEach((result, i) => {
2083
+ const index = statementIndexes[i];
2084
+ if (index !== void 0) changed[index] = result.rowsAffected > 0;
2085
+ });
2086
+ return changed;
2087
+ }, `batch update in table ${tableName}`).catch((error) => {
2088
+ throw new MastraError({
2089
+ id: createStorageErrorId("LIBSQL", "BATCH_UPDATE_REPORTING_CHANGES", "FAILED"),
2090
+ domain: ErrorDomain.STORAGE,
2091
+ category: ErrorCategory.THIRD_PARTY,
2092
+ details: { tableName }
2093
+ }, error);
2094
+ });
2095
+ }
2096
+ /**
2061
2097
  * Internal batch delete implementation without retry logic.
2062
2098
  * Each record can be deleted based on single or composite keys.
2063
2099
  */
@@ -2190,7 +2226,7 @@ var LibSQLDB = class extends MastraBase {
2190
2226
  * @returns The total count of matching records
2191
2227
  */
2192
2228
  async selectTotalCount({ tableName, whereClause }) {
2193
- const statement = `SELECT COUNT(*) as count FROM ${parseSqlIdentifier(tableName, "table name")} ${whereClause ? `${whereClause.sql}` : ""}`;
2229
+ const statement = `SELECT COUNT(*) as count FROM ${parseSqlIdentifier(tableName, "table name")} ${whereClause ? whereClause.sql : ""}`;
2194
2230
  const result = await this.client.execute({
2195
2231
  sql: statement,
2196
2232
  args: whereClause?.args ?? []
@@ -12562,24 +12598,27 @@ var ObservabilityLibSQL = class ObservabilityLibSQL extends ObservabilityStorage
12562
12598
  }, error);
12563
12599
  }
12564
12600
  }
12565
- async batchUpdateSpans(args) {
12601
+ #toSpanUpdates(records) {
12566
12602
  const now = (/* @__PURE__ */ new Date()).toISOString();
12603
+ return records.map((record) => {
12604
+ const data = { ...record.updates };
12605
+ if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
12606
+ if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
12607
+ data.updatedAt = now;
12608
+ return {
12609
+ keys: {
12610
+ spanId: record.spanId,
12611
+ traceId: record.traceId
12612
+ },
12613
+ data
12614
+ };
12615
+ });
12616
+ }
12617
+ async batchUpdateSpans(args) {
12567
12618
  try {
12568
12619
  return this.#db.batchUpdate({
12569
12620
  tableName: TABLE_SPANS,
12570
- updates: args.records.map((record) => {
12571
- const data = { ...record.updates };
12572
- if (data.endedAt instanceof Date) data.endedAt = data.endedAt.toISOString();
12573
- if (data.startedAt instanceof Date) data.startedAt = data.startedAt.toISOString();
12574
- data.updatedAt = now;
12575
- return {
12576
- keys: {
12577
- spanId: record.spanId,
12578
- traceId: record.traceId
12579
- },
12580
- data
12581
- };
12582
- })
12621
+ updates: this.#toSpanUpdates(args.records)
12583
12622
  });
12584
12623
  } catch (error) {
12585
12624
  throw new MastraError({
@@ -12589,6 +12628,30 @@ var ObservabilityLibSQL = class ObservabilityLibSQL extends ObservabilityStorage
12589
12628
  }, error);
12590
12629
  }
12591
12630
  }
12631
+ async batchUpdateUnendedSpans(args) {
12632
+ try {
12633
+ const changed = await this.#db.batchUpdateReportingChanges({
12634
+ tableName: TABLE_SPANS,
12635
+ updates: this.#toSpanUpdates(args.records).map((update) => ({
12636
+ ...update,
12637
+ keys: {
12638
+ ...update.keys,
12639
+ endedAt: null
12640
+ }
12641
+ }))
12642
+ });
12643
+ return { updated: args.records.filter((_, index) => changed[index]).map(({ traceId, spanId }) => ({
12644
+ traceId,
12645
+ spanId
12646
+ })) };
12647
+ } catch (error) {
12648
+ throw new MastraError({
12649
+ id: createStorageErrorId("LIBSQL", "BATCH_UPDATE_UNENDED_SPANS", "FAILED"),
12650
+ domain: ErrorDomain.STORAGE,
12651
+ category: ErrorCategory.USER
12652
+ }, error);
12653
+ }
12654
+ }
12592
12655
  async batchDeleteTraces(args) {
12593
12656
  this.assertUnscopedBatchDeleteTraces(args);
12594
12657
  try {
@@ -15277,6 +15340,9 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends WorkflowsStorage {
15277
15340
  supportsConcurrentUpdates() {
15278
15341
  return true;
15279
15342
  }
15343
+ supportsGuardedSnapshotWrites() {
15344
+ return true;
15345
+ }
15280
15346
  parseWorkflowRun(row) {
15281
15347
  let parsedSnapshot = row.snapshot;
15282
15348
  if (typeof parsedSnapshot === "string") try {
@@ -15550,7 +15616,7 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends WorkflowsStorage {
15550
15616
  }
15551
15617
  }), "updateWorkflowState");
15552
15618
  }
15553
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence }) {
15619
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, createdAt, updatedAt, fence, expectedStatus }) {
15554
15620
  const now = /* @__PURE__ */ new Date();
15555
15621
  const createdAtValue = (createdAt ?? now).toISOString();
15556
15622
  const updatedAtValue = (updatedAt ?? now).toISOString();
@@ -15558,21 +15624,30 @@ var WorkflowsLibSQL = class WorkflowsLibSQL extends WorkflowsStorage {
15558
15624
  workflowName,
15559
15625
  runId
15560
15626
  });
15627
+ const expected = expectedStatus === void 0 ? [] : Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus];
15628
+ const guard = expectedStatus === void 0 ? "" : expected.length ? ` WHERE json_extract(${TABLE_WORKFLOW_SNAPSHOT}.snapshot, '$.status') IN (${expected.map(() => "?").join(", ")})` : " WHERE 0";
15561
15629
  const check = this.#runFenceCheck(fence, runId, "persistWorkflowSnapshot");
15562
- await this.executeWithRetry(() => withRunFence(this.#client, check, (writer) => writer.execute({
15630
+ return (await this.executeWithRetry(() => withRunFence(this.#client, check, (writer) => writer.execute({
15563
15631
  sql: `INSERT INTO ${TABLE_WORKFLOW_SNAPSHOT} (workflow_name, run_id, resourceId, snapshot, createdAt, updatedAt)
15564
15632
  VALUES (?, ?, ?, jsonb(?), ?, ?)
15565
15633
  ON CONFLICT(workflow_name, run_id)
15566
- DO UPDATE SET resourceId = COALESCE(excluded.resourceId, ${TABLE_WORKFLOW_SNAPSHOT}.resourceId), snapshot = excluded.snapshot, updatedAt = excluded.updatedAt`,
15634
+ DO UPDATE SET resourceId = COALESCE(excluded.resourceId, ${TABLE_WORKFLOW_SNAPSHOT}.resourceId), snapshot = excluded.snapshot, updatedAt = excluded.updatedAt${guard}`,
15567
15635
  args: [
15568
15636
  workflowName,
15569
15637
  runId,
15570
15638
  resourceId ?? null,
15571
15639
  safeStringify(snapshot),
15572
15640
  createdAtValue,
15573
- updatedAtValue
15641
+ updatedAtValue,
15642
+ ...expected
15574
15643
  ]
15575
- })), "persistWorkflowSnapshot");
15644
+ })), "persistWorkflowSnapshot")).rowsAffected > 0;
15645
+ }
15646
+ async loadWorkflowRunStatus({ workflowName, runId }) {
15647
+ return (await this.#client.execute({
15648
+ sql: `SELECT json_extract(snapshot, '$.status') AS status FROM ${TABLE_WORKFLOW_SNAPSHOT} WHERE workflow_name = ? AND run_id = ?`,
15649
+ args: [workflowName, runId]
15650
+ })).rows?.[0]?.status ?? null;
15576
15651
  }
15577
15652
  async loadWorkflowSnapshot({ workflowName, runId }) {
15578
15653
  this.logger.debug("Loading workflow snapshot", {