@epoch32/visage 0.0.0 → 1.1.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.
Files changed (139) hide show
  1. package/dist/api/client.d.ts +5 -0
  2. package/dist/api/file-router.d.ts +29 -0
  3. package/dist/api/handler.d.ts +15 -0
  4. package/dist/api/index.d.ts +12 -0
  5. package/dist/{src/api → api}/index.js +87 -2
  6. package/dist/api/middleware.d.ts +11 -0
  7. package/dist/api/pragma.d.ts +21 -0
  8. package/dist/api/procedure.d.ts +66 -0
  9. package/dist/api/types.d.ts +181 -0
  10. package/dist/charts/Area.d.ts +18 -0
  11. package/dist/charts/Axis.d.ts +21 -0
  12. package/dist/charts/Bar.d.ts +10 -0
  13. package/dist/charts/CartesianGrid.d.ts +10 -0
  14. package/dist/charts/Chart.d.ts +16 -0
  15. package/dist/charts/Cursor.d.ts +8 -0
  16. package/dist/charts/Legend.d.ts +9 -0
  17. package/dist/charts/Line.d.ts +18 -0
  18. package/dist/charts/Pie.d.ts +12 -0
  19. package/dist/charts/Tooltip.d.ts +12 -0
  20. package/dist/charts/context.d.ts +20 -0
  21. package/dist/charts/index.d.ts +14 -0
  22. package/dist/{src/charts → charts}/index.js +87 -2
  23. package/dist/charts/path.d.ts +27 -0
  24. package/dist/charts/scale.d.ts +23 -0
  25. package/dist/charts/types.d.ts +43 -0
  26. package/dist/devtools/DomTab.d.ts +6 -0
  27. package/dist/devtools/GraphInspector.d.ts +11 -0
  28. package/dist/devtools/GraphTab.d.ts +10 -0
  29. package/dist/devtools/QueryTab.d.ts +10 -0
  30. package/dist/devtools/RpcTab.d.ts +10 -0
  31. package/dist/devtools/SignalsTab.d.ts +10 -0
  32. package/dist/devtools/component.d.ts +2 -0
  33. package/dist/devtools/highlighter.d.ts +2 -0
  34. package/dist/devtools/hub.d.ts +41 -0
  35. package/dist/devtools/index.d.ts +9 -0
  36. package/dist/{src/devtools → devtools}/index.js +87 -2
  37. package/dist/devtools/styles.d.ts +1 -0
  38. package/dist/devtools/types.d.ts +44 -0
  39. package/dist/dom/async.d.ts +41 -0
  40. package/dist/dom/control-flow.d.ts +53 -0
  41. package/dist/dom/index.d.ts +3 -0
  42. package/dist/{src/dom → dom}/index.js +87 -2
  43. package/dist/dom/lazy.d.ts +30 -0
  44. package/dist/dom/render.d.ts +23 -0
  45. package/dist/dom/resource.d.ts +34 -0
  46. package/dist/form/form.d.ts +41 -0
  47. package/dist/form/index.d.ts +2 -0
  48. package/dist/{src/form → form}/index.js +87 -2
  49. package/dist/form/schema.d.ts +96 -0
  50. package/dist/index.d.ts +15 -0
  51. package/dist/index.js +3571 -1293
  52. package/dist/jsx-dev-runtime/index.d.ts +1 -0
  53. package/dist/jsx-runtime/index.d.ts +8 -0
  54. package/dist/{src/jsx-runtime → jsx-runtime}/index.js +87 -2
  55. package/dist/query/create-mutation.d.ts +3 -0
  56. package/dist/query/create-query.d.ts +4 -0
  57. package/dist/query/focus-manager.d.ts +14 -0
  58. package/dist/query/index.d.ts +9 -0
  59. package/dist/{src/query → query}/index.js +87 -2
  60. package/dist/query/mutation-cache.d.ts +24 -0
  61. package/dist/query/online-manager.d.ts +14 -0
  62. package/dist/query/query-cache.d.ts +69 -0
  63. package/dist/query/query-client.d.ts +46 -0
  64. package/dist/query/types.d.ts +131 -0
  65. package/dist/query/utils.d.ts +16 -0
  66. package/dist/router/index.d.ts +1 -0
  67. package/dist/{src/router → router}/index.js +87 -2
  68. package/dist/router/router.d.ts +91 -0
  69. package/dist/rpc/client.d.ts +2 -0
  70. package/dist/rpc/error.d.ts +16 -0
  71. package/dist/rpc/index.d.ts +7 -0
  72. package/dist/{src/rpc → rpc}/index.js +87 -2
  73. package/dist/rpc/procedure.d.ts +8 -0
  74. package/dist/rpc/router.d.ts +19 -0
  75. package/dist/rpc/server.d.ts +3 -0
  76. package/dist/rpc/types.d.ts +200 -0
  77. package/dist/server/hydrate.d.ts +5 -0
  78. package/dist/server/index.d.ts +3 -0
  79. package/dist/{src/server → server}/index.js +87 -2
  80. package/dist/server/render-to-stream.d.ts +9 -0
  81. package/dist/server/render-to-string.d.ts +6 -0
  82. package/dist/signals/computed.d.ts +61 -0
  83. package/dist/signals/context.d.ts +44 -0
  84. package/dist/signals/effect.d.ts +56 -0
  85. package/dist/signals/index.d.ts +8 -0
  86. package/dist/{src/signals → signals}/index.js +87 -2
  87. package/dist/signals/scope.d.ts +61 -0
  88. package/dist/signals/signal.d.ts +40 -0
  89. package/dist/signals/store.d.ts +45 -0
  90. package/dist/signals/system.d.ts +56 -0
  91. package/dist/signals/types.d.ts +58 -0
  92. package/dist/style/component.d.ts +49 -0
  93. package/dist/style/create-style.d.ts +63 -0
  94. package/dist/style/cx.d.ts +18 -0
  95. package/dist/style/hash.d.ts +8 -0
  96. package/dist/style/index.d.ts +8 -0
  97. package/dist/{src/style → style}/index.js +87 -2
  98. package/dist/style/layout.d.ts +129 -0
  99. package/dist/style/manager.d.ts +31 -0
  100. package/dist/style/parser.d.ts +12 -0
  101. package/dist/style/scale.d.ts +82 -0
  102. package/dist/template/index.d.ts +1 -0
  103. package/dist/{src/template → template}/index.js +87 -2
  104. package/dist/template/template.d.ts +8 -0
  105. package/dist/ui/accordion.d.ts +81 -0
  106. package/dist/ui/command-dialog.d.ts +10 -0
  107. package/dist/ui/command.d.ts +103 -0
  108. package/dist/ui/data-table.d.ts +38 -0
  109. package/dist/ui/dialog.d.ts +102 -0
  110. package/dist/ui/dropdown-menu.d.ts +92 -0
  111. package/dist/ui/icon.d.ts +18 -0
  112. package/dist/ui/index.d.ts +14 -0
  113. package/dist/{src/ui → ui}/index.js +87 -2
  114. package/dist/ui/layout.d.ts +1 -0
  115. package/dist/ui/popover.d.ts +68 -0
  116. package/dist/ui/switch.d.ts +31 -0
  117. package/dist/ui/tabs.d.ts +71 -0
  118. package/dist/ui/toast.d.ts +91 -0
  119. package/dist/ui/tooltip.d.ts +64 -0
  120. package/dist/ui/utils.d.ts +21 -0
  121. package/dist/virtual/index.d.ts +2 -0
  122. package/dist/{src/virtual → virtual}/index.js +87 -2
  123. package/dist/virtual/virtual-list.d.ts +21 -0
  124. package/dist/virtual/virtualizer.d.ts +31 -0
  125. package/dist/vite/hmr.d.ts +7 -0
  126. package/dist/vite/index.d.ts +3 -0
  127. package/dist/vite/index.js +296 -15
  128. package/dist/vite/lucide.d.ts +27 -0
  129. package/dist/vite/plugin.d.ts +3 -0
  130. package/dist/vite/rpc-middleware.d.ts +5 -0
  131. package/dist/vite/types.d.ts +59 -0
  132. package/package.json +40 -40
  133. package/src/signals/index.ts +1 -0
  134. package/src/signals/signal.ts +110 -2
  135. package/src/signals/types.ts +9 -0
  136. package/src/vite/plugin.ts +4 -0
  137. package/dist/bundle.dev.js +0 -4875
  138. package/dist/src/index.js +0 -11826
  139. package/dist/src/vite/index.js +0 -984
@@ -431,10 +431,12 @@ class Signal {
431
431
  _value;
432
432
  _subscribers = new Set;
433
433
  _equals;
434
+ _onSet;
434
435
  version = 0;
435
- constructor(initialValue, equals) {
436
+ constructor(initialValue, equals, onSet) {
436
437
  this._value = initialValue;
437
438
  this._equals = equals ?? Object.is;
439
+ this._onSet = onSet;
438
440
  }
439
441
  get() {
440
442
  if (currentSubscriber !== null && currentSubscriber.state !== 3 /* DISPOSED */) {
@@ -459,6 +461,9 @@ class Signal {
459
461
  }
460
462
  this._value = newValue;
461
463
  this.version++;
464
+ if (this._onSet) {
465
+ this._onSet(newValue);
466
+ }
462
467
  batch(() => {
463
468
  this.notify();
464
469
  });
@@ -484,11 +489,91 @@ class Signal {
484
489
  }
485
490
  }
486
491
  }
492
+ function resolveStorage(storageOption) {
493
+ if (storageOption && typeof storageOption === "object") {
494
+ return storageOption;
495
+ }
496
+ if (storageOption === "session") {
497
+ try {
498
+ if (typeof sessionStorage !== "undefined")
499
+ return sessionStorage;
500
+ } catch {}
501
+ try {
502
+ if (typeof window !== "undefined" && window.sessionStorage) {
503
+ return window.sessionStorage;
504
+ }
505
+ } catch {}
506
+ try {
507
+ if (typeof globalThis !== "undefined" && globalThis.sessionStorage) {
508
+ return globalThis.sessionStorage;
509
+ }
510
+ } catch {}
511
+ return null;
512
+ }
513
+ try {
514
+ if (typeof localStorage !== "undefined")
515
+ return localStorage;
516
+ } catch {}
517
+ try {
518
+ if (typeof window !== "undefined" && window.localStorage) {
519
+ return window.localStorage;
520
+ }
521
+ } catch {}
522
+ try {
523
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) {
524
+ return globalThis.localStorage;
525
+ }
526
+ } catch {}
527
+ return null;
528
+ }
487
529
  function signal(initialValue, optionsOrEquals) {
488
530
  const equals = typeof optionsOrEquals === "function" ? optionsOrEquals : optionsOrEquals?.equals;
489
531
  const isInternal = typeof optionsOrEquals === "object" ? optionsOrEquals?.internal : false;
490
532
  const customName = typeof optionsOrEquals === "object" ? optionsOrEquals?.name : undefined;
491
- const s = new Signal(initialValue, equals);
533
+ const persistOption = typeof optionsOrEquals === "object" ? optionsOrEquals?.persist : undefined;
534
+ const persistConfig = persistOption ? typeof persistOption === "string" ? { key: persistOption } : persistOption : undefined;
535
+ let effectiveInitial = initialValue;
536
+ const storage = persistConfig ? resolveStorage(persistConfig.storage) : null;
537
+ const serialize = persistConfig?.serialize ?? JSON.stringify;
538
+ const deserialize = persistConfig?.deserialize ?? JSON.parse;
539
+ if (persistConfig && storage) {
540
+ try {
541
+ const stored = storage.getItem(persistConfig.key);
542
+ if (stored !== null) {
543
+ effectiveInitial = deserialize(stored);
544
+ }
545
+ } catch {}
546
+ }
547
+ let isSyncing = false;
548
+ const onSet = persistConfig && storage ? (val) => {
549
+ if (isSyncing)
550
+ return;
551
+ try {
552
+ storage.setItem(persistConfig.key, serialize(val));
553
+ } catch {}
554
+ } : undefined;
555
+ const s = new Signal(effectiveInitial, equals, onSet);
556
+ if (persistConfig && storage && (persistConfig.syncTabs ?? true) && typeof window !== "undefined" && typeof window.addEventListener === "function") {
557
+ const onStorage = (e) => {
558
+ if (e.key === persistConfig.key) {
559
+ try {
560
+ const nextVal = e.newValue !== null ? deserialize(e.newValue) : initialValue;
561
+ isSyncing = true;
562
+ try {
563
+ s.set(nextVal);
564
+ } finally {
565
+ isSyncing = false;
566
+ }
567
+ } catch {}
568
+ }
569
+ };
570
+ window.addEventListener("storage", onStorage);
571
+ if (currentScope) {
572
+ currentScope.addCleanup(() => {
573
+ window.removeEventListener("storage", onStorage);
574
+ });
575
+ }
576
+ }
492
577
  const accessor = (...args) => {
493
578
  if (args.length === 0) {
494
579
  return s.get();
@@ -0,0 +1,9 @@
1
+ import type { Child } from "../dom/render";
2
+ export interface StreamOptions {
3
+ onChunk?: (chunk: string) => void;
4
+ onError?: (error: any) => void;
5
+ }
6
+ /**
7
+ * Stream HTML chunks progressively using standard Web Streams (ReadableStream)
8
+ */
9
+ export declare function renderToStream(fn: () => Child, options?: StreamOptions): ReadableStream<Uint8Array>;
@@ -0,0 +1,6 @@
1
+ import type { Child } from "../dom/render";
2
+ export declare function renderChildToString(child: Child): string;
3
+ /**
4
+ * Synchronously render a Visage component tree to an HTML string
5
+ */
6
+ export declare function renderToString(fn: () => Child): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Visage Lazy, Glitch-Free Computed State Engine
3
+ */
4
+ import type { Scope } from "./scope";
5
+ import { COMPUTED_BRAND, type ComputedOptions, type IDependency, type ISubscriber, type ReadonlySignal, SubscriberState } from "./types";
6
+ export type { ComputedOptions };
7
+ /**
8
+ * Computed / Derived reactive state (Lazy, Glitch-Free, Memoized)
9
+ */
10
+ export declare class Computed<T> implements ISubscriber, IDependency {
11
+ [COMPUTED_BRAND]: true;
12
+ private _fn;
13
+ private _value;
14
+ private _subscribers;
15
+ sources: Set<IDependency>;
16
+ sourceVersions: Map<IDependency, number>;
17
+ state: SubscriberState;
18
+ scope: Scope | null;
19
+ version: number;
20
+ constructor(fn: () => T);
21
+ get(): T;
22
+ get value(): T;
23
+ removeSubscriber(sub: ISubscriber): void;
24
+ markDirty(targetState?: SubscriberState): void;
25
+ updateIfNecessary(): boolean;
26
+ private recompute;
27
+ private cleanupSources;
28
+ dispose(): void;
29
+ }
30
+ /**
31
+ * Factory for creating lazy, memoized, glitch-free computed signals.
32
+ *
33
+ * Computed values are only recalculated when one of their recorded reactive
34
+ * dependencies changes and the value is actually accessed.
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * const count = signal(2);
39
+ * const double = computed(() => count() * 2);
40
+ *
41
+ * console.log(double()); // 4
42
+ *
43
+ * // In JSX, passes directly or as a function
44
+ * <div>Doubled value: {double}</div>
45
+ * ```
46
+ *
47
+ * @param fn - The pure reactive computation function
48
+ * @param options - Optional debug metadata and internal flags
49
+ * @returns A readonly reactive signal function accessor
50
+ */
51
+ export declare function computed<T>(fn: () => T, options?: ComputedOptions): ReadonlySignal<T>;
52
+ /**
53
+ * Solid-compatible alias for `computed()`.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const count = signal(5);
58
+ * const squared = createMemo(() => count() ** 2);
59
+ * ```
60
+ */
61
+ export declare const createMemo: typeof computed;
@@ -0,0 +1,44 @@
1
+ import { type Child } from "../dom/render";
2
+ export interface Context<T> {
3
+ id: symbol;
4
+ defaultValue: T;
5
+ Provider: (props: {
6
+ value: T;
7
+ children: Child;
8
+ }) => any;
9
+ }
10
+ /**
11
+ * Create a reactive Context for dependency injection across the component tree.
12
+ *
13
+ * Context allows passing state and utilities down the component tree without
14
+ * manually drilling props through every intermediate component.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const ThemeContext = createContext<"light" | "dark">("light");
19
+ *
20
+ * // Provide value in parent
21
+ * function App() {
22
+ * return (
23
+ * <ThemeContext.Provider value="dark">
24
+ * <Header />
25
+ * </ThemeContext.Provider>
26
+ * );
27
+ * }
28
+ *
29
+ * // Consume value in descendant
30
+ * function Header() {
31
+ * const theme = useContext(ThemeContext);
32
+ * return <div>Current theme: {theme}</div>;
33
+ * }
34
+ * ```
35
+ */
36
+ export declare function createContext<T>(defaultValue: T): Context<T>;
37
+ export declare function createContext<T>(): Context<T | undefined>;
38
+ /**
39
+ * Consume a Context value from the nearest ancestor Provider, falling back to defaultValue.
40
+ *
41
+ * @param context - The Context object returned from `createContext`
42
+ * @returns The current context value
43
+ */
44
+ export declare function useContext<T>(context: Context<T>): T;
@@ -0,0 +1,56 @@
1
+ import type { Scope } from "./scope";
2
+ import { type CleanupFn, type IDependency, type ISubscriber, SubscriberState } from "./types";
3
+ /**
4
+ * Side-effect runner with automatic dependency tracking and disposal
5
+ */
6
+ export declare class Effect implements ISubscriber {
7
+ private _fn;
8
+ sources: Set<IDependency>;
9
+ sourceVersions: Map<IDependency, number>;
10
+ cleanups: CleanupFn[];
11
+ state: SubscriberState;
12
+ scope: Scope | null;
13
+ constructor(fn: () => void | CleanupFn);
14
+ addCleanup(fn: CleanupFn): void;
15
+ private runCleanups;
16
+ markDirty(targetState?: SubscriberState): void;
17
+ run(): void;
18
+ private cleanupSources;
19
+ dispose(): void;
20
+ }
21
+ /**
22
+ * Execute side-effects reactively when dependencies change.
23
+ *
24
+ * Runs immediately upon creation and tracks any signals read within `fn`.
25
+ * When dependencies change, re-execution is scheduled automatically.
26
+ *
27
+ * An optional cleanup function can be returned from `fn`, which runs
28
+ * before the next re-execution and when the enclosing scope is disposed.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * const count = signal(0);
33
+ *
34
+ * effect(() => {
35
+ * console.log("Count changed to:", count());
36
+ *
37
+ * const timer = setInterval(() => console.log("tick"), 1000);
38
+ * return () => clearInterval(timer);
39
+ * });
40
+ * ```
41
+ *
42
+ * @param fn - The side-effect function, optionally returning a cleanup function
43
+ * @returns A manual disposal cleanup function
44
+ */
45
+ export declare function effect(fn: () => void | CleanupFn): CleanupFn;
46
+ /**
47
+ * Solid / React-compatible alias for `effect()`.
48
+ *
49
+ * @example
50
+ * ```tsx
51
+ * createEffect(() => {
52
+ * document.title = `Total: ${count()}`;
53
+ * });
54
+ * ```
55
+ */
56
+ export declare const createEffect: typeof effect;
@@ -0,0 +1,8 @@
1
+ export { Computed, type ComputedOptions, computed, createMemo } from "./computed";
2
+ export { type Context, createContext, useContext } from "./context";
3
+ export { createEffect, Effect, effect } from "./effect";
4
+ export { createRoot, getActiveScope, onCleanup, runInScope, Scope } from "./scope";
5
+ export { createSignal, Signal, type SignalOptions, signal } from "./signal";
6
+ export { createStore, mutateStore, type Store, unwrapStore } from "./store";
7
+ export { batch, untrack } from "./system";
8
+ export { type CleanupFn, COMPUTED_BRAND, type Dependency, isSignal, type PersistOptions, type ReadonlySignal, SIGNAL_BRAND, type Subscriber, SubscriberState, type WritableSignal } from "./types";
@@ -848,10 +848,12 @@ class Signal {
848
848
  _value;
849
849
  _subscribers = new Set;
850
850
  _equals;
851
+ _onSet;
851
852
  version = 0;
852
- constructor(initialValue, equals) {
853
+ constructor(initialValue, equals, onSet) {
853
854
  this._value = initialValue;
854
855
  this._equals = equals ?? Object.is;
856
+ this._onSet = onSet;
855
857
  }
856
858
  get() {
857
859
  if (currentSubscriber !== null && currentSubscriber.state !== 3 /* DISPOSED */) {
@@ -876,6 +878,9 @@ class Signal {
876
878
  }
877
879
  this._value = newValue;
878
880
  this.version++;
881
+ if (this._onSet) {
882
+ this._onSet(newValue);
883
+ }
879
884
  batch(() => {
880
885
  this.notify();
881
886
  });
@@ -901,11 +906,91 @@ class Signal {
901
906
  }
902
907
  }
903
908
  }
909
+ function resolveStorage(storageOption) {
910
+ if (storageOption && typeof storageOption === "object") {
911
+ return storageOption;
912
+ }
913
+ if (storageOption === "session") {
914
+ try {
915
+ if (typeof sessionStorage !== "undefined")
916
+ return sessionStorage;
917
+ } catch {}
918
+ try {
919
+ if (typeof window !== "undefined" && window.sessionStorage) {
920
+ return window.sessionStorage;
921
+ }
922
+ } catch {}
923
+ try {
924
+ if (typeof globalThis !== "undefined" && globalThis.sessionStorage) {
925
+ return globalThis.sessionStorage;
926
+ }
927
+ } catch {}
928
+ return null;
929
+ }
930
+ try {
931
+ if (typeof localStorage !== "undefined")
932
+ return localStorage;
933
+ } catch {}
934
+ try {
935
+ if (typeof window !== "undefined" && window.localStorage) {
936
+ return window.localStorage;
937
+ }
938
+ } catch {}
939
+ try {
940
+ if (typeof globalThis !== "undefined" && globalThis.localStorage) {
941
+ return globalThis.localStorage;
942
+ }
943
+ } catch {}
944
+ return null;
945
+ }
904
946
  function signal(initialValue, optionsOrEquals) {
905
947
  const equals = typeof optionsOrEquals === "function" ? optionsOrEquals : optionsOrEquals?.equals;
906
948
  const isInternal = typeof optionsOrEquals === "object" ? optionsOrEquals?.internal : false;
907
949
  const customName = typeof optionsOrEquals === "object" ? optionsOrEquals?.name : undefined;
908
- const s = new Signal(initialValue, equals);
950
+ const persistOption = typeof optionsOrEquals === "object" ? optionsOrEquals?.persist : undefined;
951
+ const persistConfig = persistOption ? typeof persistOption === "string" ? { key: persistOption } : persistOption : undefined;
952
+ let effectiveInitial = initialValue;
953
+ const storage = persistConfig ? resolveStorage(persistConfig.storage) : null;
954
+ const serialize = persistConfig?.serialize ?? JSON.stringify;
955
+ const deserialize = persistConfig?.deserialize ?? JSON.parse;
956
+ if (persistConfig && storage) {
957
+ try {
958
+ const stored = storage.getItem(persistConfig.key);
959
+ if (stored !== null) {
960
+ effectiveInitial = deserialize(stored);
961
+ }
962
+ } catch {}
963
+ }
964
+ let isSyncing = false;
965
+ const onSet = persistConfig && storage ? (val) => {
966
+ if (isSyncing)
967
+ return;
968
+ try {
969
+ storage.setItem(persistConfig.key, serialize(val));
970
+ } catch {}
971
+ } : undefined;
972
+ const s = new Signal(effectiveInitial, equals, onSet);
973
+ if (persistConfig && storage && (persistConfig.syncTabs ?? true) && typeof window !== "undefined" && typeof window.addEventListener === "function") {
974
+ const onStorage = (e) => {
975
+ if (e.key === persistConfig.key) {
976
+ try {
977
+ const nextVal = e.newValue !== null ? deserialize(e.newValue) : initialValue;
978
+ isSyncing = true;
979
+ try {
980
+ s.set(nextVal);
981
+ } finally {
982
+ isSyncing = false;
983
+ }
984
+ } catch {}
985
+ }
986
+ };
987
+ window.addEventListener("storage", onStorage);
988
+ if (currentScope) {
989
+ currentScope.addCleanup(() => {
990
+ window.removeEventListener("storage", onStorage);
991
+ });
992
+ }
993
+ }
909
994
  const accessor = (...args) => {
910
995
  if (args.length === 0) {
911
996
  return s.get();
@@ -0,0 +1,61 @@
1
+ import type { CleanupFn, ISubscriber } from "./types";
2
+ /**
3
+ * Reactive Scope for lifecycle and automatic memory management
4
+ */
5
+ export declare class Scope {
6
+ subscribers: Set<ISubscriber>;
7
+ cleanups: CleanupFn[];
8
+ parent: Scope | null;
9
+ isDisposed: boolean;
10
+ context?: Map<symbol, any>;
11
+ constructor(parent?: Scope | null);
12
+ addSubscriber(sub: ISubscriber): void;
13
+ addCleanup(fn: CleanupFn): void;
14
+ dispose(): void;
15
+ }
16
+ /**
17
+ * Register a cleanup callback for the current running effect or active scope.
18
+ *
19
+ * Runs automatically when the enclosing component/scope unmounts or before
20
+ * the effect re-executes.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * function TimerComponent() {
25
+ * const id = setInterval(() => console.log("tick"), 1000);
26
+ * onCleanup(() => clearInterval(id));
27
+ * return <div>Timer running</div>;
28
+ * }
29
+ * ```
30
+ */
31
+ export declare function onCleanup(fn: CleanupFn): void;
32
+ /**
33
+ * Create an isolated reactive root scope that outlives the current execution context.
34
+ *
35
+ * Computations and signals created inside `fn` will not be tied to any parent scope
36
+ * and will persist until the provided `dispose` callback is invoked.
37
+ *
38
+ * @example
39
+ * ```tsx
40
+ * const dispose = createRoot((dispose) => {
41
+ * const count = signal(0);
42
+ * effect(() => console.log(count()));
43
+ * return dispose;
44
+ * });
45
+ *
46
+ * // Later, dispose of all memory & subscribers
47
+ * dispose();
48
+ * ```
49
+ *
50
+ * @param fn - A factory function receiving the `dispose` callback
51
+ * @returns The return value of `fn`
52
+ */
53
+ export declare function createRoot<T>(fn: (dispose: () => void) => T): T;
54
+ /**
55
+ * Run a function with a specified reactive scope active.
56
+ */
57
+ export declare function runInScope<T>(scope: Scope | null, fn: () => T): T;
58
+ /**
59
+ * Get the currently active reactive scope.
60
+ */
61
+ export declare function getActiveScope(): Scope | null;
@@ -0,0 +1,40 @@
1
+ import { type IDependency, type ISubscriber, type ReadonlySignal, SIGNAL_BRAND, type SignalOptions, type WritableSignal } from "./types";
2
+ export type { SignalOptions };
3
+ /**
4
+ * Core Writable Signal implementation
5
+ */
6
+ export declare class Signal<T> implements IDependency {
7
+ [SIGNAL_BRAND]: true;
8
+ private _value;
9
+ private _subscribers;
10
+ private _equals;
11
+ private _onSet?;
12
+ version: number;
13
+ constructor(initialValue: T, equals?: (a: T, b: T) => boolean, onSet?: (val: T) => void);
14
+ get(): T;
15
+ set(newValue: T): void;
16
+ update(fn: (prev: T) => T): void;
17
+ get value(): T;
18
+ set value(val: T);
19
+ removeSubscriber(sub: ISubscriber): void;
20
+ private notify;
21
+ }
22
+ export declare function signal<T>(initialValue: T, optionsOrEquals?: ((a: T, b: T) => boolean) | SignalOptions<T>): WritableSignal<T>;
23
+ /**
24
+ * Tuple-based signal constructor returning `[getter, setter]` (Solid / Preact Signals convention).
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * const [count, setCount] = createSignal(0);
29
+ *
30
+ * setCount(5);
31
+ * setCount(prev => prev + 1);
32
+ *
33
+ * <div>Count: {count}</div>
34
+ * ```
35
+ *
36
+ * @param initialValue - The initial value stored in the signal
37
+ * @param optionsOrEquals - Custom equality comparison function or SignalOptions
38
+ * @returns A tuple containing `[ReadonlySignal<T>, (value: T | ((prev: T) => T)) => void]`
39
+ */
40
+ export declare function createSignal<T>(initialValue: T, optionsOrEquals?: ((a: T, b: T) => boolean) | SignalOptions<T>): [ReadonlySignal<T>, (value: T | ((prev: T) => T)) => void];
@@ -0,0 +1,45 @@
1
+ export type Store<T> = T;
2
+ /**
3
+ * Create a deep reactive proxy store with per-property fine-grained signal tracking.
4
+ *
5
+ * Nested objects and arrays are lazily wrapped in reactive proxies. Reading a property
6
+ * tracks that exact key, and updating a property only notifies computations observing that key.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const state = createStore({
11
+ * user: { name: "Alice", role: "admin" },
12
+ * items: ["Apple", "Banana"]
13
+ * });
14
+ *
15
+ * // Fine-grained read
16
+ * <div>User: {() => state.user.name}</div>
17
+ *
18
+ * // Fine-grained write (mutates only user.name subscribers)
19
+ * state.user.name = "Bob";
20
+ * state.items.push("Cherry");
21
+ * ```
22
+ *
23
+ * @param initialState - An object or array representing the initial state tree
24
+ * @returns A deeply reactive proxy store
25
+ */
26
+ export declare function createStore<T extends object>(initialState: T): Store<T>;
27
+ /**
28
+ * Mutate a store inside a synchronous batch operation.
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * mutateStore(state, (draft) => {
33
+ * draft.user.name = "Charlie";
34
+ * draft.user.role = "user";
35
+ * });
36
+ * ```
37
+ */
38
+ export declare function mutateStore<T extends object>(store: Store<T>, mutator: (draft: T) => void): void;
39
+ /**
40
+ * Unwrap a reactive proxy store to get the underlying raw, un-proxied JavaScript object.
41
+ *
42
+ * @param store - The reactive store proxy
43
+ * @returns The raw underlying object
44
+ */
45
+ export declare function unwrapStore<T>(store: T): T;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Visage Reactivity System Execution Context & Scheduler
3
+ */
4
+ import { type ISubscriber } from "./types";
5
+ export declare let currentSubscriber: ISubscriber | null;
6
+ export declare let currentScope: any;
7
+ export declare let batchDepth: number;
8
+ export declare const pendingEffects: Set<any>;
9
+ export declare const warnedCyclicSubscribers: WeakSet<ISubscriber>;
10
+ export declare let isFlushing: boolean;
11
+ export declare function setCurrentSubscriber(sub: ISubscriber | null): void;
12
+ export declare function getCurrentSubscriber(): ISubscriber | null;
13
+ export declare function setCurrentScope(scope: any): void;
14
+ export declare function getCurrentScope(): any;
15
+ export declare function addPendingEffect(eff: any): void;
16
+ export declare function removePendingEffect(eff: any): void;
17
+ export declare function flushEffects(): void;
18
+ /**
19
+ * Batch multiple signal mutations so downstreams and effects update synchronously once at completion.
20
+ *
21
+ * Prevents intermediate flicker, wasted recomputations, and glitch states.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * const firstName = signal("Jane");
26
+ * const lastName = signal("Doe");
27
+ *
28
+ * batch(() => {
29
+ * firstName.set("John");
30
+ * lastName.set("Smith");
31
+ * // Downstream computed and effects run only ONCE here
32
+ * });
33
+ * ```
34
+ *
35
+ * @param fn - A function performing multiple signal updates
36
+ * @returns The return value of `fn`
37
+ */
38
+ export declare function batch<T>(fn: () => T): T;
39
+ /**
40
+ * Read reactive state without subscribing the current active effect or computed computation.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * effect(() => {
45
+ * const id = currentId(); // Subscribed: effect re-runs when currentId changes
46
+ *
47
+ * // Not subscribed: effect does NOT re-run when analyticsCounter changes
48
+ * const count = untrack(() => analyticsCounter());
49
+ * sendMetric(id, count);
50
+ * });
51
+ * ```
52
+ *
53
+ * @param fn - A function reading reactive signals without tracking
54
+ * @returns The return value of `fn`
55
+ */
56
+ export declare function untrack<T>(fn: () => T): T;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Visage Fine-Grained Reactive Engine Type Definitions
3
+ */
4
+ export type CleanupFn = () => void;
5
+ export declare enum SubscriberState {
6
+ CLEAN = 0,
7
+ CHECK_DIRTY = 1,
8
+ DIRTY = 2,
9
+ DISPOSED = 3
10
+ }
11
+ export declare const SIGNAL_BRAND: unique symbol;
12
+ export declare const COMPUTED_BRAND: unique symbol;
13
+ export interface ReadonlySignal<T> {
14
+ get(): T;
15
+ readonly value: T;
16
+ (): T;
17
+ readonly [SIGNAL_BRAND]: true;
18
+ }
19
+ export interface WritableSignal<T> extends ReadonlySignal<T> {
20
+ set(value: T): void;
21
+ update(fn: (prev: T) => T): void;
22
+ (value?: T): T;
23
+ value: T;
24
+ }
25
+ export interface PersistOptions<T> {
26
+ key: string;
27
+ storage?: "local" | "session" | Storage;
28
+ serialize?: (value: T) => string;
29
+ deserialize?: (raw: string) => T;
30
+ syncTabs?: boolean;
31
+ }
32
+ export interface SignalOptions<T> {
33
+ name?: string;
34
+ equals?: (a: T, b: T) => boolean;
35
+ internal?: boolean;
36
+ persist?: string | PersistOptions<T>;
37
+ }
38
+ export interface ComputedOptions {
39
+ name?: string;
40
+ internal?: boolean;
41
+ }
42
+ /**
43
+ * Check if a value is a Visage signal or computed
44
+ */
45
+ export declare function isSignal<T = unknown>(value: unknown): value is ReadonlySignal<T>;
46
+ export interface ISubscriber {
47
+ state: SubscriberState;
48
+ sources: Set<IDependency>;
49
+ sourceVersions: Map<IDependency, number>;
50
+ markDirty(targetState?: SubscriberState): void;
51
+ dispose(): void;
52
+ }
53
+ export interface IDependency {
54
+ version: number;
55
+ removeSubscriber(sub: ISubscriber): void;
56
+ }
57
+ export type Subscriber = ISubscriber;
58
+ export type Dependency = IDependency;