@jsenv/navi 0.29.436 → 0.29.437
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/dist/dev/jsenv_navi.js +820 -117
- package/dist/dev/jsenv_navi.js.map +15 -11
- package/dist/jsenv_navi.js +820 -117
- package/dist/jsenv_navi.js.map +15 -11
- package/docs/AI_INSTRUCTIONS.md +3 -2
- package/docs/data_states.md +3 -1
- package/docs/list_refresh.md +4 -1
- package/docs/network_policy.md +9 -4
- package/docs/resource.md +93 -1
- package/docs/scroll.md +3 -1
- package/package.json +1 -1
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -84,8 +84,9 @@ start when unsure which export solves a problem.
|
|
|
84
84
|
action and a command on one press. Before running an action from a component.
|
|
85
85
|
- `resource.md` — `resource()` and its relations (never an `op` discriminator),
|
|
86
86
|
`GET_RANGE`, a search as the same `GET_MANY`, `withParams()` scopes and
|
|
87
|
-
`dependencies`, `persist
|
|
88
|
-
writing a resource, and before
|
|
87
|
+
`dependencies`, `persist` (and `keepPageOnScreen` for the page on screen), a
|
|
88
|
+
function versus the verb's instance. Before writing a resource, and before
|
|
89
|
+
caching a response yourself.
|
|
89
90
|
- `data_states.md` — `data`, `loading` and `error` are three questions;
|
|
90
91
|
`loading: true` never suspends; a skeleton is told whether it is loading.
|
|
91
92
|
Before drawing a skeleton on `!data` or hiding content because `error` is set.
|
package/docs/data_states.md
CHANGED
|
@@ -79,7 +79,9 @@ action, a `bindParams` holding one) included, when the signal moves to other
|
|
|
79
79
|
params. A row known from the previous page load counts too, when the resource
|
|
80
80
|
asks for it:
|
|
81
81
|
[`persist`](./resource.md#persist-the-last-answer-drawn-again-after-a-reload)
|
|
82
|
-
puts a reload on the second line as well
|
|
82
|
+
puts a reload on the second line as well, and so does
|
|
83
|
+
[`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document) for
|
|
84
|
+
what the page on screen reads — a `GET_MANY` and a `GET_RANGE` included.
|
|
83
85
|
|
|
84
86
|
**The emptiness test is `data === undefined`, never `loading`.** Reading
|
|
85
87
|
`loading` as "there is nothing to display" blanks the screen on every refresh —
|
package/docs/list_refresh.md
CHANGED
|
@@ -127,7 +127,10 @@ A composition is about the **values** its params hold, not the reader instance:
|
|
|
127
127
|
writes the same one. A verb in `rerunOn.GET_RANGE`, or `reader.invalidate()`,
|
|
128
128
|
drops the compositions — they stand for an order that is gone — and
|
|
129
129
|
`memoryBudget` (1000 ranks by default) trims the ranks far from any window,
|
|
130
|
-
which are asked for again if the user goes back to them.
|
|
130
|
+
which are asked for again if the user goes back to them. A composition read by a
|
|
131
|
+
list on screen also outlives the document when the page is kept
|
|
132
|
+
([`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)): the ranks around the window it draws are written with
|
|
133
|
+
their rows, and a reload is the same return after a longer absence.
|
|
131
134
|
|
|
132
135
|
### Who decides the re-read — and who does not
|
|
133
136
|
|
package/docs/network_policy.md
CHANGED
|
@@ -67,9 +67,13 @@ was asked:
|
|
|
67
67
|
what it holds instead of falling into its error boundary. A route opening a
|
|
68
68
|
user by id **or** by slug gives navi both keys to look by:
|
|
69
69
|
`resource("user", { uniqueKeys: ["slug"], … })`.
|
|
70
|
-
- **A
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
- **A read drawing a kept answer completes with it.** A root `GET` or
|
|
71
|
+
`GET_MANY` whose first run drew what the page kept from the previous document
|
|
72
|
+
([`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)) answers with those rows: a reload with no network reopens
|
|
73
|
+
on the page.
|
|
74
|
+
- **A completed read stays completed.** Otherwise `GET_MANY` (and every other
|
|
75
|
+
read) has nowhere to answer from: the store holds items, not queries, and only
|
|
76
|
+
the action's own value knows which ids answered `/users?scope=shareable`. So a
|
|
73
77
|
rerun asked of a completed read under the policy is held — the action keeps
|
|
74
78
|
its state, its value and its data, exactly as a `run()` on a completed action
|
|
75
79
|
would.
|
|
@@ -185,7 +189,8 @@ has nothing to decide which one says why.
|
|
|
185
189
|
which signal, for which session: a resource declaring
|
|
186
190
|
[`persist`](./resource.md#persist-the-last-answer-drawn-again-after-a-reload)
|
|
187
191
|
has its kept row answer the `GET` under the policy, so a reload offline
|
|
188
|
-
reopens on it.
|
|
192
|
+
reopens on it; [`keepPageOnScreen`](./resource.md#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document) does the same for the page
|
|
193
|
+
on screen.
|
|
189
194
|
- **A queue of writes to replay** once the network is back — not navi's: what a
|
|
190
195
|
replayed write means (a score entered twice? a seat taken since?) is the app's
|
|
191
196
|
business.
|
package/docs/resource.md
CHANGED
|
@@ -34,6 +34,7 @@ Actions are read in components through the action system (`useAsyncData`,
|
|
|
34
34
|
|
|
35
35
|
- [`store.upsert()` is not how data enters the store](#storeupsert-is-not-how-data-enters-the-store)
|
|
36
36
|
- [`persist`: the last answer, drawn again after a reload](#persist-the-last-answer-drawn-again-after-a-reload)
|
|
37
|
+
- [`keepPageOnScreen`: the page on screen, drawn again by the next document](#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document)
|
|
37
38
|
- [`GET_RANGE`: feeding a list that loads as it scrolls](#get_range-feeding-a-list-that-loads-as-it-scrolls)
|
|
38
39
|
- [Searching the same collection](#searching-the-same-collection)
|
|
39
40
|
- [Relations: pick one of the four methods](#relations-pick-one-of-the-four-methods)
|
|
@@ -129,11 +130,102 @@ What the copy is not:
|
|
|
129
130
|
- **an answer.** The `GET` goes out every time; a screen must not read `data` as
|
|
130
131
|
"the server confirmed" — that is `loading`, and the two are independent.
|
|
131
132
|
- **a store on disk.** One row per params key, `GET` only: meant for a singleton
|
|
132
|
-
or a handful of rows, not a resource whose `GET` is asked for every id.
|
|
133
|
+
or a handful of rows, not a resource whose `GET` is asked for every id. What
|
|
134
|
+
the page on screen reads, lists included, is
|
|
135
|
+
[`keepPageOnScreen`](#keeppageonscreen-the-page-on-screen-drawn-again-by-the-next-document),
|
|
136
|
+
below.
|
|
133
137
|
- **seeded at declaration.** The row enters the store at the first run of the
|
|
134
138
|
`GET`, once the relations are declared, so its relation values are normalized
|
|
135
139
|
as a real answer's are. `ME.store` read before that first run is empty.
|
|
136
140
|
|
|
141
|
+
## `keepPageOnScreen`: the page on screen, drawn again by the next document
|
|
142
|
+
|
|
143
|
+
A reload, a pull-to-refresh, a tab the system discarded in the background: a
|
|
144
|
+
new document opens at the same address, on the page that was left, and what
|
|
145
|
+
that page reads almost always answers what it answered a moment ago. `persist`
|
|
146
|
+
keeps one row of one resource. A page reads several things, lists included,
|
|
147
|
+
and which ones depends on the page. `keepPageOnScreen` keeps that set, one
|
|
148
|
+
slot, replaced when the user moves:
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const pageKeptSignal = stateSignal(undefined, {
|
|
152
|
+
id: `page@${APP_VERSION}`,
|
|
153
|
+
persists: true,
|
|
154
|
+
type: "object",
|
|
155
|
+
});
|
|
156
|
+
// Before the routes start: the first runs are the ones that look.
|
|
157
|
+
keepPageOnScreen({
|
|
158
|
+
signal: pageKeptSignal,
|
|
159
|
+
when: () => !viewAsSignal.value,
|
|
160
|
+
always: [MY_GAMES_PAGE],
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**The unit is the page, not the resource.** One resource is read by several
|
|
165
|
+
screens: `GAME.GET` for any game opened, `USER.GET_MANY` for a tab and for
|
|
166
|
+
every search. Kept per resource, that is every game ever opened. The page's
|
|
167
|
+
reads are what navi already runs for it: the route actions asking something for
|
|
168
|
+
the page (`activeRouteActionsSignal`, which the app also reads to rerun the page
|
|
169
|
+
when it comes back to the foreground), and the compositions the `<List.Items>`
|
|
170
|
+
on screen read. A navigation replaces the slot with the next page's reads.
|
|
171
|
+
|
|
172
|
+
What is kept, for the next document's first run of the same read with the same
|
|
173
|
+
params:
|
|
174
|
+
|
|
175
|
+
| read | kept |
|
|
176
|
+
| --------------------------------------- | ------------------------------------------------------------ |
|
|
177
|
+
| a route action on a resource `GET` | the row, its relations inline |
|
|
178
|
+
| a route action on a resource `GET_MANY` | the rows in order, written the same way |
|
|
179
|
+
| a `<List.Items>` reading a `GET_RANGE` | the count and the ranks around the window drawn, rows inline |
|
|
180
|
+
|
|
181
|
+
The `GET` and the `GET_MANY` draw it as their provisional value: `data` set
|
|
182
|
+
while `loading` is `true`, the answer replacing it. The list finds it as a
|
|
183
|
+
composition it left
|
|
184
|
+
([list_refresh.md](./list_refresh.md#leaving-the-screen-and-coming-back)): every
|
|
185
|
+
rank stale, one request for the window. Under a network policy answering reads
|
|
186
|
+
from the store, the kept answer answers the read
|
|
187
|
+
([network_policy.md](./network_policy.md)).
|
|
188
|
+
|
|
189
|
+
What is not kept, and why:
|
|
190
|
+
|
|
191
|
+
- **a relationship read** (`.one()`, `.many()`, `.scopedOne()`,
|
|
192
|
+
`.scopedMany()`). Its answer enters the store through its owner: the parent
|
|
193
|
+
row it is nested in, or the store of the owner it is scoped to. It is not rows
|
|
194
|
+
of its own that could be written back alone.
|
|
195
|
+
- **a plain `createAction`** (a summary, a count the server computes). Its value
|
|
196
|
+
is whatever the callback returns, with no store to give it a shape to write
|
|
197
|
+
and to read back. A page's code is a route action too, and it is a function.
|
|
198
|
+
- **a read that is not a route action**: a search typed on the page, a popup's
|
|
199
|
+
own load.
|
|
200
|
+
|
|
201
|
+
A read writes what it shows once it has answered in this document. Until then
|
|
202
|
+
the slot keeps what the previous document kept for it, never what the read
|
|
203
|
+
shows meanwhile: an answer handed over from other params (a list standing in
|
|
204
|
+
for the next search) is not about these. The write waits for the page to settle
|
|
205
|
+
and is flushed when the document is hidden, which is when the system may discard
|
|
206
|
+
it. Between those moments a navigation goes through half-built pages: the route
|
|
207
|
+
matched but its list not mounted, or the page left still drawn under a
|
|
208
|
+
transition.
|
|
209
|
+
|
|
210
|
+
**The start page.** An installed app launched after being killed opens at its
|
|
211
|
+
start address, not on the page that was left. The reads made while a route of
|
|
212
|
+
`always` matches stay kept once that page is left, until the next visit
|
|
213
|
+
replaces them.
|
|
214
|
+
|
|
215
|
+
What stays the app's, as for `persist`: **the key** (the deployed version in the
|
|
216
|
+
signal's `id`), **`when`** (`false` reads nothing, writes nothing, and empties
|
|
217
|
+
the signal), and **the moment**: sign-out writes `undefined` into the signal,
|
|
218
|
+
whatever page it holds, and nothing is written again until a read lands. Also
|
|
219
|
+
**what the reads feed beside their data**: a count the app sets from an answer
|
|
220
|
+
is its own signal to keep. When a list's opening position comes from such a
|
|
221
|
+
count, `defaultScrolled` follows the fresh value as long as nobody has moved the
|
|
222
|
+
list ([scroll.md](./scroll.md#where-the-list-opens-and-where-it-is)).
|
|
223
|
+
|
|
224
|
+
What it costs: the copy follows the store, so a `PUT` on a row rewrites it, and
|
|
225
|
+
every change in the stores the page reads serializes the page's reads again.
|
|
226
|
+
Only the write to the signal is coalesced. A list's window with its rows inline
|
|
227
|
+
is tens of kilobytes.
|
|
228
|
+
|
|
137
229
|
## `GET_RANGE`: feeding a list that loads as it scrolls
|
|
138
230
|
|
|
139
231
|
A `<List.Items>` asks for the rows it is about to draw and keeps what it gets.
|
package/docs/scroll.md
CHANGED
|
@@ -121,7 +121,9 @@ say so with `"document"` or the element itself.
|
|
|
121
121
|
by MEASURING it: it lands where it was even if rows were inserted before it,
|
|
122
122
|
whatever the screen it was saved on. "Reopen a thread where I left it" is
|
|
123
123
|
already provided; keep the position whole, its `visibleCount` sizes the first
|
|
124
|
-
paint. A
|
|
124
|
+
paint. A `defaultScrolled` that changes before anyone has moved the list is
|
|
125
|
+
followed — a place read from an answer that refreshes (yesterday's "today", then
|
|
126
|
+
today's) — and it is compared by value, so it can be computed at every render. A list with an `id` also **comes back where it was** when its screen is
|
|
125
127
|
left and come back to, the way the page does (its `id` must name one list of the
|
|
126
128
|
app, see [below](#a-list-that-comes-back-one-id-one-list));
|
|
127
129
|
**`scrollResetOnNavigation`** opts out. A list scrolling the document that
|