elit 3.6.5 → 3.6.7

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 (154) hide show
  1. package/Cargo.lock +1 -1
  2. package/Cargo.toml +1 -1
  3. package/README.md +6 -0
  4. package/dist/build.cjs +421 -331
  5. package/dist/build.d.ts +1 -16
  6. package/dist/build.js +420 -330
  7. package/dist/build.mjs +420 -330
  8. package/dist/chokidar.cjs +219 -182
  9. package/dist/chokidar.d.ts +25 -10
  10. package/dist/chokidar.js +217 -182
  11. package/dist/chokidar.mjs +218 -183
  12. package/dist/cli.cjs +21608 -20241
  13. package/dist/cli.d.ts +19 -37
  14. package/dist/cli.mjs +21262 -19910
  15. package/dist/config.cjs +357 -350
  16. package/dist/config.d.ts +19 -240
  17. package/dist/config.js +520 -515
  18. package/dist/config.mjs +346 -341
  19. package/dist/contracts-BeW9k0yZ.d.ts +54 -0
  20. package/dist/contracts-D7KIS-TK.d.ts +36 -0
  21. package/dist/coverage.cjs +448 -485
  22. package/dist/coverage.d.ts +13 -59
  23. package/dist/coverage.js +447 -484
  24. package/dist/coverage.mjs +447 -484
  25. package/dist/database.cjs +819 -828
  26. package/dist/database.d.ts +8 -24
  27. package/dist/database.js +818 -829
  28. package/dist/database.mjs +818 -829
  29. package/dist/desktop-auto-render.cjs +1700 -1522
  30. package/dist/desktop-auto-render.d.ts +4 -9
  31. package/dist/desktop-auto-render.js +1695 -1517
  32. package/dist/desktop-auto-render.mjs +1696 -1518
  33. package/dist/desktop.cjs +3 -1
  34. package/dist/desktop.d.ts +4 -1
  35. package/dist/desktop.js +1 -1
  36. package/dist/desktop.mjs +1 -1
  37. package/dist/dev-build.cjs +830 -0
  38. package/dist/dev-build.d.ts +53 -0
  39. package/dist/dev-build.js +3318 -0
  40. package/dist/dev-build.mjs +797 -0
  41. package/dist/dom.cjs +717 -590
  42. package/dist/dom.d.ts +2 -15
  43. package/dist/dom.js +714 -587
  44. package/dist/dom.mjs +716 -589
  45. package/dist/el.cjs +62 -52
  46. package/dist/el.d.ts +5 -10
  47. package/dist/el.js +60 -52
  48. package/dist/el.mjs +60 -52
  49. package/dist/fs.cjs +72 -63
  50. package/dist/fs.d.ts +22 -19
  51. package/dist/fs.js +71 -62
  52. package/dist/fs.mjs +71 -62
  53. package/dist/hmr.cjs +40 -14
  54. package/dist/hmr.d.ts +11 -23
  55. package/dist/hmr.js +38 -14
  56. package/dist/hmr.mjs +38 -14
  57. package/dist/http.cjs +251 -99
  58. package/dist/http.d.ts +38 -104
  59. package/dist/http.js +249 -99
  60. package/dist/http.mjs +249 -99
  61. package/dist/https.cjs +524 -228
  62. package/dist/https.d.ts +44 -36
  63. package/dist/https.js +520 -226
  64. package/dist/https.mjs +522 -228
  65. package/dist/index.cjs +7502 -7690
  66. package/dist/index.d.ts +8 -3
  67. package/dist/index.js +7486 -7676
  68. package/dist/index.mjs +7497 -7686
  69. package/dist/mime-types.cjs +10 -4
  70. package/dist/mime-types.d.ts +8 -11
  71. package/dist/mime-types.js +9 -3
  72. package/dist/mime-types.mjs +9 -3
  73. package/dist/native.cjs +8616 -8869
  74. package/dist/native.d.ts +7 -8
  75. package/dist/native.js +8682 -8935
  76. package/dist/native.mjs +8615 -8868
  77. package/dist/path.cjs +83 -77
  78. package/dist/path.d.ts +29 -29
  79. package/dist/path.js +82 -76
  80. package/dist/path.mjs +82 -76
  81. package/dist/pm.cjs +3300 -0
  82. package/dist/pm.d.ts +256 -0
  83. package/dist/pm.js +5638 -0
  84. package/dist/pm.mjs +3196 -0
  85. package/dist/preview-build.cjs +712 -0
  86. package/dist/preview-build.d.ts +59 -0
  87. package/dist/preview-build.js +3194 -0
  88. package/dist/preview-build.mjs +676 -0
  89. package/dist/render-context.cjs +13 -2
  90. package/dist/render-context.d.ts +9 -31
  91. package/dist/render-context.js +11 -2
  92. package/dist/render-context.mjs +11 -2
  93. package/dist/router.cjs +787 -645
  94. package/dist/router.d.ts +8 -12
  95. package/dist/router.js +786 -644
  96. package/dist/router.mjs +786 -644
  97. package/dist/runtime.cjs +1 -1
  98. package/dist/runtime.js +1 -1
  99. package/dist/runtime.mjs +1 -1
  100. package/dist/server.cjs +3315 -2603
  101. package/dist/server.d.ts +49 -4
  102. package/dist/server.js +7611 -2834
  103. package/dist/server.mjs +3317 -2607
  104. package/dist/smtp-server.cjs +128 -0
  105. package/dist/smtp-server.d.ts +27 -0
  106. package/dist/smtp-server.js +4199 -0
  107. package/dist/smtp-server.mjs +100 -0
  108. package/dist/state-DvEkDehk.d.ts +195 -0
  109. package/dist/state.cjs +768 -658
  110. package/dist/state.d.ts +11 -69
  111. package/dist/state.js +760 -650
  112. package/dist/state.mjs +767 -657
  113. package/dist/style.cjs +1011 -968
  114. package/dist/style.d.ts +13 -127
  115. package/dist/style.js +1009 -970
  116. package/dist/style.mjs +1011 -971
  117. package/dist/test-reporter.cjs +332 -316
  118. package/dist/test-reporter.d.ts +28 -33
  119. package/dist/test-reporter.js +328 -312
  120. package/dist/test-reporter.mjs +328 -312
  121. package/dist/test-runtime.cjs +927 -968
  122. package/dist/test-runtime.d.ts +24 -99
  123. package/dist/test-runtime.js +922 -965
  124. package/dist/test-runtime.mjs +922 -965
  125. package/dist/test.cjs +4428 -4273
  126. package/dist/test.d.ts +2 -8
  127. package/dist/test.js +4307 -4154
  128. package/dist/test.mjs +4419 -4267
  129. package/dist/types-BONVzPtp.d.ts +59 -0
  130. package/dist/types-BR4wMiVx.d.ts +32 -0
  131. package/dist/types-C4gKykuG.d.ts +23 -0
  132. package/dist/types-CIhpN1-K.d.ts +64 -0
  133. package/dist/types-Ckj8md_j.d.ts +84 -0
  134. package/dist/types-CpjQTAkX.d.ts +24 -0
  135. package/dist/types-D0LjrYjS.d.ts +14 -0
  136. package/dist/types-DAisuVr5.d.ts +75 -0
  137. package/dist/types-tJn88E1N.d.ts +242 -0
  138. package/dist/types.d.ts +71 -226
  139. package/dist/universal.cjs +1 -1
  140. package/dist/universal.d.ts +1 -5
  141. package/dist/universal.js +1 -1
  142. package/dist/universal.mjs +1 -1
  143. package/dist/websocket-XfyK23zD.d.ts +119 -0
  144. package/dist/ws.cjs +129 -108
  145. package/dist/ws.d.ts +21 -131
  146. package/dist/ws.js +128 -109
  147. package/dist/ws.mjs +128 -109
  148. package/dist/wss.cjs +757 -479
  149. package/dist/wss.d.ts +31 -28
  150. package/dist/wss.js +755 -479
  151. package/dist/wss.mjs +758 -482
  152. package/package.json +16 -1
  153. package/vendor/epaint-0.31.1/src/image.rs +418 -0
  154. package/dist/server-CcBFc2F5.d.ts +0 -449
package/dist/router.cjs CHANGED
@@ -17,7 +17,7 @@ var __copyProps = (to, from, except, desc) => {
17
17
  };
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
 
20
- // src/router.ts
20
+ // src/client/router/index.ts
21
21
  var router_exports = {};
22
22
  __export(router_exports, {
23
23
  createRouter: () => createRouter,
@@ -26,13 +26,17 @@ __export(router_exports, {
26
26
  });
27
27
  module.exports = __toCommonJS(router_exports);
28
28
 
29
- // src/render-context.ts
29
+ // src/desktop/render-context/constants.ts
30
30
  var RUNTIME_TARGET_KEY = "__ELIT_RUNTIME_TARGET__";
31
31
  var CAPTURED_RENDER_KEY = "__ELIT_CAPTURED_RENDER__";
32
32
  var RUNTIME_TARGET_ENV = "ELIT_RUNTIME_TARGET";
33
+
34
+ // src/desktop/render-context/globals.ts
33
35
  function getGlobalRenderScope() {
34
36
  return globalThis;
35
37
  }
38
+
39
+ // src/desktop/render-context/runtime-target.ts
36
40
  function isRenderRuntimeTarget(value) {
37
41
  return value === "web" || value === "desktop" || value === "mobile" || value === "unknown";
38
42
  }
@@ -48,7 +52,8 @@ function detectRenderRuntimeTarget() {
48
52
  if (typeof globalScope.createWindow === "function") {
49
53
  return "desktop";
50
54
  }
51
- const argv = Array.isArray(globalScope.process?.argv) ? globalScope.process.argv.join(" ") : "";
55
+ const argvValues = globalScope.process?.argv;
56
+ const argv = Array.isArray(argvValues) ? argvValues.join(" ") : "";
52
57
  if (/\bdesktop\b/i.test(argv)) {
53
58
  return "desktop";
54
59
  }
@@ -57,6 +62,8 @@ function detectRenderRuntimeTarget() {
57
62
  }
58
63
  return "unknown";
59
64
  }
65
+
66
+ // src/desktop/render-context/captured-render.ts
60
67
  function captureRenderedVNode(rootElement, vNode, target = detectRenderRuntimeTarget()) {
61
68
  const globalScope = getGlobalRenderScope();
62
69
  globalScope[RUNTIME_TARGET_KEY] = target;
@@ -67,7 +74,7 @@ function captureRenderedVNode(rootElement, vNode, target = detectRenderRuntimeTa
67
74
  };
68
75
  }
69
76
 
70
- // src/dom.ts
77
+ // src/client/dom/helpers.ts
71
78
  function resolveElement(rootElement) {
72
79
  return typeof rootElement === "string" ? document.getElementById(rootElement.replace("#", "")) : rootElement;
73
80
  }
@@ -95,668 +102,788 @@ function resolveTextareaValue(tagName, props) {
95
102
  function hasDocumentApi() {
96
103
  return typeof document !== "undefined";
97
104
  }
98
- var DomNode = class {
99
- constructor() {
100
- this.elementCache = /* @__PURE__ */ new WeakMap();
101
- this.reactiveNodes = /* @__PURE__ */ new Map();
102
- }
103
- createElement(tagName, props = {}, children = []) {
104
- return { tagName, props, children };
105
- }
106
- renderToDOM(vNode, parent) {
107
- if (vNode == null || vNode === false) return;
108
- if (typeof vNode !== "object") {
109
- parent.appendChild(document.createTextNode(String(vNode)));
110
- return;
111
- }
112
- if (this.isState(vNode)) {
113
- const textNode = document.createTextNode(String(vNode.value ?? ""));
114
- parent.appendChild(textNode);
115
- vNode.subscribe((newValue) => {
116
- textNode.textContent = String(newValue ?? "");
117
- });
118
- return;
119
- }
120
- if (Array.isArray(vNode)) {
121
- for (const child of vNode) {
122
- this.renderToDOM(child, parent);
105
+ function isState(value) {
106
+ return value && typeof value === "object" && "value" in value && "subscribe" in value && typeof value.subscribe === "function";
107
+ }
108
+
109
+ // src/client/dom/dom-render.ts
110
+ function isSvgElement(tagName, parent) {
111
+ return tagName === "svg" || tagName[0] === "s" && tagName[1] === "v" && tagName[2] === "g" || parent.namespaceURI === "http://www.w3.org/2000/svg";
112
+ }
113
+ function applyProps(el, props, textareaValue) {
114
+ for (const key in props) {
115
+ const value = props[key];
116
+ if (value == null || value === false) continue;
117
+ const c = key.charCodeAt(0);
118
+ if (c === 99 && (key.length < 6 || key[5] === "N")) {
119
+ const classValue = Array.isArray(value) ? value.join(" ") : String(value);
120
+ if (el instanceof SVGElement) {
121
+ el.setAttribute("class", classValue);
122
+ } else {
123
+ el.className = classValue;
123
124
  }
124
- return;
125
- }
126
- const { tagName, props, children } = vNode;
127
- const textareaValue = resolveTextareaValue(tagName, props);
128
- if (!tagName) {
129
- for (const child of children) {
130
- if (shouldSkipChild(child)) continue;
131
- if (Array.isArray(child)) {
132
- for (const c of child) {
133
- !shouldSkipChild(c) && this.renderToDOM(c, parent);
134
- }
135
- } else {
136
- this.renderToDOM(child, parent);
125
+ } else if (c === 115 && key.length === 5) {
126
+ if (typeof value === "string") {
127
+ el.style.cssText = value;
128
+ } else {
129
+ const style = el.style;
130
+ for (const styleKey in value) {
131
+ style[styleKey] = value[styleKey];
137
132
  }
138
133
  }
139
- return;
140
- }
141
- const isSVG = tagName === "svg" || tagName[0] === "s" && tagName[1] === "v" && tagName[2] === "g" || parent.namespaceURI === "http://www.w3.org/2000/svg";
142
- const el = isSVG ? document.createElementNS("http://www.w3.org/2000/svg", tagName.replace("svg", "").toLowerCase() || tagName) : document.createElement(tagName);
143
- for (const key in props) {
144
- const value = props[key];
145
- if (value == null || value === false) continue;
146
- const c = key.charCodeAt(0);
147
- if (c === 99 && (key.length < 6 || key[5] === "N")) {
148
- const classValue = Array.isArray(value) ? value.join(" ") : value;
149
- isSVG ? el.setAttribute("class", classValue) : el.className = classValue;
150
- } else if (c === 115 && key.length === 5) {
151
- if (typeof value === "string") {
152
- el.style.cssText = value;
134
+ } else if (c === 111 && key.charCodeAt(1) === 110) {
135
+ el[key.toLowerCase()] = value;
136
+ } else if (c === 100 && key.length > 20) {
137
+ el.innerHTML = value.__html;
138
+ } else if (c === 114 && key === "ref") {
139
+ setTimeout(() => {
140
+ if (typeof value === "function") {
141
+ value(el);
153
142
  } else {
154
- const s = el.style;
155
- for (const k in value) s[k] = value[k];
143
+ value.current = el;
156
144
  }
157
- } else if (c === 111 && key.charCodeAt(1) === 110) {
158
- el[key.toLowerCase()] = value;
159
- } else if (c === 100 && key.length > 20) {
160
- el.innerHTML = value.__html;
161
- } else if (c === 114 && key === "ref") {
162
- setTimeout(() => {
163
- typeof value === "function" ? value(el) : value.current = el;
164
- }, 0);
165
- } else if (textareaValue !== void 0 && key === "value") {
166
- continue;
167
- } else {
168
- el.setAttribute(key, value === true ? "" : String(value));
169
- }
170
- }
171
- const renderableChildren = textareaValue === void 0 ? children : [];
172
- const len = renderableChildren.length;
173
- if (!len) {
174
- if (textareaValue !== void 0) {
175
- el.value = textareaValue;
176
- }
177
- parent.appendChild(el);
178
- return;
145
+ }, 0);
146
+ } else if (textareaValue !== void 0 && key === "value") {
147
+ continue;
148
+ } else {
149
+ el.setAttribute(key, value === true ? "" : String(value));
179
150
  }
180
- const renderChildren = (target) => {
181
- for (let i = 0; i < len; i++) {
182
- const child = renderableChildren[i];
183
- if (shouldSkipChild(child)) continue;
184
- if (Array.isArray(child)) {
185
- for (let j = 0, cLen = child.length; j < cLen; j++) {
186
- const c = child[j];
187
- !shouldSkipChild(c) && this.renderToDOM(c, target);
188
- }
189
- } else {
190
- this.renderToDOM(child, target);
151
+ }
152
+ }
153
+ function renderChildren(children, target) {
154
+ const len = children.length;
155
+ for (let i = 0; i < len; i++) {
156
+ const child = children[i];
157
+ if (shouldSkipChild(child)) continue;
158
+ if (Array.isArray(child)) {
159
+ for (let j = 0, childLen = child.length; j < childLen; j++) {
160
+ const nestedChild = child[j];
161
+ if (!shouldSkipChild(nestedChild)) {
162
+ renderToDOM(nestedChild, target);
191
163
  }
192
164
  }
193
- };
194
- if (len > 30) {
195
- const fragment = document.createDocumentFragment();
196
- renderChildren(fragment);
197
- el.appendChild(fragment);
198
165
  } else {
199
- renderChildren(el);
166
+ renderToDOM(child, target);
200
167
  }
201
- parent.appendChild(el);
202
168
  }
203
- render(rootElement, vNode) {
204
- if (!hasDocumentApi()) {
205
- const runtimeTarget = detectRenderRuntimeTarget();
206
- if (runtimeTarget === "desktop" || runtimeTarget === "mobile") {
207
- captureRenderedVNode(rootElement, vNode, runtimeTarget);
208
- return {};
209
- }
210
- throw new Error("render() requires a DOM or an Elit desktop/mobile runtime target.");
211
- }
212
- const el = ensureElement(resolveElement(rootElement), rootElement);
213
- el.innerHTML = "";
214
- if (vNode.children && vNode.children.length > 500) {
215
- const fragment = document.createDocumentFragment();
216
- this.renderToDOM(vNode, fragment);
217
- el.appendChild(fragment);
218
- } else {
219
- this.renderToDOM(vNode, el);
169
+ }
170
+ function renderToDOM(vNode, parent) {
171
+ if (vNode == null || vNode === false) return;
172
+ if (typeof vNode !== "object") {
173
+ parent.appendChild(document.createTextNode(String(vNode)));
174
+ return;
175
+ }
176
+ if (isState(vNode)) {
177
+ const textNode = document.createTextNode(String(vNode.value ?? ""));
178
+ parent.appendChild(textNode);
179
+ vNode.subscribe((newValue) => {
180
+ textNode.textContent = String(newValue ?? "");
181
+ });
182
+ return;
183
+ }
184
+ if (Array.isArray(vNode)) {
185
+ for (const child of vNode) {
186
+ renderToDOM(child, parent);
220
187
  }
221
- return el;
188
+ return;
222
189
  }
223
- batchRender(rootElement, vNodes) {
224
- const el = ensureElement(resolveElement(rootElement), rootElement);
225
- const len = vNodes.length;
226
- if (len > 3e3) {
227
- const fragment = document.createDocumentFragment();
228
- let processed = 0;
229
- const chunkSize = 1500;
230
- const processChunk = () => {
231
- const end = Math.min(processed + chunkSize, len);
232
- for (let i = processed; i < end; i++) {
233
- this.renderToDOM(vNodes[i], fragment);
234
- }
235
- processed = end;
236
- if (processed >= len) {
237
- el.appendChild(fragment);
238
- } else {
239
- requestAnimationFrame(processChunk);
240
- }
241
- };
242
- processChunk();
243
- } else {
244
- const fragment = document.createDocumentFragment();
245
- for (let i = 0; i < len; i++) {
246
- this.renderToDOM(vNodes[i], fragment);
247
- }
248
- el.appendChild(fragment);
190
+ const { tagName, props, children } = vNode;
191
+ const textareaValue = resolveTextareaValue(tagName, props);
192
+ if (!tagName) {
193
+ renderChildren(children, parent);
194
+ return;
195
+ }
196
+ const el = isSvgElement(tagName, parent) ? document.createElementNS("http://www.w3.org/2000/svg", tagName.replace("svg", "").toLowerCase() || tagName) : document.createElement(tagName);
197
+ applyProps(el, props, textareaValue);
198
+ const renderableChildren = textareaValue === void 0 ? children : [];
199
+ if (!renderableChildren.length) {
200
+ if (textareaValue !== void 0) {
201
+ el.value = textareaValue;
249
202
  }
250
- return el;
203
+ parent.appendChild(el);
204
+ return;
251
205
  }
252
- renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
253
- const el = ensureElement(resolveElement(rootElement), rootElement);
254
- const len = vNodes.length;
255
- let index = 0;
256
- const renderChunk = () => {
257
- const end = Math.min(index + chunkSize, len);
258
- const fragment = document.createDocumentFragment();
259
- for (let i = index; i < end; i++) {
260
- this.renderToDOM(vNodes[i], fragment);
206
+ if (renderableChildren.length > 30) {
207
+ const fragment = document.createDocumentFragment();
208
+ renderChildren(renderableChildren, fragment);
209
+ el.appendChild(fragment);
210
+ } else {
211
+ renderChildren(renderableChildren, el);
212
+ }
213
+ parent.appendChild(el);
214
+ }
215
+ function render(rootElement, vNode) {
216
+ if (!hasDocumentApi()) {
217
+ const runtimeTarget = detectRenderRuntimeTarget();
218
+ if (runtimeTarget === "desktop" || runtimeTarget === "mobile") {
219
+ captureRenderedVNode(rootElement, vNode, runtimeTarget);
220
+ return {};
221
+ }
222
+ throw new Error("render() requires a DOM or an Elit desktop/mobile runtime target.");
223
+ }
224
+ const el = ensureElement(resolveElement(rootElement), rootElement);
225
+ el.innerHTML = "";
226
+ if (vNode.children && vNode.children.length > 500) {
227
+ const fragment = document.createDocumentFragment();
228
+ renderToDOM(vNode, fragment);
229
+ el.appendChild(fragment);
230
+ } else {
231
+ renderToDOM(vNode, el);
232
+ }
233
+ return el;
234
+ }
235
+ function batchRender(rootElement, vNodes) {
236
+ const el = ensureElement(resolveElement(rootElement), rootElement);
237
+ const len = vNodes.length;
238
+ if (len > 3e3) {
239
+ const fragment = document.createDocumentFragment();
240
+ let processed = 0;
241
+ const chunkSize = 1500;
242
+ const processChunk = () => {
243
+ const end = Math.min(processed + chunkSize, len);
244
+ for (let i = processed; i < end; i++) {
245
+ renderToDOM(vNodes[i], fragment);
261
246
  }
262
- el.appendChild(fragment);
263
- index = end;
264
- if (onProgress) onProgress(index, len);
265
- if (index < len) {
266
- requestAnimationFrame(renderChunk);
247
+ processed = end;
248
+ if (processed >= len) {
249
+ el.appendChild(fragment);
250
+ } else {
251
+ requestAnimationFrame(processChunk);
267
252
  }
268
253
  };
269
- requestAnimationFrame(renderChunk);
270
- return el;
254
+ processChunk();
255
+ } else {
256
+ const fragment = document.createDocumentFragment();
257
+ for (let i = 0; i < len; i++) {
258
+ renderToDOM(vNodes[i], fragment);
259
+ }
260
+ el.appendChild(fragment);
271
261
  }
272
- renderToHead(...vNodes) {
273
- const head = document.head;
274
- if (head) {
275
- for (const vNode of vNodes.flat()) {
276
- vNode && this.renderToDOM(vNode, head);
262
+ return el;
263
+ }
264
+ function renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
265
+ const el = ensureElement(resolveElement(rootElement), rootElement);
266
+ const len = vNodes.length;
267
+ let index = 0;
268
+ const renderChunkFrame = () => {
269
+ const end = Math.min(index + chunkSize, len);
270
+ const fragment = document.createDocumentFragment();
271
+ for (let i = index; i < end; i++) {
272
+ renderToDOM(vNodes[i], fragment);
273
+ }
274
+ el.appendChild(fragment);
275
+ index = end;
276
+ if (onProgress) {
277
+ onProgress(index, len);
278
+ }
279
+ if (index < len) {
280
+ requestAnimationFrame(renderChunkFrame);
281
+ }
282
+ };
283
+ requestAnimationFrame(renderChunkFrame);
284
+ return el;
285
+ }
286
+ function renderToHead(...vNodes) {
287
+ const head = document.head;
288
+ if (head) {
289
+ for (const vNode of vNodes.flat()) {
290
+ if (vNode) {
291
+ renderToDOM(vNode, head);
277
292
  }
278
293
  }
279
- return head;
280
- }
281
- addStyle(cssText) {
282
- const el = document.createElement("style");
283
- el.textContent = cssText;
284
- return document.head.appendChild(el);
285
294
  }
286
- addMeta(attrs) {
287
- const el = document.createElement("meta");
288
- for (const k in attrs) el.setAttribute(k, attrs[k]);
289
- return document.head.appendChild(el);
295
+ return head;
296
+ }
297
+ function addStyle(cssText) {
298
+ const el = document.createElement("style");
299
+ el.textContent = cssText;
300
+ return document.head.appendChild(el);
301
+ }
302
+ function addMeta(attrs) {
303
+ const el = document.createElement("meta");
304
+ for (const key in attrs) {
305
+ el.setAttribute(key, attrs[key]);
290
306
  }
291
- addLink(attrs) {
292
- const el = document.createElement("link");
293
- for (const k in attrs) el.setAttribute(k, attrs[k]);
294
- return document.head.appendChild(el);
307
+ return document.head.appendChild(el);
308
+ }
309
+ function addLink(attrs) {
310
+ const el = document.createElement("link");
311
+ for (const key in attrs) {
312
+ el.setAttribute(key, attrs[key]);
295
313
  }
296
- setTitle(text) {
297
- return document.title = text;
314
+ return document.head.appendChild(el);
315
+ }
316
+ function setTitle(text) {
317
+ return document.title = text;
318
+ }
319
+ function cleanupUnusedElements(root, elementCache) {
320
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
321
+ const toRemove = [];
322
+ while (walker.nextNode()) {
323
+ const node = walker.currentNode;
324
+ if (node.id && node.id.startsWith("r") && !elementCache.has(node)) {
325
+ toRemove.push(node);
326
+ }
298
327
  }
299
- // Reactive State Management
300
- createState(initialValue, options = {}) {
301
- let value = initialValue;
302
- const listeners = /* @__PURE__ */ new Set();
303
- let updateTimer = null;
304
- const { throttle = 0, deep = false } = options;
305
- const notify = () => listeners.forEach((fn) => fn(value));
306
- const scheduleUpdate = () => {
307
- if (throttle > 0) {
308
- if (!updateTimer) {
309
- updateTimer = setTimeout(() => {
310
- updateTimer = null;
311
- notify();
312
- }, throttle);
313
- }
314
- } else {
315
- notify();
316
- }
317
- };
318
- return {
319
- get value() {
320
- return value;
321
- },
322
- set value(newValue) {
323
- const changed = deep ? JSON.stringify(value) !== JSON.stringify(newValue) : value !== newValue;
324
- if (changed) {
325
- value = newValue;
326
- scheduleUpdate();
327
- }
328
- },
329
- subscribe(fn) {
330
- listeners.add(fn);
331
- return () => listeners.delete(fn);
332
- },
333
- destroy() {
334
- listeners.clear();
335
- updateTimer && clearTimeout(updateTimer);
328
+ toRemove.forEach((el) => el.remove());
329
+ return toRemove.length;
330
+ }
331
+
332
+ // src/client/dom/reactive.ts
333
+ function createReactiveChild(state, reactiveNodes, renderFn) {
334
+ const currentValue = renderFn(state.value);
335
+ if (typeof window !== "undefined" && typeof document !== "undefined") {
336
+ const entry = { node: null, renderFn };
337
+ reactiveNodes.set(state, entry);
338
+ state.subscribe(() => {
339
+ if (entry.node && entry.node.parentNode) {
340
+ const newValue = renderFn(state.value);
341
+ entry.node.textContent = String(newValue ?? "");
336
342
  }
337
- };
338
- }
339
- computed(states, computeFn) {
340
- const values = states.map((s) => s.value);
341
- const result = this.createState(computeFn(...values));
342
- states.forEach((state, index) => {
343
- state.subscribe((newValue) => {
344
- values[index] = newValue;
345
- result.value = computeFn(...values);
346
- });
347
343
  });
348
- return result;
349
344
  }
350
- effect(stateFn) {
351
- stateFn();
345
+ return currentValue;
346
+ }
347
+
348
+ // src/client/dom/string-render.ts
349
+ var SELF_CLOSING_TAGS = /* @__PURE__ */ new Set([
350
+ "area",
351
+ "base",
352
+ "br",
353
+ "col",
354
+ "embed",
355
+ "hr",
356
+ "img",
357
+ "input",
358
+ "link",
359
+ "meta",
360
+ "param",
361
+ "source",
362
+ "track",
363
+ "wbr"
364
+ ]);
365
+ function resolveStateValue(value) {
366
+ return isState(value) ? value.value : value;
367
+ }
368
+ function isReactiveWrapper(vNode) {
369
+ if (!vNode || typeof vNode !== "object" || !vNode.tagName) {
370
+ return false;
352
371
  }
353
- // Virtual scrolling helper for large lists
354
- createVirtualList(container, items, renderItem, itemHeight = 50, bufferSize = 5) {
355
- const viewportHeight = container.clientHeight;
356
- const totalHeight = items.length * itemHeight;
357
- let scrollTop = 0;
358
- const getVisibleRange = () => {
359
- const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
360
- const end = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + bufferSize);
361
- return { start, end };
362
- };
363
- const render2 = () => {
364
- const { start, end } = getVisibleRange();
365
- const wrapper = document.createElement("div");
366
- wrapper.style.cssText = `height:${totalHeight}px;position:relative`;
367
- for (let i = start; i < end; i++) {
368
- const itemEl = document.createElement("div");
369
- itemEl.style.cssText = `position:absolute;top:${i * itemHeight}px;height:${itemHeight}px;width:100%`;
370
- this.renderToDOM(renderItem(items[i], i), itemEl);
371
- wrapper.appendChild(itemEl);
372
- }
373
- container.innerHTML = "";
374
- container.appendChild(wrapper);
375
- };
376
- const scrollHandler = () => {
377
- scrollTop = container.scrollTop;
378
- requestAnimationFrame(render2);
379
- };
380
- container.addEventListener("scroll", scrollHandler);
381
- render2();
382
- return {
383
- render: render2,
384
- destroy: () => {
385
- container.removeEventListener("scroll", scrollHandler);
386
- container.innerHTML = "";
387
- }
388
- };
372
+ return vNode.tagName === "span" && vNode.props?.id && typeof vNode.props.id === "string" && /^r[a-z0-9]{9}$/.test(vNode.props.id);
373
+ }
374
+ function unwrapReactive(vNode) {
375
+ if (!isReactiveWrapper(vNode)) {
376
+ return vNode;
389
377
  }
390
- // Lazy load components
391
- lazy(loadFn) {
392
- let component = null;
393
- let loading = false;
394
- return async (...args) => {
395
- if (!component && !loading) {
396
- loading = true;
397
- component = await loadFn();
398
- loading = false;
399
- }
400
- return component ? component(...args) : { tagName: "div", props: { class: "loading" }, children: ["Loading..."] };
401
- };
378
+ const children = vNode.children;
379
+ if (!children || children.length === 0) {
380
+ return "";
402
381
  }
403
- // Memory management - cleanup unused elements
404
- cleanupUnusedElements(root) {
405
- const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT);
406
- const toRemove = [];
407
- while (walker.nextNode()) {
408
- const node = walker.currentNode;
409
- if (node.id && node.id.startsWith("r") && !this.elementCache.has(node)) {
410
- toRemove.push(node);
382
+ if (children.length === 1) {
383
+ const child = children[0];
384
+ if (child && typeof child === "object" && child.tagName === "span") {
385
+ const props = child.props;
386
+ const hasNoProps = !props || Object.keys(props).length === 0;
387
+ const hasSingleStringChild = child.children && child.children.length === 1 && typeof child.children[0] === "string";
388
+ if (hasNoProps && hasSingleStringChild) {
389
+ return child.children[0];
411
390
  }
412
391
  }
413
- toRemove.forEach((el) => el.remove());
414
- return toRemove.length;
392
+ return unwrapReactive(child);
415
393
  }
416
- // Server-Side Rendering - convert VNode to HTML string
417
- renderToString(vNode, options = {}) {
418
- const { pretty = false, indent = 0 } = options;
419
- const indentStr = pretty ? " ".repeat(indent) : "";
420
- const newLine = pretty ? "\n" : "";
421
- let resolvedVNode = this.resolveStateValue(vNode);
422
- resolvedVNode = this.unwrapReactive(resolvedVNode);
423
- if (Array.isArray(resolvedVNode)) {
424
- return resolvedVNode.map((child) => this.renderToString(child, options)).join("");
425
- }
426
- if (typeof resolvedVNode !== "object" || resolvedVNode === null) {
427
- if (resolvedVNode === null || resolvedVNode === void 0 || resolvedVNode === false) {
428
- return "";
394
+ return children.map((child) => unwrapReactive(child));
395
+ }
396
+ function escapeHtml(text) {
397
+ const htmlEscapes = {
398
+ "&": "&amp;",
399
+ "<": "&lt;",
400
+ ">": "&gt;",
401
+ '"': "&quot;",
402
+ "'": "&#x27;"
403
+ };
404
+ return text.replace(/[&<>"']/g, (char) => htmlEscapes[char]);
405
+ }
406
+ function isSelfClosingTag(tagName) {
407
+ return SELF_CLOSING_TAGS.has(tagName.toLowerCase());
408
+ }
409
+ function styleToString(style) {
410
+ if (typeof style === "string") {
411
+ return style;
412
+ }
413
+ if (typeof style === "object" && style !== null) {
414
+ const styles = [];
415
+ for (const key in style) {
416
+ const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
417
+ styles.push(`${cssKey}:${style[key]}`);
418
+ }
419
+ return styles.join(";");
420
+ }
421
+ return "";
422
+ }
423
+ function propsToAttributes(props, tagName) {
424
+ const attrs = [];
425
+ for (const key in props) {
426
+ if (key === "children" || key === "dangerouslySetInnerHTML" || key === "ref" || tagName === "textarea" && key === "value") {
427
+ continue;
428
+ }
429
+ let value = props[key];
430
+ value = resolveStateValue(value);
431
+ if (value == null || value === false) continue;
432
+ if (key.startsWith("on") && typeof value === "function") {
433
+ continue;
434
+ }
435
+ if (key === "className" || key === "class") {
436
+ const className = Array.isArray(value) ? value.join(" ") : value;
437
+ if (className) {
438
+ attrs.push(`class="${escapeHtml(String(className))}"`);
429
439
  }
430
- return this.escapeHtml(String(resolvedVNode));
440
+ continue;
431
441
  }
432
- const { tagName, props, children } = resolvedVNode;
433
- const textareaValue = resolveTextareaValue(tagName, props);
434
- const isSelfClosing = this.isSelfClosingTag(tagName);
435
- let html = `${indentStr}<${tagName}`;
436
- const attrs = this.propsToAttributes(props, tagName);
437
- if (attrs) {
438
- html += ` ${attrs}`;
442
+ if (key === "style") {
443
+ const styleStr = styleToString(value);
444
+ if (styleStr) {
445
+ attrs.push(`style="${escapeHtml(styleStr)}"`);
446
+ }
447
+ continue;
439
448
  }
440
- if (isSelfClosing) {
441
- html += ` />${newLine}`;
442
- return html;
449
+ if (value === true) {
450
+ attrs.push(key);
451
+ continue;
443
452
  }
444
- html += ">";
445
- if (textareaValue !== void 0) {
446
- html += this.escapeHtml(textareaValue);
447
- html += `</${tagName}>${newLine}`;
448
- return html;
449
- }
450
- if (props.dangerouslySetInnerHTML) {
451
- html += props.dangerouslySetInnerHTML.__html;
452
- html += `</${tagName}>${newLine}`;
453
- return html;
454
- }
455
- if (children && children.length > 0) {
456
- const resolvedChildren = children.map((c) => {
457
- const resolved = this.resolveStateValue(c);
458
- return this.unwrapReactive(resolved);
459
- });
460
- const hasComplexChildren = resolvedChildren.some(
461
- (c) => typeof c === "object" && c !== null && !Array.isArray(c) && "tagName" in c
462
- );
463
- if (pretty && hasComplexChildren) {
464
- html += newLine;
465
- for (const child of resolvedChildren) {
466
- if (shouldSkipChild(child)) continue;
467
- if (Array.isArray(child)) {
468
- for (const c of child) {
469
- if (!shouldSkipChild(c)) {
470
- html += this.renderToString(c, { pretty, indent: indent + 1 });
471
- }
453
+ attrs.push(`${key}="${escapeHtml(String(value))}"`);
454
+ }
455
+ return attrs.join(" ");
456
+ }
457
+ function renderToString(vNode, options = {}) {
458
+ const { pretty = false, indent = 0 } = options;
459
+ const indentStr = pretty ? " ".repeat(indent) : "";
460
+ const newLine = pretty ? "\n" : "";
461
+ let resolvedVNode = resolveStateValue(vNode);
462
+ resolvedVNode = unwrapReactive(resolvedVNode);
463
+ if (Array.isArray(resolvedVNode)) {
464
+ return resolvedVNode.map((child) => renderToString(child, options)).join("");
465
+ }
466
+ if (typeof resolvedVNode !== "object" || resolvedVNode === null) {
467
+ if (resolvedVNode === null || resolvedVNode === void 0 || resolvedVNode === false) {
468
+ return "";
469
+ }
470
+ return escapeHtml(String(resolvedVNode));
471
+ }
472
+ const { tagName, props, children } = resolvedVNode;
473
+ const textareaValue = resolveTextareaValue(tagName, props);
474
+ const selfClosing = isSelfClosingTag(tagName);
475
+ let html = `${indentStr}<${tagName}`;
476
+ const attrs = propsToAttributes(props, tagName);
477
+ if (attrs) {
478
+ html += ` ${attrs}`;
479
+ }
480
+ if (selfClosing) {
481
+ html += ` />${newLine}`;
482
+ return html;
483
+ }
484
+ html += ">";
485
+ if (textareaValue !== void 0) {
486
+ html += escapeHtml(textareaValue);
487
+ html += `</${tagName}>${newLine}`;
488
+ return html;
489
+ }
490
+ if (props.dangerouslySetInnerHTML) {
491
+ html += props.dangerouslySetInnerHTML.__html;
492
+ html += `</${tagName}>${newLine}`;
493
+ return html;
494
+ }
495
+ const isRawText = tagName === "script" || tagName === "style";
496
+ if (children && children.length > 0) {
497
+ const resolvedChildren = children.map((child) => unwrapReactive(resolveStateValue(child)));
498
+ const hasComplexChildren = resolvedChildren.some(
499
+ (child) => typeof child === "object" && child !== null && !Array.isArray(child) && "tagName" in child
500
+ );
501
+ if (pretty && hasComplexChildren) {
502
+ html += newLine;
503
+ for (const child of resolvedChildren) {
504
+ if (shouldSkipChild(child)) continue;
505
+ if (Array.isArray(child)) {
506
+ for (const nestedChild of child) {
507
+ if (!shouldSkipChild(nestedChild)) {
508
+ html += isRawText && typeof nestedChild === "string" ? nestedChild : renderToString(nestedChild, { pretty, indent: indent + 1 });
472
509
  }
473
- } else {
474
- html += this.renderToString(child, { pretty, indent: indent + 1 });
475
510
  }
511
+ } else {
512
+ html += isRawText && typeof child === "string" ? child : renderToString(child, { pretty, indent: indent + 1 });
476
513
  }
477
- html += indentStr;
478
- } else {
479
- for (const child of resolvedChildren) {
480
- if (shouldSkipChild(child)) continue;
481
- if (Array.isArray(child)) {
482
- for (const c of child) {
483
- if (!shouldSkipChild(c)) {
484
- html += this.renderToString(c, { pretty: false, indent: 0 });
485
- }
514
+ }
515
+ html += indentStr;
516
+ } else {
517
+ for (const child of resolvedChildren) {
518
+ if (shouldSkipChild(child)) continue;
519
+ if (Array.isArray(child)) {
520
+ for (const nestedChild of child) {
521
+ if (!shouldSkipChild(nestedChild)) {
522
+ html += isRawText && typeof nestedChild === "string" ? nestedChild : renderToString(nestedChild, { pretty: false, indent: 0 });
486
523
  }
487
- } else {
488
- html += this.renderToString(child, { pretty: false, indent: 0 });
489
524
  }
525
+ } else {
526
+ html += isRawText && typeof child === "string" ? child : renderToString(child, { pretty: false, indent: 0 });
490
527
  }
491
528
  }
492
529
  }
493
- html += `</${tagName}>${newLine}`;
494
- return html;
495
530
  }
496
- resolveStateValue(value) {
497
- if (value && typeof value === "object" && "value" in value && "subscribe" in value) {
498
- return value.value;
531
+ html += `</${tagName}>${newLine}`;
532
+ return html;
533
+ }
534
+ function renderToHTMLDocument(vNode, options = {}) {
535
+ const {
536
+ title = "",
537
+ meta = [],
538
+ links = [],
539
+ scripts = [],
540
+ styles = [],
541
+ lang = "en",
542
+ head = "",
543
+ bodyAttrs = {},
544
+ pretty = false
545
+ } = options;
546
+ const nl = pretty ? "\n" : "";
547
+ const indent = pretty ? " " : "";
548
+ const indent2 = pretty ? " " : "";
549
+ let html = `<!DOCTYPE html>${nl}<html lang="${lang}">${nl}${indent}<head>${nl}${indent2}<meta charset="UTF-8">${nl}${indent2}<meta name="viewport" content="width=device-width, initial-scale=1.0">${nl}`;
550
+ if (title) {
551
+ html += `${indent2}<title>${escapeHtml(title)}</title>${nl}`;
552
+ }
553
+ for (const metaAttrs of meta) {
554
+ html += `${indent2}<meta`;
555
+ for (const key in metaAttrs) {
556
+ html += ` ${key}="${escapeHtml(metaAttrs[key])}"`;
499
557
  }
500
- return value;
558
+ html += `>${nl}`;
501
559
  }
502
- isReactiveWrapper(vNode) {
503
- if (!vNode || typeof vNode !== "object" || !vNode.tagName) {
504
- return false;
560
+ for (const linkAttrs of links) {
561
+ html += `${indent2}<link`;
562
+ for (const key in linkAttrs) {
563
+ html += ` ${key}="${escapeHtml(linkAttrs[key])}"`;
505
564
  }
506
- return vNode.tagName === "span" && vNode.props?.id && typeof vNode.props.id === "string" && vNode.props.id.match(/^r[a-z0-9]{9}$/);
565
+ html += `>${nl}`;
507
566
  }
508
- unwrapReactive(vNode) {
509
- if (!this.isReactiveWrapper(vNode)) {
510
- return vNode;
567
+ for (const style of styles) {
568
+ if (style.href) {
569
+ html += `${indent2}<link rel="stylesheet" href="${escapeHtml(style.href)}">${nl}`;
570
+ } else if (style.content) {
571
+ html += `${indent2}<style>${style.content}</style>${nl}`;
511
572
  }
512
- const children = vNode.children;
513
- if (!children || children.length === 0) {
514
- return "";
573
+ }
574
+ if (head) {
575
+ html += head + nl;
576
+ }
577
+ html += `${indent}</head>${nl}${indent}<body`;
578
+ for (const key in bodyAttrs) {
579
+ html += ` ${key}="${escapeHtml(bodyAttrs[key])}"`;
580
+ }
581
+ html += `>${nl}`;
582
+ html += renderToString(vNode, { pretty, indent: 2 });
583
+ for (const script of scripts) {
584
+ html += `${indent2}<script`;
585
+ if (script.type) {
586
+ html += ` type="${escapeHtml(script.type)}"`;
515
587
  }
516
- if (children.length === 1) {
517
- const child = children[0];
518
- if (child && typeof child === "object" && child.tagName === "span") {
519
- const props = child.props;
520
- const hasNoProps = !props || Object.keys(props).length === 0;
521
- const hasSingleStringChild = child.children && child.children.length === 1 && typeof child.children[0] === "string";
522
- if (hasNoProps && hasSingleStringChild) {
523
- return child.children[0];
524
- }
525
- }
526
- return this.unwrapReactive(child);
588
+ if (script.async) {
589
+ html += " async";
590
+ }
591
+ if (script.defer) {
592
+ html += " defer";
593
+ }
594
+ if (script.src) {
595
+ html += ` src="${escapeHtml(script.src)}"></script>${nl}`;
596
+ } else if (script.content) {
597
+ html += `>${script.content}</script>${nl}`;
598
+ } else {
599
+ html += `></script>${nl}`;
527
600
  }
528
- return children.map((c) => this.unwrapReactive(c));
529
601
  }
530
- escapeHtml(text) {
531
- const htmlEscapes = {
532
- "&": "&amp;",
533
- "<": "&lt;",
534
- ">": "&gt;",
535
- '"': "&quot;",
536
- "'": "&#x27;"
537
- };
538
- return text.replace(/[&<>"']/g, (char) => htmlEscapes[char]);
539
- }
540
- isSelfClosingTag(tagName) {
541
- const selfClosingTags = /* @__PURE__ */ new Set([
542
- "area",
543
- "base",
544
- "br",
545
- "col",
546
- "embed",
547
- "hr",
548
- "img",
549
- "input",
550
- "link",
551
- "meta",
552
- "param",
553
- "source",
554
- "track",
555
- "wbr"
556
- ]);
557
- return selfClosingTags.has(tagName.toLowerCase());
558
- }
559
- propsToAttributes(props, tagName) {
560
- const attrs = [];
561
- for (const key in props) {
562
- if (key === "children" || key === "dangerouslySetInnerHTML" || key === "ref" || tagName === "textarea" && key === "value") {
563
- continue;
564
- }
565
- let value = props[key];
566
- value = this.resolveStateValue(value);
567
- if (value == null || value === false) continue;
568
- if (key.startsWith("on") && typeof value === "function") {
569
- continue;
570
- }
571
- if (key === "className" || key === "class") {
572
- const className = Array.isArray(value) ? value.join(" ") : value;
573
- if (className) {
574
- attrs.push(`class="${this.escapeHtml(String(className))}"`);
575
- }
576
- continue;
577
- }
578
- if (key === "style") {
579
- const styleStr = this.styleToString(value);
580
- if (styleStr) {
581
- attrs.push(`style="${this.escapeHtml(styleStr)}"`);
602
+ html += `${indent}</body>${nl}</html>`;
603
+ return html;
604
+ }
605
+
606
+ // src/client/dom/json.ts
607
+ function jsonToVNode(json, reactiveNodes) {
608
+ if (isState(json)) {
609
+ return createReactiveChild(json, reactiveNodes, (value) => value);
610
+ }
611
+ if (isPrimitiveJson(json)) {
612
+ return json;
613
+ }
614
+ const { tag, attributes = {}, children } = json;
615
+ const props = {};
616
+ for (const key in attributes) {
617
+ const value = attributes[key];
618
+ if (key === "class") {
619
+ props.className = isState(value) ? value.value : value;
620
+ } else {
621
+ props[key] = isState(value) ? value.value : value;
622
+ }
623
+ }
624
+ const childrenArray = [];
625
+ if (children != null) {
626
+ if (Array.isArray(children)) {
627
+ for (const child of children) {
628
+ if (isState(child)) {
629
+ childrenArray.push(createReactiveChild(child, reactiveNodes, (value) => value));
630
+ } else {
631
+ const converted = jsonToVNode(child, reactiveNodes);
632
+ if (converted != null && converted !== false) {
633
+ childrenArray.push(converted);
634
+ }
582
635
  }
583
- continue;
584
636
  }
585
- if (value === true) {
586
- attrs.push(key);
587
- continue;
637
+ } else if (isState(children)) {
638
+ childrenArray.push(createReactiveChild(children, reactiveNodes, (value) => value));
639
+ } else if (typeof children === "object" && children !== null && "tag" in children) {
640
+ const converted = jsonToVNode(children, reactiveNodes);
641
+ if (converted != null && converted !== false) {
642
+ childrenArray.push(converted);
588
643
  }
589
- attrs.push(`${key}="${this.escapeHtml(String(value))}"`);
644
+ } else {
645
+ childrenArray.push(children);
590
646
  }
591
- return attrs.join(" ");
592
647
  }
593
- styleToString(style) {
594
- if (typeof style === "string") {
595
- return style;
596
- }
597
- if (typeof style === "object" && style !== null) {
598
- const styles = [];
599
- for (const key in style) {
600
- const cssKey = key.replace(/([A-Z])/g, "-$1").toLowerCase();
601
- styles.push(`${cssKey}:${style[key]}`);
648
+ return { tagName: tag, props, children: childrenArray };
649
+ }
650
+ function vNodeJsonToVNode(json, reactiveNodes) {
651
+ if (isState(json)) {
652
+ return createReactiveChild(json, reactiveNodes, (value) => value);
653
+ }
654
+ if (isPrimitiveJson(json)) {
655
+ return json;
656
+ }
657
+ const { tagName, props = {}, children = [] } = json;
658
+ const resolvedProps = {};
659
+ for (const key in props) {
660
+ const value = props[key];
661
+ resolvedProps[key] = isState(value) ? value.value : value;
662
+ }
663
+ const childrenArray = [];
664
+ for (const child of children) {
665
+ if (isState(child)) {
666
+ childrenArray.push(createReactiveChild(child, reactiveNodes, (value) => value));
667
+ } else {
668
+ const converted = vNodeJsonToVNode(child, reactiveNodes);
669
+ if (converted != null && converted !== false) {
670
+ childrenArray.push(converted);
602
671
  }
603
- return styles.join(";");
604
672
  }
605
- return "";
606
673
  }
607
- isState(value) {
608
- return value && typeof value === "object" && "value" in value && "subscribe" in value && typeof value.subscribe === "function";
609
- }
610
- createReactiveChild(state, renderFn) {
611
- const currentValue = renderFn(state.value);
612
- if (typeof window !== "undefined" && typeof document !== "undefined") {
613
- const entry = { node: null, renderFn };
614
- this.reactiveNodes.set(state, entry);
615
- state.subscribe(() => {
616
- if (entry.node && entry.node.parentNode) {
617
- const newValue = renderFn(state.value);
618
- entry.node.textContent = String(newValue ?? "");
619
- }
620
- });
621
- }
622
- return currentValue;
674
+ return { tagName, props: resolvedProps, children: childrenArray };
675
+ }
676
+ function renderJson(rootElement, json, reactiveNodes) {
677
+ const vNode = jsonToVNode(json, reactiveNodes);
678
+ if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
679
+ throw new Error("Invalid JSON structure");
623
680
  }
624
- jsonToVNode(json) {
625
- if (this.isState(json)) {
626
- return this.createReactiveChild(json, (v) => v);
627
- }
628
- if (isPrimitiveJson(json)) {
629
- return json;
630
- }
631
- const { tag, attributes = {}, children } = json;
632
- const props = {};
633
- for (const key in attributes) {
634
- const value = attributes[key];
635
- if (key === "class") {
636
- props.className = this.isState(value) ? value.value : value;
637
- } else {
638
- props[key] = this.isState(value) ? value.value : value;
681
+ return render(rootElement, vNode);
682
+ }
683
+ function renderVNode(rootElement, json, reactiveNodes) {
684
+ const vNode = vNodeJsonToVNode(json, reactiveNodes);
685
+ if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
686
+ throw new Error("Invalid VNode JSON structure");
687
+ }
688
+ return render(rootElement, vNode);
689
+ }
690
+ function renderJsonToString(json, reactiveNodes, options = {}) {
691
+ const vNode = jsonToVNode(json, reactiveNodes);
692
+ return renderToString(vNode, options);
693
+ }
694
+ function renderVNodeToString(json, reactiveNodes, options = {}) {
695
+ const vNode = vNodeJsonToVNode(json, reactiveNodes);
696
+ return renderToString(vNode, options);
697
+ }
698
+
699
+ // src/client/dom/state-utils.ts
700
+ function createState(initialValue, options = {}) {
701
+ let value = initialValue;
702
+ const listeners = /* @__PURE__ */ new Set();
703
+ let updateTimer = null;
704
+ const { throttle = 0, deep = false } = options;
705
+ const notify = () => listeners.forEach((listener) => listener(value));
706
+ const scheduleUpdate = () => {
707
+ if (throttle > 0) {
708
+ if (!updateTimer) {
709
+ updateTimer = setTimeout(() => {
710
+ updateTimer = null;
711
+ notify();
712
+ }, throttle);
639
713
  }
714
+ } else {
715
+ notify();
640
716
  }
641
- const childrenArray = [];
642
- if (children != null) {
643
- if (Array.isArray(children)) {
644
- for (const child of children) {
645
- if (this.isState(child)) {
646
- childrenArray.push(this.createReactiveChild(child, (v) => v));
647
- } else {
648
- const converted = this.jsonToVNode(child);
649
- if (converted != null && converted !== false) {
650
- childrenArray.push(converted);
651
- }
652
- }
653
- }
654
- } else if (this.isState(children)) {
655
- childrenArray.push(this.createReactiveChild(children, (v) => v));
656
- } else if (typeof children === "object" && "tag" in children) {
657
- const converted = this.jsonToVNode(children);
658
- if (converted != null && converted !== false) {
659
- childrenArray.push(converted);
660
- }
661
- } else {
662
- childrenArray.push(children);
717
+ };
718
+ return {
719
+ get value() {
720
+ return value;
721
+ },
722
+ set value(newValue) {
723
+ const changed = deep ? JSON.stringify(value) !== JSON.stringify(newValue) : value !== newValue;
724
+ if (changed) {
725
+ value = newValue;
726
+ scheduleUpdate();
727
+ }
728
+ },
729
+ subscribe(fn) {
730
+ listeners.add(fn);
731
+ return () => listeners.delete(fn);
732
+ },
733
+ destroy() {
734
+ listeners.clear();
735
+ if (updateTimer) {
736
+ clearTimeout(updateTimer);
663
737
  }
664
738
  }
665
- return { tagName: tag, props, children: childrenArray };
739
+ };
740
+ }
741
+ function computed(states, computeFn) {
742
+ const values = states.map((state) => state.value);
743
+ const result = createState(computeFn(...values));
744
+ states.forEach((state, index) => {
745
+ state.subscribe((newValue) => {
746
+ values[index] = newValue;
747
+ result.value = computeFn(...values);
748
+ });
749
+ });
750
+ return result;
751
+ }
752
+ function effect(stateFn) {
753
+ stateFn();
754
+ }
755
+ function createVirtualList(container, items, renderItem, itemHeight = 50, bufferSize = 5) {
756
+ const viewportHeight = container.clientHeight;
757
+ const totalHeight = items.length * itemHeight;
758
+ let scrollTop = 0;
759
+ const getVisibleRange = () => {
760
+ const start = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferSize);
761
+ const end = Math.min(items.length, Math.ceil((scrollTop + viewportHeight) / itemHeight) + bufferSize);
762
+ return { start, end };
763
+ };
764
+ const render3 = () => {
765
+ const { start, end } = getVisibleRange();
766
+ const wrapper = document.createElement("div");
767
+ wrapper.style.cssText = `height:${totalHeight}px;position:relative`;
768
+ for (let i = start; i < end; i++) {
769
+ const itemEl = document.createElement("div");
770
+ itemEl.style.cssText = `position:absolute;top:${i * itemHeight}px;height:${itemHeight}px;width:100%`;
771
+ renderToDOM(renderItem(items[i], i), itemEl);
772
+ wrapper.appendChild(itemEl);
773
+ }
774
+ container.innerHTML = "";
775
+ container.appendChild(wrapper);
776
+ };
777
+ const scrollHandler = () => {
778
+ scrollTop = container.scrollTop;
779
+ requestAnimationFrame(render3);
780
+ };
781
+ container.addEventListener("scroll", scrollHandler);
782
+ render3();
783
+ return {
784
+ render: render3,
785
+ destroy: () => {
786
+ container.removeEventListener("scroll", scrollHandler);
787
+ container.innerHTML = "";
788
+ }
789
+ };
790
+ }
791
+ function lazy(loadFn) {
792
+ let component = null;
793
+ let loading = false;
794
+ return async (...args) => {
795
+ if (!component && !loading) {
796
+ loading = true;
797
+ component = await loadFn();
798
+ loading = false;
799
+ }
800
+ return component ? component(...args) : { tagName: "div", props: { class: "loading" }, children: ["Loading..."] };
801
+ };
802
+ }
803
+
804
+ // src/client/dom/index.ts
805
+ var DomNode = class {
806
+ constructor() {
807
+ this.elementCache = /* @__PURE__ */ new WeakMap();
808
+ this.reactiveNodes = /* @__PURE__ */ new Map();
809
+ }
810
+ createElement(tagName, props = {}, children = []) {
811
+ return { tagName, props, children };
812
+ }
813
+ renderToDOM(vNode, parent) {
814
+ return renderToDOM(vNode, parent);
815
+ }
816
+ render(rootElement, vNode) {
817
+ return render(rootElement, vNode);
818
+ }
819
+ batchRender(rootElement, vNodes) {
820
+ return batchRender(rootElement, vNodes);
821
+ }
822
+ renderChunked(rootElement, vNodes, chunkSize = 5e3, onProgress) {
823
+ return renderChunked(rootElement, vNodes, chunkSize, onProgress);
824
+ }
825
+ renderToHead(...vNodes) {
826
+ return renderToHead(...vNodes);
827
+ }
828
+ addStyle(cssText) {
829
+ return addStyle(cssText);
830
+ }
831
+ addMeta(attrs) {
832
+ return addMeta(attrs);
833
+ }
834
+ addLink(attrs) {
835
+ return addLink(attrs);
836
+ }
837
+ setTitle(text) {
838
+ return setTitle(text);
839
+ }
840
+ // Reactive State Management
841
+ createState(initialValue, options = {}) {
842
+ return createState(initialValue, options);
843
+ }
844
+ computed(states, computeFn) {
845
+ return computed(states, computeFn);
846
+ }
847
+ effect(stateFn) {
848
+ effect(stateFn);
849
+ }
850
+ // Virtual scrolling helper for large lists
851
+ createVirtualList(container, items, renderItem, itemHeight = 50, bufferSize = 5) {
852
+ return createVirtualList(container, items, renderItem, itemHeight, bufferSize);
853
+ }
854
+ // Lazy load components
855
+ lazy(loadFn) {
856
+ return lazy(loadFn);
857
+ }
858
+ // Memory management - cleanup unused elements
859
+ cleanupUnusedElements(root) {
860
+ return cleanupUnusedElements(root, this.elementCache);
861
+ }
862
+ // Server-Side Rendering - convert VNode to HTML string
863
+ renderToString(vNode, options = {}) {
864
+ return renderToString(vNode, options);
865
+ }
866
+ jsonToVNode(json) {
867
+ return jsonToVNode(json, this.reactiveNodes);
666
868
  }
667
869
  vNodeJsonToVNode(json) {
668
- if (this.isState(json)) {
669
- return this.createReactiveChild(json, (v) => v);
670
- }
671
- if (isPrimitiveJson(json)) {
672
- return json;
673
- }
674
- const { tagName, props = {}, children = [] } = json;
675
- const resolvedProps = {};
676
- for (const key in props) {
677
- const value = props[key];
678
- resolvedProps[key] = this.isState(value) ? value.value : value;
679
- }
680
- const childrenArray = [];
681
- for (const child of children) {
682
- if (this.isState(child)) {
683
- childrenArray.push(this.createReactiveChild(child, (v) => v));
684
- } else {
685
- const converted = this.vNodeJsonToVNode(child);
686
- if (converted != null && converted !== false) {
687
- childrenArray.push(converted);
688
- }
689
- }
690
- }
691
- return { tagName, props: resolvedProps, children: childrenArray };
870
+ return vNodeJsonToVNode(json, this.reactiveNodes);
692
871
  }
693
872
  renderJson(rootElement, json) {
694
- const vNode = this.jsonToVNode(json);
695
- if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
696
- throw new Error("Invalid JSON structure");
697
- }
698
- return this.render(rootElement, vNode);
873
+ return renderJson(rootElement, json, this.reactiveNodes);
699
874
  }
700
875
  renderVNode(rootElement, json) {
701
- const vNode = this.vNodeJsonToVNode(json);
702
- if (!vNode || typeof vNode !== "object" || !("tagName" in vNode)) {
703
- throw new Error("Invalid VNode JSON structure");
704
- }
705
- return this.render(rootElement, vNode);
876
+ return renderVNode(rootElement, json, this.reactiveNodes);
706
877
  }
707
878
  renderJsonToString(json, options = {}) {
708
- const vNode = this.jsonToVNode(json);
709
- return this.renderToString(vNode, options);
879
+ return renderJsonToString(json, this.reactiveNodes, options);
710
880
  }
711
881
  renderVNodeToString(json, options = {}) {
712
- const vNode = this.vNodeJsonToVNode(json);
713
- return this.renderToString(vNode, options);
882
+ return renderVNodeToString(json, this.reactiveNodes, options);
714
883
  }
715
884
  // Generate complete HTML document as string (for SSR)
716
885
  renderToHTMLDocument(vNode, options = {}) {
717
- const { title = "", meta = [], links = [], scripts = [], styles = [], lang = "en", head = "", bodyAttrs = {}, pretty = false } = options;
718
- const nl = pretty ? "\n" : "";
719
- const indent = pretty ? " " : "";
720
- const indent2 = pretty ? " " : "";
721
- let html = `<!DOCTYPE html>${nl}<html lang="${lang}">${nl}${indent}<head>${nl}${indent2}<meta charset="UTF-8">${nl}${indent2}<meta name="viewport" content="width=device-width, initial-scale=1.0">${nl}`;
722
- if (title) html += `${indent2}<title>${this.escapeHtml(title)}</title>${nl}`;
723
- for (const m of meta) {
724
- html += `${indent2}<meta`;
725
- for (const k in m) html += ` ${k}="${this.escapeHtml(m[k])}"`;
726
- html += `>${nl}`;
727
- }
728
- for (const l of links) {
729
- html += `${indent2}<link`;
730
- for (const k in l) html += ` ${k}="${this.escapeHtml(l[k])}"`;
731
- html += `>${nl}`;
732
- }
733
- for (const s of styles) {
734
- if (s.href) {
735
- html += `${indent2}<link rel="stylesheet" href="${this.escapeHtml(s.href)}">${nl}`;
736
- } else if (s.content) {
737
- html += `${indent2}<style>${s.content}</style>${nl}`;
738
- }
739
- }
740
- if (head) html += head + nl;
741
- html += `${indent}</head>${nl}${indent}<body`;
742
- for (const k in bodyAttrs) html += ` ${k}="${this.escapeHtml(bodyAttrs[k])}"`;
743
- html += `>${nl}`;
744
- html += this.renderToString(vNode, { pretty, indent: 2 });
745
- for (const script of scripts) {
746
- html += `${indent2}<script`;
747
- if (script.type) html += ` type="${this.escapeHtml(script.type)}"`;
748
- if (script.async) html += ` async`;
749
- if (script.defer) html += ` defer`;
750
- if (script.src) {
751
- html += ` src="${this.escapeHtml(script.src)}"></script>${nl}`;
752
- } else if (script.content) {
753
- html += `>${script.content}</script>${nl}`;
754
- } else {
755
- html += `></script>${nl}`;
756
- }
757
- }
758
- html += `${indent}</body>${nl}</html>`;
759
- return html;
886
+ return renderToHTMLDocument(vNode, options);
760
887
  }
761
888
  // Expose elementCache for reactive updates
762
889
  getElementCache() {
@@ -764,10 +891,10 @@ var DomNode = class {
764
891
  }
765
892
  };
766
893
  var dom = new DomNode();
767
- var render = dom.render.bind(dom);
768
- var renderToString = dom.renderToString.bind(dom);
894
+ var render2 = dom.render.bind(dom);
895
+ var renderToString2 = dom.renderToString.bind(dom);
769
896
 
770
- // src/router.ts
897
+ // src/client/router/helpers.ts
771
898
  function matchRoute(pattern, path) {
772
899
  const patternParts = pattern.split("/").filter(Boolean);
773
900
  const pathParts = path.split("/").filter(Boolean);
@@ -777,7 +904,9 @@ function matchRoute(pattern, path) {
777
904
  return { "*": path.slice(basePattern.length) };
778
905
  }
779
906
  }
780
- if (patternParts.length !== pathParts.length) return null;
907
+ if (patternParts.length !== pathParts.length) {
908
+ return null;
909
+ }
781
910
  const params = {};
782
911
  for (let i = 0; i < patternParts.length; i++) {
783
912
  const patternPart = patternParts[i];
@@ -792,7 +921,9 @@ function matchRoute(pattern, path) {
792
921
  }
793
922
  function executeGuard(guard, to, from, navigate, replace = false) {
794
923
  const result = guard(to, from);
795
- if (result === false) return false;
924
+ if (result === false) {
925
+ return false;
926
+ }
796
927
  if (typeof result === "string") {
797
928
  navigate(result, replace);
798
929
  return false;
@@ -805,69 +936,76 @@ function wrapComponent(component) {
805
936
  }
806
937
  return { tagName: "span", props: {}, children: [component] };
807
938
  }
939
+ function parseQuery(search) {
940
+ const query = {};
941
+ const params = new URLSearchParams(search);
942
+ params.forEach((value, key) => {
943
+ query[key] = value;
944
+ });
945
+ return query;
946
+ }
947
+ function parseLocation(path) {
948
+ const [pathPart, queryPart = ""] = path.split("?");
949
+ const [cleanPath, hash = ""] = pathPart.split("#");
950
+ return {
951
+ path: cleanPath || "/",
952
+ params: {},
953
+ query: parseQuery(queryPart),
954
+ hash: hash ? `#${hash}` : ""
955
+ };
956
+ }
957
+ function findRoute(routes, path) {
958
+ for (const route of routes) {
959
+ const params = matchRoute(route.path, path);
960
+ if (params !== null) {
961
+ return { route, params };
962
+ }
963
+ }
964
+ return null;
965
+ }
966
+ function getCurrentPath(mode, base) {
967
+ if (mode === "hash") {
968
+ return window.location.hash.slice(1) || "/";
969
+ }
970
+ return window.location.pathname.replace(base, "") || "/";
971
+ }
972
+ function buildNavigationUrl(mode, base, path) {
973
+ if (mode === "hash") {
974
+ return `#${path}`;
975
+ }
976
+ const hasBaseSlash = base.endsWith("/");
977
+ const hasPathSlash = path.startsWith("/");
978
+ let urlPath = path;
979
+ if (hasBaseSlash && hasPathSlash) {
980
+ urlPath = path.slice(1);
981
+ } else if (!hasBaseSlash && !hasPathSlash && path !== "/") {
982
+ urlPath = `/${path}`;
983
+ }
984
+ return base + urlPath;
985
+ }
986
+
987
+ // src/client/router/router.ts
808
988
  function createRouter(options) {
809
989
  const { mode = "history", base = "", routes } = options;
810
990
  const globalGuards = [];
811
- const parseQuery = (search) => {
812
- const query = {};
813
- const params = new URLSearchParams(search);
814
- params.forEach((value, key) => {
815
- query[key] = value;
816
- });
817
- return query;
818
- };
819
- const getCurrentPath = () => {
820
- if (mode === "hash") {
821
- return window.location.hash.slice(1) || "/";
822
- }
823
- return window.location.pathname.replace(base, "") || "/";
824
- };
825
- const parseLocation = (path) => {
826
- const [pathPart, queryPart = ""] = path.split("?");
827
- const [cleanPath, hash = ""] = pathPart.split("#");
828
- return {
829
- path: cleanPath || "/",
830
- params: {},
831
- query: parseQuery(queryPart),
832
- hash: hash ? "#" + hash : ""
833
- };
834
- };
835
- const findRoute = (path) => {
836
- for (const route of routes) {
837
- const params = matchRoute(route.path, path);
838
- if (params !== null) {
839
- return { route, params };
840
- }
841
- }
842
- return null;
843
- };
844
- const currentRoute = dom.createState(parseLocation(getCurrentPath()));
991
+ const currentRoute = dom.createState(parseLocation(getCurrentPath(mode, base)));
845
992
  const navigate = (path, replace = false) => {
846
993
  const location = parseLocation(path);
847
- const match = findRoute(location.path);
994
+ const match = findRoute(routes, location.path);
848
995
  if (match) {
849
996
  location.params = match.params;
850
997
  }
851
998
  for (const guard of globalGuards) {
852
- if (!executeGuard(guard, location, currentRoute.value, navigate, replace)) return;
999
+ if (!executeGuard(guard, location, currentRoute.value, navigate, replace)) {
1000
+ return;
1001
+ }
853
1002
  }
854
1003
  if (match?.route.beforeEnter) {
855
- if (!executeGuard(match.route.beforeEnter, location, currentRoute.value, navigate, replace)) return;
856
- }
857
- let urlPath = path;
858
- let url;
859
- if (mode === "hash") {
860
- url = "#" + path;
861
- } else {
862
- const hasBaseSlash = base.endsWith("/");
863
- const hasPathSlash = path.startsWith("/");
864
- if (hasBaseSlash && hasPathSlash) {
865
- urlPath = path.slice(1);
866
- } else if (!hasBaseSlash && !hasPathSlash && path !== "/") {
867
- urlPath = "/" + path;
1004
+ if (!executeGuard(match.route.beforeEnter, location, currentRoute.value, navigate, replace)) {
1005
+ return;
868
1006
  }
869
- url = base + urlPath;
870
1007
  }
1008
+ const url = buildNavigationUrl(mode, base, path);
871
1009
  if (replace) {
872
1010
  window.history.replaceState({ path }, "", url);
873
1011
  } else {
@@ -876,9 +1014,9 @@ function createRouter(options) {
876
1014
  currentRoute.value = location;
877
1015
  };
878
1016
  const handlePopState = () => {
879
- const path = getCurrentPath();
1017
+ const path = getCurrentPath(mode, base);
880
1018
  const location = parseLocation(path);
881
- const match = findRoute(location.path);
1019
+ const match = findRoute(routes, location.path);
882
1020
  if (match) {
883
1021
  location.params = match.params;
884
1022
  }
@@ -907,11 +1045,13 @@ function createRouter(options) {
907
1045
  }
908
1046
  };
909
1047
  }
1048
+
1049
+ // src/client/router/view.ts
910
1050
  function createRouterView(router, options) {
911
1051
  const { routes, notFound } = options;
912
1052
  return () => {
913
1053
  const location = router.currentRoute.value;
914
- const match = routes.find((r) => matchRoute(r.path, location.path) !== null);
1054
+ const match = routes.find((route) => matchRoute(route.path, location.path) !== null);
915
1055
  if (match) {
916
1056
  const params = matchRoute(match.path, location.path) || {};
917
1057
  const component = match.component({ ...params, ...location.query });
@@ -924,6 +1064,8 @@ function createRouterView(router, options) {
924
1064
  return { tagName: "div", props: {}, children: ["404 - Not Found"] };
925
1065
  };
926
1066
  }
1067
+
1068
+ // src/client/router/link.ts
927
1069
  var routerLink = (router, props, ...children) => {
928
1070
  const href = router.mode === "hash" ? `#${props.to}` : props.to;
929
1071
  return {
@@ -931,8 +1073,8 @@ var routerLink = (router, props, ...children) => {
931
1073
  props: {
932
1074
  ...props,
933
1075
  href,
934
- onclick: (e) => {
935
- e.preventDefault();
1076
+ onclick: (event) => {
1077
+ event.preventDefault();
936
1078
  router.push(props.to);
937
1079
  }
938
1080
  },