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

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 (150) 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 +59 -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 +58 -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 +179 -0
  23. package/dist/manage-record.d.ts +13 -0
  24. package/dist/manage-record.js +114 -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 +415 -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 +455 -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 +476 -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 +39 -0
  69. package/dist/serializer.d.ts +17 -0
  70. package/dist/serializer.js +136 -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 +45 -0
  80. package/dist/timescale/timescale-db.js +84 -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 +169 -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.ts +90 -0
  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 +92 -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} +60 -34
  103. package/src/{manage-record.js → manage-record.ts} +42 -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} +537 -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} +169 -97
  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} +198 -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 +53 -0
  126. package/src/{serializer.js → serializer.ts} +52 -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 +119 -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} +51 -24
  143. package/src/view.ts +22 -0
  144. package/src/belongs-to.js +0 -70
  145. package/src/has-many.js +0 -68
  146. package/src/relationships.js +0 -43
  147. package/src/timescale/timescale-db.js +0 -111
  148. package/src/transforms.js +0 -20
  149. package/src/utils.js +0 -12
  150. 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,34 @@ 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 sourceIdType = schemas[viewSchema.source]?.idType || 'number';
239
+ const schema: ModelSchema = {
240
+ table: viewSchema.viewName,
241
+ idType: sourceIdType,
242
+ columns: viewSchema.columns || {},
243
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
244
+ relationships: { belongsTo: {}, hasMany: {} },
245
+ vectorColumns: {},
246
+ memory: false,
247
+ };
169
248
  const { sql, values } = this.deps.buildSelect(schema.table);
170
249
 
171
250
  try {
172
- const result = await this.pool.query(sql, values);
251
+ const result = await this.requirePool().query(sql, values);
173
252
 
174
253
  for (const row of result.rows) {
175
254
  const rawData = this._rowToRawData(row, schema);
176
255
  this.deps.createRecord(viewName, rawData, { isDbRecord: true, serialize: false, transform: false });
177
256
  }
178
257
  } catch (error) {
179
- if (error.code === '42P01') {
180
- this.deps.log.db(`View '${viewSchema.viewName}' does not exist yet. Skipping load for '${viewName}'.`);
258
+ if (isDbError(error) && error.code === '42P01') {
259
+ this.deps.log.db!(`View '${viewSchema.viewName}' does not exist yet. Skipping load for '${viewName}'.`);
181
260
  continue;
182
261
  }
183
262
  throw error;
@@ -188,27 +267,33 @@ export default class PostgresDB {
188
267
  /**
189
268
  * @deprecated Use loadMemoryRecords() instead. Kept for backward compatibility.
190
269
  */
191
- async loadAllRecords() {
270
+ async loadAllRecords(): Promise<void> {
192
271
  return this.loadMemoryRecords();
193
272
  }
194
273
 
195
274
  /**
196
275
  * Find a single record by ID from PostgreSQL.
197
276
  * 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
277
  */
202
- async findRecord(modelName, id) {
278
+ async findRecord(modelName: string, id: string | number): Promise<OrmRecord | undefined> {
203
279
  const schemas = this.deps.introspectModels();
204
- let schema = schemas[modelName];
280
+ let schema: ModelSchema | undefined = schemas[modelName];
205
281
 
206
282
  // Check views if not found in models
207
283
  if (!schema) {
208
284
  const viewSchemas = this.deps.introspectViews();
209
285
  const viewSchema = viewSchemas[modelName];
210
286
  if (viewSchema) {
211
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
287
+ const sourceIdType = schemas[viewSchema.source]?.idType || 'number';
288
+ schema = {
289
+ table: viewSchema.viewName,
290
+ idType: sourceIdType,
291
+ columns: viewSchema.columns || {},
292
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
293
+ relationships: { belongsTo: {}, hasMany: {} },
294
+ vectorColumns: {},
295
+ memory: false,
296
+ };
212
297
  }
213
298
  }
214
299
 
@@ -217,38 +302,44 @@ export default class PostgresDB {
217
302
  const { sql, values } = this.deps.buildSelect(schema.table, { id });
218
303
 
219
304
  try {
220
- const result = await this.pool.query(sql, values);
305
+ const result = await this.requirePool().query(sql, values);
221
306
 
222
307
  if (result.rows.length === 0) return undefined;
223
308
 
224
309
  const rawData = this._rowToRawData(result.rows[0], schema);
225
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
310
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
226
311
 
227
312
  this._evictIfNotMemory(modelName, record);
228
313
 
229
314
  return record;
230
315
  } catch (error) {
231
- if (error.code === '42P01') return undefined;
316
+ if (isDbError(error) && error.code === '42P01') return undefined;
232
317
  throw error;
233
318
  }
234
319
  }
235
320
 
236
321
  /**
237
322
  * 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
323
  */
242
- async findAll(modelName, conditions) {
324
+ async findAll(modelName: string, conditions?: Record<string, unknown>): Promise<OrmRecord[]> {
243
325
  const schemas = this.deps.introspectModels();
244
- let schema = schemas[modelName];
326
+ let schema: ModelSchema | undefined = schemas[modelName];
245
327
 
246
328
  // Check views if not found in models
247
329
  if (!schema) {
248
330
  const viewSchemas = this.deps.introspectViews();
249
331
  const viewSchema = viewSchemas[modelName];
250
332
  if (viewSchema) {
251
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
333
+ const sourceIdType = schemas[viewSchema.source]?.idType || 'number';
334
+ schema = {
335
+ table: viewSchema.viewName,
336
+ idType: sourceIdType,
337
+ columns: viewSchema.columns || {},
338
+ foreignKeys: (viewSchema.foreignKeys || {}) as Record<string, ForeignKeyDef>,
339
+ relationships: { belongsTo: {}, hasMany: {} },
340
+ vectorColumns: {},
341
+ memory: false,
342
+ };
252
343
  }
253
344
  }
254
345
 
@@ -257,11 +348,11 @@ export default class PostgresDB {
257
348
  const { sql, values } = this.deps.buildSelect(schema.table, conditions);
258
349
 
259
350
  try {
260
- const result = await this.pool.query(sql, values);
351
+ const result = await this.requirePool().query(sql, values);
261
352
 
262
353
  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 });
354
+ const rawData = this._rowToRawData(row, schema!);
355
+ return this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
265
356
  });
266
357
 
267
358
  for (const record of records) {
@@ -270,22 +361,15 @@ export default class PostgresDB {
270
361
 
271
362
  return records;
272
363
  } catch (error) {
273
- if (error.code === '42P01') return [];
364
+ if (isDbError(error) && error.code === '42P01') return [];
274
365
  throw error;
275
366
  }
276
367
  }
277
368
 
278
369
  /**
279
370
  * 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
371
  */
288
- async vectorSearch(modelName, vectorColumn, queryVector, options = {}) {
372
+ async vectorSearch(modelName: string, vectorColumn: string, queryVector: number[], options: VectorSearchOptions = {}): Promise<SearchResult[]> {
289
373
  const schemas = this.deps.introspectModels();
290
374
  const schema = schemas[modelName];
291
375
  if (!schema) return [];
@@ -293,33 +377,26 @@ export default class PostgresDB {
293
377
  const { sql, values } = this.deps.buildVectorSearch(schema.table, vectorColumn, queryVector, options);
294
378
 
295
379
  try {
296
- const result = await this.pool.query(sql, values);
380
+ const result = await this.requirePool().query(sql, values);
297
381
 
298
382
  return result.rows.map(row => {
299
- const distance = row.distance;
383
+ const distance = row.distance as number;
300
384
  delete row.distance;
301
385
  const rawData = this._rowToRawData(row, schema);
302
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
386
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
303
387
  this._evictIfNotMemory(modelName, record);
304
388
  return { record, distance };
305
389
  });
306
390
  } catch (error) {
307
- if (error.code === '42P01') return [];
391
+ if (isDbError(error) && error.code === '42P01') return [];
308
392
  throw error;
309
393
  }
310
394
  }
311
395
 
312
396
  /**
313
397
  * 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
398
  */
322
- async hybridSearch(modelName, vectorColumn, queryVector, textColumn, textQuery, options = {}) {
399
+ async hybridSearch(modelName: string, vectorColumn: string, queryVector: number[], textColumn: string, textQuery: string, options: VectorSearchOptions = {}): Promise<SearchResult[]> {
323
400
  const schemas = this.deps.introspectModels();
324
401
  const schema = schemas[modelName];
325
402
  if (!schema) return [];
@@ -327,40 +404,44 @@ export default class PostgresDB {
327
404
  const { sql, values } = this.deps.buildHybridSearch(schema.table, vectorColumn, queryVector, textColumn, textQuery, options);
328
405
 
329
406
  try {
330
- const result = await this.pool.query(sql, values);
407
+ const result = await this.requirePool().query(sql, values);
331
408
 
332
409
  return result.rows.map(row => {
333
- const distance = row.distance;
410
+ const distance = row.distance as number;
334
411
  delete row.distance;
335
412
  const rawData = this._rowToRawData(row, schema);
336
- const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false });
413
+ const record = this.deps.createRecord(modelName, rawData, { isDbRecord: true, serialize: false, transform: false }) as unknown as OrmRecord;
337
414
  this._evictIfNotMemory(modelName, record);
338
415
  return { record, distance };
339
416
  });
340
417
  } catch (error) {
341
- if (error.code === '42P01') return [];
418
+ if (isDbError(error) && error.code === '42P01') return [];
342
419
  throw error;
343
420
  }
344
421
  }
345
422
 
346
423
  /**
347
424
  * 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.
425
+ * The record object itself survives -- the caller retains the reference.
349
426
  * @private
350
427
  */
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);
428
+ private _evictIfNotMemory(modelName: string, record: OrmRecord): void {
429
+ const storeRef = this.deps.store as {
430
+ _memoryResolver?: (name: string) => boolean;
431
+ get?: (name: string) => Map<unknown, unknown> | undefined;
432
+ data?: { get(name: string): Map<unknown, unknown> | undefined };
433
+ };
434
+
435
+ if (storeRef._memoryResolver && !storeRef._memoryResolver(modelName)) {
436
+ const modelStore = storeRef.get?.(modelName) ?? storeRef.data?.get(modelName);
356
437
  if (modelStore) modelStore.delete(record.id);
357
438
  }
358
439
  }
359
440
 
360
- _rowToRawData(row, schema) {
361
- const rawData = { ...row };
441
+ private _rowToRawData(row: Record<string, unknown>, schema: ModelSchema): Record<string, unknown> {
442
+ const rawData: Record<string, unknown> = { ...row };
362
443
 
363
- // PostgreSQL returns native booleans and parsed JSONB no manual conversion needed.
444
+ // PostgreSQL returns native booleans and parsed JSONB -- no manual conversion needed.
364
445
  // Only FK remapping and timestamp stripping required.
365
446
 
366
447
  // Map FK columns back to relationship keys
@@ -373,17 +454,17 @@ export default class PostgresDB {
373
454
  }
374
455
  }
375
456
 
376
- // Remove timestamp columns managed by PostgreSQL
457
+ // Remove timestamp columns -- managed by PostgreSQL
377
458
  delete rawData.created_at;
378
459
  delete rawData.updated_at;
379
460
 
380
461
  return rawData;
381
462
  }
382
463
 
383
- async persist(operation, modelName, context, response) {
384
- // Views are read-only no-op for all write operations
464
+ async persist(operation: string, modelName: string, context: PersistContext, response: PersistResponse): Promise<void> {
465
+ // Views are read-only -- no-op for all write operations
385
466
  const Orm = (await import('@stonyx/orm')).default;
386
- if (Orm.instance?.isView?.(modelName)) return;
467
+ if ((Orm.instance as { isView?: (name: string) => boolean })?.isView?.(modelName)) return;
387
468
 
388
469
  switch (operation) {
389
470
  case 'create':
@@ -395,14 +476,17 @@ export default class PostgresDB {
395
476
  }
396
477
  }
397
478
 
398
- async _persistCreate(modelName, context, response) {
479
+ private async _persistCreate(modelName: string, _context: PersistContext, response: PersistResponse): Promise<void> {
399
480
  const schemas = this.deps.introspectModels();
400
481
  const schema = schemas[modelName];
401
482
 
402
483
  if (!schema) return;
403
484
 
404
485
  const recordId = response?.data?.id;
405
- const record = recordId != null ? this.deps.store.get(modelName, isNaN(recordId) ? recordId : parseInt(recordId)) : null;
486
+ const storeRef = this.deps.store as unknown as {
487
+ get(name: string, id: unknown): OrmRecord | null;
488
+ };
489
+ const record = recordId != null ? storeRef.get(modelName, isNaN(recordId as number) ? recordId : parseInt(recordId as string)) : null;
406
490
 
407
491
  if (!record) return;
408
492
 
@@ -417,13 +501,13 @@ export default class PostgresDB {
417
501
 
418
502
  const { sql, values } = this.deps.buildInsert(schema.table, insertData);
419
503
 
420
- const result = await this.pool.query(sql, values);
504
+ const result = await this.requirePool().query(sql, values);
421
505
 
422
506
  // Re-key the record in the store if PostgreSQL generated the ID (via RETURNING)
423
507
  if (isPendingId && result.rows.length > 0) {
424
508
  const pendingId = record.id;
425
509
  const realId = result.rows[0].id;
426
- const modelStore = this.deps.store.get(modelName);
510
+ const modelStore = (this.deps.store as unknown as { get(name: string): Map<unknown, unknown> }).get(modelName);
427
511
 
428
512
  modelStore.delete(pendingId);
429
513
  record.__data.id = realId;
@@ -439,7 +523,7 @@ export default class PostgresDB {
439
523
  }
440
524
  }
441
525
 
442
- async _persistUpdate(modelName, context, response) {
526
+ private async _persistUpdate(modelName: string, context: PersistContext, _response: PersistResponse): Promise<void> {
443
527
  const schemas = this.deps.introspectModels();
444
528
  const schema = schemas[modelName];
445
529
 
@@ -453,7 +537,7 @@ export default class PostgresDB {
453
537
  const currentData = record.__data;
454
538
 
455
539
  // Build a diff of changed columns
456
- const changedData = {};
540
+ const changedData: Record<string, unknown> = {};
457
541
 
458
542
  for (const [col] of Object.entries(schema.columns)) {
459
543
  if (currentData[col] !== oldState[col]) {
@@ -464,7 +548,7 @@ export default class PostgresDB {
464
548
  // Check FK changes too
465
549
  for (const fkCol of Object.keys(schema.foreignKeys)) {
466
550
  const relName = fkCol.replace(/_id$/, '');
467
- const currentFkValue = record.__relationships[relName]?.id ?? null;
551
+ const currentFkValue = (record.__relationships[relName] as { id: unknown } | undefined)?.id ?? null;
468
552
  const oldFkValue = oldState[relName] ?? null;
469
553
 
470
554
  if (currentFkValue !== oldFkValue) {
@@ -474,14 +558,14 @@ export default class PostgresDB {
474
558
 
475
559
  if (Object.keys(changedData).length === 0) return;
476
560
 
477
- // PostgreSQL doesn't have ON UPDATE CURRENT_TIMESTAMP set updated_at manually
561
+ // PostgreSQL doesn't have ON UPDATE CURRENT_TIMESTAMP -- set updated_at manually
478
562
  changedData.updated_at = new Date();
479
563
 
480
564
  const { sql, values } = this.deps.buildUpdate(schema.table, id, changedData);
481
- await this.pool.query(sql, values);
565
+ await this.requirePool().query(sql, values);
482
566
  }
483
567
 
484
- async _persistDelete(modelName, context) {
568
+ private async _persistDelete(modelName: string, context: PersistContext): Promise<void> {
485
569
  const schemas = this.deps.introspectModels();
486
570
  const schema = schemas[modelName];
487
571
 
@@ -491,11 +575,11 @@ export default class PostgresDB {
491
575
  if (id == null) return;
492
576
 
493
577
  const { sql, values } = this.deps.buildDelete(schema.table, id);
494
- await this.pool.query(sql, values);
578
+ await this.requirePool().query(sql, values);
495
579
  }
496
580
 
497
- _recordToRow(record, schema) {
498
- const row = {};
581
+ private _recordToRow(record: OrmRecord, schema: ModelSchema): Record<string, unknown> {
582
+ const row: Record<string, unknown> = {};
499
583
  const data = record.__data;
500
584
 
501
585
  // ID
@@ -519,7 +603,7 @@ export default class PostgresDB {
519
603
  const related = record.__relationships[relName];
520
604
 
521
605
  if (related) {
522
- row[fkCol] = related.id;
606
+ row[fkCol] = (related as { id: unknown }).id;
523
607
  } else if (data[relName] !== undefined) {
524
608
  // Raw FK value (e.g., from create payload)
525
609
  row[fkCol] = data[relName];