@getforma/core 1.0.9 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-263HW3KN.cjs +35 -0
- package/dist/chunk-263HW3KN.cjs.map +1 -0
- package/dist/chunk-2QQBKQIF.js +30 -0
- package/dist/chunk-2QQBKQIF.js.map +1 -0
- package/dist/{chunk-6FW5E54W.cjs → chunk-2Y5US35K.cjs} +127 -103
- package/dist/chunk-2Y5US35K.cjs.map +1 -0
- package/dist/{chunk-3G7ET4O5.js → chunk-INNOI6TG.js} +121 -94
- package/dist/chunk-INNOI6TG.js.map +1 -0
- package/dist/{chunk-T33QUD2Y.cjs → chunk-JRQNDXX7.cjs} +43 -43
- package/dist/{chunk-T33QUD2Y.cjs.map → chunk-JRQNDXX7.cjs.map} +1 -1
- package/dist/{chunk-AFRFF7XL.js → chunk-MIOMT2CB.js} +4 -4
- package/dist/{chunk-AFRFF7XL.js.map → chunk-MIOMT2CB.js.map} +1 -1
- package/dist/chunk-W7OUWVRA.cjs +106 -0
- package/dist/chunk-W7OUWVRA.cjs.map +1 -0
- package/dist/chunk-XLVBYXOU.js +100 -0
- package/dist/chunk-XLVBYXOU.js.map +1 -0
- package/dist/forma-runtime-csp.js +92 -6
- package/dist/forma-runtime.js +138 -12
- package/dist/formajs-runtime-hardened.global.js +92 -6
- package/dist/formajs-runtime-hardened.global.js.map +1 -1
- package/dist/formajs-runtime.global.js +138 -12
- package/dist/formajs-runtime.global.js.map +1 -1
- package/dist/formajs.global.js +192 -95
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +66 -66
- package/dist/index.d.cts +11 -25
- package/dist/index.d.ts +11 -25
- package/dist/index.js +6 -6
- package/dist/{resource-DeEzxUz6.d.cts → resource-D6HfVgiA.d.cts} +7 -2
- package/dist/{resource-BHsgURy0.d.ts → resource-ljs2X5NM.d.ts} +7 -2
- package/dist/runtime-hardened.cjs +93 -5
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.js +93 -5
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +47 -31
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +21 -5
- package/dist/runtime.js.map +1 -1
- package/dist/server.cjs +7 -7
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/{signal-C9v4akyJ.d.cts → signal-CRBJYQ6B.d.cts} +0 -8
- package/dist/{signal-C9v4akyJ.d.ts → signal-CRBJYQ6B.d.ts} +0 -8
- package/dist/ssr/index.cjs +23 -42
- package/dist/ssr/index.cjs.map +1 -1
- package/dist/ssr/index.js +23 -42
- package/dist/ssr/index.js.map +1 -1
- package/dist/tc39-compat.cjs +3 -6
- package/dist/tc39-compat.cjs.map +1 -1
- package/dist/tc39-compat.d.cts +1 -1
- package/dist/tc39-compat.d.ts +1 -1
- package/dist/tc39-compat.js +1 -4
- package/dist/tc39-compat.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-3G7ET4O5.js.map +0 -1
- package/dist/chunk-6FW5E54W.cjs.map +0 -1
- package/dist/chunk-HLM5BZZQ.js +0 -35
- package/dist/chunk-HLM5BZZQ.js.map +0 -1
- package/dist/chunk-QLPCVK7C.cjs +0 -38
- package/dist/chunk-QLPCVK7C.cjs.map +0 -1
package/dist/ssr/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/ssr/render.ts","../../src/ssr/ssr-reactive.ts","../../src/ssr/client-script.ts","../../src/ssr/stream.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Void elements that don't have closing tags\nexport const VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// Map of prop names to HTML attribute names\nexport const PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n tabIndex: 'tabindex',\n};\n\n// Escape HTML entities\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n// Escape attribute values\nexport function escapeAttr(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/'/g, ''');\n}\n\n// Dangerous URI detection for href, src, action, formaction attributes\nexport const DANGEROUS_URI_ATTRS = new Set(['href', 'src', 'action', 'formaction']);\nexport const DANGEROUS_URI_RE = /^\\s*(javascript|vbscript|data\\s*:\\s*text\\/html)/i;\n\nexport interface VNode {\n tag: string;\n props: Record<string, unknown> | null;\n children: unknown[];\n}\n\n/**\n * Server-side hyperscript — creates a virtual node instead of a DOM element.\n */\nexport function sh(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): VNode {\n return { tag, props: props ?? null, children };\n}\n\n/**\n * Render a FormaJS virtual tree to an HTML string.\n *\n * Uses an array buffer internally to avoid O(n^2) string concatenation\n * for large trees, then joins once at the end.\n *\n * Usage:\n * ```ts\n * import { renderToString, sh } from '@getforma/core/ssr';\n *\n * const html = renderToString(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()), // signal getters resolved synchronously\n * )\n * );\n * ```\n */\nexport function renderToString(node: unknown): string {\n const parts: string[] = [];\n renderToBuffer(node, parts);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer.\n * Avoids O(n^2) string concatenation for large trees.\n */\nfunction renderToBuffer(node: unknown, parts: string[]): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter) → resolve and render\n if (typeof node === 'function') { renderToBuffer(node(), parts); return; }\n\n // Array → render each\n if (Array.isArray(node)) {\n for (const child of node) renderToBuffer(child, parts);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip event handlers, refs, and internal props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n const attrName = PROP_TO_ATTR[key] ?? key;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n if (DANGEROUS_URI_ATTRS.has(attrName) && typeof resolved === 'string' && DANGEROUS_URI_RE.test(resolved)) {\n continue; // skip dangerous URI\n }\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw === 'object' && raw != null && '__html' in raw) {\n const html = (raw as { __html: unknown }).__html;\n if (typeof html === 'string') {\n parts.push(html);\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n // Render children\n for (const child of children) {\n renderToBuffer(child, parts);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\nexport function isVNode(v: unknown): v is VNode {\n return v != null && typeof v === 'object' && 'tag' in v && 'children' in v;\n}\n\n// ---------------------------------------------------------------------------\n// Hydration-aware rendering\n// ---------------------------------------------------------------------------\n\n/** Per-render hydration context — avoids shared module-level counter. */\ninterface HydrationContext {\n id: number;\n}\n\n/**\n * Render a FormaJS virtual tree to an HTML string with hydration markers.\n *\n * Like `renderToString`, but injects comment markers and `data-forma-h`\n * attributes so the client-side `hydrate()` function can adopt existing\n * DOM nodes without re-creating them.\n *\n * Each call creates its own hydration counter, so concurrent calls\n * (e.g. multiple SSR requests in the same process) produce independent,\n * non-overlapping hydration IDs.\n *\n * Marker types:\n * - `data-forma-h=\"N\"` — element boundary (attribute on the element)\n * - `<!--forma-t:N-->` / `<!--/forma-t:N-->` — reactive text boundary\n * - `<!--forma-l:N-->` / `<!--/forma-l:N-->` — list boundary\n *\n * Usage:\n * ```ts\n * import { renderToStringWithHydration, sh } from '@getforma/core/ssr';\n *\n * const html = renderToStringWithHydration(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()),\n * )\n * );\n * ```\n */\nexport function renderToStringWithHydration(node: unknown): string {\n const ctx: HydrationContext = { id: 0 };\n const parts: string[] = [];\n renderToBufferHydrated(node, parts, ctx);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer with hydration markers.\n * The `ctx` object carries the hydration counter so concurrent renders are isolated.\n */\nfunction renderToBufferHydrated(node: unknown, parts: string[], ctx: HydrationContext): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter / reactive text) → wrap with text markers\n if (typeof node === 'function') {\n const id = ctx.id++;\n parts.push(`<!--forma-t:${id}-->`);\n renderToBufferHydrated(node(), parts, ctx);\n parts.push(`<!--/forma-t:${id}-->`);\n return;\n }\n\n // Array → wrap with list markers\n if (Array.isArray(node)) {\n const id = ctx.id++;\n parts.push(`<!--forma-l:${id}-->`);\n for (const child of node) renderToBufferHydrated(child, parts, ctx);\n parts.push(`<!--/forma-l:${id}-->`);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const id = ctx.id++;\n const { tag, props, children } = node;\n\n // Add hydration data-attribute to element\n parts.push('<', tag, ` data-forma-h=\"${id}\"`);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip event handlers, refs, and internal props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n const attrName = PROP_TO_ATTR[key] ?? key;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n if (DANGEROUS_URI_ATTRS.has(attrName) && typeof resolved === 'string' && DANGEROUS_URI_RE.test(resolved)) {\n continue; // skip dangerous URI\n }\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw === 'object' && raw != null && '__html' in raw) {\n const html = (raw as { __html: unknown }).__html;\n if (typeof html === 'string') {\n parts.push(html);\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n // Render children\n for (const child of children) {\n renderToBufferHydrated(child, parts, ctx);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\n","/**\n * SSR-only signal: just a value holder, no reactivity needed on server.\n */\nexport function ssrSignal<T>(initial: T): [get: () => T, set: (v: T) => void] {\n let value = initial;\n return [() => value, (v) => { value = v; }];\n}\n\nexport function ssrComputed<T>(fn: () => T): () => T {\n return fn; // On server, just call the function directly\n}\n","/**\n * FormaJS SSR - Client Swap Script\n *\n * Tiny inline script injected into the HTML stream that handles\n * out-of-order Suspense boundary resolution.\n *\n * When the server resolves a Suspense boundary, it sends:\n * <script>$FORMA_SWAP(\"forma-s:0\",\"<resolved html>\")</script>\n *\n * This script replaces the fallback placeholder with the resolved content.\n */\n\n/**\n * Returns the client-side swap script as a string, ready to be injected\n * into the HTML stream (typically right after <body> or before first Suspense).\n */\nexport function getSwapScript(): string {\n return `<script>\nfunction $FORMA_SWAP(id,html){\n var el=document.getElementById(id);\n if(el){\n var tpl=document.createElement('template');\n tpl.innerHTML=html;\n el.replaceWith(tpl.content);\n }\n}\n</script>`;\n}\n\n/**\n * Serializes a value to a JSON string that is safe to embed inside a\n * `<script>` block. Standard `JSON.stringify` does not escape `<` or `>`,\n * so a payload containing `</script>` would prematurely close the script\n * tag, enabling an injection attack. We also escape U+2028 and U+2029\n * which are valid JSON but treated as line terminators in JavaScript source.\n */\nfunction safeJsonStringify(val: unknown): string {\n return JSON.stringify(val)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Returns a script tag that swaps a specific Suspense boundary's content.\n * Called when an async resource resolves during streaming.\n */\nexport function getSwapTag(id: string, html: string): string {\n // safeJsonStringify escapes <, >, U+2028, and U+2029 so the serialized\n // content cannot break out of the surrounding <script> block.\n return `<script>$FORMA_SWAP(${safeJsonStringify(id)},${safeJsonStringify(html)})</script>`;\n}\n","/**\n * FormaJS SSR - Streaming Renderer\n *\n * Implements true chunked HTML streaming with out-of-order Suspense resolution.\n *\n * How it works:\n * 1. Yields shell HTML immediately (opening tags, head, static content)\n * 2. When a Suspense boundary is encountered:\n * - Yields the fallback HTML with a placeholder: <div id=\"forma-s:N\" style=\"display:contents\">\n * - Continues rendering siblings without blocking\n * - When the async resource resolves, yields a swap script\n * 3. Suspense boundaries resolve independently in any order (out-of-order streaming)\n */\n\nimport { getSwapScript, getSwapTag } from './client-script.js';\nimport { escapeHtml, escapeAttr, isVNode, VOID_ELEMENTS, PROP_TO_ATTR, DANGEROUS_URI_ATTRS, DANGEROUS_URI_RE, type VNode } from './render.js';\n\n// ---------------------------------------------------------------------------\n// Suspense boundary tracking\n// ---------------------------------------------------------------------------\n\ninterface PendingBoundary {\n id: string;\n promise: Promise<string>;\n}\n\n/** Per-render mutable state, scoped to a single renderToStreamNew call. */\ninterface StreamState {\n suspenseCounter: number;\n pendingBoundaries: PendingBoundary[];\n}\n\n// ---------------------------------------------------------------------------\n// Synchronous render to buffer (for non-async content)\n// ---------------------------------------------------------------------------\n\nfunction renderSync(node: unknown, parts: string[]): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderSync(node(), parts); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderSync(child, parts);\n return;\n }\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const attrName = PROP_TO_ATTR[key] ?? key;\n const resolved = typeof value === 'function' ? value() : value;\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n if (DANGEROUS_URI_ATTRS.has(attrName) && typeof resolved === 'string' && DANGEROUS_URI_RE.test(resolved)) {\n continue; // skip dangerous URI\n }\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw !== 'object' || raw == null || !('__html' in raw) || typeof (raw as { __html: unknown }).__html !== 'string') {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n parts.push((raw as { __html: string }).__html);\n } else {\n for (const child of children) renderSync(child, parts);\n }\n parts.push('</', tag, '>');\n return;\n }\n parts.push(escapeHtml(String(node)));\n}\n\n// ---------------------------------------------------------------------------\n// Suspense VNode detection\n// ---------------------------------------------------------------------------\n\n/**\n * A Suspense boundary VNode has a special shape:\n * { tag: 'forma-suspense', props: { fallback: VNode }, children: [asyncChildren] }\n *\n * Or it can be created via:\n * shSuspense(fallbackVNode, () => asyncContent)\n */\nexport interface SuspenseVNode {\n tag: 'forma-suspense';\n props: { fallback: unknown };\n children: [() => Promise<unknown>];\n}\n\nfunction isSuspenseVNode(v: unknown): v is SuspenseVNode {\n return isVNode(v) && v.tag === 'forma-suspense';\n}\n\n/**\n * Create a server-side Suspense VNode for streaming SSR.\n */\nexport function shSuspense(fallback: unknown, asyncChildren: () => Promise<unknown>): SuspenseVNode {\n return {\n tag: 'forma-suspense',\n props: { fallback },\n children: [asyncChildren],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming renderer\n// ---------------------------------------------------------------------------\n\nexport interface StreamOptions {\n /** Script to bootstrap the client-side app (e.g. <script src=\"/app.js\"></script>) */\n bootstrapScript?: string;\n /** Whether to inject the $FORMA_SWAP client script (default: true) */\n injectSwapScript?: boolean;\n}\n\n/**\n * Render a FormaJS virtual tree to a stream of HTML chunks.\n *\n * Yields HTML progressively:\n * - Static content is yielded immediately\n * - Suspense boundaries yield their fallback, then resolve later\n * - Resolved Suspense boundaries yield swap scripts\n *\n * Usage:\n * ```ts\n * import { renderToStream, shSuspense, sh } from '@getforma/core/ssr';\n *\n * const stream = renderToStream(\n * sh('html', null,\n * sh('body', null,\n * sh('h1', null, 'Hello'),\n * shSuspense(\n * sh('div', null, 'Loading...'),\n * async () => {\n * const data = await fetchData();\n * return sh('div', null, data.name);\n * },\n * ),\n * ),\n * ),\n * );\n *\n * for await (const chunk of stream) {\n * response.write(chunk);\n * }\n * ```\n */\nexport async function* renderToStreamNew(\n node: unknown,\n options?: StreamOptions,\n): AsyncGenerator<string> {\n // Per-render state — scoped to this generator invocation\n const state: StreamState = {\n suspenseCounter: 0,\n pendingBoundaries: [],\n };\n\n const injectSwap = options?.injectSwapScript !== false;\n\n // Inject the swap script first (if there could be Suspense boundaries)\n if (injectSwap) {\n yield getSwapScript();\n }\n\n // Render the main tree synchronously, collecting Suspense boundaries\n const mainParts: string[] = [];\n renderStreamNode(node, mainParts, state);\n yield mainParts.join('');\n\n // Yield bootstrap script if provided\n if (options?.bootstrapScript) {\n yield options.bootstrapScript;\n }\n\n // Wait for and yield all pending Suspense boundaries\n // Each resolves independently (out-of-order)\n while (state.pendingBoundaries.length > 0) {\n const pending = [...state.pendingBoundaries];\n state.pendingBoundaries = [];\n\n // Race all pending boundaries — yield each as it resolves\n const results = await Promise.allSettled(\n pending.map(async (boundary) => {\n const html = await boundary.promise;\n return { id: boundary.id, html };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n yield getSwapTag(result.value.id, result.value.html);\n }\n // On rejection, the fallback stays visible (no swap)\n }\n }\n}\n\n/**\n * Render a node synchronously. If a Suspense boundary is encountered,\n * render the fallback inline and queue the async resolution.\n */\nfunction renderStreamNode(node: unknown, parts: string[], state: StreamState): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderStreamNode(node(), parts, state); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderStreamNode(child, parts, state);\n return;\n }\n\n // Suspense boundary: render fallback, queue async resolution\n if (isSuspenseVNode(node)) {\n const id = `forma-s:${state.suspenseCounter++}`;\n const fallback = node.props.fallback;\n const asyncFn = node.children[0]!;\n\n // Render fallback inside a replaceable container\n parts.push(`<div id=\"${id}\" style=\"display:contents\">`);\n renderSync(fallback, parts);\n parts.push('</div>');\n\n // Queue the async resolution\n const promise = asyncFn().then((resolved: unknown) => {\n const resolvedParts: string[] = [];\n renderSync(resolved, resolvedParts);\n return resolvedParts.join('');\n });\n state.pendingBoundaries.push({ id, promise });\n return;\n }\n\n // Regular VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'fallback') continue; // skip internal suspense props\n if (key.startsWith('on') || key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const attrName = PROP_TO_ATTR[key] ?? key;\n const resolved = typeof value === 'function' ? value() : value;\n if (resolved === true) {\n parts.push(' ', attrName);\n } else if (resolved !== false && resolved != null) {\n if (DANGEROUS_URI_ATTRS.has(attrName) && typeof resolved === 'string' && DANGEROUS_URI_RE.test(resolved)) {\n continue; // skip dangerous URI\n }\n parts.push(' ', attrName, '=\"', escapeAttr(String(resolved)), '\"');\n }\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw !== 'object' || raw == null || !('__html' in raw) || typeof (raw as { __html: unknown }).__html !== 'string') {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n parts.push((raw as { __html: string }).__html);\n } else {\n for (const child of children) renderStreamNode(child, parts, state);\n }\n parts.push('</', tag, '>');\n return;\n }\n\n parts.push(escapeHtml(String(node)));\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/ssr/render.ts","../../src/ssr/ssr-reactive.ts","../../src/ssr/client-script.ts","../../src/ssr/stream.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["// Void elements that don't have closing tags\nexport const VOID_ELEMENTS = new Set([\n 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',\n 'link', 'meta', 'param', 'source', 'track', 'wbr',\n]);\n\n// Map of prop names to HTML attribute names\nexport const PROP_TO_ATTR: Record<string, string> = {\n className: 'class',\n htmlFor: 'for',\n tabIndex: 'tabindex',\n};\n\n// Escape HTML entities\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n// Escape attribute values\nexport function escapeAttr(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/'/g, ''');\n}\n\nimport { isDangerousUrl, isUrlAttr, isEventHandlerAttr, isSafeAttrName } from '../security/url-safety.js';\n\n// Dangerous URI detection for href, src, action, formaction attributes.\n// Retained for backwards compatibility with external importers; the actual\n// scheme check now goes through `isDangerousUrl`, which additionally strips the\n// control characters browsers ignore in a URL scheme (see security/url-safety).\nexport const DANGEROUS_URI_ATTRS = new Set(['href', 'src', 'action', 'formaction']);\nexport const DANGEROUS_URI_RE = /^\\s*(javascript|vbscript|data\\s*:\\s*text\\/html)/i;\n\n/**\n * Decide whether a resolved prop should be emitted as an attribute, and return\n * the escaped `name=\"value\"` fragment (or `name` for boolean `true`). Returns\n * null when the attribute must be dropped for safety. Shared by the plain and\n * hydration-aware renderers so the security rules cannot drift apart.\n */\nexport function renderAttr(key: string, resolved: unknown): string | null {\n const attrName = PROP_TO_ATTR[key] ?? key;\n // Never emit event-handler or malformed attribute names — an attacker who\n // controls a prop key could otherwise inject `onload=…` or break out of the\n // attribute entirely with whitespace/quotes.\n if (isEventHandlerAttr(attrName) || !isSafeAttrName(attrName)) return null;\n if (resolved === true) return ' ' + attrName;\n if (resolved === false || resolved == null) return null;\n const str = String(resolved);\n if (isUrlAttr(attrName) && isDangerousUrl(str)) return null;\n return ' ' + attrName + '=\"' + escapeAttr(str) + '\"';\n}\n\nexport interface VNode {\n tag: string;\n props: Record<string, unknown> | null;\n children: unknown[];\n}\n\n/**\n * Server-side hyperscript — creates a virtual node instead of a DOM element.\n */\nexport function sh(tag: string, props?: Record<string, unknown> | null, ...children: unknown[]): VNode {\n return { tag, props: props ?? null, children };\n}\n\n/**\n * Render a FormaJS virtual tree to an HTML string.\n *\n * Uses an array buffer internally to avoid O(n^2) string concatenation\n * for large trees, then joins once at the end.\n *\n * Usage:\n * ```ts\n * import { renderToString, sh } from '@getforma/core/ssr';\n *\n * const html = renderToString(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()), // signal getters resolved synchronously\n * )\n * );\n * ```\n */\nexport function renderToString(node: unknown): string {\n const parts: string[] = [];\n renderToBuffer(node, parts);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer.\n * Avoids O(n^2) string concatenation for large trees.\n */\nfunction renderToBuffer(node: unknown, parts: string[]): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter) → resolve and render\n if (typeof node === 'function') { renderToBuffer(node(), parts); return; }\n\n // Array → render each\n if (Array.isArray(node)) {\n for (const child of node) renderToBuffer(child, parts);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip refs and internal props; event handlers and unsafe attribute\n // names are dropped inside renderAttr.\n if (key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n const frag = renderAttr(key, resolved);\n if (frag !== null) parts.push(frag);\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw === 'object' && raw != null && '__html' in raw) {\n const html = (raw as { __html: unknown }).__html;\n if (typeof html === 'string') {\n parts.push(html);\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n // Render children\n for (const child of children) {\n renderToBuffer(child, parts);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\nexport function isVNode(v: unknown): v is VNode {\n return v != null && typeof v === 'object' && 'tag' in v && 'children' in v;\n}\n\n// ---------------------------------------------------------------------------\n// Hydration-aware rendering\n// ---------------------------------------------------------------------------\n\n/** Per-render hydration context — avoids shared module-level counter. */\ninterface HydrationContext {\n id: number;\n}\n\n/**\n * Render a FormaJS virtual tree to an HTML string with hydration markers.\n *\n * Like `renderToString`, but injects comment markers and `data-forma-h`\n * attributes so the client-side `hydrate()` function can adopt existing\n * DOM nodes without re-creating them.\n *\n * Each call creates its own hydration counter, so concurrent calls\n * (e.g. multiple SSR requests in the same process) produce independent,\n * non-overlapping hydration IDs.\n *\n * Marker types:\n * - `data-forma-h=\"N\"` — element boundary (attribute on the element)\n * - `<!--forma-t:N-->` / `<!--/forma-t:N-->` — reactive text boundary\n * - `<!--forma-l:N-->` / `<!--/forma-l:N-->` — list boundary\n *\n * Usage:\n * ```ts\n * import { renderToStringWithHydration, sh } from '@getforma/core/ssr';\n *\n * const html = renderToStringWithHydration(\n * sh('div', { class: 'app' },\n * sh('h1', null, 'Hello SSR!'),\n * sh('p', null, () => count()),\n * )\n * );\n * ```\n */\nexport function renderToStringWithHydration(node: unknown): string {\n const ctx: HydrationContext = { id: 0 };\n const parts: string[] = [];\n renderToBufferHydrated(node, parts, ctx);\n return parts.join('');\n}\n\n/**\n * Internal: recursively render into a string array buffer with hydration markers.\n * The `ctx` object carries the hydration counter so concurrent renders are isolated.\n */\nfunction renderToBufferHydrated(node: unknown, parts: string[], ctx: HydrationContext): void {\n // null/undefined/boolean → empty\n if (node == null || node === true || node === false) return;\n\n // String → escaped text\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n\n // Number → stringified\n if (typeof node === 'number') { parts.push(String(node)); return; }\n\n // Function (signal getter / reactive text) → wrap with text markers\n if (typeof node === 'function') {\n const id = ctx.id++;\n parts.push(`<!--forma-t:${id}-->`);\n renderToBufferHydrated(node(), parts, ctx);\n parts.push(`<!--/forma-t:${id}-->`);\n return;\n }\n\n // Array → wrap with list markers\n if (Array.isArray(node)) {\n const id = ctx.id++;\n parts.push(`<!--forma-l:${id}-->`);\n for (const child of node) renderToBufferHydrated(child, parts, ctx);\n parts.push(`<!--/forma-l:${id}-->`);\n return;\n }\n\n // VNode\n if (isVNode(node)) {\n const id = ctx.id++;\n const { tag, props, children } = node;\n\n // Add hydration data-attribute to element\n parts.push('<', tag, ` data-forma-h=\"${id}\"`);\n\n // Render props as attributes\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n // Skip refs and internal props; event handlers and unsafe attribute\n // names are dropped inside renderAttr.\n if (key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n\n // Resolve reactive values\n const resolved = typeof value === 'function' ? value() : value;\n\n const frag = renderAttr(key, resolved);\n if (frag !== null) parts.push(frag);\n }\n }\n\n // Void elements\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n\n parts.push('>');\n\n // dangerouslySetInnerHTML\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw === 'object' && raw != null && '__html' in raw) {\n const html = (raw as { __html: unknown }).__html;\n if (typeof html === 'string') {\n parts.push(html);\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n } else {\n // Render children\n for (const child of children) {\n renderToBufferHydrated(child, parts, ctx);\n }\n }\n\n parts.push('</', tag, '>');\n return;\n }\n\n // Fallback: stringify\n parts.push(escapeHtml(String(node)));\n}\n\n","/**\n * SSR-only signal: just a value holder, no reactivity needed on server.\n */\nexport function ssrSignal<T>(initial: T): [get: () => T, set: (v: T) => void] {\n let value = initial;\n return [() => value, (v) => { value = v; }];\n}\n\nexport function ssrComputed<T>(fn: () => T): () => T {\n return fn; // On server, just call the function directly\n}\n","/**\n * FormaJS SSR - Client Swap Script\n *\n * Tiny inline script injected into the HTML stream that handles\n * out-of-order Suspense boundary resolution.\n *\n * When the server resolves a Suspense boundary, it sends:\n * <script>$FORMA_SWAP(\"forma-s:0\",\"<resolved html>\")</script>\n *\n * This script replaces the fallback placeholder with the resolved content.\n */\n\n/**\n * Returns the client-side swap script as a string, ready to be injected\n * into the HTML stream (typically right after <body> or before first Suspense).\n */\nexport function getSwapScript(): string {\n return `<script>\nfunction $FORMA_SWAP(id,html){\n var el=document.getElementById(id);\n if(el){\n var tpl=document.createElement('template');\n tpl.innerHTML=html;\n el.replaceWith(tpl.content);\n }\n}\n</script>`;\n}\n\n/**\n * Serializes a value to a JSON string that is safe to embed inside a\n * `<script>` block. Standard `JSON.stringify` does not escape `<` or `>`,\n * so a payload containing `</script>` would prematurely close the script\n * tag, enabling an injection attack. We also escape U+2028 and U+2029\n * which are valid JSON but treated as line terminators in JavaScript source.\n */\nfunction safeJsonStringify(val: unknown): string {\n return JSON.stringify(val)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/\\u2028/g, '\\\\u2028')\n .replace(/\\u2029/g, '\\\\u2029');\n}\n\n/**\n * Returns a script tag that swaps a specific Suspense boundary's content.\n * Called when an async resource resolves during streaming.\n */\nexport function getSwapTag(id: string, html: string): string {\n // safeJsonStringify escapes <, >, U+2028, and U+2029 so the serialized\n // content cannot break out of the surrounding <script> block.\n return `<script>$FORMA_SWAP(${safeJsonStringify(id)},${safeJsonStringify(html)})</script>`;\n}\n","/**\n * FormaJS SSR - Streaming Renderer\n *\n * Implements true chunked HTML streaming with out-of-order Suspense resolution.\n *\n * How it works:\n * 1. Yields shell HTML immediately (opening tags, head, static content)\n * 2. When a Suspense boundary is encountered:\n * - Yields the fallback HTML with a placeholder: <div id=\"forma-s:N\" style=\"display:contents\">\n * - Continues rendering siblings without blocking\n * - When the async resource resolves, yields a swap script\n * 3. Suspense boundaries resolve independently in any order (out-of-order streaming)\n */\n\nimport { getSwapScript, getSwapTag } from './client-script.js';\nimport { escapeHtml, isVNode, VOID_ELEMENTS, renderAttr, type VNode } from './render.js';\n\n// ---------------------------------------------------------------------------\n// Suspense boundary tracking\n// ---------------------------------------------------------------------------\n\ninterface PendingBoundary {\n id: string;\n promise: Promise<string>;\n}\n\n/** Per-render mutable state, scoped to a single renderToStreamNew call. */\ninterface StreamState {\n suspenseCounter: number;\n pendingBoundaries: PendingBoundary[];\n}\n\n// ---------------------------------------------------------------------------\n// Synchronous render to buffer (for non-async content)\n// ---------------------------------------------------------------------------\n\nfunction renderSync(node: unknown, parts: string[]): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderSync(node(), parts); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderSync(child, parts);\n return;\n }\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const resolved = typeof value === 'function' ? value() : value;\n const frag = renderAttr(key, resolved);\n if (frag !== null) parts.push(frag);\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw !== 'object' || raw == null || !('__html' in raw) || typeof (raw as { __html: unknown }).__html !== 'string') {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n parts.push((raw as { __html: string }).__html);\n } else {\n for (const child of children) renderSync(child, parts);\n }\n parts.push('</', tag, '>');\n return;\n }\n parts.push(escapeHtml(String(node)));\n}\n\n// ---------------------------------------------------------------------------\n// Suspense VNode detection\n// ---------------------------------------------------------------------------\n\n/**\n * A Suspense boundary VNode has a special shape:\n * { tag: 'forma-suspense', props: { fallback: VNode }, children: [asyncChildren] }\n *\n * Or it can be created via:\n * shSuspense(fallbackVNode, () => asyncContent)\n */\nexport interface SuspenseVNode {\n tag: 'forma-suspense';\n props: { fallback: unknown };\n children: [() => Promise<unknown>];\n}\n\nfunction isSuspenseVNode(v: unknown): v is SuspenseVNode {\n return isVNode(v) && v.tag === 'forma-suspense';\n}\n\n/**\n * Create a server-side Suspense VNode for streaming SSR.\n */\nexport function shSuspense(fallback: unknown, asyncChildren: () => Promise<unknown>): SuspenseVNode {\n return {\n tag: 'forma-suspense',\n props: { fallback },\n children: [asyncChildren],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Streaming renderer\n// ---------------------------------------------------------------------------\n\nexport interface StreamOptions {\n /** Script to bootstrap the client-side app (e.g. <script src=\"/app.js\"></script>) */\n bootstrapScript?: string;\n /** Whether to inject the $FORMA_SWAP client script (default: true) */\n injectSwapScript?: boolean;\n}\n\n/**\n * Render a FormaJS virtual tree to a stream of HTML chunks.\n *\n * Yields HTML progressively:\n * - Static content is yielded immediately\n * - Suspense boundaries yield their fallback, then resolve later\n * - Resolved Suspense boundaries yield swap scripts\n *\n * Usage:\n * ```ts\n * import { renderToStream, shSuspense, sh } from '@getforma/core/ssr';\n *\n * const stream = renderToStream(\n * sh('html', null,\n * sh('body', null,\n * sh('h1', null, 'Hello'),\n * shSuspense(\n * sh('div', null, 'Loading...'),\n * async () => {\n * const data = await fetchData();\n * return sh('div', null, data.name);\n * },\n * ),\n * ),\n * ),\n * );\n *\n * for await (const chunk of stream) {\n * response.write(chunk);\n * }\n * ```\n */\nexport async function* renderToStreamNew(\n node: unknown,\n options?: StreamOptions,\n): AsyncGenerator<string> {\n // Per-render state — scoped to this generator invocation\n const state: StreamState = {\n suspenseCounter: 0,\n pendingBoundaries: [],\n };\n\n const injectSwap = options?.injectSwapScript !== false;\n\n // Inject the swap script first (if there could be Suspense boundaries)\n if (injectSwap) {\n yield getSwapScript();\n }\n\n // Render the main tree synchronously, collecting Suspense boundaries\n const mainParts: string[] = [];\n renderStreamNode(node, mainParts, state);\n yield mainParts.join('');\n\n // Yield bootstrap script if provided\n if (options?.bootstrapScript) {\n yield options.bootstrapScript;\n }\n\n // Wait for and yield all pending Suspense boundaries\n // Each resolves independently (out-of-order)\n while (state.pendingBoundaries.length > 0) {\n const pending = [...state.pendingBoundaries];\n state.pendingBoundaries = [];\n\n // Race all pending boundaries — yield each as it resolves\n const results = await Promise.allSettled(\n pending.map(async (boundary) => {\n const html = await boundary.promise;\n return { id: boundary.id, html };\n }),\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n yield getSwapTag(result.value.id, result.value.html);\n }\n // On rejection, the fallback stays visible (no swap)\n }\n }\n}\n\n/**\n * Render a node synchronously. If a Suspense boundary is encountered,\n * render the fallback inline and queue the async resolution.\n */\nfunction renderStreamNode(node: unknown, parts: string[], state: StreamState): void {\n if (node == null || node === true || node === false) return;\n if (typeof node === 'string') { parts.push(escapeHtml(node)); return; }\n if (typeof node === 'number') { parts.push(String(node)); return; }\n if (typeof node === 'function') { renderStreamNode(node(), parts, state); return; }\n if (Array.isArray(node)) {\n for (const child of node) renderStreamNode(child, parts, state);\n return;\n }\n\n // Suspense boundary: render fallback, queue async resolution\n if (isSuspenseVNode(node)) {\n const id = `forma-s:${state.suspenseCounter++}`;\n const fallback = node.props.fallback;\n const asyncFn = node.children[0]!;\n\n // Render fallback inside a replaceable container\n parts.push(`<div id=\"${id}\" style=\"display:contents\">`);\n renderSync(fallback, parts);\n parts.push('</div>');\n\n // Queue the async resolution\n const promise = asyncFn().then((resolved: unknown) => {\n const resolvedParts: string[] = [];\n renderSync(resolved, resolvedParts);\n return resolvedParts.join('');\n });\n state.pendingBoundaries.push({ id, promise });\n return;\n }\n\n // Regular VNode\n if (isVNode(node)) {\n const { tag, props, children } = node;\n parts.push('<', tag);\n if (props) {\n for (const [key, value] of Object.entries(props)) {\n if (key === 'fallback') continue; // skip internal suspense props\n if (key === 'ref' || key === 'dangerouslySetInnerHTML') continue;\n const resolved = typeof value === 'function' ? value() : value;\n const frag = renderAttr(key, resolved);\n if (frag !== null) parts.push(frag);\n }\n }\n if (VOID_ELEMENTS.has(tag)) { parts.push(' />'); return; }\n parts.push('>');\n if (props?.['dangerouslySetInnerHTML']) {\n const raw = props['dangerouslySetInnerHTML'];\n if (typeof raw !== 'object' || raw == null || !('__html' in raw) || typeof (raw as { __html: unknown }).__html !== 'string') {\n throw new TypeError('dangerouslySetInnerHTML must be { __html: string }');\n }\n parts.push((raw as { __html: string }).__html);\n } else {\n for (const child of children) renderStreamNode(child, parts, state);\n }\n parts.push('</', tag, '>');\n return;\n }\n\n parts.push(escapeHtml(String(node)));\n}\n"]}
|
package/dist/tc39-compat.cjs
CHANGED
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkW7OUWVRA_cjs = require('./chunk-W7OUWVRA.cjs');
|
|
4
4
|
|
|
5
5
|
// src/reactive/tc39-compat.ts
|
|
6
6
|
var State = class {
|
|
7
7
|
_get;
|
|
8
8
|
_set;
|
|
9
9
|
constructor(initialValue, options) {
|
|
10
|
-
const [getter, setter] =
|
|
10
|
+
const [getter, setter] = chunkW7OUWVRA_cjs.createSignal(initialValue, options);
|
|
11
11
|
this._get = getter;
|
|
12
12
|
this._set = setter;
|
|
13
|
-
if (options?.name) {
|
|
14
|
-
Object.defineProperty(getter, "name", { value: options.name });
|
|
15
|
-
}
|
|
16
13
|
}
|
|
17
14
|
get() {
|
|
18
15
|
return this._get();
|
|
@@ -24,7 +21,7 @@ var State = class {
|
|
|
24
21
|
var Computed = class {
|
|
25
22
|
_get;
|
|
26
23
|
constructor(fn) {
|
|
27
|
-
this._get =
|
|
24
|
+
this._get = chunkW7OUWVRA_cjs.createComputed(fn);
|
|
28
25
|
}
|
|
29
26
|
get() {
|
|
30
27
|
return this._get();
|
package/dist/tc39-compat.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/tc39-compat.ts"],"names":["createSignal","createComputed"],"mappings":";;;;;AAyBO,IAAM,QAAN,MAAe;AAAA,EACZ,IAAA;AAAA,EACA,IAAA;AAAA,EAER,WAAA,CAAY,cAAiB,OAAA,EAA4B;
|
|
1
|
+
{"version":3,"sources":["../src/reactive/tc39-compat.ts"],"names":["createSignal","createComputed"],"mappings":";;;;;AAyBO,IAAM,QAAN,MAAe;AAAA,EACZ,IAAA;AAAA,EACA,IAAA;AAAA,EAER,WAAA,CAAY,cAAiB,OAAA,EAA4B;AAGvD,IAAA,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,GAAIA,8BAAA,CAAa,cAAc,OAAO,CAAA;AAC3D,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAAA,EACd;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AAAA,EAEA,IAAI,KAAA,EAAgB;AAClB,IAAA,IAAA,CAAK,KAAK,KAAK,CAAA;AAAA,EACjB;AACF;AAcO,IAAM,WAAN,MAAkB;AAAA,EACf,IAAA;AAAA,EAER,YAAY,EAAA,EAAa;AACvB,IAAA,IAAA,CAAK,IAAA,GAAOC,iCAAe,EAAE,CAAA;AAAA,EAC/B;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AACF","file":"tc39-compat.cjs","sourcesContent":["/**\n * Forma Reactive - TC39 Signals Compatibility Layer\n *\n * Optional class-based API matching the TC39 Signals proposal (Stage 1).\n * Not included in main bundle — import from '@getforma/core/tc39' subpath.\n *\n * When the TC39 proposal advances, these classes can be replaced with\n * native Signal.State and Signal.Computed.\n *\n * @see https://github.com/tc39/proposal-signals\n */\n\nimport { createSignal, type SignalOptions } from './signal.js';\nimport { createComputed } from './computed.js';\n\n/**\n * TC39-compatible reactive state container.\n * Equivalent to `Signal.State` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * console.log(count.get()); // 0\n * count.set(1);\n * ```\n */\nexport class State<T> {\n private _get: () => T;\n private _set: (v: T | ((prev: T) => T)) => void;\n\n constructor(initialValue: T, options?: SignalOptions<T>) {\n // createSignal natively supports equals and the debug name (stored in a side\n // table so isSignal() keeps recognizing the getter) — pass options through.\n const [getter, setter] = createSignal(initialValue, options);\n this._get = getter;\n this._set = setter;\n }\n\n get(): T {\n return this._get();\n }\n\n set(value: T): void {\n this._set(value);\n }\n}\n\n/**\n * TC39-compatible computed value.\n * Equivalent to `Signal.Computed` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * const doubled = new Computed(() => count.get() * 2);\n * console.log(doubled.get()); // 0\n * count.set(5);\n * console.log(doubled.get()); // 10\n * ```\n */\nexport class Computed<T> {\n private _get: () => T;\n\n constructor(fn: () => T) {\n this._get = createComputed(fn);\n }\n\n get(): T {\n return this._get();\n }\n}\n"]}
|
package/dist/tc39-compat.d.cts
CHANGED
package/dist/tc39-compat.d.ts
CHANGED
package/dist/tc39-compat.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createSignal, createComputed } from './chunk-
|
|
1
|
+
import { createSignal, createComputed } from './chunk-XLVBYXOU.js';
|
|
2
2
|
|
|
3
3
|
// src/reactive/tc39-compat.ts
|
|
4
4
|
var State = class {
|
|
@@ -8,9 +8,6 @@ var State = class {
|
|
|
8
8
|
const [getter, setter] = createSignal(initialValue, options);
|
|
9
9
|
this._get = getter;
|
|
10
10
|
this._set = setter;
|
|
11
|
-
if (options?.name) {
|
|
12
|
-
Object.defineProperty(getter, "name", { value: options.name });
|
|
13
|
-
}
|
|
14
11
|
}
|
|
15
12
|
get() {
|
|
16
13
|
return this._get();
|
package/dist/tc39-compat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/tc39-compat.ts"],"names":[],"mappings":";;;AAyBO,IAAM,QAAN,MAAe;AAAA,EACZ,IAAA;AAAA,EACA,IAAA;AAAA,EAER,WAAA,CAAY,cAAiB,OAAA,EAA4B;
|
|
1
|
+
{"version":3,"sources":["../src/reactive/tc39-compat.ts"],"names":[],"mappings":";;;AAyBO,IAAM,QAAN,MAAe;AAAA,EACZ,IAAA;AAAA,EACA,IAAA;AAAA,EAER,WAAA,CAAY,cAAiB,OAAA,EAA4B;AAGvD,IAAA,MAAM,CAAC,MAAA,EAAQ,MAAM,CAAA,GAAI,YAAA,CAAa,cAAc,OAAO,CAAA;AAC3D,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AACZ,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAAA,EACd;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AAAA,EAEA,IAAI,KAAA,EAAgB;AAClB,IAAA,IAAA,CAAK,KAAK,KAAK,CAAA;AAAA,EACjB;AACF;AAcO,IAAM,WAAN,MAAkB;AAAA,EACf,IAAA;AAAA,EAER,YAAY,EAAA,EAAa;AACvB,IAAA,IAAA,CAAK,IAAA,GAAO,eAAe,EAAE,CAAA;AAAA,EAC/B;AAAA,EAEA,GAAA,GAAS;AACP,IAAA,OAAO,KAAK,IAAA,EAAK;AAAA,EACnB;AACF","file":"tc39-compat.js","sourcesContent":["/**\n * Forma Reactive - TC39 Signals Compatibility Layer\n *\n * Optional class-based API matching the TC39 Signals proposal (Stage 1).\n * Not included in main bundle — import from '@getforma/core/tc39' subpath.\n *\n * When the TC39 proposal advances, these classes can be replaced with\n * native Signal.State and Signal.Computed.\n *\n * @see https://github.com/tc39/proposal-signals\n */\n\nimport { createSignal, type SignalOptions } from './signal.js';\nimport { createComputed } from './computed.js';\n\n/**\n * TC39-compatible reactive state container.\n * Equivalent to `Signal.State` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * console.log(count.get()); // 0\n * count.set(1);\n * ```\n */\nexport class State<T> {\n private _get: () => T;\n private _set: (v: T | ((prev: T) => T)) => void;\n\n constructor(initialValue: T, options?: SignalOptions<T>) {\n // createSignal natively supports equals and the debug name (stored in a side\n // table so isSignal() keeps recognizing the getter) — pass options through.\n const [getter, setter] = createSignal(initialValue, options);\n this._get = getter;\n this._set = setter;\n }\n\n get(): T {\n return this._get();\n }\n\n set(value: T): void {\n this._set(value);\n }\n}\n\n/**\n * TC39-compatible computed value.\n * Equivalent to `Signal.Computed` in the TC39 proposal.\n *\n * ```ts\n * const count = new State(0);\n * const doubled = new Computed(() => count.get() * 2);\n * console.log(doubled.get()); // 0\n * count.set(5);\n * console.log(doubled.get()); // 10\n * ```\n */\nexport class Computed<T> {\n private _get: () => T;\n\n constructor(fn: () => T) {\n this._get = createComputed(fn);\n }\n\n get(): T {\n return this._get();\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getforma/core",
|
|
3
3
|
"author": "Forma <victor@getforma.dev>",
|
|
4
|
-
"version": "1.0
|
|
5
|
-
"description": "Real DOM reactive library — fine-grained signals, islands architecture, SSR hydration. No virtual DOM, no diffing. ~
|
|
4
|
+
"version": "1.1.0",
|
|
5
|
+
"description": "Real DOM reactive library — fine-grained signals, islands architecture, SSR hydration. No virtual DOM, no diffing. Core entry ~8KB gzipped; full HTML runtime ~24KB gzipped.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
8
8
|
"module": "./dist/index.js",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/root.ts","../src/reactive/cleanup.ts","../src/reactive/dev.ts","../src/reactive/effect.ts","../src/reactive/memo.ts","../src/reactive/batch.ts","../src/reactive/untrack.ts","../src/reactive/on.ts","../src/reactive/ref.ts","../src/reactive/reducer.ts","../src/reactive/suspense-context.ts","../src/reactive/resource.ts"],"names":["rawEffectScope","rawEffect","setActiveSub","value"],"mappings":"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","file":"chunk-3G7ET4O5.js","sourcesContent":["/**\n * Forma Reactive - Root\n *\n * Explicit reactive ownership scope. All effects created inside a root\n * are automatically disposed when the root is torn down.\n *\n * Uses alien-signals' `effectScope` under the hood for native graph-level\n * effect tracking, with a userland disposer list for non-effect cleanup\n * (e.g., event listeners, DOM references, timers).\n *\n * Roots created inside another root are automatically owned by the parent\n * (Solid-style). When the parent is disposed, all child roots are disposed\n * too. Use {@link createUnownedRoot} for roots that must outlive their\n * lexical parent (e.g., mount points, test harnesses).\n */\n\nimport { effectScope as rawEffectScope, setActiveSub } from 'alien-signals';\n\n// ---------------------------------------------------------------------------\n// Root scope tracking\n// ---------------------------------------------------------------------------\n\nlet currentRoot: RootScope | null = null;\nconst rootStack: (RootScope | null)[] = [];\n\ninterface RootScope {\n /** Userland disposers (event listeners, DOM refs, timers, etc.) */\n disposers: (() => void)[];\n /** alien-signals effect scope dispose — tears down all reactive effects */\n scopeDispose: (() => void) | null;\n}\n\n// ---------------------------------------------------------------------------\n// Shared implementation\n// ---------------------------------------------------------------------------\n\nfunction createRootImpl<T>(fn: (dispose: () => void) => T, owned: boolean): T {\n const scope: RootScope = { disposers: [], scopeDispose: null };\n const parentRoot = owned ? currentRoot : null;\n\n rootStack.push(currentRoot);\n currentRoot = scope;\n\n let disposed = false;\n const dispose = () => {\n if (disposed) return; // idempotent — safe to call after parent already disposed this\n disposed = true;\n // Dispose alien-signals effect scope first (reactive graph cleanup)\n if (scope.scopeDispose) {\n try { scope.scopeDispose(); } catch { /* ensure userland disposers still run */ }\n scope.scopeDispose = null;\n }\n // Then run userland disposers (includes child root disposes)\n for (const d of scope.disposers) {\n try { d(); } catch { /* ensure all disposers run */ }\n }\n scope.disposers.length = 0;\n };\n\n // Auto-register with parent root so child dies when parent dies (Solid-style)\n if (parentRoot) {\n parentRoot.disposers.push(dispose);\n }\n\n let result: T;\n try {\n if (owned) {\n // Owned: alien-signals nesting is kept (belt + suspenders with Forma cascade)\n scope.scopeDispose = rawEffectScope(() => {\n result = fn(dispose);\n });\n } else {\n // Unowned: break alien-signals scope nesting so this scope is NOT\n // disposed when the lexical parent scope is disposed.\n const prevSub = setActiveSub(undefined);\n try {\n scope.scopeDispose = rawEffectScope(() => {\n result = fn(dispose);\n });\n } finally {\n setActiveSub(prevSub);\n }\n }\n } finally {\n currentRoot = rootStack.pop() ?? null;\n }\n\n return result!;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive root scope.\n *\n * All effects created (via `createEffect`) inside the callback are tracked\n * at both the reactive graph level (via alien-signals effectScope) and the\n * userland level (via registerDisposer). The returned `dispose` function\n * tears down everything.\n *\n * Roots created inside another root are automatically owned by the parent.\n * When the parent is disposed, this root is disposed too.\n *\n * ```ts\n * const dispose = createRoot(() => {\n * createEffect(() => console.log(count()));\n * createEffect(() => console.log(name()));\n * });\n * // later: dispose() stops both effects\n * ```\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n return createRootImpl(fn, true);\n}\n\n/**\n * Create a reactive root that is NOT owned by any parent root.\n *\n * Use this for roots that must outlive their lexical parent:\n * top-level mount points, island hydration, test harnesses, etc.\n *\n * ```ts\n * createRoot(() => {\n * // This root outlives the parent even though it's nested:\n * createUnownedRoot((dispose) => {\n * // effects here survive parent disposal\n * });\n * });\n * ```\n */\nexport function createUnownedRoot<T>(fn: (dispose: () => void) => T): T {\n return createRootImpl(fn, false);\n}\n\n/**\n * @internal — called by createEffect and DOM primitives to register disposers\n * in the current root.\n */\nexport function registerDisposer(dispose: () => void): void {\n if (currentRoot) {\n currentRoot.disposers.push(dispose);\n }\n}\n\n/**\n * @internal — check if we're inside a root scope.\n */\nexport function hasActiveRoot(): boolean {\n return currentRoot !== null;\n}\n","/**\n * Forma Reactive - Cleanup\n *\n * Register cleanup functions within reactive scopes.\n * Inspired by SolidJS onCleanup().\n */\n\n// ---------------------------------------------------------------------------\n// Cleanup context tracking\n// ---------------------------------------------------------------------------\n\ntype CleanupCollector = ((fn: () => void) => void) | null;\n\nlet currentCleanupCollector: CleanupCollector = null;\n\n/**\n * Register a cleanup function in the current reactive scope.\n * The cleanup runs before the effect re-executes and on disposal.\n *\n * More composable than returning a cleanup from the effect function,\n * since it can be called from helper functions.\n *\n * ```ts\n * createEffect(() => {\n * const timer = setInterval(tick, 1000);\n * onCleanup(() => clearInterval(timer));\n * });\n * ```\n */\nexport function onCleanup(fn: () => void): void {\n currentCleanupCollector?.(fn);\n}\n\n/**\n * @internal — Set the cleanup collector for the current effect execution.\n */\nexport function setCleanupCollector(collector: CleanupCollector): CleanupCollector {\n const prev = currentCleanupCollector;\n currentCleanupCollector = collector;\n return prev;\n}\n","/**\n * Forma Reactive - Dev Mode\n *\n * Development utilities stripped from production builds via __DEV__ flag.\n * Bundlers replace __DEV__ with false → dead-code elimination removes all dev paths.\n */\n\n// __DEV__ is replaced by bundler (tsup define). Defaults to true for unbundled usage.\ndeclare const process: { env?: Record<string, string | undefined> } | undefined;\nexport const __DEV__: boolean = typeof process !== 'undefined'\n ? (process!.env?.NODE_ENV !== 'production')\n : true;\n\n// ---------------------------------------------------------------------------\n// Global error handler\n// ---------------------------------------------------------------------------\n\n/** Callback signature for the global {@link onError} handler. */\nexport type ErrorHandler = (error: unknown, info?: { source?: string }) => void;\n\nlet _errorHandler: ErrorHandler | null = null;\n\n/**\n * Install a global error handler for FormaJS reactive errors.\n * Called when effects, computeds, or event handlers throw.\n *\n * ```ts\n * onError((err, info) => {\n * console.error(`[${info?.source}]`, err);\n * Sentry.captureException(err);\n * });\n * ```\n */\nexport function onError(handler: ErrorHandler): void {\n _errorHandler = handler;\n}\n\n/** @internal */\nexport function reportError(error: unknown, source?: string): void {\n if (_errorHandler) {\n try { _errorHandler(error, source ? { source } : {}); } catch { /* prevent infinite loop */ }\n }\n if (__DEV__) {\n console.error(`[forma] ${source ?? 'Unknown'} error:`, error);\n }\n}\n","/**\n * Forma Reactive - Effect\n *\n * Side-effectful reactive computation that auto-tracks signal dependencies.\n * Backed by alien-signals for automatic dependency tracking.\n *\n * TC39 Signals equivalent: Signal.subtle.Watcher (effect is a userland concept)\n */\n\nimport { effect as rawEffect } from 'alien-signals';\nimport { hasActiveRoot, registerDisposer } from './root.js';\nimport { setCleanupCollector } from './cleanup.js';\nimport { reportError } from './dev.js';\n\n// ---------------------------------------------------------------------------\n// Cleanup array pool — avoids allocating a new array every effect re-run\n// ---------------------------------------------------------------------------\n\nconst POOL_SIZE = 32;\nconst MAX_REENTRANT_RUNS = 100;\nconst pool: (() => void)[][] = [];\nfor (let i = 0; i < POOL_SIZE; i++) pool.push([]);\nlet poolIdx = POOL_SIZE;\n\nfunction acquireArray(): (() => void)[] {\n if (poolIdx > 0) {\n const arr = pool[--poolIdx]!;\n arr.length = 0;\n return arr;\n }\n return [];\n}\n\nfunction releaseArray(arr: (() => void)[]): void {\n arr.length = 0;\n if (poolIdx < POOL_SIZE) {\n pool[poolIdx++] = arr;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Unified cleanup runner — single function for both re-run and dispose paths\n// ---------------------------------------------------------------------------\n\nfunction runCleanup(fn: (() => void) | undefined): void {\n if (fn === undefined) return;\n try {\n fn();\n } catch (e) {\n reportError(e, 'effect cleanup');\n }\n}\n\nfunction runCleanups(bag: (() => void)[] | undefined): void {\n if (bag === undefined) return;\n for (let i = 0; i < bag.length; i++) {\n try { bag[i]!(); } catch (e) { reportError(e, 'effect cleanup'); }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive effect that auto-tracks signal dependencies.\n *\n * The provided function runs immediately and re-runs whenever any signal it\n * reads changes. If the function returns a cleanup function, that cleanup is\n * called before each re-run and on disposal.\n *\n * Additionally, `onCleanup()` can be called inside the effect to register\n * cleanup functions composably.\n *\n * Returns a dispose function that stops the effect.\n */\n/**\n * @internal — Lightweight effect for Forma's internal DOM bindings.\n *\n * Bypasses createEffect's cleanup infrastructure (pool, collector, error\n * reporting, engine compression) since internal effects never use\n * onCleanup() or return cleanup functions. This saves ~4 function calls\n * per effect creation and per re-run.\n *\n * ONLY use for effects that:\n * 1. Never call onCleanup()\n * 2. Never return a cleanup function\n * 3. Contain simple \"read signal → write DOM\" logic\n */\nexport function internalEffect(fn: () => void): () => void {\n const dispose = rawEffect(fn);\n if (hasActiveRoot()) {\n registerDisposer(dispose);\n }\n return dispose;\n}\n\nexport function createEffect(fn: () => void | (() => void)): () => void {\n const shouldRegister = hasActiveRoot();\n\n // Most effects have zero or one cleanup. Track the single-cleanup case\n // without array allocation; only promote to pooled array when needed.\n let cleanup: (() => void) | undefined;\n let cleanupBag: (() => void)[] | undefined;\n let nextCleanup: (() => void) | undefined;\n let nextCleanupBag: (() => void)[] | undefined;\n\n const addCleanup = (cb: () => void) => {\n if (nextCleanupBag !== undefined) {\n nextCleanupBag.push(cb);\n return;\n }\n if (nextCleanup !== undefined) {\n const bag = acquireArray();\n bag.push(nextCleanup, cb);\n nextCleanup = undefined;\n nextCleanupBag = bag;\n return;\n }\n nextCleanup = cb;\n };\n\n // \"Engine Compression\" exploit: most effects never use cleanup (onCleanup()\n // or return value). After the first clean run, we know this effect is\n // \"cleanup-free\" and can skip the entire cleanup infrastructure on re-runs.\n // This saves 4 function calls per re-run: acquireArray, setCleanupCollector,\n // releaseArray, and bag inspection. Like the engine compression ratio exploit —\n // the \"rules\" say we should always prepare for cleanup, but we measure at\n // \"room temperature\" (first run) and exploit the gap.\n let skipCleanupInfra = false;\n let firstRun = true;\n let running = false;\n let rerunRequested = false;\n\n const runOnce = () => {\n // Run and clear previous cleanups (only if any exist).\n if (cleanup !== undefined) {\n runCleanup(cleanup);\n cleanup = undefined;\n }\n if (cleanupBag !== undefined) {\n runCleanups(cleanupBag);\n releaseArray(cleanupBag);\n cleanupBag = undefined;\n }\n\n // Ultra-fast path: this effect has proven it doesn't use cleanup.\n // Skip acquireArray + setCleanupCollector + bag checks + releaseArray.\n if (skipCleanupInfra) {\n try { fn(); } catch (e) { reportError(e, 'effect'); }\n return;\n }\n\n nextCleanup = undefined;\n nextCleanupBag = undefined;\n\n // Full path: install collector for onCleanup() calls.\n const prevCollector = setCleanupCollector(addCleanup);\n\n try {\n const result = fn();\n\n if (typeof result === 'function') {\n addCleanup(result as () => void);\n }\n\n // Hot path: no cleanups registered or returned — enable ultra-fast path.\n if (nextCleanup === undefined && nextCleanupBag === undefined) {\n // First clean run → enable ultra-fast path for all subsequent runs\n if (firstRun) skipCleanupInfra = true;\n return;\n }\n\n if (nextCleanupBag !== undefined) {\n cleanupBag = nextCleanupBag;\n } else {\n cleanup = nextCleanup;\n }\n } catch (e) {\n reportError(e, 'effect');\n\n if (nextCleanupBag !== undefined) {\n cleanupBag = nextCleanupBag;\n } else {\n cleanup = nextCleanup;\n }\n } finally {\n setCleanupCollector(prevCollector);\n firstRun = false;\n }\n };\n\n const safeFn = () => {\n if (running) {\n rerunRequested = true;\n return;\n }\n\n running = true;\n try {\n let reentrantRuns = 0;\n do {\n rerunRequested = false;\n runOnce();\n if (rerunRequested) {\n reentrantRuns++;\n if (reentrantRuns >= MAX_REENTRANT_RUNS) {\n reportError(\n new Error(`createEffect exceeded ${MAX_REENTRANT_RUNS} re-entrant runs`),\n 'effect',\n );\n rerunRequested = false;\n }\n }\n } while (rerunRequested);\n } finally {\n running = false;\n }\n };\n\n const dispose = rawEffect(safeFn);\n\n // Wrap dispose to also run final cleanups\n let disposed = false;\n const wrappedDispose = () => {\n if (disposed) return;\n disposed = true;\n dispose();\n if (cleanup !== undefined) {\n runCleanup(cleanup);\n cleanup = undefined;\n }\n if (cleanupBag !== undefined) {\n runCleanups(cleanupBag);\n releaseArray(cleanupBag);\n cleanupBag = undefined;\n }\n };\n\n // Register in current root scope (if any)\n if (shouldRegister) {\n registerDisposer(wrappedDispose);\n }\n\n return wrappedDispose;\n}\n","/**\n * Forma Reactive - Memo\n *\n * Alias for createComputed with SolidJS/React-familiar naming.\n * A memoized derived value that only recomputes when its dependencies change.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { createComputed } from './computed.js';\n\n/**\n * Create a memoized computed value.\n * Identical to `createComputed` — provided for React/SolidJS familiarity.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), both createComputed and createMemo in FormaJS are lazy\n * cached derivations — equivalent to SolidJS's createMemo. They are\n * identical.\n *\n * The computation runs lazily and caches the result. It only recomputes\n * when a signal it reads during computation changes.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createMemo(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n */\nexport const createMemo: typeof createComputed = createComputed;\n","/**\n * Forma Reactive - Batch\n *\n * Groups multiple signal updates and defers effect execution until the\n * outermost batch completes. Prevents intermediate re-renders.\n * Backed by alien-signals.\n *\n * TC39 Signals: no built-in batch — this is a userland optimization.\n */\n\nimport { startBatch, endBatch } from 'alien-signals';\n\n/**\n * Group multiple signal updates so that effects only run once after all\n * updates have been applied.\n *\n * ```ts\n * batch(() => {\n * setA(1);\n * setB(2);\n * // effects that depend on A and B won't run yet\n * });\n * // effects run here, once\n * ```\n *\n * Batches are nestable — only the outermost batch triggers the flush.\n */\nexport function batch(fn: () => void): void {\n startBatch();\n try {\n fn();\n } finally {\n endBatch();\n }\n}\n","/**\n * Forma Reactive - Untrack\n *\n * Read signals without subscribing to them in the reactive graph.\n * Essential for reading values inside effects without creating dependencies.\n *\n * TC39 Signals equivalent: Signal.subtle.untrack()\n */\n\nimport { setActiveSub } from 'alien-signals';\n\n/**\n * Execute a function without tracking signal reads.\n * Any signals read inside `fn` will NOT become dependencies of the\n * surrounding effect or computed.\n *\n * ```ts\n * createEffect(() => {\n * const a = count(); // tracked — effect re-runs when count changes\n * const b = untrack(() => other()); // NOT tracked — effect ignores other changes\n * });\n * ```\n */\nexport function untrack<T>(fn: () => T): T {\n const prev = setActiveSub(undefined);\n try {\n return fn();\n } finally {\n setActiveSub(prev);\n }\n}\n","/**\n * Forma Reactive - On\n *\n * Explicit dependency tracking for effects.\n * Only re-runs when the specified signals change, ignoring all other reads.\n *\n * SolidJS equivalent: on()\n * Vue equivalent: watch() with explicit deps\n * React equivalent: useEffect dependency array\n */\n\nimport { untrack } from './untrack.js';\n\n/**\n * Create a tracked effect body that only fires when specific dependencies change.\n *\n * Wraps a function so that only the `deps` signals are tracked.\n * All signal reads inside `fn` are untracked (won't cause re-runs).\n *\n * Use with `createEffect`:\n *\n * ```ts\n * const [a, setA] = createSignal(1);\n * const [b, setB] = createSignal(2);\n *\n * // Only re-runs when `a` changes, NOT when `b` changes:\n * createEffect(on(a, (value, prev) => {\n * console.log(`a changed: ${prev} → ${value}, b is ${b()}`);\n * }));\n *\n * setA(10); // fires: \"a changed: 1 → 10, b is 2\"\n * setB(20); // does NOT fire\n * ```\n *\n * Multiple dependencies:\n * ```ts\n * createEffect(on(\n * () => [a(), b()] as const,\n * ([aVal, bVal], prev) => { ... }\n * ));\n * ```\n */\nexport function on<T, U>(\n deps: () => T,\n fn: (value: T, prev: T | undefined) => U,\n options?: { defer?: boolean },\n): () => U | undefined {\n let prev: T | undefined;\n let isFirst = true;\n\n return () => {\n // Track only the deps\n const value = deps();\n\n if (options?.defer && isFirst) {\n isFirst = false;\n prev = value;\n return undefined;\n }\n\n // Run the body untracked so it doesn't add extra dependencies\n const result = untrack(() => fn(value, prev));\n prev = value;\n return result;\n };\n}\n","/**\n * Forma Reactive - Ref\n *\n * Mutable container that does NOT trigger reactivity.\n * Use for DOM references, previous values, instance variables —\n * anything that needs to persist across effect re-runs without\n * causing re-execution.\n *\n * React equivalent: useRef\n * SolidJS equivalent: (none — uses plain variables in setup)\n */\n\n/** A mutable container that does NOT trigger reactivity when written to. */\nexport interface Ref<T> {\n /** The stored value. Mutating this does not notify effects. */\n current: T;\n}\n\n/**\n * Create a mutable ref container.\n *\n * Unlike signals, writing to `.current` does NOT trigger effects.\n * Use when you need a stable reference across reactive scopes.\n *\n * ```ts\n * const timerRef = createRef<number | null>(null);\n *\n * createEffect(() => {\n * timerRef.current = setInterval(tick, 1000);\n * onCleanup(() => clearInterval(timerRef.current!));\n * });\n *\n * // DOM ref pattern:\n * const elRef = createRef<HTMLElement | null>(null);\n * h('div', { ref: (el) => { elRef.current = el; } });\n * ```\n */\nexport function createRef<T>(initialValue: T): Ref<T> {\n return { current: initialValue };\n}\n","/**\n * Forma Reactive - Reducer\n *\n * State machine pattern — dispatch actions to a pure reducer function.\n * Fine-grained: only the resulting state signal is reactive.\n *\n * React equivalent: useReducer\n * SolidJS equivalent: (none — uses createSignal + helpers)\n */\n\nimport { createSignal, type SignalGetter } from './signal.js';\n\n/** A function that dispatches an action to a reducer. */\nexport type Dispatch<A> = (action: A) => void;\n\n/**\n * Create a reducer — predictable state updates via dispatched actions.\n *\n * The reducer function must be pure: `(state, action) => newState`.\n * Returns a [state, dispatch] tuple.\n *\n * ```ts\n * type Action = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };\n *\n * const [count, dispatch] = createReducer(\n * (state: number, action: Action) => {\n * switch (action.type) {\n * case 'increment': return state + 1;\n * case 'decrement': return state - 1;\n * case 'reset': return 0;\n * }\n * },\n * 0,\n * );\n *\n * dispatch({ type: 'increment' }); // count() === 1\n * dispatch({ type: 'increment' }); // count() === 2\n * dispatch({ type: 'reset' }); // count() === 0\n * ```\n */\nexport function createReducer<S, A>(\n reducer: (state: S, action: A) => S,\n initialState: S,\n): [state: SignalGetter<S>, dispatch: Dispatch<A>] {\n const [state, setState] = createSignal(initialState);\n\n const dispatch: Dispatch<A> = (action) => {\n setState((prev) => reducer(prev, action));\n };\n\n return [state, dispatch];\n}\n","/**\n * Forma Reactive - Suspense Context\n *\n * Shared context stack for Suspense boundaries. Lives in the reactive layer\n * (not DOM) so that createResource can import it without circular dependencies.\n *\n * The pattern mirrors the lifecycle context stack in component/define.ts.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Context object for a Suspense boundary, tracking pending async resources. */\nexport interface SuspenseContext {\n /** Increment when a resource starts loading inside this boundary. */\n increment(): void;\n /** Decrement when a resource resolves/rejects inside this boundary. */\n decrement(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Context stack\n// ---------------------------------------------------------------------------\n\nlet currentSuspenseContext: SuspenseContext | null = null;\nconst suspenseStack: (SuspenseContext | null)[] = [];\n\nexport function pushSuspenseContext(ctx: SuspenseContext): void {\n suspenseStack.push(currentSuspenseContext);\n currentSuspenseContext = ctx;\n}\n\nexport function popSuspenseContext(): void {\n currentSuspenseContext = suspenseStack.pop() ?? null;\n}\n\n/** Get the current Suspense context (if any). Called by createResource. */\nexport function getSuspenseContext(): SuspenseContext | null {\n return currentSuspenseContext;\n}\n","/**\n * Forma Reactive - Resource\n *\n * Async data fetching primitive with reactive loading/error state.\n * Tracks a source signal and refetches when it changes.\n *\n * SolidJS equivalent: createResource\n * React equivalent: use() + Suspense (React 19), or useSWR/react-query\n */\n\nimport { createSignal, type SignalGetter } from './signal.js';\nimport { internalEffect } from './effect.js';\nimport { untrack } from './untrack.js';\nimport { getSuspenseContext } from './suspense-context.js';\n\n/** An async data resource with reactive loading and error state. */\nexport interface Resource<T> {\n /** The resolved data (or undefined while loading). */\n (): T | undefined;\n /** True while the fetcher is running. */\n loading: SignalGetter<boolean>;\n /** The error if the fetcher rejected (or undefined). */\n error: SignalGetter<unknown>;\n /** Manually refetch with the current source value. */\n refetch: () => void;\n /** Manually set the data (overrides fetcher result). */\n mutate: (value: T | undefined) => void;\n}\n\n/** Options for {@link createResource}. */\nexport interface ResourceOptions<T> {\n /** Initial value before first fetch resolves. */\n initialValue?: T;\n}\n\n/**\n * Create an async resource that fetches data reactively.\n *\n * When `source` changes, the fetcher re-runs automatically.\n * Provides reactive `loading` and `error` signals.\n *\n * ```ts\n * const [userId, setUserId] = createSignal(1);\n *\n * const user = createResource(\n * userId, // source signal\n * (id) => fetch(`/api/users/${id}`).then(r => r.json()), // fetcher\n * );\n *\n * internalEffect(() => {\n * if (user.loading()) console.log('Loading...');\n * else if (user.error()) console.log('Error:', user.error());\n * else console.log('User:', user());\n * });\n *\n * setUserId(2); // automatically refetches\n * ```\n *\n * Without a source signal (static fetch):\n * ```ts\n * const posts = createResource(\n * () => true, // constant source — fetches once\n * () => fetch('/api/posts').then(r => r.json()),\n * );\n * ```\n */\nexport function createResource<T, S = true>(\n source: SignalGetter<S>,\n fetcher: (source: S) => Promise<T>,\n options?: ResourceOptions<T>,\n): Resource<T> {\n const [data, setData] = createSignal<T | undefined>(options?.initialValue);\n const [loading, setLoading] = createSignal(false);\n const [error, setError] = createSignal<unknown>(undefined);\n\n // Capture the Suspense context at creation time (not at fetch time).\n // This is critical because the Suspense boundary pushes/pops its context\n // synchronously during children() execution.\n const suspenseCtx = getSuspenseContext();\n\n let abortController: AbortController | null = null;\n let fetchVersion = 0;\n\n const doFetch = () => {\n // Read source outside tracking to get current value\n const sourceValue = untrack(source);\n\n // Abort previous in-flight request\n if (abortController) {\n abortController.abort();\n }\n const controller = new AbortController();\n abortController = controller;\n\n const version = ++fetchVersion;\n const isLatest = () => version === fetchVersion;\n let suspensePending = false;\n\n // Notify Suspense boundary that a fetch has started\n if (suspenseCtx) {\n suspenseCtx.increment();\n suspensePending = true;\n }\n\n setLoading(true);\n setError(undefined);\n\n Promise.resolve(fetcher(sourceValue))\n .then((result) => {\n // Only apply if this is still the latest fetch and wasn't aborted.\n if (isLatest() && !controller.signal.aborted) {\n setData(() => result);\n }\n })\n .catch((err) => {\n if (isLatest() && !controller.signal.aborted) {\n // Ignore abort errors\n if (err?.name !== 'AbortError') {\n setError(err);\n }\n }\n })\n .finally(() => {\n if (suspensePending) suspenseCtx?.decrement();\n if (isLatest()) {\n setLoading(false);\n if (abortController === controller) {\n abortController = null; // Release controller for GC\n }\n }\n });\n };\n\n // Auto-fetch when source changes\n internalEffect(() => {\n source(); // track the source signal\n doFetch();\n });\n\n // Build the resource object\n const resource = (() => data()) as Resource<T>;\n resource.loading = loading;\n resource.error = error;\n resource.refetch = doFetch;\n resource.mutate = (value) => setData(() => value);\n\n return resource;\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/root.ts","../src/reactive/cleanup.ts","../src/reactive/dev.ts","../src/reactive/effect.ts","../src/reactive/memo.ts","../src/reactive/batch.ts","../src/reactive/untrack.ts","../src/reactive/on.ts","../src/reactive/ref.ts","../src/reactive/reducer.ts","../src/reactive/suspense-context.ts","../src/reactive/resource.ts"],"names":["rawEffectScope","setActiveSub","rawEffect","createComputed","startBatch","endBatch","value","createSignal"],"mappings":"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","file":"chunk-6FW5E54W.cjs","sourcesContent":["/**\n * Forma Reactive - Root\n *\n * Explicit reactive ownership scope. All effects created inside a root\n * are automatically disposed when the root is torn down.\n *\n * Uses alien-signals' `effectScope` under the hood for native graph-level\n * effect tracking, with a userland disposer list for non-effect cleanup\n * (e.g., event listeners, DOM references, timers).\n *\n * Roots created inside another root are automatically owned by the parent\n * (Solid-style). When the parent is disposed, all child roots are disposed\n * too. Use {@link createUnownedRoot} for roots that must outlive their\n * lexical parent (e.g., mount points, test harnesses).\n */\n\nimport { effectScope as rawEffectScope, setActiveSub } from 'alien-signals';\n\n// ---------------------------------------------------------------------------\n// Root scope tracking\n// ---------------------------------------------------------------------------\n\nlet currentRoot: RootScope | null = null;\nconst rootStack: (RootScope | null)[] = [];\n\ninterface RootScope {\n /** Userland disposers (event listeners, DOM refs, timers, etc.) */\n disposers: (() => void)[];\n /** alien-signals effect scope dispose — tears down all reactive effects */\n scopeDispose: (() => void) | null;\n}\n\n// ---------------------------------------------------------------------------\n// Shared implementation\n// ---------------------------------------------------------------------------\n\nfunction createRootImpl<T>(fn: (dispose: () => void) => T, owned: boolean): T {\n const scope: RootScope = { disposers: [], scopeDispose: null };\n const parentRoot = owned ? currentRoot : null;\n\n rootStack.push(currentRoot);\n currentRoot = scope;\n\n let disposed = false;\n const dispose = () => {\n if (disposed) return; // idempotent — safe to call after parent already disposed this\n disposed = true;\n // Dispose alien-signals effect scope first (reactive graph cleanup)\n if (scope.scopeDispose) {\n try { scope.scopeDispose(); } catch { /* ensure userland disposers still run */ }\n scope.scopeDispose = null;\n }\n // Then run userland disposers (includes child root disposes)\n for (const d of scope.disposers) {\n try { d(); } catch { /* ensure all disposers run */ }\n }\n scope.disposers.length = 0;\n };\n\n // Auto-register with parent root so child dies when parent dies (Solid-style)\n if (parentRoot) {\n parentRoot.disposers.push(dispose);\n }\n\n let result: T;\n try {\n if (owned) {\n // Owned: alien-signals nesting is kept (belt + suspenders with Forma cascade)\n scope.scopeDispose = rawEffectScope(() => {\n result = fn(dispose);\n });\n } else {\n // Unowned: break alien-signals scope nesting so this scope is NOT\n // disposed when the lexical parent scope is disposed.\n const prevSub = setActiveSub(undefined);\n try {\n scope.scopeDispose = rawEffectScope(() => {\n result = fn(dispose);\n });\n } finally {\n setActiveSub(prevSub);\n }\n }\n } finally {\n currentRoot = rootStack.pop() ?? null;\n }\n\n return result!;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive root scope.\n *\n * All effects created (via `createEffect`) inside the callback are tracked\n * at both the reactive graph level (via alien-signals effectScope) and the\n * userland level (via registerDisposer). The returned `dispose` function\n * tears down everything.\n *\n * Roots created inside another root are automatically owned by the parent.\n * When the parent is disposed, this root is disposed too.\n *\n * ```ts\n * const dispose = createRoot(() => {\n * createEffect(() => console.log(count()));\n * createEffect(() => console.log(name()));\n * });\n * // later: dispose() stops both effects\n * ```\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n return createRootImpl(fn, true);\n}\n\n/**\n * Create a reactive root that is NOT owned by any parent root.\n *\n * Use this for roots that must outlive their lexical parent:\n * top-level mount points, island hydration, test harnesses, etc.\n *\n * ```ts\n * createRoot(() => {\n * // This root outlives the parent even though it's nested:\n * createUnownedRoot((dispose) => {\n * // effects here survive parent disposal\n * });\n * });\n * ```\n */\nexport function createUnownedRoot<T>(fn: (dispose: () => void) => T): T {\n return createRootImpl(fn, false);\n}\n\n/**\n * @internal — called by createEffect and DOM primitives to register disposers\n * in the current root.\n */\nexport function registerDisposer(dispose: () => void): void {\n if (currentRoot) {\n currentRoot.disposers.push(dispose);\n }\n}\n\n/**\n * @internal — check if we're inside a root scope.\n */\nexport function hasActiveRoot(): boolean {\n return currentRoot !== null;\n}\n","/**\n * Forma Reactive - Cleanup\n *\n * Register cleanup functions within reactive scopes.\n * Inspired by SolidJS onCleanup().\n */\n\n// ---------------------------------------------------------------------------\n// Cleanup context tracking\n// ---------------------------------------------------------------------------\n\ntype CleanupCollector = ((fn: () => void) => void) | null;\n\nlet currentCleanupCollector: CleanupCollector = null;\n\n/**\n * Register a cleanup function in the current reactive scope.\n * The cleanup runs before the effect re-executes and on disposal.\n *\n * More composable than returning a cleanup from the effect function,\n * since it can be called from helper functions.\n *\n * ```ts\n * createEffect(() => {\n * const timer = setInterval(tick, 1000);\n * onCleanup(() => clearInterval(timer));\n * });\n * ```\n */\nexport function onCleanup(fn: () => void): void {\n currentCleanupCollector?.(fn);\n}\n\n/**\n * @internal — Set the cleanup collector for the current effect execution.\n */\nexport function setCleanupCollector(collector: CleanupCollector): CleanupCollector {\n const prev = currentCleanupCollector;\n currentCleanupCollector = collector;\n return prev;\n}\n","/**\n * Forma Reactive - Dev Mode\n *\n * Development utilities stripped from production builds via __DEV__ flag.\n * Bundlers replace __DEV__ with false → dead-code elimination removes all dev paths.\n */\n\n// __DEV__ is replaced by bundler (tsup define). Defaults to true for unbundled usage.\ndeclare const process: { env?: Record<string, string | undefined> } | undefined;\nexport const __DEV__: boolean = typeof process !== 'undefined'\n ? (process!.env?.NODE_ENV !== 'production')\n : true;\n\n// ---------------------------------------------------------------------------\n// Global error handler\n// ---------------------------------------------------------------------------\n\n/** Callback signature for the global {@link onError} handler. */\nexport type ErrorHandler = (error: unknown, info?: { source?: string }) => void;\n\nlet _errorHandler: ErrorHandler | null = null;\n\n/**\n * Install a global error handler for FormaJS reactive errors.\n * Called when effects, computeds, or event handlers throw.\n *\n * ```ts\n * onError((err, info) => {\n * console.error(`[${info?.source}]`, err);\n * Sentry.captureException(err);\n * });\n * ```\n */\nexport function onError(handler: ErrorHandler): void {\n _errorHandler = handler;\n}\n\n/** @internal */\nexport function reportError(error: unknown, source?: string): void {\n if (_errorHandler) {\n try { _errorHandler(error, source ? { source } : {}); } catch { /* prevent infinite loop */ }\n }\n if (__DEV__) {\n console.error(`[forma] ${source ?? 'Unknown'} error:`, error);\n }\n}\n","/**\n * Forma Reactive - Effect\n *\n * Side-effectful reactive computation that auto-tracks signal dependencies.\n * Backed by alien-signals for automatic dependency tracking.\n *\n * TC39 Signals equivalent: Signal.subtle.Watcher (effect is a userland concept)\n */\n\nimport { effect as rawEffect } from 'alien-signals';\nimport { hasActiveRoot, registerDisposer } from './root.js';\nimport { setCleanupCollector } from './cleanup.js';\nimport { reportError } from './dev.js';\n\n// ---------------------------------------------------------------------------\n// Cleanup array pool — avoids allocating a new array every effect re-run\n// ---------------------------------------------------------------------------\n\nconst POOL_SIZE = 32;\nconst MAX_REENTRANT_RUNS = 100;\nconst pool: (() => void)[][] = [];\nfor (let i = 0; i < POOL_SIZE; i++) pool.push([]);\nlet poolIdx = POOL_SIZE;\n\nfunction acquireArray(): (() => void)[] {\n if (poolIdx > 0) {\n const arr = pool[--poolIdx]!;\n arr.length = 0;\n return arr;\n }\n return [];\n}\n\nfunction releaseArray(arr: (() => void)[]): void {\n arr.length = 0;\n if (poolIdx < POOL_SIZE) {\n pool[poolIdx++] = arr;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Unified cleanup runner — single function for both re-run and dispose paths\n// ---------------------------------------------------------------------------\n\nfunction runCleanup(fn: (() => void) | undefined): void {\n if (fn === undefined) return;\n try {\n fn();\n } catch (e) {\n reportError(e, 'effect cleanup');\n }\n}\n\nfunction runCleanups(bag: (() => void)[] | undefined): void {\n if (bag === undefined) return;\n for (let i = 0; i < bag.length; i++) {\n try { bag[i]!(); } catch (e) { reportError(e, 'effect cleanup'); }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Create a reactive effect that auto-tracks signal dependencies.\n *\n * The provided function runs immediately and re-runs whenever any signal it\n * reads changes. If the function returns a cleanup function, that cleanup is\n * called before each re-run and on disposal.\n *\n * Additionally, `onCleanup()` can be called inside the effect to register\n * cleanup functions composably.\n *\n * Returns a dispose function that stops the effect.\n */\n/**\n * @internal — Lightweight effect for Forma's internal DOM bindings.\n *\n * Bypasses createEffect's cleanup infrastructure (pool, collector, error\n * reporting, engine compression) since internal effects never use\n * onCleanup() or return cleanup functions. This saves ~4 function calls\n * per effect creation and per re-run.\n *\n * ONLY use for effects that:\n * 1. Never call onCleanup()\n * 2. Never return a cleanup function\n * 3. Contain simple \"read signal → write DOM\" logic\n */\nexport function internalEffect(fn: () => void): () => void {\n const dispose = rawEffect(fn);\n if (hasActiveRoot()) {\n registerDisposer(dispose);\n }\n return dispose;\n}\n\nexport function createEffect(fn: () => void | (() => void)): () => void {\n const shouldRegister = hasActiveRoot();\n\n // Most effects have zero or one cleanup. Track the single-cleanup case\n // without array allocation; only promote to pooled array when needed.\n let cleanup: (() => void) | undefined;\n let cleanupBag: (() => void)[] | undefined;\n let nextCleanup: (() => void) | undefined;\n let nextCleanupBag: (() => void)[] | undefined;\n\n const addCleanup = (cb: () => void) => {\n if (nextCleanupBag !== undefined) {\n nextCleanupBag.push(cb);\n return;\n }\n if (nextCleanup !== undefined) {\n const bag = acquireArray();\n bag.push(nextCleanup, cb);\n nextCleanup = undefined;\n nextCleanupBag = bag;\n return;\n }\n nextCleanup = cb;\n };\n\n // \"Engine Compression\" exploit: most effects never use cleanup (onCleanup()\n // or return value). After the first clean run, we know this effect is\n // \"cleanup-free\" and can skip the entire cleanup infrastructure on re-runs.\n // This saves 4 function calls per re-run: acquireArray, setCleanupCollector,\n // releaseArray, and bag inspection. Like the engine compression ratio exploit —\n // the \"rules\" say we should always prepare for cleanup, but we measure at\n // \"room temperature\" (first run) and exploit the gap.\n let skipCleanupInfra = false;\n let firstRun = true;\n let running = false;\n let rerunRequested = false;\n\n const runOnce = () => {\n // Run and clear previous cleanups (only if any exist).\n if (cleanup !== undefined) {\n runCleanup(cleanup);\n cleanup = undefined;\n }\n if (cleanupBag !== undefined) {\n runCleanups(cleanupBag);\n releaseArray(cleanupBag);\n cleanupBag = undefined;\n }\n\n // Ultra-fast path: this effect has proven it doesn't use cleanup.\n // Skip acquireArray + setCleanupCollector + bag checks + releaseArray.\n if (skipCleanupInfra) {\n try { fn(); } catch (e) { reportError(e, 'effect'); }\n return;\n }\n\n nextCleanup = undefined;\n nextCleanupBag = undefined;\n\n // Full path: install collector for onCleanup() calls.\n const prevCollector = setCleanupCollector(addCleanup);\n\n try {\n const result = fn();\n\n if (typeof result === 'function') {\n addCleanup(result as () => void);\n }\n\n // Hot path: no cleanups registered or returned — enable ultra-fast path.\n if (nextCleanup === undefined && nextCleanupBag === undefined) {\n // First clean run → enable ultra-fast path for all subsequent runs\n if (firstRun) skipCleanupInfra = true;\n return;\n }\n\n if (nextCleanupBag !== undefined) {\n cleanupBag = nextCleanupBag;\n } else {\n cleanup = nextCleanup;\n }\n } catch (e) {\n reportError(e, 'effect');\n\n if (nextCleanupBag !== undefined) {\n cleanupBag = nextCleanupBag;\n } else {\n cleanup = nextCleanup;\n }\n } finally {\n setCleanupCollector(prevCollector);\n firstRun = false;\n }\n };\n\n const safeFn = () => {\n if (running) {\n rerunRequested = true;\n return;\n }\n\n running = true;\n try {\n let reentrantRuns = 0;\n do {\n rerunRequested = false;\n runOnce();\n if (rerunRequested) {\n reentrantRuns++;\n if (reentrantRuns >= MAX_REENTRANT_RUNS) {\n reportError(\n new Error(`createEffect exceeded ${MAX_REENTRANT_RUNS} re-entrant runs`),\n 'effect',\n );\n rerunRequested = false;\n }\n }\n } while (rerunRequested);\n } finally {\n running = false;\n }\n };\n\n const dispose = rawEffect(safeFn);\n\n // Wrap dispose to also run final cleanups\n let disposed = false;\n const wrappedDispose = () => {\n if (disposed) return;\n disposed = true;\n dispose();\n if (cleanup !== undefined) {\n runCleanup(cleanup);\n cleanup = undefined;\n }\n if (cleanupBag !== undefined) {\n runCleanups(cleanupBag);\n releaseArray(cleanupBag);\n cleanupBag = undefined;\n }\n };\n\n // Register in current root scope (if any)\n if (shouldRegister) {\n registerDisposer(wrappedDispose);\n }\n\n return wrappedDispose;\n}\n","/**\n * Forma Reactive - Memo\n *\n * Alias for createComputed with SolidJS/React-familiar naming.\n * A memoized derived value that only recomputes when its dependencies change.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { createComputed } from './computed.js';\n\n/**\n * Create a memoized computed value.\n * Identical to `createComputed` — provided for React/SolidJS familiarity.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), both createComputed and createMemo in FormaJS are lazy\n * cached derivations — equivalent to SolidJS's createMemo. They are\n * identical.\n *\n * The computation runs lazily and caches the result. It only recomputes\n * when a signal it reads during computation changes.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createMemo(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n */\nexport const createMemo: typeof createComputed = createComputed;\n","/**\n * Forma Reactive - Batch\n *\n * Groups multiple signal updates and defers effect execution until the\n * outermost batch completes. Prevents intermediate re-renders.\n * Backed by alien-signals.\n *\n * TC39 Signals: no built-in batch — this is a userland optimization.\n */\n\nimport { startBatch, endBatch } from 'alien-signals';\n\n/**\n * Group multiple signal updates so that effects only run once after all\n * updates have been applied.\n *\n * ```ts\n * batch(() => {\n * setA(1);\n * setB(2);\n * // effects that depend on A and B won't run yet\n * });\n * // effects run here, once\n * ```\n *\n * Batches are nestable — only the outermost batch triggers the flush.\n */\nexport function batch(fn: () => void): void {\n startBatch();\n try {\n fn();\n } finally {\n endBatch();\n }\n}\n","/**\n * Forma Reactive - Untrack\n *\n * Read signals without subscribing to them in the reactive graph.\n * Essential for reading values inside effects without creating dependencies.\n *\n * TC39 Signals equivalent: Signal.subtle.untrack()\n */\n\nimport { setActiveSub } from 'alien-signals';\n\n/**\n * Execute a function without tracking signal reads.\n * Any signals read inside `fn` will NOT become dependencies of the\n * surrounding effect or computed.\n *\n * ```ts\n * createEffect(() => {\n * const a = count(); // tracked — effect re-runs when count changes\n * const b = untrack(() => other()); // NOT tracked — effect ignores other changes\n * });\n * ```\n */\nexport function untrack<T>(fn: () => T): T {\n const prev = setActiveSub(undefined);\n try {\n return fn();\n } finally {\n setActiveSub(prev);\n }\n}\n","/**\n * Forma Reactive - On\n *\n * Explicit dependency tracking for effects.\n * Only re-runs when the specified signals change, ignoring all other reads.\n *\n * SolidJS equivalent: on()\n * Vue equivalent: watch() with explicit deps\n * React equivalent: useEffect dependency array\n */\n\nimport { untrack } from './untrack.js';\n\n/**\n * Create a tracked effect body that only fires when specific dependencies change.\n *\n * Wraps a function so that only the `deps` signals are tracked.\n * All signal reads inside `fn` are untracked (won't cause re-runs).\n *\n * Use with `createEffect`:\n *\n * ```ts\n * const [a, setA] = createSignal(1);\n * const [b, setB] = createSignal(2);\n *\n * // Only re-runs when `a` changes, NOT when `b` changes:\n * createEffect(on(a, (value, prev) => {\n * console.log(`a changed: ${prev} → ${value}, b is ${b()}`);\n * }));\n *\n * setA(10); // fires: \"a changed: 1 → 10, b is 2\"\n * setB(20); // does NOT fire\n * ```\n *\n * Multiple dependencies:\n * ```ts\n * createEffect(on(\n * () => [a(), b()] as const,\n * ([aVal, bVal], prev) => { ... }\n * ));\n * ```\n */\nexport function on<T, U>(\n deps: () => T,\n fn: (value: T, prev: T | undefined) => U,\n options?: { defer?: boolean },\n): () => U | undefined {\n let prev: T | undefined;\n let isFirst = true;\n\n return () => {\n // Track only the deps\n const value = deps();\n\n if (options?.defer && isFirst) {\n isFirst = false;\n prev = value;\n return undefined;\n }\n\n // Run the body untracked so it doesn't add extra dependencies\n const result = untrack(() => fn(value, prev));\n prev = value;\n return result;\n };\n}\n","/**\n * Forma Reactive - Ref\n *\n * Mutable container that does NOT trigger reactivity.\n * Use for DOM references, previous values, instance variables —\n * anything that needs to persist across effect re-runs without\n * causing re-execution.\n *\n * React equivalent: useRef\n * SolidJS equivalent: (none — uses plain variables in setup)\n */\n\n/** A mutable container that does NOT trigger reactivity when written to. */\nexport interface Ref<T> {\n /** The stored value. Mutating this does not notify effects. */\n current: T;\n}\n\n/**\n * Create a mutable ref container.\n *\n * Unlike signals, writing to `.current` does NOT trigger effects.\n * Use when you need a stable reference across reactive scopes.\n *\n * ```ts\n * const timerRef = createRef<number | null>(null);\n *\n * createEffect(() => {\n * timerRef.current = setInterval(tick, 1000);\n * onCleanup(() => clearInterval(timerRef.current!));\n * });\n *\n * // DOM ref pattern:\n * const elRef = createRef<HTMLElement | null>(null);\n * h('div', { ref: (el) => { elRef.current = el; } });\n * ```\n */\nexport function createRef<T>(initialValue: T): Ref<T> {\n return { current: initialValue };\n}\n","/**\n * Forma Reactive - Reducer\n *\n * State machine pattern — dispatch actions to a pure reducer function.\n * Fine-grained: only the resulting state signal is reactive.\n *\n * React equivalent: useReducer\n * SolidJS equivalent: (none — uses createSignal + helpers)\n */\n\nimport { createSignal, type SignalGetter } from './signal.js';\n\n/** A function that dispatches an action to a reducer. */\nexport type Dispatch<A> = (action: A) => void;\n\n/**\n * Create a reducer — predictable state updates via dispatched actions.\n *\n * The reducer function must be pure: `(state, action) => newState`.\n * Returns a [state, dispatch] tuple.\n *\n * ```ts\n * type Action = { type: 'increment' } | { type: 'decrement' } | { type: 'reset' };\n *\n * const [count, dispatch] = createReducer(\n * (state: number, action: Action) => {\n * switch (action.type) {\n * case 'increment': return state + 1;\n * case 'decrement': return state - 1;\n * case 'reset': return 0;\n * }\n * },\n * 0,\n * );\n *\n * dispatch({ type: 'increment' }); // count() === 1\n * dispatch({ type: 'increment' }); // count() === 2\n * dispatch({ type: 'reset' }); // count() === 0\n * ```\n */\nexport function createReducer<S, A>(\n reducer: (state: S, action: A) => S,\n initialState: S,\n): [state: SignalGetter<S>, dispatch: Dispatch<A>] {\n const [state, setState] = createSignal(initialState);\n\n const dispatch: Dispatch<A> = (action) => {\n setState((prev) => reducer(prev, action));\n };\n\n return [state, dispatch];\n}\n","/**\n * Forma Reactive - Suspense Context\n *\n * Shared context stack for Suspense boundaries. Lives in the reactive layer\n * (not DOM) so that createResource can import it without circular dependencies.\n *\n * The pattern mirrors the lifecycle context stack in component/define.ts.\n */\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Context object for a Suspense boundary, tracking pending async resources. */\nexport interface SuspenseContext {\n /** Increment when a resource starts loading inside this boundary. */\n increment(): void;\n /** Decrement when a resource resolves/rejects inside this boundary. */\n decrement(): void;\n}\n\n// ---------------------------------------------------------------------------\n// Context stack\n// ---------------------------------------------------------------------------\n\nlet currentSuspenseContext: SuspenseContext | null = null;\nconst suspenseStack: (SuspenseContext | null)[] = [];\n\nexport function pushSuspenseContext(ctx: SuspenseContext): void {\n suspenseStack.push(currentSuspenseContext);\n currentSuspenseContext = ctx;\n}\n\nexport function popSuspenseContext(): void {\n currentSuspenseContext = suspenseStack.pop() ?? null;\n}\n\n/** Get the current Suspense context (if any). Called by createResource. */\nexport function getSuspenseContext(): SuspenseContext | null {\n return currentSuspenseContext;\n}\n","/**\n * Forma Reactive - Resource\n *\n * Async data fetching primitive with reactive loading/error state.\n * Tracks a source signal and refetches when it changes.\n *\n * SolidJS equivalent: createResource\n * React equivalent: use() + Suspense (React 19), or useSWR/react-query\n */\n\nimport { createSignal, type SignalGetter } from './signal.js';\nimport { internalEffect } from './effect.js';\nimport { untrack } from './untrack.js';\nimport { getSuspenseContext } from './suspense-context.js';\n\n/** An async data resource with reactive loading and error state. */\nexport interface Resource<T> {\n /** The resolved data (or undefined while loading). */\n (): T | undefined;\n /** True while the fetcher is running. */\n loading: SignalGetter<boolean>;\n /** The error if the fetcher rejected (or undefined). */\n error: SignalGetter<unknown>;\n /** Manually refetch with the current source value. */\n refetch: () => void;\n /** Manually set the data (overrides fetcher result). */\n mutate: (value: T | undefined) => void;\n}\n\n/** Options for {@link createResource}. */\nexport interface ResourceOptions<T> {\n /** Initial value before first fetch resolves. */\n initialValue?: T;\n}\n\n/**\n * Create an async resource that fetches data reactively.\n *\n * When `source` changes, the fetcher re-runs automatically.\n * Provides reactive `loading` and `error` signals.\n *\n * ```ts\n * const [userId, setUserId] = createSignal(1);\n *\n * const user = createResource(\n * userId, // source signal\n * (id) => fetch(`/api/users/${id}`).then(r => r.json()), // fetcher\n * );\n *\n * internalEffect(() => {\n * if (user.loading()) console.log('Loading...');\n * else if (user.error()) console.log('Error:', user.error());\n * else console.log('User:', user());\n * });\n *\n * setUserId(2); // automatically refetches\n * ```\n *\n * Without a source signal (static fetch):\n * ```ts\n * const posts = createResource(\n * () => true, // constant source — fetches once\n * () => fetch('/api/posts').then(r => r.json()),\n * );\n * ```\n */\nexport function createResource<T, S = true>(\n source: SignalGetter<S>,\n fetcher: (source: S) => Promise<T>,\n options?: ResourceOptions<T>,\n): Resource<T> {\n const [data, setData] = createSignal<T | undefined>(options?.initialValue);\n const [loading, setLoading] = createSignal(false);\n const [error, setError] = createSignal<unknown>(undefined);\n\n // Capture the Suspense context at creation time (not at fetch time).\n // This is critical because the Suspense boundary pushes/pops its context\n // synchronously during children() execution.\n const suspenseCtx = getSuspenseContext();\n\n let abortController: AbortController | null = null;\n let fetchVersion = 0;\n\n const doFetch = () => {\n // Read source outside tracking to get current value\n const sourceValue = untrack(source);\n\n // Abort previous in-flight request\n if (abortController) {\n abortController.abort();\n }\n const controller = new AbortController();\n abortController = controller;\n\n const version = ++fetchVersion;\n const isLatest = () => version === fetchVersion;\n let suspensePending = false;\n\n // Notify Suspense boundary that a fetch has started\n if (suspenseCtx) {\n suspenseCtx.increment();\n suspensePending = true;\n }\n\n setLoading(true);\n setError(undefined);\n\n Promise.resolve(fetcher(sourceValue))\n .then((result) => {\n // Only apply if this is still the latest fetch and wasn't aborted.\n if (isLatest() && !controller.signal.aborted) {\n setData(() => result);\n }\n })\n .catch((err) => {\n if (isLatest() && !controller.signal.aborted) {\n // Ignore abort errors\n if (err?.name !== 'AbortError') {\n setError(err);\n }\n }\n })\n .finally(() => {\n if (suspensePending) suspenseCtx?.decrement();\n if (isLatest()) {\n setLoading(false);\n if (abortController === controller) {\n abortController = null; // Release controller for GC\n }\n }\n });\n };\n\n // Auto-fetch when source changes\n internalEffect(() => {\n source(); // track the source signal\n doFetch();\n });\n\n // Build the resource object\n const resource = (() => data()) as Resource<T>;\n resource.loading = loading;\n resource.error = error;\n resource.refetch = doFetch;\n resource.mutate = (value) => setData(() => value);\n\n return resource;\n}\n"]}
|
package/dist/chunk-HLM5BZZQ.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { signal, computed, setActiveSub } from 'alien-signals';
|
|
2
|
-
|
|
3
|
-
// src/reactive/signal.ts
|
|
4
|
-
function applySignalSet(s, v, equals) {
|
|
5
|
-
if (typeof v !== "function") {
|
|
6
|
-
if (equals) {
|
|
7
|
-
const prevSub2 = setActiveSub(void 0);
|
|
8
|
-
const prev2 = s();
|
|
9
|
-
setActiveSub(prevSub2);
|
|
10
|
-
if (equals(prev2, v)) return;
|
|
11
|
-
}
|
|
12
|
-
s(v);
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
const prevSub = setActiveSub(void 0);
|
|
16
|
-
const prev = s();
|
|
17
|
-
setActiveSub(prevSub);
|
|
18
|
-
const next = v(prev);
|
|
19
|
-
if (equals && equals(prev, next)) return;
|
|
20
|
-
s(next);
|
|
21
|
-
}
|
|
22
|
-
function createSignal(initialValue, options) {
|
|
23
|
-
const s = signal(initialValue);
|
|
24
|
-
const getter = s;
|
|
25
|
-
const eq = options?.equals;
|
|
26
|
-
const setter = (v) => applySignalSet(s, v, eq);
|
|
27
|
-
return [getter, setter];
|
|
28
|
-
}
|
|
29
|
-
function createComputed(fn) {
|
|
30
|
-
return computed(fn);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export { createComputed, createSignal };
|
|
34
|
-
//# sourceMappingURL=chunk-HLM5BZZQ.js.map
|
|
35
|
-
//# sourceMappingURL=chunk-HLM5BZZQ.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/signal.ts","../src/reactive/computed.ts"],"names":["prevSub","prev","createRawSignal","rawComputed"],"mappings":";;;AAoEA,SAAS,cAAA,CACP,CAAA,EACA,CAAA,EACA,MAAA,EACM;AACN,EAAA,IAAI,OAAO,MAAM,UAAA,EAAY;AAC3B,IAAA,IAAI,MAAA,EAAQ;AAEV,MAAA,MAAMA,QAAAA,GAAU,aAAa,MAAS,CAAA;AACtC,MAAA,MAAMC,QAAO,CAAA,EAAE;AACf,MAAA,YAAA,CAAaD,QAAO,CAAA;AACpB,MAAA,IAAI,MAAA,CAAOC,KAAAA,EAAM,CAAC,CAAA,EAAG;AAAA,IACvB;AACA,IAAA,CAAA,CAAE,CAAC,CAAA;AACH,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,OAAA,GAAU,aAAa,MAAS,CAAA;AACtC,EAAA,MAAM,OAAO,CAAA,EAAE;AACf,EAAA,YAAA,CAAa,OAAO,CAAA;AACpB,EAAA,MAAM,IAAA,GAAQ,EAAqB,IAAI,CAAA;AACvC,EAAA,IAAI,MAAA,IAAU,MAAA,CAAO,IAAA,EAAM,IAAI,CAAA,EAAG;AAClC,EAAA,CAAA,CAAE,IAAI,CAAA;AACR;AAqBO,SAAS,YAAA,CAAgB,cAAiB,OAAA,EAA0E;AACzH,EAAA,MAAM,CAAA,GAAIC,OAAmB,YAAY,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,CAAA;AACf,EAAA,MAAM,KAAK,OAAA,EAAS,MAAA;AACpB,EAAA,MAAM,SAA0B,CAAC,CAAA,KAA4B,cAAA,CAAe,CAAA,EAAG,GAAG,EAAE,CAAA;AAEpF,EAAA,OAAO,CAAC,QAAQ,MAAM,CAAA;AACxB;AC/EO,SAAS,eAAkB,EAAA,EAAuC;AACvE,EAAA,OAAOC,SAAY,EAAE,CAAA;AACvB","file":"chunk-HLM5BZZQ.js","sourcesContent":["/**\n * Forma Reactive - Signal\n *\n * Fine-grained reactive primitive backed by alien-signals.\n * API: createSignal returns [getter, setter] tuple following SolidJS conventions.\n *\n * TC39 Signals equivalent: Signal.State\n */\n\nimport { signal as createRawSignal, setActiveSub } from 'alien-signals';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** A function that reads the current value of a signal. */\nexport type SignalGetter<T> = () => T;\n/** A function that updates a signal — accepts a value or an updater function. */\nexport type SignalSetter<T> = (v: T | ((prev: T) => T)) => void;\n\nexport interface SignalOptions<T> {\n /** Debug name — attached to getter in dev mode for devtools inspection. */\n name?: string;\n /**\n * Custom equality check. When provided, the setter will read the current\n * value and only update the signal if `equals(prev, next)` returns `false`.\n *\n * Default: none (alien-signals uses strict inequality `!==` internally).\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n *\n * setPos({ x: 0, y: 0 }); // skipped — equals returns true\n * setPos({ x: 1, y: 0 }); // applied — equals returns false\n * ```\n */\n equals?: (prev: T, next: T) => boolean;\n}\n\n/**\n * Wrap a value so the setter treats it as a literal value, not a functional updater.\n *\n * When `T` is itself a function type, passing a function to the setter is\n * ambiguous -- it looks like a functional update (`prev => next`). Use\n * `value()` to disambiguate:\n *\n * ```ts\n * const [getFn, setFn] = createSignal<() => void>(() => console.log('a'));\n *\n * // BUG: interpreted as a functional update -- calls the arrow with prev\n * // setFn(() => console.log('b'));\n *\n * // Correct: wraps in a thunk so the setter stores it as-is\n * setFn(value(() => console.log('b')));\n * ```\n */\nexport function value<T>(v: T): () => T {\n return () => v;\n}\n\ntype RawSignal<T> = {\n (): T;\n (value: T): void;\n};\n\nfunction applySignalSet<T>(\n s: RawSignal<T>,\n v: T | ((prev: T) => T),\n equals?: (prev: T, next: T) => boolean,\n): void {\n if (typeof v !== 'function') {\n if (equals) {\n // Read current value without tracking\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n if (equals(prev, v)) return; // skip — values are equal\n }\n s(v);\n return;\n }\n\n // Functional update: read prev without tracking\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n const next = (v as (prev: T) => T)(prev);\n if (equals && equals(prev, next)) return; // skip — values are equal\n s(next);\n}\n\n/**\n * Create a reactive signal.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * console.log(count()); // 0\n * setCount(1);\n * setCount(prev => prev + 1);\n * ```\n *\n * With custom equality:\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n * ```\n */\nexport function createSignal<T>(initialValue: T, options?: SignalOptions<T>): [get: SignalGetter<T>, set: SignalSetter<T>] {\n const s = createRawSignal<T>(initialValue) as RawSignal<T>;\n const getter = s as unknown as SignalGetter<T>;\n const eq = options?.equals;\n const setter: SignalSetter<T> = (v: T | ((prev: T) => T)) => applySignalSet(s, v, eq);\n\n return [getter, setter];\n}\n","/**\n * Forma Reactive - Computed\n *\n * Lazy, cached derived value that participates in the reactive graph.\n * Backed by alien-signals for automatic dependency tracking\n * and cache invalidation.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { computed as rawComputed } from 'alien-signals';\n\n/**\n * Create a lazy, cached computed value.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), this is a lazy cached derivation — equivalent to\n * SolidJS's createMemo. Both createComputed and createMemo in FormaJS\n * are identical.\n *\n * The getter receives the previous value as an argument, enabling\n * efficient diffing patterns without a separate signal:\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createComputed(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n *\n * With previous value (for diffing):\n *\n * ```ts\n * const changes = createComputed((prev) => {\n * const next = items();\n * if (prev) console.log(`changed from ${prev.length} to ${next.length} items`);\n * return next;\n * });\n * ```\n */\nexport function createComputed<T>(fn: (previousValue?: T) => T): () => T {\n return rawComputed(fn);\n}\n"]}
|
package/dist/chunk-QLPCVK7C.cjs
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var alienSignals = require('alien-signals');
|
|
4
|
-
|
|
5
|
-
// src/reactive/signal.ts
|
|
6
|
-
function applySignalSet(s, v, equals) {
|
|
7
|
-
if (typeof v !== "function") {
|
|
8
|
-
if (equals) {
|
|
9
|
-
const prevSub2 = alienSignals.setActiveSub(void 0);
|
|
10
|
-
const prev2 = s();
|
|
11
|
-
alienSignals.setActiveSub(prevSub2);
|
|
12
|
-
if (equals(prev2, v)) return;
|
|
13
|
-
}
|
|
14
|
-
s(v);
|
|
15
|
-
return;
|
|
16
|
-
}
|
|
17
|
-
const prevSub = alienSignals.setActiveSub(void 0);
|
|
18
|
-
const prev = s();
|
|
19
|
-
alienSignals.setActiveSub(prevSub);
|
|
20
|
-
const next = v(prev);
|
|
21
|
-
if (equals && equals(prev, next)) return;
|
|
22
|
-
s(next);
|
|
23
|
-
}
|
|
24
|
-
function createSignal(initialValue, options) {
|
|
25
|
-
const s = alienSignals.signal(initialValue);
|
|
26
|
-
const getter = s;
|
|
27
|
-
const eq = options?.equals;
|
|
28
|
-
const setter = (v) => applySignalSet(s, v, eq);
|
|
29
|
-
return [getter, setter];
|
|
30
|
-
}
|
|
31
|
-
function createComputed(fn) {
|
|
32
|
-
return alienSignals.computed(fn);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
exports.createComputed = createComputed;
|
|
36
|
-
exports.createSignal = createSignal;
|
|
37
|
-
//# sourceMappingURL=chunk-QLPCVK7C.cjs.map
|
|
38
|
-
//# sourceMappingURL=chunk-QLPCVK7C.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reactive/signal.ts","../src/reactive/computed.ts"],"names":["prevSub","setActiveSub","prev","createRawSignal","rawComputed"],"mappings":";;;;;AAoEA,SAAS,cAAA,CACP,CAAA,EACA,CAAA,EACA,MAAA,EACM;AACN,EAAA,IAAI,OAAO,MAAM,UAAA,EAAY;AAC3B,IAAA,IAAI,MAAA,EAAQ;AAEV,MAAA,MAAMA,QAAAA,GAAUC,0BAAa,MAAS,CAAA;AACtC,MAAA,MAAMC,QAAO,CAAA,EAAE;AACf,MAAAD,yBAAA,CAAaD,QAAO,CAAA;AACpB,MAAA,IAAI,MAAA,CAAOE,KAAAA,EAAM,CAAC,CAAA,EAAG;AAAA,IACvB;AACA,IAAA,CAAA,CAAE,CAAC,CAAA;AACH,IAAA;AAAA,EACF;AAGA,EAAA,MAAM,OAAA,GAAUD,0BAAa,MAAS,CAAA;AACtC,EAAA,MAAM,OAAO,CAAA,EAAE;AACf,EAAAA,yBAAA,CAAa,OAAO,CAAA;AACpB,EAAA,MAAM,IAAA,GAAQ,EAAqB,IAAI,CAAA;AACvC,EAAA,IAAI,MAAA,IAAU,MAAA,CAAO,IAAA,EAAM,IAAI,CAAA,EAAG;AAClC,EAAA,CAAA,CAAE,IAAI,CAAA;AACR;AAqBO,SAAS,YAAA,CAAgB,cAAiB,OAAA,EAA0E;AACzH,EAAA,MAAM,CAAA,GAAIE,oBAAmB,YAAY,CAAA;AACzC,EAAA,MAAM,MAAA,GAAS,CAAA;AACf,EAAA,MAAM,KAAK,OAAA,EAAS,MAAA;AACpB,EAAA,MAAM,SAA0B,CAAC,CAAA,KAA4B,cAAA,CAAe,CAAA,EAAG,GAAG,EAAE,CAAA;AAEpF,EAAA,OAAO,CAAC,QAAQ,MAAM,CAAA;AACxB;AC/EO,SAAS,eAAkB,EAAA,EAAuC;AACvE,EAAA,OAAOC,sBAAY,EAAE,CAAA;AACvB","file":"chunk-QLPCVK7C.cjs","sourcesContent":["/**\n * Forma Reactive - Signal\n *\n * Fine-grained reactive primitive backed by alien-signals.\n * API: createSignal returns [getter, setter] tuple following SolidJS conventions.\n *\n * TC39 Signals equivalent: Signal.State\n */\n\nimport { signal as createRawSignal, setActiveSub } from 'alien-signals';\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** A function that reads the current value of a signal. */\nexport type SignalGetter<T> = () => T;\n/** A function that updates a signal — accepts a value or an updater function. */\nexport type SignalSetter<T> = (v: T | ((prev: T) => T)) => void;\n\nexport interface SignalOptions<T> {\n /** Debug name — attached to getter in dev mode for devtools inspection. */\n name?: string;\n /**\n * Custom equality check. When provided, the setter will read the current\n * value and only update the signal if `equals(prev, next)` returns `false`.\n *\n * Default: none (alien-signals uses strict inequality `!==` internally).\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n *\n * setPos({ x: 0, y: 0 }); // skipped — equals returns true\n * setPos({ x: 1, y: 0 }); // applied — equals returns false\n * ```\n */\n equals?: (prev: T, next: T) => boolean;\n}\n\n/**\n * Wrap a value so the setter treats it as a literal value, not a functional updater.\n *\n * When `T` is itself a function type, passing a function to the setter is\n * ambiguous -- it looks like a functional update (`prev => next`). Use\n * `value()` to disambiguate:\n *\n * ```ts\n * const [getFn, setFn] = createSignal<() => void>(() => console.log('a'));\n *\n * // BUG: interpreted as a functional update -- calls the arrow with prev\n * // setFn(() => console.log('b'));\n *\n * // Correct: wraps in a thunk so the setter stores it as-is\n * setFn(value(() => console.log('b')));\n * ```\n */\nexport function value<T>(v: T): () => T {\n return () => v;\n}\n\ntype RawSignal<T> = {\n (): T;\n (value: T): void;\n};\n\nfunction applySignalSet<T>(\n s: RawSignal<T>,\n v: T | ((prev: T) => T),\n equals?: (prev: T, next: T) => boolean,\n): void {\n if (typeof v !== 'function') {\n if (equals) {\n // Read current value without tracking\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n if (equals(prev, v)) return; // skip — values are equal\n }\n s(v);\n return;\n }\n\n // Functional update: read prev without tracking\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n const next = (v as (prev: T) => T)(prev);\n if (equals && equals(prev, next)) return; // skip — values are equal\n s(next);\n}\n\n/**\n * Create a reactive signal.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * console.log(count()); // 0\n * setCount(1);\n * setCount(prev => prev + 1);\n * ```\n *\n * With custom equality:\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n * ```\n */\nexport function createSignal<T>(initialValue: T, options?: SignalOptions<T>): [get: SignalGetter<T>, set: SignalSetter<T>] {\n const s = createRawSignal<T>(initialValue) as RawSignal<T>;\n const getter = s as unknown as SignalGetter<T>;\n const eq = options?.equals;\n const setter: SignalSetter<T> = (v: T | ((prev: T) => T)) => applySignalSet(s, v, eq);\n\n return [getter, setter];\n}\n","/**\n * Forma Reactive - Computed\n *\n * Lazy, cached derived value that participates in the reactive graph.\n * Backed by alien-signals for automatic dependency tracking\n * and cache invalidation.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { computed as rawComputed } from 'alien-signals';\n\n/**\n * Create a lazy, cached computed value.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), this is a lazy cached derivation — equivalent to\n * SolidJS's createMemo. Both createComputed and createMemo in FormaJS\n * are identical.\n *\n * The getter receives the previous value as an argument, enabling\n * efficient diffing patterns without a separate signal:\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createComputed(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n *\n * With previous value (for diffing):\n *\n * ```ts\n * const changes = createComputed((prev) => {\n * const next = items();\n * if (prev) console.log(`changed from ${prev.length} to ${next.length} items`);\n * return next;\n * });\n * ```\n */\nexport function createComputed<T>(fn: (previousValue?: T) => T): () => T {\n return rawComputed(fn);\n}\n"]}
|