@loadbare/app 0.5.6 → 0.7.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 (143) 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 +81 -7
  5. package/dist/build/assemble.js.map +1 -0
  6. package/dist/build/cli.d.ts +2 -2
  7. package/dist/build/cli.js +3 -2
  8. package/dist/build/cli.js.map +1 -0
  9. package/dist/build/elements.js +1 -0
  10. package/dist/build/elements.js.map +1 -0
  11. package/dist/build/expand.d.ts.map +1 -1
  12. package/dist/build/expand.js +20 -32
  13. package/dist/build/expand.js.map +1 -0
  14. package/dist/build/format.js +1 -0
  15. package/dist/build/format.js.map +1 -0
  16. package/dist/build/locations.d.ts +4 -4
  17. package/dist/build/locations.d.ts.map +1 -1
  18. package/dist/build/locations.js +16 -5
  19. package/dist/build/locations.js.map +1 -0
  20. package/dist/build/origins.d.ts +0 -13
  21. package/dist/build/origins.d.ts.map +1 -1
  22. package/dist/build/origins.js +33 -8
  23. package/dist/build/origins.js.map +1 -0
  24. package/dist/build/package-root.js +1 -0
  25. package/dist/build/package-root.js.map +1 -0
  26. package/dist/build/pages.d.ts +7 -3
  27. package/dist/build/pages.d.ts.map +1 -1
  28. package/dist/build/pages.js +14 -7
  29. package/dist/build/pages.js.map +1 -0
  30. package/dist/build/styles.js +1 -0
  31. package/dist/build/styles.js.map +1 -0
  32. package/dist/core/lb-constants.d.ts +15 -13
  33. package/dist/core/lb-constants.d.ts.map +1 -1
  34. package/dist/core/lb-constants.js +104 -53
  35. package/dist/core/lb-constants.js.map +1 -0
  36. package/dist/core/lb-types.d.ts +103 -62
  37. package/dist/core/lb-types.d.ts.map +1 -1
  38. package/dist/core/lb-types.js +12 -3
  39. package/dist/core/lb-types.js.map +1 -0
  40. package/dist/hub/lb-apply.d.ts +28 -4
  41. package/dist/hub/lb-apply.d.ts.map +1 -1
  42. package/dist/hub/lb-apply.js +227 -40
  43. package/dist/hub/lb-apply.js.map +1 -0
  44. package/dist/hub/lb-hub.browser.d.ts +1 -1
  45. package/dist/hub/lb-hub.browser.d.ts.map +1 -1
  46. package/dist/hub/lb-hub.browser.js +165 -110
  47. package/dist/hub/lb-hub.browser.js.map +1 -0
  48. package/dist/server/lb-express.d.ts +8 -5
  49. package/dist/server/lb-express.d.ts.map +1 -1
  50. package/dist/server/lb-express.js +46 -33
  51. package/dist/server/lb-express.js.map +1 -0
  52. package/dist/server/lb-server.d.ts +67 -45
  53. package/dist/server/lb-server.d.ts.map +1 -1
  54. package/dist/server/lb-server.js +56 -18
  55. package/dist/server/lb-server.js.map +1 -0
  56. package/docs/TECHREF-1.0.md +1107 -0
  57. package/docs/analysis-accidental-complexity.md +149 -0
  58. package/docs/reference/builder.md +4 -4
  59. package/docs/reference/chrome.md +10 -9
  60. package/docs/reference/custom-elements.md +54 -46
  61. package/docs/reference/data-binding.md +179 -97
  62. package/docs/reference/overview.md +1 -1
  63. package/docs/reference/page-files.md +64 -49
  64. package/docs/reference/server.md +25 -6
  65. package/docs/reference/widgets.md +22 -30
  66. package/docs/roadmap.md +68 -22
  67. package/docs/testing.md +47 -17
  68. package/docs/theory.md +116 -3
  69. package/docs/tutorials/010-pages-and-navigation.md +8 -8
  70. package/docs/tutorials/040-displaying-data.md +9 -9
  71. package/docs/tutorials/050-actions.md +5 -5
  72. package/docs/tutorials/060-custom-element-code.md +1 -1
  73. package/docs/tutorials/065-conditional-rendering.md +4 -4
  74. package/docs/tutorials/070-displaying-a-list.md +24 -47
  75. package/docs/tutorials/072-inserting-into-a-list.md +18 -15
  76. package/docs/tutorials/074-deleting-from-a-list.md +15 -17
  77. package/docs/tutorials/076-updating-a-list-item.md +20 -22
  78. package/docs/tutorials/080-widget-requests.md +22 -35
  79. package/docs/tutorials/090-using-widget-libraries.md +1 -1
  80. package/package.json +2 -3
  81. package/dist/hub/lb-rows.d.ts +0 -18
  82. package/dist/hub/lb-rows.d.ts.map +0 -1
  83. package/dist/hub/lb-rows.js +0 -106
  84. package/dist/tests/assemble.test.d.ts +0 -8
  85. package/dist/tests/assemble.test.d.ts.map +0 -1
  86. package/dist/tests/assemble.test.js +0 -58
  87. package/dist/tests/elements.test.d.ts +0 -8
  88. package/dist/tests/elements.test.d.ts.map +0 -1
  89. package/dist/tests/elements.test.js +0 -118
  90. package/dist/tests/expand.test.d.ts +0 -10
  91. package/dist/tests/expand.test.d.ts.map +0 -1
  92. package/dist/tests/expand.test.js +0 -250
  93. package/dist/tests/fixtures/elements/collision/imports.d.ts +0 -3
  94. package/dist/tests/fixtures/elements/collision/imports.d.ts.map +0 -1
  95. package/dist/tests/fixtures/elements/collision/imports.js +0 -1
  96. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts +0 -2
  97. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.d.ts.map +0 -1
  98. package/dist/tests/fixtures/elements/collision/widgets/acme-widget.browser.js +0 -1
  99. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts +0 -2
  100. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.d.ts.map +0 -1
  101. package/dist/tests/fixtures/elements/local/widgets/app-box.browser.js +0 -1
  102. package/dist/tests/fixtures/elements/manifest/imports.d.ts +0 -3
  103. package/dist/tests/fixtures/elements/manifest/imports.d.ts.map +0 -1
  104. package/dist/tests/fixtures/elements/manifest/imports.js +0 -1
  105. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts +0 -3
  106. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.d.ts.map +0 -1
  107. package/dist/tests/fixtures/elements/manifest-bad-entry/imports.js +0 -1
  108. package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts +0 -5
  109. package/dist/tests/fixtures/elements/manifest-not-array/imports.d.ts.map +0 -1
  110. package/dist/tests/fixtures/elements/manifest-not-array/imports.js +0 -1
  111. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts +0 -2
  112. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.d.ts.map +0 -1
  113. package/dist/tests/fixtures/elements/pkg/acme-widget.browser.js +0 -1
  114. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts +0 -6
  115. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.d.ts.map +0 -1
  116. package/dist/tests/fixtures/elements/unmarked/widgets/app-box.js +0 -1
  117. package/dist/tests/helpers/console.d.ts +0 -20
  118. package/dist/tests/helpers/console.d.ts.map +0 -1
  119. package/dist/tests/helpers/console.js +0 -28
  120. package/dist/tests/helpers/dom.d.ts +0 -18
  121. package/dist/tests/helpers/dom.d.ts.map +0 -1
  122. package/dist/tests/helpers/dom.js +0 -22
  123. package/dist/tests/lb-apply.test.d.ts +0 -8
  124. package/dist/tests/lb-apply.test.d.ts.map +0 -1
  125. package/dist/tests/lb-apply.test.js +0 -153
  126. package/dist/tests/lb-express.test.d.ts +0 -14
  127. package/dist/tests/lb-express.test.d.ts.map +0 -1
  128. package/dist/tests/lb-express.test.js +0 -238
  129. package/dist/tests/lb-rows.test.d.ts +0 -12
  130. package/dist/tests/lb-rows.test.d.ts.map +0 -1
  131. package/dist/tests/lb-rows.test.js +0 -336
  132. package/dist/tests/lb-server.test.d.ts +0 -9
  133. package/dist/tests/lb-server.test.d.ts.map +0 -1
  134. package/dist/tests/lb-server.test.js +0 -495
  135. package/dist/tests/origins.test.d.ts +0 -10
  136. package/dist/tests/origins.test.d.ts.map +0 -1
  137. package/dist/tests/origins.test.js +0 -369
  138. package/dist/tests/pages.test.d.ts +0 -6
  139. package/dist/tests/pages.test.d.ts.map +0 -1
  140. package/dist/tests/pages.test.js +0 -98
  141. package/dist/tests/styles.test.d.ts +0 -7
  142. package/dist/tests/styles.test.d.ts.map +0 -1
  143. package/dist/tests/styles.test.js +0 -76
@@ -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
  }
@@ -300,3 +324,4 @@ export async function scanAll(builtin, src, importsFile) {
300
324
  export function cssFrom(scans) {
301
325
  return scans.flatMap((scan) => scan.css);
302
326
  }
327
+ //# sourceMappingURL=origins.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"origins.js","sourceRoot":"","sources":["../../build/origins.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where a build's files come from — see docs/reference/builder.md.\n *\n * Three kinds of file reach the assembled application: a `.html` definition\n * that expands a tag, a `.browser.ts` script that registers its class, and a\n * `.css` stylesheet. All three arrive from the same places, so all three are\n * found the same way: one ordered list of origins, scanned once each, read by\n * whoever needs them.\n *\n * A script says `.browser` because shipping is the thing worth being sure\n * about. Under a plain `lb-thing.ts` rule a filename alone decides what\n * crosses to the browser, so a module named after a tag someone else writes\n * later is bundled without anyone having chosen that — and the author who\n * would most like to know is the one who never considered it. Requiring the\n * word puts the decision where a reader can see it, and leaves every\n * unmarked module in a source tree exactly what it looks like: not browser\n * code. It costs a widget author one word, once.\n *\n * The order is the cascade, and it is the same for every file kind:\n *\n * 1. this package's own `hub/`\n * 2. each package declared in `imports.ts`, in first-mention order\n * 3. the application's own `--src` tree\n *\n * A package contributes one directory of its own choosing — `loadbare.widgets`\n * in its package.json, or the whole package when it says nothing. See\n * `widgetDir` below for why a package gets to answer that.\n *\n * The one built-in origin holds one tag, `lb-hub`, and it is there because\n * every application has a hub whether or not it says so. A widget is not in\n * that position: `@loadbare/widgets` is a declared package like any other,\n * and the builder cannot tell it from someone else's.\n *\n * Two rules fall out of that list, and there is no third:\n *\n * - Within one origin, two files claiming the same tag are an error. That\n * is a typo, and the origin's own author is the one who can fix it.\n * - Across origins, the later one wins. An application always has the\n * final say over a package it imported, and a package over a built-in,\n * without anyone having to out-name a dependency they do not control.\n */\n\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport type { Dirent } from \"node:fs\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\n/** Lowercase, hyphenated — the one shape every custom element tag has, and the one native elements never do. */\nexport const CUSTOM_ELEMENT_TAG = /^[a-z][a-z0-9]*(-[a-z0-9]+)+$/;\n\nconst HTML_SUFFIX = \".html\";\n/** A widget script says so in its name: `lb-thing.browser.ts` and nothing else. */\nconst BROWSER_SUFFIXES = [\".browser.ts\", \".browser.js\"];\n/** What an unmarked module ends in — enough to recognise a near miss and say so. */\nconst MODULE_SUFFIXES = [\".ts\", \".js\"];\nconst CSS_SUFFIX = \".css\";\n\nexport interface Origin {\n /** How this origin is named in an error message: a package name, or \"src\". */\n name: string;\n /** The roots to scan. */\n dirs: string[];\n /**\n * How a script from here is imported, when a bare specifier is wanted\n * instead of the scanned file's own path. Only this package's `hub/` uses\n * it: `lb-hub` is the package's main entry point rather than a file an\n * application should reach into.\n */\n specifier?: (tag: string) => string;\n}\n\nexport interface OriginScan {\n name: string;\n specifier?: (tag: string) => string;\n /** Tag to its `.html` definition. */\n definitions: Map<string, string>;\n /** Tag to its `.browser.ts` script, the trailing `.ts` stripped. */\n scripts: Map<string, string>;\n /**\n * Tag to the unmarked module that would have claimed it — `lb-thing.ts`\n * where `lb-thing.browser.ts` was meant. Nothing is built from this. It is\n * here so build/elements.ts can name the file when the tag turns out to\n * have nothing to register it.\n */\n unmarked: Map<string, string>;\n /** Every `.css` file here, sorted by filename with the full path breaking a tie. */\n css: string[];\n}\n\n/**\n * Every file under `dir`, its own nested `node_modules` excluded — those\n * belong to the packages that declare them, not to this one.\n */\nasync function filesUnder(dir: string): Promise<string[]> {\n let entries: Dirent[];\n try {\n entries = await readdir(dir, { withFileTypes: true, recursive: true });\n } catch {\n return [];\n }\n return entries\n .filter((entry) => {\n if (!entry.isFile()) return false;\n const relative = path.relative(dir, entry.parentPath);\n return !relative.split(path.sep).includes(\"node_modules\");\n })\n .map((entry) => path.join(entry.parentPath, entry.name));\n}\n\n/**\n * One origin's contribution, classified by filename alone.\n *\n * A `.html` file is a widget's only when its basename is a tag, and a module\n * only when it is `<tag>.browser.ts` — which is what excludes `chrome.html`,\n * `about.page.html`, `about.requests.ts` and `imports.ts` here without naming\n * any of them. Those are the application's own files, and\n * build/locations.ts picks them out of `src` separately.\n *\n * A module named after a tag without the `.browser` goes in `unmarked`, so\n * an author who forgot the word gets told which file to rename rather than\n * being told their tag does not exist.\n */\n/**\n * The tag a filename claims, or null when it claims none.\n *\n * A filename is the tag verbatim, never folded. HTML matches an element name\n * case-insensitively, so folding would let `My-Widget.html` and\n * `my-widget.html` be two files for one tag — a collision on a\n * case-sensitive filesystem and one file on a case-insensitive one, which is\n * a difference between machines rather than a rule anyone can read.\n *\n * A name that is a tag only once folded is refused rather than ignored: the\n * author meant a widget, and silence would leave them with a tag that never\n * expands.\n */\nfunction tagIn(stem: string, file: string): string | null {\n if (CUSTOM_ELEMENT_TAG.test(stem)) return stem;\n const folded = stem.toLowerCase();\n if (!CUSTOM_ELEMENT_TAG.test(folded)) return null;\n const suffix = path.basename(file).slice(stem.length);\n throw new Error(\n `origins: '${file}' carries capitals; a tag is lowercase, so this ` +\n `file must be named '${folded}${suffix}'`,\n );\n}\n\nexport async function scanOrigin(origin: Origin): Promise<OriginScan> {\n const definitions = new Map<string, string>();\n const scripts = new Map<string, string>();\n const unmarked = new Map<string, string>();\n const css: string[] = [];\n\n const claim = (\n into: Map<string, string>,\n what: string,\n tag: string,\n file: string,\n ): void => {\n const already = into.get(tag);\n if (already) {\n throw new Error(\n `origins: '${origin.name}' has two ${what} files for <${tag}>: ` +\n `'${already}' and '${file}'`,\n );\n }\n into.set(tag, file);\n };\n\n for (const dir of origin.dirs) {\n for (const file of await filesUnder(dir)) {\n const name = path.basename(file);\n if (name.endsWith(CSS_SUFFIX)) {\n css.push(file);\n } else if (name.endsWith(HTML_SUFFIX)) {\n const tag = tagIn(name.slice(0, -HTML_SUFFIX.length), file);\n if (tag) claim(definitions, \"definition\", tag, file);\n } else {\n const browser = BROWSER_SUFFIXES.find((s) => name.endsWith(s));\n if (browser) {\n const tag = tagIn(name.slice(0, -browser.length), file);\n // Only the trailing \".ts\" comes off, never the \".browser\": what is\n // left becomes an import specifier, and esbuild resolves\n // \"lb-thing.browser\" to \"lb-thing.browser.ts\" the same way as to\n // \".js\".\n if (tag) {\n const ext = MODULE_SUFFIXES.find((e) => name.endsWith(e))!;\n claim(scripts, \"script\", tag, file.slice(0, -ext.length));\n }\n continue;\n }\n const ext = MODULE_SUFFIXES.find((e) => name.endsWith(e));\n if (!ext) continue;\n // A module whose name is a tag but which never said it was browser\n // code. It contributes nothing. Remembering it costs a map entry and\n // buys an error message that names the file instead of describing an\n // absence. A `.d.ts` never reaches here — stripping \".ts\" leaves a\n // dot, which is not a tag. Tested rather than tagIn()'d: an unmarked\n // module was never claimed to be a widget, so a capital in its name\n // is not a mistake anyone here can see.\n const tag = name.slice(0, -ext.length);\n if (CUSTOM_ELEMENT_TAG.test(tag) && !unmarked.has(tag)) {\n unmarked.set(tag, file);\n }\n }\n }\n }\n\n // By filename first, the full path only to break a tie. A directory a file\n // happens to sit in never affects the order.\n css.sort(\n (a, b) =>\n path.basename(a).localeCompare(path.basename(b)) || a.localeCompare(b),\n );\n\n return {\n name: origin.name,\n ...(origin.specifier ? { specifier: origin.specifier } : {}),\n definitions,\n scripts,\n unmarked,\n css,\n };\n}\n\n/** The shape of an `imports.ts` module's default export: the packages an application pulls widgets from. */\nexport type ElementManifest = string[];\n\n/**\n * `imports.ts`, executed rather than parsed — it runs through the same\n * loader as the rest of the build, so it is ordinary TypeScript, not a data\n * format with its own rules. It lists the packages an application pulls\n * widgets from, and each one becomes an origin, scanned exactly as `src` is.\n */\nexport async function loadElementManifest(\n file: string,\n): Promise<ElementManifest> {\n let mod: { default?: unknown };\n try {\n // pathToFileURL requires an absolute path; the caller's may not be one.\n mod = (await import(pathToFileURL(path.resolve(file)).href)) as {\n default?: unknown;\n };\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ERR_MODULE_NOT_FOUND\")\n return [];\n throw new Error(\n `elements: '${file}' failed to load: ${(err as Error).message}`,\n );\n }\n const declared = mod.default ?? [];\n if (!Array.isArray(declared)) {\n throw new Error(\n `imports.ts: must default-export an array of package names, got ${typeof declared}`,\n );\n }\n for (const name of declared) {\n if (typeof name !== \"string\" || name === \"\") {\n throw new Error(\n `imports.ts: every entry must be a package name, got ${JSON.stringify(name)}`,\n );\n }\n }\n return declared as ElementManifest;\n}\n\n/**\n * The ordinary node_modules walk, stopping at the first directory that has\n * the package installed — not a full resolution of an entry file, on\n * purpose: this only needs the package's root directory, and stopping short\n * avoids caring whether the package exposes an \"exports\" map at all. Which\n * part of that root holds the widgets is the package's own answer to give —\n * `widgetDir` asks it.\n */\nexport async function findInstalledPackage(\n packageName: string,\n fromDir: string,\n): Promise<string> {\n let dir = path.resolve(fromDir);\n for (;;) {\n const candidate = path.join(dir, \"node_modules\", packageName);\n try {\n await stat(path.join(candidate, \"package.json\"));\n return candidate;\n } catch {\n // not here — keep walking up\n }\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(\n `origins: could not find installed package '${packageName}' above '${fromDir}'`,\n );\n }\n dir = parent;\n }\n}\n\n/**\n * Where in an installed package its widgets are, from the package's own\n * manifest:\n *\n * \"loadbare\": { \"widgets\": \"./dist\" }\n *\n * A package that says nothing has its whole directory scanned, which is what\n * every package got before this field existed.\n *\n * The field is here because a published package is not a source tree. It is a\n * distribution artifact with a shape npm defines — compiled output in `dist/`,\n * the source excluded, plus a README, a licence, docs and whatever else its\n * author ships — and scanning all of it makes every one of those files a\n * possible widget. A package that compiles then carries the same tag twice,\n * once as `.ts` and once as `.js`, and the duplicate-tag error fires on a\n * package the application does not control and cannot fix.\n *\n * So the package says where to look, and the rest of it is none of the\n * builder's business. This costs an application author nothing: they still\n * list the package in `imports.ts` and write no import statement. It costs the\n * package author one line, in the file npm already expects to carry exactly\n * this kind of declaration.\n */\nasync function widgetDir(root: string, packageName: string): Promise<string> {\n let manifest: { loadbare?: { widgets?: unknown } };\n try {\n manifest = JSON.parse(\n await readFile(path.join(root, \"package.json\"), \"utf-8\"),\n );\n } catch (err) {\n throw new Error(\n `origins: could not read the package.json of '${packageName}': ${(err as Error).message}`,\n );\n }\n\n const declared = manifest.loadbare?.widgets;\n if (declared === undefined) return root;\n\n if (typeof declared !== \"string\" || declared === \"\") {\n throw new Error(\n `origins: '${packageName}' has a \"loadbare.widgets\" that is not a path: ` +\n `${JSON.stringify(declared)}`,\n );\n }\n\n // A path out of the package would put a package's declaration in charge of\n // directories it does not own — including the application's own.\n const dir = path.resolve(root, declared);\n const inside = path.relative(root, dir);\n if (path.isAbsolute(declared) || inside.startsWith(\"..\")) {\n throw new Error(\n `origins: '${packageName}' declares widgets at '${declared}', which is outside the package`,\n );\n }\n\n // A declared directory that is not there means the package shipped without\n // its widgets — its build did not run, or `files` left the directory out.\n // Scanning the package root instead would find some of them and look like it\n // worked, so this stops.\n try {\n await stat(dir);\n } catch {\n throw new Error(\n `origins: '${packageName}' declares widgets at '${declared}', which is not in the installed package`,\n );\n }\n\n return dir;\n}\n\nexport interface BuiltinDirs {\n /** This package's own hub/ — where lb-hub's script lives, found the same way as any widget. */\n builtinHubDir: string;\n /** This package's own name, e.g. \"@loadbare/app\" — for naming the built-in's import specifier. */\n packageName: string;\n}\n\n/**\n * The cascade, in order. A declared package is an origin like any other: the\n * directory it points `loadbare.widgets` at is scanned the way `src` is — its\n * whole installed directory, when it points at nothing — and a widget there is\n * found by its filename, not by being named in a manifest.\n *\n * `hub/` is the only origin naming a specifier, because `lb-hub` is the\n * package's main entry point rather than a file to reach into.\n */\nexport async function buildOrigins(\n builtin: BuiltinDirs,\n declared: ElementManifest,\n src: string,\n): Promise<Origin[]> {\n const { builtinHubDir, packageName } = builtin;\n\n const packages: Origin[] = [];\n for (const name of declared) {\n const root = await findInstalledPackage(name, src);\n packages.push({ name, dirs: [await widgetDir(root, name)] });\n }\n\n return [\n {\n name: packageName,\n dirs: [builtinHubDir],\n specifier: () => packageName,\n },\n ...packages,\n { name: src, dirs: [src] },\n ];\n}\n\n/**\n * The whole cascade, scanned. Everything a build reads from disk that is not\n * a page, the chrome, or a page's server half comes back from here.\n */\nexport async function scanAll(\n builtin: BuiltinDirs,\n src: string,\n importsFile?: string,\n): Promise<OriginScan[]> {\n const declared = importsFile ? await loadElementManifest(importsFile) : [];\n const origins = await buildOrigins(builtin, declared, src);\n return Promise.all(origins.map(scanOrigin));\n}\n\n/** Every origin's stylesheets, concatenated in cascade order. */\nexport function cssFrom(scans: OriginScan[]): string[] {\n return scans.flatMap((scan) => scan.css);\n}\n"]}
@@ -22,3 +22,4 @@ export async function findPackageRoot(dir) {
22
22
  }
23
23
  }
24
24
  }
25
+ //# sourceMappingURL=package-root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-root.js","sourceRoot":"","sources":["../../build/package-root.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,GAAW;IAC/C,SAAS,CAAC;QACR,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,OAAO,CAAC,CAAC;YACxD,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;gBACnB,MAAM,IAAI,KAAK,CAAC,wCAAwC,GAAG,GAAG,CAAC,CAAC;YAClE,CAAC;YACD,GAAG,GAAG,MAAM,CAAC;QACf,CAAC;IACH,CAAC;AACH,CAAC","sourcesContent":["/**\n * Walk up from `dir` to the nearest ancestor holding a package.json.\n *\n * Needed because a caller may run from source (build/*.ts) or compiled\n * (dist/build/*.js) — both are the same distance from the package root, but\n * nothing here should assume that stays true.\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport async function findPackageRoot(dir: string): Promise<string> {\n for (;;) {\n try {\n await readFile(path.join(dir, \"package.json\"), \"utf-8\");\n return dir;\n } catch {\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(`could not find a package.json above '${dir}'`);\n }\n dir = parent;\n }\n }\n}\n"]}
@@ -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,17 @@
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
+ * Each import keeps its `.ts` extension. Node's loader, including its own
15
+ * type stripping, looks for exactly the path it is given and never tries an
16
+ * extension, so a path without one runs under tsx and nowhere else.
17
+ *
18
+ * Nothing here is emitted for a page with neither a requests nor a queries
15
19
  * file — a purely static page needs no hub entry, and createHub is only
16
20
  * called at all when there is at least one.
17
21
  */
18
22
  import type { PageFile, PageSource } from "./locations.js";
19
23
  /**
20
- * `undefined` when no page has a hooks or queries file — the caller writes
24
+ * `undefined` when no page has a requests or queries file — the caller writes
21
25
  * nothing in that case, the same way an app with no custom elements gets no
22
26
  * client entry beyond the built-ins.
23
27
  */
@@ -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;;;;;;;;;;;;;;;;;;;;GAoBG;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,11 @@
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
+ * Each import keeps its `.ts` extension. Node's loader, including its own
15
+ * type stripping, looks for exactly the path it is given and never tries an
16
+ * extension, so a path without one runs under tsx and nowhere else.
17
+ *
18
+ * Nothing here is emitted for a page with neither a requests nor a queries
15
19
  * file — a purely static page needs no hub entry, and createHub is only
16
20
  * called at all when there is at least one.
17
21
  */
@@ -25,7 +29,7 @@ function relativeImport(outDir, file) {
25
29
  return rel.startsWith(".") ? rel : `./${rel}`;
26
30
  }
27
31
  /**
28
- * `undefined` when no page has a hooks or queries file — the caller writes
32
+ * `undefined` when no page has a requests or queries file — the caller writes
29
33
  * nothing in that case, the same way an app with no custom elements gets no
30
34
  * client entry beyond the built-ins.
31
35
  */
@@ -40,15 +44,18 @@ export function pagesSource(pages, pageSources, outDir, packageName) {
40
44
  const source = pageSources.get(name);
41
45
  if (!source)
42
46
  continue;
43
- const hooksId = source.hooks ? identifier(name, "Hooks") : undefined;
47
+ const requestsId = source.requests
48
+ ? identifier(name, "Requests")
49
+ : undefined;
44
50
  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))};`);
51
+ if (source.requests) {
52
+ imports.push(`import { requests as ${requestsId} } from ${JSON.stringify(relativeImport(outDir, source.requests))};`);
47
53
  }
48
54
  if (source.queries) {
49
55
  imports.push(`import { queries as ${queriesId} } from ${JSON.stringify(relativeImport(outDir, source.queries))};`);
50
56
  }
51
- entries.push(` ${JSON.stringify(name)}: { hooks: ${hooksId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
57
+ entries.push(` ${JSON.stringify(name)}: { requests: ${requestsId ?? "{}"}, queries: ${queriesId ?? "{}"} },`);
52
58
  }
53
59
  return ([...imports, "", "export const hub = createHub({", ...entries, "});"].join("\n") + "\n");
54
60
  }
61
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../../build/pages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;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. What this\n * module writes is the file an application used to hand-maintain: an import\n * of each pair, a `Pages` object naming them by page, and the one\n * `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"]}
@@ -16,3 +16,4 @@ export async function stylesSource(cssFiles) {
16
16
  const parts = await Promise.all(cssFiles.map((file) => readFile(file, "utf-8")));
17
17
  return parts.join("\n");
18
18
  }
19
+ //# sourceMappingURL=styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.js","sourceRoot":"","sources":["../../build/styles.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,wFAAwF;AACxF,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,QAAkB;IAElB,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC7B,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAChD,CAAC;IACF,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Concatenating an application's CSS.\n *\n * There is no widget, page, or chrome concept here — every `.css` file\n * anywhere under `src` is joined, in the order build/locations.ts already\n * sorted them into, with nothing added, removed, or scoped. What ships is\n * exactly the concatenation of what was authored, the same \"view source is\n * what ships\" rule build/assemble.ts holds for markup. Ordering is the\n * author's own, by filename — see build/locations.ts for the convention.\n */\n\nimport { readFile } from \"node:fs/promises\";\n\n/** `undefined` when there is no CSS at all — the caller writes nothing in that case. */\nexport async function stylesSource(\n cssFiles: string[],\n): Promise<string | undefined> {\n if (cssFiles.length === 0) return undefined;\n const parts = await Promise.all(\n cssFiles.map((file) => readFile(file, \"utf-8\")),\n );\n return parts.join(\"\\n\");\n}\n"]}
@@ -1,27 +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-";
17
- export declare const NAV_QUERY = "lb-navigation";
19
+ export declare const ATTR_PAGE = "lb-page";
20
+ export declare const NAV_ROW = "lb-navigation";
18
21
  export declare const NAV_CELL_LABEL = "page-label";
19
22
  export declare const NAV_CELL_URI = "page-uri";
20
23
  export declare const ATTR_UNKNOWN_PAGE = "lb-unknown-page";
21
24
  export declare const HUB_TAG_NAME = "lb-hub";
22
- export declare const ATTR_PENDING = "data-lb-pending";
23
- export declare const ATTR_ERROR = "data-lb-error";
24
- export declare const LB_DATA_ENDPOINT = "/lb/data";
25
- 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";
26
28
  export declare const LB_REQUEST_TIMEOUT_MS = 10000;
27
29
  //# sourceMappingURL=lb-constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,aAAa,CAAC;AACrC,eAAO,MAAM,QAAQ,WAAW,CAAC;AACjC,eAAO,MAAM,SAAS,YAAY,CAAC;AACnC,eAAO,MAAM,UAAU,aAAa,CAAC;AAMrC,eAAO,MAAM,UAAU,aAAa,CAAC;AAKrC,eAAO,MAAM,SAAS,YAAY,CAAC;AAOnC,eAAO,MAAM,cAAc,cAAc,CAAC;AAK1C,eAAO,MAAM,aAAa,eAAe,CAAC;AAM1C,eAAO,MAAM,WAAW,cAAc,CAAC;AAMvC,eAAO,MAAM,WAAW,cAAc,CAAC;AAKvC,eAAO,MAAM,WAAW,cAAc,CAAC;AAOvC,eAAO,MAAM,WAAW,cAAc,CAAC;AAMvC,eAAO,MAAM,SAAS,YAAY,CAAC;AAYnC,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAG3C,eAAO,MAAM,oBAAoB,UAAU,CAAC;AAO5C,eAAO,MAAM,SAAS,kBAAkB,CAAC;AAKzC,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,YAAY,aAAa,CAAC;AAUvC,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAQnD,eAAO,MAAM,YAAY,WAAW,CAAC;AAQrC,eAAO,MAAM,YAAY,oBAAoB,CAAC;AAO9C,eAAO,MAAM,UAAU,kBAAkB,CAAC;AAG1C,eAAO,MAAM,gBAAgB,aAAa,CAAC;AAC3C,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAIzC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}
1
+ {"version":3,"file":"lb-constants.d.ts","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,QAAQ,WAAW,CAAC;AAMjC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAQ7C,eAAO,MAAM,SAAS,YAAY,CAAC;AAEnC,eAAO,MAAM,UAAU,aAAa,CAAC;AAOrC,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAK7C,eAAO,MAAM,aAAa,eAAe,CAAC;AAS1C,eAAO,MAAM,WAAW,cAAc,CAAC;AAYvC,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AAMxC,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAKjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAOjD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAMjD,eAAO,MAAM,kBAAkB,mBAAmB,CAAC;AAInD,eAAO,MAAM,UAAU,gFAKb,CAAC;AAMX,eAAO,MAAM,SAAS,YAAY,CAAC;AAYnC,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAK3C,eAAO,MAAM,aAAa,gBAAgB,CAAC;AAM3C,eAAO,MAAM,SAAS,YAAY,CAAC;AAQnC,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAKvC,eAAO,MAAM,cAAc,eAAe,CAAC;AAC3C,eAAO,MAAM,YAAY,aAAa,CAAC;AAUvC,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AAQnD,eAAO,MAAM,YAAY,WAAW,CAAC;AAQrC,eAAO,MAAM,YAAY,eAAe,CAAC;AAOzC,eAAO,MAAM,UAAU,aAAa,CAAC;AAMrC,eAAO,MAAM,WAAW,QAAQ,CAAC;AAIjC,eAAO,MAAM,qBAAqB,QAAS,CAAC"}
@@ -2,49 +2,93 @@
2
2
  // literal — not a widget, not a page host, not a test. See
3
3
  // docs/reference/data-binding.md.
4
4
  // --- Binding (docs/reference/data-binding.md) ---------------------------
5
- export const ATTR_QUERY = "lb-query";
5
+ // One containment ladder: a list holds rows, a row holds cells. Both scope
6
+ // attributes take a name the server declares, and a nested one of either
7
+ // kind begins a new scope.
8
+ // Scopes DOM children to a named set of rows.
9
+ export const ATTR_LIST = "lb-list";
10
+ // Scopes DOM children to one named row.
11
+ //
12
+ // A single-row scope is read-only. Every write operation needs a key, and a
13
+ // key exists only on a live row the hub stamped inside a list, so a declared
14
+ // action is the only thing such a scope can send. An application that wants
15
+ // a writable single row declares a list that answers with one row.
16
+ export const ATTR_ROW = "lb-row";
17
+ // Named on a row template by the developer: the column that identifies a row
18
+ // of the list. A property of the list rather than of the template — a list
19
+ // without a fixed key column is meaningless — but written where the rows
20
+ // land, because that is the only place the page says anything about them.
6
21
  export const ATTR_KEY = "lb-key";
22
+ // Stamped on a live row by the row machinery: that row's value of the column
23
+ // ATTR_KEY names. Written by the hub, never by a developer, and read back by
24
+ // whatever needs to know which row it is inside — a delete button, an update
25
+ // form, a widget building its own request, or a stylesheet.
26
+ export const ATTR_KEY_VALUE = "lb-key-value";
27
+ // The column an element displays.
28
+ //
29
+ // Its value is a column name and never a cell name. A cell has no name of
30
+ // its own: it is identified by its row and its column, and the row arrives
31
+ // from scope, so the column is all that is left to say. ATTR_KEY holds a
32
+ // column name for the same reason.
7
33
  export const ATTR_CELL = "lb-cell";
8
34
  export const ATTR_VALUE = "lb-value";
9
- // Named on a row template, and read by a list widget that groups its rows
10
- // rather than by the hub. Grouping is placement, and placement is the
11
- // widget's — but the name is shared, because a grouped <select> and a table
12
- // with section headings are asking the same thing of the same data.
13
- export const ATTR_GROUP = "lb-group";
14
- // Named on a row template beside lb-group, and read by the same kind of
15
- // widget for the same reason. Sorting and grouping are one question — where
16
- // does this row go? — and both answers are local.
17
- export const ATTR_SORT = "lb-sort";
18
- // Stamped on a list widget by the shared row machinery: how many rows it is
19
- // showing. It is HTML's namespace rather than Loadbare App's because nothing
20
- // binds to it — it is derived from rows the hub already knows, exactly as
21
- // the section heading in lb-table is. It exists so that "no members yet"
22
- // is a stylesheet rule rather than a conditional in every list widget.
23
- export const ATTR_ROW_COUNT = "data-rows";
35
+ // Stamped on a list scope by the shared row machinery: how many rows it is
36
+ // showing. Written by the hub and never bound to — it is derived from rows
37
+ // the hub already knows, exactly as the section heading in lb-table is. A
38
+ // list widget watches it, and it exists so that "no members yet" is a
39
+ // stylesheet rule rather than a conditional in every list widget.
40
+ export const ATTR_ROW_COUNT = "lb-row-count";
24
41
  // --- Requests (docs/reference/data-binding.md) --------------------------
25
42
  // One event name, used by a widget reaching the server and by a widget
26
43
  // reaching an ancestor widget.
27
44
  export const LB_EVENT_NAME = "lb-request";
28
- // Names a declared action. On a native element, the hub turns a click into
29
- // the request; on a widget, the widget builds it from whatever interaction
30
- // it owns. Either way the name must appear in the page's hooks or it is
31
- // refused.
45
+ // Names what an interaction asks for.
46
+ //
47
+ // One attribute name, one wire field, one set of values: the request field
48
+ // `action` carries this attribute's value verbatim, and nothing is
49
+ // translated in between. On a native element the hub turns the element's own
50
+ // event into the request — a form submits, anything else clicks; on a
51
+ // widget, the widget builds it from whatever interaction it owns.
32
52
  export const ATTR_ACTION = "lb-action";
33
- // Marks a native element as deleting the tuple it is inside — the CRUD
34
- // counterpart to lb-action. It needs no declared name: tuple-delete takes
35
- // only the row's own lb-query/lb-key binding, which is all the server
53
+ // --- Reserved names ----------------------------------------------------
54
+ // Loadbare owns every value beginning with this prefix, in every lb-*
55
+ // attribute: a name so prefixed is the hub's own (NAV_ROW), and an
56
+ // lb-action so prefixed is an operation (below). The server refuses a page
57
+ // that declares a query or an action beginning with it, so a new reserved
58
+ // name can never collide with one an application already uses.
59
+ //
60
+ // It is also the whole of the wire discriminant. A request whose `action`
61
+ // begins with it is one of the four operations; anything else is a name the
62
+ // page declared under its requests. Nothing else tells them apart.
63
+ export const LB_RESERVED_PREFIX = "lb-";
64
+ // On a <form> in a list scope: builds a new row on submit. The hub gathers
65
+ // every lb-cell inside the form into a values map — no key, because there is
66
+ // no row yet — and the form's own lb-list says which list it is inserting
67
+ // into.
68
+ export const ACTION_ROW_INSERT = "lb-row-insert";
69
+ // Deletes the row the element is inside. Needs no declared name: it takes
70
+ // only the row's own lb-list/lb-key-value binding, which is all the server
36
71
  // needs to know which row and whether the operation is permitted on it.
37
- export const ATTR_DELETE = "lb-delete";
38
- // Marks a <form> as building a new tuple on submit. The hub gathers every
39
- // lb-cell inside it into a values map — no lb-key, because there is no row
40
- // yet — and the form's own lb-query says which query it is inserting into.
41
- export const ATTR_INSERT = "lb-insert";
42
- // Marks a <form> as committing several cells of one existing tuple at once
43
- // on submit — the batch counterpart to cell-change. The hub gathers the
44
- // same values map lb-insert does, plus the row's own lb-key: a form that is
45
- // a list's row template root, or sits inside one, has both from its
46
- // ancestors exactly as a delete button does.
47
- export const ATTR_UPDATE = "lb-update";
72
+ export const ACTION_ROW_DELETE = "lb-row-delete";
73
+ // On a <form> inside a live row: commits several cells of that row at once
74
+ // on submit — the batch counterpart to a cell change. The hub gathers the
75
+ // same values map, plus the row's own lb-key-value: a form that is a list's
76
+ // row template root, or sits inside one, has both from its ancestors exactly
77
+ // as a delete button does.
78
+ export const ACTION_ROW_UPDATE = "lb-row-update";
79
+ // On a widget wrapping one control: commits that one cell when the control
80
+ // changes. The hub never sends this itself — a native element has no widget
81
+ // to decide what a change is — so a widget that carries it builds the
82
+ // operation from its own lb-list/lb-key-value/lb-cell binding.
83
+ export const ACTION_CELL_CHANGE = "lb-cell-change";
84
+ // Every reserved lb-action, in one place, so the builder can refuse an
85
+ // unknown one and the hub can refuse to send one it does not implement.
86
+ export const LB_ACTIONS = [
87
+ ACTION_ROW_INSERT,
88
+ ACTION_ROW_DELETE,
89
+ ACTION_ROW_UPDATE,
90
+ ACTION_CELL_CHANGE,
91
+ ];
48
92
  // --- Expansion (docs/reference/custom-elements.md) ---------------------
49
93
  // Marks the element in a definition whose children receive the authored
50
94
  // inner content. An attribute rather than an element because HTML content
@@ -65,27 +109,31 @@ export const ATTR_TEMPLATE = "lb-template";
65
109
  // Only anchors carrying this are intercepted, so external links are never
66
110
  // hijacked.
67
111
  export const ATTR_NAV_LINK = "lb-nav-link";
68
- // A page host ships as <template id="page-<name>">.
69
- export const PAGE_TEMPLATE_PREFIX = "page-";
70
- // The hub's own query. Landed on every navigation, on any subtree inside
71
- // the hub that names it, through the same applyData() a server answer goes
112
+ // A page host ships as <template lb-page="<name>">. An attribute rather than
113
+ // an id: an id lives in the application's own namespace, where a page named
114
+ // for something the chrome also ids would collide, and getElementById()
115
+ // answers a collision by silently picking one.
116
+ export const ATTR_PAGE = "lb-page";
117
+ // The hub's own row. Landed on every navigation, on any subtree inside the
118
+ // hub that names it, through the same applyData() a server answer goes
72
119
  // through — so a chrome displays where the page is the way it displays
73
- // anything else. The `lb-` prefix keeps it out of a server's namespace:
74
- // no server answers a query so named.
75
- export const NAV_QUERY = "lb-navigation";
76
- // Its two cells. The label is the nav's: the text of the lb-nav-link anchor
77
- // whose href names the current page, and empty when no anchor does. The
78
- // URI is the path as the browser has it.
120
+ // anything else. One row rather than a list, so a subtree binds it with
121
+ // lb-row. A reserved name (LB_RESERVED_PREFIX): the server refuses to
122
+ // declare a query so named.
123
+ export const NAV_ROW = "lb-navigation";
124
+ // Its two columns. The label is the nav's: the text of the lb-nav-link
125
+ // anchor whose href names the current page, and empty when no anchor does.
126
+ // The URI is the path as the browser has it.
79
127
  export const NAV_CELL_LABEL = "page-label";
80
128
  export const NAV_CELL_URI = "page-uri";
81
129
  // Marks the chrome's own <dialog> for a page name that resolves to no
82
130
  // template — a routing miss discovered client-side, not an HTTP 404 (every
83
131
  // route gets the same 200 response; see docs/tutorials/010-pages-and-navigation.md).
84
- // The hub only opens it; what it says comes from NAV_QUERY, which the
85
- // dialog consumes like any other subtree, by naming it in lb-query.
86
- // Optional: an application that declares none gets today's console.error
87
- // and nothing more. The builder requires the element it's on to be a
88
- // <dialog> (build/assemble.ts), since the hub calls showModal() on it.
132
+ // The hub only opens it; what it says comes from NAV_ROW, which the dialog
133
+ // consumes like any other subtree, by naming it in lb-row. Optional: an
134
+ // application that declares none gets today's console.error and nothing
135
+ // more. The builder requires the element it's on to be a <dialog>
136
+ // (build/assemble.ts), since the hub calls showModal() on it.
89
137
  export const ATTR_UNKNOWN_PAGE = "lb-unknown-page";
90
138
  // The hub's own tag. It is exempt from the rule every other LB-* tag
91
139
  // follows: expansion does not require it to have a definition (build/expand.ts),
@@ -100,16 +148,19 @@ export const HUB_TAG_NAME = "lb-hub";
100
148
  // meaning beyond "in flight": a stylesheet dims the element, or a custom
101
149
  // button widget watches it to disable itself, but the hub itself only sets
102
150
  // and clears it.
103
- export const ATTR_PENDING = "data-lb-pending";
151
+ export const ATTR_PENDING = "lb-pending";
104
152
  // Stamped on the same element if that round trip failed — a non-2xx
105
153
  // response, a thrown application error, or a timeout, all alike. Cleared at
106
154
  // the start of that element's next request. What (if anything) an
107
155
  // application shows for it is undecided; this is only the fact that it
108
156
  // happened.
109
- export const ATTR_ERROR = "data-lb-error";
157
+ export const ATTR_ERROR = "lb-error";
110
158
  // --- Wire --------------------------------------------------------------
111
- export const LB_DATA_ENDPOINT = "/lb/data";
112
- export const LB_REQUEST_ENDPOINT = "/lb";
159
+ // One path, one method: everything the hub asks of the server is a POST to
160
+ // this path. Navigation is therefore the only GET an application serves,
161
+ // which is what leaves the page name space unrestricted.
162
+ export const LB_ENDPOINT = "/lb";
113
163
  // A client-side deadline for one round trip. fetch() has no deadline of its
114
164
  // own; past this the request is aborted and treated as a failure.
115
165
  export const LB_REQUEST_TIMEOUT_MS = 10_000;
166
+ //# sourceMappingURL=lb-constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lb-constants.js","sourceRoot":"","sources":["../../core/lb-constants.ts"],"names":[],"mappings":"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","sourcesContent":["// Every hub attribute name lives here. Nothing writes one as a string\n// literal — not a widget, not a page host, not a test. See\n// docs/reference/data-binding.md.\n\n// --- Binding (docs/reference/data-binding.md) ---------------------------\n// One containment ladder: a list holds rows, a row holds cells. Both scope\n// attributes take a name the server declares, and a nested one of either\n// kind begins a new scope.\n\n// Scopes DOM children to a named set of rows.\nexport const ATTR_LIST = \"lb-list\";\n\n// Scopes DOM children to one named row.\n//\n// A single-row scope is read-only. Every write operation needs a key, and a\n// key exists only on a live row the hub stamped inside a list, so a declared\n// action is the only thing such a scope can send. An application that wants\n// a writable single row declares a list that answers with one row.\nexport const ATTR_ROW = \"lb-row\";\n\n// Named on a row template by the developer: the column that identifies a row\n// of the list. A property of the list rather than of the template — a list\n// without a fixed key column is meaningless — but written where the rows\n// land, because that is the only place the page says anything about them.\nexport const ATTR_KEY = \"lb-key\";\n\n// Stamped on a live row by the row machinery: that row's value of the column\n// ATTR_KEY names. Written by the hub, never by a developer, and read back by\n// whatever needs to know which row it is inside — a delete button, an update\n// form, a widget building its own request, or a stylesheet.\nexport const ATTR_KEY_VALUE = \"lb-key-value\";\n\n// The column an element displays.\n//\n// Its value is a column name and never a cell name. A cell has no name of\n// its own: it is identified by its row and its column, and the row arrives\n// from scope, so the column is all that is left to say. ATTR_KEY holds a\n// column name for the same reason.\nexport const ATTR_CELL = \"lb-cell\";\n\nexport const ATTR_VALUE = \"lb-value\";\n\n// Stamped on a list scope by the shared row machinery: how many rows it is\n// showing. Written by the hub and never bound to — it is derived from rows\n// the hub already knows, exactly as the section heading in lb-table is. A\n// list widget watches it, and it exists so that \"no members yet\" is a\n// stylesheet rule rather than a conditional in every list widget.\nexport const ATTR_ROW_COUNT = \"lb-row-count\";\n\n// --- Requests (docs/reference/data-binding.md) --------------------------\n// One event name, used by a widget reaching the server and by a widget\n// reaching an ancestor widget.\nexport const LB_EVENT_NAME = \"lb-request\";\n\n// Names what an interaction asks for.\n//\n// One attribute name, one wire field, one set of values: the request field\n// `action` carries this attribute's value verbatim, and nothing is\n// translated in between. On a native element the hub turns the element's own\n// event into the request — a form submits, anything else clicks; on a\n// widget, the widget builds it from whatever interaction it owns.\nexport const ATTR_ACTION = \"lb-action\";\n\n// --- Reserved names ----------------------------------------------------\n// Loadbare owns every value beginning with this prefix, in every lb-*\n// attribute: a name so prefixed is the hub's own (NAV_ROW), and an\n// lb-action so prefixed is an operation (below). The server refuses a page\n// that declares a query or an action beginning with it, so a new reserved\n// name can never collide with one an application already uses.\n//\n// It is also the whole of the wire discriminant. A request whose `action`\n// begins with it is one of the four operations; anything else is a name the\n// page declared under its requests. Nothing else tells them apart.\nexport const LB_RESERVED_PREFIX = \"lb-\";\n\n// On a <form> in a list scope: builds a new row on submit. The hub gathers\n// every lb-cell inside the form into a values map — no key, because there is\n// no row yet — and the form's own lb-list says which list it is inserting\n// into.\nexport const ACTION_ROW_INSERT = \"lb-row-insert\";\n\n// Deletes the row the element is inside. Needs no declared name: it takes\n// only the row's own lb-list/lb-key-value binding, which is all the server\n// needs to know which row and whether the operation is permitted on it.\nexport const ACTION_ROW_DELETE = \"lb-row-delete\";\n\n// On a <form> inside a live row: commits several cells of that row at once\n// on submit — the batch counterpart to a cell change. The hub gathers the\n// same values map, plus the row's own lb-key-value: a form that is a list's\n// row template root, or sits inside one, has both from its ancestors exactly\n// as a delete button does.\nexport const ACTION_ROW_UPDATE = \"lb-row-update\";\n\n// On a widget wrapping one control: commits that one cell when the control\n// changes. The hub never sends this itself — a native element has no widget\n// to decide what a change is — so a widget that carries it builds the\n// operation from its own lb-list/lb-key-value/lb-cell binding.\nexport const ACTION_CELL_CHANGE = \"lb-cell-change\";\n\n// Every reserved lb-action, in one place, so the builder can refuse an\n// unknown one and the hub can refuse to send one it does not implement.\nexport const LB_ACTIONS = [\n ACTION_ROW_INSERT,\n ACTION_ROW_DELETE,\n ACTION_ROW_UPDATE,\n ACTION_CELL_CHANGE,\n] as const;\n\n// --- Expansion (docs/reference/custom-elements.md) ---------------------\n// Marks the element in a definition whose children receive the authored\n// inner content. An attribute rather than an element because HTML content\n// models discard foreign elements inside <select> and <table>.\nexport const ATTR_SLOT = \"lb-slot\";\n\n// One name in two positions. In a definition it marks a destination; on an\n// authored <template> it names the destination that template is for, and the\n// template is unwrapped into it.\n//\n// It exists because the parser, not the content model, is the obstacle. A\n// <thead> written inside <lb-table> on a page is not in a table, so the\n// tokenizer drops the tag and keeps the text. A <template> survives anywhere\n// and its contents parse as if they were in a table, so wrapping the markup\n// is what gets it to expansion intact — and unwrapping it is what puts real\n// elements in the shipped document.\nexport const ATTR_TEMPLATE = \"lb-template\";\n\n// --- Host channel (docs/reference/chrome.md) ---------------------------\n// Only anchors carrying this are intercepted, so external links are never\n// hijacked.\nexport const ATTR_NAV_LINK = \"lb-nav-link\";\n\n// A page host ships as <template lb-page=\"<name>\">. An attribute rather than\n// an id: an id lives in the application's own namespace, where a page named\n// for something the chrome also ids would collide, and getElementById()\n// answers a collision by silently picking one.\nexport const ATTR_PAGE = \"lb-page\";\n\n// The hub's own row. Landed on every navigation, on any subtree inside the\n// hub that names it, through the same applyData() a server answer goes\n// through — so a chrome displays where the page is the way it displays\n// anything else. One row rather than a list, so a subtree binds it with\n// lb-row. A reserved name (LB_RESERVED_PREFIX): the server refuses to\n// declare a query so named.\nexport const NAV_ROW = \"lb-navigation\";\n\n// Its two columns. The label is the nav's: the text of the lb-nav-link\n// anchor whose href names the current page, and empty when no anchor does.\n// The URI is the path as the browser has it.\nexport const NAV_CELL_LABEL = \"page-label\";\nexport const NAV_CELL_URI = \"page-uri\";\n\n// Marks the chrome's own <dialog> for a page name that resolves to no\n// template — a routing miss discovered client-side, not an HTTP 404 (every\n// route gets the same 200 response; see docs/tutorials/010-pages-and-navigation.md).\n// The hub only opens it; what it says comes from NAV_ROW, which the dialog\n// consumes like any other subtree, by naming it in lb-row. Optional: an\n// application that declares none gets today's console.error and nothing\n// more. The builder requires the element it's on to be a <dialog>\n// (build/assemble.ts), since the hub calls showModal() on it.\nexport const ATTR_UNKNOWN_PAGE = \"lb-unknown-page\";\n\n// The hub's own tag. It is exempt from the rule every other LB-* tag\n// follows: expansion does not require it to have a definition (build/expand.ts),\n// and the client build does not require it to have a script of its own\n// (build/elements.ts) — it is the framework's live element, always present,\n// registered once by the package's main entry point, not a widget an\n// application discovers or declares.\nexport const HUB_TAG_NAME = \"lb-hub\";\n\n// --- Request lifecycle --------------------------------------------------\n// Stamped on the element that dispatched a request — the button, form, or\n// widget itself — for the duration of its round trip. It carries no\n// meaning beyond \"in flight\": a stylesheet dims the element, or a custom\n// button widget watches it to disable itself, but the hub itself only sets\n// and clears it.\nexport const ATTR_PENDING = \"lb-pending\";\n\n// Stamped on the same element if that round trip failed — a non-2xx\n// response, a thrown application error, or a timeout, all alike. Cleared at\n// the start of that element's next request. What (if anything) an\n// application shows for it is undecided; this is only the fact that it\n// happened.\nexport const ATTR_ERROR = \"lb-error\";\n\n// --- Wire --------------------------------------------------------------\n// One path, one method: everything the hub asks of the server is a POST to\n// this path. Navigation is therefore the only GET an application serves,\n// which is what leaves the page name space unrestricted.\nexport const LB_ENDPOINT = \"/lb\";\n\n// A client-side deadline for one round trip. fetch() has no deadline of its\n// own; past this the request is aborted and treated as a failure.\nexport const LB_REQUEST_TIMEOUT_MS = 10_000;\n"]}