Socket
Book a DemoSign in
Socket

@getforma/core

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@getforma/core - npm Package Compare versions

Comparing version
1.0.6
to
1.0.7
+1519
dist/chunk-FJGTMWKY.js
import { internalEffect, createRoot, untrack, __DEV__ } from './chunk-OUVOAYIO.js';
import { createSignal } from './chunk-OZCHIVAZ.js';
// src/dom/element.ts
var Fragment = /* @__PURE__ */ Symbol.for("forma.fragment");
var SVG_NS = "http://www.w3.org/2000/svg";
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_TAGS = /* @__PURE__ */ new Set([
"svg",
"path",
"circle",
"rect",
"line",
"polyline",
"polygon",
"ellipse",
"g",
"text",
"tspan",
"textPath",
"defs",
"use",
"symbol",
"clipPath",
"mask",
"pattern",
"marker",
"linearGradient",
"radialGradient",
"stop",
"filter",
"feGaussianBlur",
"feColorMatrix",
"feOffset",
"feBlend",
"feMerge",
"feMergeNode",
"feComposite",
"feFlood",
"feMorphology",
"feTurbulence",
"feDisplacementMap",
"feImage",
"foreignObject",
"animate",
"animateTransform",
"animateMotion",
"set",
"image",
"switch",
"desc",
"title",
"metadata"
]);
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
"disabled",
"checked",
"readonly",
"required",
"autofocus",
"autoplay",
"controls",
"default",
"defer",
"formnovalidate",
"hidden",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"reversed",
"selected",
"async"
]);
var ELEMENT_PROTOS = null;
function getProto(tag) {
if (!ELEMENT_PROTOS) {
ELEMENT_PROTOS = /* @__PURE__ */ Object.create(null);
for (const t of [
"div",
"span",
"p",
"a",
"li",
"ul",
"ol",
"button",
"input",
"label",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"section",
"header",
"footer",
"main",
"nav",
"table",
"tr",
"td",
"th",
"tbody",
"img",
"form",
"select",
"option",
"textarea",
"i",
"b",
"strong",
"em",
"small",
"article",
"aside",
"details",
"summary"
]) {
ELEMENT_PROTOS[t] = document.createElement(t);
}
}
return ELEMENT_PROTOS[tag] ?? (ELEMENT_PROTOS[tag] = document.createElement(tag));
}
var EVENT_NAMES = /* @__PURE__ */ Object.create(null);
function eventName(key) {
return EVENT_NAMES[key] ?? (EVENT_NAMES[key] = key.slice(2).toLowerCase());
}
var ABORT_SYM = /* @__PURE__ */ Symbol.for("forma-abort");
function getAbortController(el) {
let controller = el[ABORT_SYM];
if (!controller) {
controller = new AbortController();
el[ABORT_SYM] = controller;
}
return controller;
}
function cleanup(el) {
const controller = el[ABORT_SYM];
if (controller) {
controller.abort();
delete el[ABORT_SYM];
}
}
var CACHE_SYM = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function getCache(el) {
return el[CACHE_SYM] ?? (el[CACHE_SYM] = /* @__PURE__ */ Object.create(null));
}
function handleClass(el, _key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache["class"] === v) return;
cache["class"] = v;
if (el instanceof HTMLElement) {
el.className = v;
} else {
el.setAttribute("class", v);
}
});
} else {
const cache = getCache(el);
if (cache["class"] === value) return;
cache["class"] = value;
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
}
}
function handleStyle(el, _key, value) {
if (typeof value === "function") {
let prevKeys = [];
internalEffect(() => {
const v = value();
if (typeof v === "string") {
const cache = getCache(el);
if (cache["style"] === v) return;
cache["style"] = v;
prevKeys = [];
el.style.cssText = v;
} else if (v && typeof v === "object") {
const style = el.style;
const nextKeys = Object.keys(v);
for (const k of prevKeys) {
if (!(k in v)) {
style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
}
}
Object.assign(style, v);
prevKeys = nextKeys;
}
});
} else if (typeof value === "string") {
const cache = getCache(el);
if (cache["style"] === value) return;
cache["style"] = value;
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
}
function handleEvent(el, key, value) {
const controller = getAbortController(el);
el.addEventListener(
eventName(key),
value,
{ signal: controller.signal }
);
}
function handleInnerHTML(el, _key, value) {
if (typeof value === "function") {
internalEffect(() => {
const resolved = value();
if (resolved == null) {
el.innerHTML = "";
return;
}
if (typeof resolved !== "object" || !("__html" in resolved)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof resolved
);
}
const html = resolved.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
const cache = getCache(el);
if (cache["innerHTML"] === html) return;
cache["innerHTML"] = html;
el.innerHTML = html;
});
} else {
if (value == null) {
el.innerHTML = "";
return;
}
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
}
}
function handleXLink(el, key, value) {
const localName = key.slice(6);
if (typeof value === "function") {
internalEffect(() => {
const v = value();
if (v == null || v === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(v));
}
});
} else {
if (value == null || value === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(value));
}
}
}
function handleBooleanAttr(el, key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache[key] === v) return;
cache[key] = v;
if (v) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
});
} else {
const cache = getCache(el);
if (cache[key] === value) return;
cache[key] = value;
if (value) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
}
}
function handleGenericAttr(el, key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
if (v == null || v === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(v);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
});
} else {
if (value == null || value === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(value);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
}
}
var PROP_HANDLERS = /* @__PURE__ */ new Map();
PROP_HANDLERS.set("class", handleClass);
PROP_HANDLERS.set("className", handleClass);
PROP_HANDLERS.set("style", handleStyle);
PROP_HANDLERS.set("ref", () => {
});
PROP_HANDLERS.set("dangerouslySetInnerHTML", handleInnerHTML);
for (const attr of BOOLEAN_ATTRS) {
PROP_HANDLERS.set(attr, handleBooleanAttr);
}
function applyProp(el, key, value) {
if (key === "class") {
handleClass(el, key, value);
return;
}
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
return;
}
const handler = PROP_HANDLERS.get(key);
if (handler) {
handler(el, key, value);
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
handleXLink(el, key, value);
return;
}
handleGenericAttr(el, key, value);
}
function applyStaticProp(el, key, value) {
if (value == null || value === false) return;
if (key === "class" || key === "className") {
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
return;
}
if (key === "style") {
if (typeof value === "string") {
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
return;
}
if (key === "dangerouslySetInnerHTML") {
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
el.setAttributeNS(XLINK_NS, key, String(value));
return;
}
if (BOOLEAN_ATTRS.has(key)) {
if (value) el.setAttribute(key, "");
return;
}
if (value === true) {
el.setAttribute(key, "");
} else {
el.setAttribute(key, String(value));
}
}
function appendChild(parent, child) {
if (child instanceof Node) {
parent.appendChild(child);
return;
}
if (typeof child === "string") {
parent.appendChild(new Text(child));
return;
}
if (child == null || child === false || child === true) {
return;
}
if (typeof child === "number") {
parent.appendChild(new Text(String(child)));
return;
}
if (typeof child === "function") {
if (parent instanceof Element) {
parent[DYNAMIC_CHILD_SYM] = true;
}
let currentNode = null;
let currentFragChildren = null;
let warnedArray = false;
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const clearCurrent = () => {
if (currentFragChildren) {
for (const c of currentFragChildren) {
if (c.parentNode === parent) parent.removeChild(c);
}
currentFragChildren = null;
}
if (currentNode && currentNode.parentNode === parent) {
parent.removeChild(currentNode);
}
currentNode = null;
};
internalEffect(() => {
const v = child();
let resolved = v;
if (Array.isArray(v)) {
const frag = document.createDocumentFragment();
for (const item of v) {
if (item instanceof Node) frag.appendChild(item);
else if (Array.isArray(item)) {
if (DEBUG) console.warn("[forma] Nested arrays in function children are not supported. Flatten the array or use createList().");
} else if (item != null && item !== false && item !== true) {
frag.appendChild(new Text(String(item)));
}
}
resolved = frag.childNodes.length > 0 ? frag : null;
if (DEBUG && !warnedArray) {
warnedArray = true;
console.warn("[forma] Function child returned an array \u2014 auto-wrapped in DocumentFragment. Consider using createList() or wrapping in a container element for better performance.");
}
}
if (resolved instanceof Node) {
clearCurrent();
const isNewFrag = resolved instanceof DocumentFragment;
if (isNewFrag) {
currentFragChildren = Array.from(resolved.childNodes);
}
parent.appendChild(resolved);
currentNode = isNewFrag ? null : resolved;
} else if (resolved == null || resolved === false || resolved === true) {
clearCurrent();
} else {
if (currentFragChildren) {
for (const c of currentFragChildren) {
if (c.parentNode === parent) parent.removeChild(c);
}
currentFragChildren = null;
}
const text = typeof resolved === "symbol" ? String(resolved) : String(resolved ?? "");
if (!currentNode) {
currentNode = new Text(text);
parent.appendChild(currentNode);
} else if (currentNode.nodeType === 3) {
currentNode.data = text;
} else {
const tn = new Text(text);
parent.replaceChild(tn, currentNode);
currentNode = tn;
}
}
});
return;
}
if (Array.isArray(child)) {
for (const item of child) {
appendChild(parent, item);
}
return;
}
}
function h(tag, props, ...children) {
if (typeof tag === "function" && tag !== Fragment) {
const mergedProps = { ...props ?? {}, children };
return tag(mergedProps);
}
if (tag === Fragment) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
const tagName = tag;
if (hydrating) {
return { type: "element", tag: tagName, props: props ?? null, children };
}
let el;
if (ELEMENT_PROTOS && ELEMENT_PROTOS[tagName]) {
el = ELEMENT_PROTOS[tagName].cloneNode(false);
} else if (SVG_TAGS.has(tagName)) {
el = document.createElementNS(SVG_NS, tagName);
} else {
el = getProto(tagName).cloneNode(false);
}
if (props) {
let hasDynamic = false;
for (const key in props) {
if (key === "ref") continue;
const value = props[key];
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
continue;
}
if (typeof value === "function") {
if (!hasDynamic) {
el[CACHE_SYM] = /* @__PURE__ */ Object.create(null);
hasDynamic = true;
}
applyProp(el, key, value);
continue;
}
applyStaticProp(el, key, value);
}
}
const childLen = children.length;
if (childLen === 1) {
const only = children[0];
if (typeof only === "string") {
el.textContent = only;
} else if (typeof only === "number") {
el.textContent = String(only);
} else {
appendChild(el, only);
}
} else if (childLen > 1) {
for (const child of children) {
appendChild(el, child);
}
}
if (props && typeof props["ref"] === "function") {
props["ref"](el);
}
return el;
}
function fragment(...children) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
// src/dom/show.ts
function createShow(when, thenFn, elseFn) {
if (hydrating) {
const branch = when() ? thenFn() : elseFn?.() ?? null;
return {
type: "show",
condition: when,
whenTrue: thenFn,
whenFalse: elseFn,
initialBranch: branch
};
}
const startMarker = document.createComment("forma-show");
const endMarker = document.createComment("/forma-show");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let currentNode = null;
let lastTruthy = null;
let currentDispose = null;
const showDispose = internalEffect(() => {
const truthy = !!when();
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
if (truthy === lastTruthy) {
if (DEBUG) console.log("[forma:show] skip (same)", truthy, DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show]", lastTruthy, "\u2192", truthy, DEBUG_LABEL);
lastTruthy = truthy;
const parent = startMarker.parentNode;
if (!parent) {
if (DEBUG) console.warn("[forma:show] parentNode is null! skipping.", DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show] parent:", parent.nodeName, "inDoc:", document.contains(parent));
if (currentDispose) {
currentDispose();
currentDispose = null;
}
if (currentNode) {
if (currentNode.parentNode === parent) {
parent.removeChild(currentNode);
} else {
while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
parent.removeChild(startMarker.nextSibling);
}
}
}
const branchFn = truthy ? thenFn : elseFn;
if (branchFn) {
let branchDispose;
currentNode = createRoot((dispose) => {
branchDispose = dispose;
return untrack(() => branchFn());
});
currentDispose = branchDispose;
} else {
currentNode = null;
}
if (currentNode) {
parent.insertBefore(currentNode, endMarker);
}
});
fragment2.__showDispose = () => {
showDispose();
if (currentDispose) {
currentDispose();
currentDispose = null;
}
};
return fragment2;
}
// src/dom/hydrate.ts
var ABORT_SYM2 = /* @__PURE__ */ Symbol.for("forma-abort");
var hydrating = false;
function setHydrating(value) {
hydrating = value;
}
function isDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "element";
}
function isShowDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "show";
}
function isListDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "list";
}
function applyDynamicProps(el, props) {
if (!props) return;
for (const key in props) {
const value = props[key];
if (typeof value !== "function") continue;
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
let ac = el[ABORT_SYM2];
if (!ac) {
ac = new AbortController();
el[ABORT_SYM2] = ac;
}
el.addEventListener(key.slice(2).toLowerCase(), value, { signal: ac.signal });
continue;
}
const fn = value;
const attrKey = key;
internalEffect(() => {
const v = fn();
if (v === false || v == null) {
el.removeAttribute(attrKey);
} else if (v === true) {
el.setAttribute(attrKey, "");
} else {
el.setAttribute(attrKey, String(v));
}
});
}
}
function ensureNode(value) {
if (value instanceof Node) return value;
if (value == null || value === false || value === true) return null;
if (typeof value === "string") return new Text(value);
if (typeof value === "number") return new Text(String(value));
if (isDescriptor(value)) return descriptorToElement(value);
if (isShowDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createShow(
value.condition,
() => ensureNode(value.whenTrue()) ?? document.createComment("empty"),
value.whenFalse ? () => ensureNode(value.whenFalse()) ?? document.createComment("empty") : void 0
);
} finally {
hydrating = prevH;
}
}
if (isListDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createList(value.items, value.keyFn, value.renderFn, value.options);
} finally {
hydrating = prevH;
}
}
return null;
}
function descriptorToElement(desc) {
const prevHydrating = hydrating;
hydrating = false;
try {
const children = desc.children.map((child) => {
if (isDescriptor(child)) return descriptorToElement(child);
if (isShowDescriptor(child)) return ensureNode(child);
if (isListDescriptor(child)) return ensureNode(child);
return child;
});
return h(desc.tag, desc.props, ...children);
} finally {
hydrating = prevHydrating;
}
}
function isIslandStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 105;
}
function isShowStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 115;
}
function isTextStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 116;
}
function isListStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 108;
}
function findClosingMarker(start) {
const closing = "/" + start.data;
let node = start.nextSibling;
while (node) {
if (node.nodeType === 8 && node.data === closing) {
return node;
}
node = node.nextSibling;
}
return null;
}
function findTextBetween(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 3) return node;
node = node.nextSibling;
}
return null;
}
function nextElementBetweenMarkers(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) return node;
node = node.nextSibling;
}
return void 0;
}
function extractContentBetweenMarkers(start, end) {
const frag = document.createDocumentFragment();
let node = start.nextSibling;
while (node && node !== end) {
const next = node.nextSibling;
frag.appendChild(node);
node = next;
}
return frag;
}
function setupShowEffect(desc, marker) {
let currentCondition = !!desc.condition();
let thenFragment = null;
let elseFragment = null;
const hasSSRContent = marker.start.nextSibling !== marker.end;
if (!hasSSRContent && currentCondition) {
if (__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
const trueBranch = desc.whenTrue();
if (trueBranch instanceof Node) {
marker.start.parentNode.insertBefore(trueBranch, marker.end);
}
}
internalEffect(() => {
const next = !!desc.condition();
if (next === currentCondition) return;
currentCondition = next;
const parent = marker.start.parentNode;
if (!parent) return;
const current = extractContentBetweenMarkers(marker.start, marker.end);
if (!next) {
thenFragment = current;
} else {
elseFragment = current;
}
let branch = next ? thenFragment ?? desc.whenTrue() : desc.whenFalse ? elseFragment ?? desc.whenFalse() : null;
if (next && thenFragment) thenFragment = null;
if (!next && elseFragment) elseFragment = null;
if (branch != null && !(branch instanceof Node)) {
branch = ensureNode(branch);
}
if (branch instanceof Node) {
parent.insertBefore(branch, marker.end);
}
});
}
function adoptBranchContent(desc, regionStart, regionEnd) {
if (isDescriptor(desc)) {
const el = nextElementBetweenMarkers(regionStart, regionEnd);
if (el) adoptNode(desc, el);
} else if (isShowDescriptor(desc)) {
let node = regionStart.nextSibling;
while (node && node !== regionEnd) {
if (node.nodeType === 8 && isShowStart(node.data)) {
const innerStart = node;
const innerEnd = findClosingMarker(innerStart);
if (innerEnd) {
if (desc.initialBranch) {
adoptBranchContent(desc.initialBranch, innerStart, innerEnd);
}
setupShowEffect(desc, { start: innerStart, end: innerEnd});
}
break;
}
node = node.nextSibling;
}
}
}
function adoptNode(desc, ssrEl) {
if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
if (__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
const fresh = descriptorToElement(desc);
if (ssrEl) ssrEl.replaceWith(fresh);
return;
}
applyDynamicProps(ssrEl, desc.props);
let cursor = ssrEl.firstChild;
for (const child of desc.children) {
if (child === false || child == null) continue;
if (isDescriptor(child)) {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
while (cursor && cursor.nodeType === 1 && cursor.hasAttribute("data-forma-island")) {
cursor = cursor.nextSibling;
}
if (!cursor) {
ssrEl.appendChild(descriptorToElement(child));
continue;
}
if (cursor.nodeType === 1) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(child, el);
} else if (cursor.nodeType === 8 && isIslandStart(cursor.data)) {
const end = findClosingMarker(cursor);
const fresh = descriptorToElement(child);
if (end) {
end.parentNode.insertBefore(fresh, end);
cursor = end.nextSibling;
} else {
ssrEl.appendChild(fresh);
cursor = null;
}
} else {
ssrEl.appendChild(descriptorToElement(child));
}
} else if (isShowDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isShowStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
if (child.initialBranch) {
adoptBranchContent(child.initialBranch, start, end);
}
setupShowEffect(child, { start, end});
cursor = end.nextSibling;
}
}
} else if (isListDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isListStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
const ssrKeyMap = /* @__PURE__ */ new Map();
const ssrElements = [];
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) {
const el = node;
ssrElements.push(el);
const key = el.getAttribute("data-forma-key");
if (key != null) {
ssrKeyMap.set(key, el);
}
}
node = node.nextSibling;
}
const currentItems = untrack(() => child.items());
const listKeyFn = child.keyFn;
const listRenderFn = child.renderFn;
const useIndexFallback = ssrKeyMap.size === 0 && ssrElements.length > 0;
const adoptedNodes = [];
const adoptedItems = [];
const usedIndices = /* @__PURE__ */ new Set();
for (let i = 0; i < currentItems.length; i++) {
const item = currentItems[i];
const key = listKeyFn(item);
let ssrNode;
if (useIndexFallback) {
if (i < ssrElements.length) {
ssrNode = ssrElements[i];
usedIndices.add(i);
}
} else {
ssrNode = ssrKeyMap.get(String(key));
if (ssrNode) ssrKeyMap.delete(String(key));
}
if (ssrNode) {
adoptedNodes.push(ssrNode);
adoptedItems.push(item);
} else {
if (__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
const prevHydrating = hydrating;
hydrating = false;
try {
const [getIndex] = createSignal(i);
const fresh = listRenderFn(item, getIndex);
end.parentNode.insertBefore(fresh, end);
adoptedNodes.push(fresh);
adoptedItems.push(item);
} finally {
hydrating = prevHydrating;
}
}
}
if (useIndexFallback) {
for (let i = 0; i < ssrElements.length; i++) {
if (!usedIndices.has(i) && ssrElements[i].parentNode) {
ssrElements[i].parentNode.removeChild(ssrElements[i]);
}
}
} else {
for (const [unusedKey, unusedNode] of ssrKeyMap) {
if (__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
if (unusedNode.parentNode) {
unusedNode.parentNode.removeChild(unusedNode);
}
}
}
const parent = start.parentNode;
for (const adoptedNode of adoptedNodes) {
parent.insertBefore(adoptedNode, end);
}
let cache = /* @__PURE__ */ new Map();
for (let i = 0; i < adoptedItems.length; i++) {
const item = adoptedItems[i];
const key = listKeyFn(item);
const [getIndex, setIndex] = createSignal(i);
cache.set(key, {
element: adoptedNodes[i],
item,
getIndex,
setIndex
});
}
let reconcileNodes = adoptedNodes.slice();
let reconcileItems = adoptedItems.slice();
internalEffect(() => {
const newItems = child.items();
const parent2 = start.parentNode;
if (!parent2) return;
const result = reconcileList(
parent2,
reconcileItems,
newItems,
reconcileNodes,
listKeyFn,
(item) => {
const prevHydrating = hydrating;
hydrating = false;
try {
const key = listKeyFn(item);
const [getIndex, setIndex] = createSignal(0);
const element = untrack(() => listRenderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
} finally {
hydrating = prevHydrating;
}
},
(_node, item) => {
const key = listKeyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
},
end
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < newItems.length; i++) {
const key = listKeyFn(newItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
reconcileNodes = result.nodes;
reconcileItems = result.items;
});
cursor = end.nextSibling;
}
}
} else if (typeof child === "function") {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
if (cursor && cursor.nodeType === 1) {
const initial = child();
if (isDescriptor(initial)) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(initial, el);
continue;
}
}
if (cursor && cursor.nodeType === 8) {
const data = cursor.data;
if (isTextStart(data)) {
const endMarker = findClosingMarker(cursor);
let textNode = cursor.nextSibling;
if (!textNode || textNode.nodeType !== 3) {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
const created = document.createTextNode("");
cursor.parentNode.insertBefore(created, endMarker || cursor.nextSibling);
textNode = created;
}
internalEffect(() => {
textNode.data = String(child());
});
cursor = endMarker ? endMarker.nextSibling : textNode.nextSibling;
} else if (isShowStart(data)) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
let textNode = findTextBetween(start, end);
if (!textNode) {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
textNode = document.createTextNode("");
start.parentNode.insertBefore(textNode, end);
}
internalEffect(() => {
textNode.data = String(child());
});
cursor = end.nextSibling;
} else {
cursor = cursor.nextSibling;
}
} else {
cursor = cursor.nextSibling;
}
} else if (cursor && cursor.nodeType === 3) {
const textNode = cursor;
cursor = cursor.nextSibling;
internalEffect(() => {
textNode.data = String(child());
});
} else {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
const textNode = document.createTextNode("");
ssrEl.appendChild(textNode);
internalEffect(() => {
textNode.data = String(child());
});
}
} else if (typeof child === "string" || typeof child === "number") {
if (cursor && cursor.nodeType === 3) {
cursor = cursor.nextSibling;
}
}
}
}
function hydrateIsland(component, target) {
const hasSSRContent = target.childElementCount > 0 || target.childNodes.length > 0 && Array.from(target.childNodes).some((n) => n.nodeType === 1 || n.nodeType === 3 && n.data.trim());
if (!hasSSRContent) {
if (__DEV__) {
const name = target.getAttribute("data-forma-component") || "unknown";
console.warn(
`[forma] Island "${name}" has no SSR content \u2014 falling back to CSR. This means the IR walker did not render content between ISLAND_START and ISLAND_END.`
);
}
const result = component();
if (result instanceof Element) {
for (const attr of Array.from(target.attributes)) {
if (attr.name.startsWith("data-forma-")) {
result.setAttribute(attr.name, attr.value);
}
}
target.replaceWith(result);
return result;
} else if (result instanceof Node) {
target.appendChild(result);
}
return target;
}
setHydrating(true);
let descriptor;
try {
descriptor = component();
} finally {
setHydrating(false);
}
if (!descriptor || !isDescriptor(descriptor)) {
target.removeAttribute("data-forma-ssr");
return target;
}
if (target.hasAttribute("data-forma-island")) {
adoptNode(descriptor, target);
} else {
adoptNode(descriptor, target.children[0]);
}
target.removeAttribute("data-forma-ssr");
return target;
}
// src/dom/list.ts
function longestIncreasingSubsequence(arr) {
const n = arr.length;
if (n === 0) return [];
const tails = new Int32Array(n);
const tailIndices = new Int32Array(n);
const predecessor = new Int32Array(n).fill(-1);
let tailsLen = 0;
for (let i = 0; i < n; i++) {
const val = arr[i];
let lo = 0, hi = tailsLen;
while (lo < hi) {
const mid = lo + hi >> 1;
if (tails[mid] < val) lo = mid + 1;
else hi = mid;
}
tails[lo] = val;
tailIndices[lo] = i;
if (lo > 0) predecessor[i] = tailIndices[lo - 1];
if (lo >= tailsLen) tailsLen++;
}
const result = new Array(tailsLen);
let idx = tailIndices[tailsLen - 1];
for (let i = tailsLen - 1; i >= 0; i--) {
result[i] = idx;
idx = predecessor[idx];
}
return result;
}
var SMALL_LIST_THRESHOLD = 32;
var ABORT_SYM3 = /* @__PURE__ */ Symbol.for("forma-abort");
var CACHE_SYM2 = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM2 = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function canPatchStaticElement(target, source) {
return target instanceof HTMLElement && source instanceof HTMLElement && target.tagName === source.tagName && !target[ABORT_SYM3] && !target[CACHE_SYM2] && !target[DYNAMIC_CHILD_SYM2] && !source[ABORT_SYM3] && !source[CACHE_SYM2] && !source[DYNAMIC_CHILD_SYM2];
}
function patchStaticElement(target, source) {
const sourceAttrNames = /* @__PURE__ */ new Set();
for (const attr of Array.from(source.attributes)) {
sourceAttrNames.add(attr.name);
if (target.getAttribute(attr.name) !== attr.value) {
target.setAttribute(attr.name, attr.value);
}
}
for (const attr of Array.from(target.attributes)) {
if (!sourceAttrNames.has(attr.name)) {
target.removeAttribute(attr.name);
}
}
target.replaceChildren(...Array.from(source.childNodes));
}
function reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
const oldKeys = new Array(oldLen);
for (let i = 0; i < oldLen; i++) {
oldKeys[i] = keyFn(oldItems[i]);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
let found = -1;
for (let j = 0; j < oldLen; j++) {
if (!oldUsed[j] && oldKeys[j] === key) {
found = j;
oldUsed[j] = 1;
break;
}
}
oldIndices[i] = found;
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function reconcileList(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
if (newLen === 0) {
for (let i = 0; i < oldLen; i++) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
return { nodes: [], items: [] };
}
if (oldLen === 0) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = createFn(newItems[i]);
if (beforeNode) {
parent.insertBefore(node, beforeNode);
} else {
parent.appendChild(node);
}
hooks?.onInsert?.(node);
nodes[i] = node;
}
return { nodes, items: newItems };
}
if (oldLen < SMALL_LIST_THRESHOLD) {
return reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks);
}
const oldKeyMap = /* @__PURE__ */ new Map();
for (let i = 0; i < oldLen; i++) {
oldKeyMap.set(keyFn(oldItems[i]), i);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
const oldIdx = oldKeyMap.get(key);
if (oldIdx !== void 0) {
oldIndices[i] = oldIdx;
oldUsed[oldIdx] = 1;
} else {
oldIndices[i] = -1;
}
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function createList(items, keyFn, renderFn, options) {
if (hydrating) {
return { type: "list", items, keyFn, renderFn, options };
}
const startMarker = document.createComment("forma-list-start");
const endMarker = document.createComment("forma-list-end");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let cache = /* @__PURE__ */ new Map();
let currentNodes = [];
let currentItems = [];
const updateOnItemChange = options?.updateOnItemChange ?? "none";
internalEffect(() => {
const newItems = items();
const parent = startMarker.parentNode;
if (!parent) {
return;
}
if (!Array.isArray(newItems)) {
if (__DEV__) {
console.warn("[forma] createList: value is not an array, treating as empty");
}
for (const node of currentNodes) {
if (node.parentNode === parent) parent.removeChild(node);
}
cache = /* @__PURE__ */ new Map();
currentNodes = [];
currentItems = [];
return;
}
let cleanItems = newItems;
for (let i = 0; i < newItems.length; i++) {
if (newItems[i] == null) {
cleanItems = newItems.filter((item) => item != null);
break;
}
}
if (__DEV__) {
const seen = /* @__PURE__ */ new Set();
for (const item of cleanItems) {
const key = keyFn(item);
if (seen.has(key)) {
console.warn("[forma] createList: duplicate key detected:", key);
}
seen.add(key);
}
}
const updateRow = updateOnItemChange === "rerender" ? (node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (!cached) return;
if (cached.item === item) return;
cached.item = item;
if (!(node instanceof HTMLElement)) return;
if (node[ABORT_SYM3] || node[CACHE_SYM2] || node[DYNAMIC_CHILD_SYM2]) {
return;
}
const next = untrack(() => renderFn(item, cached.getIndex));
if (canPatchStaticElement(node, next)) {
patchStaticElement(node, next);
cached.element = node;
}
} : (_node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
};
const result = reconcileList(
parent,
currentItems,
cleanItems,
currentNodes,
keyFn,
// createFn: create element + cache entry
(item) => {
const key = keyFn(item);
const [getIndex, setIndex] = createSignal(0);
const element = untrack(() => renderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
},
updateRow,
// beforeNode: insert items before the end marker
endMarker
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < cleanItems.length; i++) {
const key = keyFn(cleanItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
currentNodes = result.nodes;
currentItems = result.items;
});
return fragment2;
}
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList };
//# sourceMappingURL=chunk-FJGTMWKY.js.map
//# sourceMappingURL=chunk-FJGTMWKY.js.map

Sorry, the diff of this file is too big to display

'use strict';
var chunkDCTOXHPF_cjs = require('./chunk-DCTOXHPF.cjs');
var chunk3U57L2TY_cjs = require('./chunk-3U57L2TY.cjs');
// src/dom/element.ts
var Fragment = /* @__PURE__ */ Symbol.for("forma.fragment");
var SVG_NS = "http://www.w3.org/2000/svg";
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_TAGS = /* @__PURE__ */ new Set([
"svg",
"path",
"circle",
"rect",
"line",
"polyline",
"polygon",
"ellipse",
"g",
"text",
"tspan",
"textPath",
"defs",
"use",
"symbol",
"clipPath",
"mask",
"pattern",
"marker",
"linearGradient",
"radialGradient",
"stop",
"filter",
"feGaussianBlur",
"feColorMatrix",
"feOffset",
"feBlend",
"feMerge",
"feMergeNode",
"feComposite",
"feFlood",
"feMorphology",
"feTurbulence",
"feDisplacementMap",
"feImage",
"foreignObject",
"animate",
"animateTransform",
"animateMotion",
"set",
"image",
"switch",
"desc",
"title",
"metadata"
]);
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
"disabled",
"checked",
"readonly",
"required",
"autofocus",
"autoplay",
"controls",
"default",
"defer",
"formnovalidate",
"hidden",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"reversed",
"selected",
"async"
]);
var ELEMENT_PROTOS = null;
function getProto(tag) {
if (!ELEMENT_PROTOS) {
ELEMENT_PROTOS = /* @__PURE__ */ Object.create(null);
for (const t of [
"div",
"span",
"p",
"a",
"li",
"ul",
"ol",
"button",
"input",
"label",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"section",
"header",
"footer",
"main",
"nav",
"table",
"tr",
"td",
"th",
"tbody",
"img",
"form",
"select",
"option",
"textarea",
"i",
"b",
"strong",
"em",
"small",
"article",
"aside",
"details",
"summary"
]) {
ELEMENT_PROTOS[t] = document.createElement(t);
}
}
return ELEMENT_PROTOS[tag] ?? (ELEMENT_PROTOS[tag] = document.createElement(tag));
}
var EVENT_NAMES = /* @__PURE__ */ Object.create(null);
function eventName(key) {
return EVENT_NAMES[key] ?? (EVENT_NAMES[key] = key.slice(2).toLowerCase());
}
var ABORT_SYM = /* @__PURE__ */ Symbol.for("forma-abort");
function getAbortController(el) {
let controller = el[ABORT_SYM];
if (!controller) {
controller = new AbortController();
el[ABORT_SYM] = controller;
}
return controller;
}
function cleanup(el) {
const controller = el[ABORT_SYM];
if (controller) {
controller.abort();
delete el[ABORT_SYM];
}
}
var CACHE_SYM = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function getCache(el) {
return el[CACHE_SYM] ?? (el[CACHE_SYM] = /* @__PURE__ */ Object.create(null));
}
function handleClass(el, _key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache["class"] === v) return;
cache["class"] = v;
if (el instanceof HTMLElement) {
el.className = v;
} else {
el.setAttribute("class", v);
}
});
} else {
const cache = getCache(el);
if (cache["class"] === value) return;
cache["class"] = value;
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
}
}
function handleStyle(el, _key, value) {
if (typeof value === "function") {
let prevKeys = [];
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (typeof v === "string") {
const cache = getCache(el);
if (cache["style"] === v) return;
cache["style"] = v;
prevKeys = [];
el.style.cssText = v;
} else if (v && typeof v === "object") {
const style = el.style;
const nextKeys = Object.keys(v);
for (const k of prevKeys) {
if (!(k in v)) {
style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
}
}
Object.assign(style, v);
prevKeys = nextKeys;
}
});
} else if (typeof value === "string") {
const cache = getCache(el);
if (cache["style"] === value) return;
cache["style"] = value;
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
}
function handleEvent(el, key, value) {
const controller = getAbortController(el);
el.addEventListener(
eventName(key),
value,
{ signal: controller.signal }
);
}
function handleInnerHTML(el, _key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const resolved = value();
if (resolved == null) {
el.innerHTML = "";
return;
}
if (typeof resolved !== "object" || !("__html" in resolved)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof resolved
);
}
const html = resolved.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
const cache = getCache(el);
if (cache["innerHTML"] === html) return;
cache["innerHTML"] = html;
el.innerHTML = html;
});
} else {
if (value == null) {
el.innerHTML = "";
return;
}
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
}
}
function handleXLink(el, key, value) {
const localName = key.slice(6);
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (v == null || v === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(v));
}
});
} else {
if (value == null || value === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(value));
}
}
}
function handleBooleanAttr(el, key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache[key] === v) return;
cache[key] = v;
if (v) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
});
} else {
const cache = getCache(el);
if (cache[key] === value) return;
cache[key] = value;
if (value) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
}
}
function handleGenericAttr(el, key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (v == null || v === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(v);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
});
} else {
if (value == null || value === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(value);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
}
}
var PROP_HANDLERS = /* @__PURE__ */ new Map();
PROP_HANDLERS.set("class", handleClass);
PROP_HANDLERS.set("className", handleClass);
PROP_HANDLERS.set("style", handleStyle);
PROP_HANDLERS.set("ref", () => {
});
PROP_HANDLERS.set("dangerouslySetInnerHTML", handleInnerHTML);
for (const attr of BOOLEAN_ATTRS) {
PROP_HANDLERS.set(attr, handleBooleanAttr);
}
function applyProp(el, key, value) {
if (key === "class") {
handleClass(el, key, value);
return;
}
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
return;
}
const handler = PROP_HANDLERS.get(key);
if (handler) {
handler(el, key, value);
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
handleXLink(el, key, value);
return;
}
handleGenericAttr(el, key, value);
}
function applyStaticProp(el, key, value) {
if (value == null || value === false) return;
if (key === "class" || key === "className") {
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
return;
}
if (key === "style") {
if (typeof value === "string") {
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
return;
}
if (key === "dangerouslySetInnerHTML") {
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
el.setAttributeNS(XLINK_NS, key, String(value));
return;
}
if (BOOLEAN_ATTRS.has(key)) {
if (value) el.setAttribute(key, "");
return;
}
if (value === true) {
el.setAttribute(key, "");
} else {
el.setAttribute(key, String(value));
}
}
function appendChild(parent, child) {
if (child instanceof Node) {
parent.appendChild(child);
return;
}
if (typeof child === "string") {
parent.appendChild(new Text(child));
return;
}
if (child == null || child === false || child === true) {
return;
}
if (typeof child === "number") {
parent.appendChild(new Text(String(child)));
return;
}
if (typeof child === "function") {
if (parent instanceof Element) {
parent[DYNAMIC_CHILD_SYM] = true;
}
let currentNode = null;
let currentFragChildren = null;
let warnedArray = false;
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const clearCurrent = () => {
if (currentFragChildren) {
for (const c of currentFragChildren) {
if (c.parentNode === parent) parent.removeChild(c);
}
currentFragChildren = null;
}
if (currentNode && currentNode.parentNode === parent) {
parent.removeChild(currentNode);
}
currentNode = null;
};
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = child();
let resolved = v;
if (Array.isArray(v)) {
const frag = document.createDocumentFragment();
for (const item of v) {
if (item instanceof Node) frag.appendChild(item);
else if (Array.isArray(item)) {
if (DEBUG) console.warn("[forma] Nested arrays in function children are not supported. Flatten the array or use createList().");
} else if (item != null && item !== false && item !== true) {
frag.appendChild(new Text(String(item)));
}
}
resolved = frag.childNodes.length > 0 ? frag : null;
if (DEBUG && !warnedArray) {
warnedArray = true;
console.warn("[forma] Function child returned an array \u2014 auto-wrapped in DocumentFragment. Consider using createList() or wrapping in a container element for better performance.");
}
}
if (resolved instanceof Node) {
clearCurrent();
const isNewFrag = resolved instanceof DocumentFragment;
if (isNewFrag) {
currentFragChildren = Array.from(resolved.childNodes);
}
parent.appendChild(resolved);
currentNode = isNewFrag ? null : resolved;
} else if (resolved == null || resolved === false || resolved === true) {
clearCurrent();
} else {
if (currentFragChildren) {
for (const c of currentFragChildren) {
if (c.parentNode === parent) parent.removeChild(c);
}
currentFragChildren = null;
}
const text = typeof resolved === "symbol" ? String(resolved) : String(resolved ?? "");
if (!currentNode) {
currentNode = new Text(text);
parent.appendChild(currentNode);
} else if (currentNode.nodeType === 3) {
currentNode.data = text;
} else {
const tn = new Text(text);
parent.replaceChild(tn, currentNode);
currentNode = tn;
}
}
});
return;
}
if (Array.isArray(child)) {
for (const item of child) {
appendChild(parent, item);
}
return;
}
}
function h(tag, props, ...children) {
if (typeof tag === "function" && tag !== Fragment) {
const mergedProps = { ...props ?? {}, children };
return tag(mergedProps);
}
if (tag === Fragment) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
const tagName = tag;
if (hydrating) {
return { type: "element", tag: tagName, props: props ?? null, children };
}
let el;
if (ELEMENT_PROTOS && ELEMENT_PROTOS[tagName]) {
el = ELEMENT_PROTOS[tagName].cloneNode(false);
} else if (SVG_TAGS.has(tagName)) {
el = document.createElementNS(SVG_NS, tagName);
} else {
el = getProto(tagName).cloneNode(false);
}
if (props) {
let hasDynamic = false;
for (const key in props) {
if (key === "ref") continue;
const value = props[key];
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
continue;
}
if (typeof value === "function") {
if (!hasDynamic) {
el[CACHE_SYM] = /* @__PURE__ */ Object.create(null);
hasDynamic = true;
}
applyProp(el, key, value);
continue;
}
applyStaticProp(el, key, value);
}
}
const childLen = children.length;
if (childLen === 1) {
const only = children[0];
if (typeof only === "string") {
el.textContent = only;
} else if (typeof only === "number") {
el.textContent = String(only);
} else {
appendChild(el, only);
}
} else if (childLen > 1) {
for (const child of children) {
appendChild(el, child);
}
}
if (props && typeof props["ref"] === "function") {
props["ref"](el);
}
return el;
}
function fragment(...children) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
// src/dom/show.ts
function createShow(when, thenFn, elseFn) {
if (hydrating) {
const branch = when() ? thenFn() : elseFn?.() ?? null;
return {
type: "show",
condition: when,
whenTrue: thenFn,
whenFalse: elseFn,
initialBranch: branch
};
}
const startMarker = document.createComment("forma-show");
const endMarker = document.createComment("/forma-show");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let currentNode = null;
let lastTruthy = null;
let currentDispose = null;
const showDispose = chunkDCTOXHPF_cjs.internalEffect(() => {
const truthy = !!when();
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
if (truthy === lastTruthy) {
if (DEBUG) console.log("[forma:show] skip (same)", truthy, DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show]", lastTruthy, "\u2192", truthy, DEBUG_LABEL);
lastTruthy = truthy;
const parent = startMarker.parentNode;
if (!parent) {
if (DEBUG) console.warn("[forma:show] parentNode is null! skipping.", DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show] parent:", parent.nodeName, "inDoc:", document.contains(parent));
if (currentDispose) {
currentDispose();
currentDispose = null;
}
if (currentNode) {
if (currentNode.parentNode === parent) {
parent.removeChild(currentNode);
} else {
while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
parent.removeChild(startMarker.nextSibling);
}
}
}
const branchFn = truthy ? thenFn : elseFn;
if (branchFn) {
let branchDispose;
currentNode = chunkDCTOXHPF_cjs.createRoot((dispose) => {
branchDispose = dispose;
return chunkDCTOXHPF_cjs.untrack(() => branchFn());
});
currentDispose = branchDispose;
} else {
currentNode = null;
}
if (currentNode) {
parent.insertBefore(currentNode, endMarker);
}
});
fragment2.__showDispose = () => {
showDispose();
if (currentDispose) {
currentDispose();
currentDispose = null;
}
};
return fragment2;
}
// src/dom/hydrate.ts
var ABORT_SYM2 = /* @__PURE__ */ Symbol.for("forma-abort");
var hydrating = false;
function setHydrating(value) {
hydrating = value;
}
function isDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "element";
}
function isShowDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "show";
}
function isListDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "list";
}
function applyDynamicProps(el, props) {
if (!props) return;
for (const key in props) {
const value = props[key];
if (typeof value !== "function") continue;
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
let ac = el[ABORT_SYM2];
if (!ac) {
ac = new AbortController();
el[ABORT_SYM2] = ac;
}
el.addEventListener(key.slice(2).toLowerCase(), value, { signal: ac.signal });
continue;
}
const fn = value;
const attrKey = key;
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = fn();
if (v === false || v == null) {
el.removeAttribute(attrKey);
} else if (v === true) {
el.setAttribute(attrKey, "");
} else {
el.setAttribute(attrKey, String(v));
}
});
}
}
function ensureNode(value) {
if (value instanceof Node) return value;
if (value == null || value === false || value === true) return null;
if (typeof value === "string") return new Text(value);
if (typeof value === "number") return new Text(String(value));
if (isDescriptor(value)) return descriptorToElement(value);
if (isShowDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createShow(
value.condition,
() => ensureNode(value.whenTrue()) ?? document.createComment("empty"),
value.whenFalse ? () => ensureNode(value.whenFalse()) ?? document.createComment("empty") : void 0
);
} finally {
hydrating = prevH;
}
}
if (isListDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createList(value.items, value.keyFn, value.renderFn, value.options);
} finally {
hydrating = prevH;
}
}
return null;
}
function descriptorToElement(desc) {
const prevHydrating = hydrating;
hydrating = false;
try {
const children = desc.children.map((child) => {
if (isDescriptor(child)) return descriptorToElement(child);
if (isShowDescriptor(child)) return ensureNode(child);
if (isListDescriptor(child)) return ensureNode(child);
return child;
});
return h(desc.tag, desc.props, ...children);
} finally {
hydrating = prevHydrating;
}
}
function isIslandStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 105;
}
function isShowStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 115;
}
function isTextStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 116;
}
function isListStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 108;
}
function findClosingMarker(start) {
const closing = "/" + start.data;
let node = start.nextSibling;
while (node) {
if (node.nodeType === 8 && node.data === closing) {
return node;
}
node = node.nextSibling;
}
return null;
}
function findTextBetween(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 3) return node;
node = node.nextSibling;
}
return null;
}
function nextElementBetweenMarkers(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) return node;
node = node.nextSibling;
}
return void 0;
}
function extractContentBetweenMarkers(start, end) {
const frag = document.createDocumentFragment();
let node = start.nextSibling;
while (node && node !== end) {
const next = node.nextSibling;
frag.appendChild(node);
node = next;
}
return frag;
}
function setupShowEffect(desc, marker) {
let currentCondition = !!desc.condition();
let thenFragment = null;
let elseFragment = null;
const hasSSRContent = marker.start.nextSibling !== marker.end;
if (!hasSSRContent && currentCondition) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
const trueBranch = desc.whenTrue();
if (trueBranch instanceof Node) {
marker.start.parentNode.insertBefore(trueBranch, marker.end);
}
}
chunkDCTOXHPF_cjs.internalEffect(() => {
const next = !!desc.condition();
if (next === currentCondition) return;
currentCondition = next;
const parent = marker.start.parentNode;
if (!parent) return;
const current = extractContentBetweenMarkers(marker.start, marker.end);
if (!next) {
thenFragment = current;
} else {
elseFragment = current;
}
let branch = next ? thenFragment ?? desc.whenTrue() : desc.whenFalse ? elseFragment ?? desc.whenFalse() : null;
if (next && thenFragment) thenFragment = null;
if (!next && elseFragment) elseFragment = null;
if (branch != null && !(branch instanceof Node)) {
branch = ensureNode(branch);
}
if (branch instanceof Node) {
parent.insertBefore(branch, marker.end);
}
});
}
function adoptBranchContent(desc, regionStart, regionEnd) {
if (isDescriptor(desc)) {
const el = nextElementBetweenMarkers(regionStart, regionEnd);
if (el) adoptNode(desc, el);
} else if (isShowDescriptor(desc)) {
let node = regionStart.nextSibling;
while (node && node !== regionEnd) {
if (node.nodeType === 8 && isShowStart(node.data)) {
const innerStart = node;
const innerEnd = findClosingMarker(innerStart);
if (innerEnd) {
if (desc.initialBranch) {
adoptBranchContent(desc.initialBranch, innerStart, innerEnd);
}
setupShowEffect(desc, { start: innerStart, end: innerEnd});
}
break;
}
node = node.nextSibling;
}
}
}
function adoptNode(desc, ssrEl) {
if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
const fresh = descriptorToElement(desc);
if (ssrEl) ssrEl.replaceWith(fresh);
return;
}
applyDynamicProps(ssrEl, desc.props);
let cursor = ssrEl.firstChild;
for (const child of desc.children) {
if (child === false || child == null) continue;
if (isDescriptor(child)) {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
while (cursor && cursor.nodeType === 1 && cursor.hasAttribute("data-forma-island")) {
cursor = cursor.nextSibling;
}
if (!cursor) {
ssrEl.appendChild(descriptorToElement(child));
continue;
}
if (cursor.nodeType === 1) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(child, el);
} else if (cursor.nodeType === 8 && isIslandStart(cursor.data)) {
const end = findClosingMarker(cursor);
const fresh = descriptorToElement(child);
if (end) {
end.parentNode.insertBefore(fresh, end);
cursor = end.nextSibling;
} else {
ssrEl.appendChild(fresh);
cursor = null;
}
} else {
ssrEl.appendChild(descriptorToElement(child));
}
} else if (isShowDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isShowStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
if (child.initialBranch) {
adoptBranchContent(child.initialBranch, start, end);
}
setupShowEffect(child, { start, end});
cursor = end.nextSibling;
}
}
} else if (isListDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isListStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
const ssrKeyMap = /* @__PURE__ */ new Map();
const ssrElements = [];
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) {
const el = node;
ssrElements.push(el);
const key = el.getAttribute("data-forma-key");
if (key != null) {
ssrKeyMap.set(key, el);
}
}
node = node.nextSibling;
}
const currentItems = chunkDCTOXHPF_cjs.untrack(() => child.items());
const listKeyFn = child.keyFn;
const listRenderFn = child.renderFn;
const useIndexFallback = ssrKeyMap.size === 0 && ssrElements.length > 0;
const adoptedNodes = [];
const adoptedItems = [];
const usedIndices = /* @__PURE__ */ new Set();
for (let i = 0; i < currentItems.length; i++) {
const item = currentItems[i];
const key = listKeyFn(item);
let ssrNode;
if (useIndexFallback) {
if (i < ssrElements.length) {
ssrNode = ssrElements[i];
usedIndices.add(i);
}
} else {
ssrNode = ssrKeyMap.get(String(key));
if (ssrNode) ssrKeyMap.delete(String(key));
}
if (ssrNode) {
adoptedNodes.push(ssrNode);
adoptedItems.push(item);
} else {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
const prevHydrating = hydrating;
hydrating = false;
try {
const [getIndex] = chunk3U57L2TY_cjs.createSignal(i);
const fresh = listRenderFn(item, getIndex);
end.parentNode.insertBefore(fresh, end);
adoptedNodes.push(fresh);
adoptedItems.push(item);
} finally {
hydrating = prevHydrating;
}
}
}
if (useIndexFallback) {
for (let i = 0; i < ssrElements.length; i++) {
if (!usedIndices.has(i) && ssrElements[i].parentNode) {
ssrElements[i].parentNode.removeChild(ssrElements[i]);
}
}
} else {
for (const [unusedKey, unusedNode] of ssrKeyMap) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
if (unusedNode.parentNode) {
unusedNode.parentNode.removeChild(unusedNode);
}
}
}
const parent = start.parentNode;
for (const adoptedNode of adoptedNodes) {
parent.insertBefore(adoptedNode, end);
}
let cache = /* @__PURE__ */ new Map();
for (let i = 0; i < adoptedItems.length; i++) {
const item = adoptedItems[i];
const key = listKeyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(i);
cache.set(key, {
element: adoptedNodes[i],
item,
getIndex,
setIndex
});
}
let reconcileNodes = adoptedNodes.slice();
let reconcileItems = adoptedItems.slice();
chunkDCTOXHPF_cjs.internalEffect(() => {
const newItems = child.items();
const parent2 = start.parentNode;
if (!parent2) return;
const result = reconcileList(
parent2,
reconcileItems,
newItems,
reconcileNodes,
listKeyFn,
(item) => {
const prevHydrating = hydrating;
hydrating = false;
try {
const key = listKeyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(0);
const element = chunkDCTOXHPF_cjs.untrack(() => listRenderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
} finally {
hydrating = prevHydrating;
}
},
(_node, item) => {
const key = listKeyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
},
end
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < newItems.length; i++) {
const key = listKeyFn(newItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
reconcileNodes = result.nodes;
reconcileItems = result.items;
});
cursor = end.nextSibling;
}
}
} else if (typeof child === "function") {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
if (cursor && cursor.nodeType === 1) {
const initial = child();
if (isDescriptor(initial)) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(initial, el);
continue;
}
}
if (cursor && cursor.nodeType === 8) {
const data = cursor.data;
if (isTextStart(data)) {
const endMarker = findClosingMarker(cursor);
let textNode = cursor.nextSibling;
if (!textNode || textNode.nodeType !== 3) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
const created = document.createTextNode("");
cursor.parentNode.insertBefore(created, endMarker || cursor.nextSibling);
textNode = created;
}
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
cursor = endMarker ? endMarker.nextSibling : textNode.nextSibling;
} else if (isShowStart(data)) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
let textNode = findTextBetween(start, end);
if (!textNode) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
textNode = document.createTextNode("");
start.parentNode.insertBefore(textNode, end);
}
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
cursor = end.nextSibling;
} else {
cursor = cursor.nextSibling;
}
} else {
cursor = cursor.nextSibling;
}
} else if (cursor && cursor.nodeType === 3) {
const textNode = cursor;
cursor = cursor.nextSibling;
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
} else {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
const textNode = document.createTextNode("");
ssrEl.appendChild(textNode);
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
}
} else if (typeof child === "string" || typeof child === "number") {
if (cursor && cursor.nodeType === 3) {
cursor = cursor.nextSibling;
}
}
}
}
function hydrateIsland(component, target) {
const hasSSRContent = target.childElementCount > 0 || target.childNodes.length > 0 && Array.from(target.childNodes).some((n) => n.nodeType === 1 || n.nodeType === 3 && n.data.trim());
if (!hasSSRContent) {
if (chunkDCTOXHPF_cjs.__DEV__) {
const name = target.getAttribute("data-forma-component") || "unknown";
console.warn(
`[forma] Island "${name}" has no SSR content \u2014 falling back to CSR. This means the IR walker did not render content between ISLAND_START and ISLAND_END.`
);
}
const result = component();
if (result instanceof Element) {
for (const attr of Array.from(target.attributes)) {
if (attr.name.startsWith("data-forma-")) {
result.setAttribute(attr.name, attr.value);
}
}
target.replaceWith(result);
return result;
} else if (result instanceof Node) {
target.appendChild(result);
}
return target;
}
setHydrating(true);
let descriptor;
try {
descriptor = component();
} finally {
setHydrating(false);
}
if (!descriptor || !isDescriptor(descriptor)) {
target.removeAttribute("data-forma-ssr");
return target;
}
if (target.hasAttribute("data-forma-island")) {
adoptNode(descriptor, target);
} else {
adoptNode(descriptor, target.children[0]);
}
target.removeAttribute("data-forma-ssr");
return target;
}
// src/dom/list.ts
function longestIncreasingSubsequence(arr) {
const n = arr.length;
if (n === 0) return [];
const tails = new Int32Array(n);
const tailIndices = new Int32Array(n);
const predecessor = new Int32Array(n).fill(-1);
let tailsLen = 0;
for (let i = 0; i < n; i++) {
const val = arr[i];
let lo = 0, hi = tailsLen;
while (lo < hi) {
const mid = lo + hi >> 1;
if (tails[mid] < val) lo = mid + 1;
else hi = mid;
}
tails[lo] = val;
tailIndices[lo] = i;
if (lo > 0) predecessor[i] = tailIndices[lo - 1];
if (lo >= tailsLen) tailsLen++;
}
const result = new Array(tailsLen);
let idx = tailIndices[tailsLen - 1];
for (let i = tailsLen - 1; i >= 0; i--) {
result[i] = idx;
idx = predecessor[idx];
}
return result;
}
var SMALL_LIST_THRESHOLD = 32;
var ABORT_SYM3 = /* @__PURE__ */ Symbol.for("forma-abort");
var CACHE_SYM2 = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM2 = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function canPatchStaticElement(target, source) {
return target instanceof HTMLElement && source instanceof HTMLElement && target.tagName === source.tagName && !target[ABORT_SYM3] && !target[CACHE_SYM2] && !target[DYNAMIC_CHILD_SYM2] && !source[ABORT_SYM3] && !source[CACHE_SYM2] && !source[DYNAMIC_CHILD_SYM2];
}
function patchStaticElement(target, source) {
const sourceAttrNames = /* @__PURE__ */ new Set();
for (const attr of Array.from(source.attributes)) {
sourceAttrNames.add(attr.name);
if (target.getAttribute(attr.name) !== attr.value) {
target.setAttribute(attr.name, attr.value);
}
}
for (const attr of Array.from(target.attributes)) {
if (!sourceAttrNames.has(attr.name)) {
target.removeAttribute(attr.name);
}
}
target.replaceChildren(...Array.from(source.childNodes));
}
function reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
const oldKeys = new Array(oldLen);
for (let i = 0; i < oldLen; i++) {
oldKeys[i] = keyFn(oldItems[i]);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
let found = -1;
for (let j = 0; j < oldLen; j++) {
if (!oldUsed[j] && oldKeys[j] === key) {
found = j;
oldUsed[j] = 1;
break;
}
}
oldIndices[i] = found;
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function reconcileList(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
if (newLen === 0) {
for (let i = 0; i < oldLen; i++) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
return { nodes: [], items: [] };
}
if (oldLen === 0) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = createFn(newItems[i]);
if (beforeNode) {
parent.insertBefore(node, beforeNode);
} else {
parent.appendChild(node);
}
hooks?.onInsert?.(node);
nodes[i] = node;
}
return { nodes, items: newItems };
}
if (oldLen < SMALL_LIST_THRESHOLD) {
return reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks);
}
const oldKeyMap = /* @__PURE__ */ new Map();
for (let i = 0; i < oldLen; i++) {
oldKeyMap.set(keyFn(oldItems[i]), i);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
const oldIdx = oldKeyMap.get(key);
if (oldIdx !== void 0) {
oldIndices[i] = oldIdx;
oldUsed[oldIdx] = 1;
} else {
oldIndices[i] = -1;
}
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function createList(items, keyFn, renderFn, options) {
if (hydrating) {
return { type: "list", items, keyFn, renderFn, options };
}
const startMarker = document.createComment("forma-list-start");
const endMarker = document.createComment("forma-list-end");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let cache = /* @__PURE__ */ new Map();
let currentNodes = [];
let currentItems = [];
const updateOnItemChange = options?.updateOnItemChange ?? "none";
chunkDCTOXHPF_cjs.internalEffect(() => {
const newItems = items();
const parent = startMarker.parentNode;
if (!parent) {
return;
}
if (!Array.isArray(newItems)) {
if (chunkDCTOXHPF_cjs.__DEV__) {
console.warn("[forma] createList: value is not an array, treating as empty");
}
for (const node of currentNodes) {
if (node.parentNode === parent) parent.removeChild(node);
}
cache = /* @__PURE__ */ new Map();
currentNodes = [];
currentItems = [];
return;
}
let cleanItems = newItems;
for (let i = 0; i < newItems.length; i++) {
if (newItems[i] == null) {
cleanItems = newItems.filter((item) => item != null);
break;
}
}
if (chunkDCTOXHPF_cjs.__DEV__) {
const seen = /* @__PURE__ */ new Set();
for (const item of cleanItems) {
const key = keyFn(item);
if (seen.has(key)) {
console.warn("[forma] createList: duplicate key detected:", key);
}
seen.add(key);
}
}
const updateRow = updateOnItemChange === "rerender" ? (node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (!cached) return;
if (cached.item === item) return;
cached.item = item;
if (!(node instanceof HTMLElement)) return;
if (node[ABORT_SYM3] || node[CACHE_SYM2] || node[DYNAMIC_CHILD_SYM2]) {
return;
}
const next = chunkDCTOXHPF_cjs.untrack(() => renderFn(item, cached.getIndex));
if (canPatchStaticElement(node, next)) {
patchStaticElement(node, next);
cached.element = node;
}
} : (_node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
};
const result = reconcileList(
parent,
currentItems,
cleanItems,
currentNodes,
keyFn,
// createFn: create element + cache entry
(item) => {
const key = keyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(0);
const element = chunkDCTOXHPF_cjs.untrack(() => renderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
},
updateRow,
// beforeNode: insert items before the end marker
endMarker
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < cleanItems.length; i++) {
const key = keyFn(cleanItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
currentNodes = result.nodes;
currentItems = result.items;
});
return fragment2;
}
exports.Fragment = Fragment;
exports.cleanup = cleanup;
exports.createList = createList;
exports.createShow = createShow;
exports.fragment = fragment;
exports.h = h;
exports.hydrateIsland = hydrateIsland;
exports.reconcileList = reconcileList;
//# sourceMappingURL=chunk-SZSKW57A.cjs.map
//# sourceMappingURL=chunk-SZSKW57A.cjs.map

Sorry, the diff of this file is too big to display

+2
-2

@@ -62,6 +62,6 @@ 'use strict';

url();
execute();
void execute();
});
} else {
execute();
void execute();
}

@@ -68,0 +68,0 @@ return {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/http/fetch.ts","../src/http/sse.ts","../src/http/ws.ts"],"names":["createSignal","internalEffect"],"mappings":"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","file":"http.cjs","sourcesContent":["/**\n * Forma HTTP - Fetch\n *\n * Typed fetch wrapper with reactive signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal, internalEffect } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchOptions<T> extends Omit<RequestInit, 'signal'> {\n base?: string;\n params?: Record<string, string>;\n timeout?: number; // ms, default 30000\n transform?: (data: unknown) => T;\n}\n\nexport interface FetchResult<T> {\n data: () => T | null;\n error: () => Error | null;\n loading: () => boolean;\n refetch: () => Promise<void>;\n abort: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createFetch — reactive fetch with signals\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive fetch that exposes data/error/loading as signals.\n *\n * If `url` is a signal getter (function), an effect auto-refetches when it\n * changes.\n *\n * ```ts\n * const { data, loading, error, refetch, abort } = createFetch<User[]>('/api/users');\n * ```\n */\nexport function createFetch<T>(\n url: string | (() => string),\n options?: FetchOptions<T>,\n): FetchResult<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Error | null>(null);\n const [loading, setLoading] = createSignal<boolean>(false);\n\n let currentController: AbortController | null = null;\n\n /** Resolve the URL string, applying base and params. */\n function resolveURL(): string {\n const raw = typeof url === 'function' ? url() : url;\n const base = options?.base || (typeof window !== 'undefined' ? window.location.origin : '');\n const fullURL = new URL(raw, base || undefined);\n\n if (options?.params) {\n for (const [key, value] of Object.entries(options.params)) {\n fullURL.searchParams.set(key, value);\n }\n }\n\n return fullURL.toString();\n }\n\n /** Execute a single fetch request. */\n async function execute(): Promise<void> {\n // Abort any in-flight request\n if (currentController) {\n currentController.abort();\n }\n\n currentController = new AbortController();\n const controller = currentController;\n const timeoutMs = options?.timeout ?? 30_000;\n\n // Set up timeout\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n setLoading(true);\n setError(null);\n\n try {\n const resolvedURL = resolveURL();\n\n const { base: _base, params: _params, timeout: _timeout, transform, ...fetchInit } =\n options ?? {};\n\n const response = await fetch(resolvedURL, {\n ...fetchInit,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const json: unknown = await response.json();\n const transformed = transform ? transform(json) : (json as T);\n setData(transformed);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Ignore aborts — they are intentional or timeouts\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n clearTimeout(timeoutId);\n // Only update loading if this controller is still current\n if (currentController === controller) {\n currentController = null;\n setLoading(false);\n }\n }\n }\n\n // If url is a reactive getter, set up an effect to auto-refetch.\n if (typeof url === 'function') {\n internalEffect(() => {\n // Read the signal so the effect re-runs when it changes\n url();\n execute();\n });\n } else {\n // Kick off the first request immediately\n execute();\n }\n\n return {\n data,\n error,\n loading,\n refetch: execute,\n abort() {\n if (currentController) {\n currentController.abort();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fetchJSON — simple one-shot helper\n// ---------------------------------------------------------------------------\n\n/**\n * One-shot fetch that returns parsed JSON.\n *\n * ```ts\n * const users = await fetchJSON<User[]>('/api/users');\n * ```\n */\nexport async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {\n const response = await fetch(url, options);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n return (await response.json()) as T;\n}\n","/**\n * Forma HTTP - Server-Sent Events\n *\n * Reactive SSE wrapper with signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SSEOptions<T = unknown> {\n withCredentials?: boolean;\n headers?: Record<string, string>; // Note: native EventSource does not support custom headers\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => T;\n}\n\nexport interface SSEConnection<T = unknown> {\n data: () => T | null;\n error: () => Event | null;\n connected: () => boolean;\n close: () => void;\n on(event: string, handler: (data: unknown) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createSSE\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive Server-Sent Events connection.\n *\n * ```ts\n * const sse = createSSE<{ message: string }>('/api/events');\n * createEffect(() => {\n * const msg = sse.data();\n * if (msg) console.log(msg.message);\n * });\n * ```\n */\nexport function createSSE<T = unknown>(\n url: string,\n options?: SSEOptions<T>,\n): SSEConnection<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Event | null>(null);\n const [connected, setConnected] = createSignal<boolean>(false);\n\n const source = new EventSource(url, {\n withCredentials: options?.withCredentials ?? false,\n });\n\n source.onopen = () => {\n setConnected(true);\n setError(null);\n };\n\n const parseMessage = options?.parse ?? ((raw: string): T => {\n try { return JSON.parse(raw) as T; }\n catch { return raw as T; }\n });\n\n source.onmessage = (event: MessageEvent) => {\n setData(parseMessage(event.data as string));\n };\n\n source.onerror = (event: Event) => {\n setError(event);\n setConnected(false);\n };\n\n return {\n data,\n error,\n connected,\n\n close(): void {\n source.close();\n setConnected(false);\n },\n\n on(event: string, handler: (data: unknown) => void): () => void {\n const parseEvent = options?.parse ?? ((raw: string) => {\n try { return JSON.parse(raw); }\n catch { return raw; }\n });\n const listener = (e: MessageEvent) => {\n handler(parseEvent(e.data as string));\n };\n source.addEventListener(event, listener as EventListener);\n return () => {\n source.removeEventListener(event, listener as EventListener);\n };\n },\n };\n}\n","/**\n * Forma HTTP - WebSocket\n *\n * Reactive WebSocket wrapper with auto-reconnect and signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WSStatus = 'connecting' | 'open' | 'closed' | 'error';\n\nexport interface WSOptions<TReceive = unknown> {\n protocols?: string | string[];\n reconnect?: boolean; // default true\n reconnectInterval?: number; // ms, default 1000\n maxReconnects?: number; // default 5\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => TReceive;\n}\n\nexport interface WSConnection<TSend = unknown, TReceive = unknown> {\n data: () => TReceive | null;\n status: () => WSStatus;\n send(data: TSend): void;\n close(): void;\n on(handler: (data: TReceive) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createWebSocket\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive WebSocket connection with auto-reconnect.\n *\n * ```ts\n * const ws = createWebSocket<string, ChatMessage>('wss://chat.example.com');\n * ws.send('hello');\n * createEffect(() => {\n * const msg = ws.data();\n * if (msg) console.log(msg);\n * });\n * ```\n */\nexport function createWebSocket<TSend = unknown, TReceive = unknown>(\n url: string,\n options?: WSOptions<TReceive>,\n): WSConnection<TSend, TReceive> {\n const shouldReconnect = options?.reconnect ?? true;\n const baseInterval = options?.reconnectInterval ?? 1000;\n const maxReconnects = options?.maxReconnects ?? 5;\n\n const [data, setData] = createSignal<TReceive | null>(null);\n const [status, setStatus] = createSignal<WSStatus>('connecting');\n\n const handlers = new Set<(data: TReceive) => void>();\n\n let socket: WebSocket | null = null;\n let reconnectCount = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let permanentlyClosed = false;\n\n function connect(): void {\n if (permanentlyClosed) return;\n\n setStatus('connecting');\n socket = new WebSocket(url, options?.protocols);\n\n socket.onopen = () => {\n setStatus('open');\n reconnectCount = 0; // Reset on successful connection\n };\n\n const parseMessage = options?.parse ?? ((raw: string): TReceive => {\n try { return JSON.parse(raw) as TReceive; }\n catch { return raw as TReceive; }\n });\n\n socket.onmessage = (event: MessageEvent) => {\n const parsed = parseMessage(event.data as string);\n setData(parsed);\n for (const handler of handlers) {\n handler(parsed);\n }\n };\n\n socket.onerror = () => {\n setStatus('error');\n };\n\n socket.onclose = () => {\n if (permanentlyClosed) {\n setStatus('closed');\n return;\n }\n\n setStatus('closed');\n\n if (shouldReconnect && reconnectCount < maxReconnects) {\n // Exponential backoff: baseInterval * 2^reconnectCount\n const delay = baseInterval * Math.pow(2, reconnectCount);\n reconnectCount++;\n reconnectTimer = setTimeout(connect, delay);\n }\n };\n }\n\n // Initiate the first connection\n connect();\n\n return {\n data,\n status,\n\n send(value: TSend): void {\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(value));\n }\n },\n\n close(): void {\n permanentlyClosed = true;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (socket) {\n socket.close();\n socket = null;\n }\n setStatus('closed');\n },\n\n on(handler: (data: TReceive) => void): () => void {\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n };\n },\n };\n}\n"]}
{"version":3,"sources":["../src/http/fetch.ts","../src/http/sse.ts","../src/http/ws.ts"],"names":["createSignal","internalEffect"],"mappings":"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","file":"http.cjs","sourcesContent":["/**\n * Forma HTTP - Fetch\n *\n * Typed fetch wrapper with reactive signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal, internalEffect } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchOptions<T> extends Omit<RequestInit, 'signal'> {\n base?: string;\n params?: Record<string, string>;\n timeout?: number; // ms, default 30000\n transform?: (data: unknown) => T;\n}\n\nexport interface FetchResult<T> {\n data: () => T | null;\n error: () => Error | null;\n loading: () => boolean;\n refetch: () => Promise<void>;\n abort: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createFetch — reactive fetch with signals\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive fetch that exposes data/error/loading as signals.\n *\n * If `url` is a signal getter (function), an effect auto-refetches when it\n * changes.\n *\n * ```ts\n * const { data, loading, error, refetch, abort } = createFetch<User[]>('/api/users');\n * ```\n */\nexport function createFetch<T>(\n url: string | (() => string),\n options?: FetchOptions<T>,\n): FetchResult<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Error | null>(null);\n const [loading, setLoading] = createSignal<boolean>(false);\n\n let currentController: AbortController | null = null;\n\n /** Resolve the URL string, applying base and params. */\n function resolveURL(): string {\n const raw = typeof url === 'function' ? url() : url;\n const base = options?.base || (typeof window !== 'undefined' ? window.location.origin : '');\n const fullURL = new URL(raw, base || undefined);\n\n if (options?.params) {\n for (const [key, value] of Object.entries(options.params)) {\n fullURL.searchParams.set(key, value);\n }\n }\n\n return fullURL.toString();\n }\n\n /** Execute a single fetch request. */\n async function execute(): Promise<void> {\n // Abort any in-flight request\n if (currentController) {\n currentController.abort();\n }\n\n currentController = new AbortController();\n const controller = currentController;\n const timeoutMs = options?.timeout ?? 30_000;\n\n // Set up timeout\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n setLoading(true);\n setError(null);\n\n try {\n const resolvedURL = resolveURL();\n\n const { base: _base, params: _params, timeout: _timeout, transform, ...fetchInit } =\n options ?? {};\n\n const response = await fetch(resolvedURL, {\n ...fetchInit,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const json: unknown = await response.json();\n const transformed = transform ? transform(json) : (json as T);\n setData(transformed);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Ignore aborts — they are intentional or timeouts\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n clearTimeout(timeoutId);\n // Only update loading if this controller is still current\n if (currentController === controller) {\n currentController = null;\n setLoading(false);\n }\n }\n }\n\n // If url is a reactive getter, set up an effect to auto-refetch.\n if (typeof url === 'function') {\n internalEffect(() => {\n // Read the signal so the effect re-runs when it changes\n url();\n void execute();\n });\n } else {\n // Kick off the first request immediately\n void execute();\n }\n\n return {\n data,\n error,\n loading,\n refetch: execute,\n abort() {\n if (currentController) {\n currentController.abort();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fetchJSON — simple one-shot helper\n// ---------------------------------------------------------------------------\n\n/**\n * One-shot fetch that returns parsed JSON.\n *\n * ```ts\n * const users = await fetchJSON<User[]>('/api/users');\n * ```\n */\nexport async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {\n const response = await fetch(url, options);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n return (await response.json()) as T;\n}\n","/**\n * Forma HTTP - Server-Sent Events\n *\n * Reactive SSE wrapper with signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SSEOptions<T = unknown> {\n withCredentials?: boolean;\n headers?: Record<string, string>; // Note: native EventSource does not support custom headers\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => T;\n}\n\nexport interface SSEConnection<T = unknown> {\n data: () => T | null;\n error: () => Event | null;\n connected: () => boolean;\n close: () => void;\n on(event: string, handler: (data: unknown) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createSSE\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive Server-Sent Events connection.\n *\n * ```ts\n * const sse = createSSE<{ message: string }>('/api/events');\n * createEffect(() => {\n * const msg = sse.data();\n * if (msg) console.log(msg.message);\n * });\n * ```\n */\nexport function createSSE<T = unknown>(\n url: string,\n options?: SSEOptions<T>,\n): SSEConnection<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Event | null>(null);\n const [connected, setConnected] = createSignal<boolean>(false);\n\n const source = new EventSource(url, {\n withCredentials: options?.withCredentials ?? false,\n });\n\n source.onopen = () => {\n setConnected(true);\n setError(null);\n };\n\n const parseMessage = options?.parse ?? ((raw: string): T => {\n try { return JSON.parse(raw) as T; }\n catch { return raw as T; }\n });\n\n source.onmessage = (event: MessageEvent) => {\n setData(parseMessage(event.data as string));\n };\n\n source.onerror = (event: Event) => {\n setError(event);\n setConnected(false);\n };\n\n return {\n data,\n error,\n connected,\n\n close(): void {\n source.close();\n setConnected(false);\n },\n\n on(event: string, handler: (data: unknown) => void): () => void {\n const parseEvent = options?.parse ?? ((raw: string) => {\n try { return JSON.parse(raw); }\n catch { return raw; }\n });\n const listener = (e: MessageEvent) => {\n handler(parseEvent(e.data as string));\n };\n source.addEventListener(event, listener as EventListener);\n return () => {\n source.removeEventListener(event, listener as EventListener);\n };\n },\n };\n}\n","/**\n * Forma HTTP - WebSocket\n *\n * Reactive WebSocket wrapper with auto-reconnect and signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WSStatus = 'connecting' | 'open' | 'closed' | 'error';\n\nexport interface WSOptions<TReceive = unknown> {\n protocols?: string | string[];\n reconnect?: boolean; // default true\n reconnectInterval?: number; // ms, default 1000\n maxReconnects?: number; // default 5\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => TReceive;\n}\n\nexport interface WSConnection<TSend = unknown, TReceive = unknown> {\n data: () => TReceive | null;\n status: () => WSStatus;\n send(data: TSend): void;\n close(): void;\n on(handler: (data: TReceive) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createWebSocket\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive WebSocket connection with auto-reconnect.\n *\n * ```ts\n * const ws = createWebSocket<string, ChatMessage>('wss://chat.example.com');\n * ws.send('hello');\n * createEffect(() => {\n * const msg = ws.data();\n * if (msg) console.log(msg);\n * });\n * ```\n */\nexport function createWebSocket<TSend = unknown, TReceive = unknown>(\n url: string,\n options?: WSOptions<TReceive>,\n): WSConnection<TSend, TReceive> {\n const shouldReconnect = options?.reconnect ?? true;\n const baseInterval = options?.reconnectInterval ?? 1000;\n const maxReconnects = options?.maxReconnects ?? 5;\n\n const [data, setData] = createSignal<TReceive | null>(null);\n const [status, setStatus] = createSignal<WSStatus>('connecting');\n\n const handlers = new Set<(data: TReceive) => void>();\n\n let socket: WebSocket | null = null;\n let reconnectCount = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let permanentlyClosed = false;\n\n function connect(): void {\n if (permanentlyClosed) return;\n\n setStatus('connecting');\n socket = new WebSocket(url, options?.protocols);\n\n socket.onopen = () => {\n setStatus('open');\n reconnectCount = 0; // Reset on successful connection\n };\n\n const parseMessage = options?.parse ?? ((raw: string): TReceive => {\n try { return JSON.parse(raw) as TReceive; }\n catch { return raw as TReceive; }\n });\n\n socket.onmessage = (event: MessageEvent) => {\n const parsed = parseMessage(event.data as string);\n setData(parsed);\n for (const handler of handlers) {\n handler(parsed);\n }\n };\n\n socket.onerror = () => {\n setStatus('error');\n };\n\n socket.onclose = () => {\n if (permanentlyClosed) {\n setStatus('closed');\n return;\n }\n\n setStatus('closed');\n\n if (shouldReconnect && reconnectCount < maxReconnects) {\n // Exponential backoff: baseInterval * 2^reconnectCount\n const delay = baseInterval * Math.pow(2, reconnectCount);\n reconnectCount++;\n reconnectTimer = setTimeout(connect, delay);\n }\n };\n }\n\n // Initiate the first connection\n connect();\n\n return {\n data,\n status,\n\n send(value: TSend): void {\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(value));\n }\n },\n\n close(): void {\n permanentlyClosed = true;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (socket) {\n socket.close();\n socket = null;\n }\n setStatus('closed');\n },\n\n on(handler: (data: TReceive) => void): () => void {\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n };\n },\n };\n}\n"]}

@@ -60,6 +60,6 @@ import { internalEffect } from './chunk-OUVOAYIO.js';

url();
execute();
void execute();
});
} else {
execute();
void execute();
}

@@ -66,0 +66,0 @@ return {

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/http/fetch.ts","../src/http/sse.ts","../src/http/ws.ts"],"names":[],"mappings":"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","file":"http.js","sourcesContent":["/**\n * Forma HTTP - Fetch\n *\n * Typed fetch wrapper with reactive signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal, internalEffect } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchOptions<T> extends Omit<RequestInit, 'signal'> {\n base?: string;\n params?: Record<string, string>;\n timeout?: number; // ms, default 30000\n transform?: (data: unknown) => T;\n}\n\nexport interface FetchResult<T> {\n data: () => T | null;\n error: () => Error | null;\n loading: () => boolean;\n refetch: () => Promise<void>;\n abort: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createFetch — reactive fetch with signals\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive fetch that exposes data/error/loading as signals.\n *\n * If `url` is a signal getter (function), an effect auto-refetches when it\n * changes.\n *\n * ```ts\n * const { data, loading, error, refetch, abort } = createFetch<User[]>('/api/users');\n * ```\n */\nexport function createFetch<T>(\n url: string | (() => string),\n options?: FetchOptions<T>,\n): FetchResult<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Error | null>(null);\n const [loading, setLoading] = createSignal<boolean>(false);\n\n let currentController: AbortController | null = null;\n\n /** Resolve the URL string, applying base and params. */\n function resolveURL(): string {\n const raw = typeof url === 'function' ? url() : url;\n const base = options?.base || (typeof window !== 'undefined' ? window.location.origin : '');\n const fullURL = new URL(raw, base || undefined);\n\n if (options?.params) {\n for (const [key, value] of Object.entries(options.params)) {\n fullURL.searchParams.set(key, value);\n }\n }\n\n return fullURL.toString();\n }\n\n /** Execute a single fetch request. */\n async function execute(): Promise<void> {\n // Abort any in-flight request\n if (currentController) {\n currentController.abort();\n }\n\n currentController = new AbortController();\n const controller = currentController;\n const timeoutMs = options?.timeout ?? 30_000;\n\n // Set up timeout\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n setLoading(true);\n setError(null);\n\n try {\n const resolvedURL = resolveURL();\n\n const { base: _base, params: _params, timeout: _timeout, transform, ...fetchInit } =\n options ?? {};\n\n const response = await fetch(resolvedURL, {\n ...fetchInit,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const json: unknown = await response.json();\n const transformed = transform ? transform(json) : (json as T);\n setData(transformed);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Ignore aborts — they are intentional or timeouts\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n clearTimeout(timeoutId);\n // Only update loading if this controller is still current\n if (currentController === controller) {\n currentController = null;\n setLoading(false);\n }\n }\n }\n\n // If url is a reactive getter, set up an effect to auto-refetch.\n if (typeof url === 'function') {\n internalEffect(() => {\n // Read the signal so the effect re-runs when it changes\n url();\n execute();\n });\n } else {\n // Kick off the first request immediately\n execute();\n }\n\n return {\n data,\n error,\n loading,\n refetch: execute,\n abort() {\n if (currentController) {\n currentController.abort();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fetchJSON — simple one-shot helper\n// ---------------------------------------------------------------------------\n\n/**\n * One-shot fetch that returns parsed JSON.\n *\n * ```ts\n * const users = await fetchJSON<User[]>('/api/users');\n * ```\n */\nexport async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {\n const response = await fetch(url, options);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n return (await response.json()) as T;\n}\n","/**\n * Forma HTTP - Server-Sent Events\n *\n * Reactive SSE wrapper with signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SSEOptions<T = unknown> {\n withCredentials?: boolean;\n headers?: Record<string, string>; // Note: native EventSource does not support custom headers\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => T;\n}\n\nexport interface SSEConnection<T = unknown> {\n data: () => T | null;\n error: () => Event | null;\n connected: () => boolean;\n close: () => void;\n on(event: string, handler: (data: unknown) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createSSE\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive Server-Sent Events connection.\n *\n * ```ts\n * const sse = createSSE<{ message: string }>('/api/events');\n * createEffect(() => {\n * const msg = sse.data();\n * if (msg) console.log(msg.message);\n * });\n * ```\n */\nexport function createSSE<T = unknown>(\n url: string,\n options?: SSEOptions<T>,\n): SSEConnection<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Event | null>(null);\n const [connected, setConnected] = createSignal<boolean>(false);\n\n const source = new EventSource(url, {\n withCredentials: options?.withCredentials ?? false,\n });\n\n source.onopen = () => {\n setConnected(true);\n setError(null);\n };\n\n const parseMessage = options?.parse ?? ((raw: string): T => {\n try { return JSON.parse(raw) as T; }\n catch { return raw as T; }\n });\n\n source.onmessage = (event: MessageEvent) => {\n setData(parseMessage(event.data as string));\n };\n\n source.onerror = (event: Event) => {\n setError(event);\n setConnected(false);\n };\n\n return {\n data,\n error,\n connected,\n\n close(): void {\n source.close();\n setConnected(false);\n },\n\n on(event: string, handler: (data: unknown) => void): () => void {\n const parseEvent = options?.parse ?? ((raw: string) => {\n try { return JSON.parse(raw); }\n catch { return raw; }\n });\n const listener = (e: MessageEvent) => {\n handler(parseEvent(e.data as string));\n };\n source.addEventListener(event, listener as EventListener);\n return () => {\n source.removeEventListener(event, listener as EventListener);\n };\n },\n };\n}\n","/**\n * Forma HTTP - WebSocket\n *\n * Reactive WebSocket wrapper with auto-reconnect and signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WSStatus = 'connecting' | 'open' | 'closed' | 'error';\n\nexport interface WSOptions<TReceive = unknown> {\n protocols?: string | string[];\n reconnect?: boolean; // default true\n reconnectInterval?: number; // ms, default 1000\n maxReconnects?: number; // default 5\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => TReceive;\n}\n\nexport interface WSConnection<TSend = unknown, TReceive = unknown> {\n data: () => TReceive | null;\n status: () => WSStatus;\n send(data: TSend): void;\n close(): void;\n on(handler: (data: TReceive) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createWebSocket\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive WebSocket connection with auto-reconnect.\n *\n * ```ts\n * const ws = createWebSocket<string, ChatMessage>('wss://chat.example.com');\n * ws.send('hello');\n * createEffect(() => {\n * const msg = ws.data();\n * if (msg) console.log(msg);\n * });\n * ```\n */\nexport function createWebSocket<TSend = unknown, TReceive = unknown>(\n url: string,\n options?: WSOptions<TReceive>,\n): WSConnection<TSend, TReceive> {\n const shouldReconnect = options?.reconnect ?? true;\n const baseInterval = options?.reconnectInterval ?? 1000;\n const maxReconnects = options?.maxReconnects ?? 5;\n\n const [data, setData] = createSignal<TReceive | null>(null);\n const [status, setStatus] = createSignal<WSStatus>('connecting');\n\n const handlers = new Set<(data: TReceive) => void>();\n\n let socket: WebSocket | null = null;\n let reconnectCount = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let permanentlyClosed = false;\n\n function connect(): void {\n if (permanentlyClosed) return;\n\n setStatus('connecting');\n socket = new WebSocket(url, options?.protocols);\n\n socket.onopen = () => {\n setStatus('open');\n reconnectCount = 0; // Reset on successful connection\n };\n\n const parseMessage = options?.parse ?? ((raw: string): TReceive => {\n try { return JSON.parse(raw) as TReceive; }\n catch { return raw as TReceive; }\n });\n\n socket.onmessage = (event: MessageEvent) => {\n const parsed = parseMessage(event.data as string);\n setData(parsed);\n for (const handler of handlers) {\n handler(parsed);\n }\n };\n\n socket.onerror = () => {\n setStatus('error');\n };\n\n socket.onclose = () => {\n if (permanentlyClosed) {\n setStatus('closed');\n return;\n }\n\n setStatus('closed');\n\n if (shouldReconnect && reconnectCount < maxReconnects) {\n // Exponential backoff: baseInterval * 2^reconnectCount\n const delay = baseInterval * Math.pow(2, reconnectCount);\n reconnectCount++;\n reconnectTimer = setTimeout(connect, delay);\n }\n };\n }\n\n // Initiate the first connection\n connect();\n\n return {\n data,\n status,\n\n send(value: TSend): void {\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(value));\n }\n },\n\n close(): void {\n permanentlyClosed = true;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (socket) {\n socket.close();\n socket = null;\n }\n setStatus('closed');\n },\n\n on(handler: (data: TReceive) => void): () => void {\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n };\n },\n };\n}\n"]}
{"version":3,"sources":["../src/http/fetch.ts","../src/http/sse.ts","../src/http/ws.ts"],"names":[],"mappings":"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","file":"http.js","sourcesContent":["/**\n * Forma HTTP - Fetch\n *\n * Typed fetch wrapper with reactive signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal, internalEffect } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface FetchOptions<T> extends Omit<RequestInit, 'signal'> {\n base?: string;\n params?: Record<string, string>;\n timeout?: number; // ms, default 30000\n transform?: (data: unknown) => T;\n}\n\nexport interface FetchResult<T> {\n data: () => T | null;\n error: () => Error | null;\n loading: () => boolean;\n refetch: () => Promise<void>;\n abort: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createFetch — reactive fetch with signals\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive fetch that exposes data/error/loading as signals.\n *\n * If `url` is a signal getter (function), an effect auto-refetches when it\n * changes.\n *\n * ```ts\n * const { data, loading, error, refetch, abort } = createFetch<User[]>('/api/users');\n * ```\n */\nexport function createFetch<T>(\n url: string | (() => string),\n options?: FetchOptions<T>,\n): FetchResult<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Error | null>(null);\n const [loading, setLoading] = createSignal<boolean>(false);\n\n let currentController: AbortController | null = null;\n\n /** Resolve the URL string, applying base and params. */\n function resolveURL(): string {\n const raw = typeof url === 'function' ? url() : url;\n const base = options?.base || (typeof window !== 'undefined' ? window.location.origin : '');\n const fullURL = new URL(raw, base || undefined);\n\n if (options?.params) {\n for (const [key, value] of Object.entries(options.params)) {\n fullURL.searchParams.set(key, value);\n }\n }\n\n return fullURL.toString();\n }\n\n /** Execute a single fetch request. */\n async function execute(): Promise<void> {\n // Abort any in-flight request\n if (currentController) {\n currentController.abort();\n }\n\n currentController = new AbortController();\n const controller = currentController;\n const timeoutMs = options?.timeout ?? 30_000;\n\n // Set up timeout\n const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n setLoading(true);\n setError(null);\n\n try {\n const resolvedURL = resolveURL();\n\n const { base: _base, params: _params, timeout: _timeout, transform, ...fetchInit } =\n options ?? {};\n\n const response = await fetch(resolvedURL, {\n ...fetchInit,\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n\n const json: unknown = await response.json();\n const transformed = transform ? transform(json) : (json as T);\n setData(transformed);\n } catch (err) {\n if (err instanceof Error && err.name === 'AbortError') {\n // Ignore aborts — they are intentional or timeouts\n return;\n }\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n clearTimeout(timeoutId);\n // Only update loading if this controller is still current\n if (currentController === controller) {\n currentController = null;\n setLoading(false);\n }\n }\n }\n\n // If url is a reactive getter, set up an effect to auto-refetch.\n if (typeof url === 'function') {\n internalEffect(() => {\n // Read the signal so the effect re-runs when it changes\n url();\n void execute();\n });\n } else {\n // Kick off the first request immediately\n void execute();\n }\n\n return {\n data,\n error,\n loading,\n refetch: execute,\n abort() {\n if (currentController) {\n currentController.abort();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// fetchJSON — simple one-shot helper\n// ---------------------------------------------------------------------------\n\n/**\n * One-shot fetch that returns parsed JSON.\n *\n * ```ts\n * const users = await fetchJSON<User[]>('/api/users');\n * ```\n */\nexport async function fetchJSON<T>(url: string, options?: RequestInit): Promise<T> {\n const response = await fetch(url, options);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n return (await response.json()) as T;\n}\n","/**\n * Forma HTTP - Server-Sent Events\n *\n * Reactive SSE wrapper with signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SSEOptions<T = unknown> {\n withCredentials?: boolean;\n headers?: Record<string, string>; // Note: native EventSource does not support custom headers\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => T;\n}\n\nexport interface SSEConnection<T = unknown> {\n data: () => T | null;\n error: () => Event | null;\n connected: () => boolean;\n close: () => void;\n on(event: string, handler: (data: unknown) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createSSE\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive Server-Sent Events connection.\n *\n * ```ts\n * const sse = createSSE<{ message: string }>('/api/events');\n * createEffect(() => {\n * const msg = sse.data();\n * if (msg) console.log(msg.message);\n * });\n * ```\n */\nexport function createSSE<T = unknown>(\n url: string,\n options?: SSEOptions<T>,\n): SSEConnection<T> {\n const [data, setData] = createSignal<T | null>(null);\n const [error, setError] = createSignal<Event | null>(null);\n const [connected, setConnected] = createSignal<boolean>(false);\n\n const source = new EventSource(url, {\n withCredentials: options?.withCredentials ?? false,\n });\n\n source.onopen = () => {\n setConnected(true);\n setError(null);\n };\n\n const parseMessage = options?.parse ?? ((raw: string): T => {\n try { return JSON.parse(raw) as T; }\n catch { return raw as T; }\n });\n\n source.onmessage = (event: MessageEvent) => {\n setData(parseMessage(event.data as string));\n };\n\n source.onerror = (event: Event) => {\n setError(event);\n setConnected(false);\n };\n\n return {\n data,\n error,\n connected,\n\n close(): void {\n source.close();\n setConnected(false);\n },\n\n on(event: string, handler: (data: unknown) => void): () => void {\n const parseEvent = options?.parse ?? ((raw: string) => {\n try { return JSON.parse(raw); }\n catch { return raw; }\n });\n const listener = (e: MessageEvent) => {\n handler(parseEvent(e.data as string));\n };\n source.addEventListener(event, listener as EventListener);\n return () => {\n source.removeEventListener(event, listener as EventListener);\n };\n },\n };\n}\n","/**\n * Forma HTTP - WebSocket\n *\n * Reactive WebSocket wrapper with auto-reconnect and signal integration.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { createSignal } from 'forma/reactive';\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type WSStatus = 'connecting' | 'open' | 'closed' | 'error';\n\nexport interface WSOptions<TReceive = unknown> {\n protocols?: string | string[];\n reconnect?: boolean; // default true\n reconnectInterval?: number; // ms, default 1000\n maxReconnects?: number; // default 5\n /** Custom parser for incoming messages. Defaults to JSON.parse with raw data fallback. */\n parse?: (data: string) => TReceive;\n}\n\nexport interface WSConnection<TSend = unknown, TReceive = unknown> {\n data: () => TReceive | null;\n status: () => WSStatus;\n send(data: TSend): void;\n close(): void;\n on(handler: (data: TReceive) => void): () => void;\n}\n\n// ---------------------------------------------------------------------------\n// createWebSocket\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive WebSocket connection with auto-reconnect.\n *\n * ```ts\n * const ws = createWebSocket<string, ChatMessage>('wss://chat.example.com');\n * ws.send('hello');\n * createEffect(() => {\n * const msg = ws.data();\n * if (msg) console.log(msg);\n * });\n * ```\n */\nexport function createWebSocket<TSend = unknown, TReceive = unknown>(\n url: string,\n options?: WSOptions<TReceive>,\n): WSConnection<TSend, TReceive> {\n const shouldReconnect = options?.reconnect ?? true;\n const baseInterval = options?.reconnectInterval ?? 1000;\n const maxReconnects = options?.maxReconnects ?? 5;\n\n const [data, setData] = createSignal<TReceive | null>(null);\n const [status, setStatus] = createSignal<WSStatus>('connecting');\n\n const handlers = new Set<(data: TReceive) => void>();\n\n let socket: WebSocket | null = null;\n let reconnectCount = 0;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let permanentlyClosed = false;\n\n function connect(): void {\n if (permanentlyClosed) return;\n\n setStatus('connecting');\n socket = new WebSocket(url, options?.protocols);\n\n socket.onopen = () => {\n setStatus('open');\n reconnectCount = 0; // Reset on successful connection\n };\n\n const parseMessage = options?.parse ?? ((raw: string): TReceive => {\n try { return JSON.parse(raw) as TReceive; }\n catch { return raw as TReceive; }\n });\n\n socket.onmessage = (event: MessageEvent) => {\n const parsed = parseMessage(event.data as string);\n setData(parsed);\n for (const handler of handlers) {\n handler(parsed);\n }\n };\n\n socket.onerror = () => {\n setStatus('error');\n };\n\n socket.onclose = () => {\n if (permanentlyClosed) {\n setStatus('closed');\n return;\n }\n\n setStatus('closed');\n\n if (shouldReconnect && reconnectCount < maxReconnects) {\n // Exponential backoff: baseInterval * 2^reconnectCount\n const delay = baseInterval * Math.pow(2, reconnectCount);\n reconnectCount++;\n reconnectTimer = setTimeout(connect, delay);\n }\n };\n }\n\n // Initiate the first connection\n connect();\n\n return {\n data,\n status,\n\n send(value: TSend): void {\n if (socket && socket.readyState === WebSocket.OPEN) {\n socket.send(JSON.stringify(value));\n }\n },\n\n close(): void {\n permanentlyClosed = true;\n if (reconnectTimer !== null) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (socket) {\n socket.close();\n socket = null;\n }\n setStatus('closed');\n },\n\n on(handler: (data: TReceive) => void): () => void {\n handlers.add(handler);\n return () => {\n handlers.delete(handler);\n };\n },\n };\n}\n"]}
'use strict';
var chunkV732ZBCU_cjs = require('./chunk-V732ZBCU.cjs');
var chunkSZSKW57A_cjs = require('./chunk-SZSKW57A.cjs');
var chunkDCTOXHPF_cjs = require('./chunk-DCTOXHPF.cjs');

@@ -29,3 +29,3 @@ var chunk3U57L2TY_cjs = require('./chunk-3U57L2TY.cjs');

disposeRoot = dispose;
chunkV732ZBCU_cjs.hydrateIsland(component, target);
chunkSZSKW57A_cjs.hydrateIsland(component, target);
});

@@ -330,3 +330,3 @@ } else {

chunkDCTOXHPF_cjs.createRoot((dispose) => {
activeRoot = chunkV732ZBCU_cjs.hydrateIsland(() => hydrateFn(root, props), root);
activeRoot = chunkSZSKW57A_cjs.hydrateIsland(() => hydrateFn(root, props), root);
activeRoot.__formaDispose = dispose;

@@ -1021,31 +1021,31 @@ });

enumerable: true,
get: function () { return chunkV732ZBCU_cjs.Fragment; }
get: function () { return chunkSZSKW57A_cjs.Fragment; }
});
Object.defineProperty(exports, "cleanup", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.cleanup; }
get: function () { return chunkSZSKW57A_cjs.cleanup; }
});
Object.defineProperty(exports, "createList", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.createList; }
get: function () { return chunkSZSKW57A_cjs.createList; }
});
Object.defineProperty(exports, "createShow", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.createShow; }
get: function () { return chunkSZSKW57A_cjs.createShow; }
});
Object.defineProperty(exports, "fragment", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.fragment; }
get: function () { return chunkSZSKW57A_cjs.fragment; }
});
Object.defineProperty(exports, "h", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.h; }
get: function () { return chunkSZSKW57A_cjs.h; }
});
Object.defineProperty(exports, "hydrateIsland", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.hydrateIsland; }
get: function () { return chunkSZSKW57A_cjs.hydrateIsland; }
});
Object.defineProperty(exports, "reconcileList", {
enumerable: true,
get: function () { return chunkV732ZBCU_cjs.reconcileList; }
get: function () { return chunkSZSKW57A_cjs.reconcileList; }
});

@@ -1052,0 +1052,0 @@ Object.defineProperty(exports, "batch", {

@@ -1,3 +0,3 @@

import { hydrateIsland } from './chunk-VTPFK5TJ.js';
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-VTPFK5TJ.js';
import { hydrateIsland } from './chunk-FJGTMWKY.js';
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList } from './chunk-FJGTMWKY.js';
import { internalEffect, createRoot, untrack, createEffect, pushSuspenseContext, popSuspenseContext, __DEV__, reportError, batch } from './chunk-OUVOAYIO.js';

@@ -4,0 +4,0 @@ export { batch, createEffect, createMemo, createReducer, createRef, createResource, createRoot, getBatchDepth, isComputed, isEffect, isEffectScope, isSignal, on, onCleanup, onError, trigger, untrack } from './chunk-OUVOAYIO.js';

{
"name": "@getforma/core",
"author": "Forma <victor@getforma.dev>",
"version": "1.0.6",
"version": "1.0.7",
"description": "Real DOM reactive library — fine-grained signals, islands architecture, SSR hydration. No virtual DOM, no diffing. ~15KB gzipped.",

@@ -6,0 +6,0 @@ "type": "module",

'use strict';
var chunkDCTOXHPF_cjs = require('./chunk-DCTOXHPF.cjs');
var chunk3U57L2TY_cjs = require('./chunk-3U57L2TY.cjs');
// src/dom/element.ts
var Fragment = /* @__PURE__ */ Symbol.for("forma.fragment");
var SVG_NS = "http://www.w3.org/2000/svg";
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_TAGS = /* @__PURE__ */ new Set([
"svg",
"path",
"circle",
"rect",
"line",
"polyline",
"polygon",
"ellipse",
"g",
"text",
"tspan",
"textPath",
"defs",
"use",
"symbol",
"clipPath",
"mask",
"pattern",
"marker",
"linearGradient",
"radialGradient",
"stop",
"filter",
"feGaussianBlur",
"feColorMatrix",
"feOffset",
"feBlend",
"feMerge",
"feMergeNode",
"feComposite",
"feFlood",
"feMorphology",
"feTurbulence",
"feDisplacementMap",
"feImage",
"foreignObject",
"animate",
"animateTransform",
"animateMotion",
"set",
"image",
"switch",
"desc",
"title",
"metadata"
]);
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
"disabled",
"checked",
"readonly",
"required",
"autofocus",
"autoplay",
"controls",
"default",
"defer",
"formnovalidate",
"hidden",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"reversed",
"selected",
"async"
]);
var ELEMENT_PROTOS = null;
function getProto(tag) {
if (!ELEMENT_PROTOS) {
ELEMENT_PROTOS = /* @__PURE__ */ Object.create(null);
for (const t of [
"div",
"span",
"p",
"a",
"li",
"ul",
"ol",
"button",
"input",
"label",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"section",
"header",
"footer",
"main",
"nav",
"table",
"tr",
"td",
"th",
"tbody",
"img",
"form",
"select",
"option",
"textarea",
"i",
"b",
"strong",
"em",
"small",
"article",
"aside",
"details",
"summary"
]) {
ELEMENT_PROTOS[t] = document.createElement(t);
}
}
return ELEMENT_PROTOS[tag] ?? (ELEMENT_PROTOS[tag] = document.createElement(tag));
}
var EVENT_NAMES = /* @__PURE__ */ Object.create(null);
function eventName(key) {
return EVENT_NAMES[key] ?? (EVENT_NAMES[key] = key.slice(2).toLowerCase());
}
var ABORT_SYM = /* @__PURE__ */ Symbol.for("forma-abort");
function getAbortController(el) {
let controller = el[ABORT_SYM];
if (!controller) {
controller = new AbortController();
el[ABORT_SYM] = controller;
}
return controller;
}
function cleanup(el) {
const controller = el[ABORT_SYM];
if (controller) {
controller.abort();
delete el[ABORT_SYM];
}
}
var CACHE_SYM = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function getCache(el) {
return el[CACHE_SYM] ?? (el[CACHE_SYM] = /* @__PURE__ */ Object.create(null));
}
function handleClass(el, _key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache["class"] === v) return;
cache["class"] = v;
if (el instanceof HTMLElement) {
el.className = v;
} else {
el.setAttribute("class", v);
}
});
} else {
const cache = getCache(el);
if (cache["class"] === value) return;
cache["class"] = value;
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
}
}
function handleStyle(el, _key, value) {
if (typeof value === "function") {
let prevKeys = [];
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (typeof v === "string") {
const cache = getCache(el);
if (cache["style"] === v) return;
cache["style"] = v;
prevKeys = [];
el.style.cssText = v;
} else if (v && typeof v === "object") {
const style = el.style;
const nextKeys = Object.keys(v);
for (const k of prevKeys) {
if (!(k in v)) {
style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
}
}
Object.assign(style, v);
prevKeys = nextKeys;
}
});
} else if (typeof value === "string") {
const cache = getCache(el);
if (cache["style"] === value) return;
cache["style"] = value;
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
}
function handleEvent(el, key, value) {
const controller = getAbortController(el);
el.addEventListener(
eventName(key),
value,
{ signal: controller.signal }
);
}
function handleInnerHTML(el, _key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const resolved = value();
if (resolved == null) {
el.innerHTML = "";
return;
}
if (typeof resolved !== "object" || !("__html" in resolved)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof resolved
);
}
const html = resolved.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
const cache = getCache(el);
if (cache["innerHTML"] === html) return;
cache["innerHTML"] = html;
el.innerHTML = html;
});
} else {
if (value == null) {
el.innerHTML = "";
return;
}
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
}
}
function handleXLink(el, key, value) {
const localName = key.slice(6);
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (v == null || v === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(v));
}
});
} else {
if (value == null || value === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(value));
}
}
}
function handleBooleanAttr(el, key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache[key] === v) return;
cache[key] = v;
if (v) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
});
} else {
const cache = getCache(el);
if (cache[key] === value) return;
cache[key] = value;
if (value) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
}
}
function handleGenericAttr(el, key, value) {
if (typeof value === "function") {
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = value();
if (v == null || v === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(v);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
});
} else {
if (value == null || value === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(value);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
}
}
var PROP_HANDLERS = /* @__PURE__ */ new Map();
PROP_HANDLERS.set("class", handleClass);
PROP_HANDLERS.set("className", handleClass);
PROP_HANDLERS.set("style", handleStyle);
PROP_HANDLERS.set("ref", () => {
});
PROP_HANDLERS.set("dangerouslySetInnerHTML", handleInnerHTML);
for (const attr of BOOLEAN_ATTRS) {
PROP_HANDLERS.set(attr, handleBooleanAttr);
}
function applyProp(el, key, value) {
if (key === "class") {
handleClass(el, key, value);
return;
}
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
return;
}
const handler = PROP_HANDLERS.get(key);
if (handler) {
handler(el, key, value);
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
handleXLink(el, key, value);
return;
}
handleGenericAttr(el, key, value);
}
function applyStaticProp(el, key, value) {
if (value == null || value === false) return;
if (key === "class" || key === "className") {
el.className = value;
return;
}
if (key === "style") {
if (typeof value === "string") {
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
return;
}
if (key === "dangerouslySetInnerHTML") {
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
el.setAttributeNS(XLINK_NS, key, String(value));
return;
}
if (BOOLEAN_ATTRS.has(key)) {
if (value) el.setAttribute(key, "");
return;
}
if (value === true) {
el.setAttribute(key, "");
} else {
el.setAttribute(key, String(value));
}
}
function appendChild(parent, child) {
if (child instanceof Node) {
parent.appendChild(child);
return;
}
if (typeof child === "string") {
parent.appendChild(new Text(child));
return;
}
if (child == null || child === false || child === true) {
return;
}
if (typeof child === "number") {
parent.appendChild(new Text(String(child)));
return;
}
if (typeof child === "function") {
if (parent instanceof Element) {
parent[DYNAMIC_CHILD_SYM] = true;
}
let currentNode = null;
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = child();
if (v instanceof Node) {
if (currentNode) {
parent.replaceChild(v, currentNode);
} else {
parent.appendChild(v);
}
currentNode = v;
} else {
const text = typeof v === "symbol" ? String(v) : String(v ?? "");
if (!currentNode) {
currentNode = new Text(text);
parent.appendChild(currentNode);
} else if (currentNode.nodeType === 3) {
currentNode.data = text;
} else {
const tn = new Text(text);
parent.replaceChild(tn, currentNode);
currentNode = tn;
}
}
});
return;
}
if (Array.isArray(child)) {
for (const item of child) {
appendChild(parent, item);
}
return;
}
}
function h(tag, props, ...children) {
if (typeof tag === "function" && tag !== Fragment) {
const mergedProps = { ...props ?? {}, children };
return tag(mergedProps);
}
if (tag === Fragment) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
const tagName = tag;
if (hydrating) {
return { type: "element", tag: tagName, props: props ?? null, children };
}
let el;
if (ELEMENT_PROTOS && ELEMENT_PROTOS[tagName]) {
el = ELEMENT_PROTOS[tagName].cloneNode(false);
} else if (SVG_TAGS.has(tagName)) {
el = document.createElementNS(SVG_NS, tagName);
} else {
el = getProto(tagName).cloneNode(false);
}
if (props) {
let hasDynamic = false;
for (const key in props) {
if (key === "ref") continue;
const value = props[key];
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
continue;
}
if (typeof value === "function") {
if (!hasDynamic) {
el[CACHE_SYM] = /* @__PURE__ */ Object.create(null);
hasDynamic = true;
}
applyProp(el, key, value);
continue;
}
applyStaticProp(el, key, value);
}
}
const childLen = children.length;
if (childLen === 1) {
const only = children[0];
if (typeof only === "string") {
el.textContent = only;
} else if (typeof only === "number") {
el.textContent = String(only);
} else {
appendChild(el, only);
}
} else if (childLen > 1) {
for (const child of children) {
appendChild(el, child);
}
}
if (props && typeof props["ref"] === "function") {
props["ref"](el);
}
return el;
}
function fragment(...children) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
// src/dom/show.ts
function createShow(when, thenFn, elseFn) {
if (hydrating) {
const branch = when() ? thenFn() : elseFn?.() ?? null;
return {
type: "show",
condition: when,
whenTrue: thenFn,
whenFalse: elseFn,
initialBranch: branch
};
}
const startMarker = document.createComment("forma-show");
const endMarker = document.createComment("/forma-show");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let currentNode = null;
let lastTruthy = null;
let currentDispose = null;
const showDispose = chunkDCTOXHPF_cjs.internalEffect(() => {
const truthy = !!when();
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
if (truthy === lastTruthy) {
if (DEBUG) console.log("[forma:show] skip (same)", truthy, DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show]", lastTruthy, "\u2192", truthy, DEBUG_LABEL);
lastTruthy = truthy;
const parent = startMarker.parentNode;
if (!parent) {
if (DEBUG) console.warn("[forma:show] parentNode is null! skipping.", DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show] parent:", parent.nodeName, "inDoc:", document.contains(parent));
if (currentDispose) {
currentDispose();
currentDispose = null;
}
if (currentNode) {
if (currentNode.parentNode === parent) {
parent.removeChild(currentNode);
} else {
while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
parent.removeChild(startMarker.nextSibling);
}
}
}
const branchFn = truthy ? thenFn : elseFn;
if (branchFn) {
let branchDispose;
currentNode = chunkDCTOXHPF_cjs.createRoot((dispose) => {
branchDispose = dispose;
return chunkDCTOXHPF_cjs.untrack(() => branchFn());
});
currentDispose = branchDispose;
} else {
currentNode = null;
}
if (currentNode) {
parent.insertBefore(currentNode, endMarker);
}
});
fragment2.__showDispose = () => {
showDispose();
if (currentDispose) {
currentDispose();
currentDispose = null;
}
};
return fragment2;
}
// src/dom/hydrate.ts
var ABORT_SYM2 = /* @__PURE__ */ Symbol.for("forma-abort");
var hydrating = false;
function setHydrating(value) {
hydrating = value;
}
function isDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "element";
}
function isShowDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "show";
}
function isListDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "list";
}
function applyDynamicProps(el, props) {
if (!props) return;
for (const key in props) {
const value = props[key];
if (typeof value !== "function") continue;
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
let ac = el[ABORT_SYM2];
if (!ac) {
ac = new AbortController();
el[ABORT_SYM2] = ac;
}
el.addEventListener(key.slice(2).toLowerCase(), value, { signal: ac.signal });
continue;
}
const fn = value;
const attrKey = key;
chunkDCTOXHPF_cjs.internalEffect(() => {
const v = fn();
if (v === false || v == null) {
el.removeAttribute(attrKey);
} else if (v === true) {
el.setAttribute(attrKey, "");
} else {
el.setAttribute(attrKey, String(v));
}
});
}
}
function ensureNode(value) {
if (value instanceof Node) return value;
if (value == null || value === false || value === true) return null;
if (typeof value === "string") return new Text(value);
if (typeof value === "number") return new Text(String(value));
if (isDescriptor(value)) return descriptorToElement(value);
if (isShowDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createShow(
value.condition,
() => ensureNode(value.whenTrue()) ?? document.createComment("empty"),
value.whenFalse ? () => ensureNode(value.whenFalse()) ?? document.createComment("empty") : void 0
);
} finally {
hydrating = prevH;
}
}
if (isListDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createList(value.items, value.keyFn, value.renderFn, value.options);
} finally {
hydrating = prevH;
}
}
return null;
}
function descriptorToElement(desc) {
const prevHydrating = hydrating;
hydrating = false;
try {
const children = desc.children.map((child) => {
if (isDescriptor(child)) return descriptorToElement(child);
if (isShowDescriptor(child)) return ensureNode(child);
if (isListDescriptor(child)) return ensureNode(child);
return child;
});
return h(desc.tag, desc.props, ...children);
} finally {
hydrating = prevHydrating;
}
}
function isIslandStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 105;
}
function isShowStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 115;
}
function isTextStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 116;
}
function isListStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 108;
}
function findClosingMarker(start) {
const closing = "/" + start.data;
let node = start.nextSibling;
while (node) {
if (node.nodeType === 8 && node.data === closing) {
return node;
}
node = node.nextSibling;
}
return null;
}
function findTextBetween(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 3) return node;
node = node.nextSibling;
}
return null;
}
function nextElementBetweenMarkers(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) return node;
node = node.nextSibling;
}
return void 0;
}
function extractContentBetweenMarkers(start, end) {
const frag = document.createDocumentFragment();
let node = start.nextSibling;
while (node && node !== end) {
const next = node.nextSibling;
frag.appendChild(node);
node = next;
}
return frag;
}
function setupShowEffect(desc, marker) {
let currentCondition = !!desc.condition();
let thenFragment = null;
let elseFragment = null;
const hasSSRContent = marker.start.nextSibling !== marker.end;
if (!hasSSRContent && currentCondition) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
const trueBranch = desc.whenTrue();
if (trueBranch instanceof Node) {
marker.start.parentNode.insertBefore(trueBranch, marker.end);
}
}
chunkDCTOXHPF_cjs.internalEffect(() => {
const next = !!desc.condition();
if (next === currentCondition) return;
currentCondition = next;
const parent = marker.start.parentNode;
if (!parent) return;
const current = extractContentBetweenMarkers(marker.start, marker.end);
if (!next) {
thenFragment = current;
} else {
elseFragment = current;
}
let branch = next ? thenFragment ?? desc.whenTrue() : desc.whenFalse ? elseFragment ?? desc.whenFalse() : null;
if (next && thenFragment) thenFragment = null;
if (!next && elseFragment) elseFragment = null;
if (branch != null && !(branch instanceof Node)) {
branch = ensureNode(branch);
}
if (branch instanceof Node) {
parent.insertBefore(branch, marker.end);
}
});
}
function adoptBranchContent(desc, regionStart, regionEnd) {
if (isDescriptor(desc)) {
const el = nextElementBetweenMarkers(regionStart, regionEnd);
if (el) adoptNode(desc, el);
} else if (isShowDescriptor(desc)) {
let node = regionStart.nextSibling;
while (node && node !== regionEnd) {
if (node.nodeType === 8 && isShowStart(node.data)) {
const innerStart = node;
const innerEnd = findClosingMarker(innerStart);
if (innerEnd) {
if (desc.initialBranch) {
adoptBranchContent(desc.initialBranch, innerStart, innerEnd);
}
setupShowEffect(desc, { start: innerStart, end: innerEnd});
}
break;
}
node = node.nextSibling;
}
}
}
function adoptNode(desc, ssrEl) {
if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
const fresh = descriptorToElement(desc);
if (ssrEl) ssrEl.replaceWith(fresh);
return;
}
applyDynamicProps(ssrEl, desc.props);
let cursor = ssrEl.firstChild;
for (const child of desc.children) {
if (child === false || child == null) continue;
if (isDescriptor(child)) {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
while (cursor && cursor.nodeType === 1 && cursor.hasAttribute("data-forma-island")) {
cursor = cursor.nextSibling;
}
if (!cursor) {
ssrEl.appendChild(descriptorToElement(child));
continue;
}
if (cursor.nodeType === 1) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(child, el);
} else if (cursor.nodeType === 8 && isIslandStart(cursor.data)) {
const end = findClosingMarker(cursor);
const fresh = descriptorToElement(child);
if (end) {
end.parentNode.insertBefore(fresh, end);
cursor = end.nextSibling;
} else {
ssrEl.appendChild(fresh);
cursor = null;
}
} else {
ssrEl.appendChild(descriptorToElement(child));
}
} else if (isShowDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isShowStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
if (child.initialBranch) {
adoptBranchContent(child.initialBranch, start, end);
}
setupShowEffect(child, { start, end});
cursor = end.nextSibling;
}
}
} else if (isListDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isListStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
const ssrKeyMap = /* @__PURE__ */ new Map();
const ssrElements = [];
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) {
const el = node;
ssrElements.push(el);
const key = el.getAttribute("data-forma-key");
if (key != null) {
ssrKeyMap.set(key, el);
}
}
node = node.nextSibling;
}
const currentItems = chunkDCTOXHPF_cjs.untrack(() => child.items());
const listKeyFn = child.keyFn;
const listRenderFn = child.renderFn;
const useIndexFallback = ssrKeyMap.size === 0 && ssrElements.length > 0;
const adoptedNodes = [];
const adoptedItems = [];
const usedIndices = /* @__PURE__ */ new Set();
for (let i = 0; i < currentItems.length; i++) {
const item = currentItems[i];
const key = listKeyFn(item);
let ssrNode;
if (useIndexFallback) {
if (i < ssrElements.length) {
ssrNode = ssrElements[i];
usedIndices.add(i);
}
} else {
ssrNode = ssrKeyMap.get(String(key));
if (ssrNode) ssrKeyMap.delete(String(key));
}
if (ssrNode) {
adoptedNodes.push(ssrNode);
adoptedItems.push(item);
} else {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
const prevHydrating = hydrating;
hydrating = false;
try {
const [getIndex] = chunk3U57L2TY_cjs.createSignal(i);
const fresh = listRenderFn(item, getIndex);
end.parentNode.insertBefore(fresh, end);
adoptedNodes.push(fresh);
adoptedItems.push(item);
} finally {
hydrating = prevHydrating;
}
}
}
if (useIndexFallback) {
for (let i = 0; i < ssrElements.length; i++) {
if (!usedIndices.has(i) && ssrElements[i].parentNode) {
ssrElements[i].parentNode.removeChild(ssrElements[i]);
}
}
} else {
for (const [unusedKey, unusedNode] of ssrKeyMap) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
if (unusedNode.parentNode) {
unusedNode.parentNode.removeChild(unusedNode);
}
}
}
const parent = start.parentNode;
for (const adoptedNode of adoptedNodes) {
parent.insertBefore(adoptedNode, end);
}
let cache = /* @__PURE__ */ new Map();
for (let i = 0; i < adoptedItems.length; i++) {
const item = adoptedItems[i];
const key = listKeyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(i);
cache.set(key, {
element: adoptedNodes[i],
item,
getIndex,
setIndex
});
}
let reconcileNodes = adoptedNodes.slice();
let reconcileItems = adoptedItems.slice();
chunkDCTOXHPF_cjs.internalEffect(() => {
const newItems = child.items();
const parent2 = start.parentNode;
if (!parent2) return;
const result = reconcileList(
parent2,
reconcileItems,
newItems,
reconcileNodes,
listKeyFn,
(item) => {
const prevHydrating = hydrating;
hydrating = false;
try {
const key = listKeyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(0);
const element = chunkDCTOXHPF_cjs.untrack(() => listRenderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
} finally {
hydrating = prevHydrating;
}
},
(_node, item) => {
const key = listKeyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
},
end
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < newItems.length; i++) {
const key = listKeyFn(newItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
reconcileNodes = result.nodes;
reconcileItems = result.items;
});
cursor = end.nextSibling;
}
}
} else if (typeof child === "function") {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
if (cursor && cursor.nodeType === 1) {
const initial = child();
if (isDescriptor(initial)) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(initial, el);
continue;
}
}
if (cursor && cursor.nodeType === 8) {
const data = cursor.data;
if (isTextStart(data)) {
const endMarker = findClosingMarker(cursor);
let textNode = cursor.nextSibling;
if (!textNode || textNode.nodeType !== 3) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
const created = document.createTextNode("");
cursor.parentNode.insertBefore(created, endMarker || cursor.nextSibling);
textNode = created;
}
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
cursor = endMarker ? endMarker.nextSibling : textNode.nextSibling;
} else if (isShowStart(data)) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
let textNode = findTextBetween(start, end);
if (!textNode) {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
textNode = document.createTextNode("");
start.parentNode.insertBefore(textNode, end);
}
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
cursor = end.nextSibling;
} else {
cursor = cursor.nextSibling;
}
} else {
cursor = cursor.nextSibling;
}
} else if (cursor && cursor.nodeType === 3) {
const textNode = cursor;
cursor = cursor.nextSibling;
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
} else {
if (chunkDCTOXHPF_cjs.__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
const textNode = document.createTextNode("");
ssrEl.appendChild(textNode);
chunkDCTOXHPF_cjs.internalEffect(() => {
textNode.data = String(child());
});
}
} else if (typeof child === "string" || typeof child === "number") {
if (cursor && cursor.nodeType === 3) {
cursor = cursor.nextSibling;
}
}
}
}
function hydrateIsland(component, target) {
const hasSSRContent = target.childElementCount > 0 || target.childNodes.length > 0 && Array.from(target.childNodes).some((n) => n.nodeType === 1 || n.nodeType === 3 && n.data.trim());
if (!hasSSRContent) {
if (chunkDCTOXHPF_cjs.__DEV__) {
const name = target.getAttribute("data-forma-component") || "unknown";
console.warn(
`[forma] Island "${name}" has no SSR content \u2014 falling back to CSR. This means the IR walker did not render content between ISLAND_START and ISLAND_END.`
);
}
const result = component();
if (result instanceof Element) {
for (const attr of Array.from(target.attributes)) {
if (attr.name.startsWith("data-forma-")) {
result.setAttribute(attr.name, attr.value);
}
}
target.replaceWith(result);
return result;
} else if (result instanceof Node) {
target.appendChild(result);
}
return target;
}
setHydrating(true);
let descriptor;
try {
descriptor = component();
} finally {
setHydrating(false);
}
if (!descriptor || !isDescriptor(descriptor)) {
target.removeAttribute("data-forma-ssr");
return target;
}
if (target.hasAttribute("data-forma-island")) {
adoptNode(descriptor, target);
} else {
adoptNode(descriptor, target.children[0]);
}
target.removeAttribute("data-forma-ssr");
return target;
}
// src/dom/list.ts
function longestIncreasingSubsequence(arr) {
const n = arr.length;
if (n === 0) return [];
const tails = new Int32Array(n);
const tailIndices = new Int32Array(n);
const predecessor = new Int32Array(n).fill(-1);
let tailsLen = 0;
for (let i = 0; i < n; i++) {
const val = arr[i];
let lo = 0, hi = tailsLen;
while (lo < hi) {
const mid = lo + hi >> 1;
if (tails[mid] < val) lo = mid + 1;
else hi = mid;
}
tails[lo] = val;
tailIndices[lo] = i;
if (lo > 0) predecessor[i] = tailIndices[lo - 1];
if (lo >= tailsLen) tailsLen++;
}
const result = new Array(tailsLen);
let idx = tailIndices[tailsLen - 1];
for (let i = tailsLen - 1; i >= 0; i--) {
result[i] = idx;
idx = predecessor[idx];
}
return result;
}
var SMALL_LIST_THRESHOLD = 32;
var ABORT_SYM3 = /* @__PURE__ */ Symbol.for("forma-abort");
var CACHE_SYM2 = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM2 = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function canPatchStaticElement(target, source) {
return target instanceof HTMLElement && source instanceof HTMLElement && target.tagName === source.tagName && !target[ABORT_SYM3] && !target[CACHE_SYM2] && !target[DYNAMIC_CHILD_SYM2] && !source[ABORT_SYM3] && !source[CACHE_SYM2] && !source[DYNAMIC_CHILD_SYM2];
}
function patchStaticElement(target, source) {
const sourceAttrNames = /* @__PURE__ */ new Set();
for (const attr of Array.from(source.attributes)) {
sourceAttrNames.add(attr.name);
if (target.getAttribute(attr.name) !== attr.value) {
target.setAttribute(attr.name, attr.value);
}
}
for (const attr of Array.from(target.attributes)) {
if (!sourceAttrNames.has(attr.name)) {
target.removeAttribute(attr.name);
}
}
target.replaceChildren(...Array.from(source.childNodes));
}
function reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
const oldKeys = new Array(oldLen);
for (let i = 0; i < oldLen; i++) {
oldKeys[i] = keyFn(oldItems[i]);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
let found = -1;
for (let j = 0; j < oldLen; j++) {
if (!oldUsed[j] && oldKeys[j] === key) {
found = j;
oldUsed[j] = 1;
break;
}
}
oldIndices[i] = found;
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function reconcileList(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
if (newLen === 0) {
for (let i = 0; i < oldLen; i++) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
return { nodes: [], items: [] };
}
if (oldLen === 0) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = createFn(newItems[i]);
if (beforeNode) {
parent.insertBefore(node, beforeNode);
} else {
parent.appendChild(node);
}
hooks?.onInsert?.(node);
nodes[i] = node;
}
return { nodes, items: newItems };
}
if (oldLen < SMALL_LIST_THRESHOLD) {
return reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks);
}
const oldKeyMap = /* @__PURE__ */ new Map();
for (let i = 0; i < oldLen; i++) {
oldKeyMap.set(keyFn(oldItems[i]), i);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
const oldIdx = oldKeyMap.get(key);
if (oldIdx !== void 0) {
oldIndices[i] = oldIdx;
oldUsed[oldIdx] = 1;
} else {
oldIndices[i] = -1;
}
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function createList(items, keyFn, renderFn, options) {
if (hydrating) {
return { type: "list", items, keyFn, renderFn, options };
}
const startMarker = document.createComment("forma-list-start");
const endMarker = document.createComment("forma-list-end");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let cache = /* @__PURE__ */ new Map();
let currentNodes = [];
let currentItems = [];
const updateOnItemChange = options?.updateOnItemChange ?? "none";
chunkDCTOXHPF_cjs.internalEffect(() => {
const newItems = items();
const parent = startMarker.parentNode;
if (!parent) {
return;
}
if (!Array.isArray(newItems)) {
if (chunkDCTOXHPF_cjs.__DEV__) {
console.warn("[forma] createList: value is not an array, treating as empty");
}
for (const node of currentNodes) {
if (node.parentNode === parent) parent.removeChild(node);
}
cache = /* @__PURE__ */ new Map();
currentNodes = [];
currentItems = [];
return;
}
let cleanItems = newItems;
for (let i = 0; i < newItems.length; i++) {
if (newItems[i] == null) {
cleanItems = newItems.filter((item) => item != null);
break;
}
}
if (chunkDCTOXHPF_cjs.__DEV__) {
const seen = /* @__PURE__ */ new Set();
for (const item of cleanItems) {
const key = keyFn(item);
if (seen.has(key)) {
console.warn("[forma] createList: duplicate key detected:", key);
}
seen.add(key);
}
}
const updateRow = updateOnItemChange === "rerender" ? (node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (!cached) return;
if (cached.item === item) return;
cached.item = item;
if (!(node instanceof HTMLElement)) return;
if (node[ABORT_SYM3] || node[CACHE_SYM2] || node[DYNAMIC_CHILD_SYM2]) {
return;
}
const next = chunkDCTOXHPF_cjs.untrack(() => renderFn(item, cached.getIndex));
if (canPatchStaticElement(node, next)) {
patchStaticElement(node, next);
cached.element = node;
}
} : (_node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
};
const result = reconcileList(
parent,
currentItems,
cleanItems,
currentNodes,
keyFn,
// createFn: create element + cache entry
(item) => {
const key = keyFn(item);
const [getIndex, setIndex] = chunk3U57L2TY_cjs.createSignal(0);
const element = chunkDCTOXHPF_cjs.untrack(() => renderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
},
updateRow,
// beforeNode: insert items before the end marker
endMarker
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < cleanItems.length; i++) {
const key = keyFn(cleanItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
currentNodes = result.nodes;
currentItems = result.items;
});
return fragment2;
}
exports.Fragment = Fragment;
exports.cleanup = cleanup;
exports.createList = createList;
exports.createShow = createShow;
exports.fragment = fragment;
exports.h = h;
exports.hydrateIsland = hydrateIsland;
exports.reconcileList = reconcileList;
//# sourceMappingURL=chunk-V732ZBCU.cjs.map
//# sourceMappingURL=chunk-V732ZBCU.cjs.map

Sorry, the diff of this file is too big to display

import { internalEffect, createRoot, untrack, __DEV__ } from './chunk-OUVOAYIO.js';
import { createSignal } from './chunk-OZCHIVAZ.js';
// src/dom/element.ts
var Fragment = /* @__PURE__ */ Symbol.for("forma.fragment");
var SVG_NS = "http://www.w3.org/2000/svg";
var XLINK_NS = "http://www.w3.org/1999/xlink";
var SVG_TAGS = /* @__PURE__ */ new Set([
"svg",
"path",
"circle",
"rect",
"line",
"polyline",
"polygon",
"ellipse",
"g",
"text",
"tspan",
"textPath",
"defs",
"use",
"symbol",
"clipPath",
"mask",
"pattern",
"marker",
"linearGradient",
"radialGradient",
"stop",
"filter",
"feGaussianBlur",
"feColorMatrix",
"feOffset",
"feBlend",
"feMerge",
"feMergeNode",
"feComposite",
"feFlood",
"feMorphology",
"feTurbulence",
"feDisplacementMap",
"feImage",
"foreignObject",
"animate",
"animateTransform",
"animateMotion",
"set",
"image",
"switch",
"desc",
"title",
"metadata"
]);
var BOOLEAN_ATTRS = /* @__PURE__ */ new Set([
"disabled",
"checked",
"readonly",
"required",
"autofocus",
"autoplay",
"controls",
"default",
"defer",
"formnovalidate",
"hidden",
"ismap",
"loop",
"multiple",
"muted",
"nomodule",
"novalidate",
"open",
"playsinline",
"reversed",
"selected",
"async"
]);
var ELEMENT_PROTOS = null;
function getProto(tag) {
if (!ELEMENT_PROTOS) {
ELEMENT_PROTOS = /* @__PURE__ */ Object.create(null);
for (const t of [
"div",
"span",
"p",
"a",
"li",
"ul",
"ol",
"button",
"input",
"label",
"h1",
"h2",
"h3",
"h4",
"h5",
"h6",
"section",
"header",
"footer",
"main",
"nav",
"table",
"tr",
"td",
"th",
"tbody",
"img",
"form",
"select",
"option",
"textarea",
"i",
"b",
"strong",
"em",
"small",
"article",
"aside",
"details",
"summary"
]) {
ELEMENT_PROTOS[t] = document.createElement(t);
}
}
return ELEMENT_PROTOS[tag] ?? (ELEMENT_PROTOS[tag] = document.createElement(tag));
}
var EVENT_NAMES = /* @__PURE__ */ Object.create(null);
function eventName(key) {
return EVENT_NAMES[key] ?? (EVENT_NAMES[key] = key.slice(2).toLowerCase());
}
var ABORT_SYM = /* @__PURE__ */ Symbol.for("forma-abort");
function getAbortController(el) {
let controller = el[ABORT_SYM];
if (!controller) {
controller = new AbortController();
el[ABORT_SYM] = controller;
}
return controller;
}
function cleanup(el) {
const controller = el[ABORT_SYM];
if (controller) {
controller.abort();
delete el[ABORT_SYM];
}
}
var CACHE_SYM = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function getCache(el) {
return el[CACHE_SYM] ?? (el[CACHE_SYM] = /* @__PURE__ */ Object.create(null));
}
function handleClass(el, _key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache["class"] === v) return;
cache["class"] = v;
if (el instanceof HTMLElement) {
el.className = v;
} else {
el.setAttribute("class", v);
}
});
} else {
const cache = getCache(el);
if (cache["class"] === value) return;
cache["class"] = value;
if (el instanceof HTMLElement) {
el.className = value;
} else {
el.setAttribute("class", value);
}
}
}
function handleStyle(el, _key, value) {
if (typeof value === "function") {
let prevKeys = [];
internalEffect(() => {
const v = value();
if (typeof v === "string") {
const cache = getCache(el);
if (cache["style"] === v) return;
cache["style"] = v;
prevKeys = [];
el.style.cssText = v;
} else if (v && typeof v === "object") {
const style = el.style;
const nextKeys = Object.keys(v);
for (const k of prevKeys) {
if (!(k in v)) {
style.removeProperty(k.replace(/[A-Z]/g, (c) => "-" + c.toLowerCase()));
}
}
Object.assign(style, v);
prevKeys = nextKeys;
}
});
} else if (typeof value === "string") {
const cache = getCache(el);
if (cache["style"] === value) return;
cache["style"] = value;
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
}
function handleEvent(el, key, value) {
const controller = getAbortController(el);
el.addEventListener(
eventName(key),
value,
{ signal: controller.signal }
);
}
function handleInnerHTML(el, _key, value) {
if (typeof value === "function") {
internalEffect(() => {
const resolved = value();
if (resolved == null) {
el.innerHTML = "";
return;
}
if (typeof resolved !== "object" || !("__html" in resolved)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof resolved
);
}
const html = resolved.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
const cache = getCache(el);
if (cache["innerHTML"] === html) return;
cache["innerHTML"] = html;
el.innerHTML = html;
});
} else {
if (value == null) {
el.innerHTML = "";
return;
}
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
}
}
function handleXLink(el, key, value) {
const localName = key.slice(6);
if (typeof value === "function") {
internalEffect(() => {
const v = value();
if (v == null || v === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(v));
}
});
} else {
if (value == null || value === false) {
el.removeAttributeNS(XLINK_NS, localName);
} else {
el.setAttributeNS(XLINK_NS, key, String(value));
}
}
}
function handleBooleanAttr(el, key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
const cache = getCache(el);
if (cache[key] === v) return;
cache[key] = v;
if (v) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
});
} else {
const cache = getCache(el);
if (cache[key] === value) return;
cache[key] = value;
if (value) {
el.setAttribute(key, "");
} else {
el.removeAttribute(key);
}
}
}
function handleGenericAttr(el, key, value) {
if (typeof value === "function") {
internalEffect(() => {
const v = value();
if (v == null || v === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(v);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
});
} else {
if (value == null || value === false) {
const cache = getCache(el);
if (cache[key] === null) return;
cache[key] = null;
el.removeAttribute(key);
} else {
const strVal = String(value);
const cache = getCache(el);
if (cache[key] === strVal) return;
cache[key] = strVal;
el.setAttribute(key, strVal);
}
}
}
var PROP_HANDLERS = /* @__PURE__ */ new Map();
PROP_HANDLERS.set("class", handleClass);
PROP_HANDLERS.set("className", handleClass);
PROP_HANDLERS.set("style", handleStyle);
PROP_HANDLERS.set("ref", () => {
});
PROP_HANDLERS.set("dangerouslySetInnerHTML", handleInnerHTML);
for (const attr of BOOLEAN_ATTRS) {
PROP_HANDLERS.set(attr, handleBooleanAttr);
}
function applyProp(el, key, value) {
if (key === "class") {
handleClass(el, key, value);
return;
}
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
return;
}
const handler = PROP_HANDLERS.get(key);
if (handler) {
handler(el, key, value);
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
handleXLink(el, key, value);
return;
}
handleGenericAttr(el, key, value);
}
function applyStaticProp(el, key, value) {
if (value == null || value === false) return;
if (key === "class" || key === "className") {
el.className = value;
return;
}
if (key === "style") {
if (typeof value === "string") {
el.style.cssText = value;
} else if (value && typeof value === "object") {
Object.assign(el.style, value);
}
return;
}
if (key === "dangerouslySetInnerHTML") {
if (typeof value !== "object" || !("__html" in value)) {
throw new TypeError(
"dangerouslySetInnerHTML: expected { __html: string }, got " + typeof value
);
}
const html = value.__html;
if (typeof html !== "string") {
throw new TypeError(
"dangerouslySetInnerHTML: __html must be a string, got " + typeof html
);
}
el.innerHTML = html;
return;
}
if (key.charCodeAt(0) === 120 && key.startsWith("xlink:")) {
el.setAttributeNS(XLINK_NS, key, String(value));
return;
}
if (BOOLEAN_ATTRS.has(key)) {
if (value) el.setAttribute(key, "");
return;
}
if (value === true) {
el.setAttribute(key, "");
} else {
el.setAttribute(key, String(value));
}
}
function appendChild(parent, child) {
if (child instanceof Node) {
parent.appendChild(child);
return;
}
if (typeof child === "string") {
parent.appendChild(new Text(child));
return;
}
if (child == null || child === false || child === true) {
return;
}
if (typeof child === "number") {
parent.appendChild(new Text(String(child)));
return;
}
if (typeof child === "function") {
if (parent instanceof Element) {
parent[DYNAMIC_CHILD_SYM] = true;
}
let currentNode = null;
internalEffect(() => {
const v = child();
if (v instanceof Node) {
if (currentNode) {
parent.replaceChild(v, currentNode);
} else {
parent.appendChild(v);
}
currentNode = v;
} else {
const text = typeof v === "symbol" ? String(v) : String(v ?? "");
if (!currentNode) {
currentNode = new Text(text);
parent.appendChild(currentNode);
} else if (currentNode.nodeType === 3) {
currentNode.data = text;
} else {
const tn = new Text(text);
parent.replaceChild(tn, currentNode);
currentNode = tn;
}
}
});
return;
}
if (Array.isArray(child)) {
for (const item of child) {
appendChild(parent, item);
}
return;
}
}
function h(tag, props, ...children) {
if (typeof tag === "function" && tag !== Fragment) {
const mergedProps = { ...props ?? {}, children };
return tag(mergedProps);
}
if (tag === Fragment) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
const tagName = tag;
if (hydrating) {
return { type: "element", tag: tagName, props: props ?? null, children };
}
let el;
if (ELEMENT_PROTOS && ELEMENT_PROTOS[tagName]) {
el = ELEMENT_PROTOS[tagName].cloneNode(false);
} else if (SVG_TAGS.has(tagName)) {
el = document.createElementNS(SVG_NS, tagName);
} else {
el = getProto(tagName).cloneNode(false);
}
if (props) {
let hasDynamic = false;
for (const key in props) {
if (key === "ref") continue;
const value = props[key];
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
handleEvent(el, key, value);
continue;
}
if (typeof value === "function") {
if (!hasDynamic) {
el[CACHE_SYM] = /* @__PURE__ */ Object.create(null);
hasDynamic = true;
}
applyProp(el, key, value);
continue;
}
applyStaticProp(el, key, value);
}
}
const childLen = children.length;
if (childLen === 1) {
const only = children[0];
if (typeof only === "string") {
el.textContent = only;
} else if (typeof only === "number") {
el.textContent = String(only);
} else {
appendChild(el, only);
}
} else if (childLen > 1) {
for (const child of children) {
appendChild(el, child);
}
}
if (props && typeof props["ref"] === "function") {
props["ref"](el);
}
return el;
}
function fragment(...children) {
const frag = document.createDocumentFragment();
for (const child of children) {
appendChild(frag, child);
}
return frag;
}
// src/dom/show.ts
function createShow(when, thenFn, elseFn) {
if (hydrating) {
const branch = when() ? thenFn() : elseFn?.() ?? null;
return {
type: "show",
condition: when,
whenTrue: thenFn,
whenFalse: elseFn,
initialBranch: branch
};
}
const startMarker = document.createComment("forma-show");
const endMarker = document.createComment("/forma-show");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let currentNode = null;
let lastTruthy = null;
let currentDispose = null;
const showDispose = internalEffect(() => {
const truthy = !!when();
const DEBUG = typeof globalThis.__FORMA_DEBUG__ !== "undefined";
const DEBUG_LABEL = DEBUG ? thenFn.toString().slice(0, 60) : "";
if (truthy === lastTruthy) {
if (DEBUG) console.log("[forma:show] skip (same)", truthy, DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show]", lastTruthy, "\u2192", truthy, DEBUG_LABEL);
lastTruthy = truthy;
const parent = startMarker.parentNode;
if (!parent) {
if (DEBUG) console.warn("[forma:show] parentNode is null! skipping.", DEBUG_LABEL);
return;
}
if (DEBUG) console.log("[forma:show] parent:", parent.nodeName, "inDoc:", document.contains(parent));
if (currentDispose) {
currentDispose();
currentDispose = null;
}
if (currentNode) {
if (currentNode.parentNode === parent) {
parent.removeChild(currentNode);
} else {
while (startMarker.nextSibling && startMarker.nextSibling !== endMarker) {
parent.removeChild(startMarker.nextSibling);
}
}
}
const branchFn = truthy ? thenFn : elseFn;
if (branchFn) {
let branchDispose;
currentNode = createRoot((dispose) => {
branchDispose = dispose;
return untrack(() => branchFn());
});
currentDispose = branchDispose;
} else {
currentNode = null;
}
if (currentNode) {
parent.insertBefore(currentNode, endMarker);
}
});
fragment2.__showDispose = () => {
showDispose();
if (currentDispose) {
currentDispose();
currentDispose = null;
}
};
return fragment2;
}
// src/dom/hydrate.ts
var ABORT_SYM2 = /* @__PURE__ */ Symbol.for("forma-abort");
var hydrating = false;
function setHydrating(value) {
hydrating = value;
}
function isDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "element";
}
function isShowDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "show";
}
function isListDescriptor(v) {
return v != null && typeof v === "object" && "type" in v && v.type === "list";
}
function applyDynamicProps(el, props) {
if (!props) return;
for (const key in props) {
const value = props[key];
if (typeof value !== "function") continue;
if (key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && key.length > 2) {
let ac = el[ABORT_SYM2];
if (!ac) {
ac = new AbortController();
el[ABORT_SYM2] = ac;
}
el.addEventListener(key.slice(2).toLowerCase(), value, { signal: ac.signal });
continue;
}
const fn = value;
const attrKey = key;
internalEffect(() => {
const v = fn();
if (v === false || v == null) {
el.removeAttribute(attrKey);
} else if (v === true) {
el.setAttribute(attrKey, "");
} else {
el.setAttribute(attrKey, String(v));
}
});
}
}
function ensureNode(value) {
if (value instanceof Node) return value;
if (value == null || value === false || value === true) return null;
if (typeof value === "string") return new Text(value);
if (typeof value === "number") return new Text(String(value));
if (isDescriptor(value)) return descriptorToElement(value);
if (isShowDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createShow(
value.condition,
() => ensureNode(value.whenTrue()) ?? document.createComment("empty"),
value.whenFalse ? () => ensureNode(value.whenFalse()) ?? document.createComment("empty") : void 0
);
} finally {
hydrating = prevH;
}
}
if (isListDescriptor(value)) {
const prevH = hydrating;
hydrating = false;
try {
return createList(value.items, value.keyFn, value.renderFn, value.options);
} finally {
hydrating = prevH;
}
}
return null;
}
function descriptorToElement(desc) {
const prevHydrating = hydrating;
hydrating = false;
try {
const children = desc.children.map((child) => {
if (isDescriptor(child)) return descriptorToElement(child);
if (isShowDescriptor(child)) return ensureNode(child);
if (isListDescriptor(child)) return ensureNode(child);
return child;
});
return h(desc.tag, desc.props, ...children);
} finally {
hydrating = prevHydrating;
}
}
function isIslandStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 105;
}
function isShowStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 115;
}
function isTextStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 116;
}
function isListStart(data) {
return data.length >= 4 && data.charCodeAt(0) === 102 && data.charCodeAt(1) === 58 && data.charCodeAt(2) === 108;
}
function findClosingMarker(start) {
const closing = "/" + start.data;
let node = start.nextSibling;
while (node) {
if (node.nodeType === 8 && node.data === closing) {
return node;
}
node = node.nextSibling;
}
return null;
}
function findTextBetween(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 3) return node;
node = node.nextSibling;
}
return null;
}
function nextElementBetweenMarkers(start, end) {
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) return node;
node = node.nextSibling;
}
return void 0;
}
function extractContentBetweenMarkers(start, end) {
const frag = document.createDocumentFragment();
let node = start.nextSibling;
while (node && node !== end) {
const next = node.nextSibling;
frag.appendChild(node);
node = next;
}
return frag;
}
function setupShowEffect(desc, marker) {
let currentCondition = !!desc.condition();
let thenFragment = null;
let elseFragment = null;
const hasSSRContent = marker.start.nextSibling !== marker.end;
if (!hasSSRContent && currentCondition) {
if (__DEV__) console.warn("[forma] Hydration: show condition mismatch \u2014 SSR empty but client condition is true");
const trueBranch = desc.whenTrue();
if (trueBranch instanceof Node) {
marker.start.parentNode.insertBefore(trueBranch, marker.end);
}
}
internalEffect(() => {
const next = !!desc.condition();
if (next === currentCondition) return;
currentCondition = next;
const parent = marker.start.parentNode;
if (!parent) return;
const current = extractContentBetweenMarkers(marker.start, marker.end);
if (!next) {
thenFragment = current;
} else {
elseFragment = current;
}
let branch = next ? thenFragment ?? desc.whenTrue() : desc.whenFalse ? elseFragment ?? desc.whenFalse() : null;
if (next && thenFragment) thenFragment = null;
if (!next && elseFragment) elseFragment = null;
if (branch != null && !(branch instanceof Node)) {
branch = ensureNode(branch);
}
if (branch instanceof Node) {
parent.insertBefore(branch, marker.end);
}
});
}
function adoptBranchContent(desc, regionStart, regionEnd) {
if (isDescriptor(desc)) {
const el = nextElementBetweenMarkers(regionStart, regionEnd);
if (el) adoptNode(desc, el);
} else if (isShowDescriptor(desc)) {
let node = regionStart.nextSibling;
while (node && node !== regionEnd) {
if (node.nodeType === 8 && isShowStart(node.data)) {
const innerStart = node;
const innerEnd = findClosingMarker(innerStart);
if (innerEnd) {
if (desc.initialBranch) {
adoptBranchContent(desc.initialBranch, innerStart, innerEnd);
}
setupShowEffect(desc, { start: innerStart, end: innerEnd});
}
break;
}
node = node.nextSibling;
}
}
}
function adoptNode(desc, ssrEl) {
if (!ssrEl || ssrEl.tagName !== desc.tag.toUpperCase()) {
if (__DEV__) console.warn(`Hydration mismatch: expected <${desc.tag}>, got <${ssrEl?.tagName?.toLowerCase() ?? "nothing"}>`);
const fresh = descriptorToElement(desc);
if (ssrEl) ssrEl.replaceWith(fresh);
return;
}
applyDynamicProps(ssrEl, desc.props);
let cursor = ssrEl.firstChild;
for (const child of desc.children) {
if (child === false || child == null) continue;
if (isDescriptor(child)) {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
while (cursor && cursor.nodeType === 1 && cursor.hasAttribute("data-forma-island")) {
cursor = cursor.nextSibling;
}
if (!cursor) {
ssrEl.appendChild(descriptorToElement(child));
continue;
}
if (cursor.nodeType === 1) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(child, el);
} else if (cursor.nodeType === 8 && isIslandStart(cursor.data)) {
const end = findClosingMarker(cursor);
const fresh = descriptorToElement(child);
if (end) {
end.parentNode.insertBefore(fresh, end);
cursor = end.nextSibling;
} else {
ssrEl.appendChild(fresh);
cursor = null;
}
} else {
ssrEl.appendChild(descriptorToElement(child));
}
} else if (isShowDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isShowStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
if (child.initialBranch) {
adoptBranchContent(child.initialBranch, start, end);
}
setupShowEffect(child, { start, end});
cursor = end.nextSibling;
}
}
} else if (isListDescriptor(child)) {
while (cursor && !(cursor.nodeType === 8 && isListStart(cursor.data))) {
cursor = cursor.nextSibling;
}
if (cursor) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
const ssrKeyMap = /* @__PURE__ */ new Map();
const ssrElements = [];
let node = start.nextSibling;
while (node && node !== end) {
if (node.nodeType === 1) {
const el = node;
ssrElements.push(el);
const key = el.getAttribute("data-forma-key");
if (key != null) {
ssrKeyMap.set(key, el);
}
}
node = node.nextSibling;
}
const currentItems = untrack(() => child.items());
const listKeyFn = child.keyFn;
const listRenderFn = child.renderFn;
const useIndexFallback = ssrKeyMap.size === 0 && ssrElements.length > 0;
const adoptedNodes = [];
const adoptedItems = [];
const usedIndices = /* @__PURE__ */ new Set();
for (let i = 0; i < currentItems.length; i++) {
const item = currentItems[i];
const key = listKeyFn(item);
let ssrNode;
if (useIndexFallback) {
if (i < ssrElements.length) {
ssrNode = ssrElements[i];
usedIndices.add(i);
}
} else {
ssrNode = ssrKeyMap.get(String(key));
if (ssrNode) ssrKeyMap.delete(String(key));
}
if (ssrNode) {
adoptedNodes.push(ssrNode);
adoptedItems.push(item);
} else {
if (__DEV__) console.warn(`[FormaJS] Hydration: list item key "${key}" not found in SSR \u2014 rendering fresh`);
const prevHydrating = hydrating;
hydrating = false;
try {
const [getIndex] = createSignal(i);
const fresh = listRenderFn(item, getIndex);
end.parentNode.insertBefore(fresh, end);
adoptedNodes.push(fresh);
adoptedItems.push(item);
} finally {
hydrating = prevHydrating;
}
}
}
if (useIndexFallback) {
for (let i = 0; i < ssrElements.length; i++) {
if (!usedIndices.has(i) && ssrElements[i].parentNode) {
ssrElements[i].parentNode.removeChild(ssrElements[i]);
}
}
} else {
for (const [unusedKey, unusedNode] of ssrKeyMap) {
if (__DEV__) console.warn(`[FormaJS] Hydration: removing extra SSR list item with key "${unusedKey}"`);
if (unusedNode.parentNode) {
unusedNode.parentNode.removeChild(unusedNode);
}
}
}
const parent = start.parentNode;
for (const adoptedNode of adoptedNodes) {
parent.insertBefore(adoptedNode, end);
}
let cache = /* @__PURE__ */ new Map();
for (let i = 0; i < adoptedItems.length; i++) {
const item = adoptedItems[i];
const key = listKeyFn(item);
const [getIndex, setIndex] = createSignal(i);
cache.set(key, {
element: adoptedNodes[i],
item,
getIndex,
setIndex
});
}
let reconcileNodes = adoptedNodes.slice();
let reconcileItems = adoptedItems.slice();
internalEffect(() => {
const newItems = child.items();
const parent2 = start.parentNode;
if (!parent2) return;
const result = reconcileList(
parent2,
reconcileItems,
newItems,
reconcileNodes,
listKeyFn,
(item) => {
const prevHydrating = hydrating;
hydrating = false;
try {
const key = listKeyFn(item);
const [getIndex, setIndex] = createSignal(0);
const element = untrack(() => listRenderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
} finally {
hydrating = prevHydrating;
}
},
(_node, item) => {
const key = listKeyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
},
end
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < newItems.length; i++) {
const key = listKeyFn(newItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
reconcileNodes = result.nodes;
reconcileItems = result.items;
});
cursor = end.nextSibling;
}
}
} else if (typeof child === "function") {
while (cursor && cursor.nodeType === 3 && !cursor.data.trim()) {
cursor = cursor.nextSibling;
}
if (cursor && cursor.nodeType === 1) {
const initial = child();
if (isDescriptor(initial)) {
const el = cursor;
cursor = cursor.nextSibling;
adoptNode(initial, el);
continue;
}
}
if (cursor && cursor.nodeType === 8) {
const data = cursor.data;
if (isTextStart(data)) {
const endMarker = findClosingMarker(cursor);
let textNode = cursor.nextSibling;
if (!textNode || textNode.nodeType !== 3) {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node for marker ${data} \u2014 SSR walker should emit content between markers`);
const created = document.createTextNode("");
cursor.parentNode.insertBefore(created, endMarker || cursor.nextSibling);
textNode = created;
}
internalEffect(() => {
textNode.data = String(child());
});
cursor = endMarker ? endMarker.nextSibling : textNode.nextSibling;
} else if (isShowStart(data)) {
const start = cursor;
const end = findClosingMarker(start);
if (end) {
let textNode = findTextBetween(start, end);
if (!textNode) {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node for show marker ${start.data} \u2014 SSR walker should emit content between markers`);
textNode = document.createTextNode("");
start.parentNode.insertBefore(textNode, end);
}
internalEffect(() => {
textNode.data = String(child());
});
cursor = end.nextSibling;
} else {
cursor = cursor.nextSibling;
}
} else {
cursor = cursor.nextSibling;
}
} else if (cursor && cursor.nodeType === 3) {
const textNode = cursor;
cursor = cursor.nextSibling;
internalEffect(() => {
textNode.data = String(child());
});
} else {
if (__DEV__) console.warn(`[FormaJS] Hydration: created text node in empty <${ssrEl.tagName.toLowerCase()}> \u2014 IR may not cover this component`);
const textNode = document.createTextNode("");
ssrEl.appendChild(textNode);
internalEffect(() => {
textNode.data = String(child());
});
}
} else if (typeof child === "string" || typeof child === "number") {
if (cursor && cursor.nodeType === 3) {
cursor = cursor.nextSibling;
}
}
}
}
function hydrateIsland(component, target) {
const hasSSRContent = target.childElementCount > 0 || target.childNodes.length > 0 && Array.from(target.childNodes).some((n) => n.nodeType === 1 || n.nodeType === 3 && n.data.trim());
if (!hasSSRContent) {
if (__DEV__) {
const name = target.getAttribute("data-forma-component") || "unknown";
console.warn(
`[forma] Island "${name}" has no SSR content \u2014 falling back to CSR. This means the IR walker did not render content between ISLAND_START and ISLAND_END.`
);
}
const result = component();
if (result instanceof Element) {
for (const attr of Array.from(target.attributes)) {
if (attr.name.startsWith("data-forma-")) {
result.setAttribute(attr.name, attr.value);
}
}
target.replaceWith(result);
return result;
} else if (result instanceof Node) {
target.appendChild(result);
}
return target;
}
setHydrating(true);
let descriptor;
try {
descriptor = component();
} finally {
setHydrating(false);
}
if (!descriptor || !isDescriptor(descriptor)) {
target.removeAttribute("data-forma-ssr");
return target;
}
if (target.hasAttribute("data-forma-island")) {
adoptNode(descriptor, target);
} else {
adoptNode(descriptor, target.children[0]);
}
target.removeAttribute("data-forma-ssr");
return target;
}
// src/dom/list.ts
function longestIncreasingSubsequence(arr) {
const n = arr.length;
if (n === 0) return [];
const tails = new Int32Array(n);
const tailIndices = new Int32Array(n);
const predecessor = new Int32Array(n).fill(-1);
let tailsLen = 0;
for (let i = 0; i < n; i++) {
const val = arr[i];
let lo = 0, hi = tailsLen;
while (lo < hi) {
const mid = lo + hi >> 1;
if (tails[mid] < val) lo = mid + 1;
else hi = mid;
}
tails[lo] = val;
tailIndices[lo] = i;
if (lo > 0) predecessor[i] = tailIndices[lo - 1];
if (lo >= tailsLen) tailsLen++;
}
const result = new Array(tailsLen);
let idx = tailIndices[tailsLen - 1];
for (let i = tailsLen - 1; i >= 0; i--) {
result[i] = idx;
idx = predecessor[idx];
}
return result;
}
var SMALL_LIST_THRESHOLD = 32;
var ABORT_SYM3 = /* @__PURE__ */ Symbol.for("forma-abort");
var CACHE_SYM2 = /* @__PURE__ */ Symbol.for("forma-attr-cache");
var DYNAMIC_CHILD_SYM2 = /* @__PURE__ */ Symbol.for("forma-dynamic-child");
function canPatchStaticElement(target, source) {
return target instanceof HTMLElement && source instanceof HTMLElement && target.tagName === source.tagName && !target[ABORT_SYM3] && !target[CACHE_SYM2] && !target[DYNAMIC_CHILD_SYM2] && !source[ABORT_SYM3] && !source[CACHE_SYM2] && !source[DYNAMIC_CHILD_SYM2];
}
function patchStaticElement(target, source) {
const sourceAttrNames = /* @__PURE__ */ new Set();
for (const attr of Array.from(source.attributes)) {
sourceAttrNames.add(attr.name);
if (target.getAttribute(attr.name) !== attr.value) {
target.setAttribute(attr.name, attr.value);
}
}
for (const attr of Array.from(target.attributes)) {
if (!sourceAttrNames.has(attr.name)) {
target.removeAttribute(attr.name);
}
}
target.replaceChildren(...Array.from(source.childNodes));
}
function reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
const oldKeys = new Array(oldLen);
for (let i = 0; i < oldLen; i++) {
oldKeys[i] = keyFn(oldItems[i]);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
let found = -1;
for (let j = 0; j < oldLen; j++) {
if (!oldUsed[j] && oldKeys[j] === key) {
found = j;
oldUsed[j] = 1;
break;
}
}
oldIndices[i] = found;
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function reconcileList(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks) {
const oldLen = oldItems.length;
const newLen = newItems.length;
if (newLen === 0) {
for (let i = 0; i < oldLen; i++) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
return { nodes: [], items: [] };
}
if (oldLen === 0) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = createFn(newItems[i]);
if (beforeNode) {
parent.insertBefore(node, beforeNode);
} else {
parent.appendChild(node);
}
hooks?.onInsert?.(node);
nodes[i] = node;
}
return { nodes, items: newItems };
}
if (oldLen < SMALL_LIST_THRESHOLD) {
return reconcileSmall(parent, oldItems, newItems, oldNodes, keyFn, createFn, updateFn, beforeNode, hooks);
}
const oldKeyMap = /* @__PURE__ */ new Map();
for (let i = 0; i < oldLen; i++) {
oldKeyMap.set(keyFn(oldItems[i]), i);
}
const oldIndices = new Array(newLen);
const oldUsed = new Uint8Array(oldLen);
for (let i = 0; i < newLen; i++) {
const key = keyFn(newItems[i]);
const oldIdx = oldKeyMap.get(key);
if (oldIdx !== void 0) {
oldIndices[i] = oldIdx;
oldUsed[oldIdx] = 1;
} else {
oldIndices[i] = -1;
}
}
for (let i = 0; i < oldLen; i++) {
if (!oldUsed[i]) {
if (hooks?.onBeforeRemove) {
const node = oldNodes[i];
hooks.onBeforeRemove(node, () => {
if (node.parentNode) node.parentNode.removeChild(node);
});
} else {
parent.removeChild(oldNodes[i]);
}
}
}
if (oldLen === newLen) {
let allSameOrder = true;
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== i) {
allSameOrder = false;
break;
}
}
if (allSameOrder) {
const nodes = new Array(newLen);
for (let i = 0; i < newLen; i++) {
const node = oldNodes[i];
updateFn(node, newItems[i]);
nodes[i] = node;
}
return { nodes, items: newItems };
}
}
const reusedIndices = [];
const reusedPositions = [];
for (let i = 0; i < newLen; i++) {
if (oldIndices[i] !== -1) {
reusedIndices.push(oldIndices[i]);
reusedPositions.push(i);
}
}
const lisOfReused = longestIncreasingSubsequence(reusedIndices);
const lisFlags = new Uint8Array(newLen);
for (const li of lisOfReused) {
lisFlags[reusedPositions[li]] = 1;
}
const newNodes = new Array(newLen);
let nextSibling = beforeNode ?? null;
for (let i = newLen - 1; i >= 0; i--) {
let node;
let isNew = false;
if (oldIndices[i] === -1) {
node = createFn(newItems[i]);
isNew = true;
} else {
node = oldNodes[oldIndices[i]];
updateFn(node, newItems[i]);
if (lisFlags[i]) {
newNodes[i] = node;
nextSibling = node;
continue;
}
}
if (nextSibling) {
parent.insertBefore(node, nextSibling);
} else {
parent.appendChild(node);
}
if (isNew) hooks?.onInsert?.(node);
newNodes[i] = node;
nextSibling = node;
}
return { nodes: newNodes, items: newItems };
}
function createList(items, keyFn, renderFn, options) {
if (hydrating) {
return { type: "list", items, keyFn, renderFn, options };
}
const startMarker = document.createComment("forma-list-start");
const endMarker = document.createComment("forma-list-end");
const fragment2 = document.createDocumentFragment();
fragment2.appendChild(startMarker);
fragment2.appendChild(endMarker);
let cache = /* @__PURE__ */ new Map();
let currentNodes = [];
let currentItems = [];
const updateOnItemChange = options?.updateOnItemChange ?? "none";
internalEffect(() => {
const newItems = items();
const parent = startMarker.parentNode;
if (!parent) {
return;
}
if (!Array.isArray(newItems)) {
if (__DEV__) {
console.warn("[forma] createList: value is not an array, treating as empty");
}
for (const node of currentNodes) {
if (node.parentNode === parent) parent.removeChild(node);
}
cache = /* @__PURE__ */ new Map();
currentNodes = [];
currentItems = [];
return;
}
let cleanItems = newItems;
for (let i = 0; i < newItems.length; i++) {
if (newItems[i] == null) {
cleanItems = newItems.filter((item) => item != null);
break;
}
}
if (__DEV__) {
const seen = /* @__PURE__ */ new Set();
for (const item of cleanItems) {
const key = keyFn(item);
if (seen.has(key)) {
console.warn("[forma] createList: duplicate key detected:", key);
}
seen.add(key);
}
}
const updateRow = updateOnItemChange === "rerender" ? (node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (!cached) return;
if (cached.item === item) return;
cached.item = item;
if (!(node instanceof HTMLElement)) return;
if (node[ABORT_SYM3] || node[CACHE_SYM2] || node[DYNAMIC_CHILD_SYM2]) {
return;
}
const next = untrack(() => renderFn(item, cached.getIndex));
if (canPatchStaticElement(node, next)) {
patchStaticElement(node, next);
cached.element = node;
}
} : (_node, item) => {
const key = keyFn(item);
const cached = cache.get(key);
if (cached) cached.item = item;
};
const result = reconcileList(
parent,
currentItems,
cleanItems,
currentNodes,
keyFn,
// createFn: create element + cache entry
(item) => {
const key = keyFn(item);
const [getIndex, setIndex] = createSignal(0);
const element = untrack(() => renderFn(item, getIndex));
cache.set(key, { element, item, getIndex, setIndex });
return element;
},
updateRow,
// beforeNode: insert items before the end marker
endMarker
);
const newCache = /* @__PURE__ */ new Map();
for (let i = 0; i < cleanItems.length; i++) {
const key = keyFn(cleanItems[i]);
const cached = cache.get(key);
if (cached) {
cached.setIndex(i);
newCache.set(key, cached);
}
}
cache = newCache;
currentNodes = result.nodes;
currentItems = result.items;
});
return fragment2;
}
export { Fragment, cleanup, createList, createShow, fragment, h, hydrateIsland, reconcileList };
//# sourceMappingURL=chunk-VTPFK5TJ.js.map
//# sourceMappingURL=chunk-VTPFK5TJ.js.map

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display