@mastra/pg 0.2.11-alpha.2 → 0.3.0-alpha.4

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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/pg@0.2.11-alpha.2 build /home/runner/work/mastra/mastra/stores/pg
2
+ > @mastra/pg@0.3.0-alpha.4 build /home/runner/work/mastra/mastra/stores/pg
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.4.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 11183ms
9
+ TSC ⚡️ Build success in 10612ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.2
13
13
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.2
15
15
  Writing package typings: /home/runner/work/mastra/mastra/stores/pg/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 12473ms
16
+ DTS ⚡️ Build success in 11768ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- CJS dist/index.cjs 46.10 KB
21
- CJS ⚡️ Build success in 1276ms
22
- ESM dist/index.js 45.68 KB
23
- ESM ⚡️ Build success in 1276ms
20
+ ESM dist/index.js 46.99 KB
21
+ ESM ⚡️ Build success in 1698ms
22
+ CJS dist/index.cjs 47.42 KB
23
+ CJS ⚡️ Build success in 1697ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @mastra/pg
2
2
 
3
+ ## 0.3.0-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 373458f: updated schema for storage config to align with pgvector and added validation for pg connections
8
+ - Updated dependencies [7e92011]
9
+ - @mastra/core@0.9.0-alpha.4
10
+
11
+ ## 0.3.0-alpha.3
12
+
13
+ ### Minor Changes
14
+
15
+ - fe3ae4d: Remove \_\_ functions in storage and move to storage proxy to make sure init is called
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [fe3ae4d]
20
+ - @mastra/core@0.9.0-alpha.3
21
+
3
22
  ## 0.2.11-alpha.2
4
23
 
5
24
  ### Patch Changes
@@ -246,6 +246,10 @@ export { PgVector }
246
246
  export { PgVector as PgVector_alias_1 }
247
247
 
248
248
  declare type PostgresConfig = {
249
+ schemaName?: string;
250
+ /**
251
+ * @deprecated Use `schemaName` instead. Support for `schema` will be removed in a future release.
252
+ */
249
253
  schema?: string;
250
254
  } & ({
251
255
  host: string;
@@ -246,6 +246,10 @@ export { PgVector }
246
246
  export { PgVector as PgVector_alias_1 }
247
247
 
248
248
  declare type PostgresConfig = {
249
+ schemaName?: string;
250
+ /**
251
+ * @deprecated Use `schemaName` instead. Support for `schema` will be removed in a future release.
252
+ */
249
253
  schema?: string;
250
254
  } & ({
251
255
  host: string;
package/dist/index.cjs CHANGED
@@ -305,8 +305,13 @@ var PgVector = class extends vector.MastraVector {
305
305
  vectorExtensionInstalled = void 0;
306
306
  schemaSetupComplete = void 0;
307
307
  constructor(config) {
308
- super();
309
308
  const connectionString = typeof config === "string" ? config : config.connectionString;
309
+ if (!connectionString || typeof connectionString !== "string" || connectionString.trim() === "") {
310
+ throw new Error(
311
+ "PgVector: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
312
+ );
313
+ }
314
+ super();
310
315
  this.schema = typeof config === "string" ? void 0 : config.schemaName;
311
316
  const basePool = new pg__default.default.Pool({
312
317
  connectionString,
@@ -793,9 +798,30 @@ var PostgresStore = class extends storage.MastraStorage {
793
798
  setupSchemaPromise = null;
794
799
  schemaSetupComplete = void 0;
795
800
  constructor(config) {
801
+ if ("connectionString" in config) {
802
+ if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
803
+ throw new Error(
804
+ "PostgresStore: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
805
+ );
806
+ }
807
+ } else {
808
+ const required = ["host", "database", "user", "password"];
809
+ for (const key of required) {
810
+ if (!(key in config) || typeof config[key] !== "string" || config[key].trim() === "") {
811
+ throw new Error(
812
+ `PostgresStore: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`
813
+ );
814
+ }
815
+ }
816
+ }
796
817
  super({ name: "PostgresStore" });
797
818
  this.pgp = pgPromise__default.default();
798
- this.schema = config.schema;
819
+ if ("schema" in config && config.schema) {
820
+ console.warn(
821
+ '[DEPRECATION NOTICE] The "schema" option in PostgresStore is deprecated. Please use "schemaName" instead. Support for "schema" will be removed in a future release.'
822
+ );
823
+ }
824
+ this.schema = config.schemaName ?? config.schema;
799
825
  this.db = this.pgp(
800
826
  `connectionString` in config ? { connectionString: config.connectionString } : {
801
827
  host: config.host,
package/dist/index.js CHANGED
@@ -297,8 +297,13 @@ var PgVector = class extends MastraVector {
297
297
  vectorExtensionInstalled = void 0;
298
298
  schemaSetupComplete = void 0;
299
299
  constructor(config) {
300
- super();
301
300
  const connectionString = typeof config === "string" ? config : config.connectionString;
301
+ if (!connectionString || typeof connectionString !== "string" || connectionString.trim() === "") {
302
+ throw new Error(
303
+ "PgVector: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
304
+ );
305
+ }
306
+ super();
302
307
  this.schema = typeof config === "string" ? void 0 : config.schemaName;
303
308
  const basePool = new pg.Pool({
304
309
  connectionString,
@@ -785,9 +790,30 @@ var PostgresStore = class extends MastraStorage {
785
790
  setupSchemaPromise = null;
786
791
  schemaSetupComplete = void 0;
787
792
  constructor(config) {
793
+ if ("connectionString" in config) {
794
+ if (!config.connectionString || typeof config.connectionString !== "string" || config.connectionString.trim() === "") {
795
+ throw new Error(
796
+ "PostgresStore: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults."
797
+ );
798
+ }
799
+ } else {
800
+ const required = ["host", "database", "user", "password"];
801
+ for (const key of required) {
802
+ if (!(key in config) || typeof config[key] !== "string" || config[key].trim() === "") {
803
+ throw new Error(
804
+ `PostgresStore: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`
805
+ );
806
+ }
807
+ }
808
+ }
788
809
  super({ name: "PostgresStore" });
789
810
  this.pgp = pgPromise();
790
- this.schema = config.schema;
811
+ if ("schema" in config && config.schema) {
812
+ console.warn(
813
+ '[DEPRECATION NOTICE] The "schema" option in PostgresStore is deprecated. Please use "schemaName" instead. Support for "schema" will be removed in a future release.'
814
+ );
815
+ }
816
+ this.schema = config.schemaName ?? config.schema;
791
817
  this.db = this.pgp(
792
818
  `connectionString` in config ? { connectionString: config.connectionString } : {
793
819
  host: config.host,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/pg",
3
- "version": "0.2.11-alpha.2",
3
+ "version": "0.3.0-alpha.4",
4
4
  "description": "Postgres provider for Mastra - includes both vector and db storage capabilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,7 +24,7 @@
24
24
  "pg": "^8.13.3",
25
25
  "pg-promise": "^11.11.0",
26
26
  "xxhash-wasm": "^1.1.0",
27
- "@mastra/core": "^0.8.4-alpha.2"
27
+ "@mastra/core": "^0.9.0-alpha.4"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@microsoft/api-extractor": "^7.52.1",
@@ -104,21 +104,65 @@ describe('PostgresStore', () => {
104
104
  }
105
105
  });
106
106
 
107
+ // --- Validation tests ---
108
+ describe('Validation', () => {
109
+ const validConfig = TEST_CONFIG;
110
+ it('throws if connectionString is empty', () => {
111
+ expect(() => new PostgresStore({ connectionString: '' })).toThrow(
112
+ /connectionString must be provided and cannot be empty/,
113
+ );
114
+ });
115
+ it('throws if host is missing or empty', () => {
116
+ expect(() => new PostgresStore({ ...validConfig, host: '' })).toThrow(
117
+ /host must be provided and cannot be empty/,
118
+ );
119
+ const { host, ...rest } = validConfig;
120
+ expect(() => new PostgresStore(rest as any)).toThrow(/host must be provided and cannot be empty/);
121
+ });
122
+ it('throws if user is missing or empty', () => {
123
+ expect(() => new PostgresStore({ ...validConfig, user: '' })).toThrow(
124
+ /user must be provided and cannot be empty/,
125
+ );
126
+ const { user, ...rest } = validConfig;
127
+ expect(() => new PostgresStore(rest as any)).toThrow(/user must be provided and cannot be empty/);
128
+ });
129
+ it('throws if database is missing or empty', () => {
130
+ expect(() => new PostgresStore({ ...validConfig, database: '' })).toThrow(
131
+ /database must be provided and cannot be empty/,
132
+ );
133
+ const { database, ...rest } = validConfig;
134
+ expect(() => new PostgresStore(rest as any)).toThrow(/database must be provided and cannot be empty/);
135
+ });
136
+ it('throws if password is missing or empty', () => {
137
+ expect(() => new PostgresStore({ ...validConfig, password: '' })).toThrow(
138
+ /password must be provided and cannot be empty/,
139
+ );
140
+ const { password, ...rest } = validConfig;
141
+ expect(() => new PostgresStore(rest as any)).toThrow(/password must be provided and cannot be empty/);
142
+ });
143
+ it('does not throw on valid config (host-based)', () => {
144
+ expect(() => new PostgresStore(validConfig)).not.toThrow();
145
+ });
146
+ it('does not throw on non-empty connection string', () => {
147
+ expect(() => new PostgresStore({ connectionString })).not.toThrow();
148
+ });
149
+ });
150
+
107
151
  describe('Thread Operations', () => {
108
152
  it('should create and retrieve a thread', async () => {
109
153
  const thread = createSampleThread();
110
154
 
111
155
  // Save thread
112
- const savedThread = await store.__saveThread({ thread });
156
+ const savedThread = await store.saveThread({ thread });
113
157
  expect(savedThread).toEqual(thread);
114
158
 
115
159
  // Retrieve thread
116
- const retrievedThread = await store.__getThreadById({ threadId: thread.id });
160
+ const retrievedThread = await store.getThreadById({ threadId: thread.id });
117
161
  expect(retrievedThread?.title).toEqual(thread.title);
118
162
  });
119
163
 
120
164
  it('should return null for non-existent thread', async () => {
121
- const result = await store.__getThreadById({ threadId: 'non-existent' });
165
+ const result = await store.getThreadById({ threadId: 'non-existent' });
122
166
  expect(result).toBeNull();
123
167
  });
124
168
 
@@ -126,20 +170,20 @@ describe('PostgresStore', () => {
126
170
  const thread1 = createSampleThread();
127
171
  const thread2 = { ...createSampleThread(), resourceId: thread1.resourceId };
128
172
 
129
- await store.__saveThread({ thread: thread1 });
130
- await store.__saveThread({ thread: thread2 });
173
+ await store.saveThread({ thread: thread1 });
174
+ await store.saveThread({ thread: thread2 });
131
175
 
132
- const threads = await store.__getThreadsByResourceId({ resourceId: thread1.resourceId });
176
+ const threads = await store.getThreadsByResourceId({ resourceId: thread1.resourceId });
133
177
  expect(threads).toHaveLength(2);
134
178
  expect(threads.map(t => t.id)).toEqual(expect.arrayContaining([thread1.id, thread2.id]));
135
179
  });
136
180
 
137
181
  it('should update thread title and metadata', async () => {
138
182
  const thread = createSampleThread();
139
- await store.__saveThread({ thread });
183
+ await store.saveThread({ thread });
140
184
 
141
185
  const newMetadata = { newKey: 'newValue' };
142
- const updatedThread = await store.__updateThread({
186
+ const updatedThread = await store.updateThread({
143
187
  id: thread.id,
144
188
  title: 'Updated Title',
145
189
  metadata: newMetadata,
@@ -152,25 +196,25 @@ describe('PostgresStore', () => {
152
196
  });
153
197
 
154
198
  // Verify persistence
155
- const retrievedThread = await store.__getThreadById({ threadId: thread.id });
199
+ const retrievedThread = await store.getThreadById({ threadId: thread.id });
156
200
  expect(retrievedThread).toEqual(updatedThread);
157
201
  });
158
202
 
159
203
  it('should delete thread and its messages', async () => {
160
204
  const thread = createSampleThread();
161
- await store.__saveThread({ thread });
205
+ await store.saveThread({ thread });
162
206
 
163
207
  // Add some messages
164
208
  const messages = [createSampleMessage(thread.id), createSampleMessage(thread.id)];
165
- await store.__saveMessages({ messages });
209
+ await store.saveMessages({ messages });
166
210
 
167
- await store.__deleteThread({ threadId: thread.id });
211
+ await store.deleteThread({ threadId: thread.id });
168
212
 
169
- const retrievedThread = await store.__getThreadById({ threadId: thread.id });
213
+ const retrievedThread = await store.getThreadById({ threadId: thread.id });
170
214
  expect(retrievedThread).toBeNull();
171
215
 
172
216
  // Verify messages were also deleted
173
- const retrievedMessages = await store.__getMessages({ threadId: thread.id });
217
+ const retrievedMessages = await store.getMessages({ threadId: thread.id });
174
218
  expect(retrievedMessages).toHaveLength(0);
175
219
  });
176
220
  });
@@ -178,28 +222,28 @@ describe('PostgresStore', () => {
178
222
  describe('Message Operations', () => {
179
223
  it('should save and retrieve messages', async () => {
180
224
  const thread = createSampleThread();
181
- await store.__saveThread({ thread });
225
+ await store.saveThread({ thread });
182
226
 
183
227
  const messages = [createSampleMessage(thread.id), createSampleMessage(thread.id)];
184
228
 
185
229
  // Save messages
186
- const savedMessages = await store.__saveMessages({ messages });
230
+ const savedMessages = await store.saveMessages({ messages });
187
231
  expect(savedMessages).toEqual(messages);
188
232
 
189
233
  // Retrieve messages
190
- const retrievedMessages = await store.__getMessages({ threadId: thread.id });
234
+ const retrievedMessages = await store.getMessages({ threadId: thread.id });
191
235
  expect(retrievedMessages).toHaveLength(2);
192
236
  expect(retrievedMessages).toEqual(expect.arrayContaining(messages));
193
237
  });
194
238
 
195
239
  it('should handle empty message array', async () => {
196
- const result = await store.__saveMessages({ messages: [] });
240
+ const result = await store.saveMessages({ messages: [] });
197
241
  expect(result).toEqual([]);
198
242
  });
199
243
 
200
244
  it('should maintain message order', async () => {
201
245
  const thread = createSampleThread();
202
- await store.__saveThread({ thread });
246
+ await store.saveThread({ thread });
203
247
 
204
248
  const messages = [
205
249
  { ...createSampleMessage(thread.id), content: [{ type: 'text', text: 'First' }] as MessageType['content'] },
@@ -207,9 +251,9 @@ describe('PostgresStore', () => {
207
251
  { ...createSampleMessage(thread.id), content: [{ type: 'text', text: 'Third' }] as MessageType['content'] },
208
252
  ];
209
253
 
210
- await store.__saveMessages({ messages });
254
+ await store.saveMessages({ messages });
211
255
 
212
- const retrievedMessages = await store.__getMessages({ threadId: thread.id });
256
+ const retrievedMessages = await store.getMessages({ threadId: thread.id });
213
257
  expect(retrievedMessages).toHaveLength(3);
214
258
 
215
259
  // Verify order is maintained
@@ -220,17 +264,17 @@ describe('PostgresStore', () => {
220
264
 
221
265
  it('should rollback on error during message save', async () => {
222
266
  const thread = createSampleThread();
223
- await store.__saveThread({ thread });
267
+ await store.saveThread({ thread });
224
268
 
225
269
  const messages = [
226
270
  createSampleMessage(thread.id),
227
271
  { ...createSampleMessage(thread.id), id: null } as any, // This will cause an error
228
272
  ];
229
273
 
230
- await expect(store.__saveMessages({ messages })).rejects.toThrow();
274
+ await expect(store.saveMessages({ messages })).rejects.toThrow();
231
275
 
232
276
  // Verify no messages were saved
233
- const savedMessages = await store.__getMessages({ threadId: thread.id });
277
+ const savedMessages = await store.getMessages({ threadId: thread.id });
234
278
  expect(savedMessages).toHaveLength(0);
235
279
  });
236
280
  });
@@ -248,8 +292,8 @@ describe('PostgresStore', () => {
248
292
  metadata: largeMetadata,
249
293
  };
250
294
 
251
- await store.__saveThread({ thread: threadWithLargeMetadata });
252
- const retrieved = await store.__getThreadById({ threadId: thread.id });
295
+ await store.saveThread({ thread: threadWithLargeMetadata });
296
+ const retrieved = await store.getThreadById({ threadId: thread.id });
253
297
 
254
298
  expect(retrieved?.metadata).toEqual(largeMetadata);
255
299
  });
@@ -260,19 +304,19 @@ describe('PostgresStore', () => {
260
304
  title: 'Special \'quotes\' and "double quotes" and emoji 🎉',
261
305
  };
262
306
 
263
- await store.__saveThread({ thread });
264
- const retrieved = await store.__getThreadById({ threadId: thread.id });
307
+ await store.saveThread({ thread });
308
+ const retrieved = await store.getThreadById({ threadId: thread.id });
265
309
 
266
310
  expect(retrieved?.title).toBe(thread.title);
267
311
  });
268
312
 
269
313
  it('should handle concurrent thread updates', async () => {
270
314
  const thread = createSampleThread();
271
- await store.__saveThread({ thread });
315
+ await store.saveThread({ thread });
272
316
 
273
317
  // Perform multiple updates concurrently
274
318
  const updates = Array.from({ length: 5 }, (_, i) =>
275
- store.__updateThread({
319
+ store.updateThread({
276
320
  id: thread.id,
277
321
  title: `Update ${i}`,
278
322
  metadata: { update: i },
@@ -282,7 +326,7 @@ describe('PostgresStore', () => {
282
326
  await expect(Promise.all(updates)).resolves.toBeDefined();
283
327
 
284
328
  // Verify final state
285
- const finalThread = await store.__getThreadById({ threadId: thread.id });
329
+ const finalThread = await store.getThreadById({ threadId: thread.id });
286
330
  expect(finalThread).toBeDefined();
287
331
  });
288
332
  });
@@ -433,7 +477,7 @@ describe('PostgresStore', () => {
433
477
  await store.clearTable({ tableName: TABLE_WORKFLOW_SNAPSHOT });
434
478
  });
435
479
  it('returns empty array when no workflows exist', async () => {
436
- const { runs, total } = await store.__getWorkflowRuns();
480
+ const { runs, total } = await store.getWorkflowRuns();
437
481
  expect(runs).toEqual([]);
438
482
  expect(total).toBe(0);
439
483
  });
@@ -449,7 +493,7 @@ describe('PostgresStore', () => {
449
493
  await new Promise(resolve => setTimeout(resolve, 10)); // Small delay to ensure different timestamps
450
494
  await store.persistWorkflowSnapshot({ workflowName: workflowName2, runId: runId2, snapshot: workflow2 });
451
495
 
452
- const { runs, total } = await store.__getWorkflowRuns();
496
+ const { runs, total } = await store.getWorkflowRuns();
453
497
  expect(runs).toHaveLength(2);
454
498
  expect(total).toBe(2);
455
499
  expect(runs[0]!.workflowName).toBe(workflowName2); // Most recent first
@@ -471,7 +515,7 @@ describe('PostgresStore', () => {
471
515
  await new Promise(resolve => setTimeout(resolve, 10)); // Small delay to ensure different timestamps
472
516
  await store.persistWorkflowSnapshot({ workflowName: workflowName2, runId: runId2, snapshot: workflow2 });
473
517
 
474
- const { runs, total } = await store.__getWorkflowRuns({ workflowName: workflowName1 });
518
+ const { runs, total } = await store.getWorkflowRuns({ workflowName: workflowName1 });
475
519
  expect(runs).toHaveLength(1);
476
520
  expect(total).toBe(1);
477
521
  expect(runs[0]!.workflowName).toBe(workflowName1);
@@ -522,7 +566,7 @@ describe('PostgresStore', () => {
522
566
  },
523
567
  });
524
568
 
525
- const { runs } = await store.__getWorkflowRuns({
569
+ const { runs } = await store.getWorkflowRuns({
526
570
  fromDate: yesterday,
527
571
  toDate: now,
528
572
  });
@@ -552,7 +596,7 @@ describe('PostgresStore', () => {
552
596
  await store.persistWorkflowSnapshot({ workflowName: workflowName3, runId: runId3, snapshot: workflow3 });
553
597
 
554
598
  // Get first page
555
- const page1 = await store.__getWorkflowRuns({ limit: 2, offset: 0 });
599
+ const page1 = await store.getWorkflowRuns({ limit: 2, offset: 0 });
556
600
  expect(page1.runs).toHaveLength(2);
557
601
  expect(page1.total).toBe(3); // Total count of all records
558
602
  expect(page1.runs[0]!.workflowName).toBe(workflowName3);
@@ -563,7 +607,7 @@ describe('PostgresStore', () => {
563
607
  expect(secondSnapshot.context?.steps[stepId2]?.status).toBe('running');
564
608
 
565
609
  // Get second page
566
- const page2 = await store.__getWorkflowRuns({ limit: 2, offset: 2 });
610
+ const page2 = await store.getWorkflowRuns({ limit: 2, offset: 2 });
567
611
  expect(page2.runs).toHaveLength(1);
568
612
  expect(page2.total).toBe(3);
569
613
  expect(page2.runs[0]!.workflowName).toBe(workflowName1);
@@ -662,7 +706,7 @@ describe('PostgresStore', () => {
662
706
  beforeAll(async () => {
663
707
  customSchemaStore = new PostgresStore({
664
708
  ...TEST_CONFIG,
665
- schema: customSchema,
709
+ schemaName: customSchema,
666
710
  });
667
711
 
668
712
  await customSchemaStore.init();
@@ -691,10 +735,10 @@ describe('PostgresStore', () => {
691
735
  it('should create and query tables in custom schema', async () => {
692
736
  // Create thread in custom schema
693
737
  const thread = createSampleThread();
694
- await customSchemaStore.__saveThread({ thread });
738
+ await customSchemaStore.saveThread({ thread });
695
739
 
696
740
  // Verify thread exists in custom schema
697
- const retrieved = await customSchemaStore.__getThreadById({ threadId: thread.id });
741
+ const retrieved = await customSchemaStore.getThreadById({ threadId: thread.id });
698
742
  expect(retrieved?.title).toBe(thread.title);
699
743
  });
700
744
 
@@ -703,19 +747,19 @@ describe('PostgresStore', () => {
703
747
  const defaultThread = createSampleThread();
704
748
  const customThread = createSampleThread();
705
749
 
706
- await store.__saveThread({ thread: defaultThread });
707
- await customSchemaStore.__saveThread({ thread: customThread });
750
+ await store.saveThread({ thread: defaultThread });
751
+ await customSchemaStore.saveThread({ thread: customThread });
708
752
 
709
753
  // Verify threads exist in respective schemas
710
- const defaultResult = await store.__getThreadById({ threadId: defaultThread.id });
711
- const customResult = await customSchemaStore.__getThreadById({ threadId: customThread.id });
754
+ const defaultResult = await store.getThreadById({ threadId: defaultThread.id });
755
+ const customResult = await customSchemaStore.getThreadById({ threadId: customThread.id });
712
756
 
713
757
  expect(defaultResult?.id).toBe(defaultThread.id);
714
758
  expect(customResult?.id).toBe(customThread.id);
715
759
 
716
760
  // Verify cross-schema isolation
717
- const defaultInCustom = await customSchemaStore.__getThreadById({ threadId: defaultThread.id });
718
- const customInDefault = await store.__getThreadById({ threadId: customThread.id });
761
+ const defaultInCustom = await customSchemaStore.getThreadById({ threadId: defaultThread.id });
762
+ const customInDefault = await store.getThreadById({ threadId: customThread.id });
719
763
 
720
764
  expect(defaultInCustom).toBeNull();
721
765
  expect(customInDefault).toBeNull();
@@ -844,7 +888,7 @@ describe('PostgresStore', () => {
844
888
  ...TEST_CONFIG,
845
889
  user: schemaRestrictedUser,
846
890
  password: restrictedPassword,
847
- schema: testSchema,
891
+ schemaName: testSchema,
848
892
  });
849
893
 
850
894
  // Create a fresh connection for verification
@@ -876,7 +920,7 @@ describe('PostgresStore', () => {
876
920
  ...TEST_CONFIG,
877
921
  user: schemaRestrictedUser,
878
922
  password: restrictedPassword,
879
- schema: testSchema,
923
+ schemaName: testSchema,
880
924
  });
881
925
 
882
926
  // Create a fresh connection for verification
@@ -887,7 +931,7 @@ describe('PostgresStore', () => {
887
931
  await expect(async () => {
888
932
  await restrictedDB.init();
889
933
  const thread = createSampleThread();
890
- await restrictedDB.__saveThread({ thread });
934
+ await restrictedDB.saveThread({ thread });
891
935
  }).rejects.toThrow(
892
936
  `Unable to create schema "${testSchema}". This requires CREATE privilege on the database.`,
893
937
  );
@@ -13,7 +13,13 @@ import type { WorkflowRunState } from '@mastra/core/workflows';
13
13
  import pgPromise from 'pg-promise';
14
14
  import type { ISSLConfig } from 'pg-promise/typescript/pg-subset';
15
15
 
16
- export type PostgresConfig = { schema?: string } & (
16
+ export type PostgresConfig = {
17
+ schemaName?: string;
18
+ /**
19
+ * @deprecated Use `schemaName` instead. Support for `schema` will be removed in a future release.
20
+ */
21
+ schema?: string;
22
+ } & (
17
23
  | {
18
24
  host: string;
19
25
  port: number;
@@ -35,9 +41,36 @@ export class PostgresStore extends MastraStorage {
35
41
  private schemaSetupComplete: boolean | undefined = undefined;
36
42
 
37
43
  constructor(config: PostgresConfig) {
44
+ // Validation: connectionString or host/database/user/password must not be empty
45
+ if ('connectionString' in config) {
46
+ if (
47
+ !config.connectionString ||
48
+ typeof config.connectionString !== 'string' ||
49
+ config.connectionString.trim() === ''
50
+ ) {
51
+ throw new Error(
52
+ 'PostgresStore: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.',
53
+ );
54
+ }
55
+ } else {
56
+ const required = ['host', 'database', 'user', 'password'];
57
+ for (const key of required) {
58
+ if (!(key in config) || typeof (config as any)[key] !== 'string' || (config as any)[key].trim() === '') {
59
+ throw new Error(
60
+ `PostgresStore: ${key} must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.`,
61
+ );
62
+ }
63
+ }
64
+ }
38
65
  super({ name: 'PostgresStore' });
39
66
  this.pgp = pgPromise();
40
- this.schema = config.schema;
67
+ // Deprecation notice for schema (old option)
68
+ if ('schema' in config && config.schema) {
69
+ console.warn(
70
+ '[DEPRECATION NOTICE] The "schema" option in PostgresStore is deprecated. Please use "schemaName" instead. Support for "schema" will be removed in a future release.',
71
+ );
72
+ }
73
+ this.schema = config.schemaName ?? config.schema;
41
74
  this.db = this.pgp(
42
75
  `connectionString` in config
43
76
  ? { connectionString: config.connectionString }
@@ -21,6 +21,24 @@ describe('PgVector', () => {
21
21
  await vectorDB.disconnect();
22
22
  });
23
23
 
24
+ // --- Validation tests ---
25
+ describe('Validation', () => {
26
+ it('throws if connectionString is empty (string)', () => {
27
+ expect(() => new PgVector('')).toThrow(/connectionString must be provided and cannot be empty/);
28
+ });
29
+ it('throws if connectionString is empty (object)', () => {
30
+ expect(() => new PgVector({ connectionString: '' })).toThrow(
31
+ /connectionString must be provided and cannot be empty/,
32
+ );
33
+ });
34
+ it('does not throw on non-empty connection string (string)', () => {
35
+ expect(() => new PgVector(connectionString)).not.toThrow();
36
+ });
37
+ it('does not throw on non-empty connection string (object)', () => {
38
+ expect(() => new PgVector({ connectionString })).not.toThrow();
39
+ });
40
+ });
41
+
24
42
  // Index Management Tests
25
43
  describe('Index Management', () => {
26
44
  describe('createIndex', () => {
@@ -329,8 +347,8 @@ describe('PgVector', () => {
329
347
  expect(results[0]?.vector).toEqual(newVector);
330
348
  });
331
349
 
332
- it('should throw exception when no updates are given', () => {
333
- expect(vectorDB.updateIndexById(testIndexName, 'id', {})).rejects.toThrow('No updates provided');
350
+ it('should throw exception when no updates are given', async () => {
351
+ await expect(vectorDB.updateIndexById(testIndexName, 'id', {})).rejects.toThrow('No updates provided');
334
352
  });
335
353
  });
336
354
 
@@ -73,9 +73,14 @@ export class PgVector extends MastraVector {
73
73
  constructor(connectionString: string);
74
74
  constructor(config: { connectionString: string; schemaName?: string });
75
75
  constructor(config: string | { connectionString: string; schemaName?: string }) {
76
+ const connectionString = typeof config === 'string' ? config : config.connectionString;
77
+ if (!connectionString || typeof connectionString !== 'string' || connectionString.trim() === '') {
78
+ throw new Error(
79
+ 'PgVector: connectionString must be provided and cannot be empty. Passing an empty string may cause fallback to local Postgres defaults.',
80
+ );
81
+ }
76
82
  super();
77
83
 
78
- const connectionString = typeof config === 'string' ? config : config.connectionString;
79
84
  this.schema = typeof config === 'string' ? undefined : config.schemaName;
80
85
 
81
86
  const basePool = new pg.Pool({