what-server 0.6.6 → 0.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.
- package/dist/index.js +57 -12
- package/dist/index.js.map +2 -2
- package/dist/index.min.js +10 -10
- package/dist/index.min.js.map +3 -3
- package/package.json +1 -1
- package/src/index.js +65 -12
package/dist/index.js
CHANGED
|
@@ -412,7 +412,7 @@ function _renderHydratable(vnode) {
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
if (Array.isArray(vnode)) {
|
|
415
|
-
return `<!--[]-->${vnode
|
|
415
|
+
return `<!--[]-->${renderChildrenToString(vnode, _renderHydratable)}<!--/[]-->`;
|
|
416
416
|
}
|
|
417
417
|
if (typeof vnode.tag === "function") {
|
|
418
418
|
const hkId = nextHydrationId();
|
|
@@ -435,7 +435,7 @@ function _renderHydratable(vnode) {
|
|
|
435
435
|
const open = `<${tag}${attrs}>`;
|
|
436
436
|
if (VOID_ELEMENTS.has(tag)) return open;
|
|
437
437
|
const rawInner = _resolveInnerHTML(props);
|
|
438
|
-
const inner = rawInner != null ? String(rawInner) : children
|
|
438
|
+
const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, _renderHydratable);
|
|
439
439
|
return `${open}${inner}</${tag}>`;
|
|
440
440
|
}
|
|
441
441
|
function injectHydrationKey(html, hkId) {
|
|
@@ -468,7 +468,7 @@ function renderToString(vnode) {
|
|
|
468
468
|
}
|
|
469
469
|
}
|
|
470
470
|
if (Array.isArray(vnode)) {
|
|
471
|
-
return vnode
|
|
471
|
+
return renderChildrenToString(vnode, renderToString);
|
|
472
472
|
}
|
|
473
473
|
if (typeof vnode.tag === "function") {
|
|
474
474
|
const componentName = vnode.tag.displayName || vnode.tag.name || "Anonymous";
|
|
@@ -489,9 +489,16 @@ function renderToString(vnode) {
|
|
|
489
489
|
const open = `<${tag}${attrs}>`;
|
|
490
490
|
if (VOID_ELEMENTS.has(tag)) return open;
|
|
491
491
|
const rawInner = _resolveInnerHTML(props);
|
|
492
|
-
const inner = rawInner != null ? String(rawInner) : children
|
|
492
|
+
const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, renderToString);
|
|
493
493
|
return `${open}${inner}</${tag}>`;
|
|
494
494
|
}
|
|
495
|
+
function renderChildrenToString(children, renderChild) {
|
|
496
|
+
let html = "";
|
|
497
|
+
for (let i = 0; i < children.length; i++) {
|
|
498
|
+
html += renderChild(children[i]);
|
|
499
|
+
}
|
|
500
|
+
return html;
|
|
501
|
+
}
|
|
495
502
|
async function* renderToStream(vnode) {
|
|
496
503
|
if (vnode == null || vnode === false || vnode === true) return;
|
|
497
504
|
if (typeof vnode === "string" || typeof vnode === "number") {
|
|
@@ -629,16 +636,20 @@ function _resolveInnerHTML(props) {
|
|
|
629
636
|
}
|
|
630
637
|
function renderAttrs(props) {
|
|
631
638
|
let out = "";
|
|
632
|
-
|
|
639
|
+
const keys = Object.keys(props);
|
|
640
|
+
for (let i = 0; i < keys.length; i++) {
|
|
641
|
+
const key = keys[i];
|
|
642
|
+
const val = props[key];
|
|
633
643
|
if (key === "key" || key === "ref" || key === "children" || key === "dangerouslySetInnerHTML" || key === "innerHTML") continue;
|
|
634
644
|
if (key.startsWith("on") && key.length > 2) continue;
|
|
635
645
|
if (val === false || val == null) continue;
|
|
636
|
-
const
|
|
637
|
-
|
|
646
|
+
const attr = getHtmlAttributeMetadata(key);
|
|
647
|
+
const attrName = attr.name;
|
|
648
|
+
if (!attr.valid) {
|
|
638
649
|
if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);
|
|
639
650
|
continue;
|
|
640
651
|
}
|
|
641
|
-
if (!isSafeUrlAttributeValue(
|
|
652
|
+
if (!isSafeUrlAttributeValue(attr.urlKind, val)) {
|
|
642
653
|
if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute "${attrName}": ${val}`);
|
|
643
654
|
continue;
|
|
644
655
|
}
|
|
@@ -664,6 +675,25 @@ function getHtmlAttributeName(name) {
|
|
|
664
675
|
if (name === "htmlFor") return "for";
|
|
665
676
|
return name;
|
|
666
677
|
}
|
|
678
|
+
var ATTR_METADATA_CACHE = /* @__PURE__ */ new Map();
|
|
679
|
+
var ATTR_METADATA_CACHE_MAX = 2048;
|
|
680
|
+
var URL_ATTR_KIND_NONE = 0;
|
|
681
|
+
var URL_ATTR_KIND_SINGLE = 1;
|
|
682
|
+
var URL_ATTR_KIND_LIST = 2;
|
|
683
|
+
function getHtmlAttributeMetadata(key) {
|
|
684
|
+
const cached = ATTR_METADATA_CACHE.get(key);
|
|
685
|
+
if (cached) return cached;
|
|
686
|
+
const name = getHtmlAttributeName(key);
|
|
687
|
+
const valid = isValidHtmlAttributeName(name);
|
|
688
|
+
const meta = {
|
|
689
|
+
name,
|
|
690
|
+
valid,
|
|
691
|
+
urlKind: valid ? getUrlAttributeKind(name) : URL_ATTR_KIND_NONE
|
|
692
|
+
};
|
|
693
|
+
if (ATTR_METADATA_CACHE.size >= ATTR_METADATA_CACHE_MAX) ATTR_METADATA_CACHE.clear();
|
|
694
|
+
ATTR_METADATA_CACHE.set(key, meta);
|
|
695
|
+
return meta;
|
|
696
|
+
}
|
|
667
697
|
function isValidHtmlAttributeName(name) {
|
|
668
698
|
return /^[^\s"'>/=\x00-\x1f\x7f]+$/.test(name);
|
|
669
699
|
}
|
|
@@ -678,10 +708,25 @@ var URL_ATTRS = /* @__PURE__ */ new Set([
|
|
|
678
708
|
"xlink:href"
|
|
679
709
|
]);
|
|
680
710
|
var URL_LIST_ATTRS = /* @__PURE__ */ new Set(["srcset"]);
|
|
681
|
-
function
|
|
682
|
-
|
|
683
|
-
if (URL_LIST_ATTRS.has(
|
|
684
|
-
if (
|
|
711
|
+
function getUrlAttributeKind(name) {
|
|
712
|
+
if (URL_ATTRS.has(name)) return URL_ATTR_KIND_SINGLE;
|
|
713
|
+
if (URL_LIST_ATTRS.has(name)) return URL_ATTR_KIND_LIST;
|
|
714
|
+
if (!hasAsciiUppercase(name)) return URL_ATTR_KIND_NONE;
|
|
715
|
+
const normalizedName = name.toLowerCase();
|
|
716
|
+
if (URL_ATTRS.has(normalizedName)) return URL_ATTR_KIND_SINGLE;
|
|
717
|
+
if (URL_LIST_ATTRS.has(normalizedName)) return URL_ATTR_KIND_LIST;
|
|
718
|
+
return URL_ATTR_KIND_NONE;
|
|
719
|
+
}
|
|
720
|
+
function hasAsciiUppercase(str) {
|
|
721
|
+
for (let i = 0; i < str.length; i++) {
|
|
722
|
+
const code = str.charCodeAt(i);
|
|
723
|
+
if (code >= 65 && code <= 90) return true;
|
|
724
|
+
}
|
|
725
|
+
return false;
|
|
726
|
+
}
|
|
727
|
+
function isSafeUrlAttributeValue(urlKind, value) {
|
|
728
|
+
if (urlKind === URL_ATTR_KIND_LIST) return isSafeSrcsetValue(value);
|
|
729
|
+
if (urlKind === URL_ATTR_KIND_SINGLE) return isSafeUrlValue(value);
|
|
685
730
|
return true;
|
|
686
731
|
}
|
|
687
732
|
function isSafeUrlValue(value) {
|
package/dist/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js", "../src/actions.js"],
|
|
4
|
-
"sourcesContent": ["// What Framework - Server\n// SSR, static site generation, server components.\n// Zero-JS pages by default. Islands opt-in to client JS.\n\nimport { h } from 'what-core';\n\n// --- SSR Error Collection ---\n// Errors that occur during SSR are collected and serialized into the HTML output\n// so the client can pick them up during hydration and display/report them.\n\nlet _ssrErrors = [];\nconst MAX_SSR_ERRORS = 50;\n\nfunction _collectSSRError(error, context = {}) {\n const entry = {\n code: error.code || 'ERR_SSR_RENDER',\n message: error.message || String(error),\n component: context.component || null,\n timestamp: Date.now(),\n };\n // In dev mode, include extra detail for debugging\n if (_isDevMode) {\n entry.suggestion = error.suggestion || null;\n entry.stack = error.stack?.split('\\n').slice(0, 5).join('\\n') || null;\n }\n _ssrErrors.push(entry);\n if (_ssrErrors.length > MAX_SSR_ERRORS) _ssrErrors.shift();\n}\n\nfunction _resetSSRErrors() {\n _ssrErrors = [];\n}\n\n/**\n * Serialize collected SSR errors into a script tag for client hydration.\n * In dev mode: includes full error details (message, suggestion, stack).\n * In production: includes only error code and component name.\n */\nexport function serializeSSRErrors() {\n if (_ssrErrors.length === 0) return '';\n const payload = _isDevMode\n ? _ssrErrors\n : _ssrErrors.map(e => ({ code: e.code, component: e.component }));\n const json = JSON.stringify(payload).replace(/<\\//g, '<\\\\/'); // prevent XSS via </script>\n return `<script type=\"application/json\" data-what-ssr-errors>${json}</script>`;\n}\n\n/**\n * Read SSR errors from the DOM during client hydration.\n * Call this on the client side during hydration to pick up errors from SSR.\n * Returns an array of error objects, or empty array if none.\n */\nexport function hydrateSSRErrors() {\n if (typeof document === 'undefined') return [];\n const el = document.querySelector('script[data-what-ssr-errors]');\n if (!el) return [];\n try {\n const errors = JSON.parse(el.textContent);\n el.remove(); // clean up after reading\n return errors;\n } catch {\n return [];\n }\n}\n\n/**\n * Get collected SSR errors (for programmatic access before serialization).\n */\nexport function getSSRErrors() {\n return _ssrErrors.slice();\n}\n\n// --- Hydration ID Generator ---\nlet _hydrationIdCounter = 0;\n\nfunction resetHydrationId() {\n _hydrationIdCounter = 0;\n}\n\nfunction nextHydrationId() {\n return 'h' + (_hydrationIdCounter++);\n}\n\n// --- Render to Hydratable String ---\n// Renders with hydration markers (data-hk attributes, comment boundaries)\n// so the client can reuse the server-rendered DOM.\n\nexport function renderToHydratableString(vnode) {\n resetHydrationId();\n _resetSSRErrors();\n return _renderHydratable(vnode);\n}\n\nfunction _renderHydratable(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap\n if (typeof vnode === 'function' && vnode._signal) {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n }\n\n // Reactive function child \u2014 wrap in dynamic content markers\n if (typeof vnode === 'function') {\n try {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '<!--$--><!--/$-->';\n }\n }\n\n // Array \u2014 wrap in list markers\n if (Array.isArray(vnode)) {\n return `<!--[]-->${vnode.map(_renderHydratable).join('')}<!--/[]-->`;\n }\n\n // Component \u2014 add hydration key to root element\n if (typeof vnode.tag === 'function') {\n const hkId = nextHydrationId();\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n const html = _renderHydratable(result);\n // Inject data-hk into the first element tag if present\n return injectHydrationKey(html, hkId);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!--ssr-error:${escapeHtml(componentName)}-->`;\n }\n return `<!--ssr-error-->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : children.map(_renderHydratable).join('');\n return `${open}${inner}</${tag}>`;\n}\n\n// Inject data-hk=\"id\" into the first HTML opening tag\nfunction injectHydrationKey(html, hkId) {\n // Skip comment markers to find the first real element\n const match = html.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);\n if (match) {\n const prefix = match[1];\n const tagName = match[2];\n const insertAt = prefix.length + 1 + tagName.length; // after '<tagName'\n return html.slice(0, insertAt) + ` data-hk=\"${hkId}\"` + html.slice(insertAt);\n }\n return html;\n}\n\n// --- Render to String ---\n// Renders a VNode tree to an HTML string. Used for SSR and static gen.\n\nexport function renderToString(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n return renderToString(vnode());\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n return renderToString(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '';\n }\n }\n\n // Array\n if (Array.isArray(vnode)) {\n return vnode.map(renderToString).join('');\n }\n\n // Component\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n return renderToString(result);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message)} -->`;\n }\n return `<!-- SSR Error -->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : children.map(renderToString).join('');\n return `${open}${inner}</${tag}>`;\n}\n\n// --- Stream Render ---\n// Returns an async iterator for streaming SSR.\n\nexport async function* renderToStream(vnode) {\n if (vnode == null || vnode === false || vnode === true) return;\n\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n yield escapeHtml(String(vnode));\n return;\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n yield* renderToStream(vnode());\n return;\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n yield* renderToStream(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);\n }\n }\n return;\n }\n\n if (Array.isArray(vnode)) {\n for (const child of vnode) {\n yield* renderToStream(child);\n }\n return;\n }\n\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n // Support async components\n const resolved = result instanceof Promise ? await result : result;\n yield* renderToStream(resolved);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in stream SSR:`, e.message);\n }\n yield _isDevMode\n ? `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message || 'Component error')} -->`\n : `<!-- SSR Error -->`;\n }\n return;\n }\n\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n yield `<${tag}${attrs}>`;\n\n if (!VOID_ELEMENTS.has(tag)) {\n const rawInner = _resolveInnerHTML(props);\n if (rawInner != null) {\n yield String(rawInner);\n } else {\n for (const child of children) {\n yield* renderToStream(child);\n }\n }\n yield `</${tag}>`;\n }\n}\n\n// --- Static Site Generation ---\n\nexport function definePage(config) {\n return {\n // 'static' = pre-render at build time (default)\n // 'server' = render on each request\n // 'client' = render in browser (SPA)\n // 'hybrid' = static shell + islands\n mode: 'static',\n ...config,\n };\n}\n\n// Generate static HTML for a page\nexport function generateStaticPage(page, data = {}) {\n _resetSSRErrors();\n const vnode = page.component(data);\n const html = renderToString(vnode);\n const islands = page.islands || [];\n\n return wrapDocument({\n title: page.title || '',\n meta: page.meta || {},\n body: html,\n islands,\n scripts: page.mode === 'static' ? [] : page.scripts || [],\n styles: page.styles || [],\n mode: page.mode,\n ssrErrors: serializeSSRErrors(),\n });\n}\n\nfunction wrapDocument({ title, meta, body, islands, scripts, styles, mode, ssrErrors = '' }) {\n const metaTags = Object.entries(meta)\n .map(([name, content]) => `<meta name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\">`)\n .join('\\n ');\n\n const styleTags = styles\n .map(href => `<link rel=\"stylesheet\" href=\"${escapeHtml(href)}\">`)\n .join('\\n ');\n\n const islandScript = islands.length > 0 ? `\n <script type=\"module\">\n import { hydrateIslands } from '/@what/islands.js';\n hydrateIslands();\n </script>` : '';\n\n const scriptTags = scripts\n .map(src => `<script type=\"module\" src=\"${escapeHtml(src)}\"></script>`)\n .join('\\n ');\n\n const clientScript = mode === 'client' ? `\n <script type=\"module\" src=\"/@what/client.js\"></script>` : '';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n ${metaTags}\n <title>${escapeHtml(title)}</title>\n ${styleTags}\n </head>\n <body>\n <div id=\"app\">${body}</div>\n ${ssrErrors}\n ${islandScript}\n ${scriptTags}\n ${clientScript}\n </body>\n</html>`;\n}\n\n// --- Server Component ---\n// Renders on the server, sends HTML to client. No JS shipped.\n\nexport function server(Component) {\n Component._server = true;\n return Component;\n}\n\n// --- Helpers ---\n\n// Dev-mode flag for server\nconst _isDevMode = typeof process !== 'undefined'\n ? process.env?.NODE_ENV !== 'production'\n : true;\n\n/**\n * Resolve innerHTML / dangerouslySetInnerHTML from props.\n * Requires { __html: ... } wrapper. Plain string innerHTML is rejected (XSS prevention).\n */\nfunction _resolveInnerHTML(props) {\n if (!props) return null;\n\n // dangerouslySetInnerHTML always requires { __html }\n if (props.dangerouslySetInnerHTML) {\n return props.dangerouslySetInnerHTML.__html ?? null;\n }\n\n // innerHTML with { __html } wrapper \u2014 allowed\n if (props.innerHTML && typeof props.innerHTML === 'object' && '__html' in props.innerHTML) {\n return props.innerHTML.__html ?? null;\n }\n\n // innerHTML as plain string \u2014 reject with warning\n if (props.innerHTML != null && typeof props.innerHTML === 'string') {\n if (_isDevMode) {\n console.warn(\n '[what-server] innerHTML received a raw string. This is a security risk (XSS). ' +\n 'Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead.'\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction renderAttrs(props) {\n let out = '';\n for (const [key, val] of Object.entries(props)) {\n if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;\n if (key.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR\n if (val === false || val == null) continue;\n\n const attrName = getHtmlAttributeName(key);\n if (!isValidHtmlAttributeName(attrName)) {\n if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);\n continue;\n }\n if (!isSafeUrlAttributeValue(attrName, val)) {\n if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute \"${attrName}\": ${val}`);\n continue;\n }\n\n if (key === 'className' || key === 'class') {\n out += ` class=\"${escapeHtml(String(val))}\"`;\n } else if (key === 'style' && typeof val === 'object') {\n const css = Object.entries(val)\n .map(([p, v]) => `${camelToKebab(p)}:${v}`)\n .join(';');\n out += ` style=\"${escapeHtml(css)}\"`;\n } else if (val === true) {\n // ARIA attributes require explicit =\"true\", HTML boolean attrs can be bare\n if (attrName.startsWith('aria-') || attrName === 'role') {\n out += ` ${attrName}=\"true\"`;\n } else {\n out += ` ${attrName}`;\n }\n } else {\n out += ` ${attrName}=\"${escapeHtml(String(val))}\"`;\n }\n }\n\n return out;\n}\n\nfunction getHtmlAttributeName(name) {\n if (name === 'className') return 'class';\n if (name === 'htmlFor') return 'for';\n return name;\n}\n\nfunction isValidHtmlAttributeName(name) {\n return /^[^\\s\"'>/=\\x00-\\x1f\\x7f]+$/.test(name);\n}\n\nconst URL_ATTRS = new Set([\n 'href',\n 'src',\n 'action',\n 'formaction',\n 'poster',\n 'cite',\n 'background',\n 'xlink:href',\n]);\nconst URL_LIST_ATTRS = new Set(['srcset']);\n\nfunction isSafeUrlAttributeValue(name, value) {\n const normalizedName = String(name).toLowerCase();\n if (URL_LIST_ATTRS.has(normalizedName)) return isSafeSrcsetValue(value);\n if (URL_ATTRS.has(normalizedName)) return isSafeUrlValue(value);\n return true;\n}\n\nfunction isSafeUrlValue(value) {\n if (typeof value !== 'string') return true;\n const normalized = value.trim().replace(/[\\s\\x00-\\x1f\\x7f]/g, '').toLowerCase();\n return !(normalized.startsWith('javascript:') || normalized.startsWith('data:') || normalized.startsWith('vbscript:'));\n}\n\nfunction isSafeSrcsetValue(value) {\n if (typeof value !== 'string') return true;\n return value\n .split(',')\n .every(candidate => {\n const url = candidate.trim().split(/\\s+/, 1)[0] || '';\n return url === '' || isSafeUrlValue(url);\n });\n}\n\nfunction escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction camelToKebab(str) {\n if (str.startsWith('--')) return str; // CSS custom properties (variables) \u2014 leave unchanged\n return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// SSR error serialization is exported above:\n// serializeSSRErrors() \u2014 serialize collected errors to script tag\n// hydrateSSRErrors() \u2014 read errors from DOM during client hydration\n// getSSRErrors() \u2014 programmatic access to collected errors\n\n// Re-export server actions\nexport {\n action,\n formAction,\n useAction,\n useFormAction,\n useOptimistic,\n useMutation,\n onRevalidate,\n invalidatePath,\n handleActionRequest,\n getRegisteredActions,\n generateCsrfToken,\n validateCsrfToken,\n csrfMetaTag,\n} from './actions.js';\n", "// What Framework - Server Actions\n// Call server-side functions from client code seamlessly.\n// Similar to Next.js Server Actions / SolidStart server functions.\n//\n// Usage:\n// // Define on server\n// const saveUser = action(async (formData) => {\n// 'use server';\n// const user = await db.users.create(formData);\n// return { success: true, id: user.id };\n// });\n//\n// // Call from client\n// const result = await saveUser({ name: 'John' });\n\nimport { signal, batch } from 'what-core';\n\n// Registry of server actions\nconst actionRegistry = new Map();\n\n// --- CSRF Protection ---\n// Server generates a token per session; client sends it with every action request.\n// The token is injected into the page via a meta tag or embedded in the server response.\n\n// Client: read the CSRF token from the page meta tag or cookie\n// Re-reads on every call to handle token rotation\nfunction getCsrfToken() {\n if (typeof document !== 'undefined') {\n // Try meta tag first\n const meta = document.querySelector('meta[name=\"what-csrf-token\"]');\n if (meta) {\n return meta.getAttribute('content');\n }\n // Try cookie\n const match = document.cookie.match(/(?:^|;\\s*)what-csrf=([^;]+)/);\n if (match) {\n return decodeURIComponent(match[1]);\n }\n }\n return null;\n}\n\n// Server: generate a CSRF token (call this per session/request)\nexport function generateCsrfToken() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID \u2014 use crypto.getRandomValues\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, b => b.toString(16).padStart(2, '0')).join('');\n }\n // Last resort \u2014 should not be reached in modern environments\n throw new Error('[what] No secure random source available for CSRF token generation');\n}\n\n// Server: validate CSRF token from request header against session token\nexport function validateCsrfToken(requestToken, sessionToken) {\n if (!requestToken || !sessionToken) return false;\n // Constant-time comparison to prevent timing attacks\n if (requestToken.length !== sessionToken.length) return false;\n let result = 0;\n for (let i = 0; i < requestToken.length; i++) {\n result |= requestToken.charCodeAt(i) ^ sessionToken.charCodeAt(i);\n }\n return result === 0;\n}\n\n// Server: middleware helper to inject CSRF meta tag into HTML\nexport function csrfMetaTag(token) {\n // HTML-escape the token to prevent XSS if a non-standard value is used\n const escaped = String(token).replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n return `<meta name=\"what-csrf-token\" content=\"${escaped}\">`;\n}\n\n// --- Define a server action ---\n\nlet _actionCounter = 0;\n\nfunction generateActionId() {\n // Generate a deterministic ID \u2014 prefer crypto.getRandomValues, fall back to a\n // monotonic counter (never Math.random, which is not cryptographically safe and\n // produces predictable IDs in some runtimes).\n const rand = typeof crypto !== 'undefined' && crypto.getRandomValues\n ? Array.from(crypto.getRandomValues(new Uint8Array(6)), b => b.toString(16).padStart(2, '0')).join('')\n : `c${(++_actionCounter).toString(36)}_${Date.now().toString(36)}`;\n return `a_${rand}`;\n}\n\nexport function action(fn, options = {}) {\n const id = options.id || generateActionId();\n const { onError, onSuccess, revalidate } = options;\n\n // Server-side: register the action\n if (typeof window === 'undefined') {\n actionRegistry.set(id, { fn, options });\n }\n\n // Create the callable wrapper\n async function callAction(...args) {\n // Server-side: call directly\n if (typeof window === 'undefined') {\n return fn(...args);\n }\n\n // Client-side: call via fetch with timeout support\n const timeout = options.timeout || 30000; // Default 30s timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const csrfToken = getCsrfToken();\n const headers = {\n 'Content-Type': 'application/json',\n 'X-What-Action': id,\n };\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken;\n\n const response = await fetch('/__what_action', {\n method: 'POST',\n headers,\n credentials: 'same-origin',\n signal: controller.signal,\n body: JSON.stringify({ args }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Action failed' }));\n throw new Error(error.message || 'Action failed');\n }\n\n const result = await response.json();\n\n if (onSuccess) onSuccess(result);\n if (revalidate) {\n // Trigger revalidation of specified paths\n for (const path of revalidate) {\n invalidatePath(path);\n }\n }\n\n return result;\n } catch (error) {\n if (error.name === 'AbortError') {\n const timeoutError = new Error(`Action \"${id}\" timed out after ${timeout}ms`);\n timeoutError.code = 'TIMEOUT';\n if (onError) onError(timeoutError);\n throw timeoutError;\n }\n if (onError) onError(error);\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n callAction._actionId = id;\n callAction._isAction = true;\n\n return callAction;\n}\n\n// --- Form action helper ---\n// For forms that submit to server actions.\n\nexport function formAction(actionFn, options = {}) {\n const { onSuccess, onError, resetOnSuccess = true } = options;\n\n return async (formDataOrEvent) => {\n let formData;\n let form;\n\n if (formDataOrEvent instanceof Event) {\n formDataOrEvent.preventDefault();\n form = formDataOrEvent.target;\n formData = new FormData(form);\n } else {\n formData = formDataOrEvent;\n }\n\n // Convert FormData to plain object, preserving File instances\n const data = {};\n let hasFiles = false;\n for (const [key, value] of formData.entries()) {\n if (typeof File !== 'undefined' && value instanceof File) {\n hasFiles = true;\n }\n if (data[key]) {\n // Handle multiple values (e.g., checkboxes, multi-file inputs)\n if (Array.isArray(data[key])) {\n data[key].push(value);\n } else {\n data[key] = [data[key], value];\n }\n } else {\n data[key] = value;\n }\n }\n\n try {\n // If form contains files, pass the raw FormData as second arg\n // so the action handler can access files directly\n const result = hasFiles\n ? await actionFn(data, formData)\n : await actionFn(data);\n if (onSuccess) onSuccess(result, form);\n if (resetOnSuccess && form) form.reset();\n return result;\n } catch (error) {\n if (onError) onError(error, form);\n throw error;\n }\n };\n}\n\n// --- useAction hook ---\n// Returns action state and trigger function.\n\nexport function useAction(actionFn) {\n const isPending = signal(false);\n const error = signal(null);\n const data = signal(null);\n\n async function trigger(...args) {\n isPending.set(true);\n error.set(null);\n\n try {\n const result = await actionFn(...args);\n data.set(result);\n return result;\n } catch (e) {\n error.set(e);\n throw e;\n } finally {\n isPending.set(false);\n }\n }\n\n return {\n trigger,\n isPending: () => isPending(),\n error: () => error(),\n data: () => data(),\n reset: () => {\n error.set(null);\n data.set(null);\n },\n };\n}\n\n// --- useFormAction hook ---\n// Combines useAction with form handling.\n\nexport function useFormAction(actionFn, options = {}) {\n const { resetOnSuccess = true } = options;\n const formRef = { current: null };\n const actionState = useAction(formAction(actionFn, { resetOnSuccess }));\n\n function handleSubmit(e) {\n e.preventDefault();\n const formData = new FormData(e.target);\n formRef.current = e.target;\n return actionState.trigger(formData);\n }\n\n return {\n ...actionState,\n handleSubmit,\n formRef,\n };\n}\n\n// --- Optimistic updates ---\n\nexport function useOptimistic(initialValue, reducer) {\n const value = signal(initialValue);\n const pending = signal([]);\n const baseValue = signal(initialValue); // Track the confirmed server value\n\n function addOptimistic(action) {\n const optimisticValue = reducer(value.peek(), action);\n batch(() => {\n pending.set([...pending.peek(), action]);\n value.set(optimisticValue);\n });\n }\n\n function resolve(action, serverValue) {\n batch(() => {\n pending.set(pending.peek().filter(a => a !== action));\n if (serverValue !== undefined) {\n baseValue.set(serverValue);\n // Recompute optimistic state from new base + remaining pending actions\n let current = serverValue;\n for (const a of pending.peek()) {\n current = reducer(current, a);\n }\n value.set(current);\n }\n });\n }\n\n function rollback(action, realValue) {\n batch(() => {\n const newPending = pending.peek().filter(a => a !== action);\n pending.set(newPending);\n const base = realValue !== undefined ? realValue : baseValue.peek();\n baseValue.set(base);\n // Recompute from base + remaining pending actions\n let current = base;\n for (const a of newPending) {\n current = reducer(current, a);\n }\n value.set(current);\n });\n }\n\n // Auto-rollback helper: wraps an async action with automatic rollback on error\n async function withOptimistic(action, asyncFn) {\n addOptimistic(action);\n try {\n const result = await asyncFn();\n resolve(action, result);\n return result;\n } catch (e) {\n rollback(action);\n throw e;\n }\n }\n\n return {\n value: () => value(),\n isPending: () => pending().length > 0,\n addOptimistic,\n resolve,\n rollback,\n withOptimistic,\n set: (v) => { value.set(v); baseValue.set(v); },\n };\n}\n\n// --- Path revalidation ---\n\nconst revalidationCallbacks = new Map();\n\nexport function onRevalidate(path, callback) {\n if (!revalidationCallbacks.has(path)) {\n revalidationCallbacks.set(path, new Set());\n }\n revalidationCallbacks.get(path).add(callback);\n\n return () => {\n revalidationCallbacks.get(path)?.delete(callback);\n };\n}\n\nexport function invalidatePath(path) {\n const callbacks = revalidationCallbacks.get(path);\n if (callbacks) {\n for (const cb of callbacks) {\n try { cb(); } catch (e) { console.error('[what] Revalidation error:', e); }\n }\n }\n}\n\n// --- Server-side action handler ---\n// Add this to your server middleware.\n\nexport function handleActionRequest(req, actionId, args, options = {}) {\n const { csrfToken: sessionCsrfToken, skipCsrf = false } = options;\n\n // Validate CSRF token unless explicitly skipped\n if (!skipCsrf) {\n if (!sessionCsrfToken) {\n // Fail closed: no CSRF token configured means the developer forgot to set it up.\n // This prevents silent security vulnerabilities in production.\n return Promise.resolve({\n status: 500,\n body: {\n message: '[what] CSRF token not configured. ' +\n 'Pass { csrfToken: sessionToken } to handleActionRequest, ' +\n 'or { skipCsrf: true } to explicitly opt out.'\n }\n });\n }\n const requestCsrfToken = req?.headers?.['x-csrf-token'] || req?.headers?.['X-CSRF-Token'];\n if (!validateCsrfToken(requestCsrfToken, sessionCsrfToken)) {\n return Promise.resolve({ status: 403, body: { message: 'Invalid CSRF token' } });\n }\n }\n\n const action = actionRegistry.get(actionId);\n if (!action) {\n return Promise.resolve({ status: 404, body: { message: 'Action not found' } });\n }\n\n // Validate args is an array to prevent prototype pollution\n if (!Array.isArray(args)) {\n return Promise.resolve({ status: 400, body: { message: 'Invalid action arguments' } });\n }\n\n return action.fn(...args)\n .then(result => ({ status: 200, body: result }))\n .catch(error => {\n // Log the full error server-side, return generic message to client\n console.error(`[what] Action \"${actionId}\" error:`, error);\n return {\n status: 500,\n body: { message: 'Action failed' },\n };\n });\n}\n\n// --- Get all registered actions (for SSR/build) ---\n\nexport function getRegisteredActions() {\n return [...actionRegistry.keys()];\n}\n\n// --- Mutation helper ---\n// Like useSWR mutation but simpler.\n\nexport function useMutation(mutationFn, options = {}) {\n const { onSuccess, onError, onSettled } = options;\n\n const state = {\n isPending: signal(false),\n error: signal(null),\n data: signal(null),\n };\n\n async function mutate(...args) {\n state.isPending.set(true);\n state.error.set(null);\n\n try {\n const result = await mutationFn(...args);\n state.data.set(result);\n if (onSuccess) onSuccess(result, ...args);\n return result;\n } catch (error) {\n state.error.set(error);\n if (onError) onError(error, ...args);\n throw error;\n } finally {\n state.isPending.set(false);\n if (onSettled) onSettled(state.data.peek(), state.error.peek(), ...args);\n }\n }\n\n return {\n mutate,\n isPending: () => state.isPending(),\n error: () => state.error(),\n data: () => state.data(),\n reset: () => {\n state.error.set(null);\n state.data.set(null);\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["// What Framework - Server\n// SSR, static site generation, server components.\n// Zero-JS pages by default. Islands opt-in to client JS.\n\nimport { h } from 'what-core';\n\n// --- SSR Error Collection ---\n// Errors that occur during SSR are collected and serialized into the HTML output\n// so the client can pick them up during hydration and display/report them.\n\nlet _ssrErrors = [];\nconst MAX_SSR_ERRORS = 50;\n\nfunction _collectSSRError(error, context = {}) {\n const entry = {\n code: error.code || 'ERR_SSR_RENDER',\n message: error.message || String(error),\n component: context.component || null,\n timestamp: Date.now(),\n };\n // In dev mode, include extra detail for debugging\n if (_isDevMode) {\n entry.suggestion = error.suggestion || null;\n entry.stack = error.stack?.split('\\n').slice(0, 5).join('\\n') || null;\n }\n _ssrErrors.push(entry);\n if (_ssrErrors.length > MAX_SSR_ERRORS) _ssrErrors.shift();\n}\n\nfunction _resetSSRErrors() {\n _ssrErrors = [];\n}\n\n/**\n * Serialize collected SSR errors into a script tag for client hydration.\n * In dev mode: includes full error details (message, suggestion, stack).\n * In production: includes only error code and component name.\n */\nexport function serializeSSRErrors() {\n if (_ssrErrors.length === 0) return '';\n const payload = _isDevMode\n ? _ssrErrors\n : _ssrErrors.map(e => ({ code: e.code, component: e.component }));\n const json = JSON.stringify(payload).replace(/<\\//g, '<\\\\/'); // prevent XSS via </script>\n return `<script type=\"application/json\" data-what-ssr-errors>${json}</script>`;\n}\n\n/**\n * Read SSR errors from the DOM during client hydration.\n * Call this on the client side during hydration to pick up errors from SSR.\n * Returns an array of error objects, or empty array if none.\n */\nexport function hydrateSSRErrors() {\n if (typeof document === 'undefined') return [];\n const el = document.querySelector('script[data-what-ssr-errors]');\n if (!el) return [];\n try {\n const errors = JSON.parse(el.textContent);\n el.remove(); // clean up after reading\n return errors;\n } catch {\n return [];\n }\n}\n\n/**\n * Get collected SSR errors (for programmatic access before serialization).\n */\nexport function getSSRErrors() {\n return _ssrErrors.slice();\n}\n\n// --- Hydration ID Generator ---\nlet _hydrationIdCounter = 0;\n\nfunction resetHydrationId() {\n _hydrationIdCounter = 0;\n}\n\nfunction nextHydrationId() {\n return 'h' + (_hydrationIdCounter++);\n}\n\n// --- Render to Hydratable String ---\n// Renders with hydration markers (data-hk attributes, comment boundaries)\n// so the client can reuse the server-rendered DOM.\n\nexport function renderToHydratableString(vnode) {\n resetHydrationId();\n _resetSSRErrors();\n return _renderHydratable(vnode);\n}\n\nfunction _renderHydratable(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap\n if (typeof vnode === 'function' && vnode._signal) {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n }\n\n // Reactive function child \u2014 wrap in dynamic content markers\n if (typeof vnode === 'function') {\n try {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '<!--$--><!--/$-->';\n }\n }\n\n // Array \u2014 wrap in list markers\n if (Array.isArray(vnode)) {\n return `<!--[]-->${renderChildrenToString(vnode, _renderHydratable)}<!--/[]-->`;\n }\n\n // Component \u2014 add hydration key to root element\n if (typeof vnode.tag === 'function') {\n const hkId = nextHydrationId();\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n const html = _renderHydratable(result);\n // Inject data-hk into the first element tag if present\n return injectHydrationKey(html, hkId);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!--ssr-error:${escapeHtml(componentName)}-->`;\n }\n return `<!--ssr-error-->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, _renderHydratable);\n return `${open}${inner}</${tag}>`;\n}\n\n// Inject data-hk=\"id\" into the first HTML opening tag\nfunction injectHydrationKey(html, hkId) {\n // Skip comment markers to find the first real element\n const match = html.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);\n if (match) {\n const prefix = match[1];\n const tagName = match[2];\n const insertAt = prefix.length + 1 + tagName.length; // after '<tagName'\n return html.slice(0, insertAt) + ` data-hk=\"${hkId}\"` + html.slice(insertAt);\n }\n return html;\n}\n\n// --- Render to String ---\n// Renders a VNode tree to an HTML string. Used for SSR and static gen.\n\nexport function renderToString(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n return renderToString(vnode());\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n return renderToString(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '';\n }\n }\n\n // Array\n if (Array.isArray(vnode)) {\n return renderChildrenToString(vnode, renderToString);\n }\n\n // Component\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n return renderToString(result);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message)} -->`;\n }\n return `<!-- SSR Error -->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, renderToString);\n return `${open}${inner}</${tag}>`;\n}\n\nfunction renderChildrenToString(children, renderChild) {\n let html = '';\n for (let i = 0; i < children.length; i++) {\n html += renderChild(children[i]);\n }\n return html;\n}\n\n// --- Stream Render ---\n// Returns an async iterator for streaming SSR.\n\nexport async function* renderToStream(vnode) {\n if (vnode == null || vnode === false || vnode === true) return;\n\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n yield escapeHtml(String(vnode));\n return;\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n yield* renderToStream(vnode());\n return;\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n yield* renderToStream(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);\n }\n }\n return;\n }\n\n if (Array.isArray(vnode)) {\n for (const child of vnode) {\n yield* renderToStream(child);\n }\n return;\n }\n\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n // Support async components\n const resolved = result instanceof Promise ? await result : result;\n yield* renderToStream(resolved);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in stream SSR:`, e.message);\n }\n yield _isDevMode\n ? `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message || 'Component error')} -->`\n : `<!-- SSR Error -->`;\n }\n return;\n }\n\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n yield `<${tag}${attrs}>`;\n\n if (!VOID_ELEMENTS.has(tag)) {\n const rawInner = _resolveInnerHTML(props);\n if (rawInner != null) {\n yield String(rawInner);\n } else {\n for (const child of children) {\n yield* renderToStream(child);\n }\n }\n yield `</${tag}>`;\n }\n}\n\n// --- Static Site Generation ---\n\nexport function definePage(config) {\n return {\n // 'static' = pre-render at build time (default)\n // 'server' = render on each request\n // 'client' = render in browser (SPA)\n // 'hybrid' = static shell + islands\n mode: 'static',\n ...config,\n };\n}\n\n// Generate static HTML for a page\nexport function generateStaticPage(page, data = {}) {\n _resetSSRErrors();\n const vnode = page.component(data);\n const html = renderToString(vnode);\n const islands = page.islands || [];\n\n return wrapDocument({\n title: page.title || '',\n meta: page.meta || {},\n body: html,\n islands,\n scripts: page.mode === 'static' ? [] : page.scripts || [],\n styles: page.styles || [],\n mode: page.mode,\n ssrErrors: serializeSSRErrors(),\n });\n}\n\nfunction wrapDocument({ title, meta, body, islands, scripts, styles, mode, ssrErrors = '' }) {\n const metaTags = Object.entries(meta)\n .map(([name, content]) => `<meta name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\">`)\n .join('\\n ');\n\n const styleTags = styles\n .map(href => `<link rel=\"stylesheet\" href=\"${escapeHtml(href)}\">`)\n .join('\\n ');\n\n const islandScript = islands.length > 0 ? `\n <script type=\"module\">\n import { hydrateIslands } from '/@what/islands.js';\n hydrateIslands();\n </script>` : '';\n\n const scriptTags = scripts\n .map(src => `<script type=\"module\" src=\"${escapeHtml(src)}\"></script>`)\n .join('\\n ');\n\n const clientScript = mode === 'client' ? `\n <script type=\"module\" src=\"/@what/client.js\"></script>` : '';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n ${metaTags}\n <title>${escapeHtml(title)}</title>\n ${styleTags}\n </head>\n <body>\n <div id=\"app\">${body}</div>\n ${ssrErrors}\n ${islandScript}\n ${scriptTags}\n ${clientScript}\n </body>\n</html>`;\n}\n\n// --- Server Component ---\n// Renders on the server, sends HTML to client. No JS shipped.\n\nexport function server(Component) {\n Component._server = true;\n return Component;\n}\n\n// --- Helpers ---\n\n// Dev-mode flag for server\nconst _isDevMode = typeof process !== 'undefined'\n ? process.env?.NODE_ENV !== 'production'\n : true;\n\n/**\n * Resolve innerHTML / dangerouslySetInnerHTML from props.\n * Requires { __html: ... } wrapper. Plain string innerHTML is rejected (XSS prevention).\n */\nfunction _resolveInnerHTML(props) {\n if (!props) return null;\n\n // dangerouslySetInnerHTML always requires { __html }\n if (props.dangerouslySetInnerHTML) {\n return props.dangerouslySetInnerHTML.__html ?? null;\n }\n\n // innerHTML with { __html } wrapper \u2014 allowed\n if (props.innerHTML && typeof props.innerHTML === 'object' && '__html' in props.innerHTML) {\n return props.innerHTML.__html ?? null;\n }\n\n // innerHTML as plain string \u2014 reject with warning\n if (props.innerHTML != null && typeof props.innerHTML === 'string') {\n if (_isDevMode) {\n console.warn(\n '[what-server] innerHTML received a raw string. This is a security risk (XSS). ' +\n 'Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead.'\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction renderAttrs(props) {\n let out = '';\n const keys = Object.keys(props);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const val = props[key];\n if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;\n if (key.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR\n if (val === false || val == null) continue;\n\n const attr = getHtmlAttributeMetadata(key);\n const attrName = attr.name;\n if (!attr.valid) {\n if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);\n continue;\n }\n if (!isSafeUrlAttributeValue(attr.urlKind, val)) {\n if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute \"${attrName}\": ${val}`);\n continue;\n }\n\n if (key === 'className' || key === 'class') {\n out += ` class=\"${escapeHtml(String(val))}\"`;\n } else if (key === 'style' && typeof val === 'object') {\n const css = Object.entries(val)\n .map(([p, v]) => `${camelToKebab(p)}:${v}`)\n .join(';');\n out += ` style=\"${escapeHtml(css)}\"`;\n } else if (val === true) {\n // ARIA attributes require explicit =\"true\", HTML boolean attrs can be bare\n if (attrName.startsWith('aria-') || attrName === 'role') {\n out += ` ${attrName}=\"true\"`;\n } else {\n out += ` ${attrName}`;\n }\n } else {\n out += ` ${attrName}=\"${escapeHtml(String(val))}\"`;\n }\n }\n\n return out;\n}\n\nfunction getHtmlAttributeName(name) {\n if (name === 'className') return 'class';\n if (name === 'htmlFor') return 'for';\n return name;\n}\n\nconst ATTR_METADATA_CACHE = new Map();\nconst ATTR_METADATA_CACHE_MAX = 2048;\nconst URL_ATTR_KIND_NONE = 0;\nconst URL_ATTR_KIND_SINGLE = 1;\nconst URL_ATTR_KIND_LIST = 2;\n\nfunction getHtmlAttributeMetadata(key) {\n const cached = ATTR_METADATA_CACHE.get(key);\n if (cached) return cached;\n\n const name = getHtmlAttributeName(key);\n const valid = isValidHtmlAttributeName(name);\n const meta = {\n name,\n valid,\n urlKind: valid ? getUrlAttributeKind(name) : URL_ATTR_KIND_NONE,\n };\n\n if (ATTR_METADATA_CACHE.size >= ATTR_METADATA_CACHE_MAX) ATTR_METADATA_CACHE.clear();\n ATTR_METADATA_CACHE.set(key, meta);\n return meta;\n}\n\nfunction isValidHtmlAttributeName(name) {\n return /^[^\\s\"'>/=\\x00-\\x1f\\x7f]+$/.test(name);\n}\n\nconst URL_ATTRS = new Set([\n 'href',\n 'src',\n 'action',\n 'formaction',\n 'poster',\n 'cite',\n 'background',\n 'xlink:href',\n]);\nconst URL_LIST_ATTRS = new Set(['srcset']);\n\nfunction getUrlAttributeKind(name) {\n if (URL_ATTRS.has(name)) return URL_ATTR_KIND_SINGLE;\n if (URL_LIST_ATTRS.has(name)) return URL_ATTR_KIND_LIST;\n if (!hasAsciiUppercase(name)) return URL_ATTR_KIND_NONE;\n\n const normalizedName = name.toLowerCase();\n if (URL_ATTRS.has(normalizedName)) return URL_ATTR_KIND_SINGLE;\n if (URL_LIST_ATTRS.has(normalizedName)) return URL_ATTR_KIND_LIST;\n return URL_ATTR_KIND_NONE;\n}\n\nfunction hasAsciiUppercase(str) {\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code >= 65 && code <= 90) return true;\n }\n return false;\n}\n\nfunction isSafeUrlAttributeValue(urlKind, value) {\n if (urlKind === URL_ATTR_KIND_LIST) return isSafeSrcsetValue(value);\n if (urlKind === URL_ATTR_KIND_SINGLE) return isSafeUrlValue(value);\n return true;\n}\n\nfunction isSafeUrlValue(value) {\n if (typeof value !== 'string') return true;\n const normalized = value.trim().replace(/[\\s\\x00-\\x1f\\x7f]/g, '').toLowerCase();\n return !(normalized.startsWith('javascript:') || normalized.startsWith('data:') || normalized.startsWith('vbscript:'));\n}\n\nfunction isSafeSrcsetValue(value) {\n if (typeof value !== 'string') return true;\n return value\n .split(',')\n .every(candidate => {\n const url = candidate.trim().split(/\\s+/, 1)[0] || '';\n return url === '' || isSafeUrlValue(url);\n });\n}\n\nfunction escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction camelToKebab(str) {\n if (str.startsWith('--')) return str; // CSS custom properties (variables) \u2014 leave unchanged\n return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// SSR error serialization is exported above:\n// serializeSSRErrors() \u2014 serialize collected errors to script tag\n// hydrateSSRErrors() \u2014 read errors from DOM during client hydration\n// getSSRErrors() \u2014 programmatic access to collected errors\n\n// Re-export server actions\nexport {\n action,\n formAction,\n useAction,\n useFormAction,\n useOptimistic,\n useMutation,\n onRevalidate,\n invalidatePath,\n handleActionRequest,\n getRegisteredActions,\n generateCsrfToken,\n validateCsrfToken,\n csrfMetaTag,\n} from './actions.js';\n", "// What Framework - Server Actions\n// Call server-side functions from client code seamlessly.\n// Similar to Next.js Server Actions / SolidStart server functions.\n//\n// Usage:\n// // Define on server\n// const saveUser = action(async (formData) => {\n// 'use server';\n// const user = await db.users.create(formData);\n// return { success: true, id: user.id };\n// });\n//\n// // Call from client\n// const result = await saveUser({ name: 'John' });\n\nimport { signal, batch } from 'what-core';\n\n// Registry of server actions\nconst actionRegistry = new Map();\n\n// --- CSRF Protection ---\n// Server generates a token per session; client sends it with every action request.\n// The token is injected into the page via a meta tag or embedded in the server response.\n\n// Client: read the CSRF token from the page meta tag or cookie\n// Re-reads on every call to handle token rotation\nfunction getCsrfToken() {\n if (typeof document !== 'undefined') {\n // Try meta tag first\n const meta = document.querySelector('meta[name=\"what-csrf-token\"]');\n if (meta) {\n return meta.getAttribute('content');\n }\n // Try cookie\n const match = document.cookie.match(/(?:^|;\\s*)what-csrf=([^;]+)/);\n if (match) {\n return decodeURIComponent(match[1]);\n }\n }\n return null;\n}\n\n// Server: generate a CSRF token (call this per session/request)\nexport function generateCsrfToken() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID \u2014 use crypto.getRandomValues\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, b => b.toString(16).padStart(2, '0')).join('');\n }\n // Last resort \u2014 should not be reached in modern environments\n throw new Error('[what] No secure random source available for CSRF token generation');\n}\n\n// Server: validate CSRF token from request header against session token\nexport function validateCsrfToken(requestToken, sessionToken) {\n if (!requestToken || !sessionToken) return false;\n // Constant-time comparison to prevent timing attacks\n if (requestToken.length !== sessionToken.length) return false;\n let result = 0;\n for (let i = 0; i < requestToken.length; i++) {\n result |= requestToken.charCodeAt(i) ^ sessionToken.charCodeAt(i);\n }\n return result === 0;\n}\n\n// Server: middleware helper to inject CSRF meta tag into HTML\nexport function csrfMetaTag(token) {\n // HTML-escape the token to prevent XSS if a non-standard value is used\n const escaped = String(token).replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n return `<meta name=\"what-csrf-token\" content=\"${escaped}\">`;\n}\n\n// --- Define a server action ---\n\nlet _actionCounter = 0;\n\nfunction generateActionId() {\n // Generate a deterministic ID \u2014 prefer crypto.getRandomValues, fall back to a\n // monotonic counter (never Math.random, which is not cryptographically safe and\n // produces predictable IDs in some runtimes).\n const rand = typeof crypto !== 'undefined' && crypto.getRandomValues\n ? Array.from(crypto.getRandomValues(new Uint8Array(6)), b => b.toString(16).padStart(2, '0')).join('')\n : `c${(++_actionCounter).toString(36)}_${Date.now().toString(36)}`;\n return `a_${rand}`;\n}\n\nexport function action(fn, options = {}) {\n const id = options.id || generateActionId();\n const { onError, onSuccess, revalidate } = options;\n\n // Server-side: register the action\n if (typeof window === 'undefined') {\n actionRegistry.set(id, { fn, options });\n }\n\n // Create the callable wrapper\n async function callAction(...args) {\n // Server-side: call directly\n if (typeof window === 'undefined') {\n return fn(...args);\n }\n\n // Client-side: call via fetch with timeout support\n const timeout = options.timeout || 30000; // Default 30s timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const csrfToken = getCsrfToken();\n const headers = {\n 'Content-Type': 'application/json',\n 'X-What-Action': id,\n };\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken;\n\n const response = await fetch('/__what_action', {\n method: 'POST',\n headers,\n credentials: 'same-origin',\n signal: controller.signal,\n body: JSON.stringify({ args }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Action failed' }));\n throw new Error(error.message || 'Action failed');\n }\n\n const result = await response.json();\n\n if (onSuccess) onSuccess(result);\n if (revalidate) {\n // Trigger revalidation of specified paths\n for (const path of revalidate) {\n invalidatePath(path);\n }\n }\n\n return result;\n } catch (error) {\n if (error.name === 'AbortError') {\n const timeoutError = new Error(`Action \"${id}\" timed out after ${timeout}ms`);\n timeoutError.code = 'TIMEOUT';\n if (onError) onError(timeoutError);\n throw timeoutError;\n }\n if (onError) onError(error);\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n callAction._actionId = id;\n callAction._isAction = true;\n\n return callAction;\n}\n\n// --- Form action helper ---\n// For forms that submit to server actions.\n\nexport function formAction(actionFn, options = {}) {\n const { onSuccess, onError, resetOnSuccess = true } = options;\n\n return async (formDataOrEvent) => {\n let formData;\n let form;\n\n if (formDataOrEvent instanceof Event) {\n formDataOrEvent.preventDefault();\n form = formDataOrEvent.target;\n formData = new FormData(form);\n } else {\n formData = formDataOrEvent;\n }\n\n // Convert FormData to plain object, preserving File instances\n const data = {};\n let hasFiles = false;\n for (const [key, value] of formData.entries()) {\n if (typeof File !== 'undefined' && value instanceof File) {\n hasFiles = true;\n }\n if (data[key]) {\n // Handle multiple values (e.g., checkboxes, multi-file inputs)\n if (Array.isArray(data[key])) {\n data[key].push(value);\n } else {\n data[key] = [data[key], value];\n }\n } else {\n data[key] = value;\n }\n }\n\n try {\n // If form contains files, pass the raw FormData as second arg\n // so the action handler can access files directly\n const result = hasFiles\n ? await actionFn(data, formData)\n : await actionFn(data);\n if (onSuccess) onSuccess(result, form);\n if (resetOnSuccess && form) form.reset();\n return result;\n } catch (error) {\n if (onError) onError(error, form);\n throw error;\n }\n };\n}\n\n// --- useAction hook ---\n// Returns action state and trigger function.\n\nexport function useAction(actionFn) {\n const isPending = signal(false);\n const error = signal(null);\n const data = signal(null);\n\n async function trigger(...args) {\n isPending.set(true);\n error.set(null);\n\n try {\n const result = await actionFn(...args);\n data.set(result);\n return result;\n } catch (e) {\n error.set(e);\n throw e;\n } finally {\n isPending.set(false);\n }\n }\n\n return {\n trigger,\n isPending: () => isPending(),\n error: () => error(),\n data: () => data(),\n reset: () => {\n error.set(null);\n data.set(null);\n },\n };\n}\n\n// --- useFormAction hook ---\n// Combines useAction with form handling.\n\nexport function useFormAction(actionFn, options = {}) {\n const { resetOnSuccess = true } = options;\n const formRef = { current: null };\n const actionState = useAction(formAction(actionFn, { resetOnSuccess }));\n\n function handleSubmit(e) {\n e.preventDefault();\n const formData = new FormData(e.target);\n formRef.current = e.target;\n return actionState.trigger(formData);\n }\n\n return {\n ...actionState,\n handleSubmit,\n formRef,\n };\n}\n\n// --- Optimistic updates ---\n\nexport function useOptimistic(initialValue, reducer) {\n const value = signal(initialValue);\n const pending = signal([]);\n const baseValue = signal(initialValue); // Track the confirmed server value\n\n function addOptimistic(action) {\n const optimisticValue = reducer(value.peek(), action);\n batch(() => {\n pending.set([...pending.peek(), action]);\n value.set(optimisticValue);\n });\n }\n\n function resolve(action, serverValue) {\n batch(() => {\n pending.set(pending.peek().filter(a => a !== action));\n if (serverValue !== undefined) {\n baseValue.set(serverValue);\n // Recompute optimistic state from new base + remaining pending actions\n let current = serverValue;\n for (const a of pending.peek()) {\n current = reducer(current, a);\n }\n value.set(current);\n }\n });\n }\n\n function rollback(action, realValue) {\n batch(() => {\n const newPending = pending.peek().filter(a => a !== action);\n pending.set(newPending);\n const base = realValue !== undefined ? realValue : baseValue.peek();\n baseValue.set(base);\n // Recompute from base + remaining pending actions\n let current = base;\n for (const a of newPending) {\n current = reducer(current, a);\n }\n value.set(current);\n });\n }\n\n // Auto-rollback helper: wraps an async action with automatic rollback on error\n async function withOptimistic(action, asyncFn) {\n addOptimistic(action);\n try {\n const result = await asyncFn();\n resolve(action, result);\n return result;\n } catch (e) {\n rollback(action);\n throw e;\n }\n }\n\n return {\n value: () => value(),\n isPending: () => pending().length > 0,\n addOptimistic,\n resolve,\n rollback,\n withOptimistic,\n set: (v) => { value.set(v); baseValue.set(v); },\n };\n}\n\n// --- Path revalidation ---\n\nconst revalidationCallbacks = new Map();\n\nexport function onRevalidate(path, callback) {\n if (!revalidationCallbacks.has(path)) {\n revalidationCallbacks.set(path, new Set());\n }\n revalidationCallbacks.get(path).add(callback);\n\n return () => {\n revalidationCallbacks.get(path)?.delete(callback);\n };\n}\n\nexport function invalidatePath(path) {\n const callbacks = revalidationCallbacks.get(path);\n if (callbacks) {\n for (const cb of callbacks) {\n try { cb(); } catch (e) { console.error('[what] Revalidation error:', e); }\n }\n }\n}\n\n// --- Server-side action handler ---\n// Add this to your server middleware.\n\nexport function handleActionRequest(req, actionId, args, options = {}) {\n const { csrfToken: sessionCsrfToken, skipCsrf = false } = options;\n\n // Validate CSRF token unless explicitly skipped\n if (!skipCsrf) {\n if (!sessionCsrfToken) {\n // Fail closed: no CSRF token configured means the developer forgot to set it up.\n // This prevents silent security vulnerabilities in production.\n return Promise.resolve({\n status: 500,\n body: {\n message: '[what] CSRF token not configured. ' +\n 'Pass { csrfToken: sessionToken } to handleActionRequest, ' +\n 'or { skipCsrf: true } to explicitly opt out.'\n }\n });\n }\n const requestCsrfToken = req?.headers?.['x-csrf-token'] || req?.headers?.['X-CSRF-Token'];\n if (!validateCsrfToken(requestCsrfToken, sessionCsrfToken)) {\n return Promise.resolve({ status: 403, body: { message: 'Invalid CSRF token' } });\n }\n }\n\n const action = actionRegistry.get(actionId);\n if (!action) {\n return Promise.resolve({ status: 404, body: { message: 'Action not found' } });\n }\n\n // Validate args is an array to prevent prototype pollution\n if (!Array.isArray(args)) {\n return Promise.resolve({ status: 400, body: { message: 'Invalid action arguments' } });\n }\n\n return action.fn(...args)\n .then(result => ({ status: 200, body: result }))\n .catch(error => {\n // Log the full error server-side, return generic message to client\n console.error(`[what] Action \"${actionId}\" error:`, error);\n return {\n status: 500,\n body: { message: 'Action failed' },\n };\n });\n}\n\n// --- Get all registered actions (for SSR/build) ---\n\nexport function getRegisteredActions() {\n return [...actionRegistry.keys()];\n}\n\n// --- Mutation helper ---\n// Like useSWR mutation but simpler.\n\nexport function useMutation(mutationFn, options = {}) {\n const { onSuccess, onError, onSettled } = options;\n\n const state = {\n isPending: signal(false),\n error: signal(null),\n data: signal(null),\n };\n\n async function mutate(...args) {\n state.isPending.set(true);\n state.error.set(null);\n\n try {\n const result = await mutationFn(...args);\n state.data.set(result);\n if (onSuccess) onSuccess(result, ...args);\n return result;\n } catch (error) {\n state.error.set(error);\n if (onError) onError(error, ...args);\n throw error;\n } finally {\n state.isPending.set(false);\n if (onSettled) onSettled(state.data.peek(), state.error.peek(), ...args);\n }\n }\n\n return {\n mutate,\n isPending: () => state.isPending(),\n error: () => state.error(),\n data: () => state.data(),\n reset: () => {\n state.error.set(null);\n state.data.set(null);\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["action"]
|
|
7
7
|
}
|
package/dist/index.min.js
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import"what-core";import{signal as y,batch as
|
|
1
|
+
import"what-core";import{signal as y,batch as E}from"what-core";var x=new Map;function z(){if(typeof document<"u"){let t=document.querySelector('meta[name="what-csrf-token"]');if(t)return t.getAttribute("content");let e=document.cookie.match(/(?:^|;\s*)what-csrf=([^;]+)/);if(e)return decodeURIComponent(e[1])}return null}function X(){if(typeof crypto<"u"&&crypto.randomUUID)return crypto.randomUUID();if(typeof crypto<"u"&&crypto.getRandomValues){let t=new Uint8Array(16);return crypto.getRandomValues(t),Array.from(t,e=>e.toString(16).padStart(2,"0")).join("")}throw new Error("[what] No secure random source available for CSRF token generation")}function j(t,e){if(!t||!e||t.length!==e.length)return!1;let r=0;for(let o=0;o<t.length;o++)r|=t.charCodeAt(o)^e.charCodeAt(o);return r===0}function q(t){return`<meta name="what-csrf-token" content="${String(t).replace(/&/g,"&").replace(/"/g,""").replace(/</g,"<").replace(/>/g,">")}">`}var J=0;function Z(){return`a_${typeof crypto<"u"&&crypto.getRandomValues?Array.from(crypto.getRandomValues(new Uint8Array(6)),e=>e.toString(16).padStart(2,"0")).join(""):`c${(++J).toString(36)}_${Date.now().toString(36)}`}`}function G(t,e={}){let r=e.id||Z(),{onError:o,onSuccess:i,revalidate:n}=e;typeof window>"u"&&x.set(r,{fn:t,options:e});async function s(...c){if(typeof window>"u")return t(...c);let a=e.timeout||3e4,l=new AbortController,u=setTimeout(()=>l.abort(),a);try{let f=z(),d={"Content-Type":"application/json","X-What-Action":r};f&&(d["X-CSRF-Token"]=f);let p=await fetch("/__what_action",{method:"POST",headers:d,credentials:"same-origin",signal:l.signal,body:JSON.stringify({args:c})});if(!p.ok){let k=await p.json().catch(()=>({message:"Action failed"}));throw new Error(k.message||"Action failed")}let h=await p.json();if(i&&i(h),n)for(let k of n)D(k);return h}catch(f){if(f.name==="AbortError"){let d=new Error(`Action "${r}" timed out after ${a}ms`);throw d.code="TIMEOUT",o&&o(d),d}throw o&&o(f),f}finally{clearTimeout(u)}}return s._actionId=r,s._isAction=!0,s}function P(t,e={}){let{onSuccess:r,onError:o,resetOnSuccess:i=!0}=e;return async n=>{let s,c;n instanceof Event?(n.preventDefault(),c=n.target,s=new FormData(c)):s=n;let a={},l=!1;for(let[u,f]of s.entries())typeof File<"u"&&f instanceof File&&(l=!0),a[u]?Array.isArray(a[u])?a[u].push(f):a[u]=[a[u],f]:a[u]=f;try{let u=l?await t(a,s):await t(a);return r&&r(u,c),i&&c&&c.reset(),u}catch(u){throw o&&o(u,c),u}}}function U(t){let e=y(!1),r=y(null),o=y(null);async function i(...n){e.set(!0),r.set(null);try{let s=await t(...n);return o.set(s),s}catch(s){throw r.set(s),s}finally{e.set(!1)}}return{trigger:i,isPending:()=>e(),error:()=>r(),data:()=>o(),reset:()=>{r.set(null),o.set(null)}}}function Y(t,e={}){let{resetOnSuccess:r=!0}=e,o={current:null},i=U(P(t,{resetOnSuccess:r}));function n(s){s.preventDefault();let c=new FormData(s.target);return o.current=s.target,i.trigger(c)}return{...i,handleSubmit:n,formRef:o}}function B(t,e){let r=y(t),o=y([]),i=y(t);function n(l){let u=e(r.peek(),l);E(()=>{o.set([...o.peek(),l]),r.set(u)})}function s(l,u){E(()=>{if(o.set(o.peek().filter(f=>f!==l)),u!==void 0){i.set(u);let f=u;for(let d of o.peek())f=e(f,d);r.set(f)}})}function c(l,u){E(()=>{let f=o.peek().filter(h=>h!==l);o.set(f);let d=u!==void 0?u:i.peek();i.set(d);let p=d;for(let h of f)p=e(p,h);r.set(p)})}async function a(l,u){n(l);try{let f=await u();return s(l,f),f}catch(f){throw c(l),f}}return{value:()=>r(),isPending:()=>o().length>0,addOptimistic:n,resolve:s,rollback:c,withOptimistic:a,set:l=>{r.set(l),i.set(l)}}}var T=new Map;function Q(t,e){return T.has(t)||T.set(t,new Set),T.get(t).add(e),()=>{T.get(t)?.delete(e)}}function D(t){let e=T.get(t);if(e)for(let r of e)try{r()}catch(o){console.error("[what] Revalidation error:",o)}}function v(t,e,r,o={}){let{csrfToken:i,skipCsrf:n=!1}=o;if(!n){if(!i)return Promise.resolve({status:500,body:{message:"[what] CSRF token not configured. Pass { csrfToken: sessionToken } to handleActionRequest, or { skipCsrf: true } to explicitly opt out."}});let c=t?.headers?.["x-csrf-token"]||t?.headers?.["X-CSRF-Token"];if(!j(c,i))return Promise.resolve({status:403,body:{message:"Invalid CSRF token"}})}let s=x.get(e);return s?Array.isArray(r)?s.fn(...r).then(c=>({status:200,body:c})).catch(c=>(console.error(`[what] Action "${e}" error:`,c),{status:500,body:{message:"Action failed"}})):Promise.resolve({status:400,body:{message:"Invalid action arguments"}}):Promise.resolve({status:404,body:{message:"Action not found"}})}function tt(){return[...x.keys()]}function et(t,e={}){let{onSuccess:r,onError:o,onSettled:i}=e,n={isPending:y(!1),error:y(null),data:y(null)};async function s(...c){n.isPending.set(!0),n.error.set(null);try{let a=await t(...c);return n.data.set(a),r&&r(a,...c),a}catch(a){throw n.error.set(a),o&&o(a,...c),a}finally{n.isPending.set(!1),i&&i(n.data.peek(),n.error.peek(),...c)}}return{mutate:s,isPending:()=>n.isPending(),error:()=>n.error(),data:()=>n.data(),reset:()=>{n.error.set(null),n.data.set(null)}}}var S=[],rt=50;function $(t,e={}){let r={code:t.code||"ERR_SSR_RENDER",message:t.message||String(t),component:e.component||null,timestamp:Date.now()};g&&(r.suggestion=t.suggestion||null,r.stack=t.stack?.split(`
|
|
2
2
|
`).slice(0,5).join(`
|
|
3
|
-
`)||null),S.push(r),S.length>
|
|
4
|
-
`),l=
|
|
5
|
-
`),u=
|
|
3
|
+
`)||null),S.push(r),S.length>rt&&S.shift()}function V(){S=[]}function nt(){if(S.length===0)return"";let t=g?S:S.map(r=>({code:r.code,component:r.component}));return`<script type="application/json" data-what-ssr-errors>${JSON.stringify(t).replace(/<\//g,"<\\/")}<\/script>`}function $t(){if(typeof document>"u")return[];let t=document.querySelector("script[data-what-ssr-errors]");if(!t)return[];try{let e=JSON.parse(t.textContent);return t.remove(),e}catch{return[]}}function Tt(){return S.slice()}var K=0;function ot(){K=0}function it(){return"h"+K++}function Rt(t){return ot(),V(),w(t)}function w(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return m(String(t));if(typeof t=="function"&&t._signal)return`<!--$-->${w(t())}<!--/$-->`;if(typeof t=="function")try{return`<!--$-->${w(t())}<!--/$-->`}catch(a){return $(a,{component:"reactive-function"}),g&&console.warn("[what-server] Error rendering reactive function in SSR:",a.message),"<!--$--><!--/$-->"}if(Array.isArray(t))return`<!--[]-->${b(t,w)}<!--/[]-->`;if(typeof t.tag=="function"){let a=it(),l=t.tag.displayName||t.tag.name||"Anonymous";try{let u=t.tag({...t.props,children:t.children}),f=w(u);return st(f,a)}catch(u){return $(u,{component:l}),g?(console.warn(`[what-server] Error rendering component "${l}" in SSR:`,u.message),`<!--ssr-error:${m(l)}-->`):"<!--ssr-error-->"}}let{tag:e,props:r,children:o}=t,i=N(r||{}),n=`<${e}${i}>`;if(H.has(e))return n;let s=L(r),c=s!=null?String(s):b(o,w);return`${n}${c}</${e}>`}function st(t,e){let r=t.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);if(r){let o=r[1],i=r[2],n=o.length+1+i.length;return t.slice(0,n)+` data-hk="${e}"`+t.slice(n)}return t}function A(t){if(t==null||t===!1||t===!0)return"";if(typeof t=="string"||typeof t=="number")return m(String(t));if(typeof t=="function"&&t._signal)return A(t());if(typeof t=="function")try{return A(t())}catch(a){return $(a,{component:"reactive-function"}),g&&console.warn("[what-server] Error rendering reactive function in SSR:",a.message),""}if(Array.isArray(t))return b(t,A);if(typeof t.tag=="function"){let a=t.tag.displayName||t.tag.name||"Anonymous";try{let l=t.tag({...t.props,children:t.children});return A(l)}catch(l){return $(l,{component:a}),g?(console.warn(`[what-server] Error rendering component "${a}" in SSR:`,l.message),`<!-- SSR Error in ${m(a)}: ${m(l.message)} -->`):"<!-- SSR Error -->"}}let{tag:e,props:r,children:o}=t,i=N(r||{}),n=`<${e}${i}>`;if(H.has(e))return n;let s=L(r),c=s!=null?String(s):b(o,A);return`${n}${c}</${e}>`}function b(t,e){let r="";for(let o=0;o<t.length;o++)r+=e(t[o]);return r}async function*R(t){if(t==null||t===!1||t===!0)return;if(typeof t=="string"||typeof t=="number"){yield m(String(t));return}if(typeof t=="function"&&t._signal){yield*R(t());return}if(typeof t=="function"){try{yield*R(t())}catch(n){$(n,{component:"reactive-function"}),g&&console.warn("[what-server] Error rendering reactive function in stream SSR:",n.message)}return}if(Array.isArray(t)){for(let n of t)yield*R(n);return}if(typeof t.tag=="function"){let n=t.tag.displayName||t.tag.name||"Anonymous";try{let s=t.tag({...t.props,children:t.children}),c=s instanceof Promise?await s:s;yield*R(c)}catch(s){$(s,{component:n}),g&&console.warn(`[what-server] Error rendering component "${n}" in stream SSR:`,s.message),yield g?`<!-- SSR Error in ${m(n)}: ${m(s.message||"Component error")} -->`:"<!-- SSR Error -->"}return}let{tag:e,props:r,children:o}=t,i=N(r||{});if(yield`<${e}${i}>`,!H.has(e)){let n=L(r);if(n!=null)yield String(n);else for(let s of o)yield*R(s);yield`</${e}>`}}function _t(t){return{mode:"static",...t}}function bt(t,e={}){V();let r=t.component(e),o=A(r),i=t.islands||[];return ct({title:t.title||"",meta:t.meta||{},body:o,islands:i,scripts:t.mode==="static"?[]:t.scripts||[],styles:t.styles||[],mode:t.mode,ssrErrors:nt()})}function ct({title:t,meta:e,body:r,islands:o,scripts:i,styles:n,mode:s,ssrErrors:c=""}){let a=Object.entries(e).map(([p,h])=>`<meta name="${m(p)}" content="${m(h)}">`).join(`
|
|
4
|
+
`),l=n.map(p=>`<link rel="stylesheet" href="${m(p)}">`).join(`
|
|
5
|
+
`),u=o.length>0?`
|
|
6
6
|
<script type="module">
|
|
7
7
|
import { hydrateIslands } from '/@what/islands.js';
|
|
8
8
|
hydrateIslands();
|
|
9
|
-
<\/script>`:"",f=
|
|
10
|
-
`),
|
|
9
|
+
<\/script>`:"",f=i.map(p=>`<script type="module" src="${m(p)}"><\/script>`).join(`
|
|
10
|
+
`),d=s==="client"?`
|
|
11
11
|
<script type="module" src="/@what/client.js"><\/script>`:"";return`<!DOCTYPE html>
|
|
12
12
|
<html lang="en">
|
|
13
13
|
<head>
|
|
14
14
|
<meta charset="UTF-8">
|
|
15
15
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
16
|
-
${
|
|
16
|
+
${a}
|
|
17
17
|
<title>${m(t)}</title>
|
|
18
18
|
${l}
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
|
21
21
|
<div id="app">${r}</div>
|
|
22
|
-
${
|
|
22
|
+
${c}
|
|
23
23
|
${u}
|
|
24
24
|
${f}
|
|
25
|
-
${
|
|
25
|
+
${d}
|
|
26
26
|
</body>
|
|
27
|
-
</html>`}function
|
|
27
|
+
</html>`}function kt(t){return t._server=!0,t}var g=!(typeof process<"u");function L(t){return t?t.dangerouslySetInnerHTML?t.dangerouslySetInnerHTML.__html??null:t.innerHTML&&typeof t.innerHTML=="object"&&"__html"in t.innerHTML?t.innerHTML.__html??null:(t.innerHTML!=null&&typeof t.innerHTML=="string"&&g&&console.warn("[what-server] innerHTML received a raw string. This is a security risk (XSS). Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead."),null):null}function N(t){let e="",r=Object.keys(t);for(let o=0;o<r.length;o++){let i=r[o],n=t[i];if(i==="key"||i==="ref"||i==="children"||i==="dangerouslySetInnerHTML"||i==="innerHTML"||i.startsWith("on")&&i.length>2||n===!1||n==null)continue;let s=lt(i),c=s.name;if(!s.valid){g&&console.warn(`[what-server] Omitted invalid attribute name: ${i}`);continue}if(!pt(s.urlKind,n)){g&&console.warn(`[what-server] Omitted unsafe URL attribute "${c}": ${n}`);continue}if(i==="className"||i==="class")e+=` class="${m(String(n))}"`;else if(i==="style"&&typeof n=="object"){let a=Object.entries(n).map(([l,u])=>`${yt(l)}:${u}`).join(";");e+=` style="${m(a)}"`}else n===!0?c.startsWith("aria-")||c==="role"?e+=` ${c}="true"`:e+=` ${c}`:e+=` ${c}="${m(String(n))}"`}return e}function at(t){return t==="className"?"class":t==="htmlFor"?"for":t}var _=new Map,ut=2048,C=0,I=1,M=2;function lt(t){let e=_.get(t);if(e)return e;let r=at(t),o=ft(r),i={name:r,valid:o,urlKind:o?mt(r):C};return _.size>=ut&&_.clear(),_.set(t,i),i}function ft(t){return/^[^\s"'>/=\x00-\x1f\x7f]+$/.test(t)}var O=new Set(["href","src","action","formaction","poster","cite","background","xlink:href"]),F=new Set(["srcset"]);function mt(t){if(O.has(t))return I;if(F.has(t))return M;if(!dt(t))return C;let e=t.toLowerCase();return O.has(e)?I:F.has(e)?M:C}function dt(t){for(let e=0;e<t.length;e++){let r=t.charCodeAt(e);if(r>=65&&r<=90)return!0}return!1}function pt(t,e){return t===M?gt(e):t===I?W(e):!0}function W(t){if(typeof t!="string")return!0;let e=t.trim().replace(/[\s\x00-\x1f\x7f]/g,"").toLowerCase();return!(e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("vbscript:"))}function gt(t){return typeof t!="string"?!0:t.split(",").every(e=>{let r=e.trim().split(/\s+/,1)[0]||"";return r===""||W(r)})}function m(t){return t.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function yt(t){return t.startsWith("--")?t:t.replace(/([A-Z])/g,"-$1").toLowerCase()}var H=new Set(["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"]);export{G as action,q as csrfMetaTag,_t as definePage,P as formAction,X as generateCsrfToken,bt as generateStaticPage,tt as getRegisteredActions,Tt as getSSRErrors,v as handleActionRequest,$t as hydrateSSRErrors,D as invalidatePath,Q as onRevalidate,Rt as renderToHydratableString,R as renderToStream,A as renderToString,nt as serializeSSRErrors,kt as server,U as useAction,Y as useFormAction,et as useMutation,B as useOptimistic,j as validateCsrfToken};
|
|
28
28
|
//# sourceMappingURL=index.min.js.map
|
package/dist/index.min.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.js", "../src/actions.js"],
|
|
4
|
-
"sourcesContent": ["// What Framework - Server\n// SSR, static site generation, server components.\n// Zero-JS pages by default. Islands opt-in to client JS.\n\nimport { h } from 'what-core';\n\n// --- SSR Error Collection ---\n// Errors that occur during SSR are collected and serialized into the HTML output\n// so the client can pick them up during hydration and display/report them.\n\nlet _ssrErrors = [];\nconst MAX_SSR_ERRORS = 50;\n\nfunction _collectSSRError(error, context = {}) {\n const entry = {\n code: error.code || 'ERR_SSR_RENDER',\n message: error.message || String(error),\n component: context.component || null,\n timestamp: Date.now(),\n };\n // In dev mode, include extra detail for debugging\n if (_isDevMode) {\n entry.suggestion = error.suggestion || null;\n entry.stack = error.stack?.split('\\n').slice(0, 5).join('\\n') || null;\n }\n _ssrErrors.push(entry);\n if (_ssrErrors.length > MAX_SSR_ERRORS) _ssrErrors.shift();\n}\n\nfunction _resetSSRErrors() {\n _ssrErrors = [];\n}\n\n/**\n * Serialize collected SSR errors into a script tag for client hydration.\n * In dev mode: includes full error details (message, suggestion, stack).\n * In production: includes only error code and component name.\n */\nexport function serializeSSRErrors() {\n if (_ssrErrors.length === 0) return '';\n const payload = _isDevMode\n ? _ssrErrors\n : _ssrErrors.map(e => ({ code: e.code, component: e.component }));\n const json = JSON.stringify(payload).replace(/<\\//g, '<\\\\/'); // prevent XSS via </script>\n return `<script type=\"application/json\" data-what-ssr-errors>${json}</script>`;\n}\n\n/**\n * Read SSR errors from the DOM during client hydration.\n * Call this on the client side during hydration to pick up errors from SSR.\n * Returns an array of error objects, or empty array if none.\n */\nexport function hydrateSSRErrors() {\n if (typeof document === 'undefined') return [];\n const el = document.querySelector('script[data-what-ssr-errors]');\n if (!el) return [];\n try {\n const errors = JSON.parse(el.textContent);\n el.remove(); // clean up after reading\n return errors;\n } catch {\n return [];\n }\n}\n\n/**\n * Get collected SSR errors (for programmatic access before serialization).\n */\nexport function getSSRErrors() {\n return _ssrErrors.slice();\n}\n\n// --- Hydration ID Generator ---\nlet _hydrationIdCounter = 0;\n\nfunction resetHydrationId() {\n _hydrationIdCounter = 0;\n}\n\nfunction nextHydrationId() {\n return 'h' + (_hydrationIdCounter++);\n}\n\n// --- Render to Hydratable String ---\n// Renders with hydration markers (data-hk attributes, comment boundaries)\n// so the client can reuse the server-rendered DOM.\n\nexport function renderToHydratableString(vnode) {\n resetHydrationId();\n _resetSSRErrors();\n return _renderHydratable(vnode);\n}\n\nfunction _renderHydratable(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap\n if (typeof vnode === 'function' && vnode._signal) {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n }\n\n // Reactive function child \u2014 wrap in dynamic content markers\n if (typeof vnode === 'function') {\n try {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '<!--$--><!--/$-->';\n }\n }\n\n // Array \u2014 wrap in list markers\n if (Array.isArray(vnode)) {\n return `<!--[]-->${vnode.map(_renderHydratable).join('')}<!--/[]-->`;\n }\n\n // Component \u2014 add hydration key to root element\n if (typeof vnode.tag === 'function') {\n const hkId = nextHydrationId();\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n const html = _renderHydratable(result);\n // Inject data-hk into the first element tag if present\n return injectHydrationKey(html, hkId);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!--ssr-error:${escapeHtml(componentName)}-->`;\n }\n return `<!--ssr-error-->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : children.map(_renderHydratable).join('');\n return `${open}${inner}</${tag}>`;\n}\n\n// Inject data-hk=\"id\" into the first HTML opening tag\nfunction injectHydrationKey(html, hkId) {\n // Skip comment markers to find the first real element\n const match = html.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);\n if (match) {\n const prefix = match[1];\n const tagName = match[2];\n const insertAt = prefix.length + 1 + tagName.length; // after '<tagName'\n return html.slice(0, insertAt) + ` data-hk=\"${hkId}\"` + html.slice(insertAt);\n }\n return html;\n}\n\n// --- Render to String ---\n// Renders a VNode tree to an HTML string. Used for SSR and static gen.\n\nexport function renderToString(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n return renderToString(vnode());\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n return renderToString(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '';\n }\n }\n\n // Array\n if (Array.isArray(vnode)) {\n return vnode.map(renderToString).join('');\n }\n\n // Component\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n return renderToString(result);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message)} -->`;\n }\n return `<!-- SSR Error -->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : children.map(renderToString).join('');\n return `${open}${inner}</${tag}>`;\n}\n\n// --- Stream Render ---\n// Returns an async iterator for streaming SSR.\n\nexport async function* renderToStream(vnode) {\n if (vnode == null || vnode === false || vnode === true) return;\n\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n yield escapeHtml(String(vnode));\n return;\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n yield* renderToStream(vnode());\n return;\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n yield* renderToStream(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);\n }\n }\n return;\n }\n\n if (Array.isArray(vnode)) {\n for (const child of vnode) {\n yield* renderToStream(child);\n }\n return;\n }\n\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n // Support async components\n const resolved = result instanceof Promise ? await result : result;\n yield* renderToStream(resolved);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in stream SSR:`, e.message);\n }\n yield _isDevMode\n ? `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message || 'Component error')} -->`\n : `<!-- SSR Error -->`;\n }\n return;\n }\n\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n yield `<${tag}${attrs}>`;\n\n if (!VOID_ELEMENTS.has(tag)) {\n const rawInner = _resolveInnerHTML(props);\n if (rawInner != null) {\n yield String(rawInner);\n } else {\n for (const child of children) {\n yield* renderToStream(child);\n }\n }\n yield `</${tag}>`;\n }\n}\n\n// --- Static Site Generation ---\n\nexport function definePage(config) {\n return {\n // 'static' = pre-render at build time (default)\n // 'server' = render on each request\n // 'client' = render in browser (SPA)\n // 'hybrid' = static shell + islands\n mode: 'static',\n ...config,\n };\n}\n\n// Generate static HTML for a page\nexport function generateStaticPage(page, data = {}) {\n _resetSSRErrors();\n const vnode = page.component(data);\n const html = renderToString(vnode);\n const islands = page.islands || [];\n\n return wrapDocument({\n title: page.title || '',\n meta: page.meta || {},\n body: html,\n islands,\n scripts: page.mode === 'static' ? [] : page.scripts || [],\n styles: page.styles || [],\n mode: page.mode,\n ssrErrors: serializeSSRErrors(),\n });\n}\n\nfunction wrapDocument({ title, meta, body, islands, scripts, styles, mode, ssrErrors = '' }) {\n const metaTags = Object.entries(meta)\n .map(([name, content]) => `<meta name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\">`)\n .join('\\n ');\n\n const styleTags = styles\n .map(href => `<link rel=\"stylesheet\" href=\"${escapeHtml(href)}\">`)\n .join('\\n ');\n\n const islandScript = islands.length > 0 ? `\n <script type=\"module\">\n import { hydrateIslands } from '/@what/islands.js';\n hydrateIslands();\n </script>` : '';\n\n const scriptTags = scripts\n .map(src => `<script type=\"module\" src=\"${escapeHtml(src)}\"></script>`)\n .join('\\n ');\n\n const clientScript = mode === 'client' ? `\n <script type=\"module\" src=\"/@what/client.js\"></script>` : '';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n ${metaTags}\n <title>${escapeHtml(title)}</title>\n ${styleTags}\n </head>\n <body>\n <div id=\"app\">${body}</div>\n ${ssrErrors}\n ${islandScript}\n ${scriptTags}\n ${clientScript}\n </body>\n</html>`;\n}\n\n// --- Server Component ---\n// Renders on the server, sends HTML to client. No JS shipped.\n\nexport function server(Component) {\n Component._server = true;\n return Component;\n}\n\n// --- Helpers ---\n\n// Dev-mode flag for server\nconst _isDevMode = typeof process !== 'undefined'\n ? process.env?.NODE_ENV !== 'production'\n : true;\n\n/**\n * Resolve innerHTML / dangerouslySetInnerHTML from props.\n * Requires { __html: ... } wrapper. Plain string innerHTML is rejected (XSS prevention).\n */\nfunction _resolveInnerHTML(props) {\n if (!props) return null;\n\n // dangerouslySetInnerHTML always requires { __html }\n if (props.dangerouslySetInnerHTML) {\n return props.dangerouslySetInnerHTML.__html ?? null;\n }\n\n // innerHTML with { __html } wrapper \u2014 allowed\n if (props.innerHTML && typeof props.innerHTML === 'object' && '__html' in props.innerHTML) {\n return props.innerHTML.__html ?? null;\n }\n\n // innerHTML as plain string \u2014 reject with warning\n if (props.innerHTML != null && typeof props.innerHTML === 'string') {\n if (_isDevMode) {\n console.warn(\n '[what-server] innerHTML received a raw string. This is a security risk (XSS). ' +\n 'Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead.'\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction renderAttrs(props) {\n let out = '';\n for (const [key, val] of Object.entries(props)) {\n if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;\n if (key.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR\n if (val === false || val == null) continue;\n\n const attrName = getHtmlAttributeName(key);\n if (!isValidHtmlAttributeName(attrName)) {\n if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);\n continue;\n }\n if (!isSafeUrlAttributeValue(attrName, val)) {\n if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute \"${attrName}\": ${val}`);\n continue;\n }\n\n if (key === 'className' || key === 'class') {\n out += ` class=\"${escapeHtml(String(val))}\"`;\n } else if (key === 'style' && typeof val === 'object') {\n const css = Object.entries(val)\n .map(([p, v]) => `${camelToKebab(p)}:${v}`)\n .join(';');\n out += ` style=\"${escapeHtml(css)}\"`;\n } else if (val === true) {\n // ARIA attributes require explicit =\"true\", HTML boolean attrs can be bare\n if (attrName.startsWith('aria-') || attrName === 'role') {\n out += ` ${attrName}=\"true\"`;\n } else {\n out += ` ${attrName}`;\n }\n } else {\n out += ` ${attrName}=\"${escapeHtml(String(val))}\"`;\n }\n }\n\n return out;\n}\n\nfunction getHtmlAttributeName(name) {\n if (name === 'className') return 'class';\n if (name === 'htmlFor') return 'for';\n return name;\n}\n\nfunction isValidHtmlAttributeName(name) {\n return /^[^\\s\"'>/=\\x00-\\x1f\\x7f]+$/.test(name);\n}\n\nconst URL_ATTRS = new Set([\n 'href',\n 'src',\n 'action',\n 'formaction',\n 'poster',\n 'cite',\n 'background',\n 'xlink:href',\n]);\nconst URL_LIST_ATTRS = new Set(['srcset']);\n\nfunction isSafeUrlAttributeValue(name, value) {\n const normalizedName = String(name).toLowerCase();\n if (URL_LIST_ATTRS.has(normalizedName)) return isSafeSrcsetValue(value);\n if (URL_ATTRS.has(normalizedName)) return isSafeUrlValue(value);\n return true;\n}\n\nfunction isSafeUrlValue(value) {\n if (typeof value !== 'string') return true;\n const normalized = value.trim().replace(/[\\s\\x00-\\x1f\\x7f]/g, '').toLowerCase();\n return !(normalized.startsWith('javascript:') || normalized.startsWith('data:') || normalized.startsWith('vbscript:'));\n}\n\nfunction isSafeSrcsetValue(value) {\n if (typeof value !== 'string') return true;\n return value\n .split(',')\n .every(candidate => {\n const url = candidate.trim().split(/\\s+/, 1)[0] || '';\n return url === '' || isSafeUrlValue(url);\n });\n}\n\nfunction escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction camelToKebab(str) {\n if (str.startsWith('--')) return str; // CSS custom properties (variables) \u2014 leave unchanged\n return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// SSR error serialization is exported above:\n// serializeSSRErrors() \u2014 serialize collected errors to script tag\n// hydrateSSRErrors() \u2014 read errors from DOM during client hydration\n// getSSRErrors() \u2014 programmatic access to collected errors\n\n// Re-export server actions\nexport {\n action,\n formAction,\n useAction,\n useFormAction,\n useOptimistic,\n useMutation,\n onRevalidate,\n invalidatePath,\n handleActionRequest,\n getRegisteredActions,\n generateCsrfToken,\n validateCsrfToken,\n csrfMetaTag,\n} from './actions.js';\n", "// What Framework - Server Actions\n// Call server-side functions from client code seamlessly.\n// Similar to Next.js Server Actions / SolidStart server functions.\n//\n// Usage:\n// // Define on server\n// const saveUser = action(async (formData) => {\n// 'use server';\n// const user = await db.users.create(formData);\n// return { success: true, id: user.id };\n// });\n//\n// // Call from client\n// const result = await saveUser({ name: 'John' });\n\nimport { signal, batch } from 'what-core';\n\n// Registry of server actions\nconst actionRegistry = new Map();\n\n// --- CSRF Protection ---\n// Server generates a token per session; client sends it with every action request.\n// The token is injected into the page via a meta tag or embedded in the server response.\n\n// Client: read the CSRF token from the page meta tag or cookie\n// Re-reads on every call to handle token rotation\nfunction getCsrfToken() {\n if (typeof document !== 'undefined') {\n // Try meta tag first\n const meta = document.querySelector('meta[name=\"what-csrf-token\"]');\n if (meta) {\n return meta.getAttribute('content');\n }\n // Try cookie\n const match = document.cookie.match(/(?:^|;\\s*)what-csrf=([^;]+)/);\n if (match) {\n return decodeURIComponent(match[1]);\n }\n }\n return null;\n}\n\n// Server: generate a CSRF token (call this per session/request)\nexport function generateCsrfToken() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID \u2014 use crypto.getRandomValues\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, b => b.toString(16).padStart(2, '0')).join('');\n }\n // Last resort \u2014 should not be reached in modern environments\n throw new Error('[what] No secure random source available for CSRF token generation');\n}\n\n// Server: validate CSRF token from request header against session token\nexport function validateCsrfToken(requestToken, sessionToken) {\n if (!requestToken || !sessionToken) return false;\n // Constant-time comparison to prevent timing attacks\n if (requestToken.length !== sessionToken.length) return false;\n let result = 0;\n for (let i = 0; i < requestToken.length; i++) {\n result |= requestToken.charCodeAt(i) ^ sessionToken.charCodeAt(i);\n }\n return result === 0;\n}\n\n// Server: middleware helper to inject CSRF meta tag into HTML\nexport function csrfMetaTag(token) {\n // HTML-escape the token to prevent XSS if a non-standard value is used\n const escaped = String(token).replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n return `<meta name=\"what-csrf-token\" content=\"${escaped}\">`;\n}\n\n// --- Define a server action ---\n\nlet _actionCounter = 0;\n\nfunction generateActionId() {\n // Generate a deterministic ID \u2014 prefer crypto.getRandomValues, fall back to a\n // monotonic counter (never Math.random, which is not cryptographically safe and\n // produces predictable IDs in some runtimes).\n const rand = typeof crypto !== 'undefined' && crypto.getRandomValues\n ? Array.from(crypto.getRandomValues(new Uint8Array(6)), b => b.toString(16).padStart(2, '0')).join('')\n : `c${(++_actionCounter).toString(36)}_${Date.now().toString(36)}`;\n return `a_${rand}`;\n}\n\nexport function action(fn, options = {}) {\n const id = options.id || generateActionId();\n const { onError, onSuccess, revalidate } = options;\n\n // Server-side: register the action\n if (typeof window === 'undefined') {\n actionRegistry.set(id, { fn, options });\n }\n\n // Create the callable wrapper\n async function callAction(...args) {\n // Server-side: call directly\n if (typeof window === 'undefined') {\n return fn(...args);\n }\n\n // Client-side: call via fetch with timeout support\n const timeout = options.timeout || 30000; // Default 30s timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const csrfToken = getCsrfToken();\n const headers = {\n 'Content-Type': 'application/json',\n 'X-What-Action': id,\n };\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken;\n\n const response = await fetch('/__what_action', {\n method: 'POST',\n headers,\n credentials: 'same-origin',\n signal: controller.signal,\n body: JSON.stringify({ args }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Action failed' }));\n throw new Error(error.message || 'Action failed');\n }\n\n const result = await response.json();\n\n if (onSuccess) onSuccess(result);\n if (revalidate) {\n // Trigger revalidation of specified paths\n for (const path of revalidate) {\n invalidatePath(path);\n }\n }\n\n return result;\n } catch (error) {\n if (error.name === 'AbortError') {\n const timeoutError = new Error(`Action \"${id}\" timed out after ${timeout}ms`);\n timeoutError.code = 'TIMEOUT';\n if (onError) onError(timeoutError);\n throw timeoutError;\n }\n if (onError) onError(error);\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n callAction._actionId = id;\n callAction._isAction = true;\n\n return callAction;\n}\n\n// --- Form action helper ---\n// For forms that submit to server actions.\n\nexport function formAction(actionFn, options = {}) {\n const { onSuccess, onError, resetOnSuccess = true } = options;\n\n return async (formDataOrEvent) => {\n let formData;\n let form;\n\n if (formDataOrEvent instanceof Event) {\n formDataOrEvent.preventDefault();\n form = formDataOrEvent.target;\n formData = new FormData(form);\n } else {\n formData = formDataOrEvent;\n }\n\n // Convert FormData to plain object, preserving File instances\n const data = {};\n let hasFiles = false;\n for (const [key, value] of formData.entries()) {\n if (typeof File !== 'undefined' && value instanceof File) {\n hasFiles = true;\n }\n if (data[key]) {\n // Handle multiple values (e.g., checkboxes, multi-file inputs)\n if (Array.isArray(data[key])) {\n data[key].push(value);\n } else {\n data[key] = [data[key], value];\n }\n } else {\n data[key] = value;\n }\n }\n\n try {\n // If form contains files, pass the raw FormData as second arg\n // so the action handler can access files directly\n const result = hasFiles\n ? await actionFn(data, formData)\n : await actionFn(data);\n if (onSuccess) onSuccess(result, form);\n if (resetOnSuccess && form) form.reset();\n return result;\n } catch (error) {\n if (onError) onError(error, form);\n throw error;\n }\n };\n}\n\n// --- useAction hook ---\n// Returns action state and trigger function.\n\nexport function useAction(actionFn) {\n const isPending = signal(false);\n const error = signal(null);\n const data = signal(null);\n\n async function trigger(...args) {\n isPending.set(true);\n error.set(null);\n\n try {\n const result = await actionFn(...args);\n data.set(result);\n return result;\n } catch (e) {\n error.set(e);\n throw e;\n } finally {\n isPending.set(false);\n }\n }\n\n return {\n trigger,\n isPending: () => isPending(),\n error: () => error(),\n data: () => data(),\n reset: () => {\n error.set(null);\n data.set(null);\n },\n };\n}\n\n// --- useFormAction hook ---\n// Combines useAction with form handling.\n\nexport function useFormAction(actionFn, options = {}) {\n const { resetOnSuccess = true } = options;\n const formRef = { current: null };\n const actionState = useAction(formAction(actionFn, { resetOnSuccess }));\n\n function handleSubmit(e) {\n e.preventDefault();\n const formData = new FormData(e.target);\n formRef.current = e.target;\n return actionState.trigger(formData);\n }\n\n return {\n ...actionState,\n handleSubmit,\n formRef,\n };\n}\n\n// --- Optimistic updates ---\n\nexport function useOptimistic(initialValue, reducer) {\n const value = signal(initialValue);\n const pending = signal([]);\n const baseValue = signal(initialValue); // Track the confirmed server value\n\n function addOptimistic(action) {\n const optimisticValue = reducer(value.peek(), action);\n batch(() => {\n pending.set([...pending.peek(), action]);\n value.set(optimisticValue);\n });\n }\n\n function resolve(action, serverValue) {\n batch(() => {\n pending.set(pending.peek().filter(a => a !== action));\n if (serverValue !== undefined) {\n baseValue.set(serverValue);\n // Recompute optimistic state from new base + remaining pending actions\n let current = serverValue;\n for (const a of pending.peek()) {\n current = reducer(current, a);\n }\n value.set(current);\n }\n });\n }\n\n function rollback(action, realValue) {\n batch(() => {\n const newPending = pending.peek().filter(a => a !== action);\n pending.set(newPending);\n const base = realValue !== undefined ? realValue : baseValue.peek();\n baseValue.set(base);\n // Recompute from base + remaining pending actions\n let current = base;\n for (const a of newPending) {\n current = reducer(current, a);\n }\n value.set(current);\n });\n }\n\n // Auto-rollback helper: wraps an async action with automatic rollback on error\n async function withOptimistic(action, asyncFn) {\n addOptimistic(action);\n try {\n const result = await asyncFn();\n resolve(action, result);\n return result;\n } catch (e) {\n rollback(action);\n throw e;\n }\n }\n\n return {\n value: () => value(),\n isPending: () => pending().length > 0,\n addOptimistic,\n resolve,\n rollback,\n withOptimistic,\n set: (v) => { value.set(v); baseValue.set(v); },\n };\n}\n\n// --- Path revalidation ---\n\nconst revalidationCallbacks = new Map();\n\nexport function onRevalidate(path, callback) {\n if (!revalidationCallbacks.has(path)) {\n revalidationCallbacks.set(path, new Set());\n }\n revalidationCallbacks.get(path).add(callback);\n\n return () => {\n revalidationCallbacks.get(path)?.delete(callback);\n };\n}\n\nexport function invalidatePath(path) {\n const callbacks = revalidationCallbacks.get(path);\n if (callbacks) {\n for (const cb of callbacks) {\n try { cb(); } catch (e) { console.error('[what] Revalidation error:', e); }\n }\n }\n}\n\n// --- Server-side action handler ---\n// Add this to your server middleware.\n\nexport function handleActionRequest(req, actionId, args, options = {}) {\n const { csrfToken: sessionCsrfToken, skipCsrf = false } = options;\n\n // Validate CSRF token unless explicitly skipped\n if (!skipCsrf) {\n if (!sessionCsrfToken) {\n // Fail closed: no CSRF token configured means the developer forgot to set it up.\n // This prevents silent security vulnerabilities in production.\n return Promise.resolve({\n status: 500,\n body: {\n message: '[what] CSRF token not configured. ' +\n 'Pass { csrfToken: sessionToken } to handleActionRequest, ' +\n 'or { skipCsrf: true } to explicitly opt out.'\n }\n });\n }\n const requestCsrfToken = req?.headers?.['x-csrf-token'] || req?.headers?.['X-CSRF-Token'];\n if (!validateCsrfToken(requestCsrfToken, sessionCsrfToken)) {\n return Promise.resolve({ status: 403, body: { message: 'Invalid CSRF token' } });\n }\n }\n\n const action = actionRegistry.get(actionId);\n if (!action) {\n return Promise.resolve({ status: 404, body: { message: 'Action not found' } });\n }\n\n // Validate args is an array to prevent prototype pollution\n if (!Array.isArray(args)) {\n return Promise.resolve({ status: 400, body: { message: 'Invalid action arguments' } });\n }\n\n return action.fn(...args)\n .then(result => ({ status: 200, body: result }))\n .catch(error => {\n // Log the full error server-side, return generic message to client\n console.error(`[what] Action \"${actionId}\" error:`, error);\n return {\n status: 500,\n body: { message: 'Action failed' },\n };\n });\n}\n\n// --- Get all registered actions (for SSR/build) ---\n\nexport function getRegisteredActions() {\n return [...actionRegistry.keys()];\n}\n\n// --- Mutation helper ---\n// Like useSWR mutation but simpler.\n\nexport function useMutation(mutationFn, options = {}) {\n const { onSuccess, onError, onSettled } = options;\n\n const state = {\n isPending: signal(false),\n error: signal(null),\n data: signal(null),\n };\n\n async function mutate(...args) {\n state.isPending.set(true);\n state.error.set(null);\n\n try {\n const result = await mutationFn(...args);\n state.data.set(result);\n if (onSuccess) onSuccess(result, ...args);\n return result;\n } catch (error) {\n state.error.set(error);\n if (onError) onError(error, ...args);\n throw error;\n } finally {\n state.isPending.set(false);\n if (onSettled) onSettled(state.data.peek(), state.error.peek(), ...args);\n }\n }\n\n return {\n mutate,\n isPending: () => state.isPending(),\n error: () => state.error(),\n data: () => state.data(),\n reset: () => {\n state.error.set(null);\n state.data.set(null);\n },\n };\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["signal", "batch", "actionRegistry", "getCsrfToken", "meta", "match", "generateCsrfToken", "arr", "b", "validateCsrfToken", "requestToken", "sessionToken", "result", "i", "csrfMetaTag", "token", "_actionCounter", "generateActionId", "action", "fn", "options", "id", "onError", "onSuccess", "revalidate", "callAction", "args", "timeout", "controller", "timeoutId", "csrfToken", "headers", "response", "error", "path", "invalidatePath", "timeoutError", "formAction", "actionFn", "resetOnSuccess", "formDataOrEvent", "formData", "form", "data", "hasFiles", "key", "value", "useAction", "isPending", "trigger", "e", "useFormAction", "formRef", "actionState", "handleSubmit", "useOptimistic", "initialValue", "reducer", "pending", "baseValue", "addOptimistic", "optimisticValue", "resolve", "serverValue", "a", "current", "rollback", "realValue", "newPending", "base", "withOptimistic", "asyncFn", "v", "revalidationCallbacks", "onRevalidate", "callback", "callbacks", "cb", "handleActionRequest", "req", "actionId", "sessionCsrfToken", "skipCsrf", "requestCsrfToken", "getRegisteredActions", "useMutation", "mutationFn", "onSettled", "state", "mutate", "_ssrErrors", "MAX_SSR_ERRORS", "_collectSSRError", "error", "context", "entry", "_isDevMode", "_resetSSRErrors", "serializeSSRErrors", "payload", "e", "hydrateSSRErrors", "el", "errors", "getSSRErrors", "_hydrationIdCounter", "resetHydrationId", "nextHydrationId", "renderToHydratableString", "vnode", "_renderHydratable", "escapeHtml", "hkId", "componentName", "result", "html", "injectHydrationKey", "tag", "props", "children", "attrs", "renderAttrs", "open", "VOID_ELEMENTS", "rawInner", "_resolveInnerHTML", "inner", "match", "prefix", "tagName", "insertAt", "renderToString", "renderToStream", "child", "resolved", "definePage", "config", "generateStaticPage", "page", "data", "islands", "wrapDocument", "title", "meta", "body", "scripts", "styles", "mode", "ssrErrors", "metaTags", "name", "content", "styleTags", "href", "islandScript", "scriptTags", "src", "clientScript", "server", "Component", "out", "key", "val", "
|
|
4
|
+
"sourcesContent": ["// What Framework - Server\n// SSR, static site generation, server components.\n// Zero-JS pages by default. Islands opt-in to client JS.\n\nimport { h } from 'what-core';\n\n// --- SSR Error Collection ---\n// Errors that occur during SSR are collected and serialized into the HTML output\n// so the client can pick them up during hydration and display/report them.\n\nlet _ssrErrors = [];\nconst MAX_SSR_ERRORS = 50;\n\nfunction _collectSSRError(error, context = {}) {\n const entry = {\n code: error.code || 'ERR_SSR_RENDER',\n message: error.message || String(error),\n component: context.component || null,\n timestamp: Date.now(),\n };\n // In dev mode, include extra detail for debugging\n if (_isDevMode) {\n entry.suggestion = error.suggestion || null;\n entry.stack = error.stack?.split('\\n').slice(0, 5).join('\\n') || null;\n }\n _ssrErrors.push(entry);\n if (_ssrErrors.length > MAX_SSR_ERRORS) _ssrErrors.shift();\n}\n\nfunction _resetSSRErrors() {\n _ssrErrors = [];\n}\n\n/**\n * Serialize collected SSR errors into a script tag for client hydration.\n * In dev mode: includes full error details (message, suggestion, stack).\n * In production: includes only error code and component name.\n */\nexport function serializeSSRErrors() {\n if (_ssrErrors.length === 0) return '';\n const payload = _isDevMode\n ? _ssrErrors\n : _ssrErrors.map(e => ({ code: e.code, component: e.component }));\n const json = JSON.stringify(payload).replace(/<\\//g, '<\\\\/'); // prevent XSS via </script>\n return `<script type=\"application/json\" data-what-ssr-errors>${json}</script>`;\n}\n\n/**\n * Read SSR errors from the DOM during client hydration.\n * Call this on the client side during hydration to pick up errors from SSR.\n * Returns an array of error objects, or empty array if none.\n */\nexport function hydrateSSRErrors() {\n if (typeof document === 'undefined') return [];\n const el = document.querySelector('script[data-what-ssr-errors]');\n if (!el) return [];\n try {\n const errors = JSON.parse(el.textContent);\n el.remove(); // clean up after reading\n return errors;\n } catch {\n return [];\n }\n}\n\n/**\n * Get collected SSR errors (for programmatic access before serialization).\n */\nexport function getSSRErrors() {\n return _ssrErrors.slice();\n}\n\n// --- Hydration ID Generator ---\nlet _hydrationIdCounter = 0;\n\nfunction resetHydrationId() {\n _hydrationIdCounter = 0;\n}\n\nfunction nextHydrationId() {\n return 'h' + (_hydrationIdCounter++);\n}\n\n// --- Render to Hydratable String ---\n// Renders with hydration markers (data-hk attributes, comment boundaries)\n// so the client can reuse the server-rendered DOM.\n\nexport function renderToHydratableString(vnode) {\n resetHydrationId();\n _resetSSRErrors();\n return _renderHydratable(vnode);\n}\n\nfunction _renderHydratable(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap\n if (typeof vnode === 'function' && vnode._signal) {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n }\n\n // Reactive function child \u2014 wrap in dynamic content markers\n if (typeof vnode === 'function') {\n try {\n return `<!--$-->${_renderHydratable(vnode())}<!--/$-->`;\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '<!--$--><!--/$-->';\n }\n }\n\n // Array \u2014 wrap in list markers\n if (Array.isArray(vnode)) {\n return `<!--[]-->${renderChildrenToString(vnode, _renderHydratable)}<!--/[]-->`;\n }\n\n // Component \u2014 add hydration key to root element\n if (typeof vnode.tag === 'function') {\n const hkId = nextHydrationId();\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n const html = _renderHydratable(result);\n // Inject data-hk into the first element tag if present\n return injectHydrationKey(html, hkId);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!--ssr-error:${escapeHtml(componentName)}-->`;\n }\n return `<!--ssr-error-->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, _renderHydratable);\n return `${open}${inner}</${tag}>`;\n}\n\n// Inject data-hk=\"id\" into the first HTML opening tag\nfunction injectHydrationKey(html, hkId) {\n // Skip comment markers to find the first real element\n const match = html.match(/^((?:<!--.*?-->)*)<([a-zA-Z][a-zA-Z0-9-]*)/);\n if (match) {\n const prefix = match[1];\n const tagName = match[2];\n const insertAt = prefix.length + 1 + tagName.length; // after '<tagName'\n return html.slice(0, insertAt) + ` data-hk=\"${hkId}\"` + html.slice(insertAt);\n }\n return html;\n}\n\n// --- Render to String ---\n// Renders a VNode tree to an HTML string. Used for SSR and static gen.\n\nexport function renderToString(vnode) {\n if (vnode == null || vnode === false || vnode === true) return '';\n\n // Text\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n return escapeHtml(String(vnode));\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n return renderToString(vnode());\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n return renderToString(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in SSR:', e.message);\n }\n return '';\n }\n }\n\n // Array\n if (Array.isArray(vnode)) {\n return renderChildrenToString(vnode, renderToString);\n }\n\n // Component\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n return renderToString(result);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in SSR:`, e.message);\n return `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message)} -->`;\n }\n return `<!-- SSR Error -->`;\n }\n }\n\n // Element\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n const open = `<${tag}${attrs}>`;\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) return open;\n\n const rawInner = _resolveInnerHTML(props);\n const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, renderToString);\n return `${open}${inner}</${tag}>`;\n}\n\nfunction renderChildrenToString(children, renderChild) {\n let html = '';\n for (let i = 0; i < children.length; i++) {\n html += renderChild(children[i]);\n }\n return html;\n}\n\n// --- Stream Render ---\n// Returns an async iterator for streaming SSR.\n\nexport async function* renderToStream(vnode) {\n if (vnode == null || vnode === false || vnode === true) return;\n\n if (typeof vnode === 'string' || typeof vnode === 'number') {\n yield escapeHtml(String(vnode));\n return;\n }\n\n // Signal \u2014 unwrap by calling it\n if (typeof vnode === 'function' && vnode._signal) {\n yield* renderToStream(vnode());\n return;\n }\n\n // Reactive function child \u2014 call to get value\n if (typeof vnode === 'function') {\n try {\n yield* renderToStream(vnode());\n } catch (e) {\n _collectSSRError(e, { component: 'reactive-function' });\n if (_isDevMode) {\n console.warn('[what-server] Error rendering reactive function in stream SSR:', e.message);\n }\n }\n return;\n }\n\n if (Array.isArray(vnode)) {\n for (const child of vnode) {\n yield* renderToStream(child);\n }\n return;\n }\n\n if (typeof vnode.tag === 'function') {\n const componentName = vnode.tag.displayName || vnode.tag.name || 'Anonymous';\n try {\n const result = vnode.tag({ ...vnode.props, children: vnode.children });\n // Support async components\n const resolved = result instanceof Promise ? await result : result;\n yield* renderToStream(resolved);\n } catch (e) {\n _collectSSRError(e, { component: componentName });\n if (_isDevMode) {\n console.warn(`[what-server] Error rendering component \"${componentName}\" in stream SSR:`, e.message);\n }\n yield _isDevMode\n ? `<!-- SSR Error in ${escapeHtml(componentName)}: ${escapeHtml(e.message || 'Component error')} -->`\n : `<!-- SSR Error -->`;\n }\n return;\n }\n\n const { tag, props, children } = vnode;\n const attrs = renderAttrs(props || {});\n yield `<${tag}${attrs}>`;\n\n if (!VOID_ELEMENTS.has(tag)) {\n const rawInner = _resolveInnerHTML(props);\n if (rawInner != null) {\n yield String(rawInner);\n } else {\n for (const child of children) {\n yield* renderToStream(child);\n }\n }\n yield `</${tag}>`;\n }\n}\n\n// --- Static Site Generation ---\n\nexport function definePage(config) {\n return {\n // 'static' = pre-render at build time (default)\n // 'server' = render on each request\n // 'client' = render in browser (SPA)\n // 'hybrid' = static shell + islands\n mode: 'static',\n ...config,\n };\n}\n\n// Generate static HTML for a page\nexport function generateStaticPage(page, data = {}) {\n _resetSSRErrors();\n const vnode = page.component(data);\n const html = renderToString(vnode);\n const islands = page.islands || [];\n\n return wrapDocument({\n title: page.title || '',\n meta: page.meta || {},\n body: html,\n islands,\n scripts: page.mode === 'static' ? [] : page.scripts || [],\n styles: page.styles || [],\n mode: page.mode,\n ssrErrors: serializeSSRErrors(),\n });\n}\n\nfunction wrapDocument({ title, meta, body, islands, scripts, styles, mode, ssrErrors = '' }) {\n const metaTags = Object.entries(meta)\n .map(([name, content]) => `<meta name=\"${escapeHtml(name)}\" content=\"${escapeHtml(content)}\">`)\n .join('\\n ');\n\n const styleTags = styles\n .map(href => `<link rel=\"stylesheet\" href=\"${escapeHtml(href)}\">`)\n .join('\\n ');\n\n const islandScript = islands.length > 0 ? `\n <script type=\"module\">\n import { hydrateIslands } from '/@what/islands.js';\n hydrateIslands();\n </script>` : '';\n\n const scriptTags = scripts\n .map(src => `<script type=\"module\" src=\"${escapeHtml(src)}\"></script>`)\n .join('\\n ');\n\n const clientScript = mode === 'client' ? `\n <script type=\"module\" src=\"/@what/client.js\"></script>` : '';\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n ${metaTags}\n <title>${escapeHtml(title)}</title>\n ${styleTags}\n </head>\n <body>\n <div id=\"app\">${body}</div>\n ${ssrErrors}\n ${islandScript}\n ${scriptTags}\n ${clientScript}\n </body>\n</html>`;\n}\n\n// --- Server Component ---\n// Renders on the server, sends HTML to client. No JS shipped.\n\nexport function server(Component) {\n Component._server = true;\n return Component;\n}\n\n// --- Helpers ---\n\n// Dev-mode flag for server\nconst _isDevMode = typeof process !== 'undefined'\n ? process.env?.NODE_ENV !== 'production'\n : true;\n\n/**\n * Resolve innerHTML / dangerouslySetInnerHTML from props.\n * Requires { __html: ... } wrapper. Plain string innerHTML is rejected (XSS prevention).\n */\nfunction _resolveInnerHTML(props) {\n if (!props) return null;\n\n // dangerouslySetInnerHTML always requires { __html }\n if (props.dangerouslySetInnerHTML) {\n return props.dangerouslySetInnerHTML.__html ?? null;\n }\n\n // innerHTML with { __html } wrapper \u2014 allowed\n if (props.innerHTML && typeof props.innerHTML === 'object' && '__html' in props.innerHTML) {\n return props.innerHTML.__html ?? null;\n }\n\n // innerHTML as plain string \u2014 reject with warning\n if (props.innerHTML != null && typeof props.innerHTML === 'string') {\n if (_isDevMode) {\n console.warn(\n '[what-server] innerHTML received a raw string. This is a security risk (XSS). ' +\n 'Use innerHTML={{ __html: trustedString }} or dangerouslySetInnerHTML={{ __html: trustedString }} instead.'\n );\n }\n return null;\n }\n\n return null;\n}\n\nfunction renderAttrs(props) {\n let out = '';\n const keys = Object.keys(props);\n for (let i = 0; i < keys.length; i++) {\n const key = keys[i];\n const val = props[key];\n if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;\n if (key.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR\n if (val === false || val == null) continue;\n\n const attr = getHtmlAttributeMetadata(key);\n const attrName = attr.name;\n if (!attr.valid) {\n if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);\n continue;\n }\n if (!isSafeUrlAttributeValue(attr.urlKind, val)) {\n if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute \"${attrName}\": ${val}`);\n continue;\n }\n\n if (key === 'className' || key === 'class') {\n out += ` class=\"${escapeHtml(String(val))}\"`;\n } else if (key === 'style' && typeof val === 'object') {\n const css = Object.entries(val)\n .map(([p, v]) => `${camelToKebab(p)}:${v}`)\n .join(';');\n out += ` style=\"${escapeHtml(css)}\"`;\n } else if (val === true) {\n // ARIA attributes require explicit =\"true\", HTML boolean attrs can be bare\n if (attrName.startsWith('aria-') || attrName === 'role') {\n out += ` ${attrName}=\"true\"`;\n } else {\n out += ` ${attrName}`;\n }\n } else {\n out += ` ${attrName}=\"${escapeHtml(String(val))}\"`;\n }\n }\n\n return out;\n}\n\nfunction getHtmlAttributeName(name) {\n if (name === 'className') return 'class';\n if (name === 'htmlFor') return 'for';\n return name;\n}\n\nconst ATTR_METADATA_CACHE = new Map();\nconst ATTR_METADATA_CACHE_MAX = 2048;\nconst URL_ATTR_KIND_NONE = 0;\nconst URL_ATTR_KIND_SINGLE = 1;\nconst URL_ATTR_KIND_LIST = 2;\n\nfunction getHtmlAttributeMetadata(key) {\n const cached = ATTR_METADATA_CACHE.get(key);\n if (cached) return cached;\n\n const name = getHtmlAttributeName(key);\n const valid = isValidHtmlAttributeName(name);\n const meta = {\n name,\n valid,\n urlKind: valid ? getUrlAttributeKind(name) : URL_ATTR_KIND_NONE,\n };\n\n if (ATTR_METADATA_CACHE.size >= ATTR_METADATA_CACHE_MAX) ATTR_METADATA_CACHE.clear();\n ATTR_METADATA_CACHE.set(key, meta);\n return meta;\n}\n\nfunction isValidHtmlAttributeName(name) {\n return /^[^\\s\"'>/=\\x00-\\x1f\\x7f]+$/.test(name);\n}\n\nconst URL_ATTRS = new Set([\n 'href',\n 'src',\n 'action',\n 'formaction',\n 'poster',\n 'cite',\n 'background',\n 'xlink:href',\n]);\nconst URL_LIST_ATTRS = new Set(['srcset']);\n\nfunction getUrlAttributeKind(name) {\n if (URL_ATTRS.has(name)) return URL_ATTR_KIND_SINGLE;\n if (URL_LIST_ATTRS.has(name)) return URL_ATTR_KIND_LIST;\n if (!hasAsciiUppercase(name)) return URL_ATTR_KIND_NONE;\n\n const normalizedName = name.toLowerCase();\n if (URL_ATTRS.has(normalizedName)) return URL_ATTR_KIND_SINGLE;\n if (URL_LIST_ATTRS.has(normalizedName)) return URL_ATTR_KIND_LIST;\n return URL_ATTR_KIND_NONE;\n}\n\nfunction hasAsciiUppercase(str) {\n for (let i = 0; i < str.length; i++) {\n const code = str.charCodeAt(i);\n if (code >= 65 && code <= 90) return true;\n }\n return false;\n}\n\nfunction isSafeUrlAttributeValue(urlKind, value) {\n if (urlKind === URL_ATTR_KIND_LIST) return isSafeSrcsetValue(value);\n if (urlKind === URL_ATTR_KIND_SINGLE) return isSafeUrlValue(value);\n return true;\n}\n\nfunction isSafeUrlValue(value) {\n if (typeof value !== 'string') return true;\n const normalized = value.trim().replace(/[\\s\\x00-\\x1f\\x7f]/g, '').toLowerCase();\n return !(normalized.startsWith('javascript:') || normalized.startsWith('data:') || normalized.startsWith('vbscript:'));\n}\n\nfunction isSafeSrcsetValue(value) {\n if (typeof value !== 'string') return true;\n return value\n .split(',')\n .every(candidate => {\n const url = candidate.trim().split(/\\s+/, 1)[0] || '';\n return url === '' || isSafeUrlValue(url);\n });\n}\n\nfunction escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\nfunction camelToKebab(str) {\n if (str.startsWith('--')) return str; // CSS custom properties (variables) \u2014 leave unchanged\n return str.replace(/([A-Z])/g, '-$1').toLowerCase();\n}\n\nconst VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// SSR error serialization is exported above:\n// serializeSSRErrors() \u2014 serialize collected errors to script tag\n// hydrateSSRErrors() \u2014 read errors from DOM during client hydration\n// getSSRErrors() \u2014 programmatic access to collected errors\n\n// Re-export server actions\nexport {\n action,\n formAction,\n useAction,\n useFormAction,\n useOptimistic,\n useMutation,\n onRevalidate,\n invalidatePath,\n handleActionRequest,\n getRegisteredActions,\n generateCsrfToken,\n validateCsrfToken,\n csrfMetaTag,\n} from './actions.js';\n", "// What Framework - Server Actions\n// Call server-side functions from client code seamlessly.\n// Similar to Next.js Server Actions / SolidStart server functions.\n//\n// Usage:\n// // Define on server\n// const saveUser = action(async (formData) => {\n// 'use server';\n// const user = await db.users.create(formData);\n// return { success: true, id: user.id };\n// });\n//\n// // Call from client\n// const result = await saveUser({ name: 'John' });\n\nimport { signal, batch } from 'what-core';\n\n// Registry of server actions\nconst actionRegistry = new Map();\n\n// --- CSRF Protection ---\n// Server generates a token per session; client sends it with every action request.\n// The token is injected into the page via a meta tag or embedded in the server response.\n\n// Client: read the CSRF token from the page meta tag or cookie\n// Re-reads on every call to handle token rotation\nfunction getCsrfToken() {\n if (typeof document !== 'undefined') {\n // Try meta tag first\n const meta = document.querySelector('meta[name=\"what-csrf-token\"]');\n if (meta) {\n return meta.getAttribute('content');\n }\n // Try cookie\n const match = document.cookie.match(/(?:^|;\\s*)what-csrf=([^;]+)/);\n if (match) {\n return decodeURIComponent(match[1]);\n }\n }\n return null;\n}\n\n// Server: generate a CSRF token (call this per session/request)\nexport function generateCsrfToken() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n // Fallback for environments without crypto.randomUUID \u2014 use crypto.getRandomValues\n if (typeof crypto !== 'undefined' && crypto.getRandomValues) {\n const arr = new Uint8Array(16);\n crypto.getRandomValues(arr);\n return Array.from(arr, b => b.toString(16).padStart(2, '0')).join('');\n }\n // Last resort \u2014 should not be reached in modern environments\n throw new Error('[what] No secure random source available for CSRF token generation');\n}\n\n// Server: validate CSRF token from request header against session token\nexport function validateCsrfToken(requestToken, sessionToken) {\n if (!requestToken || !sessionToken) return false;\n // Constant-time comparison to prevent timing attacks\n if (requestToken.length !== sessionToken.length) return false;\n let result = 0;\n for (let i = 0; i < requestToken.length; i++) {\n result |= requestToken.charCodeAt(i) ^ sessionToken.charCodeAt(i);\n }\n return result === 0;\n}\n\n// Server: middleware helper to inject CSRF meta tag into HTML\nexport function csrfMetaTag(token) {\n // HTML-escape the token to prevent XSS if a non-standard value is used\n const escaped = String(token).replace(/&/g, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\n return `<meta name=\"what-csrf-token\" content=\"${escaped}\">`;\n}\n\n// --- Define a server action ---\n\nlet _actionCounter = 0;\n\nfunction generateActionId() {\n // Generate a deterministic ID \u2014 prefer crypto.getRandomValues, fall back to a\n // monotonic counter (never Math.random, which is not cryptographically safe and\n // produces predictable IDs in some runtimes).\n const rand = typeof crypto !== 'undefined' && crypto.getRandomValues\n ? Array.from(crypto.getRandomValues(new Uint8Array(6)), b => b.toString(16).padStart(2, '0')).join('')\n : `c${(++_actionCounter).toString(36)}_${Date.now().toString(36)}`;\n return `a_${rand}`;\n}\n\nexport function action(fn, options = {}) {\n const id = options.id || generateActionId();\n const { onError, onSuccess, revalidate } = options;\n\n // Server-side: register the action\n if (typeof window === 'undefined') {\n actionRegistry.set(id, { fn, options });\n }\n\n // Create the callable wrapper\n async function callAction(...args) {\n // Server-side: call directly\n if (typeof window === 'undefined') {\n return fn(...args);\n }\n\n // Client-side: call via fetch with timeout support\n const timeout = options.timeout || 30000; // Default 30s timeout\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const csrfToken = getCsrfToken();\n const headers = {\n 'Content-Type': 'application/json',\n 'X-What-Action': id,\n };\n if (csrfToken) headers['X-CSRF-Token'] = csrfToken;\n\n const response = await fetch('/__what_action', {\n method: 'POST',\n headers,\n credentials: 'same-origin',\n signal: controller.signal,\n body: JSON.stringify({ args }),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({ message: 'Action failed' }));\n throw new Error(error.message || 'Action failed');\n }\n\n const result = await response.json();\n\n if (onSuccess) onSuccess(result);\n if (revalidate) {\n // Trigger revalidation of specified paths\n for (const path of revalidate) {\n invalidatePath(path);\n }\n }\n\n return result;\n } catch (error) {\n if (error.name === 'AbortError') {\n const timeoutError = new Error(`Action \"${id}\" timed out after ${timeout}ms`);\n timeoutError.code = 'TIMEOUT';\n if (onError) onError(timeoutError);\n throw timeoutError;\n }\n if (onError) onError(error);\n throw error;\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n callAction._actionId = id;\n callAction._isAction = true;\n\n return callAction;\n}\n\n// --- Form action helper ---\n// For forms that submit to server actions.\n\nexport function formAction(actionFn, options = {}) {\n const { onSuccess, onError, resetOnSuccess = true } = options;\n\n return async (formDataOrEvent) => {\n let formData;\n let form;\n\n if (formDataOrEvent instanceof Event) {\n formDataOrEvent.preventDefault();\n form = formDataOrEvent.target;\n formData = new FormData(form);\n } else {\n formData = formDataOrEvent;\n }\n\n // Convert FormData to plain object, preserving File instances\n const data = {};\n let hasFiles = false;\n for (const [key, value] of formData.entries()) {\n if (typeof File !== 'undefined' && value instanceof File) {\n hasFiles = true;\n }\n if (data[key]) {\n // Handle multiple values (e.g., checkboxes, multi-file inputs)\n if (Array.isArray(data[key])) {\n data[key].push(value);\n } else {\n data[key] = [data[key], value];\n }\n } else {\n data[key] = value;\n }\n }\n\n try {\n // If form contains files, pass the raw FormData as second arg\n // so the action handler can access files directly\n const result = hasFiles\n ? await actionFn(data, formData)\n : await actionFn(data);\n if (onSuccess) onSuccess(result, form);\n if (resetOnSuccess && form) form.reset();\n return result;\n } catch (error) {\n if (onError) onError(error, form);\n throw error;\n }\n };\n}\n\n// --- useAction hook ---\n// Returns action state and trigger function.\n\nexport function useAction(actionFn) {\n const isPending = signal(false);\n const error = signal(null);\n const data = signal(null);\n\n async function trigger(...args) {\n isPending.set(true);\n error.set(null);\n\n try {\n const result = await actionFn(...args);\n data.set(result);\n return result;\n } catch (e) {\n error.set(e);\n throw e;\n } finally {\n isPending.set(false);\n }\n }\n\n return {\n trigger,\n isPending: () => isPending(),\n error: () => error(),\n data: () => data(),\n reset: () => {\n error.set(null);\n data.set(null);\n },\n };\n}\n\n// --- useFormAction hook ---\n// Combines useAction with form handling.\n\nexport function useFormAction(actionFn, options = {}) {\n const { resetOnSuccess = true } = options;\n const formRef = { current: null };\n const actionState = useAction(formAction(actionFn, { resetOnSuccess }));\n\n function handleSubmit(e) {\n e.preventDefault();\n const formData = new FormData(e.target);\n formRef.current = e.target;\n return actionState.trigger(formData);\n }\n\n return {\n ...actionState,\n handleSubmit,\n formRef,\n };\n}\n\n// --- Optimistic updates ---\n\nexport function useOptimistic(initialValue, reducer) {\n const value = signal(initialValue);\n const pending = signal([]);\n const baseValue = signal(initialValue); // Track the confirmed server value\n\n function addOptimistic(action) {\n const optimisticValue = reducer(value.peek(), action);\n batch(() => {\n pending.set([...pending.peek(), action]);\n value.set(optimisticValue);\n });\n }\n\n function resolve(action, serverValue) {\n batch(() => {\n pending.set(pending.peek().filter(a => a !== action));\n if (serverValue !== undefined) {\n baseValue.set(serverValue);\n // Recompute optimistic state from new base + remaining pending actions\n let current = serverValue;\n for (const a of pending.peek()) {\n current = reducer(current, a);\n }\n value.set(current);\n }\n });\n }\n\n function rollback(action, realValue) {\n batch(() => {\n const newPending = pending.peek().filter(a => a !== action);\n pending.set(newPending);\n const base = realValue !== undefined ? realValue : baseValue.peek();\n baseValue.set(base);\n // Recompute from base + remaining pending actions\n let current = base;\n for (const a of newPending) {\n current = reducer(current, a);\n }\n value.set(current);\n });\n }\n\n // Auto-rollback helper: wraps an async action with automatic rollback on error\n async function withOptimistic(action, asyncFn) {\n addOptimistic(action);\n try {\n const result = await asyncFn();\n resolve(action, result);\n return result;\n } catch (e) {\n rollback(action);\n throw e;\n }\n }\n\n return {\n value: () => value(),\n isPending: () => pending().length > 0,\n addOptimistic,\n resolve,\n rollback,\n withOptimistic,\n set: (v) => { value.set(v); baseValue.set(v); },\n };\n}\n\n// --- Path revalidation ---\n\nconst revalidationCallbacks = new Map();\n\nexport function onRevalidate(path, callback) {\n if (!revalidationCallbacks.has(path)) {\n revalidationCallbacks.set(path, new Set());\n }\n revalidationCallbacks.get(path).add(callback);\n\n return () => {\n revalidationCallbacks.get(path)?.delete(callback);\n };\n}\n\nexport function invalidatePath(path) {\n const callbacks = revalidationCallbacks.get(path);\n if (callbacks) {\n for (const cb of callbacks) {\n try { cb(); } catch (e) { console.error('[what] Revalidation error:', e); }\n }\n }\n}\n\n// --- Server-side action handler ---\n// Add this to your server middleware.\n\nexport function handleActionRequest(req, actionId, args, options = {}) {\n const { csrfToken: sessionCsrfToken, skipCsrf = false } = options;\n\n // Validate CSRF token unless explicitly skipped\n if (!skipCsrf) {\n if (!sessionCsrfToken) {\n // Fail closed: no CSRF token configured means the developer forgot to set it up.\n // This prevents silent security vulnerabilities in production.\n return Promise.resolve({\n status: 500,\n body: {\n message: '[what] CSRF token not configured. ' +\n 'Pass { csrfToken: sessionToken } to handleActionRequest, ' +\n 'or { skipCsrf: true } to explicitly opt out.'\n }\n });\n }\n const requestCsrfToken = req?.headers?.['x-csrf-token'] || req?.headers?.['X-CSRF-Token'];\n if (!validateCsrfToken(requestCsrfToken, sessionCsrfToken)) {\n return Promise.resolve({ status: 403, body: { message: 'Invalid CSRF token' } });\n }\n }\n\n const action = actionRegistry.get(actionId);\n if (!action) {\n return Promise.resolve({ status: 404, body: { message: 'Action not found' } });\n }\n\n // Validate args is an array to prevent prototype pollution\n if (!Array.isArray(args)) {\n return Promise.resolve({ status: 400, body: { message: 'Invalid action arguments' } });\n }\n\n return action.fn(...args)\n .then(result => ({ status: 200, body: result }))\n .catch(error => {\n // Log the full error server-side, return generic message to client\n console.error(`[what] Action \"${actionId}\" error:`, error);\n return {\n status: 500,\n body: { message: 'Action failed' },\n };\n });\n}\n\n// --- Get all registered actions (for SSR/build) ---\n\nexport function getRegisteredActions() {\n return [...actionRegistry.keys()];\n}\n\n// --- Mutation helper ---\n// Like useSWR mutation but simpler.\n\nexport function useMutation(mutationFn, options = {}) {\n const { onSuccess, onError, onSettled } = options;\n\n const state = {\n isPending: signal(false),\n error: signal(null),\n data: signal(null),\n };\n\n async function mutate(...args) {\n state.isPending.set(true);\n state.error.set(null);\n\n try {\n const result = await mutationFn(...args);\n state.data.set(result);\n if (onSuccess) onSuccess(result, ...args);\n return result;\n } catch (error) {\n state.error.set(error);\n if (onError) onError(error, ...args);\n throw error;\n } finally {\n state.isPending.set(false);\n if (onSettled) onSettled(state.data.peek(), state.error.peek(), ...args);\n }\n }\n\n return {\n mutate,\n isPending: () => state.isPending(),\n error: () => state.error(),\n data: () => state.data(),\n reset: () => {\n state.error.set(null);\n state.data.set(null);\n },\n };\n}\n"],
|
|
5
|
+
"mappings": "AAIA,MAAkB,YCWlB,OAAS,UAAAA,EAAQ,SAAAC,MAAa,YAG9B,IAAMC,EAAiB,IAAI,IAQ3B,SAASC,GAAe,CACtB,GAAI,OAAO,SAAa,IAAa,CAEnC,IAAMC,EAAO,SAAS,cAAc,8BAA8B,EAClE,GAAIA,EACF,OAAOA,EAAK,aAAa,SAAS,EAGpC,IAAMC,EAAQ,SAAS,OAAO,MAAM,6BAA6B,EACjE,GAAIA,EACF,OAAO,mBAAmBA,EAAM,CAAC,CAAC,CAEtC,CACA,OAAO,IACT,CAGO,SAASC,GAAoB,CAClC,GAAI,OAAO,OAAW,KAAe,OAAO,WAC1C,OAAO,OAAO,WAAW,EAG3B,GAAI,OAAO,OAAW,KAAe,OAAO,gBAAiB,CAC3D,IAAMC,EAAM,IAAI,WAAW,EAAE,EAC7B,cAAO,gBAAgBA,CAAG,EACnB,MAAM,KAAKA,EAAKC,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CACtE,CAEA,MAAM,IAAI,MAAM,oEAAoE,CACtF,CAGO,SAASC,EAAkBC,EAAcC,EAAc,CAG5D,GAFI,CAACD,GAAgB,CAACC,GAElBD,EAAa,SAAWC,EAAa,OAAQ,MAAO,GACxD,IAAIC,EAAS,EACb,QAASC,EAAI,EAAGA,EAAIH,EAAa,OAAQG,IACvCD,GAAUF,EAAa,WAAWG,CAAC,EAAIF,EAAa,WAAWE,CAAC,EAElE,OAAOD,IAAW,CACpB,CAGO,SAASE,EAAYC,EAAO,CAGjC,MAAO,yCADS,OAAOA,CAAK,EAAE,QAAQ,KAAM,OAAO,EAAE,QAAQ,KAAM,QAAQ,EAAE,QAAQ,KAAM,MAAM,EAAE,QAAQ,KAAM,MAAM,CAChE,IACzD,CAIA,IAAIC,EAAiB,EAErB,SAASC,GAAmB,CAO1B,MAAO,KAHM,OAAO,OAAW,KAAe,OAAO,gBACjD,MAAM,KAAK,OAAO,gBAAgB,IAAI,WAAW,CAAC,CAAC,EAAGT,GAAKA,EAAE,SAAS,EAAE,EAAE,SAAS,EAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EACnG,KAAK,EAAEQ,GAAgB,SAAS,EAAE,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC,EAClD,EAClB,CAEO,SAASE,EAAOC,EAAIC,EAAU,CAAC,EAAG,CACvC,IAAMC,EAAKD,EAAQ,IAAMH,EAAiB,EACpC,CAAE,QAAAK,EAAS,UAAAC,EAAW,WAAAC,CAAW,EAAIJ,EAGvC,OAAO,OAAW,KACpBlB,EAAe,IAAImB,EAAI,CAAE,GAAAF,EAAI,QAAAC,CAAQ,CAAC,EAIxC,eAAeK,KAAcC,EAAM,CAEjC,GAAI,OAAO,OAAW,IACpB,OAAOP,EAAG,GAAGO,CAAI,EAInB,IAAMC,EAAUP,EAAQ,SAAW,IAC7BQ,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAM,EAAGD,CAAO,EAE9D,GAAI,CACF,IAAMG,EAAY3B,EAAa,EACzB4B,EAAU,CACd,eAAgB,mBAChB,gBAAiBV,CACnB,EACIS,IAAWC,EAAQ,cAAc,EAAID,GAEzC,IAAME,EAAW,MAAM,MAAM,iBAAkB,CAC7C,OAAQ,OACR,QAAAD,EACA,YAAa,cACb,OAAQH,EAAW,OACnB,KAAM,KAAK,UAAU,CAAE,KAAAF,CAAK,CAAC,CAC/B,CAAC,EAED,GAAI,CAACM,EAAS,GAAI,CAChB,IAAMC,EAAQ,MAAMD,EAAS,KAAK,EAAE,MAAM,KAAO,CAAE,QAAS,eAAgB,EAAE,EAC9E,MAAM,IAAI,MAAMC,EAAM,SAAW,eAAe,CAClD,CAEA,IAAMrB,EAAS,MAAMoB,EAAS,KAAK,EAGnC,GADIT,GAAWA,EAAUX,CAAM,EAC3BY,EAEF,QAAWU,KAAQV,EACjBW,EAAeD,CAAI,EAIvB,OAAOtB,CACT,OAASqB,EAAO,CACd,GAAIA,EAAM,OAAS,aAAc,CAC/B,IAAMG,EAAe,IAAI,MAAM,WAAWf,CAAE,qBAAqBM,CAAO,IAAI,EAC5E,MAAAS,EAAa,KAAO,UAChBd,GAASA,EAAQc,CAAY,EAC3BA,CACR,CACA,MAAId,GAASA,EAAQW,CAAK,EACpBA,CACR,QAAE,CACA,aAAaJ,CAAS,CACxB,CACF,CAEA,OAAAJ,EAAW,UAAYJ,EACvBI,EAAW,UAAY,GAEhBA,CACT,CAKO,SAASY,EAAWC,EAAUlB,EAAU,CAAC,EAAG,CACjD,GAAM,CAAE,UAAAG,EAAW,QAAAD,EAAS,eAAAiB,EAAiB,EAAK,EAAInB,EAEtD,MAAO,OAAOoB,GAAoB,CAChC,IAAIC,EACAC,EAEAF,aAA2B,OAC7BA,EAAgB,eAAe,EAC/BE,EAAOF,EAAgB,OACvBC,EAAW,IAAI,SAASC,CAAI,GAE5BD,EAAWD,EAIb,IAAMG,EAAO,CAAC,EACVC,EAAW,GACf,OAAW,CAACC,EAAKC,CAAK,IAAKL,EAAS,QAAQ,EACtC,OAAO,KAAS,KAAeK,aAAiB,OAClDF,EAAW,IAETD,EAAKE,CAAG,EAEN,MAAM,QAAQF,EAAKE,CAAG,CAAC,EACzBF,EAAKE,CAAG,EAAE,KAAKC,CAAK,EAEpBH,EAAKE,CAAG,EAAI,CAACF,EAAKE,CAAG,EAAGC,CAAK,EAG/BH,EAAKE,CAAG,EAAIC,EAIhB,GAAI,CAGF,IAAMlC,EAASgC,EACX,MAAMN,EAASK,EAAMF,CAAQ,EAC7B,MAAMH,EAASK,CAAI,EACvB,OAAIpB,GAAWA,EAAUX,EAAQ8B,CAAI,EACjCH,GAAkBG,GAAMA,EAAK,MAAM,EAChC9B,CACT,OAASqB,EAAO,CACd,MAAIX,GAASA,EAAQW,EAAOS,CAAI,EAC1BT,CACR,CACF,CACF,CAKO,SAASc,EAAUT,EAAU,CAClC,IAAMU,EAAYhD,EAAO,EAAK,EACxBiC,EAAQjC,EAAO,IAAI,EACnB2C,EAAO3C,EAAO,IAAI,EAExB,eAAeiD,KAAWvB,EAAM,CAC9BsB,EAAU,IAAI,EAAI,EAClBf,EAAM,IAAI,IAAI,EAEd,GAAI,CACF,IAAMrB,EAAS,MAAM0B,EAAS,GAAGZ,CAAI,EACrC,OAAAiB,EAAK,IAAI/B,CAAM,EACRA,CACT,OAASsC,EAAG,CACV,MAAAjB,EAAM,IAAIiB,CAAC,EACLA,CACR,QAAE,CACAF,EAAU,IAAI,EAAK,CACrB,CACF,CAEA,MAAO,CACL,QAAAC,EACA,UAAW,IAAMD,EAAU,EAC3B,MAAO,IAAMf,EAAM,EACnB,KAAM,IAAMU,EAAK,EACjB,MAAO,IAAM,CACXV,EAAM,IAAI,IAAI,EACdU,EAAK,IAAI,IAAI,CACf,CACF,CACF,CAKO,SAASQ,EAAcb,EAAUlB,EAAU,CAAC,EAAG,CACpD,GAAM,CAAE,eAAAmB,EAAiB,EAAK,EAAInB,EAC5BgC,EAAU,CAAE,QAAS,IAAK,EAC1BC,EAAcN,EAAUV,EAAWC,EAAU,CAAE,eAAAC,CAAe,CAAC,CAAC,EAEtE,SAASe,EAAaJ,EAAG,CACvBA,EAAE,eAAe,EACjB,IAAMT,EAAW,IAAI,SAASS,EAAE,MAAM,EACtC,OAAAE,EAAQ,QAAUF,EAAE,OACbG,EAAY,QAAQZ,CAAQ,CACrC,CAEA,MAAO,CACL,GAAGY,EACH,aAAAC,EACA,QAAAF,CACF,CACF,CAIO,SAASG,EAAcC,EAAcC,EAAS,CACnD,IAAMX,EAAQ9C,EAAOwD,CAAY,EAC3BE,EAAU1D,EAAO,CAAC,CAAC,EACnB2D,EAAY3D,EAAOwD,CAAY,EAErC,SAASI,EAAc1C,EAAQ,CAC7B,IAAM2C,EAAkBJ,EAAQX,EAAM,KAAK,EAAG5B,CAAM,EACpDjB,EAAM,IAAM,CACVyD,EAAQ,IAAI,CAAC,GAAGA,EAAQ,KAAK,EAAGxC,CAAM,CAAC,EACvC4B,EAAM,IAAIe,CAAe,CAC3B,CAAC,CACH,CAEA,SAASC,EAAQ5C,EAAQ6C,EAAa,CACpC9D,EAAM,IAAM,CAEV,GADAyD,EAAQ,IAAIA,EAAQ,KAAK,EAAE,OAAOM,GAAKA,IAAM9C,CAAM,CAAC,EAChD6C,IAAgB,OAAW,CAC7BJ,EAAU,IAAII,CAAW,EAEzB,IAAIE,EAAUF,EACd,QAAWC,KAAKN,EAAQ,KAAK,EAC3BO,EAAUR,EAAQQ,EAASD,CAAC,EAE9BlB,EAAM,IAAImB,CAAO,CACnB,CACF,CAAC,CACH,CAEA,SAASC,EAAShD,EAAQiD,EAAW,CACnClE,EAAM,IAAM,CACV,IAAMmE,EAAaV,EAAQ,KAAK,EAAE,OAAOM,GAAKA,IAAM9C,CAAM,EAC1DwC,EAAQ,IAAIU,CAAU,EACtB,IAAMC,EAAOF,IAAc,OAAYA,EAAYR,EAAU,KAAK,EAClEA,EAAU,IAAIU,CAAI,EAElB,IAAIJ,EAAUI,EACd,QAAWL,KAAKI,EACdH,EAAUR,EAAQQ,EAASD,CAAC,EAE9BlB,EAAM,IAAImB,CAAO,CACnB,CAAC,CACH,CAGA,eAAeK,EAAepD,EAAQqD,EAAS,CAC7CX,EAAc1C,CAAM,EACpB,GAAI,CACF,IAAMN,EAAS,MAAM2D,EAAQ,EAC7B,OAAAT,EAAQ5C,EAAQN,CAAM,EACfA,CACT,OAASsC,EAAG,CACV,MAAAgB,EAAShD,CAAM,EACTgC,CACR,CACF,CAEA,MAAO,CACL,MAAO,IAAMJ,EAAM,EACnB,UAAW,IAAMY,EAAQ,EAAE,OAAS,EACpC,cAAAE,EACA,QAAAE,EACA,SAAAI,EACA,eAAAI,EACA,IAAME,GAAM,CAAE1B,EAAM,IAAI0B,CAAC,EAAGb,EAAU,IAAIa,CAAC,CAAG,CAChD,CACF,CAIA,IAAMC,EAAwB,IAAI,IAE3B,SAASC,EAAaxC,EAAMyC,EAAU,CAC3C,OAAKF,EAAsB,IAAIvC,CAAI,GACjCuC,EAAsB,IAAIvC,EAAM,IAAI,GAAK,EAE3CuC,EAAsB,IAAIvC,CAAI,EAAE,IAAIyC,CAAQ,EAErC,IAAM,CACXF,EAAsB,IAAIvC,CAAI,GAAG,OAAOyC,CAAQ,CAClD,CACF,CAEO,SAASxC,EAAeD,EAAM,CACnC,IAAM0C,EAAYH,EAAsB,IAAIvC,CAAI,EAChD,GAAI0C,EACF,QAAWC,KAAMD,EACf,GAAI,CAAEC,EAAG,CAAG,OAAS3B,EAAG,CAAE,QAAQ,MAAM,6BAA8BA,CAAC,CAAG,CAGhF,CAKO,SAAS4B,EAAoBC,EAAKC,EAAUtD,EAAMN,EAAU,CAAC,EAAG,CACrE,GAAM,CAAE,UAAW6D,EAAkB,SAAAC,EAAW,EAAM,EAAI9D,EAG1D,GAAI,CAAC8D,EAAU,CACb,GAAI,CAACD,EAGH,OAAO,QAAQ,QAAQ,CACrB,OAAQ,IACR,KAAM,CACJ,QAAS,yIAGX,CACF,CAAC,EAEH,IAAME,EAAmBJ,GAAK,UAAU,cAAc,GAAKA,GAAK,UAAU,cAAc,EACxF,GAAI,CAACtE,EAAkB0E,EAAkBF,CAAgB,EACvD,OAAO,QAAQ,QAAQ,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,oBAAqB,CAAE,CAAC,CAEnF,CAEA,IAAM/D,EAAShB,EAAe,IAAI8E,CAAQ,EAC1C,OAAK9D,EAKA,MAAM,QAAQQ,CAAI,EAIhBR,EAAO,GAAG,GAAGQ,CAAI,EACrB,KAAKd,IAAW,CAAE,OAAQ,IAAK,KAAMA,CAAO,EAAE,EAC9C,MAAMqB,IAEL,QAAQ,MAAM,kBAAkB+C,CAAQ,WAAY/C,CAAK,EAClD,CACL,OAAQ,IACR,KAAM,CAAE,QAAS,eAAgB,CACnC,EACD,EAZM,QAAQ,QAAQ,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,0BAA2B,CAAE,CAAC,EAL9E,QAAQ,QAAQ,CAAE,OAAQ,IAAK,KAAM,CAAE,QAAS,kBAAmB,CAAE,CAAC,CAkBjF,CAIO,SAASmD,IAAuB,CACrC,MAAO,CAAC,GAAGlF,EAAe,KAAK,CAAC,CAClC,CAKO,SAASmF,GAAYC,EAAYlE,EAAU,CAAC,EAAG,CACpD,GAAM,CAAE,UAAAG,EAAW,QAAAD,EAAS,UAAAiE,CAAU,EAAInE,EAEpCoE,EAAQ,CACZ,UAAWxF,EAAO,EAAK,EACvB,MAAOA,EAAO,IAAI,EAClB,KAAMA,EAAO,IAAI,CACnB,EAEA,eAAeyF,KAAU/D,EAAM,CAC7B8D,EAAM,UAAU,IAAI,EAAI,EACxBA,EAAM,MAAM,IAAI,IAAI,EAEpB,GAAI,CACF,IAAM5E,EAAS,MAAM0E,EAAW,GAAG5D,CAAI,EACvC,OAAA8D,EAAM,KAAK,IAAI5E,CAAM,EACjBW,GAAWA,EAAUX,EAAQ,GAAGc,CAAI,EACjCd,CACT,OAASqB,EAAO,CACd,MAAAuD,EAAM,MAAM,IAAIvD,CAAK,EACjBX,GAASA,EAAQW,EAAO,GAAGP,CAAI,EAC7BO,CACR,QAAE,CACAuD,EAAM,UAAU,IAAI,EAAK,EACrBD,GAAWA,EAAUC,EAAM,KAAK,KAAK,EAAGA,EAAM,MAAM,KAAK,EAAG,GAAG9D,CAAI,CACzE,CACF,CAEA,MAAO,CACL,OAAA+D,EACA,UAAW,IAAMD,EAAM,UAAU,EACjC,MAAO,IAAMA,EAAM,MAAM,EACzB,KAAM,IAAMA,EAAM,KAAK,EACvB,MAAO,IAAM,CACXA,EAAM,MAAM,IAAI,IAAI,EACpBA,EAAM,KAAK,IAAI,IAAI,CACrB,CACF,CACF,CDpcA,IAAIE,EAAa,CAAC,EACZC,GAAiB,GAEvB,SAASC,EAAiBC,EAAOC,EAAU,CAAC,EAAG,CAC7C,IAAMC,EAAQ,CACZ,KAAMF,EAAM,MAAQ,iBACpB,QAASA,EAAM,SAAW,OAAOA,CAAK,EACtC,UAAWC,EAAQ,WAAa,KAChC,UAAW,KAAK,IAAI,CACtB,EAEIE,IACFD,EAAM,WAAaF,EAAM,YAAc,KACvCE,EAAM,MAAQF,EAAM,OAAO,MAAM;AAAA,CAAI,EAAE,MAAM,EAAG,CAAC,EAAE,KAAK;AAAA,CAAI,GAAK,MAEnEH,EAAW,KAAKK,CAAK,EACjBL,EAAW,OAASC,IAAgBD,EAAW,MAAM,CAC3D,CAEA,SAASO,GAAkB,CACzBP,EAAa,CAAC,CAChB,CAOO,SAASQ,IAAqB,CACnC,GAAIR,EAAW,SAAW,EAAG,MAAO,GACpC,IAAMS,EAAUH,EACZN,EACAA,EAAW,IAAIU,IAAM,CAAE,KAAMA,EAAE,KAAM,UAAWA,EAAE,SAAU,EAAE,EAElE,MAAO,wDADM,KAAK,UAAUD,CAAO,EAAE,QAAQ,OAAQ,MAAM,CACQ,YACrE,CAOO,SAASE,IAAmB,CACjC,GAAI,OAAO,SAAa,IAAa,MAAO,CAAC,EAC7C,IAAMC,EAAK,SAAS,cAAc,8BAA8B,EAChE,GAAI,CAACA,EAAI,MAAO,CAAC,EACjB,GAAI,CACF,IAAMC,EAAS,KAAK,MAAMD,EAAG,WAAW,EACxC,OAAAA,EAAG,OAAO,EACHC,CACT,MAAQ,CACN,MAAO,CAAC,CACV,CACF,CAKO,SAASC,IAAe,CAC7B,OAAOd,EAAW,MAAM,CAC1B,CAGA,IAAIe,EAAsB,EAE1B,SAASC,IAAmB,CAC1BD,EAAsB,CACxB,CAEA,SAASE,IAAkB,CACzB,MAAO,IAAOF,GAChB,CAMO,SAASG,GAAyBC,EAAO,CAC9C,OAAAH,GAAiB,EACjBT,EAAgB,EACTa,EAAkBD,CAAK,CAChC,CAEA,SAASC,EAAkBD,EAAO,CAChC,GAAIA,GAAS,MAAQA,IAAU,IAASA,IAAU,GAAM,MAAO,GAG/D,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,SAChD,OAAOE,EAAW,OAAOF,CAAK,CAAC,EAIjC,GAAI,OAAOA,GAAU,YAAcA,EAAM,QACvC,MAAO,WAAWC,EAAkBD,EAAM,CAAC,CAAC,YAI9C,GAAI,OAAOA,GAAU,WACnB,GAAI,CACF,MAAO,WAAWC,EAAkBD,EAAM,CAAC,CAAC,WAC9C,OAAST,EAAG,CACV,OAAAR,EAAiBQ,EAAG,CAAE,UAAW,mBAAoB,CAAC,EAClDJ,GACF,QAAQ,KAAK,0DAA2DI,EAAE,OAAO,EAE5E,mBACT,CAIF,GAAI,MAAM,QAAQS,CAAK,EACrB,MAAO,YAAYG,EAAuBH,EAAOC,CAAiB,CAAC,aAIrE,GAAI,OAAOD,EAAM,KAAQ,WAAY,CACnC,IAAMI,EAAON,GAAgB,EACvBO,EAAgBL,EAAM,IAAI,aAAeA,EAAM,IAAI,MAAQ,YACjE,GAAI,CACF,IAAMM,EAASN,EAAM,IAAI,CAAE,GAAGA,EAAM,MAAO,SAAUA,EAAM,QAAS,CAAC,EAC/DO,EAAON,EAAkBK,CAAM,EAErC,OAAOE,GAAmBD,EAAMH,CAAI,CACtC,OAASb,EAAG,CAEV,OADAR,EAAiBQ,EAAG,CAAE,UAAWc,CAAc,CAAC,EAC5ClB,GACF,QAAQ,KAAK,4CAA4CkB,CAAa,YAAad,EAAE,OAAO,EACrF,iBAAiBW,EAAWG,CAAa,CAAC,OAE5C,kBACT,CACF,CAGA,GAAM,CAAE,IAAAI,EAAK,MAAAC,EAAO,SAAAC,CAAS,EAAIX,EAC3BY,EAAQC,EAAYH,GAAS,CAAC,CAAC,EAC/BI,EAAO,IAAIL,CAAG,GAAGG,CAAK,IAG5B,GAAIG,EAAc,IAAIN,CAAG,EAAG,OAAOK,EAEnC,IAAME,EAAWC,EAAkBP,CAAK,EAClCQ,EAAQF,GAAY,KAAO,OAAOA,CAAQ,EAAIb,EAAuBQ,EAAUV,CAAiB,EACtG,MAAO,GAAGa,CAAI,GAAGI,CAAK,KAAKT,CAAG,GAChC,CAGA,SAASD,GAAmBD,EAAMH,EAAM,CAEtC,IAAMe,EAAQZ,EAAK,MAAM,4CAA4C,EACrE,GAAIY,EAAO,CACT,IAAMC,EAASD,EAAM,CAAC,EAChBE,EAAUF,EAAM,CAAC,EACjBG,EAAWF,EAAO,OAAS,EAAIC,EAAQ,OAC7C,OAAOd,EAAK,MAAM,EAAGe,CAAQ,EAAI,aAAalB,CAAI,IAAMG,EAAK,MAAMe,CAAQ,CAC7E,CACA,OAAOf,CACT,CAKO,SAASgB,EAAevB,EAAO,CACpC,GAAIA,GAAS,MAAQA,IAAU,IAASA,IAAU,GAAM,MAAO,GAG/D,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,SAChD,OAAOE,EAAW,OAAOF,CAAK,CAAC,EAIjC,GAAI,OAAOA,GAAU,YAAcA,EAAM,QACvC,OAAOuB,EAAevB,EAAM,CAAC,EAI/B,GAAI,OAAOA,GAAU,WACnB,GAAI,CACF,OAAOuB,EAAevB,EAAM,CAAC,CAC/B,OAAST,EAAG,CACV,OAAAR,EAAiBQ,EAAG,CAAE,UAAW,mBAAoB,CAAC,EAClDJ,GACF,QAAQ,KAAK,0DAA2DI,EAAE,OAAO,EAE5E,EACT,CAIF,GAAI,MAAM,QAAQS,CAAK,EACrB,OAAOG,EAAuBH,EAAOuB,CAAc,EAIrD,GAAI,OAAOvB,EAAM,KAAQ,WAAY,CACnC,IAAMK,EAAgBL,EAAM,IAAI,aAAeA,EAAM,IAAI,MAAQ,YACjE,GAAI,CACF,IAAMM,EAASN,EAAM,IAAI,CAAE,GAAGA,EAAM,MAAO,SAAUA,EAAM,QAAS,CAAC,EACrE,OAAOuB,EAAejB,CAAM,CAC9B,OAASf,EAAG,CAEV,OADAR,EAAiBQ,EAAG,CAAE,UAAWc,CAAc,CAAC,EAC5ClB,GACF,QAAQ,KAAK,4CAA4CkB,CAAa,YAAad,EAAE,OAAO,EACrF,qBAAqBW,EAAWG,CAAa,CAAC,KAAKH,EAAWX,EAAE,OAAO,CAAC,QAE1E,oBACT,CACF,CAGA,GAAM,CAAE,IAAAkB,EAAK,MAAAC,EAAO,SAAAC,CAAS,EAAIX,EAC3BY,EAAQC,EAAYH,GAAS,CAAC,CAAC,EAC/BI,EAAO,IAAIL,CAAG,GAAGG,CAAK,IAG5B,GAAIG,EAAc,IAAIN,CAAG,EAAG,OAAOK,EAEnC,IAAME,EAAWC,EAAkBP,CAAK,EAClCQ,EAAQF,GAAY,KAAO,OAAOA,CAAQ,EAAIb,EAAuBQ,EAAUY,CAAc,EACnG,MAAO,GAAGT,CAAI,GAAGI,CAAK,KAAKT,CAAG,GAChC,CAEA,SAASN,EAAuBQ,EAAUa,EAAa,CACrD,IAAIjB,EAAO,GACX,QAASkB,EAAI,EAAGA,EAAId,EAAS,OAAQc,IACnClB,GAAQiB,EAAYb,EAASc,CAAC,CAAC,EAEjC,OAAOlB,CACT,CAKA,eAAuBmB,EAAe1B,EAAO,CAC3C,GAAIA,GAAS,MAAQA,IAAU,IAASA,IAAU,GAAM,OAExD,GAAI,OAAOA,GAAU,UAAY,OAAOA,GAAU,SAAU,CAC1D,MAAME,EAAW,OAAOF,CAAK,CAAC,EAC9B,MACF,CAGA,GAAI,OAAOA,GAAU,YAAcA,EAAM,QAAS,CAChD,MAAO0B,EAAe1B,EAAM,CAAC,EAC7B,MACF,CAGA,GAAI,OAAOA,GAAU,WAAY,CAC/B,GAAI,CACF,MAAO0B,EAAe1B,EAAM,CAAC,CAC/B,OAAST,EAAG,CACVR,EAAiBQ,EAAG,CAAE,UAAW,mBAAoB,CAAC,EAClDJ,GACF,QAAQ,KAAK,iEAAkEI,EAAE,OAAO,CAE5F,CACA,MACF,CAEA,GAAI,MAAM,QAAQS,CAAK,EAAG,CACxB,QAAW2B,KAAS3B,EAClB,MAAO0B,EAAeC,CAAK,EAE7B,MACF,CAEA,GAAI,OAAO3B,EAAM,KAAQ,WAAY,CACnC,IAAMK,EAAgBL,EAAM,IAAI,aAAeA,EAAM,IAAI,MAAQ,YACjE,GAAI,CACF,IAAMM,EAASN,EAAM,IAAI,CAAE,GAAGA,EAAM,MAAO,SAAUA,EAAM,QAAS,CAAC,EAE/D4B,EAAWtB,aAAkB,QAAU,MAAMA,EAASA,EAC5D,MAAOoB,EAAeE,CAAQ,CAChC,OAASrC,EAAG,CACVR,EAAiBQ,EAAG,CAAE,UAAWc,CAAc,CAAC,EAC5ClB,GACF,QAAQ,KAAK,4CAA4CkB,CAAa,mBAAoBd,EAAE,OAAO,EAErG,MAAMJ,EACF,qBAAqBe,EAAWG,CAAa,CAAC,KAAKH,EAAWX,EAAE,SAAW,iBAAiB,CAAC,OAC7F,oBACN,CACA,MACF,CAEA,GAAM,CAAE,IAAAkB,EAAK,MAAAC,EAAO,SAAAC,CAAS,EAAIX,EAC3BY,EAAQC,EAAYH,GAAS,CAAC,CAAC,EAGrC,GAFA,KAAM,IAAID,CAAG,GAAGG,CAAK,IAEjB,CAACG,EAAc,IAAIN,CAAG,EAAG,CAC3B,IAAMO,EAAWC,EAAkBP,CAAK,EACxC,GAAIM,GAAY,KACd,MAAM,OAAOA,CAAQ,MAErB,SAAWW,KAAShB,EAClB,MAAOe,EAAeC,CAAK,EAG/B,KAAM,KAAKlB,CAAG,GAChB,CACF,CAIO,SAASoB,GAAWC,EAAQ,CACjC,MAAO,CAKL,KAAM,SACN,GAAGA,CACL,CACF,CAGO,SAASC,GAAmBC,EAAMC,EAAO,CAAC,EAAG,CAClD7C,EAAgB,EAChB,IAAMY,EAAQgC,EAAK,UAAUC,CAAI,EAC3B1B,EAAOgB,EAAevB,CAAK,EAC3BkC,EAAUF,EAAK,SAAW,CAAC,EAEjC,OAAOG,GAAa,CAClB,MAAOH,EAAK,OAAS,GACrB,KAAMA,EAAK,MAAQ,CAAC,EACpB,KAAMzB,EACN,QAAA2B,EACA,QAASF,EAAK,OAAS,SAAW,CAAC,EAAIA,EAAK,SAAW,CAAC,EACxD,OAAQA,EAAK,QAAU,CAAC,EACxB,KAAMA,EAAK,KACX,UAAW3C,GAAmB,CAChC,CAAC,CACH,CAEA,SAAS8C,GAAa,CAAE,MAAAC,EAAO,KAAAC,EAAM,KAAAC,EAAM,QAAAJ,EAAS,QAAAK,EAAS,OAAAC,EAAQ,KAAAC,EAAM,UAAAC,EAAY,EAAG,EAAG,CAC3F,IAAMC,EAAW,OAAO,QAAQN,CAAI,EACjC,IAAI,CAAC,CAACO,EAAMC,CAAO,IAAM,eAAe3C,EAAW0C,CAAI,CAAC,cAAc1C,EAAW2C,CAAO,CAAC,IAAI,EAC7F,KAAK;AAAA,KAAQ,EAEVC,EAAYN,EACf,IAAIO,GAAQ,gCAAgC7C,EAAW6C,CAAI,CAAC,IAAI,EAChE,KAAK;AAAA,KAAQ,EAEVC,EAAed,EAAQ,OAAS,EAAI;AAAA;AAAA;AAAA;AAAA,gBAI3B,GAETe,EAAaV,EAChB,IAAIW,GAAO,8BAA8BhD,EAAWgD,CAAG,CAAC,cAAa,EACrE,KAAK;AAAA,KAAQ,EAEVC,EAAeV,IAAS,SAAW;AAAA,6DACmB,GAE5D,MAAO;AAAA;AAAA;AAAA;AAAA;AAAA,MAKHE,CAAQ;AAAA,aACDzC,EAAWkC,CAAK,CAAC;AAAA,MACxBU,CAAS;AAAA;AAAA;AAAA,oBAGKR,CAAI;AAAA,MAClBI,CAAS;AAAA,MACTM,CAAY;AAAA,MACZC,CAAU;AAAA,MACVE,CAAY;AAAA;AAAA,QAGlB,CAKO,SAASC,GAAOC,EAAW,CAChC,OAAAA,EAAU,QAAU,GACbA,CACT,CAKA,IAAMlE,EAAa,SAAO,QAAY,KAQtC,SAAS8B,EAAkBP,EAAO,CAChC,OAAKA,EAGDA,EAAM,wBACDA,EAAM,wBAAwB,QAAU,KAI7CA,EAAM,WAAa,OAAOA,EAAM,WAAc,UAAY,WAAYA,EAAM,UACvEA,EAAM,UAAU,QAAU,MAI/BA,EAAM,WAAa,MAAQ,OAAOA,EAAM,WAAc,UACpDvB,GACF,QAAQ,KACN,yLAEF,EAEK,MApBU,IAwBrB,CAEA,SAAS0B,EAAYH,EAAO,CAC1B,IAAI4C,EAAM,GACJC,EAAO,OAAO,KAAK7C,CAAK,EAC9B,QAASe,EAAI,EAAGA,EAAI8B,EAAK,OAAQ9B,IAAK,CACpC,IAAM+B,EAAMD,EAAK9B,CAAC,EACZgC,EAAM/C,EAAM8C,CAAG,EAGrB,GAFIA,IAAQ,OAASA,IAAQ,OAASA,IAAQ,YAAcA,IAAQ,2BAA6BA,IAAQ,aACrGA,EAAI,WAAW,IAAI,GAAKA,EAAI,OAAS,GACrCC,IAAQ,IAASA,GAAO,KAAM,SAElC,IAAMC,EAAOC,GAAyBH,CAAG,EACnCI,EAAWF,EAAK,KACtB,GAAI,CAACA,EAAK,MAAO,CACXvE,GAAY,QAAQ,KAAK,iDAAiDqE,CAAG,EAAE,EACnF,QACF,CACA,GAAI,CAACK,GAAwBH,EAAK,QAASD,CAAG,EAAG,CAC3CtE,GAAY,QAAQ,KAAK,+CAA+CyE,CAAQ,MAAMH,CAAG,EAAE,EAC/F,QACF,CAEA,GAAID,IAAQ,aAAeA,IAAQ,QACjCF,GAAO,WAAWpD,EAAW,OAAOuD,CAAG,CAAC,CAAC,YAChCD,IAAQ,SAAW,OAAOC,GAAQ,SAAU,CACrD,IAAMK,EAAM,OAAO,QAAQL,CAAG,EAC3B,IAAI,CAAC,CAACM,EAAGC,CAAC,IAAM,GAAGC,GAAaF,CAAC,CAAC,IAAIC,CAAC,EAAE,EACzC,KAAK,GAAG,EACXV,GAAO,WAAWpD,EAAW4D,CAAG,CAAC,GACnC,MAAWL,IAAQ,GAEbG,EAAS,WAAW,OAAO,GAAKA,IAAa,OAC/CN,GAAO,IAAIM,CAAQ,UAEnBN,GAAO,IAAIM,CAAQ,GAGrBN,GAAO,IAAIM,CAAQ,KAAK1D,EAAW,OAAOuD,CAAG,CAAC,CAAC,GAEnD,CAEA,OAAOH,CACT,CAEA,SAASY,GAAqBtB,EAAM,CAClC,OAAIA,IAAS,YAAoB,QAC7BA,IAAS,UAAkB,MACxBA,CACT,CAEA,IAAMuB,EAAsB,IAAI,IAC1BC,GAA0B,KAC1BC,EAAqB,EACrBC,EAAuB,EACvBC,EAAqB,EAE3B,SAASZ,GAAyBH,EAAK,CACrC,IAAMgB,EAASL,EAAoB,IAAIX,CAAG,EAC1C,GAAIgB,EAAQ,OAAOA,EAEnB,IAAM5B,EAAOsB,GAAqBV,CAAG,EAC/BiB,EAAQC,GAAyB9B,CAAI,EACrCP,EAAO,CACX,KAAAO,EACA,MAAA6B,EACA,QAASA,EAAQE,GAAoB/B,CAAI,EAAIyB,CAC/C,EAEA,OAAIF,EAAoB,MAAQC,IAAyBD,EAAoB,MAAM,EACnFA,EAAoB,IAAIX,EAAKnB,CAAI,EAC1BA,CACT,CAEA,SAASqC,GAAyB9B,EAAM,CACtC,MAAO,6BAA6B,KAAKA,CAAI,CAC/C,CAEA,IAAMgC,EAAY,IAAI,IAAI,CACxB,OACA,MACA,SACA,aACA,SACA,OACA,aACA,YACF,CAAC,EACKC,EAAiB,IAAI,IAAI,CAAC,QAAQ,CAAC,EAEzC,SAASF,GAAoB/B,EAAM,CACjC,GAAIgC,EAAU,IAAIhC,CAAI,EAAG,OAAO0B,EAChC,GAAIO,EAAe,IAAIjC,CAAI,EAAG,OAAO2B,EACrC,GAAI,CAACO,GAAkBlC,CAAI,EAAG,OAAOyB,EAErC,IAAMU,EAAiBnC,EAAK,YAAY,EACxC,OAAIgC,EAAU,IAAIG,CAAc,EAAUT,EACtCO,EAAe,IAAIE,CAAc,EAAUR,EACxCF,CACT,CAEA,SAASS,GAAkBE,EAAK,CAC9B,QAASvD,EAAI,EAAGA,EAAIuD,EAAI,OAAQvD,IAAK,CACnC,IAAMwD,EAAOD,EAAI,WAAWvD,CAAC,EAC7B,GAAIwD,GAAQ,IAAMA,GAAQ,GAAI,MAAO,EACvC,CACA,MAAO,EACT,CAEA,SAASpB,GAAwBqB,EAASC,EAAO,CAC/C,OAAID,IAAYX,EAA2Ba,GAAkBD,CAAK,EAC9DD,IAAYZ,EAA6Be,EAAeF,CAAK,EAC1D,EACT,CAEA,SAASE,EAAeF,EAAO,CAC7B,GAAI,OAAOA,GAAU,SAAU,MAAO,GACtC,IAAMG,EAAaH,EAAM,KAAK,EAAE,QAAQ,qBAAsB,EAAE,EAAE,YAAY,EAC9E,MAAO,EAAEG,EAAW,WAAW,aAAa,GAAKA,EAAW,WAAW,OAAO,GAAKA,EAAW,WAAW,WAAW,EACtH,CAEA,SAASF,GAAkBD,EAAO,CAChC,OAAI,OAAOA,GAAU,SAAiB,GAC/BA,EACJ,MAAM,GAAG,EACT,MAAMI,GAAa,CAClB,IAAMC,EAAMD,EAAU,KAAK,EAAE,MAAM,MAAO,CAAC,EAAE,CAAC,GAAK,GACnD,OAAOC,IAAQ,IAAMH,EAAeG,CAAG,CACzC,CAAC,CACL,CAEA,SAAStF,EAAW8E,EAAK,CACvB,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,OAAO,CAC1B,CAEA,SAASf,GAAae,EAAK,CACzB,OAAIA,EAAI,WAAW,IAAI,EAAUA,EAC1BA,EAAI,QAAQ,WAAY,KAAK,EAAE,YAAY,CACpD,CAEA,IAAMjE,EAAgB,IAAI,IAAI,CAC5B,OAAQ,OAAQ,KAAM,MAAO,QAAS,KAAM,MAAO,QACnD,OAAQ,OAAQ,QAAS,SAAU,QAAS,KAC9C,CAAC",
|
|
6
|
+
"names": ["signal", "batch", "actionRegistry", "getCsrfToken", "meta", "match", "generateCsrfToken", "arr", "b", "validateCsrfToken", "requestToken", "sessionToken", "result", "i", "csrfMetaTag", "token", "_actionCounter", "generateActionId", "action", "fn", "options", "id", "onError", "onSuccess", "revalidate", "callAction", "args", "timeout", "controller", "timeoutId", "csrfToken", "headers", "response", "error", "path", "invalidatePath", "timeoutError", "formAction", "actionFn", "resetOnSuccess", "formDataOrEvent", "formData", "form", "data", "hasFiles", "key", "value", "useAction", "isPending", "trigger", "e", "useFormAction", "formRef", "actionState", "handleSubmit", "useOptimistic", "initialValue", "reducer", "pending", "baseValue", "addOptimistic", "optimisticValue", "resolve", "serverValue", "a", "current", "rollback", "realValue", "newPending", "base", "withOptimistic", "asyncFn", "v", "revalidationCallbacks", "onRevalidate", "callback", "callbacks", "cb", "handleActionRequest", "req", "actionId", "sessionCsrfToken", "skipCsrf", "requestCsrfToken", "getRegisteredActions", "useMutation", "mutationFn", "onSettled", "state", "mutate", "_ssrErrors", "MAX_SSR_ERRORS", "_collectSSRError", "error", "context", "entry", "_isDevMode", "_resetSSRErrors", "serializeSSRErrors", "payload", "e", "hydrateSSRErrors", "el", "errors", "getSSRErrors", "_hydrationIdCounter", "resetHydrationId", "nextHydrationId", "renderToHydratableString", "vnode", "_renderHydratable", "escapeHtml", "renderChildrenToString", "hkId", "componentName", "result", "html", "injectHydrationKey", "tag", "props", "children", "attrs", "renderAttrs", "open", "VOID_ELEMENTS", "rawInner", "_resolveInnerHTML", "inner", "match", "prefix", "tagName", "insertAt", "renderToString", "renderChild", "i", "renderToStream", "child", "resolved", "definePage", "config", "generateStaticPage", "page", "data", "islands", "wrapDocument", "title", "meta", "body", "scripts", "styles", "mode", "ssrErrors", "metaTags", "name", "content", "styleTags", "href", "islandScript", "scriptTags", "src", "clientScript", "server", "Component", "out", "keys", "key", "val", "attr", "getHtmlAttributeMetadata", "attrName", "isSafeUrlAttributeValue", "css", "p", "v", "camelToKebab", "getHtmlAttributeName", "ATTR_METADATA_CACHE", "ATTR_METADATA_CACHE_MAX", "URL_ATTR_KIND_NONE", "URL_ATTR_KIND_SINGLE", "URL_ATTR_KIND_LIST", "cached", "valid", "isValidHtmlAttributeName", "getUrlAttributeKind", "URL_ATTRS", "URL_LIST_ATTRS", "hasAsciiUppercase", "normalizedName", "str", "code", "urlKind", "value", "isSafeSrcsetValue", "isSafeUrlValue", "normalized", "candidate", "url"]
|
|
7
7
|
}
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -119,7 +119,7 @@ function _renderHydratable(vnode) {
|
|
|
119
119
|
|
|
120
120
|
// Array — wrap in list markers
|
|
121
121
|
if (Array.isArray(vnode)) {
|
|
122
|
-
return `<!--[]-->${vnode
|
|
122
|
+
return `<!--[]-->${renderChildrenToString(vnode, _renderHydratable)}<!--/[]-->`;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
// Component — add hydration key to root element
|
|
@@ -150,7 +150,7 @@ function _renderHydratable(vnode) {
|
|
|
150
150
|
if (VOID_ELEMENTS.has(tag)) return open;
|
|
151
151
|
|
|
152
152
|
const rawInner = _resolveInnerHTML(props);
|
|
153
|
-
const inner = rawInner != null ? String(rawInner) : children
|
|
153
|
+
const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, _renderHydratable);
|
|
154
154
|
return `${open}${inner}</${tag}>`;
|
|
155
155
|
}
|
|
156
156
|
|
|
@@ -198,7 +198,7 @@ export function renderToString(vnode) {
|
|
|
198
198
|
|
|
199
199
|
// Array
|
|
200
200
|
if (Array.isArray(vnode)) {
|
|
201
|
-
return vnode
|
|
201
|
+
return renderChildrenToString(vnode, renderToString);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
204
|
// Component
|
|
@@ -226,10 +226,18 @@ export function renderToString(vnode) {
|
|
|
226
226
|
if (VOID_ELEMENTS.has(tag)) return open;
|
|
227
227
|
|
|
228
228
|
const rawInner = _resolveInnerHTML(props);
|
|
229
|
-
const inner = rawInner != null ? String(rawInner) : children
|
|
229
|
+
const inner = rawInner != null ? String(rawInner) : renderChildrenToString(children, renderToString);
|
|
230
230
|
return `${open}${inner}</${tag}>`;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
function renderChildrenToString(children, renderChild) {
|
|
234
|
+
let html = '';
|
|
235
|
+
for (let i = 0; i < children.length; i++) {
|
|
236
|
+
html += renderChild(children[i]);
|
|
237
|
+
}
|
|
238
|
+
return html;
|
|
239
|
+
}
|
|
240
|
+
|
|
233
241
|
// --- Stream Render ---
|
|
234
242
|
// Returns an async iterator for streaming SSR.
|
|
235
243
|
|
|
@@ -424,17 +432,21 @@ function _resolveInnerHTML(props) {
|
|
|
424
432
|
|
|
425
433
|
function renderAttrs(props) {
|
|
426
434
|
let out = '';
|
|
427
|
-
|
|
435
|
+
const keys = Object.keys(props);
|
|
436
|
+
for (let i = 0; i < keys.length; i++) {
|
|
437
|
+
const key = keys[i];
|
|
438
|
+
const val = props[key];
|
|
428
439
|
if (key === 'key' || key === 'ref' || key === 'children' || key === 'dangerouslySetInnerHTML' || key === 'innerHTML') continue;
|
|
429
440
|
if (key.startsWith('on') && key.length > 2) continue; // Skip event handlers in SSR
|
|
430
441
|
if (val === false || val == null) continue;
|
|
431
442
|
|
|
432
|
-
const
|
|
433
|
-
|
|
443
|
+
const attr = getHtmlAttributeMetadata(key);
|
|
444
|
+
const attrName = attr.name;
|
|
445
|
+
if (!attr.valid) {
|
|
434
446
|
if (_isDevMode) console.warn(`[what-server] Omitted invalid attribute name: ${key}`);
|
|
435
447
|
continue;
|
|
436
448
|
}
|
|
437
|
-
if (!isSafeUrlAttributeValue(
|
|
449
|
+
if (!isSafeUrlAttributeValue(attr.urlKind, val)) {
|
|
438
450
|
if (_isDevMode) console.warn(`[what-server] Omitted unsafe URL attribute "${attrName}": ${val}`);
|
|
439
451
|
continue;
|
|
440
452
|
}
|
|
@@ -467,6 +479,29 @@ function getHtmlAttributeName(name) {
|
|
|
467
479
|
return name;
|
|
468
480
|
}
|
|
469
481
|
|
|
482
|
+
const ATTR_METADATA_CACHE = new Map();
|
|
483
|
+
const ATTR_METADATA_CACHE_MAX = 2048;
|
|
484
|
+
const URL_ATTR_KIND_NONE = 0;
|
|
485
|
+
const URL_ATTR_KIND_SINGLE = 1;
|
|
486
|
+
const URL_ATTR_KIND_LIST = 2;
|
|
487
|
+
|
|
488
|
+
function getHtmlAttributeMetadata(key) {
|
|
489
|
+
const cached = ATTR_METADATA_CACHE.get(key);
|
|
490
|
+
if (cached) return cached;
|
|
491
|
+
|
|
492
|
+
const name = getHtmlAttributeName(key);
|
|
493
|
+
const valid = isValidHtmlAttributeName(name);
|
|
494
|
+
const meta = {
|
|
495
|
+
name,
|
|
496
|
+
valid,
|
|
497
|
+
urlKind: valid ? getUrlAttributeKind(name) : URL_ATTR_KIND_NONE,
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
if (ATTR_METADATA_CACHE.size >= ATTR_METADATA_CACHE_MAX) ATTR_METADATA_CACHE.clear();
|
|
501
|
+
ATTR_METADATA_CACHE.set(key, meta);
|
|
502
|
+
return meta;
|
|
503
|
+
}
|
|
504
|
+
|
|
470
505
|
function isValidHtmlAttributeName(name) {
|
|
471
506
|
return /^[^\s"'>/=\x00-\x1f\x7f]+$/.test(name);
|
|
472
507
|
}
|
|
@@ -483,10 +518,28 @@ const URL_ATTRS = new Set([
|
|
|
483
518
|
]);
|
|
484
519
|
const URL_LIST_ATTRS = new Set(['srcset']);
|
|
485
520
|
|
|
486
|
-
function
|
|
487
|
-
|
|
488
|
-
if (URL_LIST_ATTRS.has(
|
|
489
|
-
if (
|
|
521
|
+
function getUrlAttributeKind(name) {
|
|
522
|
+
if (URL_ATTRS.has(name)) return URL_ATTR_KIND_SINGLE;
|
|
523
|
+
if (URL_LIST_ATTRS.has(name)) return URL_ATTR_KIND_LIST;
|
|
524
|
+
if (!hasAsciiUppercase(name)) return URL_ATTR_KIND_NONE;
|
|
525
|
+
|
|
526
|
+
const normalizedName = name.toLowerCase();
|
|
527
|
+
if (URL_ATTRS.has(normalizedName)) return URL_ATTR_KIND_SINGLE;
|
|
528
|
+
if (URL_LIST_ATTRS.has(normalizedName)) return URL_ATTR_KIND_LIST;
|
|
529
|
+
return URL_ATTR_KIND_NONE;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
function hasAsciiUppercase(str) {
|
|
533
|
+
for (let i = 0; i < str.length; i++) {
|
|
534
|
+
const code = str.charCodeAt(i);
|
|
535
|
+
if (code >= 65 && code <= 90) return true;
|
|
536
|
+
}
|
|
537
|
+
return false;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
function isSafeUrlAttributeValue(urlKind, value) {
|
|
541
|
+
if (urlKind === URL_ATTR_KIND_LIST) return isSafeSrcsetValue(value);
|
|
542
|
+
if (urlKind === URL_ATTR_KIND_SINGLE) return isSafeUrlValue(value);
|
|
490
543
|
return true;
|
|
491
544
|
}
|
|
492
545
|
|