@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
@@ -1,183 +1,26 @@
1
1
  import {isKey, isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {GenericCallback, Key, PlainObject} from '@oscarpalmer/atoms/models';
3
3
  import {startBatch, stopBatch} from '../batch';
4
- import {NAME_STORE} from '../constants';
4
+ import {NAME_MORA, NAME_STORE} from '../constants';
5
5
  import {
6
6
  emityProxyValues,
7
7
  getReactiveValueInProxy,
8
8
  setProxyValue,
9
9
  setValueInProxy,
10
10
  } from '../helpers/proxy';
11
- import {getValue} from '../helpers/value';
12
- import type {ReactiveOptions, ReactiveState, Unsubscribe} from '../models';
13
- import {noop, subscribe} from '../subscription';
14
- import type {Computed} from './computed';
15
- import {Reactive} from './reactive';
16
-
17
- export class Store<Value extends PlainObject> extends Reactive<Value, Value> {
18
- readonly #keyed = new Map<Key, Computed<unknown>>();
19
-
20
- constructor(value: Value, options?: ReactiveOptions<Value>) {
21
- super(
22
- NAME_STORE,
23
- new Proxy(value, {
24
- set: (target: Value, property: PropertyKey, value: unknown) =>
25
- setValueInProxy({
26
- target,
27
- property,
28
- value,
29
- isArray: false,
30
- state: this.state,
31
- }),
32
- }),
33
- options,
34
- );
35
- }
36
-
37
- /**
38
- * @inheritdoc
39
- */
40
- get(): Value;
41
-
42
- /**
43
- * Get a value by key
44
- * @param key Key of the value to get
45
- * @returns Value for the specified key, or `undefined` if it doesn't exist
46
- */
47
- get<Key extends keyof Value>(key: Key): Value[Key];
48
-
49
- /**
50
- * Get a value by key
51
- * @param key Key of the value to get
52
- * @returns Value for the specified key, or `undefined` if it doesn't exist
53
- */
54
- get(key: Key): unknown;
55
-
56
- get(key?: unknown): unknown {
57
- return isKey(key)
58
- ? getReactiveValueInProxy(this, this.#keyed, key, false).get()
59
- : getValue(this.state);
60
- }
61
-
62
- /**
63
- * Notify dependents of changes
64
- *
65
- * _This bypasses equality checks and will immediately notify dependents.
66
- * Use this only if you're modifying nested data that would be ignored by equality checks._
67
- */
68
- notify(): void {
69
- emityProxyValues(this.state, this.#keyed);
70
- }
71
-
72
- /**
73
- * Get the value _(without reactivity)_
74
- * @returns The current value
75
- */
76
- override peek(): Value;
77
-
78
- /**
79
- * Get a value by key _(without reactivity)_
80
- * @param key Key of the value to get
81
- * @returns Value for the specified key, or `undefined` if it doesn't exist
82
- */
83
- override peek<Key extends keyof Value>(key: Key): Value[Key];
84
-
85
- /**
86
- * Get a value by key _(without reactivity)_
87
- * @param key Key of the value to get
88
- * @returns Value for the specified key, or `undefined` if it doesn't exist
89
- */
90
- override peek(key: Key): unknown;
91
-
92
- override peek(key?: unknown): unknown {
93
- return isKey(key) ? this.state.value[key] : {...this.state.value};
94
- }
95
-
96
- /**
97
- * Set the value
98
- * @param value New value _(defaults to an empty object)_
99
- */
100
- set(
101
- value?:
102
- | Value
103
- | (() => null | undefined | Value | Promise<null | undefined | Value>)
104
- | Promise<null | undefined | Value>,
105
- ): void;
106
-
107
- /**
108
- * Set a value by key
109
- * @param key Key of the value to set
110
- * @param value New value
111
- */
112
- set<Key extends keyof Value>(
113
- key: Key,
114
- value: Value[Key] | (() => Value[Key] | Promise<Value[Key]>) | Promise<Value[Key]>,
115
- ): void;
116
-
117
- /**
118
- * Set a value by key
119
- * @param key Key of the value to set
120
- * @param value New value
121
- */
122
- set(key: Key, value: unknown): void;
123
-
124
- set(first?: unknown, second?: unknown): void {
125
- setProxyValue<Value>(
126
- false,
127
- this.state,
128
- isStoreObject,
129
- isKey,
130
- setObject,
131
- setProperty,
132
- first,
133
- second,
134
- );
135
- }
136
-
137
- /**
138
- * @inheritdoc
139
- */
140
- override subscribe(callback: (value: Value) => void): Unsubscribe;
141
-
142
- /**
143
- * Subscribe to changes for a specific key
144
- * @param key Key of the value to subscribe to
145
- * @param callback Callback for changes
146
- * @returns Unsubscribe callback
147
- */
148
- override subscribe<Key extends keyof Value>(
149
- key: Key,
150
- callback: (value: Value[Key] | undefined) => void,
151
- ): Unsubscribe;
152
-
153
- /**
154
- * Subscribe to changes for a specific key
155
- * @param key Key of the value to subscribe to
156
- * @param callback Callback for changes
157
- * @returns Unsubscribe callback
158
- */
159
- override subscribe(key: Key, callback: (value: unknown) => void): Unsubscribe;
160
-
161
- override subscribe(first: Key | GenericCallback, second?: GenericCallback): Unsubscribe {
162
- if (isKey(first) && typeof second === 'function') {
163
- return getReactiveValueInProxy(this, this.#keyed, first, false).subscribe(second);
164
- }
165
-
166
- return typeof first === 'function' ? subscribe(this.state, first) : noop;
167
- }
168
-
169
- /**
170
- * Update the value _(based on the current value)_
171
- * @param callback Callback to update the value
172
- */
173
- update(callback: (value: Value) => Value): void {
174
- const updated = callback(this.state.value);
175
-
176
- if (updated == null || isPlainObject(updated)) {
177
- setObject(this.state, updated);
178
- }
179
- }
180
- }
11
+ import {getSimpleValue} from '../helpers/value';
12
+ import type {
13
+ Computed,
14
+ ComputedEffect,
15
+ ReactiveOptions,
16
+ ReactiveState,
17
+ ReactiveStore,
18
+ ReadonlyInstances,
19
+ Unsubscribe,
20
+ } from '../models';
21
+ import {noop, subscribe, unsubscribe} from '../subscription';
22
+ import {reactive} from './reactive';
23
+ import {getReadonlyInstance} from './readonly';
181
24
 
182
25
  function isStoreObject<Value extends PlainObject>(value: unknown): value is Value | undefined {
183
26
  return value == null || isPlainObject(value);
@@ -215,6 +58,34 @@ function setObject(state: ReactiveState<PlainObject, never>, value: PlainObject
215
58
  stopBatch();
216
59
  }
217
60
 
61
+ function peekStoreValue<Value extends PlainObject>(
62
+ state: ReactiveState<Value, Value>,
63
+ first?: unknown,
64
+ second?: boolean,
65
+ ): unknown {
66
+ let value: unknown;
67
+
68
+ if (isKey(first)) {
69
+ value = state.value[first];
70
+ } else {
71
+ value = state.value;
72
+ }
73
+
74
+ if (!(first === true || second === true)) {
75
+ return value;
76
+ }
77
+
78
+ if (Array.isArray(value)) {
79
+ return value.slice();
80
+ }
81
+
82
+ if (isPlainObject(value)) {
83
+ return {...value};
84
+ }
85
+
86
+ return value;
87
+ }
88
+
218
89
  function setProperty<Value extends PlainObject>(
219
90
  state: ReactiveState<Value, Value>,
220
91
  key: unknown,
@@ -225,6 +96,7 @@ function setProperty<Value extends PlainObject>(
225
96
 
226
97
  /**
227
98
  * Create a reactive store from a function result
99
+ *
228
100
  * @param value Initial object value
229
101
  * @param options Reactivity options
230
102
  * @returns Reactive store
@@ -232,10 +104,11 @@ function setProperty<Value extends PlainObject>(
232
104
  export function store<Value extends PlainObject>(
233
105
  value: () => Value | Promise<Value>,
234
106
  options?: ReactiveOptions<Value>,
235
- ): Store<Value>;
107
+ ): ReactiveStore<Value>;
236
108
 
237
109
  /**
238
110
  * Create a reactive store from a promise
111
+ *
239
112
  * @param value Initial object value
240
113
  * @param options Reactivity options
241
114
  * @returns Reactive store
@@ -243,10 +116,11 @@ export function store<Value extends PlainObject>(
243
116
  export function store<Value extends PlainObject>(
244
117
  value: Promise<Value>,
245
118
  options?: ReactiveOptions<Value>,
246
- ): Store<Value>;
119
+ ): ReactiveStore<Value>;
247
120
 
248
121
  /**
249
122
  * Create a reactive store
123
+ *
250
124
  * @param value Initial object value
251
125
  * @param options Reactivity options
252
126
  * @returns Reactive store
@@ -254,15 +128,94 @@ export function store<Value extends PlainObject>(
254
128
  export function store<Value extends PlainObject>(
255
129
  value: Value,
256
130
  options?: ReactiveOptions<Value>,
257
- ): Store<Value>;
131
+ ): ReactiveStore<Value>;
258
132
 
259
133
  export function store<Value extends PlainObject>(
260
134
  value: Value | (() => Value | Promise<Value>) | Promise<Value>,
261
135
  options?: ReactiveOptions<Value>,
262
- ): Store<Value> {
263
- const instance = new Store({} as unknown as Value, options);
136
+ ): ReactiveStore<Value> {
137
+ const [rx, state] = reactive<Value>(undefined as never, options);
138
+
139
+ const keyed = new Map<Key, [Computed<unknown>, ComputedEffect]>();
140
+ const readonlies: ReadonlyInstances<Value> = {};
141
+
142
+ let instance: Record<string, GenericCallback> = {};
143
+
144
+ state.value = new Proxy({} as Value, {
145
+ set: (target: Value, property: PropertyKey, value: unknown) =>
146
+ setValueInProxy({
147
+ target,
148
+ property,
149
+ state,
150
+ value,
151
+ isArray: false,
152
+ }),
153
+ });
154
+
155
+ const handlers = {
156
+ ...rx,
157
+ get: (value?: unknown): unknown =>
158
+ isKey(value)
159
+ ? getReactiveValueInProxy(instance as ReactiveStore<Value>, keyed, value, false).get()
160
+ : getSimpleValue(state),
161
+ peek: (first?: unknown, second?: boolean): unknown => peekStoreValue(state, first, second),
162
+ subscribe: (first: Key | GenericCallback, second?: GenericCallback): Unsubscribe => {
163
+ if (isKey(first) && typeof second === 'function') {
164
+ return getReactiveValueInProxy(
165
+ instance as ReactiveStore<Value>,
166
+ keyed,
167
+ first,
168
+ false,
169
+ ).subscribe(second);
170
+ }
171
+
172
+ return typeof first === 'function' ? subscribe(state, first) : noop;
173
+ },
174
+ unsubscribe: (first: Key | GenericCallback, second?: GenericCallback) => {
175
+ if (isKey(first) && typeof second === 'function') {
176
+ getReactiveValueInProxy(instance as ReactiveStore<Value>, keyed, first, false)?.unsubscribe(
177
+ second,
178
+ );
179
+ } else if (typeof first === 'function') {
180
+ unsubscribe(state, first);
181
+ }
182
+ },
183
+ };
184
+
185
+ instance = {
186
+ ...handlers,
187
+ asReadonly: (frozen?: unknown) =>
188
+ getReadonlyInstance(state, readonlies, handlers, frozen === true),
189
+ notify(): void {
190
+ emityProxyValues(state, keyed);
191
+ },
192
+ set: (first?: unknown, second?: unknown) => {
193
+ setProxyValue<Value>(
194
+ false,
195
+ state,
196
+ isStoreObject,
197
+ isKey,
198
+ setObject,
199
+ setProperty,
200
+ first,
201
+ second,
202
+ );
203
+ },
204
+ update: (callback: (value: Value) => Value) => {
205
+ const updated = callback(state.value);
206
+
207
+ if (updated == null || isPlainObject(updated)) {
208
+ setObject(state, updated);
209
+ }
210
+ },
211
+ };
212
+
213
+ Object.defineProperty(instance, NAME_MORA, {
214
+ enumerable: false,
215
+ value: NAME_STORE,
216
+ });
264
217
 
265
218
  instance.set(value);
266
219
 
267
- return instance;
220
+ return Object.freeze(instance) as ReactiveStore<Value>;
268
221
  }
@@ -12,5 +12,5 @@ export declare const NAME_EFFECT = "effect";
12
12
  export declare const NAME_MORA = "$mora";
13
13
  export declare const NAME_SIGNAL = "signal";
14
14
  export declare const NAME_STORE = "store";
15
- export declare const NAMES: Set<string>;
15
+ export declare const NAME_ALL: Set<string>;
16
16
  export declare const PROPERTY_LENGTH = "length";
package/types/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { startBatch, stopBatch } from './batch';
2
2
  export { effect, type Effect } from './effect';
3
- export { isArray, isComputed, isEffect, isReactive, isSignal, } from './helpers/is';
3
+ export { isArray, isComputed, isEffect, isReactive, isSignal } from './helpers/is';
4
4
  export type { Unsubscribe } from './models';
5
5
  export { array, type ReactiveArray } from './value/array';
6
6
  export { computed, type Computed } from './value/computed';
@@ -131,7 +131,7 @@ export declare class ReactiveArray<Item> extends Reactive<Item[], Item> {
131
131
  /**
132
132
  * Create a reactive array
133
133
  * @param value Initial array of items
134
- * @param options Optional reactivity options
134
+ * @param options Reactivity options
135
135
  * @returns Reactive array
136
136
  */
137
137
  export declare function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
@@ -10,5 +10,8 @@ export declare class Computed<Value> extends Reactive<Value> {
10
10
  }
11
11
  /**
12
12
  * Create a computed value
13
+ * @param callback Callback to compute the value
14
+ * @param options Reactivity options
15
+ * @returns Computed value
13
16
  */
14
17
  export declare function computed<Value>(callback: () => Value, options?: ReactiveOptions<Value>): Computed<Value>;
@@ -20,7 +20,7 @@ export declare class Signal<Value> extends Reactive<Value> {
20
20
  /**
21
21
  * Create a reactive value
22
22
  * @param value Initial value
23
- * @param options Optional reactivity options
23
+ * @param options Reactivity options
24
24
  * @returns Reactive value
25
25
  */
26
26
  export declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
@@ -88,7 +88,7 @@ export declare class Store<Value extends PlainObject> extends Reactive<Value, Va
88
88
  /**
89
89
  * Create a reactive store
90
90
  * @param value Initial object value
91
- * @param options Optional reactivity options
91
+ * @param options Reactivity options
92
92
  * @returns Reactive store
93
93
  */
94
94
  export declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): Store<Value>;