@fictjs/runtime 0.3.0 → 0.5.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/dist/advanced.cjs +10 -8
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +4 -3
- package/dist/advanced.d.ts +4 -3
- package/dist/advanced.js +10 -8
- package/dist/advanced.js.map +1 -1
- package/dist/{chunk-TWELIZRY.js → chunk-5AA7HP4S.js} +5 -3
- package/dist/{chunk-TWELIZRY.js.map → chunk-5AA7HP4S.js.map} +1 -1
- package/dist/chunk-6SOPF5LZ.cjs +2363 -0
- package/dist/chunk-6SOPF5LZ.cjs.map +1 -0
- package/dist/{chunk-SO6X7G5S.js → chunk-BQG7VEBY.js} +501 -1880
- package/dist/chunk-BQG7VEBY.js.map +1 -0
- package/dist/chunk-FKDMDAUR.js +2363 -0
- package/dist/chunk-FKDMDAUR.js.map +1 -0
- package/dist/{chunk-L4DIV3RC.cjs → chunk-GHUV2FLD.cjs} +9 -7
- package/dist/chunk-GHUV2FLD.cjs.map +1 -0
- package/dist/{chunk-XLIZJMMJ.js → chunk-KKKYW54Z.js} +8 -6
- package/dist/{chunk-XLIZJMMJ.js.map → chunk-KKKYW54Z.js.map} +1 -1
- package/dist/{chunk-M2TSXZ4C.cjs → chunk-KYLNC4CD.cjs} +18 -16
- package/dist/chunk-KYLNC4CD.cjs.map +1 -0
- package/dist/chunk-TKWN42TA.cjs +2259 -0
- package/dist/chunk-TKWN42TA.cjs.map +1 -0
- package/dist/{context-B25xyQrJ.d.cts → context-CTBE00S_.d.cts} +1 -1
- package/dist/{context-CGdP7_Jb.d.ts → context-lkLhbkFJ.d.ts} +1 -1
- package/dist/{effect-D6kaLM2-.d.cts → effect-BpSNEJJz.d.cts} +7 -67
- package/dist/{effect-D6kaLM2-.d.ts → effect-BpSNEJJz.d.ts} +7 -67
- package/dist/index.cjs +40 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.dev.js +92 -4
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +19 -17
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +189 -202
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +13 -23
- package/dist/internal.d.ts +13 -23
- package/dist/internal.js +195 -208
- package/dist/internal.js.map +1 -1
- package/dist/loader.cjs +280 -0
- package/dist/loader.cjs.map +1 -0
- package/dist/loader.d.cts +57 -0
- package/dist/loader.d.ts +57 -0
- package/dist/loader.js +280 -0
- package/dist/loader.js.map +1 -0
- package/dist/{props-BIfromL0.d.cts → props-XTHYD19o.d.cts} +13 -2
- package/dist/{props-BEgIVMRx.d.ts → props-x-HbI-jX.d.ts} +13 -2
- package/dist/resume-BrAkmSTY.d.cts +79 -0
- package/dist/resume-Dx8_l72o.d.ts +79 -0
- package/dist/{scope-CzNkn587.d.ts → scope-CdbGmsFf.d.ts} +1 -1
- package/dist/{scope-Cx_3CjIZ.d.cts → scope-DfcP9I-A.d.cts} +1 -1
- package/dist/signal-C4ISF17w.d.cts +66 -0
- package/dist/signal-C4ISF17w.d.ts +66 -0
- package/package.json +8 -3
- package/src/binding.ts +254 -5
- package/src/dom.ts +103 -5
- package/src/hooks.ts +15 -2
- package/src/hydration.ts +75 -0
- package/src/internal.ts +34 -2
- package/src/list-helpers.ts +113 -12
- package/src/loader.ts +437 -0
- package/src/node-ops.ts +65 -0
- package/src/resume.ts +517 -0
- package/src/store.ts +8 -0
- package/dist/chunk-ID3WBWNO.cjs +0 -3638
- package/dist/chunk-ID3WBWNO.cjs.map +0 -1
- package/dist/chunk-L4DIV3RC.cjs.map +0 -1
- package/dist/chunk-M2TSXZ4C.cjs.map +0 -1
- package/dist/chunk-SO6X7G5S.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -2,40 +2,42 @@ import {
|
|
|
2
2
|
createContext,
|
|
3
3
|
hasContext,
|
|
4
4
|
useContext
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-KKKYW54Z.js";
|
|
6
6
|
import {
|
|
7
7
|
Fragment,
|
|
8
|
-
|
|
9
|
-
createEffect,
|
|
8
|
+
batch,
|
|
10
9
|
createElement,
|
|
11
|
-
createMemo,
|
|
12
10
|
createPortal,
|
|
11
|
+
insertNodesBefore,
|
|
12
|
+
keyed,
|
|
13
|
+
mergeProps,
|
|
14
|
+
prop,
|
|
15
|
+
removeNodes,
|
|
16
|
+
render,
|
|
17
|
+
startTransition,
|
|
18
|
+
toNodeArray,
|
|
19
|
+
untrack,
|
|
20
|
+
useDeferredValue,
|
|
21
|
+
useTransition
|
|
22
|
+
} from "./chunk-BQG7VEBY.js";
|
|
23
|
+
import {
|
|
24
|
+
createEffect,
|
|
25
|
+
createMemo,
|
|
13
26
|
createRoot,
|
|
14
27
|
createRootContext,
|
|
15
28
|
destroyRoot,
|
|
16
29
|
flushOnMount,
|
|
17
30
|
getCurrentRoot,
|
|
18
31
|
handleError,
|
|
19
|
-
insertNodesBefore,
|
|
20
|
-
keyed,
|
|
21
|
-
mergeProps,
|
|
22
32
|
onCleanup,
|
|
23
33
|
onDestroy,
|
|
24
34
|
onMount,
|
|
25
35
|
popRoot,
|
|
26
|
-
prop,
|
|
27
36
|
pushRoot,
|
|
28
37
|
registerErrorHandler,
|
|
29
38
|
registerSuspenseHandler,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
signal,
|
|
33
|
-
startTransition,
|
|
34
|
-
toNodeArray,
|
|
35
|
-
untrack2 as untrack,
|
|
36
|
-
useDeferredValue,
|
|
37
|
-
useTransition
|
|
38
|
-
} from "./chunk-SO6X7G5S.js";
|
|
39
|
+
signal
|
|
40
|
+
} from "./chunk-FKDMDAUR.js";
|
|
39
41
|
|
|
40
42
|
// src/ref.ts
|
|
41
43
|
function createRef() {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ref.ts","../src/error-boundary.ts","../src/suspense.ts"],"sourcesContent":["import type { RefObject } from './types'\n\n/**\n * Create a ref object for DOM element references.\n *\n * @returns A ref object with a `current` property initialized to `null`\n *\n * @example\n * ```tsx\n * import { createRef } from 'fict'\n *\n * function Component() {\n * const inputRef = createRef<HTMLInputElement>()\n *\n * $effect(() => {\n * inputRef.current?.focus()\n * })\n *\n * return <input ref={inputRef} />\n * }\n * ```\n */\nexport function createRef<T extends Element = HTMLElement>(): RefObject<T> {\n return { current: null }\n}\n","import { createElement } from './dom'\nimport { createEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n pushRoot,\n popRoot,\n registerErrorHandler,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport type { BaseProps, FictNode } from './types'\n\ninterface ErrorBoundaryProps extends BaseProps {\n fallback: FictNode | ((err: unknown, reset?: () => void) => FictNode)\n onError?: (err: unknown) => void\n resetKeys?: unknown | (() => unknown)\n}\n\nexport function ErrorBoundary(props: ErrorBoundaryProps): FictNode {\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:error-boundary')\n fragment.appendChild(marker)\n\n const hostRoot = getCurrentRoot()\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n let renderingFallback = false\n\n let reset = () => {}\n const toView = (err: unknown | null): FictNode | null => {\n if (err != null) {\n return typeof props.fallback === 'function'\n ? (props.fallback as (e: unknown, reset?: () => void) => FictNode)(err, reset)\n : props.fallback\n }\n return props.children ?? null\n }\n\n const renderValue = (value: FictNode | null) => {\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (value == null || value === false) {\n return\n }\n\n const root = createRootContext(hostRoot)\n const prev = pushRoot(root)\n let nodes: Node[] = []\n try {\n const output = createElement(value)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } catch (err) {\n popRoot(prev)\n destroyRoot(root)\n // Fall back immediately on render errors, avoid infinite recursion\n if (renderingFallback) {\n throw err\n }\n // nested errors. If fallback rendering also throws, we should NOT reset\n // the flag until we're sure no more recursion is happening.\n renderingFallback = true\n try {\n renderValue(toView(err))\n // Only reset if successful - if renderValue threw, we want to keep\n // renderingFallback = true to prevent infinite recursion\n renderingFallback = false\n props.onError?.(err)\n } catch (fallbackErr) {\n // Fallback rendering failed - keep renderingFallback = true\n // to prevent further attempts, then rethrow\n // If fallback fails, report both errors\n props.onError?.(err)\n throw fallbackErr\n }\n return\n }\n popRoot(prev)\n flushOnMount(root)\n\n cleanup = () => {\n destroyRoot(root)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n reset = () => {\n renderingFallback = false\n renderValue(toView(null))\n }\n\n renderValue(props.children ?? null)\n\n registerErrorHandler(err => {\n renderValue(toView(err))\n props.onError?.(err)\n return true\n })\n\n if (props.resetKeys !== undefined) {\n const isGetter =\n typeof props.resetKeys === 'function' && (props.resetKeys as () => unknown).length === 0\n const getter = isGetter ? (props.resetKeys as () => unknown) : undefined\n let prev = isGetter ? getter!() : props.resetKeys\n createEffect(() => {\n const next = getter ? getter() : props.resetKeys\n if (prev !== next) {\n prev = next\n renderValue(toView(null))\n }\n })\n }\n\n return fragment\n}\n","import { createElement } from './dom'\nimport { createEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n handleError,\n pushRoot,\n popRoot,\n registerSuspenseHandler,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport { createSignal } from './signal'\nimport type { BaseProps, FictNode, SuspenseToken } from './types'\n\nexport interface SuspenseProps extends BaseProps {\n fallback: FictNode | ((err?: unknown) => FictNode)\n onResolve?: () => void\n onReject?: (err: unknown) => void\n resetKeys?: unknown | (() => unknown)\n}\n\nexport interface SuspenseHandle {\n token: SuspenseToken\n resolve: () => void\n reject: (err: unknown) => void\n}\n\nexport function createSuspenseToken(): SuspenseHandle {\n let resolve!: () => void\n let reject!: (err: unknown) => void\n const promise = new Promise<void>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {\n token: {\n then: promise.then.bind(promise),\n },\n resolve,\n reject,\n }\n}\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as PromiseLike<unknown>).then === 'function'\n\nexport function Suspense(props: SuspenseProps): FictNode {\n const pending = createSignal(0)\n let resolvedOnce = false\n let epoch = 0\n const hostRoot = getCurrentRoot()\n\n const toFallback = (err?: unknown) =>\n typeof props.fallback === 'function'\n ? (props.fallback as (e?: unknown) => FictNode)(err)\n : props.fallback\n\n const renderView = (view: FictNode | null) => {\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (view == null || view === false) {\n return\n }\n\n const root = createRootContext(hostRoot)\n const prev = pushRoot(root)\n let nodes: Node[] = []\n try {\n const output = createElement(view)\n nodes = toNodeArray(output)\n // Suspended view: child threw a suspense token and was handled upstream.\n // Avoid replacing existing fallback content; tear down this attempt.\n const suspendedAttempt =\n root.suspended ||\n (nodes.length > 0 &&\n nodes.every(node => node instanceof Comment && (node as Comment).data === 'fict:suspend'))\n if (suspendedAttempt) {\n popRoot(prev)\n destroyRoot(root)\n return\n }\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } catch (err) {\n popRoot(prev)\n destroyRoot(root)\n if (!handleError(err, { source: 'render' }, hostRoot)) {\n throw err\n }\n return\n }\n popRoot(prev)\n flushOnMount(root)\n\n cleanup = () => {\n destroyRoot(root)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:suspense')\n fragment.appendChild(marker)\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const onResolveMaybe = () => {\n if (!resolvedOnce) {\n resolvedOnce = true\n props.onResolve?.()\n }\n }\n\n registerSuspenseHandler(token => {\n const tokenEpoch = epoch\n pending(pending() + 1)\n // Directly render fallback instead of using switchView to avoid\n // triggering the effect which would cause duplicate renders\n renderView(toFallback())\n\n const thenable = (token as SuspenseToken).then\n ? (token as SuspenseToken)\n : isThenable(token)\n ? token\n : null\n\n if (thenable) {\n thenable.then(\n () => {\n // This prevents stale token resolutions from affecting state after\n // a reset. The order is important: check epoch first, then update state.\n if (epoch !== tokenEpoch) {\n // Token is stale (from before a reset), ignore it completely\n return\n }\n // Use Math.max as a defensive measure - pending should never go below 0,\n // but this protects against edge cases where a token might resolve twice\n // or after the component has been reset.\n const newPending = Math.max(0, pending() - 1)\n pending(newPending)\n if (newPending === 0) {\n // Directly render children instead of using switchView\n renderView(props.children ?? null)\n onResolveMaybe()\n }\n },\n err => {\n // Same epoch check - ignore stale tokens\n if (epoch !== tokenEpoch) {\n return\n }\n const newPending = Math.max(0, pending() - 1)\n pending(newPending)\n props.onReject?.(err)\n if (!handleError(err, { source: 'render' }, hostRoot)) {\n throw err\n }\n },\n )\n return true\n }\n\n return false\n })\n\n // Initial render - render children directly\n // Note: This will be called synchronously during component creation.\n // If children suspend, the handler above will be called and switch to fallback.\n renderView(props.children ?? null)\n\n if (props.resetKeys !== undefined) {\n const isGetter =\n typeof props.resetKeys === 'function' && (props.resetKeys as () => unknown).length === 0\n const getter = isGetter ? (props.resetKeys as () => unknown) : undefined\n let prev = isGetter ? getter!() : props.resetKeys\n createEffect(() => {\n const next = getter ? getter() : props.resetKeys\n if (prev !== next) {\n prev = next\n epoch++\n pending(0)\n // Directly render children instead of using switchView\n renderView(props.children ?? null)\n }\n })\n }\n\n return fragment\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/ref.ts","../src/error-boundary.ts","../src/suspense.ts"],"sourcesContent":["import type { RefObject } from './types'\n\n/**\n * Create a ref object for DOM element references.\n *\n * @returns A ref object with a `current` property initialized to `null`\n *\n * @example\n * ```tsx\n * import { createRef } from 'fict'\n *\n * function Component() {\n * const inputRef = createRef<HTMLInputElement>()\n *\n * $effect(() => {\n * inputRef.current?.focus()\n * })\n *\n * return <input ref={inputRef} />\n * }\n * ```\n */\nexport function createRef<T extends Element = HTMLElement>(): RefObject<T> {\n return { current: null }\n}\n","import { createElement } from './dom'\nimport { createEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n pushRoot,\n popRoot,\n registerErrorHandler,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport type { BaseProps, FictNode } from './types'\n\ninterface ErrorBoundaryProps extends BaseProps {\n fallback: FictNode | ((err: unknown, reset?: () => void) => FictNode)\n onError?: (err: unknown) => void\n resetKeys?: unknown | (() => unknown)\n}\n\nexport function ErrorBoundary(props: ErrorBoundaryProps): FictNode {\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:error-boundary')\n fragment.appendChild(marker)\n\n const hostRoot = getCurrentRoot()\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n let renderingFallback = false\n\n let reset = () => {}\n const toView = (err: unknown | null): FictNode | null => {\n if (err != null) {\n return typeof props.fallback === 'function'\n ? (props.fallback as (e: unknown, reset?: () => void) => FictNode)(err, reset)\n : props.fallback\n }\n return props.children ?? null\n }\n\n const renderValue = (value: FictNode | null) => {\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (value == null || value === false) {\n return\n }\n\n const root = createRootContext(hostRoot)\n const prev = pushRoot(root)\n let nodes: Node[] = []\n try {\n const output = createElement(value)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } catch (err) {\n popRoot(prev)\n destroyRoot(root)\n // Fall back immediately on render errors, avoid infinite recursion\n if (renderingFallback) {\n throw err\n }\n // nested errors. If fallback rendering also throws, we should NOT reset\n // the flag until we're sure no more recursion is happening.\n renderingFallback = true\n try {\n renderValue(toView(err))\n // Only reset if successful - if renderValue threw, we want to keep\n // renderingFallback = true to prevent infinite recursion\n renderingFallback = false\n props.onError?.(err)\n } catch (fallbackErr) {\n // Fallback rendering failed - keep renderingFallback = true\n // to prevent further attempts, then rethrow\n // If fallback fails, report both errors\n props.onError?.(err)\n throw fallbackErr\n }\n return\n }\n popRoot(prev)\n flushOnMount(root)\n\n cleanup = () => {\n destroyRoot(root)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n reset = () => {\n renderingFallback = false\n renderValue(toView(null))\n }\n\n renderValue(props.children ?? null)\n\n registerErrorHandler(err => {\n renderValue(toView(err))\n props.onError?.(err)\n return true\n })\n\n if (props.resetKeys !== undefined) {\n const isGetter =\n typeof props.resetKeys === 'function' && (props.resetKeys as () => unknown).length === 0\n const getter = isGetter ? (props.resetKeys as () => unknown) : undefined\n let prev = isGetter ? getter!() : props.resetKeys\n createEffect(() => {\n const next = getter ? getter() : props.resetKeys\n if (prev !== next) {\n prev = next\n renderValue(toView(null))\n }\n })\n }\n\n return fragment\n}\n","import { createElement } from './dom'\nimport { createEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n handleError,\n pushRoot,\n popRoot,\n registerSuspenseHandler,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport { createSignal } from './signal'\nimport type { BaseProps, FictNode, SuspenseToken } from './types'\n\nexport interface SuspenseProps extends BaseProps {\n fallback: FictNode | ((err?: unknown) => FictNode)\n onResolve?: () => void\n onReject?: (err: unknown) => void\n resetKeys?: unknown | (() => unknown)\n}\n\nexport interface SuspenseHandle {\n token: SuspenseToken\n resolve: () => void\n reject: (err: unknown) => void\n}\n\nexport function createSuspenseToken(): SuspenseHandle {\n let resolve!: () => void\n let reject!: (err: unknown) => void\n const promise = new Promise<void>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {\n token: {\n then: promise.then.bind(promise),\n },\n resolve,\n reject,\n }\n}\n\nconst isThenable = (value: unknown): value is PromiseLike<unknown> =>\n typeof value === 'object' &&\n value !== null &&\n typeof (value as PromiseLike<unknown>).then === 'function'\n\nexport function Suspense(props: SuspenseProps): FictNode {\n const pending = createSignal(0)\n let resolvedOnce = false\n let epoch = 0\n const hostRoot = getCurrentRoot()\n\n const toFallback = (err?: unknown) =>\n typeof props.fallback === 'function'\n ? (props.fallback as (e?: unknown) => FictNode)(err)\n : props.fallback\n\n const renderView = (view: FictNode | null) => {\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (view == null || view === false) {\n return\n }\n\n const root = createRootContext(hostRoot)\n const prev = pushRoot(root)\n let nodes: Node[] = []\n try {\n const output = createElement(view)\n nodes = toNodeArray(output)\n // Suspended view: child threw a suspense token and was handled upstream.\n // Avoid replacing existing fallback content; tear down this attempt.\n const suspendedAttempt =\n root.suspended ||\n (nodes.length > 0 &&\n nodes.every(node => node instanceof Comment && (node as Comment).data === 'fict:suspend'))\n if (suspendedAttempt) {\n popRoot(prev)\n destroyRoot(root)\n return\n }\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } catch (err) {\n popRoot(prev)\n destroyRoot(root)\n if (!handleError(err, { source: 'render' }, hostRoot)) {\n throw err\n }\n return\n }\n popRoot(prev)\n flushOnMount(root)\n\n cleanup = () => {\n destroyRoot(root)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:suspense')\n fragment.appendChild(marker)\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const onResolveMaybe = () => {\n if (!resolvedOnce) {\n resolvedOnce = true\n props.onResolve?.()\n }\n }\n\n registerSuspenseHandler(token => {\n const tokenEpoch = epoch\n pending(pending() + 1)\n // Directly render fallback instead of using switchView to avoid\n // triggering the effect which would cause duplicate renders\n renderView(toFallback())\n\n const thenable = (token as SuspenseToken).then\n ? (token as SuspenseToken)\n : isThenable(token)\n ? token\n : null\n\n if (thenable) {\n thenable.then(\n () => {\n // This prevents stale token resolutions from affecting state after\n // a reset. The order is important: check epoch first, then update state.\n if (epoch !== tokenEpoch) {\n // Token is stale (from before a reset), ignore it completely\n return\n }\n // Use Math.max as a defensive measure - pending should never go below 0,\n // but this protects against edge cases where a token might resolve twice\n // or after the component has been reset.\n const newPending = Math.max(0, pending() - 1)\n pending(newPending)\n if (newPending === 0) {\n // Directly render children instead of using switchView\n renderView(props.children ?? null)\n onResolveMaybe()\n }\n },\n err => {\n // Same epoch check - ignore stale tokens\n if (epoch !== tokenEpoch) {\n return\n }\n const newPending = Math.max(0, pending() - 1)\n pending(newPending)\n props.onReject?.(err)\n if (!handleError(err, { source: 'render' }, hostRoot)) {\n throw err\n }\n },\n )\n return true\n }\n\n return false\n })\n\n // Initial render - render children directly\n // Note: This will be called synchronously during component creation.\n // If children suspend, the handler above will be called and switch to fallback.\n renderView(props.children ?? null)\n\n if (props.resetKeys !== undefined) {\n const isGetter =\n typeof props.resetKeys === 'function' && (props.resetKeys as () => unknown).length === 0\n const getter = isGetter ? (props.resetKeys as () => unknown) : undefined\n let prev = isGetter ? getter!() : props.resetKeys\n createEffect(() => {\n const next = getter ? getter() : props.resetKeys\n if (prev !== next) {\n prev = next\n epoch++\n pending(0)\n // Directly render children instead of using switchView\n renderView(props.children ?? null)\n }\n })\n }\n\n return fragment\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBO,SAAS,YAA2D;AACzE,SAAO,EAAE,SAAS,KAAK;AACzB;;;ACJO,SAAS,cAAc,OAAqC;AACjE,QAAM,WAAW,SAAS,uBAAuB;AACjD,QAAM,SAAS,SAAS,cAAc,qBAAqB;AAC3D,WAAS,YAAY,MAAM;AAE3B,QAAM,WAAW,eAAe;AAEhC,MAAI;AACJ,MAAI,cAAsB,CAAC;AAC3B,MAAI,oBAAoB;AAExB,MAAI,QAAQ,MAAM;AAAA,EAAC;AACnB,QAAM,SAAS,CAAC,QAAyC;AACvD,QAAI,OAAO,MAAM;AACf,aAAO,OAAO,MAAM,aAAa,aAC5B,MAAM,SAA0D,KAAK,KAAK,IAC3E,MAAM;AAAA,IACZ;AACA,WAAO,MAAM,YAAY;AAAA,EAC3B;AAEA,QAAM,cAAc,CAAC,UAA2B;AAC9C,QAAI,SAAS;AACX,cAAQ;AACR,gBAAU;AAAA,IACZ;AACA,QAAI,YAAY,QAAQ;AACtB,kBAAY,WAAW;AACvB,oBAAc,CAAC;AAAA,IACjB;AAEA,QAAI,SAAS,QAAQ,UAAU,OAAO;AACpC;AAAA,IACF;AAEA,UAAM,OAAO,kBAAkB,QAAQ;AACvC,UAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,QAAgB,CAAC;AACrB,QAAI;AACF,YAAM,SAAS,cAAc,KAAK;AAClC,cAAQ,YAAY,MAAM;AAC1B,YAAM,aAAa,OAAO;AAC1B,UAAI,YAAY;AACd,0BAAkB,YAAY,OAAO,MAAM;AAAA,MAC7C;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,IAAI;AACZ,kBAAY,IAAI;AAEhB,UAAI,mBAAmB;AACrB,cAAM;AAAA,MACR;AAGA,0BAAoB;AACpB,UAAI;AACF,oBAAY,OAAO,GAAG,CAAC;AAGvB,4BAAoB;AACpB,cAAM,UAAU,GAAG;AAAA,MACrB,SAAS,aAAa;AAIpB,cAAM,UAAU,GAAG;AACnB,cAAM;AAAA,MACR;AACA;AAAA,IACF;AACA,YAAQ,IAAI;AACZ,iBAAa,IAAI;AAEjB,cAAU,MAAM;AACd,kBAAY,IAAI;AAChB,kBAAY,KAAK;AAAA,IACnB;AACA,kBAAc;AAAA,EAChB;AAEA,UAAQ,MAAM;AACZ,wBAAoB;AACpB,gBAAY,OAAO,IAAI,CAAC;AAAA,EAC1B;AAEA,cAAY,MAAM,YAAY,IAAI;AAElC,uBAAqB,SAAO;AAC1B,gBAAY,OAAO,GAAG,CAAC;AACvB,UAAM,UAAU,GAAG;AACnB,WAAO;AAAA,EACT,CAAC;AAED,MAAI,MAAM,cAAc,QAAW;AACjC,UAAM,WACJ,OAAO,MAAM,cAAc,cAAe,MAAM,UAA4B,WAAW;AACzF,UAAM,SAAS,WAAY,MAAM,YAA8B;AAC/D,QAAI,OAAO,WAAW,OAAQ,IAAI,MAAM;AACxC,iBAAa,MAAM;AACjB,YAAM,OAAO,SAAS,OAAO,IAAI,MAAM;AACvC,UAAI,SAAS,MAAM;AACjB,eAAO;AACP,oBAAY,OAAO,IAAI,CAAC;AAAA,MAC1B;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;ACnGO,SAAS,sBAAsC;AACpD,MAAI;AACJ,MAAI;AACJ,QAAM,UAAU,IAAI,QAAc,CAAC,KAAK,QAAQ;AAC9C,cAAU;AACV,aAAS;AAAA,EACX,CAAC;AACD,SAAO;AAAA,IACL,OAAO;AAAA,MACL,MAAM,QAAQ,KAAK,KAAK,OAAO;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,aAAa,CAAC,UAClB,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAA+B,SAAS;AAE3C,SAAS,SAAS,OAAgC;AACvD,QAAM,UAAU,OAAa,CAAC;AAC9B,MAAI,eAAe;AACnB,MAAI,QAAQ;AACZ,QAAM,WAAW,eAAe;AAEhC,QAAM,aAAa,CAAC,QAClB,OAAO,MAAM,aAAa,aACrB,MAAM,SAAuC,GAAG,IACjD,MAAM;AAEZ,QAAM,aAAa,CAAC,SAA0B;AAC5C,QAAI,SAAS;AACX,cAAQ;AACR,gBAAU;AAAA,IACZ;AACA,QAAI,YAAY,QAAQ;AACtB,kBAAY,WAAW;AACvB,oBAAc,CAAC;AAAA,IACjB;AAEA,QAAI,QAAQ,QAAQ,SAAS,OAAO;AAClC;AAAA,IACF;AAEA,UAAM,OAAO,kBAAkB,QAAQ;AACvC,UAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,QAAgB,CAAC;AACrB,QAAI;AACF,YAAM,SAAS,cAAc,IAAI;AACjC,cAAQ,YAAY,MAAM;AAG1B,YAAM,mBACJ,KAAK,aACJ,MAAM,SAAS,KACd,MAAM,MAAM,UAAQ,gBAAgB,WAAY,KAAiB,SAAS,cAAc;AAC5F,UAAI,kBAAkB;AACpB,gBAAQ,IAAI;AACZ,oBAAY,IAAI;AAChB;AAAA,MACF;AACA,YAAM,aAAa,OAAO;AAC1B,UAAI,YAAY;AACd,0BAAkB,YAAY,OAAO,MAAM;AAAA,MAC7C;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,IAAI;AACZ,kBAAY,IAAI;AAChB,UAAI,CAAC,YAAY,KAAK,EAAE,QAAQ,SAAS,GAAG,QAAQ,GAAG;AACrD,cAAM;AAAA,MACR;AACA;AAAA,IACF;AACA,YAAQ,IAAI;AACZ,iBAAa,IAAI;AAEjB,cAAU,MAAM;AACd,kBAAY,IAAI;AAChB,kBAAY,KAAK;AAAA,IACnB;AACA,kBAAc;AAAA,EAChB;AAEA,QAAM,WAAW,SAAS,uBAAuB;AACjD,QAAM,SAAS,SAAS,cAAc,eAAe;AACrD,WAAS,YAAY,MAAM;AAC3B,MAAI;AACJ,MAAI,cAAsB,CAAC;AAE3B,QAAM,iBAAiB,MAAM;AAC3B,QAAI,CAAC,cAAc;AACjB,qBAAe;AACf,YAAM,YAAY;AAAA,IACpB;AAAA,EACF;AAEA,0BAAwB,WAAS;AAC/B,UAAM,aAAa;AACnB,YAAQ,QAAQ,IAAI,CAAC;AAGrB,eAAW,WAAW,CAAC;AAEvB,UAAM,WAAY,MAAwB,OACrC,QACD,WAAW,KAAK,IACd,QACA;AAEN,QAAI,UAAU;AACZ,eAAS;AAAA,QACP,MAAM;AAGJ,cAAI,UAAU,YAAY;AAExB;AAAA,UACF;AAIA,gBAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC;AAC5C,kBAAQ,UAAU;AAClB,cAAI,eAAe,GAAG;AAEpB,uBAAW,MAAM,YAAY,IAAI;AACjC,2BAAe;AAAA,UACjB;AAAA,QACF;AAAA,QACA,SAAO;AAEL,cAAI,UAAU,YAAY;AACxB;AAAA,UACF;AACA,gBAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,IAAI,CAAC;AAC5C,kBAAQ,UAAU;AAClB,gBAAM,WAAW,GAAG;AACpB,cAAI,CAAC,YAAY,KAAK,EAAE,QAAQ,SAAS,GAAG,QAAQ,GAAG;AACrD,kBAAM;AAAA,UACR;AAAA,QACF;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AAKD,aAAW,MAAM,YAAY,IAAI;AAEjC,MAAI,MAAM,cAAc,QAAW;AACjC,UAAM,WACJ,OAAO,MAAM,cAAc,cAAe,MAAM,UAA4B,WAAW;AACzF,UAAM,SAAS,WAAY,MAAM,YAA8B;AAC/D,QAAI,OAAO,WAAW,OAAQ,IAAI,MAAM;AACxC,iBAAa,MAAM;AACjB,YAAM,OAAO,SAAS,OAAO,IAAI,MAAM;AACvC,UAAI,SAAS,MAAM;AACjB,eAAO;AACP;AACA,gBAAQ,CAAC;AAET,mBAAW,MAAM,YAAY,IAAI;AAAA,MACnC;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;","names":[]}
|