@stonyx/orm 0.3.2-alpha.81 → 0.3.2-alpha.83

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 (2) hide show
  1. package/README.md +99 -4
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1325,6 +1325,32 @@ or an `errors` member for the withheld case makes the route an **existence
1325
1325
  oracle** — see [Filter functions](#filter-functions) for the rule and for the
1326
1326
  measurement that closed it on this route.
1327
1327
 
1328
+ **A per-record deny for a *related* resource cannot be expressed, and nothing
1329
+ tells you so at the point you would write it.** `createLinkageFilter` resolves
1330
+ the related model's access class by **type**: `context.recordId` is `null`, and
1331
+ `request.params` names a record of a **different model** — the one the route is
1332
+ addressed to. So `access()` is handed the model, the operation and the request,
1333
+ and **a rule that has to know *which* related record it is being asked about
1334
+ cannot be written**. Model-level denies (`return false` for a model) work.
1335
+ Request-level denies (a header, a tenant, the method) work. The per-record
1336
+ **filter** shape works too — `access()` may return a function, and that function
1337
+ receives the whole record, id included. What does not work is branching on the
1338
+ record's identity *before* returning, because `access()` is not told it.
1339
+
1340
+ This is a fixed property of the mechanism rather than a defect awaiting a fix.
1341
+ The verdict is resolved **once per type** and cached before any record has been
1342
+ examined, so seeding `recordId` from a record would let the first member of a
1343
+ `hasMany` decide the context for all of them. The rule the framework holds to is
1344
+ **`recordId` may name a record only where the route addresses exactly one record
1345
+ of the model being asked about** — true for `GET /owners/{id}`, false for
1346
+ linkage, and false for a `hasMany` related-resource route. The consumer-facing
1347
+ consequence is the part to check: a predicate that branches on `recordId` sees
1348
+ `null` here and takes whichever branch `null` takes, with no warning, and if
1349
+ that branch grants then it **grants**. Express the rule as a returned filter
1350
+ function instead. Stated again, with the same label, under
1351
+ [Known limitations](#known-limitations)
1352
+ ([#232](https://github.com/abofs/stonyx-orm/issues/232)).
1353
+
1328
1354
  Do this:
1329
1355
 
1330
1356
  ```js
@@ -1442,10 +1468,31 @@ they are recorded here.
1442
1468
  return 404. Only affects function-style `access` users, for whom the old
1443
1469
  behaviour was the bypass.
1444
1470
 
1445
- "Seven surfaces" means the seven endpoints of **the filtered model**. A write
1446
- to another collection can still reach one of its records through a
1447
- relationship — [#207](https://github.com/abofs/stonyx-orm/issues/207), which
1448
- is **not** closed here.
1471
+ "Seven surfaces" means the seven endpoints of **the filtered model**
1472
+ `GET /:models`, `GET /:models/:id`, `GET /:models/:id/{relationship}`,
1473
+ `GET /:models/:id/relationships/{relationship}`, `POST /:models`,
1474
+ `PATCH /:models/:id` and `DELETE /:models/:id`. That count is still seven and
1475
+ is still the model's own endpoints, but **it is no longer the whole
1476
+ population**: the boundary moved outward rather than the number changing, and
1477
+ this sentence used to be read as saying a filtered model's predicate is
1478
+ consulted nowhere else. It now is, in two further places, both from
1479
+ *another* model's routes —
1480
+
1481
+ - on **both relationship route families**, where the related record is the
1482
+ primary data and the filtered model's own class decides whether it is
1483
+ served at all (breaking change 9 below,
1484
+ [#232](https://github.com/abofs/stonyx-orm/issues/232)); and
1485
+ - on the `relationships.*.data` **linkage** of every request-bound surface
1486
+ that serializes a record through `toJSON()`
1487
+ ([#234](https://github.com/abofs/stonyx-orm/issues/234),
1488
+ [#235](https://github.com/abofs/stonyx-orm/issues/235)), which decides
1489
+ which ids another model's document may name.
1490
+
1491
+ A **write** to another collection can still reach one of its records through
1492
+ a relationship — [#207](https://github.com/abofs/stonyx-orm/issues/207),
1493
+ which is **not** closed here. That is the half of the old sentence that
1494
+ survives, and it is a read/write asymmetry now rather than a blanket
1495
+ statement about cross-model reach.
1449
1496
  5. **A predicate that throws is treated as a denial** rather than propagating to
1450
1497
  Express's default 500 handler. So is an `access()` that throws.
1451
1498
  6. **`access()` returning a bare string is one permission, not full access.**
@@ -1520,6 +1567,54 @@ they are recorded here.
1520
1567
  `stonyx/log`. Previously this case threw out of the handler and express
1521
1568
  answered `500` with a stack trace.
1522
1569
 
1570
+ 9. **Both relationship route families now resolve the *related* model's own
1571
+ access class, so a related record that is hidden on its own routes is no
1572
+ longer served through another model's.**
1573
+ [#232](https://github.com/abofs/stonyx-orm/issues/232). Affects
1574
+ function-style `access` users with relationships between filtered models. The
1575
+ old behaviour was a bypass at **zero query parameters** and with no
1576
+ `include=`: measured on `dev @ 8dda5d6`, `GET /animals/1/owner` returned
1577
+ angela's full document and `GET /animals/1/relationships/owner` returned
1578
+ `{"type":"owner","id":"angela"}`, while `GET /owners/angela` answered `404`.
1579
+ The severe case is a model claimed by **no** access class — `getAccess()`
1580
+ returns `undefined`, no route is mounted for it at all, and it was still
1581
+ readable as a related resource.
1582
+
1583
+ **The shapes, on both families.** A denied `hasMany` member is **dropped from
1584
+ the array**: `200`, `links` intact, no `errors` member. A denied `belongsTo`
1585
+ target answers **`200` with `data: null`**, byte-identical to a target that
1586
+ genuinely does not exist — same status, same bytes modulo the parent id the
1587
+ caller put in the URL. `404` on these routes is now reserved for the
1588
+ **parent**.
1589
+
1590
+ **`data: null` and not `404`, deliberately.** The 404 spelling was an
1591
+ existence oracle and was measured as one on this branch: unauthenticated, no
1592
+ query string, one request each, against `tag` — the model with no route
1593
+ mounted at all — `GET /traits/1/tag` (absent) answered `200`
1594
+ `application/json` at 68 bytes while `GET /traits/2/tag` (denied) answered
1595
+ `404` `text/plain` at 9 bytes, and the document surface reported both as
1596
+ `{"data":null}`. It also brings these two routes into line with this module's
1597
+ rule that every status on a record route is identical for filtered-out and
1598
+ does-not-exist (see [Filter functions](#filter-functions)), which the `404`
1599
+ spelling was the one exception to.
1600
+
1601
+ **What to check before you upgrade.** If a consumer reaches a related record
1602
+ through `GET /:models/:id/{relationship}` that it cannot reach on that
1603
+ record's own collection route, it was relying on the bypass and will now get
1604
+ `data: null` or a shorter array. And the related model's class is resolved
1605
+ through the same `Orm.instance.getAccess` path as the linkage filter, so it
1606
+ inherits the same arity limit: a **single-argument** predicate answers about
1607
+ the collection the request was *addressed to*, not the one it was asked
1608
+ about, and that is the direction that **grants**. See
1609
+ [Known limitations](#known-limitations) and
1610
+ [#221](https://github.com/abofs/stonyx-orm/issues/221).
1611
+
1612
+ **Not closed here:** whether a related resource appears in `included` at all
1613
+ ([#233](https://github.com/abofs/stonyx-orm/issues/233)), and the
1614
+ re-parenting write ([#207](https://github.com/abofs/stonyx-orm/issues/207)).
1615
+ A **per-record** deny for a related resource is not expressible on these
1616
+ routes at all — see [Consumer Contracts](#consumer-contracts).
1617
+
1523
1618
  ### Include Parameter (Sideloading Relationships)
1524
1619
 
1525
1620
  The ORM supports JSON API-compliant relationship sideloading via the `include` query parameter. This reduces the need for multiple API requests by embedding related records in a single response.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "stonyx-async",
5
5
  "stonyx-module"
6
6
  ],
7
- "version": "0.3.2-alpha.81",
7
+ "version": "0.3.2-alpha.83",
8
8
  "description": "",
9
9
  "main": "dist/index.js",
10
10
  "type": "module",