@mastra/pg 0.3.4 → 0.4.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.
@@ -41,10 +41,7 @@ const createSampleMessage = (threadId: string): MessageType => ({
41
41
  createdAt: new Date(),
42
42
  });
43
43
 
44
- const createSampleWorkflowSnapshot = (
45
- status: WorkflowRunState['context']['steps'][string]['status'],
46
- createdAt?: Date,
47
- ) => {
44
+ const createSampleWorkflowSnapshot = (status: WorkflowRunState['context'][string]['status'], createdAt?: Date) => {
48
45
  const runId = `run-${randomUUID()}`;
49
46
  const stepId = `step-${randomUUID()}`;
50
47
  const timestamp = createdAt || new Date();
@@ -52,21 +49,18 @@ const createSampleWorkflowSnapshot = (
52
49
  result: { success: true },
53
50
  value: {},
54
51
  context: {
55
- steps: {
56
- [stepId]: {
57
- status,
58
- payload: {},
59
- error: undefined,
60
- },
52
+ [stepId]: {
53
+ status,
54
+ payload: {},
55
+ error: undefined,
61
56
  },
62
- triggerData: {},
63
- attempts: {},
57
+ input: {},
64
58
  },
65
59
  activePaths: [],
66
60
  suspendedPaths: {},
67
61
  runId,
68
62
  timestamp: timestamp.getTime(),
69
- };
63
+ } as unknown as WorkflowRunState;
70
64
  return { snapshot, runId, stepId };
71
65
  };
72
66
 
@@ -92,7 +86,7 @@ const checkWorkflowSnapshot = (snapshot: WorkflowRunState | string, stepId: stri
92
86
  if (typeof snapshot === 'string') {
93
87
  throw new Error('Expected WorkflowRunState, got string');
94
88
  }
95
- expect(snapshot.context?.steps[stepId]?.status).toBe(status);
89
+ expect(snapshot.context?.[stepId]?.status).toBe(status);
96
90
  };
97
91
 
98
92
  describe('PostgresStore', () => {
@@ -356,17 +350,15 @@ describe('PostgresStore', () => {
356
350
  const snapshot = {
357
351
  status: 'running',
358
352
  context: {
359
- steps: {},
360
- stepResults: {},
361
- attempts: {},
362
- triggerData: { type: 'manual' },
353
+ input: { type: 'manual' },
354
+ step1: { status: 'success', output: { data: 'test' } },
363
355
  },
364
356
  value: {},
365
357
  activePaths: [],
366
358
  suspendedPaths: {},
367
359
  runId,
368
360
  timestamp: new Date().getTime(),
369
- };
361
+ } as unknown as WorkflowRunState;
370
362
 
371
363
  await store.persistWorkflowSnapshot({
372
364
  workflowName,
@@ -397,10 +389,7 @@ describe('PostgresStore', () => {
397
389
  const initialSnapshot = {
398
390
  status: 'running',
399
391
  context: {
400
- steps: {},
401
- stepResults: {},
402
- attempts: {},
403
- triggerData: { type: 'manual' },
392
+ input: { type: 'manual' },
404
393
  },
405
394
  value: {},
406
395
  activePaths: [],
@@ -412,18 +401,14 @@ describe('PostgresStore', () => {
412
401
  await store.persistWorkflowSnapshot({
413
402
  workflowName,
414
403
  runId,
415
- snapshot: initialSnapshot,
404
+ snapshot: initialSnapshot as unknown as WorkflowRunState,
416
405
  });
417
406
 
418
407
  const updatedSnapshot = {
419
- status: 'completed',
408
+ status: 'success',
420
409
  context: {
421
- steps: {},
422
- stepResults: {
423
- 'step-1': { status: 'success', result: { data: 'test' } },
424
- },
425
- attempts: { 'step-1': 1 },
426
- triggerData: { type: 'manual' },
410
+ input: { type: 'manual' },
411
+ 'step-1': { status: 'success', result: { data: 'test' } },
427
412
  },
428
413
  value: {},
429
414
  activePaths: [],
@@ -435,7 +420,7 @@ describe('PostgresStore', () => {
435
420
  await store.persistWorkflowSnapshot({
436
421
  workflowName,
437
422
  runId,
438
- snapshot: updatedSnapshot,
423
+ snapshot: updatedSnapshot as unknown as WorkflowRunState,
439
424
  });
440
425
 
441
426
  const loadedSnapshot = await store.loadWorkflowSnapshot({
@@ -452,25 +437,21 @@ describe('PostgresStore', () => {
452
437
  const complexSnapshot = {
453
438
  value: { currentState: 'running' },
454
439
  context: {
455
- stepResults: {
456
- 'step-1': {
457
- status: 'success',
458
- result: {
459
- nestedData: {
460
- array: [1, 2, 3],
461
- object: { key: 'value' },
462
- date: new Date().toISOString(),
463
- },
440
+ 'step-1': {
441
+ status: 'success',
442
+ output: {
443
+ nestedData: {
444
+ array: [1, 2, 3],
445
+ object: { key: 'value' },
446
+ date: new Date().toISOString(),
464
447
  },
465
448
  },
466
- 'step-2': {
467
- status: 'waiting',
468
- dependencies: ['step-3', 'step-4'],
469
- },
470
449
  },
471
- steps: {},
472
- attempts: { 'step-1': 1, 'step-2': 0 },
473
- triggerData: {
450
+ 'step-2': {
451
+ status: 'waiting',
452
+ dependencies: ['step-3', 'step-4'],
453
+ },
454
+ input: {
474
455
  type: 'scheduled',
475
456
  metadata: {
476
457
  schedule: '0 0 * * *',
@@ -498,7 +479,7 @@ describe('PostgresStore', () => {
498
479
  await store.persistWorkflowSnapshot({
499
480
  workflowName,
500
481
  runId,
501
- snapshot: complexSnapshot,
482
+ snapshot: complexSnapshot as unknown as WorkflowRunState,
502
483
  });
503
484
 
504
485
  const loadedSnapshot = await store.loadWorkflowSnapshot({
@@ -704,7 +685,7 @@ describe('PostgresStore', () => {
704
685
  // Insert multiple workflow runs for the same resourceId
705
686
  resourceId = 'resource-shared';
706
687
  for (const status of ['success', 'failed']) {
707
- const sample = createSampleWorkflowSnapshot(status as WorkflowRunState['context']['steps'][string]['status']);
688
+ const sample = createSampleWorkflowSnapshot(status as WorkflowRunState['context'][string]['status']);
708
689
  runIds.push(sample.runId);
709
690
  await store.insert({
710
691
  tableName: TABLE_WORKFLOW_SNAPSHOT,
@@ -719,7 +700,7 @@ describe('PostgresStore', () => {
719
700
  });
720
701
  }
721
702
  // Insert a run with a different resourceId
722
- const other = createSampleWorkflowSnapshot('waiting');
703
+ const other = createSampleWorkflowSnapshot('suspended');
723
704
  await store.insert({
724
705
  tableName: TABLE_WORKFLOW_SNAPSHOT,
725
706
  record: {
@@ -16,16 +16,13 @@ import type {
16
16
  WorkflowRun,
17
17
  WorkflowRuns,
18
18
  } from '@mastra/core/storage';
19
+ import { parseSqlIdentifier } from '@mastra/core/utils';
19
20
  import type { WorkflowRunState } from '@mastra/core/workflows';
20
21
  import pgPromise from 'pg-promise';
21
22
  import type { ISSLConfig } from 'pg-promise/typescript/pg-subset';
22
23
 
23
24
  export type PostgresConfig = {
24
25
  schemaName?: string;
25
- /**
26
- * @deprecated Use `schemaName` instead. Support for `schema` will be removed on May 20th, 2025.
27
- */
28
- schema?: string;
29
26
  } & (
30
27
  | {
31
28
  host: string;
@@ -71,13 +68,7 @@ export class PostgresStore extends MastraStorage {
71
68
  }
72
69
  super({ name: 'PostgresStore' });
73
70
  this.pgp = pgPromise();
74
- // Deprecation notice for schema (old option)
75
- if ('schema' in config && config.schema) {
76
- console.warn(
77
- '[DEPRECATION NOTICE] The "schema" option in PostgresStore is deprecated. Please use "schemaName" instead. Support for "schema" will be removed on May 20th, 2025.',
78
- );
79
- }
80
- this.schema = config.schemaName ?? config.schema;
71
+ this.schema = config.schemaName;
81
72
  this.db = this.pgp(
82
73
  `connectionString` in config
83
74
  ? { connectionString: config.connectionString }
@@ -93,7 +84,9 @@ export class PostgresStore extends MastraStorage {
93
84
  }
94
85
 
95
86
  private getTableName(indexName: string) {
96
- return this.schema ? `${this.schema}."${indexName}"` : `"${indexName}"`;
87
+ const parsedIndexName = parseSqlIdentifier(indexName, 'table name');
88
+ const parsedSchemaName = this.schema ? parseSqlIdentifier(this.schema, 'schema name') : undefined;
89
+ return parsedSchemaName ? `${parsedSchemaName}."${parsedIndexName}"` : `"${parsedIndexName}"`;
97
90
  }
98
91
 
99
92
  async getEvalsByAgentName(agentName: string, type?: 'test' | 'live'): Promise<EvalRow[]> {
@@ -192,13 +185,15 @@ export class PostgresStore extends MastraStorage {
192
185
  }
193
186
  if (attributes) {
194
187
  Object.keys(attributes).forEach(key => {
195
- conditions.push(`attributes->>'${key}' = \$${idx++}`);
188
+ const parsedKey = parseSqlIdentifier(key, 'attribute key');
189
+ conditions.push(`attributes->>'${parsedKey}' = \$${idx++}`);
196
190
  });
197
191
  }
198
192
 
199
193
  if (filters) {
200
194
  Object.entries(filters).forEach(([key]) => {
201
- conditions.push(`${key} = \$${idx++}`);
195
+ const parsedKey = parseSqlIdentifier(key, 'filter key');
196
+ conditions.push(`${parsedKey} = \$${idx++}`);
202
197
  });
203
198
  }
204
199
 
@@ -341,10 +336,11 @@ export class PostgresStore extends MastraStorage {
341
336
  try {
342
337
  const columns = Object.entries(schema)
343
338
  .map(([name, def]) => {
339
+ const parsedName = parseSqlIdentifier(name, 'column name');
344
340
  const constraints = [];
345
341
  if (def.primaryKey) constraints.push('PRIMARY KEY');
346
342
  if (!def.nullable) constraints.push('NOT NULL');
347
- return `"${name}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
343
+ return `"${parsedName}" ${def.type.toUpperCase()} ${constraints.join(' ')}`;
348
344
  })
349
345
  .join(',\n');
350
346
 
@@ -392,7 +388,7 @@ export class PostgresStore extends MastraStorage {
392
388
 
393
389
  async insert({ tableName, record }: { tableName: TABLE_NAMES; record: Record<string, any> }): Promise<void> {
394
390
  try {
395
- const columns = Object.keys(record);
391
+ const columns = Object.keys(record).map(col => parseSqlIdentifier(col, 'column name'));
396
392
  const values = Object.values(record);
397
393
  const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
398
394
 
@@ -408,7 +404,7 @@ export class PostgresStore extends MastraStorage {
408
404
 
409
405
  async load<R>({ tableName, keys }: { tableName: TABLE_NAMES; keys: Record<string, string> }): Promise<R | null> {
410
406
  try {
411
- const keyEntries = Object.entries(keys);
407
+ const keyEntries = Object.entries(keys).map(([key, value]) => [parseSqlIdentifier(key, 'column name'), value]);
412
408
  const conditions = keyEntries.map(([key], index) => `"${key}" = $${index + 1}`).join(' AND ');
413
409
  const values = keyEntries.map(([_, value]) => value);
414
410