@loadbare/app 0.9.0 → 0.11.0

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 (82) hide show
  1. package/README.md +3 -3
  2. package/dist/build/assemble.d.ts.map +1 -1
  3. package/dist/build/assemble.js +107 -90
  4. package/dist/build/assemble.js.map +1 -1
  5. package/dist/build/expand.d.ts +6 -1
  6. package/dist/build/expand.d.ts.map +1 -1
  7. package/dist/build/expand.js +112 -26
  8. package/dist/build/expand.js.map +1 -1
  9. package/dist/build/locations.d.ts +2 -3
  10. package/dist/build/locations.d.ts.map +1 -1
  11. package/dist/build/locations.js +2 -3
  12. package/dist/build/locations.js.map +1 -1
  13. package/dist/build/pages.d.ts +3 -4
  14. package/dist/build/pages.d.ts.map +1 -1
  15. package/dist/build/pages.js +3 -4
  16. package/dist/build/pages.js.map +1 -1
  17. package/dist/core/lb-constants.d.ts +27 -24
  18. package/dist/core/lb-constants.d.ts.map +1 -1
  19. package/dist/core/lb-constants.js +103 -168
  20. package/dist/core/lb-constants.js.map +1 -1
  21. package/dist/core/lb-types.d.ts +64 -77
  22. package/dist/core/lb-types.d.ts.map +1 -1
  23. package/dist/core/lb-types.js +40 -7
  24. package/dist/core/lb-types.js.map +1 -1
  25. package/dist/hub/lb-apply.d.ts +47 -37
  26. package/dist/hub/lb-apply.d.ts.map +1 -1
  27. package/dist/hub/lb-apply.js +195 -199
  28. package/dist/hub/lb-apply.js.map +1 -1
  29. package/dist/hub/lb-hub.browser.d.ts +1 -1
  30. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  31. package/dist/hub/lb-hub.browser.js +410 -449
  32. package/dist/hub/lb-hub.browser.js.map +1 -1
  33. package/dist/server/lb-express.d.ts +5 -5
  34. package/dist/server/lb-express.d.ts.map +1 -1
  35. package/dist/server/lb-express.js +35 -66
  36. package/dist/server/lb-express.js.map +1 -1
  37. package/dist/server/lb-server.d.ts +77 -135
  38. package/dist/server/lb-server.d.ts.map +1 -1
  39. package/dist/server/lb-server.js +132 -79
  40. package/dist/server/lb-server.js.map +1 -1
  41. package/docs/TECHREF-1.0.md +908 -585
  42. package/docs/comparison.md +243 -185
  43. package/docs/prior-art.md +15 -14
  44. package/docs/reference/builder.md +9 -3
  45. package/docs/reference/chrome.md +107 -56
  46. package/docs/reference/custom-elements.md +291 -173
  47. package/docs/reference/data-binding.md +381 -374
  48. package/docs/reference/overview.md +12 -10
  49. package/docs/reference/page-files.md +164 -99
  50. package/docs/reference/server.md +2 -2
  51. package/docs/reference/widgets.md +104 -110
  52. package/docs/roadmap.md +32 -39
  53. package/docs/terms-of-art.md +57 -0
  54. package/docs/testing.md +97 -68
  55. package/docs/theory.md +92 -58
  56. package/docs/tutorials/010-pages-and-navigation.md +20 -12
  57. package/docs/tutorials/020-css.md +6 -3
  58. package/docs/tutorials/030-html-decomposition.md +9 -7
  59. package/docs/tutorials/040-displaying-data.md +30 -13
  60. package/docs/tutorials/{050-actions.md → 050-requests.md} +25 -15
  61. package/docs/tutorials/060-custom-element-code.md +17 -16
  62. package/docs/tutorials/065-conditional-rendering.md +34 -23
  63. package/docs/tutorials/070-displaying-a-list.md +29 -21
  64. package/docs/tutorials/072-inserting-into-a-list.md +24 -16
  65. package/docs/tutorials/074-deleting-from-a-list.md +9 -7
  66. package/docs/tutorials/076-updating-a-list-item.md +11 -10
  67. package/docs/tutorials/080-widget-requests.md +71 -43
  68. package/docs/tutorials/090-using-widget-libraries.md +22 -22
  69. package/docs/what-does-loadbare-extend.md +124 -0
  70. package/package.json +1 -1
  71. package/skills/loadbare-app/SKILL.md +201 -123
  72. package/skills/loadbare-app/references/TECHREF-1.0.md +908 -585
  73. package/skills/loadbare-app/references/builder.md +9 -3
  74. package/skills/loadbare-app/references/chrome.md +107 -56
  75. package/skills/loadbare-app/references/custom-elements.md +291 -173
  76. package/skills/loadbare-app/references/data-binding.md +381 -374
  77. package/skills/loadbare-app/references/overview.md +12 -10
  78. package/skills/loadbare-app/references/page-files.md +164 -99
  79. package/skills/loadbare-app/references/server.md +2 -2
  80. package/skills/loadbare-app/references/widgets.md +104 -110
  81. package/docs/analysis-accidental-complexity.md +0 -149
  82. package/docs/analysis-closed-set.md +0 -210
@@ -2,7 +2,7 @@
2
2
 
3
3
  This reference is organized into four groups that reflect how an application is
4
4
  put together: the shell it lives in, the build that assembles it, the pages it
5
- serves, and the widgets those pages are made of.
5
+ serves, and the custom elements those pages are made of.
6
6
 
7
7
  ## The application shell
8
8
 
@@ -24,15 +24,17 @@ serves, and the widgets those pages are made of.
24
24
 
25
25
  | Topic | Description |
26
26
  |---------------------------------------------------------|--------------------------------|
27
- | [`<name>.page.html`](./page-files.md#html) | The page's HTML |
28
- | [`<name>.queries.ts`](./page-files.md#queries) | The data the page displays |
29
- | [`<name>.requests.ts`](./page-files.md#requests-actions-crud) | Data Channel handlers |
27
+ | [`<stub>.page.html`](./page-files.md#html) | The page's HTML |
28
+ | [`<stub>.queries.ts`](./page-files.md#queries) | The queries the page shows |
29
+ | [`<stub>.requests.ts`](./page-files.md#requests) | The page's request handlers |
30
30
  | [Data Binding](./data-binding.md) | Connecting HTML to server data |
31
+ | [The URL](./chrome.md#the-url) | Pages, links and query parms |
31
32
 
32
- ## Widgets
33
+ ## Custom elements
33
34
 
34
- | Topic | Description |
35
- |------------------------------------------------|---------------------------------|
36
- | [What a widget is](./custom-elements.md) | An HTML file, a script, or both |
37
- | [`<tag-name>.html`](./custom-elements.md#html) | The markup the tag expands into |
38
- | [`<tag-name>.ts`](./custom-elements.md#code) | The class the tag registers |
35
+ | Topic | Description |
36
+ |--------------------------------------------------------|---------------------------------|
37
+ | [What a custom element is](./custom-elements.md) | An HTML file, a script, or both |
38
+ | [`<tag-name>.html`](./custom-elements.md#html) | The markup the tag expands into |
39
+ | [`<tag-name>.browser.ts`](./custom-elements.md#code) | The class the tag registers |
40
+ | [The Basic Widget Library](./widgets.md) | `@loadbare/widgets` |
@@ -1,92 +1,141 @@
1
1
  # Page Files
2
2
 
3
- A page is a set of files sharing one base name. The application writes the
4
- HTML, and adds queries and requests when the page shows data.
3
+ A page is a set of files sharing one stub, the file name less its suffix.
4
+ The application writes the HTML, and adds queries and requests when the page
5
+ shows data.
5
6
 
6
7
  | File | Holds |
7
8
  |----------------------|-------------------------------|
8
- | `<name>.page.html` | The page's HTML |
9
- | `<name>.queries.ts` | The data the page displays |
10
- | `<name>.requests.ts` | What the page does when asked |
9
+ | `<stub>.page.html` | The page's HTML |
10
+ | `<stub>.queries.ts` | The queries the page shows |
11
+ | `<stub>.requests.ts` | What the page does when asked |
11
12
 
12
13
  Name the landing page `index.page.html`. A bare `/` resolves to `index`, and
13
- every other path names the page of the same name.
14
+ every other path names the page whose stub it is.
14
15
 
15
- Put the files anywhere under `src/`. The builder pairs them by base name, not
16
- by directory; `src/pages/` is the convention.
16
+ Put the files anywhere under `src/`. The builder pairs them by stub, not by
17
+ directory; `src/pages/` is the convention.
18
+
19
+ ## What the page files are
20
+
21
+ A page's queries are that page's view model, answered on the server. Each
22
+ is shaped for the elements that name it, on one page, so it answers in the
23
+ form the page shows: a date formatted, an amount with its separators, and
24
+ the phrase a reader sees, such as "no postings yet" or "12 postings",
25
+ rather than a count for the browser to word. The hub does no conversion,
26
+ so there is no later place for that work to go. The markup says where each
27
+ value goes, and the query says what it is.
28
+
29
+ A value the page reads back, rather than only shows, stays data:
30
+
31
+ | Value | Read back by | So it is |
32
+ |------------------------------------|---------------------------------|-----------------------------------|
33
+ | A column that lands on a control | The request that gathers it | What the handler expects to receive |
34
+ | The key column | Every request from the row | What the handler looks the row up by |
35
+ | A column `lb-show` names | The hub | `null` or `false` for off |
36
+ | A column a stylesheet selects on | `[lb-column-value="…"]` | A fixed token, not wording |
37
+
38
+ The page files are not a data layer; they sit on one. The application
39
+ opens its own database and keeps its rules there, or in code its handlers
40
+ share, and the page files stay thin because integrity is not their job.
41
+
42
+ What two pages share goes beneath them, not between. Two pages showing the
43
+ same data each write the query, or both read a database view. Shared
44
+ wording and shared parm reading go in a helper module the queries import.
45
+ Everything that belongs to one page stays in one place, so a change to a
46
+ page touches that page's files.
17
47
 
18
48
  ## HTML
19
49
 
20
- Write the page as a fragment. The fragment will land in `<main>`, which
21
- is supplied by [chrome.html](./chrome.md).
50
+ Write the page as a fragment. The fragment lands in `<main>`, which
51
+ [chrome.html](./chrome.md) supplies.
22
52
 
23
53
  ```html
24
54
  <!-- src/pages/about.page.html -->
55
+ <title>About</title>
25
56
  <h1>About</h1>
26
57
  <p>This is the about page.</p>
27
58
 
28
- <div lb-row="visits">
29
- <p>This page has been visited <span lb-cell="count"></span> times.</p>
59
+ <div lb-query="visits">
60
+ <p>This page has been visited <span lb-column="count"></span> times.</p>
30
61
  </div>
31
62
  ```
32
63
 
33
- Bind elements to data with `lb-list` or `lb-row`, `lb-cell`, and the rest of the
34
- attribute vocabulary in [Data Binding](./data-binding.md).
64
+ Show data with `lb-query`, `lb-column`, and the rest of the attributes in
65
+ [Data Binding](./data-binding.md).
66
+
67
+ A page that shows no data needs no other file.
68
+
69
+ ### The page title
35
70
 
36
- A page that displays no data needs no other file.
71
+ Write a `<title>` in the page file to name the page. The builder removes it
72
+ from the page and stamps its text on the page's `<template>` as
73
+ `lb-page-title`. The hub shows it as the `lb-url` column `lb-page-label`, and
74
+ sets the document title to it; see [The URL](./chrome.md#the-url). A page
75
+ with no `<title>` is labeled by its stub.
37
76
 
38
77
  ## Queries
39
78
 
40
- Export `queries` from `<name>.queries.ts`. Each key is a name the HTML binds
41
- to with `lb-list` or `lb-row`, and each value takes the request context and returns
42
- that query's result:
79
+ Export `queries` from `<stub>.queries.ts`. Each key is a query name the HTML
80
+ names with `lb-query`, and each value declares the query's kind, its key
81
+ column, and the function that answers it:
43
82
 
44
83
  ```ts
45
- // src/pages/about.queries.ts
46
- import { row, type Queries } from "@loadbare/app/server";
84
+ // src/pages/directory.queries.ts
85
+ import { row, rows, type Queries } from "@loadbare/app/server";
47
86
 
48
87
  export const queries: Queries = {
49
- visits: row(async (ctx) => ({ count: String(await ctx.db.visitCount()) })),
88
+ visits: row("page", async (ctx) => ({
89
+ page: "directory",
90
+ count: String(await ctx.db.visitCount()),
91
+ })),
92
+ directory: rows("id", (ctx) => ctx.db.directory()),
50
93
  };
51
94
  ```
52
95
 
53
- Declare each query with `row()` or `list()`. Cardinality is a property of the
54
- name rather than of any one answer, so one name answers with one shape,
55
- always, and `createHub` refuses an answer that disagrees. A page that needs
56
- the same data as one row and as a set declares two queries.
96
+ | Declared with | Answers with |
97
+ |-------------------|-------------------------------------------|
98
+ | `row(key, run)` | One row, an object |
99
+ | `rows(key, run)` | All its rows, an array of objects, in order |
57
100
 
58
- The hub hands each cell to the browser untouched and takes no position on
59
- its type, so what a number, a date or a null looks like is decided here, in
60
- the query. Formatting it here means the browser displays a value it never
61
- computes.
101
+ `key` names the column that identifies a row. Give every row that column,
102
+ including a `row` query's: an aggregate row answers with a constant key. The
103
+ hub matches rows to live rows by key, and a request names a row by key.
62
104
 
63
- Declare a query that answers with many rows using `list()`, and return the
64
- array itself:
105
+ Kind and key belong to the name, so one name answers with one shape, always,
106
+ and `createHub` leaves out an answer that disagrees with its declaration. A
107
+ page that needs the same data as one row and as a set declares two queries.
65
108
 
66
- ```ts
67
- // src/pages/directory.queries.ts
68
- import { list, type Queries } from "@loadbare/app/server";
109
+ The hub hands each value to the browser untouched, so what a number, a date
110
+ or a null looks like is decided here, in the query.
69
111
 
70
- export const queries: Queries = {
71
- directory: list((ctx) => ctx.db.directory()),
72
- };
73
- ```
112
+ Return the full answer every time, and the rows in the order the page shows
113
+ them. Sending only what changed is a request's job — see
114
+ [refresh and patch](#refresh-and-patch).
74
115
 
75
- Give every row a column that identifies it, and name that column with
76
- `lb-key` in the HTML. Return the rows in the order the page shows them.
116
+ ## Requests
77
117
 
78
- Return the full result every time. Sending only what changed is a request's job —
79
- see [refresh and patch](#refresh-and-patch).
118
+ Export `requests` from `<stub>.requests.ts`. It holds three keys, each
119
+ optional:
80
120
 
81
- ## Requests, actions, CRUD
121
+ | Key | Runs |
122
+ |---------------|--------------------------------------------------------|
123
+ | `onPageEnter` | Before the page's queries, when the page loads |
124
+ | `handlers` | The page's declared requests, by request name |
125
+ | `crud` | The requests Loadbare provides, by query name |
82
126
 
83
- Export `requests` from `<name>.requests.ts`. It holds three keys, each optional:
127
+ Every request arrives with one shape, and a handler receives it less its
128
+ name:
84
129
 
85
- | Key | Runs |
86
- |---------------|------------------------------------------------------|
87
- | `onPageEnter` | Before the page's queries, on entering the page |
88
- | `actions` | What the page may be asked to do, by name |
89
- | `crud` | The three operations a list permits on its rows |
130
+ | Field | Holds |
131
+ |----------|----------------------------------------------------------|
132
+ | `name` | The request name, from `lb-request` |
133
+ | `query` | The query the request is for |
134
+ | `key` | The key of the row the request is for |
135
+ | `values` | The gathered control values, by column |
136
+
137
+ The hub sends `query`, `key` and `values` when it finds them; see
138
+ [Gathering](./data-binding.md#gathering).
90
139
 
91
140
  ### onPageEnter
92
141
 
@@ -101,51 +150,39 @@ export const requests: Requests = {
101
150
 
102
151
  Declare no refresh set here. The page's queries run afterward.
103
152
 
104
- ### actions
153
+ ### handlers
105
154
 
106
- Declare an action under the name the HTML gives `lb-action`. Pair what it
155
+ Declare a request under the name the HTML gives `lb-request`. Pair what it
107
156
  does with the queries to re-run once it has:
108
157
 
109
158
  ```ts
110
159
  export const requests: Requests = {
111
- actions: {
160
+ handlers: {
112
161
  resetVisits: {
113
162
  run: (ctx) => ctx.db.resetVisits(),
114
163
  refresh: ["visits"],
115
164
  },
165
+ sendReminder: {
166
+ run: (ctx, { key }) => ctx.mail.remind(key),
167
+ refresh: [],
168
+ },
116
169
  },
117
170
  };
118
171
  ```
119
172
 
120
- Declare every action the page allows. A name the page does not declare is
121
- refused.
122
-
123
- Read where the interaction happened from `run`'s second argument, which
124
- carries `list` or `row`, whichever attribute scoped the element, plus `key`,
125
- `cell` and `value` when the element that dispatched the request had them.
173
+ Declare every request the page allows. The server refuses a name the page
174
+ does not declare.
126
175
 
127
176
  ### crud
128
177
 
129
- Declare CRUD operations under `crud`, keyed by the list they operate on. All
130
- three are list operations: each needs a key, and a key exists only on a live
131
- row inside a list, so a single-row scope is read-only and a declared action is
132
- the only thing it can send. Each operation takes the binding its trigger
133
- supplies:
134
-
135
- | Operation | The page writes | `run` receives |
136
- |-------------|------------------------------------------------------------------------|-----------------|
137
- | `rowDelete` | `lb-action="lb-row-delete"` | `key` |
138
- | `rowInsert` | `<form lb-action="lb-row-insert">` | `values` |
139
- | `rowUpdate` | `<form lb-action="lb-row-update">` or `<lb-input lb-action="lb-row-update">` | `key`, `values` |
140
-
141
- Write `rowUpdate` to set the columns `values` names and leave every other
142
- column as it is. A form sends the cells it holds, and a widget cell sends
143
- itself alone. Check the names in `values` against the columns the list lets
144
- the page edit.
178
+ Declare what the three requests Loadbare provides do under `crud`, keyed by
179
+ the query they operate on:
145
180
 
146
- The operation names are reserved: a name beginning with `lb-` cannot be
147
- declared under `actions` or as a query, and `createHub` refuses a page that
148
- tries.
181
+ | Request name | Runs | `run` receives |
182
+ |-----------------|-------------|------------------|
183
+ | `lb-row-insert` | `rowInsert` | `query`, `values` |
184
+ | `lb-row-update` | `rowUpdate` | `query`, `key`, `values` |
185
+ | `lb-row-delete` | `rowDelete` | `query`, `key` |
149
186
 
150
187
  ```ts
151
188
  // src/pages/directory.requests.ts
@@ -161,30 +198,46 @@ export const requests: Requests = {
161
198
  },
162
199
  refresh: [],
163
200
  },
201
+ rowUpdate: {
202
+ run: async (ctx, { key, values }) => {
203
+ const entry = await ctx.db.updateDirectoryEntry(key, values);
204
+ return { directory: patch({ rows: [entry] }) };
205
+ },
206
+ refresh: [],
207
+ },
164
208
  },
165
209
  },
166
210
  };
167
211
  ```
168
212
 
169
- Declare every operation the list permits. An operation a list does not
170
- declare is refused, and a name with no `crud` entry permits none.
213
+ Declare every request the query permits. The server refuses a request a
214
+ query does not declare, and a query with no `crud` entry permits none.
215
+
216
+ Write `rowUpdate` to set the columns `values` names and leave every other
217
+ column as it is. A form or a live row sends every control it gathers, and a
218
+ single control sends itself alone. Check the names in `values` against the
219
+ columns the page may edit.
220
+
221
+ Every value in `values` is a string, as a control's value is.
171
222
 
172
223
  ### refresh and patch
173
224
 
174
- List in `refresh` every query whose whole answer the operation changed.
225
+ List in `refresh` every query whose whole answer the request changed.
175
226
 
176
- Return a result from `run` to state a narrower change than re-running a query
177
- would. What `run` returns is laid over the refreshed queries:
227
+ Return answers from `run` to state a narrower change than re-running a query
228
+ would. What `run` returns is keyed by query name and laid over the refreshed
229
+ queries:
178
230
 
179
- | Result | States |
231
+ | Answer | States |
180
232
  |--------------------------|---------------------------------------------|
181
- | `[...]` | The entire set, and its order |
233
+ | `{ ... }` | The row of a `row` query |
234
+ | `[...]` | All rows of a `rows` query, and their order |
182
235
  | `patch({ rows: [...] })` | These rows arrive or change; the rest stand |
183
236
  | `patch({ drop: [...] })` | These keys are gone; the rest stand |
184
237
 
185
- Return a patch for a change the operation knows the extent of — one row added,
238
+ Return a patch for a change the request knows the extent of — one row added,
186
239
  one row dropped, one row edited — and leave `refresh` empty. Re-run the query
187
- instead when membership or order changed in a way the operation cannot name:
240
+ instead when membership or order changed in a way the request cannot name:
188
241
 
189
242
  ```ts
190
243
  resetRoster: {
@@ -193,18 +246,16 @@ resetRoster: {
193
246
  },
194
247
  ```
195
248
 
196
- Return `queryParms()` instead when the server response changes the query
197
- parms: after an insert, only the server knows the new key, and after a delete,
198
- only the server knows the parm should go. The page loads at the query string
199
- with those parms set, in the same round trip, and the hub writes them into the
200
- URL, replacing the history entry. This is Post/Redirect/Get without the
201
- redirect. The refresh set does not run, and nothing else `run` returned is
202
- sent, since both answered for the query string the page left. Name at least
203
- one parm, give each a string, and use an empty string to take one out:
249
+ ### Moving the URL
250
+
251
+ Return `url()` from `run` when the request changes what the URL should say:
252
+ after an insert, only the server knows the new key, and after a delete, only
253
+ the server knows the parm should go. `url()` takes columns of the `lb-url`
254
+ query; see [The URL](./chrome.md#the-url).
204
255
 
205
256
  ```ts
206
257
  // src/pages/accounts.requests.ts
207
- import { queryParms, type Requests } from "@loadbare/app/server";
258
+ import { url, type Requests } from "@loadbare/app/server";
208
259
 
209
260
  export const requests: Requests = {
210
261
  crud: {
@@ -212,14 +263,14 @@ export const requests: Requests = {
212
263
  rowInsert: {
213
264
  run: async (ctx, { values }) => {
214
265
  const id = await ctx.db.addAccount(values);
215
- return queryParms({ acct: String(id) });
266
+ return url({ acct: String(id) });
216
267
  },
217
268
  refresh: [],
218
269
  },
219
270
  rowDelete: {
220
271
  run: async (ctx, { key }) => {
221
272
  await ctx.db.deleteAccount(key);
222
- if (key === ctx.acct) return queryParms({ acct: "" });
273
+ if (key === ctx.acct) return url({ acct: "" });
223
274
  },
224
275
  refresh: ["accounts"],
225
276
  },
@@ -228,6 +279,20 @@ export const requests: Requests = {
228
279
  };
229
280
  ```
230
281
 
231
- Only parms: `run` cannot send the browser to another page. The loaded page
232
- reads the new parms through `contextFor` like any others; see
282
+ A column other than `lb-path` is a query parm: set, or taken out when its
283
+ value is an empty string. Name at least one column, and give each a string.
284
+ With `lb-path` naming another page, the page is entered with only the parms
285
+ named:
286
+
287
+ ```ts
288
+ return url({ "lb-path": "/accounts", acct: String(id) });
289
+ ```
290
+
291
+ The server loads the page at the new URL in the same round trip, as a cold
292
+ load of it would, and the hub writes the URL: it pushes a history entry when
293
+ the page changes, or when the element that sent the request carries
294
+ `lb-url-push`, and replaces the current one otherwise. This is
295
+ Post/Redirect/Get without the redirect. The refresh set does not run, and
296
+ nothing else `run` returned is sent, since both answered for the URL the page
297
+ left. The loaded page reads the new parms through `contextFor`; see
233
298
  [the Express server](./server.md#database-layer).
@@ -64,7 +64,7 @@ Register authentication before `hubRoutes`.
64
64
 
65
65
  Serve `dist/app.html` for every route the application does not claim,
66
66
  including a path that names no page. See [`chrome.html`](./chrome.md) for the
67
- `<dialog lb-unknown-page>` that announces that case to the user.
67
+ `<dialog lb-url-unknown>` that announces that case to the user.
68
68
 
69
69
  Give the server the origin root. The hub reaches its own route by absolute
70
70
  path, so the application cannot be hosted under a subpath such as
@@ -149,7 +149,7 @@ function contextFor(req: Request, parms: URLSearchParams): HubContext {
149
149
 
150
150
  Read the parms from that argument, not from `req.query`. When a server
151
151
  response changes the query parms, `contextFor` is called a second time for
152
- that request, with the new parms, to load the page at them; see [refresh and patch](./page-files.md#refresh-and-patch). Write it to
152
+ that request, with the new parms, to load the page at them; see [Moving the URL](./page-files.md#moving-the-url). Write it to
153
153
  be safe to call twice, and make a write visible to the second context by the
154
154
  time `run` returns: a handle opened per request, with no transaction held
155
155
  open across the two, is both.