@actuallab/fusion-react 13.0.25 → 13.0.167

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/index.cjs CHANGED
@@ -32,46 +32,48 @@ module.exports = __toCommonJS(index_exports);
32
32
  var import_react = require("react");
33
33
  var import_fusion = require("@actuallab/fusion");
34
34
  function useComputedState(computer, deps, options) {
35
- const [, forceRender] = (0, import_react.useReducer)((c) => c + 1, 0);
36
35
  const stateRef = (0, import_react.useRef)(null);
37
- const depsRef = (0, import_react.useRef)(deps);
38
- const depsChanged = deps.length !== depsRef.current.length || deps.some((d, i) => d !== depsRef.current[i]);
39
- if (depsChanged || stateRef.current === null) {
40
- stateRef.current?.dispose();
41
- depsRef.current = deps;
42
- stateRef.current = new import_fusion.ComputedState(computer, options);
43
- }
44
- const state = stateRef.current;
45
- (0, import_react.useEffect)(() => {
46
- let cancelled = false;
47
- const subscribe = async () => {
48
- try {
49
- await state.whenFirstTimeUpdated();
50
- } catch {
51
- return;
52
- }
53
- if (cancelled || state.isDisposed) return;
54
- forceRender();
55
- while (!cancelled && !state.isDisposed) {
56
- try {
57
- await state.whenUpdated();
58
- } catch {
59
- return;
36
+ const generationRef = (0, import_react.useRef)(0);
37
+ const subscribe = (0, import_react.useCallback)(
38
+ (onStoreChange) => {
39
+ const state2 = new import_fusion.ComputedState(computer, options);
40
+ generationRef.current++;
41
+ stateRef.current = state2;
42
+ onStoreChange();
43
+ let cancelled = false;
44
+ void (async () => {
45
+ while (!cancelled && !state2.isDisposed) {
46
+ const sinceIndex = state2.updateIndex;
47
+ try {
48
+ await state2.whenUpdated(sinceIndex);
49
+ } catch {
50
+ return;
51
+ }
52
+ if (!cancelled && !state2.isDisposed)
53
+ onStoreChange();
60
54
  }
61
- if (!cancelled && !state.isDisposed) forceRender();
62
- }
63
- };
64
- void subscribe();
65
- return () => {
66
- cancelled = true;
67
- state.dispose();
68
- };
69
- }, [state]);
70
- const isInitial = state.updateIndex === 0;
55
+ })();
56
+ return () => {
57
+ cancelled = true;
58
+ state2.dispose();
59
+ if (stateRef.current === state2)
60
+ stateRef.current = null;
61
+ };
62
+ },
63
+ deps
64
+ );
65
+ (0, import_react.useSyncExternalStore)(
66
+ subscribe,
67
+ () => stateRef.current === null ? "" : `${generationRef.current}:${stateRef.current.updateIndex}`,
68
+ () => ""
69
+ );
70
+ const state = stateRef.current ?? void 0;
71
+ if (state === void 0 || state.updateIndex === 0)
72
+ return { value: void 0, error: void 0, isInitial: true, state };
71
73
  return {
72
- value: isInitial ? void 0 : state.valueOrUndefined,
73
- error: isInitial ? void 0 : state.error,
74
- isInitial,
74
+ value: state.valueOrUndefined,
75
+ error: state.error,
76
+ isInitial: false,
75
77
  state
76
78
  };
77
79
  }
@@ -80,28 +82,41 @@ function useComputedState(computer, deps, options) {
80
82
  var import_react2 = require("react");
81
83
  var import_fusion2 = require("@actuallab/fusion");
82
84
  function useMutableState(initial) {
83
- const [, forceRender] = (0, import_react2.useReducer)((c) => c + 1, 0);
84
85
  const stateRef = (0, import_react2.useRef)(null);
85
86
  stateRef.current ??= new import_fusion2.MutableState(initial);
86
87
  const state = stateRef.current;
87
- (0, import_react2.useEffect)(() => {
88
- let cancelled = false;
89
- const subscribe = async () => {
90
- while (!cancelled) {
91
- try {
92
- await state.whenUpdated();
93
- } catch {
94
- return;
88
+ const subscribe = (0, import_react2.useCallback)(
89
+ (onStoreChange) => {
90
+ let cancelled = false;
91
+ void (async () => {
92
+ while (!cancelled) {
93
+ const sinceIndex = state.updateIndex;
94
+ try {
95
+ await state.whenUpdated(sinceIndex);
96
+ } catch {
97
+ return;
98
+ }
99
+ if (!cancelled)
100
+ onStoreChange();
95
101
  }
96
- if (!cancelled) forceRender();
97
- }
98
- };
99
- void subscribe();
100
- return () => {
101
- cancelled = true;
102
- };
103
- }, [state]);
104
- return [state.value, (v) => state.set(v), state];
102
+ })();
103
+ return () => {
104
+ cancelled = true;
105
+ };
106
+ },
107
+ [state]
108
+ );
109
+ (0, import_react2.useSyncExternalStore)(
110
+ subscribe,
111
+ () => state.updateIndex,
112
+ () => state.updateIndex
113
+ );
114
+ return {
115
+ value: state.valueOrUndefined,
116
+ error: state.error,
117
+ set: (value) => state.set(value),
118
+ state
119
+ };
105
120
  }
106
121
 
107
122
  // src/index.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-computed-state.ts","../src/use-mutable-state.ts"],"sourcesContent":["export { useComputedState } from './use-computed-state.js';\r\nexport type { UseComputedStateResult } from './use-computed-state.js';\r\nexport { useMutableState } from './use-mutable-state.js';\r\n// Re-export from @actuallab/fusion for convenience\r\nexport { UIActionTracker, uiActions, UIUpdateDelayer } from '@actuallab/fusion';\r\n","import { useEffect, useReducer, useRef } from 'react';\r\nimport {\r\n ComputedState,\r\n type ComputedStateOptions,\r\n type StateComputer,\r\n} from '@actuallab/fusion';\r\n\r\nexport interface UseComputedStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n isInitial: boolean;\r\n state: ComputedState<T>;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's ComputedState.\r\n * Creates a ComputedState keyed on deps, subscribes to updates, and triggers re-renders.\r\n */\r\nexport function useComputedState<T>(\r\n computer: StateComputer<T>,\r\n deps: readonly unknown[],\r\n options?: ComputedStateOptions<T>\r\n): UseComputedStateResult<T> {\r\n const [, forceRender] = useReducer(c => c + 1, 0);\r\n const stateRef = useRef<ComputedState<T> | null>(null);\r\n const depsRef = useRef<readonly unknown[]>(deps);\r\n\r\n // Check if deps changed\r\n const depsChanged =\r\n deps.length !== depsRef.current.length ||\r\n deps.some((d, i) => d !== depsRef.current[i]);\r\n\r\n if (depsChanged || stateRef.current === null) {\r\n // Dispose old state\r\n stateRef.current?.dispose();\r\n depsRef.current = deps;\r\n stateRef.current = new ComputedState<T>(computer, options);\r\n }\r\n\r\n const state = stateRef.current;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n\r\n const subscribe = async () => {\r\n // Wait for first update\r\n try {\r\n await state.whenFirstTimeUpdated();\r\n } catch {\r\n return;\r\n }\r\n if (cancelled || state.isDisposed) return;\r\n forceRender();\r\n\r\n // Subscribe to subsequent updates\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled && !state.isDisposed) {\r\n try {\r\n await state.whenUpdated();\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled && !state.isDisposed) forceRender();\r\n }\r\n };\r\n\r\n void subscribe();\r\n\r\n return () => {\r\n cancelled = true;\r\n state.dispose();\r\n };\r\n }, [state]);\r\n\r\n const isInitial = state.updateIndex === 0;\r\n return {\r\n value: isInitial ? undefined : state.valueOrUndefined,\r\n error: isInitial ? undefined : state.error,\r\n isInitial,\r\n state,\r\n };\r\n}\r\n","import { useEffect, useReducer, useRef } from 'react';\r\nimport { MutableState } from '@actuallab/fusion';\r\nimport type { Result } from '@actuallab/core';\r\n\r\n/**\r\n * React hook wrapping Fusion's MutableState.\r\n * Returns [value, setter, state] — re-renders on updates.\r\n */\r\nexport function useMutableState<T>(\r\n initial: T\r\n): [T, (value: Result<T> | T) => void, MutableState<T>] {\r\n const [, forceRender] = useReducer(c => c + 1, 0);\r\n const stateRef = useRef<MutableState<T> | null>(null);\r\n\r\n stateRef.current ??= new MutableState<T>(initial);\r\n\r\n const state = stateRef.current;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n\r\n const subscribe = async () => {\r\n while (!cancelled) {\r\n try {\r\n await state.whenUpdated();\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled) forceRender();\r\n }\r\n };\r\n\r\n void subscribe();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [state]);\r\n\r\n return [state.value, v => state.set(v), state];\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA8C;AAC9C,oBAIO;AAaA,SAAS,iBACZ,UACA,MACA,SACyB;AACzB,QAAM,CAAC,EAAE,WAAW,QAAI,yBAAW,OAAK,IAAI,GAAG,CAAC;AAChD,QAAM,eAAW,qBAAgC,IAAI;AACrD,QAAM,cAAU,qBAA2B,IAAI;AAG/C,QAAM,cACF,KAAK,WAAW,QAAQ,QAAQ,UAChC,KAAK,KAAK,CAAC,GAAG,MAAM,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAEhD,MAAI,eAAe,SAAS,YAAY,MAAM;AAE1C,aAAS,SAAS,QAAQ;AAC1B,YAAQ,UAAU;AAClB,aAAS,UAAU,IAAI,4BAAiB,UAAU,OAAO;AAAA,EAC7D;AAEA,QAAM,QAAQ,SAAS;AAEvB,8BAAU,MAAM;AACZ,QAAI,YAAY;AAEhB,UAAM,YAAY,YAAY;AAE1B,UAAI;AACA,cAAM,MAAM,qBAAqB;AAAA,MACrC,QAAQ;AACJ;AAAA,MACJ;AACA,UAAI,aAAa,MAAM,WAAY;AACnC,kBAAY;AAIZ,aAAO,CAAC,aAAa,CAAC,MAAM,YAAY;AACpC,YAAI;AACA,gBAAM,MAAM,YAAY;AAAA,QAC5B,QAAQ;AACJ;AAAA,QACJ;AAEA,YAAI,CAAC,aAAa,CAAC,MAAM,WAAY,aAAY;AAAA,MACrD;AAAA,IACJ;AAEA,SAAK,UAAU;AAEf,WAAO,MAAM;AACT,kBAAY;AACZ,YAAM,QAAQ;AAAA,IAClB;AAAA,EACJ,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,YAAY,MAAM,gBAAgB;AACxC,SAAO;AAAA,IACH,OAAO,YAAY,SAAY,MAAM;AAAA,IACrC,OAAO,YAAY,SAAY,MAAM;AAAA,IACrC;AAAA,IACA;AAAA,EACJ;AACJ;;;AClFA,IAAAA,gBAA8C;AAC9C,IAAAC,iBAA6B;AAOtB,SAAS,gBACZ,SACoD;AACpD,QAAM,CAAC,EAAE,WAAW,QAAI,0BAAW,OAAK,IAAI,GAAG,CAAC;AAChD,QAAM,eAAW,sBAA+B,IAAI;AAEpD,WAAS,YAAY,IAAI,4BAAgB,OAAO;AAEhD,QAAM,QAAQ,SAAS;AAEvB,+BAAU,MAAM;AACZ,QAAI,YAAY;AAEhB,UAAM,YAAY,YAAY;AAC1B,aAAO,CAAC,WAAW;AACf,YAAI;AACA,gBAAM,MAAM,YAAY;AAAA,QAC5B,QAAQ;AACJ;AAAA,QACJ;AAEA,YAAI,CAAC,UAAW,aAAY;AAAA,MAChC;AAAA,IACJ;AAEA,SAAK,UAAU;AAEf,WAAO,MAAM;AACT,kBAAY;AAAA,IAChB;AAAA,EACJ,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO,CAAC,MAAM,OAAO,OAAK,MAAM,IAAI,CAAC,GAAG,KAAK;AACjD;;;AFrCA,IAAAC,iBAA4D;","names":["import_react","import_fusion","import_fusion"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-computed-state.ts","../src/use-mutable-state.ts"],"sourcesContent":["export { useComputedState } from './use-computed-state.js';\r\nexport type { UseComputedStateResult } from './use-computed-state.js';\r\nexport { useMutableState } from './use-mutable-state.js';\r\nexport type { UseMutableStateResult } from './use-mutable-state.js';\r\n// Re-export from @actuallab/fusion for convenience\r\nexport { UIActionTracker, uiActions, UIUpdateDelayer } from '@actuallab/fusion';\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport {\r\n ComputedState,\r\n type ComputedStateOptions,\r\n type StateComputer,\r\n} from '@actuallab/fusion';\r\n\r\nexport interface UseComputedStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n isInitial: boolean;\r\n state: ComputedState<T> | undefined;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's ComputedState, built on useSyncExternalStore so\r\n * updates between render and subscription are never lost and rendering is tear-free.\r\n * The ComputedState is created and disposed exclusively inside `subscribe` (keyed on\r\n * deps), so a discarded concurrent render leaks nothing and a StrictMode\r\n * mount→cleanup→remount rebuilds a live state instead of freezing on a disposed one.\r\n */\r\nexport function useComputedState<T>(\r\n computer: StateComputer<T>,\r\n deps: readonly unknown[],\r\n options?: ComputedStateOptions<T>\r\n): UseComputedStateResult<T> {\r\n const stateRef = useRef<ComputedState<T> | null>(null);\r\n const generationRef = useRef(0);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const state = new ComputedState<T>(computer, options);\r\n generationRef.current++;\r\n stateRef.current = state;\r\n onStoreChange();\r\n let cancelled = false;\r\n void (async () => {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled && !state.isDisposed) {\r\n const sinceIndex = state.updateIndex;\r\n try {\r\n await state.whenUpdated(sinceIndex);\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled && !state.isDisposed)\r\n onStoreChange();\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n state.dispose();\r\n if (stateRef.current === state)\r\n stateRef.current = null;\r\n };\r\n },\r\n deps\r\n );\r\n\r\n // Snapshot is `generation:updateIndex` — the generation makes a rebuilt state\r\n // distinct even at the same updateIndex (a sync computer reaches index 1 inside\r\n // the constructor, so a deps-change replacement would otherwise collide with the\r\n // rendered snapshot and never re-render). '' marks \"no state yet\" (the pre-effect\r\n // first render), which the built-in re-check upgrades once `subscribe` creates one.\r\n useSyncExternalStore(\r\n subscribe,\r\n () =>\r\n stateRef.current === null\r\n ? ''\r\n : `${generationRef.current}:${stateRef.current.updateIndex}`,\r\n () => ''\r\n );\r\n\r\n const state = stateRef.current ?? undefined;\r\n if (state === undefined || state.updateIndex === 0)\r\n return { value: undefined, error: undefined, isInitial: true, state };\r\n\r\n return {\r\n value: state.valueOrUndefined,\r\n error: state.error,\r\n isInitial: false,\r\n state,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { MutableState } from '@actuallab/fusion';\r\nimport type { Result } from '@actuallab/core';\r\n\r\nexport interface UseMutableStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n set: (value: Result<T> | T) => void;\r\n state: MutableState<T>;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's MutableState, built on useSyncExternalStore so a\r\n * `set` landing between render and subscription is never lost. `value` is\r\n * `state.valueOrUndefined` and never throws on an error result; the error is\r\n * exposed separately.\r\n */\r\nexport function useMutableState<T>(initial: T): UseMutableStateResult<T> {\r\n const stateRef = useRef<MutableState<T> | null>(null);\r\n stateRef.current ??= new MutableState<T>(initial);\r\n const state = stateRef.current;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n let cancelled = false;\r\n void (async () => {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled) {\r\n const sinceIndex = state.updateIndex;\r\n try {\r\n await state.whenUpdated(sinceIndex);\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled)\r\n onStoreChange();\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n },\r\n [state]\r\n );\r\n\r\n useSyncExternalStore(\r\n subscribe,\r\n () => state.updateIndex,\r\n () => state.updateIndex\r\n );\r\n\r\n return {\r\n value: state.valueOrUndefined,\r\n error: state.error,\r\n set: (value: Result<T> | T) => state.set(value),\r\n state,\r\n };\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAA0D;AAC1D,oBAIO;AAgBA,SAAS,iBACZ,UACA,MACA,SACyB;AACzB,QAAM,eAAW,qBAAgC,IAAI;AACrD,QAAM,oBAAgB,qBAAO,CAAC;AAE9B,QAAM,gBAAY;AAAA,IACd,CAAC,kBAA8B;AAC3B,YAAMA,SAAQ,IAAI,4BAAiB,UAAU,OAAO;AACpD,oBAAc;AACd,eAAS,UAAUA;AACnB,oBAAc;AACd,UAAI,YAAY;AAChB,YAAM,YAAY;AAEd,eAAO,CAAC,aAAa,CAACA,OAAM,YAAY;AACpC,gBAAM,aAAaA,OAAM;AACzB,cAAI;AACA,kBAAMA,OAAM,YAAY,UAAU;AAAA,UACtC,QAAQ;AACJ;AAAA,UACJ;AAEA,cAAI,CAAC,aAAa,CAACA,OAAM;AACrB,0BAAc;AAAA,QACtB;AAAA,MACJ,GAAG;AAEH,aAAO,MAAM;AACT,oBAAY;AACZ,QAAAA,OAAM,QAAQ;AACd,YAAI,SAAS,YAAYA;AACrB,mBAAS,UAAU;AAAA,MAC3B;AAAA,IACJ;AAAA,IACA;AAAA,EACJ;AAOA;AAAA,IACI;AAAA,IACA,MACI,SAAS,YAAY,OACf,KACA,GAAG,cAAc,OAAO,IAAI,SAAS,QAAQ,WAAW;AAAA,IAClE,MAAM;AAAA,EACV;AAEA,QAAM,QAAQ,SAAS,WAAW;AAClC,MAAI,UAAU,UAAa,MAAM,gBAAgB;AAC7C,WAAO,EAAE,OAAO,QAAW,OAAO,QAAW,WAAW,MAAM,MAAM;AAExE,SAAO;AAAA,IACH,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,WAAW;AAAA,IACX;AAAA,EACJ;AACJ;;;ACrFA,IAAAC,gBAA0D;AAC1D,IAAAC,iBAA6B;AAgBtB,SAAS,gBAAmB,SAAsC;AACrE,QAAM,eAAW,sBAA+B,IAAI;AACpD,WAAS,YAAY,IAAI,4BAAgB,OAAO;AAChD,QAAM,QAAQ,SAAS;AAEvB,QAAM,gBAAY;AAAA,IACd,CAAC,kBAA8B;AAC3B,UAAI,YAAY;AAChB,YAAM,YAAY;AAEd,eAAO,CAAC,WAAW;AACf,gBAAM,aAAa,MAAM;AACzB,cAAI;AACA,kBAAM,MAAM,YAAY,UAAU;AAAA,UACtC,QAAQ;AACJ;AAAA,UACJ;AAEA,cAAI,CAAC;AACD,0BAAc;AAAA,QACtB;AAAA,MACJ,GAAG;AAEH,aAAO,MAAM;AACT,oBAAY;AAAA,MAChB;AAAA,IACJ;AAAA,IACA,CAAC,KAAK;AAAA,EACV;AAEA;AAAA,IACI;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,EAChB;AAEA,SAAO;AAAA,IACH,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,KAAK,CAAC,UAAyB,MAAM,IAAI,KAAK;AAAA,IAC9C;AAAA,EACJ;AACJ;;;AFtDA,IAAAC,iBAA4D;","names":["state","import_react","import_fusion","import_fusion"]}
package/dist/index.d.cts CHANGED
@@ -6,18 +6,29 @@ interface UseComputedStateResult<T> {
6
6
  value: T | undefined;
7
7
  error: unknown;
8
8
  isInitial: boolean;
9
- state: ComputedState<T>;
9
+ state: ComputedState<T> | undefined;
10
10
  }
11
11
  /**
12
- * React hook wrapping Fusion's ComputedState.
13
- * Creates a ComputedState keyed on deps, subscribes to updates, and triggers re-renders.
12
+ * React hook wrapping Fusion's ComputedState, built on useSyncExternalStore so
13
+ * updates between render and subscription are never lost and rendering is tear-free.
14
+ * The ComputedState is created and disposed exclusively inside `subscribe` (keyed on
15
+ * deps), so a discarded concurrent render leaks nothing and a StrictMode
16
+ * mount→cleanup→remount rebuilds a live state instead of freezing on a disposed one.
14
17
  */
15
18
  declare function useComputedState<T>(computer: StateComputer<T>, deps: readonly unknown[], options?: ComputedStateOptions<T>): UseComputedStateResult<T>;
16
19
 
20
+ interface UseMutableStateResult<T> {
21
+ value: T | undefined;
22
+ error: unknown;
23
+ set: (value: Result<T> | T) => void;
24
+ state: MutableState<T>;
25
+ }
17
26
  /**
18
- * React hook wrapping Fusion's MutableState.
19
- * Returns [value, setter, state] — re-renders on updates.
27
+ * React hook wrapping Fusion's MutableState, built on useSyncExternalStore so a
28
+ * `set` landing between render and subscription is never lost. `value` is
29
+ * `state.valueOrUndefined` and never throws on an error result; the error is
30
+ * exposed separately.
20
31
  */
21
- declare function useMutableState<T>(initial: T): [T, (value: Result<T> | T) => void, MutableState<T>];
32
+ declare function useMutableState<T>(initial: T): UseMutableStateResult<T>;
22
33
 
23
- export { type UseComputedStateResult, useComputedState, useMutableState };
34
+ export { type UseComputedStateResult, type UseMutableStateResult, useComputedState, useMutableState };
package/dist/index.d.ts CHANGED
@@ -6,18 +6,29 @@ interface UseComputedStateResult<T> {
6
6
  value: T | undefined;
7
7
  error: unknown;
8
8
  isInitial: boolean;
9
- state: ComputedState<T>;
9
+ state: ComputedState<T> | undefined;
10
10
  }
11
11
  /**
12
- * React hook wrapping Fusion's ComputedState.
13
- * Creates a ComputedState keyed on deps, subscribes to updates, and triggers re-renders.
12
+ * React hook wrapping Fusion's ComputedState, built on useSyncExternalStore so
13
+ * updates between render and subscription are never lost and rendering is tear-free.
14
+ * The ComputedState is created and disposed exclusively inside `subscribe` (keyed on
15
+ * deps), so a discarded concurrent render leaks nothing and a StrictMode
16
+ * mount→cleanup→remount rebuilds a live state instead of freezing on a disposed one.
14
17
  */
15
18
  declare function useComputedState<T>(computer: StateComputer<T>, deps: readonly unknown[], options?: ComputedStateOptions<T>): UseComputedStateResult<T>;
16
19
 
20
+ interface UseMutableStateResult<T> {
21
+ value: T | undefined;
22
+ error: unknown;
23
+ set: (value: Result<T> | T) => void;
24
+ state: MutableState<T>;
25
+ }
17
26
  /**
18
- * React hook wrapping Fusion's MutableState.
19
- * Returns [value, setter, state] — re-renders on updates.
27
+ * React hook wrapping Fusion's MutableState, built on useSyncExternalStore so a
28
+ * `set` landing between render and subscription is never lost. `value` is
29
+ * `state.valueOrUndefined` and never throws on an error result; the error is
30
+ * exposed separately.
20
31
  */
21
- declare function useMutableState<T>(initial: T): [T, (value: Result<T> | T) => void, MutableState<T>];
32
+ declare function useMutableState<T>(initial: T): UseMutableStateResult<T>;
22
33
 
23
- export { type UseComputedStateResult, useComputedState, useMutableState };
34
+ export { type UseComputedStateResult, type UseMutableStateResult, useComputedState, useMutableState };
package/dist/index.js CHANGED
@@ -1,79 +1,94 @@
1
1
  // src/use-computed-state.ts
2
- import { useEffect, useReducer, useRef } from "react";
2
+ import { useCallback, useRef, useSyncExternalStore } from "react";
3
3
  import {
4
4
  ComputedState
5
5
  } from "@actuallab/fusion";
6
6
  function useComputedState(computer, deps, options) {
7
- const [, forceRender] = useReducer((c) => c + 1, 0);
8
7
  const stateRef = useRef(null);
9
- const depsRef = useRef(deps);
10
- const depsChanged = deps.length !== depsRef.current.length || deps.some((d, i) => d !== depsRef.current[i]);
11
- if (depsChanged || stateRef.current === null) {
12
- stateRef.current?.dispose();
13
- depsRef.current = deps;
14
- stateRef.current = new ComputedState(computer, options);
15
- }
16
- const state = stateRef.current;
17
- useEffect(() => {
18
- let cancelled = false;
19
- const subscribe = async () => {
20
- try {
21
- await state.whenFirstTimeUpdated();
22
- } catch {
23
- return;
24
- }
25
- if (cancelled || state.isDisposed) return;
26
- forceRender();
27
- while (!cancelled && !state.isDisposed) {
28
- try {
29
- await state.whenUpdated();
30
- } catch {
31
- return;
8
+ const generationRef = useRef(0);
9
+ const subscribe = useCallback(
10
+ (onStoreChange) => {
11
+ const state2 = new ComputedState(computer, options);
12
+ generationRef.current++;
13
+ stateRef.current = state2;
14
+ onStoreChange();
15
+ let cancelled = false;
16
+ void (async () => {
17
+ while (!cancelled && !state2.isDisposed) {
18
+ const sinceIndex = state2.updateIndex;
19
+ try {
20
+ await state2.whenUpdated(sinceIndex);
21
+ } catch {
22
+ return;
23
+ }
24
+ if (!cancelled && !state2.isDisposed)
25
+ onStoreChange();
32
26
  }
33
- if (!cancelled && !state.isDisposed) forceRender();
34
- }
35
- };
36
- void subscribe();
37
- return () => {
38
- cancelled = true;
39
- state.dispose();
40
- };
41
- }, [state]);
42
- const isInitial = state.updateIndex === 0;
27
+ })();
28
+ return () => {
29
+ cancelled = true;
30
+ state2.dispose();
31
+ if (stateRef.current === state2)
32
+ stateRef.current = null;
33
+ };
34
+ },
35
+ deps
36
+ );
37
+ useSyncExternalStore(
38
+ subscribe,
39
+ () => stateRef.current === null ? "" : `${generationRef.current}:${stateRef.current.updateIndex}`,
40
+ () => ""
41
+ );
42
+ const state = stateRef.current ?? void 0;
43
+ if (state === void 0 || state.updateIndex === 0)
44
+ return { value: void 0, error: void 0, isInitial: true, state };
43
45
  return {
44
- value: isInitial ? void 0 : state.valueOrUndefined,
45
- error: isInitial ? void 0 : state.error,
46
- isInitial,
46
+ value: state.valueOrUndefined,
47
+ error: state.error,
48
+ isInitial: false,
47
49
  state
48
50
  };
49
51
  }
50
52
 
51
53
  // src/use-mutable-state.ts
52
- import { useEffect as useEffect2, useReducer as useReducer2, useRef as useRef2 } from "react";
54
+ import { useCallback as useCallback2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
53
55
  import { MutableState } from "@actuallab/fusion";
54
56
  function useMutableState(initial) {
55
- const [, forceRender] = useReducer2((c) => c + 1, 0);
56
57
  const stateRef = useRef2(null);
57
58
  stateRef.current ??= new MutableState(initial);
58
59
  const state = stateRef.current;
59
- useEffect2(() => {
60
- let cancelled = false;
61
- const subscribe = async () => {
62
- while (!cancelled) {
63
- try {
64
- await state.whenUpdated();
65
- } catch {
66
- return;
60
+ const subscribe = useCallback2(
61
+ (onStoreChange) => {
62
+ let cancelled = false;
63
+ void (async () => {
64
+ while (!cancelled) {
65
+ const sinceIndex = state.updateIndex;
66
+ try {
67
+ await state.whenUpdated(sinceIndex);
68
+ } catch {
69
+ return;
70
+ }
71
+ if (!cancelled)
72
+ onStoreChange();
67
73
  }
68
- if (!cancelled) forceRender();
69
- }
70
- };
71
- void subscribe();
72
- return () => {
73
- cancelled = true;
74
- };
75
- }, [state]);
76
- return [state.value, (v) => state.set(v), state];
74
+ })();
75
+ return () => {
76
+ cancelled = true;
77
+ };
78
+ },
79
+ [state]
80
+ );
81
+ useSyncExternalStore2(
82
+ subscribe,
83
+ () => state.updateIndex,
84
+ () => state.updateIndex
85
+ );
86
+ return {
87
+ value: state.valueOrUndefined,
88
+ error: state.error,
89
+ set: (value) => state.set(value),
90
+ state
91
+ };
77
92
  }
78
93
 
79
94
  // src/index.ts
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-computed-state.ts","../src/use-mutable-state.ts","../src/index.ts"],"sourcesContent":["import { useEffect, useReducer, useRef } from 'react';\r\nimport {\r\n ComputedState,\r\n type ComputedStateOptions,\r\n type StateComputer,\r\n} from '@actuallab/fusion';\r\n\r\nexport interface UseComputedStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n isInitial: boolean;\r\n state: ComputedState<T>;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's ComputedState.\r\n * Creates a ComputedState keyed on deps, subscribes to updates, and triggers re-renders.\r\n */\r\nexport function useComputedState<T>(\r\n computer: StateComputer<T>,\r\n deps: readonly unknown[],\r\n options?: ComputedStateOptions<T>\r\n): UseComputedStateResult<T> {\r\n const [, forceRender] = useReducer(c => c + 1, 0);\r\n const stateRef = useRef<ComputedState<T> | null>(null);\r\n const depsRef = useRef<readonly unknown[]>(deps);\r\n\r\n // Check if deps changed\r\n const depsChanged =\r\n deps.length !== depsRef.current.length ||\r\n deps.some((d, i) => d !== depsRef.current[i]);\r\n\r\n if (depsChanged || stateRef.current === null) {\r\n // Dispose old state\r\n stateRef.current?.dispose();\r\n depsRef.current = deps;\r\n stateRef.current = new ComputedState<T>(computer, options);\r\n }\r\n\r\n const state = stateRef.current;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n\r\n const subscribe = async () => {\r\n // Wait for first update\r\n try {\r\n await state.whenFirstTimeUpdated();\r\n } catch {\r\n return;\r\n }\r\n if (cancelled || state.isDisposed) return;\r\n forceRender();\r\n\r\n // Subscribe to subsequent updates\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled && !state.isDisposed) {\r\n try {\r\n await state.whenUpdated();\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled && !state.isDisposed) forceRender();\r\n }\r\n };\r\n\r\n void subscribe();\r\n\r\n return () => {\r\n cancelled = true;\r\n state.dispose();\r\n };\r\n }, [state]);\r\n\r\n const isInitial = state.updateIndex === 0;\r\n return {\r\n value: isInitial ? undefined : state.valueOrUndefined,\r\n error: isInitial ? undefined : state.error,\r\n isInitial,\r\n state,\r\n };\r\n}\r\n","import { useEffect, useReducer, useRef } from 'react';\r\nimport { MutableState } from '@actuallab/fusion';\r\nimport type { Result } from '@actuallab/core';\r\n\r\n/**\r\n * React hook wrapping Fusion's MutableState.\r\n * Returns [value, setter, state] — re-renders on updates.\r\n */\r\nexport function useMutableState<T>(\r\n initial: T\r\n): [T, (value: Result<T> | T) => void, MutableState<T>] {\r\n const [, forceRender] = useReducer(c => c + 1, 0);\r\n const stateRef = useRef<MutableState<T> | null>(null);\r\n\r\n stateRef.current ??= new MutableState<T>(initial);\r\n\r\n const state = stateRef.current;\r\n\r\n useEffect(() => {\r\n let cancelled = false;\r\n\r\n const subscribe = async () => {\r\n while (!cancelled) {\r\n try {\r\n await state.whenUpdated();\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled) forceRender();\r\n }\r\n };\r\n\r\n void subscribe();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n }, [state]);\r\n\r\n return [state.value, v => state.set(v), state];\r\n}\r\n","export { useComputedState } from './use-computed-state.js';\r\nexport type { UseComputedStateResult } from './use-computed-state.js';\r\nexport { useMutableState } from './use-mutable-state.js';\r\n// Re-export from @actuallab/fusion for convenience\r\nexport { UIActionTracker, uiActions, UIUpdateDelayer } from '@actuallab/fusion';\r\n"],"mappings":";AAAA,SAAS,WAAW,YAAY,cAAc;AAC9C;AAAA,EACI;AAAA,OAGG;AAaA,SAAS,iBACZ,UACA,MACA,SACyB;AACzB,QAAM,CAAC,EAAE,WAAW,IAAI,WAAW,OAAK,IAAI,GAAG,CAAC;AAChD,QAAM,WAAW,OAAgC,IAAI;AACrD,QAAM,UAAU,OAA2B,IAAI;AAG/C,QAAM,cACF,KAAK,WAAW,QAAQ,QAAQ,UAChC,KAAK,KAAK,CAAC,GAAG,MAAM,MAAM,QAAQ,QAAQ,CAAC,CAAC;AAEhD,MAAI,eAAe,SAAS,YAAY,MAAM;AAE1C,aAAS,SAAS,QAAQ;AAC1B,YAAQ,UAAU;AAClB,aAAS,UAAU,IAAI,cAAiB,UAAU,OAAO;AAAA,EAC7D;AAEA,QAAM,QAAQ,SAAS;AAEvB,YAAU,MAAM;AACZ,QAAI,YAAY;AAEhB,UAAM,YAAY,YAAY;AAE1B,UAAI;AACA,cAAM,MAAM,qBAAqB;AAAA,MACrC,QAAQ;AACJ;AAAA,MACJ;AACA,UAAI,aAAa,MAAM,WAAY;AACnC,kBAAY;AAIZ,aAAO,CAAC,aAAa,CAAC,MAAM,YAAY;AACpC,YAAI;AACA,gBAAM,MAAM,YAAY;AAAA,QAC5B,QAAQ;AACJ;AAAA,QACJ;AAEA,YAAI,CAAC,aAAa,CAAC,MAAM,WAAY,aAAY;AAAA,MACrD;AAAA,IACJ;AAEA,SAAK,UAAU;AAEf,WAAO,MAAM;AACT,kBAAY;AACZ,YAAM,QAAQ;AAAA,IAClB;AAAA,EACJ,GAAG,CAAC,KAAK,CAAC;AAEV,QAAM,YAAY,MAAM,gBAAgB;AACxC,SAAO;AAAA,IACH,OAAO,YAAY,SAAY,MAAM;AAAA,IACrC,OAAO,YAAY,SAAY,MAAM;AAAA,IACrC;AAAA,IACA;AAAA,EACJ;AACJ;;;AClFA,SAAS,aAAAA,YAAW,cAAAC,aAAY,UAAAC,eAAc;AAC9C,SAAS,oBAAoB;AAOtB,SAAS,gBACZ,SACoD;AACpD,QAAM,CAAC,EAAE,WAAW,IAAID,YAAW,OAAK,IAAI,GAAG,CAAC;AAChD,QAAM,WAAWC,QAA+B,IAAI;AAEpD,WAAS,YAAY,IAAI,aAAgB,OAAO;AAEhD,QAAM,QAAQ,SAAS;AAEvB,EAAAF,WAAU,MAAM;AACZ,QAAI,YAAY;AAEhB,UAAM,YAAY,YAAY;AAC1B,aAAO,CAAC,WAAW;AACf,YAAI;AACA,gBAAM,MAAM,YAAY;AAAA,QAC5B,QAAQ;AACJ;AAAA,QACJ;AAEA,YAAI,CAAC,UAAW,aAAY;AAAA,MAChC;AAAA,IACJ;AAEA,SAAK,UAAU;AAEf,WAAO,MAAM;AACT,kBAAY;AAAA,IAChB;AAAA,EACJ,GAAG,CAAC,KAAK,CAAC;AAEV,SAAO,CAAC,MAAM,OAAO,OAAK,MAAM,IAAI,CAAC,GAAG,KAAK;AACjD;;;ACrCA,SAAS,iBAAiB,WAAW,uBAAuB;","names":["useEffect","useReducer","useRef"]}
1
+ {"version":3,"sources":["../src/use-computed-state.ts","../src/use-mutable-state.ts","../src/index.ts"],"sourcesContent":["import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport {\r\n ComputedState,\r\n type ComputedStateOptions,\r\n type StateComputer,\r\n} from '@actuallab/fusion';\r\n\r\nexport interface UseComputedStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n isInitial: boolean;\r\n state: ComputedState<T> | undefined;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's ComputedState, built on useSyncExternalStore so\r\n * updates between render and subscription are never lost and rendering is tear-free.\r\n * The ComputedState is created and disposed exclusively inside `subscribe` (keyed on\r\n * deps), so a discarded concurrent render leaks nothing and a StrictMode\r\n * mount→cleanup→remount rebuilds a live state instead of freezing on a disposed one.\r\n */\r\nexport function useComputedState<T>(\r\n computer: StateComputer<T>,\r\n deps: readonly unknown[],\r\n options?: ComputedStateOptions<T>\r\n): UseComputedStateResult<T> {\r\n const stateRef = useRef<ComputedState<T> | null>(null);\r\n const generationRef = useRef(0);\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n const state = new ComputedState<T>(computer, options);\r\n generationRef.current++;\r\n stateRef.current = state;\r\n onStoreChange();\r\n let cancelled = false;\r\n void (async () => {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled && !state.isDisposed) {\r\n const sinceIndex = state.updateIndex;\r\n try {\r\n await state.whenUpdated(sinceIndex);\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled && !state.isDisposed)\r\n onStoreChange();\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n state.dispose();\r\n if (stateRef.current === state)\r\n stateRef.current = null;\r\n };\r\n },\r\n deps\r\n );\r\n\r\n // Snapshot is `generation:updateIndex` — the generation makes a rebuilt state\r\n // distinct even at the same updateIndex (a sync computer reaches index 1 inside\r\n // the constructor, so a deps-change replacement would otherwise collide with the\r\n // rendered snapshot and never re-render). '' marks \"no state yet\" (the pre-effect\r\n // first render), which the built-in re-check upgrades once `subscribe` creates one.\r\n useSyncExternalStore(\r\n subscribe,\r\n () =>\r\n stateRef.current === null\r\n ? ''\r\n : `${generationRef.current}:${stateRef.current.updateIndex}`,\r\n () => ''\r\n );\r\n\r\n const state = stateRef.current ?? undefined;\r\n if (state === undefined || state.updateIndex === 0)\r\n return { value: undefined, error: undefined, isInitial: true, state };\r\n\r\n return {\r\n value: state.valueOrUndefined,\r\n error: state.error,\r\n isInitial: false,\r\n state,\r\n };\r\n}\r\n","import { useCallback, useRef, useSyncExternalStore } from 'react';\r\nimport { MutableState } from '@actuallab/fusion';\r\nimport type { Result } from '@actuallab/core';\r\n\r\nexport interface UseMutableStateResult<T> {\r\n value: T | undefined;\r\n error: unknown;\r\n set: (value: Result<T> | T) => void;\r\n state: MutableState<T>;\r\n}\r\n\r\n/**\r\n * React hook wrapping Fusion's MutableState, built on useSyncExternalStore so a\r\n * `set` landing between render and subscription is never lost. `value` is\r\n * `state.valueOrUndefined` and never throws on an error result; the error is\r\n * exposed separately.\r\n */\r\nexport function useMutableState<T>(initial: T): UseMutableStateResult<T> {\r\n const stateRef = useRef<MutableState<T> | null>(null);\r\n stateRef.current ??= new MutableState<T>(initial);\r\n const state = stateRef.current;\r\n\r\n const subscribe = useCallback(\r\n (onStoreChange: () => void) => {\r\n let cancelled = false;\r\n void (async () => {\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n while (!cancelled) {\r\n const sinceIndex = state.updateIndex;\r\n try {\r\n await state.whenUpdated(sinceIndex);\r\n } catch {\r\n return;\r\n }\r\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\r\n if (!cancelled)\r\n onStoreChange();\r\n }\r\n })();\r\n\r\n return () => {\r\n cancelled = true;\r\n };\r\n },\r\n [state]\r\n );\r\n\r\n useSyncExternalStore(\r\n subscribe,\r\n () => state.updateIndex,\r\n () => state.updateIndex\r\n );\r\n\r\n return {\r\n value: state.valueOrUndefined,\r\n error: state.error,\r\n set: (value: Result<T> | T) => state.set(value),\r\n state,\r\n };\r\n}\r\n","export { useComputedState } from './use-computed-state.js';\r\nexport type { UseComputedStateResult } from './use-computed-state.js';\r\nexport { useMutableState } from './use-mutable-state.js';\r\nexport type { UseMutableStateResult } from './use-mutable-state.js';\r\n// Re-export from @actuallab/fusion for convenience\r\nexport { UIActionTracker, uiActions, UIUpdateDelayer } from '@actuallab/fusion';\r\n"],"mappings":";AAAA,SAAS,aAAa,QAAQ,4BAA4B;AAC1D;AAAA,EACI;AAAA,OAGG;AAgBA,SAAS,iBACZ,UACA,MACA,SACyB;AACzB,QAAM,WAAW,OAAgC,IAAI;AACrD,QAAM,gBAAgB,OAAO,CAAC;AAE9B,QAAM,YAAY;AAAA,IACd,CAAC,kBAA8B;AAC3B,YAAMA,SAAQ,IAAI,cAAiB,UAAU,OAAO;AACpD,oBAAc;AACd,eAAS,UAAUA;AACnB,oBAAc;AACd,UAAI,YAAY;AAChB,YAAM,YAAY;AAEd,eAAO,CAAC,aAAa,CAACA,OAAM,YAAY;AACpC,gBAAM,aAAaA,OAAM;AACzB,cAAI;AACA,kBAAMA,OAAM,YAAY,UAAU;AAAA,UACtC,QAAQ;AACJ;AAAA,UACJ;AAEA,cAAI,CAAC,aAAa,CAACA,OAAM;AACrB,0BAAc;AAAA,QACtB;AAAA,MACJ,GAAG;AAEH,aAAO,MAAM;AACT,oBAAY;AACZ,QAAAA,OAAM,QAAQ;AACd,YAAI,SAAS,YAAYA;AACrB,mBAAS,UAAU;AAAA,MAC3B;AAAA,IACJ;AAAA,IACA;AAAA,EACJ;AAOA;AAAA,IACI;AAAA,IACA,MACI,SAAS,YAAY,OACf,KACA,GAAG,cAAc,OAAO,IAAI,SAAS,QAAQ,WAAW;AAAA,IAClE,MAAM;AAAA,EACV;AAEA,QAAM,QAAQ,SAAS,WAAW;AAClC,MAAI,UAAU,UAAa,MAAM,gBAAgB;AAC7C,WAAO,EAAE,OAAO,QAAW,OAAO,QAAW,WAAW,MAAM,MAAM;AAExE,SAAO;AAAA,IACH,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,WAAW;AAAA,IACX;AAAA,EACJ;AACJ;;;ACrFA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,wBAAAC,6BAA4B;AAC1D,SAAS,oBAAoB;AAgBtB,SAAS,gBAAmB,SAAsC;AACrE,QAAM,WAAWD,QAA+B,IAAI;AACpD,WAAS,YAAY,IAAI,aAAgB,OAAO;AAChD,QAAM,QAAQ,SAAS;AAEvB,QAAM,YAAYD;AAAA,IACd,CAAC,kBAA8B;AAC3B,UAAI,YAAY;AAChB,YAAM,YAAY;AAEd,eAAO,CAAC,WAAW;AACf,gBAAM,aAAa,MAAM;AACzB,cAAI;AACA,kBAAM,MAAM,YAAY,UAAU;AAAA,UACtC,QAAQ;AACJ;AAAA,UACJ;AAEA,cAAI,CAAC;AACD,0BAAc;AAAA,QACtB;AAAA,MACJ,GAAG;AAEH,aAAO,MAAM;AACT,oBAAY;AAAA,MAChB;AAAA,IACJ;AAAA,IACA,CAAC,KAAK;AAAA,EACV;AAEA,EAAAE;AAAA,IACI;AAAA,IACA,MAAM,MAAM;AAAA,IACZ,MAAM,MAAM;AAAA,EAChB;AAEA,SAAO;AAAA,IACH,OAAO,MAAM;AAAA,IACb,OAAO,MAAM;AAAA,IACb,KAAK,CAAC,UAAyB,MAAM,IAAI,KAAK;AAAA,IAC9C;AAAA,EACJ;AACJ;;;ACtDA,SAAS,iBAAiB,WAAW,uBAAuB;","names":["state","useCallback","useRef","useSyncExternalStore"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuallab/fusion-react",
3
- "version": "13.0.25",
3
+ "version": "13.0.167",
4
4
  "description": "React integration for Fusion - hooks and UI action tracking",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,8 +26,8 @@
26
26
  "clean": "rm -rf dist"
27
27
  },
28
28
  "dependencies": {
29
- "@actuallab/core": "13.0.25",
30
- "@actuallab/fusion": "13.0.25"
29
+ "@actuallab/core": "13.0.167",
30
+ "@actuallab/fusion": "13.0.167"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "react": "^19.0.0"