@oscarpalmer/mora 0.32.0 → 0.33.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 (45) hide show
  1. package/dist/batch.mjs +2 -2
  2. package/dist/constants.d.mts +2 -0
  3. package/dist/constants.mjs +3 -1
  4. package/dist/effect.d.mts +1 -0
  5. package/dist/effect.mjs +8 -5
  6. package/dist/helpers/is.d.mts +6 -6
  7. package/dist/helpers/misc.d.mts +4 -0
  8. package/dist/helpers/misc.mjs +9 -0
  9. package/dist/helpers/proxy.d.mts +8 -10
  10. package/dist/helpers/proxy.mjs +71 -33
  11. package/dist/helpers/subscription.d.mts +4 -4
  12. package/dist/helpers/subscription.mjs +16 -13
  13. package/dist/helpers/value.d.mts +8 -7
  14. package/dist/helpers/value.mjs +35 -22
  15. package/dist/models.d.mts +47 -24
  16. package/dist/models.mjs +1 -0
  17. package/dist/mora.full.mjs +307 -236
  18. package/dist/value/array.d.mts +1 -0
  19. package/dist/value/array.mjs +69 -61
  20. package/dist/value/computed.d.mts +3 -1
  21. package/dist/value/computed.mjs +39 -25
  22. package/dist/value/readonly.d.mts +3 -3
  23. package/dist/value/readonly.mjs +31 -24
  24. package/dist/value/signal.d.mts +1 -0
  25. package/dist/value/signal.mjs +27 -20
  26. package/dist/value/store.d.mts +1 -0
  27. package/dist/value/store.mjs +23 -41
  28. package/package.json +1 -1
  29. package/src/batch.ts +2 -2
  30. package/src/constants.ts +4 -0
  31. package/src/effect.ts +16 -11
  32. package/src/helpers/is.ts +6 -6
  33. package/src/helpers/misc.ts +15 -0
  34. package/src/helpers/proxy.ts +116 -89
  35. package/src/helpers/subscription.ts +46 -35
  36. package/src/helpers/value.ts +61 -35
  37. package/src/models.ts +54 -28
  38. package/src/value/array.ts +103 -124
  39. package/src/value/computed.ts +70 -40
  40. package/src/value/readonly.ts +54 -41
  41. package/src/value/signal.ts +46 -35
  42. package/src/value/store.ts +36 -83
  43. package/dist/value/reactive.d.mts +0 -4
  44. package/dist/value/reactive.mjs +0 -15
  45. package/src/value/reactive.ts +0 -27
@@ -2,58 +2,71 @@ import {
2
2
  NAME_MORA,
3
3
  NAME_READONLY,
4
4
  SUBSCRIPTION_TYPE_FROZEN,
5
- SUBSCRIPTION_TYPE_READONLY,
5
+ SUBSCRIPTION_TYPE_ORIGINAL,
6
+ SYMBOL_STATE,
6
7
  } from '../constants';
7
- import {subscribeToReactive} from '../helpers/subscription';
8
- import {getFrozenValue, getSimpleValue, peekSimpleValue} from '../helpers/value';
9
- import type {
10
- ReactiveState,
11
- ReadonlyFrozenSignal,
12
- ReadonlyInstances,
13
- ReadonlySignal,
14
- } from '../models';
8
+ import {subscribeToReadonly} from '../helpers/subscription';
9
+ import {getFrozenValue, getSignalValue, getStringValue, peekSignalValue} from '../helpers/value';
10
+ import type {ReadonlyFrozenSignal, ReadonlySignal, SignalState, InternalSignal} from '../models';
11
+
12
+ // #region Instance
13
+
14
+ function Readonly(this: any, state: SignalState, frozen: boolean) {
15
+ this[SYMBOL_STATE] = state;
16
+
17
+ Object.defineProperty(this, SUBSCRIPTION_TYPE_FROZEN, {
18
+ enumerable: true,
19
+ value: frozen,
20
+ });
21
+ }
22
+
23
+ Readonly.prototype[NAME_MORA] = NAME_READONLY;
24
+
25
+ Readonly.prototype.subscribe = subscribeToReadonly;
26
+ Readonly.prototype.toString = getStringValue;
27
+
28
+ Readonly.prototype.get = function () {
29
+ return getReadonlyValue(this, false, this[SUBSCRIPTION_TYPE_FROZEN]);
30
+ };
31
+
32
+ Readonly.prototype.peek = function (copy?: boolean) {
33
+ return getReadonlyValue(this, true, this[SUBSCRIPTION_TYPE_FROZEN], copy);
34
+ };
35
+
36
+ Readonly.prototype.toJSON = function () {
37
+ return this[SYMBOL_STATE].value;
38
+ };
39
+
40
+ // #endregion
15
41
 
16
42
  // #region Functions
17
43
 
18
44
  export function getReadonlyInstance<Value>(
19
- state: ReactiveState<Value, never>,
20
- instances: ReadonlyInstances<Value>,
21
- frozen: boolean,
45
+ this: InternalSignal,
46
+ frozen?: never,
22
47
  ): ReadonlySignal<Value> {
23
- const key = frozen ? 'frozen' : 'original';
48
+ const key = (frozen === true ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_ORIGINAL) as never;
49
+
50
+ this[SYMBOL_STATE].readonlies ??= {};
24
51
 
25
- instances[key] ??= getReadonlySignal(state, frozen) as never;
52
+ this[SYMBOL_STATE].readonlies[key] ??= getReadonlySignal(
53
+ this[SYMBOL_STATE],
54
+ frozen === true,
55
+ ) as never;
26
56
 
27
- return instances[key] as never;
57
+ return this[SYMBOL_STATE].readonlies[key] as never;
28
58
  }
29
59
 
30
- export function getReadonlySignal<Value>(
31
- state: ReactiveState<Value, never>,
60
+ export function getReadonlySignal(
61
+ state: SignalState,
32
62
  frozen: boolean,
33
- ): ReadonlySignal<Value> | ReadonlyFrozenSignal<Value> {
34
- const type = frozen ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_READONLY;
35
-
36
- const instance = {
37
- get: () => getReadonlyValue(state, false, frozen),
38
- peek: (copy?: boolean) => getReadonlyValue(state, true, frozen, copy),
39
- subscribe: (callback: never) => subscribeToReactive(type, state, callback),
40
- };
41
-
42
- Object.defineProperties(instance, {
43
- [NAME_MORA]: {
44
- value: NAME_READONLY,
45
- },
46
- frozen: {
47
- enumerable: true,
48
- value: frozen,
49
- },
50
- });
51
-
52
- return Object.freeze(instance) as never;
63
+ ): ReadonlySignal<unknown> | ReadonlyFrozenSignal<unknown> {
64
+ // @ts-expect-error All good, no worries :-)
65
+ return new Readonly(state, frozen);
53
66
  }
54
67
 
55
- function getReadonlyValue<Value>(
56
- state: ReactiveState<Value, never>,
68
+ function getReadonlyValue(
69
+ instance: InternalSignal,
57
70
  peek: boolean,
58
71
  frozen: boolean,
59
72
  copy?: boolean,
@@ -61,9 +74,9 @@ function getReadonlyValue<Value>(
61
74
  let value: unknown;
62
75
 
63
76
  if (peek) {
64
- value = peekSimpleValue(state.value, copy === true);
77
+ value = peekSignalValue.call(instance, copy === true);
65
78
  } else {
66
- value = getSimpleValue(state);
79
+ value = getSignalValue.call(instance);
67
80
  }
68
81
 
69
82
  return frozen ? getFrozenValue(value) : value;
@@ -1,23 +1,56 @@
1
- import {NAME_MORA, NAME_SIGNAL} from '../constants';
1
+ import {NAME_MORA, NAME_SIGNAL, SYMBOL_STATE} from '../constants';
2
+ import {getState} from '../helpers/misc';
2
3
  import {subscribeToSignal} from '../helpers/subscription';
3
4
  import {
4
5
  emitValue,
5
- getSimpleValue,
6
- handleSimpleValue,
7
- peekSimpleValue,
8
- updateSimpleValue,
6
+ getSignalValue,
7
+ getStringValue,
8
+ handleSignalValue,
9
+ peekSignalValue,
10
+ updateSignalValue,
9
11
  } from '../helpers/value';
10
- import type {ReactiveOptions, ReactiveState, ReadonlyInstances, Signal} from '../models';
11
- import {reactive} from './reactive';
12
+ import type {ReactiveOptions, Signal, InternalSignal} from '../models';
12
13
  import {getReadonlyInstance} from './readonly';
13
14
 
15
+ // #region Instance
16
+
17
+ function Signal(this: any, value: unknown, options?: ReactiveOptions<unknown>) {
18
+ this[SYMBOL_STATE] = getState(undefined, options);
19
+
20
+ handleSignalValue(this, value, setAndEmit);
21
+ }
22
+
23
+ Signal.prototype[NAME_MORA] = NAME_SIGNAL;
24
+
25
+ Signal.prototype.asReadonly = getReadonlyInstance;
26
+ Signal.prototype.get = getSignalValue;
27
+ Signal.prototype.peek = peekSignalValue;
28
+ Signal.prototype.subscribe = subscribeToSignal;
29
+ Signal.prototype.toString = getStringValue;
30
+
31
+ Signal.prototype.set = function (value: never) {
32
+ return handleSignalValue(this, value, setAndEmit);
33
+ };
34
+
35
+ Signal.prototype.toJSON = function () {
36
+ return this[SYMBOL_STATE].value;
37
+ };
38
+
39
+ Signal.prototype.update = function (callback: never) {
40
+ return updateSignalValue(this, callback, setAndEmit);
41
+ };
42
+
43
+ // #endregion
44
+
14
45
  // #region Functions
15
46
 
16
- function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): void {
17
- if (!state.equal(state.value, value)) {
47
+ function setAndEmit(instance: InternalSignal, value: unknown): void {
48
+ const state = instance[SYMBOL_STATE];
49
+
50
+ if (!(state.equal ?? Object.is)(state.value as never, value as never)) {
18
51
  state.value = value;
19
52
 
20
- emitValue(state);
53
+ emitValue(instance);
21
54
  }
22
55
  }
23
56
 
@@ -54,31 +87,9 @@ export function signal<Value>(
54
87
  */
55
88
  export function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
56
89
 
57
- export function signal<Value>(
58
- value: Value | (() => Value | Promise<Value>) | Promise<Value>,
59
- options?: ReactiveOptions<Value>,
60
- ): Signal<Value> {
61
- const [rx, state] = reactive<Value>(undefined as unknown as Value, options);
62
-
63
- const readonlies: ReadonlyInstances<Value> = {};
64
-
65
- const instance = {
66
- ...rx,
67
- asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
68
- get: () => getSimpleValue(state),
69
- peek: (copy?: boolean) => peekSimpleValue(state.value, copy === true),
70
- set: (value: never) => handleSimpleValue(state, value, setAndEmit),
71
- subscribe: (callback: never, copy?: never) => subscribeToSignal(state, callback, copy === true),
72
- update: (callback: never) => updateSimpleValue(state, callback, setAndEmit),
73
- };
74
-
75
- Object.defineProperty(instance, NAME_MORA, {
76
- value: NAME_SIGNAL,
77
- });
78
-
79
- handleSimpleValue(state, value, setAndEmit);
80
-
81
- return Object.freeze(instance) as never;
90
+ export function signal(value: unknown, options?: ReactiveOptions<unknown>): Signal<unknown> {
91
+ // @ts-expect-error All good, no worries :-)
92
+ return new Signal(value, options);
82
93
  }
83
94
 
84
95
  // #endregion
@@ -1,6 +1,6 @@
1
- import type {Key, PlainObject} from '@oscarpalmer/atoms/models';
2
- import {startBatch, stopBatch} from '../batch';
3
- import {NAME_MORA, NAME_STORE} from '../constants';
1
+ import type {PlainObject} from '@oscarpalmer/atoms/models';
2
+ import {NAME_MORA, NAME_STORE, SYMBOL_STATE} from '../constants';
3
+ import {getState} from '../helpers/misc';
4
4
  import {
5
5
  emitProxyValues,
6
6
  getValueInProxy,
@@ -10,61 +10,46 @@ import {
10
10
  updateProxyValue,
11
11
  } from '../helpers/proxy';
12
12
  import {subscribeToProxy} from '../helpers/subscription';
13
- import type {
14
- Computed,
15
- ComputedEffect,
16
- ReactiveOptions,
17
- ReactiveState,
18
- ReactiveStore,
19
- ReadonlyInstances,
20
- } from '../models';
21
- import {reactive} from './reactive';
13
+ import {getStringValue} from '../helpers/value';
14
+ import type {ReactiveOptions, ReactiveStore} from '../models';
22
15
  import {getReadonlyInstance} from './readonly';
23
16
 
24
- // #region Functions
25
-
26
- function setPropertyValue<Value extends PlainObject, Item = Value>(
27
- state: ReactiveState<Value, Item>,
28
- key: unknown,
29
- value: unknown,
30
- ): void {
31
- (state.value as PlainObject)[key as Key] = value;
32
- }
33
-
34
- function setStoreValue<Value extends PlainObject, Item = Value>(
35
- state: ReactiveState<Value, Item>,
36
- value: PlainObject | undefined,
37
- ): void {
38
- startBatch();
39
-
40
- const actual = value ?? {};
41
- const proxy = state.value as PlainObject;
17
+ // #region Instance
42
18
 
43
- const proxyKeys = Object.keys(proxy);
44
- const actualKeys = Object.keys(actual);
45
-
46
- let {length} = proxyKeys;
19
+ function ReactiveStore(this: any, value: never, options?: ReactiveOptions<unknown>) {
20
+ this[SYMBOL_STATE] = {
21
+ ...getState(undefined, options),
22
+ isArray: false,
23
+ };
47
24
 
48
- for (let index = 0; index < length; index += 1) {
49
- const key = proxyKeys[index];
25
+ this[SYMBOL_STATE].value = new Proxy(
26
+ {},
27
+ {
28
+ set: (target, property, value) => setValueInProxy(this, target, property, value),
29
+ },
30
+ );
50
31
 
51
- proxy[key] = actualKeys.includes(key) ? actual[key] : undefined;
52
- }
32
+ setProxyValue.call(this, value);
33
+ }
53
34
 
54
- length = actualKeys.length;
35
+ ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
55
36
 
56
- for (let index = 0; index < length; index += 1) {
57
- const key = actualKeys[index];
37
+ ReactiveStore.prototype.asReadonly = getReadonlyInstance;
38
+ ReactiveStore.prototype.get = getValueInProxy;
39
+ ReactiveStore.prototype.notify = emitProxyValues;
40
+ ReactiveStore.prototype.peek = peekValueInProxy;
41
+ ReactiveStore.prototype.set = setProxyValue;
42
+ ReactiveStore.prototype.subscribe = subscribeToProxy;
43
+ ReactiveStore.prototype.toString = getStringValue;
44
+ ReactiveStore.prototype.update = updateProxyValue;
58
45
 
59
- if (!proxyKeys.includes(key)) {
60
- const keyedValue = actual[key];
46
+ ReactiveStore.prototype.toJSON = function () {
47
+ return this[SYMBOL_STATE].value;
48
+ };
61
49
 
62
- proxy[key] = keyedValue;
63
- }
64
- }
50
+ // #endregion
65
51
 
66
- stopBatch();
67
- }
52
+ // #region Functions
68
53
 
69
54
  /**
70
55
  * Create a reactive store from a function result
@@ -102,41 +87,9 @@ export function store<Value extends PlainObject>(
102
87
  options?: ReactiveOptions<Value>,
103
88
  ): ReactiveStore<Value>;
104
89
 
105
- export function store<Value extends PlainObject>(
106
- value: Value | (() => Value | Promise<Value>) | Promise<Value>,
107
- options?: ReactiveOptions<Value>,
108
- ): ReactiveStore<Value> {
109
- const [rx, state] = reactive<Value>(undefined as never, options);
110
-
111
- const isArray = false;
112
- const keyed = new Map<Key, [Computed<unknown>, ComputedEffect]>();
113
- const readonlies: ReadonlyInstances<Value> = {};
114
-
115
- state.value = new Proxy({} as Value, {
116
- set: (target, property, value) => setValueInProxy(isArray, state, target, property, value),
117
- });
118
-
119
- const instance = {
120
- ...rx,
121
- asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
122
- get: (value?: never) => getValueInProxy(isArray, instance as never, state, keyed, value),
123
- notify: () => emitProxyValues(state, keyed),
124
- peek: (first?: never, second?: never) => peekValueInProxy(isArray, state, first, second),
125
- set: (first?: never, second?: never) =>
126
- setProxyValue<Value>(isArray, state, setStoreValue, setPropertyValue, first, second),
127
- subscribe: (first: never, second?: never, third?: never) =>
128
- subscribeToProxy(isArray, instance as never, state, keyed, first, second, third),
129
- update: (callback: never) =>
130
- updateProxyValue(isArray, state, setStoreValue, setPropertyValue, callback),
131
- };
132
-
133
- Object.defineProperty(instance, NAME_MORA, {
134
- value: NAME_STORE,
135
- });
136
-
137
- instance.set(value as never);
138
-
139
- return Object.freeze(instance) as never;
90
+ export function store(value: unknown, options?: ReactiveOptions<unknown>): ReactiveStore<unknown> {
91
+ // @ts-expect-error All good, no worries :-)
92
+ return new ReactiveStore(value, options);
140
93
  }
141
94
 
142
95
  // #endregion
@@ -1,4 +0,0 @@
1
- import { Reactive, ReactiveOptions, ReactiveState } from "../models.mjs";
2
- //#region src/value/reactive.d.ts
3
- export declare function reactive<Value, Item = Value>(value: Value, options?: ReactiveOptions<Item>): [Reactive<Value>, ReactiveState<Value, Item>];
4
- //#endregion
@@ -1,15 +0,0 @@
1
- //#region src/value/reactive.ts
2
- function reactive(value, options) {
3
- const state = {
4
- computeds: /* @__PURE__ */ new Set(),
5
- effects: /* @__PURE__ */ new Set(),
6
- equal: typeof options === "object" && typeof options?.equal === "function" ? options.equal : Object.is,
7
- value
8
- };
9
- return [{
10
- toJSON: () => state.value,
11
- toString: () => String(state.value)
12
- }, state];
13
- }
14
- //#endregion
15
- export { reactive };
@@ -1,27 +0,0 @@
1
- import type {Reactive, ReactiveOptions, ReactiveState} from '../models';
2
-
3
- // #region Functions
4
-
5
- export function reactive<Value, Item = Value>(
6
- value: Value,
7
- options?: ReactiveOptions<Item>,
8
- ): [Reactive<Value>, ReactiveState<Value, Item>] {
9
- const state: ReactiveState<Value, Item> = {
10
- computeds: new Set(),
11
- effects: new Set(),
12
- equal:
13
- typeof options === 'object' && typeof options?.equal === 'function'
14
- ? options.equal
15
- : Object.is,
16
- value: value as Value,
17
- };
18
-
19
- const instance = {
20
- toJSON: () => state.value,
21
- toString: () => String(state.value),
22
- };
23
-
24
- return [instance, state];
25
- }
26
-
27
- // #endregion