@llui/dom 0.0.11 → 0.0.14

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 (127) hide show
  1. package/dist/addressed.d.ts +1 -1
  2. package/dist/addressed.d.ts.map +1 -1
  3. package/dist/addressed.js +1 -1
  4. package/dist/addressed.js.map +1 -1
  5. package/dist/binding.d.ts +1 -1
  6. package/dist/binding.d.ts.map +1 -1
  7. package/dist/binding.js +15 -1
  8. package/dist/binding.js.map +1 -1
  9. package/dist/component.d.ts +1 -1
  10. package/dist/component.d.ts.map +1 -1
  11. package/dist/component.js.map +1 -1
  12. package/dist/compose.d.ts +78 -0
  13. package/dist/compose.d.ts.map +1 -0
  14. package/dist/compose.js +47 -0
  15. package/dist/compose.js.map +1 -0
  16. package/dist/devtools.d.ts +19 -3
  17. package/dist/devtools.d.ts.map +1 -1
  18. package/dist/devtools.js +131 -74
  19. package/dist/devtools.js.map +1 -1
  20. package/dist/el-split.d.ts +2 -2
  21. package/dist/el-split.d.ts.map +1 -1
  22. package/dist/el-split.js +19 -5
  23. package/dist/el-split.js.map +1 -1
  24. package/dist/el-template.d.ts +1 -1
  25. package/dist/el-template.d.ts.map +1 -1
  26. package/dist/el-template.js +3 -3
  27. package/dist/el-template.js.map +1 -1
  28. package/dist/elements.js +3 -3
  29. package/dist/elements.js.map +1 -1
  30. package/dist/form.js.map +1 -1
  31. package/dist/hmr.d.ts +1 -1
  32. package/dist/hmr.d.ts.map +1 -1
  33. package/dist/hmr.js +7 -7
  34. package/dist/hmr.js.map +1 -1
  35. package/dist/hydrate.js.map +1 -1
  36. package/dist/index.d.ts +34 -33
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +31 -30
  39. package/dist/index.js.map +1 -1
  40. package/dist/mathml-elements.js +3 -3
  41. package/dist/mathml-elements.js.map +1 -1
  42. package/dist/merge-handlers.js.map +1 -1
  43. package/dist/mount.d.ts +1 -1
  44. package/dist/mount.d.ts.map +1 -1
  45. package/dist/mount.js +37 -8
  46. package/dist/mount.js.map +1 -1
  47. package/dist/primitives/branch.d.ts +1 -1
  48. package/dist/primitives/branch.d.ts.map +1 -1
  49. package/dist/primitives/branch.js +40 -18
  50. package/dist/primitives/branch.js.map +1 -1
  51. package/dist/primitives/child.d.ts +1 -1
  52. package/dist/primitives/child.d.ts.map +1 -1
  53. package/dist/primitives/child.js +6 -6
  54. package/dist/primitives/child.js.map +1 -1
  55. package/dist/primitives/context.js +2 -2
  56. package/dist/primitives/context.js.map +1 -1
  57. package/dist/primitives/each.d.ts +1 -1
  58. package/dist/primitives/each.d.ts.map +1 -1
  59. package/dist/primitives/each.js +24 -20
  60. package/dist/primitives/each.js.map +1 -1
  61. package/dist/primitives/error-boundary.js +2 -2
  62. package/dist/primitives/error-boundary.js.map +1 -1
  63. package/dist/primitives/foreign.d.ts +1 -1
  64. package/dist/primitives/foreign.d.ts.map +1 -1
  65. package/dist/primitives/foreign.js +3 -3
  66. package/dist/primitives/foreign.js.map +1 -1
  67. package/dist/primitives/lazy.d.ts +2 -2
  68. package/dist/primitives/lazy.d.ts.map +1 -1
  69. package/dist/primitives/lazy.js +5 -5
  70. package/dist/primitives/lazy.js.map +1 -1
  71. package/dist/primitives/memo.js.map +1 -1
  72. package/dist/primitives/on-mount.d.ts +22 -0
  73. package/dist/primitives/on-mount.d.ts.map +1 -1
  74. package/dist/primitives/on-mount.js +59 -4
  75. package/dist/primitives/on-mount.js.map +1 -1
  76. package/dist/primitives/portal.d.ts +1 -1
  77. package/dist/primitives/portal.d.ts.map +1 -1
  78. package/dist/primitives/portal.js +2 -2
  79. package/dist/primitives/portal.js.map +1 -1
  80. package/dist/primitives/selector.d.ts +1 -1
  81. package/dist/primitives/selector.d.ts.map +1 -1
  82. package/dist/primitives/selector.js +5 -5
  83. package/dist/primitives/selector.js.map +1 -1
  84. package/dist/primitives/show.d.ts +1 -1
  85. package/dist/primitives/show.d.ts.map +1 -1
  86. package/dist/primitives/show.js +1 -1
  87. package/dist/primitives/show.js.map +1 -1
  88. package/dist/primitives/slice.d.ts +2 -2
  89. package/dist/primitives/slice.d.ts.map +1 -1
  90. package/dist/primitives/slice.js +7 -7
  91. package/dist/primitives/slice.js.map +1 -1
  92. package/dist/primitives/text.js +4 -4
  93. package/dist/primitives/text.js.map +1 -1
  94. package/dist/primitives/virtual-each.d.ts +1 -1
  95. package/dist/primitives/virtual-each.d.ts.map +1 -1
  96. package/dist/primitives/virtual-each.js +9 -8
  97. package/dist/primitives/virtual-each.js.map +1 -1
  98. package/dist/render-context.d.ts +2 -2
  99. package/dist/render-context.d.ts.map +1 -1
  100. package/dist/render-context.js +8 -2
  101. package/dist/render-context.js.map +1 -1
  102. package/dist/runtime.js +1 -1
  103. package/dist/runtime.js.map +1 -1
  104. package/dist/scope.d.ts +1 -1
  105. package/dist/scope.d.ts.map +1 -1
  106. package/dist/scope.js.map +1 -1
  107. package/dist/slice-handler.js.map +1 -1
  108. package/dist/ssr-dom.js.map +1 -1
  109. package/dist/ssr.d.ts +1 -1
  110. package/dist/ssr.d.ts.map +1 -1
  111. package/dist/ssr.js +4 -4
  112. package/dist/ssr.js.map +1 -1
  113. package/dist/structural.js.map +1 -1
  114. package/dist/svg-elements.js +3 -3
  115. package/dist/svg-elements.js.map +1 -1
  116. package/dist/types.d.ts +1 -1
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js.map +1 -1
  119. package/dist/update-loop.d.ts +2 -2
  120. package/dist/update-loop.d.ts.map +1 -1
  121. package/dist/update-loop.js +10 -6
  122. package/dist/update-loop.js.map +1 -1
  123. package/dist/view-helpers.d.ts +3 -3
  124. package/dist/view-helpers.d.ts.map +1 -1
  125. package/dist/view-helpers.js +7 -7
  126. package/dist/view-helpers.js.map +1 -1
  127. package/package.json +10 -10
@@ -1,4 +1,4 @@
1
- import type { ComponentDef } from './types';
1
+ import type { ComponentDef } from './types.js';
2
2
  export interface AddressedEffect {
3
3
  __addressed: true;
4
4
  __targetKey: string | number;
@@ -1 +1 @@
1
- {"version":3,"file":"addressed.d.ts","sourceRoot":"","sources":["../src/addressed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAG3C,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,IAAI,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAA;IAC5B,KAAK,EAAE,OAAO,CAAA;CACf;AAKD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG,IAAI,CAEhG;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1D;AAYD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,eAAe,CAM5E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,GAAG,EAAE,MAAM,GAAG,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,eAAe,CAAC,CAavD"}
1
+ {"version":3,"file":"addressed.d.ts","sourceRoot":"","sources":["../src/addressed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAG9C,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,IAAI,CAAA;IACjB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAA;IAC5B,KAAK,EAAE,OAAO,CAAA;CACf;AAKD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE;IAAE,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG,IAAI,CAEhG;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAE1D;AAYD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,eAAe,CAM5E;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAC/B,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1B,GAAG,EAAE,MAAM,GAAG,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,KAAK,eAAe,CAAC,CAavD"}
package/dist/addressed.js CHANGED
@@ -1,4 +1,4 @@
1
- import { setAddressedDispatcher } from './update-loop';
1
+ import { setAddressedDispatcher } from './update-loop.js';
2
2
  // Global component registry — keyed by child key
3
3
  const registry = new Map();
4
4
  export function registerChild(key, inst) {
@@ -1 +1 @@
1
- {"version":3,"file":"addressed.js","sourceRoot":"","sources":["../src/addressed.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAQtD,iDAAiD;AACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqD,CAAA;AAE7E,MAAM,UAAU,aAAa,CAAC,GAAoB,EAAE,IAAsC;IACxF,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAoB;IAClD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AACtB,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAwD;IACjF,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,sBAAsB,CAAC,iBAAiB,CAAC,CAAA;AAEzC,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACd,MAA0B,CAAC,WAAW,KAAK,IAAI,CACjD,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,GAA0B,EAC1B,GAAoB;IAEpB,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;IAC7B,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IAExB,MAAM,OAAO,GAA0D,EAAE,CAAA;IACzE,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAgB,EAAE,EAAE,CAAC,CAAC;YACrC,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"addressed.js","sourceRoot":"","sources":["../src/addressed.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAQzD,iDAAiD;AACjD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqD,CAAA;AAE7E,MAAM,UAAU,aAAa,CAAC,GAAoB,EAAE,IAAsC;IACxF,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;AACzB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAoB;IAClD,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;AACtB,CAAC;AAED,SAAS,iBAAiB,CAAC,MAAwD;IACjF,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;IAC/C,IAAI,MAAM,EAAE,CAAC;QACX,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;IAC3B,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,sBAAsB,CAAC,iBAAiB,CAAC,CAAA;AAEzC,MAAM,UAAU,iBAAiB,CAAC,MAAe;IAC/C,OAAO,CACL,OAAO,MAAM,KAAK,QAAQ;QAC1B,MAAM,KAAK,IAAI;QACd,MAA0B,CAAC,WAAW,KAAK,IAAI,CACjD,CAAA;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,GAA0B,EAC1B,GAAoB;IAEpB,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;IAC7B,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAA;IAExB,MAAM,OAAO,GAA0D,EAAE,CAAA;IACzE,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,MAAgB,EAAE,EAAE,CAAC,CAAC;YACrC,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,GAAG;YAChB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC;SACvB,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC","sourcesContent":["import type { ComponentDef } from './types.js'\nimport { setAddressedDispatcher } from './update-loop.js'\n\nexport interface AddressedEffect {\n __addressed: true\n __targetKey: string | number\n __msg: unknown\n}\n\n// Global component registry — keyed by child key\nconst registry = new Map<string | number, { send: (msg: unknown) => void }>()\n\nexport function registerChild(key: string | number, inst: { send: (msg: unknown) => void }): void {\n registry.set(key, inst)\n}\n\nexport function unregisterChild(key: string | number): void {\n registry.delete(key)\n}\n\nfunction dispatchAddressed(effect: { __targetKey: string | number; __msg: unknown }): void {\n const target = registry.get(effect.__targetKey)\n if (target) {\n target.send(effect.__msg)\n }\n}\n\n// Register the dispatcher — this runs when addressed.ts is first imported\nsetAddressedDispatcher(dispatchAddressed)\n\nexport function isAddressedEffect(effect: unknown): effect is AddressedEffect {\n return (\n typeof effect === 'object' &&\n effect !== null &&\n (effect as AddressedEffect).__addressed === true\n )\n}\n\n/**\n * Build a typed address builder from a component definition's `receives` map.\n */\nexport function addressOf<S, M, E>(\n def: ComponentDef<S, M, E>,\n key: string | number,\n): Record<string, (params?: unknown) => AddressedEffect> {\n const receives = def.receives\n if (!receives) return {}\n\n const builder: Record<string, (params?: unknown) => AddressedEffect> = {}\n for (const [name, handler] of Object.entries(receives)) {\n builder[name] = (params?: unknown) => ({\n __addressed: true,\n __targetKey: key,\n __msg: handler(params),\n })\n }\n return builder\n}\n"]}
package/dist/binding.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Scope, Binding, BindingKind } from './types';
1
+ import type { Scope, Binding, BindingKind } from './types.js';
2
2
  export interface CreateBindingOpts {
3
3
  mask: number;
4
4
  accessor: (state: never) => unknown;
@@ -1 +1 @@
1
- {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAG1D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,IAAI,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAID,wBAAgB,eAAe,IAAI,OAAO,EAAE,GAAG,IAAI,CAElD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAiB5E;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,KAAK,EAAE,OAAO,GACb,IAAI,CAwCN"}
1
+ {"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAA;IACnC,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,IAAI,CAAA;IACV,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,OAAO,CAAA;CACjB;AAID,wBAAgB,eAAe,IAAI,OAAO,EAAE,GAAG,IAAI,CAElD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,IAAI,GAAG,IAAI,CAE3D;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAiB5E;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACvD,KAAK,EAAE,OAAO,GACb,IAAI,CAyDN"}
package/dist/binding.js CHANGED
@@ -1,4 +1,4 @@
1
- import { addBinding } from './scope';
1
+ import { addBinding } from './scope.js';
2
2
  let flatBindings = null;
3
3
  export function getFlatBindings() {
4
4
  return flatBindings;
@@ -24,6 +24,20 @@ export function createBinding(scope, opts) {
24
24
  return binding;
25
25
  }
26
26
  export function applyBinding(target, value) {
27
+ // Defensive guard: if a reactive accessor leaks through as a raw
28
+ // function value, emitting its `.toString()` into the DOM (e.g. as
29
+ // an attribute) would be a silent correctness bug that only surfaces
30
+ // on server-rendered pages. Throw loudly so the callsite is obvious.
31
+ // Event handlers (onXxx → 'prop' kind) are NOT handled here; events
32
+ // are registered via addEventListener in the element helpers, not
33
+ // via applyBinding.
34
+ if (typeof value === 'function') {
35
+ throw new TypeError(`[LLui] applyBinding(${target.kind}${target.key ? `, '${target.key}'` : ''}) received ` +
36
+ `a function as its value. This means an accessor wasn't invoked before ` +
37
+ `reaching the binding layer — usually a bug in a compiled binding tuple or ` +
38
+ `in a helper that forwards props without calling them. The arrow's source ` +
39
+ `would have been serialized into the DOM otherwise. Offender: ${value.toString().slice(0, 120)}`);
40
+ }
27
41
  switch (target.kind) {
28
42
  case 'text':
29
43
  target.node.nodeValue = String(value);
@@ -1 +1 @@
1
- {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAWpC,IAAI,YAAY,GAAqB,IAAI,CAAA;AAEzC,MAAM,UAAU,eAAe;IAC7B,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAqB;IACnD,YAAY,GAAG,GAAG,CAAA;AACpB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAAY,EAAE,IAAuB;IACjE,MAAM,OAAO,GAAY;QACvB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAuC;QACtD,SAAS,EAAE,SAAS;QACpB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,UAAU,EAAE,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,KAAK;KACZ,CAAA;IAED,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAC1B,IAAI,YAAY;QAAE,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAE5C,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAuD,EACvD,KAAc;IAEd,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,MAAM,CAAC,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;YACrC,MAAK;QAEP,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,MAAM,EAAE,GAAG,MAAM,CAAC,IAAmB,CAEpC;YAAC,EAAU,CAAC,MAAM,CAAC,GAAI,CAAC,GAAG,KAAK,CAAA;YACjC,MAAK;QACP,CAAC;QAED,KAAK,MAAM;YACT,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBAClB,CAAC;gBAAC,MAAM,CAAC,IAAgB,CAAC,eAAe,CAAC,MAAM,CAAC,GAAI,CAAC,CAAA;YACxD,CAAC;iBAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;gBAC3B,4DAA4D;gBAC5D,IAAI,MAAM,CAAC,GAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBACpC,CAAC;oBAAC,MAAM,CAAC,IAAgB,CAAC,YAAY,CAAC,MAAM,CAAC,GAAI,EAAE,OAAO,CAAC,CAAA;gBAC9D,CAAC;qBAAM,CAAC;oBACN,CAAC;oBAAC,MAAM,CAAC,IAAgB,CAAC,eAAe,CAAC,MAAM,CAAC,GAAI,CAAC,CAAA;gBACxD,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,CAAC;gBAAC,MAAM,CAAC,IAAgB,CAAC,YAAY,CAAC,MAAM,CAAC,GAAI,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;YACpE,CAAC;YACD,MAAK;QAEP,KAAK,OAAO;YACV,CAAC;YAAC,MAAM,CAAC,IAAgB,CAAC,YAAY,CAAC,OAAO,EAAE,KAAe,CAAC,CAAA;YAChE,MAAK;QAEP,KAAK,OAAO;YACV,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;gBAClB,CAAC;gBAAC,MAAM,CAAC,IAAoB,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GAAI,CAAC,CAAA;YACjE,CAAC;iBAAM,CAAC;gBACN,CAAC;gBAAC,MAAM,CAAC,IAAoB,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,GAAI,EAAE,KAAe,CAAC,CAAA;YAC/E,CAAC;YACD,MAAK;IACT,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"binding.js","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Scope, Binding, BindingKind } from './types.js'\nimport { addBinding } from './scope.js'\n\nexport interface CreateBindingOpts {\n mask: number\n accessor: (state: never) => unknown\n kind: BindingKind\n node: Node\n key?: string\n perItem: boolean\n}\n\nlet flatBindings: Binding[] | null = null\n\nexport function getFlatBindings(): Binding[] | null {\n return flatBindings\n}\n\nexport function setFlatBindings(arr: Binding[] | null): void {\n flatBindings = arr\n}\n\nexport function createBinding(scope: Scope, opts: CreateBindingOpts): Binding {\n const binding: Binding = {\n mask: opts.mask,\n accessor: opts.accessor as (state: unknown) => unknown,\n lastValue: undefined,\n kind: opts.kind,\n node: opts.node,\n key: opts.key,\n ownerScope: scope,\n perItem: opts.perItem,\n dead: false,\n }\n\n addBinding(scope, binding)\n if (flatBindings) flatBindings.push(binding)\n\n return binding\n}\n\nexport function applyBinding(\n target: { kind: BindingKind; node: Node; key?: string },\n value: unknown,\n): void {\n // Defensive guard: if a reactive accessor leaks through as a raw\n // function value, emitting its `.toString()` into the DOM (e.g. as\n // an attribute) would be a silent correctness bug that only surfaces\n // on server-rendered pages. Throw loudly so the callsite is obvious.\n // Event handlers (onXxx → 'prop' kind) are NOT handled here; events\n // are registered via addEventListener in the element helpers, not\n // via applyBinding.\n if (typeof value === 'function') {\n throw new TypeError(\n `[LLui] applyBinding(${target.kind}${target.key ? `, '${target.key}'` : ''}) received ` +\n `a function as its value. This means an accessor wasn't invoked before ` +\n `reaching the binding layer — usually a bug in a compiled binding tuple or ` +\n `in a helper that forwards props without calling them. The arrow's source ` +\n `would have been serialized into the DOM otherwise. Offender: ${value.toString().slice(0, 120)}`,\n )\n }\n\n switch (target.kind) {\n case 'text':\n target.node.nodeValue = String(value)\n break\n\n case 'prop': {\n const el = target.node as HTMLElement\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ;(el as any)[target.key!] = value\n break\n }\n\n case 'attr':\n if (value == null) {\n ;(target.node as Element).removeAttribute(target.key!)\n } else if (value === false) {\n // ARIA attributes need explicit \"false\"; others are removed\n if (target.key!.startsWith('aria-')) {\n ;(target.node as Element).setAttribute(target.key!, 'false')\n } else {\n ;(target.node as Element).removeAttribute(target.key!)\n }\n } else {\n ;(target.node as Element).setAttribute(target.key!, String(value))\n }\n break\n\n case 'class':\n ;(target.node as Element).setAttribute('class', value as string)\n break\n\n case 'style':\n if (value == null) {\n ;(target.node as HTMLElement).style.removeProperty(target.key!)\n } else {\n ;(target.node as HTMLElement).style.setProperty(target.key!, value as string)\n }\n break\n }\n}\n"]}
@@ -1,3 +1,3 @@
1
- import type { ComponentDef } from './types';
1
+ import type { ComponentDef } from './types.js';
2
2
  export declare function component<S, M, E = never, D = void>(def: ComponentDef<S, M, E, D>): ComponentDef<S, M, E, D>;
3
3
  //# sourceMappingURL=component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE3C,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,EACjD,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1B"}
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,IAAI,EACjD,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAC5B,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAE1B"}
@@ -1 +1 @@
1
- {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CACvB,GAA6B;IAE7B,OAAO,GAAG,CAAA;AACZ,CAAC"}
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../src/component.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS,CACvB,GAA6B;IAE7B,OAAO,GAAG,CAAA;AACZ,CAAC","sourcesContent":["import type { ComponentDef } from './types.js'\n\nexport function component<S, M, E = never, D = void>(\n def: ComponentDef<S, M, E, D>,\n): ComponentDef<S, M, E, D> {\n return def\n}\n"]}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Type-level utilities + runtime helper for composing multiple child
3
+ * component modules into a parent's State and Msg types.
4
+ *
5
+ * Eliminates the manual State/Msg union declarations for each embedded
6
+ * sub-component. The developer declares the children map once, and
7
+ * `ChildState` / `ChildMsg` derive the wrapper types.
8
+ *
9
+ * ```ts
10
+ * import type { ChildState, ChildMsg } from '@llui/dom'
11
+ * import { childHandlers } from '@llui/dom'
12
+ * import { dialog } from '@llui/components/dialog'
13
+ * import { sortable } from '@llui/components/sortable'
14
+ *
15
+ * const children = { dialog, sort: sortable } as const
16
+ *
17
+ * type State = ChildState<typeof children> & { items: string[] }
18
+ * type Msg = ChildMsg<typeof children> | { type: 'addItem'; text: string }
19
+ *
20
+ * const update = mergeHandlers<State, Msg, never>(
21
+ * childHandlers(children),
22
+ * appUpdate,
23
+ * )
24
+ * ```
25
+ */
26
+ /**
27
+ * Extract the state type from a component module's update function.
28
+ * Works with both property and method syntax.
29
+ */
30
+ export type ModuleState<T> = T extends {
31
+ update: (state: infer S, msg: infer _M) => [infer _S2, infer _E];
32
+ } ? S : never;
33
+ /**
34
+ * Extract the message type from a component module's update function.
35
+ */
36
+ export type ModuleMsg<T> = T extends {
37
+ update: (state: infer _S, msg: infer M) => [infer _S2, infer _E];
38
+ } ? M : never;
39
+ /**
40
+ * Given a record of component modules, derive the combined child state.
41
+ * Each key maps to its module's state type.
42
+ *
43
+ * ```ts
44
+ * const children = { dialog, sort: sortable } as const
45
+ * type CS = ChildState<typeof children>
46
+ * // → { dialog: DialogState; sort: SortableState }
47
+ * ```
48
+ */
49
+ export type ChildState<T extends Record<string, unknown>> = {
50
+ [K in keyof T]: ModuleState<T[K]>;
51
+ };
52
+ /**
53
+ * Given a record of component modules, derive the combined child message
54
+ * union. Each module's messages are wrapped in `{ type: key; msg: SubMsg }`.
55
+ *
56
+ * ```ts
57
+ * const children = { dialog, sort: sortable } as const
58
+ * type CM = ChildMsg<typeof children>
59
+ * // → { type: 'dialog'; msg: DialogMsg } | { type: 'sort'; msg: SortableMsg }
60
+ * ```
61
+ */
62
+ export type ChildMsg<T extends Record<string, unknown>> = {
63
+ [K in keyof T]: {
64
+ type: K;
65
+ msg: ModuleMsg<T[K]>;
66
+ };
67
+ }[keyof T];
68
+ /**
69
+ * Create a merged handler from a map of component modules. Each module's
70
+ * update is wired via `sliceHandler(key, module.update)` convention:
71
+ * state[key] holds the sub-state, messages match `{ type: key; msg: SubMsg }`.
72
+ *
73
+ * Returns a handler compatible with `mergeHandlers`.
74
+ */
75
+ export declare function childHandlers<S, M, E>(modules: Record<string, {
76
+ update: (state: never, msg: never) => [unknown, unknown[]];
77
+ }>): (state: S, msg: M) => [S, E[]] | null;
78
+ //# sourceMappingURL=compose.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.d.ts","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;GAGG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS;IACrC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;CACjE,GACG,CAAC,GACD,KAAK,CAAA;AAET;;GAEG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS;IACnC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;CACjE,GACG,CAAC,GACD,KAAK,CAAA;AAET;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;KACzD,CAAC,IAAI,MAAM,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAClC,CAAA;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;KACvD,CAAC,IAAI,MAAM,CAAC,GAAG;QAAE,IAAI,EAAE,CAAC,CAAC;QAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE;CAClD,CAAC,MAAM,CAAC,CAAC,CAAA;AAEV;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACnC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,GACtF,CAAC,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAWvC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Type-level utilities + runtime helper for composing multiple child
3
+ * component modules into a parent's State and Msg types.
4
+ *
5
+ * Eliminates the manual State/Msg union declarations for each embedded
6
+ * sub-component. The developer declares the children map once, and
7
+ * `ChildState` / `ChildMsg` derive the wrapper types.
8
+ *
9
+ * ```ts
10
+ * import type { ChildState, ChildMsg } from '@llui/dom'
11
+ * import { childHandlers } from '@llui/dom'
12
+ * import { dialog } from '@llui/components/dialog'
13
+ * import { sortable } from '@llui/components/sortable'
14
+ *
15
+ * const children = { dialog, sort: sortable } as const
16
+ *
17
+ * type State = ChildState<typeof children> & { items: string[] }
18
+ * type Msg = ChildMsg<typeof children> | { type: 'addItem'; text: string }
19
+ *
20
+ * const update = mergeHandlers<State, Msg, never>(
21
+ * childHandlers(children),
22
+ * appUpdate,
23
+ * )
24
+ * ```
25
+ */
26
+ /**
27
+ * Create a merged handler from a map of component modules. Each module's
28
+ * update is wired via `sliceHandler(key, module.update)` convention:
29
+ * state[key] holds the sub-state, messages match `{ type: key; msg: SubMsg }`.
30
+ *
31
+ * Returns a handler compatible with `mergeHandlers`.
32
+ */
33
+ export function childHandlers(modules) {
34
+ const keys = Object.keys(modules);
35
+ return (state, msg) => {
36
+ const m = msg;
37
+ if (!m.type || !('msg' in m))
38
+ return null;
39
+ if (!keys.includes(m.type))
40
+ return null;
41
+ const mod = modules[m.type];
42
+ const slice = state[m.type];
43
+ const [nextSlice, effects] = mod.update(slice, m.msg);
44
+ return [{ ...state, [m.type]: nextSlice }, effects];
45
+ };
46
+ }
47
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAiDH;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAuF;IAEvF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACjC,OAAO,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACpB,MAAM,CAAC,GAAG,GAAsC,CAAA;QAChD,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;YAAE,OAAO,IAAI,CAAA;QACzC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QACvC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAE,CAAA;QAC5B,MAAM,KAAK,GAAI,KAAiC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACxD,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,KAAc,EAAE,CAAC,CAAC,GAAY,CAAC,CAAA;QACvE,OAAO,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,SAAS,EAAO,EAAE,OAAc,CAAC,CAAA;IACjE,CAAC,CAAA;AACH,CAAC","sourcesContent":["/**\n * Type-level utilities + runtime helper for composing multiple child\n * component modules into a parent's State and Msg types.\n *\n * Eliminates the manual State/Msg union declarations for each embedded\n * sub-component. The developer declares the children map once, and\n * `ChildState` / `ChildMsg` derive the wrapper types.\n *\n * ```ts\n * import type { ChildState, ChildMsg } from '@llui/dom'\n * import { childHandlers } from '@llui/dom'\n * import { dialog } from '@llui/components/dialog'\n * import { sortable } from '@llui/components/sortable'\n *\n * const children = { dialog, sort: sortable } as const\n *\n * type State = ChildState<typeof children> & { items: string[] }\n * type Msg = ChildMsg<typeof children> | { type: 'addItem'; text: string }\n *\n * const update = mergeHandlers<State, Msg, never>(\n * childHandlers(children),\n * appUpdate,\n * )\n * ```\n */\n\n/**\n * Extract the state type from a component module's update function.\n * Works with both property and method syntax.\n */\nexport type ModuleState<T> = T extends {\n update: (state: infer S, msg: infer _M) => [infer _S2, infer _E]\n}\n ? S\n : never\n\n/**\n * Extract the message type from a component module's update function.\n */\nexport type ModuleMsg<T> = T extends {\n update: (state: infer _S, msg: infer M) => [infer _S2, infer _E]\n}\n ? M\n : never\n\n/**\n * Given a record of component modules, derive the combined child state.\n * Each key maps to its module's state type.\n *\n * ```ts\n * const children = { dialog, sort: sortable } as const\n * type CS = ChildState<typeof children>\n * // → { dialog: DialogState; sort: SortableState }\n * ```\n */\nexport type ChildState<T extends Record<string, unknown>> = {\n [K in keyof T]: ModuleState<T[K]>\n}\n\n/**\n * Given a record of component modules, derive the combined child message\n * union. Each module's messages are wrapped in `{ type: key; msg: SubMsg }`.\n *\n * ```ts\n * const children = { dialog, sort: sortable } as const\n * type CM = ChildMsg<typeof children>\n * // → { type: 'dialog'; msg: DialogMsg } | { type: 'sort'; msg: SortableMsg }\n * ```\n */\nexport type ChildMsg<T extends Record<string, unknown>> = {\n [K in keyof T]: { type: K; msg: ModuleMsg<T[K]> }\n}[keyof T]\n\n/**\n * Create a merged handler from a map of component modules. Each module's\n * update is wired via `sliceHandler(key, module.update)` convention:\n * state[key] holds the sub-state, messages match `{ type: key; msg: SubMsg }`.\n *\n * Returns a handler compatible with `mergeHandlers`.\n */\nexport function childHandlers<S, M, E>(\n modules: Record<string, { update: (state: never, msg: never) => [unknown, unknown[]] }>,\n): (state: S, msg: M) => [S, E[]] | null {\n const keys = Object.keys(modules)\n return (state, msg) => {\n const m = msg as { type: string; msg?: unknown }\n if (!m.type || !('msg' in m)) return null\n if (!keys.includes(m.type)) return null\n const mod = modules[m.type]!\n const slice = (state as Record<string, unknown>)[m.type]\n const [nextSlice, effects] = mod.update(slice as never, m.msg as never)\n return [{ ...state, [m.type]: nextSlice } as S, effects as E[]]\n }\n}\n"]}
@@ -6,9 +6,25 @@
6
6
  */
7
7
  export declare function enableDevTools(): void;
8
8
  /**
9
- * Connect to a local MCP server's WebSocket and forward tool calls to
10
- * `window.__lluiDebug`. Auto-reconnects on close. Safe to call multiple times
11
- * (only the first call actually runs).
9
+ * Register the MCP relay for this page.
10
+ *
11
+ * Discovery happens in two phases:
12
+ *
13
+ * 1. **Status endpoint** (preferred): fetches `/__llui_mcp_status`,
14
+ * which the Vite plugin serves from the active marker file written
15
+ * by `@llui/mcp`. If the MCP server is running, the response gives
16
+ * us the actual port — we connect immediately. This avoids the race
17
+ * where HMR events fire before the listener registers, and handles
18
+ * cases where MCP runs on a non-default port.
19
+ * 2. **Compile-time fallback**: if the status endpoint is unavailable
20
+ * (404, network error, non-Vite environment), we attempt a single
21
+ * connection to the compiled-in `port` parameter as a best-effort.
22
+ *
23
+ * Either way: no retry loop. If both fail, `window.__lluiConnect(port?)`
24
+ * is exposed so the developer can connect manually from the console
25
+ * when the MCP server starts. The Vite plugin also dispatches an
26
+ * `llui:mcp-ready` HMR custom event when the marker file appears later,
27
+ * which the compiler-injected dev code forwards to `__lluiConnect`.
12
28
  */
13
29
  export declare function startRelay(port?: number): void;
14
30
  export interface MessageRecord {
@@ -1 +1 @@
1
- {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"devtools.d.ts","sourceRoot":"","sources":["../src/devtools.ts"],"names":[],"mappings":"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"}
package/dist/devtools.js CHANGED
@@ -1,5 +1,5 @@
1
- import { flushInstance, _forceState } from './update-loop';
2
- import { _setDevToolsInstall } from './mount';
1
+ import { flushInstance, _forceState } from './update-loop.js';
2
+ import { _setDevToolsInstall } from './mount.js';
3
3
  /**
4
4
  * Enable devtools auto-installation for every mountApp call. Called by
5
5
  * compiler-generated dev code — never imported in production builds.
@@ -12,86 +12,143 @@ export function enableDevTools() {
12
12
  // ── MCP WebSocket Relay ─────────────────────────────────────────────
13
13
  // Forwards method calls from an out-of-process MCP server to the
14
14
  // current __lluiDebug API. Dev-mode only — compiler injects startRelay(port).
15
- let relayStarted = false;
15
+ //
16
+ // On-demand: tries a SINGLE connection on page load. If it succeeds
17
+ // (MCP server is already running), the relay stays open and reconnects
18
+ // on drop. If it fails, no retry loop — just registers
19
+ // `window.__lluiConnect(port?)` so the developer can connect later
20
+ // from the console or when the MCP server starts.
21
+ let relayPort = 5200;
22
+ let relayConnected = false;
23
+ function handleMessage(ws, event) {
24
+ let req;
25
+ try {
26
+ req = JSON.parse(String(event.data));
27
+ }
28
+ catch {
29
+ return;
30
+ }
31
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
32
+ const g = globalThis;
33
+ if (req.method === '__listComponents') {
34
+ const keys = g.__lluiComponents ? Object.keys(g.__lluiComponents) : [];
35
+ const active = g.__lluiDebug && g.__lluiComponents
36
+ ? (Object.entries(g.__lluiComponents).find(([, v]) => v === g.__lluiDebug)?.[0] ?? null)
37
+ : null;
38
+ ws.send(JSON.stringify({ id: req.id, result: { components: keys, active } }));
39
+ return;
40
+ }
41
+ if (req.method === '__selectComponent') {
42
+ const key = req.args?.[0] ?? '';
43
+ const entry = g.__lluiComponents?.[key];
44
+ if (!entry) {
45
+ ws.send(JSON.stringify({ id: req.id, error: `unknown component: ${key}` }));
46
+ return;
47
+ }
48
+ g.__lluiDebug = entry;
49
+ ws.send(JSON.stringify({ id: req.id, result: { active: key } }));
50
+ return;
51
+ }
52
+ const api = g.__lluiDebug;
53
+ if (!api) {
54
+ ws.send(JSON.stringify({ id: req.id, error: '__lluiDebug not available' }));
55
+ return;
56
+ }
57
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
+ const fn = api[req.method];
59
+ if (typeof fn !== 'function') {
60
+ ws.send(JSON.stringify({ id: req.id, error: `unknown method: ${req.method}` }));
61
+ return;
62
+ }
63
+ try {
64
+ const result = fn.apply(api, req.args ?? []);
65
+ ws.send(JSON.stringify({ id: req.id, result: result ?? null }));
66
+ }
67
+ catch (e) {
68
+ ws.send(JSON.stringify({ id: req.id, error: e instanceof Error ? e.message : String(e) }));
69
+ }
70
+ }
71
+ function connectRelay(port, isInitial) {
72
+ if (typeof WebSocket === 'undefined')
73
+ return;
74
+ let ws;
75
+ try {
76
+ ws = new WebSocket(`ws://127.0.0.1:${port}`);
77
+ }
78
+ catch {
79
+ if (!isInitial)
80
+ console.warn(`[LLui MCP] failed to connect to ws://127.0.0.1:${port}`);
81
+ return;
82
+ }
83
+ ws.onopen = () => {
84
+ relayConnected = true;
85
+ console.log(`[LLui MCP] connected to ws://127.0.0.1:${port}`);
86
+ };
87
+ ws.onmessage = (event) => handleMessage(ws, event);
88
+ ws.onclose = () => {
89
+ if (relayConnected) {
90
+ relayConnected = false;
91
+ console.log('[LLui MCP] disconnected — call __lluiConnect() to reconnect');
92
+ }
93
+ };
94
+ ws.onerror = () => {
95
+ // onclose fires after onerror — nothing to do here
96
+ };
97
+ }
16
98
  /**
17
- * Connect to a local MCP server's WebSocket and forward tool calls to
18
- * `window.__lluiDebug`. Auto-reconnects on close. Safe to call multiple times
19
- * (only the first call actually runs).
99
+ * Register the MCP relay for this page.
100
+ *
101
+ * Discovery happens in two phases:
102
+ *
103
+ * 1. **Status endpoint** (preferred): fetches `/__llui_mcp_status`,
104
+ * which the Vite plugin serves from the active marker file written
105
+ * by `@llui/mcp`. If the MCP server is running, the response gives
106
+ * us the actual port — we connect immediately. This avoids the race
107
+ * where HMR events fire before the listener registers, and handles
108
+ * cases where MCP runs on a non-default port.
109
+ * 2. **Compile-time fallback**: if the status endpoint is unavailable
110
+ * (404, network error, non-Vite environment), we attempt a single
111
+ * connection to the compiled-in `port` parameter as a best-effort.
112
+ *
113
+ * Either way: no retry loop. If both fail, `window.__lluiConnect(port?)`
114
+ * is exposed so the developer can connect manually from the console
115
+ * when the MCP server starts. The Vite plugin also dispatches an
116
+ * `llui:mcp-ready` HMR custom event when the marker file appears later,
117
+ * which the compiler-injected dev code forwards to `__lluiConnect`.
20
118
  */
21
119
  export function startRelay(port = 5200) {
22
- if (relayStarted)
23
- return;
24
- relayStarted = true;
120
+ relayPort = port;
25
121
  if (typeof WebSocket === 'undefined')
26
122
  return;
27
- function connect() {
28
- let ws;
29
- try {
30
- ws = new WebSocket(`ws://127.0.0.1:${port}`);
31
- }
32
- catch {
33
- // Unable to open retry later
34
- setTimeout(connect, 3000);
35
- return;
36
- }
37
- ws.onmessage = (event) => {
38
- let req;
39
- try {
40
- req = JSON.parse(String(event.data));
41
- }
42
- catch {
43
- return;
44
- }
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- const g = globalThis;
47
- // Registry introspection methods work even when __lluiDebug isn't set
48
- if (req.method === '__listComponents') {
49
- const keys = g.__lluiComponents ? Object.keys(g.__lluiComponents) : [];
50
- const active = g.__lluiDebug && g.__lluiComponents
51
- ? (Object.entries(g.__lluiComponents).find(([, v]) => v === g.__lluiDebug)?.[0] ?? null)
52
- : null;
53
- ws.send(JSON.stringify({ id: req.id, result: { components: keys, active } }));
54
- return;
55
- }
56
- if (req.method === '__selectComponent') {
57
- const key = req.args?.[0] ?? '';
58
- const entry = g.__lluiComponents?.[key];
59
- if (!entry) {
60
- ws.send(JSON.stringify({ id: req.id, error: `unknown component: ${key}` }));
61
- return;
62
- }
63
- g.__lluiDebug = entry;
64
- ws.send(JSON.stringify({ id: req.id, result: { active: key } }));
65
- return;
66
- }
67
- const api = g.__lluiDebug;
68
- if (!api) {
69
- ws.send(JSON.stringify({ id: req.id, error: '__lluiDebug not available' }));
70
- return;
71
- }
72
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
- const fn = api[req.method];
74
- if (typeof fn !== 'function') {
75
- ws.send(JSON.stringify({ id: req.id, error: `unknown method: ${req.method}` }));
76
- return;
77
- }
78
- try {
79
- const result = fn.apply(api, req.args ?? []);
80
- ws.send(JSON.stringify({ id: req.id, result: result ?? null }));
123
+ // Expose manual connect for on-demand use
124
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
125
+ const g = globalThis;
126
+ g.__lluiConnect = (p) => {
127
+ connectRelay(p ?? relayPort, false);
128
+ };
129
+ // Try the Vite middleware first (knows the actual port from the marker file)
130
+ if (typeof fetch !== 'undefined') {
131
+ fetch('/__llui_mcp_status')
132
+ .then((res) => (res.ok ? res.json() : null))
133
+ .then((data) => {
134
+ if (data && typeof data.port === 'number') {
135
+ relayPort = data.port;
136
+ connectRelay(data.port, true);
81
137
  }
82
- catch (e) {
83
- ws.send(JSON.stringify({ id: req.id, error: e instanceof Error ? e.message : String(e) }));
138
+ else {
139
+ // Endpoint replied 404 MCP not running. Don't fall back to the
140
+ // compile-time port; the HMR event will fire if MCP starts later.
84
141
  }
85
- };
86
- ws.onclose = () => {
87
- // Retry until the MCP server is up
88
- setTimeout(connect, 2000);
89
- };
90
- ws.onerror = () => {
91
- // onclose will fire and handle retry
92
- };
142
+ })
143
+ .catch(() => {
144
+ // Network error or non-Vite environment fall back to compile-time port
145
+ connectRelay(port, true);
146
+ });
147
+ }
148
+ else {
149
+ // No fetch available — use the compile-time port directly
150
+ connectRelay(port, true);
93
151
  }
94
- connect();
95
152
  }
96
153
  const MAX_HISTORY = 1000;
97
154
  export function installDevTools(inst) {