@loadbare/app 0.5.5 → 0.6.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 (81) hide show
  1. package/README.md +3 -4
  2. package/dist/build/assemble.d.ts +1 -1
  3. package/dist/build/assemble.d.ts.map +1 -1
  4. package/dist/build/assemble.js +80 -7
  5. package/dist/build/cli.d.ts +2 -2
  6. package/dist/build/cli.js +2 -2
  7. package/dist/build/expand.d.ts.map +1 -1
  8. package/dist/build/expand.js +30 -34
  9. package/dist/build/locations.d.ts +3 -3
  10. package/dist/build/locations.d.ts.map +1 -1
  11. package/dist/build/locations.js +15 -3
  12. package/dist/build/origins.d.ts +0 -13
  13. package/dist/build/origins.d.ts.map +1 -1
  14. package/dist/build/origins.js +32 -8
  15. package/dist/build/pages.d.ts +3 -3
  16. package/dist/build/pages.d.ts.map +1 -1
  17. package/dist/build/pages.js +9 -7
  18. package/dist/core/lb-constants.d.ts +17 -12
  19. package/dist/core/lb-constants.d.ts.map +1 -1
  20. package/dist/core/lb-constants.js +106 -43
  21. package/dist/core/lb-types.d.ts +103 -62
  22. package/dist/core/lb-types.d.ts.map +1 -1
  23. package/dist/core/lb-types.js +11 -3
  24. package/dist/hub/lb-apply.d.ts +18 -4
  25. package/dist/hub/lb-apply.d.ts.map +1 -1
  26. package/dist/hub/lb-apply.js +184 -34
  27. package/dist/hub/lb-hub.browser.d.ts +1 -1
  28. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  29. package/dist/hub/lb-hub.browser.js +165 -83
  30. package/dist/server/lb-express.d.ts +7 -4
  31. package/dist/server/lb-express.d.ts.map +1 -1
  32. package/dist/server/lb-express.js +45 -33
  33. package/dist/server/lb-server.d.ts +67 -45
  34. package/dist/server/lb-server.d.ts.map +1 -1
  35. package/dist/server/lb-server.js +55 -18
  36. package/dist/tests/assemble.test.js +154 -2
  37. package/dist/tests/expand.test.js +30 -3
  38. package/dist/tests/helpers/hub.d.ts +73 -0
  39. package/dist/tests/helpers/hub.d.ts.map +1 -0
  40. package/dist/tests/helpers/hub.js +151 -0
  41. package/dist/tests/lb-apply.test.js +86 -62
  42. package/dist/tests/lb-express.test.d.ts +1 -1
  43. package/dist/tests/lb-express.test.js +43 -38
  44. package/dist/tests/lb-hub.test.d.ts +14 -0
  45. package/dist/tests/lb-hub.test.d.ts.map +1 -0
  46. package/dist/tests/lb-hub.test.js +319 -0
  47. package/dist/tests/{lb-rows.test.d.ts → lb-list.test.d.ts} +1 -1
  48. package/dist/tests/lb-list.test.d.ts.map +1 -0
  49. package/dist/tests/{lb-rows.test.js → lb-list.test.js} +109 -106
  50. package/dist/tests/lb-server.test.js +151 -100
  51. package/dist/tests/origins.test.js +19 -1
  52. package/dist/tests/pages.test.d.ts +1 -1
  53. package/dist/tests/pages.test.js +64 -14
  54. package/docs/TECHREF-1.0.md +1000 -0
  55. package/docs/reference/builder.md +4 -4
  56. package/docs/reference/chrome.md +56 -5
  57. package/docs/reference/custom-elements.md +59 -36
  58. package/docs/reference/data-binding.md +142 -84
  59. package/docs/reference/overview.md +1 -1
  60. package/docs/reference/page-files.md +64 -49
  61. package/docs/reference/server.md +7 -6
  62. package/docs/reference/widgets.md +43 -32
  63. package/docs/roadmap.md +68 -22
  64. package/docs/testing.md +47 -17
  65. package/docs/theory.md +2 -2
  66. package/docs/tutorials/010-pages-and-navigation.md +14 -8
  67. package/docs/tutorials/040-displaying-data.md +9 -9
  68. package/docs/tutorials/050-actions.md +5 -5
  69. package/docs/tutorials/060-custom-element-code.md +1 -1
  70. package/docs/tutorials/065-conditional-rendering.md +4 -4
  71. package/docs/tutorials/070-displaying-a-list.md +24 -47
  72. package/docs/tutorials/072-inserting-into-a-list.md +17 -17
  73. package/docs/tutorials/074-deleting-from-a-list.md +15 -17
  74. package/docs/tutorials/076-updating-a-list-item.md +20 -22
  75. package/docs/tutorials/080-widget-requests.md +27 -23
  76. package/docs/tutorials/090-using-widget-libraries.md +23 -1
  77. package/package.json +1 -2
  78. package/dist/hub/lb-rows.d.ts +0 -18
  79. package/dist/hub/lb-rows.d.ts.map +0 -1
  80. package/dist/hub/lb-rows.js +0 -106
  81. package/dist/tests/lb-rows.test.d.ts.map +0 -1
package/README.md CHANGED
@@ -4,17 +4,18 @@ The fundamentals of @loadbare/app are fixed, we are now working
4
4
  on completeness.
5
5
 
6
6
  > This package is being prepped for 1.0, current work is:
7
+ >
7
8
  > 1. docs (nearly done)
8
9
  > 2. demos (must be normalized and checked against docs)
9
10
  > 3. test a release/use cycle, make sure all instructions work
10
11
 
11
-
12
12
  ## Purpose
13
13
 
14
14
  Loadbare App is a web app framework built specifically to deliver high performance
15
- to the user for server-bound applications.
15
+ to the user for server-bound applications.
16
16
 
17
17
  Loadbare App has two twin primary goals
18
+
18
19
  - To follow the loadbare philosophy of reducing accidental complexity
19
20
  to near zero, so the app developer is spending maximum time on the app
20
21
  instead of the tooling.
@@ -62,5 +63,3 @@ each lesson.
62
63
  ## Roadmap Items for 1.0 and 1.1
63
64
 
64
65
  [Roadmap](./docs/roadmap.md)
65
-
66
-
@@ -6,7 +6,7 @@
6
6
  * built-in widgets and whatever definitions were found in the caller's own
7
7
  * source tree. `expand()` only ever sees a fragment, so the chrome's own is
8
8
  * the text between its `<body>` tags; a page's is the whole file. A page's
9
- * expanded host is wrapped in `<template id="page-<name>">` and, together
9
+ * expanded host is wrapped in `<template lb-page="<name>">` and, together
10
10
  * with the chrome's own expanded body content, spliced back into the
11
11
  * chrome's `<body>`. The result is the one document a server has to serve
12
12
  * for every route — see docs/tutorials/000-getting-started.md and
@@ -1 +1 @@
1
- {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAeH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAuB/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,GACN,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CtC"}
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../../build/assemble.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAoBH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAwH/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,8EAA8E;IAC9E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,wBAAsB,QAAQ,CAAC,EAC7B,UAAU,EACV,KAAK,EACL,KAAK,GACN,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CA2CtC"}
@@ -6,7 +6,7 @@
6
6
  * built-in widgets and whatever definitions were found in the caller's own
7
7
  * source tree. `expand()` only ever sees a fragment, so the chrome's own is
8
8
  * the text between its `<body>` tags; a page's is the whole file. A page's
9
- * expanded host is wrapped in `<template id="page-<name>">` and, together
9
+ * expanded host is wrapped in `<template lb-page="<name>">` and, together
10
10
  * with the chrome's own expanded body content, spliced back into the
11
11
  * chrome's `<body>`. The result is the one document a server has to serve
12
12
  * for every route — see docs/tutorials/000-getting-started.md and
@@ -20,12 +20,17 @@ import { readFile } from "node:fs/promises";
20
20
  import { JSDOM } from "jsdom";
21
21
  import { addDefinitions, customElementTags, expand, } from "./expand.js";
22
22
  import { formatHtml } from "./format.js";
23
- import { ATTR_UNKNOWN_PAGE, PAGE_TEMPLATE_PREFIX, } from "../core/lb-constants.js";
23
+ import { ATTR_CELL, ATTR_KEY, ATTR_LIST, ATTR_PAGE, ATTR_ROW, ATTR_UNKNOWN_PAGE, HUB_TAG_NAME, } from "../core/lb-constants.js";
24
24
  /**
25
- * The hub calls showModal() on whatever carries lb-unknown-page (hub/lb-hub.browser.ts),
26
- * so a chrome that puts the attribute on anything else would fail at
27
- * runtime, on whichever page happens to 404 first, rather than at build
28
- * time. The attribute is optional; only a wrong element on it is an error.
25
+ * The hub calls showModal() on whatever carries lb-unknown-page
26
+ * (hub/lb-hub.browser.ts), so a chrome that puts the attribute on anything
27
+ * else would fail at runtime, on whichever page happens to 404 first, rather
28
+ * than at build time. The attribute is optional; a wrong element on it, or a
29
+ * right one out of the hub's reach, is an error.
30
+ *
31
+ * Out of reach because the hub looks for the dialog inside itself, as it
32
+ * does for everything else it acts on. A dialog elsewhere in the body would
33
+ * never open and would say nothing about why.
29
34
  */
30
35
  function checkUnknownPageDialog(chromeFile, expandedBody) {
31
36
  const fragment = JSDOM.fragment(expandedBody);
@@ -36,6 +41,72 @@ function checkUnknownPageDialog(chromeFile, expandedBody) {
36
41
  throw new Error(`assemble: '${chromeFile}' declares ${ATTR_UNKNOWN_PAGE} on ` +
37
42
  `<${el.tagName.toLowerCase()}>, must be a <dialog>`);
38
43
  }
44
+ if (!el.closest(HUB_TAG_NAME)) {
45
+ throw new Error(`assemble: '${chromeFile}' declares ${ATTR_UNKNOWN_PAGE} outside ` +
46
+ `<${HUB_TAG_NAME}>, where the hub cannot reach it`);
47
+ }
48
+ }
49
+ /**
50
+ * The binding vocabulary, checked once against the finished document rather
51
+ * than discovered one console error at a time in a browser.
52
+ *
53
+ * Everything here is a fact the markup states about itself, so none of it
54
+ * needs the server: a key is named on a row template, a row template belongs
55
+ * to a list, and a cell belongs to a scope. What the markup and the server
56
+ * say about cardinality cannot be compared here, because they are separate
57
+ * artifacts — createHub holds that check. See docs/reference/data-binding.md.
58
+ *
59
+ * A list scope with no row template is not an error. It is bound to the list
60
+ * without displaying it, which is what an insert form naming the list it adds
61
+ * to already is.
62
+ *
63
+ * A `<template>`'s children are walked in the scope that encloses the
64
+ * template, because a row template's clones land in the list around it.
65
+ */
66
+ function walkBinding(node, scope, problems) {
67
+ for (const el of node.children) {
68
+ const listName = el.getAttribute(ATTR_LIST);
69
+ const rowName = el.getAttribute(ATTR_ROW);
70
+ const tag = el.localName;
71
+ let here = scope;
72
+ if (listName !== null && rowName !== null) {
73
+ problems.push(`<${tag}> carries both ${ATTR_LIST} and ${ATTR_ROW}; a scope is ` +
74
+ `one or the other, and a name answers with one shape`);
75
+ }
76
+ if (listName !== null)
77
+ here = { kind: "list", name: listName };
78
+ else if (rowName !== null)
79
+ here = { kind: "row", name: rowName };
80
+ if (el.hasAttribute(ATTR_CELL) && here === null) {
81
+ problems.push(`<${tag}> carries ${ATTR_CELL}="${el.getAttribute(ATTR_CELL)}" with ` +
82
+ `no ${ATTR_LIST} or ${ATTR_ROW} scope around it`);
83
+ }
84
+ if (el.hasAttribute(ATTR_KEY)) {
85
+ if (tag !== "template") {
86
+ problems.push(`<${tag}> carries ${ATTR_KEY}; it names the column that identifies ` +
87
+ `a row and belongs on the row <template>`);
88
+ }
89
+ else if (here === null || here.kind !== "list") {
90
+ problems.push(`a <template> carries ${ATTR_KEY} outside any ${ATTR_LIST} scope; ` +
91
+ `only a list has rows to identify`);
92
+ }
93
+ }
94
+ walkBinding(el, here, problems);
95
+ // By tag name rather than by instanceof: JSDOM.fragment() parses into a
96
+ // document with no window, so the constructor is not reachable from the
97
+ // node.
98
+ if (tag === "template") {
99
+ walkBinding(el.content, here, problems);
100
+ }
101
+ }
102
+ }
103
+ /** Check one expanded fragment, and name the file if anything is wrong. */
104
+ function checkBinding(file, html) {
105
+ const problems = [];
106
+ walkBinding(JSDOM.fragment(html), null, problems);
107
+ if (problems.length > 0) {
108
+ throw new Error(`assemble: '${file}' binding:\n ` + problems.join(`\n `));
109
+ }
39
110
  }
40
111
  export async function assemble({ chromeFile, pages, scans, }) {
41
112
  // One layer per origin, in order, so a later origin's definition overrides
@@ -49,7 +120,8 @@ export async function assemble({ chromeFile, pages, scans, }) {
49
120
  for (const { name, file } of pages) {
50
121
  const authored = await readFile(file, "utf-8");
51
122
  const host = expand(authored, defs);
52
- templates.push(`<template id="${PAGE_TEMPLATE_PREFIX}${name}">${host}</template>`);
123
+ checkBinding(file, host);
124
+ templates.push(`<template ${ATTR_PAGE}="${name}">${host}</template>`);
53
125
  }
54
126
  let chromeSource;
55
127
  try {
@@ -65,6 +137,7 @@ export async function assemble({ chromeFile, pages, scans, }) {
65
137
  const [whole, bodyAttrs, bodyContent] = body;
66
138
  const expandedBody = expand(bodyContent, defs);
67
139
  checkUnknownPageDialog(chromeFile, expandedBody);
140
+ checkBinding(chromeFile, expandedBody);
68
141
  const withPages = chromeSource.replace(whole, () => `<body${bodyAttrs}>${expandedBody}\n${templates.join("\n")}\n</body>`);
69
142
  return {
70
143
  html: await formatHtml(withPages),
@@ -7,7 +7,7 @@
7
7
  * loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
8
8
  *
9
9
  * `--src` names the application's own tree: the one `chrome.html` file,
10
- * every `*.page.html` page, its `*.hooks.ts`/`*.queries.ts` pairs, and
10
+ * every `*.page.html` page, its `*.requests.ts`/`*.queries.ts` pairs, and
11
11
  * `imports.ts` if there is one — see build/locations.ts. It is also the last
12
12
  * origin in the cascade that supplies widget definitions, widget scripts and
13
13
  * stylesheets; this package's own widgets come first and each package named
@@ -17,7 +17,7 @@
17
17
  * The assembled document is written to `<out>/app.html`; the client bundle
18
18
  * to `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
19
19
  * build/elements.ts for how a tag is resolved to the script that registers
20
- * it. When any page has a hooks or queries file, the hub registry that used
20
+ * it. When any page has a requests or queries file, the hub registry that used
21
21
  * to be hand-maintained is written to `<out>/pages.ts` — see build/pages.ts.
22
22
  * Every origin's stylesheets are concatenated in that same cascade order and
23
23
  * written to `<out>/app.css` — see build/styles.ts.
package/dist/build/cli.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * loadbare-app-build [--src src] [--out dist] [--watch] [--minify]
8
8
  *
9
9
  * `--src` names the application's own tree: the one `chrome.html` file,
10
- * every `*.page.html` page, its `*.hooks.ts`/`*.queries.ts` pairs, and
10
+ * every `*.page.html` page, its `*.requests.ts`/`*.queries.ts` pairs, and
11
11
  * `imports.ts` if there is one — see build/locations.ts. It is also the last
12
12
  * origin in the cascade that supplies widget definitions, widget scripts and
13
13
  * stylesheets; this package's own widgets come first and each package named
@@ -17,7 +17,7 @@
17
17
  * The assembled document is written to `<out>/app.html`; the client bundle
18
18
  * to `<out>/client.js`, by way of a generated `<out>/client-entry.ts` — see
19
19
  * build/elements.ts for how a tag is resolved to the script that registers
20
- * it. When any page has a hooks or queries file, the hub registry that used
20
+ * it. When any page has a requests or queries file, the hub registry that used
21
21
  * to be hand-maintained is written to `<out>/pages.ts` — see build/pages.ts.
22
22
  * Every origin's stylesheets are concatenated in that same cascade order and
23
23
  * written to `<out>/app.css` — see build/styles.ts.
@@ -1 +1 @@
1
- {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA0CH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA8BjD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB1E;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,OAAO,CAAC,WAAW,CAAC,CAStB;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAS3D;AAkPD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAIhE"}
1
+ {"version":3,"file":"expand.d.ts","sourceRoot":"","sources":["../../build/expand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkCH,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AA8BjD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB1E;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GACzB,OAAO,CAAC,WAAW,CAAC,CAStB;AA4CD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAS3D;AA8OD;;;;;GAKG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAIhE"}
@@ -11,29 +11,26 @@
11
11
  */
12
12
  import { readdir, readFile } from "node:fs/promises";
13
13
  import { JSDOM } from "jsdom";
14
- import { ATTR_SLOT, ATTR_TEMPLATE, HUB_TAG_NAME, } from "../core/lb-constants.js";
15
- /**
16
- * The framework's reserved namespace. Every one of these must resolve to a
17
- * definition — a typo here is caught immediately rather than shipped as an
18
- * inert tag. A tag outside this namespace still expands when a definition
19
- * exists for it (an application is free to name its own widgets anything),
20
- * but is not required to have one: with no definition, it is assumed to be
21
- * a plain custom element, registered by script alone.
22
- */
23
- const WIDGET_TAG = /^LB-/;
14
+ import { ATTR_SLOT, ATTR_TEMPLATE } from "../core/lb-constants.js";
15
+ /** Lowercase, hyphenated — the one shape a custom element tag has. */
16
+ const CUSTOM_ELEMENT = /-/;
17
+ /** A placeholder is an entire attribute value or an entire text node. */
24
18
  /**
25
- * The one LB-* tag that is not a widget: the hub itself, always present in
26
- * the chrome, registered by script rather than by definition. Expanding the
27
- * chrome (see build/assemble.ts) would otherwise demand a definition for it
28
- * that could never exist.
19
+ * `{{name}}` or `{{name|default}}`. The default is a literal that stands in
20
+ * when the tag supplies nothing; it is not evaluated and nothing branches on
21
+ * it. Whitespace around the name and the default is not part of either, so
22
+ * `{{button-text | OK}}` is the same placeholder as `{{button-text|OK}}` —
23
+ * a spaced form that shipped as literal braces would be a silent failure
24
+ * an author would find in the browser.
29
25
  */
30
- const HUB_TAG = HUB_TAG_NAME.toUpperCase();
31
- /** A placeholder is an entire attribute value or an entire text node. */
32
- const PLACEHOLDER = /^\{\{([A-Za-z][\w-]*)\}\}$/;
26
+ const PLACEHOLDER = /^\{\{\s*([A-Za-z][\w-]*)\s*(?:\|\s*([^}]*?)\s*)?\}\}$/;
33
27
  /**
34
28
  * Marks an attribute as input to expansion. Three namespaces share the tag:
35
- * `lb-` is the hub's, `exp-` is expansion's, and everything unprefixed is
36
- * HTML's and means what HTML says it means. So an author is free to write
29
+ * `lb-` holds the names Loadbare defines, `exp-` the names a widget author
30
+ * defines, and everything unprefixed is HTML's and means what HTML says it
31
+ * means. The split is by owner rather than by phase — `lb-slot` and
32
+ * `lb-template` are expansion's own and never reach the browser, while
33
+ * `lb-list` and `lb-cell` are read at both times. So an author is free to write
37
34
  * `class`, `title` or `hidden` on any widget, whatever its parameters happen
38
35
  * to be named. The prefix marks the slot on the tag, where the ambiguity was;
39
36
  * inside a definition `{{ }}` already says the same thing, so `exp-label`
@@ -171,13 +168,13 @@ export function customElementTags(html) {
171
168
  }
172
169
  return tags;
173
170
  }
174
- /** Which widgets a definition uses. Undefined tags are caught during expansion. */
175
- function referencesOf(source) {
171
+ /** Which definitions a definition uses. A tag with no definition expands to nothing and cannot cycle. */
172
+ function referencesOf(source, defs) {
176
173
  const refs = new Set();
177
174
  for (const scope of scopes(parse(source))) {
178
175
  for (const el of scope.querySelectorAll("*")) {
179
176
  const tag = el.tagName.toUpperCase();
180
- if (WIDGET_TAG.test(tag))
177
+ if (CUSTOM_ELEMENT.test(tag) && tag in defs)
181
178
  refs.add(tag);
182
179
  }
183
180
  }
@@ -190,7 +187,7 @@ function referencesOf(source) {
190
187
  function checkAcyclic(defs) {
191
188
  const refs = new Map();
192
189
  for (const [tag, source] of Object.entries(defs)) {
193
- refs.set(tag, referencesOf(source));
190
+ refs.set(tag, referencesOf(source, defs));
194
191
  }
195
192
  const done = new Set();
196
193
  const path = [];
@@ -212,7 +209,8 @@ function checkAcyclic(defs) {
212
209
  visit(tag);
213
210
  }
214
211
  /**
215
- * Resolve a placeholder against what the author wrote. Unset means absent.
212
+ * Resolve a placeholder against what the author wrote: the supplied value,
213
+ * else the definition's default, else absent.
216
214
  *
217
215
  * Every name the definition asks for is recorded, whether or not it was
218
216
  * supplied, so expansion can report a parameter the definition never declared.
@@ -223,7 +221,7 @@ function resolve(text, attrs, asked) {
223
221
  return text;
224
222
  const name = match[1];
225
223
  asked.add(name);
226
- return attrs.get(name) ?? null;
224
+ return attrs.get(name) ?? match[2] ?? null;
227
225
  }
228
226
  function substitute(root, attrs, asked) {
229
227
  for (const scope of scopes(root)) {
@@ -326,12 +324,8 @@ function fillSlot(tag, root, content) {
326
324
  slot.append(...content);
327
325
  slot.removeAttribute(ATTR_SLOT);
328
326
  }
329
- function expandElement(el, defs) {
327
+ function expandElement(el, defs, source) {
330
328
  const tag = el.tagName.toUpperCase();
331
- const source = defs[tag];
332
- if (source === undefined) {
333
- throw new Error(`expand: <${tag.toLowerCase()}> has no definition. Every hub tag must resolve.`);
334
- }
335
329
  // The tag survives expansion carrying every attribute the author wrote,
336
330
  // `exp-` ones included. They cost nothing where they are, and what ships
337
331
  // shows what was asked for beside what it produced.
@@ -360,10 +354,12 @@ function expandElement(el, defs) {
360
354
  }
361
355
  function walk(root, defs) {
362
356
  for (const child of [...root.children]) {
363
- const tag = child.tagName.toUpperCase();
364
- if (tag in defs || (WIDGET_TAG.test(tag) && tag !== HUB_TAG)) {
365
- expandElement(child, defs);
366
- }
357
+ const source = defs[child.tagName.toUpperCase()];
358
+ // A tag with no definition is left alone. It is a custom element a
359
+ // script registers, and build/elements.ts is where a tag that has
360
+ // neither a definition nor a script is refused — one rule, one place.
361
+ if (source !== undefined)
362
+ expandElement(child, defs, source);
367
363
  // Definitions may use other widgets, so descend after expanding. The
368
364
  // acyclicity check is what makes this terminate.
369
365
  walk(child, defs);
@@ -8,7 +8,7 @@
8
8
  * filename suffix does instead: `chrome.html` is the one chrome file, and
9
9
  * `*.page.html` is a page.
10
10
  *
11
- * A page's server half is named the same way: `*.hooks.ts` and
11
+ * A page's server half is named the same way: `*.requests.ts` and
12
12
  * `*.queries.ts` pair with a `*.page.html` by sharing its basename, and are
13
13
  * free to sit anywhere too. Either may be absent — a page with no data needs
14
14
  * neither — but one with no matching `*.page.html` is a naming mistake and
@@ -32,8 +32,8 @@ export interface PageFile {
32
32
  file: string;
33
33
  }
34
34
  export interface PageSource {
35
- /** Absolute path, without extension, to the `.hooks.ts` module, if this page has one. */
36
- hooks?: string;
35
+ /** Absolute path, without extension, to the `.requests.ts` module, if this page has one. */
36
+ requests?: string;
37
37
  /** Absolute path, without extension, to the `.queries.ts` module, if this page has one. */
38
38
  queries?: string;
39
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../build/locations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,yFAAyF;IACzF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,UAAU;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,gHAAgH;AAChH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA6ClE;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC;AAEtC,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAmBxD;AAED,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CA6C7E"}
1
+ {"version":3,"file":"locations.d.ts","sourceRoot":"","sources":["../../build/locations.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAOH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAQlD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,UAAU;IAClB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,gHAAgH;AAChH,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA2ClE;AAED,MAAM,MAAM,WAAW,GAAG,WAAW,CAAC;AAEtC,wBAAsB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAmBxD;AAED,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CA2D7E"}
@@ -8,7 +8,7 @@
8
8
  * filename suffix does instead: `chrome.html` is the one chrome file, and
9
9
  * `*.page.html` is a page.
10
10
  *
11
- * A page's server half is named the same way: `*.hooks.ts` and
11
+ * A page's server half is named the same way: `*.requests.ts` and
12
12
  * `*.queries.ts` pair with a `*.page.html` by sharing its basename, and are
13
13
  * free to sit anywhere too. Either may be absent — a page with no data needs
14
14
  * neither — but one with no matching `*.page.html` is a naming mistake and
@@ -32,7 +32,7 @@ import { findPackageRoot } from "./package-root.js";
32
32
  export { CUSTOM_ELEMENT_TAG } from "./origins.js";
33
33
  const CHROME_FILENAME = "chrome.html";
34
34
  const PAGE_SUFFIX = ".page.html";
35
- const HOOKS_SUFFIX = ".hooks.ts";
35
+ const HOOKS_SUFFIX = ".requests.ts";
36
36
  const QUERIES_SUFFIX = ".queries.ts";
37
37
  const IMPORTS_FILENAME = "imports.ts";
38
38
  /** The raw walk, with no uniqueness checks — usable on its own by anything that does not need a chrome file. */
@@ -70,7 +70,8 @@ export async function discoverSrc(src) {
70
70
  pages.push({ name: entry.name.slice(0, -PAGE_SUFFIX.length), file });
71
71
  }
72
72
  else if (entry.name.endsWith(HOOKS_SUFFIX)) {
73
- pageSource(entry.name.slice(0, -HOOKS_SUFFIX.length)).hooks = file.slice(0, -".ts".length);
73
+ pageSource(entry.name.slice(0, -HOOKS_SUFFIX.length)).requests =
74
+ file.slice(0, -".ts".length);
74
75
  }
75
76
  else if (entry.name.endsWith(QUERIES_SUFFIX)) {
76
77
  pageSource(entry.name.slice(0, -QUERIES_SUFFIX.length)).queries =
@@ -116,6 +117,17 @@ export async function resolveLocations(args) {
116
117
  throw new Error(`locations: more than one ${IMPORTS_FILENAME} found under '${args.src}': ` +
117
118
  importsCandidates.join(", "));
118
119
  }
120
+ // Sorted by name above, so duplicates are adjacent. A stub is the page's
121
+ // whole identity — its route, its template and its server half all key on
122
+ // it — so two files claiming one stub is a naming mistake, not a merge.
123
+ for (let i = 1; i < pages.length; i++) {
124
+ const [prev, here] = [pages[i - 1], pages[i]];
125
+ if (prev.name === here.name) {
126
+ throw new Error(`locations: more than one '${here.name}${PAGE_SUFFIX}' found under ` +
127
+ `'${args.src}': ${prev.file}, ${here.file}; the page namespace is ` +
128
+ `flat, so a stub names one page`);
129
+ }
130
+ }
119
131
  const pageNames = new Set(pages.map((p) => p.name));
120
132
  for (const name of pageSources.keys()) {
121
133
  if (!pageNames.has(name)) {
@@ -71,19 +71,6 @@ export interface OriginScan {
71
71
  /** Every `.css` file here, sorted by filename with the full path breaking a tie. */
72
72
  css: string[];
73
73
  }
74
- /**
75
- * One origin's contribution, classified by filename alone.
76
- *
77
- * A `.html` file is a widget's only when its basename is a tag, and a module
78
- * only when it is `<tag>.browser.ts` — which is what excludes `chrome.html`,
79
- * `about.page.html`, `about.hooks.ts` and `imports.ts` here without naming
80
- * any of them. Those are the application's own files, and
81
- * build/locations.ts picks them out of `src` separately.
82
- *
83
- * A module named after a tag without the `.browser` goes in `unmarked`, so
84
- * an author who forgot the word gets told which file to rename rather than
85
- * being told their tag does not exist.
86
- */
87
74
  export declare function scanOrigin(origin: Origin): Promise<OriginScan>;
88
75
  /** The shape of an `imports.ts` module's default export: the packages an application pulls widgets from. */
89
76
  export type ElementManifest = string[];
@@ -1 +1 @@
1
- {"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../../build/origins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAOH,gHAAgH;AAChH,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AASlE,MAAM,WAAW,MAAM;IACrB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,qCAAqC;IACrC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AAsBD;;;;;;;;;;;;GAYG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4EpE;AAED,4GAA4G;AAC5G,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC;AAEvC;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAwED,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,aAAa,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBnB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAIvB;AAED,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAErD"}
1
+ {"version":3,"file":"origins.d.ts","sourceRoot":"","sources":["../../build/origins.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAOH,gHAAgH;AAChH,eAAO,MAAM,kBAAkB,QAAkC,CAAC;AASlE,MAAM,WAAW,MAAM;IACrB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;;OAKG;IACH,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,qCAAqC;IACrC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,oFAAoF;IACpF,GAAG,EAAE,MAAM,EAAE,CAAC;CACf;AA2DD,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CA4EpE;AAED,4GAA4G;AAC5G,MAAM,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC;AAEvC;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,eAAe,CAAC,CA4B1B;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAwED,MAAM,WAAW,WAAW;IAC1B,+FAA+F;IAC/F,aAAa,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,eAAe,EACzB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,EAAE,CAAC,CAkBnB;AAED;;;GAGG;AACH,wBAAsB,OAAO,CAC3B,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,EAAE,CAAC,CAIvB;AAED,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,MAAM,EAAE,CAErD"}
@@ -76,7 +76,7 @@ async function filesUnder(dir) {
76
76
  *
77
77
  * A `.html` file is a widget's only when its basename is a tag, and a module
78
78
  * only when it is `<tag>.browser.ts` — which is what excludes `chrome.html`,
79
- * `about.page.html`, `about.hooks.ts` and `imports.ts` here without naming
79
+ * `about.page.html`, `about.requests.ts` and `imports.ts` here without naming
80
80
  * any of them. Those are the application's own files, and
81
81
  * build/locations.ts picks them out of `src` separately.
82
82
  *
@@ -84,6 +84,29 @@ async function filesUnder(dir) {
84
84
  * an author who forgot the word gets told which file to rename rather than
85
85
  * being told their tag does not exist.
86
86
  */
87
+ /**
88
+ * The tag a filename claims, or null when it claims none.
89
+ *
90
+ * A filename is the tag verbatim, never folded. HTML matches an element name
91
+ * case-insensitively, so folding would let `My-Widget.html` and
92
+ * `my-widget.html` be two files for one tag — a collision on a
93
+ * case-sensitive filesystem and one file on a case-insensitive one, which is
94
+ * a difference between machines rather than a rule anyone can read.
95
+ *
96
+ * A name that is a tag only once folded is refused rather than ignored: the
97
+ * author meant a widget, and silence would leave them with a tag that never
98
+ * expands.
99
+ */
100
+ function tagIn(stem, file) {
101
+ if (CUSTOM_ELEMENT_TAG.test(stem))
102
+ return stem;
103
+ const folded = stem.toLowerCase();
104
+ if (!CUSTOM_ELEMENT_TAG.test(folded))
105
+ return null;
106
+ const suffix = path.basename(file).slice(stem.length);
107
+ throw new Error(`origins: '${file}' carries capitals; a tag is lowercase, so this ` +
108
+ `file must be named '${folded}${suffix}'`);
109
+ }
87
110
  export async function scanOrigin(origin) {
88
111
  const definitions = new Map();
89
112
  const scripts = new Map();
@@ -104,20 +127,19 @@ export async function scanOrigin(origin) {
104
127
  css.push(file);
105
128
  }
106
129
  else if (name.endsWith(HTML_SUFFIX)) {
107
- const tag = name.slice(0, -HTML_SUFFIX.length).toLowerCase();
108
- if (CUSTOM_ELEMENT_TAG.test(tag)) {
130
+ const tag = tagIn(name.slice(0, -HTML_SUFFIX.length), file);
131
+ if (tag)
109
132
  claim(definitions, "definition", tag, file);
110
- }
111
133
  }
112
134
  else {
113
135
  const browser = BROWSER_SUFFIXES.find((s) => name.endsWith(s));
114
136
  if (browser) {
115
- const tag = name.slice(0, -browser.length).toLowerCase();
137
+ const tag = tagIn(name.slice(0, -browser.length), file);
116
138
  // Only the trailing ".ts" comes off, never the ".browser": what is
117
139
  // left becomes an import specifier, and esbuild resolves
118
140
  // "lb-thing.browser" to "lb-thing.browser.ts" the same way as to
119
141
  // ".js".
120
- if (CUSTOM_ELEMENT_TAG.test(tag)) {
142
+ if (tag) {
121
143
  const ext = MODULE_SUFFIXES.find((e) => name.endsWith(e));
122
144
  claim(scripts, "script", tag, file.slice(0, -ext.length));
123
145
  }
@@ -130,8 +152,10 @@ export async function scanOrigin(origin) {
130
152
  // code. It contributes nothing. Remembering it costs a map entry and
131
153
  // buys an error message that names the file instead of describing an
132
154
  // absence. A `.d.ts` never reaches here — stripping ".ts" leaves a
133
- // dot, which is not a tag.
134
- const tag = name.slice(0, -ext.length).toLowerCase();
155
+ // dot, which is not a tag. Tested rather than tagIn()'d: an unmarked
156
+ // module was never claimed to be a widget, so a capital in its name
157
+ // is not a mistake anyone here can see.
158
+ const tag = name.slice(0, -ext.length);
135
159
  if (CUSTOM_ELEMENT_TAG.test(tag) && !unmarked.has(tag)) {
136
160
  unmarked.set(tag, file);
137
161
  }
@@ -2,7 +2,7 @@
2
2
  * Generating the hub's page registry — the server-side counterpart to
3
3
  * build/elements.ts's client entry point.
4
4
  *
5
- * `*.hooks.ts` and `*.queries.ts` are ordinary TypeScript, found by
5
+ * `*.requests.ts` and `*.queries.ts` are ordinary TypeScript, found by
6
6
  * build/locations.ts and handed here already paired by page name. What this
7
7
  * module writes is the file an application used to hand-maintain: an import
8
8
  * of each pair, a `Pages` object naming them by page, and the one
@@ -11,13 +11,13 @@
11
11
  * code, so it never needs a bundler step the way the browser-bound client
12
12
  * entry does.
13
13
  *
14
- * Nothing here is emitted for a page with neither a hooks nor a queries
14
+ * Nothing here is emitted for a page with neither a requests nor a queries
15
15
  * file — a purely static page needs no hub entry, and createHub is only
16
16
  * called at all when there is at least one.
17
17
  */
18
18
  import type { PageFile, PageSource } from "./locations.js";
19
19
  /**
20
- * `undefined` when no page has a hooks or queries file — the caller writes
20
+ * `undefined` when no page has a requests or queries file — the caller writes
21
21
  * nothing in that case, the same way an app with no custom elements gets no
22
22
  * client entry beyond the built-ins.
23
23
  */
@@ -1 +1 @@
1
- {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAY3D;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAoCpB"}
1
+ {"version":3,"file":"pages.d.ts","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAY3D;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,QAAQ,EAAE,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,SAAS,CAsCpB"}
@@ -2,7 +2,7 @@
2
2
  * Generating the hub's page registry — the server-side counterpart to
3
3
  * build/elements.ts's client entry point.
4
4
  *
5
- * `*.hooks.ts` and `*.queries.ts` are ordinary TypeScript, found by
5
+ * `*.requests.ts` and `*.queries.ts` are ordinary TypeScript, found by
6
6
  * build/locations.ts and handed here already paired by page name. What this
7
7
  * module writes is the file an application used to hand-maintain: an import
8
8
  * of each pair, a `Pages` object naming them by page, and the one
@@ -11,7 +11,7 @@
11
11
  * code, so it never needs a bundler step the way the browser-bound client
12
12
  * entry does.
13
13
  *
14
- * Nothing here is emitted for a page with neither a hooks nor a queries
14
+ * Nothing here is emitted for a page with neither a requests nor a queries
15
15
  * file — a purely static page needs no hub entry, and createHub is only
16
16
  * called at all when there is at least one.
17
17
  */
@@ -25,7 +25,7 @@ function relativeImport(outDir, file) {
25
25
  return rel.startsWith(".") ? rel : `./${rel}`;
26
26
  }
27
27
  /**
28
- * `undefined` when no page has a hooks or queries file — the caller writes
28
+ * `undefined` when no page has a requests or queries file — the caller writes
29
29
  * nothing in that case, the same way an app with no custom elements gets no
30
30
  * client entry beyond the built-ins.
31
31
  */
@@ -40,15 +40,17 @@ export function pagesSource(pages, pageSources, outDir, packageName) {
40
40
  const source = pageSources.get(name);
41
41
  if (!source)
42
42
  continue;
43
- const hooksId = source.hooks ? identifier(name, "Hooks") : undefined;
43
+ const requestsId = source.requests
44
+ ? identifier(name, "Requests")
45
+ : undefined;
44
46
  const queriesId = source.queries ? identifier(name, "Queries") : undefined;
45
- if (source.hooks) {
46
- imports.push(`import { hooks as ${hooksId} } from ${JSON.stringify(relativeImport(outDir, source.hooks))};`);
47
+ if (source.requests) {
48
+ imports.push(`import { requests as ${requestsId} } from ${JSON.stringify(relativeImport(outDir, source.requests))};`);
47
49
  }
48
50
  if (source.queries) {
49
51
  imports.push(`import { queries as ${queriesId} } from ${JSON.stringify(relativeImport(outDir, source.queries))};`);
50
52
  }
51
- entries.push(` ${JSON.stringify(name)}: { hooks: ${hooksId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
53
+ entries.push(` ${JSON.stringify(name)}: { requests: ${requestsId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
52
54
  }
53
55
  return ([...imports, "", "export const hub = createHub({", ...entries, "});"].join("\n") + "\n");
54
56
  }
@@ -1,24 +1,29 @@
1
- export declare const ATTR_QUERY = "lb-query";
1
+ export declare const ATTR_LIST = "lb-list";
2
+ export declare const ATTR_ROW = "lb-row";
2
3
  export declare const ATTR_KEY = "lb-key";
4
+ export declare const ATTR_KEY_VALUE = "lb-key-value";
3
5
  export declare const ATTR_CELL = "lb-cell";
4
6
  export declare const ATTR_VALUE = "lb-value";
5
- export declare const ATTR_GROUP = "lb-group";
6
- export declare const ATTR_SORT = "lb-sort";
7
- export declare const ATTR_ROW_COUNT = "data-rows";
7
+ export declare const ATTR_ROW_COUNT = "lb-row-count";
8
8
  export declare const LB_EVENT_NAME = "lb-request";
9
9
  export declare const ATTR_ACTION = "lb-action";
10
- export declare const ATTR_DELETE = "lb-delete";
11
- export declare const ATTR_INSERT = "lb-insert";
12
- export declare const ATTR_UPDATE = "lb-update";
10
+ export declare const LB_RESERVED_PREFIX = "lb-";
11
+ export declare const ACTION_ROW_INSERT = "lb-row-insert";
12
+ export declare const ACTION_ROW_DELETE = "lb-row-delete";
13
+ export declare const ACTION_ROW_UPDATE = "lb-row-update";
14
+ export declare const ACTION_CELL_CHANGE = "lb-cell-change";
15
+ export declare const LB_ACTIONS: readonly ["lb-row-insert", "lb-row-delete", "lb-row-update", "lb-cell-change"];
13
16
  export declare const ATTR_SLOT = "lb-slot";
14
17
  export declare const ATTR_TEMPLATE = "lb-template";
15
18
  export declare const ATTR_NAV_LINK = "lb-nav-link";
16
- export declare const PAGE_TEMPLATE_PREFIX = "page-";
19
+ export declare const ATTR_PAGE = "lb-page";
20
+ export declare const NAV_ROW = "lb-navigation";
21
+ export declare const NAV_CELL_LABEL = "page-label";
22
+ export declare const NAV_CELL_URI = "page-uri";
17
23
  export declare const ATTR_UNKNOWN_PAGE = "lb-unknown-page";
18
24
  export declare const HUB_TAG_NAME = "lb-hub";
19
- export declare const ATTR_PENDING = "data-lb-pending";
20
- export declare const ATTR_ERROR = "data-lb-error";
21
- export declare const LB_DATA_ENDPOINT = "/lb/data";
22
- export declare const LB_REQUEST_ENDPOINT = "/lb";
25
+ export declare const ATTR_PENDING = "lb-pending";
26
+ export declare const ATTR_ERROR = "lb-error";
27
+ export declare const LB_ENDPOINT = "/lb";
23
28
  export declare const LB_REQUEST_TIMEOUT_MS = 10000;
24
29
  //# sourceMappingURL=lb-constants.d.ts.map