@oscarpalmer/mora 0.31.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 (58) hide show
  1. package/dist/batch.d.mts +4 -5
  2. package/dist/batch.mjs +4 -2
  3. package/dist/constants.d.mts +31 -19
  4. package/dist/constants.mjs +19 -2
  5. package/dist/effect.d.mts +5 -5
  6. package/dist/effect.mjs +8 -5
  7. package/dist/helpers/is.d.mts +14 -9
  8. package/dist/helpers/is.mjs +6 -0
  9. package/dist/helpers/misc.d.mts +4 -0
  10. package/dist/helpers/misc.mjs +9 -0
  11. package/dist/helpers/proxy.d.mts +10 -10
  12. package/dist/helpers/proxy.mjs +83 -27
  13. package/dist/helpers/subscription.d.mts +8 -0
  14. package/dist/helpers/subscription.mjs +30 -0
  15. package/dist/helpers/value.d.mts +10 -7
  16. package/dist/helpers/value.mjs +56 -17
  17. package/dist/index.d.mts +3 -2
  18. package/dist/models.d.mts +82 -101
  19. package/dist/models.mjs +1 -0
  20. package/dist/mora.full.mjs +659 -412
  21. package/dist/value/array.d.mts +5 -5
  22. package/dist/value/array.mjs +70 -109
  23. package/dist/value/computed.d.mts +5 -4
  24. package/dist/value/computed.mjs +45 -31
  25. package/dist/value/readonly.d.mts +4 -6
  26. package/dist/value/readonly.mjs +34 -31
  27. package/dist/value/signal.d.mts +5 -5
  28. package/dist/value/signal.mjs +28 -34
  29. package/dist/value/store.d.mts +5 -5
  30. package/dist/value/store.mjs +25 -83
  31. package/package.json +6 -6
  32. package/src/batch.ts +15 -5
  33. package/src/constants.ts +36 -2
  34. package/src/effect.ts +20 -11
  35. package/src/helpers/is.ts +16 -6
  36. package/src/helpers/misc.ts +15 -0
  37. package/src/helpers/proxy.ts +151 -74
  38. package/src/helpers/subscription.ts +89 -0
  39. package/src/helpers/value.ts +121 -30
  40. package/src/index.ts +1 -1
  41. package/src/models.ts +81 -98
  42. package/src/value/array.ts +116 -226
  43. package/src/value/computed.ts +96 -43
  44. package/src/value/readonly.ts +68 -54
  45. package/src/value/signal.ts +54 -46
  46. package/src/value/store.ts +42 -168
  47. package/types/constants.d.ts +1 -1
  48. package/types/index.d.ts +1 -1
  49. package/types/value/array.d.ts +1 -1
  50. package/types/value/computed.d.ts +0 -3
  51. package/types/value/signal.d.ts +1 -1
  52. package/types/value/store.d.ts +1 -1
  53. package/dist/subscription.d.mts +0 -7
  54. package/dist/subscription.mjs +0 -27
  55. package/dist/value/reactive.d.mts +0 -5
  56. package/dist/value/reactive.mjs +0 -16
  57. package/src/subscription.ts +0 -45
  58. package/src/value/reactive.ts +0 -24
@@ -1,90 +1,32 @@
1
- import { NAME_MORA, NAME_STORE } from "../constants.mjs";
2
- import { startBatch, stopBatch } from "../batch.mjs";
3
- import { getSimpleValue } from "../helpers/value.mjs";
4
- import { noop, subscribe, unsubscribe } from "../subscription.mjs";
5
- import { reactive } from "./reactive.mjs";
6
- import { emityProxyValues, getReactiveValueInProxy, setProxyValue, setValueInProxy } from "../helpers/proxy.mjs";
1
+ import { NAME_MORA, NAME_STORE, SYMBOL_STATE } from "../constants.mjs";
2
+ import { getStringValue } from "../helpers/value.mjs";
3
+ import { getState } from "../helpers/misc.mjs";
4
+ import { subscribeToProxy } from "../helpers/subscription.mjs";
5
+ import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
7
6
  import { getReadonlyInstance } from "./readonly.mjs";
8
- import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
9
7
  //#region src/value/store.ts
10
- function isStoreObject(value) {
11
- return value == null || isPlainObject(value);
12
- }
13
- function setObject(state, value) {
14
- startBatch();
15
- const actual = value ?? {};
16
- const proxy = state.value;
17
- const proxyKeys = Object.keys(proxy);
18
- const actualKeys = Object.keys(actual);
19
- let { length } = proxyKeys;
20
- for (let index = 0; index < length; index += 1) {
21
- const key = proxyKeys[index];
22
- proxy[key] = actualKeys.includes(key) ? actual[key] : void 0;
23
- }
24
- length = actualKeys.length;
25
- for (let index = 0; index < length; index += 1) {
26
- const key = actualKeys[index];
27
- if (!proxyKeys.includes(key)) proxy[key] = actual[key];
28
- }
29
- stopBatch();
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
- }
40
- function setProperty(state, key, value) {
41
- state.value[key] = value;
42
- }
43
- function store(value, 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,
8
+ function ReactiveStore(value, options) {
9
+ this[SYMBOL_STATE] = {
10
+ ...getState(void 0, options),
53
11
  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
12
  };
82
- Object.defineProperty(instance, NAME_MORA, {
83
- enumerable: false,
84
- value: NAME_STORE
85
- });
86
- instance.set(value);
87
- return Object.freeze(instance);
13
+ this[SYMBOL_STATE].value = new Proxy({}, { set: (target, property, value) => setValueInProxy(this, target, property, value) });
14
+ setProxyValue.call(this, value);
15
+ }
16
+ ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
17
+ ReactiveStore.prototype.asReadonly = getReadonlyInstance;
18
+ ReactiveStore.prototype.get = getValueInProxy;
19
+ ReactiveStore.prototype.notify = emitProxyValues;
20
+ ReactiveStore.prototype.peek = peekValueInProxy;
21
+ ReactiveStore.prototype.set = setProxyValue;
22
+ ReactiveStore.prototype.subscribe = subscribeToProxy;
23
+ ReactiveStore.prototype.toString = getStringValue;
24
+ ReactiveStore.prototype.update = updateProxyValue;
25
+ ReactiveStore.prototype.toJSON = function() {
26
+ return this[SYMBOL_STATE].value;
27
+ };
28
+ function store(value, options) {
29
+ return new ReactiveStore(value, options);
88
30
  }
89
31
  //#endregion
90
32
  export { store };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.31.0",
3
+ "version": "0.33.0",
4
4
  "description": "Signals and stuff…",
5
5
  "keywords": [
6
6
  "reactive",
@@ -39,17 +39,17 @@
39
39
  "test:leak": "vpx vp test run --detect-async-leaks --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@oscarpalmer/atoms": "^0.190"
42
+ "@oscarpalmer/atoms": "^0.202"
43
43
  },
44
44
  "devDependencies": {
45
- "@types/node": "^26.2",
46
- "@vitest/coverage-istanbul": "4.1.10",
45
+ "@types/node": "^26.5",
46
+ "@vitest/coverage-istanbul": "^4.1",
47
47
  "jsdom": "^30",
48
- "tsdown": "^0.22",
48
+ "tsdown": "^0.23",
49
49
  "typescript": "^6",
50
50
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
51
51
  "vite-plus": "latest",
52
52
  "vitest": "npm:@voidzero-dev/vite-plus-test@latest"
53
53
  },
54
- "packageManager": "npm@11.11.1"
54
+ "packageManager": "npm@11.19.0"
55
55
  }
package/src/batch.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import {BATCH} from './constants';
2
2
  import {runEffect} from './effect';
3
- import type {EffectState, Subscription} from './models';
3
+ import {getFrozenValue, peekSignalValue} from './helpers/value';
4
+ import type {EffectState, StoredSubscription} from './models';
5
+
6
+ // #region Functions
4
7
 
5
8
  export function flushHandlers(): void {
6
9
  if (BATCH.flushing) {
@@ -17,10 +20,15 @@ export function flushHandlers(): void {
17
20
  BATCH.handlers.clear();
18
21
 
19
22
  for (let index = 0; index < length; index += 1) {
20
- const handler = handlers[index];
21
-
22
- if (typeof (handler as Subscription).destroy === 'function') {
23
- (handler as Subscription).callback((handler as Subscription).state.value);
23
+ const [, handler] = handlers[index];
24
+ const subscription = handler as StoredSubscription;
25
+
26
+ if (typeof subscription.frozen === 'boolean') {
27
+ subscription.callback(
28
+ subscription.frozen
29
+ ? getFrozenValue(subscription.state.value)
30
+ : peekSignalValue.call(subscription.instance, subscription.copy),
31
+ );
24
32
  } else {
25
33
  runEffect(handler as EffectState);
26
34
  }
@@ -50,3 +58,5 @@ export function stopBatch(): void {
50
58
 
51
59
  flushHandlers();
52
60
  }
61
+
62
+ // #endregion
package/src/constants.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type {Active, Batch, EffectState, Subscription} from './models';
1
+ import type {Active, Batch, SubscriptionType} from './models';
2
+
3
+ // #region Variables
2
4
 
3
5
  export const ACTIVE: Active = {};
4
6
 
@@ -11,7 +13,7 @@ export const ARRAY_PEEK = 10;
11
13
  export const BATCH: Batch = {
12
14
  depth: 0,
13
15
  flushing: false,
14
- handlers: new Set<EffectState | Subscription>(),
16
+ handlers: new Map(),
15
17
  };
16
18
 
17
19
  export const METHODS_AFFECTING_LENGTH = new Set<string>(['pop', 'push', 'shift', 'unshift']);
@@ -39,6 +41,8 @@ export const NAME_SIGNAL = 'signal';
39
41
 
40
42
  export const NAME_STORE = 'store';
41
43
 
44
+ export const NAME_SUBSCRIPTION = 'subscription';
45
+
42
46
  export const NAME_ALL = new Set([
43
47
  NAME_ARRAY,
44
48
  NAME_COMPUTED,
@@ -48,3 +52,33 @@ export const NAME_ALL = new Set([
48
52
  ]);
49
53
 
50
54
  export const PROPERTY_LENGTH = 'length';
55
+
56
+ export const SYMBOL_EFFECT = Symbol('effect');
57
+
58
+ export const SYMBOL_STATE = Symbol('state');
59
+
60
+ export const SUBSCRIPTION_PROPERTY = {
61
+ key: NAME_MORA,
62
+ value: NAME_SUBSCRIPTION,
63
+ };
64
+
65
+ export const SUBSCRIPTION_TYPE_COPY: SubscriptionType = 'copy';
66
+
67
+ export const SUBSCRIPTION_TYPE_FROZEN: SubscriptionType = 'frozen';
68
+
69
+ export const SUBSCRIPTION_TYPE_ORIGINAL: SubscriptionType = 'original';
70
+
71
+ export const SUBSCRIPTION_TYPE_READONLY: SubscriptionType = 'readonly';
72
+
73
+ export const SUBSCRIPTION_TYPES_COPY = new Set<SubscriptionType>([
74
+ SUBSCRIPTION_TYPE_COPY,
75
+ SUBSCRIPTION_TYPE_READONLY,
76
+ ]);
77
+
78
+ export const SUBSCRIPTION_TYPES: Set<SubscriptionType> = new Set([
79
+ ...SUBSCRIPTION_TYPES_COPY,
80
+ SUBSCRIPTION_TYPE_FROZEN,
81
+ SUBSCRIPTION_TYPE_ORIGINAL,
82
+ ]);
83
+
84
+ // #endregion
package/src/effect.ts CHANGED
@@ -1,7 +1,21 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {ACTIVE, NAME_EFFECT} from './constants';
2
+ import {ACTIVE, NAME_EFFECT, NAME_MORA, SYMBOL_STATE} from './constants';
3
3
  import type {Effect, EffectState} from './models';
4
4
 
5
+ // #region Instance
6
+
7
+ function Effect(this: any, callback: GenericCallback) {
8
+ this[SYMBOL_STATE] = {callback};
9
+
10
+ runEffect(this[SYMBOL_STATE]);
11
+ }
12
+
13
+ Effect.prototype[NAME_MORA] = NAME_EFFECT;
14
+
15
+ // #endregion
16
+
17
+ // #region Functions
18
+
5
19
  /**
6
20
  * Create an effect
7
21
  *
@@ -17,17 +31,10 @@ export function effect(callback: GenericCallback): Effect {
17
31
  }
18
32
 
19
33
  function getEffect(callback: GenericCallback): [Effect, EffectState] {
20
- const state: EffectState = {
21
- callback,
22
- };
23
-
24
- const instance = Object.freeze({
25
- $mora: NAME_EFFECT,
26
- });
34
+ // @ts-expect-error All good, no worries :-)
35
+ const instance = new Effect(callback);
27
36
 
28
- runEffect(state);
29
-
30
- return [instance, state];
37
+ return [instance, instance[SYMBOL_STATE]];
31
38
  }
32
39
 
33
40
  export function internalEffect(callback: GenericCallback): EffectState {
@@ -45,3 +52,5 @@ export function runEffect(state: EffectState): void {
45
52
  ACTIVE.effect = previousEffect;
46
53
  }
47
54
  }
55
+
56
+ // #endregion
package/src/helpers/is.ts CHANGED
@@ -19,13 +19,15 @@ import type {
19
19
  Signal,
20
20
  } from '../models';
21
21
 
22
+ // #region Functions
23
+
22
24
  /**
23
25
  * Is the value a computed signal?
24
26
  *
25
27
  * @param value Value to check
26
28
  * @returns True if value is a {@link Computed}
27
29
  */
28
- export function isComputed<Value>(value: unknown): value is Computed<Value> {
30
+ export function isComputed<Value = unknown>(value: unknown): value is Computed<Value> {
29
31
  return isMora<Computed<Value>>(value, NAME_COMPUTED);
30
32
  }
31
33
 
@@ -56,7 +58,7 @@ function isMora<Instance>(value: unknown, name: string | Set<string>): value is
56
58
  * @param value Value to check
57
59
  * @returns True if value is a {@link Reactive}
58
60
  */
59
- export function isReactive<Value>(value: unknown): value is Reactive<Value> {
61
+ export function isReactive<Value = unknown>(value: unknown): value is Reactive<Value> {
60
62
  return isMora<Reactive<Value>>(value, NAME_ALL);
61
63
  }
62
64
 
@@ -66,7 +68,7 @@ export function isReactive<Value>(value: unknown): value is Reactive<Value> {
66
68
  * @param value Value to check
67
69
  * @returns True if value is a {@link Signal}
68
70
  */
69
- export function isSignal<Value>(value: unknown): value is Signal<Value> {
71
+ export function isSignal<Value = unknown>(value: unknown): value is Signal<Value> {
70
72
  return isMora<Signal<Value>>(value, NAME_SIGNAL);
71
73
  }
72
74
 
@@ -76,7 +78,7 @@ export function isSignal<Value>(value: unknown): value is Signal<Value> {
76
78
  * @param value Value to check
77
79
  * @returns True if value is a {@link ReactiveArray}
78
80
  */
79
- export function isReactiveArray<Item>(value: unknown): value is ReactiveArray<Item> {
81
+ export function isReactiveArray<Item = unknown>(value: unknown): value is ReactiveArray<Item> {
80
82
  return isMora<ReactiveArray<Item>>(value, NAME_ARRAY);
81
83
  }
82
84
 
@@ -86,12 +88,20 @@ export function isReactiveArray<Item>(value: unknown): value is ReactiveArray<It
86
88
  * @param value Value to check
87
89
  * @returns True if value is a {@link Store}
88
90
  */
89
- export function isReactiveStore<Value extends PlainObject>(
91
+ export function isReactiveStore<Value extends PlainObject = PlainObject>(
90
92
  value: unknown,
91
93
  ): value is ReactiveStore<Value> {
92
94
  return isMora<ReactiveStore<Value>>(value, NAME_STORE);
93
95
  }
94
96
 
95
- export function isReadonlySignal<Value>(value: unknown): value is ReadonlySignal<Value> {
97
+ /**
98
+ * Is the value a readonly signal?
99
+ *
100
+ * @param value Value to check
101
+ * @returns True if value is a {@link ReadonlySignal}
102
+ */
103
+ export function isReadonlySignal<Value = unknown>(value: unknown): value is ReadonlySignal<Value> {
96
104
  return isMora<ReadonlySignal<Value>>(value, NAME_READONLY);
97
105
  }
106
+
107
+ // #endregion
@@ -0,0 +1,15 @@
1
+ import type {ReactiveOptions, ReactiveState} from '../models';
2
+
3
+ // #region Functions
4
+
5
+ export function getState(value: unknown, options?: ReactiveOptions<unknown>): ReactiveState {
6
+ return {
7
+ value,
8
+ equal:
9
+ typeof options === 'object' && typeof options?.equal === 'function'
10
+ ? options.equal
11
+ : undefined,
12
+ };
13
+ }
14
+
15
+ // #endregion
@@ -1,100 +1,164 @@
1
+ import {isKey, isPlainObject} from '@oscarpalmer/atoms/is';
1
2
  import type {ArrayOrPlainObject, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
- import {PROPERTY_LENGTH} from '../constants';
3
+ import {startBatch, stopBatch} from '../batch';
4
+ import {PROPERTY_LENGTH, SYMBOL_STATE} from '../constants';
3
5
  import type {
4
6
  Computed,
5
- ComputedEffect,
6
7
  ReactiveArray,
7
- ReactiveState,
8
+ ReactiveProxyState,
8
9
  ReactiveStore,
9
- SetValueInProxyParameters,
10
+ InternalProxy,
10
11
  } from '../models';
11
12
  import {internalComputed} from '../value/computed';
12
- import {emitValue} from './value';
13
-
14
- export function emityProxyValues<Value>(
15
- state: ReactiveState<Value, Value>,
16
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
17
- ): void;
18
-
19
- export function emityProxyValues<Value>(
20
- state: ReactiveState<Value[], Value>,
21
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
22
- ): void;
23
-
24
- export function emityProxyValues(
25
- state: ReactiveState<unknown, unknown>,
26
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
27
- ): void {
28
- const values = [...mapped.values()];
13
+ import {emitValue, getSignalValue, peekSignalValue} from './value';
14
+
15
+ // #region Functions
16
+
17
+ export function emitProxyValues(this: InternalProxy): void {
18
+ const state = this[SYMBOL_STATE];
19
+
20
+ state.mapped ??= new Map();
21
+
22
+ const values = [...state.mapped.values()];
29
23
  const {length} = values;
30
24
 
31
25
  for (let index = 0; index < length; index += 1) {
32
26
  values[index][1].dirty = true;
33
27
  }
34
28
 
35
- emitValue(state);
29
+ emitValue(this);
36
30
  }
37
31
 
38
- export function getReactiveValueInProxy<Value, Result = Value>(
39
- array: ReactiveArray<Value>,
40
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
41
- index: number,
42
- isArray: true,
43
- ): Computed<Result>;
44
-
45
- export function getReactiveValueInProxy<Value extends PlainObject>(
46
- store: ReactiveStore<Value>,
47
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
48
- key: Key,
49
- isArray: false,
50
- ): Computed<unknown>;
51
-
52
32
  export function getReactiveValueInProxy(
53
- reactive: ReactiveArray<unknown> | ReactiveStore<PlainObject>,
54
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
33
+ instance: ReactiveArray<unknown> | ReactiveStore<unknown>,
55
34
  key: Key,
56
- isArray: boolean,
57
35
  ): Computed<unknown> {
58
- let item = mapped.get(key);
36
+ const state = (instance as unknown as InternalProxy)[SYMBOL_STATE];
37
+
38
+ state.mapped ??= new Map();
39
+
40
+ const mapKey = String(key);
41
+
42
+ let item = state.mapped.get(mapKey);
59
43
 
60
44
  if (item == null) {
61
45
  item = internalComputed(() =>
62
- isArray
63
- ? (reactive.get() as unknown[]).at(key as number)
64
- : (reactive.get() as PlainObject)[key],
46
+ state.isArray
47
+ ? (instance.get() as unknown[]).at(key as number)
48
+ : (instance.get() as PlainObject)[key],
65
49
  );
66
50
 
67
- mapped.set(key, item);
51
+ state.mapped.set(mapKey, item);
68
52
  }
69
53
 
70
54
  return item[0];
71
55
  }
72
56
 
73
- export function setProxyValue<Value, Item = Value>(
74
- array: boolean,
75
- state: ReactiveState<Value, Item>,
76
- isObject: (value: unknown) => value is Value | undefined,
77
- isProperty: (value: unknown) => boolean,
78
- setObject: (state: ReactiveState<Value, Item>, value: Value | undefined) => void,
79
- setProperty: (state: ReactiveState<Value, Item>, property: unknown, value: Item) => void,
80
- first?: unknown,
81
- second?: unknown,
82
- ): void {
83
- if (array && first === PROPERTY_LENGTH) {
57
+ export function getValueInProxy(this: InternalProxy, first?: unknown): unknown {
58
+ if (this[SYMBOL_STATE].isArray ? typeof first === 'number' : isKey(first)) {
59
+ return getReactiveValueInProxy(this as never, first as Key).get();
60
+ }
61
+
62
+ return getSignalValue.call(this);
63
+ }
64
+
65
+ function isProxyObject(isArray: boolean, value: unknown): value is ArrayOrPlainObject {
66
+ return value == null || (isArray ? Array.isArray(value) : isPlainObject(value));
67
+ }
68
+
69
+ export function peekValueInProxy(this: InternalProxy, first?: unknown, second?: boolean): unknown {
70
+ const state = this[SYMBOL_STATE];
71
+
72
+ let value: unknown;
73
+
74
+ if (state.isArray ? typeof first === 'number' : isKey(first)) {
75
+ value = state.isArray
76
+ ? (state.value as unknown[]).at(first as number)
77
+ : (state.value as PlainObject)[first as Key];
78
+ } else {
79
+ value = state.value;
80
+ }
81
+
82
+ return peekSignalValue.call([this, value], first === true || second === true);
83
+ }
84
+
85
+ function setProxyObject(state: ReactiveProxyState, value: unknown): void {
86
+ if (state.isArray) {
87
+ (state.value as unknown[]).splice(
88
+ 0,
89
+ (state.value as unknown[]).length,
90
+ ...((value ?? []) as unknown[]),
91
+ );
92
+
93
+ return;
94
+ }
95
+
96
+ startBatch();
97
+
98
+ const actual = (value as Record<string, unknown>) ?? {};
99
+ const proxy = state.value as PlainObject;
100
+
101
+ const proxyKeys = Object.keys(proxy);
102
+ const actualKeys = Object.keys(actual);
103
+
104
+ let {length} = proxyKeys;
105
+
106
+ for (let index = 0; index < length; index += 1) {
107
+ const key = proxyKeys[index];
108
+
109
+ proxy[key] = actualKeys.includes(key) ? actual[key] : undefined;
110
+ }
111
+
112
+ length = actualKeys.length;
113
+
114
+ for (let index = 0; index < length; index += 1) {
115
+ const key = actualKeys[index];
116
+
117
+ if (!proxyKeys.includes(key)) {
118
+ const keyedValue = actual[key];
119
+
120
+ proxy[key] = keyedValue;
121
+ }
122
+ }
123
+
124
+ stopBatch();
125
+ }
126
+
127
+ function setProxyProperty(state: ReactiveProxyState, property: unknown, value: unknown): void {
128
+ if (!state.isArray) {
129
+ (state.value as PlainObject)[property as Key] = value;
130
+
131
+ return;
132
+ }
133
+
134
+ const actual =
135
+ (property as number) < 0
136
+ ? (state.value as unknown[]).length + (property as number)
137
+ : (property as number);
138
+
139
+ if (actual > -1) {
140
+ (state.value as unknown[])[actual] = value;
141
+ }
142
+ }
143
+
144
+ export function setProxyValue(this: InternalProxy, first?: unknown, second?: unknown): void {
145
+ const state = this[SYMBOL_STATE] as ReactiveProxyState;
146
+
147
+ if (state.isArray && first === PROPERTY_LENGTH) {
84
148
  (state.value as unknown[]).length = second as number;
85
149
 
86
150
  return;
87
151
  }
88
152
 
89
- if (isObject(first)) {
90
- setObject(state, first);
153
+ if (isProxyObject(state.isArray, first)) {
154
+ setProxyObject(state, first);
91
155
 
92
156
  return;
93
157
  }
94
158
 
95
- const property = isProperty(first);
159
+ const property = state.isArray ? typeof first === 'number' : isKey(first);
96
160
 
97
- if (array && property && Number.isNaN(first)) {
161
+ if (state.isArray && property && Number.isNaN(first)) {
98
162
  return;
99
163
  }
100
164
 
@@ -122,15 +186,15 @@ export function setProxyValue<Value, Item = Value>(
122
186
  if (property && state.promises!.get(first as Key) === actual) {
123
187
  state.promises!.delete(first as Key);
124
188
 
125
- setProperty(state, first, value);
189
+ setProxyProperty(state, first, value);
126
190
 
127
191
  return;
128
192
  }
129
193
 
130
- if (!property && isObject(value) && state.promise === actual) {
194
+ if (!property && isProxyObject(state.isArray, value) && state.promise === actual) {
131
195
  state.promise = undefined;
132
196
 
133
- setObject(state, value);
197
+ setProxyObject(state, value);
134
198
  }
135
199
  })
136
200
  .catch(() => {
@@ -141,18 +205,21 @@ export function setProxyValue<Value, Item = Value>(
141
205
  }
142
206
  });
143
207
  } else if (property) {
144
- setProperty(state, first, actual as Item);
145
- } else if (isObject(actual)) {
146
- setObject(state, actual);
208
+ setProxyProperty(state, first, actual);
209
+ } else if (isProxyObject(state.isArray, actual)) {
210
+ setProxyObject(state, actual);
147
211
  }
148
212
  }
149
213
 
150
- export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
151
- parameters: SetValueInProxyParameters<Value, Equal>,
214
+ export function setValueInProxy(
215
+ instance: InternalProxy,
216
+ target: object,
217
+ property: PropertyKey,
218
+ value: unknown,
152
219
  ): boolean {
153
- const {isArray, length, property, state, target, value} = parameters;
220
+ const state = instance[SYMBOL_STATE];
154
221
 
155
- if (isArray) {
222
+ if (state.isArray) {
156
223
  const isIndex = !Number.isNaN(Number(property));
157
224
  const isLength = property === PROPERTY_LENGTH;
158
225
 
@@ -161,17 +228,27 @@ export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
161
228
  }
162
229
  }
163
230
 
164
- const previous = Reflect.get(target, property);
231
+ const previous = Reflect.get(target as object, property);
165
232
 
166
- if (!state.equal(previous as never, value as never)) {
233
+ if (!(state.equal ?? Object.is)(previous as never, value as never)) {
167
234
  Reflect.set(target, property, value);
168
235
 
169
- emitValue(state);
236
+ emitValue(instance);
170
237
 
171
- if (isArray) {
172
- length?.set((target as unknown[]).length);
238
+ if (state.isArray) {
239
+ state.length?.set((target as unknown[]).length);
173
240
  }
174
241
  }
175
242
 
176
243
  return true;
177
244
  }
245
+
246
+ export function updateProxyValue(this: InternalProxy, callback: unknown): void {
247
+ if (typeof callback !== 'function') {
248
+ throw new TypeError('Callback must be a function');
249
+ }
250
+
251
+ setProxyValue.call(this, callback(this[SYMBOL_STATE].value));
252
+ }
253
+
254
+ // #endregion