@mastra/convex 1.1.0 → 1.2.0-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/CHANGELOG.md +60 -0
- package/README.md +31 -13
- package/dist/{chunk-FZDLZ4S3.js → chunk-AJFME2ZF.js} +323 -16
- package/dist/chunk-AJFME2ZF.js.map +1 -0
- package/dist/{chunk-JPWDG4L3.js → chunk-MC75WADX.js} +79 -4
- package/dist/chunk-MC75WADX.js.map +1 -0
- package/dist/{chunk-EEELVBWO.cjs → chunk-ORSDZTO4.cjs} +322 -15
- package/dist/chunk-ORSDZTO4.cjs.map +1 -0
- package/dist/{chunk-CV23JOCS.cjs → chunk-SFRHJGSM.cjs} +102 -2
- package/dist/chunk-SFRHJGSM.cjs.map +1 -0
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +63 -23
- package/dist/index.cjs +634 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +574 -49
- package/dist/index.js.map +1 -1
- package/dist/schema.cjs +60 -20
- package/dist/schema.d.ts +192 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +1 -1
- package/dist/server/index-map.d.ts.map +1 -1
- package/dist/server/index.cjs +63 -23
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/storage/db/index.d.ts +28 -1
- package/dist/storage/db/index.d.ts.map +1 -1
- package/dist/storage/domains/background-tasks/index.d.ts.map +1 -1
- package/dist/storage/domains/channels/index.d.ts +19 -0
- package/dist/storage/domains/channels/index.d.ts.map +1 -0
- package/dist/storage/domains/schedules/index.d.ts +19 -0
- package/dist/storage/domains/schedules/index.d.ts.map +1 -0
- package/dist/storage/index.d.ts +3 -1
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/types.d.ts +42 -0
- package/dist/storage/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-CV23JOCS.cjs.map +0 -1
- package/dist/chunk-EEELVBWO.cjs.map +0 -1
- package/dist/chunk-FZDLZ4S3.js.map +0 -1
- package/dist/chunk-JPWDG4L3.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,65 @@
|
|
|
1
1
|
# @mastra/convex
|
|
2
2
|
|
|
3
|
+
## 1.2.0-alpha.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Convex can now persist channel installations and provider configuration. ([#16718](https://github.com/mastra-ai/mastra/pull/16718))
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { ConvexStore } from '@mastra/convex';
|
|
11
|
+
|
|
12
|
+
const storage = new ConvexStore({
|
|
13
|
+
id: 'app-storage',
|
|
14
|
+
deploymentUrl: process.env.CONVEX_URL!,
|
|
15
|
+
adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const channels = await storage.getStore('channels');
|
|
19
|
+
|
|
20
|
+
await channels?.saveInstallation({
|
|
21
|
+
id: 'slack-agent-1',
|
|
22
|
+
platform: 'slack',
|
|
23
|
+
agentId: 'agent-1',
|
|
24
|
+
status: 'active',
|
|
25
|
+
webhookId: 'webhook-1',
|
|
26
|
+
data: { teamId: 'T123', botUserId: 'U123' },
|
|
27
|
+
createdAt: new Date(),
|
|
28
|
+
updatedAt: new Date(),
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Workflow schedules can now be stored in Convex. ([#16710](https://github.com/mastra-ai/mastra/pull/16710))
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { ConvexStore } from '@mastra/convex';
|
|
36
|
+
|
|
37
|
+
const storage = new ConvexStore({
|
|
38
|
+
id: 'app-storage',
|
|
39
|
+
deploymentUrl: process.env.CONVEX_URL!,
|
|
40
|
+
adminAuthToken: process.env.CONVEX_ADMIN_KEY!,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const schedules = await storage.getStore('schedules');
|
|
44
|
+
|
|
45
|
+
await schedules?.createSchedule({
|
|
46
|
+
id: 'daily-summary',
|
|
47
|
+
target: { type: 'workflow', workflowId: 'summary-workflow' },
|
|
48
|
+
cron: '0 9 * * *',
|
|
49
|
+
status: 'active',
|
|
50
|
+
nextFireAt: Date.now(),
|
|
51
|
+
createdAt: Date.now(),
|
|
52
|
+
updatedAt: Date.now(),
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- Improved Convex background task reliability with safer lifecycle updates, faster filtering, and smoother upgrades from older storage rows. ([#16724](https://github.com/mastra-ai/mastra/pull/16724))
|
|
59
|
+
|
|
60
|
+
- Updated dependencies [[`0cbece9`](https://github.com/mastra-ai/mastra/commit/0cbece9d832cb134a74cdbf3682d390a058215a4), [`7dfe1bc`](https://github.com/mastra-ai/mastra/commit/7dfe1bcfe71d261a6fd6bbf29b1dec49d78fb98f), [`70cb714`](https://github.com/mastra-ai/mastra/commit/70cb7149c8f16f478e15b58498254a53181750a4), [`7f9da22`](https://github.com/mastra-ai/mastra/commit/7f9da22efd5aa595e138a31de55a5f0f2f28b33d)]:
|
|
61
|
+
- @mastra/core@1.37.0-alpha.6
|
|
62
|
+
|
|
3
63
|
## 1.1.0
|
|
4
64
|
|
|
5
65
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Convex adapters for Mastra:
|
|
4
4
|
|
|
5
|
-
- `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources).
|
|
5
|
+
- `ConvexStore` implements the Mastra storage contract (threads, messages, workflows, scores, resources, schedules, channels, background tasks).
|
|
6
6
|
- `ConvexVector` stores embeddings inside Convex and performs development-scale cosine similarity search.
|
|
7
7
|
- `ConvexNativeVector` uses Convex native vector search for production workloads.
|
|
8
8
|
- `ConvexServerCache` stores Mastra server cache entries in Convex for durable stream replay and response caching.
|
|
@@ -28,6 +28,11 @@ import {
|
|
|
28
28
|
mastraResourcesTable,
|
|
29
29
|
mastraWorkflowSnapshotsTable,
|
|
30
30
|
mastraScoresTable,
|
|
31
|
+
mastraSchedulesTable,
|
|
32
|
+
mastraScheduleTriggersTable,
|
|
33
|
+
mastraChannelInstallationsTable,
|
|
34
|
+
mastraChannelConfigTable,
|
|
35
|
+
mastraBackgroundTasksTable,
|
|
31
36
|
mastraVectorIndexesTable,
|
|
32
37
|
mastraVectorsTable,
|
|
33
38
|
mastraCacheTable,
|
|
@@ -41,6 +46,11 @@ export default defineSchema({
|
|
|
41
46
|
mastra_resources: mastraResourcesTable,
|
|
42
47
|
mastra_workflow_snapshots: mastraWorkflowSnapshotsTable,
|
|
43
48
|
mastra_scorers: mastraScoresTable,
|
|
49
|
+
mastra_schedules: mastraSchedulesTable,
|
|
50
|
+
mastra_schedule_triggers: mastraScheduleTriggersTable,
|
|
51
|
+
mastra_channel_installations: mastraChannelInstallationsTable,
|
|
52
|
+
mastra_channel_config: mastraChannelConfigTable,
|
|
53
|
+
mastra_background_tasks: mastraBackgroundTasksTable,
|
|
44
54
|
mastra_vector_indexes: mastraVectorIndexesTable,
|
|
45
55
|
mastra_vectors: mastraVectorsTable,
|
|
46
56
|
mastra_cache: mastraCacheTable,
|
|
@@ -160,24 +170,32 @@ Native vector search uses Convex's schema-defined vector indexes and action-only
|
|
|
160
170
|
|
|
161
171
|
This adapter uses **typed Convex tables** for each Mastra domain:
|
|
162
172
|
|
|
163
|
-
| Domain
|
|
164
|
-
|
|
|
165
|
-
| Threads
|
|
166
|
-
| Messages
|
|
167
|
-
| Resources
|
|
168
|
-
| Workflows
|
|
169
|
-
| Scorers
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
173
|
+
| Domain | Convex Table | Purpose |
|
|
174
|
+
| ---------------- | ------------------------------------------------------- | -------------------------------- |
|
|
175
|
+
| Threads | `mastra_threads` | Conversation threads |
|
|
176
|
+
| Messages | `mastra_messages` | Chat messages |
|
|
177
|
+
| Resources | `mastra_resources` | User working memory |
|
|
178
|
+
| Workflows | `mastra_workflow_snapshots` | Workflow state |
|
|
179
|
+
| Scorers | `mastra_scorers` | Evaluation data |
|
|
180
|
+
| Schedules | `mastra_schedules` | Workflow schedules |
|
|
181
|
+
| Triggers | `mastra_schedule_triggers` | Schedule history |
|
|
182
|
+
| Channels | `mastra_channel_installations`, `mastra_channel_config` | Channel installations and config |
|
|
183
|
+
| Background Tasks | `mastra_background_tasks` | Background task state |
|
|
184
|
+
| Vector Indexes | `mastra_vector_indexes` | Index metadata |
|
|
185
|
+
| Vectors | `mastra_vectors` | Embeddings |
|
|
186
|
+
| Cache | `mastra_cache` | Cache metadata |
|
|
187
|
+
| Cache Items | `mastra_cache_list_items` | Cache list entries |
|
|
188
|
+
| Fallback | `mastra_documents` | Unknown tables |
|
|
175
189
|
|
|
176
190
|
All typed tables include:
|
|
177
191
|
|
|
178
192
|
- An `id` field for Mastra's record ID (distinct from Convex's auto-generated `_id`)
|
|
179
193
|
- A `by_record_id` index for efficient lookups by Mastra ID
|
|
180
194
|
|
|
195
|
+
Schedule due reads and trigger-history reads use bounded Convex queries to avoid deployment read limits. When no explicit trigger-history limit is provided, the adapter returns the newest 100 rows. Schedule listing is capped at 8,000 rows per call. Schedule rows also store a normalized `workflow_id` alongside the serialized target so workflow filters can run inside Convex before the listing cap is applied.
|
|
196
|
+
|
|
197
|
+
Background task reads and updates also tolerate older rows that were written to the fallback `mastra_documents` table.
|
|
198
|
+
|
|
181
199
|
## Testing
|
|
182
200
|
|
|
183
201
|
Set the following environment variables before running tests:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { mutationGeneric, actionGeneric, queryGeneric } from 'convex/server';
|
|
2
|
-
import { TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_RESOURCES, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage/constants';
|
|
2
|
+
import { TABLE_BACKGROUND_TASKS, TABLE_CHANNEL_CONFIG, TABLE_CHANNEL_INSTALLATIONS, TABLE_SCHEDULE_TRIGGERS, TABLE_SCHEDULES, TABLE_SCORERS, TABLE_WORKFLOW_SNAPSHOT, TABLE_RESOURCES, TABLE_MESSAGES, TABLE_THREADS } from '@mastra/core/storage/constants';
|
|
3
3
|
import { v } from 'convex/values';
|
|
4
4
|
|
|
5
5
|
// src/server/cache.ts
|
|
@@ -227,6 +227,41 @@ var TABLE_INDEX_MAP = {
|
|
|
227
227
|
{ name: "by_created", fields: ["createdAt"] },
|
|
228
228
|
{ name: "by_record_id", fields: ["id"] }
|
|
229
229
|
],
|
|
230
|
+
mastra_schedules: [
|
|
231
|
+
{ name: "by_workflow_status", fields: ["workflow_id", "status"] },
|
|
232
|
+
{ name: "by_workflow_id", fields: ["workflow_id"] },
|
|
233
|
+
{ name: "by_owner", fields: ["owner_type", "owner_id"] },
|
|
234
|
+
{ name: "by_owner_id", fields: ["owner_id"] },
|
|
235
|
+
{ name: "by_status_next_fire_at", fields: ["status", "next_fire_at"] },
|
|
236
|
+
{ name: "by_created", fields: ["created_at"] },
|
|
237
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
238
|
+
],
|
|
239
|
+
mastra_schedule_triggers: [
|
|
240
|
+
{ name: "by_schedule_actual", fields: ["schedule_id", "actual_fire_at"] },
|
|
241
|
+
{ name: "by_parent_trigger", fields: ["parent_trigger_id"] },
|
|
242
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
243
|
+
],
|
|
244
|
+
mastra_channel_installations: [
|
|
245
|
+
{ name: "by_platform_agent", fields: ["platform", "agentId"] },
|
|
246
|
+
{ name: "by_webhook", fields: ["webhookId"] },
|
|
247
|
+
{ name: "by_platform", fields: ["platform"] },
|
|
248
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
249
|
+
],
|
|
250
|
+
mastra_channel_config: [
|
|
251
|
+
{ name: "by_platform", fields: ["platform"] },
|
|
252
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
253
|
+
],
|
|
254
|
+
mastra_background_tasks: [
|
|
255
|
+
{ name: "by_agent_status", fields: ["agent_id", "status"] },
|
|
256
|
+
{ name: "by_status_created", fields: ["status", "createdAt"] },
|
|
257
|
+
{ name: "by_run", fields: ["run_id"] },
|
|
258
|
+
{ name: "by_tool_call", fields: ["tool_call_id"] },
|
|
259
|
+
{ name: "by_thread", fields: ["thread_id"] },
|
|
260
|
+
{ name: "by_resource", fields: ["resource_id"] },
|
|
261
|
+
{ name: "by_tool", fields: ["tool_name"] },
|
|
262
|
+
{ name: "by_created", fields: ["createdAt"] },
|
|
263
|
+
{ name: "by_record_id", fields: ["id"] }
|
|
264
|
+
],
|
|
230
265
|
mastra_vector_indexes: [
|
|
231
266
|
{ name: "by_name", fields: ["indexName"] },
|
|
232
267
|
{ name: "by_record_id", fields: ["id"] }
|
|
@@ -263,7 +298,44 @@ function findBestIndex(convexTable, filters) {
|
|
|
263
298
|
var TABLE_VECTOR_INDEXES = "mastra_vector_indexes";
|
|
264
299
|
var VECTOR_TABLE_PREFIX = "mastra_vector_";
|
|
265
300
|
var CONVEX_TABLE_WORKFLOW_SNAPSHOTS = "mastra_workflow_snapshots";
|
|
301
|
+
var CONVEX_TABLE_BACKGROUND_TASKS = "mastra_background_tasks";
|
|
302
|
+
var CONVEX_TABLE_DOCUMENTS = "mastra_documents";
|
|
266
303
|
var STORAGE_MUTATION_BATCH_SIZE = 25;
|
|
304
|
+
var DEFAULT_SCHEDULE_QUERY_LIMIT = 100;
|
|
305
|
+
var BACKGROUND_TASK_FIELD_ALIASES = {
|
|
306
|
+
tool_call_id: "toolCallId",
|
|
307
|
+
toolCallId: "tool_call_id",
|
|
308
|
+
tool_name: "toolName",
|
|
309
|
+
toolName: "tool_name",
|
|
310
|
+
agent_id: "agentId",
|
|
311
|
+
agentId: "agent_id",
|
|
312
|
+
run_id: "runId",
|
|
313
|
+
runId: "run_id",
|
|
314
|
+
thread_id: "threadId",
|
|
315
|
+
threadId: "thread_id",
|
|
316
|
+
resource_id: "resourceId",
|
|
317
|
+
resourceId: "resource_id",
|
|
318
|
+
suspend_payload: "suspendPayload",
|
|
319
|
+
suspendPayload: "suspend_payload",
|
|
320
|
+
retry_count: "retryCount",
|
|
321
|
+
retryCount: "retry_count",
|
|
322
|
+
max_retries: "maxRetries",
|
|
323
|
+
maxRetries: "max_retries",
|
|
324
|
+
timeout_ms: "timeoutMs",
|
|
325
|
+
timeoutMs: "timeout_ms"
|
|
326
|
+
};
|
|
327
|
+
function normalizeScheduleQueryLimit(limit) {
|
|
328
|
+
if (limit == null || !Number.isFinite(limit)) return DEFAULT_SCHEDULE_QUERY_LIMIT;
|
|
329
|
+
return Math.max(0, Math.floor(limit));
|
|
330
|
+
}
|
|
331
|
+
function applyConvexEqualityFilters(query, filters, indexedFields = /* @__PURE__ */ new Set()) {
|
|
332
|
+
const remainingFilters = filters?.filter((filter) => !indexedFields.has(filter.field));
|
|
333
|
+
if (!remainingFilters?.length) return query;
|
|
334
|
+
return query.filter((q) => {
|
|
335
|
+
const predicates = remainingFilters.map((filter) => q.eq(q.field(filter.field), filter.value));
|
|
336
|
+
return predicates.length === 1 ? predicates[0] : q.and(...predicates);
|
|
337
|
+
});
|
|
338
|
+
}
|
|
267
339
|
async function mapInBatches(inputs, batchSize, mapper) {
|
|
268
340
|
const results = [];
|
|
269
341
|
for (let index = 0; index < inputs.length; index += batchSize) {
|
|
@@ -278,6 +350,58 @@ async function findExistingDocsByIds(ids, findDoc) {
|
|
|
278
350
|
const docs = await mapInBatches([...new Set(ids)], STORAGE_MUTATION_BATCH_SIZE, findDoc);
|
|
279
351
|
return docs.filter((doc) => Boolean(doc));
|
|
280
352
|
}
|
|
353
|
+
function isBackgroundTasksTable(convexTable, request) {
|
|
354
|
+
return convexTable === CONVEX_TABLE_BACKGROUND_TASKS && request.tableName === TABLE_BACKGROUND_TASKS;
|
|
355
|
+
}
|
|
356
|
+
function matchesFilters(record, filters) {
|
|
357
|
+
return filters.every((filter) => {
|
|
358
|
+
if (record[filter.field] === filter.value) return true;
|
|
359
|
+
const alternateField = BACKGROUND_TASK_FIELD_ALIASES[filter.field];
|
|
360
|
+
return alternateField ? record[alternateField] === filter.value : false;
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
function mergeLegacyRecord(record, patch) {
|
|
364
|
+
const merged = { ...record };
|
|
365
|
+
for (const [field, value] of Object.entries(patch)) {
|
|
366
|
+
const alternateField = BACKGROUND_TASK_FIELD_ALIASES[field];
|
|
367
|
+
if (alternateField) delete merged[alternateField];
|
|
368
|
+
merged[field] = value;
|
|
369
|
+
}
|
|
370
|
+
return merged;
|
|
371
|
+
}
|
|
372
|
+
function stripPatchKeys(record, keys) {
|
|
373
|
+
const stripped = { ...record };
|
|
374
|
+
for (const key of keys) delete stripped[key];
|
|
375
|
+
return stripped;
|
|
376
|
+
}
|
|
377
|
+
function dedupeByRecordId(records) {
|
|
378
|
+
const seen = /* @__PURE__ */ new Set();
|
|
379
|
+
return records.filter((record) => {
|
|
380
|
+
if (record?.id == null) return true;
|
|
381
|
+
const id = String(record.id);
|
|
382
|
+
if (seen.has(id)) return false;
|
|
383
|
+
seen.add(id);
|
|
384
|
+
return true;
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
function isMissingBackgroundTaskSchemaError(error) {
|
|
388
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
389
|
+
return message.includes(CONVEX_TABLE_BACKGROUND_TASKS) && (message.includes("does not exist") || message.includes("not found") || message.includes("not defined") || message.includes("no such"));
|
|
390
|
+
}
|
|
391
|
+
async function findGenericDocumentById(ctx, tableName, id) {
|
|
392
|
+
return await ctx.db.query(CONVEX_TABLE_DOCUMENTS).withIndex("by_table_primary", (q) => q.eq("table", tableName).eq("primaryKey", String(id))).unique();
|
|
393
|
+
}
|
|
394
|
+
async function findGenericDocumentsByTable(ctx, tableName, limit) {
|
|
395
|
+
return await ctx.db.query(CONVEX_TABLE_DOCUMENTS).withIndex("by_table", (q) => q.eq("table", tableName)).take(limit);
|
|
396
|
+
}
|
|
397
|
+
async function filterLegacyRecordsWithoutTypedCopy(ctx, convexTable, legacyRecords) {
|
|
398
|
+
const records = await mapInBatches(legacyRecords, STORAGE_MUTATION_BATCH_SIZE, async (record) => {
|
|
399
|
+
if (record.id == null) return record;
|
|
400
|
+
const typedDoc = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", String(record.id))).unique();
|
|
401
|
+
return typedDoc ? null : record;
|
|
402
|
+
});
|
|
403
|
+
return records.filter((record) => Boolean(record));
|
|
404
|
+
}
|
|
281
405
|
function coalesceTypedRecordsForBatchInsert(records) {
|
|
282
406
|
const recordsById = /* @__PURE__ */ new Map();
|
|
283
407
|
for (const record of records) {
|
|
@@ -309,6 +433,16 @@ function resolveTable(tableName) {
|
|
|
309
433
|
return { convexTable: CONVEX_TABLE_WORKFLOW_SNAPSHOTS, isTyped: true };
|
|
310
434
|
case TABLE_SCORERS:
|
|
311
435
|
return { convexTable: "mastra_scorers", isTyped: true };
|
|
436
|
+
case TABLE_SCHEDULES:
|
|
437
|
+
return { convexTable: "mastra_schedules", isTyped: true };
|
|
438
|
+
case TABLE_SCHEDULE_TRIGGERS:
|
|
439
|
+
return { convexTable: "mastra_schedule_triggers", isTyped: true };
|
|
440
|
+
case TABLE_CHANNEL_INSTALLATIONS:
|
|
441
|
+
return { convexTable: "mastra_channel_installations", isTyped: true };
|
|
442
|
+
case TABLE_CHANNEL_CONFIG:
|
|
443
|
+
return { convexTable: "mastra_channel_config", isTyped: true };
|
|
444
|
+
case TABLE_BACKGROUND_TASKS:
|
|
445
|
+
return { convexTable: CONVEX_TABLE_BACKGROUND_TASKS, isTyped: true };
|
|
312
446
|
case TABLE_VECTOR_INDEXES:
|
|
313
447
|
return { convexTable: "mastra_vector_indexes", isTyped: true };
|
|
314
448
|
default:
|
|
@@ -325,6 +459,14 @@ var mastraStorage = mutationGeneric(async (ctx, request) => {
|
|
|
325
459
|
return handleVectorOperation(ctx, request);
|
|
326
460
|
}
|
|
327
461
|
if (isTyped) {
|
|
462
|
+
if (isBackgroundTasksTable(convexTable, request)) {
|
|
463
|
+
try {
|
|
464
|
+
return await handleTypedOperation(ctx, convexTable, request);
|
|
465
|
+
} catch (error) {
|
|
466
|
+
if (!isMissingBackgroundTaskSchemaError(error)) throw error;
|
|
467
|
+
return handleGenericOperation(ctx, request);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
328
470
|
return handleTypedOperation(ctx, convexTable, request);
|
|
329
471
|
}
|
|
330
472
|
return handleGenericOperation(ctx, request);
|
|
@@ -338,6 +480,100 @@ var mastraStorage = mutationGeneric(async (ctx, request) => {
|
|
|
338
480
|
});
|
|
339
481
|
async function handleTypedOperation(ctx, convexTable, request) {
|
|
340
482
|
switch (request.op) {
|
|
483
|
+
case "createSchedule": {
|
|
484
|
+
if (convexTable !== "mastra_schedules") {
|
|
485
|
+
throw new Error(`createSchedule is only supported for mastra_schedules`);
|
|
486
|
+
}
|
|
487
|
+
const record = request.record;
|
|
488
|
+
const id = record.id;
|
|
489
|
+
if (!id) {
|
|
490
|
+
throw new Error(`Schedule is missing an id`);
|
|
491
|
+
}
|
|
492
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", id)).unique();
|
|
493
|
+
if (existing) {
|
|
494
|
+
throw new Error(`Schedule with id "${id}" already exists`);
|
|
495
|
+
}
|
|
496
|
+
await ctx.db.insert(convexTable, record);
|
|
497
|
+
return { ok: true };
|
|
498
|
+
}
|
|
499
|
+
case "recordScheduleTrigger": {
|
|
500
|
+
if (convexTable !== "mastra_schedule_triggers") {
|
|
501
|
+
throw new Error(`recordScheduleTrigger is only supported for mastra_schedule_triggers`);
|
|
502
|
+
}
|
|
503
|
+
const record = request.record;
|
|
504
|
+
const id = record.id;
|
|
505
|
+
if (!id) {
|
|
506
|
+
throw new Error(`Schedule trigger is missing an id`);
|
|
507
|
+
}
|
|
508
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", id)).unique();
|
|
509
|
+
if (existing) {
|
|
510
|
+
throw new Error(`Schedule trigger with id "${id}" already exists`);
|
|
511
|
+
}
|
|
512
|
+
await ctx.db.insert(convexTable, record);
|
|
513
|
+
return { ok: true };
|
|
514
|
+
}
|
|
515
|
+
case "listDueSchedules": {
|
|
516
|
+
if (convexTable !== "mastra_schedules") {
|
|
517
|
+
throw new Error(`listDueSchedules is only supported for mastra_schedules`);
|
|
518
|
+
}
|
|
519
|
+
const query = ctx.db.query(convexTable).withIndex("by_status_next_fire_at", (q) => q.eq("status", "active").lte("next_fire_at", request.now));
|
|
520
|
+
const docs = await query.take(normalizeScheduleQueryLimit(request.limit));
|
|
521
|
+
return { ok: true, result: docs };
|
|
522
|
+
}
|
|
523
|
+
case "updateScheduleNextFire": {
|
|
524
|
+
if (convexTable !== "mastra_schedules") {
|
|
525
|
+
throw new Error(`updateScheduleNextFire is only supported for mastra_schedules`);
|
|
526
|
+
}
|
|
527
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", request.id)).unique();
|
|
528
|
+
if (!existing || existing.status !== "active" || existing.next_fire_at !== request.expectedNextFireAt) {
|
|
529
|
+
return { ok: true, result: false };
|
|
530
|
+
}
|
|
531
|
+
await ctx.db.patch(existing._id, {
|
|
532
|
+
next_fire_at: request.newNextFireAt,
|
|
533
|
+
last_fire_at: request.lastFireAt,
|
|
534
|
+
last_run_id: request.lastRunId,
|
|
535
|
+
updated_at: Date.now()
|
|
536
|
+
});
|
|
537
|
+
return { ok: true, result: true };
|
|
538
|
+
}
|
|
539
|
+
case "updateSchedule": {
|
|
540
|
+
if (convexTable !== "mastra_schedules") {
|
|
541
|
+
throw new Error(`updateSchedule is only supported for mastra_schedules`);
|
|
542
|
+
}
|
|
543
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", request.id)).unique();
|
|
544
|
+
if (!existing) {
|
|
545
|
+
throw new Error(`Schedule ${request.id} not found`);
|
|
546
|
+
}
|
|
547
|
+
await ctx.db.patch(existing._id, request.patch);
|
|
548
|
+
return { ok: true, result: { ...existing, ...request.patch } };
|
|
549
|
+
}
|
|
550
|
+
case "listScheduleTriggers": {
|
|
551
|
+
if (convexTable !== "mastra_schedule_triggers") {
|
|
552
|
+
throw new Error(`listScheduleTriggers is only supported for mastra_schedule_triggers`);
|
|
553
|
+
}
|
|
554
|
+
const query = ctx.db.query(convexTable).withIndex("by_schedule_actual", (q) => {
|
|
555
|
+
let builder = q.eq("schedule_id", request.scheduleId);
|
|
556
|
+
if (request.fromActualFireAt != null) {
|
|
557
|
+
builder = builder.gte("actual_fire_at", request.fromActualFireAt);
|
|
558
|
+
}
|
|
559
|
+
if (request.toActualFireAt != null) {
|
|
560
|
+
builder = builder.lt("actual_fire_at", request.toActualFireAt);
|
|
561
|
+
}
|
|
562
|
+
return builder;
|
|
563
|
+
}).order("desc");
|
|
564
|
+
const docs = await query.take(normalizeScheduleQueryLimit(request.limit));
|
|
565
|
+
return { ok: true, result: docs };
|
|
566
|
+
}
|
|
567
|
+
case "deleteScheduleTriggers": {
|
|
568
|
+
if (convexTable !== "mastra_schedule_triggers") {
|
|
569
|
+
throw new Error(`deleteScheduleTriggers is only supported for mastra_schedule_triggers`);
|
|
570
|
+
}
|
|
571
|
+
const docs = await ctx.db.query(convexTable).withIndex("by_schedule_actual", (q) => q.eq("schedule_id", request.scheduleId)).take(STORAGE_MUTATION_BATCH_SIZE + 1);
|
|
572
|
+
const hasMore = docs.length > STORAGE_MUTATION_BATCH_SIZE;
|
|
573
|
+
const docsToDelete = hasMore ? docs.slice(0, STORAGE_MUTATION_BATCH_SIZE) : docs;
|
|
574
|
+
await deleteDocs(ctx, docsToDelete);
|
|
575
|
+
return { ok: true, hasMore };
|
|
576
|
+
}
|
|
341
577
|
case "insert": {
|
|
342
578
|
const record = request.record;
|
|
343
579
|
const id = record.id;
|
|
@@ -367,10 +603,36 @@ async function handleTypedOperation(ctx, convexTable, request) {
|
|
|
367
603
|
});
|
|
368
604
|
return { ok: true };
|
|
369
605
|
}
|
|
606
|
+
case "patch": {
|
|
607
|
+
const patchRecord = stripPatchKeys(request.record, ["id"]);
|
|
608
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", request.id)).unique();
|
|
609
|
+
if (!existing) {
|
|
610
|
+
if (isBackgroundTasksTable(convexTable, request)) {
|
|
611
|
+
const legacy = await findGenericDocumentById(ctx, request.tableName, request.id);
|
|
612
|
+
if (legacy) {
|
|
613
|
+
await ctx.db.patch(legacy._id, { record: mergeLegacyRecord(legacy.record, patchRecord) });
|
|
614
|
+
return { ok: true, result: true };
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return { ok: true, result: false };
|
|
618
|
+
}
|
|
619
|
+
await ctx.db.patch(existing._id, patchRecord);
|
|
620
|
+
if (isBackgroundTasksTable(convexTable, request)) {
|
|
621
|
+
const legacy = await findGenericDocumentById(ctx, request.tableName, request.id);
|
|
622
|
+
if (legacy) {
|
|
623
|
+
await ctx.db.delete(legacy._id);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return { ok: true, result: true };
|
|
627
|
+
}
|
|
370
628
|
case "load": {
|
|
371
629
|
const keys = request.keys;
|
|
372
630
|
if (keys.id) {
|
|
373
631
|
const doc = await ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", keys.id)).unique();
|
|
632
|
+
if (!doc && isBackgroundTasksTable(convexTable, request)) {
|
|
633
|
+
const legacy = await findGenericDocumentById(ctx, request.tableName, String(keys.id));
|
|
634
|
+
return { ok: true, result: legacy?.record ?? null };
|
|
635
|
+
}
|
|
374
636
|
return { ok: true, result: doc || null };
|
|
375
637
|
}
|
|
376
638
|
if (convexTable === CONVEX_TABLE_WORKFLOW_SNAPSHOTS && typeof keys.workflow_name === "string" && typeof keys.run_id === "string") {
|
|
@@ -383,34 +645,47 @@ async function handleTypedOperation(ctx, convexTable, request) {
|
|
|
383
645
|
}
|
|
384
646
|
case "queryTable": {
|
|
385
647
|
const maxDocs = request.limit ? Math.min(request.limit * 2, 1e4) : 1e4;
|
|
386
|
-
let
|
|
648
|
+
let query;
|
|
649
|
+
let indexedFields = /* @__PURE__ */ new Set();
|
|
387
650
|
if (request.indexHint) {
|
|
388
651
|
const hint = request.indexHint;
|
|
389
652
|
if (hint.index === "by_workflow") {
|
|
390
|
-
|
|
653
|
+
query = ctx.db.query(convexTable).withIndex("by_workflow", (q) => q.eq("workflow_name", hint.workflowName));
|
|
391
654
|
} else if (hint.index === "by_workflow_run") {
|
|
392
|
-
|
|
655
|
+
query = ctx.db.query(convexTable).withIndex(
|
|
656
|
+
"by_workflow_run",
|
|
657
|
+
(q) => q.eq("workflow_name", hint.workflowName).eq("run_id", hint.runId)
|
|
658
|
+
);
|
|
393
659
|
} else {
|
|
394
|
-
|
|
660
|
+
query = ctx.db.query(convexTable);
|
|
395
661
|
}
|
|
396
662
|
} else if (request.filters && request.filters.length > 0) {
|
|
397
663
|
const match = findBestIndex(convexTable, request.filters);
|
|
398
664
|
if (match) {
|
|
399
|
-
|
|
665
|
+
query = ctx.db.query(convexTable).withIndex(match.indexName, (q) => {
|
|
400
666
|
let builder = q;
|
|
401
667
|
for (const filter of match.indexedFilters) {
|
|
402
668
|
builder = builder.eq(filter.field, filter.value);
|
|
403
669
|
}
|
|
404
670
|
return builder;
|
|
405
|
-
})
|
|
671
|
+
});
|
|
672
|
+
indexedFields = new Set(match.indexedFilters.map((filter) => filter.field));
|
|
406
673
|
} else {
|
|
407
|
-
|
|
674
|
+
query = ctx.db.query(convexTable);
|
|
408
675
|
}
|
|
409
676
|
} else {
|
|
410
|
-
|
|
677
|
+
query = ctx.db.query(convexTable);
|
|
411
678
|
}
|
|
412
|
-
|
|
413
|
-
|
|
679
|
+
let docs = await applyConvexEqualityFilters(query, request.filters, indexedFields).take(maxDocs);
|
|
680
|
+
if (isBackgroundTasksTable(convexTable, request)) {
|
|
681
|
+
const legacyDocs = await findGenericDocumentsByTable(ctx, request.tableName, maxDocs);
|
|
682
|
+
let legacyRecords = legacyDocs.map((doc) => doc.record);
|
|
683
|
+
if (request.filters && request.filters.length > 0) {
|
|
684
|
+
legacyRecords = legacyRecords.filter((record) => matchesFilters(record, request.filters));
|
|
685
|
+
}
|
|
686
|
+
legacyRecords = await filterLegacyRecordsWithoutTypedCopy(ctx, convexTable, legacyRecords);
|
|
687
|
+
docs.push(...legacyRecords);
|
|
688
|
+
docs = dedupeByRecordId(docs);
|
|
414
689
|
}
|
|
415
690
|
if (request.limit) {
|
|
416
691
|
docs = docs.slice(0, request.limit);
|
|
@@ -421,15 +696,27 @@ async function handleTypedOperation(ctx, convexTable, request) {
|
|
|
421
696
|
case "dropTable": {
|
|
422
697
|
const docs = await ctx.db.query(convexTable).take(STORAGE_MUTATION_BATCH_SIZE + 1);
|
|
423
698
|
const hasMore = docs.length > STORAGE_MUTATION_BATCH_SIZE;
|
|
424
|
-
|
|
699
|
+
let docsToDelete = hasMore ? docs.slice(0, STORAGE_MUTATION_BATCH_SIZE) : docs;
|
|
700
|
+
let legacyHasMore = false;
|
|
701
|
+
if (!hasMore && docsToDelete.length < STORAGE_MUTATION_BATCH_SIZE && isBackgroundTasksTable(convexTable, request)) {
|
|
702
|
+
const remainingBatchSize = STORAGE_MUTATION_BATCH_SIZE - docsToDelete.length;
|
|
703
|
+
const legacyDocs = await findGenericDocumentsByTable(ctx, request.tableName, remainingBatchSize + 1);
|
|
704
|
+
legacyHasMore = legacyDocs.length > remainingBatchSize;
|
|
705
|
+
docsToDelete = docsToDelete.concat(legacyHasMore ? legacyDocs.slice(0, remainingBatchSize) : legacyDocs);
|
|
706
|
+
}
|
|
425
707
|
await deleteDocs(ctx, docsToDelete);
|
|
426
|
-
return { ok: true, hasMore };
|
|
708
|
+
return { ok: true, hasMore: hasMore || legacyHasMore };
|
|
427
709
|
}
|
|
428
710
|
case "deleteMany": {
|
|
429
711
|
const docsToDelete = await findExistingDocsByIds(
|
|
430
712
|
request.ids,
|
|
431
713
|
(id) => ctx.db.query(convexTable).withIndex("by_record_id", (q) => q.eq("id", id)).unique()
|
|
432
714
|
);
|
|
715
|
+
if (isBackgroundTasksTable(convexTable, request)) {
|
|
716
|
+
docsToDelete.push(
|
|
717
|
+
...await findExistingDocsByIds(request.ids, (id) => findGenericDocumentById(ctx, request.tableName, id))
|
|
718
|
+
);
|
|
719
|
+
}
|
|
433
720
|
await deleteDocs(ctx, docsToDelete);
|
|
434
721
|
return { ok: true };
|
|
435
722
|
}
|
|
@@ -484,6 +771,15 @@ async function handleVectorOperation(ctx, request) {
|
|
|
484
771
|
});
|
|
485
772
|
return { ok: true };
|
|
486
773
|
}
|
|
774
|
+
case "patch": {
|
|
775
|
+
const patchRecord = stripPatchKeys(request.record, ["id", "indexName"]);
|
|
776
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_index_id", (q) => q.eq("indexName", indexName).eq("id", request.id)).unique();
|
|
777
|
+
if (!existing) {
|
|
778
|
+
return { ok: true, result: false };
|
|
779
|
+
}
|
|
780
|
+
await ctx.db.patch(existing._id, patchRecord);
|
|
781
|
+
return { ok: true, result: true };
|
|
782
|
+
}
|
|
487
783
|
case "load": {
|
|
488
784
|
const keys = request.keys;
|
|
489
785
|
if (keys.id) {
|
|
@@ -565,6 +861,17 @@ async function handleGenericOperation(ctx, request) {
|
|
|
565
861
|
});
|
|
566
862
|
return { ok: true };
|
|
567
863
|
}
|
|
864
|
+
case "patch": {
|
|
865
|
+
const patchRecord = stripPatchKeys(request.record, ["id"]);
|
|
866
|
+
const existing = await ctx.db.query(convexTable).withIndex("by_table_primary", (q) => q.eq("table", tableName).eq("primaryKey", String(request.id))).unique();
|
|
867
|
+
if (!existing) {
|
|
868
|
+
return { ok: true, result: false };
|
|
869
|
+
}
|
|
870
|
+
await ctx.db.patch(existing._id, {
|
|
871
|
+
record: tableName === TABLE_BACKGROUND_TASKS ? mergeLegacyRecord(existing.record, patchRecord) : { ...existing.record, ...patchRecord }
|
|
872
|
+
});
|
|
873
|
+
return { ok: true, result: true };
|
|
874
|
+
}
|
|
568
875
|
case "load": {
|
|
569
876
|
const keys = request.keys;
|
|
570
877
|
if (keys.id) {
|
|
@@ -581,7 +888,7 @@ async function handleGenericOperation(ctx, request) {
|
|
|
581
888
|
let records = docs.map((doc) => doc.record);
|
|
582
889
|
if (request.filters && request.filters.length > 0) {
|
|
583
890
|
records = records.filter(
|
|
584
|
-
(record) => request.filters.every((filter) => record?.[filter.field] === filter.value)
|
|
891
|
+
(record) => tableName === TABLE_BACKGROUND_TASKS ? matchesFilters(record, request.filters) : request.filters.every((filter) => record?.[filter.field] === filter.value)
|
|
585
892
|
);
|
|
586
893
|
}
|
|
587
894
|
if (request.limit) {
|
|
@@ -883,5 +1190,5 @@ var mastraNativeVectorMutation = mutationGeneric({
|
|
|
883
1190
|
});
|
|
884
1191
|
|
|
885
1192
|
export { mastraCache, mastraNativeVectorAction, mastraNativeVectorMutation, mastraNativeVectorQuery, mastraStorage };
|
|
886
|
-
//# sourceMappingURL=chunk-
|
|
887
|
-
//# sourceMappingURL=chunk-
|
|
1193
|
+
//# sourceMappingURL=chunk-AJFME2ZF.js.map
|
|
1194
|
+
//# sourceMappingURL=chunk-AJFME2ZF.js.map
|