@fluixi/dom 1.0.0-alpha.53

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 (195) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +657 -0
  3. package/dist/cdn/dom.cjs +1 -0
  4. package/dist/cdn/dom.d.ts +16 -0
  5. package/dist/cdn/dom.d.ts.map +1 -0
  6. package/dist/cdn/dom.global.js +1 -0
  7. package/dist/cdn/dom.js +15 -0
  8. package/dist/cdn/dom.mjs +1 -0
  9. package/dist/index.cjs +1 -0
  10. package/dist/index.d.ts +38 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +39 -0
  13. package/dist/index.mjs +1 -0
  14. package/dist/lib/component/index.cjs +1 -0
  15. package/dist/lib/component/index.d.ts +2 -0
  16. package/dist/lib/component/index.d.ts.map +1 -0
  17. package/dist/lib/component/index.js +1 -0
  18. package/dist/lib/component/index.mjs +1 -0
  19. package/dist/lib/component/modifiers.cjs +1 -0
  20. package/dist/lib/component/modifiers.d.ts +21 -0
  21. package/dist/lib/component/modifiers.d.ts.map +1 -0
  22. package/dist/lib/component/modifiers.js +171 -0
  23. package/dist/lib/component/modifiers.mjs +1 -0
  24. package/dist/lib/dom/delegate.cjs +1 -0
  25. package/dist/lib/dom/delegate.d.ts +17 -0
  26. package/dist/lib/dom/delegate.d.ts.map +1 -0
  27. package/dist/lib/dom/delegate.js +50 -0
  28. package/dist/lib/dom/delegate.mjs +1 -0
  29. package/dist/lib/dom/hydration-state.cjs +1 -0
  30. package/dist/lib/dom/hydration-state.d.ts +45 -0
  31. package/dist/lib/dom/hydration-state.d.ts.map +1 -0
  32. package/dist/lib/dom/hydration-state.js +94 -0
  33. package/dist/lib/dom/hydration-state.mjs +1 -0
  34. package/dist/lib/dom/hydration.cjs +1 -0
  35. package/dist/lib/dom/hydration.d.ts +10 -0
  36. package/dist/lib/dom/hydration.d.ts.map +1 -0
  37. package/dist/lib/dom/hydration.js +143 -0
  38. package/dist/lib/dom/hydration.mjs +1 -0
  39. package/dist/lib/dom/index.cjs +1 -0
  40. package/dist/lib/dom/index.d.ts +9 -0
  41. package/dist/lib/dom/index.d.ts.map +1 -0
  42. package/dist/lib/dom/index.js +12 -0
  43. package/dist/lib/dom/index.mjs +1 -0
  44. package/dist/lib/dom/integration.cjs +1 -0
  45. package/dist/lib/dom/integration.d.ts +16 -0
  46. package/dist/lib/dom/integration.d.ts.map +1 -0
  47. package/dist/lib/dom/integration.js +25 -0
  48. package/dist/lib/dom/integration.mjs +1 -0
  49. package/dist/lib/dom/island.cjs +1 -0
  50. package/dist/lib/dom/island.d.ts +23 -0
  51. package/dist/lib/dom/island.d.ts.map +1 -0
  52. package/dist/lib/dom/island.js +100 -0
  53. package/dist/lib/dom/island.mjs +1 -0
  54. package/dist/lib/dom/runtime.cjs +1 -0
  55. package/dist/lib/dom/runtime.d.ts +131 -0
  56. package/dist/lib/dom/runtime.d.ts.map +1 -0
  57. package/dist/lib/dom/runtime.js +1425 -0
  58. package/dist/lib/dom/runtime.mjs +1 -0
  59. package/dist/lib/dom/server/fx-data.cjs +1 -0
  60. package/dist/lib/dom/server/fx-data.d.ts +36 -0
  61. package/dist/lib/dom/server/fx-data.d.ts.map +1 -0
  62. package/dist/lib/dom/server/fx-data.js +60 -0
  63. package/dist/lib/dom/server/fx-data.mjs +1 -0
  64. package/dist/lib/dom/server/host.cjs +1 -0
  65. package/dist/lib/dom/server/host.d.ts +14 -0
  66. package/dist/lib/dom/server/host.d.ts.map +1 -0
  67. package/dist/lib/dom/server/host.js +38 -0
  68. package/dist/lib/dom/server/host.mjs +1 -0
  69. package/dist/lib/dom/server/index.cjs +1 -0
  70. package/dist/lib/dom/server/index.d.ts +13 -0
  71. package/dist/lib/dom/server/index.d.ts.map +1 -0
  72. package/dist/lib/dom/server/index.js +12 -0
  73. package/dist/lib/dom/server/index.mjs +1 -0
  74. package/dist/lib/dom/server/nodes.cjs +1 -0
  75. package/dist/lib/dom/server/nodes.d.ts +105 -0
  76. package/dist/lib/dom/server/nodes.d.ts.map +1 -0
  77. package/dist/lib/dom/server/nodes.js +304 -0
  78. package/dist/lib/dom/server/nodes.mjs +1 -0
  79. package/dist/lib/dom/server/render.cjs +1 -0
  80. package/dist/lib/dom/server/render.d.ts +45 -0
  81. package/dist/lib/dom/server/render.d.ts.map +1 -0
  82. package/dist/lib/dom/server/render.js +174 -0
  83. package/dist/lib/dom/server/render.mjs +1 -0
  84. package/dist/lib/dom/server/request-context.cjs +1 -0
  85. package/dist/lib/dom/server/request-context.d.ts +85 -0
  86. package/dist/lib/dom/server/request-context.d.ts.map +1 -0
  87. package/dist/lib/dom/server/request-context.js +122 -0
  88. package/dist/lib/dom/server/request-context.mjs +1 -0
  89. package/dist/lib/dom/server/serialize.cjs +1 -0
  90. package/dist/lib/dom/server/serialize.d.ts +2 -0
  91. package/dist/lib/dom/server/serialize.d.ts.map +1 -0
  92. package/dist/lib/dom/server/serialize.js +98 -0
  93. package/dist/lib/dom/server/serialize.mjs +1 -0
  94. package/dist/lib/dom/server-renderer.cjs +1 -0
  95. package/dist/lib/dom/server-renderer.d.ts +2 -0
  96. package/dist/lib/dom/server-renderer.d.ts.map +1 -0
  97. package/dist/lib/dom/server-renderer.js +5 -0
  98. package/dist/lib/dom/server-renderer.mjs +1 -0
  99. package/dist/lib/dom/types.cjs +1 -0
  100. package/dist/lib/dom/types.d.ts +119 -0
  101. package/dist/lib/dom/types.d.ts.map +1 -0
  102. package/dist/lib/dom/types.js +1 -0
  103. package/dist/lib/dom/types.mjs +0 -0
  104. package/dist/lib/dom/utils.cjs +1 -0
  105. package/dist/lib/dom/utils.d.ts +411 -0
  106. package/dist/lib/dom/utils.d.ts.map +1 -0
  107. package/dist/lib/dom/utils.js +746 -0
  108. package/dist/lib/dom/utils.mjs +1 -0
  109. package/dist/lib/element/index.cjs +1 -0
  110. package/dist/lib/element/index.d.ts +2 -0
  111. package/dist/lib/element/index.d.ts.map +1 -0
  112. package/dist/lib/element/index.js +1 -0
  113. package/dist/lib/element/index.mjs +0 -0
  114. package/dist/lib/element/types.cjs +1 -0
  115. package/dist/lib/element/types.d.ts +129 -0
  116. package/dist/lib/element/types.d.ts.map +1 -0
  117. package/dist/lib/element/types.js +5 -0
  118. package/dist/lib/element/types.mjs +0 -0
  119. package/dist/lib/flow/class-map.cjs +1 -0
  120. package/dist/lib/flow/class-map.d.ts +40 -0
  121. package/dist/lib/flow/class-map.d.ts.map +1 -0
  122. package/dist/lib/flow/class-map.js +44 -0
  123. package/dist/lib/flow/class-map.mjs +1 -0
  124. package/dist/lib/flow/dynamic.cjs +1 -0
  125. package/dist/lib/flow/dynamic.d.ts +27 -0
  126. package/dist/lib/flow/dynamic.d.ts.map +1 -0
  127. package/dist/lib/flow/dynamic.js +30 -0
  128. package/dist/lib/flow/dynamic.mjs +1 -0
  129. package/dist/lib/flow/error-boundary.cjs +1 -0
  130. package/dist/lib/flow/error-boundary.d.ts +34 -0
  131. package/dist/lib/flow/error-boundary.d.ts.map +1 -0
  132. package/dist/lib/flow/error-boundary.js +49 -0
  133. package/dist/lib/flow/error-boundary.mjs +1 -0
  134. package/dist/lib/flow/for.cjs +1 -0
  135. package/dist/lib/flow/for.d.ts +64 -0
  136. package/dist/lib/flow/for.d.ts.map +1 -0
  137. package/dist/lib/flow/for.js +28 -0
  138. package/dist/lib/flow/for.mjs +1 -0
  139. package/dist/lib/flow/index-flow.cjs +1 -0
  140. package/dist/lib/flow/index-flow.d.ts +34 -0
  141. package/dist/lib/flow/index-flow.d.ts.map +1 -0
  142. package/dist/lib/flow/index-flow.js +27 -0
  143. package/dist/lib/flow/index-flow.mjs +1 -0
  144. package/dist/lib/flow/index.cjs +1 -0
  145. package/dist/lib/flow/index.d.ts +20 -0
  146. package/dist/lib/flow/index.d.ts.map +1 -0
  147. package/dist/lib/flow/index.js +11 -0
  148. package/dist/lib/flow/index.mjs +1 -0
  149. package/dist/lib/flow/portal.cjs +1 -0
  150. package/dist/lib/flow/portal.d.ts +30 -0
  151. package/dist/lib/flow/portal.d.ts.map +1 -0
  152. package/dist/lib/flow/portal.js +44 -0
  153. package/dist/lib/flow/portal.mjs +1 -0
  154. package/dist/lib/flow/show.cjs +1 -0
  155. package/dist/lib/flow/show.d.ts +35 -0
  156. package/dist/lib/flow/show.d.ts.map +1 -0
  157. package/dist/lib/flow/show.js +162 -0
  158. package/dist/lib/flow/show.mjs +1 -0
  159. package/dist/lib/flow/style-map.cjs +1 -0
  160. package/dist/lib/flow/style-map.d.ts +49 -0
  161. package/dist/lib/flow/style-map.d.ts.map +1 -0
  162. package/dist/lib/flow/style-map.js +58 -0
  163. package/dist/lib/flow/style-map.mjs +1 -0
  164. package/dist/lib/flow/switch.cjs +1 -0
  165. package/dist/lib/flow/switch.d.ts +63 -0
  166. package/dist/lib/flow/switch.d.ts.map +1 -0
  167. package/dist/lib/flow/switch.js +69 -0
  168. package/dist/lib/flow/switch.mjs +1 -0
  169. package/dist/lib/flow/utils.cjs +1 -0
  170. package/dist/lib/flow/utils.d.ts +19 -0
  171. package/dist/lib/flow/utils.d.ts.map +1 -0
  172. package/dist/lib/flow/utils.js +34 -0
  173. package/dist/lib/flow/utils.mjs +1 -0
  174. package/dist/lib/index.cjs +1 -0
  175. package/dist/lib/index.d.ts +21 -0
  176. package/dist/lib/index.d.ts.map +1 -0
  177. package/dist/lib/index.js +30 -0
  178. package/dist/lib/index.mjs +1 -0
  179. package/dist/lib/jsx/control-flow/flow.cjs +1 -0
  180. package/dist/lib/jsx/control-flow/flow.d.ts +2 -0
  181. package/dist/lib/jsx/control-flow/flow.d.ts.map +1 -0
  182. package/dist/lib/jsx/control-flow/flow.js +627 -0
  183. package/dist/lib/jsx/control-flow/flow.mjs +0 -0
  184. package/dist/lib/jsx/control-flow/for-flow.cjs +1 -0
  185. package/dist/lib/jsx/control-flow/for-flow.d.ts +2 -0
  186. package/dist/lib/jsx/control-flow/for-flow.d.ts.map +1 -0
  187. package/dist/lib/jsx/control-flow/for-flow.js +1 -0
  188. package/dist/lib/jsx/control-flow/for-flow.mjs +0 -0
  189. package/dist/lib/shared/types.cjs +1 -0
  190. package/dist/lib/shared/types.d.ts +47 -0
  191. package/dist/lib/shared/types.d.ts.map +1 -0
  192. package/dist/lib/shared/types.js +1 -0
  193. package/dist/lib/shared/types.mjs +0 -0
  194. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  195. package/package.json +99 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Server node model — a minimal, serialization-oriented stand-in for the DOM
3
+ * Node/Element API that @fluixi/dom's runtime calls. Under server mode the leaf
4
+ * factories (createNativeElement/createTextNode/createMarker) return these
5
+ * instead of real DOM nodes, so insert/spread/Show/For run unchanged and the
6
+ * result serializes to an HTML string. This is the "universal renderer" seam:
7
+ * our runtime talks to nodes via methods, so we mock exactly those methods
8
+ * (no jsdom). See ./serialize.ts for the string emitter.
9
+ *
10
+ * Only the surface the runtime actually uses is implemented (measured against
11
+ * runtime.ts): the linked tree (parentNode/childNodes/*Sibling + append/insert/
12
+ * remove/replace), attributes, the JSX_PROPERTIES properties (className/value/
13
+ * checked/selected/innerHTML/textContent/innerText/htmlFor/indeterminate),
14
+ * style, classList, and no-op event listeners.
15
+ */
16
+ import { SERVER_NODE } from './host.js';
17
+ export const NODE_ELEMENT = 1;
18
+ export const NODE_TEXT = 3;
19
+ export const NODE_COMMENT = 8;
20
+ // ---------------------------------------------------------------------------
21
+ // Base node — the linked-tree mechanics insert() relies on.
22
+ // ---------------------------------------------------------------------------
23
+ export class ServerNode {
24
+ // Brand the host.ts guards check for (lets them skip an instanceof on these classes).
25
+ [SERVER_NODE] = true;
26
+ parentNode = null;
27
+ childNodes = [];
28
+ get firstChild() {
29
+ return this.childNodes[0] ?? null;
30
+ }
31
+ get lastChild() {
32
+ return this.childNodes[this.childNodes.length - 1] ?? null;
33
+ }
34
+ get nextSibling() {
35
+ const p = this.parentNode;
36
+ if (!p)
37
+ return null;
38
+ const i = p.childNodes.indexOf(this);
39
+ return i >= 0 ? p.childNodes[i + 1] ?? null : null;
40
+ }
41
+ get previousSibling() {
42
+ const p = this.parentNode;
43
+ if (!p)
44
+ return null;
45
+ const i = p.childNodes.indexOf(this);
46
+ return i > 0 ? p.childNodes[i - 1] ?? null : null;
47
+ }
48
+ get parentElement() {
49
+ return this.parentNode;
50
+ }
51
+ appendChild(child) {
52
+ if (child.parentNode)
53
+ child.parentNode.removeChild(child);
54
+ child.parentNode = this;
55
+ this.childNodes.push(child);
56
+ return child;
57
+ }
58
+ insertBefore(child, ref) {
59
+ if (ref == null)
60
+ return this.appendChild(child);
61
+ if (child.parentNode)
62
+ child.parentNode.removeChild(child);
63
+ const i = this.childNodes.indexOf(ref);
64
+ child.parentNode = this;
65
+ if (i < 0)
66
+ this.childNodes.push(child);
67
+ else
68
+ this.childNodes.splice(i, 0, child);
69
+ return child;
70
+ }
71
+ removeChild(child) {
72
+ const i = this.childNodes.indexOf(child);
73
+ if (i >= 0)
74
+ this.childNodes.splice(i, 1);
75
+ child.parentNode = null;
76
+ return child;
77
+ }
78
+ replaceChild(next, old) {
79
+ const i = this.childNodes.indexOf(old);
80
+ if (i >= 0) {
81
+ if (next.parentNode)
82
+ next.parentNode.removeChild(next);
83
+ next.parentNode = this;
84
+ this.childNodes[i] = next;
85
+ old.parentNode = null;
86
+ }
87
+ return old;
88
+ }
89
+ // Event listeners are inert on the server.
90
+ addEventListener() { }
91
+ removeEventListener() { }
92
+ }
93
+ // ---------------------------------------------------------------------------
94
+ // Text + Comment
95
+ // ---------------------------------------------------------------------------
96
+ export class ServerText extends ServerNode {
97
+ nodeType = NODE_TEXT;
98
+ data;
99
+ constructor(value) {
100
+ super();
101
+ this.data = value;
102
+ }
103
+ get nodeValue() {
104
+ return this.data;
105
+ }
106
+ set nodeValue(v) {
107
+ this.data = v == null ? '' : String(v);
108
+ }
109
+ get textContent() {
110
+ return this.data;
111
+ }
112
+ set textContent(v) {
113
+ this.data = v == null ? '' : String(v);
114
+ }
115
+ cloneNode() {
116
+ return new ServerText(this.data);
117
+ }
118
+ }
119
+ export class ServerComment extends ServerNode {
120
+ nodeType = NODE_COMMENT;
121
+ data;
122
+ constructor(value) {
123
+ super();
124
+ this.data = value;
125
+ }
126
+ get nodeValue() {
127
+ return this.data;
128
+ }
129
+ set nodeValue(v) {
130
+ this.data = v == null ? '' : String(v);
131
+ }
132
+ cloneNode() {
133
+ return new ServerComment(this.data);
134
+ }
135
+ }
136
+ // ---------------------------------------------------------------------------
137
+ // style + classList helpers (runtime sets element.style.cssText, element.style
138
+ // .camelKey, and element.classList.toggle/add/remove/contains).
139
+ // ---------------------------------------------------------------------------
140
+ export class ServerStyle {
141
+ // declared keys are stored as own props; cssText holds a raw string blob.
142
+ cssText = '';
143
+ setProperty(name, value) {
144
+ this[name] = value;
145
+ }
146
+ removeProperty(name) {
147
+ delete this[name];
148
+ }
149
+ }
150
+ class ServerClassList {
151
+ el;
152
+ constructor(el) {
153
+ this.el = el;
154
+ }
155
+ list() {
156
+ const c = this.el.getAttribute('class');
157
+ return c ? c.split(/\s+/).filter(Boolean) : [];
158
+ }
159
+ write(arr) {
160
+ if (arr.length)
161
+ this.el.setAttribute('class', arr.join(' '));
162
+ else
163
+ this.el.removeAttribute('class');
164
+ }
165
+ add(...names) {
166
+ const arr = this.list();
167
+ for (const n of names)
168
+ if (!arr.includes(n))
169
+ arr.push(n);
170
+ this.write(arr);
171
+ }
172
+ remove(...names) {
173
+ this.write(this.list().filter((c) => !names.includes(c)));
174
+ }
175
+ contains(name) {
176
+ return this.list().includes(name);
177
+ }
178
+ toggle(name, force) {
179
+ const has = this.contains(name);
180
+ const on = force === undefined ? !has : force;
181
+ if (on)
182
+ this.add(name);
183
+ else
184
+ this.remove(name);
185
+ return on;
186
+ }
187
+ }
188
+ // ---------------------------------------------------------------------------
189
+ // Element — attributes + the JSX_PROPERTIES properties + style/classList.
190
+ // ---------------------------------------------------------------------------
191
+ export class ServerElement extends ServerNode {
192
+ nodeType = NODE_ELEMENT;
193
+ localName;
194
+ tagName;
195
+ isSVG;
196
+ namespaceURI;
197
+ attributes = new Map();
198
+ style = new ServerStyle();
199
+ classList = new ServerClassList(this);
200
+ /** raw HTML set via innerHTML — emitted verbatim, bypassing childNodes. */
201
+ rawHTML = null;
202
+ constructor(tag, isSVG = false) {
203
+ super();
204
+ this.localName = tag.toLowerCase();
205
+ this.tagName = isSVG ? tag : tag.toUpperCase();
206
+ this.isSVG = isSVG;
207
+ this.namespaceURI = isSVG ? 'http://www.w3.org/2000/svg' : null;
208
+ }
209
+ setAttribute(name, value) {
210
+ this.attributes.set(name, String(value));
211
+ }
212
+ removeAttribute(name) {
213
+ this.attributes.delete(name);
214
+ }
215
+ getAttribute(name) {
216
+ return this.attributes.has(name) ? this.attributes.get(name) : null;
217
+ }
218
+ hasAttribute(name) {
219
+ return this.attributes.has(name);
220
+ }
221
+ get id() {
222
+ return this.getAttribute('id') ?? '';
223
+ }
224
+ set id(v) {
225
+ if (v == null)
226
+ this.removeAttribute('id');
227
+ else
228
+ this.setAttribute('id', v);
229
+ }
230
+ // JSX_PROPERTIES: className/value/checked/selected/innerHTML/innerText/
231
+ // textContent/indeterminate/htmlFor flow through setProperty (element[k]=v).
232
+ get className() {
233
+ return this.getAttribute('class') ?? '';
234
+ }
235
+ set className(v) {
236
+ if (v == null)
237
+ this.removeAttribute('class');
238
+ else
239
+ this.setAttribute('class', v);
240
+ }
241
+ get htmlFor() {
242
+ return this.getAttribute('for') ?? '';
243
+ }
244
+ set htmlFor(v) {
245
+ if (v == null)
246
+ this.removeAttribute('for');
247
+ else
248
+ this.setAttribute('for', v);
249
+ }
250
+ set value(v) {
251
+ if (v == null)
252
+ this.removeAttribute('value');
253
+ else
254
+ this.setAttribute('value', String(v));
255
+ }
256
+ set checked(v) {
257
+ if (v)
258
+ this.setAttribute('checked', '');
259
+ else
260
+ this.removeAttribute('checked');
261
+ }
262
+ set selected(v) {
263
+ if (v)
264
+ this.setAttribute('selected', '');
265
+ else
266
+ this.removeAttribute('selected');
267
+ }
268
+ // indeterminate is a live-only DOM property with no HTML serialization.
269
+ set indeterminate(_v) { }
270
+ get textContent() {
271
+ return this.childNodes.map((c) => c.textContent ?? '').join('');
272
+ }
273
+ set textContent(v) {
274
+ for (const c of this.childNodes)
275
+ c.parentNode = null;
276
+ this.childNodes = [];
277
+ this.rawHTML = null;
278
+ if (v != null && v !== '')
279
+ this.appendChild(new ServerText(String(v)));
280
+ }
281
+ set innerText(v) {
282
+ this.textContent = v;
283
+ }
284
+ set innerHTML(v) {
285
+ for (const c of this.childNodes)
286
+ c.parentNode = null;
287
+ this.childNodes = [];
288
+ this.rawHTML = v == null ? '' : String(v);
289
+ }
290
+ cloneNode(deep = false) {
291
+ const el = new ServerElement(this.localName, this.isSVG);
292
+ el.attributes = new Map(this.attributes);
293
+ el.style.cssText = this.style.cssText;
294
+ el.rawHTML = this.rawHTML;
295
+ if (deep)
296
+ for (const c of this.childNodes)
297
+ el.appendChild(c.cloneNode(true));
298
+ return el;
299
+ }
300
+ }
301
+ // Server-only; serialize.ts uses it. The client-facing guards are in host.ts.
302
+ export function isServerNodeValue(x) {
303
+ return x instanceof ServerNode;
304
+ }
@@ -0,0 +1 @@
1
+ var a=Symbol.for("fluixi.server-node");var g=1,p=3,N=8,c;c=a;var n=class{constructor(){this[c]=!0;this.parentNode=null;this.childNodes=[]}get firstChild(){return this.childNodes[0]??null}get lastChild(){return this.childNodes[this.childNodes.length-1]??null}get nextSibling(){let t=this.parentNode;if(!t)return null;let e=t.childNodes.indexOf(this);return e>=0?t.childNodes[e+1]??null:null}get previousSibling(){let t=this.parentNode;if(!t)return null;let e=t.childNodes.indexOf(this);return e>0?t.childNodes[e-1]??null:null}get parentElement(){return this.parentNode}appendChild(t){return t.parentNode&&t.parentNode.removeChild(t),t.parentNode=this,this.childNodes.push(t),t}insertBefore(t,e){if(e==null)return this.appendChild(t);t.parentNode&&t.parentNode.removeChild(t);let r=this.childNodes.indexOf(e);return t.parentNode=this,r<0?this.childNodes.push(t):this.childNodes.splice(r,0,t),t}removeChild(t){let e=this.childNodes.indexOf(t);return e>=0&&this.childNodes.splice(e,1),t.parentNode=null,t}replaceChild(t,e){let r=this.childNodes.indexOf(e);return r>=0&&(t.parentNode&&t.parentNode.removeChild(t),t.parentNode=this,this.childNodes[r]=t,e.parentNode=null),e}addEventListener(){}removeEventListener(){}},o=class s extends n{constructor(e){super();this.nodeType=p;this.data=e}get nodeValue(){return this.data}set nodeValue(e){this.data=e==null?"":String(e)}get textContent(){return this.data}set textContent(e){this.data=e==null?"":String(e)}cloneNode(){return new s(this.data)}},u=class s extends n{constructor(e){super();this.nodeType=N;this.data=e}get nodeValue(){return this.data}set nodeValue(e){this.data=e==null?"":String(e)}cloneNode(){return new s(this.data)}},l=class{constructor(){this.cssText=""}setProperty(t,e){this[t]=e}removeProperty(t){delete this[t]}},d=class{constructor(t){this.el=t}list(){let t=this.el.getAttribute("class");return t?t.split(/\s+/).filter(Boolean):[]}write(t){t.length?this.el.setAttribute("class",t.join(" ")):this.el.removeAttribute("class")}add(...t){let e=this.list();for(let r of t)e.includes(r)||e.push(r);this.write(e)}remove(...t){this.write(this.list().filter(e=>!t.includes(e)))}contains(t){return this.list().includes(t)}toggle(t,e){let r=this.contains(t),i=e===void 0?!r:e;return i?this.add(t):this.remove(t),i}},h=class s extends n{constructor(e,r=!1){super();this.nodeType=g;this.attributes=new Map;this.style=new l;this.classList=new d(this);this.rawHTML=null;this.localName=e.toLowerCase(),this.tagName=r?e:e.toUpperCase(),this.isSVG=r,this.namespaceURI=r?"http://www.w3.org/2000/svg":null}setAttribute(e,r){this.attributes.set(e,String(r))}removeAttribute(e){this.attributes.delete(e)}getAttribute(e){return this.attributes.has(e)?this.attributes.get(e):null}hasAttribute(e){return this.attributes.has(e)}get id(){return this.getAttribute("id")??""}set id(e){e==null?this.removeAttribute("id"):this.setAttribute("id",e)}get className(){return this.getAttribute("class")??""}set className(e){e==null?this.removeAttribute("class"):this.setAttribute("class",e)}get htmlFor(){return this.getAttribute("for")??""}set htmlFor(e){e==null?this.removeAttribute("for"):this.setAttribute("for",e)}set value(e){e==null?this.removeAttribute("value"):this.setAttribute("value",String(e))}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}set selected(e){e?this.setAttribute("selected",""):this.removeAttribute("selected")}set indeterminate(e){}get textContent(){return this.childNodes.map(e=>e.textContent??"").join("")}set textContent(e){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=null,e!=null&&e!==""&&this.appendChild(new o(String(e)))}set innerText(e){this.textContent=e}set innerHTML(e){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=e==null?"":String(e)}cloneNode(e=!1){let r=new s(this.localName,this.isSVG);if(r.attributes=new Map(this.attributes),r.style.cssText=this.style.cssText,r.rawHTML=this.rawHTML,e)for(let i of this.childNodes)r.appendChild(i.cloneNode(!0));return r}};function m(s){return s instanceof n}export{N as NODE_COMMENT,g as NODE_ELEMENT,p as NODE_TEXT,u as ServerComment,h as ServerElement,n as ServerNode,l as ServerStyle,o as ServerText,m as isServerNodeValue};
@@ -0,0 +1 @@
1
+ "use strict";var y=Object.defineProperty;var U=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var J=Object.prototype.hasOwnProperty;var B=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},G=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of W(e))!J.call(n,s)&&s!==t&&y(n,s,{get:()=>e[s],enumerable:!(r=U(e,s))||r.enumerable});return n};var K=n=>G(y({},"__esModule",{value:!0}),n);var pe={};B(pe,{renderToString:()=>ue,renderToStringAsync:()=>de,serializeResourceData:()=>z,setDataRedactor:()=>le});module.exports=K(pe);var k=require("@fluixi/reactive/signal"),i=require("@fluixi/reactive/signal");var f=typeof document>"u";function c(n){f=n}var _=Symbol.for("fluixi.server-node"),Q=null;function D(n){Q=n}var w=1,R=3,b=8,M;M=_;var d=class{constructor(){this[M]=!0;this.parentNode=null;this.childNodes=[]}get firstChild(){return this.childNodes[0]??null}get lastChild(){return this.childNodes[this.childNodes.length-1]??null}get nextSibling(){let e=this.parentNode;if(!e)return null;let t=e.childNodes.indexOf(this);return t>=0?e.childNodes[t+1]??null:null}get previousSibling(){let e=this.parentNode;if(!e)return null;let t=e.childNodes.indexOf(this);return t>0?e.childNodes[t-1]??null:null}get parentElement(){return this.parentNode}appendChild(e){return e.parentNode&&e.parentNode.removeChild(e),e.parentNode=this,this.childNodes.push(e),e}insertBefore(e,t){if(t==null)return this.appendChild(e);e.parentNode&&e.parentNode.removeChild(e);let r=this.childNodes.indexOf(t);return e.parentNode=this,r<0?this.childNodes.push(e):this.childNodes.splice(r,0,e),e}removeChild(e){let t=this.childNodes.indexOf(e);return t>=0&&this.childNodes.splice(t,1),e.parentNode=null,e}replaceChild(e,t){let r=this.childNodes.indexOf(t);return r>=0&&(e.parentNode&&e.parentNode.removeChild(e),e.parentNode=this,this.childNodes[r]=e,t.parentNode=null),t}addEventListener(){}removeEventListener(){}},p=class n extends d{constructor(t){super();this.nodeType=R;this.data=t}get nodeValue(){return this.data}set nodeValue(t){this.data=t==null?"":String(t)}get textContent(){return this.data}set textContent(t){this.data=t==null?"":String(t)}cloneNode(){return new n(this.data)}},g=class n extends d{constructor(t){super();this.nodeType=b;this.data=t}get nodeValue(){return this.data}set nodeValue(t){this.data=t==null?"":String(t)}cloneNode(){return new n(this.data)}},S=class{constructor(){this.cssText=""}setProperty(e,t){this[e]=t}removeProperty(e){delete this[e]}},T=class{constructor(e){this.el=e}list(){let e=this.el.getAttribute("class");return e?e.split(/\s+/).filter(Boolean):[]}write(e){e.length?this.el.setAttribute("class",e.join(" ")):this.el.removeAttribute("class")}add(...e){let t=this.list();for(let r of e)t.includes(r)||t.push(r);this.write(t)}remove(...e){this.write(this.list().filter(t=>!e.includes(t)))}contains(e){return this.list().includes(e)}toggle(e,t){let r=this.contains(e),s=t===void 0?!r:t;return s?this.add(e):this.remove(e),s}},h=class n extends d{constructor(t,r=!1){super();this.nodeType=w;this.attributes=new Map;this.style=new S;this.classList=new T(this);this.rawHTML=null;this.localName=t.toLowerCase(),this.tagName=r?t:t.toUpperCase(),this.isSVG=r,this.namespaceURI=r?"http://www.w3.org/2000/svg":null}setAttribute(t,r){this.attributes.set(t,String(r))}removeAttribute(t){this.attributes.delete(t)}getAttribute(t){return this.attributes.has(t)?this.attributes.get(t):null}hasAttribute(t){return this.attributes.has(t)}get id(){return this.getAttribute("id")??""}set id(t){t==null?this.removeAttribute("id"):this.setAttribute("id",t)}get className(){return this.getAttribute("class")??""}set className(t){t==null?this.removeAttribute("class"):this.setAttribute("class",t)}get htmlFor(){return this.getAttribute("for")??""}set htmlFor(t){t==null?this.removeAttribute("for"):this.setAttribute("for",t)}set value(t){t==null?this.removeAttribute("value"):this.setAttribute("value",String(t))}set checked(t){t?this.setAttribute("checked",""):this.removeAttribute("checked")}set selected(t){t?this.setAttribute("selected",""):this.removeAttribute("selected")}set indeterminate(t){}get textContent(){return this.childNodes.map(t=>t.textContent??"").join("")}set textContent(t){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=null,t!=null&&t!==""&&this.appendChild(new p(String(t)))}set innerText(t){this.textContent=t}set innerHTML(t){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=t==null?"":String(t)}cloneNode(t=!1){let r=new n(this.localName,this.isSVG);if(r.attributes=new Map(this.attributes),r.style.cssText=this.style.cssText,r.rawHTML=this.rawHTML,t)for(let s of this.childNodes)r.appendChild(s.cloneNode(!0));return r}};var Y=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),L=/&/g,Z=/</g,ee=/>/g,te=/"/g;function E(n){return n.replace(L,"&amp;").replace(Z,"&lt;").replace(ee,"&gt;")}function O(n){return n.replace(L,"&amp;").replace(te,"&quot;")}function ne(n){return n.startsWith("--")?n:n.replace(/[A-Z]/g,e=>"-"+e.toLowerCase())}function re(n){if(!n)return"";let e=[];n.cssText&&e.push(n.cssText.trim().replace(/;\s*$/,""));for(let t of Object.keys(n)){if(t==="cssText")continue;let r=n[t];r==null||r===""||e.push(`${ne(t)}: ${r}`)}return e.join("; ")}function se(n){let e="",t=re(n.style);for(let[r,s]of n.attributes)r==="style"&&t||(e+=` ${r}="${O(s)}"`);if(t){let r=n.attributes.get("style"),s=r?`${r.replace(/;\s*$/,"")}; ${t}`:t;e+=` style="${O(s)}"`}return e}function a(n){if(n==null||n===!1||n===!0)return"";if(typeof n=="string")return E(n);if(typeof n=="number")return E(String(n));if(typeof n=="function")return a(n());if(Array.isArray(n))return n.map(a).join("");switch(n.nodeType){case R:return E(n.data??"");case b:return`<!--${n.data??""}-->`;case w:{let e=n.localName,t=`<${e}${se(n)}>`;if(Y.has(e))return t;let r=n.rawHTML!=null?n.rawHTML:(n.childNodes??[]).map(a).join("");return`${t}${r}</${e}>`}}return Array.isArray(n.childNodes)?n.childNodes.map(a).join(""):""}var $="__FX_DATA__";function I(n){return n.replace(/[<>&\u2028\u2029]/g,e=>"\\u"+e.charCodeAt(0).toString(16).padStart(4,"0"))}var m,ie={getStore:()=>m,run(n,e){let t=m;m=n;try{return e()}finally{m=t}}},F=ie;function C(n={}){n.locals||(n.locals=n.request?oe(n.request):{});let e=0,t=0,r=null,s=new Map,o=new Map;return{event:n,routeData:new Map,matchedRoute:new Map,nextId:()=>`s${e++}`,nextResourceId:()=>{if(r===null)return`r${t++}`;let u=s.get(r)??0;return s.set(r,u+1),`${r}:r${u}`},nextIslandNamespace:u=>{let l=o.get(u)??0;return o.set(u,l+1),`${u}#${l}`},withResourceScope(u,l){let v=r;r=u;try{return l()}finally{r=v}},pending:new Set,data:new Map}}function A(n,e){return F.run(n,e)}function N(){return F.getStore()}var j=new WeakMap;function oe(n){let e=j.get(n);return e||(e={},j.set(n,e)),e}var H=!1;function X(){H||(H=!0,D({createElement:(n,e)=>new h(n,e),createText:n=>new p(n),createComment:n=>new g(n)}))}function ue(n,e={}){let t=f;X(),c(!0);let r=C(e.event);try{return A(r,()=>(0,k.createRoot)(s=>{try{let o=typeof n=="function"?n():n;return a(o)}finally{s()}}))}finally{c(t)}}var V=!1;function ae(){V||(V=!0,(0,i.setResourceTracker)(n=>{N()?.pending.add(n)}),(0,i.setResourceIdSource)(()=>N()?.nextResourceId()??""),(0,i.setResourceDataSink)((n,e)=>{n&&N()?.data.set(n,e)}))}var q=null;function le(n){q=n}function z(n){if(n.data.size===0)return"";let e={};for(let[r,s]of n.data){let o=s;if(q)try{o=q(s,r)}catch{continue}o!==void 0&&(e[r]=o)}if(Object.keys(e).length===0)return"";let t=I(JSON.stringify(e));return`<script type="application/json" id="${$}">${t}<\/script>`}var ce=50;async function de(n,e={}){let t=f;X(),c(!0),ae();let r=C(e.event);try{return await A(r,async()=>{e.preload&&await e.preload(e.event);let s,o=null,u=()=>{};(0,k.createRoot)(x=>{u=x,o=(0,i.getOwner)(),s=typeof n=="function"?n():n});let l=()=>{try{(0,i.runWithOwner)(o,()=>a(s))}catch{}};l();let v=0;for(;r.pending.size>0&&v++<ce;){let x=Array.from(r.pending);r.pending.clear(),await Promise.allSettled(x),await(0,i.flush)(),l()}let P=(0,i.runWithOwner)(o,()=>a(s))+z(r);return u(),P})}finally{c(t)}}
@@ -0,0 +1,45 @@
1
+ import { type RequestContext, type RequestEvent } from './request-context.js';
2
+ export interface RenderOptions {
3
+ /** request being rendered — exposed to the app via getRequestEvent() */
4
+ event?: RequestEvent;
5
+ /**
6
+ * Runs ONCE inside the request context, before the app renders. Use it to resolve
7
+ * request-scoped state the render depends on (region, locale, session) and seed it
8
+ * into the request data so the client hydrates from the same value. @fluixi/server
9
+ * passes `preloadServerState` here to drive `createIsomorphicState`.
10
+ */
11
+ preload?: (event?: RequestEvent) => void | Promise<void>;
12
+ }
13
+ export declare function renderToString(app: () => any, options?: RenderOptions): string;
14
+ /**
15
+ * Optional redactor: transform (or drop) each resource value before it's serialized
16
+ * into `__FX_DATA__`. Resource data is embedded in the page HTML and visible to anyone,
17
+ * so use this to strip server-only fields (tokens, password hashes, internal ids).
18
+ * Return the value to serialize, or `undefined` to omit the entry entirely (the client
19
+ * then refetches that resource instead of seeding it). Server-only, app-wide.
20
+ *
21
+ * setDataRedactor((value) => {
22
+ * if (value && typeof value === 'object' && 'passwordHash' in value) {
23
+ * const { passwordHash, ...safe } = value as any;
24
+ * return safe;
25
+ * }
26
+ * return value;
27
+ * });
28
+ */
29
+ type DataRedactor = (value: unknown, id: string) => unknown;
30
+ export declare function setDataRedactor(fn: DataRedactor | null): void;
31
+ /**
32
+ * Serialize the collected resource data into an inline script the client reads
33
+ * before hydrating (seeds resources so they don't refetch). Empty when no data.
34
+ * Each value passes through the redactor (if set) first.
35
+ */
36
+ export declare function serializeResourceData(ctx: RequestContext): string;
37
+ /** Ref-count of in-flight async renders, to scope eager stale-effect scheduling. */
38
+ /**
39
+ * Async SSR: render, then await every resource fetch the render kicked off,
40
+ * letting their settle() re-run the render effects in place, until the tree is
41
+ * stable; then serialize. Resolves Suspense boundaries to their content.
42
+ */
43
+ export declare function renderToStringAsync(app: () => any, options?: RenderOptions): Promise<string>;
44
+ export {};
45
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../../src/lib/dom/server/render.ts"],"names":[],"mappings":"AAiCA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,YAAY,EAClB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAmBlF;AAqBD;;;;;;;;;;;;;;GAcG;AACH,KAAK,YAAY,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;AAE5D,wBAAgB,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAE7D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,cAAc,GAAG,MAAM,CAsBjE;AAKD,oFAAoF;AAEpF;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,GAAG,EACd,OAAO,GAAE,aAAkB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAyDjB"}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Server rendering — run an imperative app() under server mode and serialize the
3
+ * resulting node tree to HTML. `renderToString` is synchronous render-once (for
4
+ * loader-free apps); `renderToStringAsync` awaits in-flight resources/Suspense
5
+ * before serializing. Both wrap the render in createRoot so effects/owner are
6
+ * disposed afterwards.
7
+ */
8
+ import { createRoot } from '@fluixi/reactive/signal';
9
+ import { setResourceTracker, setResourceIdSource, setResourceDataSink, getOwner, runWithOwner, flush, } from '@fluixi/reactive/signal';
10
+ import { isServer, setServerMode, registerServerNodes } from './host.js';
11
+ import { ServerElement, ServerText, ServerComment } from './nodes.js';
12
+ import { serializeNode } from './serialize.js';
13
+ // Register the node classes with the runtime seam on first render. Done from here
14
+ // (not the client barrel) so the classes only load on the server.
15
+ let _nodesRegistered = false;
16
+ function ensureServerNodes() {
17
+ if (_nodesRegistered)
18
+ return;
19
+ _nodesRegistered = true;
20
+ registerServerNodes({
21
+ createElement: (tag, isSVG) => new ServerElement(tag, isSVG),
22
+ createText: (value) => new ServerText(value),
23
+ createComment: (value) => new ServerComment(value),
24
+ });
25
+ }
26
+ import { escapeFxJson, FX_DATA_ID } from './fx-data.js';
27
+ import { createRequestContext, runWithRequestContext, getRequestContext, } from './request-context.js';
28
+ export function renderToString(app, options = {}) {
29
+ const prev = isServer;
30
+ ensureServerNodes();
31
+ setServerMode(true);
32
+ const ctx = createRequestContext(options.event);
33
+ try {
34
+ return runWithRequestContext(ctx, () => createRoot((dispose) => {
35
+ try {
36
+ const tree = typeof app === 'function' ? app() : app;
37
+ return serializeNode(tree);
38
+ }
39
+ finally {
40
+ dispose();
41
+ }
42
+ }));
43
+ }
44
+ finally {
45
+ setServerMode(prev);
46
+ }
47
+ }
48
+ // Install the SSR resource hooks once (never unset). They route to the *current*
49
+ // request context — so concurrent async renders stay isolated (with an
50
+ // AsyncLocalStorage store injected) instead of a per-call setter clobbering a
51
+ // sibling render's sink.
52
+ let _hooksInstalled = false;
53
+ function ensureResourceHooks() {
54
+ if (_hooksInstalled)
55
+ return;
56
+ _hooksInstalled = true;
57
+ setResourceTracker((p) => {
58
+ getRequestContext()?.pending.add(p);
59
+ });
60
+ // Stable id (creation order) the client matches during hydration.
61
+ setResourceIdSource(() => getRequestContext()?.nextResourceId() ?? '');
62
+ // Resolved values are collected for serialization into the page.
63
+ setResourceDataSink((id, value) => {
64
+ if (id)
65
+ getRequestContext()?.data.set(id, value);
66
+ });
67
+ }
68
+ let _redactor = null;
69
+ export function setDataRedactor(fn) {
70
+ _redactor = fn;
71
+ }
72
+ /**
73
+ * Serialize the collected resource data into an inline script the client reads
74
+ * before hydrating (seeds resources so they don't refetch). Empty when no data.
75
+ * Each value passes through the redactor (if set) first.
76
+ */
77
+ export function serializeResourceData(ctx) {
78
+ if (ctx.data.size === 0)
79
+ return '';
80
+ const obj = {};
81
+ for (const [k, v] of ctx.data) {
82
+ let out = v;
83
+ if (_redactor) {
84
+ // A throwing redactor must not crash the render or leak — omit on error.
85
+ try {
86
+ out = _redactor(v, k);
87
+ }
88
+ catch {
89
+ continue;
90
+ }
91
+ }
92
+ if (out === undefined)
93
+ continue; // redacted → omit (client refetches)
94
+ obj[k] = out;
95
+ }
96
+ if (Object.keys(obj).length === 0)
97
+ return '';
98
+ // Non-executable JSON block (CSP-friendly; never runs as JS), escaped with the standard
99
+ // SSR-JSON set so no value can break out of the <script>. The client reads it via
100
+ // getServerData() → JSON.parse(textContent).
101
+ const json = escapeFxJson(JSON.stringify(obj));
102
+ return `<script type="application/json" id="${FX_DATA_ID}">${json}</script>`;
103
+ }
104
+ /** Bound on resolve→refetch waterfalls so a buggy loader can't spin forever. */
105
+ const MAX_ASYNC_ROUNDS = 50;
106
+ /** Ref-count of in-flight async renders, to scope eager stale-effect scheduling. */
107
+ /**
108
+ * Async SSR: render, then await every resource fetch the render kicked off,
109
+ * letting their settle() re-run the render effects in place, until the tree is
110
+ * stable; then serialize. Resolves Suspense boundaries to their content.
111
+ */
112
+ export async function renderToStringAsync(app, options = {}) {
113
+ const prev = isServer;
114
+ ensureServerNodes();
115
+ setServerMode(true);
116
+ ensureResourceHooks();
117
+ const ctx = createRequestContext(options.event);
118
+ try {
119
+ return await runWithRequestContext(ctx, async () => {
120
+ // Resolve request-scoped state (region/locale/session) BEFORE the app renders,
121
+ // inside the request context so it can read the event and write into ctx.data
122
+ // for client transfer. Runs once per request. Guarded so the no-preload path
123
+ // keeps its original synchronous flow into createRoot (an extra `await undefined`
124
+ // tick here reorders the lazy/Suspense drain and breaks hydration).
125
+ if (options.preload)
126
+ await options.preload(options.event);
127
+ let tree;
128
+ let rootOwner = null;
129
+ let dispose = () => { };
130
+ createRoot((d) => {
131
+ dispose = d;
132
+ rootOwner = getOwner();
133
+ tree = typeof app === 'function' ? app() : app;
134
+ });
135
+ // Serialize INSIDE the render root's owner. The probe/serialize below force
136
+ // lazy subtrees to render, which can CREATE resources; running with the root as
137
+ // owner makes those resources owned by the root, so dispose() cancels them. If
138
+ // we serialized with Owner=null (the default after createRoot returns), a
139
+ // resource created during serialize would be unowned → never cancelled → it
140
+ // settles AFTER the response is serialized and the root disposed, re-running an
141
+ // effect on a torn-down/detached tree (the storefront's "page shows, then errors
142
+ // ~2-3s later" crash). Wrapped because a suspended read may throw its promise.
143
+ const probe = () => {
144
+ try {
145
+ runWithOwner(rootOwner, () => serializeNode(tree));
146
+ }
147
+ catch { /* suspended read — resource self-registered */ }
148
+ };
149
+ // Force the (lazily-built) component tree to evaluate so every resource/lazy
150
+ // load is created and self-registers in ctx.pending BEFORE we drain. A tree
151
+ // behind deferred memos (e.g. a router <Outlet/>) renders NOTHING until serialize.
152
+ probe();
153
+ let rounds = 0;
154
+ while (ctx.pending.size > 0 && rounds++ < MAX_ASYNC_ROUNDS) {
155
+ const batch = Array.from(ctx.pending);
156
+ ctx.pending.clear();
157
+ await Promise.allSettled(batch);
158
+ // resource settle() queues the dependent render effects — run them so
159
+ // the tree reflects resolved data (and any waterfall fetch registers).
160
+ await flush();
161
+ // Re-probe: a freshly-resolved lazy/route reveals its OWN resources only
162
+ // when its subtree is evaluated — force that so they register for the next round.
163
+ probe();
164
+ }
165
+ // Serialize the resolved resource data so the client seeds it (no refetch).
166
+ const html = runWithOwner(rootOwner, () => serializeNode(tree)) + serializeResourceData(ctx);
167
+ dispose();
168
+ return html;
169
+ });
170
+ }
171
+ finally {
172
+ setServerMode(prev);
173
+ }
174
+ }
@@ -0,0 +1 @@
1
+ import{createRoot as H}from"@fluixi/reactive/signal";import{setResourceTracker as Z,setResourceIdSource as ee,setResourceDataSink as te,getOwner as ne,runWithOwner as I,flush as re}from"@fluixi/reactive/signal";var p=typeof document>"u";function l(n){p=n}var A=Symbol.for("fluixi.server-node"),z=null;function q(n){z=n}var S=1,T=3,w=8,k;k=A;var c=class{constructor(){this[k]=!0;this.parentNode=null;this.childNodes=[]}get firstChild(){return this.childNodes[0]??null}get lastChild(){return this.childNodes[this.childNodes.length-1]??null}get nextSibling(){let t=this.parentNode;if(!t)return null;let e=t.childNodes.indexOf(this);return e>=0?t.childNodes[e+1]??null:null}get previousSibling(){let t=this.parentNode;if(!t)return null;let e=t.childNodes.indexOf(this);return e>0?t.childNodes[e-1]??null:null}get parentElement(){return this.parentNode}appendChild(t){return t.parentNode&&t.parentNode.removeChild(t),t.parentNode=this,this.childNodes.push(t),t}insertBefore(t,e){if(e==null)return this.appendChild(t);t.parentNode&&t.parentNode.removeChild(t);let r=this.childNodes.indexOf(e);return t.parentNode=this,r<0?this.childNodes.push(t):this.childNodes.splice(r,0,t),t}removeChild(t){let e=this.childNodes.indexOf(t);return e>=0&&this.childNodes.splice(e,1),t.parentNode=null,t}replaceChild(t,e){let r=this.childNodes.indexOf(e);return r>=0&&(t.parentNode&&t.parentNode.removeChild(t),t.parentNode=this,this.childNodes[r]=t,e.parentNode=null),e}addEventListener(){}removeEventListener(){}},d=class n extends c{constructor(e){super();this.nodeType=T;this.data=e}get nodeValue(){return this.data}set nodeValue(e){this.data=e==null?"":String(e)}get textContent(){return this.data}set textContent(e){this.data=e==null?"":String(e)}cloneNode(){return new n(this.data)}},f=class n extends c{constructor(e){super();this.nodeType=w;this.data=e}get nodeValue(){return this.data}set nodeValue(e){this.data=e==null?"":String(e)}cloneNode(){return new n(this.data)}},x=class{constructor(){this.cssText=""}setProperty(t,e){this[t]=e}removeProperty(t){delete this[t]}},y=class{constructor(t){this.el=t}list(){let t=this.el.getAttribute("class");return t?t.split(/\s+/).filter(Boolean):[]}write(t){t.length?this.el.setAttribute("class",t.join(" ")):this.el.removeAttribute("class")}add(...t){let e=this.list();for(let r of t)e.includes(r)||e.push(r);this.write(e)}remove(...t){this.write(this.list().filter(e=>!t.includes(e)))}contains(t){return this.list().includes(t)}toggle(t,e){let r=this.contains(t),s=e===void 0?!r:e;return s?this.add(t):this.remove(t),s}},g=class n extends c{constructor(e,r=!1){super();this.nodeType=S;this.attributes=new Map;this.style=new x;this.classList=new y(this);this.rawHTML=null;this.localName=e.toLowerCase(),this.tagName=r?e:e.toUpperCase(),this.isSVG=r,this.namespaceURI=r?"http://www.w3.org/2000/svg":null}setAttribute(e,r){this.attributes.set(e,String(r))}removeAttribute(e){this.attributes.delete(e)}getAttribute(e){return this.attributes.has(e)?this.attributes.get(e):null}hasAttribute(e){return this.attributes.has(e)}get id(){return this.getAttribute("id")??""}set id(e){e==null?this.removeAttribute("id"):this.setAttribute("id",e)}get className(){return this.getAttribute("class")??""}set className(e){e==null?this.removeAttribute("class"):this.setAttribute("class",e)}get htmlFor(){return this.getAttribute("for")??""}set htmlFor(e){e==null?this.removeAttribute("for"):this.setAttribute("for",e)}set value(e){e==null?this.removeAttribute("value"):this.setAttribute("value",String(e))}set checked(e){e?this.setAttribute("checked",""):this.removeAttribute("checked")}set selected(e){e?this.setAttribute("selected",""):this.removeAttribute("selected")}set indeterminate(e){}get textContent(){return this.childNodes.map(e=>e.textContent??"").join("")}set textContent(e){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=null,e!=null&&e!==""&&this.appendChild(new d(String(e)))}set innerText(e){this.textContent=e}set innerHTML(e){for(let r of this.childNodes)r.parentNode=null;this.childNodes=[],this.rawHTML=e==null?"":String(e)}cloneNode(e=!1){let r=new n(this.localName,this.isSVG);if(r.attributes=new Map(this.attributes),r.style.cssText=this.style.cssText,r.rawHTML=this.rawHTML,e)for(let s of this.childNodes)r.appendChild(s.cloneNode(!0));return r}};var P=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]),D=/&/g,U=/</g,W=/>/g,J=/"/g;function R(n){return n.replace(D,"&amp;").replace(U,"&lt;").replace(W,"&gt;")}function _(n){return n.replace(D,"&amp;").replace(J,"&quot;")}function B(n){return n.startsWith("--")?n:n.replace(/[A-Z]/g,t=>"-"+t.toLowerCase())}function G(n){if(!n)return"";let t=[];n.cssText&&t.push(n.cssText.trim().replace(/;\s*$/,""));for(let e of Object.keys(n)){if(e==="cssText")continue;let r=n[e];r==null||r===""||t.push(`${B(e)}: ${r}`)}return t.join("; ")}function K(n){let t="",e=G(n.style);for(let[r,s]of n.attributes)r==="style"&&e||(t+=` ${r}="${_(s)}"`);if(e){let r=n.attributes.get("style"),s=r?`${r.replace(/;\s*$/,"")}; ${e}`:e;t+=` style="${_(s)}"`}return t}function u(n){if(n==null||n===!1||n===!0)return"";if(typeof n=="string")return R(n);if(typeof n=="number")return R(String(n));if(typeof n=="function")return u(n());if(Array.isArray(n))return n.map(u).join("");switch(n.nodeType){case T:return R(n.data??"");case w:return`<!--${n.data??""}-->`;case S:{let t=n.localName,e=`<${t}${K(n)}>`;if(P.has(t))return e;let r=n.rawHTML!=null?n.rawHTML:(n.childNodes??[]).map(u).join("");return`${e}${r}</${t}>`}}return Array.isArray(n.childNodes)?n.childNodes.map(u).join(""):""}var M="__FX_DATA__";function O(n){return n.replace(/[<>&\u2028\u2029]/g,t=>"\\u"+t.charCodeAt(0).toString(16).padStart(4,"0"))}var h,Q={getStore:()=>h,run(n,t){let e=h;h=n;try{return t()}finally{h=e}}},$=Q;function b(n={}){n.locals||(n.locals=n.request?Y(n.request):{});let t=0,e=0,r=null,s=new Map,i=new Map;return{event:n,routeData:new Map,matchedRoute:new Map,nextId:()=>`s${t++}`,nextResourceId:()=>{if(r===null)return`r${e++}`;let o=s.get(r)??0;return s.set(r,o+1),`${r}:r${o}`},nextIslandNamespace:o=>{let a=i.get(o)??0;return i.set(o,a+1),`${o}#${a}`},withResourceScope(o,a){let N=r;r=o;try{return a()}finally{r=N}},pending:new Set,data:new Map}}function E(n,t){return $.run(n,t)}function m(){return $.getStore()}var L=new WeakMap;function Y(n){let t=L.get(n);return t||(t={},L.set(n,t)),t}var j=!1;function V(){j||(j=!0,q({createElement:(n,t)=>new g(n,t),createText:n=>new d(n),createComment:n=>new f(n)}))}function Te(n,t={}){let e=p;V(),l(!0);let r=b(t.event);try{return E(r,()=>H(s=>{try{let i=typeof n=="function"?n():n;return u(i)}finally{s()}}))}finally{l(e)}}var F=!1;function se(){F||(F=!0,Z(n=>{m()?.pending.add(n)}),ee(()=>m()?.nextResourceId()??""),te((n,t)=>{n&&m()?.data.set(n,t)}))}var C=null;function we(n){C=n}function ie(n){if(n.data.size===0)return"";let t={};for(let[r,s]of n.data){let i=s;if(C)try{i=C(s,r)}catch{continue}i!==void 0&&(t[r]=i)}if(Object.keys(t).length===0)return"";let e=O(JSON.stringify(t));return`<script type="application/json" id="${M}">${e}<\/script>`}var oe=50;async function Re(n,t={}){let e=p;V(),l(!0),se();let r=b(t.event);try{return await E(r,async()=>{t.preload&&await t.preload(t.event);let s,i=null,o=()=>{};H(v=>{o=v,i=ne(),s=typeof n=="function"?n():n});let a=()=>{try{I(i,()=>u(s))}catch{}};a();let N=0;for(;r.pending.size>0&&N++<oe;){let v=Array.from(r.pending);r.pending.clear(),await Promise.allSettled(v),await re(),a()}let X=I(i,()=>u(s))+ie(r);return o(),X})}finally{l(e)}}export{Te as renderToString,Re as renderToStringAsync,ie as serializeResourceData,we as setDataRedactor};
@@ -0,0 +1 @@
1
+ "use strict";var a=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var w=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var k=(e,t)=>{for(var r in t)a(e,r,{get:t[r],enumerable:!0})},C=(e,t,r,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of w(t))!S.call(e,s)&&s!==r&&a(e,s,{get:()=>t[s],enumerable:!(n=f(t,s))||n.enumerable});return e};var M=e=>C(a({},"__esModule",{value:!0}),e);var b={};k(b,{createRequestContext:()=>L,getLocals:()=>y,getRequestContext:()=>q,getRequestEvent:()=>x,getRequestLocals:()=>g,runWithRequestContext:()=>v,setRequestStore:()=>T});module.exports=M(b);var c,p={getStore:()=>c,run(e,t){let r=c;c=e;try{return t()}finally{c=r}}},i=p;function T(e){i=e??p}function L(e={}){e.locals||(e.locals=e.request?g(e.request):{});let t=0,r=0,n=null,s=new Map,l=new Map;return{event:e,routeData:new Map,matchedRoute:new Map,nextId:()=>`s${t++}`,nextResourceId:()=>{if(n===null)return`r${r++}`;let o=s.get(n)??0;return s.set(n,o+1),`${n}:r${o}`},nextIslandNamespace:o=>{let u=l.get(o)??0;return l.set(o,u+1),`${o}#${u}`},withResourceScope(o,u){let d=n;n=o;try{return u()}finally{n=d}},pending:new Set,data:new Map}}function v(e,t){return i.run(e,t)}function q(){return i.getStore()}function x(){return q()?.event}function y(){let e=x();return e?(e.locals||(e.locals={}),e.locals):{}}var R=new WeakMap;function g(e){let t=R.get(e);return t||(t={},R.set(e,t)),t}