@gyral/core 0.1.0 → 0.3.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 (340) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +1 -1
  3. package/NOTICE +1 -1
  4. package/README.md +9 -7
  5. package/dist/children.d.ts +5 -2
  6. package/dist/children.js +8 -3
  7. package/dist/children.js.map +1 -1
  8. package/dist/command.js +6 -0
  9. package/dist/command.js.map +1 -1
  10. package/dist/compiler/ast.d.ts +21 -0
  11. package/dist/compiler/ast.js +145 -0
  12. package/dist/compiler/ast.js.map +1 -0
  13. package/dist/compiler/compile.d.ts +38 -0
  14. package/dist/compiler/compile.js +129 -0
  15. package/dist/compiler/compile.js.map +1 -0
  16. package/dist/compiler/hooks.d.ts +18 -0
  17. package/dist/compiler/hooks.js +186 -0
  18. package/dist/compiler/hooks.js.map +1 -0
  19. package/dist/compiler/parse5-check.d.ts +21 -0
  20. package/dist/compiler/parse5-check.js +55 -0
  21. package/dist/compiler/parse5-check.js.map +1 -0
  22. package/dist/compiler/scan.d.ts +31 -0
  23. package/dist/compiler/scan.js +152 -0
  24. package/dist/compiler/scan.js.map +1 -0
  25. package/dist/compiler/source.d.ts +34 -0
  26. package/dist/compiler/source.js +114 -0
  27. package/dist/compiler/source.js.map +1 -0
  28. package/dist/define.d.ts +2 -2
  29. package/dist/define.js +35 -334
  30. package/dist/define.js.map +1 -1
  31. package/dist/devtools-events.d.ts +5 -0
  32. package/dist/devtools-events.js.map +1 -1
  33. package/dist/devtools-off.d.ts +1 -0
  34. package/dist/devtools-off.js +1 -0
  35. package/dist/devtools-off.js.map +1 -1
  36. package/dist/devtools.d.ts +1 -0
  37. package/dist/devtools.js +3 -0
  38. package/dist/devtools.js.map +1 -1
  39. package/dist/drivers-scope.d.ts +1 -1
  40. package/dist/drivers-scope.js +1 -8
  41. package/dist/drivers-scope.js.map +1 -1
  42. package/dist/element-types.d.ts +5 -3
  43. package/dist/element-types.js.map +1 -1
  44. package/dist/element.d.ts +3 -0
  45. package/dist/element.js +269 -0
  46. package/dist/element.js.map +1 -0
  47. package/dist/eslint/imports.d.ts +24 -0
  48. package/dist/eslint/imports.js +78 -0
  49. package/dist/eslint/imports.js.map +1 -0
  50. package/dist/eslint/index.d.ts +15 -0
  51. package/dist/eslint/index.js +15 -0
  52. package/dist/eslint/index.js.map +1 -0
  53. package/dist/eslint/locate.d.ts +33 -0
  54. package/dist/eslint/locate.js +132 -0
  55. package/dist/eslint/locate.js.map +1 -0
  56. package/dist/eslint/row-purity-rule.d.ts +2 -0
  57. package/dist/eslint/row-purity-rule.js +103 -0
  58. package/dist/eslint/row-purity-rule.js.map +1 -0
  59. package/dist/eslint/template-rule.d.ts +5 -0
  60. package/dist/eslint/template-rule.js +66 -0
  61. package/dist/eslint/template-rule.js.map +1 -0
  62. package/dist/features.d.ts +39 -0
  63. package/dist/features.js +2 -0
  64. package/dist/features.js.map +1 -0
  65. package/dist/focus.d.ts +9 -2
  66. package/dist/focus.js +17 -2
  67. package/dist/focus.js.map +1 -1
  68. package/dist/hooks/invalid.d.ts +14 -0
  69. package/dist/hooks/invalid.js +72 -0
  70. package/dist/hooks/invalid.js.map +1 -0
  71. package/dist/hooks/labelled-by.d.ts +13 -0
  72. package/dist/hooks/labelled-by.js +51 -0
  73. package/dist/hooks/labelled-by.js.map +1 -0
  74. package/dist/host-model.d.ts +48 -0
  75. package/dist/host-model.js +155 -0
  76. package/dist/host-model.js.map +1 -0
  77. package/dist/hydration-client.d.ts +9 -0
  78. package/dist/hydration-client.js +35 -0
  79. package/dist/hydration-client.js.map +1 -0
  80. package/dist/hydration.d.ts +13 -19
  81. package/dist/hydration.js +45 -45
  82. package/dist/hydration.js.map +1 -1
  83. package/dist/index.d.ts +9 -16
  84. package/dist/index.js +9 -16
  85. package/dist/index.js.map +1 -1
  86. package/dist/intent.d.ts +31 -8
  87. package/dist/intent.js +82 -27
  88. package/dist/intent.js.map +1 -1
  89. package/dist/internal/interpreter.d.ts +7 -1
  90. package/dist/internal/interpreter.js +118 -73
  91. package/dist/internal/interpreter.js.map +1 -1
  92. package/dist/invokers-shim.d.ts +6 -0
  93. package/dist/invokers-shim.js +32 -0
  94. package/dist/invokers-shim.js.map +1 -0
  95. package/dist/invokers.d.ts +3 -5
  96. package/dist/invokers.js +11 -51
  97. package/dist/invokers.js.map +1 -1
  98. package/dist/islands.d.ts +0 -2
  99. package/dist/islands.js +4 -11
  100. package/dist/islands.js.map +1 -1
  101. package/dist/light-dom.d.ts +3 -52
  102. package/dist/light-dom.js +5 -100
  103. package/dist/light-dom.js.map +1 -1
  104. package/dist/prop.d.ts +66 -0
  105. package/dist/prop.js +22 -0
  106. package/dist/prop.js.map +1 -0
  107. package/dist/props.d.ts +52 -21
  108. package/dist/props.js +150 -34
  109. package/dist/props.js.map +1 -1
  110. package/dist/scheduler.d.ts +39 -0
  111. package/dist/scheduler.js +230 -0
  112. package/dist/scheduler.js.map +1 -0
  113. package/dist/server-component.d.ts +10 -0
  114. package/dist/server-component.js +102 -0
  115. package/dist/server-component.js.map +1 -0
  116. package/dist/server-specs.d.ts +6 -0
  117. package/dist/server-specs.js +13 -0
  118. package/dist/server-specs.js.map +1 -0
  119. package/dist/server.d.ts +28 -0
  120. package/dist/server.js +50 -0
  121. package/dist/server.js.map +1 -0
  122. package/dist/settled.d.ts +10 -0
  123. package/dist/settled.js +40 -0
  124. package/dist/settled.js.map +1 -0
  125. package/dist/states.d.ts +3 -3
  126. package/dist/states.js +4 -7
  127. package/dist/states.js.map +1 -1
  128. package/dist/store-binding.d.ts +6 -3
  129. package/dist/store-binding.js +5 -6
  130. package/dist/store-binding.js.map +1 -1
  131. package/dist/store-scope.d.ts +6 -7
  132. package/dist/store-scope.js +11 -26
  133. package/dist/store-scope.js.map +1 -1
  134. package/dist/store.d.ts +3 -0
  135. package/dist/store.js +9 -2
  136. package/dist/store.js.map +1 -1
  137. package/dist/stores-provider.d.ts +2 -6
  138. package/dist/stores-provider.js +21 -55
  139. package/dist/stores-provider.js.map +1 -1
  140. package/dist/transitions.d.ts +5 -6
  141. package/dist/transitions.js +11 -20
  142. package/dist/transitions.js.map +1 -1
  143. package/dist/types.d.ts +19 -23
  144. package/dist/types.js.map +1 -1
  145. package/dist/view/attributes.d.ts +6 -0
  146. package/dist/view/attributes.js +10 -0
  147. package/dist/view/attributes.js.map +1 -0
  148. package/dist/view/compiled.d.ts +1 -0
  149. package/dist/view/compiled.js +6 -0
  150. package/dist/view/compiled.js.map +1 -0
  151. package/dist/view/dev-off.d.ts +1 -0
  152. package/dist/view/dev-off.js +4 -0
  153. package/dist/view/dev-off.js.map +1 -0
  154. package/dist/view/dev.d.ts +1 -0
  155. package/dist/view/dev.js +5 -0
  156. package/dist/view/dev.js.map +1 -0
  157. package/dist/view/flags.d.ts +1 -0
  158. package/dist/view/flags.js +4 -0
  159. package/dist/view/flags.js.map +1 -0
  160. package/dist/view/index.d.ts +21 -0
  161. package/dist/view/index.js +21 -0
  162. package/dist/view/index.js.map +1 -0
  163. package/dist/view/normalize/check.d.ts +20 -0
  164. package/dist/view/normalize/check.js +78 -0
  165. package/dist/view/normalize/check.js.map +1 -0
  166. package/dist/view/normalize/emit.d.ts +13 -0
  167. package/dist/view/normalize/emit.js +136 -0
  168. package/dist/view/normalize/emit.js.map +1 -0
  169. package/dist/view/normalize/entities.d.ts +4 -0
  170. package/dist/view/normalize/entities.js +38 -0
  171. package/dist/view/normalize/entities.js.map +1 -0
  172. package/dist/view/normalize/errors.d.ts +9 -0
  173. package/dist/view/normalize/errors.js +19 -0
  174. package/dist/view/normalize/errors.js.map +1 -0
  175. package/dist/view/normalize/id.d.ts +7 -0
  176. package/dist/view/normalize/id.js +48 -0
  177. package/dist/view/normalize/id.js.map +1 -0
  178. package/dist/view/normalize/normalize.d.ts +15 -0
  179. package/dist/view/normalize/normalize.js +36 -0
  180. package/dist/view/normalize/normalize.js.map +1 -0
  181. package/dist/view/normalize/repairs.d.ts +24 -0
  182. package/dist/view/normalize/repairs.js +166 -0
  183. package/dist/view/normalize/repairs.js.map +1 -0
  184. package/dist/view/normalize/rules.d.ts +30 -0
  185. package/dist/view/normalize/rules.js +90 -0
  186. package/dist/view/normalize/rules.js.map +1 -0
  187. package/dist/view/normalize/shape.d.ts +5 -0
  188. package/dist/view/normalize/shape.js +38 -0
  189. package/dist/view/normalize/shape.js.map +1 -0
  190. package/dist/view/normalize/source.d.ts +13 -0
  191. package/dist/view/normalize/source.js +34 -0
  192. package/dist/view/normalize/source.js.map +1 -0
  193. package/dist/view/normalize/tag-states.d.ts +23 -0
  194. package/dist/view/normalize/tag-states.js +192 -0
  195. package/dist/view/normalize/tag-states.js.map +1 -0
  196. package/dist/view/normalize/tokenizer.d.ts +5 -0
  197. package/dist/view/normalize/tokenizer.js +233 -0
  198. package/dist/view/normalize/tokenizer.js.map +1 -0
  199. package/dist/view/normalize/tokens.d.ts +51 -0
  200. package/dist/view/normalize/tokens.js +24 -0
  201. package/dist/view/normalize/tokens.js.map +1 -0
  202. package/dist/view/normalize/tree.d.ts +59 -0
  203. package/dist/view/normalize/tree.js +190 -0
  204. package/dist/view/normalize/tree.js.map +1 -0
  205. package/dist/view/normalize/types.d.ts +126 -0
  206. package/dist/view/normalize/types.js +26 -0
  207. package/dist/view/normalize/types.js.map +1 -0
  208. package/dist/view/normalize/whitespace.d.ts +2 -0
  209. package/dist/view/normalize/whitespace.js +182 -0
  210. package/dist/view/normalize/whitespace.js.map +1 -0
  211. package/dist/view/prepare-stub.d.ts +3 -0
  212. package/dist/view/prepare-stub.js +7 -0
  213. package/dist/view/prepare-stub.js.map +1 -0
  214. package/dist/view/prepare.d.ts +8 -0
  215. package/dist/view/prepare.js +57 -0
  216. package/dist/view/prepare.js.map +1 -0
  217. package/dist/view/registry.d.ts +63 -0
  218. package/dist/view/registry.js +19 -0
  219. package/dist/view/registry.js.map +1 -0
  220. package/dist/view/render/adopt-attr.d.ts +14 -0
  221. package/dist/view/render/adopt-attr.js +77 -0
  222. package/dist/view/render/adopt-attr.js.map +1 -0
  223. package/dist/view/render/adopt-plan.d.ts +10 -0
  224. package/dist/view/render/adopt-plan.js +26 -0
  225. package/dist/view/render/adopt-plan.js.map +1 -0
  226. package/dist/view/render/adopt.d.ts +3 -0
  227. package/dist/view/render/adopt.js +269 -0
  228. package/dist/view/render/adopt.js.map +1 -0
  229. package/dist/view/render/attr-parts.d.ts +61 -0
  230. package/dist/view/render/attr-parts.js +229 -0
  231. package/dist/view/render/attr-parts.js.map +1 -0
  232. package/dist/view/render/child-part.d.ts +51 -0
  233. package/dist/view/render/child-part.js +170 -0
  234. package/dist/view/render/child-part.js.map +1 -0
  235. package/dist/view/render/dev-check.d.ts +12 -0
  236. package/dist/view/render/dev-check.js +122 -0
  237. package/dist/view/render/dev-check.js.map +1 -0
  238. package/dist/view/render/hook-part.d.ts +3 -0
  239. package/dist/view/render/hook-part.js +20 -0
  240. package/dist/view/render/hook-part.js.map +1 -0
  241. package/dist/view/render/hooks.d.ts +40 -0
  242. package/dist/view/render/hooks.js +46 -0
  243. package/dist/view/render/hooks.js.map +1 -0
  244. package/dist/view/render/hydrate.d.ts +10 -0
  245. package/dist/view/render/hydrate.js +33 -0
  246. package/dist/view/render/hydrate.js.map +1 -0
  247. package/dist/view/render/instance.d.ts +33 -0
  248. package/dist/view/render/instance.js +107 -0
  249. package/dist/view/render/instance.js.map +1 -0
  250. package/dist/view/render/list.d.ts +20 -0
  251. package/dist/view/render/list.js +156 -0
  252. package/dist/view/render/list.js.map +1 -0
  253. package/dist/view/render/mismatch.d.ts +10 -0
  254. package/dist/view/render/mismatch.js +53 -0
  255. package/dist/view/render/mismatch.js.map +1 -0
  256. package/dist/view/render/nodes.d.ts +10 -0
  257. package/dist/view/render/nodes.js +37 -0
  258. package/dist/view/render/nodes.js.map +1 -0
  259. package/dist/view/render/place.d.ts +18 -0
  260. package/dist/view/render/place.js +69 -0
  261. package/dist/view/render/place.js.map +1 -0
  262. package/dist/view/render/plan.d.ts +26 -0
  263. package/dist/view/render/plan.js +124 -0
  264. package/dist/view/render/plan.js.map +1 -0
  265. package/dist/view/render/raw.d.ts +18 -0
  266. package/dist/view/render/raw.js +68 -0
  267. package/dist/view/render/raw.js.map +1 -0
  268. package/dist/view/render/render.d.ts +12 -0
  269. package/dist/view/render/render.js +60 -0
  270. package/dist/view/render/render.js.map +1 -0
  271. package/dist/view/render/reorder.d.ts +4 -0
  272. package/dist/view/render/reorder.js +101 -0
  273. package/dist/view/render/reorder.js.map +1 -0
  274. package/dist/view/render/rows.d.ts +23 -0
  275. package/dist/view/render/rows.js +95 -0
  276. package/dist/view/render/rows.js.map +1 -0
  277. package/dist/view/render/seen.d.ts +11 -0
  278. package/dist/view/render/seen.js +17 -0
  279. package/dist/view/render/seen.js.map +1 -0
  280. package/dist/view/render/values.d.ts +29 -0
  281. package/dist/view/render/values.js +13 -0
  282. package/dist/view/render/values.js.map +1 -0
  283. package/dist/view/render/warn.d.ts +14 -0
  284. package/dist/view/render/warn.js +69 -0
  285. package/dist/view/render/warn.js.map +1 -0
  286. package/dist/view/server/component.d.ts +9 -0
  287. package/dist/view/server/component.js +42 -0
  288. package/dist/view/server/component.js.map +1 -0
  289. package/dist/view/server/escape.d.ts +10 -0
  290. package/dist/view/server/escape.js +34 -0
  291. package/dist/view/server/escape.js.map +1 -0
  292. package/dist/view/server/index.d.ts +28 -0
  293. package/dist/view/server/index.js +72 -0
  294. package/dist/view/server/index.js.map +1 -0
  295. package/dist/view/server/sha256.d.ts +2 -0
  296. package/dist/view/server/sha256.js +75 -0
  297. package/dist/view/server/sha256.js.map +1 -0
  298. package/dist/view/server/tags.d.ts +57 -0
  299. package/dist/view/server/tags.js +121 -0
  300. package/dist/view/server/tags.js.map +1 -0
  301. package/dist/view/server/values.d.ts +16 -0
  302. package/dist/view/server/values.js +86 -0
  303. package/dist/view/server/values.js.map +1 -0
  304. package/dist/view/server/writer.d.ts +16 -0
  305. package/dist/view/server/writer.js +155 -0
  306. package/dist/view/server/writer.js.map +1 -0
  307. package/dist/view/styles.d.ts +25 -0
  308. package/dist/view/styles.js +58 -0
  309. package/dist/view/styles.js.map +1 -0
  310. package/dist/view/template-element.d.ts +3 -0
  311. package/dist/view/template-element.js +25 -0
  312. package/dist/view/template-element.js.map +1 -0
  313. package/dist/view/template.d.ts +21 -0
  314. package/dist/view/template.js +30 -0
  315. package/dist/view/template.js.map +1 -0
  316. package/dist/vite.d.ts +45 -6
  317. package/dist/vite.js +150 -25
  318. package/dist/vite.js.map +1 -1
  319. package/package.json +41 -7
  320. package/dist/accessible-name.d.ts +0 -19
  321. package/dist/accessible-name.js +0 -56
  322. package/dist/accessible-name.js.map +0 -1
  323. package/dist/element-directive.d.ts +0 -20
  324. package/dist/element-directive.js +0 -31
  325. package/dist/element-directive.js.map +0 -1
  326. package/dist/internal/runtime.d.ts +0 -7
  327. package/dist/internal/runtime.js +0 -8
  328. package/dist/internal/runtime.js.map +0 -1
  329. package/dist/invalid.d.ts +0 -21
  330. package/dist/invalid.js +0 -70
  331. package/dist/invalid.js.map +0 -1
  332. package/dist/live-boolean.d.ts +0 -22
  333. package/dist/live-boolean.js +0 -34
  334. package/dist/live-boolean.js.map +0 -1
  335. package/dist/styles.d.ts +0 -12
  336. package/dist/styles.js +0 -11
  337. package/dist/styles.js.map +0 -1
  338. package/dist/textarea.d.ts +0 -26
  339. package/dist/textarea.js +0 -85
  340. package/dist/textarea.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,117 @@
1
1
  # @gyral/core
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 595e1c4: `renderPage({ csp })` also takes `contentSecurityPolicy()`'s options (`{ directives, styles? }`)
8
+ and builds the `Content-Security-Policy` header when the page renders, so components whose
9
+ modules were imported after startup get their `<style>` hashes too (a policy built earlier
10
+ silently lacked them, and the browser blocked those styles). `styles` defaults to the page's
11
+ own; the header is cached per options object until another component registers.
12
+ `contentSecurityPolicy()` stays for static use; in development `renderPage` warns when a header
13
+ it is given lacks a registered component's hash. `@gyral/core/server` adds `styleHashSync()`
14
+ (the CSP hash, synchronously: a small SHA-256 in JavaScript), `componentStyles()` and
15
+ `development`. The `create-gyral` ssr template uses `csp: {}`.
16
+ - ca68fe8: `@gyral/core/eslint`: an ESLint flat-config plugin (`gyral.configs.recommended`). `gyral/template`
17
+ reports the template rules (docs/design-docs/view/09-template-rules.md) in the editor, with the
18
+ compiler's and the development runtime's own messages, at the tag, text or `${…}` they are
19
+ about; `gyral/each-row-purity` reports `each` rows that read the view's scope and `each` calls
20
+ without a key function (view/03-lists.md). ESLint 9 or 10 is an optional peer dependency.
21
+ - 784bd2f: `renderOnFrame` (spec field): messages from bursty sources (a WebSocket feed, sensors) render in
22
+ the next animation frame, once for all of them, instead of in the microtask flush; reducers still
23
+ run at once, and `'StoreChanged'` covers store changes. Pages without frames render from a 100 ms
24
+ timer, so `settled()` never stalls. The microtask flush stays the default (flush-timing spike,
25
+ docs/design-docs/view/04-scheduler.md).
26
+ - 6971fbe: Smaller bundles (size pass, ADR 0018): code an app doesn't use is no longer bundled. The keyed
27
+ list reconciler comes with `each`, `raw` with `raw`, the command interpreter with `command()`,
28
+ the store binding with `defineStore()` and prop parsing with the prop builders. Hydration and
29
+ islands load lazily, with the first server-rendered host, so client-only pages never fetch them;
30
+ the invoker-command fallback loads only where `CommandEvent` is missing. A component root now
31
+ listens only for the events its templates can fire intents on (any statically named
32
+ `data-intent-on` type works without `spec.events`). New: `intents<Msg>()`, a module-level
33
+ constant of intent names for pure `each` rows.
34
+ - d3f540a: Gyral's own view layer replaces Lit (ADR 0018). No Gyral package depends on Lit any more, and
35
+ `@gyral/core` has no runtime dependencies. Upgrade guide with before/after code:
36
+ [docs/references/migrating-0.2-to-0.3.md](https://github.com/gyraljs/gyral/blob/main/docs/references/migrating-0.2-to-0.3.md).
37
+
38
+ - **`@gyral/core`:** `define()` returns a plain `HTMLElement` subclass rendered by one global
39
+ scheduler; `await settled()` replaces `el.updateComplete`. `html`, `css` and `nothing` are
40
+ Gyral's own; new are `each(items, key, row, pick?)` (keyed lists with pure rows),
41
+ `intents<Msg>()`, `raw()`, `defineHook()` (the hooks `invalid` and `labelledBy` keep their call
42
+ sites) and `HydrationMismatch`. Props are declared with `prop.string/number/boolean/json/value`
43
+ over Standard Schema: attributes are kebab-case by default and always validated. Form state
44
+ uses plain bindings (`value=${v}`, `?checked=${v}`, `<textarea>${v}</textarea>`), written
45
+ only when the model's value changes, so other renders keep the user's edits. Removed:
46
+ `svg`, `classMap`, `styleMap`, `unsafeCSS` (interpolate in `css`), `repeat`, `keyed`, `live`,
47
+ `liveBoolean`, `textarea()`, `textareaMarkup`, `directive`/`ElementDirective`,
48
+ `defineStoresProvider`, `HIDDEN_MARKER`, `HYDRATE_KEY`, `CSSStyleSheet` in `styles`, and
49
+ `LIT_PACKAGES`/`LIT_PREBUNDLE` from `@gyral/core/vite`. `gyralVitePreset()` now runs the
50
+ template compiler in `vite build` (rule errors fail the build). Server rendering is
51
+ `@gyral/core/server`; hydration is built in: each server-rendered component adopts its DOM in
52
+ place on its own, mismatches throw `HydrationMismatch` in development and re-render just that
53
+ component in production, and islands may sit anywhere.
54
+ - **`@gyral/ssr`:** renders with `@gyral/core/server` (synchronous, no DOM shim). `serverHtml` is
55
+ gone (use core's `html`); `contentSecurityPolicy()` allows components' `<style>` by hash, so
56
+ `style-src` needs no `'unsafe-inline'`. A template that writes children inside a light
57
+ component's tag is an error. `@gyral/ssr/hydrate` is kept as an empty entry; remove the import.
58
+ - **`@gyral/testing`:** `hydrated()` waits for `settled()` and takes `{ releaseIslands }`; it no
59
+ longer awaits other elements' `updateComplete`, and `mountSsr` no longer ignores Lit's console
60
+ banner.
61
+ - **`@gyral/devtools`:** the panel renders with the new view layer; no Lit peer.
62
+ - **`create-gyral`:** the templates install no Lit packages and no `lit-html` override.
63
+
64
+ ### Patch Changes
65
+
66
+ - fe849f8: Compiled templates are smaller: part tables are tuples with numeric kinds and `server` is written
67
+ only when true (about a quarter less template code raw; every example's bundle shrinks by
68
+ 35-97 B gzip). Nothing decodes them at runtime; template ids are unchanged.
69
+ - 49ff2ad: The Vite preset's new `gyral:dev-server` plugin makes server rendering under Vite's dev server
70
+ (`ssrLoadModule`) produce development output (`<!--gyral:ID-->` markers, development checks)
71
+ with installed packages too: in serve mode it keeps `@gyral/*`, and the app's dependencies that
72
+ depend on them, out of SSR externalization, so they resolve the `development` condition and
73
+ share one `@gyral/core`. `vite build` is unchanged.
74
+ - 9c6c8bd: The server renderer escapes `<` and `>` in attribute values and in the seed attribute too, so
75
+ no markup (such as `<script>`) appears raw inside an attribute. Browsers decode them, so values
76
+ and hydration are unchanged.
77
+ - 8803ef3: Leaner page shells. A `server` template (a document shell such as `@gyral/ssr`'s `page()`) is
78
+ never hydrated, so the server no longer writes anchor comments (`<!---->`) for it or for `raw()`
79
+ values in its holes; templates nested in a shell keep theirs. The whitespace rules treat
80
+ `<head>`, `<meta>`, `<link>`, `<base>` and `<title>` as block-level edges and drop all
81
+ whitespace-only text inside `<head>`. Ids of templates with those tags change (the same in the
82
+ compiler, the browser and the server).
83
+ - b2ae8e5: Production client builds no longer carry template ids: the template compiler leaves them out
84
+ of the hoisted template objects, and the renderer compares templates by object identity there
85
+ (about a fifth less compiled template code gzip: 10.32 → 8.27 KiB on a 210-template corpus).
86
+ SSR and development builds keep their ids (development markers, collision checks). One visible
87
+ difference: identical markup at call sites in two modules is now two templates in production,
88
+ so switching between them replaces the instance instead of patching it.
89
+
90
+ ## 0.2.0
91
+
92
+ ### Minor Changes
93
+
94
+ - 74054b3: Smaller and faster, with no public API change:
95
+
96
+ - **No Effect.** The command interpreter is now plain TypeScript with no runtime dependencies,
97
+ so `@gyral/core` depends only on Lit. An empty app drops from about 49 KiB to about 11 KiB
98
+ gzipped, starts faster and uses less memory; rendering speed is unchanged (ADR 0015).
99
+ - **Template whitespace minification.** `html` and `svg` remove indentation whitespace between
100
+ tags (once per template, on server and client alike), so lists build far fewer DOM nodes and
101
+ bulk list updates are 9–22% faster. Inline spacing is kept as one space; `<pre>`,
102
+ `<textarea>`, `<script>`, `<style>`, attributes and comments are unchanged. For exact
103
+ whitespace under CSS `white-space: pre*`, use `html` from `lit`.
104
+ - **lit-html leak guidance.** lit-html 3.3.1 and later leak a comment node per removed
105
+ `repeat()` item (lit/lit#5298). Development builds warn once when an affected version is
106
+ loaded; pin lit-html to 3.3.0 with an override until it is fixed upstream
107
+ (docs/references/consumer-setup.md). New `create-gyral` apps are pinned.
108
+
109
+ ### Patch Changes
110
+
111
+ - a7275e8: Copyright and package author are Mike Zupper.
112
+ - fa0156a: `focus()` returned by a reducer whose update renders inside a View Transition now runs after
113
+ the new view has rendered, instead of before it (it used to warn "matched no focusable element").
114
+
3
115
  ## 0.1.0
4
116
 
5
117
  ### Minor Changes
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 The Zoop Troop, Inc.
3
+ Copyright (c) 2026 Mike Zupper
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/NOTICE CHANGED
@@ -1,5 +1,5 @@
1
1
  Gyral
2
- Copyright (c) 2026 The Zoop Troop, Inc.
2
+ Copyright (c) 2026 Mike Zupper
3
3
 
4
4
  Gyral is inspired by Cycle.js (https://github.com/cyclejs/cyclejs), released
5
5
  under the MIT License:
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # @gyral/core
2
2
 
3
- Model-View-Intent web components on [Lit](https://lit.dev) and the web platform. _Inspired by [Cycle.js](https://cycle.js.org)._ Intent parses platform events into typed messages, the model is a set of pure reducers, and the view is a pure template that names intents. Side effects are commands: data that drivers run at the edges.
3
+ Model-View-Intent web components on the web platform, with their own view layer. _Inspired by [Cycle.js](https://cycle.js.org)._ Intent parses platform events into typed messages, the model is a set of pure reducers, and the view is a pure template that names intents. Side effects are commands: data that drivers run at the edges.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```sh
8
- pnpm add @gyral/core lit
8
+ pnpm add @gyral/core
9
9
  ```
10
10
 
11
11
  ## Example
@@ -34,17 +34,19 @@ define<{ count: number }, Msg>('gy-counter', {
34
34
  ```
35
35
 
36
36
  `@gyral/core/vite` exports `gyralVitePreset()`, the Vite/Vitest settings every Gyral app needs
37
- (one copy of Lit, Lit modules pre-bundled).
37
+ (the build-time template compiler). `@gyral/core/eslint` is an ESLint plugin with the same
38
+ template rules for the editor: `export default [gyral.configs.recommended]`.
38
39
 
39
40
  ## Documentation
40
41
 
41
42
  Guides and API reference: **[gyral.dev](https://gyral.dev)**. Source, issues and the
42
- consumer setup guide (peer dependencies, Vite preset, SSR checklist):
43
- [github.com/gyraljs/gyral](https://github.com/gyraljs/gyral).
43
+ consumer setup guide (packages, Vite preset, template compiler):
44
+ [github.com/gyraljs/gyral](https://github.com/gyraljs/gyral). Upgrading from 0.2:
45
+ [docs/references/migrating-0.2-to-0.3.md](https://github.com/gyraljs/gyral/blob/main/docs/references/migrating-0.2-to-0.3.md).
44
46
 
45
47
  > Status: pre-alpha. APIs change between 0.x releases.
46
48
 
47
49
  ## License
48
50
 
49
- MIT © The Zoop Troop, Inc. See LICENSE and NOTICE (Cycle.js attribution). Gyral, gyraljs and
50
- the Gyral logo are trademarks of The Zoop Troop, Inc.
51
+ MIT © Mike Zupper. See LICENSE and NOTICE (Cycle.js attribution). Gyral, gyraljs and
52
+ the Gyral logo are trademarks of Mike Zupper.
@@ -1,9 +1,11 @@
1
1
  import type { Command } from './command.js';
2
+ import type { LocalHost } from './features.js';
2
3
  import type { IntentParser, Tagged } from './types.js';
3
- /** Marker driver: `define()` handles emit commands itself, synchronously ordered. */
4
+ /** Marker driver: the host runs emit commands itself (`local`), synchronously ordered. */
4
5
  export declare const EMIT: {
5
6
  readonly name: "@gyral/emit";
6
7
  readonly run: () => undefined;
8
+ readonly local: (host: LocalHost, output: unknown) => void;
7
9
  };
8
10
  /**
9
11
  * A command that sends `output` to the parent component. The parent receives it as an
@@ -12,7 +14,8 @@ export declare const EMIT: {
12
14
  export declare function emit(output: Tagged & Readonly<Record<string, unknown>>): Command<never>;
13
15
  /**
14
16
  * Sends an output to the parent component. A microtask keeps outputs in order and out of the
15
- * parent's render pass; the event bubbles through the parent's shadow tree only (not composed).
17
+ * parent's render pass (settled() waits for it); the event bubbles through the parent's shadow
18
+ * tree only (not composed).
16
19
  */
17
20
  export declare function dispatchOutput(host: Element, output: unknown): void;
18
21
  /** Anything `child()` can read an output type from: a `define()` class. */
package/dist/children.js CHANGED
@@ -1,8 +1,12 @@
1
1
  import { OUTPUT_EVENT } from './intent.js';
2
- /** Marker driver: `define()` handles emit commands itself, synchronously ordered. */
2
+ import { defer } from './scheduler.js';
3
+ /** Marker driver: the host runs emit commands itself (`local`), synchronously ordered. */
3
4
  export const EMIT = {
4
5
  name: '@gyral/emit',
5
6
  run: () => undefined,
7
+ local: (host, output) => {
8
+ dispatchOutput(host.el, output);
9
+ },
6
10
  };
7
11
  /**
8
12
  * A command that sends `output` to the parent component. The parent receives it as an
@@ -13,10 +17,11 @@ export function emit(output) {
13
17
  }
14
18
  /**
15
19
  * Sends an output to the parent component. A microtask keeps outputs in order and out of the
16
- * parent's render pass; the event bubbles through the parent's shadow tree only (not composed).
20
+ * parent's render pass (settled() waits for it); the event bubbles through the parent's shadow
21
+ * tree only (not composed).
17
22
  */
18
23
  export function dispatchOutput(host, output) {
19
- queueMicrotask(() => {
24
+ defer(() => {
20
25
  if (!host.isConnected)
21
26
  return;
22
27
  host.dispatchEvent(new CustomEvent(OUTPUT_EVENT, { detail: output, bubbles: true, composed: false }));
@@ -1 +1 @@
1
- {"version":3,"file":"children.js","sourceRoot":"","sources":["../src/children.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,qFAAqF;AACrF,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,aAAa;IACnB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;CACZ,CAAC;AAEX;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,MAAkD;IACrE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AACrE,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa,EAAE,MAAe;IAC3D,cAAc,CAAC,GAAG,EAAE;QAClB,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAClF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAaD,oEAAoE;AACpE,MAAM,aAAa,GAAG,CACpB,MAAyB,EACL,EAAE,CACtB,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;IACrD,CAAC,CAAE,MAA6B;IAChC,CAAC,CAAE,MAAmC,EAAE,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CACnB,MAAyB,EACzB,KAA0C;IAE1C,OAAO,CAAC,KAAkB,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,sDAAsD;QAC1F,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACpF,8EAA8E;QAC9E,OAAO,KAAK,CAAC,KAAK,CAAC,MAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// Child components: props down, outputs up (docs/design-docs/0010-child-components.md).\nimport type { Command } from './command.js';\nimport { OUTPUT_EVENT } from './intent.js';\nimport type { IntentInput, IntentParser, Tagged } from './types.js';\n\n/** Marker driver: `define()` handles emit commands itself, synchronously ordered. */\nexport const EMIT = {\n name: '@gyral/emit',\n run: () => undefined,\n} as const;\n\n/**\n * A command that sends `output` to the parent component. The parent receives it as an\n * intent on the child element: `<my-child data-intent=${i.Picked}>` + `child(MyChild, …)`.\n */\nexport function emit(output: Tagged & Readonly<Record<string, unknown>>): Command<never> {\n return { driver: EMIT, input: output, onSuccess: () => undefined };\n}\n\n/**\n * Sends an output to the parent component. A microtask keeps outputs in order and out of the\n * parent's render pass; the event bubbles through the parent's shadow tree only (not composed).\n */\nexport function dispatchOutput(host: Element, output: unknown): void {\n queueMicrotask(() => {\n if (!host.isConnected) return;\n host.dispatchEvent(\n new CustomEvent(OUTPUT_EVENT, { detail: output, bubbles: true, composed: false }),\n );\n });\n}\n\n/** Anything `child()` can read an output type from: a `define()` class. */\nexport interface OutputSource<O extends Tagged, E extends Element> {\n new (): E;\n /** Type-only marker for the component's output union. Never set at runtime. */\n readonly outputs?: O;\n}\n\n/** A child class, or a function returning it (for recursive or later-defined components). */\nexport type ChildSource<O extends Tagged, E extends Element> =\n OutputSource<O, E> | (() => OutputSource<O, E>);\n\n// Classes have a prototype; arrow functions (the lazy form) do not.\nconst resolveSource = <O extends Tagged, E extends Element>(\n source: ChildSource<O, E>,\n): OutputSource<O, E> =>\n 'prototype' in source && source.prototype !== undefined\n ? (source as OutputSource<O, E>)\n : (source as () => OutputSource<O, E>)();\n\n/**\n * Intent parser for a child component's outputs, typed by the child's output union.\n * `el` is the child element, for reading its props (an item id, for example).\n *\n * Pass `() => Child` when the class isn't defined yet, e.g. a component that contains\n * itself; annotate the constant's type so TypeScript accepts the self-reference:\n *\n * const Folder: GyralElementClass<State, Msg, Props, Out> = define('x-folder', {\n * intent: { Child: child(() => Folder, (out, el) => …) }, …\n * });\n */\nexport function child<O extends Tagged, E extends Element, M>(\n source: ChildSource<O, E>,\n toMsg: (output: O, el: E) => M | undefined,\n): IntentParser<M> {\n return (input: IntentInput) => {\n const type = resolveSource(source); // resolved per event, so the lazy form never hits TDZ\n if (!(input.target instanceof type) || input.detail === undefined) return undefined;\n // Sound: only `emit()` from a component of this class dispatches this detail.\n return toMsg(input.detail as O, input.target);\n };\n}\n"]}
1
+ {"version":3,"file":"children.js","sourceRoot":"","sources":["../src/children.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAGvC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,IAAI,EAAE,aAAa;IACnB,GAAG,EAAE,GAAG,EAAE,CAAC,SAAS;IACpB,KAAK,EAAE,CAAC,IAAe,EAAE,MAAe,EAAE,EAAE;QAC1C,cAAc,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;CACO,CAAC;AAEX;;;GAGG;AACH,MAAM,UAAU,IAAI,CAAC,MAAkD;IACrE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,SAAS,EAAE,CAAC;AACrE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,IAAa,EAAE,MAAe;IAC3D,KAAK,CAAC,GAAG,EAAE;QACT,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAClF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAaD,oEAAoE;AACpE,MAAM,aAAa,GAAG,CACpB,MAAyB,EACL,EAAE,CACtB,WAAW,IAAI,MAAM,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS;IACrD,CAAC,CAAE,MAA6B;IAChC,CAAC,CAAE,MAAmC,EAAE,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,KAAK,CACnB,MAAyB,EACzB,KAA0C;IAE1C,OAAO,CAAC,KAAkB,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,sDAAsD;QAC1F,IAAI,CAAC,CAAC,KAAK,CAAC,MAAM,YAAY,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAC;QACpF,8EAA8E;QAC9E,OAAO,KAAK,CAAC,KAAK,CAAC,MAAW,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["// Child components: props down, outputs up (docs/design-docs/0010-child-components.md).\nimport type { Command } from './command.js';\nimport type { LocalHost } from './features.js';\nimport { OUTPUT_EVENT } from './intent.js';\nimport { defer } from './scheduler.js';\nimport type { IntentInput, IntentParser, Tagged } from './types.js';\n\n/** Marker driver: the host runs emit commands itself (`local`), synchronously ordered. */\nexport const EMIT = {\n name: '@gyral/emit',\n run: () => undefined,\n local: (host: LocalHost, output: unknown) => {\n dispatchOutput(host.el, output);\n },\n} as const;\n\n/**\n * A command that sends `output` to the parent component. The parent receives it as an\n * intent on the child element: `<my-child data-intent=${i.Picked}>` + `child(MyChild, …)`.\n */\nexport function emit(output: Tagged & Readonly<Record<string, unknown>>): Command<never> {\n return { driver: EMIT, input: output, onSuccess: () => undefined };\n}\n\n/**\n * Sends an output to the parent component. A microtask keeps outputs in order and out of the\n * parent's render pass (settled() waits for it); the event bubbles through the parent's shadow\n * tree only (not composed).\n */\nexport function dispatchOutput(host: Element, output: unknown): void {\n defer(() => {\n if (!host.isConnected) return;\n host.dispatchEvent(\n new CustomEvent(OUTPUT_EVENT, { detail: output, bubbles: true, composed: false }),\n );\n });\n}\n\n/** Anything `child()` can read an output type from: a `define()` class. */\nexport interface OutputSource<O extends Tagged, E extends Element> {\n new (): E;\n /** Type-only marker for the component's output union. Never set at runtime. */\n readonly outputs?: O;\n}\n\n/** A child class, or a function returning it (for recursive or later-defined components). */\nexport type ChildSource<O extends Tagged, E extends Element> =\n OutputSource<O, E> | (() => OutputSource<O, E>);\n\n// Classes have a prototype; arrow functions (the lazy form) do not.\nconst resolveSource = <O extends Tagged, E extends Element>(\n source: ChildSource<O, E>,\n): OutputSource<O, E> =>\n 'prototype' in source && source.prototype !== undefined\n ? (source as OutputSource<O, E>)\n : (source as () => OutputSource<O, E>)();\n\n/**\n * Intent parser for a child component's outputs, typed by the child's output union.\n * `el` is the child element, for reading its props (an item id, for example).\n *\n * Pass `() => Child` when the class isn't defined yet, e.g. a component that contains\n * itself; annotate the constant's type so TypeScript accepts the self-reference:\n *\n * const Folder: GyralElementClass<State, Msg, Props, Out> = define('x-folder', {\n * intent: { Child: child(() => Folder, (out, el) => …) }, …\n * });\n */\nexport function child<O extends Tagged, E extends Element, M>(\n source: ChildSource<O, E>,\n toMsg: (output: O, el: E) => M | undefined,\n): IntentParser<M> {\n return (input: IntentInput) => {\n const type = resolveSource(source); // resolved per event, so the lazy form never hits TDZ\n if (!(input.target instanceof type) || input.detail === undefined) return undefined;\n // Sound: only `emit()` from a component of this class dispatches this detail.\n return toMsg(input.detail as O, input.target);\n };\n}\n"]}
package/dist/command.js CHANGED
@@ -1,3 +1,8 @@
1
+ // Effects as data (docs/design-docs/0006-effects-and-drivers.md). Plain TypeScript only: the
2
+ // interpreter lives in ./internal/ (ADR 0015); `command()` registers it (features.ts), so apps
3
+ // that build no commands don't bundle it.
4
+ import { features } from './features.js';
5
+ import { hostInterpreter } from './internal/interpreter.js';
1
6
  /** Identity helper so driver literals infer `I`, `O` and `E`. */
2
7
  export function defineDriver(driver) {
3
8
  return driver;
@@ -5,6 +10,7 @@ export function defineDriver(driver) {
5
10
  /** Builds a command whose mappers are typed by the driver's input, output and error. */
6
11
  // Two message parameters so success and failure may produce different variants.
7
12
  export function command(driver, input, handlers) {
13
+ features.commands = hostInterpreter;
8
14
  const { onFailure, key, concurrency } = handlers;
9
15
  return {
10
16
  driver: driver,
@@ -1 +1 @@
1
- {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAqEA,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAoB,MAAuB;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,gFAAgF;AAChF,MAAM,UAAU,OAAO,CACrB,MAAuB,EACvB,KAAQ,EACR,QAAuC;IAEvC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAC;IACjD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,KAAK;QACL,gFAAgF;QAChF,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAW,CAAC;QACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,SAAS,CAAC,KAAU,CAAC,EAAE,CAAC;QAC5F,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CACvB,IAAgB;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,IAAgE,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC","sourcesContent":["// Effects as data (docs/design-docs/0006-effects-and-drivers.md). Plain TypeScript only:\nimport type { IntentRejected } from './types.js';\n// the Effect-based interpreter lives in ./internal/.\n\n/** How commands in the same lane interact. See ADR 0006 for the table. */\nexport type Concurrency = 'merge' | 'switch' | 'exhaust' | 'queue';\n\nexport interface RetryPolicy {\n /** Retries after the first failure. */\n readonly times: number;\n /** Delay before each retry (the base delay for exponential backoff). Default 0. */\n readonly delayMs?: number;\n readonly backoff?: 'fixed' | 'exponential';\n}\n\nexport interface DriverContext<O = unknown> {\n /** Aborts when the command is switched away or its component disconnects. */\n readonly signal: AbortSignal;\n /**\n * Delivers an extra result while the command runs (streaming drivers: routers, timers,\n * sockets). Each value goes through the command's `onSuccess`. Ignored once the command\n * has settled or been aborted. A streaming `run` usually never resolves; abort ends it.\n */\n readonly emit: (output: O) => void;\n}\n\n/** Performs one kind of side effect. A plain object: easy to fake in tests. */\nexport interface Driver<I, O, E = unknown> {\n readonly name: string;\n readonly run: (input: I, ctx: DriverContext<O>) => O | Promise<O>;\n /** Default policy for this driver's commands. Default `'merge'`. */\n readonly concurrency?: Concurrency;\n readonly retry?: RetryPolicy;\n /** Turns a thrown or rejected value into this driver's typed error. */\n readonly toError?: (cause: unknown) => E;\n}\n\n/** Any driver, with its input type erased. */\nexport type AnyDriver = Driver<never, unknown>;\n\n/** Drivers substituted by name (test fakes, configured instances). */\nexport type DriverOverrides = Readonly<Record<string, AnyDriver>>;\n\n/** A side effect described as data, plus pure mappers from its outcome to messages. */\nexport interface Command<M> {\n readonly driver: AnyDriver;\n readonly input: unknown;\n /** Concurrency lane. Default: the driver's name. */\n readonly key?: string;\n readonly concurrency?: Concurrency;\n readonly onSuccess: (output: unknown) => M | undefined;\n /** If omitted, failures are logged and dropped. */\n readonly onFailure?: (error: unknown) => M | undefined;\n}\n\nexport interface CommandHandlers<O, E, MS, MF = MS> {\n readonly onSuccess: (output: O) => MS | undefined;\n readonly onFailure?: (error: E) => MF | undefined;\n readonly key?: string;\n readonly concurrency?: Concurrency;\n}\n\n/**\n * What a reducer (or `init`) returns: new state, optionally with commands to run. Commands may\n * also answer with the framework message `IntentRejected` (e.g. `submitForm` in @gyral/http\n * when the server rejects a form), which goes to the optional `IntentRejected` reducer.\n */\nexport type Next<S, M> = S | readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n\n/** Identity helper so driver literals infer `I`, `O` and `E`. */\nexport function defineDriver<I, O, E = unknown>(driver: Driver<I, O, E>): Driver<I, O, E> {\n return driver;\n}\n\n/** Builds a command whose mappers are typed by the driver's input, output and error. */\n// Two message parameters so success and failure may produce different variants.\nexport function command<I, O, E, MS, MF = MS>(\n driver: Driver<I, O, E>,\n input: I,\n handlers: CommandHandlers<O, E, MS, MF>,\n): Command<MS | MF> {\n const { onFailure, key, concurrency } = handlers;\n return {\n driver: driver,\n input,\n // Sound: the interpreter only passes this driver's output and toError() result.\n onSuccess: (output) => handlers.onSuccess(output as O),\n ...(onFailure === undefined ? {} : { onFailure: (error: unknown) => onFailure(error as E) }),\n ...(key === undefined ? {} : { key }),\n ...(concurrency === undefined ? {} : { concurrency }),\n };\n}\n\n/** Splits a reducer result into state and commands. State is never an array (ADR 0006). */\nexport function splitNext<S, M>(\n next: Next<S, M>,\n): readonly [S, ReadonlyArray<Command<M | IntentRejected>>] {\n if (Array.isArray(next) && next.length === 2 && Array.isArray(next[1])) {\n return next as readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n }\n return [next as S, []];\n}\n"]}
1
+ {"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,+FAA+F;AAC/F,0CAA0C;AAC1C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAoE5D,iEAAiE;AACjE,MAAM,UAAU,YAAY,CAAoB,MAAuB;IACrE,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,wFAAwF;AACxF,gFAAgF;AAChF,MAAM,UAAU,OAAO,CACrB,MAAuB,EACvB,KAAQ,EACR,QAAuC;IAEvC,QAAQ,CAAC,QAAQ,GAAG,eAAe,CAAC;IACpC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,EAAE,GAAG,QAAQ,CAAC;IACjD,OAAO;QACL,MAAM,EAAE,MAAM;QACd,KAAK;QACL,gFAAgF;QAChF,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAW,CAAC;QACtD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,KAAc,EAAE,EAAE,CAAC,SAAS,CAAC,KAAU,CAAC,EAAE,CAAC;QAC5F,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC;KACtD,CAAC;AACJ,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,SAAS,CACvB,IAAgB;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,IAAgE,CAAC;IAC1E,CAAC;IACD,OAAO,CAAC,IAAS,EAAE,EAAE,CAAC,CAAC;AACzB,CAAC","sourcesContent":["// Effects as data (docs/design-docs/0006-effects-and-drivers.md). Plain TypeScript only: the\n// interpreter lives in ./internal/ (ADR 0015); `command()` registers it (features.ts), so apps\n// that build no commands don't bundle it.\nimport { features } from './features.js';\nimport { hostInterpreter } from './internal/interpreter.js';\nimport type { IntentRejected } from './types.js';\n\n/** How commands in the same lane interact. See ADR 0006 for the table. */\nexport type Concurrency = 'merge' | 'switch' | 'exhaust' | 'queue';\n\nexport interface RetryPolicy {\n /** Retries after the first failure. */\n readonly times: number;\n /** Delay before each retry (the base delay for exponential backoff). Default 0. */\n readonly delayMs?: number;\n readonly backoff?: 'fixed' | 'exponential';\n}\n\nexport interface DriverContext<O = unknown> {\n /** Aborts when the command is switched away or its component disconnects. */\n readonly signal: AbortSignal;\n /**\n * Delivers an extra result while the command runs (streaming drivers: routers, timers,\n * sockets). Each value goes through the command's `onSuccess`. Ignored once the command\n * has settled or been aborted. A streaming `run` usually never resolves; abort ends it.\n */\n readonly emit: (output: O) => void;\n}\n\n/** Performs one kind of side effect. A plain object: easy to fake in tests. */\nexport interface Driver<I, O, E = unknown> {\n readonly name: string;\n readonly run: (input: I, ctx: DriverContext<O>) => O | Promise<O>;\n /** Default policy for this driver's commands. Default `'merge'`. */\n readonly concurrency?: Concurrency;\n readonly retry?: RetryPolicy;\n /** Turns a thrown or rejected value into this driver's typed error. */\n readonly toError?: (cause: unknown) => E;\n}\n\n/** Any driver, with its input type erased. */\nexport type AnyDriver = Driver<never, unknown>;\n\n/** Drivers substituted by name (test fakes, configured instances). */\nexport type DriverOverrides = Readonly<Record<string, AnyDriver>>;\n\n/** A side effect described as data, plus pure mappers from its outcome to messages. */\nexport interface Command<M> {\n readonly driver: AnyDriver;\n readonly input: unknown;\n /** Concurrency lane. Default: the driver's name. */\n readonly key?: string;\n readonly concurrency?: Concurrency;\n readonly onSuccess: (output: unknown) => M | undefined;\n /** If omitted, failures are logged and dropped. */\n readonly onFailure?: (error: unknown) => M | undefined;\n}\n\nexport interface CommandHandlers<O, E, MS, MF = MS> {\n readonly onSuccess: (output: O) => MS | undefined;\n readonly onFailure?: (error: E) => MF | undefined;\n readonly key?: string;\n readonly concurrency?: Concurrency;\n}\n\n/**\n * What a reducer (or `init`) returns: new state, optionally with commands to run. Commands may\n * also answer with the framework message `IntentRejected` (e.g. `submitForm` in @gyral/http\n * when the server rejects a form), which goes to the optional `IntentRejected` reducer.\n */\nexport type Next<S, M> = S | readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n\n/** Identity helper so driver literals infer `I`, `O` and `E`. */\nexport function defineDriver<I, O, E = unknown>(driver: Driver<I, O, E>): Driver<I, O, E> {\n return driver;\n}\n\n/** Builds a command whose mappers are typed by the driver's input, output and error. */\n// Two message parameters so success and failure may produce different variants.\nexport function command<I, O, E, MS, MF = MS>(\n driver: Driver<I, O, E>,\n input: I,\n handlers: CommandHandlers<O, E, MS, MF>,\n): Command<MS | MF> {\n features.commands = hostInterpreter;\n const { onFailure, key, concurrency } = handlers;\n return {\n driver: driver,\n input,\n // Sound: the interpreter only passes this driver's output and toError() result.\n onSuccess: (output) => handlers.onSuccess(output as O),\n ...(onFailure === undefined ? {} : { onFailure: (error: unknown) => onFailure(error as E) }),\n ...(key === undefined ? {} : { key }),\n ...(concurrency === undefined ? {} : { concurrency }),\n };\n}\n\n/** Splits a reducer result into state and commands. State is never an array (ADR 0006). */\nexport function splitNext<S, M>(\n next: Next<S, M>,\n): readonly [S, ReadonlyArray<Command<M | IntentRejected>>] {\n if (Array.isArray(next) && next.length === 2 && Array.isArray(next[1])) {\n return next as readonly [S, ReadonlyArray<Command<M | IntentRejected>>];\n }\n return [next as S, []];\n}\n"]}
@@ -0,0 +1,21 @@
1
+ /** An ESTree node: `start`/`end` are UTF-16 offsets into the module's code. */
2
+ export interface Node {
3
+ readonly type: string;
4
+ readonly start: number;
5
+ readonly end: number;
6
+ readonly [key: string]: unknown;
7
+ }
8
+ export declare const isNode: (value: unknown) => value is Node;
9
+ export declare const nodeAt: (node: Node, key: string) => Node | undefined;
10
+ /** The node or nodes at `key` (an array, or a single node). */
11
+ export declare const nodesAt: (node: Node, key: string) => Node[];
12
+ /** The child nodes of `node` that can contain runtime code, in source order. */
13
+ export declare function children(node: Node): Node[];
14
+ /** The names a binding pattern declares (`a`, `{ a, b: [c] }`, `...d`, `e = 1`). */
15
+ export declare function patternNames(pattern: Node | undefined): string[];
16
+ export declare const isFunction: (node: Node) => boolean;
17
+ /**
18
+ * The names `node` declares for its own scope, or undefined when it opens no scope. The
19
+ * Program scope is left out: the imported binding lives there.
20
+ */
21
+ export declare function scopeNames(node: Node): string[] | undefined;
@@ -0,0 +1,145 @@
1
+ // A minimal view of the ESTree AST that Rolldown's `this.parse` returns (oxc, TypeScript
2
+ // included), for the template compiler's scanner (scan.ts): generic child iteration, the
3
+ // names a binding pattern declares, and the names each scope declares, so a local variable
4
+ // that shadows the imported `html` is never mistaken for it.
5
+ export const isNode = (value) => typeof value === 'object' &&
6
+ value !== null &&
7
+ typeof value.type === 'string';
8
+ export const nodeAt = (node, key) => {
9
+ const value = node[key];
10
+ return isNode(value) ? value : undefined;
11
+ };
12
+ /** The node or nodes at `key` (an array, or a single node). */
13
+ export const nodesAt = (node, key) => {
14
+ const value = node[key];
15
+ return Array.isArray(value) ? value.filter(isNode) : isNode(value) ? [value] : [];
16
+ };
17
+ /** TypeScript-only keys: type annotations never reference a value. */
18
+ const TYPE_KEYS = new Set([
19
+ 'typeAnnotation',
20
+ 'typeArguments',
21
+ 'typeParameters',
22
+ 'returnType',
23
+ 'superTypeArguments',
24
+ 'implements',
25
+ ]);
26
+ /** TS nodes that wrap a runtime expression; every other `TS*` node is type-only. */
27
+ const TS_VALUE = new Set([
28
+ 'TSAsExpression',
29
+ 'TSSatisfiesExpression',
30
+ 'TSNonNullExpression',
31
+ 'TSTypeAssertion',
32
+ 'TSInstantiationExpression',
33
+ 'TSExportAssignment',
34
+ ]);
35
+ /** The child nodes of `node` that can contain runtime code, in source order. */
36
+ export function children(node) {
37
+ if (node.type.startsWith('TS')) {
38
+ if (TS_VALUE.has(node.type))
39
+ return nodesAt(node, 'expression');
40
+ if (node.type === 'TSParameterProperty')
41
+ return nodesAt(node, 'parameter');
42
+ if (node.type === 'TSModuleDeclaration')
43
+ return nodesAt(node, 'body');
44
+ if (node.type === 'TSModuleBlock')
45
+ return nodesAt(node, 'body');
46
+ return [];
47
+ }
48
+ const out = [];
49
+ for (const key in node) {
50
+ if (key === 'type' || key === 'start' || key === 'end' || TYPE_KEYS.has(key))
51
+ continue;
52
+ const value = node[key];
53
+ if (Array.isArray(value))
54
+ out.push(...value.filter(isNode));
55
+ else if (isNode(value))
56
+ out.push(value);
57
+ }
58
+ return out.sort((a, b) => a.start - b.start);
59
+ }
60
+ /** The names a binding pattern declares (`a`, `{ a, b: [c] }`, `...d`, `e = 1`). */
61
+ export function patternNames(pattern) {
62
+ if (pattern === undefined)
63
+ return [];
64
+ switch (pattern.type) {
65
+ case 'Identifier':
66
+ return [pattern['name']];
67
+ case 'ObjectPattern':
68
+ return nodesAt(pattern, 'properties').flatMap((p) => p.type === 'RestElement'
69
+ ? patternNames(nodeAt(p, 'argument'))
70
+ : patternNames(nodeAt(p, 'value')));
71
+ case 'ArrayPattern':
72
+ return nodesAt(pattern, 'elements').flatMap(patternNames);
73
+ case 'RestElement':
74
+ return patternNames(nodeAt(pattern, 'argument'));
75
+ case 'AssignmentPattern':
76
+ return patternNames(nodeAt(pattern, 'left'));
77
+ case 'TSParameterProperty':
78
+ return patternNames(nodeAt(pattern, 'parameter'));
79
+ default:
80
+ return [];
81
+ }
82
+ }
83
+ const FUNCTIONS = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);
84
+ export const isFunction = (node) => FUNCTIONS.has(node.type);
85
+ /** `var` names declared anywhere in a function body (hoisted), not crossing nested functions. */
86
+ function varNames(node, out) {
87
+ if (node.type === 'VariableDeclaration' && node['kind'] === 'var') {
88
+ for (const d of nodesAt(node, 'declarations'))
89
+ out.push(...patternNames(nodeAt(d, 'id')));
90
+ }
91
+ for (const child of children(node))
92
+ if (!isFunction(child))
93
+ varNames(child, out);
94
+ }
95
+ /** Block-scoped names declared directly in a list of statements. */
96
+ function lexicalNames(statements) {
97
+ return statements.flatMap((s) => {
98
+ if (s.type === 'VariableDeclaration' && s['kind'] !== 'var') {
99
+ return nodesAt(s, 'declarations').flatMap((d) => patternNames(nodeAt(d, 'id')));
100
+ }
101
+ if (s.type === 'FunctionDeclaration' || s.type === 'ClassDeclaration') {
102
+ return patternNames(nodeAt(s, 'id'));
103
+ }
104
+ return [];
105
+ });
106
+ }
107
+ /**
108
+ * The names `node` declares for its own scope, or undefined when it opens no scope. The
109
+ * Program scope is left out: the imported binding lives there.
110
+ */
111
+ export function scopeNames(node) {
112
+ switch (node.type) {
113
+ case 'FunctionDeclaration':
114
+ case 'FunctionExpression':
115
+ case 'ArrowFunctionExpression': {
116
+ const names = nodesAt(node, 'params').flatMap(patternNames);
117
+ if (node.type === 'FunctionExpression')
118
+ names.push(...patternNames(nodeAt(node, 'id')));
119
+ const body = nodeAt(node, 'body');
120
+ if (body?.type === 'BlockStatement') {
121
+ varNames(body, names);
122
+ names.push(...lexicalNames(nodesAt(body, 'body')));
123
+ }
124
+ return names;
125
+ }
126
+ case 'BlockStatement':
127
+ case 'StaticBlock':
128
+ return lexicalNames(nodesAt(node, 'body'));
129
+ case 'SwitchStatement':
130
+ return lexicalNames(nodesAt(node, 'cases').flatMap((c) => nodesAt(c, 'consequent')));
131
+ case 'ForStatement':
132
+ case 'ForInStatement':
133
+ case 'ForOfStatement': {
134
+ const head = nodeAt(node, node.type === 'ForStatement' ? 'init' : 'left');
135
+ return head === undefined ? [] : lexicalNames([head]);
136
+ }
137
+ case 'CatchClause':
138
+ return patternNames(nodeAt(node, 'param'));
139
+ case 'ClassExpression':
140
+ return patternNames(nodeAt(node, 'id'));
141
+ default:
142
+ return undefined;
143
+ }
144
+ }
145
+ //# sourceMappingURL=ast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ast.js","sourceRoot":"","sources":["../../src/compiler/ast.ts"],"names":[],"mappings":"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","sourcesContent":["// A minimal view of the ESTree AST that Rolldown's `this.parse` returns (oxc, TypeScript\n// included), for the template compiler's scanner (scan.ts): generic child iteration, the\n// names a binding pattern declares, and the names each scope declares, so a local variable\n// that shadows the imported `html` is never mistaken for it.\n\n/** An ESTree node: `start`/`end` are UTF-16 offsets into the module's code. */\nexport interface Node {\n readonly type: string;\n readonly start: number;\n readonly end: number;\n readonly [key: string]: unknown;\n}\n\nexport const isNode = (value: unknown): value is Node =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as { type?: unknown }).type === 'string';\n\nexport const nodeAt = (node: Node, key: string): Node | undefined => {\n const value = node[key];\n return isNode(value) ? value : undefined;\n};\n\n/** The node or nodes at `key` (an array, or a single node). */\nexport const nodesAt = (node: Node, key: string): Node[] => {\n const value = node[key];\n return Array.isArray(value) ? value.filter(isNode) : isNode(value) ? [value] : [];\n};\n\n/** TypeScript-only keys: type annotations never reference a value. */\nconst TYPE_KEYS = new Set([\n 'typeAnnotation',\n 'typeArguments',\n 'typeParameters',\n 'returnType',\n 'superTypeArguments',\n 'implements',\n]);\n\n/** TS nodes that wrap a runtime expression; every other `TS*` node is type-only. */\nconst TS_VALUE = new Set([\n 'TSAsExpression',\n 'TSSatisfiesExpression',\n 'TSNonNullExpression',\n 'TSTypeAssertion',\n 'TSInstantiationExpression',\n 'TSExportAssignment',\n]);\n\n/** The child nodes of `node` that can contain runtime code, in source order. */\nexport function children(node: Node): Node[] {\n if (node.type.startsWith('TS')) {\n if (TS_VALUE.has(node.type)) return nodesAt(node, 'expression');\n if (node.type === 'TSParameterProperty') return nodesAt(node, 'parameter');\n if (node.type === 'TSModuleDeclaration') return nodesAt(node, 'body');\n if (node.type === 'TSModuleBlock') return nodesAt(node, 'body');\n return [];\n }\n const out: Node[] = [];\n for (const key in node) {\n if (key === 'type' || key === 'start' || key === 'end' || TYPE_KEYS.has(key)) continue;\n const value = node[key];\n if (Array.isArray(value)) out.push(...value.filter(isNode));\n else if (isNode(value)) out.push(value);\n }\n return out.sort((a, b) => a.start - b.start);\n}\n\n/** The names a binding pattern declares (`a`, `{ a, b: [c] }`, `...d`, `e = 1`). */\nexport function patternNames(pattern: Node | undefined): string[] {\n if (pattern === undefined) return [];\n switch (pattern.type) {\n case 'Identifier':\n return [pattern['name'] as string];\n case 'ObjectPattern':\n return nodesAt(pattern, 'properties').flatMap((p) =>\n p.type === 'RestElement'\n ? patternNames(nodeAt(p, 'argument'))\n : patternNames(nodeAt(p, 'value')),\n );\n case 'ArrayPattern':\n return nodesAt(pattern, 'elements').flatMap(patternNames);\n case 'RestElement':\n return patternNames(nodeAt(pattern, 'argument'));\n case 'AssignmentPattern':\n return patternNames(nodeAt(pattern, 'left'));\n case 'TSParameterProperty':\n return patternNames(nodeAt(pattern, 'parameter'));\n default:\n return [];\n }\n}\n\nconst FUNCTIONS = new Set(['FunctionDeclaration', 'FunctionExpression', 'ArrowFunctionExpression']);\n\nexport const isFunction = (node: Node): boolean => FUNCTIONS.has(node.type);\n\n/** `var` names declared anywhere in a function body (hoisted), not crossing nested functions. */\nfunction varNames(node: Node, out: string[]): void {\n if (node.type === 'VariableDeclaration' && node['kind'] === 'var') {\n for (const d of nodesAt(node, 'declarations')) out.push(...patternNames(nodeAt(d, 'id')));\n }\n for (const child of children(node)) if (!isFunction(child)) varNames(child, out);\n}\n\n/** Block-scoped names declared directly in a list of statements. */\nfunction lexicalNames(statements: readonly Node[]): string[] {\n return statements.flatMap((s) => {\n if (s.type === 'VariableDeclaration' && s['kind'] !== 'var') {\n return nodesAt(s, 'declarations').flatMap((d) => patternNames(nodeAt(d, 'id')));\n }\n if (s.type === 'FunctionDeclaration' || s.type === 'ClassDeclaration') {\n return patternNames(nodeAt(s, 'id'));\n }\n return [];\n });\n}\n\n/**\n * The names `node` declares for its own scope, or undefined when it opens no scope. The\n * Program scope is left out: the imported binding lives there.\n */\nexport function scopeNames(node: Node): string[] | undefined {\n switch (node.type) {\n case 'FunctionDeclaration':\n case 'FunctionExpression':\n case 'ArrowFunctionExpression': {\n const names = nodesAt(node, 'params').flatMap(patternNames);\n if (node.type === 'FunctionExpression') names.push(...patternNames(nodeAt(node, 'id')));\n const body = nodeAt(node, 'body');\n if (body?.type === 'BlockStatement') {\n varNames(body, names);\n names.push(...lexicalNames(nodesAt(body, 'body')));\n }\n return names;\n }\n case 'BlockStatement':\n case 'StaticBlock':\n return lexicalNames(nodesAt(node, 'body'));\n case 'SwitchStatement':\n return lexicalNames(nodesAt(node, 'cases').flatMap((c) => nodesAt(c, 'consequent')));\n case 'ForStatement':\n case 'ForInStatement':\n case 'ForOfStatement': {\n const head = nodeAt(node, node.type === 'ForStatement' ? 'init' : 'left');\n return head === undefined ? [] : lexicalNames([head]);\n }\n case 'CatchClause':\n return patternNames(nodeAt(node, 'param'));\n case 'ClassExpression':\n return patternNames(nodeAt(node, 'id'));\n default:\n return undefined;\n }\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import type { Node } from './ast.js';
2
+ import { type Parse5 } from './parse5-check.js';
3
+ import type { HtmlImport } from './scan.js';
4
+ import { type Lines, type SourceMap } from './source.js';
5
+ /** A build error at `start`–`end` of the module (the plugin adds the code frame). */
6
+ export declare class CompileError extends Error {
7
+ readonly start: number;
8
+ readonly end: number;
9
+ constructor(start: number, end: number, message: string);
10
+ }
11
+ export interface CompileInput {
12
+ readonly lines: Lines;
13
+ /** Module id, and its path relative to the root (for messages). */
14
+ readonly id: string;
15
+ readonly file: string;
16
+ readonly sites: readonly {
17
+ readonly node: Node;
18
+ readonly binding: HtmlImport;
19
+ }[];
20
+ /** Server build: keep the server segments. */
21
+ readonly ssr: boolean;
22
+ /**
23
+ * Keep template ids: SSR and development builds (development markers, the hydration id
24
+ * check). A production client compares templates by identity, so its objects go without.
25
+ */
26
+ readonly ids: boolean;
27
+ readonly parse5: Parse5 | undefined;
28
+ /** Template id → normalized strings and call site, for the whole build (collisions). */
29
+ readonly seen: Map<string, {
30
+ readonly strings: string;
31
+ readonly loc: string;
32
+ }>;
33
+ }
34
+ /** Rewrites every call site in the module; nested templates inside values are included. */
35
+ export declare function compileModule(input: CompileInput): {
36
+ code: string;
37
+ map: SourceMap;
38
+ };