@mastra/spanner 1.4.0 → 1.5.0-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/CHANGELOG.md CHANGED
@@ -1,5 +1,69 @@
1
1
  # @mastra/spanner
2
2
 
3
+ ## 1.5.0-alpha.1
4
+
5
+ ### Minor Changes
6
+
7
+ - Stored workflow definitions now persist across restarts on every major database backend. ([#20471](https://github.com/mastra-ai/mastra/pull/20471))
8
+
9
+ Implement the `workflowDefinitions` storage domain for libsql, pg, mysql, mssql, mongodb, and spanner. Previously the stored-workflow persistence path (`POST /stored/workflows`, `Mastra.addStoredWorkflow`) only worked against `@mastra/core`'s in-memory store. Persistent adapters returned `undefined` from `storage.getStore('workflowDefinitions')` and threw when the HTTP handler tried to read/write a workflow.
10
+
11
+ ```ts
12
+ const workflowDefinitions = await storage.getStore('workflowDefinitions');
13
+ if (!workflowDefinitions) {
14
+ throw new Error('This storage adapter does not support the workflowDefinitions domain');
15
+ }
16
+
17
+ await workflowDefinitions.upsert({
18
+ id: 'greeting-workflow',
19
+ inputSchema: { type: 'object', properties: { name: { type: 'string' } }, required: ['name'] },
20
+ outputSchema: { type: 'object', properties: { text: { type: 'string' } }, required: ['text'] },
21
+ graph: [{ type: 'agent', id: 'greet', agentId: 'greeter-agent' }],
22
+ });
23
+
24
+ const { definitions, total } = await workflowDefinitions.list({ status: 'active' });
25
+ const definition = await workflowDefinitions.get('greeting-workflow');
26
+ await workflowDefinitions.delete('greeting-workflow');
27
+ ```
28
+
29
+ Each adapter now ships a `WorkflowDefinitions*` domain that:
30
+
31
+ - Creates the shared `mastra_workflow_definitions` table (or Mongo collection) from `WORKFLOW_DEFINITIONS_SCHEMA` during `init()`, plus a default index on `status`.
32
+ - Implements `upsert` / `get` / `list` / `delete` matching `WorkflowDefinitionsStorage` semantics (`list` supports `status` and `authorId` filters and orders by `updatedAt` desc). Partial upserts preserve unspecified fields, including `authorId` updates and `createdAt` / `updatedAt` semantics.
33
+ - Handles concurrent first-writes race-safely: if two callers upsert the same new id simultaneously, the losing insert detects the duplicate key, re-reads the row, and applies the partial-update path instead of failing.
34
+ - Round-trips the JSON columns (`inputSchema`, `outputSchema`, `stateSchema`, `requestContextSchema`, `metadata`, `graph`) through each adapter's JSON handling, so declarative workflow graphs rehydrate identically no matter which backend they were stored in. Malformed persisted JSON surfaces as an actionable error naming the row and column instead of hydrating raw strings.
35
+
36
+ Exported class names by adapter: `WorkflowDefinitionsLibSQL`, `WorkflowDefinitionsPG`, `WorkflowDefinitionsMySQL`, `WorkflowDefinitionsMSSQL`, `MongoDBWorkflowDefinitionsStore`, `WorkflowDefinitionsSpanner`. The composite stores (`LibSQLStore`, `PostgresStore`, `MySQLStore`, `MSSQLStore`, `MongoDBStore`, `SpannerStore`) auto-wire the new domain, so callers do not need to construct it manually — `storage.getStore('workflowDefinitions')` now returns a live handle.
37
+
38
+ The pg adapter reads `createdAt` / `updatedAt` from the auto-added `createdAtZ` / `updatedAtZ` `timestamptz` companion columns to avoid the naive-timestamp / local-TZ drift that a plain `TIMESTAMP` read exhibits under node-pg.
39
+
40
+ `@mastra/clickhouse` and `@mastra/cloudflare` register the new `mastra_workflow_definitions` table in their table/type maps so shared table constants stay exhaustive (no workflow-definitions domain implementation yet).
41
+
42
+ ### Patch Changes
43
+
44
+ - Updated dependencies [[`4844167`](https://github.com/mastra-ai/mastra/commit/4844167cff2d5ec5004e94edd34970833040fa3f), [`5faf93f`](https://github.com/mastra-ai/mastra/commit/5faf93f03e19daea394b9e2a923f2e4f833407f2), [`80ad891`](https://github.com/mastra-ai/mastra/commit/80ad891f8cd10379aa5b5af7510c763783b2ab56), [`a1cb98d`](https://github.com/mastra-ai/mastra/commit/a1cb98d11990b560b98482292a1f34aa1a2d9092), [`598ad82`](https://github.com/mastra-ai/mastra/commit/598ad82d41c41389a686338a1d0e50b7400e1938), [`1fd6aad`](https://github.com/mastra-ai/mastra/commit/1fd6aad1ea4a9d32f65efa832307c35e981a4c0a)]:
45
+ - @mastra/core@1.56.0-alpha.4
46
+
47
+ ## 1.5.0-alpha.0
48
+
49
+ ### Minor Changes
50
+
51
+ - Added persistence for dataset item undeclared tool policies. ([#19643](https://github.com/mastra-ai/mastra/pull/19643))
52
+
53
+ ```typescript
54
+ await dataset.addItem({
55
+ input: 'What is the weather?',
56
+ unmockedToolPolicy: 'deny',
57
+ });
58
+ ```
59
+
60
+ ### Patch Changes
61
+
62
+ - Added a comment column to experiment results so review comments persist. The column is added automatically and non-destructively on startup for existing databases (https://github.com/mastra-ai/mastra/issues/19857). ([#19865](https://github.com/mastra-ai/mastra/pull/19865))
63
+
64
+ - Updated dependencies [[`c5e56ff`](https://github.com/mastra-ai/mastra/commit/c5e56ff3bcabdf062708f2d48744fec304df6792), [`4e35a56`](https://github.com/mastra-ai/mastra/commit/4e35a56cdf8d74a5ff6d5eda01f2c1deaf6cc7be)]:
65
+ - @mastra/core@1.56.0-alpha.1
66
+
3
67
  ## 1.4.0
4
68
 
5
69
  ### Minor Changes
@@ -3,7 +3,7 @@ name: mastra-spanner
3
3
  description: Documentation for @mastra/spanner. Use when working with @mastra/spanner APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/spanner"
6
- version: "1.4.0"
6
+ version: "1.5.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.4.0",
2
+ "version": "1.5.0-alpha.1",
3
3
  "package": "@mastra/spanner",
4
4
  "exports": {},
5
5
  "modules": {}
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Google Cloud Spanner storage
4
4
 
5
- The Google Cloud Spanner storage implementation provides a horizontally scalable, strongly consistent storage backend for Mastra. It targets the GoogleSQL dialect of Cloud Spanner.
5
+ The Google Cloud Spanner storage implementation provides a horizontally high-capacity, strongly consistent storage backend for Mastra. It targets the GoogleSQL dialect of Cloud Spanner.
6
6
 
7
7
  ## Installation
8
8
 
@@ -133,14 +133,14 @@ The storage adapter creates the following tables, all using the GoogleSQL dialec
133
133
 
134
134
  Tables are created with `STRING(MAX)` for text and JSON payloads, `INT64`, `FLOAT64`, `BOOL`, and `TIMESTAMP`.
135
135
 
136
- Two tables also carry Spanner-specific `STORED` generated columns that the adapter populates from JSON payloads so common filters can use a regular secondary index instead of a `JSON_VALUE` scan:
136
+ The following tables carry Spanner-specific `STORED` generated columns that the adapter populates from JSON payloads so common filters can use a regular secondary index instead of a `JSON_VALUE` scan:
137
137
 
138
- - `mastra_workflow_snapshot.snapshotStatus` extracts `$.status` from `snapshot`; backs `listWorkflowRuns({ status })`.
139
- - `mastra_schedules.target_workflow_id` extracts `$.workflowId` from `target`; backs `listSchedules({ workflowId })`.
138
+ - `mastra_workflow_snapshot.snapshotStatus`: Extracts `$.status` from `snapshot`. Backs `listWorkflowRuns({ status })`.
139
+ - `mastra_schedules.target_workflow_id`: Extracts `$.workflowId` from `target`. Backs `listSchedules({ workflowId })`.
140
140
 
141
141
  Both are added via `ALTER TABLE ... ADD COLUMN IF NOT EXISTS` during `init()` and skipped under `initMode: 'validate'` (where the schema is owned externally). When the column is absent, the adapter falls back to a `JSON_VALUE` filter at runtime.
142
142
 
143
- The adapter doesn't create or use named schemas; use a dedicated database for isolation.
143
+ The adapter doesn't create or use schemas. Use a dedicated database for isolation.
144
144
 
145
145
  ### Initialization
146
146
 
@@ -184,11 +184,11 @@ const thread = await memory?.getThreadById({ threadId: '...' })
184
184
  A few behaviors differ from other relational adapters:
185
185
 
186
186
  - Upserts use `INSERT OR UPDATE`. Spanner doesn't provide a `RETURNING` clause for upserts, so callers needing the post-write state must read it back.
187
- - No `TRUNCATE` exists; `dangerouslyClearAll()` issues `DELETE WHERE TRUE`.
187
+ - No `TRUNCATE` exists. `dangerouslyClearAll()` issues `DELETE WHERE TRUE`.
188
188
  - Identifiers are quoted with backticks.
189
189
  - DDL is applied through `database.updateSchema(...)`, which is asynchronous (long-running operation).
190
190
  - `NULLS FIRST/LAST` isn't supported. Ordering with NULL handling is emulated through an `IS NULL` ordering key.
191
- - JSON containment isn't supported natively. `listTraces` `metadata` and `scope` filters compile to per-key `JSON_VALUE(...) = @v` equality checks, and `tags` filters compile to `EXISTS` over `JSON_QUERY_ARRAY(...)`. This differs from Postgres' `@>` containment operator (which can match nested structure in a single index scan) most one-shot lookups still work but deeply nested structural matches aren't expressible.
191
+ - JSON containment isn't supported natively. `listTraces` `metadata` and `scope` filters compile to per-key `JSON_VALUE(...) = @v` equality checks, and `tags` filters compile to `EXISTS` over `JSON_QUERY_ARRAY(...)`. This differs from Postgres' `@>` containment operator (which can match nested structure in a single index scan): most one-shot lookups still work but deeply nested structural matches aren't expressible.
192
192
 
193
193
  ### Direct database access
194
194
 
@@ -217,4 +217,4 @@ gcloud spanner instances create test-instance --config=emulator-config --nodes=1
217
217
  gcloud spanner databases create test-db --instance=test-instance
218
218
  ```
219
219
 
220
- Then connect with the same env var set in your Node.js process; the `@google-cloud/spanner` client detects the emulator automatically.
220
+ Then connect with the same env var set in your Node.js process. The `@google-cloud/spanner` client detects the emulator automatically.
package/dist/index.cjs CHANGED
@@ -2788,6 +2788,7 @@ function rowToItem(row) {
2788
2788
  groundTruth: t.groundTruth ?? void 0,
2789
2789
  expectedTrajectory: t.expectedTrajectory ?? void 0,
2790
2790
  toolMocks: t.toolMocks ?? void 0,
2791
+ unmockedToolPolicy: t.unmockedToolPolicy ?? void 0,
2791
2792
  requestContext: t.requestContext ?? void 0,
2792
2793
  metadata: t.metadata ?? void 0,
2793
2794
  source: t.source ?? void 0,
@@ -2885,7 +2886,8 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
2885
2886
  ifNotExists: [
2886
2887
  "organizationId",
2887
2888
  "projectId",
2888
- "externalId"
2889
+ "externalId",
2890
+ "unmockedToolPolicy"
2889
2891
  ]
2890
2892
  });
2891
2893
  await this.createDefaultIndexes();
@@ -3350,6 +3352,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3350
3352
  groundTruth: args.groundTruth ?? null,
3351
3353
  expectedTrajectory: args.expectedTrajectory ?? null,
3352
3354
  toolMocks: args.toolMocks ?? null,
3355
+ unmockedToolPolicy: args.unmockedToolPolicy ?? null,
3353
3356
  requestContext: args.requestContext ?? null,
3354
3357
  metadata: args.metadata ?? null,
3355
3358
  source: args.source ?? null,
@@ -3370,6 +3373,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3370
3373
  groundTruth: args.groundTruth,
3371
3374
  expectedTrajectory: args.expectedTrajectory,
3372
3375
  toolMocks: args.toolMocks,
3376
+ unmockedToolPolicy: args.unmockedToolPolicy,
3373
3377
  requestContext: args.requestContext,
3374
3378
  metadata: args.metadata,
3375
3379
  source: args.source,
@@ -3419,6 +3423,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3419
3423
  groundTruth: args.groundTruth !== void 0 ? args.groundTruth : existing.groundTruth,
3420
3424
  expectedTrajectory: args.expectedTrajectory !== void 0 ? args.expectedTrajectory : existing.expectedTrajectory,
3421
3425
  toolMocks: args.toolMocks !== void 0 ? args.toolMocks : existing.toolMocks,
3426
+ unmockedToolPolicy: args.unmockedToolPolicy !== void 0 ? args.unmockedToolPolicy : existing.unmockedToolPolicy,
3422
3427
  requestContext: args.requestContext !== void 0 ? args.requestContext : existing.requestContext,
3423
3428
  metadata: args.metadata !== void 0 ? args.metadata : existing.metadata,
3424
3429
  source: args.source !== void 0 ? args.source : existing.source
@@ -3444,6 +3449,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3444
3449
  groundTruth: merged.groundTruth ?? null,
3445
3450
  expectedTrajectory: merged.expectedTrajectory ?? null,
3446
3451
  toolMocks: merged.toolMocks ?? null,
3452
+ unmockedToolPolicy: merged.unmockedToolPolicy ?? null,
3447
3453
  requestContext: merged.requestContext ?? null,
3448
3454
  metadata: merged.metadata ?? null,
3449
3455
  source: merged.source ?? null,
@@ -3465,6 +3471,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3465
3471
  groundTruth: merged.groundTruth,
3466
3472
  expectedTrajectory: merged.expectedTrajectory,
3467
3473
  toolMocks: merged.toolMocks,
3474
+ unmockedToolPolicy: merged.unmockedToolPolicy,
3468
3475
  requestContext: merged.requestContext,
3469
3476
  metadata: merged.metadata,
3470
3477
  source: merged.source,
@@ -3523,6 +3530,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3523
3530
  groundTruth: existing.groundTruth ?? null,
3524
3531
  expectedTrajectory: existing.expectedTrajectory ?? null,
3525
3532
  toolMocks: existing.toolMocks ?? null,
3533
+ unmockedToolPolicy: existing.unmockedToolPolicy ?? null,
3526
3534
  requestContext: existing.requestContext ?? null,
3527
3535
  metadata: existing.metadata ?? null,
3528
3536
  source: existing.source ?? null,
@@ -3839,6 +3847,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3839
3847
  groundTruth: insert.item.groundTruth,
3840
3848
  expectedTrajectory: insert.item.expectedTrajectory,
3841
3849
  toolMocks: insert.item.toolMocks,
3850
+ unmockedToolPolicy: insert.item.unmockedToolPolicy,
3842
3851
  requestContext: insert.item.requestContext,
3843
3852
  metadata: insert.item.metadata,
3844
3853
  source: insert.item.source,
@@ -3907,6 +3916,7 @@ var DatasetsSpanner = class DatasetsSpanner extends _mastra_core_storage.Dataset
3907
3916
  groundTruth: existing.groundTruth ?? null,
3908
3917
  expectedTrajectory: existing.expectedTrajectory ?? null,
3909
3918
  toolMocks: existing.toolMocks ?? null,
3919
+ unmockedToolPolicy: existing.unmockedToolPolicy ?? null,
3910
3920
  requestContext: existing.requestContext ?? null,
3911
3921
  metadata: existing.metadata ?? null,
3912
3922
  source: existing.source ?? null,
@@ -3991,6 +4001,7 @@ function rowToExperimentResult(row) {
3991
4001
  traceId: t.traceId ?? null,
3992
4002
  status: t.status ?? null,
3993
4003
  tags: t.tags ?? null,
4004
+ comment: t.comment ?? null,
3994
4005
  toolMockReport: t.toolMockReport ?? null,
3995
4006
  createdAt: toDate(t.createdAt)
3996
4007
  };
@@ -4035,7 +4046,11 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4035
4046
  await this.db.alterTable({
4036
4047
  tableName: _mastra_core_storage.TABLE_EXPERIMENT_RESULTS,
4037
4048
  schema: _mastra_core_storage.TABLE_SCHEMAS[_mastra_core_storage.TABLE_EXPERIMENT_RESULTS],
4038
- ifNotExists: ["organizationId", "projectId"]
4049
+ ifNotExists: [
4050
+ "comment",
4051
+ "organizationId",
4052
+ "projectId"
4053
+ ]
4039
4054
  });
4040
4055
  await this.createDefaultIndexes();
4041
4056
  await this.createCustomIndexes();
@@ -4428,7 +4443,7 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4428
4443
  }
4429
4444
  async updateExperimentResult(input) {
4430
4445
  try {
4431
- if (input.status === void 0 && input.tags === void 0) {
4446
+ if (input.status === void 0 && input.tags === void 0 && input.comment === void 0) {
4432
4447
  const existing = await this.getExperimentResultById({ id: input.id });
4433
4448
  if (!existing || input.experimentId !== void 0 && existing.experimentId !== input.experimentId) throw new _mastra_core_error.MastraError({
4434
4449
  id: (0, _mastra_core_storage.createStorageErrorId)("SPANNER", "UPDATE_EXPERIMENT_RESULT", "NOT_FOUND"),
@@ -4452,6 +4467,11 @@ var ExperimentsSpanner = class ExperimentsSpanner extends _mastra_core_storage.E
4452
4467
  params.tags = input.tags === null ? null : JSON.stringify(input.tags);
4453
4468
  types.tags = "json";
4454
4469
  }
4470
+ if (input.comment !== void 0) {
4471
+ setClauses.push(`${quoteIdent("comment", "column name")} = @comment`);
4472
+ params.comment = input.comment;
4473
+ if (input.comment === null) types.comment = "string";
4474
+ }
4455
4475
  const whereClauses = [`${quoteIdent("id", "column name")} = @id`];
4456
4476
  if (input.experimentId !== void 0) {
4457
4477
  whereClauses.push(`${quoteIdent("experimentId", "column name")} = @experimentId`);
@@ -11481,6 +11501,169 @@ var SkillsSpanner = class SkillsSpanner extends _mastra_core_storage.SkillsStora
11481
11501
  }
11482
11502
  };
11483
11503
  //#endregion
11504
+ //#region src/storage/domains/workflow-definitions/index.ts
11505
+ function rowToDefinition(row) {
11506
+ const parsed = transformFromSpannerRow({
11507
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11508
+ row
11509
+ });
11510
+ if (parsed.inputSchema == null || parsed.outputSchema == null || parsed.graph == null) throw new Error(`Workflow definition row "${String(parsed.id)}" is missing required JSON columns.`);
11511
+ const def = {
11512
+ id: String(parsed.id),
11513
+ inputSchema: parsed.inputSchema,
11514
+ outputSchema: parsed.outputSchema,
11515
+ graph: parsed.graph,
11516
+ status: parsed.status,
11517
+ source: parsed.source,
11518
+ createdAt: parsed.createdAt instanceof Date ? parsed.createdAt : new Date(parsed.createdAt),
11519
+ updatedAt: parsed.updatedAt instanceof Date ? parsed.updatedAt : new Date(parsed.updatedAt)
11520
+ };
11521
+ if (parsed.description != null) def.description = parsed.description;
11522
+ if (parsed.metadata != null) def.metadata = parsed.metadata;
11523
+ if (parsed.stateSchema != null) def.stateSchema = parsed.stateSchema;
11524
+ if (parsed.requestContextSchema != null) def.requestContextSchema = parsed.requestContextSchema;
11525
+ if (parsed.authorId != null) def.authorId = parsed.authorId;
11526
+ return def;
11527
+ }
11528
+ var WorkflowDefinitionsSpanner = class WorkflowDefinitionsSpanner extends _mastra_core_storage.WorkflowDefinitionsStorage {
11529
+ database;
11530
+ db;
11531
+ skipDefaultIndexes;
11532
+ indexes;
11533
+ static MANAGED_TABLES = [_mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS];
11534
+ constructor(config) {
11535
+ super();
11536
+ const { database, indexes, skipDefaultIndexes, initMode } = resolveSpannerConfig(config);
11537
+ this.database = database;
11538
+ this.db = new SpannerDB({
11539
+ database,
11540
+ skipDefaultIndexes,
11541
+ initMode
11542
+ });
11543
+ this.skipDefaultIndexes = skipDefaultIndexes;
11544
+ this.indexes = indexes?.filter((idx) => WorkflowDefinitionsSpanner.MANAGED_TABLES.includes(idx.table));
11545
+ }
11546
+ async init() {
11547
+ await this.db.createTable({
11548
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11549
+ schema: _mastra_core_storage.WORKFLOW_DEFINITIONS_SCHEMA
11550
+ });
11551
+ await this.createDefaultIndexes();
11552
+ await this.createCustomIndexes();
11553
+ }
11554
+ getDefaultIndexDefinitions() {
11555
+ return [{
11556
+ name: "mastra_workflow_definitions_status_idx",
11557
+ table: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11558
+ columns: ["status"]
11559
+ }];
11560
+ }
11561
+ async createDefaultIndexes() {
11562
+ if (this.skipDefaultIndexes) return;
11563
+ await this.db.createIndexes(this.getDefaultIndexDefinitions());
11564
+ }
11565
+ async createCustomIndexes() {
11566
+ if (!this.indexes || this.indexes.length === 0) return;
11567
+ await this.db.createIndexes(this.indexes);
11568
+ }
11569
+ async dangerouslyClearAll() {
11570
+ await this.db.clearTable({ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS });
11571
+ }
11572
+ async upsert(input) {
11573
+ const now = /* @__PURE__ */ new Date();
11574
+ if (!await this.get(input.id)) {
11575
+ if (!("inputSchema" in input) || input.inputSchema === void 0) throw new Error(`Cannot create workflow definition "${input.id}": inputSchema is required.`);
11576
+ if (!("outputSchema" in input) || input.outputSchema === void 0) throw new Error(`Cannot create workflow definition "${input.id}": outputSchema is required.`);
11577
+ if (!("graph" in input) || input.graph === void 0) throw new Error(`Cannot create workflow definition "${input.id}": graph is required.`);
11578
+ const record = {
11579
+ id: input.id,
11580
+ description: input.description ?? null,
11581
+ metadata: input.metadata ?? null,
11582
+ inputSchema: input.inputSchema,
11583
+ outputSchema: input.outputSchema,
11584
+ stateSchema: input.stateSchema ?? null,
11585
+ requestContextSchema: input.requestContextSchema ?? null,
11586
+ graph: input.graph,
11587
+ status: "active",
11588
+ source: "storage",
11589
+ authorId: "authorId" in input ? input.authorId ?? null : null,
11590
+ createdAt: now,
11591
+ updatedAt: now
11592
+ };
11593
+ try {
11594
+ await this.db.insert({
11595
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11596
+ record
11597
+ });
11598
+ } catch (error) {
11599
+ if (!await this.get(input.id)) throw error;
11600
+ return this.applyUpdate(input, now);
11601
+ }
11602
+ const created = await this.get(input.id);
11603
+ if (!created) throw new Error(`Failed to persist workflow definition "${input.id}".`);
11604
+ return created;
11605
+ }
11606
+ return this.applyUpdate(input, now);
11607
+ }
11608
+ async applyUpdate(input, now) {
11609
+ const data = { updatedAt: now };
11610
+ if ("description" in input && input.description !== void 0) data.description = input.description;
11611
+ if ("metadata" in input && input.metadata !== void 0) data.metadata = input.metadata;
11612
+ if ("inputSchema" in input && input.inputSchema !== void 0) data.inputSchema = input.inputSchema;
11613
+ if ("outputSchema" in input && input.outputSchema !== void 0) data.outputSchema = input.outputSchema;
11614
+ if ("stateSchema" in input && input.stateSchema !== void 0) data.stateSchema = input.stateSchema;
11615
+ if ("requestContextSchema" in input && input.requestContextSchema !== void 0) data.requestContextSchema = input.requestContextSchema;
11616
+ if ("graph" in input && input.graph !== void 0) data.graph = input.graph;
11617
+ if ("status" in input && input.status !== void 0) data.status = input.status;
11618
+ if ("authorId" in input && input.authorId !== void 0) data.authorId = input.authorId;
11619
+ await this.db.update({
11620
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11621
+ keys: { id: input.id },
11622
+ data
11623
+ });
11624
+ const updated = await this.get(input.id);
11625
+ if (!updated) throw new Error(`Failed to update workflow definition "${input.id}".`);
11626
+ return updated;
11627
+ }
11628
+ async get(id) {
11629
+ const row = await this.db.load({
11630
+ tableName: _mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS,
11631
+ keys: { id }
11632
+ });
11633
+ return row ? rowToDefinition(row) : null;
11634
+ }
11635
+ async list(args) {
11636
+ const params = {};
11637
+ const conditions = [];
11638
+ if (args?.status) {
11639
+ params.status = args.status;
11640
+ conditions.push(`${quoteIdent("status", "column name")} = @status`);
11641
+ }
11642
+ if (args?.authorId !== void 0) {
11643
+ params.authorId = args.authorId;
11644
+ conditions.push(`${quoteIdent("authorId", "column name")} = @authorId`);
11645
+ }
11646
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
11647
+ const sql = `SELECT * FROM ${quoteIdent(_mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS, "table name")} ${where} ORDER BY ${quoteIdent("updatedAt", "column name")} DESC`;
11648
+ const [rows] = await this.database.run({
11649
+ sql,
11650
+ params,
11651
+ json: true
11652
+ });
11653
+ const definitions = rows.map(rowToDefinition);
11654
+ return {
11655
+ definitions,
11656
+ total: definitions.length
11657
+ };
11658
+ }
11659
+ async delete(id) {
11660
+ await this.db.runDml({
11661
+ sql: `DELETE FROM ${quoteIdent(_mastra_core_storage.TABLE_WORKFLOW_DEFINITIONS, "table name")} WHERE id = @id`,
11662
+ params: { id }
11663
+ });
11664
+ }
11665
+ };
11666
+ //#endregion
11484
11667
  //#region src/storage/domains/workflows/index.ts
11485
11668
  /**
11486
11669
  * Spanner-backed storage for workflow run snapshots, including persistence,
@@ -12649,7 +12832,8 @@ const SPANNER_DOMAIN_KEYS = [
12649
12832
  "datasets",
12650
12833
  "experiments",
12651
12834
  "favorites",
12652
- "workspaces"
12835
+ "workspaces",
12836
+ "workflowDefinitions"
12653
12837
  ];
12654
12838
  const isPreConfiguredDatabase = (config) => "database" in config && !!config.database;
12655
12839
  /**
@@ -12737,7 +12921,8 @@ var SpannerStore = class extends _mastra_core_storage.MastraCompositeStore {
12737
12921
  ...wants("datasets") && { datasets: new DatasetsSpanner(domainConfig) },
12738
12922
  ...wants("experiments") && { experiments: new ExperimentsSpanner(domainConfig) },
12739
12923
  ...wants("favorites") && { favorites: new FavoritesSpanner(domainConfig) },
12740
- ...wants("workspaces") && { workspaces: new WorkspacesSpanner(domainConfig) }
12924
+ ...wants("workspaces") && { workspaces: new WorkspacesSpanner(domainConfig) },
12925
+ ...wants("workflowDefinitions") && { workflowDefinitions: new WorkflowDefinitionsSpanner(domainConfig) }
12741
12926
  };
12742
12927
  } catch (e) {
12743
12928
  throw new _mastra_core_error.MastraError({
@@ -12793,7 +12978,8 @@ var SpannerStore = class extends _mastra_core_storage.MastraCompositeStore {
12793
12978
  "datasets",
12794
12979
  "experiments",
12795
12980
  "workspaces",
12796
- "favorites"
12981
+ "favorites",
12982
+ "workflowDefinitions"
12797
12983
  ]) {
12798
12984
  const store = this.stores?.[key];
12799
12985
  if (store) await store.init();
@@ -12843,6 +13029,7 @@ exports.ScorerDefinitionsSpanner = ScorerDefinitionsSpanner;
12843
13029
  exports.ScoresSpanner = ScoresSpanner;
12844
13030
  exports.SkillsSpanner = SkillsSpanner;
12845
13031
  exports.SpannerStore = SpannerStore;
13032
+ exports.WorkflowDefinitionsSpanner = WorkflowDefinitionsSpanner;
12846
13033
  exports.WorkflowsSpanner = WorkflowsSpanner;
12847
13034
  exports.WorkspacesSpanner = WorkspacesSpanner;
12848
13035