@loadbare/app 0.5.5 → 0.6.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.
- package/README.md +3 -4
- package/dist/build/assemble.d.ts +1 -1
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +80 -7
- package/dist/build/cli.d.ts +2 -2
- package/dist/build/cli.js +2 -2
- package/dist/build/expand.d.ts.map +1 -1
- package/dist/build/expand.js +30 -34
- package/dist/build/locations.d.ts +3 -3
- package/dist/build/locations.d.ts.map +1 -1
- package/dist/build/locations.js +15 -3
- package/dist/build/origins.d.ts +0 -13
- package/dist/build/origins.d.ts.map +1 -1
- package/dist/build/origins.js +32 -8
- package/dist/build/pages.d.ts +3 -3
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +9 -7
- package/dist/core/lb-constants.d.ts +17 -12
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +106 -43
- package/dist/core/lb-types.d.ts +103 -62
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js +11 -3
- package/dist/hub/lb-apply.d.ts +18 -4
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +184 -34
- package/dist/hub/lb-hub.browser.d.ts +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +165 -83
- package/dist/server/lb-express.d.ts +7 -4
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +45 -33
- package/dist/server/lb-server.d.ts +67 -45
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +55 -18
- package/dist/tests/assemble.test.js +154 -2
- package/dist/tests/expand.test.js +30 -3
- package/dist/tests/helpers/hub.d.ts +73 -0
- package/dist/tests/helpers/hub.d.ts.map +1 -0
- package/dist/tests/helpers/hub.js +151 -0
- package/dist/tests/lb-apply.test.js +86 -62
- package/dist/tests/lb-express.test.d.ts +1 -1
- package/dist/tests/lb-express.test.js +43 -38
- package/dist/tests/lb-hub.test.d.ts +14 -0
- package/dist/tests/lb-hub.test.d.ts.map +1 -0
- package/dist/tests/lb-hub.test.js +319 -0
- package/dist/tests/{lb-rows.test.d.ts → lb-list.test.d.ts} +1 -1
- package/dist/tests/lb-list.test.d.ts.map +1 -0
- package/dist/tests/{lb-rows.test.js → lb-list.test.js} +109 -106
- package/dist/tests/lb-server.test.js +151 -100
- package/dist/tests/origins.test.js +19 -1
- package/dist/tests/pages.test.d.ts +1 -1
- package/dist/tests/pages.test.js +64 -14
- package/docs/TECHREF-1.0.md +1000 -0
- package/docs/reference/builder.md +4 -4
- package/docs/reference/chrome.md +56 -5
- package/docs/reference/custom-elements.md +59 -36
- package/docs/reference/data-binding.md +142 -84
- package/docs/reference/overview.md +1 -1
- package/docs/reference/page-files.md +64 -49
- package/docs/reference/server.md +7 -6
- package/docs/reference/widgets.md +43 -32
- package/docs/roadmap.md +68 -22
- package/docs/testing.md +47 -17
- package/docs/theory.md +2 -2
- package/docs/tutorials/010-pages-and-navigation.md +14 -8
- package/docs/tutorials/040-displaying-data.md +9 -9
- package/docs/tutorials/050-actions.md +5 -5
- package/docs/tutorials/060-custom-element-code.md +1 -1
- package/docs/tutorials/065-conditional-rendering.md +4 -4
- package/docs/tutorials/070-displaying-a-list.md +24 -47
- package/docs/tutorials/072-inserting-into-a-list.md +17 -17
- package/docs/tutorials/074-deleting-from-a-list.md +15 -17
- package/docs/tutorials/076-updating-a-list-item.md +20 -22
- package/docs/tutorials/080-widget-requests.md +27 -23
- package/docs/tutorials/090-using-widget-libraries.md +23 -1
- package/package.json +1 -2
- package/dist/hub/lb-rows.d.ts +0 -18
- package/dist/hub/lb-rows.d.ts.map +0 -1
- package/dist/hub/lb-rows.js +0 -106
- package/dist/tests/lb-rows.test.d.ts.map +0 -1
|
@@ -0,0 +1,1000 @@
|
|
|
1
|
+
# What 1.0 release means
|
|
2
|
+
|
|
3
|
+
This is the complete technical reference to the upcoming Release 1.0.
|
|
4
|
+
|
|
5
|
+
Method-of-work is to continually revise this document to what we want
|
|
6
|
+
to be true, then revise the code, docs, and tests to ensure it is true.
|
|
7
|
+
|
|
8
|
+
## Blockers
|
|
9
|
+
|
|
10
|
+
We cannot declare 1.0 until we determine if these blockers can be
|
|
11
|
+
added later w/o breaking changes. If we are reasonably confident
|
|
12
|
+
that they can be decided later w/o breaking changes, then we rename
|
|
13
|
+
them to "Out of Scope". Otherwise, they must be resolved before
|
|
14
|
+
we declare 1.0.
|
|
15
|
+
|
|
16
|
+
### Data types
|
|
17
|
+
|
|
18
|
+
Loadbare/app enforces no data types on values between the database and the
|
|
19
|
+
browser. A uniform and useful approach is difficult to discern, and we do
|
|
20
|
+
not want to pollute 1.0 with a potentially sub-optimal solution. Until then
|
|
21
|
+
a value is rendered by browser coercion when it lands on an HTML text
|
|
22
|
+
element, or by whatever the widget does when it lands on a widget.
|
|
23
|
+
|
|
24
|
+
We will then see if a useful solution emerges that Loadbare/app should
|
|
25
|
+
handle.
|
|
26
|
+
|
|
27
|
+
### Run-time state attributes
|
|
28
|
+
|
|
29
|
+
The hub stamps `lb-row-count`, `lb-pending` and `lb-error` for a
|
|
30
|
+
stylesheet to read. Their consumer is CSS rather than code.
|
|
31
|
+
|
|
32
|
+
- **Decide `data-` against `lb-`.** These are the only names the hub writes
|
|
33
|
+
outside its own namespace, which is either a deliberate signal that CSS
|
|
34
|
+
owns them or an inconsistency.
|
|
35
|
+
- **Say what `lb-row-count` holds.** It is stamped and never explained.
|
|
36
|
+
- **Decide whether an unarrived value needs a signal.** The roadmap proposes
|
|
37
|
+
deriving it from an absent `lb-value`. Recommend confirming that and
|
|
38
|
+
adding nothing.
|
|
39
|
+
|
|
40
|
+
### The widget protocol
|
|
41
|
+
|
|
42
|
+
Loadbare/app owns every method name beginning with `lb` on a custom element.
|
|
43
|
+
That decision is firm; what the set contains is not.
|
|
44
|
+
|
|
45
|
+
- **Decide what the build does with an unknown `lb*` method.** An element
|
|
46
|
+
carrying a method beginning with `lb` that Loadbare/app does not define may
|
|
47
|
+
be an error, a warning, or ignored.
|
|
48
|
+
- **Decide whether a widget may receive a whole row.** A cell lands one
|
|
49
|
+
element at a time and offers no escape hatch. An optional `lbAcceptRow`
|
|
50
|
+
would sit beside `lbPlaceRow` and `lbRowsLanded`, and this is the most
|
|
51
|
+
likely first request from a widget author.
|
|
52
|
+
- **Give a widget a way to fire its own request.** A custom widget must
|
|
53
|
+
reproduce the logic in the hub to fire its own event. This is a bit of a
|
|
54
|
+
smell, but was done this way to start because we do not know yet the shape
|
|
55
|
+
of how the widget could re-use code from the hub.
|
|
56
|
+
|
|
57
|
+
### Lists
|
|
58
|
+
|
|
59
|
+
- **Decide what a nested list means.** A list whose rows each hold a list is
|
|
60
|
+
legal by the nesting rule, and produces strange results when the outer list
|
|
61
|
+
is updated. Loadbare/app has no vocabulary for it.
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<div lb-list="departments">
|
|
65
|
+
<template lb-key="id"><section>
|
|
66
|
+
<h2 lb-cell="name"></h2>
|
|
67
|
+
<ul lb-list="members"><template lb-key="id"><li lb-cell="who"></li></template></ul>
|
|
68
|
+
</section></template>
|
|
69
|
+
</div>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### The server API
|
|
73
|
+
|
|
74
|
+
- **Give the chrome a way to state its own queries.** A widget in the chrome
|
|
75
|
+
bound to a query forces every page to declare that query, since the page
|
|
76
|
+
data run covers one page's set. `lb-navigation` shows the pattern from the
|
|
77
|
+
framework side, and an application has no equivalent. Recommend a
|
|
78
|
+
chrome-level query set on `createHub`.
|
|
79
|
+
- **State that only `createHub` implements `Hub`.** Otherwise a sixth
|
|
80
|
+
operation breaks anyone who wrote the interface by hand.
|
|
81
|
+
- **Decide the options-argument shape once.** Global hooks, transaction
|
|
82
|
+
wrapping, error handling and a CSRF token all want the same trailing
|
|
83
|
+
parameter on `createHub` and `hubRoutes`. Build none of them, but pick
|
|
84
|
+
where they go.
|
|
85
|
+
- **Land `staticRoutes`.** See NEXT.md. It closes the last place where an
|
|
86
|
+
application hand-writes facts the builder owns.
|
|
87
|
+
|
|
88
|
+
### The builder
|
|
89
|
+
|
|
90
|
+
- **Adopt a configuration file and fold `imports.ts` into it.** It holds
|
|
91
|
+
`out`, `minify` and `imports`, and lives where `imports.ts` lives now, so a
|
|
92
|
+
repository with several applications gets one per application. `--src`
|
|
93
|
+
stays a flag, since it is what locates the file. State the precedence
|
|
94
|
+
between a flag and a key once. Its key names join the permanent surface,
|
|
95
|
+
so this lands before 1.0 or not at all.
|
|
96
|
+
- **Take a position on the URL space.** A page name is a filename basename,
|
|
97
|
+
so `/user/42` resolves to nothing and no page is addressable by row.
|
|
98
|
+
Deciding against path parameters is a legitimate answer. Write whichever
|
|
99
|
+
one down.
|
|
100
|
+
- **Decide CSS pairing.** Naming a stylesheet `<tag>.css` beside its
|
|
101
|
+
definition would let the builder ship only what survives expansion.
|
|
102
|
+
Recommend deferring the mechanism and reserving the configuration key, so
|
|
103
|
+
that it lands as an opt-in rather than as a silent drop.
|
|
104
|
+
- **Land the remaining validations.** One `lb-hub`, one empty `<main>`, and
|
|
105
|
+
every `lb-` attribute known. Recommend landing these now, because refusing
|
|
106
|
+
markup that used to build is the kind of change 1.0 gives up.
|
|
107
|
+
- **Confirm the three output names.** `app.html`, `client.js` and `app.css`
|
|
108
|
+
are about to be fixed in `staticRoutes` as well as in the builder.
|
|
109
|
+
|
|
110
|
+
### The internal surface
|
|
111
|
+
|
|
112
|
+
None of this is surface. It is listed here because trimming it is free
|
|
113
|
+
today and breaking after 1.0.
|
|
114
|
+
|
|
115
|
+
- **Drop `./build` from the exports map.** Nothing in the repository imports
|
|
116
|
+
it, and nothing outside the CLI should call `resolveElements` or
|
|
117
|
+
`clientEntrySource`.
|
|
118
|
+
- **Audit what the hub does.** It appears to have routines that go beyond
|
|
119
|
+
what the `lb-*` attribute namespace specifies.
|
|
120
|
+
- **Keep the rest as constants.** The endpoint, `index` and the timeout are
|
|
121
|
+
linkage between the builder and the hub rather than facts an application
|
|
122
|
+
varies. Adding a knob for any of them buys a second shape of application
|
|
123
|
+
to reason about.
|
|
124
|
+
- **Give the hub a configuration channel without using it.** `<lb-hub>` reads
|
|
125
|
+
no attributes at all, which is the free landing spot for the roadmap's
|
|
126
|
+
static-origin split and for a timeout an application eventually outgrows.
|
|
127
|
+
Reserve the idea, build nothing.
|
|
128
|
+
|
|
129
|
+
## The build
|
|
130
|
+
|
|
131
|
+
### Running the builder
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`--src` names the tree the builder scans, and is defined under
|
|
138
|
+
[What the builder scans](#what-the-builder-scans).
|
|
139
|
+
|
|
140
|
+
`--out` names the directory the builder writes into, and defaults to `dist`.
|
|
141
|
+
Every output path in this document is written relative to it, so an
|
|
142
|
+
application that passes `--out` reads `dist/app.html` here as
|
|
143
|
+
`<out>/app.html`. See [Build outputs](#build-outputs).
|
|
144
|
+
|
|
145
|
+
`--watch` builds once, then rebuilds on every change to an `.html`, `.ts` or
|
|
146
|
+
`.css` file under `--src`. A build that throws is reported and the watch
|
|
147
|
+
continues. There is no dev server and no browser reload.
|
|
148
|
+
|
|
149
|
+
`--minify` minifies `client.js` and `app.css`. It leaves `app.html` alone,
|
|
150
|
+
which stays markup an application can read.
|
|
151
|
+
|
|
152
|
+
The builder bundles `client.js` from a `client-entry.ts` it generates into
|
|
153
|
+
`--out` and leaves there. Nothing reads it after the bundle, and an
|
|
154
|
+
application neither writes nor imports it.
|
|
155
|
+
|
|
156
|
+
Every `.css` file in every origin joins `app.css`. There is no widget, page
|
|
157
|
+
or chrome stylesheet: the builder concatenates them all, with nothing added,
|
|
158
|
+
removed or scoped, so what ships is what was authored. Within one origin
|
|
159
|
+
they are ordered by filename, the full path breaking a tie, and a
|
|
160
|
+
subdirectory never affects the order. Origins follow the cascade — this
|
|
161
|
+
package's own widgets, then each package named in `imports.ts`, then `--src`
|
|
162
|
+
last — so an application's own stylesheet always lands after the ones it
|
|
163
|
+
imported. A file named `00-global.css` sorts first by saying so.
|
|
164
|
+
|
|
165
|
+
### What the builder scans
|
|
166
|
+
|
|
167
|
+
#### The current project
|
|
168
|
+
|
|
169
|
+
The builder scans one directory named by its `--src` parameter,
|
|
170
|
+
which defaults to `src/`.
|
|
171
|
+
|
|
172
|
+
The sections below often state something like, "anywhere in
|
|
173
|
+
the `--src` tree", because Loadbare/app assigns semantic meaning
|
|
174
|
+
to file names, not subdir names.
|
|
175
|
+
|
|
176
|
+
#### Imported widget libraries
|
|
177
|
+
|
|
178
|
+
Any number of widget libraries can be used. They are named
|
|
179
|
+
in the file `imports.ts`, located anywhere in builder `--src`.
|
|
180
|
+
A project can leave out `imports.ts`, or have exactly one, but
|
|
181
|
+
two or more is an error.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// imports.ts, anywhere in --src
|
|
185
|
+
export default ["@namespace/widget-library"];
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
If the named package's `package.json` declares where the widgets
|
|
189
|
+
are, Loadbare scans that. Otherwise it scans the entire installed
|
|
190
|
+
directory. The declaration, if present, would look like this:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
"loadbare": { "widgets": "./dist" }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### File names and the builder
|
|
197
|
+
|
|
198
|
+
The builder finds a file by its name. A subdirectory carries no meaning, so
|
|
199
|
+
the whole of `--src` is one flat namespace, as stated in
|
|
200
|
+
[What the builder scans](#what-the-builder-scans).
|
|
201
|
+
|
|
202
|
+
Three kinds of file hold HTML.
|
|
203
|
+
|
|
204
|
+
| File | Holds | Found in |
|
|
205
|
+
|--------------------|--------------------------------------|--------------|
|
|
206
|
+
| `chrome.html` | The one HTML document | `--src` |
|
|
207
|
+
| `<stub>.page.html` | One page's markup, as a fragment | `--src` |
|
|
208
|
+
| `<tag-name>.html` | One widget definition, as a fragment | Every origin |
|
|
209
|
+
|
|
210
|
+
As stated in [Chrome](#chrome), `chrome.html` is a required singleton.
|
|
211
|
+
|
|
212
|
+
As stated in [Pages](#pages), the `<stub>` of a `.page.html` is the path the
|
|
213
|
+
page maps to. The builder puts all pages into `dist/app.html` as HTML `<template>` objects
|
|
214
|
+
that carry `lb-page="<stub>"`.
|
|
215
|
+
|
|
216
|
+
A widget file is exactly `<kebab-case-name>.html`. A [widget](#widgets) is
|
|
217
|
+
an HTML custom element, suitable for organizing large HTML trees, or adding
|
|
218
|
+
custom behavior, or both. A widget definition is named for the tag it expands.
|
|
219
|
+
|
|
220
|
+
Any other HTML file, one that is not `chrome.html`, or `<stub>.page.html` or
|
|
221
|
+
`<kebab-case-name>.html` will either:
|
|
222
|
+
- be an error if it looks like a widget with capitalization
|
|
223
|
+
- be skipped
|
|
224
|
+
|
|
225
|
+
## HTML
|
|
226
|
+
|
|
227
|
+
In a Loadbare/app application, HTML is static after the build, and once
|
|
228
|
+
it is sent to the browser on initial page load, no HTML is ever sent
|
|
229
|
+
again.
|
|
230
|
+
|
|
231
|
+
### Widget Expansion
|
|
232
|
+
|
|
233
|
+
The builder executes a process we call "expansion". When it finds
|
|
234
|
+
a custom element in the HTML it is processing, such as `<my-element>`,
|
|
235
|
+
it looks for the file `<my-element>.html`, and follows the expansion
|
|
236
|
+
rules to place the contents of `<my-element>.html`.
|
|
237
|
+
|
|
238
|
+
A custom element with no definition file is left alone. A custom element
|
|
239
|
+
with neither a definition nor a `.browser.ts` script is a build error, as
|
|
240
|
+
stated in [Widgets](#widgets).
|
|
241
|
+
|
|
242
|
+
#### Build time parameters
|
|
243
|
+
|
|
244
|
+
A build time parameter is supplied as an attribute on a custom element,
|
|
245
|
+
and is converted to a fixed value within the HTML by the builder.
|
|
246
|
+
|
|
247
|
+
The attribute value carries an `exp-` prefix, as in `exp-label`, and
|
|
248
|
+
the substitution locations use `{{label}}` (no exp-prefix) inside the
|
|
249
|
+
widget's HTML definition file:
|
|
250
|
+
|
|
251
|
+
```html
|
|
252
|
+
<!-- lb-input.html, the definition -->
|
|
253
|
+
<label>{{label}} <input readonly="{{readonly}}" /></label>
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
```html
|
|
257
|
+
<!-- a page -->
|
|
258
|
+
<lb-input lb-cell="name" exp-label="Name"></lb-input>
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```html
|
|
262
|
+
<!-- dist/app.html -->
|
|
263
|
+
<lb-input lb-cell="name" exp-label="Name"
|
|
264
|
+
><label>Name <input /></label
|
|
265
|
+
></lb-input>
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
A definition states a default with `{{name|default}}`, as in
|
|
269
|
+
`{{button-label|OK}}`. Whitespace around the name and around the default is
|
|
270
|
+
discarded. The default is a literal.
|
|
271
|
+
|
|
272
|
+
| The attribute is assigned | Expansion defines | Result |
|
|
273
|
+
|---------------------------|--------------------|----------------------------------|
|
|
274
|
+
| `exp-name="text"` | `{{name}}` | `text` |
|
|
275
|
+
| `exp-name=""` | `{{name}}` | An empty string |
|
|
276
|
+
| Nothing | `{{name|default}}` | `default` |
|
|
277
|
+
| Nothing | `{{name}}` | Nothing, and the attribute drops |
|
|
278
|
+
| `exp-name` | No `{{name}}` | A build error |
|
|
279
|
+
|
|
280
|
+
Attributes without the `exp-` prefix are ignored during expansion.
|
|
281
|
+
|
|
282
|
+
A placeholder is the whole of an attribute value or the whole of a text node.
|
|
283
|
+
`title="Hello {{name}}"` ships literally.
|
|
284
|
+
|
|
285
|
+
A placeholder name carrying a capital is a build error.
|
|
286
|
+
|
|
287
|
+
A parameter value cannot become markup.
|
|
288
|
+
|
|
289
|
+
#### Slots and templates
|
|
290
|
+
|
|
291
|
+
A widget definition may contain any number of named templates and optionally
|
|
292
|
+
one slot.
|
|
293
|
+
|
|
294
|
+
This simplified definition of `lb-table` names two templates, `head` and
|
|
295
|
+
`foot`, and marks `<tbody>` as the slot. A page supplies whichever templates
|
|
296
|
+
it wants, and everything else it writes goes to the slot.
|
|
297
|
+
|
|
298
|
+
```html
|
|
299
|
+
<!-- lb-table.html, the definition -->
|
|
300
|
+
<table>
|
|
301
|
+
<caption>
|
|
302
|
+
{{caption}}
|
|
303
|
+
</caption>
|
|
304
|
+
<thead lb-template="head"></thead>
|
|
305
|
+
<tbody lb-slot></tbody>
|
|
306
|
+
<tfoot lb-template="foot"></tfoot>
|
|
307
|
+
</table>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
When we use `lb-table` in HTML, the example below supplies the header
|
|
311
|
+
template and an unnamed template. The unnamed one names neither `head` nor
|
|
312
|
+
`foot`, so it goes to the slot. This usage writes no footer.
|
|
313
|
+
|
|
314
|
+
```html
|
|
315
|
+
<!-- a page -->
|
|
316
|
+
<lb-table lb-list="staff" exp-caption="Everyone, by team">
|
|
317
|
+
<template lb-template="head">
|
|
318
|
+
<tr><th>Name</th><th>Role</th></tr>
|
|
319
|
+
</template>
|
|
320
|
+
<template lb-key="id">
|
|
321
|
+
<tr><td lb-cell="name"></td><td lb-cell="role"></td></tr>
|
|
322
|
+
</template>
|
|
323
|
+
</lb-table>
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
When a named template is expanded, the `<template>` tag is discarded and its
|
|
327
|
+
contents are placed as children of the tag that names it. The built document
|
|
328
|
+
holds:
|
|
329
|
+
|
|
330
|
+
```html
|
|
331
|
+
<!-- dist/app.html -->
|
|
332
|
+
<lb-table lb-list="staff" exp-caption="Everyone, by team"
|
|
333
|
+
><table>
|
|
334
|
+
<caption>
|
|
335
|
+
Everyone, by team
|
|
336
|
+
</caption>
|
|
337
|
+
<thead>
|
|
338
|
+
<tr>
|
|
339
|
+
<th>Name</th>
|
|
340
|
+
<th>Role</th>
|
|
341
|
+
</tr>
|
|
342
|
+
</thead>
|
|
343
|
+
<tbody>
|
|
344
|
+
<template lb-key="id">
|
|
345
|
+
<tr>
|
|
346
|
+
<td lb-cell="name"></td>
|
|
347
|
+
<td lb-cell="role"></td>
|
|
348
|
+
</tr>
|
|
349
|
+
</template>
|
|
350
|
+
</tbody>
|
|
351
|
+
<tfoot></tfoot></table
|
|
352
|
+
></lb-table>
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
These are build errors:
|
|
356
|
+
|
|
357
|
+
- A definition with two templates of one name
|
|
358
|
+
- A definition with more than one `lb-slot`
|
|
359
|
+
- A page naming a template the definition does not have
|
|
360
|
+
- A page giving two templates for one name
|
|
361
|
+
- Content written in a widget whose definition has no slot
|
|
362
|
+
|
|
363
|
+
### Binding
|
|
364
|
+
|
|
365
|
+
---- UNEDITED ----
|
|
366
|
+
|
|
367
|
+
Binding attributes determine how the hub updates the DOM, either by
|
|
368
|
+
directly updating a plain HTML element or instructing
|
|
369
|
+
custom widgets to update themselves.
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
| Attribute | Assigned By | Behavior |
|
|
373
|
+
|--------------|-------------|-------------------------------------------------------------------------------------------|
|
|
374
|
+
| lb-list | Developer | Scopes DOM children to a named set of rows; a nested lb-list or lb-row begins a new scope |
|
|
375
|
+
| lb-row | Developer | Scopes DOM children to one named row; a nested lb-list or lb-row begins a new scope |
|
|
376
|
+
| lb-cell | Developer | This DOM node displays this column of the row in scope |
|
|
377
|
+
| lb-key | Developer | Names the column that identifies a row, on the row template inside an lb-list |
|
|
378
|
+
| lb-key-value | Hub | Stamped on a live row: that row's value of `lb-key` |
|
|
379
|
+
| lb-value | Hub | Custom widgets update themselves when this attribute changes |
|
|
380
|
+
|
|
381
|
+
#### How a value lands
|
|
382
|
+
|
|
383
|
+
A cell lands one of two ways, and the tag name decides which.
|
|
384
|
+
|
|
385
|
+
| Element | Receives the value as |
|
|
386
|
+
|----------------------------------|--------------------------|
|
|
387
|
+
| A native element | Its `textContent` |
|
|
388
|
+
| A custom element, tag hyphenated | Its `lb-value` attribute |
|
|
389
|
+
|
|
390
|
+
A native element has no behavior of its own, so its value is its text. A
|
|
391
|
+
widget owns whatever control it wraps, so it is handed the value and renders
|
|
392
|
+
it itself.
|
|
393
|
+
|
|
394
|
+
The test is the hyphen in the tag name. The hub holds no knowledge of any
|
|
395
|
+
particular element, and the same test decides that the hub leaves a widget's
|
|
396
|
+
own clicks alone — see [Requests](#requests).
|
|
397
|
+
|
|
398
|
+
Nothing an application writes ever sets `lb-value`. Loadbare writes it and
|
|
399
|
+
the widget it is written on reads it.
|
|
400
|
+
|
|
401
|
+
The value arrives as the query produced it, with no conversion, so the
|
|
402
|
+
browser decides what a non-string looks like.
|
|
403
|
+
|
|
404
|
+
A widget sees `attributeChangedCallback` for an `lb-value` already present
|
|
405
|
+
when it upgrades, so a widget cannot tell a first landing from a refresh,
|
|
406
|
+
and does not need to.
|
|
407
|
+
|
|
408
|
+
### Requests
|
|
409
|
+
|
|
410
|
+
---- UNEDITED ----
|
|
411
|
+
|
|
412
|
+
Any element can carry the `lb-action` attribute. The hub listens for
|
|
413
|
+
events `click` and `submit`, and fires a server request when it catches
|
|
414
|
+
one of those events.
|
|
415
|
+
|
|
416
|
+
The hub does not act on `click` or `submit` on a custom widget, they
|
|
417
|
+
must fire their own event. The assumption is a custom widget is present
|
|
418
|
+
because custom behavior is desired, and we don't want the hub to conflict
|
|
419
|
+
with that custom behavior. A widget is told apart by the hyphen in its tag
|
|
420
|
+
name, the same test that decides how a value lands — see
|
|
421
|
+
[How a value lands](#how-a-value-lands).
|
|
422
|
+
|
|
423
|
+
| lb-action | Written on |
|
|
424
|
+
|----------------|---------------------------------------------------|
|
|
425
|
+
| lb-row-insert | a `<form>` in a list scope |
|
|
426
|
+
| lb-row-delete | anything inside a live row |
|
|
427
|
+
| lb-row-update | a `<form>` inside a live row |
|
|
428
|
+
| lb-cell-change | a widget wrapping one control |
|
|
429
|
+
| anything else | must be a named routine in the page's server code |
|
|
430
|
+
|
|
431
|
+
The wire format is not visible to the user, but uses the same lb-*
|
|
432
|
+
attributes minus their prefix, so that it is intelligible when working on
|
|
433
|
+
Loadbare/app itself.
|
|
434
|
+
|
|
435
|
+
From a native element the hub sends the action plus whatever binding is in
|
|
436
|
+
scope, and never a scalar value.
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
{ action: "lb-row-insert", list: "rosterList", values: {...} }
|
|
440
|
+
{ action: "lb-row-delete", list: "rosterList", key: "42" }
|
|
441
|
+
{ action: "lb-row-update", list: "rosterList", key: "42", values: {...} }
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
A widget builds its own request and adds the value of the control it wraps.
|
|
445
|
+
`lb-select` and `lb-options` send an action and a value and no binding.
|
|
446
|
+
`lb-input` sends the binding as well, and is the only sender of a cell
|
|
447
|
+
change.
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
{ action: "lb-cell-change", list: "rosterList", key: "42", cell: "name", value: "Ann" }
|
|
451
|
+
{ action: "selectTab", row: "prefs", cell: "active_tab", value: "two" }
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
#### The request event
|
|
455
|
+
|
|
456
|
+
The hub does not send a request the moment it catches a click or a submit.
|
|
457
|
+
It builds the request, dispatches it from the element that acted as a
|
|
458
|
+
bubbling `lb-request` event carrying the request as its `detail`, and a
|
|
459
|
+
listener on the hub sends it.
|
|
460
|
+
|
|
461
|
+
A widget uses that same event, and it is the only channel a widget has for
|
|
462
|
+
firing a request of its own. A native element and a hand-written widget
|
|
463
|
+
therefore produce identical events.
|
|
464
|
+
|
|
465
|
+
An ancestor sees the request on its way up and may stop it. The event is
|
|
466
|
+
not cancelable, so an interceptor calls `stopPropagation`, not
|
|
467
|
+
`preventDefault`. This is what makes a confirmation wrapper possible
|
|
468
|
+
without the wrapped element knowing about it.
|
|
469
|
+
|
|
470
|
+
#### The round trip
|
|
471
|
+
|
|
472
|
+
A request the hub sends has ten seconds to come back. Past that the hub
|
|
473
|
+
aborts it and treats it as a failure, since `fetch` imposes no deadline of
|
|
474
|
+
its own. An application cannot change the deadline.
|
|
475
|
+
|
|
476
|
+
A round trip fails on a server error, on a network failure, or on that
|
|
477
|
+
deadline, and all three set `lb-error` on the element that dispatched the
|
|
478
|
+
request — see [Request state](#request-state).
|
|
479
|
+
|
|
480
|
+
A navigation that fails to load its data sets nothing. No element
|
|
481
|
+
dispatched it, so there is nothing to stamp, and the hub reports it to the
|
|
482
|
+
console.
|
|
483
|
+
|
|
484
|
+
### Links
|
|
485
|
+
|
|
486
|
+
---- UNEDITED ----
|
|
487
|
+
|
|
488
|
+
An anchor carrying `lb-nav-link` navigates inside the application: the hub
|
|
489
|
+
catches the click, pushes the anchor's path onto history, and swaps the page
|
|
490
|
+
host in `<main>`. An anchor without it is left alone and behaves like any
|
|
491
|
+
other link, so leaving the application is the default and staying in it is
|
|
492
|
+
the opt-in.
|
|
493
|
+
|
|
494
|
+
| Attribute | Assigned By | Behavior |
|
|
495
|
+
|-------------|-------------|-----------------------------------------------------------------------------------------|
|
|
496
|
+
| lb-nav-link | Developer | On an `<a>`: the hub shows the page the anchor's path names, without loading a document |
|
|
497
|
+
|
|
498
|
+
The attribute takes no value. The hub looks for the nearest ancestor
|
|
499
|
+
link to determine the path.
|
|
500
|
+
|
|
501
|
+
Path space is flat. A path such as `/members` links to the `members.*` files
|
|
502
|
+
on the server.
|
|
503
|
+
|
|
504
|
+
The bare path `/` resolves to `index`.
|
|
505
|
+
|
|
506
|
+
A path that names no page is detected in the browser, after a successful
|
|
507
|
+
200: every route gets the same document, so there is no server-delivered
|
|
508
|
+
404. A chrome that declares an `lb-unknown-page` dialog gets it opened.
|
|
509
|
+
One that declares none gets a console error and nothing on screen. See
|
|
510
|
+
[Chrome](#chrome).
|
|
511
|
+
|
|
512
|
+
```html
|
|
513
|
+
<nav>
|
|
514
|
+
<a href="/" lb-nav-link>Home</a>
|
|
515
|
+
<a href="/members" lb-nav-link>Members</a>
|
|
516
|
+
<a href="https://example.com/docs">Docs</a>
|
|
517
|
+
</nav>
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
See also [Navigation Row lb-navigation](#lb-navigation).
|
|
521
|
+
|
|
522
|
+
### lb-navigation
|
|
523
|
+
|
|
524
|
+
---- UNEDITED ----
|
|
525
|
+
|
|
526
|
+
Status: 1.0-RC.
|
|
527
|
+
|
|
528
|
+
Current page, published by the hub as a row. Can be bound anywhere just
|
|
529
|
+
like a server-produced result.
|
|
530
|
+
|
|
531
|
+
| Cell | Holds |
|
|
532
|
+
|--------------|---------------------------------------------------------------------|
|
|
533
|
+
| `page-label` | The text of the link to the current page, empty if no link names it |
|
|
534
|
+
| `page-uri` | The path as the browser has it, such as `/members` |
|
|
535
|
+
|
|
536
|
+
The `page-uri` is taken from the current URL. The `page-label` is taken from the first
|
|
537
|
+
`lb-nav-link` link in the document (presumably in a nav bar) that matches the URI.
|
|
538
|
+
|
|
539
|
+
The row lands before the page is looked up, so a path that names no page has
|
|
540
|
+
it too, and an `lb-unknown-page` dialog, where the chrome has one, displays
|
|
541
|
+
it by naming the row like any other subtree.
|
|
542
|
+
|
|
543
|
+
It lands only where a subtree names it. A chrome that displays no
|
|
544
|
+
navigation is not warned about a row with nowhere to go.
|
|
545
|
+
|
|
546
|
+
```html
|
|
547
|
+
<header lb-row="lb-navigation">
|
|
548
|
+
<h2 lb-cell="page-label"></h2>
|
|
549
|
+
</header>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
See also [Links](#links).
|
|
553
|
+
|
|
554
|
+
## Application files
|
|
555
|
+
|
|
556
|
+
### Chrome
|
|
557
|
+
|
|
558
|
+
The Loadbare/app builder always requires exactly one file
|
|
559
|
+
named `chrome.html`, located anywhere in builder `--src`.
|
|
560
|
+
|
|
561
|
+
The chrome is the application's single HTML document. This is
|
|
562
|
+
where the standard landmarks go: header, nav, footer, main.
|
|
563
|
+
|
|
564
|
+
The file is an HTML document, which must contain:
|
|
565
|
+
- `<lb-hub>` inside `<body>`. The hub can only act on its children,
|
|
566
|
+
that is why it is usually nested right below `<body>`.
|
|
567
|
+
- One empty `<main>` inside `<lb-hub>`.
|
|
568
|
+
- The script tag for `/client.js`, the javascript bundle.
|
|
569
|
+
|
|
570
|
+
It may also contain:
|
|
571
|
+
- `/app.css`, the stylesheet bundle
|
|
572
|
+
- A `<dialog>` marked with attribute `lb-unknown-page`, which the hub
|
|
573
|
+
will display to the user if an attempt is made to navigate to an
|
|
574
|
+
unknown page. It goes inside `<lb-hub>`, like everything the hub acts
|
|
575
|
+
on; the builder rejects one placed elsewhere.
|
|
576
|
+
- The `hidden` attribute on `<body>`, which the hub removes once the first page has landed.
|
|
577
|
+
|
|
578
|
+
```html
|
|
579
|
+
<!-- src/chrome.html -->
|
|
580
|
+
<!doctype html>
|
|
581
|
+
<html lang="en">
|
|
582
|
+
<head>
|
|
583
|
+
<meta charset="utf-8" />
|
|
584
|
+
<title>Membership Roster</title>
|
|
585
|
+
<script src="/client.js" defer></script>
|
|
586
|
+
<link rel="stylesheet" href="/app.css" />
|
|
587
|
+
</head>
|
|
588
|
+
<body hidden>
|
|
589
|
+
<lb-hub>
|
|
590
|
+
<header lb-row="lb-navigation">
|
|
591
|
+
<h1>Membership Roster</h1>
|
|
592
|
+
<h2 lb-cell="page-label"></h2>
|
|
593
|
+
</header>
|
|
594
|
+
<nav>
|
|
595
|
+
<a href="/" lb-nav-link>Home</a>
|
|
596
|
+
<a href="/members" lb-nav-link>Members</a>
|
|
597
|
+
<a href="https://example.org/">Our website</a>
|
|
598
|
+
</nav>
|
|
599
|
+
<main></main>
|
|
600
|
+
<dialog lb-unknown-page lb-row="lb-navigation">
|
|
601
|
+
The URL <span lb-cell="page-uri"></span> is not in this app.
|
|
602
|
+
</dialog>
|
|
603
|
+
</lb-hub>
|
|
604
|
+
</body>
|
|
605
|
+
</html>
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
The `lb-row`, `lb-cell` and `lb-nav-link` attributes in that example are
|
|
609
|
+
ordinary Loadbare/app binding. See [Binding](#binding),
|
|
610
|
+
[Links](#links) and [lb-navigation](#lb-navigation).
|
|
611
|
+
|
|
612
|
+
### Pages
|
|
613
|
+
|
|
614
|
+
The page namespace is flat. Loadbare/app does not care where in the `--src`
|
|
615
|
+
the page files are located, but they must be unique across the application.
|
|
616
|
+
A page `/deep/path/to/mypage.html` is routed to `/mypage`.
|
|
617
|
+
|
|
618
|
+
Pages are grouped as
|
|
619
|
+
- <stub>.page.html is recognized as navigable and capable of
|
|
620
|
+
having associated queries and requests
|
|
621
|
+
- <stub>.queries.ts are the queries for a page
|
|
622
|
+
- <stub>.requests.ts respond to the requests from the browser
|
|
623
|
+
|
|
624
|
+
The <stub> value for a page must match the path used in links,
|
|
625
|
+
so that `<a href='/members' lb-nav-link>Members</a>` has matching
|
|
626
|
+
files `members.pages.html` et al.
|
|
627
|
+
|
|
628
|
+
#### Page HTML
|
|
629
|
+
|
|
630
|
+
The markup in `<stub>.page.html` is the same HTML as everywhere else
|
|
631
|
+
in a Loadbare/app application, see [HTML](#html).
|
|
632
|
+
|
|
633
|
+
The builder wraps each expanded page in `<template lb-page="<stub>">` and
|
|
634
|
+
puts it in the built document. The application never writes `lb-page`; the
|
|
635
|
+
hub reads it to find the page a path names.
|
|
636
|
+
|
|
637
|
+
## The server
|
|
638
|
+
|
|
639
|
+
A page's server half is two files, `<stub>.queries.ts` and
|
|
640
|
+
`<stub>.requests.ts`. Each pairs with `<stub>.page.html` by sharing its
|
|
641
|
+
stub, as stated in [Pages](#pages). Either may be absent, and one with no
|
|
642
|
+
matching page is a build error.
|
|
643
|
+
|
|
644
|
+
### The Express server
|
|
645
|
+
|
|
646
|
+
Loadbare/app ships no server. The application writes an ordinary Express
|
|
647
|
+
app. Express is a peer dependency, installed by the application.
|
|
648
|
+
|
|
649
|
+
The builder's artifacts oblige that server to handle four things: the
|
|
650
|
+
client script, the stylesheet, the hub's data channel, and the one HTML
|
|
651
|
+
document.
|
|
652
|
+
|
|
653
|
+
| Handle | With |
|
|
654
|
+
|------------------------------|---------------------|
|
|
655
|
+
| `/client.js` | `dist/client.js` |
|
|
656
|
+
| `/app.css` | `dist/app.css` |
|
|
657
|
+
| `hubRoutes(hub, contextFor)` | The hub's own route |
|
|
658
|
+
| Every other GET | `dist/app.html` |
|
|
659
|
+
|
|
660
|
+
The builder writes `pages.ts` into `--out` whenever any page has a
|
|
661
|
+
`.queries.ts` or a `.requests.ts` file. It imports each of them, calls
|
|
662
|
+
`createHub()` once over the lot, and exports the result as `hub`. The
|
|
663
|
+
server imports that rather than maintaining the registry by hand.
|
|
664
|
+
|
|
665
|
+
```ts
|
|
666
|
+
// server.ts
|
|
667
|
+
import path from "node:path";
|
|
668
|
+
import { readFileSync } from "node:fs";
|
|
669
|
+
import express, { type Request } from "express";
|
|
670
|
+
import { hubRoutes } from "@loadbare/app/express";
|
|
671
|
+
import type { HubContext } from "@loadbare/app/server";
|
|
672
|
+
import { hub } from "./dist/pages";
|
|
673
|
+
import { openDb } from "./src/database";
|
|
674
|
+
|
|
675
|
+
const DIST = path.resolve("dist");
|
|
676
|
+
const app = express();
|
|
677
|
+
|
|
678
|
+
// All data requests are handled by the hub
|
|
679
|
+
// This example shows an application-specific openDB()
|
|
680
|
+
function contextFor(_req: Request): HubContext {
|
|
681
|
+
return { db: openDb() };
|
|
682
|
+
}
|
|
683
|
+
app.use(hubRoutes(hub, contextFor));
|
|
684
|
+
|
|
685
|
+
// Static assets, then the catch-all for app.html
|
|
686
|
+
app.get("/client.js", (_req, res) => res.sendFile(path.join(DIST, "client.js")));
|
|
687
|
+
app.get("/app.css", (_req, res) => res.sendFile(path.join(DIST, "app.css")));
|
|
688
|
+
app.get(/.*/, (_req, res) =>
|
|
689
|
+
res.type("html").send(readFileSync(path.join(DIST, "app.html"), "utf-8")),
|
|
690
|
+
);
|
|
691
|
+
|
|
692
|
+
app.listen(8787);
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
Explaining Express is beyond the scope of this technical reference. The
|
|
696
|
+
only real requirement is that the catch-all for app.html is at the end,
|
|
697
|
+
so it does not catch any other files.
|
|
698
|
+
|
|
699
|
+
Give the server the origin root. The hub reaches its own endpoints by
|
|
700
|
+
absolute path, so an application cannot be hosted under a subpath such as
|
|
701
|
+
`example.com/myapp/`, and anything proxying in front of the server passes
|
|
702
|
+
the whole path space through unchanged.
|
|
703
|
+
|
|
704
|
+
### Page Queries
|
|
705
|
+
|
|
706
|
+
`<stub>.queries.ts` exports one object named `queries`, typed `Queries`.
|
|
707
|
+
Each key is a query name, and the markup binds to that name through
|
|
708
|
+
`lb-list` or `lb-row`.
|
|
709
|
+
|
|
710
|
+
Cardinality belongs to the named query. One named query always returns a single
|
|
711
|
+
row or an array of rows. Build a query using `row()` or `list()` to return
|
|
712
|
+
the two shapes. A query cannot be built without one of these functions.
|
|
713
|
+
|
|
714
|
+
Queries names cannot begin with `lb-`, that namespace is reserved for
|
|
715
|
+
Loadbare/app queries the hub makes available in the browser, such
|
|
716
|
+
as [lb-navigation](#lb-navigation).
|
|
717
|
+
|
|
718
|
+
```ts
|
|
719
|
+
// members.queries.ts
|
|
720
|
+
import { list, row, type Queries } from "@loadbare/app/server";
|
|
721
|
+
|
|
722
|
+
export const queries: Queries = {
|
|
723
|
+
roster: list((ctx) => ctx.db.members()),
|
|
724
|
+
summary: row(async (ctx) => ({ count: String(await ctx.db.memberCount()) })),
|
|
725
|
+
};
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
Every query and every request receives `ctx`, the application's own request
|
|
729
|
+
context. The application builds it once per request and hands it to the hub,
|
|
730
|
+
see [The Express server](#the-express-server). Loadbare/app declares it empty
|
|
731
|
+
and never reads it, so a query finds exactly what the application put there,
|
|
732
|
+
which is usually a database handle opened for the authenticated caller.
|
|
733
|
+
|
|
734
|
+
Queries should not return deltas, deltas are handled in the `.requests.ts` file
|
|
735
|
+
as explained in the next section.
|
|
736
|
+
|
|
737
|
+
### Page Requests
|
|
738
|
+
|
|
739
|
+
`<stub>.requests.ts` exports one object named `requests`, typed `Requests`.
|
|
740
|
+
It has three optional keys.
|
|
741
|
+
|
|
742
|
+
| Key | Keyed by | Answers |
|
|
743
|
+
|---------------|-----------------------|----------------------------------------------------|
|
|
744
|
+
| `actions` | The `lb-action` value | Anything the page chooses to declare |
|
|
745
|
+
| `crud` | A query name | The four reserved `lb-action` values |
|
|
746
|
+
| `onPageEnter` | Nothing | Runs once on entering the page, before its queries |
|
|
747
|
+
|
|
748
|
+
|
|
749
|
+
The hub resolves an `lb-action` value against `actions`, and a reserved
|
|
750
|
+
operation against `crud` under the bound list name. A name with no entry
|
|
751
|
+
there logs a server console warning naming the page and the missing entry,
|
|
752
|
+
and answers 200 with `{}`. The browser applies `{}`, so no cell is set and
|
|
753
|
+
no row is added or removed, and `lb-pending` clears from the element that
|
|
754
|
+
sent the request. A request carrying no action answers 400, and a `run`
|
|
755
|
+
that throws answers 500.
|
|
756
|
+
|
|
757
|
+
Every entry under `actions` and `crud` has the same two members. `run`
|
|
758
|
+
performs the work, and `refresh` names the queries to re-run once the action
|
|
759
|
+
is complete.
|
|
760
|
+
|
|
761
|
+
`run` receives the same `ctx` and a `where`: the binding in scope when the
|
|
762
|
+
interaction happened, as `list`, `row`, `key`, `cell` and `value`. Under
|
|
763
|
+
`crud` the `where` carries only what that operation is typed to carry.
|
|
764
|
+
|
|
765
|
+
`run` may also return results of its own, which are laid over the refreshed
|
|
766
|
+
ones. That is how a delta reaches the browser: wrap it in `patch()`, naming
|
|
767
|
+
the rows that arrived or changed and the keys that went.
|
|
768
|
+
|
|
769
|
+
```ts
|
|
770
|
+
// members.requests.ts
|
|
771
|
+
import { patch, type Requests } from "@loadbare/app/server";
|
|
772
|
+
|
|
773
|
+
export const requests: Requests = {
|
|
774
|
+
actions: {
|
|
775
|
+
resetRoster: {
|
|
776
|
+
run: (ctx) => ctx.db.resetMembers(),
|
|
777
|
+
refresh: ["roster"],
|
|
778
|
+
},
|
|
779
|
+
},
|
|
780
|
+
crud: {
|
|
781
|
+
roster: {
|
|
782
|
+
rowDelete: {
|
|
783
|
+
run: async (ctx, where) => {
|
|
784
|
+
await ctx.db.deleteMember(where.key);
|
|
785
|
+
return { roster: patch({ drop: [where.key] }) };
|
|
786
|
+
},
|
|
787
|
+
refresh: [],
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
};
|
|
792
|
+
```
|
|
793
|
+
|
|
794
|
+
Each key under a `crud` entry is a reserved `lb-action` value with its prefix
|
|
795
|
+
stripped and the rest camel-cased, so the attribute, the wire and this key
|
|
796
|
+
are one vocabulary. All four operate on a list, because each needs a key and
|
|
797
|
+
a key exists only on a live row.
|
|
798
|
+
|
|
799
|
+
| `lb-action` | Key under `crud` | `where` carries |
|
|
800
|
+
|------------------|------------------|------------------------|
|
|
801
|
+
| `lb-cell-change` | `cellChange` | `key`, `cell`, `value` |
|
|
802
|
+
| `lb-row-delete` | `rowDelete` | `key` |
|
|
803
|
+
| `lb-row-insert` | `rowInsert` | `values` |
|
|
804
|
+
| `lb-row-update` | `rowUpdate` | `key`, `values` |
|
|
805
|
+
|
|
806
|
+
## Widgets
|
|
807
|
+
|
|
808
|
+
---- UNEDITED ----
|
|
809
|
+
|
|
810
|
+
A widget is an HTML custom element following these fixed rules:
|
|
811
|
+
- Element name must contain a hyphen, as per hTML rules, like <my-custom-element>
|
|
812
|
+
- HTML code, if present, is `my-custom-element.html`
|
|
813
|
+
- TS code, if present, is in `my-custom-element.browser.ts`. The
|
|
814
|
+
'browser' segment is a safety feature, requiring the file to be
|
|
815
|
+
explicitly named as a browser file, to help prevent unfortunate
|
|
816
|
+
naming collisions where a server file happens to have the name of
|
|
817
|
+
a widget and gets built into the browser bundle.
|
|
818
|
+
|
|
819
|
+
A widget must have either one or the other of HTML and Typescript, and
|
|
820
|
+
it may have both. If it has neither, the builder reports an error.
|
|
821
|
+
|
|
822
|
+
To use a widget from a library, add the library to `imports.ts` anywhere
|
|
823
|
+
in the builders `--src`:
|
|
824
|
+
|
|
825
|
+
```ts
|
|
826
|
+
export default ["@scope/library-name"];
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### Widget authoring
|
|
830
|
+
|
|
831
|
+
---- UNEDITED ----
|
|
832
|
+
|
|
833
|
+
The expansion grammar, `exp-`, `lb-slot`, `lb-template`, the reserved `LB-`
|
|
834
|
+
namespace, then `lbPlaceRow` and `lbRowsLanded`. One bucket spanning build
|
|
835
|
+
time and run time, because a widget is a definition and a script together.
|
|
836
|
+
|
|
837
|
+
#### Request state
|
|
838
|
+
|
|
839
|
+
The hub stamps these on the element that dispatched a request, which is the
|
|
840
|
+
widget itself when a widget fired it. A widget observes them and reacts; it
|
|
841
|
+
must name them in `observedAttributes` to see them change.
|
|
842
|
+
|
|
843
|
+
| Attribute | Written on | Holds |
|
|
844
|
+
|----------------|-------------------------|-------------------------------------------------|
|
|
845
|
+
| `lb-pending` | The dispatching element | The round trip is in flight |
|
|
846
|
+
| `lb-error` | The dispatching element | The last round trip failed, cleared on the next — see [The round trip](#the-round-trip) |
|
|
847
|
+
| `lb-row-count` | A list scope | How many rows the scope is showing |
|
|
848
|
+
|
|
849
|
+
The hub writes all three and never reads them. They are also stamped on
|
|
850
|
+
plain HTML, where a stylesheet is the only consumer: dim a pending button,
|
|
851
|
+
mark a failed one, and style an empty list against `lb-row-count` rather than
|
|
852
|
+
carrying an empty-state element.
|
|
853
|
+
|
|
854
|
+
#### Row hooks
|
|
855
|
+
|
|
856
|
+
| Name | Implemented By | Behavior |
|
|
857
|
+
|---------------------------------|----------------|-------------------------------------------------------------|
|
|
858
|
+
| `applyRow(root, row)` | Loadbare | Fills one scope from one row |
|
|
859
|
+
| `lbPlaceRow(el, row, template)` | Developer | Optional on a list scope: where a row goes |
|
|
860
|
+
| `lbRowsLanded()` | Developer | Optional on a list scope: scaffolding derived from the rows |
|
|
861
|
+
|
|
862
|
+
## Internal linkage
|
|
863
|
+
|
|
864
|
+
---- UNEDITED ----
|
|
865
|
+
|
|
866
|
+
The endpoint path, `index` for the bare path, the constant holding the
|
|
867
|
+
request deadline, the generated client entry, and the `./build` export.
|
|
868
|
+
Nobody outside this package reads any of it. The deadline itself is
|
|
869
|
+
behavior an application sees — see [The round trip](#the-round-trip) — and
|
|
870
|
+
giving it a knob later takes nothing away.
|
|
871
|
+
|
|
872
|
+
## Cross-reference
|
|
873
|
+
|
|
874
|
+
Every name Loadbare/app owns, in one place. Each row names the section
|
|
875
|
+
that defines it. The definition lives there and only there.
|
|
876
|
+
|
|
877
|
+
### Builder flags
|
|
878
|
+
|
|
879
|
+
Loadbare/app owns every flag in this table.
|
|
880
|
+
|
|
881
|
+
| Flag | Default | Names | Defined in |
|
|
882
|
+
|------------|---------|-------------------------------------------|---------------------------------------------------|
|
|
883
|
+
| `--src` | `src` | The application's own tree, scanned whole | [What the builder scans](#what-the-builder-scans) |
|
|
884
|
+
| `--out` | `dist` | Where the builder writes | [Running the builder](#running-the-builder) |
|
|
885
|
+
| `--watch` | off | Rebuild on change under `--src` | [Running the builder](#running-the-builder) |
|
|
886
|
+
| `--minify` | off | Minify `client.js` and `app.css` | [Running the builder](#running-the-builder) |
|
|
887
|
+
|
|
888
|
+
### Reserved file names
|
|
889
|
+
|
|
890
|
+
Loadbare/app owns every file name and pattern in this table. A file so
|
|
891
|
+
named carries its meaning wherever it sits in the `--src` tree, so an
|
|
892
|
+
application must not use one of these names for anything else.
|
|
893
|
+
|
|
894
|
+
| File | How many | Holds | Defined in |
|
|
895
|
+
|-------------------------|------------------------|----------------------------------------------------------|---------------------------------------------------------|
|
|
896
|
+
| `chrome.html` | Exactly one | The application's one HTML document | [Chrome](#chrome) |
|
|
897
|
+
| `imports.ts` | Zero or one | Default-exports an array of widget library package names | [Imported widget libraries](#imported-widget-libraries) |
|
|
898
|
+
| `<stub>.page.html` | One per page | One page's markup, as a fragment | [Pages](#pages) |
|
|
899
|
+
| `<stub>.queries.ts` | Zero or one per page | That page's queries | [Page Queries](#page-queries) |
|
|
900
|
+
| `<stub>.requests.ts` | Zero or one per page | That page's requests | [Page Requests](#page-requests) |
|
|
901
|
+
| `<tag-name>.html` | Zero or one per widget | One widget definition, as a fragment | [Widgets](#widgets) |
|
|
902
|
+
| `<tag-name>.browser.ts` | Zero or one per widget | One widget's script, `.js` also accepted | [Widgets](#widgets) |
|
|
903
|
+
| `*.css` | Any number | A stylesheet, concatenated into `app.css` | [Running the builder](#running-the-builder) |
|
|
904
|
+
|
|
905
|
+
### Build outputs
|
|
906
|
+
|
|
907
|
+
Loadbare/app owns every file name in this table. The builder writes them
|
|
908
|
+
into `--out`. Which of them exist depends on the application: `app.css`
|
|
909
|
+
only when some origin has a stylesheet, `pages.ts` only when some page has a
|
|
910
|
+
`.queries.ts` or a `.requests.ts`.
|
|
911
|
+
|
|
912
|
+
| Path | Written | Holds | Read by | Defined in |
|
|
913
|
+
|--------------------|-------------------|--------------------------------------------------------------------|------------|---------------------------------------------|
|
|
914
|
+
| `/app.html` | Always | The chrome, built | The server | [Chrome](#chrome) |
|
|
915
|
+
| `/client.js` | Always | `<lb-hub>` and every widget the application uses | The chrome | [Chrome](#chrome) |
|
|
916
|
+
| `/client-entry.ts` | Always | The generated entry `client.js` is bundled from | Nothing | [Running the builder](#running-the-builder) |
|
|
917
|
+
| `/app.css` | With a stylesheet | Every stylesheet in the cascade, concatenated | The chrome | [Chrome](#chrome) |
|
|
918
|
+
| `/pages.ts` | With page data | The one `createHub()` call, over every page's queries and requests | The server | [The Express server](#the-express-server) |
|
|
919
|
+
|
|
920
|
+
### Reserved package.json keys
|
|
921
|
+
|
|
922
|
+
Loadbare/app owns every key in this table. A widget library declares them.
|
|
923
|
+
An application never does.
|
|
924
|
+
|
|
925
|
+
| Key | Value | Means | Defined in |
|
|
926
|
+
|--------------------|---------------------------|------------------------------------------------------------------------------|---------------------------------------------------------|
|
|
927
|
+
| `loadbare.widgets` | A path inside the package | Where this package's widgets are. Absent means the whole installed directory | [Imported widget libraries](#imported-widget-libraries) |
|
|
928
|
+
|
|
929
|
+
### Reserved namespaces
|
|
930
|
+
|
|
931
|
+
Loadbare/app owns every namespace in this table. Ownership of a name and
|
|
932
|
+
ownership of its behavior are stated separately, because they differ.
|
|
933
|
+
|
|
934
|
+
| Namespace | Applies to | Loadbare owns | Defined in |
|
|
935
|
+
|-----------|----------------------------|----------------------------------------------|---------------------------------------------------|
|
|
936
|
+
| `lb-*` | HTML attributes | The names and their behavior | [The lb-* namespace](#the-lb--namespace) |
|
|
937
|
+
| `exp-*` | HTML attributes | The behavior; widget authors pick the values | [Build time parameters](#build-time-parameters) |
|
|
938
|
+
| `lb*` | Methods on custom elements | The names and their behavior | [Row hooks](#row-hooks) |
|
|
939
|
+
|
|
940
|
+
#### The lb-* namespace
|
|
941
|
+
|
|
942
|
+
Every HTML attribute beginning with `lb-` belongs to Loadbare/app. An
|
|
943
|
+
application writes the ones this table names and invents none of its own,
|
|
944
|
+
because a name Loadbare has not defined today it may define tomorrow.
|
|
945
|
+
|
|
946
|
+
The prefix reaches past HTML. A query name cannot begin with `lb-` either,
|
|
947
|
+
which is what keeps the four operations apart from an application's own
|
|
948
|
+
actions on the wire — see [Page Queries](#page-queries).
|
|
949
|
+
|
|
950
|
+
Each attribute is defined in one section, and this table says which.
|
|
951
|
+
|
|
952
|
+
| Attribute | Written by | Defined in |
|
|
953
|
+
|------------------|-------------|---------------------------------------------------|
|
|
954
|
+
| `lb-list` | Developer | [Binding](#binding) |
|
|
955
|
+
| `lb-row` | Developer | [Binding](#binding) |
|
|
956
|
+
| `lb-cell` | Developer | [Binding](#binding) |
|
|
957
|
+
| `lb-key` | Developer | [Binding](#binding) |
|
|
958
|
+
| `lb-key-value` | Hub | [Binding](#binding) |
|
|
959
|
+
| `lb-value` | Hub | [How a value lands](#how-a-value-lands) |
|
|
960
|
+
| `lb-action` | Developer | [Requests](#requests) |
|
|
961
|
+
| `lb-nav-link` | Developer | [Links](#links) |
|
|
962
|
+
| `lb-pending` | Hub | [Request state](#request-state) |
|
|
963
|
+
| `lb-error` | Hub | [Request state](#request-state) |
|
|
964
|
+
| `lb-row-count` | Hub | [Request state](#request-state) |
|
|
965
|
+
| `lb-unknown-page`| Developer | [Chrome](#chrome) |
|
|
966
|
+
| `lb-slot` | Widget author | [Slots and templates](#slots-and-templates) |
|
|
967
|
+
| `lb-template` | Widget author | [Slots and templates](#slots-and-templates) |
|
|
968
|
+
| `lb-page` | Builder | [Pages](#pages) |
|
|
969
|
+
|
|
970
|
+
An attribute the builder does not recognize is left alone today. Refusing
|
|
971
|
+
one is on the list of validations still to land — see
|
|
972
|
+
[The builder](#the-builder).
|
|
973
|
+
|
|
974
|
+
### Reserved tags
|
|
975
|
+
|
|
976
|
+
Loadbare/app owns every tag in this table. An application writes them and
|
|
977
|
+
never defines them.
|
|
978
|
+
|
|
979
|
+
| Tag | Written in | Means | Defined in |
|
|
980
|
+
|------------|------------|--------------------------------|-------------------|
|
|
981
|
+
| `<lb-hub>` | The chrome | The application's live element | [Chrome](#chrome) |
|
|
982
|
+
|
|
983
|
+
### Reserved events
|
|
984
|
+
|
|
985
|
+
Loadbare/app owns every DOM event in this table, both the name and what its
|
|
986
|
+
`detail` carries.
|
|
987
|
+
|
|
988
|
+
| Event | Dispatched from | Bubbles | Cancelable | Defined in |
|
|
989
|
+
|--------------|------------------------|---------|------------|-----------------------------------------|
|
|
990
|
+
| `lb-request` | The element that acted | Yes | No | [The request event](#the-request-event) |
|
|
991
|
+
|
|
992
|
+
### Reserved attributes
|
|
993
|
+
|
|
994
|
+
Loadbare/app owns every attribute in this table, both the name and its
|
|
995
|
+
behavior.
|
|
996
|
+
|
|
997
|
+
| Attribute | Written on | Takes a value | Defined in |
|
|
998
|
+
|-------------------|-------------------------------------|---------------|-------------------|
|
|
999
|
+
| `lb-unknown-page` | A `<dialog>` in chrome | No | [Chrome](#chrome) |
|
|
1000
|
+
| `lb-page` | A `<template>`, by the builder only | Yes | [Pages](#pages) |
|