@stonyx/orm 0.2.1-beta.83 → 0.2.1-beta.84

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.
Files changed (149) hide show
  1. package/dist/aggregates.d.ts +21 -0
  2. package/dist/aggregates.js +90 -0
  3. package/dist/attr.d.ts +2 -0
  4. package/dist/attr.js +22 -0
  5. package/dist/belongs-to.d.ts +11 -0
  6. package/dist/belongs-to.js +58 -0
  7. package/dist/cli.d.ts +22 -0
  8. package/dist/cli.js +148 -0
  9. package/dist/commands.d.ts +7 -0
  10. package/dist/commands.js +146 -0
  11. package/dist/db.d.ts +21 -0
  12. package/dist/db.js +174 -0
  13. package/dist/exports/db.d.ts +7 -0
  14. package/{src → dist}/exports/db.js +2 -4
  15. package/dist/has-many.d.ts +11 -0
  16. package/dist/has-many.js +57 -0
  17. package/dist/hooks.d.ts +47 -0
  18. package/dist/hooks.js +106 -0
  19. package/dist/index.d.ts +14 -0
  20. package/dist/index.js +34 -0
  21. package/dist/main.d.ts +46 -0
  22. package/dist/main.js +178 -0
  23. package/dist/manage-record.d.ts +13 -0
  24. package/dist/manage-record.js +113 -0
  25. package/dist/meta-request.d.ts +6 -0
  26. package/dist/meta-request.js +52 -0
  27. package/dist/migrate.d.ts +2 -0
  28. package/dist/migrate.js +57 -0
  29. package/dist/model-property.d.ts +9 -0
  30. package/dist/model-property.js +29 -0
  31. package/dist/model.d.ts +15 -0
  32. package/dist/model.js +18 -0
  33. package/dist/mysql/connection.d.ts +14 -0
  34. package/dist/mysql/connection.js +24 -0
  35. package/dist/mysql/migration-generator.d.ts +45 -0
  36. package/dist/mysql/migration-generator.js +245 -0
  37. package/dist/mysql/migration-runner.d.ts +12 -0
  38. package/dist/mysql/migration-runner.js +83 -0
  39. package/dist/mysql/mysql-db.d.ts +100 -0
  40. package/dist/mysql/mysql-db.js +411 -0
  41. package/dist/mysql/query-builder.d.ts +10 -0
  42. package/dist/mysql/query-builder.js +44 -0
  43. package/dist/mysql/schema-introspector.d.ts +19 -0
  44. package/dist/mysql/schema-introspector.js +286 -0
  45. package/dist/mysql/type-map.d.ts +21 -0
  46. package/dist/mysql/type-map.js +36 -0
  47. package/dist/orm-request.d.ts +38 -0
  48. package/dist/orm-request.js +453 -0
  49. package/dist/plural-registry.d.ts +4 -0
  50. package/{src → dist}/plural-registry.js +3 -6
  51. package/dist/postgres/connection.d.ts +15 -0
  52. package/dist/postgres/connection.js +30 -0
  53. package/dist/postgres/migration-generator.d.ts +45 -0
  54. package/dist/postgres/migration-generator.js +257 -0
  55. package/dist/postgres/migration-runner.d.ts +10 -0
  56. package/dist/postgres/migration-runner.js +82 -0
  57. package/dist/postgres/postgres-db.d.ts +119 -0
  58. package/dist/postgres/postgres-db.js +473 -0
  59. package/dist/postgres/query-builder.d.ts +27 -0
  60. package/dist/postgres/query-builder.js +98 -0
  61. package/dist/postgres/schema-introspector.d.ts +29 -0
  62. package/dist/postgres/schema-introspector.js +309 -0
  63. package/dist/postgres/type-map.d.ts +23 -0
  64. package/dist/postgres/type-map.js +53 -0
  65. package/dist/record.d.ts +75 -0
  66. package/dist/record.js +115 -0
  67. package/dist/relationships.d.ts +10 -0
  68. package/dist/relationships.js +35 -0
  69. package/dist/serializer.d.ts +17 -0
  70. package/dist/serializer.js +130 -0
  71. package/dist/setup-rest-server.d.ts +1 -0
  72. package/dist/setup-rest-server.js +54 -0
  73. package/dist/standalone-db.d.ts +58 -0
  74. package/dist/standalone-db.js +142 -0
  75. package/dist/store.d.ts +62 -0
  76. package/dist/store.js +271 -0
  77. package/dist/timescale/query-builder.d.ts +41 -0
  78. package/dist/timescale/query-builder.js +87 -0
  79. package/dist/timescale/timescale-db.d.ts +44 -0
  80. package/dist/timescale/timescale-db.js +81 -0
  81. package/dist/transforms.d.ts +2 -0
  82. package/dist/transforms.js +17 -0
  83. package/dist/types/orm-types.d.ts +142 -0
  84. package/dist/types/orm-types.js +1 -0
  85. package/dist/utils.d.ts +5 -0
  86. package/dist/utils.js +13 -0
  87. package/dist/view-resolver.d.ts +8 -0
  88. package/dist/view-resolver.js +165 -0
  89. package/dist/view.d.ts +11 -0
  90. package/dist/view.js +18 -0
  91. package/package.json +34 -11
  92. package/src/{aggregates.js → aggregates.ts} +27 -13
  93. package/src/{attr.js → attr.ts} +2 -2
  94. package/src/{belongs-to.js → belongs-to.ts} +36 -17
  95. package/src/{cli.js → cli.ts} +17 -11
  96. package/src/{commands.js → commands.ts} +179 -170
  97. package/src/{db.js → db.ts} +35 -26
  98. package/src/exports/db.ts +7 -0
  99. package/src/has-many.ts +91 -0
  100. package/src/{hooks.js → hooks.ts} +23 -27
  101. package/src/{index.js → index.ts} +4 -4
  102. package/src/{main.js → main.ts} +59 -34
  103. package/src/{manage-record.js → manage-record.ts} +41 -22
  104. package/src/{meta-request.js → meta-request.ts} +17 -14
  105. package/src/{migrate.js → migrate.ts} +9 -9
  106. package/src/{model-property.js → model-property.ts} +12 -6
  107. package/src/{model.js → model.ts} +5 -4
  108. package/src/mysql/{connection.js → connection.ts} +43 -28
  109. package/src/mysql/{migration-generator.js → migration-generator.ts} +332 -286
  110. package/src/mysql/{migration-runner.js → migration-runner.ts} +116 -110
  111. package/src/mysql/{mysql-db.js → mysql-db.ts} +533 -473
  112. package/src/mysql/{query-builder.js → query-builder.ts} +69 -64
  113. package/src/mysql/{schema-introspector.js → schema-introspector.ts} +355 -325
  114. package/src/mysql/{type-map.js → type-map.ts} +42 -37
  115. package/src/{orm-request.js → orm-request.ts} +165 -95
  116. package/src/plural-registry.ts +12 -0
  117. package/src/postgres/{connection.js → connection.ts} +14 -5
  118. package/src/postgres/{migration-generator.js → migration-generator.ts} +82 -38
  119. package/src/postgres/{migration-runner.js → migration-runner.ts} +11 -10
  120. package/src/postgres/{postgres-db.js → postgres-db.ts} +195 -114
  121. package/src/postgres/{query-builder.js → query-builder.ts} +27 -28
  122. package/src/postgres/{schema-introspector.js → schema-introspector.ts} +87 -58
  123. package/src/postgres/{type-map.js → type-map.ts} +10 -6
  124. package/src/{record.js → record.ts} +73 -34
  125. package/src/relationships.ts +48 -0
  126. package/src/{serializer.js → serializer.ts} +44 -36
  127. package/src/{setup-rest-server.js → setup-rest-server.ts} +18 -13
  128. package/src/{standalone-db.js → standalone-db.ts} +33 -24
  129. package/src/{store.js → store.ts} +90 -68
  130. package/src/timescale/{query-builder.js → query-builder.ts} +33 -38
  131. package/src/timescale/timescale-db.ts +107 -0
  132. package/src/transforms.ts +20 -0
  133. package/src/types/mysql2.d.ts +30 -0
  134. package/src/types/orm-types.ts +146 -0
  135. package/src/types/pg.d.ts +28 -0
  136. package/src/types/stonyx-cron.d.ts +5 -0
  137. package/src/types/stonyx-events.d.ts +4 -0
  138. package/src/types/stonyx-rest-server.d.ts +11 -0
  139. package/src/types/stonyx-utils.d.ts +33 -0
  140. package/src/types/stonyx.d.ts +21 -0
  141. package/src/utils.ts +16 -0
  142. package/src/{view-resolver.js → view-resolver.ts} +53 -28
  143. package/src/view.ts +22 -0
  144. package/src/has-many.js +0 -68
  145. package/src/relationships.js +0 -43
  146. package/src/timescale/timescale-db.js +0 -111
  147. package/src/transforms.js +0 -20
  148. package/src/utils.js +0 -12
  149. package/src/view.js +0 -21
@@ -3,15 +3,70 @@ import { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles, applyMi
3
3
  import { introspectModels, introspectViews, getTopologicalOrder, schemasToSnapshot } from './schema-introspector.js';
4
4
  import { loadLatestSnapshot, detectSchemaDrift } from './migration-generator.js';
5
5
  import { buildInsert, buildUpdate, buildDelete, buildSelect, buildVectorSearch, buildHybridSearch } from './query-builder.js';
6
- import { createRecord, store } from '@stonyx/orm';
6
+ import { store } from '@stonyx/orm';
7
+ import { createRecord } from '../manage-record.js';
7
8
  import { confirm } from '@stonyx/utils/prompt';
8
9
  import { readFile } from '@stonyx/utils/file';
9
10
  import { getPluralName } from '../plural-registry.js';
11
+ import { isDbError } from '../utils.js';
10
12
  import config from 'stonyx/config';
11
13
  import log from 'stonyx/log';
12
14
  import path from 'path';
15
+ import type { Pool } from 'pg';
16
+ import type { ForeignKeyDef, ModelSchema, OrmRecord } from '../types/orm-types.js';
13
17
 
14
- const defaultDeps = {
18
+ interface PersistContext {
19
+ record?: OrmRecord;
20
+ recordId?: unknown;
21
+ oldState?: Record<string, unknown>;
22
+ }
23
+
24
+ interface PersistResponse {
25
+ data?: { id: unknown };
26
+ }
27
+
28
+ interface SearchResult {
29
+ record: OrmRecord;
30
+ distance: number;
31
+ }
32
+
33
+ interface VectorSearchOptions {
34
+ limit?: number;
35
+ where?: Record<string, unknown>;
36
+ }
37
+
38
+ interface PostgresDeps {
39
+ getPool: typeof getPool;
40
+ closePool: typeof closePool;
41
+ ensureMigrationsTable: typeof ensureMigrationsTable;
42
+ getAppliedMigrations: typeof getAppliedMigrations;
43
+ getMigrationFiles: typeof getMigrationFiles;
44
+ applyMigration: typeof applyMigration;
45
+ parseMigrationFile: typeof parseMigrationFile;
46
+ introspectModels: typeof introspectModels;
47
+ introspectViews: typeof introspectViews;
48
+ getTopologicalOrder: typeof getTopologicalOrder;
49
+ schemasToSnapshot: typeof schemasToSnapshot;
50
+ loadLatestSnapshot: typeof loadLatestSnapshot;
51
+ detectSchemaDrift: typeof detectSchemaDrift;
52
+ buildInsert: typeof buildInsert;
53
+ buildUpdate: typeof buildUpdate;
54
+ buildDelete: typeof buildDelete;
55
+ buildSelect: typeof buildSelect;
56
+ buildVectorSearch: typeof buildVectorSearch;
57
+ buildHybridSearch: typeof buildHybridSearch;
58
+ createRecord: typeof createRecord;
59
+ store: typeof store;
60
+ confirm: typeof confirm;
61
+ readFile: typeof readFile;
62
+ getPluralName: typeof getPluralName;
63
+ config: typeof config;
64
+ log: typeof log;
65
+ path: typeof path;
66
+ [key: string]: unknown;
67
+ }
68
+
69
+ const defaultDeps: PostgresDeps = {
15
70
  getPool, closePool, ensureMigrationsTable, getAppliedMigrations,
16
71
  getMigrationFiles, applyMigration, parseMigrationFile,
17
72
  introspectModels, introspectViews, getTopologicalOrder, schemasToSnapshot,
@@ -21,54 +76,69 @@ const defaultDeps = {
21
76
  };
22
77
 
23
78
  export default class PostgresDB {
24
- /** @type {string[]} PostgreSQL extensions to enable on pool init. Subclasses can override. */
25
- static extensions = ['vector'];
79
+ /** PostgreSQL extensions to enable on pool init. Subclasses can override. */
80
+ static extensions: string[] = ['vector'];
81
+
82
+ /** Config key under config.orm for this adapter. Subclasses can override. */
83
+ static configKey: string = 'postgres';
84
+
85
+ /** Singleton instance, set by subclass constructor name. */
86
+ static instance: PostgresDB | undefined;
26
87
 
27
- /** @type {string} Config key under config.orm for this adapter. Subclasses can override. */
28
- static configKey = 'postgres';
88
+ deps!: PostgresDeps;
89
+ pool!: Pool | null;
90
+ pgConfig!: Record<string, unknown>;
29
91
 
30
- constructor(deps = {}) {
31
- const Ctor = this.constructor;
92
+ constructor(deps: Partial<PostgresDeps> = {}) {
93
+ const Ctor = this.constructor as typeof PostgresDB;
32
94
  if (Ctor.instance) return Ctor.instance;
33
95
  Ctor.instance = this;
34
96
 
35
- this.deps = { ...defaultDeps, ...deps };
97
+ this.deps = { ...defaultDeps, ...deps } as PostgresDeps;
36
98
  this.pool = null;
37
- this.pgConfig = this.deps.config.orm[Ctor.configKey];
99
+ this.pgConfig = this.deps.config.orm[Ctor.configKey] as Record<string, unknown>;
38
100
  }
39
101
 
40
- async init() {
41
- this.pool = await this.deps.getPool(this.pgConfig, this.constructor.extensions);
42
- await this.deps.ensureMigrationsTable(this.pool, this.pgConfig.migrationsTable);
102
+ protected requirePool(): Pool {
103
+ if (!this.pool) throw new Error('PostgresDB pool not initialized — call init() first');
104
+ return this.pool;
105
+ }
106
+
107
+ async init(): Promise<void> {
108
+ this.pool = await this.deps.getPool(
109
+ this.pgConfig as unknown as Parameters<typeof getPool>[0],
110
+ (this.constructor as typeof PostgresDB).extensions
111
+ );
112
+ await this.deps.ensureMigrationsTable(this.pool, this.pgConfig.migrationsTable as string | undefined);
43
113
  await this.loadMemoryRecords();
44
114
  }
45
115
 
46
- async startup() {
47
- const migrationsPath = this.deps.path.resolve(this.deps.config.rootPath, this.pgConfig.migrationsDir);
116
+ async startup(): Promise<void> {
117
+ const migrationsPath = this.deps.path.resolve(this.deps.config.rootPath, this.pgConfig.migrationsDir as string);
48
118
 
49
119
  // Check for pending migrations
50
- const applied = await this.deps.getAppliedMigrations(this.pool, this.pgConfig.migrationsTable);
120
+ const applied = await this.deps.getAppliedMigrations(this.requirePool(), this.pgConfig.migrationsTable as string | undefined);
51
121
  const files = await this.deps.getMigrationFiles(migrationsPath);
52
122
  const pending = files.filter(f => !applied.includes(f));
53
123
 
54
124
  if (pending.length > 0) {
55
- this.deps.log.db(`${pending.length} pending migration(s) found.`);
125
+ this.deps.log.db!(`${pending.length} pending migration(s) found.`);
56
126
 
57
127
  const shouldApply = await this.deps.confirm(`${pending.length} pending migration(s) found. Apply now?`);
58
128
 
59
129
  if (shouldApply) {
60
130
  for (const filename of pending) {
61
- const content = await this.deps.readFile(this.deps.path.join(migrationsPath, filename));
131
+ const content = await this.deps.readFile(this.deps.path.join(migrationsPath, filename)) as string;
62
132
  const { up } = this.deps.parseMigrationFile(content);
63
133
 
64
- await this.deps.applyMigration(this.pool, filename, up, this.pgConfig.migrationsTable);
65
- this.deps.log.db(`Applied migration: ${filename}`);
134
+ await this.deps.applyMigration(this.requirePool(), filename, up, this.pgConfig.migrationsTable as string | undefined);
135
+ this.deps.log.db!(`Applied migration: ${filename}`);
66
136
  }
67
137
 
68
138
  // Reload records after applying migrations
69
139
  await this.loadMemoryRecords();
70
140
  } else {
71
- this.deps.log.warn('Skipping pending migrations. Schema may be outdated.');
141
+ this.deps.log.warn!('Skipping pending migrations. Schema may be outdated.');
72
142
  }
73
143
  } else if (files.length === 0) {
74
144
  const schemas = this.deps.introspectModels();
@@ -85,52 +155,52 @@ export default class PostgresDB {
85
155
 
86
156
  if (result) {
87
157
  const { up } = this.deps.parseMigrationFile(result.content);
88
- await this.deps.applyMigration(this.pool, result.filename, up, this.pgConfig.migrationsTable);
89
- this.deps.log.db(`Applied migration: ${result.filename}`);
158
+ await this.deps.applyMigration(this.requirePool(), result.filename, up, this.pgConfig.migrationsTable as string | undefined);
159
+ this.deps.log.db!(`Applied migration: ${result.filename}`);
90
160
  await this.loadMemoryRecords();
91
161
  }
92
162
  } else {
93
- this.deps.log.warn('Skipping initial migration. Tables may not exist.');
163
+ this.deps.log.warn!('Skipping initial migration. Tables may not exist.');
94
164
  }
95
165
  }
96
166
  }
97
167
 
98
168
  // Check for schema drift
99
169
  const schemas = this.deps.introspectModels();
100
- const snapshot = await this.deps.loadLatestSnapshot(this.deps.path.resolve(this.deps.config.rootPath, this.pgConfig.migrationsDir));
170
+ const snapshot = await this.deps.loadLatestSnapshot(this.deps.path.resolve(this.deps.config.rootPath, this.pgConfig.migrationsDir as string));
101
171
 
102
172
  if (Object.keys(snapshot).length > 0) {
103
- const drift = this.deps.detectSchemaDrift(schemas, snapshot);
173
+ const drift = this.deps.detectSchemaDrift(schemas, snapshot as Parameters<typeof detectSchemaDrift>[1]);
104
174
 
105
175
  if (drift.hasChanges) {
106
- this.deps.log.warn('Schema drift detected: models have changed since the last migration.');
107
- this.deps.log.warn('Run `stonyx db:generate-migration` to create a new migration.');
176
+ this.deps.log.warn!('Schema drift detected: models have changed since the last migration.');
177
+ this.deps.log.warn!('Run `stonyx db:generate-migration` to create a new migration.');
108
178
  }
109
179
  }
110
180
  }
111
181
 
112
- async shutdown() {
182
+ async shutdown(): Promise<void> {
113
183
  await this.deps.closePool();
114
184
  this.pool = null;
115
185
  }
116
186
 
117
- async save() {
187
+ async save(): Promise<void> {
118
188
  // No-op: PostgreSQL persists data immediately via persist()
119
189
  }
120
190
 
121
191
  /**
122
192
  * Loads only models with memory: true into the in-memory store on startup.
123
- * Models with memory: false are skipped accessed on-demand via find()/findAll().
193
+ * Models with memory: false are skipped -- accessed on-demand via find()/findAll().
124
194
  */
125
- async loadMemoryRecords() {
195
+ async loadMemoryRecords(): Promise<void> {
126
196
  const schemas = this.deps.introspectModels();
127
197
  const order = this.deps.getTopologicalOrder(schemas);
128
198
  const Orm = (await import('@stonyx/orm')).default;
129
199
 
130
200
  for (const modelName of order) {
131
- const { modelClass } = Orm.instance.getRecordClasses(modelName);
201
+ const { modelClass } = Orm.instance.getRecordClasses(modelName) as { modelClass: { memory?: boolean } };
132
202
  if (modelClass?.memory === false) {
133
- this.deps.log.db(`Skipping memory load for '${modelName}' (memory: false)`);
203
+ this.deps.log.db!(`Skipping memory load for '${modelName}' (memory: false)`);
134
204
  continue;
135
205
  }
136
206
 
@@ -138,7 +208,7 @@ export default class PostgresDB {
138
208
  const { sql, values } = this.deps.buildSelect(schema.table);
139
209
 
140
210
  try {
141
- const result = await this.pool.query(sql, values);
211
+ const result = await this.requirePool().query(sql, values);
142
212
 
143
213
  for (const row of result.rows) {
144
214
  const rawData = this._rowToRawData(row, schema);
@@ -146,8 +216,8 @@ export default class PostgresDB {
146
216
  }
147
217
  } catch (error) {
148
218
  // 42P01 = undefined_table (PG equivalent of ER_NO_SUCH_TABLE)
149
- if (error.code === '42P01') {
150
- this.deps.log.db(`Table '${schema.table}' does not exist yet. Skipping load for '${modelName}'.`);
219
+ if (isDbError(error) && error.code === '42P01') {
220
+ this.deps.log.db!(`Table '${schema.table}' does not exist yet. Skipping load for '${modelName}'.`);
151
221
  continue;
152
222
  }
153
223
 
@@ -159,25 +229,33 @@ export default class PostgresDB {
159
229
  const viewSchemas = this.deps.introspectViews();
160
230
 
161
231
  for (const [viewName, viewSchema] of Object.entries(viewSchemas)) {
162
- const { modelClass: viewClass } = Orm.instance.getRecordClasses(viewName);
232
+ const { modelClass: viewClass } = Orm.instance.getRecordClasses(viewName) as { modelClass: { memory?: boolean } };
163
233
  if (viewClass?.memory !== true) {
164
- this.deps.log.db(`Skipping memory load for view '${viewName}' (memory: false)`);
234
+ this.deps.log.db!(`Skipping memory load for view '${viewName}' (memory: false)`);
165
235
  continue;
166
236
  }
167
237
 
168
- const schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
238
+ const schema: ModelSchema = {
239
+ table: viewSchema.viewName,
240
+ idType: 'number',
241
+ columns: viewSchema.columns || {},
242
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
243
+ relationships: { belongsTo: {}, hasMany: {} },
244
+ vectorColumns: {},
245
+ memory: false,
246
+ };
169
247
  const { sql, values } = this.deps.buildSelect(schema.table);
170
248
 
171
249
  try {
172
- const result = await this.pool.query(sql, values);
250
+ const result = await this.requirePool().query(sql, values);
173
251
 
174
252
  for (const row of result.rows) {
175
253
  const rawData = this._rowToRawData(row, schema);
176
254
  this.deps.createRecord(viewName, rawData, { isDbRecord: true, serialize: false, transform: false });
177
255
  }
178
256
  } catch (error) {
179
- if (error.code === '42P01') {
180
- this.deps.log.db(`View '${viewSchema.viewName}' does not exist yet. Skipping load for '${viewName}'.`);
257
+ if (isDbError(error) && error.code === '42P01') {
258
+ this.deps.log.db!(`View '${viewSchema.viewName}' does not exist yet. Skipping load for '${viewName}'.`);
181
259
  continue;
182
260
  }
183
261
  throw error;
@@ -188,27 +266,32 @@ export default class PostgresDB {
188
266
  /**
189
267
  * @deprecated Use loadMemoryRecords() instead. Kept for backward compatibility.
190
268
  */
191
- async loadAllRecords() {
269
+ async loadAllRecords(): Promise<void> {
192
270
  return this.loadMemoryRecords();
193
271
  }
194
272
 
195
273
  /**
196
274
  * Find a single record by ID from PostgreSQL.
197
275
  * Does NOT cache the result in the store for memory: false models.
198
- * @param {string} modelName
199
- * @param {string|number} id
200
- * @returns {Promise<Record|undefined>}
201
276
  */
202
- async findRecord(modelName, id) {
277
+ async findRecord(modelName: string, id: string | number): Promise<OrmRecord | undefined> {
203
278
  const schemas = this.deps.introspectModels();
204
- let schema = schemas[modelName];
279
+ let schema: ModelSchema | undefined = schemas[modelName];
205
280
 
206
281
  // Check views if not found in models
207
282
  if (!schema) {
208
283
  const viewSchemas = this.deps.introspectViews();
209
284
  const viewSchema = viewSchemas[modelName];
210
285
  if (viewSchema) {
211
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
286
+ schema = {
287
+ table: viewSchema.viewName,
288
+ idType: 'number',
289
+ columns: viewSchema.columns || {},
290
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
291
+ relationships: { belongsTo: {}, hasMany: {} },
292
+ vectorColumns: {},
293
+ memory: false,
294
+ };
212
295
  }
213
296
  }
214
297
 
@@ -217,38 +300,43 @@ export default class PostgresDB {
217
300
  const { sql, values } = this.deps.buildSelect(schema.table, { id });
218
301
 
219
302
  try {
220
- const result = await this.pool.query(sql, values);
303
+ const result = await this.requirePool().query(sql, values);
221
304
 
222
305
  if (result.rows.length === 0) return undefined;
223
306
 
224
307
  const rawData = this._rowToRawData(result.rows[0], schema);
225
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
308
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
226
309
 
227
310
  this._evictIfNotMemory(modelName, record);
228
311
 
229
312
  return record;
230
313
  } catch (error) {
231
- if (error.code === '42P01') return undefined;
314
+ if (isDbError(error) && error.code === '42P01') return undefined;
232
315
  throw error;
233
316
  }
234
317
  }
235
318
 
236
319
  /**
237
320
  * Find all records of a model from PostgreSQL, with optional conditions.
238
- * @param {string} modelName
239
- * @param {Object} [conditions] - Optional WHERE conditions (key-value pairs)
240
- * @returns {Promise<Record[]>}
241
321
  */
242
- async findAll(modelName, conditions) {
322
+ async findAll(modelName: string, conditions?: Record<string, unknown>): Promise<OrmRecord[]> {
243
323
  const schemas = this.deps.introspectModels();
244
- let schema = schemas[modelName];
324
+ let schema: ModelSchema | undefined = schemas[modelName];
245
325
 
246
326
  // Check views if not found in models
247
327
  if (!schema) {
248
328
  const viewSchemas = this.deps.introspectViews();
249
329
  const viewSchema = viewSchemas[modelName];
250
330
  if (viewSchema) {
251
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
331
+ schema = {
332
+ table: viewSchema.viewName,
333
+ idType: 'number',
334
+ columns: viewSchema.columns || {},
335
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
336
+ relationships: { belongsTo: {}, hasMany: {} },
337
+ vectorColumns: {},
338
+ memory: false,
339
+ };
252
340
  }
253
341
  }
254
342
 
@@ -257,11 +345,11 @@ export default class PostgresDB {
257
345
  const { sql, values } = this.deps.buildSelect(schema.table, conditions);
258
346
 
259
347
  try {
260
- const result = await this.pool.query(sql, values);
348
+ const result = await this.requirePool().query(sql, values);
261
349
 
262
350
  const records = result.rows.map(row => {
263
- const rawData = this._rowToRawData(row, schema);
264
- return this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
351
+ const rawData = this._rowToRawData(row, schema!);
352
+ return this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
265
353
  });
266
354
 
267
355
  for (const record of records) {
@@ -270,22 +358,15 @@ export default class PostgresDB {
270
358
 
271
359
  return records;
272
360
  } catch (error) {
273
- if (error.code === '42P01') return [];
361
+ if (isDbError(error) && error.code === '42P01') return [];
274
362
  throw error;
275
363
  }
276
364
  }
277
365
 
278
366
  /**
279
367
  * Perform a vector similarity search using cosine distance.
280
- * @param {string} modelName
281
- * @param {string} vectorColumn - Name of the vector column
282
- * @param {number[]} queryVector - The query vector
283
- * @param {Object} [options]
284
- * @param {number} [options.limit=10]
285
- * @param {Object} [options.where] - Additional conditions
286
- * @returns {Promise<{ record: Record, distance: number }[]>}
287
368
  */
288
- async vectorSearch(modelName, vectorColumn, queryVector, options = {}) {
369
+ async vectorSearch(modelName: string, vectorColumn: string, queryVector: number[], options: VectorSearchOptions = {}): Promise<SearchResult[]> {
289
370
  const schemas = this.deps.introspectModels();
290
371
  const schema = schemas[modelName];
291
372
  if (!schema) return [];
@@ -293,33 +374,26 @@ export default class PostgresDB {
293
374
  const { sql, values } = this.deps.buildVectorSearch(schema.table, vectorColumn, queryVector, options);
294
375
 
295
376
  try {
296
- const result = await this.pool.query(sql, values);
377
+ const result = await this.requirePool().query(sql, values);
297
378
 
298
379
  return result.rows.map(row => {
299
- const distance = row.distance;
380
+ const distance = row.distance as number;
300
381
  delete row.distance;
301
382
  const rawData = this._rowToRawData(row, schema);
302
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
383
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
303
384
  this._evictIfNotMemory(modelName, record);
304
385
  return { record, distance };
305
386
  });
306
387
  } catch (error) {
307
- if (error.code === '42P01') return [];
388
+ if (isDbError(error) && error.code === '42P01') return [];
308
389
  throw error;
309
390
  }
310
391
  }
311
392
 
312
393
  /**
313
394
  * Perform a hybrid search combining vector similarity with text filtering.
314
- * @param {string} modelName
315
- * @param {string} vectorColumn
316
- * @param {number[]} queryVector
317
- * @param {string} textColumn
318
- * @param {string} textQuery
319
- * @param {Object} [options]
320
- * @returns {Promise<{ record: Record, distance: number }[]>}
321
395
  */
322
- async hybridSearch(modelName, vectorColumn, queryVector, textColumn, textQuery, options = {}) {
396
+ async hybridSearch(modelName: string, vectorColumn: string, queryVector: number[], textColumn: string, textQuery: string, options: VectorSearchOptions = {}): Promise<SearchResult[]> {
323
397
  const schemas = this.deps.introspectModels();
324
398
  const schema = schemas[modelName];
325
399
  if (!schema) return [];
@@ -327,40 +401,44 @@ export default class PostgresDB {
327
401
  const { sql, values } = this.deps.buildHybridSearch(schema.table, vectorColumn, queryVector, textColumn, textQuery, options);
328
402
 
329
403
  try {
330
- const result = await this.pool.query(sql, values);
404
+ const result = await this.requirePool().query(sql, values);
331
405
 
332
406
  return result.rows.map(row => {
333
- const distance = row.distance;
407
+ const distance = row.distance as number;
334
408
  delete row.distance;
335
409
  const rawData = this._rowToRawData(row, schema);
336
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
410
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
337
411
  this._evictIfNotMemory(modelName, record);
338
412
  return { record, distance };
339
413
  });
340
414
  } catch (error) {
341
- if (error.code === '42P01') return [];
415
+ if (isDbError(error) && error.code === '42P01') return [];
342
416
  throw error;
343
417
  }
344
418
  }
345
419
 
346
420
  /**
347
421
  * Remove a record from the in-memory store if its model has memory: false.
348
- * The record object itself survives the caller retains the reference.
422
+ * The record object itself survives -- the caller retains the reference.
349
423
  * @private
350
424
  */
351
- _evictIfNotMemory(modelName, record) {
352
- const store = this.deps.store;
353
-
354
- if (store._memoryResolver && !store._memoryResolver(modelName)) {
355
- const modelStore = store.get?.(modelName) ?? store.data?.get(modelName);
425
+ private _evictIfNotMemory(modelName: string, record: OrmRecord): void {
426
+ const storeRef = this.deps.store as unknown as {
427
+ _memoryResolver?: (name: string) => boolean;
428
+ get?: (name: string) => Map<unknown, unknown> | undefined;
429
+ data?: { get(name: string): Map<unknown, unknown> | undefined };
430
+ };
431
+
432
+ if (storeRef._memoryResolver && !storeRef._memoryResolver(modelName)) {
433
+ const modelStore = storeRef.get?.(modelName) ?? storeRef.data?.get(modelName);
356
434
  if (modelStore) modelStore.delete(record.id);
357
435
  }
358
436
  }
359
437
 
360
- _rowToRawData(row, schema) {
361
- const rawData = { ...row };
438
+ private _rowToRawData(row: Record<string, unknown>, schema: ModelSchema): Record<string, unknown> {
439
+ const rawData: Record<string, unknown> = { ...row };
362
440
 
363
- // PostgreSQL returns native booleans and parsed JSONB no manual conversion needed.
441
+ // PostgreSQL returns native booleans and parsed JSONB -- no manual conversion needed.
364
442
  // Only FK remapping and timestamp stripping required.
365
443
 
366
444
  // Map FK columns back to relationship keys
@@ -373,17 +451,17 @@ export default class PostgresDB {
373
451
  }
374
452
  }
375
453
 
376
- // Remove timestamp columns managed by PostgreSQL
454
+ // Remove timestamp columns -- managed by PostgreSQL
377
455
  delete rawData.created_at;
378
456
  delete rawData.updated_at;
379
457
 
380
458
  return rawData;
381
459
  }
382
460
 
383
- async persist(operation, modelName, context, response) {
384
- // Views are read-only no-op for all write operations
461
+ async persist(operation: string, modelName: string, context: PersistContext, response: PersistResponse): Promise<void> {
462
+ // Views are read-only -- no-op for all write operations
385
463
  const Orm = (await import('@stonyx/orm')).default;
386
- if (Orm.instance?.isView?.(modelName)) return;
464
+ if ((Orm.instance as { isView?: (name: string) => boolean })?.isView?.(modelName)) return;
387
465
 
388
466
  switch (operation) {
389
467
  case 'create':
@@ -395,14 +473,17 @@ export default class PostgresDB {
395
473
  }
396
474
  }
397
475
 
398
- async _persistCreate(modelName, context, response) {
476
+ private async _persistCreate(modelName: string, _context: PersistContext, response: PersistResponse): Promise<void> {
399
477
  const schemas = this.deps.introspectModels();
400
478
  const schema = schemas[modelName];
401
479
 
402
480
  if (!schema) return;
403
481
 
404
482
  const recordId = response?.data?.id;
405
- const record = recordId != null ? this.deps.store.get(modelName, isNaN(recordId) ? recordId : parseInt(recordId)) : null;
483
+ const storeRef = this.deps.store as unknown as {
484
+ get(name: string, id: unknown): OrmRecord | null;
485
+ };
486
+ const record = recordId != null ? storeRef.get(modelName, isNaN(recordId as number) ? recordId : parseInt(recordId as string)) : null;
406
487
 
407
488
  if (!record) return;
408
489
 
@@ -417,13 +498,13 @@ export default class PostgresDB {
417
498
 
418
499
  const { sql, values } = this.deps.buildInsert(schema.table, insertData);
419
500
 
420
- const result = await this.pool.query(sql, values);
501
+ const result = await this.requirePool().query(sql, values);
421
502
 
422
503
  // Re-key the record in the store if PostgreSQL generated the ID (via RETURNING)
423
504
  if (isPendingId && result.rows.length > 0) {
424
505
  const pendingId = record.id;
425
506
  const realId = result.rows[0].id;
426
- const modelStore = this.deps.store.get(modelName);
507
+ const modelStore = (this.deps.store as unknown as { get(name: string): Map<unknown, unknown> }).get(modelName);
427
508
 
428
509
  modelStore.delete(pendingId);
429
510
  record.__data.id = realId;
@@ -439,7 +520,7 @@ export default class PostgresDB {
439
520
  }
440
521
  }
441
522
 
442
- async _persistUpdate(modelName, context, response) {
523
+ private async _persistUpdate(modelName: string, context: PersistContext, _response: PersistResponse): Promise<void> {
443
524
  const schemas = this.deps.introspectModels();
444
525
  const schema = schemas[modelName];
445
526
 
@@ -453,7 +534,7 @@ export default class PostgresDB {
453
534
  const currentData = record.__data;
454
535
 
455
536
  // Build a diff of changed columns
456
- const changedData = {};
537
+ const changedData: Record<string, unknown> = {};
457
538
 
458
539
  for (const [col] of Object.entries(schema.columns)) {
459
540
  if (currentData[col] !== oldState[col]) {
@@ -464,7 +545,7 @@ export default class PostgresDB {
464
545
  // Check FK changes too
465
546
  for (const fkCol of Object.keys(schema.foreignKeys)) {
466
547
  const relName = fkCol.replace(/_id$/, '');
467
- const currentFkValue = record.__relationships[relName]?.id ?? null;
548
+ const currentFkValue = (record.__relationships[relName] as { id: unknown } | undefined)?.id ?? null;
468
549
  const oldFkValue = oldState[relName] ?? null;
469
550
 
470
551
  if (currentFkValue !== oldFkValue) {
@@ -474,14 +555,14 @@ export default class PostgresDB {
474
555
 
475
556
  if (Object.keys(changedData).length === 0) return;
476
557
 
477
- // PostgreSQL doesn't have ON UPDATE CURRENT_TIMESTAMP set updated_at manually
558
+ // PostgreSQL doesn't have ON UPDATE CURRENT_TIMESTAMP -- set updated_at manually
478
559
  changedData.updated_at = new Date();
479
560
 
480
561
  const { sql, values } = this.deps.buildUpdate(schema.table, id, changedData);
481
- await this.pool.query(sql, values);
562
+ await this.requirePool().query(sql, values);
482
563
  }
483
564
 
484
- async _persistDelete(modelName, context) {
565
+ private async _persistDelete(modelName: string, context: PersistContext): Promise<void> {
485
566
  const schemas = this.deps.introspectModels();
486
567
  const schema = schemas[modelName];
487
568
 
@@ -491,11 +572,11 @@ export default class PostgresDB {
491
572
  if (id == null) return;
492
573
 
493
574
  const { sql, values } = this.deps.buildDelete(schema.table, id);
494
- await this.pool.query(sql, values);
575
+ await this.requirePool().query(sql, values);
495
576
  }
496
577
 
497
- _recordToRow(record, schema) {
498
- const row = {};
578
+ private _recordToRow(record: OrmRecord, schema: ModelSchema): Record<string, unknown> {
579
+ const row: Record<string, unknown> = {};
499
580
  const data = record.__data;
500
581
 
501
582
  // ID
@@ -519,7 +600,7 @@ export default class PostgresDB {
519
600
  const related = record.__relationships[relName];
520
601
 
521
602
  if (related) {
522
- row[fkCol] = related.id;
603
+ row[fkCol] = (related as { id: unknown }).id;
523
604
  } else if (data[relName] !== undefined) {
524
605
  // Raw FK value (e.g., from create payload)
525
606
  row[fkCol] = data[relName];