@oscarpalmer/mora 0.27.0 → 0.29.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.
@@ -1,5 +1,7 @@
1
- import { h as Reactive, p as Computed, s as ReactiveOptions, u as Unsubscribe } from "../models-QwNga87R.mjs";
2
- import { h as PROPERTY_LENGTH } from "../constants-Cy6_M9Vk.mjs";
1
+ import { Reactive } from "./reactive.mjs";
2
+ import { Computed } from "./computed.mjs";
3
+ import { ReactiveOptions, Unsubscribe } from "../models.mjs";
4
+ import { PROPERTY_LENGTH } from "../constants.mjs";
3
5
 
4
6
  //#region src/value/array.d.ts
5
7
  declare class ReactiveArray<Item> extends Reactive<Item[], Item> {
@@ -81,13 +83,13 @@ declare class ReactiveArray<Item> extends Reactive<Item[], Item> {
81
83
  * Set the value
82
84
  * @param value New array of items _(defaults to an empty array)_
83
85
  */
84
- set(value?: Item[]): void;
86
+ set(value?: Item[] | (() => null | undefined | Item[] | Promise<null | undefined | Item[]>) | Promise<null | undefined | Item[]>): void;
85
87
  /**
86
88
  * Set the value at an index
87
89
  * @param index Index of item to set __(if negative, starts from the end)_
88
90
  * @param value New item
89
91
  */
90
- set(index: number, value: Item): void;
92
+ set(index: number, value: Item | (() => Item | Promise<Item>) | Promise<Item>): void;
91
93
  /**
92
94
  * Set the length of the array
93
95
  * @param value New array length
@@ -129,6 +131,20 @@ declare class ReactiveArray<Item> extends Reactive<Item[], Item> {
129
131
  */
130
132
  update(callback: (value: Item[]) => Item[]): void;
131
133
  }
134
+ /**
135
+ * Create a reactive array from a function result
136
+ * @param value Initial array of items
137
+ * @param options Reactivity options
138
+ * @returns Reactive array
139
+ */
140
+ declare function array<Item>(value: () => Item[] | Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
141
+ /**
142
+ * Create a reactive array from a promise
143
+ * @param value Initial array of items
144
+ * @param options Reactivity options
145
+ * @returns Reactive array
146
+ */
147
+ declare function array<Item>(value: Promise<Item[]>, options?: ReactiveOptions<Item>): ReactiveArray<Item>;
132
148
  /**
133
149
  * Create a reactive array
134
150
  * @param value Initial array of items
@@ -3,7 +3,7 @@ import { noop, subscribe } from "../subscription.mjs";
3
3
  import { Reactive } from "./reactive.mjs";
4
4
  import { computed } from "./computed.mjs";
5
5
  import { emitValue, equalArrays, getValue } from "../helpers/value.mjs";
6
- import { emityProxyValues, getReactiveValueInProxy, setValueInProxy } from "../helpers/proxy.mjs";
6
+ import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
7
7
  import { signal } from "./signal.mjs";
8
8
  //#region src/value/array.ts
9
9
  var ReactiveArray = class extends Reactive {
@@ -72,7 +72,7 @@ var ReactiveArray = class extends Reactive {
72
72
  }
73
73
  peek(value) {
74
74
  if (value === "length") return this.#size.peek();
75
- return typeof value === "number" ? this.state.value.at(value) : [...this.state.value];
75
+ return typeof value === "number" ? this.state.value.at(value) : this.state.value.slice();
76
76
  }
77
77
  /**
78
78
  * Remove and return the last item of the array
@@ -90,9 +90,7 @@ var ReactiveArray = class extends Reactive {
90
90
  return this.state.value.push(...items);
91
91
  }
92
92
  set(first, second) {
93
- if (first == null || Array.isArray(first)) this.state.value.splice(0, this.state.value.length, ...first ?? []);
94
- else if (first === "length") this.length = second;
95
- else if (typeof first === "number" && !Number.isNaN(first)) setAtIndex(this.state.value, first, second);
93
+ setProxyValue(true, this.state, isArrayValue, isArrayIndex, setArray, setAtIndex, first, second);
96
94
  }
97
95
  /**
98
96
  * Remove and return the first item of the array
@@ -132,18 +130,20 @@ var ReactiveArray = class extends Reactive {
132
130
  if (updated == null || Array.isArray(updated)) this.set(updated);
133
131
  }
134
132
  };
135
- /**
136
- * Create a reactive array
137
- * @param value Initial array of items
138
- * @param options Reactivity options
139
- * @returns Reactive array
140
- */
141
133
  function array(value, options) {
142
- return new ReactiveArray(Array.isArray(value) ? value : [], options);
134
+ const instance = new ReactiveArray([], options);
135
+ instance.set(value);
136
+ return instance;
137
+ }
138
+ function isArrayIndex(value) {
139
+ return typeof value === "number";
140
+ }
141
+ function isArrayValue(value) {
142
+ return value == null || Array.isArray(value);
143
143
  }
144
144
  function updateArray(type, array, state, length) {
145
145
  const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
146
- const previousArray = affectsLength ? [] : [...array];
146
+ const previousArray = affectsLength ? [] : array.slice();
147
147
  const previousLength = array.length;
148
148
  return (...args) => {
149
149
  const result = array[type](...args);
@@ -154,9 +154,12 @@ function updateArray(type, array, state, length) {
154
154
  return result;
155
155
  };
156
156
  }
157
- function setAtIndex(array, index, value) {
158
- const actual = index < 0 ? array.length + index : index;
159
- if (actual > -1) array[actual] = value;
157
+ function setArray(state, value) {
158
+ state.value.splice(0, state.value.length, ...value ?? []);
159
+ }
160
+ function setAtIndex(state, index, value) {
161
+ const actual = index < 0 ? state.value.length + index : index;
162
+ if (actual > -1) state.value[actual] = value;
160
163
  }
161
164
  //#endregion
162
165
  export { ReactiveArray, array };
@@ -1,2 +1,21 @@
1
- import { m as computed, p as Computed } from "../models-QwNga87R.mjs";
1
+ import { Reactive } from "./reactive.mjs";
2
+ import { ReactiveOptions } from "../models.mjs";
3
+
4
+ //#region src/value/computed.d.ts
5
+ declare class Computed<Value> extends Reactive<Value> {
6
+ private readonly effect;
7
+ constructor(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>);
8
+ /**
9
+ * @inheritdoc
10
+ */
11
+ get(): Value;
12
+ }
13
+ /**
14
+ * Create a computed value
15
+ * @param callback Callback to compute the value
16
+ * @param options Reactivity options
17
+ * @returns Computed value
18
+ */
19
+ declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
20
+ //#endregion
2
21
  export { Computed, computed };
@@ -1,5 +1,6 @@
1
1
  import { ACTIVE, BATCH, NAME_COMPUTED } from "../constants.mjs";
2
2
  import { effect, runEffect } from "../effect.mjs";
3
+ import { flushHandlers } from "../batch.mjs";
3
4
  import { Reactive } from "./reactive.mjs";
4
5
  //#region src/value/computed.ts
5
6
  var Computed = class extends Reactive {
@@ -10,18 +11,10 @@ var Computed = class extends Reactive {
10
11
  constructor(callback, options) {
11
12
  super(NAME_COMPUTED, void 0, options);
12
13
  this.effect.instance = effect(() => {
13
- if (!this.effect.dirty) return;
14
- const previousComputed = ACTIVE.computed;
15
- ACTIVE.computed = this;
16
- const value = callback();
17
- ACTIVE.computed = previousComputed;
18
- if (!this.state.equal(this.state.value, value)) {
19
- this.state.value = value;
20
- for (const computed of this.state.computeds) computed.effect.dirty = true;
21
- for (const effect of this.state.effects) BATCH.handlers.add(effect);
22
- for (const [, subscription] of this.state.subscriptions) subscription.callback(value);
14
+ if (this.effect.dirty) {
15
+ setValue(this, this.state, callback);
16
+ this.effect.dirty = false;
23
17
  }
24
- this.effect.dirty = false;
25
18
  });
26
19
  }
27
20
  /**
@@ -43,5 +36,33 @@ var Computed = class extends Reactive {
43
36
  function computed(callback, options) {
44
37
  return new Computed(callback, options);
45
38
  }
39
+ function setAndEmit(state, value) {
40
+ if (state.equal(state.value, value)) return;
41
+ state.value = value;
42
+ for (const computed of state.computeds) computed.effect.dirty = true;
43
+ for (const effect of state.effects) BATCH.handlers.add(effect);
44
+ for (const [, subscription] of state.subscriptions) subscription.callback(value);
45
+ flushHandlers();
46
+ }
47
+ function setValue(instance, state, callback) {
48
+ const previousComputed = ACTIVE.computed;
49
+ ACTIVE.computed = instance;
50
+ try {
51
+ const value = callback();
52
+ if (value instanceof Promise) {
53
+ state.promise = value;
54
+ value.then((resolvedValue) => {
55
+ if (state.promise === value) {
56
+ state.promise = void 0;
57
+ setAndEmit(state, resolvedValue);
58
+ }
59
+ }).catch(() => {
60
+ if (state.promise === value) state.promise = void 0;
61
+ });
62
+ } else setAndEmit(state, value);
63
+ } finally {
64
+ ACTIVE.computed = previousComputed;
65
+ }
66
+ }
46
67
  //#endregion
47
68
  export { Computed, computed };
@@ -1,2 +1,51 @@
1
- import { h as Reactive } from "../models-QwNga87R.mjs";
1
+ import { ReactiveOptions, ReactiveState, Unsubscribe } from "../models.mjs";
2
+
3
+ //#region src/value/reactive.d.ts
4
+ declare abstract class Reactive<Value, Equal = Value> {
5
+ /**
6
+ * Internal name of the reactive value
7
+ *
8
+ * @internal
9
+ */
10
+ protected readonly $mora: string;
11
+ /**
12
+ * Internal state of the reactive value
13
+ *
14
+ * @internal
15
+ */
16
+ protected readonly state: ReactiveState<Value, Equal>;
17
+ constructor(name: string, value: Value, options?: ReactiveOptions<Equal>);
18
+ /**
19
+ * Get the value
20
+ * @return Current value
21
+ */
22
+ abstract get(): Value;
23
+ /**
24
+ * Get the value _(without reactivity)_
25
+ * @return Current value
26
+ */
27
+ peek(): Value;
28
+ /**
29
+ * Subscribe to changes
30
+ * @param callback Callback for changes
31
+ * @return Unsubscribe callback
32
+ */
33
+ subscribe(callback: (value: Value) => void): Unsubscribe;
34
+ /**
35
+ * JSON representation of the value
36
+ * @return JSON value
37
+ */
38
+ toJSON(): Value;
39
+ /**
40
+ * String representation of the value
41
+ * @return Value as string
42
+ */
43
+ toString(): string;
44
+ /**
45
+ * Unsubscribe from changes
46
+ * @param callback Callback to unsubscribe
47
+ */
48
+ unsubscribe(callback: (value: Value) => void): void;
49
+ }
50
+ //#endregion
2
51
  export { Reactive };
@@ -2,6 +2,11 @@ import { NAME_MORA } from "../constants.mjs";
2
2
  import { subscribe, unsubscribe } from "../subscription.mjs";
3
3
  //#region src/value/reactive.ts
4
4
  var Reactive = class {
5
+ /**
6
+ * Internal state of the reactive value
7
+ *
8
+ * @internal
9
+ */
5
10
  state = {
6
11
  computeds: /* @__PURE__ */ new Set(),
7
12
  effects: /* @__PURE__ */ new Set(),
@@ -1,2 +1,44 @@
1
- import { d as Signal, f as signal } from "../models-QwNga87R.mjs";
1
+ import { Reactive } from "./reactive.mjs";
2
+ import { ReactiveOptions } from "../models.mjs";
3
+
4
+ //#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
+ /**
23
+ * Create a reactive value from a function result
24
+ * @param value Initial value
25
+ * @param options Reactivity options
26
+ * @returns Reactive value
27
+ */
28
+ declare function signal<Value>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
29
+ /**
30
+ * Create a reactive value from a promise
31
+ * @param value Initial value
32
+ * @param options Reactivity options
33
+ * @returns Reactive value
34
+ */
35
+ declare function signal<Value>(value: Promise<Value>, options?: ReactiveOptions<Value>): Signal<Value>;
36
+ /**
37
+ * Create a reactive value
38
+ * @param value Initial value
39
+ * @param options Reactivity options
40
+ * @returns Reactive value
41
+ */
42
+ declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
43
+ //#endregion
2
44
  export { Signal, signal };
@@ -17,10 +17,7 @@ var Signal = class extends Reactive {
17
17
  * @param value New value
18
18
  */
19
19
  set(value) {
20
- if (!this.state.equal(this.state.value, value)) {
21
- this.state.value = value;
22
- emitValue(this.state);
23
- }
20
+ setValue(this.state, value);
24
21
  }
25
22
  /**
26
23
  * Update the value _(based on the current value)_
@@ -30,14 +27,33 @@ var Signal = class extends Reactive {
30
27
  this.set(callback(this.state.value));
31
28
  }
32
29
  };
33
- /**
34
- * Create a reactive value
35
- * @param value Initial value
36
- * @param options Reactivity options
37
- * @returns Reactive value
38
- */
30
+ function setAndEmit(state, value) {
31
+ if (!state.equal(state.value, value)) {
32
+ state.value = value;
33
+ emitValue(state);
34
+ }
35
+ }
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
+ }
39
53
  function signal(value, options) {
40
- return new Signal(value, options);
54
+ const instance = new Signal(void 0, options);
55
+ instance.set(value);
56
+ return instance;
41
57
  }
42
58
  //#endregion
43
59
  export { Signal, signal };
@@ -1,4 +1,5 @@
1
- import { h as Reactive, s as ReactiveOptions, u as Unsubscribe } from "../models-QwNga87R.mjs";
1
+ import { Reactive } from "./reactive.mjs";
2
+ import { ReactiveOptions, Unsubscribe } from "../models.mjs";
2
3
  import { Key, PlainObject } from "@oscarpalmer/atoms/models";
3
4
 
4
5
  //#region src/value/store.d.ts
@@ -49,13 +50,13 @@ declare class Store<Value extends PlainObject> extends Reactive<Value, Value> {
49
50
  * Set the value
50
51
  * @param value New value _(defaults to an empty object)_
51
52
  */
52
- set(value?: Value): void;
53
+ set(value?: Value | (() => null | undefined | Value | Promise<null | undefined | Value>) | Promise<null | undefined | Value>): void;
53
54
  /**
54
55
  * Set a value by key
55
56
  * @param key Key of the value to set
56
57
  * @param value New value
57
58
  */
58
- set<Key extends keyof Value>(key: Key, value: Value[Key]): void;
59
+ set<Key extends keyof Value>(key: Key, value: Value[Key] | (() => Value[Key] | Promise<Value[Key]>) | Promise<Value[Key]>): void;
59
60
  /**
60
61
  * Set a value by key
61
62
  * @param key Key of the value to set
@@ -86,6 +87,20 @@ declare class Store<Value extends PlainObject> extends Reactive<Value, Value> {
86
87
  */
87
88
  update(callback: (value: Value) => Value): void;
88
89
  }
90
+ /**
91
+ * Create a reactive store from a function result
92
+ * @param value Initial object value
93
+ * @param options Reactivity options
94
+ * @returns Reactive store
95
+ */
96
+ declare function store<Value extends PlainObject>(value: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Store<Value>;
97
+ /**
98
+ * Create a reactive store from a promise
99
+ * @param value Initial object value
100
+ * @param options Reactivity options
101
+ * @returns Reactive store
102
+ */
103
+ declare function store<Value extends PlainObject>(value: Promise<Value>, options?: ReactiveOptions<Value>): Store<Value>;
89
104
  /**
90
105
  * Create a reactive store
91
106
  * @param value Initial object value
@@ -1,4 +1,5 @@
1
1
  import { NAME_STORE } from "../constants.mjs";
2
+ import { startBatch, stopBatch } from "../batch.mjs";
2
3
  import { noop, subscribe } from "../subscription.mjs";
3
4
  import { Reactive } from "./reactive.mjs";
4
5
  import { getValue } from "../helpers/value.mjs";
@@ -32,8 +33,7 @@ var Store = class extends Reactive {
32
33
  return isKey(key) ? this.state.value[key] : { ...this.state.value };
33
34
  }
34
35
  set(first, second) {
35
- if (isKey(first)) this.state.value[first] = second;
36
- else if (first == null || isPlainObject(first)) setProxyValue(this.state.value, first ?? {});
36
+ setProxyValue(false, this.state, isStoreObject, isKey, setObject, setProperty, first, second);
37
37
  }
38
38
  subscribe(first, second) {
39
39
  if (isKey(first) && typeof second === "function") return getReactiveValueInProxy(this, this.#keyed, first, false).subscribe(second);
@@ -44,18 +44,38 @@ var Store = class extends Reactive {
44
44
  * @param callback Callback to update the value
45
45
  */
46
46
  update(callback) {
47
- const updated = callback({ ...this.state.value });
48
- if (updated == null || isPlainObject(updated)) setProxyValue(this.state.value, updated ?? {});
47
+ const updated = callback(this.state.value);
48
+ if (updated == null || isPlainObject(updated)) setObject(this.state, updated);
49
49
  }
50
50
  };
51
- /**
52
- * Create a reactive store
53
- * @param value Initial object value
54
- * @param options Reactivity options
55
- * @returns Reactive store
56
- */
51
+ function isStoreObject(value) {
52
+ return value == null || isPlainObject(value);
53
+ }
54
+ function setObject(state, value) {
55
+ startBatch();
56
+ const actual = value ?? {};
57
+ const proxy = state.value;
58
+ const proxyKeys = Object.keys(proxy);
59
+ const actualKeys = Object.keys(actual);
60
+ let { length } = proxyKeys;
61
+ for (let index = 0; index < length; index += 1) {
62
+ const key = proxyKeys[index];
63
+ proxy[key] = actualKeys.includes(key) ? actual[key] : void 0;
64
+ }
65
+ length = actualKeys.length;
66
+ for (let index = 0; index < length; index += 1) {
67
+ const key = actualKeys[index];
68
+ if (!proxyKeys.includes(key)) proxy[key] = actual[key];
69
+ }
70
+ stopBatch();
71
+ }
72
+ function setProperty(state, key, value) {
73
+ state.value[key] = value;
74
+ }
57
75
  function store(value, options) {
58
- return new Store(isPlainObject(value) ? value : {}, options);
76
+ const instance = new Store({}, options);
77
+ instance.set(value);
78
+ return instance;
59
79
  }
60
80
  //#endregion
61
81
  export { Store, store };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Signals and stuff…",
5
5
  "keywords": [
6
6
  "reactive",
@@ -35,16 +35,17 @@
35
35
  "build": "vpx vp run tsdown:build && vpx vp pack",
36
36
  "tsdown:build": "vpx tsdown -c ./tsdown.config.ts",
37
37
  "tsdown:watch": "vpx tsdown -c ./tsdown.config.ts --watch",
38
- "test": "vpx vp test run --coverage",
38
+ "test": "npx vp test run --coverage",
39
39
  "test:leak": "vpx vp test run --detect-async-leaks --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@oscarpalmer/atoms": "^0.165"
42
+ "@oscarpalmer/atoms": "^0.184"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^25.5",
45
+ "@oxlint/plugins": "^1.62",
46
+ "@types/node": "^25.6",
46
47
  "@vitest/coverage-istanbul": "^4.1",
47
- "jsdom": "^28.1",
48
+ "jsdom": "^29.1",
48
49
  "tsdown": "^0.21",
49
50
  "typescript": "^5.9",
50
51
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
package/src/effect.ts CHANGED
@@ -3,8 +3,18 @@ import {ACTIVE, NAME_EFFECT, NAME_MORA} from './constants';
3
3
  import type {EffectState, InternalEffect} from './models';
4
4
 
5
5
  export class Effect {
6
+ /**
7
+ * Internal name of the effect
8
+ *
9
+ * @internal
10
+ */
6
11
  declare private readonly $mora: string;
7
12
 
13
+ /**
14
+ * Internal state of the effect
15
+ *
16
+ * @internal
17
+ */
8
18
  declare private readonly state: EffectState;
9
19
 
10
20
  constructor(callback: GenericCallback) {
@@ -1,5 +1,4 @@
1
1
  import type {ArrayOrPlainObject, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
- import {startBatch, stopBatch} from '../batch';
3
2
  import {PROPERTY_LENGTH} from '../constants';
4
3
  import type {InternalComputed, ReactiveState, SetValueInProxyParameters} from '../models';
5
4
  import type {ReactiveArray} from '../value/array';
@@ -66,33 +65,81 @@ export function getReactiveValueInProxy(
66
65
  return item;
67
66
  }
68
67
 
69
- export function setProxyValue(proxy: ArrayOrPlainObject, value: ArrayOrPlainObject): void {
70
- startBatch();
68
+ export function setProxyValue<Value, Item = Value>(
69
+ array: boolean,
70
+ state: ReactiveState<Value, Item>,
71
+ isObject: (value: unknown) => value is Value | undefined,
72
+ isProperty: (value: unknown) => boolean,
73
+ setObject: (state: ReactiveState<Value, Item>, value: Value | undefined) => void,
74
+ setProperty: (state: ReactiveState<Value, Item>, property: unknown, value: Item) => void,
75
+ first?: unknown,
76
+ second?: unknown,
77
+ ): void {
78
+ if (array && first === PROPERTY_LENGTH) {
79
+ (state.value as unknown[]).length = second as number;
71
80
 
72
- const proxyKeys = Object.keys(proxy);
73
- const valueKeys = Object.keys(value);
81
+ return;
82
+ }
74
83
 
75
- let {length} = proxyKeys;
84
+ if (isObject(first)) {
85
+ setObject(state, first);
76
86
 
77
- for (let index = 0; index < length; index += 1) {
78
- const key = proxyKeys[index];
79
-
80
- (proxy as PlainObject)[key] = valueKeys.includes(key) ? (value as PlainObject)[key] : undefined;
87
+ return;
81
88
  }
82
89
 
83
- length = valueKeys.length;
90
+ const property = isProperty(first);
84
91
 
85
- for (let index = 0; index < length; index += 1) {
86
- const key = valueKeys[index];
92
+ if (array && property && Number.isNaN(first)) {
93
+ return;
94
+ }
87
95
 
88
- if (!proxyKeys.includes(key)) {
89
- const keyedValue = (value as PlainObject)[key];
96
+ let actual = property ? second : first;
90
97
 
91
- (proxy as PlainObject)[key] = keyedValue;
98
+ if (typeof actual === 'function') {
99
+ try {
100
+ actual = (actual as Function)();
101
+ } catch {
102
+ return;
92
103
  }
93
104
  }
94
105
 
95
- stopBatch();
106
+ if (actual instanceof Promise) {
107
+ if (property) {
108
+ state.promises ??= new Map();
109
+
110
+ state.promises.set(first as Key, actual as Promise<never>);
111
+ } else {
112
+ state.promise = actual as Promise<never>;
113
+ }
114
+
115
+ void actual
116
+ .then(value => {
117
+ if (property && state.promises!.get(first as Key) === actual) {
118
+ state.promises!.delete(first as Key);
119
+
120
+ setProperty(state, first, value);
121
+
122
+ return;
123
+ }
124
+
125
+ if (!property && isObject(value) && state.promise === actual) {
126
+ state.promise = undefined;
127
+
128
+ setObject(state, value);
129
+ }
130
+ })
131
+ .catch(() => {
132
+ if (property && state.promises!.get(first as Key) === actual) {
133
+ state.promises!.delete(first as Key);
134
+ } else if (!property && state.promise === actual) {
135
+ state.promise = undefined;
136
+ }
137
+ });
138
+ } else if (property) {
139
+ setProperty(state, first, actual as Item);
140
+ } else if (isObject(actual)) {
141
+ setObject(state, actual);
142
+ }
96
143
  }
97
144
 
98
145
  export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
@@ -1,3 +1,4 @@
1
+ import {round} from '@oscarpalmer/atoms/math';
1
2
  import {flushHandlers} from '../batch';
2
3
  import {ACTIVE, ARRAY_OFFSET, ARRAY_PEEK, ARRAY_THRESHOLD, BATCH} from '../constants';
3
4
  import type {InternalComputed, ReactiveState} from '../models';
@@ -34,7 +35,7 @@ export function equalArrays<Value>(
34
35
  let offset = 0;
35
36
 
36
37
  if (length >= ARRAY_THRESHOLD) {
37
- offset = Math.round(length / ARRAY_PEEK);
38
+ offset = round(length / ARRAY_PEEK);
38
39
  offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
39
40
 
40
41
  for (let index = 0; index < offset; index += 1) {
package/src/models.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {GenericCallback} from '@oscarpalmer/atoms/models';
1
+ import type {GenericCallback, Key} from '@oscarpalmer/atoms/models';
2
2
  import type {Effect} from './effect';
3
3
  import type {Subscription} from './subscription';
4
4
  import type {Computed} from './value/computed';
@@ -47,6 +47,8 @@ export type ReactiveState<Value, Equal> = {
47
47
  computeds: Set<Computed<unknown>>;
48
48
  effects: Set<Effect>;
49
49
  equal: (first: Equal, second: Equal) => boolean;
50
+ promise?: Promise<Value>;
51
+ promises?: Map<Key, Promise<never>>;
50
52
  subscriptions: Map<GenericCallback, Subscription>;
51
53
  value: Value;
52
54
  };