@stonyx/orm 0.2.1-alpha.12 → 0.2.1-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.claude/index.md CHANGED
@@ -3,7 +3,6 @@
3
3
  ## Detailed Guides
4
4
 
5
5
  - [Usage Patterns](usage-patterns.md) — Model definitions, serializers, transforms, CRUD, DB schema, persistence, access control, REST API, and include parameters
6
- - [Views](views.md) — Read-only computed views with aggregate helpers, in-memory resolver, and MySQL VIEW auto-generation
7
6
  - [Middleware Hooks System](hooks.md) — Before/after hooks for CRUD operations, halting, context object, change detection, and testing
8
7
  - [Code Style Rules](code-style-rules.md) — Strict prettier/eslint rules to apply across all Stonyx projects
9
8
 
@@ -22,7 +21,6 @@
22
21
  5. **REST API Generation**: Auto-generated RESTful endpoints with access control
23
22
  6. **Data Transformation**: Custom type conversion and formatting
24
23
  7. **Middleware Hooks**: Before/after hooks for all CRUD operations with halting capability
25
- 8. **Views**: Read-only computed projections over model data with aggregate helpers (count, avg, sum, min, max)
26
24
 
27
25
  ---
28
26
 
@@ -40,9 +38,6 @@
40
38
  8. **Include Logic** (inline in [src/orm-request.js](src/orm-request.js)) - Parses include query params, traverses relationships, collects and deduplicates included records
41
39
  9. **Hooks** ([src/hooks.js](src/hooks.js)) - Middleware-based hook registry for CRUD lifecycle
42
40
  10. **MySQL Driver** ([src/mysql/mysql-db.js](src/mysql/mysql-db.js)) - MySQL persistence, migrations, schema introspection. Loads records in topological order. `_rowToRawData()` converts TINYINT(1) → boolean, remaps FK columns, strips timestamps.
43
- 11. **View** ([src/view.js](src/view.js)) - Read-only base class for computed views (does NOT extend Model)
44
- 12. **Aggregates** ([src/aggregates.js](src/aggregates.js)) - AggregateProperty class + helper functions (count, avg, sum, min, max)
45
- 13. **ViewResolver** ([src/view-resolver.js](src/view-resolver.js)) - In-memory view resolver (iterates source model, computes aggregates + resolve map)
46
41
 
47
42
  ### Project Structure
48
43
 
@@ -71,9 +66,6 @@ stonyx-orm/
71
66
  │ ├── commands.js # CLI commands (db:migrate-*, etc.)
72
67
  │ ├── utils.js # Pluralize wrapper for dasherized names
73
68
  │ ├── plural-registry.js # Plural name registry (populated at init, supports Model.pluralName overrides)
74
- │ ├── view.js # View base class (read-only, source, resolve, memory)
75
- │ ├── aggregates.js # AggregateProperty + count/avg/sum/min/max helpers
76
- │ ├── view-resolver.js # In-memory view resolver
77
69
  │ ├── exports/
78
70
  │ │ └── db.js # Convenience re-export of DB instance
79
71
  │ └── mysql/
@@ -94,7 +86,6 @@ stonyx-orm/
94
86
  │ ├── serializers/ # Example serializers
95
87
  │ ├── transforms/ # Custom transforms
96
88
  │ ├── access/ # Access control
97
- │ ├── views/ # Example views
98
89
  │ ├── db-schema.js # DB schema
99
90
  │ └── payload.js # Test data
100
91
  └── package.json
@@ -112,8 +103,7 @@ config.orm = {
112
103
  model: './models',
113
104
  serializer: './serializers',
114
105
  transform: './transforms',
115
- access: './access',
116
- view: './views'
106
+ access: './access'
117
107
  },
118
108
  db: {
119
109
  autosave: 'false',
@@ -239,10 +229,9 @@ The ORM supports two storage modes, configured via `db.mode`:
239
229
  **Import the ORM:**
240
230
  ```javascript
241
231
  import {
242
- Orm, Model, View, Serializer, attr, hasMany, belongsTo,
232
+ Orm, Model, Serializer, attr, hasMany, belongsTo,
243
233
  createRecord, updateRecord, store,
244
- beforeHook, afterHook, clearHook, clearAllHooks,
245
- count, avg, sum, min, max
234
+ beforeHook, afterHook, clearHook, clearAllHooks
246
235
  } from '@stonyx/orm';
247
236
  ```
248
237
 
@@ -232,69 +232,3 @@ GET /scenes/e001-s001?include=slides.dialogue.character
232
232
  - `traverseIncludePath()` - Recursively traverses relationship paths
233
233
  - `collectIncludedRecords()` - Orchestrates traversal and deduplication
234
234
  - All implemented in [src/orm-request.js](src/orm-request.js)
235
-
236
- ## 10. Views (Read-Only Computed Data)
237
-
238
- Views are read-only projections that compute derived data from existing models. They work in both JSON mode (in-memory) and MySQL mode (auto-generated SQL VIEWs). See the full guide at [views.md](views.md).
239
-
240
- ### Defining a View
241
-
242
- ```javascript
243
- // views/owner-stats.js
244
- import { View, attr, belongsTo, count, avg } from '@stonyx/orm';
245
-
246
- export default class OwnerStatsView extends View {
247
- static source = 'owner'; // Required: model whose records produce view records
248
-
249
- animalCount = count('pets'); // COUNT of hasMany relationship
250
- averageAge = avg('pets', 'age'); // AVG of a field on related records
251
- owner = belongsTo('owner'); // Link back to source record
252
- }
253
- ```
254
-
255
- ### Aggregate Helpers
256
-
257
- | Helper | Example | JS Behavior | MySQL |
258
- |--------|---------|-------------|-------|
259
- | `count(rel)` | `count('pets')` | `records.length` | `COUNT(table.id)` |
260
- | `avg(rel, field)` | `avg('pets', 'age')` | Average of values | `AVG(table.field)` |
261
- | `sum(rel, field)` | `sum('pets', 'age')` | Sum of values | `SUM(table.field)` |
262
- | `min(rel, field)` | `min('pets', 'age')` | Minimum value | `MIN(table.field)` |
263
- | `max(rel, field)` | `max('pets', 'age')` | Maximum value | `MAX(table.field)` |
264
-
265
- ### Resolve Map (Escape Hatch)
266
-
267
- For fields that can't be expressed as aggregates:
268
-
269
- ```javascript
270
- export default class OwnerStatsView extends View {
271
- static source = 'owner';
272
- static resolve = {
273
- gender: 'gender', // String path from source data
274
- score: (owner) => owner.__data.age * 10, // Function
275
- };
276
-
277
- gender = attr('string'); // Must also define as attr()
278
- score = attr('number');
279
- animalCount = count('pets');
280
- }
281
- ```
282
-
283
- ### Querying Views
284
-
285
- ```javascript
286
- const stats = await store.findAll('owner-stats');
287
- const stat = await store.find('owner-stats', ownerId);
288
- ```
289
-
290
- ### Read-Only Enforcement
291
-
292
- ```javascript
293
- createRecord('owner-stats', data); // Throws: Cannot create records for read-only view
294
- updateRecord(viewRecord, data); // Throws: Cannot update records for read-only view
295
- store.remove('owner-stats', id); // Throws: Cannot remove records from read-only view
296
- ```
297
-
298
- ### REST API
299
-
300
- Only GET endpoints are mounted for views — no POST, PATCH, or DELETE.
@@ -4,7 +4,6 @@ const {
4
4
  ORM_REST_ROUTE,
5
5
  ORM_SERIALIZER_PATH,
6
6
  ORM_TRANSFORM_PATH,
7
- ORM_VIEW_PATH,
8
7
  ORM_USE_REST_SERVER,
9
8
  DB_AUTO_SAVE,
10
9
  DB_FILE,
@@ -37,8 +36,7 @@ export default {
37
36
  access: ORM_ACCESS_PATH ?? './access', // Optional for restServer access hooks
38
37
  model: ORM_MODEL_PATH ?? './models',
39
38
  serializer: ORM_SERIALIZER_PATH ?? './serializers',
40
- transform: ORM_TRANSFORM_PATH ?? './transforms',
41
- view: ORM_VIEW_PATH ?? './views'
39
+ transform: ORM_TRANSFORM_PATH ?? './transforms'
42
40
  },
43
41
  mysql: MYSQL_HOST ? {
44
42
  host: MYSQL_HOST ?? 'localhost',
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.2.1-alpha.12",
7
+ "version": "0.2.1-alpha.13",
8
8
  "description": "",
9
9
  "main": "src/main.js",
10
10
  "type": "module",
@@ -33,9 +33,9 @@
33
33
  },
34
34
  "homepage": "https://github.com/abofs/stonyx-orm#readme",
35
35
  "dependencies": {
36
- "stonyx": "0.2.3-beta.5",
37
- "@stonyx/events": "0.1.1-beta.6",
38
- "@stonyx/cron": "0.2.1-beta.10"
36
+ "stonyx": "0.2.3-beta.6",
37
+ "@stonyx/events": "0.1.1-beta.7",
38
+ "@stonyx/cron": "0.2.1-beta.12"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "mysql2": "^3.0.0"
@@ -46,8 +46,8 @@
46
46
  }
47
47
  },
48
48
  "devDependencies": {
49
- "@stonyx/rest-server": "0.2.1-beta.14",
50
- "@stonyx/utils": "0.2.3-beta.4",
49
+ "@stonyx/rest-server": "0.2.1-beta.16",
50
+ "@stonyx/utils": "0.2.3-beta.5",
51
51
  "qunit": "^2.24.1",
52
52
  "sinon": "^21.0.0"
53
53
  },
package/src/index.js CHANGED
@@ -15,20 +15,17 @@
15
15
  */
16
16
 
17
17
  import Model from './model.js';
18
- import View from './view.js';
19
18
  import Serializer from './serializer.js';
20
19
 
21
20
  import attr from './attr.js';
22
21
  import belongsTo from './belongs-to.js';
23
22
  import hasMany from './has-many.js';
24
23
  import { createRecord, updateRecord } from './manage-record.js';
25
- import { count, avg, sum, min, max } from './aggregates.js';
26
24
 
27
25
  export { default } from './main.js';
28
26
  export { store, relationships } from './main.js';
29
- export { Model, View, Serializer }; // base classes
27
+ export { Model, Serializer }; // base classes
30
28
  export { attr, belongsTo, hasMany, createRecord, updateRecord }; // helpers
31
- export { count, avg, sum, min, max }; // aggregate helpers
32
29
  export { beforeHook, afterHook, clearHook, clearAllHooks } from './hooks.js'; // middleware hooks
33
30
 
34
31
  // Store API:
package/src/main.js CHANGED
@@ -37,7 +37,6 @@ export default class Orm {
37
37
 
38
38
  models = {};
39
39
  serializers = {};
40
- views = {};
41
40
  transforms = { ...baseTransforms };
42
41
  warnings = new Set();
43
42
 
@@ -80,28 +79,14 @@ export default class Orm {
80
79
  // Wait for imports before db & rest server setup
81
80
  await Promise.all(promises);
82
81
 
83
- // Discover views from paths.view (separate from model/serializer/transform)
84
- if (paths.view) {
85
- await forEachFileImport(paths.view, (exported, { name }) => {
86
- const alias = `${kebabCaseToPascalCase(name)}View`;
87
- Orm.store.set(name, new Map());
88
- registerPluralName(name, exported);
89
- this.views[alias] = exported;
90
- }, { ignoreAccessFailure: true, rawName: true, recursive: true, recursiveNaming: true });
91
- }
92
-
93
82
  // Setup event names for hooks after models are loaded
94
83
  const eventNames = [];
95
84
  const operations = ['list', 'get', 'create', 'update', 'delete'];
96
- const viewOperations = ['list', 'get'];
97
85
  const timings = ['before', 'after'];
98
86
 
99
87
  for (const modelName of Orm.store.data.keys()) {
100
- const isView = this.isView(modelName);
101
- const ops = isView ? viewOperations : operations;
102
-
103
88
  for (const timing of timings) {
104
- for (const operation of ops) {
89
+ for (const operation of operations) {
105
90
  eventNames.push(`${timing}:${operation}:${modelName}`);
106
91
  }
107
92
  }
@@ -156,27 +141,13 @@ export default class Orm {
156
141
 
157
142
  getRecordClasses(modelName) {
158
143
  const modelClassPrefix = kebabCaseToPascalCase(modelName);
159
-
160
- // Check views first, then models
161
- const viewClass = this.views[`${modelClassPrefix}View`];
162
- if (viewClass) {
163
- return {
164
- modelClass: viewClass,
165
- serializerClass: this.serializers[`${modelClassPrefix}Serializer`] || Serializer
166
- };
167
- }
168
-
144
+
169
145
  return {
170
146
  modelClass: this.models[`${modelClassPrefix}Model`],
171
147
  serializerClass: this.serializers[`${modelClassPrefix}Serializer`] || Serializer
172
148
  };
173
149
  }
174
150
 
175
- isView(modelName) {
176
- const modelClassPrefix = kebabCaseToPascalCase(modelName);
177
- return !!this.views[`${modelClassPrefix}View`];
178
- }
179
-
180
151
  // Queue warnings to avoid the same error from being logged in the same iteration
181
152
  warn(message) {
182
153
  this.warnings.add(message);
@@ -14,11 +14,6 @@ export function createRecord(modelName, rawData={}, userOptions={}) {
14
14
 
15
15
  if (!initialized && !options.isDbRecord) throw new Error('ORM is not ready');
16
16
 
17
- // Guard: read-only views cannot have records created directly
18
- if (orm?.isView?.(modelName) && !options.isDbRecord) {
19
- throw new Error(`Cannot create records for read-only view '${modelName}'`);
20
- }
21
-
22
17
  const modelStore = store.get(modelName);
23
18
  const globalRelationships = relationships.get('global');
24
19
  const pendingRelationships = relationships.get('pending');
@@ -88,12 +83,6 @@ export function createRecord(modelName, rawData={}, userOptions={}) {
88
83
  export function updateRecord(record, rawData, userOptions={}) {
89
84
  if (!rawData) throw new Error('rawData must be passed in to updateRecord call');
90
85
 
91
- // Guard: read-only views cannot be updated
92
- const modelName = record?.__model?.__name;
93
- if (modelName && Orm.instance?.isView?.(modelName)) {
94
- throw new Error(`Cannot update records for read-only view '${modelName}'`);
95
- }
96
-
97
86
  const options = { ...defaultOptions, ...userOptions, update:true };
98
87
 
99
88
  record.serialize(rawData, options);
@@ -22,8 +22,8 @@ export default class ModelProperty {
22
22
  return this._value = newValue;
23
23
  }
24
24
 
25
- if (newValue === undefined || newValue === null) return;
25
+ if (newValue === undefined) return;
26
26
 
27
- this._value = Orm.instance.transforms[this.type](newValue);
27
+ this._value = newValue === null ? null : Orm.instance.transforms[this.type](newValue);
28
28
  }
29
29
  }
@@ -1,4 +1,4 @@
1
- import { introspectModels, introspectViews, buildTableDDL, buildViewDDL, schemasToSnapshot, viewSchemasToSnapshot, getTopologicalOrder } from './schema-introspector.js';
1
+ import { introspectModels, buildTableDDL, schemasToSnapshot, getTopologicalOrder } from './schema-introspector.js';
2
2
  import { readFile, createFile, createDirectory, fileExists } from '@stonyx/utils/file';
3
3
  import path from 'path';
4
4
  import config from 'stonyx/config';
@@ -16,18 +16,9 @@ export async function generateMigration(description = 'migration') {
16
16
  const previousSnapshot = await loadLatestSnapshot(migrationsPath);
17
17
  const diff = diffSnapshots(previousSnapshot, currentSnapshot);
18
18
 
19
- // Don't return early — check view changes too before deciding
20
19
  if (!diff.hasChanges) {
21
- // Check if there are view changes before returning null
22
- const viewSchemasPrelim = introspectViews();
23
- const currentViewSnapshotPrelim = viewSchemasToSnapshot(viewSchemasPrelim);
24
- const previousViewSnapshotPrelim = extractViewsFromSnapshot(previousSnapshot);
25
- const viewDiffPrelim = diffViewSnapshots(previousViewSnapshotPrelim, currentViewSnapshotPrelim);
26
-
27
- if (!viewDiffPrelim.hasChanges) {
28
- log.db('No schema changes detected.');
29
- return null;
30
- }
20
+ log.db('No schema changes detected.');
21
+ return null;
31
22
  }
32
23
 
33
24
  const upStatements = [];
@@ -94,71 +85,17 @@ export async function generateMigration(description = 'migration') {
94
85
  downStatements.push(`ALTER TABLE \`${table}\` ADD FOREIGN KEY (\`${column}\`) REFERENCES \`${references.references}\`(\`${references.column}\`) ON DELETE SET NULL;`);
95
86
  }
96
87
 
97
- // View migrations — views are created AFTER tables (dependency order)
98
- const viewSchemas = introspectViews();
99
- const currentViewSnapshot = viewSchemasToSnapshot(viewSchemas);
100
- const previousViewSnapshot = extractViewsFromSnapshot(previousSnapshot);
101
- const viewDiff = diffViewSnapshots(previousViewSnapshot, currentViewSnapshot);
102
-
103
- if (viewDiff.hasChanges) {
104
- upStatements.push('');
105
- upStatements.push('-- Views');
106
- downStatements.push('');
107
- downStatements.push('-- Views');
108
-
109
- // Added views
110
- for (const name of viewDiff.addedViews) {
111
- try {
112
- const ddl = buildViewDDL(name, viewSchemas[name], schemas);
113
- upStatements.push(ddl + ';');
114
- downStatements.unshift(`DROP VIEW IF EXISTS \`${viewSchemas[name].viewName}\`;`);
115
- } catch (error) {
116
- upStatements.push(`-- WARNING: Could not generate DDL for view '${name}': ${error.message}`);
117
- }
118
- }
119
-
120
- // Removed views
121
- for (const name of viewDiff.removedViews) {
122
- upStatements.push(`-- WARNING: View '${name}' was removed. Uncomment to drop view:`);
123
- upStatements.push(`-- DROP VIEW IF EXISTS \`${previousViewSnapshot[name].viewName}\`;`);
124
- downStatements.push(`-- Recreate view for removed view '${name}' manually if needed`);
125
- }
126
-
127
- // Changed views (source or aggregates changed)
128
- for (const name of viewDiff.changedViews) {
129
- try {
130
- const ddl = buildViewDDL(name, viewSchemas[name], schemas);
131
- upStatements.push(ddl + ';');
132
- } catch (error) {
133
- upStatements.push(`-- WARNING: Could not generate DDL for changed view '${name}': ${error.message}`);
134
- }
135
- }
136
- }
137
-
138
- const combinedHasChanges = diff.hasChanges || viewDiff.hasChanges;
139
-
140
- if (!combinedHasChanges) {
141
- log.db('No schema changes detected.');
142
- return null;
143
- }
144
-
145
- // Merge view snapshot into the main snapshot
146
- const combinedSnapshot = { ...currentSnapshot };
147
- for (const [name, viewSnap] of Object.entries(currentViewSnapshot)) {
148
- combinedSnapshot[name] = viewSnap;
149
- }
150
-
151
88
  const sanitizedDescription = description.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_]/g, '');
152
89
  const timestamp = Math.floor(Date.now() / 1000);
153
90
  const filename = `${timestamp}_${sanitizedDescription}.sql`;
154
91
  const content = `-- UP\n${upStatements.join('\n')}\n\n-- DOWN\n${downStatements.join('\n')}\n`;
155
92
 
156
93
  await createFile(path.join(migrationsPath, filename), content);
157
- await createFile(path.join(migrationsPath, '.snapshot.json'), JSON.stringify(combinedSnapshot, null, 2));
94
+ await createFile(path.join(migrationsPath, '.snapshot.json'), JSON.stringify(currentSnapshot, null, 2));
158
95
 
159
96
  log.db(`Migration generated: ${filename}`);
160
97
 
161
- return { filename, content, snapshot: combinedSnapshot };
98
+ return { filename, content, snapshot: currentSnapshot };
162
99
  }
163
100
 
164
101
  export async function loadLatestSnapshot(migrationsPath) {
@@ -249,38 +186,3 @@ export function detectSchemaDrift(schemas, snapshot) {
249
186
  const current = schemasToSnapshot(schemas);
250
187
  return diffSnapshots(snapshot, current);
251
188
  }
252
-
253
- export function extractViewsFromSnapshot(snapshot) {
254
- const views = {};
255
- for (const [name, entry] of Object.entries(snapshot)) {
256
- if (entry.isView) views[name] = entry;
257
- }
258
- return views;
259
- }
260
-
261
- export function diffViewSnapshots(previous, current) {
262
- const addedViews = [];
263
- const removedViews = [];
264
- const changedViews = [];
265
-
266
- for (const name of Object.keys(current)) {
267
- if (!previous[name]) {
268
- addedViews.push(name);
269
- } else if (
270
- current[name].viewQuery !== previous[name].viewQuery ||
271
- current[name].source !== previous[name].source
272
- ) {
273
- changedViews.push(name);
274
- }
275
- }
276
-
277
- for (const name of Object.keys(previous)) {
278
- if (!current[name]) {
279
- removedViews.push(name);
280
- }
281
- }
282
-
283
- const hasChanges = addedViews.length > 0 || removedViews.length > 0 || changedViews.length > 0;
284
-
285
- return { hasChanges, addedViews, removedViews, changedViews };
286
- }
@@ -1,6 +1,6 @@
1
1
  import { getPool, closePool } from './connection.js';
2
2
  import { ensureMigrationsTable, getAppliedMigrations, getMigrationFiles, applyMigration, parseMigrationFile } from './migration-runner.js';
3
- import { introspectModels, introspectViews, getTopologicalOrder, schemasToSnapshot } from './schema-introspector.js';
3
+ import { introspectModels, getTopologicalOrder, schemasToSnapshot } from './schema-introspector.js';
4
4
  import { loadLatestSnapshot, detectSchemaDrift } from './migration-generator.js';
5
5
  import { buildInsert, buildUpdate, buildDelete, buildSelect } from './query-builder.js';
6
6
  import { createRecord, store } from '@stonyx/orm';
@@ -14,7 +14,7 @@ import path from 'path';
14
14
  const defaultDeps = {
15
15
  getPool, closePool, ensureMigrationsTable, getAppliedMigrations,
16
16
  getMigrationFiles, applyMigration, parseMigrationFile,
17
- introspectModels, introspectViews, getTopologicalOrder, schemasToSnapshot,
17
+ introspectModels, getTopologicalOrder, schemasToSnapshot,
18
18
  loadLatestSnapshot, detectSchemaDrift,
19
19
  buildInsert, buildUpdate, buildDelete, buildSelect,
20
20
  createRecord, store, confirm, readFile, getPluralName, config, log, path
@@ -148,35 +148,6 @@ export default class MysqlDB {
148
148
  throw error;
149
149
  }
150
150
  }
151
-
152
- // Load views with memory: true
153
- const viewSchemas = this.deps.introspectViews();
154
-
155
- for (const [viewName, viewSchema] of Object.entries(viewSchemas)) {
156
- const { modelClass: viewClass } = Orm.instance.getRecordClasses(viewName);
157
- if (viewClass?.memory !== true) {
158
- this.deps.log.db(`Skipping memory load for view '${viewName}' (memory: false)`);
159
- continue;
160
- }
161
-
162
- const schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
163
- const { sql, values } = this.deps.buildSelect(schema.table);
164
-
165
- try {
166
- const [rows] = await this.pool.execute(sql, values);
167
-
168
- for (const row of rows) {
169
- const rawData = this._rowToRawData(row, schema);
170
- this.deps.createRecord(viewName, rawData, { isDbRecord: true, serialize: false, transform: false });
171
- }
172
- } catch (error) {
173
- if (error.code === 'ER_NO_SUCH_TABLE') {
174
- this.deps.log.db(`View '${viewSchema.viewName}' does not exist yet. Skipping load for '${viewName}'.`);
175
- continue;
176
- }
177
- throw error;
178
- }
179
- }
180
151
  }
181
152
 
182
153
  /**
@@ -195,16 +166,7 @@ export default class MysqlDB {
195
166
  */
196
167
  async findRecord(modelName, id) {
197
168
  const schemas = this.deps.introspectModels();
198
- let schema = schemas[modelName];
199
-
200
- // Check views if not found in models
201
- if (!schema) {
202
- const viewSchemas = this.deps.introspectViews();
203
- const viewSchema = viewSchemas[modelName];
204
- if (viewSchema) {
205
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
206
- }
207
- }
169
+ const schema = schemas[modelName];
208
170
 
209
171
  if (!schema) return undefined;
210
172
 
@@ -237,16 +199,7 @@ export default class MysqlDB {
237
199
  */
238
200
  async findAll(modelName, conditions) {
239
201
  const schemas = this.deps.introspectModels();
240
- let schema = schemas[modelName];
241
-
242
- // Check views if not found in models
243
- if (!schema) {
244
- const viewSchemas = this.deps.introspectViews();
245
- const viewSchema = viewSchemas[modelName];
246
- if (viewSchema) {
247
- schema = { table: viewSchema.viewName, columns: viewSchema.columns || {}, foreignKeys: viewSchema.foreignKeys || {} };
248
- }
249
- }
202
+ const schema = schemas[modelName];
250
203
 
251
204
  if (!schema) return [];
252
205
 
@@ -324,10 +277,6 @@ export default class MysqlDB {
324
277
  }
325
278
 
326
279
  async persist(operation, modelName, context, response) {
327
- // Views are read-only — no-op for all write operations
328
- const Orm = (await import('@stonyx/orm')).default;
329
- if (Orm.instance?.isView?.(modelName)) return;
330
-
331
280
  switch (operation) {
332
281
  case 'create':
333
282
  return this._persistCreate(modelName, context, response);