what-server 0.5.4 → 0.5.5

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.
package/README.md CHANGED
@@ -129,7 +129,7 @@ const submitForm = formAction(async (formData) => {
129
129
  ## Links
130
130
 
131
131
  - [Documentation](https://whatfw.com)
132
- - [GitHub](https://github.com/zvndev/what-fw)
132
+ - [GitHub](https://github.com/CelsianJs/whatfw)
133
133
 
134
134
  ## License
135
135
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "what-server",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "What Framework - SSR, islands architecture, static generation",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -29,17 +29,17 @@
29
29
  "server-rendering",
30
30
  "what-framework"
31
31
  ],
32
- "author": "",
32
+ "author": "ZVN DEV (https://zvndev.com)",
33
33
  "license": "MIT",
34
34
  "peerDependencies": {
35
35
  "what-core": "^0.5.3"
36
36
  },
37
37
  "repository": {
38
38
  "type": "git",
39
- "url": "https://github.com/zvndev/what-fw"
39
+ "url": "https://github.com/CelsianJs/whatfw"
40
40
  },
41
41
  "bugs": {
42
- "url": "https://github.com/zvndev/what-fw/issues"
42
+ "url": "https://github.com/CelsianJs/whatfw/issues"
43
43
  },
44
44
  "homepage": "https://whatfw.com"
45
45
  }
package/src/actions.js CHANGED
@@ -17,7 +17,6 @@ import { signal, batch } from 'what-core';
17
17
 
18
18
  // Registry of server actions
19
19
  const actionRegistry = new Map();
20
- let actionIdCounter = 0;
21
20
 
22
21
  // --- CSRF Protection ---
23
22
  // Server generates a token per session; client sends it with every action request.
@@ -170,11 +169,15 @@ export function formAction(actionFn, options = {}) {
170
169
  formData = formDataOrEvent;
171
170
  }
172
171
 
173
- // Convert FormData to plain object
172
+ // Convert FormData to plain object, preserving File instances
174
173
  const data = {};
174
+ let hasFiles = false;
175
175
  for (const [key, value] of formData.entries()) {
176
+ if (typeof File !== 'undefined' && value instanceof File) {
177
+ hasFiles = true;
178
+ }
176
179
  if (data[key]) {
177
- // Handle multiple values (e.g., checkboxes)
180
+ // Handle multiple values (e.g., checkboxes, multi-file inputs)
178
181
  if (Array.isArray(data[key])) {
179
182
  data[key].push(value);
180
183
  } else {
@@ -186,7 +189,11 @@ export function formAction(actionFn, options = {}) {
186
189
  }
187
190
 
188
191
  try {
189
- const result = await actionFn(data);
192
+ // If form contains files, pass the raw FormData as second arg
193
+ // so the action handler can access files directly
194
+ const result = hasFiles
195
+ ? await actionFn(data, formData)
196
+ : await actionFn(data);
190
197
  if (onSuccess) onSuccess(result, form);
191
198
  if (resetOnSuccess && form) form.reset();
192
199
  return result;
package/src/index.js CHANGED
@@ -15,6 +15,23 @@ export function renderToString(vnode) {
15
15
  return escapeHtml(String(vnode));
16
16
  }
17
17
 
18
+ // Signal — unwrap by calling it
19
+ if (typeof vnode === 'function' && vnode._signal) {
20
+ return renderToString(vnode());
21
+ }
22
+
23
+ // Reactive function child — call to get value
24
+ if (typeof vnode === 'function') {
25
+ try {
26
+ return renderToString(vnode());
27
+ } catch (e) {
28
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
29
+ console.warn('[what-server] Error rendering reactive function in SSR:', e.message);
30
+ }
31
+ return '';
32
+ }
33
+ }
34
+
18
35
  // Array
19
36
  if (Array.isArray(vnode)) {
20
37
  return vnode.map(renderToString).join('');
@@ -52,6 +69,24 @@ export async function* renderToStream(vnode) {
52
69
  return;
53
70
  }
54
71
 
72
+ // Signal — unwrap by calling it
73
+ if (typeof vnode === 'function' && vnode._signal) {
74
+ yield* renderToStream(vnode());
75
+ return;
76
+ }
77
+
78
+ // Reactive function child — call to get value
79
+ if (typeof vnode === 'function') {
80
+ try {
81
+ yield* renderToStream(vnode());
82
+ } catch (e) {
83
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
84
+ console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);
85
+ }
86
+ }
87
+ return;
88
+ }
89
+
55
90
  if (Array.isArray(vnode)) {
56
91
  for (const child of vnode) {
57
92
  yield* renderToStream(child);
@@ -60,10 +95,17 @@ export async function* renderToStream(vnode) {
60
95
  }
61
96
 
62
97
  if (typeof vnode.tag === 'function') {
63
- const result = vnode.tag({ ...vnode.props, children: vnode.children });
64
- // Support async components
65
- const resolved = result instanceof Promise ? await result : result;
66
- yield* renderToStream(resolved);
98
+ try {
99
+ const result = vnode.tag({ ...vnode.props, children: vnode.children });
100
+ // Support async components
101
+ const resolved = result instanceof Promise ? await result : result;
102
+ yield* renderToStream(resolved);
103
+ } catch (e) {
104
+ if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {
105
+ console.warn('[what-server] Error rendering component in stream SSR:', e.message);
106
+ }
107
+ yield `<!-- SSR Error: ${escapeHtml(e.message || 'Component error')} -->`;
108
+ }
67
109
  return;
68
110
  }
69
111
 
@@ -181,7 +223,12 @@ function renderAttrs(props) {
181
223
  .join(';');
182
224
  out += ` style="${escapeHtml(css)}"`;
183
225
  } else if (val === true) {
184
- out += ` ${key}`;
226
+ // ARIA attributes require explicit ="true", HTML boolean attrs can be bare
227
+ if (key.startsWith('aria-') || key === 'role') {
228
+ out += ` ${key}="true"`;
229
+ } else {
230
+ out += ` ${key}`;
231
+ }
185
232
  } else {
186
233
  out += ` ${key}="${escapeHtml(String(val))}"`;
187
234
  }
@@ -199,6 +246,7 @@ function escapeHtml(str) {
199
246
  }
200
247
 
201
248
  function camelToKebab(str) {
249
+ if (str.startsWith('--')) return str; // CSS custom properties (variables) — leave unchanged
202
250
  return str.replace(/([A-Z])/g, '-$1').toLowerCase();
203
251
  }
204
252
 
@@ -219,4 +267,7 @@ export {
219
267
  invalidatePath,
220
268
  handleActionRequest,
221
269
  getRegisteredActions,
270
+ generateCsrfToken,
271
+ validateCsrfToken,
272
+ csrfMetaTag,
222
273
  } from './actions.js';