@oscarpalmer/mora 0.29.0 → 0.31.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 (51) hide show
  1. package/dist/batch.mjs +15 -6
  2. package/dist/constants.d.mts +2 -2
  3. package/dist/constants.mjs +7 -4
  4. package/dist/effect.d.mts +5 -18
  5. package/dist/effect.mjs +23 -19
  6. package/dist/helpers/is.d.mts +17 -16
  7. package/dist/helpers/is.mjs +20 -11
  8. package/dist/helpers/proxy.d.mts +5 -9
  9. package/dist/helpers/proxy.mjs +4 -4
  10. package/dist/helpers/value.d.mts +3 -3
  11. package/dist/helpers/value.mjs +22 -5
  12. package/dist/index.d.mts +8 -9
  13. package/dist/index.mjs +2 -2
  14. package/dist/models.d.mts +423 -19
  15. package/dist/mora.full.mjs +532 -424
  16. package/dist/subscription.d.mts +1 -9
  17. package/dist/subscription.mjs +14 -15
  18. package/dist/value/array.d.mts +5 -133
  19. package/dist/value/array.mjs +108 -138
  20. package/dist/value/computed.d.mts +4 -12
  21. package/dist/value/computed.mjs +54 -50
  22. package/dist/value/reactive.d.mts +3 -49
  23. package/dist/value/reactive.mjs +10 -54
  24. package/dist/value/readonly.d.mts +7 -0
  25. package/dist/value/readonly.mjs +37 -0
  26. package/dist/value/signal.d.mts +5 -21
  27. package/dist/value/signal.mjs +33 -49
  28. package/dist/value/store.d.mts +9 -92
  29. package/dist/value/store.mjs +58 -49
  30. package/package.json +7 -8
  31. package/src/batch.ts +22 -9
  32. package/src/constants.ts +12 -5
  33. package/src/effect.ts +35 -40
  34. package/src/helpers/is.ts +36 -20
  35. package/src/helpers/proxy.ts +23 -18
  36. package/src/helpers/value.ts +39 -5
  37. package/src/index.ts +23 -8
  38. package/src/models.ts +520 -17
  39. package/src/subscription.ts +20 -20
  40. package/src/value/array.ts +220 -266
  41. package/src/value/computed.ts +80 -74
  42. package/src/value/reactive.ts +16 -77
  43. package/src/value/readonly.ts +71 -0
  44. package/src/value/signal.ts +43 -71
  45. package/src/value/store.ts +130 -177
  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 +3 -0
  50. package/types/value/signal.d.ts +1 -1
  51. package/types/value/store.d.ts +1 -1
package/src/effect.ts CHANGED
@@ -1,52 +1,47 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {ACTIVE, NAME_EFFECT, NAME_MORA} from './constants';
3
- import type {EffectState, InternalEffect} from './models';
4
-
5
- export class Effect {
6
- /**
7
- * Internal name of the effect
8
- *
9
- * @internal
10
- */
11
- declare private readonly $mora: string;
12
-
13
- /**
14
- * Internal state of the effect
15
- *
16
- * @internal
17
- */
18
- declare private readonly state: EffectState;
19
-
20
- constructor(callback: GenericCallback) {
21
- Object.defineProperty(this, NAME_MORA, {
22
- value: NAME_EFFECT,
23
- });
24
-
25
- this.state = {
26
- callback,
27
- };
28
-
29
- runEffect(this);
2
+ import {ACTIVE, NAME_EFFECT} from './constants';
3
+ import type {Effect, EffectState} from './models';
4
+
5
+ /**
6
+ * Create an effect
7
+ *
8
+ * @param callback Callback for handling signal effects
9
+ * @returns Effect
10
+ */
11
+ export function effect(callback: GenericCallback): Effect {
12
+ if (typeof callback !== 'function') {
13
+ throw new TypeError('Effect callback must be a function');
30
14
  }
15
+
16
+ return getEffect(callback)[0];
31
17
  }
32
18
 
33
- export function runEffect(effect: Effect): void {
19
+ function getEffect(callback: GenericCallback): [Effect, EffectState] {
20
+ const state: EffectState = {
21
+ callback,
22
+ };
23
+
24
+ const instance = Object.freeze({
25
+ $mora: NAME_EFFECT,
26
+ });
27
+
28
+ runEffect(state);
29
+
30
+ return [instance, state];
31
+ }
32
+
33
+ export function internalEffect(callback: GenericCallback): EffectState {
34
+ return getEffect(callback)[1];
35
+ }
36
+
37
+ export function runEffect(state: EffectState): void {
34
38
  const previousEffect = ACTIVE.effect;
35
39
 
36
- ACTIVE.effect = effect;
40
+ ACTIVE.effect = state;
37
41
 
38
42
  try {
39
- (effect as unknown as InternalEffect).state.callback();
43
+ state.callback();
40
44
  } finally {
41
45
  ACTIVE.effect = previousEffect;
42
46
  }
43
47
  }
44
-
45
- /**
46
- * Create an effect
47
- * @param callback Callback for handling signal effects
48
- * @returns Effect
49
- */
50
- export function effect(callback: GenericCallback): Effect {
51
- return typeof callback === 'function' ? new Effect(callback) : (undefined as never);
52
- }
package/src/helpers/is.ts CHANGED
@@ -5,27 +5,23 @@ import {
5
5
  NAME_COMPUTED,
6
6
  NAME_EFFECT,
7
7
  NAME_MORA,
8
+ NAME_READONLY,
8
9
  NAME_SIGNAL,
9
10
  NAME_STORE,
10
11
  } from '../constants';
11
- import type {Effect} from '../effect';
12
- import type {ReactiveArray} from '../value/array';
13
- import type {Computed} from '../value/computed';
14
- import type {Reactive} from '../value/reactive';
15
- import type {Signal} from '../value/signal';
16
- import type {Store} from '../value/store';
17
-
18
- /**
19
- * Is the value a reactive array?
20
- * @param value Value to check
21
- * @returns True if value is a {@link ReactiveArray}
22
- */
23
- export function isArray<Item>(value: unknown): value is ReactiveArray<Item> {
24
- return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
25
- }
12
+ import type {
13
+ Computed,
14
+ Effect,
15
+ Reactive,
16
+ ReactiveArray,
17
+ ReactiveStore,
18
+ ReadonlySignal,
19
+ Signal,
20
+ } from '../models';
26
21
 
27
22
  /**
28
23
  * Is the value a computed signal?
24
+ *
29
25
  * @param value Value to check
30
26
  * @returns True if value is a {@link Computed}
31
27
  */
@@ -35,6 +31,7 @@ export function isComputed<Value>(value: unknown): value is Computed<Value> {
35
31
 
36
32
  /**
37
33
  * Is the value an effect?
34
+ *
38
35
  * @param value Value to check
39
36
  * @returns True if value is an {@link Effect}
40
37
  */
@@ -42,7 +39,7 @@ export function isEffect(value: unknown): value is Effect {
42
39
  return isMora<Effect>(value, NAME_EFFECT);
43
40
  }
44
41
 
45
- function isMora<T>(value: unknown, name: string | Set<string>): value is T {
42
+ function isMora<Instance>(value: unknown, name: string | Set<string>): value is Instance {
46
43
  return (
47
44
  typeof value === 'object' &&
48
45
  value != null &&
@@ -55,15 +52,17 @@ function isMora<T>(value: unknown, name: string | Set<string>): value is T {
55
52
 
56
53
  /**
57
54
  * Is the value reactive?
55
+ *
58
56
  * @param value Value to check
59
57
  * @returns True if value is a {@link Reactive}
60
58
  */
61
- export function isReactive<Value, Equal = Value>(value: unknown): value is Reactive<Value, Equal> {
62
- return isMora<Reactive<Value, Equal>>(value, NAME_ALL);
59
+ export function isReactive<Value>(value: unknown): value is Reactive<Value> {
60
+ return isMora<Reactive<Value>>(value, NAME_ALL);
63
61
  }
64
62
 
65
63
  /**
66
64
  * Is the value a signal?
65
+ *
67
66
  * @param value Value to check
68
67
  * @returns True if value is a {@link Signal}
69
68
  */
@@ -71,11 +70,28 @@ export function isSignal<Value>(value: unknown): value is Signal<Value> {
71
70
  return isMora<Signal<Value>>(value, NAME_SIGNAL);
72
71
  }
73
72
 
73
+ /**
74
+ * Is the value a reactive array?
75
+ *
76
+ * @param value Value to check
77
+ * @returns True if value is a {@link ReactiveArray}
78
+ */
79
+ export function isReactiveArray<Item>(value: unknown): value is ReactiveArray<Item> {
80
+ return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
81
+ }
82
+
74
83
  /**
75
84
  * Is the value a reactive store?
85
+ *
76
86
  * @param value Value to check
77
87
  * @returns True if value is a {@link Store}
78
88
  */
79
- export function isStore<Value extends PlainObject>(value: unknown): value is Store<Value> {
80
- return isMora<Store<Value>>(value, NAME_STORE);
89
+ export function isReactiveStore<Value extends PlainObject>(
90
+ value: unknown,
91
+ ): value is ReactiveStore<Value> {
92
+ return isMora<ReactiveStore<Value>>(value, NAME_STORE);
93
+ }
94
+
95
+ export function isReadonlySignal<Value>(value: unknown): value is ReadonlySignal<Value> {
96
+ return isMora<ReadonlySignal<Value>>(value, NAME_READONLY);
81
97
  }
@@ -1,68 +1,73 @@
1
1
  import type {ArrayOrPlainObject, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import {PROPERTY_LENGTH} from '../constants';
3
- import type {InternalComputed, ReactiveState, SetValueInProxyParameters} from '../models';
4
- import type {ReactiveArray} from '../value/array';
5
- import {type Computed, computed} from '../value/computed';
6
- import type {Store} from '../value/store';
3
+ import type {
4
+ Computed,
5
+ ComputedEffect,
6
+ ReactiveArray,
7
+ ReactiveState,
8
+ ReactiveStore,
9
+ SetValueInProxyParameters,
10
+ } from '../models';
11
+ import {internalComputed} from '../value/computed';
7
12
  import {emitValue} from './value';
8
13
 
9
14
  export function emityProxyValues<Value>(
10
15
  state: ReactiveState<Value, Value>,
11
- mapped: Map<Key, Computed<unknown>>,
16
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
12
17
  ): void;
13
18
 
14
19
  export function emityProxyValues<Value>(
15
20
  state: ReactiveState<Value[], Value>,
16
- mapped: Map<Key, Computed<unknown>>,
21
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
17
22
  ): void;
18
23
 
19
24
  export function emityProxyValues(
20
25
  state: ReactiveState<unknown, unknown>,
21
- mapped: Map<Key, Computed<unknown>>,
26
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
22
27
  ): void {
23
28
  const values = [...mapped.values()];
24
29
  const {length} = values;
25
30
 
26
31
  for (let index = 0; index < length; index += 1) {
27
- (values[index] as unknown as InternalComputed).effect.dirty = true;
32
+ values[index][1].dirty = true;
28
33
  }
29
34
 
30
35
  emitValue(state);
31
36
  }
32
37
 
33
- export function getReactiveValueInProxy<Value>(
38
+ export function getReactiveValueInProxy<Value, Result = Value>(
34
39
  array: ReactiveArray<Value>,
35
- mapped: Map<Key, Computed<unknown>>,
40
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
36
41
  index: number,
37
42
  isArray: true,
38
- ): Computed<unknown>;
43
+ ): Computed<Result>;
39
44
 
40
45
  export function getReactiveValueInProxy<Value extends PlainObject>(
41
- store: Store<Value>,
42
- mapped: Map<Key, Computed<unknown>>,
46
+ store: ReactiveStore<Value>,
47
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
43
48
  key: Key,
44
49
  isArray: false,
45
50
  ): Computed<unknown>;
46
51
 
47
52
  export function getReactiveValueInProxy(
48
- reactive: ReactiveArray<unknown> | Store<PlainObject>,
49
- mapped: Map<Key, Computed<unknown>>,
53
+ reactive: ReactiveArray<unknown> | ReactiveStore<PlainObject>,
54
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
50
55
  key: Key,
51
56
  isArray: boolean,
52
57
  ): Computed<unknown> {
53
58
  let item = mapped.get(key);
54
59
 
55
60
  if (item == null) {
56
- item = computed(() =>
61
+ item = internalComputed(() =>
57
62
  isArray
58
63
  ? (reactive.get() as unknown[]).at(key as number)
59
64
  : (reactive.get() as PlainObject)[key],
60
- ) as Computed<unknown>;
65
+ );
61
66
 
62
67
  mapped.set(key, item);
63
68
  }
64
69
 
65
- return item;
70
+ return item[0];
66
71
  }
67
72
 
68
73
  export function setProxyValue<Value, Item = Value>(
@@ -1,11 +1,10 @@
1
- import {round} from '@oscarpalmer/atoms/math';
2
1
  import {flushHandlers} from '../batch';
3
2
  import {ACTIVE, ARRAY_OFFSET, ARRAY_PEEK, ARRAY_THRESHOLD, BATCH} from '../constants';
4
- import type {InternalComputed, ReactiveState} from '../models';
3
+ import type {ReactiveState} from '../models';
5
4
 
6
5
  export function emitValue<Value>(state: ReactiveState<Value, never>): void {
7
6
  for (const computed of state.computeds) {
8
- (computed as unknown as InternalComputed).effect.dirty = true;
7
+ computed.dirty = true;
9
8
  }
10
9
 
11
10
  for (const effect of state.effects) {
@@ -35,7 +34,7 @@ export function equalArrays<Value>(
35
34
  let offset = 0;
36
35
 
37
36
  if (length >= ARRAY_THRESHOLD) {
38
- offset = round(length / ARRAY_PEEK);
37
+ offset = Math.round(length / ARRAY_PEEK);
39
38
  offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
40
39
 
41
40
  for (let index = 0; index < offset; index += 1) {
@@ -56,7 +55,7 @@ export function equalArrays<Value>(
56
55
  return true;
57
56
  }
58
57
 
59
- export function getValue<Value>(state: ReactiveState<Value, never>): Value {
58
+ export function getSimpleValue<Value>(state: ReactiveState<Value, never>): Value {
60
59
  if (ACTIVE.computed != null) {
61
60
  state.computeds.add(ACTIVE.computed);
62
61
  }
@@ -67,3 +66,38 @@ export function getValue<Value>(state: ReactiveState<Value, never>): Value {
67
66
 
68
67
  return state.value;
69
68
  }
69
+
70
+ export function handleSimpleValue<Value>(
71
+ state: ReactiveState<Value, Value>,
72
+ origin: Value | (() => Value | Promise<Value>) | Promise<Value>,
73
+ setValue: (state: ReactiveState<Value, Value>, value: Value) => void,
74
+ onAfter?: () => void,
75
+ ): void {
76
+ try {
77
+ let actual = typeof origin === 'function' ? (origin as () => unknown)() : origin;
78
+
79
+ if (actual instanceof Promise) {
80
+ state.promise = actual;
81
+
82
+ void actual
83
+ .then(value => {
84
+ if (actual === state.promise) {
85
+ state.promise = undefined;
86
+
87
+ setValue(state, value);
88
+ }
89
+ })
90
+ .catch(() => {
91
+ if (actual === state.promise) {
92
+ state.promise = undefined;
93
+ }
94
+ });
95
+ } else {
96
+ setValue(state, actual as Value);
97
+ }
98
+ } catch {
99
+ // ?
100
+ } finally {
101
+ onAfter?.();
102
+ }
103
+ }
package/src/index.ts CHANGED
@@ -1,9 +1,24 @@
1
1
  export {startBatch, stopBatch} from './batch';
2
- export {effect, type Effect} from './effect';
3
- export {isArray, isComputed, isEffect, isReactive, isSignal} from './helpers/is';
4
- export type {Unsubscribe} from './models';
5
- export {array, type ReactiveArray} from './value/array';
6
- export {computed, type Computed} from './value/computed';
7
- export type {Reactive} from './value/reactive';
8
- export {signal, type Signal} from './value/signal';
9
- export {store, type Store} from './value/store';
2
+ export {effect} from './effect';
3
+ export {
4
+ isComputed,
5
+ isEffect,
6
+ isReactive,
7
+ isReactiveArray,
8
+ isReactiveStore,
9
+ isReadonlySignal,
10
+ isSignal,
11
+ } from './helpers/is';
12
+ export type {
13
+ Computed,
14
+ Effect,
15
+ ReactiveArray,
16
+ ReactiveStore,
17
+ ReadonlySignal,
18
+ Signal,
19
+ Unsubscribe,
20
+ } from './models';
21
+ export {array} from './value/array';
22
+ export {computed} from './value/computed';
23
+ export {signal} from './value/signal';
24
+ export {store} from './value/store';