@loadbare/app 0.18.2 → 0.19.1

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 (51) hide show
  1. package/README.md +17 -17
  2. package/dist/build/assemble.d.ts +7 -1
  3. package/dist/build/assemble.d.ts.map +1 -1
  4. package/dist/build/assemble.js +81 -11
  5. package/dist/build/assemble.js.map +1 -1
  6. package/dist/build/cli.d.ts +3 -0
  7. package/dist/build/cli.d.ts.map +1 -1
  8. package/dist/build/cli.js +6 -1
  9. package/dist/build/cli.js.map +1 -1
  10. package/dist/build/declared.d.ts +25 -0
  11. package/dist/build/declared.d.ts.map +1 -0
  12. package/dist/build/declared.js +62 -0
  13. package/dist/build/declared.js.map +1 -0
  14. package/dist/build/pages.d.ts +6 -0
  15. package/dist/build/pages.d.ts.map +1 -1
  16. package/dist/build/pages.js +32 -12
  17. package/dist/build/pages.js.map +1 -1
  18. package/docs/comparison.md +697 -484
  19. package/docs/departures.md +63 -0
  20. package/docs/miswarts.md +140 -0
  21. package/docs/opinions.md +155 -0
  22. package/docs/reference/builder.md +20 -4
  23. package/docs/reference/chrome.md +45 -10
  24. package/docs/reference/css.md +0 -1
  25. package/docs/reference/custom-elements.md +3 -25
  26. package/docs/reference/data-binding.md +265 -48
  27. package/docs/reference/names.md +250 -0
  28. package/docs/reference/overview.md +6 -0
  29. package/docs/reference/page-files.md +77 -9
  30. package/docs/reference/server.md +54 -1
  31. package/docs/roadmap.md +55 -43
  32. package/docs/testing.md +62 -28
  33. package/docs/theory.md +348 -108
  34. package/docs/tutorials/040-displaying-data.md +1 -1
  35. package/package.json +1 -1
  36. package/skills/loadbare-app/SKILL.md +19 -13
  37. package/skills/loadbare-app/references/builder.md +20 -4
  38. package/skills/loadbare-app/references/chrome.md +45 -10
  39. package/skills/loadbare-app/references/css.md +0 -1
  40. package/skills/loadbare-app/references/custom-elements.md +3 -25
  41. package/skills/loadbare-app/references/data-binding.md +265 -48
  42. package/skills/loadbare-app/references/miswarts.md +140 -0
  43. package/skills/loadbare-app/references/names.md +250 -0
  44. package/skills/loadbare-app/references/overview.md +6 -0
  45. package/skills/loadbare-app/references/page-files.md +77 -9
  46. package/skills/loadbare-app/references/server.md +54 -1
  47. package/docs/TECHREF-1.0.md +0 -2312
  48. package/docs/possible-ideas.md +0 -314
  49. package/docs/theory-old.md +0 -839
  50. package/docs/what-does-loadbare-extend.md +0 -124
  51. package/skills/loadbare-app/references/TECHREF-1.0.md +0 -2312
@@ -2,7 +2,7 @@
2
2
 
3
3
  A page shows server data by naming queries and columns in its HTML, and asks
4
4
  the server to change that data by naming requests. The developer writes
5
- nine `lb-` attributes. The server declares every query a page may show and
5
+ sixteen `lb-` attributes. The server declares every query a page may show and
6
6
  every request it may send, and refuses anything it has not declared.
7
7
 
8
8
  Everything is a query. A query is a name for rows, of kind `row` or `rows`,
@@ -48,6 +48,10 @@ each with its key, and permits the three requests on `roster`; see
48
48
  | `lb-show` | Any element but `<template>` | The column that decides whether it is present |
49
49
  | `lb-order` | An element with `lb-query` | The choice holding the user's order; see [Order](#order) |
50
50
  | `lb-filter` | An element with `lb-query` | The columns a choice filters; see [Filters](#filters) |
51
+ | `lb-group` | A `<template>` | Nothing: it marks a group template; see [Groups](#groups) |
52
+ | `lb-count` | Any element | Nothing: it shows a count of rows; see [Aggregates](#aggregates) |
53
+ | `lb-sum`, `lb-avg`, `lb-min`, `lb-max` | Any element | The column aggregated; see [Aggregates](#aggregates) |
54
+ | `lb-select` | A checkbox in a row template | Nothing: it selects its live row; see [Selecting rows](#selecting-rows) |
51
55
  | `lb-request` | Any element | The request it sends when it commits |
52
56
  | `lb-url-link` | An `<a>` | A link between pages; see [chrome](./chrome.md#links) |
53
57
  | `lb-url-push` | An element with `lb-request` | A URL change that pushes a history entry |
@@ -62,8 +66,14 @@ builder writes a stamp, and a stylesheet or a custom element reads it.
62
66
  | `lb-key-value` | The key of a live row, or of a `row` landed on an element |
63
67
  | `lb-row-live` | The element is a live row |
64
68
  | `lb-query-row-count` | The number of live rows an element shows |
69
+ | `lb-group-live`, `lb-group-column`, `lb-group-value` | A group, the column it breaks on, and its value; see [Groups](#groups) |
70
+ | `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested` | What happened to a row; see [What happened to a row](#what-happened-to-a-row) |
71
+ | `lb-row-leaving`, `lb-group-leaving` | A row or group on its way out |
72
+ | `lb-row-editing` | The row a form is pointed at; see [Editing a row in one form](#editing-a-row-in-one-form) |
73
+ | `lb-row-selected` | A row whose `lb-select` is ticked |
65
74
  | `lb-request-pending` | The element's request is in flight |
66
75
  | `lb-request-error` | The element's last request failed |
76
+ | `lb-request-refused` | The handler refused it; see [Refusals](#refusals) |
67
77
  | `lb-page` | The stub of a page, on its `<template>` |
68
78
  | `lb-page-title` | The text of a page file's `<title>` |
69
79
 
@@ -79,7 +89,8 @@ elements as the page needs.
79
89
 
80
90
  What lands depends on the query's kind and on whether the element holds a
81
91
  row template, which is the first `<template>` among its descendants outside
82
- any nested `lb-query`:
92
+ any nested `lb-query`, or, when that template is a [group
93
+ template](#groups), the innermost template inside it:
83
94
 
84
95
  | Kind | Row template | The hub |
85
96
  |--------|--------------|----------------------------------------|
@@ -118,6 +129,9 @@ row's `account`:
118
129
  `lb-column` on an element with `lb-query` is undefined unless the element is
119
130
  a control.
120
131
 
132
+ A query that arrives with nowhere to land is reported to the console,
133
+ unless its name begins with `lb-`.
134
+
121
135
  An element with `lb-column` and no ancestor row receives nothing. The hub
122
136
  still gathers from it, so an insert form writes `lb-column` on controls that
123
137
  no row fills.
@@ -140,7 +154,9 @@ element that is not a control keeps the content the builder placed in it
140
154
  from its element file, and renders `lb-column-value` itself; see
141
155
  [Custom Elements](./custom-elements.md#receiving-a-value).
142
156
 
143
- A file input receives nothing, and the hub reports it to the console.
157
+ A file input receives nothing, not even `lb-column-value`, and the hub
158
+ reports it to the console and gathers nothing from it. A `<select>` with no
159
+ option for the value shows no selection.
144
160
 
145
161
  The hub hands every value to the browser as the server sent it. What a
146
162
  number, a date or a null looks like is decided in the query, or by a custom
@@ -151,7 +167,8 @@ column the user sorts or filters raw, and format it in the element.
151
167
  ### Checkboxes
152
168
 
153
169
  A checkbox is a control. It is checked when its column is anything but
154
- `false` or null, and it sends `"true"` or `"false"`:
170
+ `false` or null, and it sends `"true"` or `"false"`
171
+ ([miswart](../miswarts.md#checkboxes-and-radio-groups-always-gather-a-value)):
155
172
 
156
173
  ```html
157
174
  <input type="checkbox" lb-column="active" lb-request="lb-row-update" />
@@ -176,8 +193,9 @@ when none is:
176
193
 
177
194
  In a row template, the hub stamps each radio button's `name` with the
178
195
  `name` written, or its column when none is, followed by `-` and the live
179
- row's key, so each live row holds a group of its own. Rows not yet entered
180
- share the empty key, and so share one group.
196
+ row's key, so each live row holds a group of its own
197
+ ([miswart](../miswarts.md#radio-button-names-in-a-row-template)). Rows not yet
198
+ entered share the empty key, and so share one group.
181
199
 
182
200
  A radio button in a query's row template answers to that query's row, as
183
201
  any element there does. A choice among a query's rows is better made with
@@ -209,7 +227,11 @@ what was typed, through a refresh and through a change of order. A row
209
227
  whose values changed is filled whole, and its controls show what arrived.
210
228
 
211
229
  A key is unique within a query. Two rows with one key in the same answer,
212
- or two live rows showing one key, are reported on the console.
230
+ or two live rows showing one key, are reported on the console. A row
231
+ missing its key column is reported and not landed.
232
+
233
+ A live row already in the markup when the first answer lands is one of the
234
+ query's rows.
213
235
 
214
236
  When the focus is in a live row that placing moves, the hub puts it back on
215
237
  the same element and scrolls it into view. When that row leaves, or a
@@ -254,14 +276,23 @@ server's order places the rows:
254
276
  </ul>
255
277
  ```
256
278
 
257
- Values compare by their JSON type: numbers numerically, strings as the
258
- user's language orders them, `false` before `true`, and `null` last. A
259
- number sent as a string sorts as a string, unless the query declares the
279
+ | Values | Compare |
280
+ |----------------------|----------------------------------------------|
281
+ | Two numbers | Numerically |
282
+ | Two strings | As the user's language orders them |
283
+ | Two booleans | `false` before `true` |
284
+ | Two different types | Booleans, then numbers, then strings |
285
+ | `null` or missing | After everything ascending, first descending |
286
+
287
+ A number sent as a string sorts as a string, unless the query declares the
260
288
  column's [type](./page-files.md#queries): a column declared `number` sorts
261
289
  numerically whatever JSON type its values arrive as, and a value that is no
262
- number sorts with the nulls. Rows that compare equal keep the order they
263
- arrived in. A row moves only when the order puts it somewhere
264
- else.
290
+ value of its declared type compares as `null` does. `readAs` in
291
+ `@loadbare/app/types` is the rule the hub applies.
292
+
293
+ Rows that compare equal keep the order they arrived in, a patch's new rows
294
+ after the rows already showing. A row moves only when the order
295
+ puts it somewhere else, so all rows landing again unchanged move nothing.
265
296
 
266
297
  ### Filters
267
298
 
@@ -280,7 +311,8 @@ its rows the user may filter, separated by commas. The column of
280
311
  ```
281
312
 
282
313
  A row that does not pass every filter is `hidden`, and a ticked `lb-select`
283
- in it is cleared. A group whose rows are all hidden is hidden too. An empty
314
+ in it is cleared. A group whose rows are all hidden is hidden too
315
+ ([miswart](../miswarts.md#rows-hidden-by-a-filter)). An empty
284
316
  choice filters nothing.
285
317
 
286
318
  A filter is one or more terms, each an operator followed by a value:
@@ -310,6 +342,9 @@ The column's type is the one its query declares, if it declares one, and
310
342
  its JSON type otherwise. A value that is not of the column's type, and a
311
343
  column that is null or missing, pass only `!=`.
312
344
 
345
+ `parseFilter`, `passes` and `passesTerm` in `@loadbare/app/types` are the
346
+ rule the hub applies.
347
+
313
348
  ### Groups
314
349
 
315
350
  A group is a run of rows sharing the order's leading term. Write a
@@ -330,11 +365,32 @@ template. The nth group template breaks on the order's nth term.
330
365
  </ul>
331
366
  ```
332
367
 
333
- The group's contents land immediately before its nested template. When that
334
- template is inside the heading's element, the contents land inside it; when
335
- it is beside the heading, they land after it. `<tbody>` and `<optgroup>` do
336
- not nest, so a table's deeper levels are heading rows. Content after the
337
- nested template is the group's footer.
368
+ The hub clones the group template where the order breaks, and the group's
369
+ contents land immediately before the clone of its nested template, so the
370
+ markup's nesting decides the shape:
371
+
372
+ | The nested template is | The group's contents land | Example |
373
+ |------------------------------|------------------------------------|-----------------------------------|
374
+ | Inside the heading's element | Inside it | `<tbody>`, `<li>`, `<details>` |
375
+ | Beside the heading | After the heading, as its siblings | A heading `<tr>` in one `<tbody>` |
376
+
377
+ `<tbody>` and `<optgroup>` do not nest, so a table's levels below the first
378
+ are heading rows beside their rows:
379
+
380
+ ```html
381
+ <table lb-query="accounts">
382
+ <thead><tr><th>Account</th></tr></thead>
383
+ <template lb-group>
384
+ <tbody>
385
+ <tr><th scope="rowgroup" lb-column="category"></th></tr>
386
+ <template><tr><td lb-column="account_name"></td></tr></template>
387
+ </tbody>
388
+ </template>
389
+ </table>
390
+ ```
391
+
392
+ Content after the nested template lands after the group's rows, which is
393
+ where a group's footer goes.
338
394
 
339
395
  A heading is filled from its group's first row, so it may show a column
340
396
  other than the one the group breaks on. The hub stamps each top-level
@@ -343,6 +399,10 @@ element of a group with `lb-group-live`, `lb-group-column` and
343
399
  row. When the order changes, a group whose run goes on is kept and moved,
344
400
  and a group whose run ended leaves.
345
401
 
402
+ A group template with no nested template is reported to the console. More
403
+ group templates than the order has terms show each extra level as one
404
+ group, and are reported once.
405
+
346
406
  ### Aggregates
347
407
 
348
408
  `lb-count`, `lb-sum="col"`, `lb-avg="col"`, `lb-min="col"` and
@@ -353,10 +413,24 @@ or of the whole list outside any group:
353
413
  <tr><td><span lb-count></span> people</td><td lb-sum="salary"></td></tr>
354
414
  ```
355
415
 
356
- A sum and an average are exact, read from JSON numbers and from strings
357
- holding a plain decimal. `null` and empty values are left out. The result
358
- lands unformatted, and covers the rows showing: a row a filter hides is
359
- left out.
416
+ | Attribute | Sets the element to |
417
+ |----------------|-----------------------------------------------------------|
418
+ | `lb-count` | The number of rows |
419
+ | `lb-sum="col"` | The sum of `col` |
420
+ | `lb-avg="col"` | The average of `col` |
421
+ | `lb-min="col"` | The least value of `col`, by the [order](#order)'s comparison |
422
+ | `lb-max="col"` | The greatest, likewise |
423
+
424
+ An aggregate lands as a column does, with `lb-column-value`. A sum and an
425
+ average are exact decimals, read from JSON numbers and from strings holding
426
+ a plain decimal, as Postgres sends `numeric`. A sum has as many places as
427
+ the most precise value, and an average up to two more. `null` and empty
428
+ values are left out, as SQL leaves them out, and any other value is left
429
+ out and reported once. The result lands unformatted.
430
+
431
+ An aggregate covers the rows showing, so a row a filter hides is left out.
432
+ Where the true total matters beyond what the page shows, have the server
433
+ answer it as a row.
360
434
 
361
435
  ### What happened to a row
362
436
 
@@ -371,9 +445,16 @@ Every change to a list is one a stylesheet can see:
371
445
  | `lb-row-leaving` | A row on its way out |
372
446
  | `lb-group-leaving` | A group on its way out |
373
447
 
374
- A row that leaves loses `lb-row-live`, takes `inert`, and is removed once
375
- its animations finish, at once when there are none. `lb-row-requested` is
376
- cleared at every landing; the others when the hub next touches the row.
448
+ The hub touches a row when an answer names it. `lb-row-created`,
449
+ `lb-row-changed` and `lb-row-moved` are cleared when it next does, and one
450
+ applied again is removed and added as two changes, so an animation keyed to
451
+ it plays again. `lb-row-requested` is cleared at every landing of the row's
452
+ query, and set only by a landing that answers a request from the page.
453
+
454
+ A row or a group that leaves loses `lb-row-live` or `lb-group-live`, takes
455
+ `lb-row-leaving` or `lb-group-leaving` and `inert`
456
+ ([miswart](../miswarts.md#leaving-rows-are-inert)), and is removed once its
457
+ animations finish, at once when there are none.
377
458
 
378
459
  A row is moved when its values changed and the rows whose values did not
379
460
  change now stand around it differently. A row it passed is not stamped,
@@ -384,6 +465,11 @@ and a change of the user's order, which changes no values, stamps none.
384
465
  [lb-row-leaving] { animation: depart 200ms forwards; }
385
466
  ```
386
467
 
468
+ A row never travels between its two places, since that takes measuring, and
469
+ measuring takes script. Scrolling to a row is script too: a custom element
470
+ reads `lb-row-requested` in
471
+ [`lbRowsLanded`](./custom-elements.md#holding-rows).
472
+
387
473
  ### What arrives later
388
474
 
389
475
  The hub keeps each query's last answer until the page changes, with any
@@ -437,6 +523,39 @@ hides is not counted. An empty query is a stylesheet rule:
437
523
  The element carrying `lb-query` is a `<div>` here rather than the `<ul>`, so
438
524
  that the message is inside it and one rule reaches both.
439
525
 
526
+ ### Master-detail
527
+
528
+ A master is a `row` query and its detail is a `rows` query, as two names
529
+ answered side by side. A column never holds rows.
530
+
531
+ ```html
532
+ <section lb-query="invoice">
533
+ <h2 lb-column="number"></h2>
534
+ <span lb-column="customer"></span>
535
+ </section>
536
+
537
+ <table>
538
+ <tbody lb-query="invoiceLines">
539
+ <template><tr><td lb-column="item"></td><td lb-column="amount"></td></tr></template>
540
+ </tbody>
541
+ </table>
542
+ ```
543
+
544
+ Many masters, each with its own detail, is one `rows` query of joined rows:
545
+ each detail row carries its master's columns. A [group template](#groups)
546
+ shows each master as a group, ordered by the master's key first.
547
+
548
+ A query nested in another query's row template receives the same rows in
549
+ every live row. That serves a picker offering the same choices on every
550
+ row, and is not a way to show a different detail per row; see
551
+ [What arrives later](#what-arrives-later).
552
+
553
+ Which master a page shows is a query parm, which a query reads off `ctx`,
554
+ since a query takes no argument from the browser; see
555
+ [Query parms](./chrome.md#query-parms). When a write creates or removes the
556
+ master, the handler moves the URL; see
557
+ [Moving the URL](./page-files.md#moving-the-url).
558
+
440
559
  ## Conditional rendering
441
560
 
442
561
  A page holds every element it can show. Write `lb-show` on an element,
@@ -551,10 +670,14 @@ answers with response items, which land like any others.
551
670
  | A control | `change` |
552
671
  | Any other element | `click` |
553
672
 
554
- A click inside an element carrying `lb-request` commits it unless the click
555
- lands on interactive content between them: a link, a button, a control or a
556
- label, as HTML defines interactive content. A click into an input inside a
557
- deletable row focuses the input.
673
+ A click inside an element carrying `lb-request` commits it, as long as the
674
+ element is inside `<lb-hub>` and no interactive content lies between them.
675
+ Interactive content is HTML's: `<a href>`, `<button>`, `<input>` other than
676
+ hidden, `<select>`, `<textarea>`, `<label>`, `<details>`, `<iframe>`,
677
+ `<embed>`, `<audio controls>`, `<video controls>` and `<img usemap>`, plus
678
+ any form-associated custom element. A click on one of them belongs to it: a
679
+ click into an input inside a deletable row focuses the input and deletes
680
+ nothing.
558
681
 
559
682
  A submit button with a form owner commits with its form. On `submit`, the
560
683
  hub sends the submitter's `lb-request` when the submitter carries one, and
@@ -588,7 +711,10 @@ carrying `lb-request`:
588
711
 
589
712
  The hub gathers from controls only, and skips a control that belongs to a
590
713
  query nested inside the group, except that a form gathers the rows of a
591
- query nested in its own; see [A form over a set of rows](#a-form-over-a-set-of-rows). A form owner includes a control outside the
714
+ query nested in its own; see [A form over a set of rows](#a-form-over-a-set-of-rows).
715
+ An element carrying `lb-query` and `lb-column` both, such as a picker,
716
+ belongs to the row around it, and is gathered with it and sends for that
717
+ row's query. A form owner includes a control outside the
592
718
  form that names it with the HTML `form` attribute, which is how a table row,
593
719
  where a form cannot go, sends one:
594
720
 
@@ -660,8 +786,10 @@ query's name, in `rows`:
660
786
  The list of deduction types lives only in the database: the nested query
661
787
  answers it, and the form shows one input per row.
662
788
 
663
- - Every live row of the nested query goes, whatever its values. What an
664
- empty value means, such as no deduction of that type, is the handler's.
789
+ - Every live row of the nested query that holds a control goes, whatever
790
+ its values. What an empty value means, such as no deduction of that type,
791
+ is the handler's. A picker's options hold no control, so a picker in the
792
+ form sends its own value and none of its rows.
665
793
  - One level only. A query nested inside a nested row shows the same rows in
666
794
  every row around it, so it is not gathered.
667
795
  - An insert, an update and a declared request carry `rows`. A delete does
@@ -671,7 +799,9 @@ answers it, and the form shows one input per row.
671
799
  - A live row gathers nothing nested, as before.
672
800
 
673
801
  The handler for the form's query receives `rows` beside `values`, and writes
674
- the master and its details together; see [crud](./page-files.md#crud).
802
+ the master and its details together; see [crud](./page-files.md#crud). The
803
+ server checks that `rows` holds string keys and string values by query, and
804
+ ignores a request whose `rows` does not.
675
805
 
676
806
  A nested query answers once for the page, so its rows are the same wherever
677
807
  they show. The form enters a new master with its details, or edits the
@@ -708,9 +838,14 @@ holds what a submit will gather:
708
838
  </section>
709
839
  ```
710
840
 
711
- The same holds for a request a custom element sends carrying `key: ""`.
712
- An update or delete with no row around it at all has no key either, and
713
- that is reported to the console.
841
+ The same holds for a request a custom element sends carrying `key: ""`. No
842
+ ancestor sees a request not sent, and it gets no `lb-request-done`. An
843
+ update or delete with no row around it at all has no key either, and that
844
+ is reported to the console.
845
+
846
+ A field the request already carries is its issuer's and is kept, so a
847
+ custom element that dispatches its own request with `values` is not
848
+ gathered over.
714
849
 
715
850
  ### Inserting, updating and deleting rows
716
851
 
@@ -727,6 +862,10 @@ The hub sends one of these only when it has what the table lists, and
727
862
  otherwise reports to the console. An insert or an update that gathers
728
863
  nothing is not sent.
729
864
 
865
+ For a query the hub serves, `lb-url` or `lb-choices`, the hub answers
866
+ `lb-row-update` itself, with no round trip; see
867
+ [Query parms](./chrome.md#query-parms) and [Choices](#choices).
868
+
730
869
  A key comes from a live row, or from an element a `row` landed on, so all
731
870
  three work against either kind:
732
871
 
@@ -790,11 +929,13 @@ round trip, including columns no element in the table shows. The user
790
929
  edits a row they can see, so the row is on the page. A row that has to be
791
930
  fetched to be edited is another page, with that row as its master.
792
931
 
793
- The hub stamps the form with the row's key, so the form's request carries
794
- it, and stamps the row `lb-row-editing`. New stamps an empty key, making the
795
- form [a row not yet entered](#a-row-not-yet-entered), and resets every
796
- control to its default. The form's state is in its `lb-key-value`
797
- stamp:
932
+ Pointing a form first empties it: every column it names lands as null, and
933
+ the form resets, so each control returns to its default. Edit then lands
934
+ the row on the form, stamps the form with the row's key, so the form's
935
+ request carries it, and stamps the row `lb-row-editing`. New stamps an
936
+ empty key, making the form [a row not yet entered](#a-row-not-yet-entered).
937
+ Both focus the form's first control. The form's state is in its
938
+ `lb-key-value` stamp:
798
939
 
799
940
  | `lb-key-value` | The form is |
800
941
  |----------------|-----------------------------|
@@ -837,6 +978,11 @@ landing again lands nothing on it, and what the user has typed stays.
837
978
  A button inside a form that carries a command is `type="button"`, or it
838
979
  submits the form instead.
839
980
 
981
+ A command the hub cannot carry out is reported to the console: Edit from
982
+ outside a live row of the form's query, Edit for a key the hub holds no row
983
+ for, and any command aimed at an element that is not a form carrying
984
+ `lb-query`.
985
+
840
986
  ### Selecting rows
841
987
 
842
988
  A checkbox carrying `lb-select` in a row template selects the live row it
@@ -913,7 +1059,7 @@ A button carries no value, so a declared request from one sends the values
913
1059
  of the live row or form it sits in, and the server computes the rest.
914
1060
 
915
1061
  A request name beginning with `lb-` that is not one of the three is refused
916
- by the builder.
1062
+ by the builder in markup, and by the hub when a script dispatches it.
917
1063
 
918
1064
  ### Request state
919
1065
 
@@ -927,7 +1073,8 @@ The hub stamps the element that committed:
927
1073
 
928
1074
  `lb-request-error` covers a non-2xx response, a network failure and a
929
1075
  timeout alike. The hub aborts a round trip after ten seconds, and sets
930
- `aria-busy="true"` for as long as `lb-request-pending` is present.
1076
+ `aria-busy="true"` for as long as `lb-request-pending` is present
1077
+ ([miswart](../miswarts.md#busy-elements-carry-aria-busy)).
931
1078
 
932
1079
  The hub ignores a commit on an element carrying `lb-request-pending`, so a
933
1080
  pending button is already disabled and a stylesheet only shows it:
@@ -977,14 +1124,23 @@ submit, as for any invalid control, so the user edits before trying again.
977
1124
 
978
1125
  A form-associated custom element takes a column's message when it has
979
1126
  `setCustomValidity` and `reportValidity`; see
980
- [Being a control](./custom-elements.md#being-a-control).
1127
+ [Being a control](./custom-elements.md#being-a-control). A request for
1128
+ several rows takes the whole-request message only. A column the request
1129
+ gathered no control for is reported to the console.
1130
+
1131
+ On the wire a refusal is status 422 with `{ refusal: { message?, columns? } }`;
1132
+ see [The data channel](./server.md#the-data-channel).
981
1133
 
982
1134
  ### The request event
983
1135
 
984
1136
  The hub dispatches every request as a bubbling `lb-request` event from the
985
1137
  element that committed, with the request as its `detail`, before sending it.
986
- An ancestor may stop the event, and the request is not sent. A custom
987
- element may dispatch the event itself; see
1138
+ The hub completes the request on the event's way down, so every ancestor
1139
+ sees it whole on the way up. An ancestor may stop it, and the request is
1140
+ not sent: the event is not cancelable, so an interceptor calls
1141
+ `stopPropagation`, not `preventDefault`. That is how a confirmation wrapper
1142
+ works without the wrapped element knowing about it. A custom element may
1143
+ dispatch the event itself; see
988
1144
  [Sending a request](./custom-elements.md#sending-a-request).
989
1145
 
990
1146
  Once the answer has landed, or the round trip has failed, the hub calls
@@ -993,6 +1149,37 @@ then dispatches a bubbling `lb-request-done` event from the same element.
993
1149
  Both carry the request and what landed because of it, or the error; see
994
1150
  [Hearing how it turned out](./custom-elements.md#hearing-how-it-turned-out).
995
1151
 
1152
+ ### The round trip
1153
+
1154
+ A request the hub sends has ten seconds to come back. Past that the hub
1155
+ aborts it and treats it as a failure, since `fetch` imposes no deadline of
1156
+ its own. An application cannot change the deadline.
1157
+
1158
+ A round trip fails on a server error, on a network failure, or on that
1159
+ deadline, and all three stamp `lb-request-error` on the element that sent
1160
+ the request. A refusal is not a failure.
1161
+
1162
+ After a successful `lb-row-insert` gathered from a form, the hub resets the
1163
+ form, after it lands the response. A failed insert resets nothing, so the
1164
+ entry can be corrected. An `lb-row-update` resets nothing: the row it sent
1165
+ lands back on its controls.
1166
+
1167
+ A reset of any form, whether the hub's, a reset button's or a script's,
1168
+ returns its controls to their defaults, and the hub removes
1169
+ `lb-column-value` from each of them. A cancelled reset keeps both.
1170
+
1171
+ A page load that fails lands nothing and is reported to the console. A load
1172
+ started by a request for `lb-url` stamps the element that sent it, as any
1173
+ request stamps its element.
1174
+
1175
+ A response answers for the URL its request was sent from, path and query
1176
+ string both. One that returns after the URL has moved lands nothing, since
1177
+ it describes what the user is no longer looking at: a request's answer,
1178
+ including any `url()` it carries, and a page load overtaken by a newer one.
1179
+ A write whose answer is dropped this way has still happened. Its
1180
+ `lb-request-done` carries no items and no error, and an insert still resets
1181
+ its form.
1182
+
996
1183
  ## The URL
997
1184
 
998
1185
  The hub serves one query of its own, `lb-url`: one row holding the path, the
@@ -1041,8 +1228,18 @@ The developer writes `lb-` attributes in markup, and script never assigns
1041
1228
  them. The builder checks the markup of the chrome and of every page, after
1042
1229
  expansion, and refuses to build:
1043
1230
 
1044
- - an `lb-` attribute that is not one of the nine the developer writes
1231
+ - an `lb-` attribute that is not one of the sixteen the developer writes;
1232
+ `lb-exp-slot` and `lb-exp-template` are consumed by expansion and never
1233
+ reach the check
1045
1234
  - `lb-request` naming an `lb-` request other than the three Loadbare provides
1235
+ - `lb-query` naming a query the page does not declare; every page and the
1236
+ chrome may name `lb-url` and `lb-choices`, and the chrome names no other
1237
+ - `lb-request` naming a request the page does not declare under `handlers`;
1238
+ the chrome sends none
1239
+ - one of the three requests for a query whose `crud` entry does not declare
1240
+ it, or any but `lb-row-update` for `lb-url` or `lb-choices`; the query is
1241
+ a form's own when the form carries `lb-query`, and otherwise the nearest
1242
+ `lb-query` around the element
1046
1243
  - `lb-url-link` on anything but an `<a>`
1047
1244
  - `lb-url-push` on an element without `lb-request`
1048
1245
  - `lb-url-unknown` on anything but a `<dialog>`, or outside `<lb-hub>`
@@ -1055,3 +1252,23 @@ expansion, and refuses to build:
1055
1252
  template, or beside `lb-column` or `lb-request`
1056
1253
  - a `command` beginning `--lb-` that is not one of the three commands
1057
1254
  - one of the three commands with no `commandfor`
1255
+ - `lb-group` on anything but a `<template>`, or on one holding no
1256
+ `<template>` for its contents
1257
+ - an aggregate on a `<template>`, or any but `lb-count` naming no column
1258
+ - a chrome without exactly one `<lb-hub>` and exactly one `<main>`, a
1259
+ `<main>` outside `<lb-hub>`, or a `<main>` holding anything but whitespace
1260
+ - a page carrying an `<lb-hub>` or a `<main>`
1261
+
1262
+ The builder also reads the script of every custom element it ships, other
1263
+ than `<lb-hub>`, and refuses one that declares a method, accessor or field
1264
+ whose name is `lb`, a capital, and anything Loadbare does not call; see
1265
+ [Reserved methods](./names.md#reserved-methods).
1266
+
1267
+ `lb-column` with no ancestor row is allowed: the hub gathers from it.
1268
+
1269
+ Script never assigns an `lb-` attribute, so the markup states every one
1270
+ that exists, and checking the markup is sound.
1271
+
1272
+ To know what each page declares, the builder loads its `.queries.ts` and
1273
+ `.requests.ts` modules, so their import-time code runs during the build, as
1274
+ it does when the server starts.