@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.
- package/README.md +17 -17
- package/dist/build/assemble.d.ts +7 -1
- package/dist/build/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +81 -11
- package/dist/build/assemble.js.map +1 -1
- package/dist/build/cli.d.ts +3 -0
- package/dist/build/cli.d.ts.map +1 -1
- package/dist/build/cli.js +6 -1
- package/dist/build/cli.js.map +1 -1
- package/dist/build/declared.d.ts +25 -0
- package/dist/build/declared.d.ts.map +1 -0
- package/dist/build/declared.js +62 -0
- package/dist/build/declared.js.map +1 -0
- package/dist/build/pages.d.ts +6 -0
- package/dist/build/pages.d.ts.map +1 -1
- package/dist/build/pages.js +32 -12
- package/dist/build/pages.js.map +1 -1
- package/docs/comparison.md +697 -484
- package/docs/departures.md +63 -0
- package/docs/miswarts.md +140 -0
- package/docs/opinions.md +155 -0
- package/docs/reference/builder.md +20 -4
- package/docs/reference/chrome.md +45 -10
- package/docs/reference/css.md +0 -1
- package/docs/reference/custom-elements.md +3 -25
- package/docs/reference/data-binding.md +265 -48
- package/docs/reference/names.md +250 -0
- package/docs/reference/overview.md +6 -0
- package/docs/reference/page-files.md +77 -9
- package/docs/reference/server.md +54 -1
- package/docs/roadmap.md +55 -43
- package/docs/testing.md +62 -28
- package/docs/theory.md +348 -108
- package/docs/tutorials/040-displaying-data.md +1 -1
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +19 -13
- package/skills/loadbare-app/references/builder.md +20 -4
- package/skills/loadbare-app/references/chrome.md +45 -10
- package/skills/loadbare-app/references/css.md +0 -1
- package/skills/loadbare-app/references/custom-elements.md +3 -25
- package/skills/loadbare-app/references/data-binding.md +265 -48
- package/skills/loadbare-app/references/miswarts.md +140 -0
- package/skills/loadbare-app/references/names.md +250 -0
- package/skills/loadbare-app/references/overview.md +6 -0
- package/skills/loadbare-app/references/page-files.md +77 -9
- package/skills/loadbare-app/references/server.md +54 -1
- package/docs/TECHREF-1.0.md +0 -2312
- package/docs/possible-ideas.md +0 -314
- package/docs/theory-old.md +0 -839
- package/docs/what-does-loadbare-extend.md +0 -124
- package/skills/loadbare-app/references/TECHREF-1.0.md +0 -2312
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# Names
|
|
2
|
+
|
|
3
|
+
Every name Loadbare/app owns, in one place. Each row links to the section
|
|
4
|
+
that defines it, and the definition lives there and only there.
|
|
5
|
+
|
|
6
|
+
An application names nothing with the `lb-` prefix: no attribute, query,
|
|
7
|
+
column, request, command, custom element or event. A name Loadbare has not
|
|
8
|
+
defined today it may define tomorrow. `createHub` refuses at startup a page
|
|
9
|
+
that declares a query, a handler or a `crud` entry whose name begins with
|
|
10
|
+
`lb-`, and the builder refuses the rest; see
|
|
11
|
+
[The markup checks](./data-binding.md#the-markup-checks).
|
|
12
|
+
|
|
13
|
+
## Builder flags
|
|
14
|
+
|
|
15
|
+
| Flag | Default | Names | Defined in |
|
|
16
|
+
| ---------- | ------- | ----------------------------------------- | --------------------------------------------------- |
|
|
17
|
+
| `--src` | `src` | The application's own tree, scanned whole | [Running the builder](./builder.md#running-the-builder) |
|
|
18
|
+
| `--out` | `dist` | Where the builder writes | [Running the builder](./builder.md#running-the-builder) |
|
|
19
|
+
| `--watch` | off | Rebuild on change under `--src` | [Running the builder](./builder.md#running-the-builder) |
|
|
20
|
+
| `--minify` | off | Minify `client.js` and `app.css` | [Running the builder](./builder.md#running-the-builder) |
|
|
21
|
+
|
|
22
|
+
## Reserved file names
|
|
23
|
+
|
|
24
|
+
A file so named carries its meaning wherever it sits in the `--src` tree,
|
|
25
|
+
so an application uses none of these names for anything else.
|
|
26
|
+
|
|
27
|
+
| File | How many | Holds | Defined in |
|
|
28
|
+
| ----------------------- | ------------------------------ | -------------------------------------------------------- | --------------------------------------------------- |
|
|
29
|
+
| `chrome.html` | Exactly one | The application's one HTML document | [chrome.html](./chrome.md) |
|
|
30
|
+
| `imports.ts` | Zero or one | Default-exports an array of widget package names | [Widgets from packages](./builder.md#widgets-from-packages) |
|
|
31
|
+
| `<stub>.page.html` | One per page | One page's markup, as a fragment | [HTML](./page-files.md#html) |
|
|
32
|
+
| `<stub>.queries.ts` | Zero or one per page | That page's queries | [Queries](./page-files.md#queries) |
|
|
33
|
+
| `<stub>.requests.ts` | Zero or one per page | That page's requests | [Requests](./page-files.md#requests) |
|
|
34
|
+
| `<tag-name>.html` | Zero or one per custom element | One element file, as a fragment | [HTML](./custom-elements.md#html) |
|
|
35
|
+
| `<tag-name>.browser.ts` | Zero or one per custom element | One custom element's script, `.js` also accepted | [Code](./custom-elements.md#code) |
|
|
36
|
+
| `*.css` | Any number | A stylesheet, concatenated into `app.css` | [CSS](./css.md) |
|
|
37
|
+
|
|
38
|
+
## Build outputs
|
|
39
|
+
|
|
40
|
+
The builder writes these into `--out`.
|
|
41
|
+
|
|
42
|
+
| File | Written | Holds | Defined in |
|
|
43
|
+
| ----------------- | ----------------- | ------------------------------------------------------------------ | ------------------------------------------------------- |
|
|
44
|
+
| `app.html` | Always | The chrome, with every page inside a `<template>` | [What the builder writes](./builder.md#what-the-builder-writes) |
|
|
45
|
+
| `client.js` | Always | `<lb-hub>` and every custom element the application uses | [What the builder writes](./builder.md#what-the-builder-writes) |
|
|
46
|
+
| `client-entry.ts` | Always | The generated entry `client.js` is bundled from | [What the builder writes](./builder.md#what-the-builder-writes) |
|
|
47
|
+
| `app.css` | With a stylesheet | Every stylesheet in the cascade, concatenated | [CSS](./css.md) |
|
|
48
|
+
| `pages.ts` | With page data | The one `createHub()` call, over every page's queries and requests | [Running the server](./server.md#running-the-server) |
|
|
49
|
+
|
|
50
|
+
## Reserved package.json keys
|
|
51
|
+
|
|
52
|
+
A widget package declares these. An application never does.
|
|
53
|
+
|
|
54
|
+
| Key | Value | Means | Defined in |
|
|
55
|
+
| ------------------ | ------------------------- | ------------------------------------------------------------------ | --------------------------------------------------- |
|
|
56
|
+
| `loadbare.widgets` | A path inside the package | Where its custom elements are; absent means the whole installed directory | [Widgets from packages](./builder.md#widgets-from-packages) |
|
|
57
|
+
|
|
58
|
+
## Reserved namespaces
|
|
59
|
+
|
|
60
|
+
| Namespace | Applies to | Loadbare owns | Defined in |
|
|
61
|
+
| --------- | ----------------------------------------------------------- | ------------------------------------------------- | ------------------------------------------------ |
|
|
62
|
+
| `lb-*` | Attributes, their values, queries, columns of `lb-` queries, requests, custom elements, events | The names and their behavior | [Attributes](#attributes) |
|
|
63
|
+
| `--lb-*` | Values of HTML's `command` attribute | The names and their behavior | [Reserved commands](#reserved-commands) |
|
|
64
|
+
| `exp-*` | Attributes on a custom element | The behavior; element file authors pick the names | [Parameters](./custom-elements.md#parameters) |
|
|
65
|
+
| `lb*` | Methods on custom elements: `lb` and a capital | The names and their behavior | [Reserved methods](#reserved-methods) |
|
|
66
|
+
|
|
67
|
+
## Attributes
|
|
68
|
+
|
|
69
|
+
The developer writes sixteen attributes in pages and the chrome, and the
|
|
70
|
+
builder refuses any other `lb-` attribute there. `lb-exp-slot` and
|
|
71
|
+
`lb-exp-template` are written for expansion, which removes them before the
|
|
72
|
+
check. The hub and the builder write the rest, as stamps, and script never
|
|
73
|
+
assigns one.
|
|
74
|
+
|
|
75
|
+
| Attribute | Written by | Defined in |
|
|
76
|
+
| -------------------- | --------------- | ---------------------------------------------------------------- |
|
|
77
|
+
| `lb-query` | Developer | [Landing](./data-binding.md#landing) |
|
|
78
|
+
| `lb-column` | Developer | [Landing](./data-binding.md#landing) |
|
|
79
|
+
| `lb-show` | Developer | [Conditional rendering](./data-binding.md#conditional-rendering) |
|
|
80
|
+
| `lb-order` | Developer | [Order](./data-binding.md#order) |
|
|
81
|
+
| `lb-filter` | Developer | [Filters](./data-binding.md#filters) |
|
|
82
|
+
| `lb-group` | Developer | [Groups](./data-binding.md#groups) |
|
|
83
|
+
| `lb-count`, `lb-sum`, `lb-avg`, `lb-min`, `lb-max` | Developer | [Aggregates](./data-binding.md#aggregates) |
|
|
84
|
+
| `lb-select` | Developer | [Selecting rows](./data-binding.md#selecting-rows) |
|
|
85
|
+
| `lb-request` | Developer | [Requests](./data-binding.md#requests) |
|
|
86
|
+
| `lb-url-link` | Developer | [Links](./chrome.md#links) |
|
|
87
|
+
| `lb-url-push` | Developer | [Query parms](./chrome.md#query-parms) |
|
|
88
|
+
| `lb-url-unknown` | Developer | [An unknown page](./chrome.md#an-unknown-page) |
|
|
89
|
+
| `lb-exp-slot` | Developer, consumed by expansion | [The slot](./custom-elements.md#the-slot) |
|
|
90
|
+
| `lb-exp-template` | Developer, consumed by expansion | [Destinations](./custom-elements.md#destinations) |
|
|
91
|
+
| `lb-column-value` | Hub | [Where a column lands](./data-binding.md#where-a-column-lands) |
|
|
92
|
+
| `lb-key-value` | Hub | [Landing](./data-binding.md#landing), [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
|
|
93
|
+
| `lb-row-live` | Hub | [Landing](./data-binding.md#landing) |
|
|
94
|
+
| `lb-query-row-count` | Hub | [Counting rows](./data-binding.md#counting-rows) |
|
|
95
|
+
| `lb-group-live`, `lb-group-column`, `lb-group-value` | Hub | [Groups](./data-binding.md#groups) |
|
|
96
|
+
| `lb-row-created`, `lb-row-changed`, `lb-row-moved`, `lb-row-requested` | Hub | [What happened to a row](./data-binding.md#what-happened-to-a-row) |
|
|
97
|
+
| `lb-row-leaving`, `lb-group-leaving` | Hub | [What happened to a row](./data-binding.md#what-happened-to-a-row) |
|
|
98
|
+
| `lb-row-editing` | Hub | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
|
|
99
|
+
| `lb-row-selected` | Hub | [Selecting rows](./data-binding.md#selecting-rows) |
|
|
100
|
+
| `lb-request-pending` | Hub | [Request state](./data-binding.md#request-state) |
|
|
101
|
+
| `lb-request-error` | Hub | [Request state](./data-binding.md#request-state) |
|
|
102
|
+
| `lb-request-refused` | Hub | [Refusals](./data-binding.md#refusals) |
|
|
103
|
+
| `lb-show` on a `<template>` | Hub and builder | [Where an absent element is](./data-binding.md#where-an-absent-element-is) |
|
|
104
|
+
| `lb-page` | Builder | [What the builder writes](./builder.md#what-the-builder-writes) |
|
|
105
|
+
| `lb-page-title` | Builder | [The page title](./page-files.md#the-page-title) |
|
|
106
|
+
|
|
107
|
+
## Reserved queries
|
|
108
|
+
|
|
109
|
+
The hub serves these in the browser. Every page and the chrome may name
|
|
110
|
+
them, and a page declares neither.
|
|
111
|
+
|
|
112
|
+
| Query | Kind | Key | Columns | Defined in |
|
|
113
|
+
| ------------ | ----- | --------- | --------------------------------------------------------------- | ------------------------------------- |
|
|
114
|
+
| `lb-url` | `row` | `lb-path` | `lb-path`, `lb-page-label`, `lb-page-unknown`, every query parm | [The URL](./chrome.md#the-url) |
|
|
115
|
+
| `lb-choices` | `row` | `lb-path` | `lb-path`, every choice | [Choices](./data-binding.md#choices) |
|
|
116
|
+
|
|
117
|
+
No query parm is reserved: a column of `lb-url` whose name begins with
|
|
118
|
+
`lb-` is never a query parm.
|
|
119
|
+
|
|
120
|
+
## Reserved request names
|
|
121
|
+
|
|
122
|
+
| Request name | Runs under `crud` | Defined in |
|
|
123
|
+
| --------------- | ----------------- | --------------------------------------------------------------------------- |
|
|
124
|
+
| `lb-row-insert` | `rowInsert` | [Inserting, updating and deleting rows](./data-binding.md#inserting-updating-and-deleting-rows) |
|
|
125
|
+
| `lb-row-update` | `rowUpdate` | [Inserting, updating and deleting rows](./data-binding.md#inserting-updating-and-deleting-rows) |
|
|
126
|
+
| `lb-row-delete` | `rowDelete` | [Inserting, updating and deleting rows](./data-binding.md#inserting-updating-and-deleting-rows) |
|
|
127
|
+
|
|
128
|
+
## Reserved commands
|
|
129
|
+
|
|
130
|
+
Loadbare/app owns every value of HTML's `command` attribute beginning with
|
|
131
|
+
`--lb-`.
|
|
132
|
+
|
|
133
|
+
| Command | Defined in |
|
|
134
|
+
| -------------- | ------------------------------------------------------------------------ |
|
|
135
|
+
| `--lb-edit` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
|
|
136
|
+
| `--lb-new` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
|
|
137
|
+
| `--lb-release` | [Editing a row in one form](./data-binding.md#editing-a-row-in-one-form) |
|
|
138
|
+
|
|
139
|
+
## Reserved tags
|
|
140
|
+
|
|
141
|
+
An application writes these and never defines them.
|
|
142
|
+
|
|
143
|
+
| Tag | Written in | Means | Defined in |
|
|
144
|
+
| ---------- | ---------- | ------------------------------ | -------------------------------------------------- |
|
|
145
|
+
| `<lb-hub>` | The chrome | The application's live element | [The parts of a chrome](./chrome.md#the-parts-of-a-chrome) |
|
|
146
|
+
|
|
147
|
+
## Reserved events
|
|
148
|
+
|
|
149
|
+
Loadbare/app owns each event's name and what its `detail` carries.
|
|
150
|
+
|
|
151
|
+
| Event | Dispatched from | Bubbles | Cancelable | Defined in |
|
|
152
|
+
| ----------------- | -------------------------- | ------- | ---------- | ----------------------------------------------------------- |
|
|
153
|
+
| `lb-request` | The element that committed | Yes | No | [The request event](./data-binding.md#the-request-event) |
|
|
154
|
+
| `lb-request-done` | The element that committed | Yes | No | [Hearing how it turned out](./custom-elements.md#hearing-how-it-turned-out) |
|
|
155
|
+
|
|
156
|
+
## Reserved methods
|
|
157
|
+
|
|
158
|
+
The build refuses a custom element script declaring any other method,
|
|
159
|
+
accessor or field named `lb` and a capital.
|
|
160
|
+
|
|
161
|
+
| Method | On | Defined in |
|
|
162
|
+
| --------------- | ------------------------------------------------------ | ----------------------------------------------------------- |
|
|
163
|
+
| `lbAttached` | Any custom element the hub places | [When its code runs](./custom-elements.md#when-its-code-runs) |
|
|
164
|
+
| `lbRowsLanded` | A custom element with `lb-query` and a row template | [Holding rows](./custom-elements.md#holding-rows) |
|
|
165
|
+
| `lbRequestDone` | A custom element that committed, or holds one that did | [Hearing how it turned out](./custom-elements.md#hearing-how-it-turned-out) |
|
|
166
|
+
| `lbDetached` | Any custom element the hub removes | [When its code runs](./custom-elements.md#when-its-code-runs) |
|
|
167
|
+
|
|
168
|
+
## Exports
|
|
169
|
+
|
|
170
|
+
| Entry point | Exports | Defined in |
|
|
171
|
+
| -------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- |
|
|
172
|
+
| `@loadbare/app/server` | `row`, `rows`, `patch`, `url`, `choices`, `refusal`, `createHub`, and the types `Queries`, `Requests`, `HubContext`, `Hub` | [Page Files](./page-files.md), [The data channel](./server.md#the-data-channel) |
|
|
173
|
+
| `@loadbare/app/express` | `hubRoutes` | [The Express Server](./server.md) |
|
|
174
|
+
| `@loadbare/app/types` | `HubRequest`, `ResponseItem`, `HubRequestDone`, `HubLifecycle`, and the rules the hub applies: `readAs`, `parseFilter`, `passes`, `passesTerm` | [The data channel](./server.md#the-data-channel), [Order](./data-binding.md#order), [Filters](./data-binding.md#filters) |
|
|
175
|
+
| `@loadbare/app` | `applyRow` | [Filling a subtree by hand](./custom-elements.md#filling-a-subtree-by-hand) |
|
|
176
|
+
| `@loadbare/app/constants` | Every name on this page; see [Constants](#constants) | [Code](./custom-elements.md#code) |
|
|
177
|
+
|
|
178
|
+
## Constants
|
|
179
|
+
|
|
180
|
+
`@loadbare/app/constants` exports every name above. Custom element code
|
|
181
|
+
imports them rather than writing a string.
|
|
182
|
+
|
|
183
|
+
| Constant | Value |
|
|
184
|
+
| ------------------------- | --------------------------------------- |
|
|
185
|
+
| `ATTR_QUERY` | `lb-query` |
|
|
186
|
+
| `ATTR_COLUMN` | `lb-column` |
|
|
187
|
+
| `ATTR_SHOW` | `lb-show` |
|
|
188
|
+
| `SHOW_NOT` | `!`, written before a column in `lb-show` |
|
|
189
|
+
| `ATTR_COLUMN_VALUE` | `lb-column-value` |
|
|
190
|
+
| `ATTR_KEY_VALUE` | `lb-key-value` |
|
|
191
|
+
| `ATTR_ROW_LIVE` | `lb-row-live` |
|
|
192
|
+
| `LIVE_ROW` | `[lb-row-live]`, the selector for a live row |
|
|
193
|
+
| `ATTR_QUERY_ROW_COUNT` | `lb-query-row-count` |
|
|
194
|
+
| `ATTR_GROUP` | `lb-group` |
|
|
195
|
+
| `ATTR_GROUP_LIVE` | `lb-group-live` |
|
|
196
|
+
| `ATTR_GROUP_COLUMN` | `lb-group-column` |
|
|
197
|
+
| `ATTR_GROUP_VALUE` | `lb-group-value` |
|
|
198
|
+
| `LIVE_GROUP` | `[lb-group-live]`, the selector for a group's element |
|
|
199
|
+
| `ATTR_COUNT`, `ATTR_SUM`, `ATTR_AVG`, `ATTR_MIN`, `ATTR_MAX` | `lb-count`, `lb-sum`, `lb-avg`, `lb-min`, `lb-max` |
|
|
200
|
+
| `AGGREGATES` | All five aggregate attributes |
|
|
201
|
+
| `ORDER_DESCENDING` | `-`, written before a column in an order |
|
|
202
|
+
| `COLUMN_TYPES` | `number`, `boolean`, `text` |
|
|
203
|
+
| `CHOICES_QUERY` | `lb-choices` |
|
|
204
|
+
| `ATTR_ORDER` | `lb-order` |
|
|
205
|
+
| `ATTR_FILTER` | `lb-filter` |
|
|
206
|
+
| `FILTER_OPERATORS` | Every filter operator, the longer first |
|
|
207
|
+
| `ATTR_ROW_CREATED` | `lb-row-created` |
|
|
208
|
+
| `ATTR_ROW_CHANGED` | `lb-row-changed` |
|
|
209
|
+
| `ATTR_ROW_MOVED` | `lb-row-moved` |
|
|
210
|
+
| `ATTR_ROW_REQUESTED` | `lb-row-requested` |
|
|
211
|
+
| `REQUESTED_CREATED`, `REQUESTED_CHANGED`, `REQUESTED_MOVED` | `created`, `changed`, `moved` |
|
|
212
|
+
| `ATTR_ROW_LEAVING` | `lb-row-leaving` |
|
|
213
|
+
| `ATTR_GROUP_LEAVING` | `lb-group-leaving` |
|
|
214
|
+
| `ATTR_REQUEST` | `lb-request` |
|
|
215
|
+
| `LB_EVENT_NAME` | `lb-request`, the event every request travels as |
|
|
216
|
+
| `LB_DONE_EVENT_NAME` | `lb-request-done` |
|
|
217
|
+
| `ATTR_REQUEST_PENDING` | `lb-request-pending` |
|
|
218
|
+
| `ATTR_REQUEST_ERROR` | `lb-request-error` |
|
|
219
|
+
| `ATTR_REQUEST_REFUSED` | `lb-request-refused` |
|
|
220
|
+
| `LB_REFUSAL_STATUS` | `422` |
|
|
221
|
+
| `COMMAND_EDIT`, `COMMAND_NEW`, `COMMAND_RELEASE` | `--lb-edit`, `--lb-new`, `--lb-release` |
|
|
222
|
+
| `LB_COMMANDS` | All three commands |
|
|
223
|
+
| `LB_COMMAND_PREFIX` | `--lb-` |
|
|
224
|
+
| `ATTR_ROW_EDITING` | `lb-row-editing` |
|
|
225
|
+
| `ATTR_SELECT` | `lb-select` |
|
|
226
|
+
| `ATTR_ROW_SELECTED` | `lb-row-selected` |
|
|
227
|
+
| `LB_RESERVED_PREFIX` | `lb-` |
|
|
228
|
+
| `REQUEST_ROW_INSERT` | `lb-row-insert` |
|
|
229
|
+
| `REQUEST_ROW_UPDATE` | `lb-row-update` |
|
|
230
|
+
| `REQUEST_ROW_DELETE` | `lb-row-delete` |
|
|
231
|
+
| `LB_ROW_REQUESTS` | All three request names |
|
|
232
|
+
| `LB_METHODS` | The four reserved methods |
|
|
233
|
+
| `BROWSER_LIFECYCLE` | The browser callbacks the build refuses |
|
|
234
|
+
| `KIND_ROW` | `row` |
|
|
235
|
+
| `KIND_ROWS` | `rows` |
|
|
236
|
+
| `URL_QUERY` | `lb-url` |
|
|
237
|
+
| `URL_COLUMN_PATH` | `lb-path` |
|
|
238
|
+
| `URL_COLUMN_PAGE_LABEL` | `lb-page-label` |
|
|
239
|
+
| `URL_COLUMN_PAGE_UNKNOWN` | `lb-page-unknown` |
|
|
240
|
+
| `ATTR_URL_LINK` | `lb-url-link` |
|
|
241
|
+
| `ATTR_URL_PUSH` | `lb-url-push` |
|
|
242
|
+
| `ATTR_URL_UNKNOWN` | `lb-url-unknown` |
|
|
243
|
+
| `ATTR_EXP_SLOT` | `lb-exp-slot` |
|
|
244
|
+
| `ATTR_EXP_TEMPLATE` | `lb-exp-template` |
|
|
245
|
+
| `ATTR_PAGE` | `lb-page` |
|
|
246
|
+
| `ATTR_PAGE_TITLE` | `lb-page-title` |
|
|
247
|
+
| `DEVELOPER_ATTRIBUTES` | The sixteen attributes a developer writes |
|
|
248
|
+
| `HUB_TAG_NAME` | `lb-hub` |
|
|
249
|
+
| `LB_ENDPOINT` | `/lb` |
|
|
250
|
+
| `LB_REQUEST_TIMEOUT_MS` | `10000` |
|
|
@@ -38,3 +38,9 @@ serves, and the custom elements those pages are made of.
|
|
|
38
38
|
| [`<tag-name>.html`](./custom-elements.md#html) | The markup the tag expands into |
|
|
39
39
|
| [`<tag-name>.browser.ts`](./custom-elements.md#code) | The class the tag registers |
|
|
40
40
|
| [The Basic Widget Library](./widgets.md) | `@loadbare/widgets` |
|
|
41
|
+
|
|
42
|
+
## Names
|
|
43
|
+
|
|
44
|
+
| Topic | Description |
|
|
45
|
+
|--------------------------|--------------------------------------------------------------|
|
|
46
|
+
| [Names](./names.md) | Every name Loadbare owns, the constants, and where each is defined |
|
|
@@ -14,7 +14,9 @@ Name the landing page `index.page.html`. A bare `/` resolves to `index`, and
|
|
|
14
14
|
every other path names the page whose stub it is.
|
|
15
15
|
|
|
16
16
|
Put the files anywhere under `src/`. The builder pairs them by stub, not by
|
|
17
|
-
directory; `src/pages/` is the convention.
|
|
17
|
+
directory; `src/pages/` is the convention. The page namespace is flat, so
|
|
18
|
+
`src/deep/path/mypage.page.html` is the page for `/mypage`, and a stub is
|
|
19
|
+
unique across the application.
|
|
18
20
|
|
|
19
21
|
## What the page files are
|
|
20
22
|
|
|
@@ -122,8 +124,19 @@ including a `row` query's: an aggregate row answers with a constant key. The
|
|
|
122
124
|
hub matches rows to live rows by key, and a request names a row by key.
|
|
123
125
|
|
|
124
126
|
Kind and key belong to the name, so one name answers with one shape, always,
|
|
125
|
-
and `createHub` leaves out an answer that disagrees with its declaration
|
|
126
|
-
page that needs the same data as one row
|
|
127
|
+
and `createHub` leaves out an answer that disagrees with its declaration,
|
|
128
|
+
with a warning naming the query. A page that needs the same data as one row
|
|
129
|
+
and as a set declares two queries.
|
|
130
|
+
|
|
131
|
+
`createHub` refuses at startup a page that declares a query, a handler or a
|
|
132
|
+
`crud` entry whose name begins with `lb-`, a query whose order has a term
|
|
133
|
+
naming no column, and a type it does not know.
|
|
134
|
+
|
|
135
|
+
Every query and every handler receives `ctx`, the application's own request
|
|
136
|
+
context, built by `contextFor`; see
|
|
137
|
+
[the Express server](./server.md#database-layer). Loadbare declares it
|
|
138
|
+
empty and never reads it, so a query finds exactly what the application put
|
|
139
|
+
there.
|
|
127
140
|
|
|
128
141
|
The hub hands each value to the browser untouched, so what a number, a date
|
|
129
142
|
or a null looks like is decided here, in the query.
|
|
@@ -229,13 +242,21 @@ export const requests: Requests = {
|
|
|
229
242
|
};
|
|
230
243
|
```
|
|
231
244
|
|
|
232
|
-
Declare every request the page allows. The
|
|
233
|
-
does not declare
|
|
245
|
+
Declare every request the page allows. The builder refuses markup naming a
|
|
246
|
+
request the page does not declare; see
|
|
247
|
+
[The markup checks](./data-binding.md#the-markup-checks). One can still
|
|
248
|
+
arrive, from a widget's script or from outside the page. The server then
|
|
249
|
+
warns on its console, naming the page and the missing entry, and answers
|
|
250
|
+
200 with no response items, so nothing lands and `lb-request-pending`
|
|
251
|
+
clears. A request carrying no name answers 400, and a `run` that throws
|
|
252
|
+
anything but a [refusal](#refusing-a-request) answers 500.
|
|
234
253
|
|
|
235
254
|
### crud
|
|
236
255
|
|
|
237
256
|
Declare what the three requests Loadbare provides do under `crud`, keyed by
|
|
238
|
-
the query they operate on
|
|
257
|
+
the query they operate on. Each key is the request name with its `lb-`
|
|
258
|
+
stripped and the rest camel-cased, so the attribute, the wire and the key
|
|
259
|
+
are one vocabulary:
|
|
239
260
|
|
|
240
261
|
| Request name | Runs | `run` receives |
|
|
241
262
|
|-----------------|-------------|------------------|
|
|
@@ -293,8 +314,10 @@ export const requests: Requests = {
|
|
|
293
314
|
};
|
|
294
315
|
```
|
|
295
316
|
|
|
296
|
-
Declare every request the query permits. The
|
|
297
|
-
query does not declare, and
|
|
317
|
+
Declare every request the query permits. The builder refuses markup sending
|
|
318
|
+
a request a query does not declare, and the server answers one that arrives
|
|
319
|
+
anyway as it does an undeclared handler, with a console warning and nothing
|
|
320
|
+
to land. A query with no `crud` entry permits none.
|
|
298
321
|
|
|
299
322
|
Write `rowUpdate` to set the columns `values` names and leave every other
|
|
300
323
|
column as it is. A form or a live row sends every control it gathers, and a
|
|
@@ -303,6 +326,36 @@ columns the page may edit.
|
|
|
303
326
|
|
|
304
327
|
Every value in `values` is a string, as a control's value is.
|
|
305
328
|
|
|
329
|
+
Two users writing the same row overlap only on the columns their requests
|
|
330
|
+
carry, and the last write wins. A single control sends its own column, so
|
|
331
|
+
two users editing different columns of a row do not overwrite each other.
|
|
332
|
+
A form sends every control it gathers, so two forms saved over one row
|
|
333
|
+
overlap on all of their columns.
|
|
334
|
+
|
|
335
|
+
A write whose outcome matters states its precondition in its own SQL, and
|
|
336
|
+
refuses when no row was updated:
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
handlers: {
|
|
340
|
+
approve: {
|
|
341
|
+
run: async (ctx, { key }) => {
|
|
342
|
+
const updated = await ctx.db.run(
|
|
343
|
+
"UPDATE invoice SET status = 'approved' WHERE id = $1 AND status = 'pending'",
|
|
344
|
+
[key],
|
|
345
|
+
);
|
|
346
|
+
if (updated === 0) {
|
|
347
|
+
throw refusal("Someone else has already decided this invoice.");
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
refresh: ["invoice"],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The request's name says what the user meant, and the precondition follows
|
|
356
|
+
from it, so no earlier value is needed. See
|
|
357
|
+
[Refusing a request](#refusing-a-request).
|
|
358
|
+
|
|
306
359
|
### refresh and patch
|
|
307
360
|
|
|
308
361
|
List in `refresh` every query whose whole answer the request changed.
|
|
@@ -342,7 +395,7 @@ removing a row never reorders the rest. So does an insert, which has the new
|
|
|
342
395
|
row's key once it has written it, and the hub places the new row by the
|
|
343
396
|
query's order. `createHub` refuses at startup a `crud` `rowInsert`,
|
|
344
397
|
`rowUpdate` or `rowDelete` on a `rows` query whose `refresh` names that same
|
|
345
|
-
query.
|
|
398
|
+
query. A `row` query sends one row and may refresh itself.
|
|
346
399
|
|
|
347
400
|
Each request's `refresh` is its own, and every query in it needs its own
|
|
348
401
|
reason. A list shared by several requests is a warning sign.
|
|
@@ -408,6 +461,21 @@ nothing else `run` returned is sent, since both answered for the URL the page
|
|
|
408
461
|
left. The loaded page reads the new parms through `contextFor`; see
|
|
409
462
|
[the Express server](./server.md#database-layer).
|
|
410
463
|
|
|
464
|
+
- A `url()` naming no column, or holding a value that is not a string, is
|
|
465
|
+
warned about on the server console and ignored, and the refresh set runs
|
|
466
|
+
as usual.
|
|
467
|
+
- If loading the page fails, the write has still happened. The response
|
|
468
|
+
carries the `lb-url` item alone, and the hub loads the page itself. A
|
|
469
|
+
failure there is a page load's, and is not stamped on the element that
|
|
470
|
+
sent the request.
|
|
471
|
+
- A user who has moved by the time the response arrives, to another page or
|
|
472
|
+
to other query parms, keeps the URL they are on.
|
|
473
|
+
|
|
474
|
+
Saving what the user has on screen, for `onPageEnter` to restore, is a
|
|
475
|
+
declared request with `refresh: []`, whose handler each page passes to a
|
|
476
|
+
routine the application's pages share. A custom element naming `lb-url`
|
|
477
|
+
sees every change of the URL and sends it.
|
|
478
|
+
|
|
411
479
|
### Refusing a request
|
|
412
480
|
|
|
413
481
|
A handler refuses a request by throwing `refusal()`, with words written for
|
package/docs/reference/server.md
CHANGED
|
@@ -17,7 +17,7 @@ import { readFileSync } from "node:fs";
|
|
|
17
17
|
import express, { type Request } from "express";
|
|
18
18
|
import { hubRoutes } from "@loadbare/app/express";
|
|
19
19
|
import type { HubContext } from "@loadbare/app/server";
|
|
20
|
-
import { hub } from "./dist/pages";
|
|
20
|
+
import { hub } from "./dist/pages.ts";
|
|
21
21
|
import { openDb } from "./src/database";
|
|
22
22
|
|
|
23
23
|
const DIST = path.resolve("dist");
|
|
@@ -181,3 +181,56 @@ open across the two, is both.
|
|
|
181
181
|
Add a field for anything else a request needs — the authenticated user, a
|
|
182
182
|
request id, a feature flag set. Queries and requests read them from `ctx`; see
|
|
183
183
|
[page files](./page-files.md).
|
|
184
|
+
|
|
185
|
+
## The data channel
|
|
186
|
+
|
|
187
|
+
One path, one method. The hub sends every round trip as
|
|
188
|
+
`POST /lb/<stub>?<query string>`, with the query string the browser is
|
|
189
|
+
showing, verbatim. An empty body, or `{}`, asks for the page load. Any other
|
|
190
|
+
body is a request:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
interface HubRequest {
|
|
194
|
+
name: string;
|
|
195
|
+
query?: string;
|
|
196
|
+
key?: string;
|
|
197
|
+
keys?: string[];
|
|
198
|
+
values?: Record<string, string>;
|
|
199
|
+
rows?: Record<string, { key: string; values: Record<string, string> }[]>;
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
A refused request is answered with status 422 and
|
|
204
|
+
`{ refusal: { message?, columns? } }`, a request with no name with 400 and
|
|
205
|
+
`[]`, and
|
|
206
|
+
any other failure with 500 and no body; see
|
|
207
|
+
[Refusing a request](./page-files.md#refusing-a-request). Every other
|
|
208
|
+
response is an array of response items:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
interface ResponseItem {
|
|
212
|
+
query: string;
|
|
213
|
+
kind: "row" | "rows";
|
|
214
|
+
key: string;
|
|
215
|
+
row?: Row;
|
|
216
|
+
rows?: Row[];
|
|
217
|
+
patch?: { rows?: Row[]; drop?: unknown[] };
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
A response item carries exactly one of `row`, `rows` or `patch`. A patch
|
|
222
|
+
answers a `rows` query only: rows named in `rows` are added or updated, keys
|
|
223
|
+
in `drop` are removed, and every other row is left alone. A `rows` item on
|
|
224
|
+
the answer that loads the page also carries the query's declared `order`
|
|
225
|
+
and `types`, which the hub keeps for every answer after it.
|
|
226
|
+
|
|
227
|
+
A choice is sent as `lb-row-update` for `lb-choices`, keyed by the path,
|
|
228
|
+
with every choice made on the page as `values`; see
|
|
229
|
+
[Choices](./data-binding.md#choices).
|
|
230
|
+
|
|
231
|
+
`createHub(pages)` builds the `Hub`, which has two calls, both answering
|
|
232
|
+
with response items: `dataForPage(page, ctx, options)` for a page load, and
|
|
233
|
+
`runRequest(page, request, ctx)` for a request. `options.parms` are the
|
|
234
|
+
parms the page is loaded at, and `options.follow` false ignores a `url()`
|
|
235
|
+
from `onPageEnter`. `hubRoutes` passes both, and an application calls
|
|
236
|
+
neither.
|
package/docs/roadmap.md
CHANGED
|
@@ -15,13 +15,6 @@ mechanism Release 1.0 already ships. Each is here because deciding it
|
|
|
15
15
|
speculatively, before a real page forces the question, risks designing the
|
|
16
16
|
wrong thing. Revisit when the described symptom actually shows up.
|
|
17
17
|
|
|
18
|
-
### Staleness and concurrent writers
|
|
19
|
-
|
|
20
|
-
Two tabs, or two users, updating the same rows at once. A solo developer
|
|
21
|
-
testing in one browser will not produce this by accident, and retrofitting a
|
|
22
|
-
version or conflict check onto every row after the fact touches every widget
|
|
23
|
-
that writes.
|
|
24
|
-
|
|
25
18
|
### A query that pages or limits its rows
|
|
26
19
|
|
|
27
20
|
The user's order and filters apply to the rows the browser holds. A query
|
|
@@ -49,6 +42,31 @@ end up living in the widget while the server remains authoritative for the
|
|
|
49
42
|
same field. Not yet designed — revisit the first time an application needs
|
|
50
43
|
inline validation.
|
|
51
44
|
|
|
45
|
+
### Compound foreign keys
|
|
46
|
+
|
|
47
|
+
A picker is the UI for a foreign key. `<select lb-query="statuses"
|
|
48
|
+
lb-column="status">` says the row's `status` references a row of
|
|
49
|
+
`statuses`, and offers every one of them. For a single-column foreign key
|
|
50
|
+
that is exactly right, which is why a query in a row template answers the
|
|
51
|
+
same rows in every live row.
|
|
52
|
+
|
|
53
|
+
A compound foreign key cannot be expressed. When `(type, status)`
|
|
54
|
+
references `allowed_statuses(type, status)`, the choices for `status`
|
|
55
|
+
depend on the row's own `type`, and every row is offered every status.
|
|
56
|
+
The database knows the constraint, and the UI cannot say it, so a user is
|
|
57
|
+
offered choices the server will refuse.
|
|
58
|
+
|
|
59
|
+
A likely direction keeps one query and one answer: the lookup answers every
|
|
60
|
+
allowed pair as ordinary rows, the picker names the columns of its row that
|
|
61
|
+
restrict it, and the hub shows each picker only the rows that match, as it
|
|
62
|
+
already filters the rows it holds. Two questions come with it:
|
|
63
|
+
|
|
64
|
+
- Whether the restricting value is the one that landed or the control's
|
|
65
|
+
current value, so the choices follow an edit before it is saved.
|
|
66
|
+
- What becomes of a chosen value the restriction no longer allows.
|
|
67
|
+
|
|
68
|
+
Revisit when an application needs a choice restricted by its row.
|
|
69
|
+
|
|
52
70
|
## Release 1.1 candidates
|
|
53
71
|
|
|
54
72
|
Net-new scope, not gaps in 1.0. Release 1.1 will optimize for operational
|
|
@@ -128,9 +146,10 @@ breaks nothing.
|
|
|
128
146
|
|
|
129
147
|
### Queries stated by the chrome
|
|
130
148
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
pattern from the framework side,
|
|
149
|
+
The chrome belongs to no page, so the builder refuses an application query
|
|
150
|
+
named there, and a custom element in the chrome cannot show application
|
|
151
|
+
data. `lb-url` and `lb-choices` show the pattern from the framework side,
|
|
152
|
+
and an application has no equivalent.
|
|
134
153
|
|
|
135
154
|
A chrome-level query set on `createHub` would close this, and it is additive.
|
|
136
155
|
It takes its place in the [options on `createHub`](#options-on-createhub).
|
|
@@ -159,39 +178,6 @@ A query parm write replaces the history entry, and `lb-url-push` pushes
|
|
|
159
178
|
one. Which of the two a user wants may be the user's to say, rather
|
|
160
179
|
than the page's.
|
|
161
180
|
|
|
162
|
-
### Build-time checking of `lb-request` against the declared requests
|
|
163
|
-
|
|
164
|
-
Release 1.0 resolves every `lb-request` value at request time. A value naming
|
|
165
|
-
no entry under `handlers`, or one of the requests Loadbare provides for a
|
|
166
|
-
query with no entry under `crud`, is found on the first commit: a server
|
|
167
|
-
console warning and an empty answer, described in
|
|
168
|
-
[Page files](./reference/page-files.md). A typo builds cleanly.
|
|
169
|
-
|
|
170
|
-
The builder already expands each page and walks the finished document in
|
|
171
|
-
`walkMarkup`, tracking the enclosing `lb-query`, so collecting every request
|
|
172
|
-
name together with the query it is issued for needs no new machinery. What
|
|
173
|
-
it lacks is the other half of the comparison: it knows the path of a
|
|
174
|
-
`.requests.ts` file and never reads its contents.
|
|
175
|
-
|
|
176
|
-
Two routes to the declared names, and the choice is the decision:
|
|
177
|
-
|
|
178
|
-
- **Load the built module.** Bundle the generated `pages.ts`, import it, and
|
|
179
|
-
read the keys off the finished objects. Exact, and indifferent to how the
|
|
180
|
-
object was written. The build then executes application import-time code,
|
|
181
|
-
and a builder that only reads files stops being that.
|
|
182
|
-
- **Parse the source.** Read the keys statically with the TypeScript compiler
|
|
183
|
-
API. No application code runs, at the price of a real dependency and of
|
|
184
|
-
guessing wrong on anything not written as a plain object literal.
|
|
185
|
-
|
|
186
|
-
Two smaller calls come with either route. The chrome belongs to no page, so
|
|
187
|
-
a request there can only be required of every page or forbidden. A page with
|
|
188
|
-
no requests file makes any request on it an error. The check is sound
|
|
189
|
-
because the developer writes `lb-*` attributes in markup and script never
|
|
190
|
-
assigns them.
|
|
191
|
-
|
|
192
|
-
The same machinery would then also let `lb-query` names be checked against
|
|
193
|
-
the declared queries.
|
|
194
|
-
|
|
195
181
|
### A language server
|
|
196
182
|
|
|
197
183
|
...for Loadbare HTML.
|
|
@@ -203,6 +189,23 @@ expansion allows a strings file. We could either preserve the fully
|
|
|
203
189
|
static build-time system and create multiple versions of `app.html`, or we
|
|
204
190
|
could add label hydration to the page navigation stage.
|
|
205
191
|
|
|
192
|
+
### Version checks on update and delete
|
|
193
|
+
|
|
194
|
+
The last write to a row wins, at the grain of the columns each request
|
|
195
|
+
carries, and a write whose outcome matters states its precondition in its
|
|
196
|
+
own SQL; see [crud](./reference/page-files.md#crud). A plain edit has no
|
|
197
|
+
such precondition: only the value the user saw, or a version standing for
|
|
198
|
+
it, tells that the row changed in between, and the server remembers
|
|
199
|
+
nothing.
|
|
200
|
+
|
|
201
|
+
A query would declare its version column. The hub keeps every query's
|
|
202
|
+
last answer, patches included, so it would send the version it holds with
|
|
203
|
+
each update and delete for that query, and the handler would put it in its
|
|
204
|
+
`WHERE`. No widget changes, and a control committing alone takes part.
|
|
205
|
+
|
|
206
|
+
Revisit when an application needs a write refused because the row changed
|
|
207
|
+
since the user saw it.
|
|
208
|
+
|
|
206
209
|
## Decided against
|
|
207
210
|
|
|
208
211
|
Settled questions, kept here so they are not reopened. Each names what was
|
|
@@ -222,3 +225,12 @@ attribute value, with no escape for either.
|
|
|
222
225
|
|
|
223
226
|
Text has a workaround: `<div>Hello, <span>{{world}}</span></div>` places the
|
|
224
227
|
placeholder in a text node of its own. Attribute values have none.
|
|
228
|
+
|
|
229
|
+
### Serving the static files
|
|
230
|
+
|
|
231
|
+
A router beside `hubRoutes` that serves `client.js`, `app.css` and
|
|
232
|
+
`app.html` would save an application three routes. It would also decide
|
|
233
|
+
for every application how those files are read, cached and placed among
|
|
234
|
+
its own routes, and whether a path naming no page answers `app.html`.
|
|
235
|
+
Loadbare/app ships no server, and serving the build's output is the
|
|
236
|
+
application's choice, as the rest of its server is.
|