@esportsplus/reactivity 0.12.4 → 0.13.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.
@@ -1,62 +1,8 @@
1
- import { REACTIVE_ARRAY } from '../constants.js';
2
- import { Computed, Infer } from '../types.js';
3
- import { ReactiveObject } from './object.js';
4
- type API<T> = Infer<T>[] & ReactiveArray<T>;
5
- type Events<T> = {
6
- pop: {
7
- item: Item<T>;
8
- };
9
- push: {
10
- items: Item<T>[];
11
- };
12
- reverse: undefined;
13
- set: {
14
- index: number;
15
- item: Item<T>;
16
- };
17
- shift: {
18
- item: Item<T>;
19
- };
20
- sort: undefined;
21
- splice: {
22
- deleteCount: number;
23
- items: Item<T>[];
24
- start: number;
25
- };
26
- unshift: {
27
- items: Item<T>[];
28
- };
1
+ import { Infer } from '../types.js';
2
+ type API<T> = Infer<T[]> & {
3
+ clear: () => void;
4
+ dispose: () => void;
29
5
  };
30
- type Item<T> = T | Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
31
- type Listener<V> = {
32
- once?: boolean;
33
- (value: V): void;
34
- };
35
- type Value<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : T extends Array<infer U> ? API<U> : T;
36
- declare class ReactiveArray<T> {
37
- [REACTIVE_ARRAY]: boolean;
38
- disposables: number;
39
- private data;
40
- private listeners;
41
- private proxy;
42
- constructor(data: Item<T>[], proxy: API<T>);
43
- get length(): number;
44
- set length(n: number);
45
- private cleanup;
46
- at(i: number): T | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
47
- dispatch<K extends keyof Events<T>, V>(event: K, value?: V): void;
48
- dispose(): void;
49
- map<R>(fn: (this: API<T>, value: Value<T>, i: number) => R, i?: number, n?: number): R[];
50
- on<K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>): void;
51
- once<K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>): void;
52
- pop(): Item<T> | undefined;
53
- push(...input: T[]): number;
54
- reverse(): this;
55
- shift(): Item<T> | undefined;
56
- sort(fn: (a: Value<T>, b: Value<T>) => number): this;
57
- splice(start: number, deleteCount?: number, ...input: T[]): Item<T>[];
58
- unshift(...input: T[]): number;
59
- }
60
- declare const _default: <T>(input: T[]) => API<T>;
6
+ declare const _default: <T>(data: T[]) => API<T>;
61
7
  export default _default;
62
8
  export type { API as ReactiveArray };
@@ -1,205 +1,217 @@
1
- import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isNumber } from '@esportsplus/utilities';
2
2
  import { REACTIVE_ARRAY } from '../constants.js';
3
- import { computed, dispose, isComputed, read } from '../system.js';
4
- import object, { isReactiveObject } from './object.js';
5
- class ReactiveArray {
6
- [REACTIVE_ARRAY] = true;
7
- disposables = 0;
8
- data;
9
- listeners = null;
10
- proxy;
11
- constructor(data, proxy) {
12
- this.data = data;
13
- this.proxy = proxy;
14
- }
15
- get length() {
16
- return this.data.length;
17
- }
18
- set length(n) {
19
- if (n > this.data.length) {
20
- return;
21
- }
22
- this.splice(n);
23
- }
24
- cleanup(item) {
25
- if (isReactiveObject(item)) {
26
- item.dispose();
27
- }
28
- else if (isComputed(item)) {
29
- dispose(item);
30
- }
3
+ import { dispose as d, isComputed, read } from '../system.js';
4
+ import { isReactiveObject } from './object.js';
5
+ function at(data, i) {
6
+ let value = data[i];
7
+ if (isComputed(value)) {
8
+ return read(value);
9
+ }
10
+ return value;
11
+ }
12
+ function cleanup(item) {
13
+ if (isReactiveObject(item)) {
14
+ item.dispose();
31
15
  }
32
- at(i) {
33
- let value = this.data[i];
34
- if (isComputed(value)) {
35
- return read(value);
36
- }
37
- return value;
16
+ else if (isComputed(item)) {
17
+ d(item);
38
18
  }
39
- dispatch(event, value) {
40
- if (this.listeners === null || this.listeners[event] === undefined) {
41
- return;
42
- }
43
- let listeners = this.listeners[event];
44
- for (let i = 0, n = listeners.length; i < n; i++) {
45
- let listener = listeners[i];
46
- if (listener === null) {
47
- continue;
48
- }
49
- try {
50
- listener(value);
51
- if (listener.once !== undefined) {
52
- listeners[i] = null;
53
- }
54
- }
55
- catch {
56
- listeners[i] = null;
19
+ }
20
+ function clear(data, listeners) {
21
+ dispose(data);
22
+ dispatch(listeners, 'clear');
23
+ }
24
+ function dispatch(listeners, event, value) {
25
+ if (listeners === null || listeners[event] === undefined) {
26
+ return;
27
+ }
28
+ let bucket = listeners[event];
29
+ for (let i = 0, n = bucket.length; i < n; i++) {
30
+ let listener = bucket[i];
31
+ if (listener === null) {
32
+ continue;
33
+ }
34
+ try {
35
+ listener(value);
36
+ if (listener.once !== undefined) {
37
+ bucket[i] = null;
57
38
  }
58
39
  }
59
- }
60
- dispose() {
61
- for (let i = 0, n = this.data.length; i < n; i++) {
62
- this.cleanup(this.data[i]);
40
+ catch {
41
+ bucket[i] = null;
63
42
  }
64
43
  }
65
- map(fn, i, n) {
66
- let { data, proxy } = this;
67
- if (i === undefined) {
68
- i = 0;
69
- }
70
- if (n === undefined) {
71
- n = data.length;
72
- }
73
- n = Math.min(n, data.length);
74
- let values = new Array(n - i);
75
- for (; i < n; i++) {
76
- let item = data[i];
77
- values[i] = fn.call(proxy, (isComputed(item) ? item.value : item), i);
78
- }
79
- return values;
80
- }
81
- on(event, listener) {
82
- if (this.listeners === null) {
83
- this.listeners = { [event]: [listener] };
84
- }
85
- else {
86
- let listeners = this.listeners[event];
87
- if (listeners === undefined) {
88
- this.listeners[event] = [listener];
89
- }
90
- else {
91
- let hole = listeners.length;
92
- for (let i = 0, n = hole; i < n; i++) {
93
- let l = listeners[i];
94
- if (l === listener) {
95
- return;
96
- }
97
- else if (l === null && hole === n) {
98
- hole = i;
99
- }
100
- }
101
- listeners[hole] = listener;
102
- }
103
- }
104
- }
105
- once(event, listener) {
106
- listener.once = true;
107
- this.on(event, listener);
108
- }
109
- pop() {
110
- let item = this.data.pop();
111
- if (item !== undefined) {
112
- this.cleanup(item);
113
- this.dispatch('pop', { item });
114
- }
115
- return item;
44
+ }
45
+ function dispose(data) {
46
+ let item;
47
+ while (item = data.pop()) {
48
+ cleanup(item);
116
49
  }
117
- push(...input) {
118
- let items = factory(input), n = this.data.push(...items);
119
- this.dispatch('push', { items });
120
- return n;
50
+ }
51
+ function map(data, proxy, fn, i, n) {
52
+ if (i === undefined) {
53
+ i = 0;
121
54
  }
122
- reverse() {
123
- this.data.reverse();
124
- this.dispatch('reverse');
125
- return this;
55
+ if (n === undefined) {
56
+ n = data.length;
126
57
  }
127
- shift() {
128
- let item = this.data.shift();
129
- if (item !== undefined) {
130
- this.cleanup(item);
131
- this.dispatch('shift', { item });
132
- }
133
- return item;
58
+ n = Math.min(n, data.length);
59
+ let values = new Array(n - i);
60
+ for (; i < n; i++) {
61
+ let item = data[i];
62
+ values[i] = fn.call(proxy, (isComputed(item) ? item.value : item), i);
134
63
  }
135
- sort(fn) {
136
- this.data.sort((a, b) => fn((isComputed(a) ? a.value : a), (isComputed(b) ? b.value : b)));
137
- this.dispatch('sort');
138
- return this;
139
- }
140
- splice(start, deleteCount = this.data.length, ...input) {
141
- let items = factory(input), removed = this.data.splice(start, deleteCount, ...items);
142
- if (items.length > 0 || removed.length > 0) {
143
- for (let i = 0, n = removed.length; i < n; i++) {
144
- this.cleanup(removed[i]);
64
+ return values;
65
+ }
66
+ function on(listeners, event, listener) {
67
+ let bucket = listeners[event];
68
+ if (bucket === undefined) {
69
+ listeners[event] = [listener];
70
+ }
71
+ else {
72
+ let hole = bucket.length;
73
+ for (let i = 0, n = hole; i < n; i++) {
74
+ let l = bucket[i];
75
+ if (l === listener) {
76
+ return;
77
+ }
78
+ else if (l === null && hole === n) {
79
+ hole = i;
145
80
  }
146
- this.dispatch('splice', {
147
- deleteCount,
148
- items,
149
- start
150
- });
151
81
  }
152
- return removed;
82
+ bucket[hole] = listener;
153
83
  }
154
- unshift(...input) {
155
- let items = factory(input), length = this.data.unshift(...items);
156
- this.dispatch('unshift', { items });
157
- return length;
84
+ }
85
+ function once(listeners, event, listener) {
86
+ listener.once = true;
87
+ on(listeners, event, listener);
88
+ }
89
+ function pop(data, listeners) {
90
+ let item = data.pop();
91
+ if (item !== undefined) {
92
+ cleanup(item);
93
+ dispatch(listeners, 'pop', { item });
158
94
  }
95
+ return item;
159
96
  }
160
- function factory(input) {
161
- let n = input.length, output = new Array(n);
162
- for (let i = 0; i < n; i++) {
163
- let value = input[i];
164
- if (isFunction(value)) {
165
- output[i] = computed(value);
166
- }
167
- else if (isObject(value)) {
168
- output[i] = object(value);
169
- }
170
- else {
171
- output[i] = value;
172
- }
97
+ function push(data, listeners, items) {
98
+ let n = data.push(...items);
99
+ dispatch(listeners, 'push', { items });
100
+ return n;
101
+ }
102
+ function reverse(data, listeners) {
103
+ data.reverse();
104
+ dispatch(listeners, 'reverse');
105
+ }
106
+ function shift(data, listeners) {
107
+ let item = data.shift();
108
+ if (item !== undefined) {
109
+ cleanup(item);
110
+ dispatch(listeners, 'shift', { item });
173
111
  }
174
- return output;
112
+ return item;
113
+ }
114
+ function sort(data, listeners, fn) {
115
+ data.sort((a, b) => fn((isComputed(a) ? a.value : a), (isComputed(b) ? b.value : b)));
116
+ dispatch(listeners, 'sort');
117
+ }
118
+ function splice(data, listeners, start, deleteCount = data.length, items = []) {
119
+ let removed = data.splice(start, deleteCount, ...items);
120
+ if (items.length > 0 || removed.length > 0) {
121
+ for (let i = 0, n = removed.length; i < n; i++) {
122
+ cleanup(removed[i]);
123
+ }
124
+ dispatch(listeners, 'splice', {
125
+ deleteCount,
126
+ items,
127
+ start
128
+ });
129
+ }
130
+ return removed;
175
131
  }
176
- export default (input) => {
177
- let proxy = new Proxy({}, {
132
+ function unshift(data, listeners, items) {
133
+ let length = data.unshift(...items);
134
+ dispatch(listeners, 'unshift', { items });
135
+ return length;
136
+ }
137
+ export default (data) => {
138
+ let listeners = {}, proxy = new Proxy({}, {
178
139
  get(_, key) {
179
140
  if (isNumber(key)) {
180
- let value = wrapped[key];
141
+ let value = data[key];
181
142
  if (isComputed(value)) {
182
143
  return read(value);
183
144
  }
184
145
  return value;
185
146
  }
186
- else if (key in array) {
187
- return array[key];
147
+ else if (key in wrapper) {
148
+ return wrapper[key];
149
+ }
150
+ else if (key === 'length') {
151
+ return data.length;
188
152
  }
189
- return wrapped[key];
153
+ return data[key];
190
154
  },
191
155
  set(_, key, value) {
192
156
  if (isNumber(key)) {
193
- array.splice(key, 1, value);
194
- return true;
157
+ splice(data, listeners, key, 1, value);
195
158
  }
196
159
  else if (key === 'length') {
197
- array.length = value;
198
- return true;
160
+ if (value >= data.length) {
161
+ }
162
+ else if (value === 0) {
163
+ clear(data, listeners);
164
+ }
165
+ else {
166
+ splice(data, listeners, value);
167
+ }
199
168
  }
200
- return false;
201
- }
202
- }), wrapped = factory(input);
203
- let array = new ReactiveArray(wrapped, proxy);
169
+ else {
170
+ return false;
171
+ }
172
+ return true;
173
+ }
174
+ }), wrapper = {
175
+ [REACTIVE_ARRAY]: true,
176
+ at: (i) => at(data, i),
177
+ clear: () => {
178
+ clear(data, listeners);
179
+ return proxy;
180
+ },
181
+ dispatch: (event, value) => {
182
+ dispatch(listeners, event, value);
183
+ return proxy;
184
+ },
185
+ dispose: () => {
186
+ dispose(data);
187
+ return proxy;
188
+ },
189
+ map: (fn, i, n) => {
190
+ return map(data, proxy, fn, i, n);
191
+ },
192
+ on: (event, listener) => {
193
+ on(listeners, event, listener);
194
+ return proxy;
195
+ },
196
+ once: (event, listener) => {
197
+ once(listeners, event, listener);
198
+ return proxy;
199
+ },
200
+ pop: () => pop(data, listeners),
201
+ push: (...items) => push(data, listeners, items),
202
+ reverse: () => {
203
+ reverse(data, listeners);
204
+ return proxy;
205
+ },
206
+ shift: () => shift(data, listeners),
207
+ sort: (fn) => {
208
+ sort(data, listeners, fn);
209
+ return proxy;
210
+ },
211
+ splice: (start, deleteCount, ...items) => {
212
+ return splice(data, listeners, start, deleteCount, items);
213
+ },
214
+ unshift: (...items) => unshift(data, listeners, items),
215
+ };
204
216
  return proxy;
205
217
  };
@@ -3,7 +3,8 @@ import object from './object.js';
3
3
  type API<T> = T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
4
4
  type Input<T> = T extends {
5
5
  dispose: any;
6
- } ? never : T extends Record<PropertyKey, unknown> ? T : T extends unknown[] ? Input<T[number]>[] : never;
7
- declare const _default: <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>) => API<T>;
6
+ } ? {
7
+ never: '[ dispose, signals ] are reserved keys';
8
+ } : T extends Record<PropertyKey, unknown> | unknown[] ? T : never;
9
+ declare const _default: <T>(input: Input<T>) => API<T>;
8
10
  export default _default;
9
- export type { Input };
@@ -1,9 +1,11 @@
1
1
  import { Prettify } from '@esportsplus/utilities';
2
2
  import { Infer } from '../types.js';
3
3
  import { REACTIVE_OBJECT } from '../constants.js';
4
- type API<T extends Record<PropertyKey, unknown>> = Prettify<{
4
+ type API<T> = Prettify<{
5
5
  [K in keyof T]: Infer<T[K]>;
6
- }> & ReactiveObject<T>;
6
+ } & {
7
+ dispose: VoidFunction;
8
+ }>;
7
9
  declare class ReactiveObject<T extends Record<PropertyKey, unknown>> {
8
10
  [REACTIVE_OBJECT]: boolean;
9
11
  private disposers;
@@ -62,11 +62,12 @@ class ReactiveObject {
62
62
  }
63
63
  }
64
64
  dispose() {
65
- let disposers = this.disposers;
66
- if (disposers) {
67
- for (let i = 0, n = disposers.length; i < n; i++) {
68
- disposers[i]();
69
- }
65
+ let disposers = this.disposers, disposer;
66
+ if (!disposers) {
67
+ return;
68
+ }
69
+ while (disposer = disposers.pop()) {
70
+ disposer();
70
71
  }
71
72
  }
72
73
  }
package/build/types.d.ts CHANGED
@@ -15,7 +15,7 @@ interface Computed<T> {
15
15
  subsTail: Link | null;
16
16
  value: T;
17
17
  }
18
- type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefined : T extends (...args: any[]) => infer R ? R : T extends (infer U)[] ? ReactiveArray<U> : T extends ReactiveObject<any> ? T : T extends Record<PropertyKey, unknown> ? {
18
+ type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefined : T extends (...args: any[]) => infer R ? R : T extends (infer U)[] ? Infer<U>[] : T extends ReactiveObject<any> ? T : T extends Record<PropertyKey, unknown> ? {
19
19
  [K in keyof T]: T[K];
20
20
  } : T;
21
21
  interface Link {
@@ -26,11 +26,10 @@ interface Link {
26
26
  prevSub: Link | null;
27
27
  version: number;
28
28
  }
29
- type Reactive<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : ReactiveArray<T>;
30
29
  type Signal<T> = {
31
30
  [SIGNAL]: true;
32
31
  subs: Link | null;
33
32
  subsTail: Link | null;
34
33
  value: T;
35
34
  };
36
- export type { Computed, Infer, Link, Signal, Reactive, ReactiveArray, ReactiveObject };
35
+ export type { Computed, Infer, Link, Signal, ReactiveArray, ReactiveObject };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "ICJR",
3
3
  "dependencies": {
4
4
  "@esportsplus/custom-function": "^0.0.13",
5
- "@esportsplus/utilities": "^0.21.1"
5
+ "@esportsplus/utilities": "^0.22.1"
6
6
  },
7
7
  "devDependencies": {
8
8
  "@esportsplus/typescript": "^0.9.2"
@@ -12,7 +12,7 @@
12
12
  "private": false,
13
13
  "type": "module",
14
14
  "types": "build/index.d.ts",
15
- "version": "0.12.4",
15
+ "version": "0.13.1",
16
16
  "scripts": {
17
17
  "build": "tsc && tsc-alias",
18
18
  "-": "-"
@@ -1,307 +1,246 @@
1
- import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isNumber } from '@esportsplus/utilities';
2
2
  import { REACTIVE_ARRAY } from '~/constants';
3
- import { computed, dispose, isComputed, read } from '~/system';
4
- import { Computed, Infer } from '~/types';
5
- import object, { isReactiveObject, ReactiveObject } from './object';
3
+ import { dispose as d, isComputed, read } from '~/system';
4
+ import { Infer } from '~/types';
5
+ import { isReactiveObject } from './object';
6
6
 
7
7
 
8
- type API<T> = Infer<T>[] & ReactiveArray<T>;
8
+ type API<T> = Infer<T[]> & {
9
+ clear: () => void;
10
+ dispose: () => void;
11
+ };
9
12
 
10
13
  type Events<T> = {
14
+ clear: undefined,
11
15
  pop: {
12
- item: Item<T>;
16
+ item: T;
13
17
  };
14
18
  push: {
15
- items: Item<T>[];
19
+ items: T[];
16
20
  };
17
21
  reverse: undefined;
18
22
  set: {
19
23
  index: number;
20
- item: Item<T>;
24
+ item: T;
21
25
  };
22
26
  shift: {
23
- item: Item<T>;
27
+ item: T;
24
28
  };
25
29
  sort: undefined;
26
30
  splice: {
27
31
  deleteCount: number;
28
- items: Item<T>[];
32
+ items: T[];
29
33
  start: number;
30
34
  };
31
35
  unshift: {
32
- items: Item<T>[];
36
+ items: T[];
33
37
  };
34
38
  };
35
39
 
36
- type Item<T> = T | Computed<T> | API<T> | ReactiveObject< T extends Record<PropertyKey, unknown> ? T : never >;
37
-
38
40
  type Listener<V> = {
39
41
  once?: boolean;
40
42
  (value: V): void;
41
43
  };
42
44
 
43
- type Value<T> =
44
- T extends Record<PropertyKey, unknown>
45
- ? ReactiveObject<T>
46
- : T extends Array<infer U>
47
- ? API<U>
48
- : T;
49
-
50
-
51
- class ReactiveArray<T> {
52
- [REACTIVE_ARRAY] = true;
53
- disposables: number = 0;
54
-
55
- private data: Item<T>[];
56
-
57
- private listeners: Record<string, (Listener<any> | null)[]> | null = null;
45
+ type Listeners = Record<string, (Listener<any> | null)[]>;
58
46
 
59
- private proxy: API<T>;
60
47
 
48
+ function at<T>(data: T[], i: number) {
49
+ let value = data[i];
61
50
 
62
- constructor(data: Item<T>[], proxy: API<T>) {
63
- this.data = data;
64
- this.proxy = proxy;
51
+ if (isComputed(value)) {
52
+ return read(value);
65
53
  }
66
54
 
55
+ return value;
56
+ }
67
57
 
68
- get length(): number {
69
- return this.data.length;
58
+ function cleanup<T>(item: T) {
59
+ if (isReactiveObject(item)) {
60
+ item.dispose();
70
61
  }
71
-
72
- set length(n: number) {
73
- if (n > this.data.length) {
74
- return;
75
- }
76
-
77
- this.splice(n);
62
+ else if (isComputed(item)) {
63
+ d(item);
78
64
  }
65
+ }
79
66
 
67
+ function clear<T>(data: T[], listeners: Listeners) {
68
+ dispose(data);
69
+ dispatch(listeners, 'clear');
70
+ }
80
71
 
81
- private cleanup(item: Item<T>) {
82
- if (isReactiveObject(item)) {
83
- item.dispose();
84
- }
85
- else if (isComputed(item)) {
86
- dispose(item);
87
- }
72
+ function dispatch<T, K extends keyof Events<T>, V>(listeners: Listeners, event: K, value?: V) {
73
+ if (listeners === null || listeners[event] === undefined) {
74
+ return;
88
75
  }
89
76
 
77
+ let bucket = listeners[event];
90
78
 
91
- at(i: number) {
92
- let value = this.data[i];
93
-
94
- if (isComputed(value)) {
95
- return read(value);
96
- }
97
-
98
- return value;
99
- }
79
+ for (let i = 0, n = bucket.length; i < n; i++) {
80
+ let listener = bucket[i];
100
81
 
101
- dispatch<K extends keyof Events<T>, V>(event: K, value?: V) {
102
- if (this.listeners === null || this.listeners[event] === undefined) {
103
- return;
82
+ if (listener === null) {
83
+ continue;
104
84
  }
105
85
 
106
- let listeners = this.listeners[event];
107
-
108
- for (let i = 0, n = listeners.length; i < n; i++) {
109
- let listener = listeners[i];
110
-
111
- if (listener === null) {
112
- continue;
113
- }
114
-
115
- try {
116
- listener(value);
86
+ try {
87
+ listener(value);
117
88
 
118
- if (listener.once !== undefined) {
119
- listeners[i] = null;
120
- }
121
- }
122
- catch {
123
- listeners[i] = null;
89
+ if (listener.once !== undefined) {
90
+ bucket[i] = null;
124
91
  }
125
92
  }
126
- }
127
-
128
- dispose() {
129
- for (let i = 0, n = this.data.length; i < n; i++) {
130
- this.cleanup(this.data[i]);
93
+ catch {
94
+ bucket[i] = null;
131
95
  }
132
96
  }
97
+ }
133
98
 
134
- map<R>(
135
- fn: (this: API<T>, value: Value<T>, i: number) => R,
136
- i?: number,
137
- n?: number
138
- ) {
139
- let { data, proxy } = this;
99
+ function dispose<T>(data: T[]) {
100
+ let item;
140
101
 
141
- if (i === undefined) {
142
- i = 0;
143
- }
102
+ while (item = data.pop()) {
103
+ cleanup(item);
104
+ }
105
+ }
144
106
 
145
- if (n === undefined) {
146
- n = data.length;
147
- }
107
+ function map<T, R>(
108
+ data: T[],
109
+ proxy: API<T>,
110
+ fn: (this: API<T>, value: T, i: number) => R,
111
+ i?: number,
112
+ n?: number
113
+ ) {
114
+ if (i === undefined) {
115
+ i = 0;
116
+ }
148
117
 
149
- n = Math.min(n, data.length);
118
+ if (n === undefined) {
119
+ n = data.length;
120
+ }
150
121
 
151
- let values: R[] = new Array(n - i);
122
+ n = Math.min(n, data.length);
152
123
 
153
- for (; i < n; i++) {
154
- let item = data[i];
124
+ let values: R[] = new Array(n - i);
155
125
 
156
- values[i] = fn.call(
157
- proxy,
158
- (isComputed(item) ? item.value : item) as Value<T>,
159
- i
160
- );
161
- }
126
+ for (; i < n; i++) {
127
+ let item = data[i];
162
128
 
163
- return values;
129
+ values[i] = fn.call(
130
+ proxy,
131
+ (isComputed(item) ? item.value : item) as T,
132
+ i
133
+ );
164
134
  }
165
135
 
166
- on<K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>) {
167
- if (this.listeners === null) {
168
- this.listeners = { [event]: [listener] };
169
- }
170
- else {
171
- let listeners = this.listeners[event];
136
+ return values;
137
+ }
172
138
 
173
- if (listeners === undefined) {
174
- this.listeners[event] = [listener];
175
- }
176
- else {
177
- let hole = listeners.length;
139
+ function on<T, K extends keyof Events<T>>(listeners: Listeners, event: K, listener: Listener<Events<T>[K]>) {
140
+ let bucket = listeners[event];
178
141
 
179
- for (let i = 0, n = hole; i < n; i++) {
180
- let l = listeners[i];
142
+ if (bucket === undefined) {
143
+ listeners[event] = [listener];
144
+ }
145
+ else {
146
+ let hole = bucket.length;
181
147
 
182
- if (l === listener) {
183
- return;
184
- }
185
- else if (l === null && hole === n) {
186
- hole = i;
187
- }
188
- }
148
+ for (let i = 0, n = hole; i < n; i++) {
149
+ let l = bucket[i];
189
150
 
190
- listeners[hole] = listener;
151
+ if (l === listener) {
152
+ return;
153
+ }
154
+ else if (l === null && hole === n) {
155
+ hole = i;
191
156
  }
192
157
  }
193
- }
194
158
 
195
- once<K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>) {
196
- listener.once = true;
197
- this.on(event, listener);
159
+ bucket[hole] = listener;
198
160
  }
161
+ }
199
162
 
200
- pop() {
201
- let item = this.data.pop();
163
+ function once<T, K extends keyof Events<T>>(listeners: Listeners, event: K, listener: Listener<Events<T>[K]>) {
164
+ listener.once = true;
165
+ on(listeners, event, listener);
166
+ }
202
167
 
203
- if (item !== undefined) {
204
- this.cleanup(item);
205
- this.dispatch('pop', { item });
206
- }
168
+ function pop<T>(data: T[], listeners: Listeners) {
169
+ let item = data.pop();
207
170
 
208
- return item;
171
+ if (item !== undefined) {
172
+ cleanup(item);
173
+ dispatch(listeners, 'pop', { item });
209
174
  }
210
175
 
211
- push(...input: T[]) {
212
- let items = factory(input),
213
- n = this.data.push(...items);
176
+ return item;
177
+ }
214
178
 
215
- this.dispatch('push', { items });
179
+ function push<T>(data: T[], listeners: Listeners, items: T[]) {
180
+ let n = data.push(...items);
216
181
 
217
- return n;
218
- }
182
+ dispatch(listeners, 'push', { items });
219
183
 
220
- reverse() {
221
- this.data.reverse();
222
- this.dispatch('reverse');
223
-
224
- return this;
225
- }
184
+ return n;
185
+ }
226
186
 
227
- shift() {
228
- let item = this.data.shift();
187
+ function reverse<T>(data: T[], listeners: Listeners) {
188
+ data.reverse();
189
+ dispatch(listeners, 'reverse');
190
+ }
229
191
 
230
- if (item !== undefined) {
231
- this.cleanup(item);
232
- this.dispatch('shift', { item });
233
- }
192
+ function shift<T>(data: T[], listeners: Listeners) {
193
+ let item = data.shift();
234
194
 
235
- return item;
195
+ if (item !== undefined) {
196
+ cleanup(item);
197
+ dispatch(listeners, 'shift', { item });
236
198
  }
237
199
 
238
- sort(fn: (a: Value<T>, b: Value<T>) => number) {
239
- this.data.sort((a, b) => fn(
240
- (isComputed(a) ? a.value : a) as Value<T>,
241
- (isComputed(b) ? b.value : b) as Value<T>
242
- ));
243
- this.dispatch('sort');
244
-
245
- return this;
246
- }
200
+ return item;
201
+ }
247
202
 
248
- splice(start: number, deleteCount: number = this.data.length, ...input: T[]) {
249
- let items = factory(input),
250
- removed = this.data.splice(start, deleteCount, ...items);
203
+ function sort<T>(data: T[], listeners: Listeners, fn: (a: T, b: T) => number) {
204
+ data.sort((a, b) => fn(
205
+ (isComputed(a) ? a.value : a) as T,
206
+ (isComputed(b) ? b.value : b) as T
207
+ ));
208
+ dispatch(listeners, 'sort');
209
+ }
251
210
 
252
- if (items.length > 0 || removed.length > 0) {
253
- for (let i = 0, n = removed.length; i < n; i++) {
254
- this.cleanup(removed[i]);
255
- }
211
+ function splice<T>(data: T[], listeners: Listeners, start: number, deleteCount: number = data.length, items: T[] = []) {
212
+ let removed = data.splice(start, deleteCount, ...items);
256
213
 
257
- this.dispatch('splice', {
258
- deleteCount,
259
- items,
260
- start
261
- });
214
+ if (items.length > 0 || removed.length > 0) {
215
+ for (let i = 0, n = removed.length; i < n; i++) {
216
+ cleanup(removed[i]);
262
217
  }
263
218
 
264
- return removed;
219
+ dispatch(listeners, 'splice', {
220
+ deleteCount,
221
+ items,
222
+ start
223
+ });
265
224
  }
266
225
 
267
- unshift(...input: T[]) {
268
- let items = factory(input),
269
- length = this.data.unshift(...items);
270
-
271
- this.dispatch('unshift', { items });
272
-
273
- return length;
274
- }
226
+ return removed;
275
227
  }
276
228
 
229
+ function unshift<T>(data: T[], listeners: Listeners, items: T[]) {
230
+ let length = data.unshift(...items);
277
231
 
278
- function factory<T>(input: T[]) {
279
- let n = input.length,
280
- output: Item<T>[] = new Array(n);
232
+ dispatch(listeners, 'unshift', { items });
281
233
 
282
- for (let i = 0; i < n; i++) {
283
- let value = input[i];
284
-
285
- if (isFunction(value)) {
286
- output[i] = computed(value as Computed<T>['fn']);
287
- }
288
- else if (isObject(value)) {
289
- output[i] = object(value) as Item<T>;
290
- }
291
- else {
292
- output[i] = value;
293
- }
294
- }
295
-
296
- return output;
234
+ return length;
297
235
  }
298
236
 
299
237
 
300
- export default <T>(input: T[]) => {
301
- let proxy = new Proxy({}, {
302
- get(_: any, key: any) {
238
+ export default <T>(data: T[]) => {
239
+ let listeners: Listeners = {},
240
+ proxy = new Proxy({}, {
241
+ get(_, key: any) {
303
242
  if (isNumber(key)) {
304
- let value = wrapped[key];
243
+ let value = data[key];
305
244
 
306
245
  if (isComputed(value)) {
307
246
  return read(value);
@@ -309,29 +248,83 @@ export default <T>(input: T[]) => {
309
248
 
310
249
  return value;
311
250
  }
312
- else if (key in array) {
313
- return array[key as keyof typeof array];
251
+ else if (key in wrapper) {
252
+ return wrapper[key as keyof typeof wrapper];
253
+ }
254
+ else if (key === 'length') {
255
+ return data.length;
314
256
  }
315
257
 
316
- return wrapped[key];
258
+ return data[key];
317
259
  },
318
- set(_: any, key: any, value: any) {
260
+ set(_, key: any, value: any) {
319
261
  if (isNumber(key)) {
320
- array.splice(key, 1, value);
321
- return true;
262
+ splice(data, listeners, key, 1, value);
322
263
  }
323
264
  else if (key === 'length') {
324
- array.length = value;
325
- return true;
265
+ if (value >= data.length) {
266
+ }
267
+ else if (value === 0) {
268
+ clear(data, listeners);
269
+ }
270
+ else {
271
+ splice(data, listeners, value);
272
+ }
273
+ }
274
+ else {
275
+ return false;
326
276
  }
327
277
 
328
- return false;
278
+ return true;
329
279
  }
330
280
  }) as API<T>,
331
- wrapped = factory(input);
332
-
333
- let array = new ReactiveArray(wrapped, proxy);
281
+ wrapper = {
282
+ [REACTIVE_ARRAY]: true,
283
+ at: (i: number) => at(data, i),
284
+ clear: () => {
285
+ clear(data, listeners);
286
+ return proxy;
287
+ },
288
+ dispatch: <K extends keyof Events<T>, V>(event: K, value?: V) => {
289
+ dispatch(listeners, event, value);
290
+ return proxy;
291
+ },
292
+ dispose: () => {
293
+ dispose(data);
294
+ return proxy;
295
+ },
296
+ map: <R>(
297
+ fn: (this: API<T>, value: T, i: number) => R,
298
+ i?: number,
299
+ n?: number
300
+ ) => {
301
+ return map(data, proxy, fn, i, n);
302
+ },
303
+ on: <K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>) => {
304
+ on(listeners, event, listener);
305
+ return proxy;
306
+ },
307
+ once: <K extends keyof Events<T>>(event: K, listener: Listener<Events<T>[K]>) => {
308
+ once(listeners, event, listener);
309
+ return proxy;
310
+ },
311
+ pop: () => pop(data, listeners),
312
+ push: (...items: T[]) => push(data, listeners, items),
313
+ reverse: () => {
314
+ reverse(data, listeners);
315
+ return proxy;
316
+ },
317
+ shift: () => shift(data, listeners),
318
+ sort: (fn: (a: T, b: T) => number) => {
319
+ sort(data, listeners, fn);
320
+ return proxy;
321
+ },
322
+ splice: (start: number, deleteCount?: number, ...items: T[]) => {
323
+ return splice(data, listeners, start, deleteCount, items);
324
+ },
325
+ unshift: (...items: T[]) => unshift(data, listeners, items),
326
+ };
334
327
 
335
328
  return proxy;
336
329
  };
337
- export type { API as ReactiveArray };
330
+ export type { API as ReactiveArray };
@@ -13,15 +13,13 @@ type API<T> =
13
13
 
14
14
  type Input<T> =
15
15
  T extends { dispose: any }
16
- ? never
17
- : T extends Record<PropertyKey, unknown>
16
+ ? { never: '[ dispose, signals ] are reserved keys' }
17
+ : T extends Record<PropertyKey, unknown> | unknown[]
18
18
  ? T
19
- : T extends unknown[]
20
- ? Input<T[number]>[]
21
- : never;
19
+ : never;
22
20
 
23
21
 
24
- export default <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>): API<T> => {
22
+ export default <T>(input: Input<T>): API<T> => {
25
23
  let value: API<T> | undefined;
26
24
 
27
25
  return root(() => {
@@ -42,5 +40,4 @@ export default <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input
42
40
 
43
41
  throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
44
42
  });
45
- };
46
- export type { Input };
43
+ };
@@ -5,7 +5,7 @@ import { REACTIVE_OBJECT } from '~/constants';
5
5
  import array from './array';
6
6
 
7
7
 
8
- type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
8
+ type API<T> = Prettify<{ [K in keyof T]: Infer<T[K]> } & { dispose: VoidFunction } >;
9
9
 
10
10
 
11
11
  class ReactiveObject<T extends Record<PropertyKey, unknown>> {
@@ -90,12 +90,15 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
90
90
 
91
91
 
92
92
  dispose() {
93
- let disposers = this.disposers;
93
+ let disposers = this.disposers,
94
+ disposer;
94
95
 
95
- if (disposers) {
96
- for (let i = 0, n = disposers.length; i < n; i++) {
97
- disposers[i]();
98
- }
96
+ if (!disposers) {
97
+ return;
98
+ }
99
+
100
+ while (disposer = disposers.pop()) {
101
+ disposer();
99
102
  }
100
103
  }
101
104
  }
package/src/types.ts CHANGED
@@ -29,7 +29,7 @@ type Infer<T> =
29
29
  : T extends (...args: any[]) => infer R
30
30
  ? R
31
31
  : T extends (infer U)[]
32
- ? ReactiveArray<U>
32
+ ? Infer<U>[]
33
33
  : T extends ReactiveObject<any>
34
34
  ? T
35
35
  : T extends Record<PropertyKey, unknown>
@@ -45,10 +45,6 @@ interface Link {
45
45
  version: number;
46
46
  }
47
47
 
48
- type Reactive<T> = T extends Record<PropertyKey, unknown>
49
- ? ReactiveObject<T>
50
- : ReactiveArray<T>;
51
-
52
48
  type Signal<T> = {
53
49
  [SIGNAL]: true;
54
50
  subs: Link | null;
@@ -62,5 +58,5 @@ export type {
62
58
  Infer,
63
59
  Link,
64
60
  Signal,
65
- Reactive, ReactiveArray, ReactiveObject
61
+ ReactiveArray, ReactiveObject
66
62
  };