@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.
Files changed (47) hide show
  1. package/dist/build/skills-cli.d.ts +12 -0
  2. package/dist/build/skills-cli.d.ts.map +1 -0
  3. package/dist/build/skills-cli.js +81 -0
  4. package/dist/build/skills-cli.js.map +1 -0
  5. package/dist/build/skills.d.ts +47 -0
  6. package/dist/build/skills.d.ts.map +1 -0
  7. package/dist/build/skills.js +124 -0
  8. package/dist/build/skills.js.map +1 -0
  9. package/dist/core/lb-constants.d.ts +2 -0
  10. package/dist/core/lb-constants.d.ts.map +1 -1
  11. package/dist/core/lb-constants.js +18 -4
  12. package/dist/core/lb-constants.js.map +1 -1
  13. package/dist/hub/lb-apply.d.ts +10 -0
  14. package/dist/hub/lb-apply.d.ts.map +1 -1
  15. package/dist/hub/lb-apply.js +15 -1
  16. package/dist/hub/lb-apply.js.map +1 -1
  17. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  18. package/dist/hub/lb-hub.browser.js +116 -35
  19. package/dist/hub/lb-hub.browser.js.map +1 -1
  20. package/dist/server/lb-express.d.ts +13 -5
  21. package/dist/server/lb-express.d.ts.map +1 -1
  22. package/dist/server/lb-express.js +26 -7
  23. package/dist/server/lb-express.js.map +1 -1
  24. package/dist/server/lb-server.d.ts +3 -0
  25. package/dist/server/lb-server.d.ts.map +1 -1
  26. package/dist/server/lb-server.js.map +1 -1
  27. package/docs/TECHREF-1.0.md +93 -22
  28. package/docs/comparison.md +8 -7
  29. package/docs/reference/chrome.md +5 -3
  30. package/docs/reference/data-binding.md +28 -0
  31. package/docs/reference/server.md +11 -0
  32. package/docs/reference/widgets.md +8 -4
  33. package/docs/roadmap.md +16 -0
  34. package/docs/theory.md +8 -1
  35. package/docs/tutorials/010-pages-and-navigation.md +2 -1
  36. package/package.json +8 -4
  37. package/skills/loadbare-app/SKILL.md +275 -0
  38. package/skills/loadbare-app/references/TECHREF-1.0.md +1260 -0
  39. package/skills/loadbare-app/references/builder.md +134 -0
  40. package/skills/loadbare-app/references/chrome.md +160 -0
  41. package/skills/loadbare-app/references/css.md +44 -0
  42. package/skills/loadbare-app/references/custom-elements.md +397 -0
  43. package/skills/loadbare-app/references/data-binding.md +485 -0
  44. package/skills/loadbare-app/references/overview.md +38 -0
  45. package/skills/loadbare-app/references/page-files.md +194 -0
  46. package/skills/loadbare-app/references/server.md +153 -0
  47. 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.