@stonyx/orm 0.3.2-alpha.82 → 0.3.2-alpha.84

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 +95 -8
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -967,11 +967,29 @@ per-record filter. An input you cannot identify must **deny**.
967
967
  ([#232](https://github.com/abofs/stonyx-orm/issues/232)). This is
968
968
  **membership**: the related resource is the route's *primary* data, so the
969
969
  filter decides whether it is served at all, not merely which ids a document
970
- may name. A denied `hasMany` member is **dropped from the array** the result
971
- is shaped exactly like a genuinely empty relationship, `links` intact, no
972
- `errors` member, same status. A denied `belongsTo` target answers **`200` with
970
+ may name. A denied `hasMany` member is **dropped from the array** and nothing
971
+ in the relationship marks the drop: `links` intact, no `errors` member, same
972
+ status, and an array of survivors shaped exactly like one from a parent that
973
+ only ever had those members. A denied `belongsTo` target answers **`200` with
973
974
  `data: null`**, byte-identical to a target that is genuinely absent, for the
974
- same reason. Nothing on either family errors and no status changes — the
975
+ same reason.
976
+
977
+ **That is a claim about the relationship, not about the document, and the gap
978
+ is measurable in this repo's own fixture.** `owner` declares a computed
979
+ `totalPets` returning `this.pets.length`, which reads the **store** and is
980
+ never filtered. Measured on this branch, unauthenticated, at zero query
981
+ parameters: `GET /owners/gina` answers `attributes.totalPets: 5` while its
982
+ `relationships.pets.data` names **four** ids, and both relationship routes
983
+ serve the same four. The relationship discloses nothing; the document it
984
+ arrives in discloses that exactly one child was withheld. Do not read
985
+ "indistinguishable" as a property of the response — it is a property of the
986
+ relationship member alone. The other channels in the same class are
987
+ [#245](https://github.com/abofs/stonyx-orm/issues/245) (computed attributes,
988
+ which is the one measured above),
989
+ [#233](https://github.com/abofs/stonyx-orm/issues/233) (`included`
990
+ membership) and [#246](https://github.com/abofs/stonyx-orm/issues/246) (the
991
+ absence of `attributes.<fk>` on a `POST` response), all open. **Audit your
992
+ computed properties before you treat a dropped member as unobservable.** Nothing on either family errors and no status changes — the
975
993
  status on these routes belongs to the **parent**, and `data` carries the
976
994
  answer about the related record. The `/relationships/` family built its `{type, id}` by
977
995
  hand rather than through `toJSON()`, which is why the linkage filter shipped in
@@ -1468,10 +1486,31 @@ they are recorded here.
1468
1486
  return 404. Only affects function-style `access` users, for whom the old
1469
1487
  behaviour was the bypass.
1470
1488
 
1471
- "Seven surfaces" means the seven endpoints of **the filtered model**. A write
1472
- to another collection can still reach one of its records through a
1473
- relationship — [#207](https://github.com/abofs/stonyx-orm/issues/207), which
1474
- is **not** closed here.
1489
+ "Seven surfaces" means the seven endpoints of **the filtered model**
1490
+ `GET /:models`, `GET /:models/:id`, `GET /:models/:id/{relationship}`,
1491
+ `GET /:models/:id/relationships/{relationship}`, `POST /:models`,
1492
+ `PATCH /:models/:id` and `DELETE /:models/:id`. That count is still seven and
1493
+ is still the model's own endpoints, but **it is no longer the whole
1494
+ population**: the boundary moved outward rather than the number changing, and
1495
+ this sentence used to be read as saying a filtered model's predicate is
1496
+ consulted nowhere else. It now is, in two further places, both from
1497
+ *another* model's routes —
1498
+
1499
+ - on **both relationship route families**, where the related record is the
1500
+ primary data and the filtered model's own class decides whether it is
1501
+ served at all (breaking change 9 below,
1502
+ [#232](https://github.com/abofs/stonyx-orm/issues/232)); and
1503
+ - on the `relationships.*.data` **linkage** of every request-bound surface
1504
+ that serializes a record through `toJSON()`
1505
+ ([#234](https://github.com/abofs/stonyx-orm/issues/234),
1506
+ [#235](https://github.com/abofs/stonyx-orm/issues/235)), which decides
1507
+ which ids another model's document may name.
1508
+
1509
+ A **write** to another collection can still reach one of its records through
1510
+ a relationship — [#207](https://github.com/abofs/stonyx-orm/issues/207),
1511
+ which is **not** closed here. That is the half of the old sentence that
1512
+ survives, and it is a read/write asymmetry now rather than a blanket
1513
+ statement about cross-model reach.
1475
1514
  5. **A predicate that throws is treated as a denial** rather than propagating to
1476
1515
  Express's default 500 handler. So is an `access()` that throws.
1477
1516
  6. **`access()` returning a bare string is one permission, not full access.**
@@ -1546,6 +1585,54 @@ they are recorded here.
1546
1585
  `stonyx/log`. Previously this case threw out of the handler and express
1547
1586
  answered `500` with a stack trace.
1548
1587
 
1588
+ 9. **Both relationship route families now resolve the *related* model's own
1589
+ access class, so a related record that is hidden on its own routes is no
1590
+ longer served through another model's.**
1591
+ [#232](https://github.com/abofs/stonyx-orm/issues/232). Affects
1592
+ function-style `access` users with relationships between filtered models. The
1593
+ old behaviour was a bypass at **zero query parameters** and with no
1594
+ `include=`: measured on `dev @ 8dda5d6`, `GET /animals/1/owner` returned
1595
+ angela's full document and `GET /animals/1/relationships/owner` returned
1596
+ `{"type":"owner","id":"angela"}`, while `GET /owners/angela` answered `404`.
1597
+ The severe case is a model claimed by **no** access class — `getAccess()`
1598
+ returns `undefined`, no route is mounted for it at all, and it was still
1599
+ readable as a related resource.
1600
+
1601
+ **The shapes, on both families.** A denied `hasMany` member is **dropped from
1602
+ the array**: `200`, `links` intact, no `errors` member. A denied `belongsTo`
1603
+ target answers **`200` with `data: null`**, byte-identical to a target that
1604
+ genuinely does not exist — same status, same bytes modulo the parent id the
1605
+ caller put in the URL. `404` on these routes is now reserved for the
1606
+ **parent**.
1607
+
1608
+ **`data: null` and not `404`, deliberately.** The 404 spelling was an
1609
+ existence oracle and was measured as one on this branch: unauthenticated, no
1610
+ query string, one request each, against `tag` — the model with no route
1611
+ mounted at all — `GET /traits/1/tag` (absent) answered `200`
1612
+ `application/json` at 68 bytes while `GET /traits/2/tag` (denied) answered
1613
+ `404` `text/plain` at 9 bytes, and the document surface reported both as
1614
+ `{"data":null}`. It also brings these two routes into line with this module's
1615
+ rule that every status on a record route is identical for filtered-out and
1616
+ does-not-exist (see [Filter functions](#filter-functions)), which the `404`
1617
+ spelling was the one exception to.
1618
+
1619
+ **What to check before you upgrade.** If a consumer reaches a related record
1620
+ through `GET /:models/:id/{relationship}` that it cannot reach on that
1621
+ record's own collection route, it was relying on the bypass and will now get
1622
+ `data: null` or a shorter array. And the related model's class is resolved
1623
+ through the same `Orm.instance.getAccess` path as the linkage filter, so it
1624
+ inherits the same arity limit: a **single-argument** predicate answers about
1625
+ the collection the request was *addressed to*, not the one it was asked
1626
+ about, and that is the direction that **grants**. See
1627
+ [Known limitations](#known-limitations) and
1628
+ [#221](https://github.com/abofs/stonyx-orm/issues/221).
1629
+
1630
+ **Not closed here:** whether a related resource appears in `included` at all
1631
+ ([#233](https://github.com/abofs/stonyx-orm/issues/233)), and the
1632
+ re-parenting write ([#207](https://github.com/abofs/stonyx-orm/issues/207)).
1633
+ A **per-record** deny for a related resource is not expressible on these
1634
+ routes at all — see [Consumer Contracts](#consumer-contracts).
1635
+
1549
1636
  ### Include Parameter (Sideloading Relationships)
1550
1637
 
1551
1638
  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.82",
7
+ "version": "0.3.2-alpha.84",
8
8
  "description": "",
9
9
  "main": "dist/index.js",
10
10
  "type": "module",