@fictjs/runtime 0.2.0 → 0.2.1
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 +8 -8
- package/dist/advanced.js +3 -3
- package/dist/{chunk-BSUMPMKX.cjs → chunk-7EAEROZ5.cjs} +7 -7
- package/dist/{chunk-BSUMPMKX.cjs.map → chunk-7EAEROZ5.cjs.map} +1 -1
- package/dist/{chunk-QV5GOCR5.js → chunk-7TPCESQS.js} +2 -2
- package/dist/{chunk-5KXEEQUO.js → chunk-FOLRR3NZ.js} +1 -1
- package/dist/chunk-FOLRR3NZ.js.map +1 -0
- package/dist/{chunk-J74L7UYP.cjs → chunk-MWI3USXB.cjs} +1 -1
- package/dist/chunk-MWI3USXB.cjs.map +1 -0
- package/dist/{chunk-FG3M7EBL.js → chunk-VVNMIER7.js} +2 -2
- package/dist/{chunk-FG3M7EBL.js.map → chunk-VVNMIER7.js.map} +1 -1
- package/dist/{chunk-527QSKFM.cjs → chunk-Z5WRKD7Y.cjs} +16 -16
- package/dist/{chunk-527QSKFM.cjs.map → chunk-Z5WRKD7Y.cjs.map} +1 -1
- package/dist/index.cjs +46 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.dev.js +8 -12
- package/dist/index.dev.js.map +1 -1
- package/dist/index.js +10 -14
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +64 -42
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.js +32 -10
- package/dist/internal.js.map +1 -1
- package/package.json +1 -1
- package/src/context.ts +1 -1
- package/src/error-boundary.ts +9 -9
- package/src/signal.ts +2 -1
- package/src/store.ts +42 -20
- package/src/suspense.ts +0 -5
- package/dist/chunk-5KXEEQUO.js.map +0 -1
- package/dist/chunk-J74L7UYP.cjs.map +0 -1
- package/dist/jsx-runtime.d.cts +0 -671
- package/dist/jsx-runtime.d.ts +0 -671
- /package/dist/{chunk-QV5GOCR5.js.map → chunk-7TPCESQS.js.map} +0 -0
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
pushRoot,
|
|
11
11
|
removeNodes,
|
|
12
12
|
toNodeArray
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-FOLRR3NZ.js";
|
|
14
14
|
|
|
15
15
|
// src/context.ts
|
|
16
16
|
var contextStorage = /* @__PURE__ */ new WeakMap();
|
|
@@ -106,4 +106,4 @@ export {
|
|
|
106
106
|
useContext,
|
|
107
107
|
hasContext
|
|
108
108
|
};
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-VVNMIER7.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["/**\n * @fileoverview Context API for Fict\n *\n * Provides a way to pass data through the component tree without having to pass\n * props down manually at every level. Context is designed for:\n *\n * - SSR isolation (different request = different context values)\n * - Multi-instance support (multiple app roots with different values)\n * - Subtree scoping (override values in specific parts of the tree)\n *\n * ## Design Principles\n *\n * 1. **Reuses existing RootContext hierarchy** - Uses parent chain for value lookup,\n * consistent with handleError/handleSuspend mechanisms.\n *\n * 2. **Zero extra root creation overhead** - Provider doesn't create new root,\n * only mounts value on current root.\n *\n * 3. **Auto-aligned with insert/suspense boundaries** - Because they create child\n * roots that inherit parent, context values propagate correctly.\n *\n * ## Usage\n *\n * ```tsx\n * // Create context with default value\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Provide value to subtree\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <ThemedComponent />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume value\n * function ThemedComponent() {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>...</div>\n * }\n * ```\n *\n * @module\n */\n\nimport { createElement } from './dom'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n popRoot,\n pushRoot,\n type RootContext,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport { createRenderEffect } from './effect'\nimport type { BaseProps, FictNode } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context object created by createContext.\n * Contains the Provider component and serves as a key for context lookup.\n */\nexport interface Context<T> {\n /** Unique identifier for this context */\n readonly id: symbol\n /** Default value when no provider is found */\n readonly defaultValue: T\n /** Provider component for supplying context values */\n Provider: ContextProvider<T>\n /** Display name for debugging */\n displayName?: string\n}\n\n/**\n * Props for the Context Provider component\n */\nexport interface ProviderProps<T> extends BaseProps {\n /** The value to provide to the subtree */\n value: T\n}\n\n/**\n * Provider component type\n */\nexport type ContextProvider<T> = (props: ProviderProps<T>) => FictNode\n\n// ============================================================================\n// Internal Context Storage\n// ============================================================================\n\n/**\n * WeakMap to store context values per RootContext.\n * Using WeakMap ensures proper garbage collection when roots are destroyed.\n */\nconst contextStorage = new WeakMap<RootContext, Map<symbol, unknown>>()\n\n/**\n * Get the context map for a root, creating it if needed\n */\nfunction getContextMap(root: RootContext): Map<symbol, unknown> {\n let map = contextStorage.get(root)\n if (!map) {\n map = new Map()\n contextStorage.set(root, map)\n }\n return map\n}\n\n// ============================================================================\n// Context API\n// ============================================================================\n\n/**\n * Creates a new context with the given default value.\n *\n * Context provides a way to pass values through the component tree without\n * explicit props drilling. It's especially useful for:\n *\n * - Theme data\n * - Locale/i18n settings\n * - Authentication state\n * - Feature flags\n * - Any data that many components at different nesting levels need\n *\n * @param defaultValue - The value to use when no Provider is found above in the tree\n * @returns A context object with a Provider component\n *\n * @example\n * ```tsx\n * // Create a theme context\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Use the provider\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <Content />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume the context\n * function Content() {\n * const theme = useContext(ThemeContext)\n * return <div class={`theme-${theme}`}>Hello</div>\n * }\n * ```\n */\nexport function createContext<T>(defaultValue: T): Context<T> {\n const id = Symbol('fict.context')\n\n const context: Context<T> = {\n id,\n defaultValue,\n Provider: null as unknown as ContextProvider<T>,\n }\n\n // Create the Provider component\n context.Provider = function Provider(props: ProviderProps<T>): FictNode {\n const hostRoot = getCurrentRoot()\n\n // Create a child root for the provider's subtree\n // This establishes the provider boundary - children will look up from here\n const providerRoot = createRootContext(hostRoot)\n\n // Store the context value on this root\n const contextMap = getContextMap(providerRoot)\n contextMap.set(id, props.value)\n\n // Create DOM structure\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:ctx')\n fragment.appendChild(marker)\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const renderChildren = (children: FictNode) => {\n // Cleanup previous render\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (children == null || children === false) {\n return\n }\n\n const prev = pushRoot(providerRoot)\n let nodes: Node[] = []\n try {\n const output = createElement(children)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } finally {\n popRoot(prev)\n flushOnMount(providerRoot)\n }\n\n cleanup = () => {\n destroyRoot(providerRoot)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n // Initial render\n createRenderEffect(() => {\n // Update context value on re-render (if value prop changes reactively)\n contextMap.set(id, props.value)\n renderChildren(props.children)\n })\n\n return fragment\n }\n\n return context\n}\n\n/**\n * Reads the current value of a context.\n *\n * useContext looks up through the RootContext parent chain to find the\n * nearest Provider for this context. If no Provider is found, returns\n * the context's default value.\n *\n * @param context - The context object created by createContext\n * @returns The current context value\n *\n * @example\n * ```tsx\n * const ThemeContext = createContext('light')\n *\n * function ThemedButton() {\n * const theme = useContext(ThemeContext)\n * return <button class={theme === 'dark' ? 'btn-dark' : 'btn-light'}>Click</button>\n * }\n * ```\n */\nexport function useContext<T>(context: Context<T>): T {\n let root = getCurrentRoot()\n\n // Walk up the parent chain looking for the context value\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return contextMap.get(context.id) as T\n }\n root = root.parent\n }\n\n // No provider found, return default value\n return context.defaultValue\n}\n\n/**\n * Checks if a context value is currently provided in the tree.\n *\n * Useful for conditional behavior when a provider may or may not exist.\n *\n * @param context - The context object to check\n * @returns true if a Provider exists above in the tree\n *\n * @example\n * ```tsx\n * function OptionalTheme() {\n * if (hasContext(ThemeContext)) {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>Themed content</div>\n * }\n * return <div>Default content</div>\n * }\n * ```\n */\nexport function hasContext<T>(context: Context<T>): boolean {\n let root = getCurrentRoot()\n\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return true\n }\n root = root.parent\n }\n\n return false\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoGA,IAAM,iBAAiB,oBAAI,QAA2C;AAKtE,SAAS,cAAc,MAAyC;AAC9D,MAAI,MAAM,eAAe,IAAI,IAAI;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,mBAAe,IAAI,MAAM,GAAG;AAAA,EAC9B;AACA,SAAO;AACT;AA0CO,SAAS,cAAiB,cAA6B;AAC5D,QAAM,KAAK,OAAO,cAAc;AAEhC,QAAM,UAAsB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AAGA,UAAQ,WAAW,SAAS,SAAS,OAAmC;AACtE,UAAM,WAAW,eAAe;AAIhC,UAAM,eAAe,kBAAkB,QAAQ;AAG/C,UAAM,aAAa,cAAc,YAAY;AAC7C,eAAW,IAAI,IAAI,MAAM,KAAK;AAG9B,UAAM,WAAW,SAAS,uBAAuB;AACjD,UAAM,SAAS,SAAS,cAAc,UAAU;AAChD,aAAS,YAAY,MAAM;AAE3B,QAAI;AACJ,QAAI,cAAsB,CAAC;AAE3B,UAAM,iBAAiB,CAAC,aAAuB;AAE7C,UAAI,SAAS;AACX,gBAAQ;AACR,kBAAU;AAAA,MACZ;AACA,UAAI,YAAY,QAAQ;AACtB,oBAAY,WAAW;AACvB,sBAAc,CAAC;AAAA,MACjB;AAEA,UAAI,YAAY,QAAQ,aAAa,OAAO;AAC1C;AAAA,MACF;AAEA,YAAM,OAAO,SAAS,YAAY;AAClC,UAAI,QAAgB,CAAC;AACrB,UAAI;AACF,cAAM,SAAS,cAAc,QAAQ;AACrC,gBAAQ,YAAY,MAAM;AAC1B,cAAM,aAAa,OAAO;AAC1B,YAAI,YAAY;AACd,4BAAkB,YAAY,OAAO,MAAM;AAAA,QAC7C;AAAA,MACF,UAAE;AACA,gBAAQ,IAAI;AACZ,qBAAa,YAAY;AAAA,MAC3B;AAEA,gBAAU,MAAM;AACd,oBAAY,YAAY;AACxB,oBAAY,KAAK;AAAA,MACnB;AACA,oBAAc;AAAA,IAChB;AAGA,uBAAmB,MAAM;AAEvB,iBAAW,IAAI,IAAI,MAAM,KAAK;AAC9B,qBAAe,MAAM,QAAQ;AAAA,IAC/B,CAAC;AAED,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAsBO,SAAS,WAAc,SAAwB;AACpD,MAAI,OAAO,eAAe;AAG1B,SAAO,MAAM;AACX,UAAM,aAAa,eAAe,IAAI,IAAI;AAC1C,QAAI,cAAc,WAAW,IAAI,QAAQ,EAAE,GAAG;AAC5C,aAAO,WAAW,IAAI,QAAQ,EAAE;AAAA,IAClC;AACA,WAAO,KAAK;AAAA,EACd;AAGA,SAAO,QAAQ;AACjB;AAqBO,SAAS,WAAc,SAA8B;AAC1D,MAAI,OAAO,eAAe;AAE1B,SAAO,MAAM;AACX,UAAM,aAAa,eAAe,IAAI,IAAI;AAC1C,QAAI,cAAc,WAAW,IAAI,QAAQ,EAAE,GAAG;AAC5C,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AAAA,EACd;AAEA,SAAO;AACT;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/context.ts"],"sourcesContent":["/**\n * @fileoverview Context API for Fict\n *\n * Provides a way to pass data through the component tree without having to pass\n * props down manually at every level. Context is designed for:\n *\n * - SSR isolation (different request = different context values)\n * - Multi-instance support (multiple app roots with different values)\n * - Subtree scoping (override values in specific parts of the tree)\n *\n * ## Design Principles\n *\n * 1. **Reuses existing RootContext hierarchy** - Uses parent chain for value lookup,\n * consistent with handleError/handleSuspend mechanisms.\n *\n * 2. **Zero extra root creation overhead** - Provider doesn't create new root,\n * only mounts value on current root.\n *\n * 3. **Auto-aligned with insert/suspense boundaries** - Because they create child\n * roots that inherit parent, context values propagate correctly.\n *\n * ## Usage\n *\n * ```tsx\n * // Create context with default value\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Provide value to subtree\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <ThemedComponent />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume value\n * function ThemedComponent() {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>...</div>\n * }\n * ```\n *\n * @module\n */\n\nimport { createElement } from './dom'\nimport { createRenderEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n popRoot,\n pushRoot,\n type RootContext,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport type { BaseProps, FictNode } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context object created by createContext.\n * Contains the Provider component and serves as a key for context lookup.\n */\nexport interface Context<T> {\n /** Unique identifier for this context */\n readonly id: symbol\n /** Default value when no provider is found */\n readonly defaultValue: T\n /** Provider component for supplying context values */\n Provider: ContextProvider<T>\n /** Display name for debugging */\n displayName?: string\n}\n\n/**\n * Props for the Context Provider component\n */\nexport interface ProviderProps<T> extends BaseProps {\n /** The value to provide to the subtree */\n value: T\n}\n\n/**\n * Provider component type\n */\nexport type ContextProvider<T> = (props: ProviderProps<T>) => FictNode\n\n// ============================================================================\n// Internal Context Storage\n// ============================================================================\n\n/**\n * WeakMap to store context values per RootContext.\n * Using WeakMap ensures proper garbage collection when roots are destroyed.\n */\nconst contextStorage = new WeakMap<RootContext, Map<symbol, unknown>>()\n\n/**\n * Get the context map for a root, creating it if needed\n */\nfunction getContextMap(root: RootContext): Map<symbol, unknown> {\n let map = contextStorage.get(root)\n if (!map) {\n map = new Map()\n contextStorage.set(root, map)\n }\n return map\n}\n\n// ============================================================================\n// Context API\n// ============================================================================\n\n/**\n * Creates a new context with the given default value.\n *\n * Context provides a way to pass values through the component tree without\n * explicit props drilling. It's especially useful for:\n *\n * - Theme data\n * - Locale/i18n settings\n * - Authentication state\n * - Feature flags\n * - Any data that many components at different nesting levels need\n *\n * @param defaultValue - The value to use when no Provider is found above in the tree\n * @returns A context object with a Provider component\n *\n * @example\n * ```tsx\n * // Create a theme context\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Use the provider\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <Content />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume the context\n * function Content() {\n * const theme = useContext(ThemeContext)\n * return <div class={`theme-${theme}`}>Hello</div>\n * }\n * ```\n */\nexport function createContext<T>(defaultValue: T): Context<T> {\n const id = Symbol('fict.context')\n\n const context: Context<T> = {\n id,\n defaultValue,\n Provider: null as unknown as ContextProvider<T>,\n }\n\n // Create the Provider component\n context.Provider = function Provider(props: ProviderProps<T>): FictNode {\n const hostRoot = getCurrentRoot()\n\n // Create a child root for the provider's subtree\n // This establishes the provider boundary - children will look up from here\n const providerRoot = createRootContext(hostRoot)\n\n // Store the context value on this root\n const contextMap = getContextMap(providerRoot)\n contextMap.set(id, props.value)\n\n // Create DOM structure\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:ctx')\n fragment.appendChild(marker)\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const renderChildren = (children: FictNode) => {\n // Cleanup previous render\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (children == null || children === false) {\n return\n }\n\n const prev = pushRoot(providerRoot)\n let nodes: Node[] = []\n try {\n const output = createElement(children)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } finally {\n popRoot(prev)\n flushOnMount(providerRoot)\n }\n\n cleanup = () => {\n destroyRoot(providerRoot)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n // Initial render\n createRenderEffect(() => {\n // Update context value on re-render (if value prop changes reactively)\n contextMap.set(id, props.value)\n renderChildren(props.children)\n })\n\n return fragment\n }\n\n return context\n}\n\n/**\n * Reads the current value of a context.\n *\n * useContext looks up through the RootContext parent chain to find the\n * nearest Provider for this context. If no Provider is found, returns\n * the context's default value.\n *\n * @param context - The context object created by createContext\n * @returns The current context value\n *\n * @example\n * ```tsx\n * const ThemeContext = createContext('light')\n *\n * function ThemedButton() {\n * const theme = useContext(ThemeContext)\n * return <button class={theme === 'dark' ? 'btn-dark' : 'btn-light'}>Click</button>\n * }\n * ```\n */\nexport function useContext<T>(context: Context<T>): T {\n let root = getCurrentRoot()\n\n // Walk up the parent chain looking for the context value\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return contextMap.get(context.id) as T\n }\n root = root.parent\n }\n\n // No provider found, return default value\n return context.defaultValue\n}\n\n/**\n * Checks if a context value is currently provided in the tree.\n *\n * Useful for conditional behavior when a provider may or may not exist.\n *\n * @param context - The context object to check\n * @returns true if a Provider exists above in the tree\n *\n * @example\n * ```tsx\n * function OptionalTheme() {\n * if (hasContext(ThemeContext)) {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>Themed content</div>\n * }\n * return <div>Default content</div>\n * }\n * ```\n */\nexport function hasContext<T>(context: Context<T>): boolean {\n let root = getCurrentRoot()\n\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return true\n }\n root = root.parent\n }\n\n return false\n}\n"],"mappings":";;;;;;;;;;;;;;;AAoGA,IAAM,iBAAiB,oBAAI,QAA2C;AAKtE,SAAS,cAAc,MAAyC;AAC9D,MAAI,MAAM,eAAe,IAAI,IAAI;AACjC,MAAI,CAAC,KAAK;AACR,UAAM,oBAAI,IAAI;AACd,mBAAe,IAAI,MAAM,GAAG;AAAA,EAC9B;AACA,SAAO;AACT;AA0CO,SAAS,cAAiB,cAA6B;AAC5D,QAAM,KAAK,OAAO,cAAc;AAEhC,QAAM,UAAsB;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,UAAU;AAAA,EACZ;AAGA,UAAQ,WAAW,SAAS,SAAS,OAAmC;AACtE,UAAM,WAAW,eAAe;AAIhC,UAAM,eAAe,kBAAkB,QAAQ;AAG/C,UAAM,aAAa,cAAc,YAAY;AAC7C,eAAW,IAAI,IAAI,MAAM,KAAK;AAG9B,UAAM,WAAW,SAAS,uBAAuB;AACjD,UAAM,SAAS,SAAS,cAAc,UAAU;AAChD,aAAS,YAAY,MAAM;AAE3B,QAAI;AACJ,QAAI,cAAsB,CAAC;AAE3B,UAAM,iBAAiB,CAAC,aAAuB;AAE7C,UAAI,SAAS;AACX,gBAAQ;AACR,kBAAU;AAAA,MACZ;AACA,UAAI,YAAY,QAAQ;AACtB,oBAAY,WAAW;AACvB,sBAAc,CAAC;AAAA,MACjB;AAEA,UAAI,YAAY,QAAQ,aAAa,OAAO;AAC1C;AAAA,MACF;AAEA,YAAM,OAAO,SAAS,YAAY;AAClC,UAAI,QAAgB,CAAC;AACrB,UAAI;AACF,cAAM,SAAS,cAAc,QAAQ;AACrC,gBAAQ,YAAY,MAAM;AAC1B,cAAM,aAAa,OAAO;AAC1B,YAAI,YAAY;AACd,4BAAkB,YAAY,OAAO,MAAM;AAAA,QAC7C;AAAA,MACF,UAAE;AACA,gBAAQ,IAAI;AACZ,qBAAa,YAAY;AAAA,MAC3B;AAEA,gBAAU,MAAM;AACd,oBAAY,YAAY;AACxB,oBAAY,KAAK;AAAA,MACnB;AACA,oBAAc;AAAA,IAChB;AAGA,uBAAmB,MAAM;AAEvB,iBAAW,IAAI,IAAI,MAAM,KAAK;AAC9B,qBAAe,MAAM,QAAQ;AAAA,IAC/B,CAAC;AAED,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAsBO,SAAS,WAAc,SAAwB;AACpD,MAAI,OAAO,eAAe;AAG1B,SAAO,MAAM;AACX,UAAM,aAAa,eAAe,IAAI,IAAI;AAC1C,QAAI,cAAc,WAAW,IAAI,QAAQ,EAAE,GAAG;AAC5C,aAAO,WAAW,IAAI,QAAQ,EAAE;AAAA,IAClC;AACA,WAAO,KAAK;AAAA,EACd;AAGA,SAAO,QAAQ;AACjB;AAqBO,SAAS,WAAc,SAA8B;AAC1D,MAAI,OAAO,eAAe;AAE1B,SAAO,MAAM;AACX,UAAM,aAAa,eAAe,IAAI,IAAI;AAC1C,QAAI,cAAc,WAAW,IAAI,QAAQ,EAAE,GAAG;AAC5C,aAAO;AAAA,IACT;AACA,WAAO,KAAK;AAAA,EACd;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
var
|
|
13
|
+
var _chunkMWI3USXBcjs = require('./chunk-MWI3USXB.cjs');
|
|
14
14
|
|
|
15
15
|
// src/context.ts
|
|
16
16
|
var contextStorage = /* @__PURE__ */ new WeakMap();
|
|
@@ -30,8 +30,8 @@ function createContext(defaultValue) {
|
|
|
30
30
|
Provider: null
|
|
31
31
|
};
|
|
32
32
|
context.Provider = function Provider(props) {
|
|
33
|
-
const hostRoot =
|
|
34
|
-
const providerRoot =
|
|
33
|
+
const hostRoot = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
|
|
34
|
+
const providerRoot = _chunkMWI3USXBcjs.createRootContext.call(void 0, hostRoot);
|
|
35
35
|
const contextMap = getContextMap(providerRoot);
|
|
36
36
|
contextMap.set(id, props.value);
|
|
37
37
|
const fragment = document.createDocumentFragment();
|
|
@@ -45,32 +45,32 @@ function createContext(defaultValue) {
|
|
|
45
45
|
cleanup = void 0;
|
|
46
46
|
}
|
|
47
47
|
if (activeNodes.length) {
|
|
48
|
-
|
|
48
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, activeNodes);
|
|
49
49
|
activeNodes = [];
|
|
50
50
|
}
|
|
51
51
|
if (children == null || children === false) {
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
|
-
const prev =
|
|
54
|
+
const prev = _chunkMWI3USXBcjs.pushRoot.call(void 0, providerRoot);
|
|
55
55
|
let nodes = [];
|
|
56
56
|
try {
|
|
57
|
-
const output =
|
|
58
|
-
nodes =
|
|
57
|
+
const output = _chunkMWI3USXBcjs.createElement.call(void 0, children);
|
|
58
|
+
nodes = _chunkMWI3USXBcjs.toNodeArray.call(void 0, output);
|
|
59
59
|
const parentNode = marker.parentNode;
|
|
60
60
|
if (parentNode) {
|
|
61
|
-
|
|
61
|
+
_chunkMWI3USXBcjs.insertNodesBefore.call(void 0, parentNode, nodes, marker);
|
|
62
62
|
}
|
|
63
63
|
} finally {
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
65
|
+
_chunkMWI3USXBcjs.flushOnMount.call(void 0, providerRoot);
|
|
66
66
|
}
|
|
67
67
|
cleanup = () => {
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, providerRoot);
|
|
69
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, nodes);
|
|
70
70
|
};
|
|
71
71
|
activeNodes = nodes;
|
|
72
72
|
};
|
|
73
|
-
|
|
73
|
+
_chunkMWI3USXBcjs.createRenderEffect.call(void 0, () => {
|
|
74
74
|
contextMap.set(id, props.value);
|
|
75
75
|
renderChildren(props.children);
|
|
76
76
|
});
|
|
@@ -79,7 +79,7 @@ function createContext(defaultValue) {
|
|
|
79
79
|
return context;
|
|
80
80
|
}
|
|
81
81
|
function useContext(context) {
|
|
82
|
-
let root =
|
|
82
|
+
let root = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
|
|
83
83
|
while (root) {
|
|
84
84
|
const contextMap = contextStorage.get(root);
|
|
85
85
|
if (contextMap && contextMap.has(context.id)) {
|
|
@@ -90,7 +90,7 @@ function useContext(context) {
|
|
|
90
90
|
return context.defaultValue;
|
|
91
91
|
}
|
|
92
92
|
function hasContext(context) {
|
|
93
|
-
let root =
|
|
93
|
+
let root = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
|
|
94
94
|
while (root) {
|
|
95
95
|
const contextMap = contextStorage.get(root);
|
|
96
96
|
if (contextMap && contextMap.has(context.id)) {
|
|
@@ -106,4 +106,4 @@ function hasContext(context) {
|
|
|
106
106
|
|
|
107
107
|
|
|
108
108
|
exports.createContext = createContext; exports.useContext = useContext; exports.hasContext = hasContext;
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
109
|
+
//# sourceMappingURL=chunk-Z5WRKD7Y.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/chunk-527QSKFM.cjs","../src/context.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/chunk-527QSKFM.cjs","sourcesContent":[null,"/**\n * @fileoverview Context API for Fict\n *\n * Provides a way to pass data through the component tree without having to pass\n * props down manually at every level. Context is designed for:\n *\n * - SSR isolation (different request = different context values)\n * - Multi-instance support (multiple app roots with different values)\n * - Subtree scoping (override values in specific parts of the tree)\n *\n * ## Design Principles\n *\n * 1. **Reuses existing RootContext hierarchy** - Uses parent chain for value lookup,\n * consistent with handleError/handleSuspend mechanisms.\n *\n * 2. **Zero extra root creation overhead** - Provider doesn't create new root,\n * only mounts value on current root.\n *\n * 3. **Auto-aligned with insert/suspense boundaries** - Because they create child\n * roots that inherit parent, context values propagate correctly.\n *\n * ## Usage\n *\n * ```tsx\n * // Create context with default value\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Provide value to subtree\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <ThemedComponent />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume value\n * function ThemedComponent() {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>...</div>\n * }\n * ```\n *\n * @module\n */\n\nimport { createElement } from './dom'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n popRoot,\n pushRoot,\n type RootContext,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport { createRenderEffect } from './effect'\nimport type { BaseProps, FictNode } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context object created by createContext.\n * Contains the Provider component and serves as a key for context lookup.\n */\nexport interface Context<T> {\n /** Unique identifier for this context */\n readonly id: symbol\n /** Default value when no provider is found */\n readonly defaultValue: T\n /** Provider component for supplying context values */\n Provider: ContextProvider<T>\n /** Display name for debugging */\n displayName?: string\n}\n\n/**\n * Props for the Context Provider component\n */\nexport interface ProviderProps<T> extends BaseProps {\n /** The value to provide to the subtree */\n value: T\n}\n\n/**\n * Provider component type\n */\nexport type ContextProvider<T> = (props: ProviderProps<T>) => FictNode\n\n// ============================================================================\n// Internal Context Storage\n// ============================================================================\n\n/**\n * WeakMap to store context values per RootContext.\n * Using WeakMap ensures proper garbage collection when roots are destroyed.\n */\nconst contextStorage = new WeakMap<RootContext, Map<symbol, unknown>>()\n\n/**\n * Get the context map for a root, creating it if needed\n */\nfunction getContextMap(root: RootContext): Map<symbol, unknown> {\n let map = contextStorage.get(root)\n if (!map) {\n map = new Map()\n contextStorage.set(root, map)\n }\n return map\n}\n\n// ============================================================================\n// Context API\n// ============================================================================\n\n/**\n * Creates a new context with the given default value.\n *\n * Context provides a way to pass values through the component tree without\n * explicit props drilling. It's especially useful for:\n *\n * - Theme data\n * - Locale/i18n settings\n * - Authentication state\n * - Feature flags\n * - Any data that many components at different nesting levels need\n *\n * @param defaultValue - The value to use when no Provider is found above in the tree\n * @returns A context object with a Provider component\n *\n * @example\n * ```tsx\n * // Create a theme context\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Use the provider\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <Content />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume the context\n * function Content() {\n * const theme = useContext(ThemeContext)\n * return <div class={`theme-${theme}`}>Hello</div>\n * }\n * ```\n */\nexport function createContext<T>(defaultValue: T): Context<T> {\n const id = Symbol('fict.context')\n\n const context: Context<T> = {\n id,\n defaultValue,\n Provider: null as unknown as ContextProvider<T>,\n }\n\n // Create the Provider component\n context.Provider = function Provider(props: ProviderProps<T>): FictNode {\n const hostRoot = getCurrentRoot()\n\n // Create a child root for the provider's subtree\n // This establishes the provider boundary - children will look up from here\n const providerRoot = createRootContext(hostRoot)\n\n // Store the context value on this root\n const contextMap = getContextMap(providerRoot)\n contextMap.set(id, props.value)\n\n // Create DOM structure\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:ctx')\n fragment.appendChild(marker)\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const renderChildren = (children: FictNode) => {\n // Cleanup previous render\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (children == null || children === false) {\n return\n }\n\n const prev = pushRoot(providerRoot)\n let nodes: Node[] = []\n try {\n const output = createElement(children)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } finally {\n popRoot(prev)\n flushOnMount(providerRoot)\n }\n\n cleanup = () => {\n destroyRoot(providerRoot)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n // Initial render\n createRenderEffect(() => {\n // Update context value on re-render (if value prop changes reactively)\n contextMap.set(id, props.value)\n renderChildren(props.children)\n })\n\n return fragment\n }\n\n return context\n}\n\n/**\n * Reads the current value of a context.\n *\n * useContext looks up through the RootContext parent chain to find the\n * nearest Provider for this context. If no Provider is found, returns\n * the context's default value.\n *\n * @param context - The context object created by createContext\n * @returns The current context value\n *\n * @example\n * ```tsx\n * const ThemeContext = createContext('light')\n *\n * function ThemedButton() {\n * const theme = useContext(ThemeContext)\n * return <button class={theme === 'dark' ? 'btn-dark' : 'btn-light'}>Click</button>\n * }\n * ```\n */\nexport function useContext<T>(context: Context<T>): T {\n let root = getCurrentRoot()\n\n // Walk up the parent chain looking for the context value\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return contextMap.get(context.id) as T\n }\n root = root.parent\n }\n\n // No provider found, return default value\n return context.defaultValue\n}\n\n/**\n * Checks if a context value is currently provided in the tree.\n *\n * Useful for conditional behavior when a provider may or may not exist.\n *\n * @param context - The context object to check\n * @returns true if a Provider exists above in the tree\n *\n * @example\n * ```tsx\n * function OptionalTheme() {\n * if (hasContext(ThemeContext)) {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>Themed content</div>\n * }\n * return <div>Default content</div>\n * }\n * ```\n */\nexport function hasContext<T>(context: Context<T>): boolean {\n let root = getCurrentRoot()\n\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return true\n }\n root = root.parent\n }\n\n return false\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/chunk-Z5WRKD7Y.cjs","../src/context.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/chunk-Z5WRKD7Y.cjs","sourcesContent":[null,"/**\n * @fileoverview Context API for Fict\n *\n * Provides a way to pass data through the component tree without having to pass\n * props down manually at every level. Context is designed for:\n *\n * - SSR isolation (different request = different context values)\n * - Multi-instance support (multiple app roots with different values)\n * - Subtree scoping (override values in specific parts of the tree)\n *\n * ## Design Principles\n *\n * 1. **Reuses existing RootContext hierarchy** - Uses parent chain for value lookup,\n * consistent with handleError/handleSuspend mechanisms.\n *\n * 2. **Zero extra root creation overhead** - Provider doesn't create new root,\n * only mounts value on current root.\n *\n * 3. **Auto-aligned with insert/suspense boundaries** - Because they create child\n * roots that inherit parent, context values propagate correctly.\n *\n * ## Usage\n *\n * ```tsx\n * // Create context with default value\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Provide value to subtree\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <ThemedComponent />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume value\n * function ThemedComponent() {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>...</div>\n * }\n * ```\n *\n * @module\n */\n\nimport { createElement } from './dom'\nimport { createRenderEffect } from './effect'\nimport {\n createRootContext,\n destroyRoot,\n flushOnMount,\n getCurrentRoot,\n popRoot,\n pushRoot,\n type RootContext,\n} from './lifecycle'\nimport { insertNodesBefore, removeNodes, toNodeArray } from './node-ops'\nimport type { BaseProps, FictNode } from './types'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Context object created by createContext.\n * Contains the Provider component and serves as a key for context lookup.\n */\nexport interface Context<T> {\n /** Unique identifier for this context */\n readonly id: symbol\n /** Default value when no provider is found */\n readonly defaultValue: T\n /** Provider component for supplying context values */\n Provider: ContextProvider<T>\n /** Display name for debugging */\n displayName?: string\n}\n\n/**\n * Props for the Context Provider component\n */\nexport interface ProviderProps<T> extends BaseProps {\n /** The value to provide to the subtree */\n value: T\n}\n\n/**\n * Provider component type\n */\nexport type ContextProvider<T> = (props: ProviderProps<T>) => FictNode\n\n// ============================================================================\n// Internal Context Storage\n// ============================================================================\n\n/**\n * WeakMap to store context values per RootContext.\n * Using WeakMap ensures proper garbage collection when roots are destroyed.\n */\nconst contextStorage = new WeakMap<RootContext, Map<symbol, unknown>>()\n\n/**\n * Get the context map for a root, creating it if needed\n */\nfunction getContextMap(root: RootContext): Map<symbol, unknown> {\n let map = contextStorage.get(root)\n if (!map) {\n map = new Map()\n contextStorage.set(root, map)\n }\n return map\n}\n\n// ============================================================================\n// Context API\n// ============================================================================\n\n/**\n * Creates a new context with the given default value.\n *\n * Context provides a way to pass values through the component tree without\n * explicit props drilling. It's especially useful for:\n *\n * - Theme data\n * - Locale/i18n settings\n * - Authentication state\n * - Feature flags\n * - Any data that many components at different nesting levels need\n *\n * @param defaultValue - The value to use when no Provider is found above in the tree\n * @returns A context object with a Provider component\n *\n * @example\n * ```tsx\n * // Create a theme context\n * const ThemeContext = createContext<'light' | 'dark'>('light')\n *\n * // Use the provider\n * function App() {\n * return (\n * <ThemeContext.Provider value=\"dark\">\n * <Content />\n * </ThemeContext.Provider>\n * )\n * }\n *\n * // Consume the context\n * function Content() {\n * const theme = useContext(ThemeContext)\n * return <div class={`theme-${theme}`}>Hello</div>\n * }\n * ```\n */\nexport function createContext<T>(defaultValue: T): Context<T> {\n const id = Symbol('fict.context')\n\n const context: Context<T> = {\n id,\n defaultValue,\n Provider: null as unknown as ContextProvider<T>,\n }\n\n // Create the Provider component\n context.Provider = function Provider(props: ProviderProps<T>): FictNode {\n const hostRoot = getCurrentRoot()\n\n // Create a child root for the provider's subtree\n // This establishes the provider boundary - children will look up from here\n const providerRoot = createRootContext(hostRoot)\n\n // Store the context value on this root\n const contextMap = getContextMap(providerRoot)\n contextMap.set(id, props.value)\n\n // Create DOM structure\n const fragment = document.createDocumentFragment()\n const marker = document.createComment('fict:ctx')\n fragment.appendChild(marker)\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n\n const renderChildren = (children: FictNode) => {\n // Cleanup previous render\n if (cleanup) {\n cleanup()\n cleanup = undefined\n }\n if (activeNodes.length) {\n removeNodes(activeNodes)\n activeNodes = []\n }\n\n if (children == null || children === false) {\n return\n }\n\n const prev = pushRoot(providerRoot)\n let nodes: Node[] = []\n try {\n const output = createElement(children)\n nodes = toNodeArray(output)\n const parentNode = marker.parentNode as (ParentNode & Node) | null\n if (parentNode) {\n insertNodesBefore(parentNode, nodes, marker)\n }\n } finally {\n popRoot(prev)\n flushOnMount(providerRoot)\n }\n\n cleanup = () => {\n destroyRoot(providerRoot)\n removeNodes(nodes)\n }\n activeNodes = nodes\n }\n\n // Initial render\n createRenderEffect(() => {\n // Update context value on re-render (if value prop changes reactively)\n contextMap.set(id, props.value)\n renderChildren(props.children)\n })\n\n return fragment\n }\n\n return context\n}\n\n/**\n * Reads the current value of a context.\n *\n * useContext looks up through the RootContext parent chain to find the\n * nearest Provider for this context. If no Provider is found, returns\n * the context's default value.\n *\n * @param context - The context object created by createContext\n * @returns The current context value\n *\n * @example\n * ```tsx\n * const ThemeContext = createContext('light')\n *\n * function ThemedButton() {\n * const theme = useContext(ThemeContext)\n * return <button class={theme === 'dark' ? 'btn-dark' : 'btn-light'}>Click</button>\n * }\n * ```\n */\nexport function useContext<T>(context: Context<T>): T {\n let root = getCurrentRoot()\n\n // Walk up the parent chain looking for the context value\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return contextMap.get(context.id) as T\n }\n root = root.parent\n }\n\n // No provider found, return default value\n return context.defaultValue\n}\n\n/**\n * Checks if a context value is currently provided in the tree.\n *\n * Useful for conditional behavior when a provider may or may not exist.\n *\n * @param context - The context object to check\n * @returns true if a Provider exists above in the tree\n *\n * @example\n * ```tsx\n * function OptionalTheme() {\n * if (hasContext(ThemeContext)) {\n * const theme = useContext(ThemeContext)\n * return <div class={theme}>Themed content</div>\n * }\n * return <div>Default content</div>\n * }\n * ```\n */\nexport function hasContext<T>(context: Context<T>): boolean {\n let root = getCurrentRoot()\n\n while (root) {\n const contextMap = contextStorage.get(root)\n if (contextMap && contextMap.has(context.id)) {\n return true\n }\n root = root.parent\n }\n\n return false\n}\n"]}
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkZ5WRKD7Ycjs = require('./chunk-Z5WRKD7Y.cjs');
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@ var _chunk527QSKFMcjs = require('./chunk-527QSKFM.cjs');
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
var
|
|
37
|
+
var _chunkMWI3USXBcjs = require('./chunk-MWI3USXB.cjs');
|
|
38
38
|
|
|
39
39
|
// src/ref.ts
|
|
40
40
|
function createRef() {
|
|
@@ -46,14 +46,15 @@ function ErrorBoundary(props) {
|
|
|
46
46
|
const fragment = document.createDocumentFragment();
|
|
47
47
|
const marker = document.createComment("fict:error-boundary");
|
|
48
48
|
fragment.appendChild(marker);
|
|
49
|
-
const
|
|
50
|
-
const hostRoot = _chunkJ74L7UYPcjs.getCurrentRoot.call(void 0, );
|
|
49
|
+
const hostRoot = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
|
|
51
50
|
let cleanup;
|
|
52
51
|
let activeNodes = [];
|
|
53
52
|
let renderingFallback = false;
|
|
53
|
+
let reset = () => {
|
|
54
|
+
};
|
|
54
55
|
const toView = (err) => {
|
|
55
56
|
if (err != null) {
|
|
56
|
-
return typeof props.fallback === "function" ? props.fallback(err) : props.fallback;
|
|
57
|
+
return typeof props.fallback === "function" ? props.fallback(err, reset) : props.fallback;
|
|
57
58
|
}
|
|
58
59
|
return _nullishCoalesce(props.children, () => ( null));
|
|
59
60
|
};
|
|
@@ -63,26 +64,25 @@ function ErrorBoundary(props) {
|
|
|
63
64
|
cleanup = void 0;
|
|
64
65
|
}
|
|
65
66
|
if (activeNodes.length) {
|
|
66
|
-
|
|
67
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, activeNodes);
|
|
67
68
|
activeNodes = [];
|
|
68
69
|
}
|
|
69
70
|
if (value == null || value === false) {
|
|
70
71
|
return;
|
|
71
72
|
}
|
|
72
|
-
const root =
|
|
73
|
-
const prev =
|
|
73
|
+
const root = _chunkMWI3USXBcjs.createRootContext.call(void 0, hostRoot);
|
|
74
|
+
const prev = _chunkMWI3USXBcjs.pushRoot.call(void 0, root);
|
|
74
75
|
let nodes = [];
|
|
75
76
|
try {
|
|
76
|
-
const output =
|
|
77
|
-
nodes =
|
|
77
|
+
const output = _chunkMWI3USXBcjs.createElement.call(void 0, value);
|
|
78
|
+
nodes = _chunkMWI3USXBcjs.toNodeArray.call(void 0, output);
|
|
78
79
|
const parentNode = marker.parentNode;
|
|
79
80
|
if (parentNode) {
|
|
80
|
-
|
|
81
|
+
_chunkMWI3USXBcjs.insertNodesBefore.call(void 0, parentNode, nodes, marker);
|
|
81
82
|
}
|
|
82
83
|
} catch (err) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
_chunkJ74L7UYPcjs.destroyRoot.call(void 0, root);
|
|
84
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
85
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, root);
|
|
86
86
|
if (renderingFallback) {
|
|
87
87
|
throw err;
|
|
88
88
|
}
|
|
@@ -97,19 +97,20 @@ function ErrorBoundary(props) {
|
|
|
97
97
|
}
|
|
98
98
|
return;
|
|
99
99
|
}
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
101
|
+
_chunkMWI3USXBcjs.flushOnMount.call(void 0, root);
|
|
102
102
|
cleanup = () => {
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, root);
|
|
104
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, nodes);
|
|
105
105
|
};
|
|
106
106
|
activeNodes = nodes;
|
|
107
107
|
};
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
renderValue(
|
|
111
|
-
}
|
|
112
|
-
|
|
108
|
+
reset = () => {
|
|
109
|
+
renderingFallback = false;
|
|
110
|
+
renderValue(toView(null));
|
|
111
|
+
};
|
|
112
|
+
renderValue(_nullishCoalesce(props.children, () => ( null)));
|
|
113
|
+
_chunkMWI3USXBcjs.registerErrorHandler.call(void 0, (err) => {
|
|
113
114
|
renderValue(toView(err));
|
|
114
115
|
_optionalChain([props, 'access', _5 => _5.onError, 'optionalCall', _6 => _6(err)]);
|
|
115
116
|
return true;
|
|
@@ -118,7 +119,7 @@ function ErrorBoundary(props) {
|
|
|
118
119
|
const isGetter = typeof props.resetKeys === "function" && props.resetKeys.length === 0;
|
|
119
120
|
const getter = isGetter ? props.resetKeys : void 0;
|
|
120
121
|
let prev = isGetter ? getter() : props.resetKeys;
|
|
121
|
-
|
|
122
|
+
_chunkMWI3USXBcjs.createEffect.call(void 0, () => {
|
|
122
123
|
const next = getter ? getter() : props.resetKeys;
|
|
123
124
|
if (prev !== next) {
|
|
124
125
|
prev = next;
|
|
@@ -147,11 +148,10 @@ function createSuspenseToken() {
|
|
|
147
148
|
}
|
|
148
149
|
var isThenable = (value) => typeof value === "object" && value !== null && typeof value.then === "function";
|
|
149
150
|
function Suspense(props) {
|
|
150
|
-
const
|
|
151
|
-
const pending = _chunkJ74L7UYPcjs.signal.call(void 0, 0);
|
|
151
|
+
const pending = _chunkMWI3USXBcjs.signal.call(void 0, 0);
|
|
152
152
|
let resolvedOnce = false;
|
|
153
153
|
let epoch = 0;
|
|
154
|
-
const hostRoot =
|
|
154
|
+
const hostRoot = _chunkMWI3USXBcjs.getCurrentRoot.call(void 0, );
|
|
155
155
|
const toFallback = (err) => typeof props.fallback === "function" ? props.fallback(err) : props.fallback;
|
|
156
156
|
const renderView = (view) => {
|
|
157
157
|
if (cleanup) {
|
|
@@ -159,42 +159,41 @@ function Suspense(props) {
|
|
|
159
159
|
cleanup = void 0;
|
|
160
160
|
}
|
|
161
161
|
if (activeNodes.length) {
|
|
162
|
-
|
|
162
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, activeNodes);
|
|
163
163
|
activeNodes = [];
|
|
164
164
|
}
|
|
165
165
|
if (view == null || view === false) {
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
|
-
const root =
|
|
169
|
-
const prev =
|
|
168
|
+
const root = _chunkMWI3USXBcjs.createRootContext.call(void 0, hostRoot);
|
|
169
|
+
const prev = _chunkMWI3USXBcjs.pushRoot.call(void 0, root);
|
|
170
170
|
let nodes = [];
|
|
171
171
|
try {
|
|
172
|
-
const output =
|
|
173
|
-
nodes =
|
|
172
|
+
const output = _chunkMWI3USXBcjs.createElement.call(void 0, view);
|
|
173
|
+
nodes = _chunkMWI3USXBcjs.toNodeArray.call(void 0, output);
|
|
174
174
|
const suspendedAttempt = root.suspended || nodes.length > 0 && nodes.every((node) => node instanceof Comment && node.data === "fict:suspend");
|
|
175
175
|
if (suspendedAttempt) {
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
177
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, root);
|
|
178
178
|
return;
|
|
179
179
|
}
|
|
180
180
|
const parentNode = marker.parentNode;
|
|
181
181
|
if (parentNode) {
|
|
182
|
-
|
|
182
|
+
_chunkMWI3USXBcjs.insertNodesBefore.call(void 0, parentNode, nodes, marker);
|
|
183
183
|
}
|
|
184
184
|
} catch (err) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (!_chunkJ74L7UYPcjs.handleError.call(void 0, err, { source: "render" }, hostRoot)) {
|
|
185
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
186
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, root);
|
|
187
|
+
if (!_chunkMWI3USXBcjs.handleError.call(void 0, err, { source: "render" }, hostRoot)) {
|
|
189
188
|
throw err;
|
|
190
189
|
}
|
|
191
190
|
return;
|
|
192
191
|
}
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
_chunkMWI3USXBcjs.popRoot.call(void 0, prev);
|
|
193
|
+
_chunkMWI3USXBcjs.flushOnMount.call(void 0, root);
|
|
195
194
|
cleanup = () => {
|
|
196
|
-
|
|
197
|
-
|
|
195
|
+
_chunkMWI3USXBcjs.destroyRoot.call(void 0, root);
|
|
196
|
+
_chunkMWI3USXBcjs.removeNodes.call(void 0, nodes);
|
|
198
197
|
};
|
|
199
198
|
activeNodes = nodes;
|
|
200
199
|
};
|
|
@@ -209,10 +208,9 @@ function Suspense(props) {
|
|
|
209
208
|
_optionalChain([props, 'access', _7 => _7.onResolve, 'optionalCall', _8 => _8()]);
|
|
210
209
|
}
|
|
211
210
|
};
|
|
212
|
-
|
|
211
|
+
_chunkMWI3USXBcjs.registerSuspenseHandler.call(void 0, (token) => {
|
|
213
212
|
const tokenEpoch = epoch;
|
|
214
213
|
pending(pending() + 1);
|
|
215
|
-
currentView(toFallback());
|
|
216
214
|
renderView(toFallback());
|
|
217
215
|
const thenable = token.then ? token : isThenable(token) ? token : null;
|
|
218
216
|
if (thenable) {
|
|
@@ -224,7 +222,6 @@ function Suspense(props) {
|
|
|
224
222
|
const newPending = Math.max(0, pending() - 1);
|
|
225
223
|
pending(newPending);
|
|
226
224
|
if (newPending === 0) {
|
|
227
|
-
currentView(_nullishCoalesce(props.children, () => ( null)));
|
|
228
225
|
renderView(_nullishCoalesce(props.children, () => ( null)));
|
|
229
226
|
onResolveMaybe();
|
|
230
227
|
}
|
|
@@ -236,7 +233,7 @@ function Suspense(props) {
|
|
|
236
233
|
const newPending = Math.max(0, pending() - 1);
|
|
237
234
|
pending(newPending);
|
|
238
235
|
_optionalChain([props, 'access', _9 => _9.onReject, 'optionalCall', _10 => _10(err)]);
|
|
239
|
-
if (!
|
|
236
|
+
if (!_chunkMWI3USXBcjs.handleError.call(void 0, err, { source: "render" }, hostRoot)) {
|
|
240
237
|
throw err;
|
|
241
238
|
}
|
|
242
239
|
}
|
|
@@ -250,13 +247,12 @@ function Suspense(props) {
|
|
|
250
247
|
const isGetter = typeof props.resetKeys === "function" && props.resetKeys.length === 0;
|
|
251
248
|
const getter = isGetter ? props.resetKeys : void 0;
|
|
252
249
|
let prev = isGetter ? getter() : props.resetKeys;
|
|
253
|
-
|
|
250
|
+
_chunkMWI3USXBcjs.createEffect.call(void 0, () => {
|
|
254
251
|
const next = getter ? getter() : props.resetKeys;
|
|
255
252
|
if (prev !== next) {
|
|
256
253
|
prev = next;
|
|
257
254
|
epoch++;
|
|
258
255
|
pending(0);
|
|
259
|
-
currentView(_nullishCoalesce(props.children, () => ( null)));
|
|
260
256
|
renderView(_nullishCoalesce(props.children, () => ( null)));
|
|
261
257
|
}
|
|
262
258
|
});
|
|
@@ -288,5 +284,5 @@ function Suspense(props) {
|
|
|
288
284
|
|
|
289
285
|
|
|
290
286
|
|
|
291
|
-
exports.ErrorBoundary = ErrorBoundary; exports.Fragment =
|
|
287
|
+
exports.ErrorBoundary = ErrorBoundary; exports.Fragment = _chunkMWI3USXBcjs.Fragment; exports.Suspense = Suspense; exports.batch = _chunkMWI3USXBcjs.batch2; exports.createContext = _chunkZ5WRKD7Ycjs.createContext; exports.createEffect = _chunkMWI3USXBcjs.createEffect; exports.createElement = _chunkMWI3USXBcjs.createElement; exports.createMemo = _chunkMWI3USXBcjs.createMemo; exports.createPortal = _chunkMWI3USXBcjs.createPortal; exports.createRef = createRef; exports.createRoot = _chunkMWI3USXBcjs.createRoot; exports.createSuspenseToken = createSuspenseToken; exports.hasContext = _chunkZ5WRKD7Ycjs.hasContext; exports.mergeProps = _chunkMWI3USXBcjs.mergeProps; exports.onCleanup = _chunkMWI3USXBcjs.onCleanup; exports.onDestroy = _chunkMWI3USXBcjs.onDestroy; exports.onMount = _chunkMWI3USXBcjs.onMount; exports.prop = _chunkMWI3USXBcjs.prop; exports.render = _chunkMWI3USXBcjs.render; exports.startTransition = _chunkMWI3USXBcjs.startTransition; exports.untrack = _chunkMWI3USXBcjs.untrack2; exports.useContext = _chunkZ5WRKD7Ycjs.useContext; exports.useDeferredValue = _chunkMWI3USXBcjs.useDeferredValue; exports.useTransition = _chunkMWI3USXBcjs.useTransition;
|
|
292
288
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/index.cjs","../src/ref.ts","../src/error-boundary.ts","../src/suspense.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/index.cjs","sourcesContent":[null,"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 { createSignal } from './signal'\nimport type { BaseProps, FictNode } from './types'\n\ninterface ErrorBoundaryProps extends BaseProps {\n fallback: FictNode | ((err: unknown) => 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 currentView = createSignal<FictNode | null>(props.children ?? null)\n const hostRoot = getCurrentRoot()\n\n let cleanup: (() => void) | undefined\n let activeNodes: Node[] = []\n let renderingFallback = false\n\n const toView = (err: unknown | null): FictNode | null => {\n if (err != null) {\n return typeof props.fallback === 'function'\n ? (props.fallback as (e: unknown) => FictNode)(err)\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 flushOnMount(root)\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 createEffect(() => {\n const value = currentView()\n renderValue(value)\n })\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 currentView = createSignal<FictNode | null>(props.children ?? null)\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 flushOnMount(root)\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 currentView(toFallback())\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 currentView(props.children ?? null)\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 currentView(props.children ?? null)\n renderView(props.children ?? null)\n }\n })\n }\n\n return fragment\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/fict/fict/packages/runtime/dist/index.cjs","../src/ref.ts","../src/error-boundary.ts","../src/suspense.ts"],"names":[],"mappings":"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","file":"/home/runner/work/fict/fict/packages/runtime/dist/index.cjs","sourcesContent":[null,"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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -101,7 +101,7 @@ declare function batch<T>(fn: () => T): T;
|
|
|
101
101
|
declare function untrack<T>(fn: () => T): T;
|
|
102
102
|
|
|
103
103
|
interface ErrorBoundaryProps extends BaseProps {
|
|
104
|
-
fallback: FictNode | ((err: unknown) => FictNode);
|
|
104
|
+
fallback: FictNode | ((err: unknown, reset?: () => void) => FictNode);
|
|
105
105
|
onError?: (err: unknown) => void;
|
|
106
106
|
resetKeys?: unknown | (() => unknown);
|
|
107
107
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -101,7 +101,7 @@ declare function batch<T>(fn: () => T): T;
|
|
|
101
101
|
declare function untrack<T>(fn: () => T): T;
|
|
102
102
|
|
|
103
103
|
interface ErrorBoundaryProps extends BaseProps {
|
|
104
|
-
fallback: FictNode | ((err: unknown) => FictNode);
|
|
104
|
+
fallback: FictNode | ((err: unknown, reset?: () => void) => FictNode);
|
|
105
105
|
onError?: (err: unknown) => void;
|
|
106
106
|
resetKeys?: unknown | (() => unknown);
|
|
107
107
|
}
|