@oscarpalmer/mora 0.13.0 → 0.15.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 (62) hide show
  1. package/dist/batch.cjs +2 -2
  2. package/dist/batch.js +1 -1
  3. package/dist/effect.cjs +2 -1
  4. package/dist/effect.js +2 -1
  5. package/dist/helpers/emit.cjs +42 -0
  6. package/dist/helpers/emit.js +42 -0
  7. package/dist/helpers/is.cjs +34 -0
  8. package/dist/helpers/is.js +34 -0
  9. package/dist/helpers/value.cjs +22 -0
  10. package/dist/helpers/value.js +22 -0
  11. package/dist/index.cjs +12 -11
  12. package/dist/index.js +5 -4
  13. package/dist/mora.full.js +200 -140
  14. package/dist/value/array.cjs +161 -0
  15. package/dist/{array.js → value/array.js} +62 -31
  16. package/dist/{computed.cjs → value/computed.cjs} +6 -5
  17. package/dist/{computed.js → value/computed.js} +4 -3
  18. package/dist/{reactive.cjs → value/reactive.cjs} +1 -1
  19. package/dist/{reactive.js → value/reactive.js} +1 -1
  20. package/dist/value/signal.cjs +33 -0
  21. package/dist/{signal.js → value/signal.js} +3 -2
  22. package/package.json +1 -1
  23. package/src/batch.ts +1 -1
  24. package/src/effect.ts +12 -11
  25. package/src/helpers/emit.ts +52 -0
  26. package/src/helpers/is.ts +56 -0
  27. package/src/helpers/value.ts +27 -0
  28. package/src/index.ts +11 -5
  29. package/src/subscription.ts +1 -1
  30. package/src/{array.ts → value/array.ts} +75 -41
  31. package/src/{computed.ts → value/computed.ts} +4 -3
  32. package/src/{reactive.ts → value/reactive.ts} +3 -3
  33. package/src/{signal.ts → value/signal.ts} +3 -2
  34. package/types/batch.d.cts +9 -9
  35. package/types/helpers/emit.d.cts +63 -0
  36. package/types/helpers/emit.d.ts +3 -0
  37. package/types/{is.d.cts → helpers/is.d.cts} +85 -9
  38. package/types/helpers/is.d.ts +26 -0
  39. package/types/{value.d.cts → helpers/value.d.cts} +9 -10
  40. package/types/{value.d.ts → helpers/value.d.ts} +1 -2
  41. package/types/index.d.cts +74 -38
  42. package/types/index.d.ts +5 -5
  43. package/types/subscription.d.cts +12 -12
  44. package/types/subscription.d.ts +1 -1
  45. package/types/{array.d.cts → value/array.d.cts} +41 -9
  46. package/types/value/array.d.ts +74 -0
  47. package/types/{computed.d.ts → value/computed.d.ts} +1 -1
  48. package/types/{reactive.d.ts → value/reactive.d.ts} +2 -2
  49. package/dist/array.cjs +0 -130
  50. package/dist/is.cjs +0 -21
  51. package/dist/is.js +0 -21
  52. package/dist/signal.cjs +0 -32
  53. package/dist/value.cjs +0 -38
  54. package/dist/value.js +0 -38
  55. package/src/is.ts +0 -39
  56. package/src/value.ts +0 -47
  57. package/types/array.d.ts +0 -41
  58. package/types/is.d.ts +0 -17
  59. package/types/{computed.d.cts → value/computed.d.cts} +9 -9
  60. package/types/{reactive.d.cts → value/reactive.d.cts} +9 -9
  61. package/types/{signal.d.cts → value/signal.d.cts} +9 -9
  62. /package/types/{signal.d.ts → value/signal.d.ts} +0 -0
@@ -5,13 +5,16 @@ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot
5
5
  var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
6
6
  var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7
7
  var _size;
8
+ import { emitValue, emitArrayChanges } from "../helpers/emit.js";
9
+ import { arrayName } from "../helpers/is.js";
10
+ import { getValue } from "../helpers/value.js";
11
+ import { computed } from "./computed.js";
8
12
  import { Reactive } from "./reactive.js";
9
13
  import { signal } from "./signal.js";
10
- import { getValue, emitValue } from "./value.js";
11
14
  class ReactiveArray extends Reactive {
12
15
  constructor(value) {
13
16
  super(
14
- "array",
17
+ arrayName,
15
18
  new Proxy(value, {
16
19
  get: (target, property) => updateMethods.has(property) ? updateArray(property, target, this.state, __privateGet(this, _size)) : Reflect.get(target, property),
17
20
  set: (target, property, value2) => setValue(target, property, value2, this.state, __privateGet(this, _size))
@@ -34,15 +37,45 @@ class ReactiveArray extends Reactive {
34
37
  this.get().length = value;
35
38
  }
36
39
  }
40
+ /**
41
+ * Get an indexed item
42
+ */
43
+ at(index) {
44
+ return this.get().at(index);
45
+ }
37
46
  /**
38
47
  * @inheritdoc
39
48
  */
40
49
  get() {
41
50
  return getValue(this.state);
42
51
  }
52
+ /**
53
+ * Create a computed, filtered array
54
+ */
55
+ filter(callback) {
56
+ return computed(() => this.get().filter(callback));
57
+ }
58
+ /**
59
+ * Create a computed, mapped array
60
+ */
61
+ map(callback) {
62
+ return computed(() => this.get().map(callback));
63
+ }
43
64
  peek(length) {
44
65
  return length === true ? __privateGet(this, _size).peek() : [...this.state.value];
45
66
  }
67
+ /**
68
+ * Remove and return the last item of the array
69
+ */
70
+ pop() {
71
+ return this.state.value.pop();
72
+ }
73
+ /**
74
+ * Add items to the end of the array
75
+ */
76
+ push(...items) {
77
+ return this.state.value.push(...items);
78
+ }
46
79
  set(first, second) {
47
80
  if (Array.isArray(first)) {
48
81
  this.state.value.splice(0, this.state.value.length, ...first);
@@ -52,34 +85,33 @@ class ReactiveArray extends Reactive {
52
85
  this.state.value[first] = second;
53
86
  }
54
87
  }
88
+ /**
89
+ * Remove and return the first item of the array
90
+ */
91
+ shift() {
92
+ return this.state.value.shift();
93
+ }
94
+ /**
95
+ * Remove and return items from the array _(and optionally add new items)_
96
+ */
97
+ splice(from, to, ...items) {
98
+ return this.state.value.splice(
99
+ from,
100
+ to ?? this.state.value.length,
101
+ ...items
102
+ );
103
+ }
104
+ /**
105
+ * Add items to the beginning of the array
106
+ */
107
+ unshift(...items) {
108
+ return this.state.value.unshift(...items);
109
+ }
55
110
  }
56
111
  _size = new WeakMap();
57
112
  function array(value) {
58
113
  return new ReactiveArray(Array.isArray(value) ? value : []);
59
114
  }
60
- function emit(first, second) {
61
- let { length } = first;
62
- if (length !== second.length) {
63
- return true;
64
- }
65
- let offset = 0;
66
- if (length >= 100) {
67
- offset = Math.round(length / 10);
68
- offset = offset > 25 ? 25 : offset;
69
- for (let index = 0; index < offset; index += 1) {
70
- if (!Object.is(first[index], second[index])) {
71
- return true;
72
- }
73
- }
74
- }
75
- length -= offset;
76
- for (let index = offset; index < length; index += 1) {
77
- if (!Object.is(first[index], second[index])) {
78
- return true;
79
- }
80
- }
81
- return false;
82
- }
83
115
  function setValue(target, property, value, state, length) {
84
116
  const isIndex = !Number.isNaN(Number(property));
85
117
  const isLength = property === "length";
@@ -87,12 +119,11 @@ function setValue(target, property, value, state, length) {
87
119
  return Reflect.set(target, property, value);
88
120
  }
89
121
  const previous = Reflect.get(target, property);
90
- if (Object.is(previous, value)) {
91
- return true;
122
+ if (!Object.is(previous, value)) {
123
+ Reflect.set(target, property, value);
124
+ emitValue(state);
125
+ length.set(target.length);
92
126
  }
93
- Reflect.set(target, property, value);
94
- emitValue(state);
95
- length.set(target.length);
96
127
  return true;
97
128
  }
98
129
  function updateArray(type, array2, state, length) {
@@ -103,7 +134,7 @@ function updateArray(type, array2, state, length) {
103
134
  const result = array2[type](
104
135
  ...args
105
136
  );
106
- if (affectsLength ? array2.length !== previousLength : emit(previousArray, array2)) {
137
+ if (affectsLength ? array2.length !== previousLength : emitArrayChanges(previousArray, array2)) {
107
138
  emitValue(state);
108
139
  length.set(array2.length);
109
140
  }
@@ -3,13 +3,14 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- const batch = require("./batch.cjs");
7
- const effect = require("./effect.cjs");
8
- const reactive = require("./reactive.cjs");
6
+ const batch = require("../batch.cjs");
7
+ const effect = require("../effect.cjs");
8
+ const helpers_is = require("../helpers/is.cjs");
9
+ const value_reactive = require("./reactive.cjs");
9
10
  exports.activeComputed = void 0;
10
- class Computed extends reactive.Reactive {
11
+ class Computed extends value_reactive.Reactive {
11
12
  constructor(callback) {
12
- super("computed", void 0);
13
+ super(helpers_is.computedName, void 0);
13
14
  __publicField(this, "effect", {
14
15
  dirty: true,
15
16
  instance: void 0
@@ -1,13 +1,14 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { batchedHandlers, batchDepth } from "./batch.js";
5
- import { effect, activeEffect, runEffect } from "./effect.js";
4
+ import { batchedHandlers, batchDepth } from "../batch.js";
5
+ import { effect, activeEffect, runEffect } from "../effect.js";
6
+ import { computedName } from "../helpers/is.js";
6
7
  import { Reactive } from "./reactive.js";
7
8
  let activeComputed;
8
9
  class Computed extends Reactive {
9
10
  constructor(callback) {
10
- super("computed", void 0);
11
+ super(computedName, void 0);
11
12
  __publicField(this, "effect", {
12
13
  dirty: true,
13
14
  instance: void 0
@@ -3,7 +3,7 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- const subscription = require("./subscription.cjs");
6
+ const subscription = require("../subscription.cjs");
7
7
  class Reactive {
8
8
  constructor(name, value) {
9
9
  __publicField(this, "state", {
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { subscribe, unsubscribe } from "./subscription.js";
4
+ import { subscribe, unsubscribe } from "../subscription.js";
5
5
  class Reactive {
6
6
  constructor(name, value) {
7
7
  __publicField(this, "state", {
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const helpers_is = require("../helpers/is.cjs");
4
+ const helpers_value = require("../helpers/value.cjs");
5
+ const value_reactive = require("./reactive.cjs");
6
+ class Signal extends value_reactive.Reactive {
7
+ constructor(value) {
8
+ super(helpers_is.signalName, value);
9
+ }
10
+ /**
11
+ * @inheritdoc
12
+ */
13
+ get() {
14
+ return helpers_value.getValue(this.state);
15
+ }
16
+ /**
17
+ * Set the value
18
+ */
19
+ set(value) {
20
+ helpers_value.setValue(this.state, value);
21
+ }
22
+ /**
23
+ * Update the value
24
+ */
25
+ update(callback) {
26
+ this.set(callback(this.state.value));
27
+ }
28
+ }
29
+ function signal(value) {
30
+ return new Signal(value);
31
+ }
32
+ exports.Signal = Signal;
33
+ exports.signal = signal;
@@ -1,8 +1,9 @@
1
+ import { signalName } from "../helpers/is.js";
2
+ import { getValue, setValue } from "../helpers/value.js";
1
3
  import { Reactive } from "./reactive.js";
2
- import { getValue, setValue } from "./value.js";
3
4
  class Signal extends Reactive {
4
5
  constructor(value) {
5
- super("signal", value);
6
+ super(signalName, value);
6
7
  }
7
8
  /**
8
9
  * @inheritdoc
package/package.json CHANGED
@@ -54,5 +54,5 @@
54
54
  },
55
55
  "type": "module",
56
56
  "types": "./types/index.d.ts",
57
- "version": "0.13.0"
57
+ "version": "0.15.0"
58
58
  }
package/src/batch.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import {type Effect, runEffect} from './effect';
2
- import {isEffect} from './is';
2
+ import {isEffect} from './helpers/is';
3
3
  import type {Subscription} from './subscription';
4
4
 
5
5
  export function flushEffects(): void {
package/src/effect.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
+ import {effectName} from './helpers/is';
2
3
 
3
4
  type EffectState = {
4
5
  callback: GenericCallback;
@@ -9,22 +10,22 @@ type InternalEffect = {
9
10
  };
10
11
 
11
12
  export class Effect {
12
- private declare readonly $mora: string;
13
+ private declare readonly $mora: string;
13
14
 
14
- private declare readonly state: EffectState;
15
+ private declare readonly state: EffectState;
15
16
 
16
- constructor(callback: GenericCallback) {
17
- Object.defineProperty(this, '$mora', {
18
- value: 'effect',
19
- });
17
+ constructor(callback: GenericCallback) {
18
+ Object.defineProperty(this, '$mora', {
19
+ value: effectName,
20
+ });
20
21
 
21
- this.state = {
22
- callback,
23
- };
22
+ this.state = {
23
+ callback,
24
+ };
24
25
 
25
- runEffect(this);
26
- }
26
+ runEffect(this);
27
27
  }
28
+ }
28
29
 
29
30
  export function runEffect(effect: Effect): void {
30
31
  const previousEffect = activeEffect;
@@ -0,0 +1,52 @@
1
+ import {batchDepth, batchedHandlers, flushEffects} from '../batch';
2
+ import type {InternalComputed} from '../value/computed';
3
+ import type {ReactiveState} from '../value/reactive';
4
+
5
+ export function emitArrayChanges(first: unknown[], second: unknown[]): boolean {
6
+ let {length} = first;
7
+
8
+ if (length !== second.length) {
9
+ return true;
10
+ }
11
+
12
+ let offset = 0;
13
+
14
+ if (length >= 100) {
15
+ offset = Math.round(length / 10);
16
+ offset = offset > 25 ? 25 : offset;
17
+
18
+ for (let index = 0; index < offset; index += 1) {
19
+ if (!Object.is(first[index], second[index])) {
20
+ return true;
21
+ }
22
+ }
23
+ }
24
+
25
+ length -= offset;
26
+
27
+ for (let index = offset; index < length; index += 1) {
28
+ if (!Object.is(first[index], second[index])) {
29
+ return true;
30
+ }
31
+ }
32
+
33
+ return false;
34
+ }
35
+
36
+ export function emitValue<Value>(state: ReactiveState<Value>): void {
37
+ for (const computed of state.computeds) {
38
+ (computed as unknown as InternalComputed).effect.dirty = true;
39
+ }
40
+
41
+ for (const effect of state.effects) {
42
+ batchedHandlers.add(effect);
43
+ }
44
+
45
+ for (const [, subscription] of state.subscriptions) {
46
+ batchedHandlers.add(subscription as never);
47
+ }
48
+
49
+ if (batchDepth === 0) {
50
+ flushEffects();
51
+ }
52
+ }
@@ -0,0 +1,56 @@
1
+ import type {PlainObject} from '@oscarpalmer/atoms/models';
2
+ import type {Effect} from '../effect';
3
+ import type {ReactiveArray} from '../value/array';
4
+ import type {Computed} from '../value/computed';
5
+ import type {Reactive} from '../value/reactive';
6
+ import {signal, type Signal} from '../value/signal';
7
+
8
+ /**
9
+ * Is the value a reactive array?
10
+ */
11
+ export function isArray(value: unknown): value is ReactiveArray<unknown> {
12
+ return isMora<ReactiveArray<unknown>>(value, arrayName);
13
+ }
14
+
15
+ /**
16
+ * Is the value a computed signal?
17
+ */
18
+ export function isComputed(value: unknown): value is Computed<unknown> {
19
+ return isMora<Computed<unknown>>(value, computedName);
20
+ }
21
+
22
+ /**
23
+ * Is the value an effect?
24
+ */
25
+ export function isEffect(value: unknown): value is Effect {
26
+ return isMora<Effect>(value, effectName);
27
+ }
28
+
29
+ function isMora<T>(value: unknown, name: string | Set<string>): value is T {
30
+ return (
31
+ typeof value === 'object' &&
32
+ value != null &&
33
+ '$mora' in value &&
34
+ (typeof name === 'string'
35
+ ? (value as PlainObject).$mora === name
36
+ : name.has((value as PlainObject).$mora as never))
37
+ );
38
+ }
39
+
40
+ export function isReactive(value: unknown): value is Reactive<unknown> {
41
+ return isMora<Reactive<unknown>>(value, reactiveNames);
42
+ }
43
+
44
+ /**
45
+ * Is the value a signal?
46
+ */
47
+ export function isSignal(value: unknown): value is Signal<unknown> {
48
+ return isMora<Signal<unknown>>(value, signalName);
49
+ }
50
+
51
+ export const arrayName = 'array';
52
+ export const computedName = 'computed';
53
+ export const effectName = 'effect';
54
+ export const signalName = 'signal';
55
+
56
+ const reactiveNames = new Set([arrayName, computedName, signalName]);
@@ -0,0 +1,27 @@
1
+ import {activeEffect} from '../effect';
2
+ import {activeComputed} from '../value/computed';
3
+ import type {ReactiveState} from '../value/reactive';
4
+ import {emitValue} from './emit';
5
+
6
+ export function getValue<Value>(state: ReactiveState<Value>): Value {
7
+ if (activeComputed != null) {
8
+ state.computeds.add(activeComputed);
9
+ }
10
+
11
+ if (activeEffect != null) {
12
+ state.effects.add(activeEffect);
13
+ }
14
+
15
+ return state.value;
16
+ }
17
+
18
+ export function setValue<Value>(
19
+ state: ReactiveState<Value>,
20
+ value: Value,
21
+ ): void {
22
+ if (!Object.is(state.value, value)) {
23
+ state.value = value;
24
+
25
+ emitValue(state);
26
+ }
27
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,13 @@
1
- export {array, type ReactiveArray} from './array';
2
1
  export {startBatch, stopBatch} from './batch';
3
- export {computed, type Computed} from './computed';
4
2
  export {effect, type Effect} from './effect';
5
- export {isComputed, isEffect, isReactive, isSignal} from './is';
6
- export type {Reactive} from './reactive';
7
- export {signal, type Signal} from './signal';
3
+ export {
4
+ isArray,
5
+ isComputed,
6
+ isEffect,
7
+ isReactive,
8
+ isSignal,
9
+ } from './helpers/is';
10
+ export {array, type ReactiveArray} from './value/array';
11
+ export {computed, type Computed} from './value/computed';
12
+ export type {Reactive} from './value/reactive';
13
+ export {signal, type Signal} from './value/signal';
@@ -1,5 +1,5 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import type {ReactiveState} from './reactive';
2
+ import type {ReactiveState} from './value/reactive';
3
3
 
4
4
  export class Subscription<Value> {
5
5
  constructor(
@@ -1,6 +1,9 @@
1
+ import {emitArrayChanges, emitValue} from '../helpers/emit';
2
+ import {arrayName} from '../helpers/is';
3
+ import {getValue} from '../helpers/value';
4
+ import {type Computed, computed} from './computed';
1
5
  import {Reactive, type ReactiveState} from './reactive';
2
6
  import {type Signal, signal} from './signal';
3
- import {emitValue, getValue} from './value';
4
7
 
5
8
  export class ReactiveArray<Item> extends Reactive<Item[]> {
6
9
  #size = signal(0);
@@ -27,7 +30,7 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
27
30
 
28
31
  constructor(value: Item[]) {
29
32
  super(
30
- 'array',
33
+ arrayName,
31
34
  new Proxy(value, {
32
35
  get: (target, property) =>
33
36
  updateMethods.has(property as string)
@@ -41,6 +44,13 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
41
44
  this.#size.set(value.length);
42
45
  }
43
46
 
47
+ /**
48
+ * Get an indexed item
49
+ */
50
+ at(index: number): Item | undefined {
51
+ return this.get().at(index);
52
+ }
53
+
44
54
  /**
45
55
  * @inheritdoc
46
56
  */
@@ -48,6 +58,24 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
48
58
  return getValue(this.state);
49
59
  }
50
60
 
61
+ /**
62
+ * Create a computed, filtered array
63
+ */
64
+ filter(
65
+ callback: (item: Item, index: number, array: Item[]) => boolean,
66
+ ): Computed<Item[]> {
67
+ return computed(() => this.get().filter(callback));
68
+ }
69
+
70
+ /**
71
+ * Create a computed, mapped array
72
+ */
73
+ map<Mapped>(
74
+ callback: (item: Item, index: number, array: Item[]) => Mapped,
75
+ ): Computed<Mapped[]> {
76
+ return computed(() => this.get().map(callback));
77
+ }
78
+
51
79
  /**
52
80
  * @inheritdoc
53
81
  */
@@ -62,6 +90,20 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
62
90
  return length === true ? this.#size.peek() : [...this.state.value];
63
91
  }
64
92
 
93
+ /**
94
+ * Remove and return the last item of the array
95
+ */
96
+ pop(): Item | undefined {
97
+ return this.state.value.pop();
98
+ }
99
+
100
+ /**
101
+ * Add items to the end of the array
102
+ */
103
+ push(...items: Item[]): number {
104
+ return this.state.value.push(...items);
105
+ }
106
+
65
107
  /**
66
108
  * Set the value
67
109
  */
@@ -86,44 +128,38 @@ export class ReactiveArray<Item> extends Reactive<Item[]> {
86
128
  this.state.value[first] = second as Item;
87
129
  }
88
130
  }
89
- }
90
-
91
- /**
92
- * Create a reactive array
93
- */
94
- export function array<Item>(value: Item[]): ReactiveArray<Item> {
95
- return new ReactiveArray(Array.isArray(value) ? value : []);
96
- }
97
-
98
- function emit(first: unknown[], second: unknown[]): boolean {
99
- let {length} = first;
100
131
 
101
- if (length !== second.length) {
102
- return true;
132
+ /**
133
+ * Remove and return the first item of the array
134
+ */
135
+ shift(): Item | undefined {
136
+ return this.state.value.shift();
103
137
  }
104
138
 
105
- let offset = 0;
106
-
107
- if (length >= 100) {
108
- offset = Math.round(length / 10);
109
- offset = offset > 25 ? 25 : offset;
110
-
111
- for (let index = 0; index < offset; index += 1) {
112
- if (!Object.is(first[index], second[index])) {
113
- return true;
114
- }
115
- }
139
+ /**
140
+ * Remove and return items from the array _(and optionally add new items)_
141
+ */
142
+ splice(from: number, to?: number, ...items: Item[]): Item[] {
143
+ return this.state.value.splice(
144
+ from,
145
+ to ?? this.state.value.length,
146
+ ...items,
147
+ );
116
148
  }
117
149
 
118
- length -= offset;
119
-
120
- for (let index = offset; index < length; index += 1) {
121
- if (!Object.is(first[index], second[index])) {
122
- return true;
123
- }
150
+ /**
151
+ * Add items to the beginning of the array
152
+ */
153
+ unshift(...items: Item[]): number {
154
+ return this.state.value.unshift(...items);
124
155
  }
156
+ }
125
157
 
126
- return false;
158
+ /**
159
+ * Create a reactive array
160
+ */
161
+ export function array<Item>(value: Item[]): ReactiveArray<Item> {
162
+ return new ReactiveArray(Array.isArray(value) ? value : []);
127
163
  }
128
164
 
129
165
  function setValue<Item>(
@@ -142,15 +178,13 @@ function setValue<Item>(
142
178
 
143
179
  const previous = Reflect.get(target, property);
144
180
 
145
- if (Object.is(previous, value)) {
146
- return true;
147
- }
148
-
149
- Reflect.set(target, property, value);
181
+ if (!Object.is(previous, value)) {
182
+ Reflect.set(target, property, value);
150
183
 
151
- emitValue(state);
184
+ emitValue(state);
152
185
 
153
- length.set(target.length);
186
+ length.set(target.length);
187
+ }
154
188
 
155
189
  return true;
156
190
  }
@@ -173,7 +207,7 @@ function updateArray<Item>(
173
207
  if (
174
208
  affectsLength
175
209
  ? array.length !== previousLength
176
- : emit(previousArray, array)
210
+ : emitArrayChanges(previousArray, array)
177
211
  ) {
178
212
  emitValue(state);
179
213
 
@@ -1,5 +1,6 @@
1
- import {batchDepth, batchedHandlers} from './batch';
2
- import {type Effect, activeEffect, effect, runEffect} from './effect';
1
+ import {batchDepth, batchedHandlers} from '../batch';
2
+ import {type Effect, activeEffect, effect, runEffect} from '../effect';
3
+ import {computedName} from '../helpers/is';
3
4
  import {Reactive, type ReactiveState} from './reactive';
4
5
 
5
6
  type ComputedEffect = {
@@ -21,7 +22,7 @@ export class Computed<Value> extends Reactive<Value> {
21
22
  };
22
23
 
23
24
  constructor(callback: () => Value) {
24
- super('computed', undefined as never);
25
+ super(computedName, undefined as never);
25
26
 
26
27
  this.effect.instance = effect(() => {
27
28
  if (this.effect.dirty) {