@mastra/mssql 1.10.5 → 1.10.6-alpha.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
@@ -5,7 +5,7 @@ import { parseSqlIdentifier } from "@mastra/core/utils";
5
5
  import { MastraBase } from "@mastra/core/base";
6
6
  import { MessageList } from "@mastra/core/agent";
7
7
  import { saveScorePayloadSchema } from "@mastra/core/evals";
8
- //#region src/storage/db/utils.ts
8
+ // src/storage/db/utils.ts
9
9
  function getSchemaName$1(schema) {
10
10
  return schema ? `[${parseSqlIdentifier(schema, "schema name")}]` : void 0;
11
11
  }
@@ -14,8 +14,7 @@ function getTableName$1({ indexName, schemaName }) {
14
14
  const quotedSchemaName = schemaName;
15
15
  return quotedSchemaName ? `${quotedSchemaName}.${quotedIndexName}` : quotedIndexName;
16
16
  }
17
- //#endregion
18
- //#region src/storage/db/index.ts
17
+ // src/storage/db/index.ts
19
18
  /**
20
19
  * Resolves MssqlDomainConfig to pool and schema.
21
20
  * Domain classes create their own MssqlDB instance from the returned pool.
@@ -1033,8 +1032,7 @@ MIGRATION REQUIRED: Duplicate spans detected in ${duplicateInfo.tableName}\n====
1033
1032
  }
1034
1033
  }
1035
1034
  };
1036
- //#endregion
1037
- //#region src/storage/domains/utils.ts
1035
+ // src/storage/domains/utils.ts
1038
1036
  function getSchemaName(schema) {
1039
1037
  return schema ? `[${parseSqlIdentifier(schema, "schema name")}]` : void 0;
1040
1038
  }
@@ -1156,8 +1154,7 @@ function transformFromSqlRow({ tableName, sqlRow }) {
1156
1154
  });
1157
1155
  return result;
1158
1156
  }
1159
- //#endregion
1160
- //#region src/storage/domains/agents/index.ts
1157
+ // src/storage/domains/agents/index.ts
1161
1158
  var AgentsMSSQL = class AgentsMSSQL extends AgentsStorage {
1162
1159
  pool;
1163
1160
  schema;
@@ -1745,8 +1742,7 @@ var AgentsMSSQL = class AgentsMSSQL extends AgentsStorage {
1745
1742
  }
1746
1743
  }
1747
1744
  };
1748
- //#endregion
1749
- //#region src/storage/domains/background-tasks/index.ts
1745
+ // src/storage/domains/background-tasks/index.ts
1750
1746
  function serializeJson(v) {
1751
1747
  if (typeof v === "object" && v != null) return JSON.stringify(v);
1752
1748
  return v ?? null;
@@ -2107,8 +2103,7 @@ var BackgroundTasksMSSQL = class BackgroundTasksMSSQL extends BackgroundTasksSto
2107
2103
  return Number(result.recordset[0]?.count ?? 0);
2108
2104
  }
2109
2105
  };
2110
- //#endregion
2111
- //#region src/storage/db/run-fencing.ts
2106
+ // src/storage/db/run-fencing.ts
2112
2107
  /**
2113
2108
  * Epoch milliseconds on the database clock. SQL Server evaluates
2114
2109
  * `SYSUTCDATETIME()` once when a statement starts, so a statement that waits
@@ -2167,16 +2162,23 @@ function withRunFence(pool, claimsTable, fence, operation, write) {
2167
2162
  * duplicate key if they don't. On retry the row exists and the claim
2168
2163
  * serializes behind its row lock.
2169
2164
  */
2170
- async function claimTransaction(pool, fn) {
2165
+ function claimTransaction(pool, fn) {
2166
+ return retryWriteRace(() => inTransaction(pool, fn));
2167
+ }
2168
+ /**
2169
+ * Runs `write` again when it lost a race to insert the same row: a deadlock
2170
+ * victim or a duplicate key. `write` must be a whole statement or
2171
+ * transaction, since a deadlock victim's transaction has already ended.
2172
+ */
2173
+ async function retryWriteRace(write) {
2171
2174
  for (let attempt = 1;; attempt++) try {
2172
- return await inTransaction(pool, fn);
2175
+ return await write();
2173
2176
  } catch (error) {
2174
2177
  const number = error.number;
2175
2178
  if (attempt >= MAX_CLAIM_ATTEMPTS || !(number === ERR_DEADLOCK_VICTIM || number === ERR_PRIMARY_KEY || number === ERR_UNIQUE_INDEX)) throw error;
2176
2179
  }
2177
2180
  }
2178
- //#endregion
2179
- //#region src/storage/domains/memory/index.ts
2181
+ // src/storage/domains/memory/index.ts
2180
2182
  function bindMssqlMetadataParams(request, params) {
2181
2183
  for (const [paramName, paramValue] of Object.entries(params)) request.input(paramName, paramValue);
2182
2184
  }
@@ -3336,8 +3338,7 @@ var MemoryMSSQL = class MemoryMSSQL extends MemoryStorage {
3336
3338
  }
3337
3339
  }
3338
3340
  };
3339
- //#endregion
3340
- //#region src/storage/domains/observability/index.ts
3341
+ // src/storage/domains/observability/index.ts
3341
3342
  var ObservabilityMSSQL = class ObservabilityMSSQL extends ObservabilityStorage {
3342
3343
  static retentionTables = { spans: {
3343
3344
  table: TABLE_SPANS,
@@ -3978,8 +3979,7 @@ var ObservabilityMSSQL = class ObservabilityMSSQL extends ObservabilityStorage {
3978
3979
  }
3979
3980
  }
3980
3981
  };
3981
- //#endregion
3982
- //#region src/storage/domains/scores/index.ts
3982
+ // src/storage/domains/scores/index.ts
3983
3983
  /**
3984
3984
  * MSSQL-specific score row transformation.
3985
3985
  * Converts timestamp strings to Date objects.
@@ -4395,8 +4395,7 @@ var ScoresMSSQL = class ScoresMSSQL extends ScoresStorage {
4395
4395
  }
4396
4396
  }
4397
4397
  };
4398
- //#endregion
4399
- //#region src/storage/domains/workflow-definitions/index.ts
4398
+ // src/storage/domains/workflow-definitions/index.ts
4400
4399
  function parseJson(value, column, rowId) {
4401
4400
  if (value == null) return null;
4402
4401
  if (typeof value === "string") try {
@@ -4599,8 +4598,7 @@ var WorkflowDefinitionsMSSQL = class WorkflowDefinitionsMSSQL extends WorkflowDe
4599
4598
  await request.query(`DELETE FROM ${tableName} WHERE [id] = @id`);
4600
4599
  }
4601
4600
  };
4602
- //#endregion
4603
- //#region src/storage/domains/workflows/index.ts
4601
+ // src/storage/domains/workflows/index.ts
4604
4602
  const RUN_OWNER_COLUMNS = `[generation], [ownerId], [leaseExpiresAt], ${DB_NOW_MS} AS [nowMs]`;
4605
4603
  function toRunOwnershipRecord(runId, row) {
4606
4604
  const leaseExpiresAt = row.leaseExpiresAt === null ? null : Number(row.leaseExpiresAt);
@@ -4646,6 +4644,9 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4646
4644
  this.skipDefaultIndexes = skipDefaultIndexes;
4647
4645
  this.indexes = indexes?.filter((idx) => WorkflowsMSSQL.MANAGED_TABLES.includes(idx.table));
4648
4646
  }
4647
+ supportsGuardedSnapshotWrites() {
4648
+ return true;
4649
+ }
4649
4650
  supportsConcurrentUpdates() {
4650
4651
  return true;
4651
4652
  }
@@ -4955,14 +4956,16 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4955
4956
  }, error);
4956
4957
  }
4957
4958
  }
4958
- async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, fence }) {
4959
+ async persistWorkflowSnapshot({ workflowName, runId, resourceId, snapshot, fence, expectedStatus }) {
4959
4960
  const table = getTableName({
4960
4961
  indexName: TABLE_WORKFLOW_SNAPSHOT,
4961
4962
  schemaName: getSchemaName(this.schema)
4962
4963
  });
4963
4964
  const now = (/* @__PURE__ */ new Date()).toISOString();
4965
+ const expectedStatuses = expectedStatus === void 0 ? void 0 : Array.isArray(expectedStatus) ? expectedStatus : [expectedStatus];
4966
+ const guard = expectedStatuses === void 0 ? "" : expectedStatuses.length === 0 ? " AND 1 = 0" : ` AND JSON_VALUE(target.[snapshot], '$.status') IN (${expectedStatuses.map((_, i) => `@expectedStatus${i}`).join(", ")})`;
4964
4967
  try {
4965
- await withRunFence(this.pool, this.#runOwnersTable, resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
4968
+ return await retryWriteRace(() => withRunFence(this.pool, this.#runOwnersTable, resolveRunFence(this, fence, runId), "persistWorkflowSnapshot", async (q) => {
4966
4969
  const request = q.request();
4967
4970
  request.input("workflow_name", workflowName);
4968
4971
  request.input("run_id", runId);
@@ -4970,17 +4973,18 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4970
4973
  request.input("snapshot", JSON.stringify(snapshot));
4971
4974
  request.input("createdAt", sql.DateTime2, new Date(now));
4972
4975
  request.input("updatedAt", sql.DateTime2, new Date(now));
4976
+ expectedStatuses?.forEach((status, i) => request.input(`expectedStatus${i}`, status));
4973
4977
  const mergeSql = `MERGE INTO ${table} AS target
4974
4978
  USING (SELECT @workflow_name AS workflow_name, @run_id AS run_id) AS src
4975
4979
  ON target.workflow_name = src.workflow_name AND target.run_id = src.run_id
4976
- WHEN MATCHED THEN UPDATE SET
4980
+ WHEN MATCHED${guard} THEN UPDATE SET
4977
4981
  resourceId = @resourceId,
4978
4982
  snapshot = @snapshot,
4979
4983
  [updatedAt] = @updatedAt
4980
4984
  WHEN NOT MATCHED THEN INSERT (workflow_name, run_id, resourceId, snapshot, [createdAt], [updatedAt])
4981
4985
  VALUES (@workflow_name, @run_id, @resourceId, @snapshot, @createdAt, @updatedAt);`;
4982
- await request.query(mergeSql);
4983
- });
4986
+ return ((await request.query(mergeSql)).rowsAffected[0] ?? 0) > 0;
4987
+ }));
4984
4988
  } catch (error) {
4985
4989
  if (isRunFenceConflictError(error)) throw error;
4986
4990
  throw new MastraError({
@@ -4994,6 +4998,27 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
4994
4998
  }, error);
4995
4999
  }
4996
5000
  }
5001
+ async loadWorkflowRunStatus({ workflowName, runId }) {
5002
+ try {
5003
+ const request = this.pool.request();
5004
+ request.input("workflow_name", workflowName);
5005
+ request.input("run_id", runId);
5006
+ return (await request.query(`SELECT TOP 1 JSON_VALUE([snapshot], '$.status') AS status FROM ${getTableName({
5007
+ indexName: TABLE_WORKFLOW_SNAPSHOT,
5008
+ schemaName: getSchemaName(this.schema)
5009
+ })} WHERE workflow_name = @workflow_name AND run_id = @run_id`)).recordset?.[0]?.status ?? null;
5010
+ } catch (error) {
5011
+ throw new MastraError({
5012
+ id: createStorageErrorId("MSSQL", "LOAD_WORKFLOW_RUN_STATUS", "FAILED"),
5013
+ domain: ErrorDomain.STORAGE,
5014
+ category: ErrorCategory.THIRD_PARTY,
5015
+ details: {
5016
+ workflowName,
5017
+ runId
5018
+ }
5019
+ }, error);
5020
+ }
5021
+ }
4997
5022
  async loadWorkflowSnapshot({ workflowName, runId }) {
4998
5023
  try {
4999
5024
  const result = await this.db.load({
@@ -5137,8 +5162,7 @@ var WorkflowsMSSQL = class WorkflowsMSSQL extends WorkflowsStorage {
5137
5162
  }
5138
5163
  }
5139
5164
  };
5140
- //#endregion
5141
- //#region src/storage/index.ts
5165
+ // src/storage/index.ts
5142
5166
  /**
5143
5167
  * Type guard for pre-configured pool config
5144
5168
  */
@@ -5251,12 +5275,8 @@ var MSSQLStore = class extends MastraCompositeStore {
5251
5275
  }
5252
5276
  }
5253
5277
  async _performInitializationAndStore() {
5254
- try {
5255
- await this.pool.connect();
5256
- return true;
5257
- } catch (err) {
5258
- throw err;
5259
- }
5278
+ await this.pool.connect();
5279
+ return true;
5260
5280
  }
5261
5281
  /**
5262
5282
  * Closes the MSSQL connection pool.
@@ -5267,7 +5287,6 @@ var MSSQLStore = class extends MastraCompositeStore {
5267
5287
  await this.pool.close();
5268
5288
  }
5269
5289
  };
5270
- //#endregion
5271
5290
  export { AgentsMSSQL, BackgroundTasksMSSQL, MSSQLStore, MemoryMSSQL, ObservabilityMSSQL, ScoresMSSQL, WorkflowDefinitionsMSSQL, WorkflowsMSSQL };
5272
5291
 
5273
5292
  //# sourceMappingURL=index.js.map