@vielzeug/ripple 26.11.4 → 26.11.6
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/_async.js +1 -2
- package/dist/_default.cjs +1 -1
- package/dist/_default.cjs.map +1 -1
- package/dist/_default.js +7 -8
- package/dist/_subscribable.js +1 -2
- package/dist/_watch.js +1 -2
- package/dist/errors.js +1 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +6 -7
- package/dist/ripple.cjs +1 -1
- package/dist/ripple.cjs.map +1 -1
- package/dist/ripple.iife.js +1 -1
- package/dist/ripple.iife.js.map +1 -1
- package/dist/ripple.js +2 -3
- package/dist/ripple.js.map +1 -1
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +2 -3
- package/dist/runtime.js.map +1 -1
- package/package.json +7 -7
package/dist/runtime.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.cjs","names":["RippleDisposedError","RippleCycleError","require_errors","REACTIVE","MAX_FLUSH_ITERATIONS","UNSET","runtime","name","sink","dependent","options","initial","next","updater","derive","label","error","owner","changed","value","fn","owned","callback","cleanup","node","scope","observer","dependency","previous","previousEffectScope","previousScope","effect","context","previousObserver","dependencies","result","commitOnError","iterations","effects","listeners","candidate"],"sources":["../src/runtime.ts"],"sourcesContent":["import { RippleCycleError, RippleDisposedError } from './errors';\nimport type {\n Cleanup,\n ComputedOptions,\n EffectHandle,\n EffectOptions,\n Readable,\n RippleErrorContext,\n RippleOptions,\n Scope,\n Signal,\n SignalOptions,\n Unsubscribe,\n} from './types';\n\nexport const REACTIVE = Symbol.for('@vielzeug/ripple/reactive');\nconst MAX_FLUSH_ITERATIONS = 100;\nconst UNSET = Symbol('ripple.unset');\n\ntype Dependency = ReactiveNode<unknown>;\n\n/** Anything that wants to hear that a dependency changed: the only member a producer may call. */\ntype Dependent = {\n onDependencyChanged(): void;\n};\n\n/** A subscribe() listener sink: enqueued as a unit so two subscriptions sharing one\n * callback each fire per flush, while one subscription never fires twice. */\ntype ListenerSink = Dependent & {\n readonly listener: () => void;\n};\n\n/** A dependency-collecting observer (computed or effect). Plain listener sinks need less. */\ntype ObserverNode = Dependent & {\n collecting?: Set<Dependency>;\n readonly dependencies: Set<Dependency>;\n};\n\ntype Owned = {\n dispose(): void;\n setOwner(owner: ScopeNode | undefined): void;\n};\n\nabstract class ReactiveNode<T> {\n readonly [REACTIVE] = true;\n readonly dependents = new Set<Dependent>();\n readonly name: string | undefined;\n protected readonly runtime: ReactiveRuntime;\n\n protected constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n abstract peek(): T;\n abstract get value(): T;\n\n subscribe(listener: () => void): Unsubscribe {\n this.runtime.assertActive();\n this.peek();\n\n // A listener sink never collects dependencies, so it is a bare dependent :\n // no dependency bookkeeping is allocated per subscription.\n const sink: ListenerSink = {\n listener,\n onDependencyChanged: () => this.runtime.enqueueListener(sink),\n };\n\n this.dependents.add(sink);\n\n return () => this.dependents.delete(sink);\n }\n\n protected notify(): void {\n for (const dependent of [...this.dependents]) dependent.onDependencyChanged();\n }\n}\n\nclass SignalNode<T> extends ReactiveNode<T> implements Signal<T> {\n private current: T;\n private readonly equals: (previous: T, next: T) => boolean;\n\n constructor(runtime: ReactiveRuntime, initial: T, options?: SignalOptions<T>) {\n super(runtime, options?.name);\n this.current = initial;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n this.runtime.track(this);\n\n return this.current;\n }\n\n set value(next: T) {\n this.runtime.assertActive();\n\n if (this.equals(this.current, next)) return;\n\n this.current = next;\n this.runtime.propagate(() => this.notify());\n }\n\n peek(): T {\n return this.current;\n }\n\n update(updater: (prev: T) => T): void {\n this.value = updater(this.current);\n }\n}\n\nclass ComputedNode<T> extends ReactiveNode<T> implements ObserverNode, Owned {\n readonly dependencies = new Set<Dependency>();\n private computing = false;\n private disposed = false;\n private dirty = true;\n private current: T | typeof UNSET = UNSET;\n private readonly derive: () => T;\n private readonly equals: (previous: T, next: T) => boolean;\n private owner: ScopeNode | undefined;\n\n constructor(runtime: ReactiveRuntime, derive: () => T, options?: ComputedOptions<T>) {\n super(runtime, options?.name);\n this.derive = derive;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n // Track before refresh: if refresh throws, the consumer still depends on\n // this computed and must be notified when a dependency change might let it\n // succeed. Without this, a first-run failure permanently severs the\n // consumer from the computed.\n this.runtime.track(this);\n this.refresh();\n\n return this.evaluated();\n }\n\n peek(): T {\n this.refresh();\n\n return this.evaluated();\n }\n\n private evaluated(): T {\n if (this.current === UNSET) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleDisposedError(`computed${label} was disposed before it was ever evaluated`);\n }\n\n return this.current as T;\n }\n\n onDependencyChanged(): void {\n if (this.disposed) return;\n\n if (!this.dirty) this.dirty = true;\n\n if (this.dependents.size > 0) {\n try {\n if (this.refresh()) this.notify();\n } catch (error) {\n this.runtime.report(error, { kind: 'computed', name: this.name });\n }\n }\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n dispose(): void {\n if (this.disposed) return;\n\n this.disposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n this.runtime.clearDependencies(this);\n this.dependents.clear();\n }\n\n private refresh(): boolean {\n if (!this.dirty || this.disposed) return false;\n\n if (this.computing) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleCycleError(`computed cycle detected${label}`);\n }\n\n this.computing = true;\n\n try {\n const next = this.runtime.collect(this, this.derive);\n const changed = this.current === UNSET || !this.equals(this.current as T, next);\n\n this.current = next;\n this.dirty = false;\n\n return changed;\n } finally {\n this.computing = false;\n }\n }\n}\n\nclass ScopeNode implements Scope, Owned {\n readonly disposalController = new AbortController();\n readonly owned = new Set<Owned>();\n readonly name: string | undefined;\n private isDisposed = false;\n private owner: ScopeNode | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n own(value: Owned): void {\n value.setOwner(this);\n this.owned.add(value);\n }\n\n release(value: Owned): void {\n this.owned.delete(value);\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n run<T>(fn: () => T): T {\n if (this.isDisposed) throw new RippleDisposedError('Cannot run a disposed scope.');\n\n return this.runtime.withScope(this, fn);\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n for (const owned of [...this.owned].reverse()) owned.dispose();\n this.owned.clear();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nclass EffectNode implements ObserverNode, EffectHandle {\n readonly dependencies = new Set<Dependency>();\n readonly disposalController = new AbortController();\n private cleanup: Cleanup | undefined;\n private isDisposed = false;\n private ownershipScope: ScopeNode | undefined;\n private runScope: ScopeNode | undefined;\n private readonly callback: () => Cleanup | undefined;\n private readonly options: EffectOptions | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, callback: () => Cleanup | undefined, options?: EffectOptions) {\n this.runtime = runtime;\n this.callback = callback;\n this.options = options;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.ownershipScope = owner;\n }\n\n onDependencyChanged(): void {\n if (this.isDisposed) return;\n\n this.runtime.enqueue(this);\n }\n\n run(): void {\n if (this.isDisposed) return;\n\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runCleanup();\n\n const owner = new ScopeNode(this.runtime);\n\n try {\n const cleanup = this.runtime.withEffectScope(owner, () => this.runtime.collectEffect(this, this.callback));\n\n this.runScope = owner;\n this.cleanup = typeof cleanup === 'function' ? cleanup : undefined;\n if (this.isDisposed) {\n this.runScope.dispose();\n this.runScope = undefined;\n this.runCleanup();\n }\n } catch (error) {\n owner.dispose();\n this.runtime.report(error, { kind: 'effect', name: this.options?.name });\n }\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.ownershipScope?.release(this);\n this.ownershipScope = undefined;\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runtime.clearDependencies(this);\n this.runCleanup();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n private runCleanup(): void {\n const cleanup = this.cleanup;\n\n this.cleanup = undefined;\n\n if (cleanup === undefined) return;\n\n try {\n cleanup();\n } catch (error) {\n this.runtime.report(error, { kind: 'cleanup', name: this.options?.name });\n }\n }\n}\n\n/**\n * A runtime owns reactive graph state and lifetime. Separate instances isolate\n * applications and SSR requests without process-wide mutable hooks.\n */\nexport class ReactiveRuntime {\n private activeEffectScope: ScopeNode | undefined;\n private activeObserver: ObserverNode | undefined;\n private activeScope: ScopeNode;\n private isDisposed = false;\n private flushDepth = 0;\n private flushing = false;\n private readonly pending = new Set<EffectNode>();\n private readonly listeners = new Set<ListenerSink>();\n private readonly rootScope: ScopeNode;\n private readonly onError: (error: unknown, context: RippleErrorContext) => void;\n\n constructor(options?: RippleOptions) {\n this.onError =\n options?.onError ??\n ((error) => {\n queueMicrotask(() => {\n throw error;\n });\n });\n this.rootScope = new ScopeNode(this, 'runtime');\n this.activeScope = this.rootScope;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.rootScope.disposalSignal;\n }\n\n readonly signal = <T>(initial: T, options?: SignalOptions<T>): Signal<T> => {\n this.assertActive();\n\n return new SignalNode(this, initial, options);\n };\n\n readonly computed = <T>(derive: () => T, options?: ComputedOptions<T>): Readable<T> => {\n this.assertActive();\n\n const node = new ComputedNode(this, derive, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n\n return node;\n };\n\n readonly effect = (callback: () => Cleanup | undefined, options?: EffectOptions): EffectHandle => {\n this.assertActive();\n\n const node = new EffectNode(this, callback, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n node.run();\n\n return node;\n };\n\n readonly createScope = (name?: string): Scope => {\n this.assertActive();\n\n const scope = new ScopeNode(this, name);\n\n // Scopes model explicit, caller-owned lifetimes. Unlike effects/computeds created during an\n // effect run, they deliberately attach to the enclosing scope rather than the per-run owner:\n // keyed DOM directives retain item scopes across reconciliation runs and dispose them only\n // when the item itself leaves the collection.\n this.activeScope.own(scope);\n\n return scope;\n };\n\n readonly batch = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.propagate(fn);\n };\n\n readonly untrack = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.withObserver(undefined, fn);\n };\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.rootScope.dispose();\n }\n\n /** Internal node guard: disposed runtimes allow inert reads but reject graph work and mutation. */\n assertActive(): void {\n if (this.isDisposed) throw new RippleDisposedError('Cannot use a disposed Ripple runtime.');\n }\n\n track(node: Dependency): void {\n const observer = this.activeObserver;\n\n if (observer?.collecting === undefined) return;\n\n observer.collecting.add(node);\n }\n\n clearDependencies(observer: ObserverNode): void {\n for (const dependency of observer.dependencies) dependency.dependents.delete(observer);\n observer.dependencies.clear();\n }\n\n collect<T>(observer: ObserverNode, fn: () => T): T {\n // On first run (no prior dependencies), commit partial dependencies even if\n // derive throws, otherwise the computed would have zero dependencies and never\n // be notified when its source changes, permanently stuck until a manual read.\n // On subsequent runs, keep previous dependencies (tested behavior).\n return this.collectWith(observer, fn, observer.dependencies.size === 0);\n }\n\n collectEffect<T>(observer: ObserverNode, fn: () => T): T {\n return this.collectWith(observer, fn, true);\n }\n\n withEffectScope<T>(scope: ScopeNode, fn: () => T): T {\n const previous = this.activeEffectScope;\n\n this.activeEffectScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previous;\n }\n }\n\n withObserver<T>(observer: ObserverNode | undefined, fn: () => T): T {\n const previous = this.activeObserver;\n\n this.activeObserver = observer;\n\n try {\n return fn();\n } finally {\n this.activeObserver = previous;\n }\n }\n\n withScope<T>(scope: ScopeNode, fn: () => T): T {\n const previousEffectScope = this.activeEffectScope;\n const previousScope = this.activeScope;\n\n this.activeEffectScope = undefined;\n this.activeScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previousEffectScope;\n this.activeScope = previousScope;\n }\n }\n\n enqueue(effect: EffectNode): void {\n this.pending.add(effect);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n enqueueListener(sink: ListenerSink): void {\n this.listeners.add(sink);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n propagate<T>(fn: () => T): T {\n this.flushDepth++;\n\n try {\n return fn();\n } finally {\n this.flushDepth--;\n\n if (this.flushDepth === 0) this.flush();\n }\n }\n\n report(error: unknown, context: RippleErrorContext): void {\n try {\n this.onError(error, context);\n } catch {\n // A failing error handler must not corrupt the graph or mask the\n // original failure; the default handler rethrows asynchronously instead.\n }\n }\n\n private collectWith<T>(observer: ObserverNode, fn: () => T, commitOnError: boolean): T {\n const previousObserver = this.activeObserver;\n const dependencies = new Set<Dependency>();\n\n observer.collecting = dependencies;\n this.activeObserver = observer;\n\n try {\n const result = fn();\n\n this.commitDependencies(observer, dependencies);\n\n return result;\n } catch (error) {\n if (commitOnError) this.commitDependencies(observer, dependencies);\n\n throw error;\n } finally {\n observer.collecting = undefined;\n this.activeObserver = previousObserver;\n }\n }\n\n private commitDependencies(observer: ObserverNode, next: Set<Dependency>): void {\n for (const dependency of observer.dependencies) {\n if (!next.has(dependency)) dependency.dependents.delete(observer);\n }\n\n for (const dependency of next) {\n if (!observer.dependencies.has(dependency)) dependency.dependents.add(observer);\n }\n\n observer.dependencies.clear();\n for (const dependency of next) observer.dependencies.add(dependency);\n }\n\n private flush(): void {\n if (this.flushing) return;\n\n this.flushing = true;\n\n let iterations = 0;\n\n try {\n while (this.pending.size > 0 || this.listeners.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // A runaway effect (one that keeps re-triggering itself): report it\n // through the error channel and abandon this flush. Throwing from\n // here would unwind into the innocent write site that happened to\n // trigger the flush, after its value was already applied.\n this.report(new RippleCycleError(`infinite reactive flush (>${MAX_FLUSH_ITERATIONS} iterations)`), {\n kind: 'flush',\n });\n this.pending.clear();\n this.listeners.clear();\n break;\n }\n\n const effects = [...this.pending];\n const listeners = [...this.listeners];\n\n this.pending.clear();\n this.listeners.clear();\n\n for (const effect of effects) effect.run();\n\n for (const sink of listeners) {\n try {\n sink.listener();\n } catch (error) {\n this.report(error, { kind: 'listener' });\n }\n }\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\nexport const isReactive = <T>(value: T | Readable<T>): value is Readable<T> => {\n if (typeof value !== 'object' || value === null || (value as { [REACTIVE]?: unknown })[REACTIVE] !== true)\n return false;\n\n const candidate = value as { peek?: unknown; subscribe?: unknown; value?: unknown };\n\n return 'value' in candidate && typeof candidate.peek === 'function' && typeof candidate.subscribe === 'function';\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.cjs","names":["RippleDisposedError","RippleCycleError","require_errors","REACTIVE","MAX_FLUSH_ITERATIONS","UNSET","runtime","name","sink","dependent","options","initial","next","updater","derive","label","error","owner","changed","value","fn","owned","callback","cleanup","node","scope","observer","dependency","previous","previousEffectScope","previousScope","effect","context","previousObserver","dependencies","result","commitOnError","iterations","effects","listeners","candidate"],"sources":["../src/runtime.ts"],"sourcesContent":["import { RippleCycleError, RippleDisposedError } from './errors';\nimport type {\n Cleanup,\n ComputedOptions,\n EffectHandle,\n EffectOptions,\n Readable,\n RippleErrorContext,\n RippleOptions,\n Scope,\n Signal,\n SignalOptions,\n Unsubscribe,\n} from './types';\n\nexport const REACTIVE = Symbol.for('@vielzeug/ripple/reactive');\nconst MAX_FLUSH_ITERATIONS = 100;\nconst UNSET = Symbol('ripple.unset');\n\ntype Dependency = ReactiveNode<unknown>;\n\n/** Anything that wants to hear that a dependency changed: the only member a producer may call. */\ntype Dependent = {\n onDependencyChanged(): void;\n};\n\n/** A subscribe() listener sink: enqueued as a unit so two subscriptions sharing one\n * callback each fire per flush, while one subscription never fires twice. */\ntype ListenerSink = Dependent & {\n readonly listener: () => void;\n};\n\n/** A dependency-collecting observer (computed or effect). Plain listener sinks need less. */\ntype ObserverNode = Dependent & {\n collecting?: Set<Dependency>;\n readonly dependencies: Set<Dependency>;\n};\n\ntype Owned = {\n dispose(): void;\n setOwner(owner: ScopeNode | undefined): void;\n};\n\nabstract class ReactiveNode<T> {\n readonly [REACTIVE] = true;\n readonly dependents = new Set<Dependent>();\n readonly name: string | undefined;\n protected readonly runtime: ReactiveRuntime;\n\n protected constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n abstract peek(): T;\n abstract get value(): T;\n\n subscribe(listener: () => void): Unsubscribe {\n this.runtime.assertActive();\n this.peek();\n\n // A listener sink never collects dependencies, so it is a bare dependent :\n // no dependency bookkeeping is allocated per subscription.\n const sink: ListenerSink = {\n listener,\n onDependencyChanged: () => this.runtime.enqueueListener(sink),\n };\n\n this.dependents.add(sink);\n\n return () => this.dependents.delete(sink);\n }\n\n protected notify(): void {\n for (const dependent of [...this.dependents]) dependent.onDependencyChanged();\n }\n}\n\nclass SignalNode<T> extends ReactiveNode<T> implements Signal<T> {\n private current: T;\n private readonly equals: (previous: T, next: T) => boolean;\n\n constructor(runtime: ReactiveRuntime, initial: T, options?: SignalOptions<T>) {\n super(runtime, options?.name);\n this.current = initial;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n this.runtime.track(this);\n\n return this.current;\n }\n\n set value(next: T) {\n this.runtime.assertActive();\n\n if (this.equals(this.current, next)) return;\n\n this.current = next;\n this.runtime.propagate(() => this.notify());\n }\n\n peek(): T {\n return this.current;\n }\n\n update(updater: (prev: T) => T): void {\n this.value = updater(this.current);\n }\n}\n\nclass ComputedNode<T> extends ReactiveNode<T> implements ObserverNode, Owned {\n readonly dependencies = new Set<Dependency>();\n private computing = false;\n private disposed = false;\n private dirty = true;\n private current: T | typeof UNSET = UNSET;\n private readonly derive: () => T;\n private readonly equals: (previous: T, next: T) => boolean;\n private owner: ScopeNode | undefined;\n\n constructor(runtime: ReactiveRuntime, derive: () => T, options?: ComputedOptions<T>) {\n super(runtime, options?.name);\n this.derive = derive;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n // Track before refresh: if refresh throws, the consumer still depends on\n // this computed and must be notified when a dependency change might let it\n // succeed. Without this, a first-run failure permanently severs the\n // consumer from the computed.\n this.runtime.track(this);\n this.refresh();\n\n return this.evaluated();\n }\n\n peek(): T {\n this.refresh();\n\n return this.evaluated();\n }\n\n private evaluated(): T {\n if (this.current === UNSET) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleDisposedError(`computed${label} was disposed before it was ever evaluated`);\n }\n\n return this.current as T;\n }\n\n onDependencyChanged(): void {\n if (this.disposed) return;\n\n if (!this.dirty) this.dirty = true;\n\n if (this.dependents.size > 0) {\n try {\n if (this.refresh()) this.notify();\n } catch (error) {\n this.runtime.report(error, { kind: 'computed', name: this.name });\n }\n }\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n dispose(): void {\n if (this.disposed) return;\n\n this.disposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n this.runtime.clearDependencies(this);\n this.dependents.clear();\n }\n\n private refresh(): boolean {\n if (!this.dirty || this.disposed) return false;\n\n if (this.computing) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleCycleError(`computed cycle detected${label}`);\n }\n\n this.computing = true;\n\n try {\n const next = this.runtime.collect(this, this.derive);\n const changed = this.current === UNSET || !this.equals(this.current as T, next);\n\n this.current = next;\n this.dirty = false;\n\n return changed;\n } finally {\n this.computing = false;\n }\n }\n}\n\nclass ScopeNode implements Scope, Owned {\n readonly disposalController = new AbortController();\n readonly owned = new Set<Owned>();\n readonly name: string | undefined;\n private isDisposed = false;\n private owner: ScopeNode | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n own(value: Owned): void {\n value.setOwner(this);\n this.owned.add(value);\n }\n\n release(value: Owned): void {\n this.owned.delete(value);\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n run<T>(fn: () => T): T {\n if (this.isDisposed) throw new RippleDisposedError('Cannot run a disposed scope.');\n\n return this.runtime.withScope(this, fn);\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n for (const owned of [...this.owned].reverse()) owned.dispose();\n this.owned.clear();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nclass EffectNode implements ObserverNode, EffectHandle {\n readonly dependencies = new Set<Dependency>();\n readonly disposalController = new AbortController();\n private cleanup: Cleanup | undefined;\n private isDisposed = false;\n private ownershipScope: ScopeNode | undefined;\n private runScope: ScopeNode | undefined;\n private readonly callback: () => Cleanup | undefined;\n private readonly options: EffectOptions | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, callback: () => Cleanup | undefined, options?: EffectOptions) {\n this.runtime = runtime;\n this.callback = callback;\n this.options = options;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.ownershipScope = owner;\n }\n\n onDependencyChanged(): void {\n if (this.isDisposed) return;\n\n this.runtime.enqueue(this);\n }\n\n run(): void {\n if (this.isDisposed) return;\n\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runCleanup();\n\n const owner = new ScopeNode(this.runtime);\n\n try {\n const cleanup = this.runtime.withEffectScope(owner, () => this.runtime.collectEffect(this, this.callback));\n\n this.runScope = owner;\n this.cleanup = typeof cleanup === 'function' ? cleanup : undefined;\n if (this.isDisposed) {\n this.runScope.dispose();\n this.runScope = undefined;\n this.runCleanup();\n }\n } catch (error) {\n owner.dispose();\n this.runtime.report(error, { kind: 'effect', name: this.options?.name });\n }\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.ownershipScope?.release(this);\n this.ownershipScope = undefined;\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runtime.clearDependencies(this);\n this.runCleanup();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n private runCleanup(): void {\n const cleanup = this.cleanup;\n\n this.cleanup = undefined;\n\n if (cleanup === undefined) return;\n\n try {\n cleanup();\n } catch (error) {\n this.runtime.report(error, { kind: 'cleanup', name: this.options?.name });\n }\n }\n}\n\n/**\n * A runtime owns reactive graph state and lifetime. Separate instances isolate\n * applications and SSR requests without process-wide mutable hooks.\n */\nexport class ReactiveRuntime {\n private activeEffectScope: ScopeNode | undefined;\n private activeObserver: ObserverNode | undefined;\n private activeScope: ScopeNode;\n private isDisposed = false;\n private flushDepth = 0;\n private flushing = false;\n private readonly pending = new Set<EffectNode>();\n private readonly listeners = new Set<ListenerSink>();\n private readonly rootScope: ScopeNode;\n private readonly onError: (error: unknown, context: RippleErrorContext) => void;\n\n constructor(options?: RippleOptions) {\n this.onError =\n options?.onError ??\n ((error) => {\n queueMicrotask(() => {\n throw error;\n });\n });\n this.rootScope = new ScopeNode(this, 'runtime');\n this.activeScope = this.rootScope;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.rootScope.disposalSignal;\n }\n\n readonly signal = <T>(initial: T, options?: SignalOptions<T>): Signal<T> => {\n this.assertActive();\n\n return new SignalNode(this, initial, options);\n };\n\n readonly computed = <T>(derive: () => T, options?: ComputedOptions<T>): Readable<T> => {\n this.assertActive();\n\n const node = new ComputedNode(this, derive, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n\n return node;\n };\n\n readonly effect = (callback: () => Cleanup | undefined, options?: EffectOptions): EffectHandle => {\n this.assertActive();\n\n const node = new EffectNode(this, callback, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n node.run();\n\n return node;\n };\n\n readonly createScope = (name?: string): Scope => {\n this.assertActive();\n\n const scope = new ScopeNode(this, name);\n\n // Scopes model explicit, caller-owned lifetimes. Unlike effects/computeds created during an\n // effect run, they deliberately attach to the enclosing scope rather than the per-run owner:\n // keyed DOM directives retain item scopes across reconciliation runs and dispose them only\n // when the item itself leaves the collection.\n this.activeScope.own(scope);\n\n return scope;\n };\n\n readonly batch = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.propagate(fn);\n };\n\n readonly untrack = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.withObserver(undefined, fn);\n };\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.rootScope.dispose();\n }\n\n /** Internal node guard: disposed runtimes allow inert reads but reject graph work and mutation. */\n assertActive(): void {\n if (this.isDisposed) throw new RippleDisposedError('Cannot use a disposed Ripple runtime.');\n }\n\n track(node: Dependency): void {\n const observer = this.activeObserver;\n\n if (observer?.collecting === undefined) return;\n\n observer.collecting.add(node);\n }\n\n clearDependencies(observer: ObserverNode): void {\n for (const dependency of observer.dependencies) dependency.dependents.delete(observer);\n observer.dependencies.clear();\n }\n\n collect<T>(observer: ObserverNode, fn: () => T): T {\n // On first run (no prior dependencies), commit partial dependencies even if\n // derive throws, otherwise the computed would have zero dependencies and never\n // be notified when its source changes, permanently stuck until a manual read.\n // On subsequent runs, keep previous dependencies (tested behavior).\n return this.collectWith(observer, fn, observer.dependencies.size === 0);\n }\n\n collectEffect<T>(observer: ObserverNode, fn: () => T): T {\n return this.collectWith(observer, fn, true);\n }\n\n withEffectScope<T>(scope: ScopeNode, fn: () => T): T {\n const previous = this.activeEffectScope;\n\n this.activeEffectScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previous;\n }\n }\n\n withObserver<T>(observer: ObserverNode | undefined, fn: () => T): T {\n const previous = this.activeObserver;\n\n this.activeObserver = observer;\n\n try {\n return fn();\n } finally {\n this.activeObserver = previous;\n }\n }\n\n withScope<T>(scope: ScopeNode, fn: () => T): T {\n const previousEffectScope = this.activeEffectScope;\n const previousScope = this.activeScope;\n\n this.activeEffectScope = undefined;\n this.activeScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previousEffectScope;\n this.activeScope = previousScope;\n }\n }\n\n enqueue(effect: EffectNode): void {\n this.pending.add(effect);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n enqueueListener(sink: ListenerSink): void {\n this.listeners.add(sink);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n propagate<T>(fn: () => T): T {\n this.flushDepth++;\n\n try {\n return fn();\n } finally {\n this.flushDepth--;\n\n if (this.flushDepth === 0) this.flush();\n }\n }\n\n report(error: unknown, context: RippleErrorContext): void {\n try {\n this.onError(error, context);\n } catch {\n // A failing error handler must not corrupt the graph or mask the\n // original failure; the default handler rethrows asynchronously instead.\n }\n }\n\n private collectWith<T>(observer: ObserverNode, fn: () => T, commitOnError: boolean): T {\n const previousObserver = this.activeObserver;\n const dependencies = new Set<Dependency>();\n\n observer.collecting = dependencies;\n this.activeObserver = observer;\n\n try {\n const result = fn();\n\n this.commitDependencies(observer, dependencies);\n\n return result;\n } catch (error) {\n if (commitOnError) this.commitDependencies(observer, dependencies);\n\n throw error;\n } finally {\n observer.collecting = undefined;\n this.activeObserver = previousObserver;\n }\n }\n\n private commitDependencies(observer: ObserverNode, next: Set<Dependency>): void {\n for (const dependency of observer.dependencies) {\n if (!next.has(dependency)) dependency.dependents.delete(observer);\n }\n\n for (const dependency of next) {\n if (!observer.dependencies.has(dependency)) dependency.dependents.add(observer);\n }\n\n observer.dependencies.clear();\n for (const dependency of next) observer.dependencies.add(dependency);\n }\n\n private flush(): void {\n if (this.flushing) return;\n\n this.flushing = true;\n\n let iterations = 0;\n\n try {\n while (this.pending.size > 0 || this.listeners.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // A runaway effect (one that keeps re-triggering itself): report it\n // through the error channel and abandon this flush. Throwing from\n // here would unwind into the innocent write site that happened to\n // trigger the flush, after its value was already applied.\n this.report(new RippleCycleError(`infinite reactive flush (>${MAX_FLUSH_ITERATIONS} iterations)`), {\n kind: 'flush',\n });\n this.pending.clear();\n this.listeners.clear();\n break;\n }\n\n const effects = [...this.pending];\n const listeners = [...this.listeners];\n\n this.pending.clear();\n this.listeners.clear();\n\n for (const effect of effects) effect.run();\n\n for (const sink of listeners) {\n try {\n sink.listener();\n } catch (error) {\n this.report(error, { kind: 'listener' });\n }\n }\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\nexport const isReactive = <T>(value: T | Readable<T>): value is Readable<T> => {\n if (typeof value !== 'object' || value === null || (value as { [REACTIVE]?: unknown })[REACTIVE] !== true)\n return false;\n\n const candidate = value as { peek?: unknown; subscribe?: unknown; value?: unknown };\n\n return 'value' in candidate && typeof candidate.peek === 'function' && typeof candidate.subscribe === 'function';\n};\n"],"mappings":"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"}
|
package/dist/runtime.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RippleCycleError as e, RippleDisposedError as t } from "./errors.js";
|
|
2
|
-
|
|
2
|
+
// src/runtime.ts
|
|
3
3
|
var n = Symbol.for("@vielzeug/ripple/reactive"), r = 100, i = Symbol("ripple.unset"), ReactiveNode = class {
|
|
4
4
|
[n] = !0;
|
|
5
5
|
dependents = /* @__PURE__ */ new Set();
|
|
@@ -29,7 +29,7 @@ var n = Symbol.for("@vielzeug/ripple/reactive"), r = 100, i = Symbol("ripple.uns
|
|
|
29
29
|
return this.runtime.track(this), this.current;
|
|
30
30
|
}
|
|
31
31
|
set value(e) {
|
|
32
|
-
this.runtime.assertActive(),
|
|
32
|
+
this.runtime.assertActive(), this.equals(this.current, e) || (this.current = e, this.runtime.propagate(() => this.notify()));
|
|
33
33
|
}
|
|
34
34
|
peek() {
|
|
35
35
|
return this.current;
|
|
@@ -341,7 +341,6 @@ var n = Symbol.for("@vielzeug/ripple/reactive"), r = 100, i = Symbol("ripple.uns
|
|
|
341
341
|
let t = e;
|
|
342
342
|
return "value" in t && typeof t.peek == "function" && typeof t.subscribe == "function";
|
|
343
343
|
};
|
|
344
|
-
//#endregion
|
|
345
344
|
export { n as REACTIVE, ReactiveRuntime, isReactive };
|
|
346
345
|
|
|
347
346
|
//# sourceMappingURL=runtime.js.map
|
package/dist/runtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.js","names":["REACTIVE","MAX_FLUSH_ITERATIONS","UNSET","runtime","name","sink","dependent","options","initial","next","updater","derive","label","RippleDisposedError","error","owner","RippleCycleError","changed","value","fn","owned","callback","cleanup","node","scope","observer","dependency","previous","previousEffectScope","previousScope","effect","context","previousObserver","dependencies","result","commitOnError","iterations","effects","listeners","candidate"],"sources":["../src/runtime.ts"],"sourcesContent":["import { RippleCycleError, RippleDisposedError } from './errors';\nimport type {\n Cleanup,\n ComputedOptions,\n EffectHandle,\n EffectOptions,\n Readable,\n RippleErrorContext,\n RippleOptions,\n Scope,\n Signal,\n SignalOptions,\n Unsubscribe,\n} from './types';\n\nexport const REACTIVE = Symbol.for('@vielzeug/ripple/reactive');\nconst MAX_FLUSH_ITERATIONS = 100;\nconst UNSET = Symbol('ripple.unset');\n\ntype Dependency = ReactiveNode<unknown>;\n\n/** Anything that wants to hear that a dependency changed: the only member a producer may call. */\ntype Dependent = {\n onDependencyChanged(): void;\n};\n\n/** A subscribe() listener sink: enqueued as a unit so two subscriptions sharing one\n * callback each fire per flush, while one subscription never fires twice. */\ntype ListenerSink = Dependent & {\n readonly listener: () => void;\n};\n\n/** A dependency-collecting observer (computed or effect). Plain listener sinks need less. */\ntype ObserverNode = Dependent & {\n collecting?: Set<Dependency>;\n readonly dependencies: Set<Dependency>;\n};\n\ntype Owned = {\n dispose(): void;\n setOwner(owner: ScopeNode | undefined): void;\n};\n\nabstract class ReactiveNode<T> {\n readonly [REACTIVE] = true;\n readonly dependents = new Set<Dependent>();\n readonly name: string | undefined;\n protected readonly runtime: ReactiveRuntime;\n\n protected constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n abstract peek(): T;\n abstract get value(): T;\n\n subscribe(listener: () => void): Unsubscribe {\n this.runtime.assertActive();\n this.peek();\n\n // A listener sink never collects dependencies, so it is a bare dependent :\n // no dependency bookkeeping is allocated per subscription.\n const sink: ListenerSink = {\n listener,\n onDependencyChanged: () => this.runtime.enqueueListener(sink),\n };\n\n this.dependents.add(sink);\n\n return () => this.dependents.delete(sink);\n }\n\n protected notify(): void {\n for (const dependent of [...this.dependents]) dependent.onDependencyChanged();\n }\n}\n\nclass SignalNode<T> extends ReactiveNode<T> implements Signal<T> {\n private current: T;\n private readonly equals: (previous: T, next: T) => boolean;\n\n constructor(runtime: ReactiveRuntime, initial: T, options?: SignalOptions<T>) {\n super(runtime, options?.name);\n this.current = initial;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n this.runtime.track(this);\n\n return this.current;\n }\n\n set value(next: T) {\n this.runtime.assertActive();\n\n if (this.equals(this.current, next)) return;\n\n this.current = next;\n this.runtime.propagate(() => this.notify());\n }\n\n peek(): T {\n return this.current;\n }\n\n update(updater: (prev: T) => T): void {\n this.value = updater(this.current);\n }\n}\n\nclass ComputedNode<T> extends ReactiveNode<T> implements ObserverNode, Owned {\n readonly dependencies = new Set<Dependency>();\n private computing = false;\n private disposed = false;\n private dirty = true;\n private current: T | typeof UNSET = UNSET;\n private readonly derive: () => T;\n private readonly equals: (previous: T, next: T) => boolean;\n private owner: ScopeNode | undefined;\n\n constructor(runtime: ReactiveRuntime, derive: () => T, options?: ComputedOptions<T>) {\n super(runtime, options?.name);\n this.derive = derive;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n // Track before refresh: if refresh throws, the consumer still depends on\n // this computed and must be notified when a dependency change might let it\n // succeed. Without this, a first-run failure permanently severs the\n // consumer from the computed.\n this.runtime.track(this);\n this.refresh();\n\n return this.evaluated();\n }\n\n peek(): T {\n this.refresh();\n\n return this.evaluated();\n }\n\n private evaluated(): T {\n if (this.current === UNSET) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleDisposedError(`computed${label} was disposed before it was ever evaluated`);\n }\n\n return this.current as T;\n }\n\n onDependencyChanged(): void {\n if (this.disposed) return;\n\n if (!this.dirty) this.dirty = true;\n\n if (this.dependents.size > 0) {\n try {\n if (this.refresh()) this.notify();\n } catch (error) {\n this.runtime.report(error, { kind: 'computed', name: this.name });\n }\n }\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n dispose(): void {\n if (this.disposed) return;\n\n this.disposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n this.runtime.clearDependencies(this);\n this.dependents.clear();\n }\n\n private refresh(): boolean {\n if (!this.dirty || this.disposed) return false;\n\n if (this.computing) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleCycleError(`computed cycle detected${label}`);\n }\n\n this.computing = true;\n\n try {\n const next = this.runtime.collect(this, this.derive);\n const changed = this.current === UNSET || !this.equals(this.current as T, next);\n\n this.current = next;\n this.dirty = false;\n\n return changed;\n } finally {\n this.computing = false;\n }\n }\n}\n\nclass ScopeNode implements Scope, Owned {\n readonly disposalController = new AbortController();\n readonly owned = new Set<Owned>();\n readonly name: string | undefined;\n private isDisposed = false;\n private owner: ScopeNode | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n own(value: Owned): void {\n value.setOwner(this);\n this.owned.add(value);\n }\n\n release(value: Owned): void {\n this.owned.delete(value);\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n run<T>(fn: () => T): T {\n if (this.isDisposed) throw new RippleDisposedError('Cannot run a disposed scope.');\n\n return this.runtime.withScope(this, fn);\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n for (const owned of [...this.owned].reverse()) owned.dispose();\n this.owned.clear();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nclass EffectNode implements ObserverNode, EffectHandle {\n readonly dependencies = new Set<Dependency>();\n readonly disposalController = new AbortController();\n private cleanup: Cleanup | undefined;\n private isDisposed = false;\n private ownershipScope: ScopeNode | undefined;\n private runScope: ScopeNode | undefined;\n private readonly callback: () => Cleanup | undefined;\n private readonly options: EffectOptions | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, callback: () => Cleanup | undefined, options?: EffectOptions) {\n this.runtime = runtime;\n this.callback = callback;\n this.options = options;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.ownershipScope = owner;\n }\n\n onDependencyChanged(): void {\n if (this.isDisposed) return;\n\n this.runtime.enqueue(this);\n }\n\n run(): void {\n if (this.isDisposed) return;\n\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runCleanup();\n\n const owner = new ScopeNode(this.runtime);\n\n try {\n const cleanup = this.runtime.withEffectScope(owner, () => this.runtime.collectEffect(this, this.callback));\n\n this.runScope = owner;\n this.cleanup = typeof cleanup === 'function' ? cleanup : undefined;\n if (this.isDisposed) {\n this.runScope.dispose();\n this.runScope = undefined;\n this.runCleanup();\n }\n } catch (error) {\n owner.dispose();\n this.runtime.report(error, { kind: 'effect', name: this.options?.name });\n }\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.ownershipScope?.release(this);\n this.ownershipScope = undefined;\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runtime.clearDependencies(this);\n this.runCleanup();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n private runCleanup(): void {\n const cleanup = this.cleanup;\n\n this.cleanup = undefined;\n\n if (cleanup === undefined) return;\n\n try {\n cleanup();\n } catch (error) {\n this.runtime.report(error, { kind: 'cleanup', name: this.options?.name });\n }\n }\n}\n\n/**\n * A runtime owns reactive graph state and lifetime. Separate instances isolate\n * applications and SSR requests without process-wide mutable hooks.\n */\nexport class ReactiveRuntime {\n private activeEffectScope: ScopeNode | undefined;\n private activeObserver: ObserverNode | undefined;\n private activeScope: ScopeNode;\n private isDisposed = false;\n private flushDepth = 0;\n private flushing = false;\n private readonly pending = new Set<EffectNode>();\n private readonly listeners = new Set<ListenerSink>();\n private readonly rootScope: ScopeNode;\n private readonly onError: (error: unknown, context: RippleErrorContext) => void;\n\n constructor(options?: RippleOptions) {\n this.onError =\n options?.onError ??\n ((error) => {\n queueMicrotask(() => {\n throw error;\n });\n });\n this.rootScope = new ScopeNode(this, 'runtime');\n this.activeScope = this.rootScope;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.rootScope.disposalSignal;\n }\n\n readonly signal = <T>(initial: T, options?: SignalOptions<T>): Signal<T> => {\n this.assertActive();\n\n return new SignalNode(this, initial, options);\n };\n\n readonly computed = <T>(derive: () => T, options?: ComputedOptions<T>): Readable<T> => {\n this.assertActive();\n\n const node = new ComputedNode(this, derive, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n\n return node;\n };\n\n readonly effect = (callback: () => Cleanup | undefined, options?: EffectOptions): EffectHandle => {\n this.assertActive();\n\n const node = new EffectNode(this, callback, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n node.run();\n\n return node;\n };\n\n readonly createScope = (name?: string): Scope => {\n this.assertActive();\n\n const scope = new ScopeNode(this, name);\n\n // Scopes model explicit, caller-owned lifetimes. Unlike effects/computeds created during an\n // effect run, they deliberately attach to the enclosing scope rather than the per-run owner:\n // keyed DOM directives retain item scopes across reconciliation runs and dispose them only\n // when the item itself leaves the collection.\n this.activeScope.own(scope);\n\n return scope;\n };\n\n readonly batch = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.propagate(fn);\n };\n\n readonly untrack = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.withObserver(undefined, fn);\n };\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.rootScope.dispose();\n }\n\n /** Internal node guard: disposed runtimes allow inert reads but reject graph work and mutation. */\n assertActive(): void {\n if (this.isDisposed) throw new RippleDisposedError('Cannot use a disposed Ripple runtime.');\n }\n\n track(node: Dependency): void {\n const observer = this.activeObserver;\n\n if (observer?.collecting === undefined) return;\n\n observer.collecting.add(node);\n }\n\n clearDependencies(observer: ObserverNode): void {\n for (const dependency of observer.dependencies) dependency.dependents.delete(observer);\n observer.dependencies.clear();\n }\n\n collect<T>(observer: ObserverNode, fn: () => T): T {\n // On first run (no prior dependencies), commit partial dependencies even if\n // derive throws, otherwise the computed would have zero dependencies and never\n // be notified when its source changes, permanently stuck until a manual read.\n // On subsequent runs, keep previous dependencies (tested behavior).\n return this.collectWith(observer, fn, observer.dependencies.size === 0);\n }\n\n collectEffect<T>(observer: ObserverNode, fn: () => T): T {\n return this.collectWith(observer, fn, true);\n }\n\n withEffectScope<T>(scope: ScopeNode, fn: () => T): T {\n const previous = this.activeEffectScope;\n\n this.activeEffectScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previous;\n }\n }\n\n withObserver<T>(observer: ObserverNode | undefined, fn: () => T): T {\n const previous = this.activeObserver;\n\n this.activeObserver = observer;\n\n try {\n return fn();\n } finally {\n this.activeObserver = previous;\n }\n }\n\n withScope<T>(scope: ScopeNode, fn: () => T): T {\n const previousEffectScope = this.activeEffectScope;\n const previousScope = this.activeScope;\n\n this.activeEffectScope = undefined;\n this.activeScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previousEffectScope;\n this.activeScope = previousScope;\n }\n }\n\n enqueue(effect: EffectNode): void {\n this.pending.add(effect);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n enqueueListener(sink: ListenerSink): void {\n this.listeners.add(sink);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n propagate<T>(fn: () => T): T {\n this.flushDepth++;\n\n try {\n return fn();\n } finally {\n this.flushDepth--;\n\n if (this.flushDepth === 0) this.flush();\n }\n }\n\n report(error: unknown, context: RippleErrorContext): void {\n try {\n this.onError(error, context);\n } catch {\n // A failing error handler must not corrupt the graph or mask the\n // original failure; the default handler rethrows asynchronously instead.\n }\n }\n\n private collectWith<T>(observer: ObserverNode, fn: () => T, commitOnError: boolean): T {\n const previousObserver = this.activeObserver;\n const dependencies = new Set<Dependency>();\n\n observer.collecting = dependencies;\n this.activeObserver = observer;\n\n try {\n const result = fn();\n\n this.commitDependencies(observer, dependencies);\n\n return result;\n } catch (error) {\n if (commitOnError) this.commitDependencies(observer, dependencies);\n\n throw error;\n } finally {\n observer.collecting = undefined;\n this.activeObserver = previousObserver;\n }\n }\n\n private commitDependencies(observer: ObserverNode, next: Set<Dependency>): void {\n for (const dependency of observer.dependencies) {\n if (!next.has(dependency)) dependency.dependents.delete(observer);\n }\n\n for (const dependency of next) {\n if (!observer.dependencies.has(dependency)) dependency.dependents.add(observer);\n }\n\n observer.dependencies.clear();\n for (const dependency of next) observer.dependencies.add(dependency);\n }\n\n private flush(): void {\n if (this.flushing) return;\n\n this.flushing = true;\n\n let iterations = 0;\n\n try {\n while (this.pending.size > 0 || this.listeners.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // A runaway effect (one that keeps re-triggering itself): report it\n // through the error channel and abandon this flush. Throwing from\n // here would unwind into the innocent write site that happened to\n // trigger the flush, after its value was already applied.\n this.report(new RippleCycleError(`infinite reactive flush (>${MAX_FLUSH_ITERATIONS} iterations)`), {\n kind: 'flush',\n });\n this.pending.clear();\n this.listeners.clear();\n break;\n }\n\n const effects = [...this.pending];\n const listeners = [...this.listeners];\n\n this.pending.clear();\n this.listeners.clear();\n\n for (const effect of effects) effect.run();\n\n for (const sink of listeners) {\n try {\n sink.listener();\n } catch (error) {\n this.report(error, { kind: 'listener' });\n }\n }\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\nexport const isReactive = <T>(value: T | Readable<T>): value is Readable<T> => {\n if (typeof value !== 'object' || value === null || (value as { [REACTIVE]?: unknown })[REACTIVE] !== true)\n return false;\n\n const candidate = value as { peek?: unknown; subscribe?: unknown; value?: unknown };\n\n return 'value' in candidate && typeof candidate.peek === 'function' && typeof candidate.subscribe === 'function';\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runtime.js","names":["REACTIVE","MAX_FLUSH_ITERATIONS","UNSET","runtime","name","sink","dependent","options","initial","next","updater","derive","label","RippleDisposedError","error","owner","RippleCycleError","changed","value","fn","owned","callback","cleanup","node","scope","observer","dependency","previous","previousEffectScope","previousScope","effect","context","previousObserver","dependencies","result","commitOnError","iterations","effects","listeners","candidate"],"sources":["../src/runtime.ts"],"sourcesContent":["import { RippleCycleError, RippleDisposedError } from './errors';\nimport type {\n Cleanup,\n ComputedOptions,\n EffectHandle,\n EffectOptions,\n Readable,\n RippleErrorContext,\n RippleOptions,\n Scope,\n Signal,\n SignalOptions,\n Unsubscribe,\n} from './types';\n\nexport const REACTIVE = Symbol.for('@vielzeug/ripple/reactive');\nconst MAX_FLUSH_ITERATIONS = 100;\nconst UNSET = Symbol('ripple.unset');\n\ntype Dependency = ReactiveNode<unknown>;\n\n/** Anything that wants to hear that a dependency changed: the only member a producer may call. */\ntype Dependent = {\n onDependencyChanged(): void;\n};\n\n/** A subscribe() listener sink: enqueued as a unit so two subscriptions sharing one\n * callback each fire per flush, while one subscription never fires twice. */\ntype ListenerSink = Dependent & {\n readonly listener: () => void;\n};\n\n/** A dependency-collecting observer (computed or effect). Plain listener sinks need less. */\ntype ObserverNode = Dependent & {\n collecting?: Set<Dependency>;\n readonly dependencies: Set<Dependency>;\n};\n\ntype Owned = {\n dispose(): void;\n setOwner(owner: ScopeNode | undefined): void;\n};\n\nabstract class ReactiveNode<T> {\n readonly [REACTIVE] = true;\n readonly dependents = new Set<Dependent>();\n readonly name: string | undefined;\n protected readonly runtime: ReactiveRuntime;\n\n protected constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n abstract peek(): T;\n abstract get value(): T;\n\n subscribe(listener: () => void): Unsubscribe {\n this.runtime.assertActive();\n this.peek();\n\n // A listener sink never collects dependencies, so it is a bare dependent :\n // no dependency bookkeeping is allocated per subscription.\n const sink: ListenerSink = {\n listener,\n onDependencyChanged: () => this.runtime.enqueueListener(sink),\n };\n\n this.dependents.add(sink);\n\n return () => this.dependents.delete(sink);\n }\n\n protected notify(): void {\n for (const dependent of [...this.dependents]) dependent.onDependencyChanged();\n }\n}\n\nclass SignalNode<T> extends ReactiveNode<T> implements Signal<T> {\n private current: T;\n private readonly equals: (previous: T, next: T) => boolean;\n\n constructor(runtime: ReactiveRuntime, initial: T, options?: SignalOptions<T>) {\n super(runtime, options?.name);\n this.current = initial;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n this.runtime.track(this);\n\n return this.current;\n }\n\n set value(next: T) {\n this.runtime.assertActive();\n\n if (this.equals(this.current, next)) return;\n\n this.current = next;\n this.runtime.propagate(() => this.notify());\n }\n\n peek(): T {\n return this.current;\n }\n\n update(updater: (prev: T) => T): void {\n this.value = updater(this.current);\n }\n}\n\nclass ComputedNode<T> extends ReactiveNode<T> implements ObserverNode, Owned {\n readonly dependencies = new Set<Dependency>();\n private computing = false;\n private disposed = false;\n private dirty = true;\n private current: T | typeof UNSET = UNSET;\n private readonly derive: () => T;\n private readonly equals: (previous: T, next: T) => boolean;\n private owner: ScopeNode | undefined;\n\n constructor(runtime: ReactiveRuntime, derive: () => T, options?: ComputedOptions<T>) {\n super(runtime, options?.name);\n this.derive = derive;\n this.equals = options?.equals ?? Object.is;\n }\n\n get value(): T {\n // Track before refresh: if refresh throws, the consumer still depends on\n // this computed and must be notified when a dependency change might let it\n // succeed. Without this, a first-run failure permanently severs the\n // consumer from the computed.\n this.runtime.track(this);\n this.refresh();\n\n return this.evaluated();\n }\n\n peek(): T {\n this.refresh();\n\n return this.evaluated();\n }\n\n private evaluated(): T {\n if (this.current === UNSET) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleDisposedError(`computed${label} was disposed before it was ever evaluated`);\n }\n\n return this.current as T;\n }\n\n onDependencyChanged(): void {\n if (this.disposed) return;\n\n if (!this.dirty) this.dirty = true;\n\n if (this.dependents.size > 0) {\n try {\n if (this.refresh()) this.notify();\n } catch (error) {\n this.runtime.report(error, { kind: 'computed', name: this.name });\n }\n }\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n dispose(): void {\n if (this.disposed) return;\n\n this.disposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n this.runtime.clearDependencies(this);\n this.dependents.clear();\n }\n\n private refresh(): boolean {\n if (!this.dirty || this.disposed) return false;\n\n if (this.computing) {\n const label = this.name === undefined ? '' : ` \"${this.name}\"`;\n\n throw new RippleCycleError(`computed cycle detected${label}`);\n }\n\n this.computing = true;\n\n try {\n const next = this.runtime.collect(this, this.derive);\n const changed = this.current === UNSET || !this.equals(this.current as T, next);\n\n this.current = next;\n this.dirty = false;\n\n return changed;\n } finally {\n this.computing = false;\n }\n }\n}\n\nclass ScopeNode implements Scope, Owned {\n readonly disposalController = new AbortController();\n readonly owned = new Set<Owned>();\n readonly name: string | undefined;\n private isDisposed = false;\n private owner: ScopeNode | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, name?: string) {\n this.runtime = runtime;\n this.name = name;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n own(value: Owned): void {\n value.setOwner(this);\n this.owned.add(value);\n }\n\n release(value: Owned): void {\n this.owned.delete(value);\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.owner = owner;\n }\n\n run<T>(fn: () => T): T {\n if (this.isDisposed) throw new RippleDisposedError('Cannot run a disposed scope.');\n\n return this.runtime.withScope(this, fn);\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.owner?.release(this);\n this.owner = undefined;\n for (const owned of [...this.owned].reverse()) owned.dispose();\n this.owned.clear();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n}\n\nclass EffectNode implements ObserverNode, EffectHandle {\n readonly dependencies = new Set<Dependency>();\n readonly disposalController = new AbortController();\n private cleanup: Cleanup | undefined;\n private isDisposed = false;\n private ownershipScope: ScopeNode | undefined;\n private runScope: ScopeNode | undefined;\n private readonly callback: () => Cleanup | undefined;\n private readonly options: EffectOptions | undefined;\n private readonly runtime: ReactiveRuntime;\n\n constructor(runtime: ReactiveRuntime, callback: () => Cleanup | undefined, options?: EffectOptions) {\n this.runtime = runtime;\n this.callback = callback;\n this.options = options;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.disposalController.signal;\n }\n\n setOwner(owner: ScopeNode | undefined): void {\n this.ownershipScope = owner;\n }\n\n onDependencyChanged(): void {\n if (this.isDisposed) return;\n\n this.runtime.enqueue(this);\n }\n\n run(): void {\n if (this.isDisposed) return;\n\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runCleanup();\n\n const owner = new ScopeNode(this.runtime);\n\n try {\n const cleanup = this.runtime.withEffectScope(owner, () => this.runtime.collectEffect(this, this.callback));\n\n this.runScope = owner;\n this.cleanup = typeof cleanup === 'function' ? cleanup : undefined;\n if (this.isDisposed) {\n this.runScope.dispose();\n this.runScope = undefined;\n this.runCleanup();\n }\n } catch (error) {\n owner.dispose();\n this.runtime.report(error, { kind: 'effect', name: this.options?.name });\n }\n }\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.ownershipScope?.release(this);\n this.ownershipScope = undefined;\n this.runScope?.dispose();\n this.runScope = undefined;\n this.runtime.clearDependencies(this);\n this.runCleanup();\n this.disposalController.abort();\n }\n\n [Symbol.dispose](): void {\n this.dispose();\n }\n\n private runCleanup(): void {\n const cleanup = this.cleanup;\n\n this.cleanup = undefined;\n\n if (cleanup === undefined) return;\n\n try {\n cleanup();\n } catch (error) {\n this.runtime.report(error, { kind: 'cleanup', name: this.options?.name });\n }\n }\n}\n\n/**\n * A runtime owns reactive graph state and lifetime. Separate instances isolate\n * applications and SSR requests without process-wide mutable hooks.\n */\nexport class ReactiveRuntime {\n private activeEffectScope: ScopeNode | undefined;\n private activeObserver: ObserverNode | undefined;\n private activeScope: ScopeNode;\n private isDisposed = false;\n private flushDepth = 0;\n private flushing = false;\n private readonly pending = new Set<EffectNode>();\n private readonly listeners = new Set<ListenerSink>();\n private readonly rootScope: ScopeNode;\n private readonly onError: (error: unknown, context: RippleErrorContext) => void;\n\n constructor(options?: RippleOptions) {\n this.onError =\n options?.onError ??\n ((error) => {\n queueMicrotask(() => {\n throw error;\n });\n });\n this.rootScope = new ScopeNode(this, 'runtime');\n this.activeScope = this.rootScope;\n }\n\n get disposed(): boolean {\n return this.isDisposed;\n }\n\n get disposalSignal(): AbortSignal {\n return this.rootScope.disposalSignal;\n }\n\n readonly signal = <T>(initial: T, options?: SignalOptions<T>): Signal<T> => {\n this.assertActive();\n\n return new SignalNode(this, initial, options);\n };\n\n readonly computed = <T>(derive: () => T, options?: ComputedOptions<T>): Readable<T> => {\n this.assertActive();\n\n const node = new ComputedNode(this, derive, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n\n return node;\n };\n\n readonly effect = (callback: () => Cleanup | undefined, options?: EffectOptions): EffectHandle => {\n this.assertActive();\n\n const node = new EffectNode(this, callback, options);\n\n (this.activeEffectScope ?? this.activeScope).own(node);\n node.run();\n\n return node;\n };\n\n readonly createScope = (name?: string): Scope => {\n this.assertActive();\n\n const scope = new ScopeNode(this, name);\n\n // Scopes model explicit, caller-owned lifetimes. Unlike effects/computeds created during an\n // effect run, they deliberately attach to the enclosing scope rather than the per-run owner:\n // keyed DOM directives retain item scopes across reconciliation runs and dispose them only\n // when the item itself leaves the collection.\n this.activeScope.own(scope);\n\n return scope;\n };\n\n readonly batch = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.propagate(fn);\n };\n\n readonly untrack = <T>(fn: () => T): T => {\n this.assertActive();\n\n return this.withObserver(undefined, fn);\n };\n\n dispose(): void {\n if (this.isDisposed) return;\n\n this.isDisposed = true;\n this.rootScope.dispose();\n }\n\n /** Internal node guard: disposed runtimes allow inert reads but reject graph work and mutation. */\n assertActive(): void {\n if (this.isDisposed) throw new RippleDisposedError('Cannot use a disposed Ripple runtime.');\n }\n\n track(node: Dependency): void {\n const observer = this.activeObserver;\n\n if (observer?.collecting === undefined) return;\n\n observer.collecting.add(node);\n }\n\n clearDependencies(observer: ObserverNode): void {\n for (const dependency of observer.dependencies) dependency.dependents.delete(observer);\n observer.dependencies.clear();\n }\n\n collect<T>(observer: ObserverNode, fn: () => T): T {\n // On first run (no prior dependencies), commit partial dependencies even if\n // derive throws, otherwise the computed would have zero dependencies and never\n // be notified when its source changes, permanently stuck until a manual read.\n // On subsequent runs, keep previous dependencies (tested behavior).\n return this.collectWith(observer, fn, observer.dependencies.size === 0);\n }\n\n collectEffect<T>(observer: ObserverNode, fn: () => T): T {\n return this.collectWith(observer, fn, true);\n }\n\n withEffectScope<T>(scope: ScopeNode, fn: () => T): T {\n const previous = this.activeEffectScope;\n\n this.activeEffectScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previous;\n }\n }\n\n withObserver<T>(observer: ObserverNode | undefined, fn: () => T): T {\n const previous = this.activeObserver;\n\n this.activeObserver = observer;\n\n try {\n return fn();\n } finally {\n this.activeObserver = previous;\n }\n }\n\n withScope<T>(scope: ScopeNode, fn: () => T): T {\n const previousEffectScope = this.activeEffectScope;\n const previousScope = this.activeScope;\n\n this.activeEffectScope = undefined;\n this.activeScope = scope;\n\n try {\n return fn();\n } finally {\n this.activeEffectScope = previousEffectScope;\n this.activeScope = previousScope;\n }\n }\n\n enqueue(effect: EffectNode): void {\n this.pending.add(effect);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n enqueueListener(sink: ListenerSink): void {\n this.listeners.add(sink);\n\n if (this.flushDepth === 0) this.flush();\n }\n\n propagate<T>(fn: () => T): T {\n this.flushDepth++;\n\n try {\n return fn();\n } finally {\n this.flushDepth--;\n\n if (this.flushDepth === 0) this.flush();\n }\n }\n\n report(error: unknown, context: RippleErrorContext): void {\n try {\n this.onError(error, context);\n } catch {\n // A failing error handler must not corrupt the graph or mask the\n // original failure; the default handler rethrows asynchronously instead.\n }\n }\n\n private collectWith<T>(observer: ObserverNode, fn: () => T, commitOnError: boolean): T {\n const previousObserver = this.activeObserver;\n const dependencies = new Set<Dependency>();\n\n observer.collecting = dependencies;\n this.activeObserver = observer;\n\n try {\n const result = fn();\n\n this.commitDependencies(observer, dependencies);\n\n return result;\n } catch (error) {\n if (commitOnError) this.commitDependencies(observer, dependencies);\n\n throw error;\n } finally {\n observer.collecting = undefined;\n this.activeObserver = previousObserver;\n }\n }\n\n private commitDependencies(observer: ObserverNode, next: Set<Dependency>): void {\n for (const dependency of observer.dependencies) {\n if (!next.has(dependency)) dependency.dependents.delete(observer);\n }\n\n for (const dependency of next) {\n if (!observer.dependencies.has(dependency)) dependency.dependents.add(observer);\n }\n\n observer.dependencies.clear();\n for (const dependency of next) observer.dependencies.add(dependency);\n }\n\n private flush(): void {\n if (this.flushing) return;\n\n this.flushing = true;\n\n let iterations = 0;\n\n try {\n while (this.pending.size > 0 || this.listeners.size > 0) {\n if (++iterations > MAX_FLUSH_ITERATIONS) {\n // A runaway effect (one that keeps re-triggering itself): report it\n // through the error channel and abandon this flush. Throwing from\n // here would unwind into the innocent write site that happened to\n // trigger the flush, after its value was already applied.\n this.report(new RippleCycleError(`infinite reactive flush (>${MAX_FLUSH_ITERATIONS} iterations)`), {\n kind: 'flush',\n });\n this.pending.clear();\n this.listeners.clear();\n break;\n }\n\n const effects = [...this.pending];\n const listeners = [...this.listeners];\n\n this.pending.clear();\n this.listeners.clear();\n\n for (const effect of effects) effect.run();\n\n for (const sink of listeners) {\n try {\n sink.listener();\n } catch (error) {\n this.report(error, { kind: 'listener' });\n }\n }\n }\n } finally {\n this.flushing = false;\n }\n }\n}\n\nexport const isReactive = <T>(value: T | Readable<T>): value is Readable<T> => {\n if (typeof value !== 'object' || value === null || (value as { [REACTIVE]?: unknown })[REACTIVE] !== true)\n return false;\n\n const candidate = value as { peek?: unknown; subscribe?: unknown; value?: unknown };\n\n return 'value' in candidate && typeof candidate.peek === 'function' && typeof candidate.subscribe === 'function';\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vielzeug/ripple",
|
|
3
|
-
"version": "26.11.
|
|
3
|
+
"version": "26.11.6",
|
|
4
4
|
"description": "Reactive runtime primitives: signals, derived values, effects, scopes, watchers, and async resources",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,20 +31,20 @@
|
|
|
31
31
|
"registry": "https://registry.npmjs.org/"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
|
-
"build": "vite build &&
|
|
34
|
+
"build": "vite build && bun run --silent build:bundle && bun run --silent build:types && bun run --silent verify:exports",
|
|
35
35
|
"build:bundle": "vite build --config vite.bundle.config.ts",
|
|
36
36
|
"build:types": "tsc -p tsconfig.declarations.json",
|
|
37
37
|
"fix": "biome check --write src",
|
|
38
38
|
"lint": "biome ci src",
|
|
39
|
-
"prepublishOnly": "
|
|
39
|
+
"prepublishOnly": "bun run --silent build",
|
|
40
40
|
"preview": "vite preview",
|
|
41
41
|
"test": "vitest",
|
|
42
|
-
"verify:exports": "
|
|
42
|
+
"verify:exports": "bun verify-exports.mjs"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@types/node": "^26.6.
|
|
45
|
+
"@types/node": "^26.6.6",
|
|
46
46
|
"typescript": "^6.0.3",
|
|
47
|
-
"vite": "^8.3.
|
|
48
|
-
"vitest": "^5.0
|
|
47
|
+
"vite": "^8.3.6",
|
|
48
|
+
"vitest": "^5.1.0"
|
|
49
49
|
}
|
|
50
50
|
}
|