@stonyx/orm 0.2.1-beta.2 → 0.2.1-beta.21
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/code-style-rules.md +44 -0
- package/.claude/hooks.md +250 -0
- package/.claude/index.md +281 -0
- package/.claude/usage-patterns.md +234 -0
- package/.github/workflows/publish.yml +17 -1
- package/README.md +440 -15
- package/config/environment.js +26 -5
- package/improvements.md +139 -0
- package/package.json +19 -8
- package/project-structure.md +343 -0
- package/src/commands.js +170 -0
- package/src/db.js +132 -6
- package/src/hooks.js +124 -0
- package/src/index.js +8 -1
- package/src/main.js +47 -3
- package/src/manage-record.js +19 -4
- package/src/migrate.js +72 -0
- package/src/model.js +11 -0
- package/src/mysql/connection.js +28 -0
- package/src/mysql/migration-generator.js +188 -0
- package/src/mysql/migration-runner.js +110 -0
- package/src/mysql/mysql-db.js +422 -0
- package/src/mysql/query-builder.js +64 -0
- package/src/mysql/schema-introspector.js +160 -0
- package/src/mysql/type-map.js +37 -0
- package/src/orm-request.js +307 -53
- package/src/plural-registry.js +12 -0
- package/src/record.js +35 -8
- package/src/serializer.js +2 -2
- package/src/setup-rest-server.js +4 -1
- package/src/store.js +105 -0
- package/src/utils.js +12 -0
- package/test-events-setup.js +41 -0
- package/test-hooks-manual.js +54 -0
- package/test-hooks-with-logging.js +52 -0
- package/.claude/project-structure.md +0 -578
- package/stonyx-bootstrap.cjs +0 -30
|
@@ -1,578 +0,0 @@
|
|
|
1
|
-
# Stonyx-ORM Guide for Claude
|
|
2
|
-
|
|
3
|
-
## Project Overview
|
|
4
|
-
|
|
5
|
-
**stonyx-orm** is a lightweight Object-Relational Mapping (ORM) library designed specifically for the Stonyx framework. It provides structured data modeling, relationship management, serialization, and persistence to JSON files, with optional REST API integration.
|
|
6
|
-
|
|
7
|
-
## Core Problem It Solves
|
|
8
|
-
|
|
9
|
-
1. **Data Modeling**: Clean, type-safe model definitions with attributes and relationships
|
|
10
|
-
2. **Data Serialization**: Transforms messy third-party data into structured model instances
|
|
11
|
-
3. **Relationship Management**: Automatic bidirectional relationships (hasMany, belongsTo)
|
|
12
|
-
4. **Data Persistence**: File-based JSON storage with auto-save
|
|
13
|
-
5. **REST API Generation**: Auto-generated RESTful endpoints with access control
|
|
14
|
-
6. **Data Transformation**: Custom type conversion and formatting
|
|
15
|
-
7. **Event System**: Pub/sub events for CRUD operations with before/after hooks
|
|
16
|
-
|
|
17
|
-
---
|
|
18
|
-
|
|
19
|
-
## Architecture Overview
|
|
20
|
-
|
|
21
|
-
### Key Components
|
|
22
|
-
|
|
23
|
-
1. **Orm** ([src/main.js](src/main.js)) - Singleton that initializes and manages the entire system
|
|
24
|
-
2. **Store** ([src/store.js](src/store.js)) - In-memory storage (nested Maps: `Map<modelName, Map<recordId, record>>`)
|
|
25
|
-
3. **Model** ([src/model.js](src/model.js)) - Base class for all models
|
|
26
|
-
4. **Record** ([src/record.js](src/record.js)) - Individual model instances
|
|
27
|
-
5. **Serializer** ([src/serializer.js](src/serializer.js)) - Maps raw data to model format
|
|
28
|
-
6. **DB** ([src/db.js](src/db.js)) - JSON file persistence layer
|
|
29
|
-
7. **Relationships** ([src/has-many.js](src/has-many.js), [src/belongs-to.js](src/belongs-to.js)) - Relationship handlers
|
|
30
|
-
8. **Include Parser** ([src/include-parser.js](src/include-parser.js)) - Parses include query params
|
|
31
|
-
9. **Include Collector** ([src/include-collector.js](src/include-collector.js)) - Collects and deduplicates included records
|
|
32
|
-
10. **Events** (from `@stonyx/events`) - Pub/sub event system for CRUD hooks
|
|
33
|
-
|
|
34
|
-
### Project Structure
|
|
35
|
-
|
|
36
|
-
```
|
|
37
|
-
stonyx-orm/
|
|
38
|
-
├── src/
|
|
39
|
-
│ ├── index.js # Main exports (includes ormEvents)
|
|
40
|
-
│ ├── main.js # Orm class
|
|
41
|
-
│ ├── model.js # Base Model
|
|
42
|
-
│ ├── record.js # Record instances
|
|
43
|
-
│ ├── serializer.js # Base Serializer
|
|
44
|
-
│ ├── store.js # In-memory storage (emits delete events)
|
|
45
|
-
│ ├── db.js # JSON persistence
|
|
46
|
-
│ ├── attr.js # Attribute helper (Proxy-based)
|
|
47
|
-
│ ├── has-many.js # One-to-many relationships
|
|
48
|
-
│ ├── belongs-to.js # Many-to-one relationships
|
|
49
|
-
│ ├── relationships.js # Relationship registry
|
|
50
|
-
│ ├── manage-record.js # createRecord/updateRecord (emits create events)
|
|
51
|
-
│ ├── model-property.js # Transform handler
|
|
52
|
-
│ ├── transforms.js # Built-in transforms
|
|
53
|
-
│ ├── setup-rest-server.js # REST integration
|
|
54
|
-
│ ├── orm-request.js # CRUD request handler (emits update events)
|
|
55
|
-
│ └── meta-request.js # Meta endpoint (dev only)
|
|
56
|
-
├── config/
|
|
57
|
-
│ └── environment.js # Default configuration
|
|
58
|
-
├── test/
|
|
59
|
-
│ ├── integration/ # Integration tests
|
|
60
|
-
│ ├── unit/ # Unit tests
|
|
61
|
-
│ └── sample/ # Test fixtures
|
|
62
|
-
│ ├── models/ # Example models
|
|
63
|
-
│ ├── serializers/ # Example serializers
|
|
64
|
-
│ ├── transforms/ # Custom transforms
|
|
65
|
-
│ ├── access/ # Access control
|
|
66
|
-
│ ├── db-schema.js # DB schema
|
|
67
|
-
│ └── payload.js # Test data
|
|
68
|
-
└── package.json
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
---
|
|
72
|
-
|
|
73
|
-
## Usage Patterns
|
|
74
|
-
|
|
75
|
-
### 1. Model Definition
|
|
76
|
-
|
|
77
|
-
Models extend `Model` and use decorators for attributes and relationships:
|
|
78
|
-
|
|
79
|
-
```javascript
|
|
80
|
-
// test/sample/models/animal.js
|
|
81
|
-
import { Model, attr, belongsTo, hasMany } from '@stonyx/orm';
|
|
82
|
-
|
|
83
|
-
export default class AnimalModel extends Model {
|
|
84
|
-
// Attributes with type transforms
|
|
85
|
-
type = attr('animal'); // Custom transform
|
|
86
|
-
age = attr('number'); // Built-in transform
|
|
87
|
-
size = attr('string');
|
|
88
|
-
|
|
89
|
-
// Relationships
|
|
90
|
-
owner = belongsTo('owner'); // Many-to-one
|
|
91
|
-
traits = hasMany('trait'); // One-to-many
|
|
92
|
-
|
|
93
|
-
// Computed properties
|
|
94
|
-
get tag() {
|
|
95
|
-
return `${this.owner.id}'s ${this.size} animal`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
**Key Points:**
|
|
101
|
-
- Use `attr(type)` for simple attributes
|
|
102
|
-
- Use `belongsTo(modelName)` for many-to-one
|
|
103
|
-
- Use `hasMany(modelName)` for one-to-many
|
|
104
|
-
- Getters work as computed properties
|
|
105
|
-
- Relationships auto-establish bidirectionally
|
|
106
|
-
|
|
107
|
-
### 2. Serializers (Data Transformation)
|
|
108
|
-
|
|
109
|
-
Serializers map raw data paths to model properties:
|
|
110
|
-
|
|
111
|
-
```javascript
|
|
112
|
-
// test/sample/serializers/animal.js
|
|
113
|
-
import { Serializer } from '@stonyx/orm';
|
|
114
|
-
|
|
115
|
-
export default class AnimalSerializer extends Serializer {
|
|
116
|
-
map = {
|
|
117
|
-
// Nested path mapping
|
|
118
|
-
age: 'details.age',
|
|
119
|
-
size: 'details.c',
|
|
120
|
-
owner: 'details.location.owner',
|
|
121
|
-
|
|
122
|
-
// Custom transformation function
|
|
123
|
-
traits: ['details', ({ x:color }) => {
|
|
124
|
-
const traits = [{ id: 1, type: 'habitat', value: 'farm' }];
|
|
125
|
-
if (color) traits.push({ id: 2, type: 'color', value: color });
|
|
126
|
-
return traits;
|
|
127
|
-
}]
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
**Key Points:**
|
|
133
|
-
- `map` object defines field mappings
|
|
134
|
-
- Supports nested paths (`'details.age'`)
|
|
135
|
-
- Custom functions for complex transformations
|
|
136
|
-
- Handlers receive raw data subset
|
|
137
|
-
|
|
138
|
-
### 3. Custom Transforms
|
|
139
|
-
|
|
140
|
-
Transforms convert data types:
|
|
141
|
-
|
|
142
|
-
```javascript
|
|
143
|
-
// test/sample/transforms/animal.js
|
|
144
|
-
const codeEnumMap = { 'dog': 1, 'cat': 2, 'bird': 3 };
|
|
145
|
-
|
|
146
|
-
export default function(value) {
|
|
147
|
-
return codeEnumMap[value] || 0;
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
**Built-in Transforms:**
|
|
152
|
-
- Type: `boolean`, `number`, `float`, `string`, `date`, `timestamp`
|
|
153
|
-
- Math: `round`, `ceil`, `floor`
|
|
154
|
-
- String: `trim`, `uppercase`
|
|
155
|
-
- Utility: `passthrough`
|
|
156
|
-
|
|
157
|
-
### 4. CRUD Operations
|
|
158
|
-
|
|
159
|
-
```javascript
|
|
160
|
-
import { createRecord, updateRecord, store } from '@stonyx/orm';
|
|
161
|
-
|
|
162
|
-
// Create
|
|
163
|
-
createRecord('owner', { id: 'bob', age: 30 });
|
|
164
|
-
|
|
165
|
-
// Read
|
|
166
|
-
const owner = store.get('owner', 'bob');
|
|
167
|
-
const allOwners = store.get('owner');
|
|
168
|
-
|
|
169
|
-
// Update
|
|
170
|
-
updateRecord(owner, { age: 31 });
|
|
171
|
-
// Or direct: owner.age = 31;
|
|
172
|
-
|
|
173
|
-
// Delete
|
|
174
|
-
store.remove('owner', 'bob');
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### 5. Database Schema
|
|
178
|
-
|
|
179
|
-
The DB schema is a Model defining top-level collections:
|
|
180
|
-
|
|
181
|
-
```javascript
|
|
182
|
-
// test/sample/db-schema.js
|
|
183
|
-
import { Model, hasMany } from '@stonyx/orm';
|
|
184
|
-
|
|
185
|
-
export default class DBModel extends Model {
|
|
186
|
-
owners = hasMany('owner');
|
|
187
|
-
animals = hasMany('animal');
|
|
188
|
-
traits = hasMany('trait');
|
|
189
|
-
}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
### 6. Persistence
|
|
193
|
-
|
|
194
|
-
```javascript
|
|
195
|
-
import Orm from '@stonyx/orm';
|
|
196
|
-
|
|
197
|
-
// Save to file
|
|
198
|
-
await Orm.db.save();
|
|
199
|
-
|
|
200
|
-
// Data auto-serializes to JSON file
|
|
201
|
-
// Reload using createRecord with serialize:false, transform:false
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### 7. Access Control
|
|
205
|
-
|
|
206
|
-
```javascript
|
|
207
|
-
// test/sample/access/global-access.js
|
|
208
|
-
export default class GlobalAccess {
|
|
209
|
-
models = ['owner', 'animal']; // or '*' for all
|
|
210
|
-
|
|
211
|
-
access(request) {
|
|
212
|
-
// Deny specific access
|
|
213
|
-
if (request.url.endsWith('/owner/angela')) return false;
|
|
214
|
-
|
|
215
|
-
// Filter collections
|
|
216
|
-
if (request.url.endsWith('/owner')) {
|
|
217
|
-
return record => record.id !== 'angela';
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Grant CRUD permissions
|
|
221
|
-
return ['read', 'create', 'update', 'delete'];
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
### 8. REST API (Auto-generated)
|
|
227
|
-
|
|
228
|
-
```javascript
|
|
229
|
-
// Endpoints auto-generated for models:
|
|
230
|
-
// GET /owners - List all
|
|
231
|
-
// GET /owners/:id - Get one
|
|
232
|
-
// POST /animals - Create
|
|
233
|
-
// PATCH /animals/:id - Update
|
|
234
|
-
// DELETE /animals/:id - Delete
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### 9. Include Parameter (Sideloading)
|
|
238
|
-
|
|
239
|
-
GET endpoints support sideloading related records with **nested relationship traversal**:
|
|
240
|
-
|
|
241
|
-
```javascript
|
|
242
|
-
// Single-level includes
|
|
243
|
-
GET /animals/1?include=owner,traits
|
|
244
|
-
|
|
245
|
-
// Nested includes (NEW!)
|
|
246
|
-
GET /animals/1?include=owner.pets,owner.company
|
|
247
|
-
|
|
248
|
-
// Deep nesting (3+ levels)
|
|
249
|
-
GET /scenes/e001-s001?include=slides.dialogue.character
|
|
250
|
-
|
|
251
|
-
// Response structure (unchanged)
|
|
252
|
-
{
|
|
253
|
-
data: { type: 'animal', id: 1, attributes: {...}, relationships: {...} },
|
|
254
|
-
included: [
|
|
255
|
-
{ type: 'owner', id: 'angela', ... },
|
|
256
|
-
{ type: 'animal', id: 7, ... }, // owner's other pets
|
|
257
|
-
{ type: 'animal', id: 11, ... }, // owner's other pets
|
|
258
|
-
{ type: 'company', id: 'acme', ... } // owner's company (if requested)
|
|
259
|
-
]
|
|
260
|
-
}
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
**How Nested Includes Work:**
|
|
264
|
-
1. Query param parsed into path segments: `owner.pets` → `[['owner'], ['owner', 'pets'], ['traits']]`
|
|
265
|
-
2. `traverseIncludePath()` recursively traverses relationships depth-first
|
|
266
|
-
3. Deduplication still by type+id (no duplicates in included array)
|
|
267
|
-
4. Gracefully handles null/missing relationships at any depth
|
|
268
|
-
5. Each included record gets full `toJSON()` representation
|
|
269
|
-
|
|
270
|
-
**Key Functions:**
|
|
271
|
-
- `parseInclude()` - Splits comma-separated includes and parses nested paths
|
|
272
|
-
- `traverseIncludePath()` - Recursively traverses relationship paths
|
|
273
|
-
- `collectIncludedRecords()` - Orchestrates traversal and deduplication
|
|
274
|
-
- All implemented in [src/orm-request.js](src/orm-request.js)
|
|
275
|
-
|
|
276
|
-
---
|
|
277
|
-
|
|
278
|
-
## Configuration
|
|
279
|
-
|
|
280
|
-
Located in [config/environment.js](config/environment.js), overridable via environment variables:
|
|
281
|
-
|
|
282
|
-
```javascript
|
|
283
|
-
config.orm = {
|
|
284
|
-
paths: {
|
|
285
|
-
model: './models',
|
|
286
|
-
serializer: './serializers',
|
|
287
|
-
transform: './transforms',
|
|
288
|
-
access: './access'
|
|
289
|
-
},
|
|
290
|
-
db: {
|
|
291
|
-
autosave: 'false',
|
|
292
|
-
file: 'db.json',
|
|
293
|
-
saveInterval: 3600,
|
|
294
|
-
schema: './config/db-schema.js'
|
|
295
|
-
},
|
|
296
|
-
restServer: {
|
|
297
|
-
enabled: 'true',
|
|
298
|
-
route: '/'
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
---
|
|
304
|
-
|
|
305
|
-
## Design Patterns
|
|
306
|
-
|
|
307
|
-
1. **Singleton**: Orm, Store, DB classes
|
|
308
|
-
2. **Proxy**: `attr()` uses Proxies for type-safe access
|
|
309
|
-
3. **Registry**: Relationships in nested Maps
|
|
310
|
-
4. **Factory**: `createRecord()` function
|
|
311
|
-
5. **Observer**: Auto-save via Cron
|
|
312
|
-
6. **Convention over Configuration**: Auto-discovery by naming
|
|
313
|
-
|
|
314
|
-
**Naming Conventions:**
|
|
315
|
-
- Models: `{PascalCase}Model` (e.g., `AnimalModel`)
|
|
316
|
-
- Serializers: `{PascalCase}Serializer` (e.g., `AnimalSerializer`)
|
|
317
|
-
- Transforms: Original filename (e.g., `animal.js`)
|
|
318
|
-
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
## Testing
|
|
322
|
-
|
|
323
|
-
**Test Runner**: QUnit with bootstrap
|
|
324
|
-
**Bootstrap**: [stonyx-bootstrap.cjs](stonyx-bootstrap.cjs) - Configures paths for test environment
|
|
325
|
-
|
|
326
|
-
**Test Structure:**
|
|
327
|
-
- **Integration**: [test/integration/orm-test.js](test/integration/orm-test.js) - Full pipeline test
|
|
328
|
-
- **Unit**: [test/unit/transforms/](test/unit/transforms/) - Transform tests
|
|
329
|
-
- **Sample**: [test/sample/](test/sample/) - Test fixtures
|
|
330
|
-
|
|
331
|
-
**Key Test Data:**
|
|
332
|
-
- [test/sample/payload.js](test/sample/payload.js) - Raw vs serialized data
|
|
333
|
-
- Demonstrates transformation from messy external data to clean models
|
|
334
|
-
|
|
335
|
-
---
|
|
336
|
-
|
|
337
|
-
## Common Workflows
|
|
338
|
-
|
|
339
|
-
### Making Updates to Models
|
|
340
|
-
|
|
341
|
-
1. Read existing model in [test/sample/models/](test/sample/models/)
|
|
342
|
-
2. Understand attribute types and relationships
|
|
343
|
-
3. Check if serializer exists in [test/sample/serializers/](test/sample/serializers/)
|
|
344
|
-
4. Update model definition
|
|
345
|
-
5. Update serializer if data mapping changes
|
|
346
|
-
6. Run tests: `npm test`
|
|
347
|
-
|
|
348
|
-
### Adding New Features
|
|
349
|
-
|
|
350
|
-
1. Check [src/index.js](src/index.js) for exports
|
|
351
|
-
2. Understand Store pattern in [src/store.js](src/store.js)
|
|
352
|
-
3. Review Record lifecycle in [src/record.js](src/record.js)
|
|
353
|
-
4. Add feature following existing patterns
|
|
354
|
-
5. Update integration tests
|
|
355
|
-
|
|
356
|
-
### Debugging Issues
|
|
357
|
-
|
|
358
|
-
1. Check Store contents: `store.get(modelName)`
|
|
359
|
-
2. Verify relationships: `relationships.registry`
|
|
360
|
-
3. Test serialization: Create record and inspect `record.__data`
|
|
361
|
-
4. Check transform application in [src/model-property.js](src/model-property.js)
|
|
362
|
-
5. Review test fixtures for expected behavior
|
|
363
|
-
|
|
364
|
-
---
|
|
365
|
-
|
|
366
|
-
## Key Insights
|
|
367
|
-
|
|
368
|
-
**Strengths:**
|
|
369
|
-
- Zero-config REST API generation
|
|
370
|
-
- Clean declarative model definitions
|
|
371
|
-
- Automatic relationship management
|
|
372
|
-
- File-based (no database setup needed)
|
|
373
|
-
- Flexible serialization for messy data
|
|
374
|
-
|
|
375
|
-
**Use Cases:**
|
|
376
|
-
- Rapid prototyping
|
|
377
|
-
- Small to medium applications
|
|
378
|
-
- Third-party API consumption with normalization
|
|
379
|
-
- Development/testing environments
|
|
380
|
-
- Applications needing quick REST APIs
|
|
381
|
-
|
|
382
|
-
**Dependencies:**
|
|
383
|
-
- `stonyx` - Main framework (peer)
|
|
384
|
-
- `@stonyx/utils` - File/string utilities
|
|
385
|
-
- `@stonyx/events` - Pub/sub event system for CRUD hooks
|
|
386
|
-
- `@stonyx/cron` - Scheduled tasks (used by DB for auto-save)
|
|
387
|
-
- `@stonyx/rest-server` - REST API
|
|
388
|
-
|
|
389
|
-
## Event System
|
|
390
|
-
|
|
391
|
-
The ORM emits events during CRUD operations, allowing applications to hook into the data lifecycle.
|
|
392
|
-
|
|
393
|
-
### Event Architecture
|
|
394
|
-
|
|
395
|
-
**Event Source**: `@stonyx/events` (Events class)
|
|
396
|
-
**Integration**: `src/index.js` initializes singleton `ormEvents` instance
|
|
397
|
-
**Event Registration**: 6 events registered on initialization:
|
|
398
|
-
- `create:before`, `create:after`
|
|
399
|
-
- `update:before`, `update:after`
|
|
400
|
-
- `delete:before`, `delete:after`
|
|
401
|
-
|
|
402
|
-
### Event Emission Points
|
|
403
|
-
|
|
404
|
-
**CREATE Events** - `src/manage-record.js`:
|
|
405
|
-
```javascript
|
|
406
|
-
// Line ~34: Before serialization
|
|
407
|
-
Events.instance?.emit('create:before', {
|
|
408
|
-
model: modelName,
|
|
409
|
-
record,
|
|
410
|
-
rawData,
|
|
411
|
-
options
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
// Line ~88: After record fully created
|
|
415
|
-
Events.instance?.emit('create:after', {
|
|
416
|
-
model: modelName,
|
|
417
|
-
record,
|
|
418
|
-
data: record.__data,
|
|
419
|
-
rawData,
|
|
420
|
-
options
|
|
421
|
-
});
|
|
422
|
-
```
|
|
423
|
-
|
|
424
|
-
**UPDATE Events** - `src/orm-request.js` (PATCH handler):
|
|
425
|
-
```javascript
|
|
426
|
-
// Line ~155: Before applying updates
|
|
427
|
-
const oldData = { ...record.__data };
|
|
428
|
-
Events.instance?.emit('update:before', {
|
|
429
|
-
model,
|
|
430
|
-
record,
|
|
431
|
-
data: record.__data,
|
|
432
|
-
oldData,
|
|
433
|
-
rawData: attributes,
|
|
434
|
-
options: {}
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
// Line ~173: After updates applied
|
|
438
|
-
Events.instance?.emit('update:after', {
|
|
439
|
-
model,
|
|
440
|
-
record,
|
|
441
|
-
data: record.__data,
|
|
442
|
-
oldData,
|
|
443
|
-
rawData: attributes,
|
|
444
|
-
options: {}
|
|
445
|
-
});
|
|
446
|
-
```
|
|
447
|
-
|
|
448
|
-
**DELETE Events** - `src/store.js` (unloadRecord):
|
|
449
|
-
```javascript
|
|
450
|
-
// Line ~45: Before cleanup
|
|
451
|
-
Events.instance?.emit('delete:before', {
|
|
452
|
-
model,
|
|
453
|
-
record,
|
|
454
|
-
data: { ...record.__data },
|
|
455
|
-
options
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
// Line ~65: After deletion
|
|
459
|
-
Events.instance?.emit('delete:after', {
|
|
460
|
-
model,
|
|
461
|
-
record: null,
|
|
462
|
-
data: null,
|
|
463
|
-
options
|
|
464
|
-
});
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### Design Decisions
|
|
468
|
-
|
|
469
|
-
**Fire Without Await**: Events are emitted without `await` to maintain backward compatibility
|
|
470
|
-
- `createRecord()` remains synchronous
|
|
471
|
-
- `unloadRecord()` remains synchronous
|
|
472
|
-
- Synchronous handlers execute immediately
|
|
473
|
-
- Async handlers run in background
|
|
474
|
-
|
|
475
|
-
**Optional Chaining**: Uses `Events.instance?.emit()` for zero overhead when not used
|
|
476
|
-
|
|
477
|
-
**Error Isolation**: Event errors are caught in Events class, never crash ORM operations
|
|
478
|
-
|
|
479
|
-
### Usage Patterns
|
|
480
|
-
|
|
481
|
-
**Auditing**:
|
|
482
|
-
```javascript
|
|
483
|
-
import { ormEvents } from '@stonyx/orm';
|
|
484
|
-
|
|
485
|
-
ormEvents.subscribe('update:after', ({ model, data, oldData }) => {
|
|
486
|
-
auditLog.write({
|
|
487
|
-
action: 'update',
|
|
488
|
-
model,
|
|
489
|
-
changes: diff(oldData, data),
|
|
490
|
-
timestamp: Date.now()
|
|
491
|
-
});
|
|
492
|
-
});
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
**Auto-Timestamps**:
|
|
496
|
-
```javascript
|
|
497
|
-
ormEvents.subscribe('create:before', ({ record }) => {
|
|
498
|
-
record.__data.createdAt = new Date().toISOString();
|
|
499
|
-
});
|
|
500
|
-
|
|
501
|
-
ormEvents.subscribe('update:before', ({ record }) => {
|
|
502
|
-
record.__data.updatedAt = new Date().toISOString();
|
|
503
|
-
});
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
**Cache Invalidation**:
|
|
507
|
-
```javascript
|
|
508
|
-
ormEvents.subscribe('update:after', ({ model, record }) => {
|
|
509
|
-
cache.invalidate(`${model}:${record.id}`);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
ormEvents.subscribe('delete:after', ({ model, record }) => {
|
|
513
|
-
if (record) cache.invalidate(`${model}:${record.id}`);
|
|
514
|
-
});
|
|
515
|
-
```
|
|
516
|
-
|
|
517
|
-
---
|
|
518
|
-
|
|
519
|
-
## Critical Files for Common Tasks
|
|
520
|
-
|
|
521
|
-
**Understanding Core Behavior:**
|
|
522
|
-
- [src/main.js](src/main.js) - Initialization flow
|
|
523
|
-
- [src/store.js](src/store.js) - Record storage/retrieval
|
|
524
|
-
- [src/manage-record.js](src/manage-record.js) - CRUD operations
|
|
525
|
-
|
|
526
|
-
**Understanding Relationships:**
|
|
527
|
-
- [src/relationships.js](src/relationships.js) - Registry system
|
|
528
|
-
- [src/has-many.js](src/has-many.js) - One-to-many logic
|
|
529
|
-
- [src/belongs-to.js](src/belongs-to.js) - Many-to-one logic
|
|
530
|
-
|
|
531
|
-
**Understanding Data Flow:**
|
|
532
|
-
- [src/serializer.js](src/serializer.js) - Raw → Model mapping
|
|
533
|
-
- [src/model-property.js](src/model-property.js) - Transform application
|
|
534
|
-
- [src/transforms.js](src/transforms.js) - Built-in transforms
|
|
535
|
-
|
|
536
|
-
**Understanding REST API:**
|
|
537
|
-
- [src/setup-rest-server.js](src/setup-rest-server.js) - Endpoint registration
|
|
538
|
-
- [src/orm-request.js](src/orm-request.js) - Request handling
|
|
539
|
-
|
|
540
|
-
---
|
|
541
|
-
|
|
542
|
-
## Quick Reference
|
|
543
|
-
|
|
544
|
-
**Import the ORM:**
|
|
545
|
-
```javascript
|
|
546
|
-
import { Orm, Model, Serializer, attr, hasMany, belongsTo, createRecord, updateRecord, store, ormEvents } from '@stonyx/orm';
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
**Initialize:**
|
|
550
|
-
```javascript
|
|
551
|
-
const orm = new Orm({ dbType: 'json' });
|
|
552
|
-
await orm.init();
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
**Access Database:**
|
|
556
|
-
```javascript
|
|
557
|
-
await Orm.db.save();
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
**Common Operations:**
|
|
561
|
-
```javascript
|
|
562
|
-
// Create
|
|
563
|
-
const record = createRecord('modelName', data);
|
|
564
|
-
|
|
565
|
-
// Read
|
|
566
|
-
const record = store.get('modelName', id);
|
|
567
|
-
const all = store.get('modelName');
|
|
568
|
-
|
|
569
|
-
// Update
|
|
570
|
-
updateRecord(record, newData);
|
|
571
|
-
|
|
572
|
-
// Delete
|
|
573
|
-
store.remove('modelName', id);
|
|
574
|
-
```
|
|
575
|
-
|
|
576
|
-
---
|
|
577
|
-
|
|
578
|
-
This guide provides the foundation for understanding stonyx-orm's architecture, patterns, and usage. Refer to test files for concrete examples and the source code for implementation details.
|
package/stonyx-bootstrap.cjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* commonJS Bootstrap loading - Stonyx must be loaded first, prior to the rest of the application
|
|
3
|
-
*/
|
|
4
|
-
const { default:Stonyx } = require('stonyx');
|
|
5
|
-
const { default:config } = require('./config/environment.js');
|
|
6
|
-
|
|
7
|
-
// Override paths for tests
|
|
8
|
-
Object.assign(config.paths, {
|
|
9
|
-
access: './test/sample/access',
|
|
10
|
-
model: './test/sample/models',
|
|
11
|
-
serializer: './test/sample/serializers',
|
|
12
|
-
transform: './test/sample/transforms'
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
// Override db settings for tests
|
|
16
|
-
Object.assign(config.db, {
|
|
17
|
-
file: './test/sample/db.json',
|
|
18
|
-
schema: './test/sample/db-schema.js'
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Create restServer module path for tests
|
|
22
|
-
config.modules = {
|
|
23
|
-
restServer: {
|
|
24
|
-
dir: './test/sample/requests'
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
new Stonyx(config, __dirname);
|
|
29
|
-
|
|
30
|
-
module.exports = Stonyx;
|