@stonyx/orm 0.3.2-alpha.119 → 0.3.2-alpha.120
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/README.md +22 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ A lightweight ORM for Stonyx projects, featuring model definitions, serializers,
|
|
|
9
9
|
|
|
10
10
|
## Highlights
|
|
11
11
|
|
|
12
|
-
- **Automatic Loading**: Models, serializers, transforms, and access classes are auto-registered from their configured directories. Models, serializers, transforms and views are also loaded from subdirectories and named after their path (`models/admin/user-role.ts` registers `admin/user-role`); access classes are loaded only from the top level of `paths.access`, and one in a subdirectory is silently never loaded.
|
|
12
|
+
- **Automatic Loading**: Models, serializers, transforms, views and access classes are auto-registered from their configured directories. Models, serializers, transforms and views are also loaded from subdirectories and named after their path (`models/admin/user-role.ts` registers `admin/user-role`); access classes are loaded only from the top level of `paths.access`, and one in a subdirectory is silently never loaded.
|
|
13
13
|
- **Models**: Define attributes with type-safe proxies (`attr`) and relationships (`hasMany`, `belongsTo`).
|
|
14
14
|
- **Serializers**: Map raw data into model-friendly structures, including nested properties.
|
|
15
15
|
- **Transforms**: Apply custom transformations on data values automatically.
|
|
@@ -313,7 +313,7 @@ In directory mode, each collection is stored as `{directory}/{collection}.json`
|
|
|
313
313
|
|
|
314
314
|
### MySQL Mode
|
|
315
315
|
|
|
316
|
-
Set the `MYSQL_HOST` environment variable to enable MySQL persistence.
|
|
316
|
+
Set the `MYSQL_HOST` environment variable to enable MySQL persistence. At startup the ORM loads into memory only the records of models declared `static memory = true`; every other model (the default is `memory = false`) is read from MySQL on demand by `store.find()` / `store.findAll()`, and the synchronous `store.get()` does not see it (see [docs/internals.md](https://github.com/abofs/stonyx-orm/blob/dev/docs/internals.md#store-reads-and-the-memory-flag)). A record read that way resolves a `belongsTo` only when the related record is already in memory; otherwise the relationship is `null`. CRUD operations are persisted to MySQL automatically. Supports schema-aware migration generation, apply, rollback, and drift detection.
|
|
317
317
|
|
|
318
318
|
| Command | Description |
|
|
319
319
|
|---------|-------------|
|
|
@@ -442,6 +442,11 @@ unrecognised spelling falls through to whatever your method returns next, so
|
|
|
442
442
|
prefer one class per model. A class may still list several models in `models`
|
|
443
443
|
when they share one rule.
|
|
444
444
|
|
|
445
|
+
`access()` covers a model's own routes only: records of that model served
|
|
446
|
+
through another model's `?include=`, `/:id/<relationship>` or
|
|
447
|
+
`/:id/relationships/<relationship>` routes are never
|
|
448
|
+
checked against it. See [docs/security.md](https://github.com/abofs/stonyx-orm/blob/dev/docs/security.md).
|
|
449
|
+
|
|
445
450
|
**Numeric ids: authorize on `request.recordId`, not on `request.params.id`.**
|
|
446
451
|
`params.id` is raw text from the client. When it looks numeric the ORM coerces
|
|
447
452
|
it *before* it resolves the record, so `7`, `007`, `7.0`, `7.9`, `7e0`, `0x7`,
|
|
@@ -639,6 +644,7 @@ GET /animals/1
|
|
|
639
644
|
#### Limitations
|
|
640
645
|
|
|
641
646
|
- Only available on GET endpoints (not POST/PATCH)
|
|
647
|
+
- Included records are authorized only by the requested model's `access()`, not by their own model's, and `include` has no built-in cap on depth or on the number of paths; a long `include` blocks the server while it is walked. [docs/security.md](https://github.com/abofs/stonyx-orm/blob/dev/docs/security.md) has the measurements and an `access()` check that caps both.
|
|
642
648
|
|
|
643
649
|
## Lifecycle Hooks
|
|
644
650
|
|
|
@@ -737,8 +743,11 @@ beforeHook('create', 'animal', (context) => {
|
|
|
737
743
|
// resolved the record by. `context.params.id` is the raw client text and is a
|
|
738
744
|
// different value for every alias of the same id, so a lookup keyed on it
|
|
739
745
|
// finds nothing on a numeric-id model (abofs/stonyx-orm#270).
|
|
740
|
-
|
|
741
|
-
|
|
746
|
+
// `store.find` rather than `store.get`: on MySQL/PostgreSQL a model without
|
|
747
|
+
// `static memory = true` is not in memory, and `get` returns undefined.
|
|
748
|
+
// Before hooks are awaited, so an async hook works here.
|
|
749
|
+
beforeHook('delete', 'animal', async (context) => {
|
|
750
|
+
const animal = await store.find('animal', context.request.recordId);
|
|
742
751
|
if (animal.protected) {
|
|
743
752
|
return { errors: [{ detail: 'Cannot delete protected animals' }] };
|
|
744
753
|
}
|
|
@@ -769,8 +778,9 @@ beforeHook('create', 'owner', (context) => {
|
|
|
769
778
|
// Send notification after animal is adopted.
|
|
770
779
|
// `oldState` holds attributes only, and a belongsTo is the related Record, not
|
|
771
780
|
// its id: capture the previous owner's id before the update, compare ids after.
|
|
772
|
-
|
|
773
|
-
|
|
781
|
+
// On SQL the owner resolves only when it is in memory (see MySQL Mode).
|
|
782
|
+
beforeHook('update', 'animal', async (context) => {
|
|
783
|
+
context.state.previousOwnerId = (await store.find('animal', context.request.recordId))?.owner?.id;
|
|
774
784
|
});
|
|
775
785
|
|
|
776
786
|
afterHook('update', 'animal', async (context) => {
|
|
@@ -839,10 +849,13 @@ afterHook('delete', 'animal', async (context) => {
|
|
|
839
849
|
// same value `access()` is handed. Authorizing on `context.params.id` instead
|
|
840
850
|
// looks up the raw client text: on a numeric-id model that lookup returns
|
|
841
851
|
// `undefined` for EVERY spelling, `animal.owner` throws, and the check never
|
|
842
|
-
// runs. Measured; abofs/stonyx-orm#270.
|
|
843
|
-
|
|
852
|
+
// runs. Measured; abofs/stonyx-orm#270. `store.find`, not `store.get`: on SQL a
|
|
853
|
+
// model without `static memory = true` is not in memory. On SQL `animal.owner`
|
|
854
|
+
// resolves only when the owner is in memory too, and is `null` otherwise, so this
|
|
855
|
+
// check then refuses the owner as well (see MySQL Mode).
|
|
856
|
+
beforeHook('delete', 'animal', async (context) => {
|
|
844
857
|
const user = context.state.currentUser;
|
|
845
|
-
const animal = store.
|
|
858
|
+
const animal = await store.find('animal', context.request.recordId);
|
|
846
859
|
|
|
847
860
|
// `animal.owner` is the related Record, so compare its id. Refuse an unowned
|
|
848
861
|
// animal outright: `animal.owner?.id !== user.id` lets a user with no id
|