@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,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
- var Reactive = class {
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: void 0
8
+ value
16
9
  };
17
- constructor(name, value, options) {
18
- this.state.value = value;
19
- if (typeof options === "object" && typeof options.equal === "function") this.state.equal = options.equal;
20
- Object.defineProperty(this, NAME_MORA, { value: name });
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 { Reactive };
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 };
@@ -1,26 +1,8 @@
1
- import { Reactive } from "./reactive.mjs";
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 { Signal, signal };
28
+ export { signal };
@@ -1,59 +1,43 @@
1
- import { NAME_SIGNAL } from "../constants.mjs";
2
- import { Reactive } from "./reactive.mjs";
3
- import { emitValue, getValue } from "../helpers/value.mjs";
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 instance = new Signal(void 0, options);
55
- instance.set(value);
56
- return instance;
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 { Signal, signal };
43
+ export { signal };
@@ -1,112 +1,29 @@
1
- import { Reactive } from "./reactive.mjs";
2
- import { ReactiveOptions, Unsubscribe } from "../models.mjs";
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>): Store<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>): Store<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>): Store<Value>;
27
+ declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
111
28
  //#endregion
112
- export { Store, store };
29
+ export { store };
@@ -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 { noop, subscribe } from "../subscription.mjs";
4
- import { Reactive } from "./reactive.mjs";
5
- import { getValue } from "../helpers/value.mjs";
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 instance = new Store({}, options);
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 { Store, store };
90
+ export { store };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.29.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.184"
42
+ "@oscarpalmer/atoms": "^0.190"
43
43
  },
44
44
  "devDependencies": {
45
- "@oxlint/plugins": "^1.62",
46
- "@types/node": "^25.6",
47
- "@vitest/coverage-istanbul": "^4.1",
48
- "jsdom": "^29.1",
49
- "tsdown": "^0.21",
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 {isEffect} from './helpers/is';
3
+ import type {EffectState, Subscription} from './models';
4
4
 
5
5
  export function flushHandlers(): void {
6
- while (BATCH.depth === 0 && BATCH.handlers.size > 0) {
7
- const handlers = [...BATCH.handlers];
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
- BATCH.handlers.clear();
19
+ for (let index = 0; index < length; index += 1) {
20
+ const handler = handlers[index];
10
21
 
11
- for (const handler of handlers) {
12
- if (isEffect(handler)) {
13
- runEffect(handler);
14
- } else {
15
- handler.callback(handler.state.value);
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 {Effect} from './effect';
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
- handlers: new Set<Effect | Subscription>(),
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([NAME_ARRAY, NAME_COMPUTED, NAME_SIGNAL, NAME_STORE]);
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';