@oscarpalmer/mora 0.31.0 → 0.32.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 (54) hide show
  1. package/dist/batch.d.mts +4 -5
  2. package/dist/batch.mjs +4 -2
  3. package/dist/constants.d.mts +29 -19
  4. package/dist/constants.mjs +17 -2
  5. package/dist/effect.d.mts +4 -5
  6. package/dist/effect.mjs +2 -2
  7. package/dist/helpers/is.d.mts +14 -9
  8. package/dist/helpers/is.mjs +6 -0
  9. package/dist/helpers/proxy.d.mts +12 -10
  10. package/dist/helpers/proxy.mjs +32 -14
  11. package/dist/helpers/subscription.d.mts +8 -0
  12. package/dist/helpers/subscription.mjs +27 -0
  13. package/dist/helpers/value.d.mts +8 -6
  14. package/dist/helpers/value.mjs +30 -4
  15. package/dist/index.d.mts +3 -2
  16. package/dist/models.d.mts +45 -87
  17. package/dist/mora.full.mjs +461 -285
  18. package/dist/value/array.d.mts +4 -5
  19. package/dist/value/array.mjs +22 -69
  20. package/dist/value/computed.d.mts +3 -4
  21. package/dist/value/computed.mjs +14 -14
  22. package/dist/value/reactive.d.mts +2 -3
  23. package/dist/value/reactive.mjs +0 -1
  24. package/dist/value/readonly.d.mts +3 -5
  25. package/dist/value/readonly.mjs +15 -19
  26. package/dist/value/signal.d.mts +4 -5
  27. package/dist/value/signal.mjs +9 -22
  28. package/dist/value/store.d.mts +4 -5
  29. package/dist/value/store.mjs +16 -56
  30. package/package.json +6 -6
  31. package/src/batch.ts +15 -5
  32. package/src/constants.ts +32 -2
  33. package/src/effect.ts +6 -2
  34. package/src/helpers/is.ts +10 -0
  35. package/src/helpers/proxy.ts +99 -49
  36. package/src/helpers/subscription.ts +78 -0
  37. package/src/helpers/value.ts +69 -4
  38. package/src/index.ts +1 -1
  39. package/src/models.ts +38 -81
  40. package/src/value/array.ts +61 -150
  41. package/src/value/computed.ts +36 -13
  42. package/src/value/reactive.ts +8 -5
  43. package/src/value/readonly.ts +26 -25
  44. package/src/value/signal.ts +19 -22
  45. package/src/value/store.ts +35 -114
  46. package/types/constants.d.ts +1 -1
  47. package/types/index.d.ts +1 -1
  48. package/types/value/array.d.ts +1 -1
  49. package/types/value/computed.d.ts +0 -3
  50. package/types/value/signal.d.ts +1 -1
  51. package/types/value/store.d.ts +1 -1
  52. package/dist/subscription.d.mts +0 -7
  53. package/dist/subscription.mjs +0 -27
  54. package/src/subscription.ts +0 -45
@@ -7,7 +7,7 @@ import { ReactiveArray, ReactiveOptions } from "../models.mjs";
7
7
  * @param options Reactivity options
8
8
  * @returns Reactive array
9
9
  */
10
- declare function array<Item>(value: () => Item[] | Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
10
+ export declare function array<Item>(value: () => Item[] | Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
11
11
  /**
12
12
  * Create a reactive array from a promise
13
13
  *
@@ -15,7 +15,7 @@ declare function array<Item>(value: () => Item[] | Promise<Item[]>, options?: Re
15
15
  * @param options Reactivity options
16
16
  * @returns Reactive array
17
17
  */
18
- declare function array<Item>(value: Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
18
+ export declare function array<Item>(value: Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
19
19
  /**
20
20
  * Create a reactive array
21
21
  *
@@ -23,6 +23,5 @@ declare function array<Item>(value: Promise<Item[]>, options?: ReactiveOptions<I
23
23
  * @param options Reactivity options
24
24
  * @returns Reactive array
25
25
  */
26
- declare function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
27
- //#endregion
28
- export { array };
26
+ export declare function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
27
+ //#endregion
@@ -1,80 +1,54 @@
1
1
  import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, NAME_MORA } from "../constants.mjs";
2
- import { emitValue, equalArrays, getSimpleValue } from "../helpers/value.mjs";
3
- import { subscribe, unsubscribe } from "../subscription.mjs";
2
+ import { emitValue, equalArrays } from "../helpers/value.mjs";
3
+ import { subscribeToProxy } from "../helpers/subscription.mjs";
4
4
  import { reactive } from "./reactive.mjs";
5
5
  import { computed } from "./computed.mjs";
6
- import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
6
+ import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
7
7
  import { getReadonlyInstance } from "./readonly.mjs";
8
8
  import { signal } from "./signal.mjs";
9
9
  import { select } from "@oscarpalmer/atoms/array";
10
10
  import { filter } from "@oscarpalmer/atoms/array/filter";
11
- import { noop } from "@oscarpalmer/atoms/function";
12
- import { isPlainObject } from "@oscarpalmer/atoms/is";
13
11
  //#region src/value/array.ts
14
12
  function array(value, options) {
15
13
  const [rx, state] = reactive([], options);
16
- const indiced = /* @__PURE__ */ new Map();
14
+ const isArray = true;
17
15
  const length = signal(0);
16
+ const mapped = /* @__PURE__ */ new Map();
18
17
  const readonlies = {};
19
- let instance = {};
20
18
  state.value = new Proxy([], {
21
19
  get: (target, property) => METHODS_UPDATE.has(property) ? updateArray(property, target, state, length) : Reflect.get(target, property),
22
- set: (target, property, value) => setValueInProxy({
23
- length,
24
- property,
25
- state,
26
- target,
27
- value,
28
- isArray: true
29
- })
20
+ set: (target, property, value) => setValueInProxy(isArray, state, target, property, value, length)
30
21
  });
31
22
  function get(value) {
32
- return getArrayValue(instance, indiced, state, length, value);
23
+ return value === "length" ? length.get() : getValueInProxy(isArray, instance, state, mapped, value);
33
24
  }
34
25
  function set(first, second) {
35
- setProxyValue(true, state, isArrayValue, isArrayIndex, setArray, setAtIndex, first, second);
26
+ setProxyValue(true, state, setArrayValue, setAtIndex, first, second);
36
27
  }
37
- const handlers = {
28
+ const instance = {
38
29
  ...rx,
39
- get: (value) => get(value),
40
- peek: (first, second) => peekArrayValue(state, length, first, second),
41
- subscribe: (first, second) => {
42
- if (typeof first === "number" && typeof second === "function") return getReactiveValueInProxy(instance, indiced, first, true).subscribe(second);
43
- return typeof first === "function" ? subscribe(state, first) : noop;
44
- },
45
- unsubscribe: (first, second) => {
46
- if (typeof first === "number" && typeof second === "function") getReactiveValueInProxy(instance, indiced, first, true)?.unsubscribe(second);
47
- else if (typeof first === "function") unsubscribe(state, first);
48
- }
49
- };
50
- instance = {
51
- ...handlers,
52
- asReadonly: (frozen) => getReadonlyInstance(state, readonlies, handlers, frozen === true),
30
+ asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
53
31
  at: (index) => get(index),
54
32
  clear: () => {
55
33
  state.value.length = 0;
56
34
  },
57
35
  filter: (callback) => computed(() => filter(get(), callback)),
36
+ get: (value) => get(value),
58
37
  map: (callback) => computed(() => get().map(callback)),
59
- notify: () => {
60
- emityProxyValues(state, indiced);
61
- },
38
+ notify: () => emitProxyValues(state, mapped),
39
+ peek: (first, second) => peekArrayValue(state, length, first, second),
62
40
  pop: () => state.value.pop(),
63
41
  push: (...items) => state.value.push(...items),
64
42
  select: (filter, map) => computed(() => select(get(), filter, map)),
65
- set: (first, second) => {
66
- set(first, second);
67
- },
43
+ set: (first, second) => set(first, second),
68
44
  shift: () => state.value.shift(),
69
45
  splice: (from, to, ...items) => state.value.splice(from, to ?? state.value.length, ...items),
46
+ subscribe: (first, second, third) => subscribeToProxy(isArray, instance, state, mapped, first, second, third),
70
47
  unshift: (...items) => state.value.unshift(...items),
71
- update: (callback) => updateArrayValue(instance, state, callback)
48
+ update: (callback) => updateProxyValue(true, state, setArrayValue, setAtIndex, callback)
72
49
  };
73
50
  Object.defineProperties(instance, {
74
- [NAME_MORA]: {
75
- enumerable: false,
76
- value: NAME_ARRAY
77
- },
51
+ [NAME_MORA]: { value: NAME_ARRAY },
78
52
  length: {
79
53
  enumerable: true,
80
54
  get: () => length.get(),
@@ -84,31 +58,14 @@ function array(value, options) {
84
58
  set(value);
85
59
  return Object.freeze(instance);
86
60
  }
87
- function getArrayValue(instance, indiced, state, length, first) {
88
- if (typeof first === "number") return getReactiveValueInProxy(instance, indiced, first, true).get();
89
- return first === "length" ? length.get() : getSimpleValue(state);
90
- }
91
- function isArrayIndex(value) {
92
- return typeof value === "number";
93
- }
94
- function isArrayValue(value) {
95
- return value == null || Array.isArray(value);
96
- }
97
61
  function peekArrayValue(state, length, first, second) {
98
- if (first === "length") return length.peek();
99
- let value;
100
- if (typeof first === "number") value = state.value.at(first);
101
- else value = state.value;
102
- if (!(first === true || second === true)) return value;
103
- if (Array.isArray(value)) return value.slice();
104
- if (isPlainObject(value)) return { ...value };
105
- return value;
106
- }
107
- function setArray(state, value) {
108
- state.value.splice(0, state.value.length, ...value ?? []);
62
+ return first === "length" ? length.peek() : peekValueInProxy(true, state, first, second);
109
63
  }
110
64
  function setArrayLength(state, value) {
111
- if (typeof value === "number" && value >= 0 && value !== state.value.length) state.value.length = value;
65
+ if (typeof value === "number" && !Number.isNaN(value) && value >= 0 && value !== state.value.length) state.value.length = value;
66
+ }
67
+ function setArrayValue(state, value) {
68
+ state.value.splice(0, state.value.length, ...value ?? []);
112
69
  }
113
70
  function setAtIndex(state, index, value) {
114
71
  const actual = index < 0 ? state.value.length + index : index;
@@ -127,9 +84,5 @@ function updateArray(type, array, state, length) {
127
84
  return result;
128
85
  };
129
86
  }
130
- function updateArrayValue(instance, state, callback) {
131
- const updated = callback(state.value);
132
- if (updated == null || Array.isArray(updated)) instance.set(updated);
133
- }
134
87
  //#endregion
135
88
  export { array };
@@ -7,7 +7,6 @@ import { Computed, ComputedEffect, ReactiveOptions } from "../models.mjs";
7
7
  * @param options Reactivity options
8
8
  * @returns Computed value
9
9
  */
10
- declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
11
- declare function internalComputed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): [Computed<Value>, ComputedEffect];
12
- //#endregion
13
- export { computed, internalComputed };
10
+ export declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
11
+ export declare function internalComputed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): [Computed<Value>, ComputedEffect];
12
+ //#endregion
@@ -1,8 +1,8 @@
1
- import { ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA } from "../constants.mjs";
1
+ import { ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY } from "../constants.mjs";
2
2
  import { internalEffect, runEffect } from "../effect.mjs";
3
+ import { handleSimpleValue, peekSimpleValue } from "../helpers/value.mjs";
3
4
  import { flushHandlers } from "../batch.mjs";
4
- import { handleSimpleValue } from "../helpers/value.mjs";
5
- import { subscribe } from "../subscription.mjs";
5
+ import { subscribeToSignal } from "../helpers/subscription.mjs";
6
6
  import { reactive } from "./reactive.mjs";
7
7
  //#region src/value/computed.ts
8
8
  /**
@@ -16,21 +16,16 @@ function computed(callback, options) {
16
16
  return getComputed(callback, options)[0];
17
17
  }
18
18
  function getComputed(callback, options) {
19
+ if (typeof callback !== "function") throw new TypeError("Computed callback must be a function");
19
20
  const [rx, state] = reactive(void 0, options);
20
21
  let fx;
21
22
  const instance = {
22
23
  ...rx,
23
24
  get: () => getValue(state, fx),
24
- peek: () => state.value,
25
- subscribe: (callback) => subscribe(state, callback),
26
- unsubscribe: (callback) => {
27
- state.subscriptions.delete(callback);
28
- }
25
+ peek: (copy) => peekSimpleValue(state.value, copy === true),
26
+ subscribe: (callback, copy) => subscribeToSignal(state, callback, copy === true)
29
27
  };
30
- Object.defineProperty(instance, NAME_MORA, {
31
- enumerable: false,
32
- value: NAME_COMPUTED
33
- });
28
+ Object.defineProperty(instance, NAME_MORA, { value: NAME_COMPUTED });
34
29
  fx = getComputedEffect(state, callback);
35
30
  return [Object.freeze(instance), fx];
36
31
  }
@@ -64,8 +59,13 @@ function setAndEmit(state, value) {
64
59
  if (state.equal(state.value, value)) return;
65
60
  state.value = value;
66
61
  for (const computed of state.computeds) computed.dirty = true;
67
- for (const effect of state.effects) BATCH.handlers.add(effect);
68
- for (const [, subscription] of state.subscriptions) subscription.callback(value);
62
+ for (const effect of state.effects) BATCH.handlers.set(effect, effect);
63
+ for (const type of SUBSCRIPTION_TYPES) {
64
+ if (type === "frozen") continue;
65
+ const copy = SUBSCRIPTION_TYPES_COPY.has(type);
66
+ const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
67
+ if (subscriptions != null) for (const [, callback] of subscriptions) callback(peekSimpleValue(state.value, copy));
68
+ }
69
69
  flushHandlers();
70
70
  }
71
71
  //#endregion
@@ -1,5 +1,4 @@
1
1
  import { Reactive, ReactiveOptions, ReactiveState } from "../models.mjs";
2
2
  //#region src/value/reactive.d.ts
3
- declare function reactive<Value, Equal = Value>(value: Value, options?: ReactiveOptions<Equal>): [Reactive<Value>, ReactiveState<Value, Equal>];
4
- //#endregion
5
- export { reactive };
3
+ export declare function reactive<Value, Item = Value>(value: Value, options?: ReactiveOptions<Item>): [Reactive<Value>, ReactiveState<Value, Item>];
4
+ //#endregion
@@ -4,7 +4,6 @@ function reactive(value, options) {
4
4
  computeds: /* @__PURE__ */ new Set(),
5
5
  effects: /* @__PURE__ */ new Set(),
6
6
  equal: typeof options === "object" && typeof options?.equal === "function" ? options.equal : Object.is,
7
- subscriptions: /* @__PURE__ */ new Map(),
8
7
  value
9
8
  };
10
9
  return [{
@@ -1,7 +1,5 @@
1
1
  import { ReactiveState, ReadonlyFrozenSignal, ReadonlyInstances, ReadonlySignal } from "../models.mjs";
2
- import { GenericCallback } from "@oscarpalmer/atoms/models";
3
2
  //#region src/value/readonly.d.ts
4
- declare function getReadonlyInstance<Value>(state: ReactiveState<Value, never>, instances: ReadonlyInstances<Value>, handlers: Record<string, GenericCallback>, frozen: boolean): ReadonlySignal<Value>;
5
- declare function getReadonlySignal<Value>(state: ReactiveState<Value, never>, handlers: Record<string, GenericCallback>, frozen: boolean): ReadonlySignal<Value> | ReadonlyFrozenSignal<Value>;
6
- //#endregion
7
- export { getReadonlyInstance, getReadonlySignal };
3
+ export declare function getReadonlyInstance<Value>(state: ReactiveState<Value, never>, instances: ReadonlyInstances<Value>, frozen: boolean): ReadonlySignal<Value>;
4
+ export declare function getReadonlySignal<Value>(state: ReactiveState<Value, never>, frozen: boolean): ReadonlySignal<Value> | ReadonlyFrozenSignal<Value>;
5
+ //#endregion
@@ -1,23 +1,21 @@
1
- import { NAME_MORA, NAME_READONLY } from "../constants.mjs";
2
- import { getSimpleValue } from "../helpers/value.mjs";
3
- import { isPlainObject } from "@oscarpalmer/atoms/is";
1
+ import { NAME_MORA, NAME_READONLY, SUBSCRIPTION_TYPE_FROZEN, SUBSCRIPTION_TYPE_READONLY } from "../constants.mjs";
2
+ import { getFrozenValue, getSimpleValue, peekSimpleValue } from "../helpers/value.mjs";
3
+ import { subscribeToReactive } from "../helpers/subscription.mjs";
4
4
  //#region src/value/readonly.ts
5
- function getReadonlyInstance(state, instances, handlers, frozen) {
5
+ function getReadonlyInstance(state, instances, frozen) {
6
6
  const key = frozen ? "frozen" : "original";
7
- instances[key] ??= getReadonlySignal(state, handlers, frozen);
7
+ instances[key] ??= getReadonlySignal(state, frozen);
8
8
  return instances[key];
9
9
  }
10
- function getReadonlySignal(state, handlers, frozen) {
10
+ function getReadonlySignal(state, frozen) {
11
+ const type = frozen ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_READONLY;
11
12
  const instance = {
12
- ...handlers,
13
13
  get: () => getReadonlyValue(state, false, frozen),
14
- peek: () => getReadonlyValue(state, true, frozen)
14
+ peek: (copy) => getReadonlyValue(state, true, frozen, copy),
15
+ subscribe: (callback) => subscribeToReactive(type, state, callback)
15
16
  };
16
17
  Object.defineProperties(instance, {
17
- [NAME_MORA]: {
18
- enumerable: false,
19
- value: NAME_READONLY
20
- },
18
+ [NAME_MORA]: { value: NAME_READONLY },
21
19
  frozen: {
22
20
  enumerable: true,
23
21
  value: frozen
@@ -25,13 +23,11 @@ function getReadonlySignal(state, handlers, frozen) {
25
23
  });
26
24
  return Object.freeze(instance);
27
25
  }
28
- function getReadonlyValue(state, peek, frozen) {
29
- let value = peek ? state.value : getSimpleValue(state);
30
- if (!frozen) return value;
31
- if (Array.isArray(state.value)) value = [...state.value];
32
- else if (isPlainObject(state.value)) value = { ...state.value };
33
- else value = state.value;
34
- return Object.freeze(value);
26
+ function getReadonlyValue(state, peek, frozen, copy) {
27
+ let value;
28
+ if (peek) value = peekSimpleValue(state.value, copy === true);
29
+ else value = getSimpleValue(state);
30
+ return frozen ? getFrozenValue(value) : value;
35
31
  }
36
32
  //#endregion
37
33
  export { getReadonlyInstance, getReadonlySignal };
@@ -7,7 +7,7 @@ import { ReactiveOptions, Signal } from "../models.mjs";
7
7
  * @param options Reactivity options
8
8
  * @returns Reactive value
9
9
  */
10
- declare function signal<Value>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
10
+ export declare function signal<Value>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
11
11
  /**
12
12
  * Create a reactive value from a promise
13
13
  *
@@ -15,7 +15,7 @@ declare function signal<Value>(value: () => Value | Promise<Value>, options?: Re
15
15
  * @param options Reactivity options
16
16
  * @returns Reactive value
17
17
  */
18
- declare function signal<Value>(value: Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
18
+ export declare function signal<Value>(value: Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
19
19
  /**
20
20
  * Create a reactive value
21
21
  *
@@ -23,6 +23,5 @@ declare function signal<Value>(value: Promise<Value>, options?: ReactiveOptions<
23
23
  * @param options Reactivity options
24
24
  * @returns Reactive value
25
25
  */
26
- declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
27
- //#endregion
28
- export { signal };
26
+ export declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
27
+ //#endregion
@@ -1,6 +1,6 @@
1
1
  import { NAME_MORA, NAME_SIGNAL } from "../constants.mjs";
2
- import { emitValue, getSimpleValue, handleSimpleValue } from "../helpers/value.mjs";
3
- import { subscribe } from "../subscription.mjs";
2
+ import { emitValue, getSimpleValue, handleSimpleValue, peekSimpleValue, updateSimpleValue } from "../helpers/value.mjs";
3
+ import { subscribeToSignal } from "../helpers/subscription.mjs";
4
4
  import { reactive } from "./reactive.mjs";
5
5
  import { getReadonlyInstance } from "./readonly.mjs";
6
6
  //#region src/value/signal.ts
@@ -13,29 +13,16 @@ function setAndEmit(state, value) {
13
13
  function signal(value, options) {
14
14
  const [rx, state] = reactive(void 0, options);
15
15
  const readonlies = {};
16
- const handlers = {
17
- ...rx,
18
- subscribe: (callback) => subscribe(state, callback),
19
- unsubscribe: (callback) => {
20
- state.subscriptions.delete(callback);
21
- }
22
- };
23
16
  const instance = {
24
- ...handlers,
25
- asReadonly: (frozen) => getReadonlyInstance(state, readonlies, handlers, frozen === true),
17
+ ...rx,
18
+ asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
26
19
  get: () => getSimpleValue(state),
27
- peek: () => state.value,
28
- set: (value) => {
29
- handleSimpleValue(state, value, setAndEmit);
30
- },
31
- update: (callback) => {
32
- handleSimpleValue(state, callback(state.value), setAndEmit);
33
- }
20
+ peek: (copy) => peekSimpleValue(state.value, copy === true),
21
+ set: (value) => handleSimpleValue(state, value, setAndEmit),
22
+ subscribe: (callback, copy) => subscribeToSignal(state, callback, copy === true),
23
+ update: (callback) => updateSimpleValue(state, callback, setAndEmit)
34
24
  };
35
- Object.defineProperty(instance, NAME_MORA, {
36
- enumerable: false,
37
- value: NAME_SIGNAL
38
- });
25
+ Object.defineProperty(instance, NAME_MORA, { value: NAME_SIGNAL });
39
26
  handleSimpleValue(state, value, setAndEmit);
40
27
  return Object.freeze(instance);
41
28
  }
@@ -8,7 +8,7 @@ import { PlainObject } from "@oscarpalmer/atoms/models";
8
8
  * @param options Reactivity options
9
9
  * @returns Reactive store
10
10
  */
11
- declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
11
+ export declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
12
12
  /**
13
13
  * Create a reactive store from a promise
14
14
  *
@@ -16,7 +16,7 @@ declare function store<Value extends PlainObject>(value: () => Value | Promise<V
16
16
  * @param options Reactivity options
17
17
  * @returns Reactive store
18
18
  */
19
- declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
19
+ export declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
20
20
  /**
21
21
  * Create a reactive store
22
22
  *
@@ -24,6 +24,5 @@ declare function store<Value extends PlainObject>(value: Promise<Value>, options
24
24
  * @param options Reactivity options
25
25
  * @returns Reactive store
26
26
  */
27
- declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
28
- //#endregion
29
- export { store };
27
+ export declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
28
+ //#endregion
@@ -1,16 +1,14 @@
1
1
  import { NAME_MORA, NAME_STORE } from "../constants.mjs";
2
2
  import { startBatch, stopBatch } from "../batch.mjs";
3
- import { getSimpleValue } from "../helpers/value.mjs";
4
- import { noop, subscribe, unsubscribe } from "../subscription.mjs";
3
+ import { subscribeToProxy } from "../helpers/subscription.mjs";
5
4
  import { reactive } from "./reactive.mjs";
6
- import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
5
+ import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
7
6
  import { getReadonlyInstance } from "./readonly.mjs";
8
- import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
9
7
  //#region src/value/store.ts
10
- function isStoreObject(value) {
11
- return value == null || isPlainObject(value);
8
+ function setPropertyValue(state, key, value) {
9
+ state.value[key] = value;
12
10
  }
13
- function setObject(state, value) {
11
+ function setStoreValue(state, value) {
14
12
  startBatch();
15
13
  const actual = value ?? {};
16
14
  const proxy = state.value;
@@ -28,61 +26,23 @@ function setObject(state, value) {
28
26
  }
29
27
  stopBatch();
30
28
  }
31
- function peekStoreValue(state, first, second) {
32
- let value;
33
- if (isKey(first)) value = state.value[first];
34
- else value = state.value;
35
- if (!(first === true || second === true)) return value;
36
- if (Array.isArray(value)) return value.slice();
37
- if (isPlainObject(value)) return { ...value };
38
- return value;
39
- }
40
- function setProperty(state, key, value) {
41
- state.value[key] = value;
42
- }
43
29
  function store(value, options) {
44
30
  const [rx, state] = reactive(void 0, options);
31
+ const isArray = false;
45
32
  const keyed = /* @__PURE__ */ new Map();
46
33
  const readonlies = {};
47
- let instance = {};
48
- state.value = new Proxy({}, { set: (target, property, value) => setValueInProxy({
49
- target,
50
- property,
51
- state,
52
- value,
53
- isArray: false
54
- }) });
55
- const handlers = {
34
+ state.value = new Proxy({}, { set: (target, property, value) => setValueInProxy(isArray, state, target, property, value) });
35
+ const instance = {
56
36
  ...rx,
57
- get: (value) => isKey(value) ? getReactiveValueInProxy(instance, keyed, value, false).get() : getSimpleValue(state),
58
- peek: (first, second) => peekStoreValue(state, first, second),
59
- subscribe: (first, second) => {
60
- if (isKey(first) && typeof second === "function") return getReactiveValueInProxy(instance, keyed, first, false).subscribe(second);
61
- return typeof first === "function" ? subscribe(state, first) : noop;
62
- },
63
- unsubscribe: (first, second) => {
64
- if (isKey(first) && typeof second === "function") getReactiveValueInProxy(instance, keyed, first, false)?.unsubscribe(second);
65
- else if (typeof first === "function") unsubscribe(state, first);
66
- }
67
- };
68
- instance = {
69
- ...handlers,
70
- asReadonly: (frozen) => getReadonlyInstance(state, readonlies, handlers, frozen === true),
71
- notify() {
72
- emityProxyValues(state, keyed);
73
- },
74
- set: (first, second) => {
75
- setProxyValue(false, state, isStoreObject, isKey, setObject, setProperty, first, second);
76
- },
77
- update: (callback) => {
78
- const updated = callback(state.value);
79
- if (updated == null || isPlainObject(updated)) setObject(state, updated);
80
- }
37
+ asReadonly: (frozen) => getReadonlyInstance(state, readonlies, frozen === true),
38
+ get: (value) => getValueInProxy(isArray, instance, state, keyed, value),
39
+ notify: () => emitProxyValues(state, keyed),
40
+ peek: (first, second) => peekValueInProxy(isArray, state, first, second),
41
+ set: (first, second) => setProxyValue(isArray, state, setStoreValue, setPropertyValue, first, second),
42
+ subscribe: (first, second, third) => subscribeToProxy(isArray, instance, state, keyed, first, second, third),
43
+ update: (callback) => updateProxyValue(isArray, state, setStoreValue, setPropertyValue, callback)
81
44
  };
82
- Object.defineProperty(instance, NAME_MORA, {
83
- enumerable: false,
84
- value: NAME_STORE
85
- });
45
+ Object.defineProperty(instance, NAME_MORA, { value: NAME_STORE });
86
46
  instance.set(value);
87
47
  return Object.freeze(instance);
88
48
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "Signals and stuff…",
5
5
  "keywords": [
6
6
  "reactive",
@@ -39,17 +39,17 @@
39
39
  "test:leak": "vpx vp test run --detect-async-leaks --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@oscarpalmer/atoms": "^0.190"
42
+ "@oscarpalmer/atoms": "^0.202"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^26.2",
46
- "@vitest/coverage-istanbul": "4.1.10",
45
+ "@types/node": "^26.5",
46
+ "@vitest/coverage-istanbul": "^4.1",
47
47
  "jsdom": "^30",
48
- "tsdown": "^0.22",
48
+ "tsdown": "^0.23",
49
49
  "typescript": "^6",
50
50
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
51
51
  "vite-plus": "latest",
52
52
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
53
53
  },
54
- "packageManager": "npm@11.11.1"
54
+ "packageManager": "npm@11.19.0"
55
55
  }
package/src/batch.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import {BATCH} from './constants';
2
2
  import {runEffect} from './effect';
3
- import type {EffectState, Subscription} from './models';
3
+ import {getFrozenValue, peekSimpleValue} from './helpers/value';
4
+ import type {EffectState, StoredSubscription} from './models';
5
+
6
+ // #region Functions
4
7
 
5
8
  export function flushHandlers(): void {
6
9
  if (BATCH.flushing) {
@@ -17,10 +20,15 @@ export function flushHandlers(): void {
17
20
  BATCH.handlers.clear();
18
21
 
19
22
  for (let index = 0; index < length; index += 1) {
20
- const handler = handlers[index];
21
-
22
- if (typeof (handler as Subscription).destroy === 'function') {
23
- (handler as Subscription).callback((handler as Subscription).state.value);
23
+ const [, handler] = handlers[index];
24
+ const subscription = handler as StoredSubscription;
25
+
26
+ if (typeof subscription.frozen === 'boolean') {
27
+ subscription.callback(
28
+ subscription.frozen
29
+ ? getFrozenValue(subscription.state.value)
30
+ : peekSimpleValue(subscription.state.value, subscription.copy),
31
+ );
24
32
  } else {
25
33
  runEffect(handler as EffectState);
26
34
  }
@@ -50,3 +58,5 @@ export function stopBatch(): void {
50
58
 
51
59
  flushHandlers();
52
60
  }
61
+
62
+ // #endregion
package/src/constants.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type {Active, Batch, EffectState, Subscription} from './models';
1
+ import type {Active, Batch, SubscriptionType} from './models';
2
+
3
+ // #region Variables
2
4
 
3
5
  export const ACTIVE: Active = {};
4
6
 
@@ -11,7 +13,7 @@ export const ARRAY_PEEK = 10;
11
13
  export const BATCH: Batch = {
12
14
  depth: 0,
13
15
  flushing: false,
14
- handlers: new Set<EffectState | Subscription>(),
16
+ handlers: new Map(),
15
17
  };
16
18
 
17
19
  export const METHODS_AFFECTING_LENGTH = new Set<string>(['pop', 'push', 'shift', 'unshift']);
@@ -39,6 +41,8 @@ export const NAME_SIGNAL = 'signal';
39
41
 
40
42
  export const NAME_STORE = 'store';
41
43
 
44
+ export const NAME_SUBSCRIPTION = 'subscription';
45
+
42
46
  export const NAME_ALL = new Set([
43
47
  NAME_ARRAY,
44
48
  NAME_COMPUTED,
@@ -48,3 +52,29 @@ export const NAME_ALL = new Set([
48
52
  ]);
49
53
 
50
54
  export const PROPERTY_LENGTH = 'length';
55
+
56
+ export const SUBSCRIPTION_PROPERTY = {
57
+ key: NAME_MORA,
58
+ value: NAME_SUBSCRIPTION,
59
+ };
60
+
61
+ export const SUBSCRIPTION_TYPE_COPY: SubscriptionType = 'copy';
62
+
63
+ export const SUBSCRIPTION_TYPE_FROZEN: SubscriptionType = 'frozen';
64
+
65
+ export const SUBSCRIPTION_TYPE_ORIGINAL: SubscriptionType = 'original';
66
+
67
+ export const SUBSCRIPTION_TYPE_READONLY: SubscriptionType = 'readonly';
68
+
69
+ export const SUBSCRIPTION_TYPES_COPY = new Set<SubscriptionType>([
70
+ SUBSCRIPTION_TYPE_COPY,
71
+ SUBSCRIPTION_TYPE_READONLY,
72
+ ]);
73
+
74
+ export const SUBSCRIPTION_TYPES: Set<SubscriptionType> = new Set([
75
+ ...SUBSCRIPTION_TYPES_COPY,
76
+ SUBSCRIPTION_TYPE_FROZEN,
77
+ SUBSCRIPTION_TYPE_ORIGINAL,
78
+ ]);
79
+
80
+ // #endregion