Conversation
`hydrate` cast the state slice straight to `FilterModel`, which is a promise
to the compiler and not a check. A condition whose operator belonged to
another kind, a `set` whose `values` was not a list, or a condition missing
the value its operator needs all reached the predicate builders as a shape
they never tested, and threw while the pipeline's `$derived` was reading
them. That read happens inside the body's `{#each grid.nodes}`, so the throw
took the render pass rather than one column. Six of the seven shapes measured
against 1.3.0 brought the grid down, and every path that reaches `hydrate` is
untrusted: share links, `localStorage`, and anything handed back to
`setState`.
`sanitizeFilterModel` now rebuilds the model from the part that can be read
and drops the rest. A column left with no readable condition stops filtering,
which shows more rows rather than none. That is the deliberate call: for a
column behind a value gate it is not strictly failing safe, and a grid that
will not render is worse.
The predicates carry a second layer for a condition arriving some other way,
`applyFilterModel` included: an unknown operator, a missing value, a `set`
whose values are not a list and a kind nothing knows now pass every row
instead of throwing.
Closes #41
`hydrate` checked `Array.isArray` and then cast, which reads as a check and is not one: a null entry in that array threw on `columnId` while the pipeline was sorting. The same untrusted path as the filter model, one layer thinner. `sanitizeSortState` keeps only the entries naming a column and a direction. An unknown direction, a missing `columnId` and a plain string entry already degraded quietly; they are now dropped rather than carried.
A `NaN` or `Infinity` width reached the CSS custom property as `NaNpx`. A custom property accepts that, but `grid-template-columns: var(...)` then resolves to an invalid value, the declaration is dropped at computed-value time, and every column folds into a single track with the cells stacked down the page. Nothing threw and nothing was logged: the grid simply looked broken, and `widthOverrides` still read as a plausible record while the layout was already gone. Two ways in, both closed. The snapshot boundary now reads values as carefully as it already read keys, and `setWidth`/`setWidths` refuse a width that is not finite, where `clamp` and `Math.round` had been carrying `NaN` straight through. An app computing a width from an empty input field reached the second without going near a snapshot. `setState` also stopped throwing on a corrupt `columns` slice: an `order` that was not an array reached `.filter` inside the caller's own call. Only string ids order the columns now, only real booleans hide one or fold a group, and a slice that is not an object is read as nothing at all. The fields are typed as unknown while they are read, because naming the type there would be the same promise that let the broken snapshot in. The regression test asserts the computed `grid-template-columns` in the browser, not the override record. Backing the fix out turns it red with one track where there should be two. Closes #43
The row index keeps the last row for a repeated id, so an edit addressed to the row the user opened was written to a different one, and a selection stood for two rows at once. Nothing reported it. The id belongs to the app and the grid cannot mend it, but failing at it silently, in the data, is the worst of the available behaviours. A development build now names the ids that collided. Production pays one integer comparison for the whole data set, `index.size` against `nodes.length`; working out which ids repeated costs a second pass and only a build that will print it pays for that. This is the library's only `console` statement, and the lint rule that forbids them is disabled on exactly that line. An error would take an app down over data the grid can still draw, and there is no logger to route it to. Closes #44
Closing the width setters and the snapshot boundary closed the routes the issue named, not the failure itself. Three more reach the same line without passing either: a container measured as `NaN`, which makes every flex column `NaNpx` at once, and a column definition written with `width: NaN` or `flex: NaN`. They all converge on `buildColumnCssVars`, so the check belongs there. A value that is not finite falls back to the column's minimum, and an unusable minimum or flex weight falls back to a width that can be drawn. A track of the wrong size is a much smaller failure than a grid that will not lay out at all. The earlier guards stay. They keep unusable values out of the model rather than papering over them at the end, and they let `setWidth` return an honest answer about the width a column still has.
fix: stop hydrate trusting the snapshot filter and sort state came back in
Both branches added under `## [Unreleased]`, which is the one place two open fix branches always collide. Resolved by keeping both blocks: changelog entries are additive, and taking either side would have dropped the other branch's lines. Ordered by when they landed, so the filter and sort entries that reached `dev` first lead.
fix: stop a width the layout cannot draw, and say when two rows share an id
The two tests that assert a typed value reaches the filter model once counted writes with no reference to how long the typing took, and the count only means something against that. `GridFilterCell` debounces at 200ms, while the browser driver round-trips once per key, so on a loaded machine the debounce fires between keystrokes and one write per key is the correct behaviour, not a regression. Three release runs on this machine measured 2, 3 and 3 writes for `365` while the same file passed alone in 5s. The bound is now what a working debounce can produce: once per window it is left alone for, plus once at the end. Where the typing fits in one window, on CI and any unloaded machine, that is exactly the 1 the tests asserted before, so nothing is given up where the assertion was meaningful. Where it does not, the test no longer fails the machine instead of the code. The assertion that the model ends holding the typed value is untouched. That is the half which catches the bug these tests were written for, the stale value winning, and it holds at any speed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cut by
npm run release. Carries everything on dev, including the version bump. The tag is pushed frommainonce this merges, and that is what publishes 1.3.1.