@loadbare/app 0.7.3 → 0.7.4
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/assemble.d.ts.map +1 -1
- package/dist/build/assemble.js +67 -6
- package/dist/build/assemble.js.map +1 -1
- package/dist/core/lb-constants.d.ts +1 -0
- package/dist/core/lb-constants.d.ts.map +1 -1
- package/dist/core/lb-constants.js +15 -2
- package/dist/core/lb-constants.js.map +1 -1
- package/dist/hub/lb-apply.d.ts +2 -2
- package/dist/hub/lb-apply.d.ts.map +1 -1
- package/dist/hub/lb-apply.js +98 -8
- package/dist/hub/lb-apply.js.map +1 -1
- package/dist/hub/lb-hub.browser.js +14 -2
- package/dist/hub/lb-hub.browser.js.map +1 -1
- package/docs/TECHREF-1.0.md +121 -88
- package/docs/analysis-closed-set.md +203 -0
- package/docs/comparison.md +1123 -0
- package/docs/prior-art.md +216 -0
- package/docs/reference/custom-elements.md +10 -0
- package/docs/reference/data-binding.md +84 -28
- package/docs/testing.md +2 -0
- package/docs/theory.md +737 -408
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# A closed set for the Loadbare/app vocabulary
|
|
2
|
+
|
|
3
|
+
> **LLM-authored, not yet revised by a person.** Drafted by Claude on
|
|
4
|
+
> 2026-09-13, from a design session against `@loadbare/app` 0.7.3 while
|
|
5
|
+
> building a chart of accounts page. Treat it as a proposal, not as the
|
|
6
|
+
> author's statement.
|
|
7
|
+
|
|
8
|
+
## The question
|
|
9
|
+
|
|
10
|
+
Relational algebra is closed: every operation takes relations and returns a
|
|
11
|
+
relation. CRUD is closed in the same way on the write side. Closure is what
|
|
12
|
+
makes both easy to program against. Combinations do not explode, the cases
|
|
13
|
+
are finite, and nothing falls outside the model.
|
|
14
|
+
|
|
15
|
+
Loadbare/app maps three data shapes (cell, row, list) and three relational
|
|
16
|
+
operations (insert, update, delete) into HTML. This document asks whether
|
|
17
|
+
recent iterations have shown enough to state a **closed** vocabulary and set
|
|
18
|
+
of behaviors: one where every combination of shape, operation, position,
|
|
19
|
+
nesting and timing is defined, so application code never meets an edge case
|
|
20
|
+
outside the model.
|
|
21
|
+
|
|
22
|
+
Author note: a page can of course define invalid combinations, but their
|
|
23
|
+
lack of validity must be intelligibly reported as a failure to stick to the
|
|
24
|
+
closed set.
|
|
25
|
+
|
|
26
|
+
"Closed" is tested four ways:
|
|
27
|
+
|
|
28
|
+
1. **Shapes are closed.** Everything a page receives is one of the shapes,
|
|
29
|
+
or a patch on a list.
|
|
30
|
+
2. **Operations are closed over shapes.** Each operation yields a defined
|
|
31
|
+
result in the same vocabulary.
|
|
32
|
+
3. **Composition is closed.** Nesting, many scopes bound to one name, scopes
|
|
33
|
+
that appear late, and several names in one response all stay inside the
|
|
34
|
+
rules.
|
|
35
|
+
4. **Anything outside the spine is justified.** It either reduces to the
|
|
36
|
+
spine or is a separate, deliberately bounded axis.
|
|
37
|
+
|
|
38
|
+
## The model in one sentence
|
|
39
|
+
|
|
40
|
+
> **A page holds named relations. Every read and every write answers with
|
|
41
|
+
> new values for those names. Every scope is a view of one name.**
|
|
42
|
+
|
|
43
|
+
Every rule below follows from it.
|
|
44
|
+
|
|
45
|
+
## 1. Shapes: three, plus identity
|
|
46
|
+
|
|
47
|
+
| Shape | Attribute | Relational |
|
|
48
|
+
| -------- | ------------------------------------------- | ----------------------------------------------------------------- |
|
|
49
|
+
| list | `lb-list` | a relation, as a variable that holds rows |
|
|
50
|
+
| row | `lb-row` | a single row, read-only; a writable single row is a list of one |
|
|
51
|
+
| cell | `lb-cell` | a column value |
|
|
52
|
+
| identity | `lb-key` written, `lb-key-value` stamped | the primary key |
|
|
53
|
+
|
|
54
|
+
## 2. What every answer is made of: three forms
|
|
55
|
+
|
|
56
|
+
Every query, CRUD operation, declared action and refresh answers with
|
|
57
|
+
`{ name: result }`, and each result is one of:
|
|
58
|
+
|
|
59
|
+
- **a whole list** (an array), which replaces the relation, membership and
|
|
60
|
+
order included;
|
|
61
|
+
- **a patch** (`{ rows, drop }`), which upserts rows by key and drops keys;
|
|
62
|
+
- **a row** (an object), which replaces the single row.
|
|
63
|
+
|
|
64
|
+
This is the closure property. No operation answers with HTML, a redirect, or
|
|
65
|
+
instructions. Declared actions are open-ended, like stored procedures, but
|
|
66
|
+
their results are still in these three forms, so the landing side stays
|
|
67
|
+
closed.
|
|
68
|
+
|
|
69
|
+
## 3. Operations: three, plus one open door
|
|
70
|
+
|
|
71
|
+
| Operation | Takes from position | Carries | Answers |
|
|
72
|
+
| ---------------- | ------------------- | -------------------------------------------- | ------------ |
|
|
73
|
+
| `lb-row-insert` | list | values, gathered from the record | patch rows |
|
|
74
|
+
| `lb-row-update` | list, key | values, gathered or supplied by a widget | patch rows |
|
|
75
|
+
| `lb-row-delete` | list, key | nothing | patch drop |
|
|
76
|
+
| a declared name | list or row, key, cell | value | any names |
|
|
77
|
+
|
|
78
|
+
### Proposed: fold `lb-cell-change` into `lb-row-update`
|
|
79
|
+
|
|
80
|
+
A cell change is an update whose `values` holds one entry, and the hub
|
|
81
|
+
already keeps `values` a widget supplies. SQL has one UPDATE whether it sets
|
|
82
|
+
one column or many. On the server, `cellChange` and `rowUpdate` merge into
|
|
83
|
+
one `update(key, values)`, where a column absent from `values` is left as it
|
|
84
|
+
is.
|
|
85
|
+
|
|
86
|
+
### Reordering is not an operation
|
|
87
|
+
|
|
88
|
+
Position is not a relational concept; order is a column. A drag is an update
|
|
89
|
+
to an ordering column such as `display_order`, which the wire already
|
|
90
|
+
carries in `values`. No move operation is needed, and no field for "where
|
|
91
|
+
the row went".
|
|
92
|
+
|
|
93
|
+
## 4. Position: taken from the page, never written in the request
|
|
94
|
+
|
|
95
|
+
- An element's own attributes say what it displays. Its ancestors say where
|
|
96
|
+
it belongs.
|
|
97
|
+
- A nested scope begins a new scope.
|
|
98
|
+
- A request is an action plus a position. The hub supplies the position;
|
|
99
|
+
only an interaction supplies a value.
|
|
100
|
+
|
|
101
|
+
These are the rules [Theory](./theory.md) already states. They are listed
|
|
102
|
+
here because closure depends on them.
|
|
103
|
+
|
|
104
|
+
## 5. Landing: every combination defined
|
|
105
|
+
|
|
106
|
+
| Result → target | list scope | row scope | no scope yet |
|
|
107
|
+
| --------------- | ------------ | --------------------------------- | -------------------------- |
|
|
108
|
+
| whole list | reconcile | error: one name has one shape | stored |
|
|
109
|
+
| patch | upsert, drop | error | applied to the stored copy |
|
|
110
|
+
| row | error | fill | stored |
|
|
111
|
+
|
|
112
|
+
A result lands on every scope bound to its name, at any depth of nesting.
|
|
113
|
+
0.7.3 already does this: a patch to `groups` reaches a picker in every row of
|
|
114
|
+
an outer table, and the outer list's reconciliation does not mistake the
|
|
115
|
+
picker's rows for its own.
|
|
116
|
+
|
|
117
|
+
### The missing rule: timing
|
|
118
|
+
|
|
119
|
+
In 0.7.3 a scope that appears after its name has landed stays empty. A row
|
|
120
|
+
added to an outer list by a patch starts with every nested list empty, and a
|
|
121
|
+
widget's scaffolding built during landing, such as a ghost row holding a
|
|
122
|
+
picker, starts empty too. Pages work around it by ordering their queries so
|
|
123
|
+
the outer list lands first, which only covers the first load.
|
|
124
|
+
|
|
125
|
+
The closed rule:
|
|
126
|
+
|
|
127
|
+
> **The hub holds each name's current value. A scope shows that value
|
|
128
|
+
> whenever it appears.**
|
|
129
|
+
|
|
130
|
+
In relational terms a name is a relation variable, results are assignments to
|
|
131
|
+
it, and scopes are views of it. Whether a scope existed before or after its
|
|
132
|
+
data arrived stops mattering.
|
|
133
|
+
|
|
134
|
+
It also settles several things that were not worked on directly:
|
|
135
|
+
|
|
136
|
+
- The blocker in [TECHREF-1.0](./TECHREF-1.0.md), "Decide how a new row
|
|
137
|
+
fills a nested list".
|
|
138
|
+
- Pickers in newly created rows, and pickers in ghost rows built after the
|
|
139
|
+
first load.
|
|
140
|
+
- Query order in a page's queries file stops mattering.
|
|
141
|
+
- **What a partial patch row means.** A patch row upserts by key, and a
|
|
142
|
+
column it does not name is left unchanged, as an SQL UPDATE leaves it.
|
|
143
|
+
The reference is silent on this today.
|
|
144
|
+
- "No scope for a name" becomes an ordinary state rather than a console
|
|
145
|
+
warning, since a scope may simply not exist yet.
|
|
146
|
+
|
|
147
|
+
#### Mechanism
|
|
148
|
+
|
|
149
|
+
- The hub fills a cloned row, nested scopes included, before inserting it.
|
|
150
|
+
This extends the rule it already follows of filling before insertion.
|
|
151
|
+
- After each landing, it sweeps for scopes that have never received data.
|
|
152
|
+
For a list, an absent `lb-row-count` already marks one. A row scope would
|
|
153
|
+
need an equivalent mark.
|
|
154
|
+
- A widget that builds scopes outside a landing, at some later time, would
|
|
155
|
+
need a `MutationObserver`. No widget built so far does that.
|
|
156
|
+
|
|
157
|
+
#### What would prove it wrong
|
|
158
|
+
|
|
159
|
+
A case where the stored copy and the document legitimately disagree. The
|
|
160
|
+
only candidate is a control holding an edit not yet sent, and landing already
|
|
161
|
+
overwrites that in 0.7.3, so the rule introduces nothing new.
|
|
162
|
+
|
|
163
|
+
## 6. State attributes: four, stamped by the hub, read by CSS
|
|
164
|
+
|
|
165
|
+
`lb-value`, `lb-row-count`, `lb-pending`, `lb-error`.
|
|
166
|
+
|
|
167
|
+
## 7. Outside the algebra, deliberately bounded
|
|
168
|
+
|
|
169
|
+
- **Composition at build time:** `lb-slot`, `lb-template`.
|
|
170
|
+
- **The host channel:** `lb-page`, `lb-nav-link`, `lb-unknown-page`, and the
|
|
171
|
+
hub's own `lb-navigation` row. That row already shows the hub's own state
|
|
172
|
+
landing through the same three forms.
|
|
173
|
+
- **The widget protocol:** the `lb-request` event, which an ancestor may
|
|
174
|
+
stop, and the `lbPlaceRow` and `lbRowsLanded` hooks.
|
|
175
|
+
|
|
176
|
+
## What this changes in 0.7.3
|
|
177
|
+
|
|
178
|
+
1. The hub holds each name's current value and fills scopes that appear
|
|
179
|
+
late.
|
|
180
|
+
2. `lb-cell-change` folds into `lb-row-update`, with widget-supplied
|
|
181
|
+
`values`, and the server's `cellChange` and `rowUpdate` merge.
|
|
182
|
+
3. The reference states the patch row rule: upsert by key, unnamed columns
|
|
183
|
+
unchanged.
|
|
184
|
+
|
|
185
|
+
Everything else is the current vocabulary, now justified as closed.
|
|
186
|
+
|
|
187
|
+
## What the evidence has not settled
|
|
188
|
+
|
|
189
|
+
Everything above is backed by pages already built. These are not:
|
|
190
|
+
|
|
191
|
+
1. **Parameters and view state:** a filter, a selection, a collapsed
|
|
192
|
+
section. The likely closed answer is that URL query parameters become
|
|
193
|
+
columns of the `lb-navigation` row and reach queries as arguments, as
|
|
194
|
+
`<form method="get">` puts its fields in the query string. No page needs
|
|
195
|
+
it yet, so this is a prediction rather than evidence.
|
|
196
|
+
2. **Checkboxes and data types:** probably HTML's presence-or-absence
|
|
197
|
+
convention, still undecided.
|
|
198
|
+
3. **Concurrent writers:** staleness is a property of the stored copy, so
|
|
199
|
+
the rule in section 5 makes this question easier to state without
|
|
200
|
+
answering it.
|
|
201
|
+
4. **Error content:** `lb-error` records that a round trip failed, not why.
|
|
202
|
+
A closed answer would deliver the message as a result like any other,
|
|
203
|
+
perhaps as a hub row. Untested.
|