@loadbare/app 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/skills-cli.d.ts +12 -0
- package/dist/build/skills-cli.d.ts.map +1 -0
- package/dist/build/skills-cli.js +81 -0
- package/dist/build/skills-cli.js.map +1 -0
- package/dist/build/skills.d.ts +47 -0
- package/dist/build/skills.d.ts.map +1 -0
- package/dist/build/skills.js +124 -0
- package/dist/build/skills.js.map +1 -0
- package/dist/core/lb-constants.d.ts +2 -0
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +18 -4
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +10 -0
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +15 -1
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.d.ts.map +1 -1
- package/dist/hub/lb-hub.browser.js +116 -35
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/dist/server/lb-express.d.ts +13 -5
- package/dist/server/lb-express.d.ts.map +1 -1
- package/dist/server/lb-express.js +26 -7
- package/dist/server/lb-express.js.map +1 -1
- package/dist/server/lb-server.d.ts +3 -0
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +93 -22
- package/docs/comparison.md +8 -7
- package/docs/reference/chrome.md +5 -3
- package/docs/reference/data-binding.md +28 -0
- package/docs/reference/server.md +11 -0
- package/docs/reference/widgets.md +8 -4
- package/docs/roadmap.md +16 -0
- package/docs/theory.md +8 -1
- package/docs/tutorials/010-pages-and-navigation.md +2 -1
- package/package.json +8 -4
- package/skills/loadbare-app/SKILL.md +275 -0
- package/skills/loadbare-app/references/TECHREF-1.0.md +1260 -0
- package/skills/loadbare-app/references/builder.md +134 -0
- package/skills/loadbare-app/references/chrome.md +160 -0
- package/skills/loadbare-app/references/css.md +44 -0
- package/skills/loadbare-app/references/custom-elements.md +397 -0
- package/skills/loadbare-app/references/data-binding.md +485 -0
- package/skills/loadbare-app/references/overview.md +38 -0
- package/skills/loadbare-app/references/page-files.md +194 -0
- package/skills/loadbare-app/references/server.md +153 -0
- package/skills/loadbare-app/references/widgets.md +178 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: loadbare-app
|
|
3
|
+
description: Build server-bound web applications with Loadbare/app. Covers the file conventions the builder finds by name (`chrome.html`, `*.page.html`, `*.queries.ts`, `*.requests.ts`, `imports.ts`, `<tag>.html`, `<tag>.browser.ts`); the `lb-*` attribute vocabulary that binds HTML to server data and sends requests (`lb-list`, `lb-row`, `lb-cell`, `lb-key`, `lb-show`, `lb-action`); query parms in the URL with `lb-query-parm`; build-time widget expansion with `exp-*` parameters, `lb-slot` and `lb-template`; custom element code; the Express wiring with `hubRoutes`; and the `@loadbare/widgets` library. Use whenever a task involves `@loadbare/app`, `@loadbare/widgets`, `loadbare-app-build`, an `lb-` attribute, or a page, query, request or widget file in a Loadbare application. The model is not React, htmx or REST, and cannot be inferred from them.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
package: "@loadbare/app"
|
|
7
|
+
homepage: https://gitlab.com/kendowns/loadbare
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Working with Loadbare/app
|
|
11
|
+
|
|
12
|
+
Loadbare/app builds a whole application into one HTML document, one client
|
|
13
|
+
script and one stylesheet. The server answers named queries with rows and
|
|
14
|
+
performs named requests; the browser lands those rows on elements that name
|
|
15
|
+
them. No component renders anything, and no page fetches anything.
|
|
16
|
+
|
|
17
|
+
The wire is relational. A name answers with one row or a set of rows, a
|
|
18
|
+
row holds cells, and a cell holds one value. Hold on to that: most wrong
|
|
19
|
+
designs come from sending a shape a relational answer cannot have.
|
|
20
|
+
|
|
21
|
+
## Build every change before handing it over
|
|
22
|
+
|
|
23
|
+
`loadbare-app-build` is the checker. It expands every page and widget and
|
|
24
|
+
refuses what it cannot ship, naming the tag and the file:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
loadbare-app-build --src src --out dist
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
It exits non-zero on the first failure and prints
|
|
31
|
+
`loadbare-app-build: <message>`. A clean run prints the files it wrote and
|
|
32
|
+
the number of custom elements it bundled.
|
|
33
|
+
|
|
34
|
+
Type-check the server side as well:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
tsc --noEmit
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`dist/pages.ts` imports page files with their `.ts` extensions, so the
|
|
41
|
+
application's `tsconfig.json` needs `allowImportingTsExtensions`.
|
|
42
|
+
|
|
43
|
+
**Write, build, fix, repeat until it is clean.** Then start the server:
|
|
44
|
+
`createHub` refuses a query that answers with the wrong shape and a page
|
|
45
|
+
that declares a reserved name, and those refusals only appear at run time.
|
|
46
|
+
Restart the server after adding or changing a `.queries.ts` or
|
|
47
|
+
`.requests.ts` file.
|
|
48
|
+
|
|
49
|
+
## The shape of an application
|
|
50
|
+
|
|
51
|
+
The builder classifies files by name, never by directory:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
src/
|
|
55
|
+
chrome.html exactly one; the document every page lands in
|
|
56
|
+
imports.ts at most one; the widget packages to scan
|
|
57
|
+
00-reset.css any .css anywhere, concatenated
|
|
58
|
+
pages/
|
|
59
|
+
index.page.html the page for /
|
|
60
|
+
members.page.html the page for /members
|
|
61
|
+
members.queries.ts what members displays
|
|
62
|
+
members.requests.ts what members may be asked to do
|
|
63
|
+
widgets/
|
|
64
|
+
note-card.html the markup <note-card> expands into
|
|
65
|
+
visit-count.browser.ts the class <visit-count> registers
|
|
66
|
+
server.ts
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The chrome carries `<lb-hub>` inside `<body>`, an empty `<main>` inside the
|
|
70
|
+
hub, and `<script src="/client.js" defer>`. Everything a user touches sits
|
|
71
|
+
inside `<lb-hub>`.
|
|
72
|
+
|
|
73
|
+
A page is an HTML fragment that lands in `<main>`:
|
|
74
|
+
|
|
75
|
+
```html
|
|
76
|
+
<!-- src/pages/members.page.html -->
|
|
77
|
+
<p lb-row="dues">Collected this year: <span lb-cell="total"></span></p>
|
|
78
|
+
|
|
79
|
+
<section lb-list="roster">
|
|
80
|
+
<form lb-action="lb-row-insert">
|
|
81
|
+
<input lb-cell="name" />
|
|
82
|
+
<button type="submit">Add</button>
|
|
83
|
+
</form>
|
|
84
|
+
<ul>
|
|
85
|
+
<template lb-key="id">
|
|
86
|
+
<li>
|
|
87
|
+
<span lb-cell="name"></span>
|
|
88
|
+
<button lb-action="lb-row-delete">Remove</button>
|
|
89
|
+
</li>
|
|
90
|
+
</template>
|
|
91
|
+
</ul>
|
|
92
|
+
</section>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// src/pages/members.queries.ts
|
|
97
|
+
import { list, row, type Queries } from "@loadbare/app/server";
|
|
98
|
+
|
|
99
|
+
export const queries: Queries = {
|
|
100
|
+
dues: row((ctx) => ctx.db.duesTotal()),
|
|
101
|
+
roster: list((ctx) => ctx.db.members()),
|
|
102
|
+
};
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// src/pages/members.requests.ts
|
|
107
|
+
import { patch, type Requests } from "@loadbare/app/server";
|
|
108
|
+
|
|
109
|
+
export const requests: Requests = {
|
|
110
|
+
crud: {
|
|
111
|
+
roster: {
|
|
112
|
+
rowInsert: {
|
|
113
|
+
run: async (ctx, { values }) => ({
|
|
114
|
+
roster: patch({ rows: [await ctx.db.addMember(values)] }),
|
|
115
|
+
}),
|
|
116
|
+
refresh: ["dues"],
|
|
117
|
+
},
|
|
118
|
+
rowDelete: {
|
|
119
|
+
run: async (ctx, { key }) => {
|
|
120
|
+
await ctx.db.removeMember(key);
|
|
121
|
+
return { roster: patch({ drop: [key] }) };
|
|
122
|
+
},
|
|
123
|
+
refresh: ["dues"],
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Where front-end knowledge misleads
|
|
131
|
+
|
|
132
|
+
Re-read this list when a build fails or a design will not fit. Each item is
|
|
133
|
+
a place a reasonable instinct from React, Vue, htmx or REST produces an
|
|
134
|
+
error or a dead end.
|
|
135
|
+
|
|
136
|
+
**There is no template language.** No `{#if}`, no `v-for`, no `map()`. A
|
|
137
|
+
list is an element carrying `lb-list` with a `<template lb-key="...">`
|
|
138
|
+
inside it, and the hub clones the template once per row. A condition is
|
|
139
|
+
`lb-show="<column>"`: write every possibility into the page and let a column
|
|
140
|
+
decide which is present. The column must be a boolean or null; the string
|
|
141
|
+
`"false"` counts as present.
|
|
142
|
+
|
|
143
|
+
**`{{placeholder}}` is build time only.** It reads an `exp-` attribute on
|
|
144
|
+
the tag, never data. Write it as an entire attribute value or an entire
|
|
145
|
+
text node. Name parameters in lowercase with hyphens: HTML lowercases
|
|
146
|
+
`exp-inputClass` before expansion sees it.
|
|
147
|
+
|
|
148
|
+
**Scope comes from ancestry, not props.** An `lb-cell` binds to the row on
|
|
149
|
+
its nearest ancestor carrying `lb-row` or a live list row. An element
|
|
150
|
+
outside every scope displays nothing. A value of `lb-cell` is a column name.
|
|
151
|
+
|
|
152
|
+
**One name, one cardinality.** Declare every query with `row()` or
|
|
153
|
+
`list()`. A page that needs the same data as a row and as a set declares
|
|
154
|
+
two queries.
|
|
155
|
+
|
|
156
|
+
**A cell never holds rows.** Master-detail is a row and a list under two
|
|
157
|
+
names. Many masters with their details is one list of joined rows, grouped
|
|
158
|
+
for display by a widget's `lbPlaceRow`. A list nested inside another list's
|
|
159
|
+
rows receives the same rows in every outer row; use it for a picker, never
|
|
160
|
+
for per-row detail.
|
|
161
|
+
|
|
162
|
+
**A URL's path names a page, never a resource.** Do not design
|
|
163
|
+
`/accounts/42`. A row is addressed by `list` and `key`, taken from where the
|
|
164
|
+
element sits.
|
|
165
|
+
|
|
166
|
+
**The query string is what the page has on screen.** Which record a page
|
|
167
|
+
shows, a filter, a date range: each is a query parm,
|
|
168
|
+
`/accounts?acct=23&from=2026-09-09`, so a reload, a bookmark or a mailed link
|
|
169
|
+
shows the same thing. A control writes one with `lb-query-parm="acct"`, which
|
|
170
|
+
replaces the history entry, reloads the page at the new URL, and sends no
|
|
171
|
+
request. A link writes several with an ordinary `lb-nav-link` href. Queries
|
|
172
|
+
still take no arguments: `contextFor` reads `req.query` onto `ctx`, and a
|
|
173
|
+
query reads it there. A parm is user input, so validate it where you read
|
|
174
|
+
it. Do not keep a selection in server state set by an action; that forces
|
|
175
|
+
`refresh: []` and a hand-written re-answer of everything the selection
|
|
176
|
+
touches.
|
|
177
|
+
|
|
178
|
+
**Loadbare is for applications, not sites.** Every route is answered with
|
|
179
|
+
`app.html`, and a path that names no page is found in the browser, not
|
|
180
|
+
answered with a 404. That is the design, not a defect to report.
|
|
181
|
+
|
|
182
|
+
**There are no endpoints to write.** `hubRoutes(hub, contextFor)` is the
|
|
183
|
+
whole data channel. Declare every action under `actions` and every
|
|
184
|
+
permitted operation under `crud`; anything undeclared is refused.
|
|
185
|
+
|
|
186
|
+
**All three CRUD operations are list operations.** Each needs a key, and a
|
|
187
|
+
key exists only on a live row inside `lb-list`. An `lb-row` scope is
|
|
188
|
+
read-only; give a writable single row a list that answers with one row.
|
|
189
|
+
|
|
190
|
+
**Write `rowUpdate` as a partial update.** A form sends every cell it
|
|
191
|
+
holds; a widget carrying `lb-cell` and `lb-action="lb-row-update"` sends its
|
|
192
|
+
one cell. Set the columns `values` names, leave the rest alone, and check
|
|
193
|
+
the names against the columns the page may edit.
|
|
194
|
+
|
|
195
|
+
**Put `lb-row-insert` and `lb-row-update` on a `<form>` or a button.** The
|
|
196
|
+
hub gathers the nearest `<form>`, `<tr>` or live row. Cells in a bare
|
|
197
|
+
`<div>` belong to no row and the request is refused. A native `<input>`
|
|
198
|
+
carrying `lb-row-update` is refused; commit one cell with a widget such as
|
|
199
|
+
`<lb-input>`. Inside a form, leave `lb-action` off the widgets.
|
|
200
|
+
|
|
201
|
+
**Return a patch when the change has a known extent.** `patch({ rows })`
|
|
202
|
+
for rows added or edited, `patch({ drop })` for keys removed, with
|
|
203
|
+
`refresh: []`. List a query in `refresh` only when its membership or order
|
|
204
|
+
changed in a way the operation cannot name.
|
|
205
|
+
|
|
206
|
+
**Format values in the query.** The hub does no type conversion. What a
|
|
207
|
+
number, date or null looks like is decided on the server.
|
|
208
|
+
|
|
209
|
+
**Checkboxes, radio buttons and file inputs are not bound.** They receive
|
|
210
|
+
no value and are not gathered. This is an open item, not a mistake in the
|
|
211
|
+
page.
|
|
212
|
+
|
|
213
|
+
**`lb-` belongs to Loadbare.** Invent no `lb-` attribute, and name no
|
|
214
|
+
query or action with the prefix. Import attribute names in widget code from
|
|
215
|
+
`@loadbare/app/constants`; never write them as string literals.
|
|
216
|
+
|
|
217
|
+
**Name a widget script `<tag>.browser.ts`.** A plain `<tag>.ts` stays on
|
|
218
|
+
the server and the tag goes unregistered.
|
|
219
|
+
|
|
220
|
+
**Light DOM, global CSS.** No shadow root, no scoping. Style empty lists
|
|
221
|
+
with `[lb-row-count="0"]`, pending requests with `[lb-pending]`, and failed
|
|
222
|
+
ones with `[lb-error]`.
|
|
223
|
+
|
|
224
|
+
**Host at the origin root.** The hub reaches its route by absolute path, so
|
|
225
|
+
a subpath such as `example.com/myapp/` does not work.
|
|
226
|
+
|
|
227
|
+
## Widgets
|
|
228
|
+
|
|
229
|
+
A widget is `<tag>.html`, `<tag>.browser.ts`, or both, and a tag with
|
|
230
|
+
neither is a build error. The HTML is expanded at build time into the tag's
|
|
231
|
+
children; the class is an ordinary custom element with no base class.
|
|
232
|
+
|
|
233
|
+
Reach for a widget only when plain HTML cannot do the job. A list, a form
|
|
234
|
+
and a condition need none. A widget exists to wrap a control that decides
|
|
235
|
+
its own moment to send (`<lb-input>` on `change`), or to place and scaffold
|
|
236
|
+
rows (`lbPlaceRow`, `lbRowsLanded`).
|
|
237
|
+
|
|
238
|
+
Before writing one, check `@loadbare/widgets`: `lb-input`, `lb-select`,
|
|
239
|
+
`lb-options`, `lb-table`, `lb-picker`, `lb-unknown-page`. Install it and
|
|
240
|
+
list it in `src/imports.ts`:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
export default ["@loadbare/widgets"];
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
The application's own definition of a tag overrides a package's.
|
|
247
|
+
|
|
248
|
+
## Reference material
|
|
249
|
+
|
|
250
|
+
Read these when the summary above does not settle the question. They ship
|
|
251
|
+
with the package, so no network is needed.
|
|
252
|
+
|
|
253
|
+
- [`references/overview.md`](references/overview.md) — the map of the
|
|
254
|
+
reference, by part of the application.
|
|
255
|
+
- [`references/data-binding.md`](references/data-binding.md) — every `lb-`
|
|
256
|
+
attribute, requests, forms, query parms, conditions and request state.
|
|
257
|
+
Start here for anything in a page.
|
|
258
|
+
- [`references/page-files.md`](references/page-files.md) — queries,
|
|
259
|
+
`onPageEnter`, actions, CRUD, refresh and patch.
|
|
260
|
+
- [`references/custom-elements.md`](references/custom-elements.md) —
|
|
261
|
+
expansion, parameters, slots, destinations, and widget code.
|
|
262
|
+
- [`references/chrome.md`](references/chrome.md) — the chrome,
|
|
263
|
+
navigation and `lb-navigation`.
|
|
264
|
+
- [`references/server.md`](references/server.md) — the Express server and
|
|
265
|
+
the request context.
|
|
266
|
+
- [`references/builder.md`](references/builder.md) — the builder's options,
|
|
267
|
+
what it reads, and where it looks.
|
|
268
|
+
- [`references/css.md`](references/css.md) — how stylesheets are ordered.
|
|
269
|
+
- [`references/widgets.md`](references/widgets.md) — the basic widget
|
|
270
|
+
library.
|
|
271
|
+
- [`references/TECHREF-1.0.md`](references/TECHREF-1.0.md) — the reserved
|
|
272
|
+
names, what a URL names and
|
|
273
|
+
[query parms](references/TECHREF-1.0.md#query-parms), and the open items
|
|
274
|
+
that block 1.0. Read it before designing around
|
|
275
|
+
something the other references do not mention.
|