@elurjs/core 3.6.1 → 3.7.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/async.cjs +1 -1
- package/dist/lib/async.js +1 -1
- package/dist/lib/component.cjs +1 -2
- package/dist/lib/component.js +2 -66
- package/dist/lib/context.cjs +1 -1
- package/dist/lib/context.js +35 -23
- package/dist/lib/devtools.cjs +1 -1
- package/dist/lib/devtools.js +1 -1
- package/dist/lib/elur/async.cjs +1 -1
- package/dist/lib/elur/async.cjs.map +1 -1
- package/dist/lib/elur/async.js +1 -1
- package/dist/lib/elur/component.cjs +1 -2
- package/dist/lib/elur/component.d.cts +1 -17
- package/dist/lib/elur/component.d.ts +1 -17
- package/dist/lib/elur/component.js +2 -66
- package/dist/lib/elur/context.cjs +1 -1
- package/dist/lib/elur/context.cjs.map +1 -1
- package/dist/lib/elur/context.d.cts +5 -0
- package/dist/lib/elur/context.d.ts +5 -0
- package/dist/lib/elur/context.js +35 -23
- package/dist/lib/elur/context.js.map +1 -1
- package/dist/lib/elur/devtools.cjs +1 -1
- package/dist/lib/elur/devtools.cjs.map +1 -1
- package/dist/lib/elur/devtools.js +1 -1
- package/dist/lib/elur/form.cjs +1 -1
- package/dist/lib/elur/form.cjs.map +1 -1
- package/dist/lib/elur/form.js +1 -1
- package/dist/lib/elur/hydrate/index.cjs +1 -2
- package/dist/lib/elur/hydrate/index.d.cts +1 -13
- package/dist/lib/elur/hydrate/index.d.ts +1 -13
- package/dist/lib/elur/hydrate/index.js +2 -410
- package/dist/lib/elur/lifecycle.cjs +1 -1
- package/dist/lib/elur/lifecycle.cjs.map +1 -1
- package/dist/lib/elur/lifecycle.d.cts +27 -0
- package/dist/lib/elur/lifecycle.d.ts +27 -0
- package/dist/lib/elur/lifecycle.js +4 -0
- package/dist/lib/elur/lifecycle.js.map +1 -1
- package/dist/lib/elur/next/component.d.cts +281 -0
- package/dist/lib/elur/next/component.d.ts +281 -0
- package/dist/lib/elur/next/dom-write.d.cts +18 -0
- package/dist/lib/elur/next/dom-write.d.ts +18 -0
- package/dist/lib/elur/next/hydrate.d.cts +14 -0
- package/dist/lib/elur/next/hydrate.d.ts +14 -0
- package/dist/lib/elur/next/keyed-diff.d.cts +46 -0
- package/dist/lib/elur/next/keyed-diff.d.ts +46 -0
- package/dist/lib/elur/next/mount-helpers.d.cts +25 -0
- package/dist/lib/elur/next/mount-helpers.d.ts +25 -0
- package/dist/lib/elur/next/mount.d.cts +29 -0
- package/dist/lib/elur/next/mount.d.ts +29 -0
- package/dist/lib/elur/next/node-binding.d.cts +6 -0
- package/dist/lib/elur/next/node-binding.d.ts +6 -0
- package/dist/lib/elur/next/reactivity.d.cts +193 -0
- package/dist/lib/elur/next/reactivity.d.ts +193 -0
- package/dist/lib/elur/next-2/component.cjs +2 -0
- package/dist/lib/elur/next-2/component.cjs.map +1 -0
- package/dist/lib/elur/next-2/component.d.cts +283 -0
- package/dist/lib/elur/next-2/component.d.ts +283 -0
- package/dist/lib/elur/next-2/component.js +340 -0
- package/dist/lib/elur/next-2/component.js.map +1 -0
- package/dist/lib/elur/next-2/dom-write.cjs +2 -0
- package/dist/lib/elur/next-2/dom-write.cjs.map +1 -0
- package/dist/lib/elur/next-2/dom-write.d.cts +18 -0
- package/dist/lib/elur/next-2/dom-write.d.ts +18 -0
- package/dist/lib/elur/next-2/dom-write.js +31 -0
- package/dist/lib/elur/next-2/dom-write.js.map +1 -0
- package/dist/lib/elur/next-2/hydrate.cjs +2 -0
- package/dist/lib/elur/next-2/hydrate.cjs.map +1 -0
- package/dist/lib/elur/next-2/hydrate.d.cts +30 -0
- package/dist/lib/elur/next-2/hydrate.d.ts +30 -0
- package/dist/lib/elur/next-2/hydrate.js +449 -0
- package/dist/lib/elur/next-2/hydrate.js.map +1 -0
- package/dist/lib/elur/next-2/keyed-diff.cjs +2 -0
- package/dist/lib/elur/next-2/keyed-diff.cjs.map +1 -0
- package/dist/lib/elur/next-2/keyed-diff.d.cts +79 -0
- package/dist/lib/elur/next-2/keyed-diff.d.ts +79 -0
- package/dist/lib/elur/next-2/keyed-diff.js +208 -0
- package/dist/lib/elur/next-2/keyed-diff.js.map +1 -0
- package/dist/lib/elur/next-2/mount-helpers.cjs +2 -0
- package/dist/lib/elur/next-2/mount-helpers.cjs.map +1 -0
- package/dist/lib/elur/next-2/mount-helpers.d.cts +25 -0
- package/dist/lib/elur/next-2/mount-helpers.d.ts +25 -0
- package/dist/lib/elur/next-2/mount-helpers.js +87 -0
- package/dist/lib/elur/next-2/mount-helpers.js.map +1 -0
- package/dist/lib/elur/next-2/mount.cjs +2 -0
- package/dist/lib/elur/next-2/mount.cjs.map +1 -0
- package/dist/lib/elur/next-2/mount.d.cts +29 -0
- package/dist/lib/elur/next-2/mount.d.ts +29 -0
- package/dist/lib/elur/next-2/mount.js +34 -0
- package/dist/lib/elur/next-2/mount.js.map +1 -0
- package/dist/lib/elur/next-2/node-binding.cjs +2 -0
- package/dist/lib/elur/next-2/node-binding.cjs.map +1 -0
- package/dist/lib/elur/next-2/node-binding.d.cts +6 -0
- package/dist/lib/elur/next-2/node-binding.d.ts +6 -0
- package/dist/lib/elur/next-2/node-binding.js +130 -0
- package/dist/lib/elur/next-2/node-binding.js.map +1 -0
- package/dist/lib/elur/next-2/reactivity.cjs +2 -0
- package/dist/lib/elur/next-2/reactivity.cjs.map +1 -0
- package/dist/lib/elur/next-2/reactivity.d.cts +220 -0
- package/dist/lib/elur/next-2/reactivity.d.ts +220 -0
- package/dist/lib/elur/next-2/reactivity.js +614 -0
- package/dist/lib/elur/next-2/reactivity.js.map +1 -0
- package/dist/lib/elur/next-2/scheduler.cjs +2 -0
- package/dist/lib/elur/next-2/scheduler.cjs.map +1 -0
- package/dist/lib/elur/next-2/scheduler.d.cts +23 -0
- package/dist/lib/elur/next-2/scheduler.d.ts +23 -0
- package/dist/lib/elur/next-2/scheduler.js +26 -0
- package/dist/lib/elur/next-2/scheduler.js.map +1 -0
- package/dist/lib/elur/plugins.cjs +1 -1
- package/dist/lib/elur/plugins.cjs.map +1 -1
- package/dist/lib/elur/plugins.js +1 -1
- package/dist/lib/elur/reactivity.cjs +1 -2
- package/dist/lib/elur/reactivity.d.cts +1 -67
- package/dist/lib/elur/reactivity.d.ts +1 -67
- package/dist/lib/elur/reactivity.js +2 -218
- package/dist/lib/elur/router.cjs +2 -2
- package/dist/lib/elur/router.cjs.map +1 -1
- package/dist/lib/elur/router.js +2 -2
- package/dist/lib/elur/router.js.map +1 -1
- package/dist/lib/elur/server/index.cjs +1 -1
- package/dist/lib/elur/server/index.cjs.map +1 -1
- package/dist/lib/elur/server/index.js +180 -108
- package/dist/lib/elur/server/index.js.map +1 -1
- package/dist/lib/elur/store.cjs +1 -1
- package/dist/lib/elur/store.cjs.map +1 -1
- package/dist/lib/elur/store.js +10 -4
- package/dist/lib/elur/store.js.map +1 -1
- package/dist/lib/elur/template/bindings.cjs +1 -1
- package/dist/lib/elur/template/bindings.cjs.map +1 -1
- package/dist/lib/elur/template/bindings.js +98 -84
- package/dist/lib/elur/template/bindings.js.map +1 -1
- package/dist/lib/elur/template/dom-write.d.cts +1 -5
- package/dist/lib/elur/template/dom-write.d.ts +1 -5
- package/dist/lib/elur/template/error-boundary.cjs +1 -1
- package/dist/lib/elur/template/error-boundary.cjs.map +1 -1
- package/dist/lib/elur/template/error-boundary.js +56 -50
- package/dist/lib/elur/template/error-boundary.js.map +1 -1
- package/dist/lib/elur/template/html.cjs +1 -1
- package/dist/lib/elur/template/html.cjs.map +1 -1
- package/dist/lib/elur/template/html.js +40 -37
- package/dist/lib/elur/template/html.js.map +1 -1
- package/dist/lib/elur/template/index.cjs +1 -1
- package/dist/lib/elur/template/index.d.cts +2 -1
- package/dist/lib/elur/template/index.d.ts +2 -1
- package/dist/lib/elur/template/index.js +13 -12
- package/dist/lib/elur/template/keyed-diff.d.cts +1 -32
- package/dist/lib/elur/template/keyed-diff.d.ts +1 -32
- package/dist/lib/elur/template/keyed.cjs +1 -1
- package/dist/lib/elur/template/keyed.cjs.map +1 -1
- package/dist/lib/elur/template/keyed.d.cts +10 -2
- package/dist/lib/elur/template/keyed.d.ts +10 -2
- package/dist/lib/elur/template/keyed.js +12 -1
- package/dist/lib/elur/template/keyed.js.map +1 -1
- package/dist/lib/elur/template/mount-helpers.d.cts +1 -29
- package/dist/lib/elur/template/mount-helpers.d.ts +1 -29
- package/dist/lib/elur/template/node-binding.d.cts +1 -6
- package/dist/lib/elur/template/node-binding.d.ts +1 -6
- package/dist/lib/elur/template/portal.cjs +1 -1
- package/dist/lib/elur/template/portal.cjs.map +1 -1
- package/dist/lib/elur/template/portal.js +1 -1
- package/dist/lib/elur/template/transitions.cjs +1 -1
- package/dist/lib/elur/template/transitions.cjs.map +1 -1
- package/dist/lib/elur/template/transitions.js +2 -2
- package/dist/lib/elur/template/types.cjs +1 -1
- package/dist/lib/elur/template/types.cjs.map +1 -1
- package/dist/lib/elur/template/types.d.cts +55 -0
- package/dist/lib/elur/template/types.d.ts +55 -0
- package/dist/lib/elur/template/types.js +5 -1
- package/dist/lib/elur/template/types.js.map +1 -1
- package/dist/lib/elur.cjs +1 -1
- package/dist/lib/elur.js +2 -2
- package/dist/lib/form.cjs +1 -1
- package/dist/lib/form.js +1 -1
- package/dist/lib/hydrate.cjs +1 -2
- package/dist/lib/hydrate.js +2 -410
- package/dist/lib/index.cjs +1 -1
- package/dist/lib/index.js +2 -2
- package/dist/lib/lifecycle.cjs +1 -1
- package/dist/lib/lifecycle.js +4 -0
- package/dist/lib/plugins.cjs +1 -1
- package/dist/lib/plugins.js +1 -1
- package/dist/lib/router.cjs +2 -2
- package/dist/lib/router.js +2 -2
- package/dist/lib/server.cjs +1 -1
- package/dist/lib/server.js +180 -108
- package/dist/lib/signals.cjs +1 -2
- package/dist/lib/signals.js +2 -218
- package/dist/lib/store.cjs +1 -1
- package/dist/lib/store.js +10 -4
- package/dist/lib/template.cjs +1 -1
- package/dist/lib/template.js +13 -12
- package/package.json +7 -9
- package/dist/lib/elur/component.cjs.map +0 -1
- package/dist/lib/elur/component.js.map +0 -1
- package/dist/lib/elur/hydrate/index.cjs.map +0 -1
- package/dist/lib/elur/hydrate/index.js.map +0 -1
- package/dist/lib/elur/reactivity.cjs.map +0 -1
- package/dist/lib/elur/reactivity.js.map +0 -1
- package/dist/lib/elur/template/dom-write.cjs +0 -2
- package/dist/lib/elur/template/dom-write.cjs.map +0 -1
- package/dist/lib/elur/template/dom-write.js +0 -16
- package/dist/lib/elur/template/dom-write.js.map +0 -1
- package/dist/lib/elur/template/keyed-diff.cjs +0 -2
- package/dist/lib/elur/template/keyed-diff.cjs.map +0 -1
- package/dist/lib/elur/template/keyed-diff.js +0 -100
- package/dist/lib/elur/template/keyed-diff.js.map +0 -1
- package/dist/lib/elur/template/mount-helpers.cjs +0 -2
- package/dist/lib/elur/template/mount-helpers.cjs.map +0 -1
- package/dist/lib/elur/template/mount-helpers.js +0 -132
- package/dist/lib/elur/template/mount-helpers.js.map +0 -1
- package/dist/lib/elur/template/node-binding.cjs +0 -2
- package/dist/lib/elur/template/node-binding.cjs.map +0 -1
- package/dist/lib/elur/template/node-binding.js +0 -95
- package/dist/lib/elur/template/node-binding.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactivity.js","names":[],"sources":["../../../../src/elur/next-2/reactivity.ts"],"sourcesContent":["/**\n * Elur \"next\" — grafo reactivo push-pull versionado (experimental).\n *\n * Módulo aislado de `../reactivity.ts` (PLAN_TECNICO_ELUR_NEXT §B.3–B.9):\n * nunca importar ambos en el mismo path de producción — mezclar signals de\n * dos grafos rompe la semántica.\n *\n * Modelo:\n * - PUSH: signal.set() → version++ → invalida consumidores (DIRTY directos,\n * CHECK transitivos) y encola effects una sola vez. No recalcula nada.\n * - PULL: antes de ejecutar un effect se estabilizan sus fuentes — los\n * computeds DIRTY/CHECK se refrescan recursivamente y sólo propagan si el\n * resultado realmente cambió (early cutoff por versión).\n * - Liveness: un computed sin consumidores vivos no se suscribe a sus\n * fuentes; se refresca por sondeo de versiones al leerse (O(1) si el\n * epoch global no cambió).\n * - Edges: `Link` doble-enlazado compartido entre la lista de sources del\n * consumidor y la de consumers del productor — subscribe/remove O(1),\n * reutilizados entre evaluaciones (mark & sweep por `evalMark`).\n */\n\nimport { inputPending, yieldControl } from \"./scheduler.js\";\n\n// --- Estados del grafo (B.4) ------------------------------------------------\n\nconst CLEAN = 0;\nconst CHECK = 1 << 0;\nconst DIRTY = 1 << 1;\nconst RUNNING = 1 << 2;\nconst ENQUEUED = 1 << 3;\nconst DISPOSED = 1 << 4;\n/** B.12.1: nodo en el stack del DFS de refresh — detecta back-edges (ciclos). */\nconst PROBING = 1 << 5;\n\n// --- Estado global del grafo -------------------------------------------------\n\nlet globalEpoch = 0;\nlet batchLevel = 0;\nlet activeConsumer: ConsumerNode | null = null;\n/** Owner activo: los consumidores creados aquí dentro le pertenecen (B.10). */\nlet activeOwner: OwnerLike | null = null;\n/** Flags de shape: booleans colapsados en un int (anexo §1.12 bitflags). */\nconst F_HAS_VALUE = 1 << 0;\nconst F_EVALUATED = 1 << 1;\nconst F_RENDER = 1 << 2;\nconst F_DEAD = 1 << 3;\n\n/** Cualquier consumidor no-computed encolable al flush (effects, bindings). */\ninterface Flushable extends ConsumerNode {\n /** Bitflags: F_RENDER → cola de render; F_DEAD → dispuesto. */\n flags: number;\n /** B.9: gen*1024 + corridas en el flush (epoch → sin reset loop). */\n _flush: number;\n run(): void;\n}\n\n/** B.8: colas separadas — render effects (bindings DOM) antes que user effects. */\nconst renderQueue: Flushable[] = [];\nconst effectQueue: Flushable[] = [];\n\n// --- Ownership (B.10 / A.11) --------------------------------------------------\n\n/** Cualquier cosa que pueda poseer consumidores: Owner raíz, Effect, Computed. */\nexport interface OwnerLike {\n /**\n * Cabeza de la lista doblemente enlazada de hijos poseídos (effects,\n * computeds, sub-owners). Lista intrusiva — el unregistro de un hijo es\n * O(1) (antes: array con indexOf+splice O(n), que hacía el clear de\n * listas grandes O(n²) — el gap de `09_clear1k` en Krausest).\n */\n childrenHead: (Disposable & OwnerLike) | null;\n /** Número de hijos vivos — `childCount` para diagnóstico/tests. */\n childCount: number;\n /** Cleanups registrados con onCleanup() durante la última evaluación. */\n cleanups: (() => void)[] | null;\n /** Owner que era activo cuando este nodo fue creado (árbol de owners). */\n parent: OwnerLike | null;\n /** Links intrusivos al sibling anterior/siguiente en la lista del padre. */\n nextOwned: (Disposable & OwnerLike) | null;\n prevOwned: (Disposable & OwnerLike) | null;\n /**\n * A.16: boundary por owner — un consumidor sin handler propio burbujea\n * el error por la cadena de owners hasta que uno lo maneja (true).\n */\n onOwnerError?: (err: unknown) => boolean;\n}\n\nexport interface Disposable {\n dispose(): void;\n}\n\n/** Raíz de ownership explícita (un mount, un test, un sub-árbol). */\nexport class Owner implements OwnerLike, Disposable {\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n onOwnerError?: (err: unknown) => boolean;\n private _disposed = false;\n\n dispose(): void {\n if (this._disposed) return;\n this._disposed = true;\n disposeOwnerChildren(this);\n unown(this);\n }\n}\n\nfunction disposeOwnerChildren(o: OwnerLike): void {\n if (o.cleanups) {\n for (const fn of o.cleanups) fn();\n o.cleanups = null;\n }\n // Detach-first: se camina la lista con los punteros del hijo ya\n // limpiados, así unown() dentro de dispose() del hijo es no-op y no\n // muta la cadena que se está recorriendo.\n let child = o.childrenHead;\n o.childrenHead = null;\n o.childCount = 0;\n while (child) {\n const next = child.nextOwned;\n child.nextOwned = null;\n child.prevOwned = null;\n child.parent = null;\n child.dispose();\n child = next;\n }\n}\n\nfunction own(d: Disposable & OwnerLike): void {\n if (!activeOwner) return;\n d.parent = activeOwner;\n const head = activeOwner.childrenHead;\n d.nextOwned = head;\n d.prevOwned = null;\n if (head) head.prevOwned = d;\n activeOwner.childrenHead = d;\n activeOwner.childCount++;\n}\n\n/** Des-registrar del padre — O(1) por links intrusivos. */\nfunction unown(d: Disposable & OwnerLike): void {\n const p = d.parent;\n d.parent = null;\n if (d.prevOwned) d.prevOwned.nextOwned = d.nextOwned;\n else if (p) p.childrenHead = d.nextOwned;\n if (d.nextOwned) d.nextOwned.prevOwned = d.prevOwned;\n d.nextOwned = null;\n d.prevOwned = null;\n if (p) p.childCount--;\n}\n\n/** Owner actual — para diagnóstico e integración con el kernel de mounts. */\nexport function getOwner(): OwnerLike | null {\n return activeOwner;\n}\n\n/**\n * Ejecuta fn con `owner` como dueño activo. Todo effect/computed creado\n * dentro queda poseído por él y se destruye con él.\n */\nexport function runWithOwner<T>(owner: OwnerLike | null, fn: () => T): T {\n const prevOwner = activeOwner;\n const prevConsumer = activeConsumer;\n activeOwner = owner;\n activeConsumer = null;\n try {\n return fn();\n } finally {\n activeOwner = prevOwner;\n activeConsumer = prevConsumer;\n }\n}\n\n/**\n * Raíz reactiva: `const dispose = createRoot(d => {...})`. Todo lo creado\n * dentro muere con `dispose()` — elimina la clase de leaks de B.2.5.\n */\nexport function createRoot<T>(fn: (dispose: () => void) => T): T {\n const root = new Owner();\n root.parent = activeOwner;\n return runWithOwner(root, () => fn(() => root.dispose()));\n}\n\n/** Registra una limpieza en el owner activo (effect, computed o root). */\nexport function onCleanup(fn: () => void): void {\n if (activeOwner) (activeOwner.cleanups ??= []).push(fn);\n}\n\n/** Cap de seguridad para writes re-entrantes en effects (B.9). */\nconst MAX_FLUSH_STEPS = 10_000;\n/** B.9: máximo de corridas de UN efecto dentro de un mismo flush. */\nconst MAX_RUNS_PER_FLUSH = 100;\n\n/**\n * B.9: ciclo de efectos — un write dentro de un effect re-encola trabajo; si\n * un nodo corre más de MAX_RUNS_PER_FLUSH en el mismo flush, es un bucle.\n * El mensaje incluye el camino de dependencias del efecto (sus fuentes y,\n * si son computeds, las suyas) para localizar el ciclo.\n */\nexport class ReactiveCycleError extends Error {\n constructor(effect: ConsumerNode, runs: number) {\n const path: string[] = [];\n for (let l = effect.sourcesHead; l; l = l.nextSource) {\n const p = l.source;\n path.push(p instanceof Computed ? `computed(v${p.version})` : `signal(v${p.version})`);\n if (path.length >= 8) { path.push(\"…\"); break; }\n }\n super(\n `[elur] Maximum effect re-execution depth exceeded (possible infinite loop). ` +\n `Ciclo de efectos: un effect corrió ${runs} veces en un solo flush ` +\n `(límite por nodo ${MAX_RUNS_PER_FLUSH}). Fuentes del effect: ` +\n `[${path.join(\", \")}]. Probable write-inside-effect que se re-encola a sí mismo.`,\n );\n this.name = \"ReactiveCycleError\";\n }\n}\n\n// --- Superficie interna (paridad con reactivity.ts para el swap de tests) -----\n\ninterface ErrorHandler {\n (err: unknown): void;\n}\nconst errorHandlerStack: ErrorHandler[] = [];\nlet activeErrorHandler: ErrorHandler | null = null;\n\n/** @internal */\nexport function _pushErrorHandler(h: ErrorHandler): void {\n errorHandlerStack.push(activeErrorHandler as ErrorHandler);\n activeErrorHandler = h;\n}\n\n/** @internal */\nexport function _popErrorHandler(): void {\n activeErrorHandler = errorHandlerStack.pop() ?? null;\n}\n\nexport interface _SignalDebugHooks {\n onCreate?: (signal: Signal<any>, initialValue: unknown) => void;\n onWrite?: (signal: Signal<any>, value: unknown) => void;\n}\n\nlet signalDebugHooks: _SignalDebugHooks | null = null;\nlet signalDebugHookSet: Set<_SignalDebugHooks> | null = null;\n\nfunction _syncSignalDebugHooks(): void {\n const set = signalDebugHookSet;\n if (!set || set.size === 0) {\n signalDebugHooks = null;\n return;\n }\n if (set.size === 1) {\n signalDebugHooks = set.values().next().value ?? null;\n return;\n }\n signalDebugHooks = {\n onCreate(signal, initialValue) {\n for (const hooks of set) hooks.onCreate?.(signal, initialValue);\n },\n onWrite(signal, value) {\n for (const hooks of set) hooks.onWrite?.(signal, value);\n },\n };\n}\n\n/** @internal */\nexport function _setSignalDebugHooks(hooks: _SignalDebugHooks | null): void {\n const set = new Set<_SignalDebugHooks>();\n if (hooks) set.add(hooks);\n signalDebugHookSet = set;\n _syncSignalDebugHooks();\n}\n\n/** @internal — añade suscriptor sin reemplazar; devuelve unsubscribe. */\nexport function _addSignalDebugHooks(hooks: _SignalDebugHooks): () => void {\n let set = signalDebugHookSet;\n if (!set) {\n set = new Set<_SignalDebugHooks>();\n if (signalDebugHooks) set.add(signalDebugHooks);\n signalDebugHookSet = set;\n }\n set.add(hooks);\n _syncSignalDebugHooks();\n return () => {\n set.delete(hooks);\n _syncSignalDebugHooks();\n };\n}\n\n/** @internal — la nueva impl no tiene notify buffer; se expone para paridad. */\nexport function _getNotifyBufSize(): number {\n return 0;\n}\n\ninterface ProducerNode {\n /** Incrementa en cada cambio real de valor. */\n version: number;\n /**\n * B.12.4 (durabilidad, Salsa): producer estático — nunca cambia.\n * `link()` no crea edge: cero bookkeeping para deps estáticas.\n */\n durable?: boolean;\n /** Nº de links vivos en `consumersHead` (liveness del productor). */\n liveCount: number;\n consumersHead: Link | null;\n}\n\n/**\n * Edge compartido: vive en dos listas a la vez — la de sources del consumer\n * (`nextSource/prevSource`) y la de consumers del producer\n * (`nextTarget/prevTarget`). `mark` lo usa el consumer para decidir qué\n * links sobrevivieron a la última evaluación; `linked` indica si está\n * insertado en la lista del producer (los computeds fríos no lo están).\n */\ninterface Link {\n source: ProducerNode;\n target: ConsumerNode;\n seenVersion: number;\n mark: number;\n linked: boolean;\n nextSource: Link | null;\n prevSource: Link | null;\n nextTarget: Link | null;\n prevTarget: Link | null;\n}\n\ninterface ConsumerNode {\n state: number;\n sourcesHead: Link | null;\n /** Contador de evaluación — marca los links usados en esta pasada. */\n evalMark: number;\n live(): boolean;\n}\n\n// --- Gestión de links ---------------------------------------------------------\n\n// B.12.1: colas de liveness — un computed que gana/pierde su última\n// suscripción se encola; el drenado ocurre en puntos seguros (link/sweep),\n// nunca recursivamente (las cadenas profundas no comen call stack).\nconst acquireQueue: Computed<unknown>[] = [];\nconst releaseQueue: Computed<unknown>[] = [];\n\n// F3: freelist de Links — las edges de bindings/effects se allocan y mueren\n// en masa al montar/clear listas; reciclarlas evita el churn de GC. Un link\n// sólo entra al pool cuando está muerto de verdad (fuera de AMBAS listas);\n// los que quedan colgados en `sourcesHead` de un consumidor vivo (muerte del\n// producer) los barre `sweep` después — nunca se poolan referencias vivas.\n// `nextSource` hace de puntero de freelist. Cap: evita retener picos.\nlet linkPool: Link | null = null;\nlet linkPoolSize = 0;\nconst LINK_POOL_MAX = 8192;\n\nfunction allocLink(\n source: ProducerNode,\n target: ConsumerNode,\n seenVersion: number,\n mark: number,\n): Link {\n const l = linkPool;\n if (l) {\n linkPool = l.nextSource;\n linkPoolSize--;\n l.source = source;\n l.target = target;\n l.seenVersion = seenVersion;\n l.mark = mark;\n l.linked = false;\n l.prevSource = null;\n l.nextTarget = null;\n l.prevTarget = null;\n // `nextSource` lo fija el caller (o queda null en bindings de 1 dep).\n return l;\n }\n return {\n source, target, seenVersion, mark,\n linked: false,\n nextSource: null, prevSource: null, nextTarget: null, prevTarget: null,\n };\n}\n\nfunction freeLink(l: Link): void {\n if (linkPoolSize >= LINK_POOL_MAX) return;\n // Soltar refs: un link pooled no debe retener productor ni consumidor.\n l.source = null as unknown as ProducerNode;\n l.target = null as unknown as ConsumerNode;\n l.nextTarget = null;\n l.prevTarget = null;\n l.prevSource = null;\n l.nextSource = linkPool;\n linkPool = l;\n linkPoolSize++;\n}\n\nfunction producerAddLink(p: ProducerNode, l: Link): void {\n l.nextTarget = p.consumersHead;\n l.prevTarget = null;\n if (p.consumersHead) p.consumersHead.prevTarget = l;\n p.consumersHead = l;\n l.linked = true;\n p.liveCount++;\n // Si el producer es un computed que acaba de ganar su primer consumidor,\n // hay que suscribirlo a SUS fuentes (podía estar frío) — encolado.\n if (p.liveCount === 1 && p instanceof Computed) acquireQueue.push(p);\n}\n\nfunction producerRemoveLink(p: ProducerNode, l: Link): void {\n if (!l.linked) return;\n if (l.prevTarget) l.prevTarget.nextTarget = l.nextTarget;\n else p.consumersHead = l.nextTarget;\n if (l.nextTarget) l.nextTarget.prevTarget = l.prevTarget;\n l.linked = false;\n p.liveCount--;\n if (p.liveCount === 0 && p instanceof Computed) releaseQueue.push(p);\n}\n\n/** Drena adquisiciones: cada computed vivo se suscribe a sus fuentes. */\nfunction drainAcquireQueue(): void {\n while (acquireQueue.length) {\n const cur = acquireQueue.pop()!;\n for (let l = cur.sourcesHead; l; l = l.nextSource) {\n if (!l.linked) producerAddLink(l.source, l);\n }\n }\n}\n\n/** Drena liberaciones: cada computed frío se des-suscribe de sus fuentes. */\nfunction drainReleaseQueue(): void {\n while (releaseQueue.length) {\n const cur = releaseQueue.pop()!;\n for (let l = cur.sourcesHead; l; l = l.nextSource) {\n producerRemoveLink(l.source, l);\n }\n }\n}\n\nfunction link(producer: ProducerNode, consumer: ConsumerNode): void {\n // B.12.4: producers estáticos nunca invalidan — no registramos la edge.\n if (producer.durable) return;\n // Reutilizar el Link si ya existe (scan lineal — n de fuentes es típicamente\n // pequeño; el orden de deps suele ser estable entre evaluaciones).\n for (let l = consumer.sourcesHead; l; l = l.nextSource) {\n if (l.source === producer) {\n l.seenVersion = producer.version;\n l.mark = consumer.evalMark;\n return;\n }\n }\n const l = allocLink(producer, consumer, producer.version, consumer.evalMark);\n l.nextSource = consumer.sourcesHead;\n if (consumer.sourcesHead) consumer.sourcesHead.prevSource = l;\n consumer.sourcesHead = l;\n if (consumer.live()) {\n producerAddLink(producer, l);\n if (acquireQueue.length) drainAcquireQueue();\n }\n}\n\n/** Elimina los links no re-usados en la última evaluación del consumidor. */\nfunction sweep(consumer: ConsumerNode): void {\n let l = consumer.sourcesHead;\n while (l) {\n const next = l.nextSource;\n if (l.mark !== consumer.evalMark) {\n if (l.prevSource) l.prevSource.nextSource = l.nextSource;\n else consumer.sourcesHead = l.nextSource;\n if (l.nextSource) l.nextSource.prevSource = l.prevSource;\n producerRemoveLink(l.source, l);\n freeLink(l);\n }\n l = next;\n }\n if (releaseQueue.length) drainReleaseQueue();\n}\n\n// --- Invalidación (fase push) ------------------------------------------------\n\n/**\n * B.12.1: worklist compartida para la cascada CHECK — marcado iterativo\n * sin call stack (anexo: DFS iterativo alien-signals). Segura porque el\n * marcado no corre código de usuario: se vacía antes de retornar.\n */\nconst checkWorklist: Link[] = [];\n\nfunction markDirty(c: ConsumerNode): void {\n if (c.state & (DIRTY | DISPOSED)) return;\n // Un computed RUNNING es un ciclo → se ignora aquí y revienta en refresh.\n // Un effect RUNNING es write-inside-effect → se re-encola (cap de flush).\n if (c instanceof Computed && c.state & RUNNING) return;\n c.state = DIRTY;\n if (c instanceof Computed) {\n // Cascada CHECK iterativa sobre el subgrafo de consumidores.\n for (let l = c.consumersHead; l; l = l.nextTarget) {\n const t = l.target;\n if (t.state === CLEAN) {\n t.state = CHECK;\n if (t instanceof Computed) checkWorklist.push(l);\n else enqueue(t as Flushable);\n }\n }\n while (checkWorklist.length) {\n const l = checkWorklist.pop()!;\n // l.target es el computed ya marcado CHECK — seguir a SUS consumidores.\n for (let u = (l.target as Computed<unknown>).consumersHead; u; u = u.nextTarget) {\n const t = u.target;\n if (t.state === CLEAN) {\n t.state = CHECK;\n if (t instanceof Computed) checkWorklist.push(u);\n else enqueue(t as Flushable);\n }\n }\n }\n } else {\n enqueue(c as Flushable);\n }\n}\n\nfunction invalidate(producer: ProducerNode): void {\n for (let l = producer.consumersHead; l; l = l.nextTarget) markDirty(l.target);\n if (batchLevel === 0) flushEffects();\n}\n\n// --- Estabilización (fase pull) ----------------------------------------------\n\n/** Re-evalúa un computed: corre fn() bajo sí mismo como consumer+owner. */\nfunction evalComputed(c: Computed<any>): void {\n // Los hijos poseídos por la pasada anterior mueren antes de re-evaluar.\n if (c.childrenHead !== null || c.cleanups !== null) disposeOwnerChildren(c);\n c.evalMark++;\n const prevConsumer = activeConsumer;\n const prevOwner = activeOwner;\n activeConsumer = c;\n activeOwner = c;\n c.state = RUNNING;\n let result: unknown;\n let threw = true;\n try {\n result = c.fn();\n threw = false;\n } finally {\n activeConsumer = prevConsumer;\n activeOwner = prevOwner;\n if (threw && c.state & RUNNING) c.state = DIRTY; // guard recuperable\n }\n sweep(c);\n c.state = CLEAN;\n c.lastCleanEpoch = globalEpoch;\n if (!(c.flags & F_HAS_VALUE) || !c.equals(result, c._value)) {\n c._value = result;\n c.flags |= F_HAS_VALUE;\n c.version++;\n }\n}\n\n// B.12.1: estado transiente del DFS vive EN el nodo (anexo: DFS manual,\n// cero frames/alloc) — flags empaquetados + cursores.\n// Bits extra en `state` durante el DFS (transientes — se limpian al salir).\nconst _P_STARTED = 1 << 6;\nconst _P_DIRTY = 1 << 7;\nconst _P_CHANGED = 1 << 8;\nconst _P_MASK = _P_STARTED | _P_DIRTY | _P_CHANGED;\n\n/**\n * B.12.1: worklist del DFS de refresh — compartida sólo en llamadas no\n * anidadas; una lectura de computed dentro de una evaluación (fn() puede leer\n * otros computeds) usa un array propio para no mezclar estados.\n */\nconst sharedProbeStack: Computed<any>[] = [];\nlet probeDepth = 0;\n\n/**\n * B.12.1: refresco iterativo (anexo: DFS manual sin call stack).\n * Post-order: las fuentes se resuelven antes que el nodo. El bit PROBING\n * marca nodos en el stack — un back-edge es un ciclo en el DAG.\n */\nfunction refreshComputed(root: Computed<any>): void {\n // Fast path en UNA pasada: necesita DFS si alguna fuente computed no está\n // resuelta; cambio si alguna versión difiere. El orden importa: un computed\n // sin resolver puede tener versión vieja — hay que resolverlo antes de\n // comparar.\n const phase = root.state & (CHECK | DIRTY | RUNNING);\n if (root.state & DISPOSED) return;\n if (phase === RUNNING) {\n throw new Error(\"[elur-next] Ciclo reactivo detectado en computed().\");\n }\n if (phase === 0 && (root.flags & F_HAS_VALUE) && (root.live() || root.lastCleanEpoch === globalEpoch)) {\n return;\n }\n let changed = phase === DIRTY || !(root.flags & F_HAS_VALUE);\n let deep = false;\n for (let l = root.sourcesHead; l; l = l.nextSource) {\n const src = l.source;\n if (\n src instanceof Computed &&\n (!(src.flags & F_HAS_VALUE) || src.state !== CLEAN ||\n (!src.live() && src.lastCleanEpoch !== globalEpoch))\n ) {\n deep = true;\n break;\n }\n if (!changed && src.version !== l.seenVersion) changed = true;\n }\n if (!deep) {\n if (changed) evalComputed(root);\n else {\n root.state = CLEAN;\n root.lastCleanEpoch = globalEpoch;\n for (let l = root.sourcesHead; l; l = l.nextSource) {\n l.seenVersion = l.source.version;\n }\n }\n return;\n }\n const stack: Computed<any>[] = probeDepth++ === 0 ? sharedProbeStack : [];\n try {\n let cur: Computed<any> | undefined = root;\n while (cur) {\n if (!(cur.state & _P_STARTED)) {\n // Primera visita: decidir según la fase baja del estado.\n cur.state |= _P_STARTED;\n cur._pCursor = null;\n cur._pLink = null;\n const phase = cur.state & (CHECK | DIRTY | RUNNING);\n if (cur.state & DISPOSED) { cur.state &= ~PROBING; cur.state &= ~_P_MASK; cur = stack.pop(); continue; }\n if (phase === RUNNING) failCycle(stack, cur);\n if (\n phase === 0 &&\n (cur.flags & F_HAS_VALUE) &&\n (cur.live() || cur.lastCleanEpoch === globalEpoch)\n ) {\n // CLEAN, fresco y con valor — nada que hacer.\n cur.state &= ~_P_MASK;\n cur = stack.pop();\n continue;\n }\n // CHECK/DIRTY, o CLEAN frío con epoch vieja: SIEMPRE descender por las\n // fuentes primero — si evaluáramos un DIRTY directo, su fn() leería\n // el getter de la fuente sin resolver y la recursión volvería por la\n // puerta de atrás (el bug de las cadenas profundas).\n cur.state |= PROBING;\n if (phase === DIRTY) cur.state |= _P_DIRTY;\n cur._pCursor = cur.sourcesHead;\n }\n // Resume tras un descend: chequear el link cuya fuente se resolvió.\n if (cur._pLink) {\n const l = cur._pLink;\n cur._pLink = null;\n if (l.source.version !== l.seenVersion) cur.state |= _P_CHANGED;\n }\n // Avanzar el cursor de fuentes.\n let descended = false;\n while (cur._pCursor) {\n const l = cur._pCursor;\n cur._pCursor = l.nextSource;\n const src = l.source;\n if (\n src instanceof Computed &&\n (!(src.flags & F_HAS_VALUE) || src.state !== CLEAN ||\n (!src.live() && src.lastCleanEpoch !== globalEpoch))\n ) {\n if (src.state & PROBING) failCycle(stack, cur); // back-edge = ciclo\n cur._pLink = l;\n stack.push(cur);\n src.state |= PROBING;\n cur = src;\n descended = true;\n break;\n }\n if (src.version !== l.seenVersion) cur.state |= _P_CHANGED;\n }\n if (descended) continue;\n // Fuentes resueltas.\n cur.state &= ~PROBING;\n if (cur.state & (_P_DIRTY | _P_CHANGED) || !(cur.flags & F_HAS_VALUE)) {\n evalComputed(cur);\n } else {\n cur.state = CLEAN;\n cur.lastCleanEpoch = globalEpoch;\n // Baselines frescas para futuros sondeos (pollCold).\n for (let l = cur.sourcesHead; l; l = l.nextSource) {\n l.seenVersion = l.source.version;\n }\n }\n cur.state &= ~_P_MASK;\n cur._pCursor = null;\n cur._pLink = null;\n cur = stack.pop();\n }\n } finally {\n probeDepth--;\n }\n}\n\nfunction failCycle(stack: Computed<any>[], cur: Computed<any>): never {\n // Limpiar PROBING + transientes en todos los nodos vivos antes de lanzar.\n for (const n of stack) {\n n.state &= ~(PROBING | _P_MASK);\n n._pCursor = null;\n n._pLink = null;\n }\n cur.state &= ~(PROBING | _P_MASK);\n stack.length = 0;\n throw new Error(\"[elur-next] Ciclo reactivo detectado en computed().\");\n}\n\n/** Asegura que un computed esté fresco: pull si dirty, sondeo si frío. */\nfunction ensureFreshComputed(c: Computed<any>): void {\n if (c.state !== CLEAN || (!c.live() && c.lastCleanEpoch !== globalEpoch)) {\n refreshComputed(c);\n }\n}\n\n/** Estabiliza las fuentes de un consumidor; true si algo cambió de verdad. */\nfunction stabilize(consumer: ConsumerNode): boolean {\n let changed = false;\n for (let l = consumer.sourcesHead; l; l = l.nextSource) {\n if (l.source instanceof Computed) ensureFreshComputed(l.source);\n if (l.source.version !== l.seenVersion) changed = true;\n }\n return changed;\n}\n\n// --- Effects ------------------------------------------------------------------\n\nfunction enqueue(e: Flushable): void {\n if (e.state & (ENQUEUED | DISPOSED)) return;\n e.state |= ENQUEUED;\n ((e.flags & F_RENDER) ? renderQueue : effectQueue).push(e);\n}\n\nlet flushing = false;\n/** B.9: id del flush en curso — los contadores por nodo expiran por epoch. */\nlet flushGeneration = 0;\n\n// F5: cursores/steps a nivel módulo — el flush puede ceder el hilo cuando hay\n// input pendiente y la continuación retoma las colas en el mismo orden.\nlet renderCursor = 0;\nlet effectCursor = 0;\nlet flushSteps = 0;\n/** Pasos desde el último yield — cada continuación trabaja un tramo completo\n * antes de volver a ceder (si no, con input sostenido el flush haría\n * yield→continue→yield sin avanzar hasta el cap de pasos). */\nlet stepsSinceYield = 0;\nlet flushContinuationScheduled = false;\n/** Pasos de user-queue antes de considerar ceder el hilo a input pendiente. */\nconst FLUSH_YIELD_STEPS = 256;\n\nconst clearQueues = (): void => {\n renderQueue.length = 0;\n effectQueue.length = 0;\n renderCursor = 0;\n effectCursor = 0;\n};\n\n/** Cierra el epoch del flush exactamente una vez (sync o tras el último yield). */\nfunction finishFlush(): void {\n flushGeneration++; // los stamps viejos expiran solos — sin reset\n flushing = false;\n}\n\nfunction flushLoop(): void {\n while (renderCursor < renderQueue.length || effectCursor < effectQueue.length) {\n if (++flushSteps > MAX_FLUSH_STEPS) {\n clearQueues();\n throw new Error(\n \"[elur] Maximum effect re-execution depth exceeded (possible infinite loop).\"\n );\n }\n // F5: una vez drenada la cola de render, si quedan muchos USER effects y\n // hay input del usuario esperando, cedemos el hilo — la continuación\n // retoma la cola en orden (scheduler.yield → al frente, no al fondo).\n // Los render writes NUNCA ceden: feedback visual en el mismo turno (INP).\n stepsSinceYield++;\n if (\n renderCursor >= renderQueue.length &&\n effectCursor < effectQueue.length &&\n stepsSinceYield > FLUSH_YIELD_STEPS &&\n inputPending()\n ) {\n scheduleFlushContinuation();\n return;\n }\n // Orden B.8: render effects primero, luego user effects.\n const e = renderCursor < renderQueue.length\n ? renderQueue[renderCursor++]\n : effectQueue[effectCursor++];\n e.state &= ~ENQUEUED;\n if (e.state & DISPOSED) continue;\n // Fast-path DIRTY: una fuente directa escribió → el cambio es real por\n // construcción; los computeds CHECK se refrescan lazy en el getter.\n // Sólo los effects CHECK (invalidados vía computed) necesitan stabilize()\n // para el early-cutoff de falsas alarmas.\n if (e.state !== DIRTY && !stabilize(e)) {\n e.state = CLEAN;\n continue;\n }\n // B.9: contador por nodo por flush (epoch stamp — sin reset loop) —\n // identifica el efecto exacto del ciclo.\n const gen = Math.floor(e._flush / 1024);\n const runs = (gen === flushGeneration ? e._flush - gen * 1024 : 0) + 1;\n e._flush = flushGeneration * 1024 + runs;\n if (runs > MAX_RUNS_PER_FLUSH) {\n clearQueues();\n throw new ReactiveCycleError(e, runs);\n }\n e.run();\n }\n clearQueues();\n}\n\nfunction scheduleFlushContinuation(): void {\n if (flushContinuationScheduled) return;\n flushContinuationScheduled = true;\n void yieldControl().then(() => {\n flushContinuationScheduled = false;\n stepsSinceYield = 0; // nuevo tramo — trabaja antes de volver a ceder\n try {\n flushLoop();\n } catch (err) {\n finishFlush();\n // El error ya no puede lanzar sync al caller — misma visibilidad que\n // una excepción de efecto async: uncaught error.\n queueMicrotask(() => {\n throw err;\n });\n return;\n }\n if (!flushContinuationScheduled) finishFlush();\n });\n}\n\nfunction flushEffects(): void {\n // Re-entrante: si un write dentro de un effect encola más trabajo, lo\n // procesa el MISMO bucle (o la continuación ya agendada tras un yield) —\n // así el cap MAX_FLUSH_STEPS sí protege contra bucles de writes (antes\n // cada flush anidado tenía su propio contador y el bucle reventaba por\n // stack overflow en vez de por el guard).\n if (flushing) return;\n flushing = true;\n flushSteps = 0;\n stepsSinceYield = 0;\n try {\n flushLoop();\n } catch (err) {\n finishFlush();\n throw err;\n }\n // Si el loop cedió, `flushing` sigue true durante el gap — writes reentrantes\n // encolan sin arrancar otro flush y la continuación los drena en orden.\n if (!flushContinuationScheduled) finishFlush();\n}\n\n// --- API -----------------------------------------------------------------------\n\nexport class Signal<T> implements ProducerNode {\n version = 0;\n liveCount = 0;\n durable = false;\n consumersHead: Link | null = null;\n private _v: T;\n\n constructor(initialValue: T) {\n this._v = initialValue;\n signalDebugHooks?.onCreate?.(this, initialValue);\n }\n\n get value(): T {\n if (activeConsumer) link(this, activeConsumer);\n return this._v;\n }\n\n set value(newValue: T) {\n if (this.durable) {\n console.warn(\"[elur-next2] constSignal: write ignorada — el producer es estático (B.12.4).\");\n return;\n }\n if (Object.is(this._v, newValue)) return;\n this._v = newValue;\n signalDebugHooks?.onWrite?.(this, newValue);\n this.version++;\n globalEpoch++;\n invalidate(this);\n }\n\n update(fn: (current: T) => T): void {\n this.value = fn(this._v);\n }\n\n peek(): T {\n return this._v;\n }\n\n dispose(): void {\n while (this.consumersHead) {\n const l = this.consumersHead;\n producerRemoveLink(this, l);\n // También quitar el link de la lista de sources del consumidor.\n if (l.prevSource) l.prevSource.nextSource = l.nextSource;\n else l.target.sourcesHead = l.nextSource;\n if (l.nextSource) l.nextSource.prevSource = l.prevSource;\n freeLink(l); // muerto de verdad: fuera de las dos listas\n }\n drainReleaseQueue();\n }\n}\n\n// Computed extiende Signal por paridad con la impl actual: el resto del\n// runtime (store, watch, templates) hace `x instanceof Signal`.\nexport class Computed<T> extends Signal<T> implements ConsumerNode, OwnerLike {\n state: number = CLEAN;\n sourcesHead: Link | null = null;\n evalMark = 0;\n /** Último epoch en que se verificó fresco — sondeo O(1) para fríos. */\n lastCleanEpoch = -1;\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n _value: T | undefined;\n fn: () => T;\n equals: (a: T, b: T) => boolean;\n flags = 0;\n /** B.12.1: transientes del DFS de refresh (null entre refreshes). */\n _pCursor: Link | null = null;\n _pLink: Link | null = null;\n\n constructor(fn: () => T, equals: (a: T, b: T) => boolean = Object.is) {\n super(undefined as T); // el valor real lo gestiona _value/_evaluated\n this.fn = fn;\n this.equals = equals;\n own(this);\n }\n\n live(): boolean {\n return this.consumersHead !== null;\n }\n\n get value(): T {\n // Refrescar antes de linkear: el consumidor debe registrar la versión\n // post-refresh, no la vieja (si no, stabilize() vería un falso cambio).\n if (!(this.flags & F_EVALUATED)) {\n this.flags |= F_EVALUATED;\n this.state = DIRTY;\n }\n ensureFreshComputed(this);\n if (activeConsumer) link(this, activeConsumer);\n return this._value as T;\n }\n\n peek(): T {\n return this._value as T;\n }\n\n dispose(): void {\n this.state = DISPOSED;\n disposeOwnerChildren(this);\n this.evalMark++; // primero: sweep quita TODOS los links (mark !== evalMark)\n sweep(this);\n let l = this.consumersHead;\n while (l) {\n const next = l.nextTarget;\n producerRemoveLink(this, l);\n // El link también sale de la lista de fuentes del consumidor — así\n // muere completo y puede reciclarse (antes quedaba colgado hasta el\n // próximo sweep del consumidor).\n if (l.prevSource) l.prevSource.nextSource = l.nextSource;\n else l.target.sourcesHead = l.nextSource;\n if (l.nextSource) l.nextSource.prevSource = l.prevSource;\n freeLink(l);\n l = next;\n }\n this.sourcesHead = null;\n drainReleaseQueue();\n unown(this);\n }\n}\n\nclass EffectNode implements ConsumerNode, OwnerLike {\n state: number = CLEAN;\n sourcesHead: Link | null = null;\n evalMark = 0;\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n flags = 0;\n _flush = 0;\n private _fn: () => void | (() => void);\n private _cleanup: (() => void) | void = undefined;\n private _errorHandler: ErrorHandler | null;\n\n constructor(fn: () => void | (() => void), render = false) {\n this._fn = fn;\n if (render) this.flags |= F_RENDER;\n this._errorHandler = activeErrorHandler;\n own(this);\n }\n\n live(): boolean {\n return true;\n }\n\n run(): void {\n if (this.flags & F_DEAD) return;\n if (this.state & RUNNING) {\n throw new Error(\"[elur-next] Effect re-entrante detectado.\");\n }\n if (typeof this._cleanup === \"function\") this._cleanup();\n // Los hijos (effects/computeds/cleanups creados en la pasada anterior)\n // mueren antes de re-evaluar — evita leaks de computations anidadas.\n // Guard inline: la mayoría de los effects no posee nada — salta la llamada.\n if (this.childrenHead !== null || this.cleanups !== null) disposeOwnerChildren(this);\n this.evalMark++;\n const prevConsumer = activeConsumer;\n const prevOwner = activeOwner;\n activeConsumer = this;\n activeOwner = this;\n this.state = RUNNING;\n let threw = true;\n try {\n this._cleanup = this._fn();\n threw = false;\n } catch (err) {\n if (this._errorHandler) {\n this._errorHandler(err);\n threw = false;\n } else {\n // A.16: burbujear por la cadena de owners hasta una boundary.\n let handled = false;\n for (let o: OwnerLike | null = this.parent; o; o = o.parent) {\n if (o.onOwnerError?.(err)) { handled = true; break; }\n }\n if (handled) threw = false;\n else throw err;\n }\n } finally {\n activeConsumer = prevConsumer;\n activeOwner = prevOwner;\n if (threw && this.state & RUNNING) this.state = DIRTY; // guard recuperable\n }\n sweep(this);\n this.state = CLEAN;\n }\n\n dispose(): void {\n if (this.flags & F_DEAD) return;\n this.flags |= F_DEAD;\n this.state = DISPOSED;\n if (typeof this._cleanup === \"function\") this._cleanup();\n disposeOwnerChildren(this);\n this.evalMark++;\n sweep(this);\n unown(this);\n }\n}\n\n// --- Factories ------------------------------------------------------------------\n\nexport function signal<T>(initialValue: T): Signal<T> {\n return new Signal(initialValue);\n}\n\n/**\n * B.12.4 (durabilidad): signal estática — `link()` no crea edges sobre ella,\n * así que dependencias estáticas no cuestan bookkeeping ni checks de refresh.\n * Escribirla rompe el contrato (warn en dev); el valor no se propaga.\n */\nexport function constSignal<T>(value: T): Signal<T> {\n const s = new Signal(value);\n s.durable = true;\n return s;\n}\n\nexport function computed<T>(\n fn: () => T,\n equals: (a: T, b: T) => boolean = Object.is,\n): Computed<T> {\n return new Computed(fn, equals);\n}\n\n/** Corre una vez al crear; re-corre sólo si una fuente cambió de verdad. */\nexport function effect(fn: () => void | (() => void)): () => void {\n const node = new EffectNode(fn);\n node.run();\n return () => node.dispose();\n}\n\n/**\n * @internal — effect de render (bindings DOM internos): se encola en la\n * cola de render, que se drena ANTES que la de user effects (B.8). La API\n * pública `effect()` sigue siendo la de usuario.\n */\nexport function _renderEffect(fn: () => void | (() => void)): () => void {\n const node = new EffectNode(fn, true);\n node.run();\n return () => node.dispose();\n}\n\nexport function batch<T>(fn: () => T): T {\n batchLevel++;\n try {\n return fn();\n } finally {\n batchLevel--;\n if (batchLevel === 0) flushEffects();\n }\n}\n\nexport function untrack<T>(fn: () => T): T {\n const prev = activeConsumer;\n activeConsumer = null;\n try {\n return fn();\n } finally {\n activeConsumer = prev;\n }\n}\n\nexport interface WatchOptions {\n immediate?: boolean;\n once?: boolean;\n}\n\nexport function watch<T>(\n source: Signal<T> | (() => T),\n cb: (value: T, oldValue: T | undefined) => void,\n options: WatchOptions = {},\n): () => void {\n const { immediate = false, once = false } = options;\n const getter: () => T =\n source instanceof Signal ? () => source.value : source;\n let first = true;\n let old: T | undefined;\n let stop: () => void = () => { };\n stop = effect(() => {\n const v = getter();\n if (first) {\n first = false;\n if (!immediate) {\n old = v;\n return;\n }\n }\n cb(v, old);\n old = v;\n if (once) stop();\n });\n return stop;\n}\n\nexport function nextTick(fn?: () => void): Promise<void> {\n return Promise.resolve().then(fn as () => void | Promise<void>);\n}\n\n// --- C.6/C.17: binding especializado signal→texto ----------------------------\n\n/**\n * Consumidor mínimo para bindings de texto (lo que el compiler emite para\n * `${expr}` cuando sabe que produce texto). Salta toda la maquinaria de\n * EffectNode: no hay cleanup-fn, ni innerCleanup, ni dispatch por tipo de\n * valor — re-corre el getter y escribe `nodeValue` directamente, en la cola\n * de render (B.8). Escribir es síncrono: el writer directo ES el punto.\n */\nclass TextBindingNode implements ConsumerNode, OwnerLike, Disposable {\n state = CLEAN;\n flags = F_RENDER;\n _flush = 0;\n sourcesHead: Link | null = null;\n evalMark = 0;\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n private _fn: () => unknown;\n private _node: { nodeValue: string | null };\n\n constructor(fn: () => unknown, node: { nodeValue: string | null }) {\n this._fn = fn;\n this._node = node;\n own(this);\n }\n\n live(): boolean {\n return true;\n }\n\n run(): void {\n if (this.flags & F_DEAD || this.state & RUNNING) return;\n if (this.childrenHead !== null || this.cleanups !== null) disposeOwnerChildren(this);\n this.evalMark++;\n const prevConsumer = activeConsumer;\n const prevOwner = activeOwner;\n activeConsumer = this;\n activeOwner = this;\n this.state = RUNNING;\n try {\n const v = this._fn();\n this._node.nodeValue = v == null || v === false ? \"\" : String(v);\n } finally {\n activeConsumer = prevConsumer;\n activeOwner = prevOwner;\n }\n sweep(this);\n this.state = CLEAN;\n }\n\n dispose(): void {\n if (this.flags & F_DEAD) return;\n this.flags |= F_DEAD;\n this.state = DISPOSED;\n disposeOwnerChildren(this);\n this.evalMark++;\n sweep(this);\n unown(this);\n }\n}\n\n/**\n * @internal — binding de texto especializado (C.6): `node.nodeValue` sigue\n * `fn()` con el mínimo consumer posible. `null`/`false`/`undefined` → \"\".\n * Devuelve disposer (mata la suscripción; el caller remueve el nodo).\n */\nexport function _bindText(\n node: { nodeValue: string | null },\n fn: () => unknown,\n): () => void {\n const b = new TextBindingNode(fn, node);\n b.run();\n return () => b.dispose();\n}\n\n// --- C.6 tier T1: binding directo signal→writer --------------------------------\n\n/**\n * C.6/C.8 tier T1: el compiler emite esto cuando la expresión es una lectura\n * directa de señal (`() => <path>.value`) — la SEÑAL viaja como arg y el\n * binding es una edge permanente: sin getter de usuario, sin tracking\n * dinámico, sin sweep — `run()` escribe y punto.\n *\n * La edge no se re-evalúa nunca (la fuente es fija por construcción), así que\n * el nodo no necesita `sourcesHead` real: el Link sólo vive en la lista de\n * consumers del producer. `seenVersion` se mantiene al día en run() para que\n * el early-cutoff de stabilize() siga funcionando si la fuente es un Computed\n * (un T1 sobre computed recibe CHECK, no DIRTY).\n */\nclass SignalBinding implements ConsumerNode, OwnerLike, Disposable {\n state = CLEAN;\n flags = F_RENDER;\n _flush = 0;\n sourcesHead: Link | null = null;\n evalMark = 0;\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n private _source: ProducerNode;\n private _link: Link | null = null;\n private _write: (v: unknown) => void;\n private _prev: unknown;\n\n constructor(source: Signal<unknown>, write: (v: unknown) => void) {\n this._source = source;\n this._write = write;\n // `.value` (no peek) sólo para computeds: fuerza la evaluación lazy de\n // los fríos — peek() devolvería _value crudo (stale). Signals planos van\n // por peek: evita el closure + save/restore de untrack en el path caliente\n // (un binding por fila en listas grandes).\n this._prev = source instanceof Computed\n ? untrack(() => source.value)\n : (source as Signal<unknown>).peek();\n write(this._prev);\n own(this);\n if (!source.durable) {\n const l = allocLink(source, this, source.version, 0);\n l.nextSource = null;\n this._link = l;\n this.sourcesHead = l;\n producerAddLink(source, l);\n // La edge puede despertar un computed frío — drenar adquisiciones.\n if (acquireQueue.length) drainAcquireQueue();\n }\n }\n\n live(): boolean {\n return true;\n }\n\n run(): void {\n const v = (this._source as Signal<unknown>).peek();\n if (this._link) this._link.seenVersion = this._source.version;\n if (v !== this._prev) {\n this._prev = v;\n this._write(v);\n }\n this.state = CLEAN;\n }\n\n dispose(): void {\n if (this.flags & F_DEAD) return;\n this.flags |= F_DEAD;\n this.state = DISPOSED;\n if (this._link) {\n producerRemoveLink(this._source, this._link);\n freeLink(this._link);\n this._link = null;\n this.sourcesHead = null;\n }\n unown(this);\n }\n}\n\n/**\n * @internal — C.6 T1: suscripción directa señal→writer. El writer corre una\n * vez al montar (valor inicial) y por cada cambio real de la señal, en la\n * cola de render. Devuelve disposer.\n */\nexport function _bindSignal<T>(\n source: Signal<T>,\n write: (v: T) => void,\n): () => void {\n const b = new SignalBinding(source, write as (v: unknown) => void);\n return () => b.dispose();\n}\n\n/**\n * C.7 T2: binding derivado — una edge permanente a la señal dep; run()\n * re-evalúa `get` untracked (las deps ya están conectadas por la edge —\n * evaluar tracked contaminaría al consumidor activo, ver bug de select1k),\n * compara el resultado y sólo escribe si cambió.\n */\nclass DerivedBinding implements ConsumerNode, OwnerLike, Disposable {\n state = CLEAN;\n flags = F_RENDER;\n _flush = 0;\n sourcesHead: Link | null = null;\n evalMark = 0;\n childrenHead: (Disposable & OwnerLike) | null = null;\n childCount = 0;\n nextOwned: (Disposable & OwnerLike) | null = null;\n prevOwned: (Disposable & OwnerLike) | null = null;\n cleanups: (() => void)[] | null = null;\n parent: OwnerLike | null = null;\n private _source: ProducerNode;\n private _link: Link | null = null;\n private _get: () => unknown;\n private _write: (v: unknown) => void;\n private _prev: unknown;\n\n constructor(\n source: Signal<unknown>,\n get: () => unknown,\n write: (v: unknown) => void,\n ) {\n this._source = source;\n this._get = get;\n this._write = write;\n this._prev = untrack(get);\n write(this._prev);\n own(this);\n if (!source.durable) {\n const l = allocLink(source, this, source.version, 0);\n l.nextSource = null;\n this._link = l;\n this.sourcesHead = l;\n producerAddLink(source, l);\n if (acquireQueue.length) drainAcquireQueue();\n }\n }\n\n live(): boolean {\n return true;\n }\n\n run(): void {\n const v = untrack(this._get);\n if (this._link) this._link.seenVersion = this._source.version;\n if (v !== this._prev) {\n this._prev = v;\n this._write(v);\n }\n this.state = CLEAN;\n }\n\n dispose(): void {\n if (this.flags & F_DEAD) return;\n this.flags |= F_DEAD;\n this.state = DISPOSED;\n if (this._link) {\n producerRemoveLink(this._source, this._link);\n freeLink(this._link);\n this._link = null;\n this.sourcesHead = null;\n }\n unown(this);\n }\n}\n\n/**\n * @internal — C.7 T2: suscripción directa señal→(getter derivado)→writer.\n * Equivale a `_bindSignal(dep, () => write(get()))` con compare incluido,\n * pero sin el closure intermedio ni el double-compare: un solo binding.\n * Devuelve disposer.\n */\nexport function _bindDerived<T>(\n source: Signal<unknown>,\n get: () => T,\n write: (v: T) => void,\n): () => void {\n const b = new DerivedBinding(source, get as () => unknown, write as (v: unknown) => void);\n return () => b.dispose();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
function e(){return typeof globalThis==`object`?globalThis.scheduler:void 0}function t(){return(typeof navigator==`object`?navigator:void 0)?.scheduling?.isInputPending?.()??!1}var n=[],r=!1,i=null;function a(e){if(n.push(e),r)return;r=!0;let t=()=>{r=!1;let e=n.splice(0);for(let t of e)t()};typeof MessageChannel==`function`?(i||(i=new MessageChannel,i.port1.onmessage=t),i.port2.postMessage(null)):setTimeout(t,0)}function o(){let t=e()?.yield;return t?t.call(e()):new Promise(e=>a(e))}exports.inputPending=t,exports.yieldControl=o;
|
|
2
|
+
//# sourceMappingURL=scheduler.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.cjs","names":[],"sources":["../../../../src/elur/next-2/scheduler.ts"],"sourcesContent":["// =============================================================================\n// --- F5: Scheduler de 3 niveles (Anexo §4.5) ----------------------------------\n// =============================================================================\n//\n// 1. sync/microtask — consistencia del grafo y DOM writes: no pasa por aquí\n// (flushEffects/queueDOMWrite ya lo hacen).\n// 2. `scheduler.postTask(priority)` — trabajo diferible: prefetch, islas,\n// efectos no visuales. Fallback: MessageChannel → setTimeout(0).\n// 3. `scheduler.yield()` — partir trabajo largo cediendo a input/paint con\n// continuación prioritaria (al frente de la cola, no al fondo).\n//\n// `postTask`/`isInputPending` son Chromium-only → TODOS los entrypoints llevan\n// fallback cross-browser. `requestIdleCallback` no se usa para nada urgente\n// (puede no disparar bajo carga — Anexo §4.5).\n// =============================================================================\n\nexport type TaskPriority = \"user-blocking\" | \"user-visible\" | \"background\";\n\ninterface SchedulerLike {\n postTask?(task: () => void, options?: { priority?: string }): unknown;\n yield?(): Promise<void>;\n}\n\n/**\n * `globalThis.scheduler` — lectura lazy: el objeto puede aparecer después\n * del module load (polyfills, tests) y una property read cuesta ~nada en\n * paths que ya van a una macrotask.\n */\nfunction getScheduler(): SchedulerLike | undefined {\n return typeof globalThis === \"object\"\n ? (globalThis as { scheduler?: SchedulerLike }).scheduler\n : undefined;\n}\n\n/**\n * ¿Hay input del usuario esperando ser procesado? (Chromium —\n * `navigator.scheduling.isInputPending`; `false` en otros engines y fuera\n * del navegador). Discrete events only — el default correcto para decidir\n * si conviene ceder el hilo antes de seguir procesando efectos.\n */\nexport function inputPending(): boolean {\n const nav =\n typeof navigator === \"object\"\n ? (navigator as { scheduling?: { isInputPending?: () => boolean } })\n : undefined;\n return nav?.scheduling?.isInputPending?.() ?? false;\n}\n\n// --- Macrotask fiable: MessageChannel (setTimeout tiene clamping en tabs\n// ocultos y tras llamadas anidadas — Anexo §4.13) -----------------------------\n\nconst _macroQueue: Array<() => void> = [];\nlet _macroScheduled = false;\nlet _channel: MessageChannel | null = null;\n\nfunction enqueueMacrotask(task: () => void): void {\n _macroQueue.push(task);\n if (_macroScheduled) return;\n _macroScheduled = true;\n const drain = (): void => {\n // Flag fuera antes de correr: un push durante el drain re-agenda.\n _macroScheduled = false;\n const q = _macroQueue.splice(0);\n for (const t of q) t();\n };\n if (typeof MessageChannel === \"function\") {\n if (!_channel) {\n _channel = new MessageChannel();\n _channel.port1.onmessage = drain;\n }\n _channel.port2.postMessage(null);\n } else {\n setTimeout(drain, 0);\n }\n}\n\n/**\n * Cede el hilo retomando con prioridad: `scheduler.yield()` (Chrome 129+)\n * reencola la continuación AL FRENTE de la cola de tareas — mejor que\n * setTimeout(0), que va al fondo. Fallback: MessageChannel → setTimeout.\n */\nexport function yieldControl(): Promise<void> {\n const y = getScheduler()?.yield;\n if (y) return y.call(getScheduler());\n return new Promise<void>((resolve) => enqueueMacrotask(resolve));\n}\n\n/**\n * Agenda una tarea con prioridad real del navegador cuando existe\n * (`scheduler.postTask`); si no, mapea a la mejor primitiva disponible:\n * - \"user-blocking\" → microtask (no puede esperar).\n * - \"user-visible\" → microtask (mantiene el turno, orden estable).\n * - \"background\" → macrotask (MessageChannel; no rIC — puede morir de\n * hambre bajo carga).\n */\nexport function scheduleTask(\n task: () => void,\n priority: TaskPriority = \"user-visible\",\n): void {\n const scheduler = getScheduler();\n if (scheduler?.postTask) {\n // En Chromium: prioridades reales para trabajo visible/deferible.\n scheduler.postTask(task, { priority });\n return;\n }\n if (priority === \"background\") enqueueMacrotask(task);\n else queueMicrotask(task);\n}\n"],"mappings":"AA4BA,SAAS,GAA0C,CACjD,OAAO,OAAO,YAAe,SACxB,WAA6C,UAC9C,IAAA,GASN,SAAgB,GAAwB,CAKtC,OAHE,OAAO,WAAc,SAChB,UACD,IAAA,KACM,YAAY,kBAAkB,EAAI,GAMhD,IAAM,EAAiC,EAAE,CACrC,EAAkB,GAClB,EAAkC,KAEtC,SAAS,EAAiB,EAAwB,CAEhD,GADA,EAAY,KAAK,EAAK,CAClB,EAAiB,OACrB,EAAkB,GAClB,IAAM,MAAoB,CAExB,EAAkB,GAClB,IAAM,EAAI,EAAY,OAAO,EAAE,CAC/B,IAAK,IAAM,KAAK,EAAG,GAAG,EAEpB,OAAO,gBAAmB,YACvB,IACH,EAAW,IAAI,eACf,EAAS,MAAM,UAAY,GAE7B,EAAS,MAAM,YAAY,KAAK,EAEhC,WAAW,EAAO,EAAE,CASxB,SAAgB,GAA8B,CAC5C,IAAM,EAAI,GAAc,EAAE,MAE1B,OADI,EAAU,EAAE,KAAK,GAAc,CAAC,CAC7B,IAAI,QAAe,GAAY,EAAiB,EAAQ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type TaskPriority = "user-blocking" | "user-visible" | "background";
|
|
2
|
+
/**
|
|
3
|
+
* ¿Hay input del usuario esperando ser procesado? (Chromium —
|
|
4
|
+
* `navigator.scheduling.isInputPending`; `false` en otros engines y fuera
|
|
5
|
+
* del navegador). Discrete events only — el default correcto para decidir
|
|
6
|
+
* si conviene ceder el hilo antes de seguir procesando efectos.
|
|
7
|
+
*/
|
|
8
|
+
export declare function inputPending(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Cede el hilo retomando con prioridad: `scheduler.yield()` (Chrome 129+)
|
|
11
|
+
* reencola la continuación AL FRENTE de la cola de tareas — mejor que
|
|
12
|
+
* setTimeout(0), que va al fondo. Fallback: MessageChannel → setTimeout.
|
|
13
|
+
*/
|
|
14
|
+
export declare function yieldControl(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Agenda una tarea con prioridad real del navegador cuando existe
|
|
17
|
+
* (`scheduler.postTask`); si no, mapea a la mejor primitiva disponible:
|
|
18
|
+
* - "user-blocking" → microtask (no puede esperar).
|
|
19
|
+
* - "user-visible" → microtask (mantiene el turno, orden estable).
|
|
20
|
+
* - "background" → macrotask (MessageChannel; no rIC — puede morir de
|
|
21
|
+
* hambre bajo carga).
|
|
22
|
+
*/
|
|
23
|
+
export declare function scheduleTask(task: () => void, priority?: TaskPriority): void;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type TaskPriority = "user-blocking" | "user-visible" | "background";
|
|
2
|
+
/**
|
|
3
|
+
* ¿Hay input del usuario esperando ser procesado? (Chromium —
|
|
4
|
+
* `navigator.scheduling.isInputPending`; `false` en otros engines y fuera
|
|
5
|
+
* del navegador). Discrete events only — el default correcto para decidir
|
|
6
|
+
* si conviene ceder el hilo antes de seguir procesando efectos.
|
|
7
|
+
*/
|
|
8
|
+
export declare function inputPending(): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Cede el hilo retomando con prioridad: `scheduler.yield()` (Chrome 129+)
|
|
11
|
+
* reencola la continuación AL FRENTE de la cola de tareas — mejor que
|
|
12
|
+
* setTimeout(0), que va al fondo. Fallback: MessageChannel → setTimeout.
|
|
13
|
+
*/
|
|
14
|
+
export declare function yieldControl(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Agenda una tarea con prioridad real del navegador cuando existe
|
|
17
|
+
* (`scheduler.postTask`); si no, mapea a la mejor primitiva disponible:
|
|
18
|
+
* - "user-blocking" → microtask (no puede esperar).
|
|
19
|
+
* - "user-visible" → microtask (mantiene el turno, orden estable).
|
|
20
|
+
* - "background" → macrotask (MessageChannel; no rIC — puede morir de
|
|
21
|
+
* hambre bajo carga).
|
|
22
|
+
*/
|
|
23
|
+
export declare function scheduleTask(task: () => void, priority?: TaskPriority): void;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/elur/next-2/scheduler.ts
|
|
2
|
+
function e() {
|
|
3
|
+
return typeof globalThis == "object" ? globalThis.scheduler : void 0;
|
|
4
|
+
}
|
|
5
|
+
function t() {
|
|
6
|
+
return (typeof navigator == "object" ? navigator : void 0)?.scheduling?.isInputPending?.() ?? !1;
|
|
7
|
+
}
|
|
8
|
+
var n = [], r = !1, i = null;
|
|
9
|
+
function a(e) {
|
|
10
|
+
if (n.push(e), r) return;
|
|
11
|
+
r = !0;
|
|
12
|
+
let t = () => {
|
|
13
|
+
r = !1;
|
|
14
|
+
let e = n.splice(0);
|
|
15
|
+
for (let t of e) t();
|
|
16
|
+
};
|
|
17
|
+
typeof MessageChannel == "function" ? (i || (i = new MessageChannel(), i.port1.onmessage = t), i.port2.postMessage(null)) : setTimeout(t, 0);
|
|
18
|
+
}
|
|
19
|
+
function o() {
|
|
20
|
+
let t = e()?.yield;
|
|
21
|
+
return t ? t.call(e()) : new Promise((e) => a(e));
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { t as inputPending, o as yieldControl };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":[],"sources":["../../../../src/elur/next-2/scheduler.ts"],"sourcesContent":["// =============================================================================\n// --- F5: Scheduler de 3 niveles (Anexo §4.5) ----------------------------------\n// =============================================================================\n//\n// 1. sync/microtask — consistencia del grafo y DOM writes: no pasa por aquí\n// (flushEffects/queueDOMWrite ya lo hacen).\n// 2. `scheduler.postTask(priority)` — trabajo diferible: prefetch, islas,\n// efectos no visuales. Fallback: MessageChannel → setTimeout(0).\n// 3. `scheduler.yield()` — partir trabajo largo cediendo a input/paint con\n// continuación prioritaria (al frente de la cola, no al fondo).\n//\n// `postTask`/`isInputPending` son Chromium-only → TODOS los entrypoints llevan\n// fallback cross-browser. `requestIdleCallback` no se usa para nada urgente\n// (puede no disparar bajo carga — Anexo §4.5).\n// =============================================================================\n\nexport type TaskPriority = \"user-blocking\" | \"user-visible\" | \"background\";\n\ninterface SchedulerLike {\n postTask?(task: () => void, options?: { priority?: string }): unknown;\n yield?(): Promise<void>;\n}\n\n/**\n * `globalThis.scheduler` — lectura lazy: el objeto puede aparecer después\n * del module load (polyfills, tests) y una property read cuesta ~nada en\n * paths que ya van a una macrotask.\n */\nfunction getScheduler(): SchedulerLike | undefined {\n return typeof globalThis === \"object\"\n ? (globalThis as { scheduler?: SchedulerLike }).scheduler\n : undefined;\n}\n\n/**\n * ¿Hay input del usuario esperando ser procesado? (Chromium —\n * `navigator.scheduling.isInputPending`; `false` en otros engines y fuera\n * del navegador). Discrete events only — el default correcto para decidir\n * si conviene ceder el hilo antes de seguir procesando efectos.\n */\nexport function inputPending(): boolean {\n const nav =\n typeof navigator === \"object\"\n ? (navigator as { scheduling?: { isInputPending?: () => boolean } })\n : undefined;\n return nav?.scheduling?.isInputPending?.() ?? false;\n}\n\n// --- Macrotask fiable: MessageChannel (setTimeout tiene clamping en tabs\n// ocultos y tras llamadas anidadas — Anexo §4.13) -----------------------------\n\nconst _macroQueue: Array<() => void> = [];\nlet _macroScheduled = false;\nlet _channel: MessageChannel | null = null;\n\nfunction enqueueMacrotask(task: () => void): void {\n _macroQueue.push(task);\n if (_macroScheduled) return;\n _macroScheduled = true;\n const drain = (): void => {\n // Flag fuera antes de correr: un push durante el drain re-agenda.\n _macroScheduled = false;\n const q = _macroQueue.splice(0);\n for (const t of q) t();\n };\n if (typeof MessageChannel === \"function\") {\n if (!_channel) {\n _channel = new MessageChannel();\n _channel.port1.onmessage = drain;\n }\n _channel.port2.postMessage(null);\n } else {\n setTimeout(drain, 0);\n }\n}\n\n/**\n * Cede el hilo retomando con prioridad: `scheduler.yield()` (Chrome 129+)\n * reencola la continuación AL FRENTE de la cola de tareas — mejor que\n * setTimeout(0), que va al fondo. Fallback: MessageChannel → setTimeout.\n */\nexport function yieldControl(): Promise<void> {\n const y = getScheduler()?.yield;\n if (y) return y.call(getScheduler());\n return new Promise<void>((resolve) => enqueueMacrotask(resolve));\n}\n\n/**\n * Agenda una tarea con prioridad real del navegador cuando existe\n * (`scheduler.postTask`); si no, mapea a la mejor primitiva disponible:\n * - \"user-blocking\" → microtask (no puede esperar).\n * - \"user-visible\" → microtask (mantiene el turno, orden estable).\n * - \"background\" → macrotask (MessageChannel; no rIC — puede morir de\n * hambre bajo carga).\n */\nexport function scheduleTask(\n task: () => void,\n priority: TaskPriority = \"user-visible\",\n): void {\n const scheduler = getScheduler();\n if (scheduler?.postTask) {\n // En Chromium: prioridades reales para trabajo visible/deferible.\n scheduler.postTask(task, { priority });\n return;\n }\n if (priority === \"background\") enqueueMacrotask(task);\n else queueMicrotask(task);\n}\n"],"mappings":";AA4BA,SAAS,IAA0C;AACjD,QAAO,OAAO,cAAe,WACxB,WAA6C,YAC9C,KAAA;;AASN,SAAgB,IAAwB;AAKtC,SAHE,OAAO,aAAc,WAChB,YACD,KAAA,IACM,YAAY,kBAAkB,IAAI;;AAMhD,IAAM,IAAiC,EAAE,EACrC,IAAkB,IAClB,IAAkC;AAEtC,SAAS,EAAiB,GAAwB;AAEhD,KADA,EAAY,KAAK,EAAK,EAClB,EAAiB;AACrB,KAAkB;CAClB,IAAM,UAAoB;AAExB,MAAkB;EAClB,IAAM,IAAI,EAAY,OAAO,EAAE;AAC/B,OAAK,IAAM,KAAK,EAAG,IAAG;;AAExB,CAAI,OAAO,kBAAmB,cACvB,MACH,IAAW,IAAI,gBAAgB,EAC/B,EAAS,MAAM,YAAY,IAE7B,EAAS,MAAM,YAAY,KAAK,IAEhC,WAAW,GAAO,EAAE;;AASxB,SAAgB,IAA8B;CAC5C,IAAM,IAAI,GAAc,EAAE;AAE1B,QADI,IAAU,EAAE,KAAK,GAAc,CAAC,GAC7B,IAAI,SAAe,MAAY,EAAiB,EAAQ,CAAC"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./elur/next-2/reactivity.cjs`);function t(t,n={}){let{storage:r=localStorage,exclude:i=[],serialize:a=JSON.stringify,deserialize:o=JSON.parse,debounce:s=0}=n;return n=>{e.untrack(async()=>{try{let e=await r.getItem(t);if(!e)return;let a=o(e),s={};for(let e of Object.keys(a))e in n.$state&&!i.includes(e)&&(s[e]=a[e]);Object.keys(s).length>0&&n.$patch(s)}catch{}});let c;return e.watch(n.$stateSignal,e=>{let n=()=>{try{let n=i.length===0?e:Object.fromEntries(Object.entries(e).filter(([e])=>!i.includes(e)));r.setItem(t,a(n))}catch{}};s>0?(clearTimeout(c),c=setTimeout(n,s)):n()})}}function n(t={}){let{collapsed:n=!0,filter:r}=t;return t=>{let i=n?console.groupCollapsed:console.group;return e.watch(t.$stateSignal,(e,n)=>{if(!n)return;let a={};for(let t of Object.keys(e))Object.is(e[t],n[t])||(a[t]=e[t]);Object.keys(a).length!==0&&(r&&!r(a)||(i(`%c[elur:${t.$id}]%c ${Object.keys(a).join(`, `)}`,`color:#7F77DD;font-weight:500`,`color:inherit;font-weight:400`),console.log(`prev →`,n),console.log(`next →`,e),console.log(`diff →`,a),console.groupEnd()))},{immediate:!0})}}function r(e){return t=>{let n=t._guardFns;for(let t of e)n.push(t);return()=>{for(let t of e){let e=n.indexOf(t);e>=0&&n.splice(e,1)}}}}function i(t,n){return r=>e.watch(t.$stateSignal,e=>{n(e,r)})}exports.bridgePlugin=i,exports.guardPlugin=r,exports.loggerPlugin=n,exports.persistPlugin=t;
|
|
2
2
|
//# sourceMappingURL=plugins.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugins.cjs","names":[],"sources":["../../src/elur/plugins.ts"],"sourcesContent":["import { watch, untrack } from \"./reactivity.js\";\nimport { type ElurPlugin, type Store, type GuardFn } from \"./store.js\";\n\n/**\n * Minimal interface that any storage adapter must implement.\n * Compatible with localStorage, sessionStorage, AsyncStorage, IndexedDB, etc.\n */\ninterface StorageAdapter {\n /** Retrieves an item from storage. Returns null if not found. */\n getItem(key: string): string | null | Promise<string | null>;\n /** Stores an item in storage. */\n setItem(key: string, value: string): void | Promise<void>;\n /** Optional: Removes an item from storage. */\n removeItem?(key: string): void | Promise<void>;\n}\n\n/**\n * Persists the store state to a storage medium (defaults to localStorage).\n * Automatically hydrates on initialization and saves changes on every reactive flush.\n */\nexport function persistPlugin<T extends Record<string, unknown>>(\n storageKey: string,\n opts: {\n /** Storage adapter to use. Defaults to localStorage. */\n storage?: StorageAdapter;\n /** Properties to exclude from persistence. */\n exclude?: Array<keyof T>;\n /** Custom serialization function. Defaults to JSON.stringify. */\n serialize?: (state: T) => string;\n /** Custom deserialization function. Defaults to JSON.parse. */\n deserialize?: (raw: string) => Partial<T>;\n /** Debounce interval in milliseconds for batching writes. Defaults to 0 (immediate). */\n debounce?: number;\n } = {},\n): ElurPlugin<T> {\n const {\n storage = localStorage,\n exclude = [],\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n debounce = 0,\n } = opts;\n\n return (store) => {\n untrack(async () => {\n try {\n const raw = await storage.getItem(storageKey);\n if (!raw) return;\n const saved = deserialize(raw) as Partial<T>;\n\n const patch: Partial<T> = {};\n for (const key of Object.keys(saved)) {\n if (key in store.$state && !exclude.includes(key as keyof T)) {\n (patch as Record<string, unknown>)[key] = (saved as Record<string, unknown>)[key];\n }\n }\n if (Object.keys(patch).length > 0) store.$patch(patch);\n } catch {\n // Silently fail if storage is inaccessible (e.g., SSR or Private Mode).\n }\n });\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n return watch(store.$stateSignal, (next) => {\n const persist = () => {\n try {\n const toSave = exclude.length === 0\n ? next\n : Object.fromEntries(\n Object.entries(next).filter(([k]) => !exclude.includes(k as keyof T))\n ) as T;\n storage.setItem(storageKey, serialize(toSave));\n } catch {\n // Fail silently if storage quota is exceeded.\n }\n };\n\n if (debounce > 0) {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(persist, debounce);\n } else {\n persist();\n }\n });\n };\n}\n\n/**\n * Logs state transitions to the console.\n * Calculates property-level diffs and groups output by store ID.\n */\nexport function loggerPlugin<T extends Record<string, unknown>>(\n opts: {\n /** Whether the console group should be collapsed by default. */\n collapsed?: boolean;\n /** Optional filter to skip logging for specific changes. */\n filter?: (diff: Partial<T>) => boolean;\n } = {},\n): ElurPlugin<T> {\n const { collapsed = true, filter } = opts;\n\n return (store) => {\n const groupFn = collapsed ? console.groupCollapsed : console.group;\n\n return watch(store.$stateSignal, (next, prev) => {\n if (!prev) return;\n\n const diff: Partial<T> = {};\n for (const key of Object.keys(next) as Array<keyof T & string>) {\n if (!Object.is(next[key], prev[key])) {\n (diff as Record<string, unknown>)[key] = next[key];\n }\n }\n\n if (Object.keys(diff).length === 0) return;\n if (filter && !filter(diff)) return;\n\n groupFn(\n `%c[elur:${store.$id}]%c ${Object.keys(diff).join(\", \")}`,\n \"color:#7F77DD;font-weight:500\",\n \"color:inherit;font-weight:400\",\n );\n console.log(\"prev →\", prev);\n console.log(\"next →\", next);\n console.log(\"diff →\", diff);\n console.groupEnd();\n }, { immediate: true });\n };\n}\n\n/**\n * Intercepts $patch and $reset calls to validate or transform state before it is applied.\n */\nexport function guardPlugin<T extends Record<string, unknown>>(\n guards: GuardFn<T>[],\n): ElurPlugin<T> {\n return (store) => {\n const registry = (store as unknown as { _guardFns: GuardFn<T>[] })._guardFns;\n for (const guard of guards) {\n registry.push(guard);\n }\n return () => {\n for (const guard of guards) {\n const idx = registry.indexOf(guard);\n if (idx >= 0) registry.splice(idx, 1);\n }\n };\n };\n}\n\n/**\n * Synchronizes data between two stores by watching one and patching the other.\n */\nexport function bridgePlugin<\n TA extends Record<string, unknown>,\n TB extends Record<string, unknown>,\n>(\n sourceStore: Store<TB>,\n sync: (sourceState: TB, targetStore: Store<TA>) => void,\n): ElurPlugin<TA> {\n return (targetStore) => {\n return watch(sourceStore.$stateSignal, (sourceState) => {\n sync(sourceState, targetStore);\n });\n };\n}"],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugins.cjs","names":[],"sources":["../../src/elur/plugins.ts"],"sourcesContent":["import { watch, untrack } from \"./reactivity.js\";\nimport { type ElurPlugin, type Store, type GuardFn } from \"./store.js\";\n\n/**\n * Minimal interface that any storage adapter must implement.\n * Compatible with localStorage, sessionStorage, AsyncStorage, IndexedDB, etc.\n */\ninterface StorageAdapter {\n /** Retrieves an item from storage. Returns null if not found. */\n getItem(key: string): string | null | Promise<string | null>;\n /** Stores an item in storage. */\n setItem(key: string, value: string): void | Promise<void>;\n /** Optional: Removes an item from storage. */\n removeItem?(key: string): void | Promise<void>;\n}\n\n/**\n * Persists the store state to a storage medium (defaults to localStorage).\n * Automatically hydrates on initialization and saves changes on every reactive flush.\n */\nexport function persistPlugin<T extends Record<string, unknown>>(\n storageKey: string,\n opts: {\n /** Storage adapter to use. Defaults to localStorage. */\n storage?: StorageAdapter;\n /** Properties to exclude from persistence. */\n exclude?: Array<keyof T>;\n /** Custom serialization function. Defaults to JSON.stringify. */\n serialize?: (state: T) => string;\n /** Custom deserialization function. Defaults to JSON.parse. */\n deserialize?: (raw: string) => Partial<T>;\n /** Debounce interval in milliseconds for batching writes. Defaults to 0 (immediate). */\n debounce?: number;\n } = {},\n): ElurPlugin<T> {\n const {\n storage = localStorage,\n exclude = [],\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n debounce = 0,\n } = opts;\n\n return (store) => {\n untrack(async () => {\n try {\n const raw = await storage.getItem(storageKey);\n if (!raw) return;\n const saved = deserialize(raw) as Partial<T>;\n\n const patch: Partial<T> = {};\n for (const key of Object.keys(saved)) {\n if (key in store.$state && !exclude.includes(key as keyof T)) {\n (patch as Record<string, unknown>)[key] = (saved as Record<string, unknown>)[key];\n }\n }\n if (Object.keys(patch).length > 0) store.$patch(patch);\n } catch {\n // Silently fail if storage is inaccessible (e.g., SSR or Private Mode).\n }\n });\n\n let timeoutId: ReturnType<typeof setTimeout>;\n\n return watch(store.$stateSignal, (next) => {\n const persist = () => {\n try {\n const toSave = exclude.length === 0\n ? next\n : Object.fromEntries(\n Object.entries(next).filter(([k]) => !exclude.includes(k as keyof T))\n ) as T;\n storage.setItem(storageKey, serialize(toSave));\n } catch {\n // Fail silently if storage quota is exceeded.\n }\n };\n\n if (debounce > 0) {\n clearTimeout(timeoutId);\n timeoutId = setTimeout(persist, debounce);\n } else {\n persist();\n }\n });\n };\n}\n\n/**\n * Logs state transitions to the console.\n * Calculates property-level diffs and groups output by store ID.\n */\nexport function loggerPlugin<T extends Record<string, unknown>>(\n opts: {\n /** Whether the console group should be collapsed by default. */\n collapsed?: boolean;\n /** Optional filter to skip logging for specific changes. */\n filter?: (diff: Partial<T>) => boolean;\n } = {},\n): ElurPlugin<T> {\n const { collapsed = true, filter } = opts;\n\n return (store) => {\n const groupFn = collapsed ? console.groupCollapsed : console.group;\n\n return watch(store.$stateSignal, (next, prev) => {\n if (!prev) return;\n\n const diff: Partial<T> = {};\n for (const key of Object.keys(next) as Array<keyof T & string>) {\n if (!Object.is(next[key], prev[key])) {\n (diff as Record<string, unknown>)[key] = next[key];\n }\n }\n\n if (Object.keys(diff).length === 0) return;\n if (filter && !filter(diff)) return;\n\n groupFn(\n `%c[elur:${store.$id}]%c ${Object.keys(diff).join(\", \")}`,\n \"color:#7F77DD;font-weight:500\",\n \"color:inherit;font-weight:400\",\n );\n console.log(\"prev →\", prev);\n console.log(\"next →\", next);\n console.log(\"diff →\", diff);\n console.groupEnd();\n }, { immediate: true });\n };\n}\n\n/**\n * Intercepts $patch and $reset calls to validate or transform state before it is applied.\n */\nexport function guardPlugin<T extends Record<string, unknown>>(\n guards: GuardFn<T>[],\n): ElurPlugin<T> {\n return (store) => {\n const registry = (store as unknown as { _guardFns: GuardFn<T>[] })._guardFns;\n for (const guard of guards) {\n registry.push(guard);\n }\n return () => {\n for (const guard of guards) {\n const idx = registry.indexOf(guard);\n if (idx >= 0) registry.splice(idx, 1);\n }\n };\n };\n}\n\n/**\n * Synchronizes data between two stores by watching one and patching the other.\n */\nexport function bridgePlugin<\n TA extends Record<string, unknown>,\n TB extends Record<string, unknown>,\n>(\n sourceStore: Store<TB>,\n sync: (sourceState: TB, targetStore: Store<TA>) => void,\n): ElurPlugin<TA> {\n return (targetStore) => {\n return watch(sourceStore.$stateSignal, (sourceState) => {\n sync(sourceState, targetStore);\n });\n };\n}"],"mappings":"mHAoBA,SAAgB,EACZ,EACA,EAWI,EAAE,CACO,CACb,GAAM,CACF,UAAU,aACV,UAAU,EAAE,CACZ,YAAY,KAAK,UACjB,cAAc,KAAK,MACnB,WAAW,GACX,EAEJ,MAAQ,IAAU,CACd,EAAA,QAAQ,SAAY,CAChB,GAAI,CACA,IAAM,EAAM,MAAM,EAAQ,QAAQ,EAAW,CAC7C,GAAI,CAAC,EAAK,OACV,IAAM,EAAQ,EAAY,EAAI,CAExB,EAAoB,EAAE,CAC5B,IAAK,IAAM,KAAO,OAAO,KAAK,EAAM,CAC5B,KAAO,EAAM,QAAU,CAAC,EAAQ,SAAS,EAAe,GACvD,EAAkC,GAAQ,EAAkC,IAGjF,OAAO,KAAK,EAAM,CAAC,OAAS,GAAG,EAAM,OAAO,EAAM,MAClD,IAGV,CAEF,IAAI,EAEJ,OAAO,EAAA,MAAM,EAAM,aAAe,GAAS,CACvC,IAAM,MAAgB,CAClB,GAAI,CACA,IAAM,EAAS,EAAQ,SAAW,EAC5B,EACA,OAAO,YACL,OAAO,QAAQ,EAAK,CAAC,QAAQ,CAAC,KAAO,CAAC,EAAQ,SAAS,EAAa,CAAC,CACxE,CACL,EAAQ,QAAQ,EAAY,EAAU,EAAO,CAAC,MAC1C,IAKR,EAAW,GACX,aAAa,EAAU,CACvB,EAAY,WAAW,EAAS,EAAS,EAEzC,GAAS,EAEf,EAQV,SAAgB,EACZ,EAKI,EAAE,CACO,CACb,GAAM,CAAE,YAAY,GAAM,UAAW,EAErC,MAAQ,IAAU,CACd,IAAM,EAAU,EAAY,QAAQ,eAAiB,QAAQ,MAE7D,OAAO,EAAA,MAAM,EAAM,cAAe,EAAM,IAAS,CAC7C,GAAI,CAAC,EAAM,OAEX,IAAM,EAAmB,EAAE,CAC3B,IAAK,IAAM,KAAO,OAAO,KAAK,EAAK,CAC1B,OAAO,GAAG,EAAK,GAAM,EAAK,GAAK,GAC/B,EAAiC,GAAO,EAAK,IAIlD,OAAO,KAAK,EAAK,CAAC,SAAW,IAC7B,GAAU,CAAC,EAAO,EAAK,GAE3B,EACI,WAAW,EAAM,IAAI,MAAM,OAAO,KAAK,EAAK,CAAC,KAAK,KAAK,GACvD,gCACA,gCACH,CACD,QAAQ,IAAI,UAAW,EAAK,CAC5B,QAAQ,IAAI,UAAW,EAAK,CAC5B,QAAQ,IAAI,UAAW,EAAK,CAC5B,QAAQ,UAAU,IACnB,CAAE,UAAW,GAAM,CAAC,EAO/B,SAAgB,EACZ,EACa,CACb,MAAQ,IAAU,CACd,IAAM,EAAY,EAAiD,UACnE,IAAK,IAAM,KAAS,EAChB,EAAS,KAAK,EAAM,CAExB,UAAa,CACT,IAAK,IAAM,KAAS,EAAQ,CACxB,IAAM,EAAM,EAAS,QAAQ,EAAM,CAC/B,GAAO,GAAG,EAAS,OAAO,EAAK,EAAE,IASrD,SAAgB,EAIZ,EACA,EACc,CACd,MAAQ,IACG,EAAA,MAAM,EAAY,aAAe,GAAgB,CACpD,EAAK,EAAa,EAAY,EAChC"}
|
package/dist/lib/elur/plugins.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { untrack as e, watch as t } from "./
|
|
1
|
+
import { untrack as e, watch as t } from "./elur/next-2/reactivity.js";
|
|
2
2
|
//#region src/elur/plugins.ts
|
|
3
3
|
function n(n, r = {}) {
|
|
4
4
|
let { storage: i = localStorage, exclude: a = [], serialize: o = JSON.stringify, deserialize: s = JSON.parse, debounce: c = 0 } = r;
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
2
|
-
//# sourceMappingURL=signals.cjs.map
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./elur/next-2/reactivity.cjs`);exports.Computed=e.Computed,exports.Owner=e.Owner,exports.ReactiveCycleError=e.ReactiveCycleError,exports.Signal=e.Signal,exports._addSignalDebugHooks=e._addSignalDebugHooks,exports._bindDerived=e._bindDerived,exports._bindSignal=e._bindSignal,exports._bindText=e._bindText,exports._getNotifyBufSize=e._getNotifyBufSize,exports._popErrorHandler=e._popErrorHandler,exports._pushErrorHandler=e._pushErrorHandler,exports._renderEffect=e._renderEffect,exports._setSignalDebugHooks=e._setSignalDebugHooks,exports.batch=e.batch,exports.computed=e.computed,exports.constSignal=e.constSignal,exports.createRoot=e.createRoot,exports.effect=e.effect,exports.getOwner=e.getOwner,exports.nextTick=e.nextTick,exports.onCleanup=e.onCleanup,exports.runWithOwner=e.runWithOwner,exports.signal=e.signal,exports.untrack=e.untrack,exports.watch=e.watch;
|
|
@@ -1,67 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @internal — Register an error boundary handler. All `effect()` calls made
|
|
3
|
-
* synchronously while this handler is active will capture it. When those
|
|
4
|
-
* effects re-run and throw, the captured handler is invoked.
|
|
5
|
-
*/
|
|
6
|
-
export declare function _pushErrorHandler(h: (err: unknown) => void): void;
|
|
7
|
-
/** @internal — Restore the previous error boundary handler. */
|
|
8
|
-
export declare function _popErrorHandler(): void;
|
|
9
|
-
export interface _SignalDebugHooks {
|
|
10
|
-
onCreate?: (signal: Signal<any>, initialValue: unknown) => void;
|
|
11
|
-
onWrite?: (signal: Signal<any>, value: unknown) => void;
|
|
12
|
-
}
|
|
13
|
-
export declare function _setSignalDebugHooks(hooks: _SignalDebugHooks | null): void;
|
|
14
|
-
/**
|
|
15
|
-
* @internal — Adds a debug hook subscriber WITHOUT replacing existing ones
|
|
16
|
-
* (unlike `_setSignalDebugHooks`). Returns an unsubscribe function.
|
|
17
|
-
* Zero cost when no subscribers are registered: the setter keeps reading a
|
|
18
|
-
* single `signalDebugHooks` object.
|
|
19
|
-
*/
|
|
20
|
-
export declare function _addSignalDebugHooks(hooks: _SignalDebugHooks): () => void;
|
|
21
|
-
/** @internal — notify buffer capacity, exposed for tests. */
|
|
22
|
-
export declare function _getNotifyBufSize(): number;
|
|
23
|
-
export declare class Signal<T> {
|
|
24
|
-
private _value;
|
|
25
|
-
private _subs;
|
|
26
|
-
constructor(initialValue: T);
|
|
27
|
-
/** Read the current value. Subscribes the active effect if one exists. */
|
|
28
|
-
get value(): T;
|
|
29
|
-
/** Write a new value. Notifies subscribers when the value changes. */
|
|
30
|
-
set value(newValue: T);
|
|
31
|
-
/** Mutate the value via a updater function. */
|
|
32
|
-
update(fn: (current: T) => T): void;
|
|
33
|
-
/** Read without subscribing the active effect. */
|
|
34
|
-
peek(): T;
|
|
35
|
-
/** @internal */
|
|
36
|
-
_removeSub(sub: () => void): void;
|
|
37
|
-
private _notify;
|
|
38
|
-
dispose(): void;
|
|
39
|
-
}
|
|
40
|
-
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
41
|
-
/**
|
|
42
|
-
* Runs `fn` and re-runs it whenever any signal read inside changes.
|
|
43
|
-
* Returns a dispose function to tear down the effect.
|
|
44
|
-
* If `fn` returns a function, it is called as cleanup before each re-run
|
|
45
|
-
* and on disposal.
|
|
46
|
-
*/
|
|
47
|
-
export declare function effect(fn: () => void | (() => void)): () => void;
|
|
48
|
-
export declare function computed<T>(fn: () => T, equals?: (a: T, b: T) => boolean): Signal<T> & {
|
|
49
|
-
dispose(): void;
|
|
50
|
-
};
|
|
51
|
-
/** Groups multiple signal writes so effects flush once at the end. */
|
|
52
|
-
export declare function batch(fn: () => void): void;
|
|
53
|
-
/** Executes `fn` without subscribing to any signals read inside it. */
|
|
54
|
-
export declare function untrack<T>(fn: () => T): T;
|
|
55
|
-
export interface WatchOptions {
|
|
56
|
-
/** Fire the callback immediately with the current value. Default: `false`. */
|
|
57
|
-
immediate?: boolean;
|
|
58
|
-
/** Automatically dispose after the first callback invocation. Default: `false`. */
|
|
59
|
-
once?: boolean;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Watches a reactive source and calls `callback(newValue, oldValue)` on each change.
|
|
63
|
-
* Accepts a Signal or a getter function. Returns a dispose function.
|
|
64
|
-
*/
|
|
65
|
-
export declare function watch<T>(source: Signal<T> | (() => T), callback: (newValue: T, oldValue: T | undefined) => void, options?: WatchOptions): () => void;
|
|
66
|
-
/** Returns a promise that resolves on the next microtask. Accepts an optional callback. */
|
|
67
|
-
export declare function nextTick(fn?: () => void): Promise<void>;
|
|
1
|
+
export * from "./next-2/reactivity.js";
|
|
@@ -1,67 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* @internal — Register an error boundary handler. All `effect()` calls made
|
|
3
|
-
* synchronously while this handler is active will capture it. When those
|
|
4
|
-
* effects re-run and throw, the captured handler is invoked.
|
|
5
|
-
*/
|
|
6
|
-
export declare function _pushErrorHandler(h: (err: unknown) => void): void;
|
|
7
|
-
/** @internal — Restore the previous error boundary handler. */
|
|
8
|
-
export declare function _popErrorHandler(): void;
|
|
9
|
-
export interface _SignalDebugHooks {
|
|
10
|
-
onCreate?: (signal: Signal<any>, initialValue: unknown) => void;
|
|
11
|
-
onWrite?: (signal: Signal<any>, value: unknown) => void;
|
|
12
|
-
}
|
|
13
|
-
export declare function _setSignalDebugHooks(hooks: _SignalDebugHooks | null): void;
|
|
14
|
-
/**
|
|
15
|
-
* @internal — Adds a debug hook subscriber WITHOUT replacing existing ones
|
|
16
|
-
* (unlike `_setSignalDebugHooks`). Returns an unsubscribe function.
|
|
17
|
-
* Zero cost when no subscribers are registered: the setter keeps reading a
|
|
18
|
-
* single `signalDebugHooks` object.
|
|
19
|
-
*/
|
|
20
|
-
export declare function _addSignalDebugHooks(hooks: _SignalDebugHooks): () => void;
|
|
21
|
-
/** @internal — notify buffer capacity, exposed for tests. */
|
|
22
|
-
export declare function _getNotifyBufSize(): number;
|
|
23
|
-
export declare class Signal<T> {
|
|
24
|
-
private _value;
|
|
25
|
-
private _subs;
|
|
26
|
-
constructor(initialValue: T);
|
|
27
|
-
/** Read the current value. Subscribes the active effect if one exists. */
|
|
28
|
-
get value(): T;
|
|
29
|
-
/** Write a new value. Notifies subscribers when the value changes. */
|
|
30
|
-
set value(newValue: T);
|
|
31
|
-
/** Mutate the value via a updater function. */
|
|
32
|
-
update(fn: (current: T) => T): void;
|
|
33
|
-
/** Read without subscribing the active effect. */
|
|
34
|
-
peek(): T;
|
|
35
|
-
/** @internal */
|
|
36
|
-
_removeSub(sub: () => void): void;
|
|
37
|
-
private _notify;
|
|
38
|
-
dispose(): void;
|
|
39
|
-
}
|
|
40
|
-
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
41
|
-
/**
|
|
42
|
-
* Runs `fn` and re-runs it whenever any signal read inside changes.
|
|
43
|
-
* Returns a dispose function to tear down the effect.
|
|
44
|
-
* If `fn` returns a function, it is called as cleanup before each re-run
|
|
45
|
-
* and on disposal.
|
|
46
|
-
*/
|
|
47
|
-
export declare function effect(fn: () => void | (() => void)): () => void;
|
|
48
|
-
export declare function computed<T>(fn: () => T, equals?: (a: T, b: T) => boolean): Signal<T> & {
|
|
49
|
-
dispose(): void;
|
|
50
|
-
};
|
|
51
|
-
/** Groups multiple signal writes so effects flush once at the end. */
|
|
52
|
-
export declare function batch(fn: () => void): void;
|
|
53
|
-
/** Executes `fn` without subscribing to any signals read inside it. */
|
|
54
|
-
export declare function untrack<T>(fn: () => T): T;
|
|
55
|
-
export interface WatchOptions {
|
|
56
|
-
/** Fire the callback immediately with the current value. Default: `false`. */
|
|
57
|
-
immediate?: boolean;
|
|
58
|
-
/** Automatically dispose after the first callback invocation. Default: `false`. */
|
|
59
|
-
once?: boolean;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Watches a reactive source and calls `callback(newValue, oldValue)` on each change.
|
|
63
|
-
* Accepts a Signal or a getter function. Returns a dispose function.
|
|
64
|
-
*/
|
|
65
|
-
export declare function watch<T>(source: Signal<T> | (() => T), callback: (newValue: T, oldValue: T | undefined) => void, options?: WatchOptions): () => void;
|
|
66
|
-
/** Returns a promise that resolves on the next microtask. Accepts an optional callback. */
|
|
67
|
-
export declare function nextTick(fn?: () => void): Promise<void>;
|
|
1
|
+
export * from "./next-2/reactivity.js";
|