@loadbare/app 0.18.1 → 0.19.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 +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/dist/core/lb-types.d.ts +6 -3
- package/dist/core/lb-types.d.ts.map +1 -1
- package/dist/core/lb-types.js.map +1 -1
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +10 -3
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/server/lb-server.d.ts +5 -2
- package/dist/server/lb-server.d.ts.map +1 -1
- package/dist/server/lb-server.js +12 -3
- package/dist/server/lb-server.js.map +1 -1
- package/docs/TECHREF-1.0.md +33 -85
- package/docs/comparison.md +695 -482
- package/docs/departures.md +63 -0
- package/docs/miswarts.md +140 -0
- package/docs/opinions.md +155 -0
- package/docs/reference/builder.md +9 -2
- package/docs/reference/chrome.md +2 -1
- package/docs/reference/css.md +163 -0
- package/docs/reference/custom-elements.md +1 -1
- package/docs/reference/data-binding.md +44 -10
- package/docs/reference/page-files.md +38 -5
- package/docs/reference/widgets.md +24 -2
- package/docs/roadmap.md +55 -43
- package/docs/testing.md +62 -28
- package/docs/theory.md +348 -108
- package/package.json +1 -1
- package/skills/loadbare-app/SKILL.md +16 -9
- package/skills/loadbare-app/references/TECHREF-1.0.md +33 -85
- package/skills/loadbare-app/references/builder.md +9 -2
- package/skills/loadbare-app/references/chrome.md +2 -1
- package/skills/loadbare-app/references/css.md +163 -0
- package/skills/loadbare-app/references/custom-elements.md +1 -1
- package/skills/loadbare-app/references/data-binding.md +44 -10
- package/skills/loadbare-app/references/miswarts.md +140 -0
- package/skills/loadbare-app/references/page-files.md +38 -5
- package/skills/loadbare-app/references/widgets.md +24 -2
- package/docs/possible-ideas.md +0 -314
- package/docs/theory-old.md +0 -839
- package/docs/what-does-loadbare-extend.md +0 -124
package/docs/theory.md
CHANGED
|
@@ -1,113 +1,353 @@
|
|
|
1
1
|
# The Theory of Loadbare/app
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
3
|
+
By Ken Downs
|
|
4
|
+
October 9, 2026
|
|
5
|
+
|
|
6
|
+
The primary goals of Loadbare/app are performance, correctness, and
|
|
7
|
+
developer ergonomics for server-bound applications. These goals motivate
|
|
8
|
+
all design and implementation decisions. The motivations cannot be
|
|
9
|
+
cleanly separated in implementation, they must all be true at once, and
|
|
10
|
+
often overlap and support each other.
|
|
11
|
+
|
|
12
|
+
Correctness: there is precise and perfect integrity between the database
|
|
13
|
+
and the screen as of the last round
|
|
14
|
+
trip. User writes hit the database once. Constraint violations are
|
|
15
|
+
intelligible and rendered in context.
|
|
16
|
+
|
|
17
|
+
Developer ergonomics means the simplest coding path results in an
|
|
18
|
+
application that is correct and performant. Getting the right
|
|
19
|
+
answer painted on the screen must be the lowest friction way to
|
|
20
|
+
write the code. This means many things, but it definitely rules
|
|
21
|
+
out elegant abstractions, excessive boilerplate and housekeeping code.
|
|
22
|
+
It must be difficult to express a solution that is slow or does not
|
|
23
|
+
maintain integrity between database and UI.
|
|
24
|
+
|
|
25
|
+
I admire elegance, but it is very dangerous when it becomes a goal
|
|
26
|
+
for its own sake. An elegant abstraction appeals from its own
|
|
27
|
+
design rather than from what it does for the user: it
|
|
28
|
+
hides the platform instead of mapping onto it, and it must be learned and
|
|
29
|
+
serviced forever.
|
|
30
|
+
|
|
31
|
+
From the user's perspective, performance is an aspect of correctness. A
|
|
32
|
+
slow round trip leaves the user wondering if what they see is correct. The
|
|
33
|
+
ideal user experience is certainty and trust in the application, which
|
|
34
|
+
requires a cumulative experience of an expectation:reward ratio of
|
|
35
|
+
exactly 1:1. All application behaviors that create doubt and
|
|
36
|
+
confusion erode the expectation:reward ratio, and must be removed
|
|
37
|
+
and made difficult to create by construction. A slow application cannot
|
|
38
|
+
provide an expectation:reward ratio of 1:1 because it is constantly
|
|
39
|
+
creating doubt.
|
|
40
|
+
|
|
41
|
+
## Currency is not a motivator
|
|
42
|
+
|
|
43
|
+
Currency is not a motivator for 1.0.
|
|
44
|
+
By currency, we mean the screen may be updated, absent any action by
|
|
45
|
+
the current user, due to the actions of other users or automated processes.
|
|
46
|
+
The class of apps that require currency are different in kind from those
|
|
47
|
+
that require only correctness. Unprompted updates may even violate
|
|
48
|
+
the Loadbare/app definition of correctness.
|
|
49
|
+
|
|
50
|
+
Currency can be considered when use cases present, and once it is
|
|
51
|
+
reconciled to correctness.
|
|
52
|
+
|
|
53
|
+
## From motivation to means
|
|
54
|
+
|
|
55
|
+
With the motivations firmly established, everything after is
|
|
56
|
+
technical decisions to establish the means.
|
|
57
|
+
|
|
58
|
+
This can be confusing, because sometimes a means and motivation
|
|
59
|
+
look the same. For example, I personally prefer to code static
|
|
60
|
+
HTML, and would like a data-driven framework that allows that.
|
|
61
|
+
It so happens Loadbare/app works that way. But it would be a
|
|
62
|
+
tedious and unproductive waste of words trying to determine if
|
|
63
|
+
that was a preference, motivation or means.
|
|
64
|
+
|
|
65
|
+
The three primary motivations force all design and implementation
|
|
66
|
+
decisions. All of my other preferences and motivations, such as
|
|
67
|
+
low boilerplate, declarative systems, static HTML and many others
|
|
68
|
+
were all worked in along the way, but always in a way that
|
|
69
|
+
supported the major three motivations.
|
|
70
|
+
|
|
71
|
+
I am happy to say I think it worked. In the sections below,
|
|
72
|
+
you will see how it was done. My own preferences and secondary
|
|
73
|
+
motivations will be plain to see in the decisions, but they
|
|
74
|
+
are always presented as what they are: a means to create a correct
|
|
75
|
+
and performant application with high developer ergonomics.
|
|
76
|
+
|
|
77
|
+
## The organizing principle
|
|
78
|
+
|
|
79
|
+
Every framework needs an organizing principle that determines implementation
|
|
80
|
+
decisions and the developer experience. For Loadbare/app, a very early
|
|
81
|
+
decision established that principle: Loadbare uses SQL vocabulary and
|
|
82
|
+
understands two SQL data shapes: a `row` or a set of `rows`. We speak
|
|
83
|
+
of rows, columns, keys, insert, update, and delete. We use the word
|
|
84
|
+
"query" when talking about named SQL SELECT statements.
|
|
85
|
+
|
|
86
|
+
Loadbare/app does not understand non-atomic column values. If a
|
|
87
|
+
document is retrieved from a store that embeds child rows into a
|
|
88
|
+
parent row, it must be converted into two separate query results.
|
|
89
|
+
|
|
90
|
+
A relational
|
|
91
|
+
expert will quickly point out that `rows` subsumes `row` and only
|
|
92
|
+
one shape is necessary, but in a UI they are not subsumed. A form
|
|
93
|
+
with its singleton form controls per column is expressed with
|
|
94
|
+
fundamentally different mechanics than a `<ul>`, `<select>` or
|
|
95
|
+
`<table>`. This is a UI framework, not a relational store, so the
|
|
96
|
+
data shapes are optimized for making user interfaces.
|
|
97
|
+
|
|
98
|
+
This one decision serves all three motivations. Two shapes and three
|
|
99
|
+
writes make a small and closed vocabulary, so coding the framework
|
|
100
|
+
is a finite and terminating task. For correctness, a finite task has
|
|
101
|
+
no open-ended edge cases to get wrong, and because every row arrives
|
|
102
|
+
with its key, the screen always knows exactly which row in the
|
|
103
|
+
database it is showing. For performance, every task the framework
|
|
104
|
+
must perform is itself finite, so each one can be measured and
|
|
105
|
+
optimized. For ergonomics, a developer of a database application must
|
|
106
|
+
already know rows, keys, SELECT and DML. Loadbare/app asks them to
|
|
107
|
+
map that knowledge onto HTML, not to learn new concepts.
|
|
108
|
+
|
|
109
|
+
This organizing principle also guards us from creating elegant
|
|
110
|
+
abstractions. Anything new must be a SQL concept, an HTML one, or a
|
|
111
|
+
member of a small, named family of things every browser application
|
|
112
|
+
needs, and an elegant abstraction is none of these. An elegant abstraction works against
|
|
113
|
+
all three motivations at once: it is a place where what the developer
|
|
114
|
+
wrote and what actually happens can drift apart, it adds work to every
|
|
115
|
+
round trip, and it is one more thing to learn and service instead of
|
|
116
|
+
the application. That last cost is where yak-shaving begins.
|
|
117
|
+
|
|
118
|
+
With this decision, Loadbare/app becomes a natural fit for SQL applications,
|
|
119
|
+
and can also be used if back-end data can be formatted to the two
|
|
120
|
+
shapes of `row` and `rows`.
|
|
121
|
+
|
|
122
|
+
## The performance goal
|
|
123
|
+
|
|
124
|
+
Because performance must be protected at all times, we need to
|
|
125
|
+
get specific about that first. All downstream decisions must know
|
|
126
|
+
the target they are protecting.
|
|
127
|
+
|
|
128
|
+
Research on response time gives two limits. Near 100ms, a response
|
|
129
|
+
feels instantaneous, as if caused directly by the user's action. Near
|
|
130
|
+
one second, the user's flow of thought begins to break (Miller 1968;
|
|
131
|
+
Card, Moran and Newell 1983; Nielsen 1993). Between the two, every
|
|
132
|
+
reduction pays, and the payoff grows sharply below a second (Doherty
|
|
133
|
+
and Thadhani 1982).
|
|
134
|
+
|
|
135
|
+
A server-bound application cannot reach 100ms, because the round trip
|
|
136
|
+
alone takes longer. So Loadbare/app sets its own target: a median of
|
|
137
|
+
300ms from click to paint, as close to instantaneous as a round trip
|
|
138
|
+
allows, and well inside the limit where doubt begins.
|
|
139
|
+
|
|
140
|
+
With a 300ms budget, we must allocate 200ms as typical wire time
|
|
141
|
+
when an application and user are in the same region, leaving 100ms
|
|
142
|
+
for everything that happens in the browser, the application server,
|
|
143
|
+
and the database.
|
|
144
|
+
|
|
145
|
+
The database's response time is not under the control of
|
|
146
|
+
Loadbare/app, and neither is the application's own server work,
|
|
147
|
+
such as authentication or picking up a connection from the pool.
|
|
148
|
+
So Loadbare/app must be thin, and get its job done in low
|
|
149
|
+
double-digit milliseconds, leaving as much of the 100ms as possible
|
|
150
|
+
to the work that belongs to the application.
|
|
151
|
+
|
|
152
|
+
This is where the organizing principle pays off a second time.
|
|
153
|
+
Having already ruled out elegant abstractions, we extend the rule to
|
|
154
|
+
reimplementations of browser behavior. The browser already parses
|
|
155
|
+
HTML, finds elements by selector, clones templates and manages focus,
|
|
156
|
+
and it does those things in native code faster than any framework
|
|
157
|
+
can do them again in JavaScript. Every millisecond Loadbare/app
|
|
158
|
+
spends redoing the browser's work is a millisecond taken from the
|
|
159
|
+
database.
|
|
160
|
+
|
|
161
|
+
## The correctness goal
|
|
162
|
+
|
|
163
|
+
Performance has a number, and we can hold a stopwatch to it.
|
|
164
|
+
Correctness has a definition instead, given at the top of this
|
|
165
|
+
document: integrity between database and screen as of the last round
|
|
166
|
+
trip, writes that hit the database once, and constraint violations
|
|
167
|
+
that are intelligible and rendered in context. We protect it by
|
|
168
|
+
making wrong states hard to reach, and by refusing or reporting the
|
|
169
|
+
ones we cannot rule out.
|
|
170
|
+
|
|
171
|
+
The foundation is an observation about data applications. Most
|
|
172
|
+
frameworks support a DOM that is fully mutable at any time for any
|
|
173
|
+
reason. But in a data application, the screen changes for one main
|
|
174
|
+
reason: the server answered with rows. The other changes, such as a
|
|
175
|
+
button waiting on its request, are a small and closed set. So
|
|
176
|
+
Loadbare/app changes the screen only from what the server sent, and
|
|
177
|
+
never ahead of it. There are no optimistic updates, and no guesses
|
|
178
|
+
about what the database probably did.
|
|
179
|
+
|
|
180
|
+
Because every row arrives with its key, the hub matches what arrived
|
|
181
|
+
to what is shown by key, never by position. A row that is gone
|
|
182
|
+
leaves, a row that changed is filled, and a row that did not change
|
|
183
|
+
is left alone, along with anything the user typed into it and has not
|
|
184
|
+
sent. An answer the server could not have meant for the current
|
|
185
|
+
screen is discarded: if the user moved to another page while a
|
|
186
|
+
request was in flight, the write still happened, but its answer does
|
|
187
|
+
not land on a page it says nothing about. When a query answers in a
|
|
188
|
+
shape it did not declare, the page goes without it. A page missing
|
|
189
|
+
one query is better than a page showing the wrong thing.
|
|
190
|
+
|
|
191
|
+
Writes hit the database once because a request comes from the
|
|
192
|
+
document, not from the developer's code. The developer names the
|
|
193
|
+
request, and the hub supplies the query and key from where the
|
|
194
|
+
element sits, so the developer cannot send the wrong key. An element
|
|
195
|
+
whose request is in flight ignores the next click, so a double click
|
|
196
|
+
cannot become a double write.
|
|
197
|
+
|
|
198
|
+
Constraints belong to the database, or to the application's own
|
|
199
|
+
server code. The framework's job is to carry the violation back to
|
|
200
|
+
the user in terms they can act on. A handler refuses a request with
|
|
201
|
+
words written for the user, either for the request as a whole or for
|
|
202
|
+
each column, and the browser shows them on the controls they belong
|
|
203
|
+
to. Anything else that goes wrong on the server reaches the browser
|
|
204
|
+
as a plain failure, so a stack trace or a database message never
|
|
205
|
+
lands in front of a user.
|
|
206
|
+
|
|
207
|
+
Finally, correctness by construction. Whatever can be checked before
|
|
208
|
+
the application runs is checked, and a mistake stops the build or the
|
|
209
|
+
server rather than shipping. The builder refuses a custom element it
|
|
210
|
+
cannot find and any `lb-` attribute the developer is not allowed to
|
|
211
|
+
write. The server refuses, at startup, patterns that are known to be
|
|
212
|
+
wrong, and refuses any request a page did not declare, so the wire
|
|
213
|
+
cannot reach anything the page has not published. What cannot be
|
|
214
|
+
checked ahead of time is reported on the console the moment it
|
|
215
|
+
happens, rather than silently papered over.
|
|
216
|
+
|
|
217
|
+
Each of these removes a source of doubt. Taken together, they are
|
|
218
|
+
how Loadbare/app keeps the expectation:reward ratio at 1:1 from the
|
|
219
|
+
correctness side, as the 300ms budget does from the performance side.
|
|
220
|
+
|
|
221
|
+
## Guarding the vocabulary
|
|
222
|
+
|
|
223
|
+
A small, clean vocabulary is a means, and it serves ergonomics first.
|
|
224
|
+
Every term a developer must learn that is not about their own
|
|
225
|
+
application is accidental complexity, time spent servicing the tool
|
|
226
|
+
instead of building the app. A messy vocabulary also hurts
|
|
227
|
+
correctness, because overlapping terms create edge cases, and edge
|
|
228
|
+
cases are where the framework and the developer disagree about what
|
|
229
|
+
the code means.
|
|
230
|
+
|
|
231
|
+
With apologies to Brooks and Pike:
|
|
232
|
+
|
|
233
|
+
> Essential complexity should dominate. Show me your abstractions and
|
|
234
|
+
> tooling and I shall remain mystified. Show me your tables and your
|
|
235
|
+
> rendering vocabulary and I shall be enlightened - the organization of
|
|
236
|
+
> the code will be self-evident.
|
|
237
|
+
|
|
238
|
+
A fair critic might say Loadbare/app does not have low accidental
|
|
239
|
+
complexity, only different accidental complexity. I take the
|
|
240
|
+
critique seriously, and it cannot be answered by counting terms. The
|
|
241
|
+
browser is inherently complex, so any complete solution will be
|
|
242
|
+
non-trivial, and vocabulary size alone is not a good proxy. What
|
|
243
|
+
matters more is where the terms come from.
|
|
244
|
+
|
|
245
|
+
Most of the vocabulary comes from SQL, which a developer of a
|
|
246
|
+
database application already knows:
|
|
247
|
+
|
|
75
248
|
- a `row`, with landing, insert, update, and delete
|
|
76
249
|
- `rows`, with landing, insert, update, and delete
|
|
77
250
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
251
|
+
The rest of it comes from HTML, which they also already know.
|
|
252
|
+
|
|
253
|
+
Not everything is SQL or HTML. Any browser application also needs
|
|
254
|
+
navigation, request status, and a way to show what changed between
|
|
255
|
+
one result and the next. These are small, closed families of their
|
|
256
|
+
own. So the rule is that a new term must be a SQL concept, an HTML
|
|
257
|
+
one, or belong to a named family. Any mechanism that is not
|
|
258
|
+
directly serving SQL, HTML or a named family is a mistake and will, sooner
|
|
259
|
+
or later, obstruct progress on the main motivations.
|
|
260
|
+
|
|
261
|
+
The real complexity comes when we map SQL onto HTML. HTML has a
|
|
262
|
+
larger surface area than SQL, but it is still finite. The accidents
|
|
263
|
+
most likely to pile up are in the mapping rules SQL does not dictate:
|
|
264
|
+
which element a row becomes, what happens when a row lands again, and
|
|
265
|
+
how a key identifies an element. That is where to look first when
|
|
266
|
+
the framework starts to feel like something you have to learn.
|
|
267
|
+
|
|
268
|
+
The same test applies to "best practices." If we end up with a list
|
|
269
|
+
of them, and they look like a bunch of stuff you have to learn, we
|
|
270
|
+
ask whether the practice already exists in SQL or HTML, and whether
|
|
271
|
+
it makes sense in the combination. If it does not, it is accidental
|
|
272
|
+
complexity, and the framework should absorb it or drop it.
|
|
273
|
+
|
|
274
|
+
The payoff is that the whole vocabulary fits in one technical
|
|
275
|
+
reference. That means it fits in a developer's head, and in an
|
|
276
|
+
LLM's context window.
|
|
277
|
+
|
|
278
|
+
## Conclusion
|
|
279
|
+
|
|
280
|
+
Loadbare/app is not for every application. It fits when the data is
|
|
281
|
+
rows, or can easily be shaped into rows, and when user actions
|
|
282
|
+
map to reading and writing those rows. It does not fit an
|
|
283
|
+
application whose data will not take the two shapes, and it does not
|
|
284
|
+
yet fit an application that needs currency. For a database
|
|
285
|
+
application, which describes a great many of the applications
|
|
286
|
+
people actually use at work, I believe it fits very well.
|
|
287
|
+
|
|
288
|
+
These are claims that can be checked. Performance has a
|
|
289
|
+
number: a median round trip of 300ms from click to paint.
|
|
290
|
+
Correctness has a definition: integrity between database and screen
|
|
291
|
+
as of the last round trip, writes that hit the database once, and
|
|
292
|
+
constraint violations rendered in context. The vocabulary has a
|
|
293
|
+
rule: every term is SQL, HTML, or a member of a named family. If
|
|
294
|
+
Loadbare/app misses the number, breaks the definition, or grows past
|
|
295
|
+
the rule, the theory has failed in a way anyone can see.
|
|
296
|
+
|
|
297
|
+
Code written for Loadbare/app should also last. Pages are HTML and
|
|
298
|
+
CSS, widgets are custom elements, the server is Express, and the
|
|
299
|
+
page code is queries against the application's own database. Each
|
|
300
|
+
of those is a standard or a mature tool in its own right, so code
|
|
301
|
+
written today is exposed to very little framework churn tomorrow.
|
|
302
|
+
As long as we have browsers, a Loadbare/app application should run.
|
|
303
|
+
|
|
304
|
+
The same properties matter more now that much code is written by
|
|
305
|
+
LLMs. SQL and HTML are about as well represented in an LLM's
|
|
306
|
+
training as anything can be, and the named families, navigation,
|
|
307
|
+
request status and showing what changed, are concepts every web
|
|
308
|
+
framework has. The model already knows nearly everything, so the
|
|
309
|
+
context we provide only has to spell out the mappings between them.
|
|
310
|
+
That is why the vocabulary fits in one reference, and one reference
|
|
311
|
+
fits in a context window. A framework that refuses mistakes at build time and at
|
|
312
|
+
startup catches the LLM's mistakes along with the developer's. The
|
|
313
|
+
correct application being the easiest one to write is just as true
|
|
314
|
+
when the writer is a model.
|
|
315
|
+
|
|
316
|
+
None of this is new to me. I first wrote this framework in 2003, as
|
|
317
|
+
Andromeda, before Node existed and before we had jQuery. The
|
|
318
|
+
foundation of Andromeda lives on in `@loadbare/db`, which makes the
|
|
319
|
+
data correct by declaring business logic in the schema. The
|
|
320
|
+
conviction behind Andromeda's web layer was that database
|
|
321
|
+
applications deserve a framework built for them. I am just as
|
|
322
|
+
convinced today, and `@loadbare/app` carries that correctness the
|
|
323
|
+
rest of the way, from the database to the paint.
|
|
324
|
+
|
|
325
|
+
So the whole theory comes down to this. Loadbare/app exists to make
|
|
326
|
+
a server-bound application correct, and to deliver that correctness
|
|
327
|
+
fast. The correct, fast application must also be the easiest one to
|
|
328
|
+
write. One early decision, that the framework speaks SQL and
|
|
329
|
+
understands only a `row` and a set of `rows`, serves all three at
|
|
330
|
+
once, and every other decision in this document is a means to the
|
|
331
|
+
same end.
|
|
332
|
+
|
|
333
|
+
## Appendix: Opinions, Departures and Miswarts
|
|
334
|
+
|
|
335
|
+
In an imperfect world, well defined rules do not always converge to
|
|
336
|
+
perfection. Loadbare/app makes two significant
|
|
337
|
+
[departures](./departures.md) from what this document implies, in
|
|
338
|
+
navigation and on the wire, and a few [miswarts](./miswarts.md)
|
|
339
|
+
appeared during development.
|
|
340
|
+
|
|
341
|
+
The decisions in this document force some positions on the developer.
|
|
342
|
+
They are listed in one place as [opinions](./opinions.md).
|
|
343
|
+
|
|
344
|
+
## References
|
|
345
|
+
|
|
346
|
+
- Card, S. K., Moran, T. P. and Newell, A. *The Psychology of
|
|
347
|
+
Human-Computer Interaction.* Lawrence Erlbaum, 1983.
|
|
348
|
+
- Doherty, W. J. and Thadhani, A. J. "The Economic Value of Rapid
|
|
349
|
+
Response Time." IBM, November 1982.
|
|
350
|
+
- Miller, R. B. "Response time in man-computer conversational
|
|
351
|
+
transactions." *Proceedings of the AFIPS Fall Joint Computer
|
|
352
|
+
Conference*, 1968.
|
|
353
|
+
- Nielsen, J. *Usability Engineering*, chapter 5. Academic Press, 1993.
|
package/package.json
CHANGED
|
@@ -42,8 +42,10 @@ tsc --noEmit
|
|
|
42
42
|
`dist/pages.ts` imports page files with their `.ts` extensions, so the
|
|
43
43
|
application's `tsconfig.json` needs `allowImportingTsExtensions`.
|
|
44
44
|
|
|
45
|
-
Write, build, fix, and repeat until the build is clean.
|
|
46
|
-
|
|
45
|
+
Write, build, fix, and repeat until the build is clean. The build loads
|
|
46
|
+
each page's `.queries.ts` and `.requests.ts` to check the names the markup
|
|
47
|
+
carries, so their import-time code runs then; open a database in
|
|
48
|
+
`contextFor`, not at import. Then start the server: `createHub` refuses a page that declares a reserved name at startup,
|
|
47
49
|
and the server console reports a query that answers with the wrong kind.
|
|
48
50
|
Restart the server after adding or changing a `.queries.ts` or
|
|
49
51
|
`.requests.ts` file.
|
|
@@ -70,7 +72,8 @@ server.ts
|
|
|
70
72
|
|
|
71
73
|
The chrome carries `<lb-hub>` inside `<body>`, an empty `<main>` inside the
|
|
72
74
|
hub, and `<script src="/client.js" defer>`. Everything a user touches sits
|
|
73
|
-
inside `<lb-hub>`.
|
|
75
|
+
inside `<lb-hub>`. The chrome belongs to no page, so it names only the
|
|
76
|
+
hub's queries, `lb-url` and `lb-choices`, and sends no declared request.
|
|
74
77
|
|
|
75
78
|
A page is an HTML fragment that lands in `<main>`:
|
|
76
79
|
|
|
@@ -155,7 +158,8 @@ answers with a constant one.
|
|
|
155
158
|
**There is no template language.** No `{#if}`, no `v-for`, no `map()`. A
|
|
156
159
|
set of rows is an element carrying `lb-query` with a `<template>` inside it,
|
|
157
160
|
the row template, which the hub clones once per row. A `row` query with no
|
|
158
|
-
row template lands on the element itself
|
|
161
|
+
row template lands on the element itself, and one answering `null` empties
|
|
162
|
+
every column there and removes its `lb-key-value`. A `rows` query with no row
|
|
159
163
|
template lands nothing, which is what an insert form naming its query is. A
|
|
160
164
|
condition is `lb-show="<column>"`: write every possibility into the page and
|
|
161
165
|
let a column decide which is present. The column is a boolean or null; the
|
|
@@ -273,7 +277,9 @@ fetched to be edited is another page, with that row as its master.
|
|
|
273
277
|
`crud`, keyed by query name, as `rowInsert`, `rowUpdate` and `rowDelete`.
|
|
274
278
|
Each works on a `row` query as well as a `rows` query, since the element a
|
|
275
279
|
`row` lands on carries its key. Every other request name is a declared
|
|
276
|
-
request, under `handlers`.
|
|
280
|
+
request, under `handlers`. The build refuses markup naming a query or
|
|
281
|
+
request the page does not declare, and the server refuses one that arrives
|
|
282
|
+
anyway.
|
|
277
283
|
|
|
278
284
|
**An empty key is a row not yet entered.** A `row` query answering with an
|
|
279
285
|
empty key, or a form `--lb-new` pointed, carries `lb-key-value=""`. The hub
|
|
@@ -334,7 +340,8 @@ reason. A list shared by several handlers is a warning sign.
|
|
|
334
340
|
one page's elements in the form they show: formatted dates and amounts, and
|
|
335
341
|
the words a reader sees, such as "12 postings". The hub does no type
|
|
336
342
|
conversion, so what a number, date or null looks like is decided on the
|
|
337
|
-
server
|
|
343
|
+
server, or by `lb-number` and `lb-date`, which format a raw value with
|
|
344
|
+
`Intl`. Send a column the user sorts or filters raw. A value the page reads back stays data: a column on a control, the
|
|
338
345
|
key, an `lb-show` condition, a value a stylesheet selects on. What pages
|
|
339
346
|
share goes beneath them, in a database view or a helper module, never in a
|
|
340
347
|
layer between the page and its queries.
|
|
@@ -457,7 +464,8 @@ own, or to act on rows once they land through the hook the hub calls,
|
|
|
457
464
|
|
|
458
465
|
Before writing one, check `@loadbare/widgets`: `lb-input`, `lb-select`,
|
|
459
466
|
`lb-options`, `lb-picker`, `lb-table`, `lb-unknown-page`, `lb-confirm`,
|
|
460
|
-
`lb-form`, `lb-insert-dialog`, `lb-master`, `lb-checkbox
|
|
467
|
+
`lb-form`, `lb-insert-dialog`, `lb-master`, `lb-checkbox`, `lb-number`,
|
|
468
|
+
`lb-date`. The first four are
|
|
461
469
|
form-associated controls that carry `lb-column` and `lb-request`
|
|
462
470
|
themselves. Install the package and list it in `src/imports.ts`:
|
|
463
471
|
|
|
@@ -494,6 +502,5 @@ with the package, so no network is needed.
|
|
|
494
502
|
library.
|
|
495
503
|
- [`references/TECHREF-1.0.md`](references/TECHREF-1.0.md) — the reserved
|
|
496
504
|
names, what a URL names and
|
|
497
|
-
[query parms](references/TECHREF-1.0.md#query-parms)
|
|
498
|
-
that block 1.0. Read it before designing around something the other
|
|
505
|
+
[query parms](references/TECHREF-1.0.md#query-parms). Read it before designing around something the other
|
|
499
506
|
references do not mention.
|