@esportsplus/reactivity 0.12.0 → 0.12.1

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.
@@ -35,6 +35,7 @@ type Listener<V> = {
35
35
  type Value<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : T extends Array<infer U> ? API<U> : T;
36
36
  declare class ReactiveArray<T> {
37
37
  [REACTIVE_ARRAY]: boolean;
38
+ disposables: number;
38
39
  private data;
39
40
  private listeners;
40
41
  private proxy;
@@ -1,9 +1,10 @@
1
1
  import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
2
2
  import { REACTIVE_ARRAY } from '../constants.js';
3
- import { computed, dispose, isComputed, onCleanup, read, root } from '../system.js';
3
+ import { computed, dispose, isComputed, read } from '../system.js';
4
4
  import object, { isReactiveObject } from './object.js';
5
5
  class ReactiveArray {
6
6
  [REACTIVE_ARRAY] = true;
7
+ disposables = 0;
7
8
  data;
8
9
  listeners = null;
9
10
  proxy;
@@ -171,36 +172,32 @@ const isReactiveArray = (value) => {
171
172
  return isObject(value) && REACTIVE_ARRAY in value;
172
173
  };
173
174
  export default function array(input) {
174
- let { array, proxy } = root(() => {
175
- let proxy = new Proxy({}, {
176
- get(_, key) {
177
- if (isNumber(key)) {
178
- let value = wrapped[key];
179
- if (isComputed(value)) {
180
- return read(value);
181
- }
182
- return value;
175
+ let proxy = new Proxy({}, {
176
+ get(_, key) {
177
+ if (isNumber(key)) {
178
+ let value = wrapped[key];
179
+ if (isComputed(value)) {
180
+ return read(value);
183
181
  }
184
- else if (key in array) {
185
- return array[key];
186
- }
187
- return wrapped[key];
188
- },
189
- set(_, key, value) {
190
- if (isNumber(key)) {
191
- array.splice(key, 1, value);
192
- return true;
193
- }
194
- else if (key === 'length') {
195
- return array.length = value;
196
- }
197
- return false;
182
+ return value;
183
+ }
184
+ else if (key in array) {
185
+ return array[key];
198
186
  }
199
- }), wrapped = factory(input);
200
- let array = new ReactiveArray(wrapped, proxy);
201
- return { array, proxy };
202
- });
203
- onCleanup(() => array.dispose());
187
+ return wrapped[key];
188
+ },
189
+ set(_, key, value) {
190
+ if (isNumber(key)) {
191
+ array.splice(key, 1, value);
192
+ return true;
193
+ }
194
+ else if (key === 'length') {
195
+ return array.length = value;
196
+ }
197
+ return false;
198
+ }
199
+ }), wrapped = factory(input);
200
+ let array = new ReactiveArray(wrapped, proxy);
204
201
  return proxy;
205
202
  }
206
203
  ;
@@ -1,12 +1,22 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
+ import { onCleanup, root } from '../system.js';
2
3
  import array from './array.js';
3
4
  import object from './object.js';
4
5
  export default (input) => {
5
- if (isArray(input)) {
6
- return array(input);
7
- }
8
- else if (isObject(input)) {
9
- return object(input);
10
- }
11
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
6
+ let value;
7
+ return root(() => {
8
+ if (isArray(input)) {
9
+ value = array(input);
10
+ }
11
+ else if (isObject(input)) {
12
+ value = object(input);
13
+ }
14
+ if (value) {
15
+ if (root.disposables) {
16
+ onCleanup(() => value.dispose());
17
+ }
18
+ return value;
19
+ }
20
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
21
+ });
12
22
  };
@@ -1,5 +1,5 @@
1
1
  import { defineProperty, isArray, isFunction, isObject, isPromise } from '@esportsplus/utilities';
2
- import { computed, dispose, effect, isComputed, onCleanup, read, root, set, signal } from '../system.js';
2
+ import { computed, dispose, effect, isComputed, read, root, set, signal } from '../system.js';
3
3
  import { REACTIVE_OBJECT } from '../constants.js';
4
4
  import array, { isReactiveArray } from './array.js';
5
5
  class ReactiveObject {
@@ -74,9 +74,7 @@ const isReactiveObject = (value) => {
74
74
  return isObject(value) && REACTIVE_OBJECT in value;
75
75
  };
76
76
  export default function object(input) {
77
- let object = root(() => new ReactiveObject(input));
78
- onCleanup(() => object.dispose());
79
- return object;
77
+ return new ReactiveObject(input);
80
78
  }
81
79
  ;
82
80
  export { isReactiveObject };
package/build/system.d.ts CHANGED
@@ -6,7 +6,10 @@ declare const isComputed: (value: unknown) => value is Computed<unknown>;
6
6
  declare const isSignal: (value: unknown) => value is Signal<unknown>;
7
7
  declare const onCleanup: (fn: VoidFunction) => typeof fn;
8
8
  declare const read: <T>(node: Signal<T> | Computed<T>) => T;
9
- declare const root: <T>(fn: () => T) => T;
9
+ declare const root: {
10
+ <T>(fn: () => T): T;
11
+ disposables: number;
12
+ };
10
13
  declare const set: <T>(signal: Signal<T>, value: T) => void;
11
14
  declare const signal: <T>(value: T) => Signal<T>;
12
15
  export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, set, signal };
package/build/system.js CHANGED
@@ -1,20 +1,20 @@
1
- import { isArray, isObject, } from '@esportsplus/utilities';
1
+ import { isArray, isObject } from '@esportsplus/utilities';
2
2
  import { COMPUTED, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
3
3
  let depth = 0, heap = new Array(2048), index = 0, length = 0, microtask = queueMicrotask, notified = false, observer = null, stabilizer = STABILIZER_IDLE, version = 0;
4
- function cleanup(node) {
5
- if (!node.cleanup) {
4
+ function cleanup(computed) {
5
+ if (!computed.cleanup) {
6
6
  return;
7
7
  }
8
- let cleanup = node.cleanup;
9
- if (isArray(cleanup)) {
10
- for (let i = 0, n = cleanup.length; i < n; i++) {
11
- cleanup[i]();
8
+ let value = computed.cleanup;
9
+ if (isArray(value)) {
10
+ for (let i = 0, n = value.length; i < n; i++) {
11
+ value[i]();
12
12
  }
13
13
  }
14
14
  else {
15
- cleanup();
15
+ value();
16
16
  }
17
- node.cleanup = null;
17
+ computed.cleanup = null;
18
18
  }
19
19
  function deleteFromHeap(computed) {
20
20
  let state = computed.state;
@@ -138,11 +138,11 @@ function recompute(computed, del) {
138
138
  depth--;
139
139
  observer = o;
140
140
  computed.state = STATE_NONE;
141
- let depsTail = computed.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
142
- if (toRemove !== null) {
141
+ let depsTail = computed.depsTail, remove = depsTail !== null ? depsTail.nextDep : computed.deps;
142
+ if (remove !== null) {
143
143
  do {
144
- toRemove = unlink(toRemove);
145
- } while (toRemove !== null);
144
+ remove = unlink(remove);
145
+ } while (remove !== null);
146
146
  if (depsTail !== null) {
147
147
  depsTail.nextDep = null;
148
148
  }
@@ -153,11 +153,11 @@ function recompute(computed, del) {
153
153
  if (ok && value !== computed.value) {
154
154
  computed.value = value;
155
155
  for (let c = computed.subs; c !== null; c = c.nextSub) {
156
- let o = c.sub, state = o.state;
156
+ let s = c.sub, state = s.state;
157
157
  if (state & STATE_CHECK) {
158
- o.state = state | STATE_DIRTY;
158
+ s.state = state | STATE_DIRTY;
159
159
  }
160
- insertIntoHeap(o);
160
+ insertIntoHeap(s);
161
161
  }
162
162
  schedule();
163
163
  }
@@ -172,24 +172,24 @@ function schedule() {
172
172
  }
173
173
  }
174
174
  function stabilize() {
175
- let o = observer;
176
- stabilizer = STABILIZER_RUNNING;
177
- for (index = 0; index <= length; index++) {
178
- let computed = heap[index];
179
- heap[index] = undefined;
180
- while (computed !== undefined) {
181
- let next = computed.nextHeap;
182
- recompute(computed, false);
183
- computed = next;
175
+ root(() => {
176
+ stabilizer = STABILIZER_RUNNING;
177
+ for (index = 0; index <= length; index++) {
178
+ let computed = heap[index];
179
+ heap[index] = undefined;
180
+ while (computed !== undefined) {
181
+ let next = computed.nextHeap;
182
+ recompute(computed, false);
183
+ computed = next;
184
+ }
184
185
  }
185
- }
186
- observer = o;
187
- if (stabilizer === STABILIZER_RESCHEDULE) {
188
- microtask(stabilize);
189
- }
190
- else {
191
- stabilizer = STABILIZER_IDLE;
192
- }
186
+ if (stabilizer === STABILIZER_RESCHEDULE) {
187
+ microtask(stabilize);
188
+ }
189
+ else {
190
+ stabilizer = STABILIZER_IDLE;
191
+ }
192
+ });
193
193
  }
194
194
  function unlink(link) {
195
195
  let { dep, nextDep, nextSub, prevSub } = link;
@@ -255,6 +255,7 @@ const computed = (fn) => {
255
255
  }
256
256
  else {
257
257
  recompute(self, false);
258
+ root.disposables++;
258
259
  }
259
260
  return self;
260
261
  };
@@ -319,12 +320,15 @@ const read = (node) => {
319
320
  return node.value;
320
321
  };
321
322
  const root = (fn) => {
322
- let o = observer;
323
+ let d = root.disposables, o = observer;
323
324
  observer = null;
325
+ root.disposables = 0;
324
326
  let value = fn();
325
327
  observer = o;
328
+ root.disposables = d;
326
329
  return value;
327
330
  };
331
+ root.disposables = 0;
328
332
  const set = (signal, value) => {
329
333
  if (signal.value === value) {
330
334
  return;
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "private": false,
13
13
  "type": "module",
14
14
  "types": "build/index.d.ts",
15
- "version": "0.12.0",
15
+ "version": "0.12.1",
16
16
  "scripts": {
17
17
  "build": "tsc && tsc-alias",
18
18
  "-": "-"
@@ -1,6 +1,6 @@
1
1
  import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
2
2
  import { REACTIVE_ARRAY } from '~/constants';
3
- import { computed, dispose, isComputed, onCleanup, read, root } from '~/system';
3
+ import { computed, dispose, isComputed, read } from '~/system';
4
4
  import { Computed, Infer } from '~/types';
5
5
  import object, { isReactiveObject, ReactiveObject } from './object';
6
6
 
@@ -50,6 +50,7 @@ type Value<T> =
50
50
 
51
51
  class ReactiveArray<T> {
52
52
  [REACTIVE_ARRAY] = true;
53
+ disposables: number = 0;
53
54
 
54
55
  private data: Item<T>[];
55
56
  private listeners: Record<string, (Listener<any> | null)[]> | null = null;
@@ -294,44 +295,38 @@ const isReactiveArray = (value: any): value is ReactiveArray<any> => {
294
295
 
295
296
 
296
297
  export default function array<T>(input: T[]) {
297
- let { array, proxy } = root(() => {
298
- let proxy = new Proxy({}, {
299
- get(_: any, key: any) {
300
- if (isNumber(key)) {
301
- let value = wrapped[key];
302
-
303
- if (isComputed(value)) {
304
- return read(value);
305
- }
306
-
307
- return value;
308
- }
309
- else if (key in array) {
310
- return array[key as keyof typeof array];
311
- }
312
-
313
- return wrapped[key];
314
- },
315
- set(_: any, key: any, value: any) {
316
- if (isNumber(key)) {
317
- array.splice(key, 1, value);
318
- return true;
319
- }
320
- else if (key === 'length') {
321
- return array.length = value;
322
- }
323
-
324
- return false;
298
+ let proxy = new Proxy({}, {
299
+ get(_: any, key: any) {
300
+ if (isNumber(key)) {
301
+ let value = wrapped[key];
302
+
303
+ if (isComputed(value)) {
304
+ return read(value);
325
305
  }
326
- }) as API<T>,
327
- wrapped = factory(input);
328
306
 
329
- let array = new ReactiveArray(wrapped, proxy);
307
+ return value;
308
+ }
309
+ else if (key in array) {
310
+ return array[key as keyof typeof array];
311
+ }
330
312
 
331
- return { array, proxy };
332
- });
313
+ return wrapped[key];
314
+ },
315
+ set(_: any, key: any, value: any) {
316
+ if (isNumber(key)) {
317
+ array.splice(key, 1, value);
318
+ return true;
319
+ }
320
+ else if (key === 'length') {
321
+ return array.length = value;
322
+ }
323
+
324
+ return false;
325
+ }
326
+ }) as API<T>,
327
+ wrapped = factory(input);
333
328
 
334
- onCleanup(() => array.dispose());
329
+ let array = new ReactiveArray(wrapped, proxy);
335
330
 
336
331
  return proxy;
337
332
  };
@@ -1,4 +1,5 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
+ import { onCleanup, root } from '~/system';
2
3
  import array from './array';
3
4
  import object from './object';
4
5
 
@@ -21,13 +22,25 @@ type Input<T> =
21
22
 
22
23
 
23
24
  export default <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>): API<T> => {
24
- if (isArray(input)) {
25
- return array(input) as API<T>;
26
- }
27
- else if (isObject(input)) {
28
- return object(input) as API<T>;
29
- }
30
-
31
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
25
+ let value: API<T> | undefined;
26
+
27
+ return root(() => {
28
+ if (isArray(input)) {
29
+ value = array(input) as API<T>;
30
+ }
31
+ else if (isObject(input)) {
32
+ value = object(input) as API<T>;
33
+ }
34
+
35
+ if (value) {
36
+ if (root.disposables) {
37
+ onCleanup(() => value!.dispose());
38
+ }
39
+
40
+ return value;
41
+ }
42
+
43
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
44
+ });
32
45
  };
33
46
  export type { Input };
@@ -1,5 +1,5 @@
1
1
  import { defineProperty, isArray, isFunction, isObject, isPromise, Prettify } from '@esportsplus/utilities';
2
- import { computed, dispose, effect, isComputed, onCleanup, read, root, set, signal } from '~/system';
2
+ import { computed, dispose, effect, isComputed, read, root, set, signal } from '~/system';
3
3
  import { Computed, Infer, Signal } from '~/types';
4
4
  import { REACTIVE_OBJECT } from '~/constants';
5
5
  import array, { isReactiveArray } from './array';
@@ -11,6 +11,7 @@ type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: In
11
11
  class ReactiveObject<T extends Record<PropertyKey, unknown>> {
12
12
  [REACTIVE_OBJECT] = true;
13
13
 
14
+
14
15
  constructor(data: T) {
15
16
  for (let key in data) {
16
17
  let value = data[key];
@@ -100,11 +101,7 @@ const isReactiveObject = (value: any): value is ReactiveObject<any> => {
100
101
 
101
102
 
102
103
  export default function object<T extends Record<PropertyKey, unknown>>(input: T) {
103
- let object = root(() => new ReactiveObject<T>(input));
104
-
105
- onCleanup(() => object.dispose());
106
-
107
- return object;
104
+ return new ReactiveObject<T>(input);
108
105
  };
109
106
  export { isReactiveObject };
110
107
  export type { API as ReactiveObject };
package/src/system.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { isArray, isObject, } from '@esportsplus/utilities';
1
+ import { isArray, isObject } from '@esportsplus/utilities';
2
2
  import {
3
3
  COMPUTED, SIGNAL,
4
4
  STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
@@ -18,23 +18,23 @@ let depth = 0,
18
18
  version = 0;
19
19
 
20
20
 
21
- function cleanup<T>(node: Computed<T>): void {
22
- if (!node.cleanup) {
21
+ function cleanup<T>(computed: Computed<T>): void {
22
+ if (!computed.cleanup) {
23
23
  return;
24
24
  }
25
25
 
26
- let cleanup = node.cleanup;
26
+ let value = computed.cleanup;
27
27
 
28
- if (isArray(cleanup)) {
29
- for (let i = 0, n = cleanup.length; i < n; i++) {
30
- cleanup[i]();
28
+ if (isArray(value)) {
29
+ for (let i = 0, n = value.length; i < n; i++) {
30
+ value[i]();
31
31
  }
32
32
  }
33
33
  else {
34
- cleanup();
34
+ value();
35
35
  }
36
36
 
37
- node.cleanup = null;
37
+ computed.cleanup = null;
38
38
  }
39
39
 
40
40
  function deleteFromHeap<T>(computed: Computed<T>) {
@@ -208,13 +208,13 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
208
208
  computed.state = STATE_NONE;
209
209
 
210
210
  let depsTail = computed.depsTail as Link | null,
211
- toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
211
+ remove = depsTail !== null ? depsTail.nextDep : computed.deps;
212
212
 
213
- if (toRemove !== null) {
213
+ if (remove !== null) {
214
214
  do {
215
- toRemove = unlink(toRemove);
215
+ remove = unlink(remove);
216
216
  }
217
- while (toRemove !== null);
217
+ while (remove !== null);
218
218
 
219
219
  if (depsTail !== null) {
220
220
  depsTail.nextDep = null;
@@ -228,14 +228,14 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
228
228
  computed.value = value as T;
229
229
 
230
230
  for (let c = computed.subs; c !== null; c = c.nextSub) {
231
- let o = c.sub,
232
- state = o.state;
231
+ let s = c.sub,
232
+ state = s.state;
233
233
 
234
234
  if (state & STATE_CHECK) {
235
- o.state = state | STATE_DIRTY;
235
+ s.state = state | STATE_DIRTY;
236
236
  }
237
237
 
238
- insertIntoHeap(o);
238
+ insertIntoHeap(s);
239
239
  }
240
240
 
241
241
  schedule();
@@ -253,32 +253,30 @@ function schedule() {
253
253
  }
254
254
 
255
255
  function stabilize() {
256
- let o = observer;
256
+ root(() => {
257
+ stabilizer = STABILIZER_RUNNING;
257
258
 
258
- stabilizer = STABILIZER_RUNNING;
259
+ for (index = 0; index <= length; index++) {
260
+ let computed = heap[index];
259
261
 
260
- for (index = 0; index <= length; index++) {
261
- let computed = heap[index];
262
+ heap[index] = undefined;
262
263
 
263
- heap[index] = undefined;
264
+ while (computed !== undefined) {
265
+ let next = computed.nextHeap;
264
266
 
265
- while (computed !== undefined) {
266
- let next = computed.nextHeap;
267
+ recompute(computed, false);
267
268
 
268
- recompute(computed, false);
269
-
270
- computed = next;
269
+ computed = next;
270
+ }
271
271
  }
272
- }
273
272
 
274
- observer = o;
275
-
276
- if (stabilizer === STABILIZER_RESCHEDULE) {
277
- microtask(stabilize);
278
- }
279
- else {
280
- stabilizer = STABILIZER_IDLE;
281
- }
273
+ if (stabilizer === STABILIZER_RESCHEDULE) {
274
+ microtask(stabilize);
275
+ }
276
+ else {
277
+ stabilizer = STABILIZER_IDLE;
278
+ }
279
+ });
282
280
  }
283
281
 
284
282
  // https://github.com/stackblitz/alien-signals/blob/v2.0.3/src/system.ts#L100
@@ -359,6 +357,7 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
359
357
  }
360
358
  else {
361
359
  recompute(self, false);
360
+ root.disposables++;
362
361
  }
363
362
 
364
363
  return self;
@@ -446,17 +445,22 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
446
445
  };
447
446
 
448
447
  const root = <T>(fn: () => T) => {
449
- let o = observer;
448
+ let d = root.disposables,
449
+ o = observer;
450
450
 
451
451
  observer = null;
452
+ root.disposables = 0;
452
453
 
453
454
  let value = fn();
454
455
 
455
456
  observer = o;
457
+ root.disposables = d;
456
458
 
457
459
  return value;
458
460
  };
459
461
 
462
+ root.disposables = 0;
463
+
460
464
  const set = <T>(signal: Signal<T>, value: T) => {
461
465
  if (signal.value === value) {
462
466
  return;