@fozy-labs/rx-toolkit 0.5.3-rc.1 → 0.5.3

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 (207) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +143 -137
  3. package/dist/common/devtools/combineDevtools.js +3 -3
  4. package/dist/common/devtools/index.d.ts +3 -3
  5. package/dist/common/devtools/index.js +3 -3
  6. package/dist/common/devtools/reduxDevtools.d.ts +1 -1
  7. package/dist/common/devtools/reduxDevtools.js +17 -17
  8. package/dist/common/devtools/types.d.ts +0 -6
  9. package/dist/common/options/DefaultOptions.d.ts +1 -1
  10. package/dist/common/options/SharedOptions.d.ts +3 -2
  11. package/dist/common/options/SharedOptions.js +6 -0
  12. package/dist/common/options/index.d.ts +1 -1
  13. package/dist/common/options/index.js +1 -1
  14. package/dist/common/react/index.d.ts +2 -2
  15. package/dist/common/react/index.js +2 -2
  16. package/dist/common/react/useConstant.js +1 -1
  17. package/dist/common/utils/deepEqual.js +1 -1
  18. package/dist/common/utils/index.d.ts +3 -3
  19. package/dist/common/utils/index.js +3 -3
  20. package/dist/common/utils/shallowEqual.js +1 -1
  21. package/dist/index.d.ts +8 -7
  22. package/dist/index.js +8 -7
  23. package/dist/query/SKIP_TOKEN.js +1 -1
  24. package/dist/query/api/createCommand.d.ts +21 -0
  25. package/dist/query/api/createCommand.js +20 -0
  26. package/dist/query/api/createOperation.d.ts +5 -3
  27. package/dist/query/api/createOperation.js +6 -2
  28. package/dist/query/api/createResource.d.ts +2 -2
  29. package/dist/query/api/createResourceDuplicator.d.ts +2 -2
  30. package/dist/query/core/Command/Command.d.ts +35 -0
  31. package/dist/query/core/{Opertation/Operation.js → Command/Command.js} +13 -14
  32. package/dist/query/core/Command/CommandAgent.d.ts +19 -0
  33. package/dist/query/core/{Opertation/OperationAgent.js → Command/CommandAgent.js} +13 -13
  34. package/dist/query/core/Command/index.d.ts +2 -0
  35. package/dist/query/core/Command/index.js +2 -0
  36. package/dist/query/core/Operation/Operation.d.ts +8 -0
  37. package/dist/query/core/Operation/Operation.js +4 -0
  38. package/dist/query/core/Operation/OperationAgent.d.ts +4 -0
  39. package/dist/query/core/Operation/OperationAgent.js +4 -0
  40. package/dist/query/core/QueriesCache.d.ts +2 -2
  41. package/dist/query/core/QueriesCache.js +1 -1
  42. package/dist/query/core/QueriesLifetimeHooks.d.ts +1 -1
  43. package/dist/query/core/QueriesLifetimeHooks.js +7 -7
  44. package/dist/query/core/Resource/Resource.d.ts +16 -16
  45. package/dist/query/core/Resource/Resource.js +7 -7
  46. package/dist/query/core/Resource/ResourceAgent.d.ts +2 -2
  47. package/dist/query/core/Resource/ResourceAgent.js +3 -3
  48. package/dist/query/core/Resource/ResourceDuplicator.d.ts +17 -17
  49. package/dist/query/core/Resource/ResourceDuplicator.js +18 -20
  50. package/dist/query/core/Resource/ResourceDuplicatorAgent.d.ts +6 -6
  51. package/dist/query/core/Resource/ResourceDuplicatorAgent.js +3 -3
  52. package/dist/query/core/Resource/ResourceRef.d.ts +2 -2
  53. package/dist/query/core/Resource/ResourceRef.js +12 -12
  54. package/dist/query/index.d.ts +11 -8
  55. package/dist/query/index.js +14 -8
  56. package/dist/query/lib/IndirectMap.js +4 -4
  57. package/dist/query/lib/ReactiveCache.d.ts +1 -1
  58. package/dist/query/react/useCommandAgent.d.ts +24 -0
  59. package/dist/query/react/useCommandAgent.js +39 -0
  60. package/dist/query/react/useOperationAgent.d.ts +6 -8
  61. package/dist/query/react/useOperationAgent.js +6 -23
  62. package/dist/query/react/useResourceAgent.d.ts +4 -4
  63. package/dist/query/react/useResourceAgent.js +1 -1
  64. package/dist/query/react/useResourceRef.d.ts +3 -3
  65. package/dist/query/react/useResourceRef.js +7 -2
  66. package/dist/query/types/Command.types.d.ts +154 -0
  67. package/dist/query/types/Command.types.js +1 -0
  68. package/dist/query/types/Operation.types.d.ts +13 -154
  69. package/dist/query/types/Resource.types.d.ts +7 -5
  70. package/dist/query/types/index.d.ts +4 -3
  71. package/dist/query/types/index.js +5 -3
  72. package/dist/query-v2/api/createApi.d.ts +10 -0
  73. package/dist/query-v2/api/createApi.js +83 -0
  74. package/dist/query-v2/core/common/CacheEntry.d.ts +29 -0
  75. package/dist/query-v2/core/common/CacheEntry.js +71 -0
  76. package/dist/query-v2/core/common/CacheMap.d.ts +38 -0
  77. package/dist/query-v2/core/common/CacheMap.js +127 -0
  78. package/dist/query-v2/core/common/LifecycleHooks.d.ts +22 -0
  79. package/dist/query-v2/core/common/LifecycleHooks.js +104 -0
  80. package/dist/query-v2/core/common/index.d.ts +3 -0
  81. package/dist/query-v2/core/common/index.js +3 -0
  82. package/dist/query-v2/core/index.d.ts +3 -0
  83. package/dist/query-v2/core/index.js +3 -0
  84. package/dist/query-v2/core/machines/Machine.d.ts +14 -0
  85. package/dist/query-v2/core/machines/Machine.js +33 -0
  86. package/dist/query-v2/core/machines/MachineError.d.ts +11 -0
  87. package/dist/query-v2/core/machines/MachineError.js +26 -0
  88. package/dist/query-v2/core/machines/MachineIdle.d.ts +8 -0
  89. package/dist/query-v2/core/machines/MachineIdle.js +19 -0
  90. package/dist/query-v2/core/machines/MachinePending.d.ts +12 -0
  91. package/dist/query-v2/core/machines/MachinePending.js +29 -0
  92. package/dist/query-v2/core/machines/MachineRefreshing.d.ts +14 -0
  93. package/dist/query-v2/core/machines/MachineRefreshing.js +46 -0
  94. package/dist/query-v2/core/machines/MachineSuccess.d.ts +16 -0
  95. package/dist/query-v2/core/machines/MachineSuccess.js +42 -0
  96. package/dist/query-v2/core/machines/MachineWithData.d.ts +18 -0
  97. package/dist/query-v2/core/machines/MachineWithData.js +40 -0
  98. package/dist/query-v2/core/machines/Patcher.d.ts +20 -0
  99. package/dist/query-v2/core/machines/Patcher.js +104 -0
  100. package/dist/query-v2/core/machines/index.d.ts +8 -0
  101. package/dist/query-v2/core/machines/index.js +8 -0
  102. package/dist/query-v2/core/resource/ResourceV2.d.ts +120 -0
  103. package/dist/query-v2/core/resource/ResourceV2.js +464 -0
  104. package/dist/query-v2/core/resource/ResourceV2Agent.d.ts +26 -0
  105. package/dist/query-v2/core/resource/ResourceV2Agent.js +132 -0
  106. package/dist/query-v2/core/resource/index.d.ts +2 -0
  107. package/dist/query-v2/core/resource/index.js +2 -0
  108. package/dist/query-v2/index.d.ts +11 -0
  109. package/dist/query-v2/index.js +17 -0
  110. package/dist/query-v2/lib/NO_VALUE.d.ts +2 -0
  111. package/dist/query-v2/lib/NO_VALUE.js +1 -0
  112. package/dist/query-v2/lib/SKIP_TOKEN.d.ts +2 -0
  113. package/dist/query-v2/lib/SKIP_TOKEN.js +1 -0
  114. package/dist/query-v2/lib/index.d.ts +4 -0
  115. package/dist/query-v2/lib/index.js +3 -0
  116. package/dist/query-v2/lib/stableStringify.d.ts +8 -0
  117. package/dist/query-v2/lib/stableStringify.js +23 -0
  118. package/dist/query-v2/plugins/ReactHooksPlugin.d.ts +25 -0
  119. package/dist/query-v2/plugins/ReactHooksPlugin.js +19 -0
  120. package/dist/query-v2/plugins/types.d.ts +1 -0
  121. package/dist/query-v2/plugins/types.js +1 -0
  122. package/dist/query-v2/react/__tests__/helpers.d.ts +12 -0
  123. package/dist/query-v2/react/__tests__/helpers.js +33 -0
  124. package/dist/query-v2/react/index.d.ts +2 -0
  125. package/dist/query-v2/react/index.js +2 -0
  126. package/dist/query-v2/react/useResourceV2Agent.d.ts +12 -0
  127. package/dist/query-v2/react/useResourceV2Agent.js +36 -0
  128. package/dist/query-v2/react/useResourceV2Ref.d.ts +12 -0
  129. package/dist/query-v2/react/useResourceV2Ref.js +57 -0
  130. package/dist/query-v2/snapshot/Snapshot.d.ts +13 -0
  131. package/dist/query-v2/snapshot/Snapshot.js +76 -0
  132. package/dist/query-v2/types/agent.types.d.ts +54 -0
  133. package/dist/query-v2/types/agent.types.js +1 -0
  134. package/dist/query-v2/types/api.types.d.ts +22 -0
  135. package/dist/query-v2/types/api.types.js +1 -0
  136. package/dist/query-v2/types/cache.types.d.ts +37 -0
  137. package/dist/query-v2/types/cache.types.js +1 -0
  138. package/dist/query-v2/types/index.d.ts +9 -0
  139. package/dist/query-v2/types/index.js +9 -0
  140. package/dist/query-v2/types/lifecycle.types.d.ts +25 -0
  141. package/dist/query-v2/types/lifecycle.types.js +1 -0
  142. package/dist/query-v2/types/machine.types.d.ts +67 -0
  143. package/dist/query-v2/types/machine.types.js +1 -0
  144. package/dist/query-v2/types/plugin.types.d.ts +38 -0
  145. package/dist/query-v2/types/plugin.types.js +1 -0
  146. package/dist/query-v2/types/resource.types.d.ts +35 -0
  147. package/dist/query-v2/types/resource.types.js +1 -0
  148. package/dist/query-v2/types/shared.types.d.ts +20 -0
  149. package/dist/query-v2/types/shared.types.js +1 -0
  150. package/dist/query-v2/types/snapshot.types.d.ts +21 -0
  151. package/dist/query-v2/types/snapshot.types.js +1 -0
  152. package/dist/signals/base/Batcher.js +9 -5
  153. package/dist/signals/base/ComputeCache.js +3 -3
  154. package/dist/signals/base/DependencyTracker.js +1 -1
  155. package/dist/signals/base/Devtools.d.ts +3 -2
  156. package/dist/signals/base/Devtools.js +54 -27
  157. package/dist/signals/base/Indexer.js +1 -1
  158. package/dist/signals/base/ReadonlySignal.js +1 -1
  159. package/dist/signals/base/SyncObservable.d.ts +1 -2
  160. package/dist/signals/base/SyncObservable.js +2 -5
  161. package/dist/signals/base/index.d.ts +6 -6
  162. package/dist/signals/base/index.js +6 -6
  163. package/dist/signals/index.d.ts +5 -4
  164. package/dist/signals/index.js +5 -4
  165. package/dist/signals/operators/index.d.ts +1 -1
  166. package/dist/signals/operators/index.js +1 -1
  167. package/dist/signals/operators/signalize.d.ts +1 -1
  168. package/dist/signals/react/index.d.ts +1 -1
  169. package/dist/signals/react/index.js +1 -1
  170. package/dist/signals/signals/Computed.d.ts +3 -4
  171. package/dist/signals/signals/Computed.js +18 -10
  172. package/dist/signals/signals/Effect.js +2 -1
  173. package/dist/signals/signals/LocalState.d.ts +44 -0
  174. package/dist/signals/signals/{LocalSignal.js → LocalState.js} +62 -28
  175. package/dist/signals/signals/Signal.d.ts +8 -7
  176. package/dist/signals/signals/Signal.js +4 -1
  177. package/dist/signals/signals/State.d.ts +4 -5
  178. package/dist/signals/signals/State.js +23 -9
  179. package/dist/signals/signals/index.d.ts +5 -5
  180. package/dist/signals/signals/index.js +5 -6
  181. package/dist/signals/types/SignalOptions.d.ts +16 -0
  182. package/dist/signals/types/SignalOptions.js +1 -0
  183. package/dist/signals/types/index.d.ts +3 -1
  184. package/dist/signals/types/index.js +3 -1
  185. package/dist/signals/types/normalizeSignalOptions.d.ts +2 -0
  186. package/dist/signals/types/normalizeSignalOptions.js +10 -0
  187. package/dist/signals/types/signals.types.d.ts +6 -2
  188. package/docs/CHANGELOG.md +111 -32
  189. package/docs/CONTRIBUTING.md +230 -0
  190. package/docs/contributing/ai-assisted-development.md +47 -0
  191. package/docs/contributing/query-v2/README.md +379 -0
  192. package/docs/{release → contributing/release}/README.md +59 -59
  193. package/docs/devtools/README.md +228 -228
  194. package/docs/migrations/0.5.0.md +58 -58
  195. package/docs/migrations/query-v2.md +171 -0
  196. package/docs/options/README.md +92 -90
  197. package/docs/query/README.md +575 -571
  198. package/docs/query-v2/README.md +280 -0
  199. package/docs/query-v2/api-reference.md +235 -0
  200. package/docs/query-v2/optimistic-updates.md +148 -0
  201. package/docs/query-v2/ssr.md +130 -0
  202. package/docs/signals/README.md +300 -295
  203. package/docs/usage/react/README.md +309 -307
  204. package/package.json +86 -63
  205. package/dist/query/core/Opertation/Operation.d.ts +0 -35
  206. package/dist/query/core/Opertation/OperationAgent.d.ts +0 -19
  207. package/dist/signals/signals/LocalSignal.d.ts +0 -32
@@ -1,7 +1,8 @@
1
- import { distinctUntilChanged, ReplaySubject, share, map, finalize } from "rxjs";
1
+ import { distinctUntilChanged, finalize, map, ReplaySubject, share } from "rxjs";
2
+ import { normalizeSignalOptions } from "../../signals/types";
2
3
  import { ComputeCache, DependencyTracker } from "../base";
3
4
  import { Effect } from "./Effect";
4
- import { Signal } from "./Signal";
5
+ import { State } from "./State";
5
6
  export class Computed {
6
7
  _computeFn;
7
8
  _state$;
@@ -13,12 +14,19 @@ export class Computed {
13
14
  _computeCache = new ComputeCache();
14
15
  constructor(_computeFn, options) {
15
16
  this._computeFn = _computeFn;
16
- const lsOptions = {
17
- base: Computed.name,
18
- ...(typeof options === 'string' ? { name: options } : options),
19
- _skipValues: [Computed._EMPTY],
17
+ const opts = normalizeSignalOptions(options);
18
+ const stateOptions = {
19
+ key: opts.key,
20
+ name: opts.name,
21
+ base: opts.base ?? Computed.name,
22
+ isDisabled: opts.isDisabled,
23
+ beforeDevtoolsPush: (value, push) => {
24
+ if (value !== Computed._EMPTY) {
25
+ push(value);
26
+ }
27
+ },
20
28
  };
21
- this._state$ = Signal.state(Computed._EMPTY, lsOptions);
29
+ this._state$ = State.create(Computed._EMPTY, stateOptions);
22
30
  this.obs = this._state$.obs.pipe(map((value) => {
23
31
  if (value === Computed._EMPTY) {
24
32
  return this._start();
@@ -36,7 +44,7 @@ export class Computed {
36
44
  DependencyTracker.track({
37
45
  getRang: () => {
38
46
  if (!this._effect) {
39
- throw new Error('Effect in not started. Possibly maximum call stack size exceeded.');
47
+ throw new Error("Effect in not started. Possibly maximum call stack size exceeded.");
40
48
  }
41
49
  return this._effect._getRang();
42
50
  },
@@ -65,7 +73,7 @@ export class Computed {
65
73
  });
66
74
  this._computeCache.clear();
67
75
  if (initialValue === Computed._EMPTY) {
68
- throw new Error('Computed value is not initialized');
76
+ throw new Error("Computed value is not initialized");
69
77
  }
70
78
  return initialValue;
71
79
  }
@@ -77,7 +85,7 @@ export class Computed {
77
85
  this._state$.set(Computed._EMPTY);
78
86
  }
79
87
  // === static ===
80
- static _EMPTY = Symbol('empty');
88
+ static _EMPTY = Symbol("empty");
81
89
  static create(computeFn, options) {
82
90
  const lc = new Computed(computeFn, options);
83
91
  function computedFn() {
@@ -15,6 +15,7 @@ export class Effect {
15
15
  this._rang = 0;
16
16
  const legacySubscriptions = this._subscriptions;
17
17
  this._subscriptions = new Map();
18
+ // eslint-disable-next-line prefer-const -- assigned after closure capture
18
19
  let scheduler;
19
20
  // Стабильная функция для планирования выполнения эффекта
20
21
  const scheduledFn = () => {
@@ -52,7 +53,7 @@ export class Effect {
52
53
  stopTracking();
53
54
  isTrackedContext = false;
54
55
  // Сохраняем teardown функцию, если она была возвращена
55
- if (typeof optionalTeardown === 'function') {
56
+ if (typeof optionalTeardown === "function") {
56
57
  this._teardown = optionalTeardown;
57
58
  }
58
59
  scheduler = Batcher.scheduler(this._rang);
@@ -0,0 +1,44 @@
1
+ import { Observable } from "rxjs";
2
+ import { ZodType } from "zod/v4";
3
+ import { type SignalOptionsOrKey, type StatefulSignalFn } from "../../signals/types";
4
+ type StorageLike = {
5
+ getItem(key: string): string | null;
6
+ setItem(key: string, value: string): void;
7
+ removeItem(key: string): void;
8
+ };
9
+ type Options<T> = {
10
+ zodSchema?: ZodType<T>;
11
+ key: string;
12
+ userId?: string;
13
+ /**
14
+ * @deprecated use checkEffect instead
15
+ */
16
+ validator$?: Observable<(value: T) => boolean>;
17
+ checkEffect?: (value: T) => boolean;
18
+ driver?: StorageLike;
19
+ defaultValue: T;
20
+ devtoolsOptions?: SignalOptionsOrKey;
21
+ };
22
+ export declare class LocalState<T = string | null | number | undefined> {
23
+ private _state$;
24
+ private _computed;
25
+ private readonly _options;
26
+ readonly obs: Observable<T>;
27
+ private get _driver();
28
+ constructor(options: Options<T>);
29
+ set(value: T): void;
30
+ peek(): T;
31
+ get(): T;
32
+ clear(): void;
33
+ private _getStorageValue;
34
+ private _setStorageValue;
35
+ private _deleteStorageValue;
36
+ static KEY_PREFIX: string;
37
+ static DEFAULT_DRIVER: Storage;
38
+ static create<T = string | null | number | undefined>(options: Options<T>): StatefulSignalFn<T>;
39
+ }
40
+ /**
41
+ * @deprecated use LocalState instead
42
+ */
43
+ export declare const LocalSignal: typeof LocalState;
44
+ export {};
@@ -1,15 +1,19 @@
1
1
  import { z } from "zod/v4";
2
2
  import { signalize } from "../operators";
3
3
  import { Computed } from "./Computed";
4
- import { State } from "@/signals/signals/State";
5
- const NONE = Symbol('NONE');
6
- export class LocalSignal {
4
+ import { State } from "./State";
5
+ const NONE = Symbol("NONE");
6
+ export class LocalState {
7
7
  _state$;
8
8
  _computed;
9
9
  _options;
10
10
  obs;
11
+ get _driver() {
12
+ return this._options.driver || LocalState.DEFAULT_DRIVER;
13
+ }
11
14
  constructor(options) {
12
- let initialValue = LocalSignal._getStorageValue(options);
15
+ this._options = options;
16
+ let initialValue = this._getStorageValue(options);
13
17
  if (initialValue === NONE) {
14
18
  initialValue = options.defaultValue;
15
19
  }
@@ -29,10 +33,9 @@ export class LocalSignal {
29
33
  return value;
30
34
  }, options.devtoolsOptions);
31
35
  this.obs = this._computed.obs;
32
- this._options = options;
33
36
  }
34
37
  set(value) {
35
- LocalSignal._setStorageValue(this._options, value);
38
+ this._setStorageValue(this._options, value);
36
39
  this._state$.set(value);
37
40
  }
38
41
  peek() {
@@ -41,23 +44,13 @@ export class LocalSignal {
41
44
  get() {
42
45
  return this._computed.get();
43
46
  }
44
- // === static ===
45
- static KEY_PREFIX = '__LSValue__';
46
- static DRIVER = localStorage;
47
- static create(options) {
48
- const localSignal = new LocalSignal(options);
49
- function signalFn() {
50
- return localSignal.get();
51
- }
52
- signalFn.peek = () => localSignal.peek();
53
- signalFn.get = () => localSignal.get();
54
- signalFn.set = (value) => localSignal.set(value);
55
- signalFn.obs = localSignal.obs;
56
- return signalFn;
47
+ clear() {
48
+ this._deleteStorageValue(this._options);
49
+ this._state$.set(this._options.defaultValue);
57
50
  }
58
- static _getStorageValue(options) {
59
- const storageKey = `${LocalSignal.KEY_PREFIX}:${options.key}`;
60
- const item = LocalSignal.DRIVER.getItem(storageKey);
51
+ _getStorageValue(options) {
52
+ const storageKey = `${LocalState.KEY_PREFIX}:${options.key}`;
53
+ const item = this._driver.getItem(storageKey);
61
54
  if (!item)
62
55
  return NONE;
63
56
  const schema = z.record(z.string(), options.zodSchema || z.any());
@@ -66,23 +59,64 @@ export class LocalSignal {
66
59
  console.warn(`Invalid value for key "${options.key}" in localStorage`, parsed.error);
67
60
  return NONE;
68
61
  }
69
- const subKey = options.userId ? `user:${options.userId}` : 'common';
62
+ const subKey = options.userId ? `user:${options.userId}` : "common";
70
63
  if (!(subKey in parsed.data)) {
71
64
  return NONE;
72
65
  }
73
66
  return parsed.data[subKey];
74
67
  }
75
- static _setStorageValue(options, value) {
76
- const storageKey = `${LocalSignal.KEY_PREFIX}:${options.key}`;
77
- const item = LocalSignal.DRIVER.getItem(storageKey) || '{}';
68
+ _setStorageValue(options, value) {
69
+ const storageKey = `${LocalState.KEY_PREFIX}:${options.key}`;
70
+ const item = this._driver.getItem(storageKey) || "{}";
78
71
  const schema = z.record(z.string(), options.zodSchema || z.any());
79
72
  const parsed = schema.safeParse(JSON.parse(item));
80
73
  let data = parsed.data ?? {};
81
74
  if (!parsed.success) {
82
75
  data = {};
83
76
  }
84
- const subKey = options.userId ? `user:${options.userId}` : 'common';
77
+ const subKey = options.userId ? `user:${options.userId}` : "common";
85
78
  data[subKey] = value;
86
- LocalSignal.DRIVER.setItem(storageKey, JSON.stringify(data));
79
+ this._driver.setItem(storageKey, JSON.stringify(data));
80
+ }
81
+ _deleteStorageValue(options) {
82
+ const storageKey = `${LocalState.KEY_PREFIX}:${options.key}`;
83
+ const item = this._driver.getItem(storageKey);
84
+ if (!item)
85
+ return;
86
+ const schema = z.record(z.string(), options.zodSchema || z.any());
87
+ const parsed = schema.safeParse(JSON.parse(item));
88
+ const data = parsed.data ?? {};
89
+ if (!parsed.success) {
90
+ this._driver.removeItem(storageKey);
91
+ return;
92
+ }
93
+ const subKey = options.userId ? `user:${options.userId}` : "common";
94
+ if (!data[subKey])
95
+ return;
96
+ delete data[subKey];
97
+ if (Object.keys(data).length === 0) {
98
+ this._driver.removeItem(storageKey);
99
+ return;
100
+ }
101
+ this._driver.setItem(storageKey, JSON.stringify(data));
102
+ }
103
+ // === static ===
104
+ static KEY_PREFIX = "__LSValue__";
105
+ static DEFAULT_DRIVER = localStorage;
106
+ static create(options) {
107
+ const localState = new LocalState(options);
108
+ function signalFn() {
109
+ return localState.get();
110
+ }
111
+ signalFn.peek = () => localState.peek();
112
+ signalFn.get = () => localState.get();
113
+ signalFn.set = (value) => localState.set(value);
114
+ signalFn.clear = () => localState.clear();
115
+ signalFn.obs = localState.obs;
116
+ return signalFn;
87
117
  }
88
118
  }
119
+ /**
120
+ * @deprecated use LocalState instead
121
+ */
122
+ export const LocalSignal = LocalState;
@@ -1,12 +1,13 @@
1
- import type { StateDevtoolsOptions } from "@/common/devtools";
2
- import { SignalFn } from "@/signals/types";
3
- import { Effect, State } from "@/signals/signals";
1
+ import type { SignalOptionsOrKey } from "../../signals/types";
2
+ import { SignalFn } from "../../signals/types";
3
+ import { Effect } from "./Effect";
4
+ import { State } from "./State";
4
5
  export declare class Signal<T> extends State<T> {
5
6
  /** @deprecated use `State` instead */
6
- constructor(initialValue: T, options?: StateDevtoolsOptions);
7
+ constructor(initialValue: T, options?: SignalOptionsOrKey<T>);
7
8
  /** @deprecated use `state` instead */
8
- static create<T>(initialValue: T, options?: StateDevtoolsOptions): SignalFn<T>;
9
- static state<T>(initialValue: T, options?: StateDevtoolsOptions): SignalFn<T>;
10
- static compute<T>(computeFn: () => T, options?: StateDevtoolsOptions): import("@/signals/types").ComputeFn<T>;
9
+ static create<T>(initialValue: T, options?: SignalOptionsOrKey<T>): SignalFn<T>;
10
+ static state<T>(initialValue: T, options?: SignalOptionsOrKey<T>): SignalFn<T>;
11
+ static compute<T>(computeFn: () => T, options?: SignalOptionsOrKey<T>): import("../../signals/types").ComputeFn<T>;
11
12
  static effect(effectFn: () => void): Effect;
12
13
  }
@@ -1,6 +1,9 @@
1
- import { Computed, Effect, State } from "@/signals/signals";
1
+ import { Computed } from "./Computed";
2
+ import { Effect } from "./Effect";
3
+ import { State } from "./State";
2
4
  export class Signal extends State {
3
5
  /** @deprecated use `State` instead */
6
+ // eslint-disable-next-line @typescript-eslint/no-useless-constructor
4
7
  constructor(initialValue, options) {
5
8
  super(initialValue, options);
6
9
  }
@@ -1,15 +1,14 @@
1
1
  import { BehaviorSubject } from "rxjs";
2
- import { StateDevtoolsOptions } from "@/common/devtools";
3
- import { SignalFn } from "@/signals/types";
2
+ import { SignalFn, SignalOptionsOrKey } from "../../signals/types";
4
3
  export declare class State<T> {
5
- private readonly _stateDevtools;
4
+ private readonly _hooks;
6
5
  private _rang;
7
6
  protected readonly bs$: BehaviorSubject<T>;
8
7
  readonly obs: import("rxjs").Observable<T>;
9
- constructor(initialValue: T, options?: StateDevtoolsOptions);
8
+ constructor(initialValue: T, options?: SignalOptionsOrKey<T>);
10
9
  peek(): T;
11
10
  set(value: T): void;
12
11
  get(): T;
13
12
  private static _finalizationRegistry;
14
- static create<T>(initialValue: T, options?: StateDevtoolsOptions): SignalFn<T>;
13
+ static create<T>(initialValue: T, options?: SignalOptionsOrKey<T>): SignalFn<T>;
15
14
  }
@@ -1,19 +1,27 @@
1
1
  import { BehaviorSubject } from "rxjs";
2
+ import { normalizeSignalOptions } from "../../signals/types";
2
3
  import { Batcher, DependencyTracker, Devtools } from "../base";
3
4
  export class State {
4
- _stateDevtools;
5
+ _hooks;
5
6
  _rang = 0;
6
7
  bs$;
7
8
  obs;
8
9
  constructor(initialValue, options) {
9
10
  this.bs$ = new BehaviorSubject(initialValue);
10
11
  this.obs = this.bs$.asObservable();
11
- this._stateDevtools = Devtools.createState(initialValue, {
12
- base: State.name,
13
- ...(typeof options === 'string' ? { name: options } : options)
12
+ const opts = normalizeSignalOptions(options);
13
+ const hooks = [];
14
+ const devtoolsHook = Devtools.createSignalHooks(initialValue, {
15
+ ...opts,
16
+ base: opts.base ?? State.name,
14
17
  });
15
- if (this._stateDevtools) {
16
- State._finalizationRegistry.register(this, this._stateDevtools);
18
+ if (devtoolsHook)
19
+ hooks.push(devtoolsHook);
20
+ if (opts.hooks)
21
+ hooks.push(...opts.hooks);
22
+ this._hooks = hooks.length > 0 ? hooks : null;
23
+ if (this._hooks) {
24
+ State._finalizationRegistry.register(this, this._hooks);
17
25
  }
18
26
  }
19
27
  peek() {
@@ -24,7 +32,11 @@ export class State {
24
32
  return;
25
33
  }
26
34
  Batcher.run(() => {
27
- this._stateDevtools?.(value);
35
+ if (this._hooks) {
36
+ for (const hook of this._hooks) {
37
+ hook.onChange?.(value);
38
+ }
39
+ }
28
40
  this.bs$.next(value);
29
41
  });
30
42
  }
@@ -37,8 +49,10 @@ export class State {
37
49
  return this.bs$.getValue();
38
50
  }
39
51
  // === static ===
40
- static _finalizationRegistry = new FinalizationRegistry((heldValue) => {
41
- heldValue('$COMPLETED');
52
+ static _finalizationRegistry = new FinalizationRegistry((hooks) => {
53
+ for (const hook of hooks) {
54
+ hook.onDispose?.();
55
+ }
42
56
  });
43
57
  static create(initialValue, options) {
44
58
  const ls = new State(initialValue, options);
@@ -1,5 +1,5 @@
1
- export * from './State';
2
- export * from './Computed';
3
- export * from './Effect';
4
- export * from './Signal';
5
- export * from './LocalSignal';
1
+ export * from "./State";
2
+ export * from "./Computed";
3
+ export * from "./Effect";
4
+ export * from "./LocalState";
5
+ export * from "./Signal";
@@ -1,6 +1,5 @@
1
- // Порядок важен:
2
- export * from './State';
3
- export * from './Computed';
4
- export * from './Effect';
5
- export * from './Signal';
6
- export * from './LocalSignal';
1
+ export * from "./State";
2
+ export * from "./Computed";
3
+ export * from "./Effect";
4
+ export * from "./LocalState";
5
+ export * from "./Signal";
@@ -0,0 +1,16 @@
1
+ export interface SignalLifecycleHook<T = any> {
2
+ onInit?: (value: T) => void;
3
+ onChange?: (newValue: T) => void;
4
+ onDispose?: () => void;
5
+ }
6
+ export type TBeforeDevtoolsPushFn<T = any> = (newValue: T, push: (v: T) => void) => void;
7
+ export interface SignalOptions<T = any> {
8
+ key?: string;
9
+ /** @deprecated use key */
10
+ name?: string;
11
+ base?: string;
12
+ isDisabled?: boolean;
13
+ beforeDevtoolsPush?: TBeforeDevtoolsPushFn<T>;
14
+ hooks?: SignalLifecycleHook<T>[];
15
+ }
16
+ export type SignalOptionsOrKey<T = any> = SignalOptions<T> | string;
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1,3 @@
1
- export * from './signals.types';
1
+ export * from "./signals.types";
2
+ export * from "./SignalOptions";
3
+ export * from "./normalizeSignalOptions";
@@ -1 +1,3 @@
1
- export * from './signals.types';
1
+ export * from "./signals.types";
2
+ export * from "./SignalOptions";
3
+ export * from "./normalizeSignalOptions";
@@ -0,0 +1,2 @@
1
+ import { SignalOptions, SignalOptionsOrKey } from "./SignalOptions";
2
+ export declare function normalizeSignalOptions<T>(options?: SignalOptionsOrKey<T>): SignalOptions<T>;
@@ -0,0 +1,10 @@
1
+ export function normalizeSignalOptions(options) {
2
+ if (!options)
3
+ return {};
4
+ if (typeof options === "string")
5
+ return { key: options };
6
+ if (options.name && !options.key) {
7
+ return { ...options, key: options.name };
8
+ }
9
+ return options;
10
+ }
@@ -10,7 +10,11 @@ export interface ReadableSignalFnLike<T> extends ReadableSignalLike<T> {
10
10
  export interface WriteableSignalLike<T> {
11
11
  set(value: T): void;
12
12
  }
13
- export interface SignalFn<T> extends ReadableSignalFnLike<T>, WriteableSignalLike<T> {
13
+ export interface ClearableSignalLike<_T> {
14
+ clear(): void;
15
+ }
16
+ export interface StatefulSignalFn<T> extends ReadableSignalFnLike<T>, WriteableSignalLike<T>, ClearableSignalLike<T> {
14
17
  }
15
- export interface ComputeFn<T> extends ReadableSignalFnLike<T> {
18
+ export interface SignalFn<T> extends ReadableSignalFnLike<T>, WriteableSignalLike<T> {
16
19
  }
20
+ export type ComputeFn<T> = ReadableSignalFnLike<T>;
package/docs/CHANGELOG.md CHANGED
@@ -1,32 +1,111 @@
1
- # CHANGELOG
2
-
3
- ## Что нового в 0.5.0
4
-
5
- [Гайд по миграции](./migrations/0.5.0.md)
6
-
7
- ### RxSignals
8
-
9
- - **Функциональный API (рекомендуется)**: `Signal.create()`, `Signal.compute()`, `Signal.effect()`
10
- - **Cleanup функции в Effect**: поддержка возврата teardown функции
11
- - **Без complete()**: теперь нет необходимости вызывать `complete()` для Signal и Computed
12
- - **Ленивый Computed**: вычисление только при наличии подписок
13
-
14
- ### RxQuery
15
-
16
- - **Расширенные состояния**: `isInitialLoading`, `isReloading`, `isLocked`
17
- - **ResourceRef API**: низкоуровневый доступ к кэшу с поддержкой транзакций
18
- - **Patch транзакции**: изменения с возможностью commit/abort
19
- - **Lifecycle хуки**: `onCacheEntryAdded`, `onQueryStarted`
20
- - **resetAllQueriesCache()**: сброс всего кэша
21
-
22
- ### React
23
-
24
- - **useResourceRef**: хук для работы с ResourceRef
25
-
26
- ### Devtools
27
-
28
- - **BatchStrategy**: настройка стратегии обновлений (`'sync'`, `'microtask'`, `'task'`)
29
-
30
- ### Конфигурация
31
-
32
- - **DefaultOptions**: расширенная конфигурация (`onQueryError`, `getScopeName`)
1
+ # CHANGELOG
2
+
3
+ ## [Unreleased]
4
+
5
+ ### Fixed
6
+ - Fixed `useResourceRef` memoization for object arguments — ref no longer recreated every render
7
+ - Fixed missing type exports from `src/query/` — consumers can now import `ResourceDefinition`, `CommandDefinition`, etc.
8
+
9
+ ### Changed
10
+ - `ResourceRefInstanse` renamed to `ResourceRefInstance` (deprecated alias preserved)
11
+ - `FrowardInfo` renamed to `ForwardInfo` (internal type)
12
+ - `Opertation/` directory renamed to `Operation/`
13
+ - Replaced `any` types with proper types in `useResourceAgent` and `ResourceDuplicator`
14
+
15
+ ### Added
16
+ - Unit tests for query core modules
17
+ - Smoke tests for React hooks
18
+ - Integration tests for query exports
19
+
20
+ ### Deprecated
21
+ - `ResourceRefInstanse` — use `ResourceRefInstance` (will be removed in v0.6.0)
22
+
23
+
24
+ ## [0.5.3-rc.2] 2026-02-23
25
+
26
+ ### Added
27
+
28
+ - `createCommand()` создание команды (мутации/действия), заменяет `createOperation()`
29
+ - `useCommandAgent()` — React-хук для работы с командой
30
+ - `LocalState` — замена `LocalSignal` с новыми возможностями
31
+ - `LocalState.clear()` — метод удаления значения из хранилища и сброса к значению по умолчанию
32
+ - Опция `driver` для `LocalState` — возможность подключить кастомное хранилище (вместо `localStorage`)
33
+
34
+ ### Changed
35
+
36
+ - `LocalSignal` переименован в `LocalState`
37
+ - Все вызовы `Signal.create()` в кодовой базе заменены на `Signal.state()` (внутренний рефакторинг)
38
+
39
+ ### Deprecated
40
+
41
+ - `createOperation()` → используйте `createCommand()` (будет удалён в v0.6.0)
42
+ - `useOperationAgent()` → используйте `useCommandAgent()` (будет удалён в v0.6.0)
43
+ - Все Operation-типы переименованы в Command-типы: `OperationDefinition` → `CommandDefinition`, `OperationInstance` → `CommandInstance`, `OperationCreateOptions` → `CommandCreateOptions`, `OperationCreateFn` → `CommandCreateFn`, `OperationQueryState` → `CommandQueryState`
44
+ - `OperationAgentInstanse` (с опечаткой) → `CommandAgentInstance` — исправлена опечатка в имени типа
45
+ - `LocalSignal` → используйте `LocalState`
46
+
47
+ ### Fixed
48
+
49
+ - Исправления в документации и демо-примерах
50
+
51
+ ## [0.5.3-rc.1] — 2026-02-22
52
+
53
+ ### Added
54
+
55
+ - Новый примитив **State** — замена `Signal` с идентичным API
56
+ - `Signal.state()` — рекомендуемый статический метод создания сигнала
57
+
58
+ ### Deprecated
59
+
60
+ - `Signal` помечен как `@deprecated` — используйте `State` вместо него
61
+ - `Signal.create()` помечен как `@deprecated` — используйте `Signal.state()` / `State.create()`
62
+
63
+ ## [0.5.2] — 2025-12-19
64
+
65
+ ### Fixed
66
+
67
+ - Исправлена работа `SKIP_TOKEN`
68
+
69
+ ## [0.5.1] — 2025-12-19
70
+
71
+ ### Fixed
72
+
73
+ - Исправление типизации
74
+
75
+ ## [0.5.0] — 2025-12-18
76
+
77
+ [Гайд по миграции с 0.4.x](./migrations/0.5.0.md)
78
+
79
+ ### Breaking Changes
80
+
81
+ - Удалены хуки `useObservable` и `useSyncObservable`
82
+ - Сигналы больше не наследуют `Observable` — используйте `.obs` для подписки
83
+ - Удалены `.value`, `.getValue()`, `.next()` — заменены на `signal()`, `.get()`, `.set()`
84
+ - Нет необходимости вызывать `complete()` для Signal и Computed
85
+
86
+ ### Added
87
+
88
+ #### Signals
89
+ - **Функциональный API**: `Signal.create()`, `Signal.compute()`, `Signal.effect()`
90
+ - **Ленивый Computed**: вычисление только при наличии подписок
91
+ - Cleanup-функции в `Effect` (возврат teardown)
92
+
93
+ #### Query
94
+ - **Расширенные состояния**: `isInitialLoading`, `isReloading`, `isLocked`
95
+ - **ResourceRef API**: низкоуровневый доступ к кэшу с поддержкой транзакций (patch с commit/abort)
96
+ - **Lifecycle хуки**: `onCacheEntryAdded`, `onQueryStarted`
97
+ - `resetAllQueriesCache()` — сброс всего кэша
98
+
99
+ #### React
100
+ - `useResourceRef` — хук для работы с ResourceRef
101
+
102
+ ### Changed
103
+
104
+ - **BatchStrategy**: настройка стратегии обновлений (`'sync'`, `'microtask'`, `'task'`)
105
+ - **DefaultOptions**: расширенная конфигурация (`onQueryError`, `getScopeName`)
106
+
107
+ [0.5.3-rc.2]: https://github.com/fozy-labs/rx-toolkit/compare/v0.5.3-rc.1...v0.5.3-rc.2
108
+ [0.5.3-rc.1]: https://github.com/fozy-labs/rx-toolkit/compare/v0.5.2...v0.5.3-rc.1
109
+ [0.5.2]: https://github.com/fozy-labs/rx-toolkit/compare/v0.5.1...v0.5.2
110
+ [0.5.1]: https://github.com/fozy-labs/rx-toolkit/compare/v0.5.0...v0.5.1
111
+ [0.5.0]: https://github.com/fozy-labs/rx-toolkit/compare/v0.4.18...v0.5.0