flexium
Version:
A lightweight, signals-based UI framework with cross-platform renderers
1 lines • 22.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/core/signal.ts"],"names":["activeEffect","owner","batchDepth","batchedEffects","onMount","fn","effect","untrack","EffectNode","onError","cleanup","dep","prevEffect","result","error","SignalNode","_value","newValue","subscriber","subscribersToNotify","ComputedNode","computeFn","signal","initialValue","node","sig","SIGNAL_MARKER","computed","comp","options","dispose","batch","effects","root","prevOwner","newOwner","isSignal","value","onCleanup","createResource","source","fetcher","loading","state","lastPromise","load","currentSource","refetching","promise","err","getSource","resource","v"],"mappings":"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","file":"chunk-Z7JNAX53.mjs","sourcesContent":["/**\n * Signal System - Fine-grained reactivity without VDOM\n *\n * Architecture:\n * - Signals are reactive primitives that notify subscribers on change\n * - Computed signals automatically track dependencies and memoize results\n * - Effects run side effects and auto-track dependencies\n * - Batching prevents cascading updates for performance\n */\n\n/**\n * Base interface for subscriber nodes\n */\ninterface ISubscriber {\n execute(): void;\n dependencies: Set<IObservable>;\n}\n\n/**\n * Base interface for observable nodes\n */\ninterface IObservable {\n subscribers: Set<ISubscriber>;\n notify(): void;\n}\n\n// Global context for dependency tracking\nlet activeEffect: ISubscriber | null = null;\nlet owner: { cleanups: (() => void)[] } | null = null;\nlet batchDepth = 0;\nlet batchedEffects = new Set<ISubscriber>();\n\n/**\n * Runs a function once when the component mounts.\n * Equivalent to `effect(() => untrack(fn))` but clearer intent.\n */\nexport function onMount(fn: () => void | (() => void)): void {\n effect(() => untrack(fn));\n}\n\n/**\n * Base interface for reactive signals\n * @internal\n */\nexport interface Signal<T> {\n value: T;\n (): T;\n set(value: T): void;\n peek(): T;\n}\n\n/**\n * Computed signal interface (read-only)\n * @internal\n */\nexport interface Computed<T> {\n readonly value: T;\n (): T;\n peek(): T;\n}\n\n/**\n * Internal effect node for dependency tracking\n */\nclass EffectNode implements ISubscriber {\n dependencies = new Set<IObservable>();\n cleanups: (() => void)[] = [];\n private isExecuting = false;\n private isQueued = false;\n\n constructor(\n public fn: () => void | (() => void),\n public onError?: (error: Error) => void\n ) { }\n\n execute(): void {\n if (this.isExecuting) {\n this.isQueued = true;\n return;\n }\n\n this.isExecuting = true;\n \n try {\n this.run();\n } finally {\n this.isExecuting = false;\n if (this.isQueued) {\n this.isQueued = false;\n // Schedule microtask to avoid stack overflow and infinite sync loops\n queueMicrotask(() => this.execute());\n }\n }\n }\n\n private run(): void {\n for (const cleanup of this.cleanups) {\n cleanup();\n }\n this.cleanups = [];\n\n // Clear previous dependencies\n for (const dep of this.dependencies) {\n dep.subscribers.delete(this);\n }\n this.dependencies.clear();\n\n const prevEffect = activeEffect;\n activeEffect = this;\n\n try {\n const result = this.fn();\n if (typeof result === 'function') {\n this.cleanups.push(result);\n }\n } catch (error) {\n if (this.onError) {\n this.onError(error as Error);\n } else {\n console.error('Error in effect:', error);\n }\n } finally {\n activeEffect = prevEffect;\n }\n }\n\n dispose(): void {\n for (const cleanup of this.cleanups) {\n cleanup();\n }\n this.cleanups = [];\n for (const dep of this.dependencies) {\n dep.subscribers.delete(this);\n }\n this.dependencies.clear();\n }\n}\n\n/**\n * Internal signal node for writable signals\n */\nclass SignalNode<T> implements IObservable {\n subscribers = new Set<ISubscriber>();\n\n constructor(private _value: T) { }\n\n get(): T {\n // Track dependency if inside an effect or computed\n if (activeEffect) {\n this.subscribers.add(activeEffect);\n activeEffect.dependencies.add(this);\n }\n return this._value;\n }\n\n set(newValue: T): void {\n if (this._value !== newValue) {\n this._value = newValue;\n this.notify();\n }\n }\n\n peek(): T {\n return this._value;\n }\n\n notify(): void {\n if (batchDepth > 0) {\n // Collect effects to run after batch completes\n for (const subscriber of this.subscribers) {\n batchedEffects.add(subscriber);\n }\n } else {\n // Run effects immediately - copy subscribers to avoid modification during iteration\n const subscribersToNotify = new Set(this.subscribers);\n for (const subscriber of subscribersToNotify) {\n subscriber.execute();\n }\n }\n }\n}\n\n/**\n * Internal computed node for derived values\n */\nclass ComputedNode<T> implements ISubscriber, IObservable {\n subscribers = new Set<ISubscriber>();\n dependencies = new Set<IObservable>();\n private _value!: T;\n private _dirty = true;\n\n constructor(private computeFn: () => T) { }\n\n execute(): void {\n // When a dependency changes, mark as dirty and notify subscribers\n this._dirty = true;\n this.notify();\n }\n\n get(): T {\n // Track dependency if inside an effect or computed\n if (activeEffect && activeEffect !== this) {\n this.subscribers.add(activeEffect);\n activeEffect.dependencies.add(this);\n }\n\n if (this._dirty) {\n this._dirty = false;\n\n // Clear previous dependencies\n for (const dep of this.dependencies) {\n dep.subscribers.delete(this);\n }\n this.dependencies.clear();\n\n const prevEffect = activeEffect;\n activeEffect = this;\n\n try {\n this._value = this.computeFn();\n } finally {\n activeEffect = prevEffect;\n }\n }\n\n return this._value;\n }\n\n peek(): T {\n if (this._dirty) {\n this._dirty = false;\n\n // Clear previous dependencies\n for (const dep of this.dependencies) {\n dep.subscribers.delete(this);\n }\n this.dependencies.clear();\n\n const prevEffect = activeEffect;\n activeEffect = this;\n\n try {\n this._value = this.computeFn();\n } finally {\n activeEffect = prevEffect;\n }\n }\n return this._value;\n }\n\n notify(): void {\n if (batchDepth > 0) {\n for (const subscriber of this.subscribers) {\n batchedEffects.add(subscriber);\n }\n } else {\n // Run effects immediately - copy subscribers to avoid modification during iteration\n const subscribersToNotify = new Set(this.subscribers);\n for (const subscriber of subscribersToNotify) {\n subscriber.execute();\n }\n }\n }\n}\n\n/**\n * Creates a reactive signal\n *\n * @param initialValue - The initial value of the signal\n * @returns A signal object with value getter/setter\n *\n * @example\n * const count = signal(0);\n * count.value++; // triggers subscribers\n * console.log(count()); // alternative getter syntax\n */\nexport function signal<T>(initialValue: T): Signal<T> {\n const node = new SignalNode(initialValue);\n\n const sig = function (this: any) {\n return node.get();\n } as Signal<T>;\n\n Object.defineProperty(sig, 'value', {\n get() {\n return node.get();\n },\n set(newValue: T) {\n node.set(newValue);\n },\n enumerable: true,\n configurable: true\n });\n\n sig.set = (newValue: T) => node.set(newValue);\n sig.peek = () => node.peek();\n\n // Mark as signal for detection\n (sig as any)[SIGNAL_MARKER] = true;\n\n return sig;\n}\n\n/**\n * Creates a computed signal (derived value)\n *\n * @param fn - Function that computes the derived value\n * @returns A read-only computed signal\n *\n * @example\n * const count = signal(1);\n * const doubled = computed(() => count.value * 2);\n * console.log(doubled.value); // 2\n */\nexport function computed<T>(fn: () => T): Computed<T> {\n const node = new ComputedNode(fn);\n\n const comp = function (this: any) {\n return node.get();\n } as Computed<T>;\n\n Object.defineProperty(comp, 'value', {\n get() {\n return node.get();\n },\n enumerable: true,\n configurable: true\n });\n\n comp.peek = () => node.peek();\n\n // Mark as signal for detection\n (comp as any)[SIGNAL_MARKER] = true;\n\n return comp;\n}\n\n/**\n * Creates a side effect that runs when dependencies change\n *\n * @param fn - Effect function, can return a cleanup function\n * @param options - Optional error handler\n * @returns Dispose function to stop the effect\n *\n * @example\n * const count = signal(0);\n * const dispose = effect(() => {\n * console.log('Count:', count.value);\n * return () => console.log('Cleanup');\n * });\n */\nexport function effect(\n fn: () => void | (() => void),\n options?: { onError?: (error: Error) => void }\n): () => void {\n const node = new EffectNode(fn, options?.onError);\n node.execute();\n const dispose = () => node.dispose();\n\n if (owner) {\n owner.cleanups.push(dispose);\n }\n\n return dispose;\n}\n\n/**\n * Batches multiple signal updates to prevent cascading updates\n *\n * @param fn - Function containing signal updates\n *\n * @example\n * batch(() => {\n * count.value++;\n * name.value = \"new\";\n * }); // effects only run once at the end\n */\nexport function batch(fn: () => void): void {\n batchDepth++;\n try {\n fn();\n } finally {\n batchDepth--;\n if (batchDepth === 0) {\n const effects = new Set(batchedEffects);\n batchedEffects.clear();\n for (const effect of effects) {\n effect.execute();\n }\n }\n }\n}\n\n/**\n * Runs a function without tracking dependencies\n * Useful for reading signals inside effects without creating dependencies\n *\n * @param fn - Function to run without tracking\n * @returns The return value of fn\n */\nexport function untrack<T>(fn: () => T): T {\n const prevEffect = activeEffect;\n activeEffect = null;\n try {\n return fn();\n } finally {\n activeEffect = prevEffect;\n }\n}\n\n/**\n * Creates a root scope for effects\n * All effects created within the scope can be disposed together\n *\n * @param fn - Function that creates effects\n * @returns Dispose function for all effects in the scope\n */\nexport function root<T>(fn: (dispose: () => void) => T): T {\n const prevOwner = owner;\n const newOwner = { cleanups: [] as (() => void)[] };\n owner = newOwner;\n\n const dispose = () => {\n for (const cleanup of newOwner.cleanups) {\n cleanup();\n }\n newOwner.cleanups = [];\n };\n\n try {\n return fn(dispose);\n } finally {\n owner = prevOwner;\n }\n}\n\n/**\n * Symbol to mark signals for detection\n * @internal\n */\nconst SIGNAL_MARKER = Symbol('flexium.signal');\n\n/**\n * Check if a value is a signal\n *\n * @param value - Value to check\n * @returns True if value is a signal or computed\n *\n * @example\n * const count = signal(0);\n * isSignal(count); // true\n * isSignal(5); // false\n */\nexport function isSignal(value: any): value is Signal<any> | Computed<any> {\n return (\n value !== null &&\n typeof value === 'function' &&\n SIGNAL_MARKER in value\n );\n}\n\n/**\n * Registers a cleanup function that runs before the current effect re-runs or is disposed\n *\n * @param fn - Cleanup function\n */\nexport function onCleanup(fn: () => void): void {\n if (activeEffect instanceof EffectNode) {\n activeEffect.cleanups.push(fn);\n } else {\n console.warn('onCleanup must be called from within an effect');\n }\n}\n\n/**\n * Resource interface for async data\n */\nexport interface Resource<T> extends Signal<T | undefined> {\n loading: boolean;\n error: any;\n state: 'unresolved' | 'pending' | 'ready' | 'refreshing' | 'errored';\n latest: T | undefined;\n /**\n * Read value, throwing Promise if pending or Error if failed.\n * Used by Suspense.\n */\n read: () => T | undefined;\n}\n\n/**\n * Creates a resource for handling async data\n *\n * @param source - Reactive source (signal or function) that triggers the fetcher\n * @param fetcher - Async function that fetches data based on source\n * @returns [Resource, Actions] tuple\n */\nexport function createResource<T, S = any>(\n source: S | Signal<S> | (() => S),\n fetcher: (source: S, { value, refetching }: { value: T | undefined; refetching: any }) => Promise<T>\n): [Resource<T>, { mutate: (v: T | undefined) => void; refetch: () => void }] {\n const value = signal<T | undefined>(undefined);\n const error = signal<any>(undefined);\n const loading = signal<boolean>(false);\n const state = signal<'unresolved' | 'pending' | 'ready' | 'refreshing' | 'errored'>('unresolved');\n\n let lastPromise: Promise<T> | null = null;\n\n const load = async (currentSource: S, refetching = false) => {\n if (refetching) {\n state.value = 'refreshing';\n loading.value = true;\n } else {\n state.value = 'pending';\n loading.value = true;\n }\n error.value = undefined;\n\n const promise = fetcher(currentSource, { value: value.peek(), refetching });\n lastPromise = promise;\n\n try {\n const result = await promise;\n if (lastPromise === promise) {\n value.value = result;\n state.value = 'ready';\n loading.value = false;\n }\n } catch (err) {\n if (lastPromise === promise) {\n error.value = err;\n state.value = 'errored';\n loading.value = false;\n }\n }\n };\n\n const getSource = () => {\n if (typeof source === 'function') {\n if (isSignal(source)) {\n return (source as Signal<S>).value;\n }\n return (source as () => S)();\n }\n return source;\n };\n\n // Track source changes\n effect(() => {\n const currentSource = getSource();\n load(currentSource, false);\n });\n\n const resource = function () {\n return value();\n } as Resource<T>;\n\n Object.defineProperties(resource, {\n value: { get: () => value.value },\n loading: { get: () => loading.value },\n error: { get: () => error.value },\n state: { get: () => state.value },\n latest: { get: () => value.peek() },\n peek: { value: () => value.peek() },\n set: { value: (v: T) => value.set(v) },\n read: {\n value: () => {\n if (state.value === 'pending' || state.value === 'refreshing') {\n throw lastPromise;\n }\n if (state.value === 'errored') {\n throw error.value;\n }\n return value.value;\n }\n }\n });\n\n (resource as any)[SIGNAL_MARKER] = true;\n\n const actions = {\n mutate: (v: T | undefined) => value.set(v),\n refetch: () => load(getSource(), true)\n };\n\n return [resource, actions];\n}\n"]}