data
data copied to clipboard
fix: many relation updates via memoized arrays and guarded hooks
Fix #346
Fix many() relation updates so array assignments and element-level mutations keep foreignKeys and inverse links in sync, guarding the “through relation” early hook from array indices.
- Maintain a stable resolved array for many relations so push/splice produce patches and relational updates get observed.
- Add regression tests covering many-relation array reassignment and push to ensure new foreign records appear and order is preserved.
Solution
This fix uses memoization: the many() relation now maintains a stable resolved array (cached and mutated in place) so Mutative can draft it and emit patches on push/splice. We didn’t materialize getters into plain arrays before drafting.
- Why memoize the resolved array? Mutative only emits patches for mutations on draftable references. The relation getter was returning a fresh array each time, so draft.comments was not the same object Mutative proxied, making push invisible (no patches). By caching a stable array per owner record and reusing it, Mutative can wrap that array, see push/splice, and generate patches. We still update that array’s contents on resolve so it reflects current foreign records while keeping the reference stable.
- Why not materialize getters up-front? Pre-flattening all relational getters into plain arrays before calling createDraft would lose the getter semantics during the draft, risk breaking circular relations, and require extra restoration logic. Memoization keeps getters intact and keeps the minimal surface change (just stabilizes the returned reference).
- Early hook guard change: The “through relation” early hook was firing for array index paths (e.g., comments.0), treating pushes as nested foreign updates and short-circuiting owner updates. Adding the path.length > relationPath.length and “next segment not a number” guard ensures only genuine nested foreign updates are intercepted; array-level updates now flow to the relation handlers.
- Foreign key sync on array updates: We now explicitly recompute/add/remove foreignKeys (and inverse links) on relation-level array reassignment and on index-level patches, so both spread (draft.comments = [...]) and push stay consistent with inverse relations and uniqueness checks.