@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.
- package/dist/batch.mjs +15 -6
- package/dist/constants.d.mts +2 -2
- package/dist/constants.mjs +7 -4
- package/dist/effect.d.mts +5 -18
- package/dist/effect.mjs +23 -19
- package/dist/helpers/is.d.mts +17 -16
- package/dist/helpers/is.mjs +20 -11
- package/dist/helpers/proxy.d.mts +5 -9
- package/dist/helpers/proxy.mjs +4 -4
- package/dist/helpers/value.d.mts +3 -3
- package/dist/helpers/value.mjs +22 -5
- package/dist/index.d.mts +8 -9
- package/dist/index.mjs +2 -2
- package/dist/models.d.mts +423 -19
- package/dist/mora.full.mjs +532 -424
- package/dist/subscription.d.mts +1 -9
- package/dist/subscription.mjs +14 -15
- package/dist/value/array.d.mts +5 -133
- package/dist/value/array.mjs +108 -138
- package/dist/value/computed.d.mts +4 -12
- package/dist/value/computed.mjs +54 -50
- package/dist/value/reactive.d.mts +3 -49
- package/dist/value/reactive.mjs +10 -54
- package/dist/value/readonly.d.mts +7 -0
- package/dist/value/readonly.mjs +37 -0
- package/dist/value/signal.d.mts +5 -21
- package/dist/value/signal.mjs +33 -49
- package/dist/value/store.d.mts +9 -92
- package/dist/value/store.mjs +58 -49
- package/package.json +7 -8
- package/src/batch.ts +22 -9
- package/src/constants.ts +12 -5
- package/src/effect.ts +35 -40
- package/src/helpers/is.ts +36 -20
- package/src/helpers/proxy.ts +23 -18
- package/src/helpers/value.ts +39 -5
- package/src/index.ts +23 -8
- package/src/models.ts +520 -17
- package/src/subscription.ts +20 -20
- package/src/value/array.ts +220 -266
- package/src/value/computed.ts +80 -74
- package/src/value/reactive.ts +16 -77
- package/src/value/readonly.ts +71 -0
- package/src/value/signal.ts +43 -71
- package/src/value/store.ts +130 -177
- package/types/constants.d.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/value/array.d.ts +1 -1
- package/types/value/computed.d.ts +3 -0
- package/types/value/signal.d.ts +1 -1
- package/types/value/store.d.ts +1 -1
package/dist/value/reactive.mjs
CHANGED
|
@@ -1,60 +1,16 @@
|
|
|
1
|
-
import { NAME_MORA } from "../constants.mjs";
|
|
2
|
-
import { subscribe, unsubscribe } from "../subscription.mjs";
|
|
3
1
|
//#region src/value/reactive.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* Internal state of the reactive value
|
|
7
|
-
*
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
state = {
|
|
2
|
+
function reactive(value, options) {
|
|
3
|
+
const state = {
|
|
11
4
|
computeds: /* @__PURE__ */ new Set(),
|
|
12
5
|
effects: /* @__PURE__ */ new Set(),
|
|
13
|
-
equal: Object.is,
|
|
6
|
+
equal: typeof options === "object" && typeof options?.equal === "function" ? options.equal : Object.is,
|
|
14
7
|
subscriptions: /* @__PURE__ */ new Map(),
|
|
15
|
-
value
|
|
8
|
+
value
|
|
16
9
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Get the value _(without reactivity)_
|
|
24
|
-
* @return Current value
|
|
25
|
-
*/
|
|
26
|
-
peek() {
|
|
27
|
-
return this.state.value;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Subscribe to changes
|
|
31
|
-
* @param callback Callback for changes
|
|
32
|
-
* @return Unsubscribe callback
|
|
33
|
-
*/
|
|
34
|
-
subscribe(callback) {
|
|
35
|
-
return subscribe(this.state, callback);
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* JSON representation of the value
|
|
39
|
-
* @return JSON value
|
|
40
|
-
*/
|
|
41
|
-
toJSON() {
|
|
42
|
-
return this.get();
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* String representation of the value
|
|
46
|
-
* @return Value as string
|
|
47
|
-
*/
|
|
48
|
-
toString() {
|
|
49
|
-
return String(this.get());
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Unsubscribe from changes
|
|
53
|
-
* @param callback Callback to unsubscribe
|
|
54
|
-
*/
|
|
55
|
-
unsubscribe(callback) {
|
|
56
|
-
unsubscribe(this.state, callback);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
10
|
+
return [{
|
|
11
|
+
toJSON: () => state.value,
|
|
12
|
+
toString: () => String(state.value)
|
|
13
|
+
}, state];
|
|
14
|
+
}
|
|
59
15
|
//#endregion
|
|
60
|
-
export {
|
|
16
|
+
export { reactive };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ReactiveState, ReadonlyFrozenSignal, ReadonlyInstances, ReadonlySignal } from "../models.mjs";
|
|
2
|
+
import { GenericCallback } from "@oscarpalmer/atoms/models";
|
|
3
|
+
//#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 };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { NAME_MORA, NAME_READONLY } from "../constants.mjs";
|
|
2
|
+
import { getSimpleValue } from "../helpers/value.mjs";
|
|
3
|
+
import { isPlainObject } from "@oscarpalmer/atoms/is";
|
|
4
|
+
//#region src/value/readonly.ts
|
|
5
|
+
function getReadonlyInstance(state, instances, handlers, frozen) {
|
|
6
|
+
const key = frozen ? "frozen" : "original";
|
|
7
|
+
instances[key] ??= getReadonlySignal(state, handlers, frozen);
|
|
8
|
+
return instances[key];
|
|
9
|
+
}
|
|
10
|
+
function getReadonlySignal(state, handlers, frozen) {
|
|
11
|
+
const instance = {
|
|
12
|
+
...handlers,
|
|
13
|
+
get: () => getReadonlyValue(state, false, frozen),
|
|
14
|
+
peek: () => getReadonlyValue(state, true, frozen)
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperties(instance, {
|
|
17
|
+
[NAME_MORA]: {
|
|
18
|
+
enumerable: false,
|
|
19
|
+
value: NAME_READONLY
|
|
20
|
+
},
|
|
21
|
+
frozen: {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
value: frozen
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return Object.freeze(instance);
|
|
27
|
+
}
|
|
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);
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { getReadonlyInstance, getReadonlySignal };
|
package/dist/value/signal.d.mts
CHANGED
|
@@ -1,26 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ReactiveOptions } from "../models.mjs";
|
|
3
|
-
|
|
1
|
+
import { ReactiveOptions, Signal } from "../models.mjs";
|
|
4
2
|
//#region src/value/signal.d.ts
|
|
5
|
-
declare class Signal<Value> extends Reactive<Value> {
|
|
6
|
-
constructor(value: Value, options?: ReactiveOptions<Value>);
|
|
7
|
-
/**
|
|
8
|
-
* @inheritdoc
|
|
9
|
-
*/
|
|
10
|
-
get(): Value;
|
|
11
|
-
/**
|
|
12
|
-
* Set the value
|
|
13
|
-
* @param value New value
|
|
14
|
-
*/
|
|
15
|
-
set(value: Value | (() => Value | Promise<Value>) | Promise<Value>): void;
|
|
16
|
-
/**
|
|
17
|
-
* Update the value _(based on the current value)_
|
|
18
|
-
* @param callback Callback to update the value
|
|
19
|
-
*/
|
|
20
|
-
update(callback: (value: Value) => Value): void;
|
|
21
|
-
}
|
|
22
3
|
/**
|
|
23
4
|
* Create a reactive value from a function result
|
|
5
|
+
*
|
|
24
6
|
* @param value Initial value
|
|
25
7
|
* @param options Reactivity options
|
|
26
8
|
* @returns Reactive value
|
|
@@ -28,6 +10,7 @@ declare class Signal<Value> extends Reactive<Value> {
|
|
|
28
10
|
declare function signal<Value>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
|
|
29
11
|
/**
|
|
30
12
|
* Create a reactive value from a promise
|
|
13
|
+
*
|
|
31
14
|
* @param value Initial value
|
|
32
15
|
* @param options Reactivity options
|
|
33
16
|
* @returns Reactive value
|
|
@@ -35,10 +18,11 @@ declare function signal<Value>(value: () => Value | Promise<Value>, options?: Re
|
|
|
35
18
|
declare function signal<Value>(value: Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
|
|
36
19
|
/**
|
|
37
20
|
* Create a reactive value
|
|
21
|
+
*
|
|
38
22
|
* @param value Initial value
|
|
39
23
|
* @param options Reactivity options
|
|
40
24
|
* @returns Reactive value
|
|
41
25
|
*/
|
|
42
26
|
declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
|
|
43
27
|
//#endregion
|
|
44
|
-
export {
|
|
28
|
+
export { signal };
|
package/dist/value/signal.mjs
CHANGED
|
@@ -1,59 +1,43 @@
|
|
|
1
|
-
import { NAME_SIGNAL } from "../constants.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { NAME_MORA, NAME_SIGNAL } from "../constants.mjs";
|
|
2
|
+
import { emitValue, getSimpleValue, handleSimpleValue } from "../helpers/value.mjs";
|
|
3
|
+
import { subscribe } from "../subscription.mjs";
|
|
4
|
+
import { reactive } from "./reactive.mjs";
|
|
5
|
+
import { getReadonlyInstance } from "./readonly.mjs";
|
|
4
6
|
//#region src/value/signal.ts
|
|
5
|
-
var Signal = class extends Reactive {
|
|
6
|
-
constructor(value, options) {
|
|
7
|
-
super(NAME_SIGNAL, value, options);
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* @inheritdoc
|
|
11
|
-
*/
|
|
12
|
-
get() {
|
|
13
|
-
return getValue(this.state);
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Set the value
|
|
17
|
-
* @param value New value
|
|
18
|
-
*/
|
|
19
|
-
set(value) {
|
|
20
|
-
setValue(this.state, value);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Update the value _(based on the current value)_
|
|
24
|
-
* @param callback Callback to update the value
|
|
25
|
-
*/
|
|
26
|
-
update(callback) {
|
|
27
|
-
this.set(callback(this.state.value));
|
|
28
|
-
}
|
|
29
|
-
};
|
|
30
7
|
function setAndEmit(state, value) {
|
|
31
8
|
if (!state.equal(state.value, value)) {
|
|
32
9
|
state.value = value;
|
|
33
10
|
emitValue(state);
|
|
34
11
|
}
|
|
35
12
|
}
|
|
36
|
-
function setValue(state, value) {
|
|
37
|
-
try {
|
|
38
|
-
let actual = value;
|
|
39
|
-
if (typeof value === "function") actual = value();
|
|
40
|
-
if (actual instanceof Promise) {
|
|
41
|
-
state.promise = actual;
|
|
42
|
-
actual.then((value) => {
|
|
43
|
-
if (actual === state.promise) {
|
|
44
|
-
state.promise = void 0;
|
|
45
|
-
setAndEmit(state, value);
|
|
46
|
-
}
|
|
47
|
-
}).catch(() => {
|
|
48
|
-
if (actual === state.promise) state.promise = void 0;
|
|
49
|
-
});
|
|
50
|
-
} else setAndEmit(state, actual);
|
|
51
|
-
} catch {}
|
|
52
|
-
}
|
|
53
13
|
function signal(value, options) {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
14
|
+
const [rx, state] = reactive(void 0, options);
|
|
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
|
+
const instance = {
|
|
24
|
+
...handlers,
|
|
25
|
+
asReadonly: (frozen) => getReadonlyInstance(state, readonlies, handlers, frozen === true),
|
|
26
|
+
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
|
+
}
|
|
34
|
+
};
|
|
35
|
+
Object.defineProperty(instance, NAME_MORA, {
|
|
36
|
+
enumerable: false,
|
|
37
|
+
value: NAME_SIGNAL
|
|
38
|
+
});
|
|
39
|
+
handleSimpleValue(state, value, setAndEmit);
|
|
40
|
+
return Object.freeze(instance);
|
|
57
41
|
}
|
|
58
42
|
//#endregion
|
|
59
|
-
export {
|
|
43
|
+
export { signal };
|
package/dist/value/store.d.mts
CHANGED
|
@@ -1,112 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { Key, PlainObject } from "@oscarpalmer/atoms/models";
|
|
4
|
-
|
|
1
|
+
import { ReactiveOptions, ReactiveStore } from "../models.mjs";
|
|
2
|
+
import { PlainObject } from "@oscarpalmer/atoms/models";
|
|
5
3
|
//#region src/value/store.d.ts
|
|
6
|
-
declare class Store<Value extends PlainObject> extends Reactive<Value, Value> {
|
|
7
|
-
#private;
|
|
8
|
-
constructor(value: Value, options?: ReactiveOptions<Value>);
|
|
9
|
-
/**
|
|
10
|
-
* @inheritdoc
|
|
11
|
-
*/
|
|
12
|
-
get(): Value;
|
|
13
|
-
/**
|
|
14
|
-
* Get a value by key
|
|
15
|
-
* @param key Key of the value to get
|
|
16
|
-
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
17
|
-
*/
|
|
18
|
-
get<Key extends keyof Value>(key: Key): Value[Key];
|
|
19
|
-
/**
|
|
20
|
-
* Get a value by key
|
|
21
|
-
* @param key Key of the value to get
|
|
22
|
-
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
23
|
-
*/
|
|
24
|
-
get(key: Key): unknown;
|
|
25
|
-
/**
|
|
26
|
-
* Notify dependents of changes
|
|
27
|
-
*
|
|
28
|
-
* _This bypasses equality checks and will immediately notify dependents.
|
|
29
|
-
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
30
|
-
*/
|
|
31
|
-
notify(): void;
|
|
32
|
-
/**
|
|
33
|
-
* Get the value _(without reactivity)_
|
|
34
|
-
* @returns The current value
|
|
35
|
-
*/
|
|
36
|
-
peek(): Value;
|
|
37
|
-
/**
|
|
38
|
-
* Get a value by key _(without reactivity)_
|
|
39
|
-
* @param key Key of the value to get
|
|
40
|
-
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
41
|
-
*/
|
|
42
|
-
peek<Key extends keyof Value>(key: Key): Value[Key];
|
|
43
|
-
/**
|
|
44
|
-
* Get a value by key _(without reactivity)_
|
|
45
|
-
* @param key Key of the value to get
|
|
46
|
-
* @returns Value for the specified key, or `undefined` if it doesn't exist
|
|
47
|
-
*/
|
|
48
|
-
peek(key: Key): unknown;
|
|
49
|
-
/**
|
|
50
|
-
* Set the value
|
|
51
|
-
* @param value New value _(defaults to an empty object)_
|
|
52
|
-
*/
|
|
53
|
-
set(value?: Value | (() => null | undefined | Value | Promise<null | undefined | Value>) | Promise<null | undefined | Value>): void;
|
|
54
|
-
/**
|
|
55
|
-
* Set a value by key
|
|
56
|
-
* @param key Key of the value to set
|
|
57
|
-
* @param value New value
|
|
58
|
-
*/
|
|
59
|
-
set<Key extends keyof Value>(key: Key, value: Value[Key] | (() => Value[Key] | Promise<Value[Key]>) | Promise<Value[Key]>): void;
|
|
60
|
-
/**
|
|
61
|
-
* Set a value by key
|
|
62
|
-
* @param key Key of the value to set
|
|
63
|
-
* @param value New value
|
|
64
|
-
*/
|
|
65
|
-
set(key: Key, value: unknown): void;
|
|
66
|
-
/**
|
|
67
|
-
* @inheritdoc
|
|
68
|
-
*/
|
|
69
|
-
subscribe(callback: (value: Value) => void): Unsubscribe;
|
|
70
|
-
/**
|
|
71
|
-
* Subscribe to changes for a specific key
|
|
72
|
-
* @param key Key of the value to subscribe to
|
|
73
|
-
* @param callback Callback for changes
|
|
74
|
-
* @returns Unsubscribe callback
|
|
75
|
-
*/
|
|
76
|
-
subscribe<Key extends keyof Value>(key: Key, callback: (value: Value[Key] | undefined) => void): Unsubscribe;
|
|
77
|
-
/**
|
|
78
|
-
* Subscribe to changes for a specific key
|
|
79
|
-
* @param key Key of the value to subscribe to
|
|
80
|
-
* @param callback Callback for changes
|
|
81
|
-
* @returns Unsubscribe callback
|
|
82
|
-
*/
|
|
83
|
-
subscribe(key: Key, callback: (value: unknown) => void): Unsubscribe;
|
|
84
|
-
/**
|
|
85
|
-
* Update the value _(based on the current value)_
|
|
86
|
-
* @param callback Callback to update the value
|
|
87
|
-
*/
|
|
88
|
-
update(callback: (value: Value) => Value): void;
|
|
89
|
-
}
|
|
90
4
|
/**
|
|
91
5
|
* Create a reactive store from a function result
|
|
6
|
+
*
|
|
92
7
|
* @param value Initial object value
|
|
93
8
|
* @param options Reactivity options
|
|
94
9
|
* @returns Reactive store
|
|
95
10
|
*/
|
|
96
|
-
declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>):
|
|
11
|
+
declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
|
|
97
12
|
/**
|
|
98
13
|
* Create a reactive store from a promise
|
|
14
|
+
*
|
|
99
15
|
* @param value Initial object value
|
|
100
16
|
* @param options Reactivity options
|
|
101
17
|
* @returns Reactive store
|
|
102
18
|
*/
|
|
103
|
-
declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>):
|
|
19
|
+
declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
|
|
104
20
|
/**
|
|
105
21
|
* Create a reactive store
|
|
22
|
+
*
|
|
106
23
|
* @param value Initial object value
|
|
107
24
|
* @param options Reactivity options
|
|
108
25
|
* @returns Reactive store
|
|
109
26
|
*/
|
|
110
|
-
declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>):
|
|
27
|
+
declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
|
|
111
28
|
//#endregion
|
|
112
|
-
export {
|
|
29
|
+
export { store };
|
package/dist/value/store.mjs
CHANGED
|
@@ -1,53 +1,12 @@
|
|
|
1
|
-
import { NAME_STORE } from "../constants.mjs";
|
|
1
|
+
import { NAME_MORA, NAME_STORE } from "../constants.mjs";
|
|
2
2
|
import { startBatch, stopBatch } from "../batch.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { getSimpleValue } from "../helpers/value.mjs";
|
|
4
|
+
import { noop, subscribe, unsubscribe } from "../subscription.mjs";
|
|
5
|
+
import { reactive } from "./reactive.mjs";
|
|
6
6
|
import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
|
|
7
|
+
import { getReadonlyInstance } from "./readonly.mjs";
|
|
7
8
|
import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
|
|
8
9
|
//#region src/value/store.ts
|
|
9
|
-
var Store = class extends Reactive {
|
|
10
|
-
#keyed = /* @__PURE__ */ new Map();
|
|
11
|
-
constructor(value, options) {
|
|
12
|
-
super(NAME_STORE, new Proxy(value, { set: (target, property, value) => setValueInProxy({
|
|
13
|
-
target,
|
|
14
|
-
property,
|
|
15
|
-
value,
|
|
16
|
-
isArray: false,
|
|
17
|
-
state: this.state
|
|
18
|
-
}) }), options);
|
|
19
|
-
}
|
|
20
|
-
get(key) {
|
|
21
|
-
return isKey(key) ? getReactiveValueInProxy(this, this.#keyed, key, false).get() : getValue(this.state);
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Notify dependents of changes
|
|
25
|
-
*
|
|
26
|
-
* _This bypasses equality checks and will immediately notify dependents.
|
|
27
|
-
* Use this only if you're modifying nested data that would be ignored by equality checks._
|
|
28
|
-
*/
|
|
29
|
-
notify() {
|
|
30
|
-
emityProxyValues(this.state, this.#keyed);
|
|
31
|
-
}
|
|
32
|
-
peek(key) {
|
|
33
|
-
return isKey(key) ? this.state.value[key] : { ...this.state.value };
|
|
34
|
-
}
|
|
35
|
-
set(first, second) {
|
|
36
|
-
setProxyValue(false, this.state, isStoreObject, isKey, setObject, setProperty, first, second);
|
|
37
|
-
}
|
|
38
|
-
subscribe(first, second) {
|
|
39
|
-
if (isKey(first) && typeof second === "function") return getReactiveValueInProxy(this, this.#keyed, first, false).subscribe(second);
|
|
40
|
-
return typeof first === "function" ? subscribe(this.state, first) : noop;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Update the value _(based on the current value)_
|
|
44
|
-
* @param callback Callback to update the value
|
|
45
|
-
*/
|
|
46
|
-
update(callback) {
|
|
47
|
-
const updated = callback(this.state.value);
|
|
48
|
-
if (updated == null || isPlainObject(updated)) setObject(this.state, updated);
|
|
49
|
-
}
|
|
50
|
-
};
|
|
51
10
|
function isStoreObject(value) {
|
|
52
11
|
return value == null || isPlainObject(value);
|
|
53
12
|
}
|
|
@@ -69,13 +28,63 @@ function setObject(state, value) {
|
|
|
69
28
|
}
|
|
70
29
|
stopBatch();
|
|
71
30
|
}
|
|
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
|
+
}
|
|
72
40
|
function setProperty(state, key, value) {
|
|
73
41
|
state.value[key] = value;
|
|
74
42
|
}
|
|
75
43
|
function store(value, options) {
|
|
76
|
-
const
|
|
44
|
+
const [rx, state] = reactive(void 0, options);
|
|
45
|
+
const keyed = /* @__PURE__ */ new Map();
|
|
46
|
+
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 = {
|
|
56
|
+
...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
|
+
}
|
|
81
|
+
};
|
|
82
|
+
Object.defineProperty(instance, NAME_MORA, {
|
|
83
|
+
enumerable: false,
|
|
84
|
+
value: NAME_STORE
|
|
85
|
+
});
|
|
77
86
|
instance.set(value);
|
|
78
|
-
return instance;
|
|
87
|
+
return Object.freeze(instance);
|
|
79
88
|
}
|
|
80
89
|
//#endregion
|
|
81
|
-
export {
|
|
90
|
+
export { store };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oscarpalmer/mora",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"description": "Signals and stuff…",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"reactive",
|
|
@@ -39,15 +39,14 @@
|
|
|
39
39
|
"test:leak": "vpx vp test run --detect-async-leaks --coverage"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@oscarpalmer/atoms": "^0.
|
|
42
|
+
"@oscarpalmer/atoms": "^0.190"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@
|
|
46
|
-
"@
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"typescript": "^5.9",
|
|
45
|
+
"@types/node": "^26.2",
|
|
46
|
+
"@vitest/coverage-istanbul": "4.1.10",
|
|
47
|
+
"jsdom": "^30",
|
|
48
|
+
"tsdown": "^0.22",
|
|
49
|
+
"typescript": "^6",
|
|
51
50
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
52
51
|
"vite-plus": "latest",
|
|
53
52
|
"vitest": "npm:@voidzero-dev/vite-plus-test@latest"
|
package/src/batch.ts
CHANGED
|
@@ -1,20 +1,33 @@
|
|
|
1
1
|
import {BATCH} from './constants';
|
|
2
2
|
import {runEffect} from './effect';
|
|
3
|
-
import {
|
|
3
|
+
import type {EffectState, Subscription} from './models';
|
|
4
4
|
|
|
5
5
|
export function flushHandlers(): void {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
if (BATCH.flushing) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
BATCH.flushing = true;
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
|
|
14
|
+
const handlers = [...BATCH.handlers];
|
|
15
|
+
const {length} = handlers;
|
|
16
|
+
|
|
17
|
+
BATCH.handlers.clear();
|
|
8
18
|
|
|
9
|
-
|
|
19
|
+
for (let index = 0; index < length; index += 1) {
|
|
20
|
+
const handler = handlers[index];
|
|
10
21
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
if (typeof (handler as Subscription).destroy === 'function') {
|
|
23
|
+
(handler as Subscription).callback((handler as Subscription).state.value);
|
|
24
|
+
} else {
|
|
25
|
+
runEffect(handler as EffectState);
|
|
26
|
+
}
|
|
16
27
|
}
|
|
17
28
|
}
|
|
29
|
+
} finally {
|
|
30
|
+
BATCH.flushing = false;
|
|
18
31
|
}
|
|
19
32
|
}
|
|
20
33
|
|
package/src/constants.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {Active, Batch} from './models';
|
|
3
|
-
import type {Subscription} from './subscription';
|
|
1
|
+
import type {Active, Batch, EffectState, Subscription} from './models';
|
|
4
2
|
|
|
5
3
|
export const ACTIVE: Active = {};
|
|
6
4
|
|
|
@@ -12,7 +10,8 @@ export const ARRAY_PEEK = 10;
|
|
|
12
10
|
|
|
13
11
|
export const BATCH: Batch = {
|
|
14
12
|
depth: 0,
|
|
15
|
-
|
|
13
|
+
flushing: false,
|
|
14
|
+
handlers: new Set<EffectState | Subscription>(),
|
|
16
15
|
};
|
|
17
16
|
|
|
18
17
|
export const METHODS_AFFECTING_LENGTH = new Set<string>(['pop', 'push', 'shift', 'unshift']);
|
|
@@ -34,10 +33,18 @@ export const NAME_EFFECT = 'effect';
|
|
|
34
33
|
|
|
35
34
|
export const NAME_MORA = '$mora';
|
|
36
35
|
|
|
36
|
+
export const NAME_READONLY = 'readonly';
|
|
37
|
+
|
|
37
38
|
export const NAME_SIGNAL = 'signal';
|
|
38
39
|
|
|
39
40
|
export const NAME_STORE = 'store';
|
|
40
41
|
|
|
41
|
-
export const NAME_ALL = new Set([
|
|
42
|
+
export const NAME_ALL = new Set([
|
|
43
|
+
NAME_ARRAY,
|
|
44
|
+
NAME_COMPUTED,
|
|
45
|
+
NAME_READONLY,
|
|
46
|
+
NAME_SIGNAL,
|
|
47
|
+
NAME_STORE,
|
|
48
|
+
]);
|
|
42
49
|
|
|
43
50
|
export const PROPERTY_LENGTH = 'length';
|