@oscarpalmer/mora 0.22.0 → 0.24.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 (44) hide show
  1. package/dist/batch.js +8 -9
  2. package/dist/constants.js +36 -0
  3. package/dist/effect.js +7 -8
  4. package/dist/helpers/is.js +9 -19
  5. package/dist/helpers/proxy.js +12 -7
  6. package/dist/helpers/value.js +7 -8
  7. package/dist/index.js +1 -1
  8. package/dist/models.js +0 -0
  9. package/dist/mora.full.js +248 -148
  10. package/dist/subscription.js +7 -5
  11. package/dist/value/array.js +27 -29
  12. package/dist/value/computed.js +18 -21
  13. package/dist/value/reactive.js +2 -1
  14. package/dist/value/signal.js +2 -2
  15. package/dist/value/store.js +12 -3
  16. package/package.json +9 -11
  17. package/src/batch.ts +11 -13
  18. package/src/constants.ts +53 -0
  19. package/src/effect.ts +12 -17
  20. package/src/helpers/is.ts +45 -22
  21. package/src/helpers/proxy.ts +62 -39
  22. package/src/helpers/value.ts +19 -14
  23. package/src/index.ts +1 -0
  24. package/src/models.ts +66 -0
  25. package/src/subscription.ts +14 -16
  26. package/src/value/array.ts +92 -55
  27. package/src/value/computed.ts +29 -39
  28. package/src/value/reactive.ts +11 -25
  29. package/src/value/signal.ts +9 -3
  30. package/src/value/store.ts +55 -13
  31. package/types/batch.d.ts +3 -5
  32. package/types/constants.d.ts +16 -0
  33. package/types/effect.d.ts +2 -1
  34. package/types/helpers/is.d.ts +23 -10
  35. package/types/helpers/proxy.d.ts +4 -3
  36. package/types/helpers/value.d.ts +1 -1
  37. package/types/index.d.ts +1 -0
  38. package/types/models.d.ts +56 -0
  39. package/types/subscription.d.ts +3 -6
  40. package/types/value/array.d.ts +48 -7
  41. package/types/value/computed.d.ts +2 -11
  42. package/types/value/reactive.d.ts +8 -17
  43. package/types/value/signal.d.ts +7 -1
  44. package/types/value/store.d.ts +38 -7
@@ -1,9 +1,15 @@
1
1
  var Subscription = class {
2
+ callback;
3
+ state;
2
4
  constructor(state, callback) {
3
5
  this.state = state;
4
6
  this.callback = callback;
5
7
  callback(state.value);
6
8
  }
9
+ destroy() {
10
+ this.callback = noop;
11
+ this.state = void 0;
12
+ }
7
13
  };
8
14
  function noop() {}
9
15
  function subscribe(state, callback) {
@@ -14,11 +20,7 @@ function subscribe(state, callback) {
14
20
  };
15
21
  }
16
22
  function unsubscribe(state, callback) {
17
- const subscription = state.subscriptions.get(callback);
23
+ state.subscriptions.get(callback)?.destroy();
18
24
  state.subscriptions.delete(callback);
19
- if (subscription != null) {
20
- subscription.callback = noop;
21
- subscription.state = void 0;
22
- }
23
25
  }
24
26
  export { Subscription, noop, subscribe, unsubscribe };
@@ -1,9 +1,9 @@
1
+ import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, PROPERTY_LENGTH } from "../constants.js";
1
2
  import { noop, subscribe } from "../subscription.js";
2
- import { arrayName } from "../helpers/is.js";
3
3
  import { Reactive } from "./reactive.js";
4
4
  import { computed } from "./computed.js";
5
5
  import { emitValue, equalArrays, getValue } from "../helpers/value.js";
6
- import { getReactiveValueInProxy, setValueInProxy } from "../helpers/proxy.js";
6
+ import { emityProxyValues, getReactiveValueInProxy, setValueInProxy } from "../helpers/proxy.js";
7
7
  import { signal } from "./signal.js";
8
8
  var ReactiveArray = class extends Reactive {
9
9
  #indiced = /* @__PURE__ */ new Map();
@@ -15,30 +15,38 @@ var ReactiveArray = class extends Reactive {
15
15
  if (typeof value === "number" && value >= 0 && value !== this.state.value.length) this.get().length = value;
16
16
  }
17
17
  constructor(value, options) {
18
- super(arrayName, new Proxy(value, {
19
- get: (target, property) => updateMethods.has(property) ? updateArray(property, target, this.state, this.#size) : Reflect.get(target, property),
20
- set: (target, property, value$1) => setValueInProxy(target, property, value$1, this.state, true, this.#size)
18
+ super(NAME_ARRAY, new Proxy(value, {
19
+ get: (target, property) => METHODS_UPDATE.has(property) ? updateArray(property, target, this.state, this.#size) : Reflect.get(target, property),
20
+ set: (target, property, value$1) => setValueInProxy({
21
+ property,
22
+ target,
23
+ value: value$1,
24
+ isArray: true,
25
+ state: this.state,
26
+ length: this.#size
27
+ })
21
28
  }), options);
22
29
  this.#size.set(value.length);
23
30
  }
24
31
  clear() {
25
32
  this.length = 0;
26
33
  }
27
- get(first) {
28
- if (typeof first === "number") return getReactiveValueInProxy(this, this.#indiced, first, true).get();
29
- if (first === "length") return this.length;
30
- return getValue(this.state);
31
- }
32
34
  filter(callback) {
33
35
  return computed(() => this.get().filter(callback));
34
36
  }
37
+ get(first) {
38
+ if (typeof first === "number") return getReactiveValueInProxy(this, this.#indiced, first, true).get();
39
+ return first === "length" ? this.length : getValue(this.state);
40
+ }
35
41
  map(callback) {
36
42
  return computed(() => this.get().map(callback));
37
43
  }
44
+ notify() {
45
+ emityProxyValues(this.state, this.#indiced);
46
+ }
38
47
  peek(value) {
39
- if (value === true) return this.#size.peek();
40
- if (typeof value === "number") return this.state.value.at(value);
41
- return [...this.state.value];
48
+ if (value === "length") return this.#size.peek();
49
+ return typeof value === "number" ? this.state.value.at(value) : [...this.state.value];
42
50
  }
43
51
  pop() {
44
52
  return this.state.value.pop();
@@ -49,7 +57,7 @@ var ReactiveArray = class extends Reactive {
49
57
  set(first, second) {
50
58
  if (first == null || Array.isArray(first)) this.state.value.splice(0, this.state.value.length, ...first ?? []);
51
59
  else if (first === "length") this.length = second;
52
- else if (typeof first === "number") this.state.value[first] = second;
60
+ else if (typeof first === "number" && !Number.isNaN(first)) setAtIndex(this.state.value, first, second);
53
61
  }
54
62
  shift() {
55
63
  return this.state.value.shift();
@@ -73,7 +81,7 @@ function array(value, options) {
73
81
  return new ReactiveArray(Array.isArray(value) ? value : [], options);
74
82
  }
75
83
  function updateArray(type, array$1, state, length) {
76
- const affectsLength = lengthAffectingMethods.has(type);
84
+ const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
77
85
  const previousArray = affectsLength ? [] : [...array$1];
78
86
  const previousLength = array$1.length;
79
87
  return (...args) => {
@@ -85,18 +93,8 @@ function updateArray(type, array$1, state, length) {
85
93
  return result;
86
94
  };
87
95
  }
88
- var lengthAffectingMethods = new Set([
89
- "pop",
90
- "push",
91
- "shift",
92
- "unshift"
93
- ]);
94
- var updateMethods = new Set([
95
- ...lengthAffectingMethods,
96
- "copyWithin",
97
- "fill",
98
- "reverse",
99
- "sort",
100
- "splice"
101
- ]);
96
+ function setAtIndex(array$1, index, value) {
97
+ const actual = index < 0 ? array$1.length + index : index;
98
+ if (actual > -1) array$1[actual] = value;
99
+ }
102
100
  export { ReactiveArray, array };
@@ -1,39 +1,36 @@
1
- import { computedName } from "../helpers/is.js";
2
- import { activeEffect, effect, runEffect } from "../effect.js";
3
- import { batchDepth, batchedHandlers } from "../batch.js";
1
+ import { ACTIVE, BATCH, NAME_COMPUTED } from "../constants.js";
2
+ import { effect, runEffect } from "../effect.js";
4
3
  import { Reactive } from "./reactive.js";
5
- let activeComputed;
6
4
  var Computed = class extends Reactive {
7
5
  effect = {
8
6
  dirty: true,
9
7
  instance: void 0
10
8
  };
11
9
  constructor(callback, options) {
12
- super(computedName, void 0, options);
10
+ super(NAME_COMPUTED, void 0, options);
13
11
  this.effect.instance = effect(() => {
14
- if (this.effect.dirty) {
15
- const previousComputed = activeComputed;
16
- activeComputed = this;
17
- const value = callback();
18
- activeComputed = previousComputed;
19
- if (!this.state.equal(this.state.value, value)) {
20
- this.state.value = value;
21
- for (const computed$1 of this.state.computeds) computed$1.effect.dirty = true;
22
- for (const effect$1 of this.state.effects) batchedHandlers.add(effect$1);
23
- for (const [, subscription] of this.state.subscriptions) subscription.callback(value);
24
- }
25
- this.effect.dirty = false;
12
+ if (!this.effect.dirty) return;
13
+ const previousComputed = ACTIVE.computed;
14
+ ACTIVE.computed = this;
15
+ const value = callback();
16
+ ACTIVE.computed = previousComputed;
17
+ if (!this.state.equal(this.state.value, value)) {
18
+ this.state.value = value;
19
+ for (const computed$1 of this.state.computeds) computed$1.effect.dirty = true;
20
+ for (const effect$1 of this.state.effects) BATCH.handlers.add(effect$1);
21
+ for (const [, subscription] of this.state.subscriptions) subscription.callback(value);
26
22
  }
23
+ this.effect.dirty = false;
27
24
  });
28
25
  }
29
26
  get() {
30
- if (activeComputed != null && this !== activeComputed) this.state.computeds.add(activeComputed);
31
- if (activeEffect != null && activeEffect !== this.effect.instance) this.state.effects.add(activeEffect);
32
- if (this.effect.dirty && batchDepth === 0) runEffect(this.effect.instance);
27
+ if (ACTIVE.computed != null && this !== ACTIVE.computed) this.state.computeds.add(ACTIVE.computed);
28
+ if (ACTIVE.effect != null && ACTIVE.effect !== this.effect.instance) this.state.effects.add(ACTIVE.effect);
29
+ if (this.effect.dirty && BATCH.depth === 0) runEffect(this.effect.instance);
33
30
  return this.state.value;
34
31
  }
35
32
  };
36
33
  function computed(callback, options) {
37
34
  return new Computed(callback, options);
38
35
  }
39
- export { Computed, activeComputed, computed };
36
+ export { Computed, computed };
@@ -1,3 +1,4 @@
1
+ import { NAME_MORA } from "../constants.js";
1
2
  import { subscribe, unsubscribe } from "../subscription.js";
2
3
  var Reactive = class {
3
4
  state = {
@@ -10,7 +11,7 @@ var Reactive = class {
10
11
  constructor(name, value, options) {
11
12
  this.state.value = value;
12
13
  if (typeof options === "object" && typeof options.equal === "function") this.state.equal = options.equal;
13
- Object.defineProperty(this, "$mora", { value: name });
14
+ Object.defineProperty(this, NAME_MORA, { value: name });
14
15
  }
15
16
  peek() {
16
17
  return this.state.value;
@@ -1,9 +1,9 @@
1
- import { signalName } from "../helpers/is.js";
1
+ import { NAME_SIGNAL } from "../constants.js";
2
2
  import { Reactive } from "./reactive.js";
3
3
  import { emitValue, getValue } from "../helpers/value.js";
4
4
  var Signal = class extends Reactive {
5
5
  constructor(value, options) {
6
- super(signalName, value, options);
6
+ super(NAME_SIGNAL, value, options);
7
7
  }
8
8
  get() {
9
9
  return getValue(this.state);
@@ -1,17 +1,26 @@
1
+ import { NAME_STORE } from "../constants.js";
1
2
  import { noop, subscribe } from "../subscription.js";
2
- import { storeName } from "../helpers/is.js";
3
3
  import { Reactive } from "./reactive.js";
4
4
  import { getValue } from "../helpers/value.js";
5
- import { getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.js";
5
+ import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.js";
6
6
  import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
7
7
  var Store = class extends Reactive {
8
8
  #keyed = /* @__PURE__ */ new Map();
9
9
  constructor(value, options) {
10
- super(storeName, new Proxy(value, { set: (target, property, value$1) => setValueInProxy(target, property, value$1, this.state, false) }), options);
10
+ super(NAME_STORE, new Proxy(value, { set: (target, property, value$1) => setValueInProxy({
11
+ target,
12
+ property,
13
+ value: value$1,
14
+ isArray: false,
15
+ state: this.state
16
+ }) }), options);
11
17
  }
12
18
  get(key) {
13
19
  return isKey(key) ? getReactiveValueInProxy(this, this.#keyed, key, false).get() : getValue(this.state);
14
20
  }
21
+ notify() {
22
+ emityProxyValues(this.state, this.#keyed);
23
+ }
15
24
  peek(key) {
16
25
  return isKey(key) ? this.state.value[key] : { ...this.state.value };
17
26
  }
package/package.json CHANGED
@@ -4,23 +4,21 @@
4
4
  "url": "https://oscarpalmer.se"
5
5
  },
6
6
  "dependencies": {
7
- "@oscarpalmer/atoms": "^0.102"
7
+ "@oscarpalmer/atoms": "^0.114"
8
8
  },
9
9
  "description": "Signals and stuff…",
10
10
  "devDependencies": {
11
- "@biomejs/biome": "^2.2",
11
+ "@biomejs/biome": "^2.3",
12
12
  "@rollup/plugin-node-resolve": "^16",
13
- "@rollup/plugin-typescript": "^12.1",
14
- "@types/node": "^24.3",
15
- "@vitest/coverage-istanbul": "^3.2",
16
- "dts-bundle-generator": "^9.5",
17
- "glob": "^11",
18
- "jsdom": "^26.1",
19
- "rollup": "^4.50",
13
+ "@rollup/plugin-typescript": "^12.3",
14
+ "@types/node": "^24.10",
15
+ "@vitest/coverage-istanbul": "^4",
16
+ "jsdom": "^27.2",
17
+ "rollup": "^4.53",
20
18
  "tslib": "^2.8",
21
19
  "typescript": "^5.9",
22
20
  "vite": "npm:rolldown-vite@latest",
23
- "vitest": "^3.2"
21
+ "vitest": "^4"
24
22
  },
25
23
  "exports": {
26
24
  "./package.json": "./package.json",
@@ -48,5 +46,5 @@
48
46
  },
49
47
  "type": "module",
50
48
  "types": "./types/index.d.ts",
51
- "version": "0.22.0"
49
+ "version": "0.24.0"
52
50
  }
package/src/batch.ts CHANGED
@@ -1,12 +1,12 @@
1
- import {type Effect, runEffect} from './effect';
1
+ import {BATCH} from './constants';
2
+ import {runEffect} from './effect';
2
3
  import {isEffect} from './helpers/is';
3
- import type {Subscription} from './subscription';
4
4
 
5
5
  export function flushHandlers(): void {
6
- while (batchDepth === 0 && batchedHandlers.size > 0) {
7
- const handlers = [...batchedHandlers];
6
+ while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
7
+ const handlers = [...BATCH.handlers];
8
8
 
9
- batchedHandlers.clear();
9
+ BATCH.handlers.clear();
10
10
 
11
11
  for (const handler of handlers) {
12
12
  if (isEffect(handler)) {
@@ -19,23 +19,21 @@ export function flushHandlers(): void {
19
19
  }
20
20
 
21
21
  /**
22
- * Start batching effects _(use `stopBatch` to flush and run batched effects)_
22
+ * Start batching effects
23
+ *
24
+ * _(Use {@link stopBatch} to flush and run batched effects)_
23
25
  */
24
26
  export function startBatch(): void {
25
- batchDepth += 1;
27
+ BATCH.depth += 1;
26
28
  }
27
29
 
28
30
  /**
29
31
  * Stop batching effects and flush _(run)_ them
30
32
  */
31
33
  export function stopBatch(): void {
32
- if (batchDepth > 0) {
33
- batchDepth -= 1;
34
+ if (BATCH.depth > 0) {
35
+ BATCH.depth -= 1;
34
36
  }
35
37
 
36
38
  flushHandlers();
37
39
  }
38
-
39
- export const batchedHandlers = new Set<Effect | Subscription>();
40
-
41
- export let batchDepth = 0;
@@ -0,0 +1,53 @@
1
+ import type {Effect} from './effect';
2
+ import type {Active, Batch} from './models';
3
+ import type {Subscription} from './subscription';
4
+
5
+ export const ACTIVE: Active = {};
6
+
7
+ export const ARRAY_THRESHOLD = 100;
8
+
9
+ export const ARRAY_OFFSET = 25;
10
+
11
+ export const ARRAY_PEEK = 10;
12
+
13
+ export const BATCH: Batch = {
14
+ depth: 0,
15
+ handlers: new Set<Effect | Subscription>(),
16
+ };
17
+
18
+ export const METHODS_AFFECTING_LENGTH: Set<string> = new Set<string>([
19
+ 'pop',
20
+ 'push',
21
+ 'shift',
22
+ 'unshift',
23
+ ]);
24
+
25
+ export const METHODS_UPDATE: Set<string> = new Set<string>([
26
+ ...METHODS_AFFECTING_LENGTH,
27
+ 'copyWithin',
28
+ 'fill',
29
+ 'reverse',
30
+ 'sort',
31
+ 'splice',
32
+ ]);
33
+
34
+ export const NAME_ARRAY = 'array';
35
+
36
+ export const NAME_COMPUTED = 'computed';
37
+
38
+ export const NAME_EFFECT = 'effect';
39
+
40
+ export const NAME_MORA = '$mora';
41
+
42
+ export const NAME_SIGNAL = 'signal';
43
+
44
+ export const NAME_STORE = 'store';
45
+
46
+ export const NAMES: Set<string> = new Set([
47
+ NAME_ARRAY,
48
+ NAME_COMPUTED,
49
+ NAME_SIGNAL,
50
+ NAME_STORE,
51
+ ]);
52
+
53
+ export const PROPERTY_LENGTH = 'length';
package/src/effect.ts CHANGED
@@ -1,13 +1,6 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {effectName} from './helpers/is';
3
-
4
- type EffectState = {
5
- callback: GenericCallback;
6
- };
7
-
8
- type InternalEffect = {
9
- state: EffectState;
10
- };
2
+ import {ACTIVE, NAME_EFFECT, NAME_MORA} from './constants';
3
+ import type {EffectState, InternalEffect} from './models';
11
4
 
12
5
  export class Effect {
13
6
  private declare readonly $mora: string;
@@ -15,8 +8,8 @@ export class Effect {
15
8
  private declare readonly state: EffectState;
16
9
 
17
10
  constructor(callback: GenericCallback) {
18
- Object.defineProperty(this, '$mora', {
19
- value: effectName,
11
+ Object.defineProperty(this, NAME_MORA, {
12
+ value: NAME_EFFECT,
20
13
  });
21
14
 
22
15
  this.state = {
@@ -28,22 +21,24 @@ export class Effect {
28
21
  }
29
22
 
30
23
  export function runEffect(effect: Effect): void {
31
- const previousEffect = activeEffect;
24
+ const previousEffect = ACTIVE.effect;
32
25
 
33
- activeEffect = effect;
26
+ ACTIVE.effect = effect;
34
27
 
35
28
  try {
36
29
  (effect as unknown as InternalEffect).state.callback();
37
30
  } finally {
38
- activeEffect = previousEffect;
31
+ ACTIVE.effect = previousEffect;
39
32
  }
40
33
  }
41
34
 
42
35
  /**
43
36
  * Create an effect
37
+ * @param callback Callback for handling signal effects
38
+ * @returns Effect
44
39
  */
45
40
  export function effect(callback: GenericCallback): Effect {
46
- return new Effect(callback);
41
+ return typeof callback === 'function'
42
+ ? new Effect(callback)
43
+ : (undefined as never);
47
44
  }
48
-
49
- export let activeEffect: Effect | undefined;
package/src/helpers/is.ts CHANGED
@@ -1,4 +1,13 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
2
+ import {
3
+ NAME_ARRAY,
4
+ NAME_COMPUTED,
5
+ NAME_EFFECT,
6
+ NAME_MORA,
7
+ NAME_SIGNAL,
8
+ NAME_STORE,
9
+ NAMES,
10
+ } from '../constants';
2
11
  import type {Effect} from '../effect';
3
12
  import type {ReactiveArray} from '../value/array';
4
13
  import type {Computed} from '../value/computed';
@@ -8,55 +17,69 @@ import type {Store} from '../value/store';
8
17
 
9
18
  /**
10
19
  * Is the value a reactive array?
20
+ * @param value Value to check
21
+ * @returns True if value is a {@link ReactiveArray}
11
22
  */
12
- export function isArray(value: unknown): value is ReactiveArray<unknown> {
13
- return isMora<ReactiveArray<unknown>>(value, arrayName);
23
+ export function isArray<Item>(value: unknown): value is ReactiveArray<Item> {
24
+ return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
14
25
  }
15
26
 
16
27
  /**
17
28
  * Is the value a computed signal?
29
+ * @param value Value to check
30
+ * @returns True if value is a {@link Computed}
18
31
  */
19
- export function isComputed(value: unknown): value is Computed<unknown> {
20
- return isMora<Computed<unknown>>(value, computedName);
32
+ export function isComputed<Value>(value: unknown): value is Computed<Value> {
33
+ return isMora<Computed<Value>>(value, NAME_COMPUTED);
21
34
  }
22
35
 
23
36
  /**
24
37
  * Is the value an effect?
38
+ * @param value Value to check
39
+ * @returns True if value is an {@link Effect}
25
40
  */
26
41
  export function isEffect(value: unknown): value is Effect {
27
- return isMora<Effect>(value, effectName);
42
+ return isMora<Effect>(value, NAME_EFFECT);
28
43
  }
29
44
 
30
45
  function isMora<T>(value: unknown, name: string | Set<string>): value is T {
31
46
  return (
32
47
  typeof value === 'object' &&
33
48
  value != null &&
34
- '$mora' in value &&
49
+ NAME_MORA in value &&
35
50
  (typeof name === 'string'
36
- ? (value as PlainObject).$mora === name
37
- : name.has((value as PlainObject).$mora as never))
51
+ ? (value as PlainObject)[NAME_MORA] === name
52
+ : name.has((value as PlainObject)[NAME_MORA] as never))
38
53
  );
39
54
  }
40
55
 
41
- export function isReactive(value: unknown): value is Reactive<unknown> {
42
- return isMora<Reactive<unknown>>(value, reactiveNames);
56
+ /**
57
+ * Is the value reactive?
58
+ * @param value Value to check
59
+ * @returns True if value is a {@link Reactive}
60
+ */
61
+ export function isReactive<Value, Equal = Value>(
62
+ value: unknown,
63
+ ): value is Reactive<Value, Equal> {
64
+ return isMora<Reactive<Value, Equal>>(value, NAMES);
43
65
  }
44
66
 
45
67
  /**
46
68
  * Is the value a signal?
69
+ * @param value Value to check
70
+ * @returns True if value is a {@link Signal}
47
71
  */
48
- export function isSignal(value: unknown): value is Signal<unknown> {
49
- return isMora<Signal<unknown>>(value, signalName);
72
+ export function isSignal<Value>(value: unknown): value is Signal<Value> {
73
+ return isMora<Signal<Value>>(value, NAME_SIGNAL);
50
74
  }
51
75
 
52
- export function isStore(value: unknown): value is Store<PlainObject> {
53
- return isMora<Reactive<unknown>>(value, storeName);
76
+ /**
77
+ * Is the value a reactive store?
78
+ * @param value Value to check
79
+ * @returns True if value is a {@link Store}
80
+ */
81
+ export function isStore<Value extends PlainObject>(
82
+ value: unknown,
83
+ ): value is Store<Value> {
84
+ return isMora<Store<Value>>(value, NAME_STORE);
54
85
  }
55
-
56
- export const arrayName = 'array';
57
- export const computedName = 'computed';
58
- export const effectName = 'effect';
59
- export const signalName = 'signal';
60
- export const storeName = 'store';
61
-
62
- const reactiveNames = new Set([arrayName, computedName, signalName, storeName]);