@loadbare/app 0.18.2 → 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.
Files changed (42) hide show
  1. package/README.md +17 -17
  2. package/dist/build/assemble.d.ts +7 -1
  3. package/dist/build/assemble.d.ts.map +1 -1
  4. package/dist/build/assemble.js +81 -11
  5. package/dist/build/assemble.js.map +1 -1
  6. package/dist/build/cli.d.ts +3 -0
  7. package/dist/build/cli.d.ts.map +1 -1
  8. package/dist/build/cli.js +6 -1
  9. package/dist/build/cli.js.map +1 -1
  10. package/dist/build/declared.d.ts +25 -0
  11. package/dist/build/declared.d.ts.map +1 -0
  12. package/dist/build/declared.js +62 -0
  13. package/dist/build/declared.js.map +1 -0
  14. package/dist/build/pages.d.ts +6 -0
  15. package/dist/build/pages.d.ts.map +1 -1
  16. package/dist/build/pages.js +32 -12
  17. package/dist/build/pages.js.map +1 -1
  18. package/docs/TECHREF-1.0.md +27 -72
  19. package/docs/comparison.md +692 -480
  20. package/docs/departures.md +63 -0
  21. package/docs/miswarts.md +140 -0
  22. package/docs/opinions.md +155 -0
  23. package/docs/reference/builder.md +9 -2
  24. package/docs/reference/chrome.md +2 -1
  25. package/docs/reference/custom-elements.md +1 -1
  26. package/docs/reference/data-binding.md +36 -9
  27. package/docs/reference/page-files.md +36 -4
  28. package/docs/roadmap.md +55 -43
  29. package/docs/testing.md +62 -28
  30. package/docs/theory.md +348 -108
  31. package/package.json +1 -1
  32. package/skills/loadbare-app/SKILL.md +10 -6
  33. package/skills/loadbare-app/references/TECHREF-1.0.md +27 -72
  34. package/skills/loadbare-app/references/builder.md +9 -2
  35. package/skills/loadbare-app/references/chrome.md +2 -1
  36. package/skills/loadbare-app/references/custom-elements.md +1 -1
  37. package/skills/loadbare-app/references/data-binding.md +36 -9
  38. package/skills/loadbare-app/references/miswarts.md +140 -0
  39. package/skills/loadbare-app/references/page-files.md +36 -4
  40. package/docs/possible-ideas.md +0 -314
  41. package/docs/theory-old.md +0 -839
  42. package/docs/what-does-loadbare-extend.md +0 -124
@@ -27,17 +27,9 @@ function relativeImport(outDir, file) {
27
27
  const rel = path.relative(outDir, file).split(path.sep).join("/");
28
28
  return rel.startsWith(".") ? rel : `./${rel}`;
29
29
  }
30
- /**
31
- * `undefined` when no page has a requests or queries file — the caller writes
32
- * nothing in that case, the same way an app with no custom elements gets no
33
- * client entry beyond the built-ins.
34
- */
35
- export function pagesSource(pages, pageSources, outDir, packageName) {
36
- if (pageSources.size === 0)
37
- return undefined;
38
- const imports = [
39
- `import { createHub } from ${JSON.stringify(`${packageName}/server`)};`,
40
- ];
30
+ /** An import of each page's modules, and its `Pages` entry naming them. */
31
+ function pageEntries(pages, pageSources, outDir) {
32
+ const imports = [];
41
33
  const entries = [];
42
34
  for (const { name } of pages) {
43
35
  const source = pageSources.get(name);
@@ -55,6 +47,34 @@ export function pagesSource(pages, pageSources, outDir, packageName) {
55
47
  }
56
48
  entries.push(` ${JSON.stringify(name)}: { requests: ${requestsId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
57
49
  }
58
- return ([...imports, "", "export const hub = createHub({", ...entries, "});"].join("\n") + "\n");
50
+ return { imports, entries };
51
+ }
52
+ /**
53
+ * `undefined` when no page has a requests or queries file — the caller writes
54
+ * nothing in that case, the same way an app with no custom elements gets no
55
+ * client entry beyond the built-ins.
56
+ */
57
+ export function pagesSource(pages, pageSources, outDir, packageName) {
58
+ if (pageSources.size === 0)
59
+ return undefined;
60
+ const { imports, entries } = pageEntries(pages, pageSources, outDir);
61
+ return ([
62
+ `import { createHub } from ${JSON.stringify(`${packageName}/server`)};`,
63
+ ...imports,
64
+ "",
65
+ "export const hub = createHub({",
66
+ ...entries,
67
+ "});",
68
+ ].join("\n") + "\n");
69
+ }
70
+ /**
71
+ * The same registry with no `createHub()`, exporting the `Pages` object
72
+ * itself, so the builder can read what each page declares — see
73
+ * build/declared.ts. Resolved from `outDir`, as `pages.ts` is.
74
+ */
75
+ export function declaredSource(pages, pageSources, outDir) {
76
+ const { imports, entries } = pageEntries(pages, pageSources, outDir);
77
+ return ([...imports, "", "export const pages = {", ...entries, "};"].join("\n") +
78
+ "\n");
59
79
  }
60
80
  //# sourceMappingURL=pages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,qFAAqF;AACrF,SAAS,UAAU,CAAC,IAAY,EAAE,MAA8B;IAC9D,OAAO,QAAQ,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,IAAI,MAAM,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,cAAc,CAAC,MAAc,EAAE,IAAY;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClE,OAAO,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;AAChD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,KAAiB,EACjB,WAAoC,EACpC,MAAc,EACd,WAAmB;IAEnB,IAAI,WAAW,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7C,MAAM,OAAO,GAAa;QACxB,6BAA6B,IAAI,CAAC,SAAS,CAAC,GAAG,WAAW,SAAS,CAAC,GAAG;KACxE,CAAC;IACF,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM;YAAE,SAAS;QAEtB,MAAM,UAAU,GAAG,MAAM,CAAC,QAAQ;YAChC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,UAAU,CAAC;YAC9B,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE3E,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YACpB,OAAO,CAAC,IAAI,CACV,wBAAwB,UAAU,WAAW,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CACxG,CAAC;QACJ,CAAC;QACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO,CAAC,IAAI,CACV,uBAAuB,SAAS,WAAW,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CACrG,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,IAAI,CACV,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,iBAAiB,UAAU,IAAI,IAAI,cAAc,SAAS,IAAI,IAAI,KAAK,CACjG,CAAC;IACJ,CAAC;IAED,OAAO,CACL,CAAC,GAAG,OAAO,EAAE,EAAE,EAAE,gCAAgC,EAAE,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,CACxE,IAAI,CACL,GAAG,IAAI,CACT,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Generating the hub's page registry — the server-side counterpart to\n * build/elements.ts's client entry point.\n *\n * `*.requests.ts` and `*.queries.ts` are ordinary TypeScript, found by\n * build/locations.ts and handed here already paired by page name. This\n * module writes an import of each pair, a `Pages` object naming them by\n * page, and the one `createHub()` call. It is written to `<out>/pages.ts` rather than compiled\n * — the application runs it directly, exactly as it does its own server\n * code, so it never needs a bundler step the way the browser-bound client\n * entry does.\n *\n * Each import keeps its `.ts` extension. Node's loader, including its own\n * type stripping, looks for exactly the path it is given and never tries an\n * extension, so a path without one runs under tsx and nowhere else.\n *\n * Nothing here is emitted for a page with neither a requests nor a queries\n * file — a purely static page needs no hub entry, and createHub is only\n * called at all when there is at least one.\n */\n\nimport path from \"node:path\";\nimport type { PageFile, PageSource } from \"./locations.js\";\n\n/** A page name may contain anything a filename can; an import identifier may not. */\nfunction identifier(name: string, suffix: \"Requests\" | \"Queries\"): string {\n return `page_${name.replace(/[^a-zA-Z0-9]/g, \"_\")}_${suffix}`;\n}\n\nfunction relativeImport(outDir: string, file: string): string {\n const rel = path.relative(outDir, file).split(path.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\n/**\n * `undefined` when no page has a requests or queries file — the caller writes\n * nothing in that case, the same way an app with no custom elements gets no\n * client entry beyond the built-ins.\n */\nexport function pagesSource(\n pages: PageFile[],\n pageSources: Map<string, PageSource>,\n outDir: string,\n packageName: string,\n): string | undefined {\n if (pageSources.size === 0) return undefined;\n\n const imports: string[] = [\n `import { createHub } from ${JSON.stringify(`${packageName}/server`)};`,\n ];\n const entries: string[] = [];\n\n for (const { name } of pages) {\n const source = pageSources.get(name);\n if (!source) continue;\n\n const requestsId = source.requests\n ? identifier(name, \"Requests\")\n : undefined;\n const queriesId = source.queries ? identifier(name, \"Queries\") : undefined;\n\n if (source.requests) {\n imports.push(\n `import { requests as ${requestsId} } from ${JSON.stringify(relativeImport(outDir, source.requests))};`,\n );\n }\n if (source.queries) {\n imports.push(\n `import { queries as ${queriesId} } from ${JSON.stringify(relativeImport(outDir, source.queries))};`,\n );\n }\n\n entries.push(\n ` ${JSON.stringify(name)}: { requests: ${requestsId ?? \"{}\"}, queries: ${queriesId ?? \"{}\"} },`,\n );\n }\n\n return (\n [...imports, \"\", \"export const hub = createHub({\", ...entries, \"});\"].join(\n \"\\n\",\n ) + \"\\n\"\n );\n}\n"]}
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generating the hub's page registry — the server-side counterpart to\n * build/elements.ts's client entry point.\n *\n * `*.requests.ts` and `*.queries.ts` are ordinary TypeScript, found by\n * build/locations.ts and handed here already paired by page name. This\n * module writes an import of each pair, a `Pages` object naming them by\n * page, and the one `createHub()` call. It is written to `<out>/pages.ts` rather than compiled\n * — the application runs it directly, exactly as it does its own server\n * code, so it never needs a bundler step the way the browser-bound client\n * entry does.\n *\n * Each import keeps its `.ts` extension. Node's loader, including its own\n * type stripping, looks for exactly the path it is given and never tries an\n * extension, so a path without one runs under tsx and nowhere else.\n *\n * Nothing here is emitted for a page with neither a requests nor a queries\n * file — a purely static page needs no hub entry, and createHub is only\n * called at all when there is at least one.\n */\n\nimport path from \"node:path\";\nimport type { PageFile, PageSource } from \"./locations.js\";\n\n/** A page name may contain anything a filename can; an import identifier may not. */\nfunction identifier(name: string, suffix: \"Requests\" | \"Queries\"): string {\n return `page_${name.replace(/[^a-zA-Z0-9]/g, \"_\")}_${suffix}`;\n}\n\nfunction relativeImport(outDir: string, file: string): string {\n const rel = path.relative(outDir, file).split(path.sep).join(\"/\");\n return rel.startsWith(\".\") ? rel : `./${rel}`;\n}\n\n/** An import of each page's modules, and its `Pages` entry naming them. */\nfunction pageEntries(\n pages: PageFile[],\n pageSources: Map<string, PageSource>,\n outDir: string,\n): { imports: string[]; entries: string[] } {\n const imports: string[] = [];\n const entries: string[] = [];\n\n for (const { name } of pages) {\n const source = pageSources.get(name);\n if (!source) continue;\n\n const requestsId = source.requests\n ? identifier(name, \"Requests\")\n : undefined;\n const queriesId = source.queries ? identifier(name, \"Queries\") : undefined;\n\n if (source.requests) {\n imports.push(\n `import { requests as ${requestsId} } from ${JSON.stringify(relativeImport(outDir, source.requests))};`,\n );\n }\n if (source.queries) {\n imports.push(\n `import { queries as ${queriesId} } from ${JSON.stringify(relativeImport(outDir, source.queries))};`,\n );\n }\n\n entries.push(\n ` ${JSON.stringify(name)}: { requests: ${requestsId ?? \"{}\"}, queries: ${queriesId ?? \"{}\"} },`,\n );\n }\n return { imports, entries };\n}\n\n/**\n * `undefined` when no page has a requests or queries file — the caller writes\n * nothing in that case, the same way an app with no custom elements gets no\n * client entry beyond the built-ins.\n */\nexport function pagesSource(\n pages: PageFile[],\n pageSources: Map<string, PageSource>,\n outDir: string,\n packageName: string,\n): string | undefined {\n if (pageSources.size === 0) return undefined;\n\n const { imports, entries } = pageEntries(pages, pageSources, outDir);\n return (\n [\n `import { createHub } from ${JSON.stringify(`${packageName}/server`)};`,\n ...imports,\n \"\",\n \"export const hub = createHub({\",\n ...entries,\n \"});\",\n ].join(\"\\n\") + \"\\n\"\n );\n}\n\n/**\n * The same registry with no `createHub()`, exporting the `Pages` object\n * itself, so the builder can read what each page declares — see\n * build/declared.ts. Resolved from `outDir`, as `pages.ts` is.\n */\nexport function declaredSource(\n pages: PageFile[],\n pageSources: Map<string, PageSource>,\n outDir: string,\n): string {\n const { imports, entries } = pageEntries(pages, pageSources, outDir);\n return (\n [...imports, \"\", \"export const pages = {\", ...entries, \"};\"].join(\"\\n\") +\n \"\\n\"\n );\n}\n"]}
@@ -5,68 +5,6 @@ This is the complete technical reference to the upcoming Release 1.0.
5
5
  Method-of-work is to continually revise this document to what we want
6
6
  to be true, then revise the code, docs, and tests to ensure it is true.
7
7
 
8
- ## Blockers
9
-
10
- We cannot declare 1.0 until we determine if these blockers can be
11
- added later w/o breaking changes. If we are reasonably confident
12
- that they can be decided later w/o breaking changes, then we rename
13
- them to "Out of Scope" or move them to the roadmap. Otherwise,
14
- they must be resolved before we declare 1.0.
15
-
16
- ### Nested queries
17
-
18
- Imagine an HTML `<select>` that shows a fixed set of choices on every row of
19
- a table, such as `customer_type` for a table of customers. When every
20
- customer may take every customer type, the system as written today is
21
- fine, because every `<select>` receives the same rows.
22
-
23
- But the allowed values may depend on other values in the row.
24
-
25
- - **Decide whether a nested query's rows may depend on its row.** A nested
26
- query has one answer, which every live row receives. See
27
- [Master-detail](#master-detail) for what a nested query is for.
28
-
29
- ```html
30
- <tbody lb-query="accounts">
31
- <template><tr>
32
- <td lb-column="name"></td>
33
- <td><select lb-query="statuses" lb-column="status"><template><option lb-column="label"></option></template></select></td>
34
- </tr></template>
35
- </tbody>
36
- ```
37
-
38
- ### The server API
39
-
40
- - **Land `staticRoutes`.** It closes the last place where an
41
- application hand-writes facts the builder owns.
42
-
43
- ### The builder
44
-
45
- - **Adopt a configuration file and fold `imports.ts` into it.** It holds
46
- `out`, `minify` and `imports`, and lives where `imports.ts` lives now, so a
47
- repository with several applications gets one per application. `--src`
48
- stays a flag, since it is what locates the file. State the precedence
49
- between a flag and a key once. Its key names join the permanent surface,
50
- so this lands before 1.0 or not at all. It reserves a key for the
51
- roadmap's tree-shaking CSS by tag name.
52
- - **Confirm the three output names.** `app.html`, `client.js` and `app.css`
53
- are about to be fixed in `staticRoutes` as well as in the builder.
54
-
55
- ### The internal surface
56
-
57
- None of this is surface. It is listed here because trimming it is free
58
- today and breaking after 1.0.
59
-
60
- - **Audit what the hub does.** Every routine it runs answers to an `lb-*`
61
- attribute, a [reserved query](#reserved-queries), or a
62
- [reserved event](#reserved-events) or [method](#reserved-methods). Trim
63
- whatever does not, and leave what stays clean: no dead code, no routine
64
- written twice, no comment the code contradicts.
65
- - **Keep the rest as constants.** The endpoint, `index` and the timeout are
66
- linkage between the builder and the hub rather than facts an application
67
- varies. Adding a knob for any of them buys a second shape of application
68
- to reason about.
69
-
70
8
  ## The build
71
9
 
72
10
  ### Running the builder
@@ -173,8 +111,8 @@ it is sent to the browser on initial page load, no HTML is ever sent
173
111
  again.
174
112
 
175
113
  The developer writes `lb-*` attributes in markup. Script never assigns
176
- them. The hub and the builder write only their stamps, and the hub writes
177
- the `name` of a radio button in a row template. See
114
+ them. The hub and the builder write only their stamps, apart from the
115
+ few exceptions listed in [Miswarts](./miswarts.md). See
178
116
  [The markup checks](#the-markup-checks).
179
117
 
180
118
  ### Expansion
@@ -1366,10 +1304,10 @@ new URL carries only the query parms the request names.
1366
1304
 
1367
1305
  Every load lands `lb-url`, and a parm the URL does not carry lands empty on
1368
1306
  every element naming it as a column, so a control shows what the address
1369
- bar says after Back as well as after a reload. This is the one exception
1370
- to landing, where a row sets the columns it names and leaves the rest as
1371
- they were: the hub reads the column names under `lb-query="lb-url"` and
1372
- names each one in the row it lands.
1307
+ bar says after Back as well as after a reload
1308
+ ([miswart](./miswarts.md#lb-url-lands-every-parm-it-names)): the hub reads the
1309
+ column names under `lb-query="lb-url"` and names each one in the row it
1310
+ lands.
1373
1311
 
1374
1312
  The hub sends the query parms with every round trip, a page load and a
1375
1313
  request alike. The server passes them to `contextFor` — see
@@ -1518,6 +1456,15 @@ to build on any of these:
1518
1456
  reach the check.
1519
1457
  - `lb-request` naming a value that begins with `lb-` and is not one of the
1520
1458
  three request names.
1459
+ - `lb-query` naming a query the page does not declare. `lb-url` and
1460
+ `lb-choices` are the hub's, and every page and the chrome may name them.
1461
+ The chrome belongs to no page, and names no other query.
1462
+ - `lb-request` naming a request the page does not declare under `handlers`.
1463
+ The chrome sends none of them.
1464
+ - One of the three request names for a query whose `crud` entry does not
1465
+ declare it, and any but `lb-row-update` for `lb-url` or `lb-choices`. The
1466
+ query is a form's own when the form carries `lb-query`, and otherwise the
1467
+ nearest `lb-query` around the element.
1521
1468
  - `lb-url-link` on anything but an `<a>`.
1522
1469
  - `lb-url-push` on an element with no `lb-request`.
1523
1470
  - `lb-url-unknown` on anything but a `<dialog>`, and in the chrome, outside
@@ -1547,6 +1494,10 @@ whose name begins with `lb` and a capital and is not in
1547
1494
 
1548
1495
  `lb-column` with no ancestor row is allowed: the hub gathers from it.
1549
1496
 
1497
+ To know what each page declares, the builder loads the page's
1498
+ `.queries.ts` and `.requests.ts` modules, so their import-time code runs
1499
+ during the build, as it does when the server starts.
1500
+
1550
1501
  Script never assigns an `lb-` attribute, so the markup states every one
1551
1502
  that exists, and checking the markup is sound.
1552
1503
 
@@ -1559,7 +1510,8 @@ uncommon, and Loadbare/app does not shape itself around them.
1559
1510
  by `name`, and a row template repeats one `name` in every live row. The
1560
1511
  hub stamps each radio button's `name` with the `name` written, or its
1561
1512
  column when none is, followed by `-` and the live row's key, so each live
1562
- row holds a group of its own.
1513
+ row holds a group of its own
1514
+ ([miswart](./miswarts.md#radio-button-names-in-a-row-template)).
1563
1515
  - **Several rows not yet entered, each holding radio buttons.** A
1564
1516
  [row not yet entered](#a-row-not-yet-entered) has an empty key, so the
1565
1517
  radio buttons of every such row share one `name` and form one group.
@@ -1821,9 +1773,12 @@ It has four optional keys.
1821
1773
  | `onChoices` | Nothing | Runs with every choice made on the page, each time the user makes one |
1822
1774
 
1823
1775
  The server resolves a declared request against `handlers`, and a request
1824
- Loadbare provides against `crud` under the request's query. A name with no
1825
- entry there logs a server console warning naming the page and the missing
1826
- entry, and answers 200 with no response items, so nothing lands and
1776
+ Loadbare provides against `crud` under the request's query. The builder
1777
+ refuses markup naming a request the page does not declare; see
1778
+ [The markup checks](#the-markup-checks). A name with no entry there can
1779
+ still arrive, from a widget's script or from outside the page, and it
1780
+ logs a server console warning naming the page and the missing entry, and
1781
+ answers 200 with no response items, so nothing lands and
1827
1782
  `lb-request-pending` clears from the element that issued the request. A
1828
1783
  request carrying no name answers 400, and a `run` that throws answers 500.
1829
1784