@esportsplus/reactivity 0.1.30 → 0.2.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.
package/build/macro.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import CustomFunction from '@esportsplus/custom-function';
2
- import { Computed, Options } from './types';
3
- type Function<A extends unknown[], R> = Computed<(...args: A) => R>['fn'];
2
+ import { Options } from './types';
4
3
  declare class Macro<A extends unknown[], R> extends CustomFunction {
5
4
  private factory;
6
- constructor(fn: Function<A, R>, options?: Options);
5
+ constructor(fn: Macro<A, R>['factory']['fn'], options?: Options);
7
6
  dispose(): void;
8
7
  }
9
- declare const _default: <A extends unknown[], R>(fn: Function<A, R>, options?: Options) => Macro<A, R>;
8
+ declare const _default: <A extends unknown[], R>(fn: Macro<A, R>["factory"]["fn"], options?: Options) => Macro<A, R>;
10
9
  export default _default;
@@ -1,59 +1,49 @@
1
- import { Listener, Object, Options } from '../types';
1
+ import { Listener, Options, Signal } from '../types';
2
2
  import { ReactiveObject } from './object';
3
3
  type Events<T> = {
4
- fill: {
5
- value: T;
6
- };
7
4
  pop: {
8
- item: T;
5
+ item: R<T>;
9
6
  };
10
7
  push: {
11
- items: T[];
8
+ items: R<T>[];
12
9
  };
13
10
  reverse: undefined;
14
11
  shift: {
15
- item: T;
12
+ item: R<T>;
16
13
  };
17
14
  sort: undefined;
18
15
  splice: {
19
16
  deleteCount: number;
20
- items: T[];
17
+ items: R<T>[];
21
18
  start: number;
22
19
  };
23
20
  unshift: {
24
- items: T[];
21
+ items: R<T>[];
25
22
  };
26
23
  };
27
- declare class ReactiveArray<T> extends Array<T> {
24
+ type R<T> = Signal<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? {
25
+ [K in keyof T]: T[K];
26
+ } : never>;
27
+ declare class ReactiveArray<T> extends Array<R<T>> {
28
+ private options;
29
+ private self;
28
30
  private signal;
29
- constructor(data: T[]);
31
+ constructor(data: R<T>[], options?: Options);
30
32
  set length(n: number);
33
+ at(index: number): any;
31
34
  dispatch<E extends keyof Events<T>>(event: E, data?: Events<T>[E]): void;
32
35
  dispose(): void;
33
- fill(value: T, start?: number, end?: number): this;
34
- map<U>(fn: (this: ReactiveArray<T>, value: T, i: number, values: this) => U): U[];
36
+ map<U>(fn: (this: T[], value: T, i: number) => U): U[];
35
37
  on<E extends keyof Events<T>>(event: E, listener: Listener<Events<T>[E]>): void;
36
38
  once<E extends keyof Events<T>>(event: E, listener: Listener<Events<T>[E]>): void;
37
- pop(): T | undefined;
38
- push(...items: T[]): number;
39
+ pop(): R<T> | undefined;
40
+ push(...input: T[]): number;
39
41
  reverse(): this;
40
- shift(): T | undefined;
42
+ shift(): R<T> | undefined;
41
43
  sort(): this;
42
- splice(start: number, deleteCount?: number, ...items: T[]): T[];
43
- track(index?: number): T | undefined;
44
- trigger(): void;
45
- unshift(...items: T[]): number;
46
- }
47
- declare class ReactiveObjectArray<T extends Object> extends ReactiveArray<ReactiveObject<T>> {
48
- private options;
49
- constructor(data: T[], options?: Options);
50
- fill(): never;
51
- reverse(): never;
52
- pop(): ReactiveObject<T> | undefined;
53
- push(...values: T[]): number;
54
- shift(): ReactiveObject<T> | undefined;
55
- sort(): never;
56
- splice(start: number, deleteCount?: number, ...values: T[]): ReactiveObject<T> | ReactiveObject<T>[] | null | undefined;
57
- unshift(...values: T[]): number;
44
+ splice(start: number, deleteCount?: number, ...input: T[]): R<T>[];
45
+ unshift(...input: T[]): number;
58
46
  }
59
- export { ReactiveArray, ReactiveObjectArray };
47
+ declare const _default: <T>(input: T[], options?: Options) => any;
48
+ export default _default;
49
+ export { ReactiveArray };
@@ -1,19 +1,63 @@
1
- import { dispose, signal } from '../signal';
1
+ import { dispose, signal, Reactive } from '../signal';
2
+ import { isInstanceOf, isNumber, isObject } from '../utilities';
2
3
  import { ReactiveObject } from './object';
3
- function factory(data, options = {}) {
4
- let signals = [];
5
- for (let i = 0, n = data.length; i < n; i++) {
6
- signals.push(new ReactiveObject(data[i], options));
4
+ let handler = {
5
+ get(target, prop) {
6
+ let value = target[prop];
7
+ if (value === undefined) {
8
+ return value;
9
+ }
10
+ if (isInstanceOf(value, Reactive)) {
11
+ return value.get();
12
+ }
13
+ else if (supported.has(prop)) {
14
+ return value;
15
+ }
16
+ throw new Error(`Reactivity: '${prop}' is not supported on reactive arrays`);
17
+ },
18
+ set(target, prop, value) {
19
+ if (isNumber(prop)) {
20
+ let host = target[prop];
21
+ if (isInstanceOf(host, Reactive)) {
22
+ host.set(value);
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+ return target[prop] = value;
28
+ }
29
+ }, supported = new Set([
30
+ 'at',
31
+ 'dispatch', 'dispose',
32
+ 'length',
33
+ 'map',
34
+ 'on', 'once',
35
+ 'pop', 'push',
36
+ 'reverse',
37
+ 'self', 'shift', 'sort', 'splice',
38
+ 'unshift'
39
+ ]);
40
+ function factory(input, options = {}) {
41
+ let items = [];
42
+ for (let i = 0, n = input.length; i < n; i++) {
43
+ let value = input[i];
44
+ if (isObject(value)) {
45
+ items[i] = new ReactiveObject(value, options);
46
+ }
47
+ else {
48
+ items[i] = signal(value);
49
+ }
7
50
  }
8
- return signals;
9
- }
10
- function unsupported(method) {
11
- throw new Error(`Reactivity: '${method}' is not supported on reactive object array`);
51
+ return items;
12
52
  }
13
53
  class ReactiveArray extends Array {
54
+ options;
55
+ self;
14
56
  signal;
15
- constructor(data) {
57
+ constructor(data, options = {}) {
16
58
  super(...data);
59
+ this.options = options;
60
+ this.self = this;
17
61
  this.signal = signal(false);
18
62
  }
19
63
  set length(n) {
@@ -22,22 +66,24 @@ class ReactiveArray extends Array {
22
66
  }
23
67
  this.splice(n);
24
68
  }
69
+ at(index) {
70
+ let value = super.at(index);
71
+ if (isInstanceOf(value, Reactive)) {
72
+ return value.get();
73
+ }
74
+ return value;
75
+ }
25
76
  dispatch(event, data) {
26
77
  this.signal.dispatch(event, data);
27
78
  }
28
79
  dispose() {
29
80
  this.signal.dispose();
30
- }
31
- fill(value, start, end) {
32
- super.fill(value, start, end);
33
- this.dispatch('fill', { value });
34
- this.trigger();
35
- return this;
81
+ dispose(this.self);
36
82
  }
37
83
  map(fn) {
38
- let values = [];
39
- for (let i = 0, n = this.length; i < n; i++) {
40
- values.push(fn.call(this, this[i], i, this));
84
+ let self = this.self, values = [];
85
+ for (let i = 0, n = self.length; i < n; i++) {
86
+ values.push(fn.call(this, self[i].value, i));
41
87
  }
42
88
  return values;
43
89
  }
@@ -50,92 +96,53 @@ class ReactiveArray extends Array {
50
96
  pop() {
51
97
  let item = super.pop();
52
98
  if (item !== undefined) {
53
- this.dispatch('pop', { item });
54
- this.trigger();
99
+ dispose(item);
100
+ this.signal.dispatch('pop', { item });
55
101
  }
56
102
  return item;
57
103
  }
58
- push(...items) {
59
- let n = super.push(...items);
60
- this.dispatch('push', { items });
61
- this.trigger();
104
+ push(...input) {
105
+ let items = factory(input, this.options), n = super.push(...items);
106
+ this.signal.dispatch('push', { items });
62
107
  return n;
63
108
  }
64
109
  reverse() {
65
110
  super.reverse();
66
- this.dispatch('reverse');
67
- this.trigger();
111
+ this.signal.dispatch('reverse');
68
112
  return this;
69
113
  }
70
114
  shift() {
71
115
  let item = super.shift();
72
116
  if (item !== undefined) {
73
- this.dispatch('shift', { item });
74
- this.trigger();
117
+ dispose(item);
118
+ this.signal.dispatch('shift', { item });
75
119
  }
76
120
  return item;
77
121
  }
78
122
  sort() {
79
123
  super.sort();
80
- this.dispatch('sort');
81
- this.trigger();
124
+ this.signal.dispatch('sort');
82
125
  return this;
83
126
  }
84
- splice(start, deleteCount = super.length, ...items) {
85
- let removed = super.splice(start, deleteCount, ...items);
127
+ splice(start, deleteCount = super.length, ...input) {
128
+ let items = factory(input, this.options), removed = super.splice(start, deleteCount, ...items);
86
129
  if (items.length > 0 || removed.length > 0) {
87
- this.dispatch('splice', {
130
+ dispose(removed);
131
+ this.signal.dispatch('splice', {
88
132
  deleteCount,
89
133
  items,
90
134
  start
91
135
  });
92
- this.trigger();
93
136
  }
94
137
  return removed;
95
138
  }
96
- track(index) {
97
- this.signal.get();
98
- return index === undefined ? undefined : this[index];
99
- }
100
- trigger() {
101
- this.signal.set(!this.signal.value);
102
- }
103
- unshift(...items) {
104
- let length = super.unshift(...items);
105
- this.dispatch('unshift', { items });
106
- this.trigger();
139
+ unshift(...input) {
140
+ let items = factory(input, this.options), length = super.unshift(...items);
141
+ this.signal.dispatch('unshift', { items });
107
142
  return length;
108
143
  }
109
144
  }
110
- class ReactiveObjectArray extends ReactiveArray {
111
- options;
112
- constructor(data, options = {}) {
113
- super(factory(data, options));
114
- this.options = options;
115
- }
116
- fill() {
117
- return unsupported('fill');
118
- }
119
- reverse() {
120
- return unsupported('reverse');
121
- }
122
- pop() {
123
- return dispose(super.pop());
124
- }
125
- push(...values) {
126
- return super.push(...factory(values, this.options));
127
- }
128
- shift() {
129
- return dispose(super.shift());
130
- }
131
- sort() {
132
- return unsupported('sort');
133
- }
134
- splice(start, deleteCount = super.length, ...values) {
135
- return dispose(super.splice(start, deleteCount, ...factory(values, this.options)));
136
- }
137
- unshift(...values) {
138
- return super.unshift(...factory(values, this.options));
139
- }
140
- }
141
- export { ReactiveArray, ReactiveObjectArray };
145
+ export default (input, options = {}) => {
146
+ return new Proxy(new ReactiveArray(factory(input, options)), handler);
147
+ };
148
+ export { ReactiveArray };
@@ -1,18 +1,13 @@
1
- import { Computed, Object, Options, Prettify, Signal } from '../types';
2
- import { ReactiveArray, ReactiveObjectArray } from './array';
1
+ import { Prettify } from '@esportsplus/typescript';
2
+ import { Options } from '../types';
3
+ import { ReactiveArray } from './array';
3
4
  import { ReactiveObject } from './object';
4
- type Guard<T> = T extends Object ? {
5
+ type Guard<T> = T extends Record<PropertyKey, unknown> ? {
5
6
  [K in keyof T]: Never<K, Guard<T[K]>>;
6
- } : T extends unknown[] ? Guard<T[number]>[] : T;
7
- type Infer<T> = T extends (...args: unknown[]) => unknown ? ReturnType<T> : T extends unknown[] ? T extends Object[] ? ReactiveObjectArray<T[0]> : ReactiveArray<T[0]> : T extends Object ? {
7
+ } : T extends unknown[] ? T : never;
8
+ type Infer<T> = T extends (...args: unknown[]) => unknown ? ReturnType<T> : T extends (infer U)[] ? Prettify<Omit<U[], 'map'> & Pick<ReactiveArray<U>, 'dispatch' | 'dispose' | 'map' | 'on' | 'once'>> : T extends Record<PropertyKey, unknown> ? {
8
9
  [K in keyof T]: T[K];
9
10
  } : T;
10
- type Never<K, V> = K extends keyof ReactiveObject<Object> ? never : V;
11
- type Nodes<T> = T extends (...args: unknown[]) => unknown ? Computed<T> : T extends unknown[] ? T extends Object[] ? ReactiveObjectArray<T[0]> : ReactiveArray<T[0]> : Signal<T>;
12
- type Signals<T extends Object> = {
13
- [K in keyof T]: Nodes<T[K]>;
14
- };
15
- declare const _default: <T extends Object>(data: Guard<T>, options?: Options) => Prettify<{ [K in keyof T]: Infer<T[K]>; } & Omit<ReactiveObject<T>, "signals"> & {
16
- signals: Signals<T>;
17
- }>;
11
+ type Never<K, V> = K extends keyof ReactiveObject<Record<PropertyKey, unknown>> ? never : V;
12
+ declare const _default: <T>(data: Guard<T>, options?: Options) => T extends Record<PropertyKey, unknown> ? { [K in keyof T]: Infer<T[K]>; } : Infer<T>;
18
13
  export default _default;
@@ -1,4 +1,16 @@
1
- import { ReactiveObject } from './object';
2
- export default (data, options) => {
3
- return new ReactiveObject(data, options);
1
+ import { default as array } from './array';
2
+ import { default as object } from './object';
3
+ import { isArray, isObject } from '../utilities';
4
+ export default (data, options = {}) => {
5
+ let value;
6
+ if (isArray(data)) {
7
+ value = array(data, options);
8
+ }
9
+ else if (isObject(data)) {
10
+ value = object(data, options);
11
+ }
12
+ else {
13
+ throw new Error('Reactivity: received invalid input for `reactive()`', data);
14
+ }
15
+ return value;
4
16
  };
@@ -1,9 +1,11 @@
1
- import { Computed, Object, Options, Signal } from '../types';
2
- import { ReactiveArray, ReactiveObjectArray } from './array';
3
- type Node = Computed<any> | ReactiveArray<any> | ReactiveObjectArray<Object> | Signal<any>;
4
- declare class ReactiveObject<T extends Object> {
5
- signals: Record<PropertyKey, Node>;
1
+ import { Computed, Options, Signal } from '../types';
2
+ import { ReactiveArray } from './array';
3
+ declare class ReactiveObject<T extends Record<PropertyKey, unknown>> {
4
+ signals: Record<PropertyKey, Computed<any> | ReactiveArray<any> | Signal<any>>;
6
5
  constructor(data: T, options?: Options);
6
+ get value(): this;
7
7
  dispose(): void;
8
8
  }
9
+ declare const _default: <T extends Record<PropertyKey, unknown>>(input: T, options?: Options) => ReactiveObject<T>;
10
+ export default _default;
9
11
  export { ReactiveObject };
@@ -1,34 +1,27 @@
1
1
  import { computed, signal } from '../signal';
2
- import { defineProperty, isArray } from '../utilities';
3
- import { ReactiveArray, ReactiveObjectArray } from './array';
2
+ import { defineProperty, isArray, isFunction } from '../utilities';
3
+ import { default as array } from './array';
4
4
  class ReactiveObject {
5
5
  signals = {};
6
6
  constructor(data, options = {}) {
7
7
  let signals = this.signals;
8
8
  for (let key in data) {
9
9
  let input = data[key];
10
- if (typeof input === 'function') {
11
- let s = signals[key] = computed(input, options);
10
+ if (isArray(input)) {
11
+ let s = signals[key] = array(input, options);
12
12
  defineProperty(this, key, {
13
13
  enumerable: true,
14
14
  get() {
15
- return s.get();
15
+ return s;
16
16
  }
17
17
  });
18
18
  }
19
- else if (isArray(input)) {
20
- let s, test = input[0];
21
- if (typeof test === 'object' && test !== null && test?.constructor?.name === 'Object') {
22
- s = signals[key] = new ReactiveObjectArray(input, options);
23
- }
24
- else {
25
- s = signals[key] = new ReactiveArray(input);
26
- }
19
+ else if (isFunction(input)) {
20
+ let s = signals[key] = computed(input, options);
27
21
  defineProperty(this, key, {
28
22
  enumerable: true,
29
23
  get() {
30
- s.track();
31
- return s;
24
+ return s.get();
32
25
  }
33
26
  });
34
27
  }
@@ -46,6 +39,9 @@ class ReactiveObject {
46
39
  }
47
40
  }
48
41
  }
42
+ get value() {
43
+ return this;
44
+ }
49
45
  dispose() {
50
46
  let signals = this.signals;
51
47
  for (let key in signals) {
@@ -53,4 +49,7 @@ class ReactiveObject {
53
49
  }
54
50
  }
55
51
  }
52
+ export default (input, options = {}) => {
53
+ return new ReactiveObject(input, options);
54
+ };
56
55
  export { ReactiveObject };
@@ -1,16 +1,15 @@
1
1
  import CustomFunction from '@esportsplus/custom-function';
2
2
  import { Options } from './types';
3
- type Function<A extends unknown[], R extends Promise<unknown>> = (...args: A) => R;
4
3
  declare class Resource<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
5
4
  private arguments;
6
5
  private okay;
7
6
  private response;
8
7
  stop: boolean | null;
9
- constructor(fn: Function<A, R>, options?: Options);
8
+ constructor(fn: (...args: A) => R, options?: Options);
10
9
  get data(): Awaited<R>;
11
10
  get input(): A | null;
12
11
  get ok(): boolean | null;
13
12
  dispose(): void;
14
13
  }
15
- declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: Function<A, R>, options?: Options) => Resource<A, R>;
14
+ declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R, options?: Options) => Resource<A, R>;
16
15
  export default _default;
package/build/types.d.ts CHANGED
@@ -21,7 +21,6 @@ type Listener<D> = {
21
21
  value: V;
22
22
  }): void;
23
23
  };
24
- type Object = Record<PropertyKey, unknown>;
25
24
  type Options = {
26
25
  changed?: Changed;
27
26
  };
@@ -38,4 +37,4 @@ type Signal<T> = {
38
37
  } & Base<T>;
39
38
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
40
39
  type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;
41
- export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Scheduler, Signal, State, Type };
40
+ export type { Changed, Computed, Effect, Event, Function, Listener, NeverAsync, Options, Prettify, Root, Scheduler, Signal, State, Type };
@@ -1,3 +1,8 @@
1
- declare const isArray: (arg: any) => arg is any[];
2
1
  declare const defineProperty: <T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>) => T;
3
- export { defineProperty, isArray };
2
+ declare const isArray: (arg: any) => arg is any[];
3
+ declare const isFunction: (value: unknown) => value is Function;
4
+ declare const isInstanceOf: <T>(instance: unknown, match: new (...args: any) => T) => instance is T;
5
+ declare const isNumber: (value: any) => value is number;
6
+ declare const isObject: (value: unknown) => value is Record<PropertyKey, unknown>;
7
+ declare const isString: (value: unknown) => value is string;
8
+ export { defineProperty, isArray, isFunction, isInstanceOf, isNumber, isObject, isString };
@@ -1,3 +1,18 @@
1
- const { isArray } = Array;
2
1
  const { defineProperty } = Object;
3
- export { defineProperty, isArray };
2
+ const { isArray } = Array;
3
+ const isFunction = (value) => {
4
+ return typeof value === 'function';
5
+ };
6
+ const isInstanceOf = (instance, match) => {
7
+ return typeof instance === 'object' && instance !== null && instance.constructor === match;
8
+ };
9
+ const isNumber = (value) => {
10
+ return !isNaN(value);
11
+ };
12
+ const isObject = (value) => {
13
+ return typeof value === 'object' && value !== null && value.constructor === Object;
14
+ };
15
+ const isString = (value) => {
16
+ return typeof value === 'string';
17
+ };
18
+ export { defineProperty, isArray, isFunction, isInstanceOf, isNumber, isObject, isString };
package/package.json CHANGED
@@ -16,5 +16,5 @@
16
16
  "prepublishOnly": "npm run build"
17
17
  },
18
18
  "types": "build/index.d.ts",
19
- "version": "0.1.30"
19
+ "version": "0.2.0"
20
20
  }
package/src/macro.ts CHANGED
@@ -3,14 +3,11 @@ import { computed } from './signal';
3
3
  import { Computed, Options } from './types';
4
4
 
5
5
 
6
- type Function<A extends unknown[], R> = Computed<(...args: A) => R>['fn'];
7
-
8
-
9
6
  class Macro<A extends unknown[], R> extends CustomFunction {
10
7
  private factory: Computed<(...args: A) => R>;
11
8
 
12
9
 
13
- constructor(fn: Function<A,R>, options: Options = {}) {
10
+ constructor(fn: Macro<A,R>['factory']['fn'], options: Options = {}) {
14
11
  super((...args: A) => {
15
12
  return this.factory.get()(...args);
16
13
  });
@@ -24,6 +21,6 @@ class Macro<A extends unknown[], R> extends CustomFunction {
24
21
  }
25
22
 
26
23
 
27
- export default <A extends unknown[], R>(fn: Function<A,R>, options: Options = {}) => {
24
+ export default <A extends unknown[], R>(fn: Macro<A,R>['factory']['fn'], options: Options = {}) => {
28
25
  return new Macro(fn, options);
29
26
  };
@@ -1,41 +1,112 @@
1
- import { dispose, signal } from '~/signal';
2
- import { Listener, Object, Options, Signal } from '~/types';
1
+ import { dispose, signal, Reactive } from '~/signal';
2
+ import { Listener, Options, Signal } from '~/types';
3
+ import { isInstanceOf, isNumber, isObject } from '~/utilities';
3
4
  import { ReactiveObject } from './object';
4
5
 
5
6
 
6
7
  type Events<T> = {
7
- fill: { value: T };
8
- pop: { item: T };
9
- push: { items: T[] };
8
+ pop: {
9
+ item: R<T>;
10
+ };
11
+ push: {
12
+ items: R<T>[];
13
+ };
10
14
  reverse: undefined;
11
- shift: { item: T };
15
+ shift: {
16
+ item: R<T>;
17
+ };
12
18
  sort: undefined;
13
- splice: { deleteCount: number, items: T[], start: number };
14
- unshift: { items: T[] };
19
+ splice: {
20
+ deleteCount: number;
21
+ items: R<T>[];
22
+ start: number;
23
+ };
24
+ unshift: {
25
+ items: R<T>[];
26
+ };
15
27
  };
16
28
 
29
+ type R<T> = Signal<T> | ReactiveObject< T extends Record<PropertyKey, unknown> ? { [K in keyof T]: T[K] } : never >;
17
30
 
18
- function factory<T extends Object>(data: T[], options: Options = {}) {
19
- let signals = [];
20
31
 
21
- for (let i = 0, n = data.length; i < n; i++) {
22
- signals.push( new ReactiveObject(data[i], options) );
23
- }
32
+ let handler = {
33
+ get(target: any, prop: any) {
34
+ let value = target[prop];
24
35
 
25
- return signals;
26
- }
36
+ if (value === undefined) {
37
+ return value;
38
+ }
39
+
40
+ if (isInstanceOf(value, Reactive)) {
41
+ return value.get();
42
+ }
43
+ else if (supported.has(prop)) {
44
+ return value;
45
+ }
46
+
47
+ throw new Error(`Reactivity: '${prop}' is not supported on reactive arrays`);
48
+ },
49
+ set(target: any, prop: any, value: any) {
50
+ if (isNumber(prop)) {
51
+ let host = target[prop];
52
+
53
+ if (isInstanceOf(host, Reactive)) {
54
+ host.set(value);
55
+ return true;
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ return target[prop] = value;
62
+ }
63
+ },
64
+ supported = new Set([
65
+ 'at',
66
+ 'dispatch', 'dispose',
67
+ 'length',
68
+ 'map',
69
+ 'on', 'once',
70
+ 'pop', 'push',
71
+ 'reverse',
72
+ 'self', 'shift', 'sort', 'splice',
73
+ 'unshift'
74
+ ]);
75
+
76
+
77
+ function factory<T>(input: T[], options: Options = {}) {
78
+ let items: R<T>[] = [];
79
+
80
+ for (let i = 0, n = input.length; i < n; i++) {
81
+ let value = input[i];
82
+
83
+ if (isObject(value)) {
84
+ items[i] = new ReactiveObject(value, options);
85
+ }
86
+ else {
87
+ items[i] = signal(value);
88
+ }
89
+ }
27
90
 
28
- function unsupported(method: string): never {
29
- throw new Error(`Reactivity: '${method}' is not supported on reactive object array`);
91
+ return items;
30
92
  }
31
93
 
32
94
 
33
- class ReactiveArray<T> extends Array<T> {
95
+ // REMINDER:
96
+ // - @ts-ignore flags are supressing type mismatch error
97
+ // - Input values are being transformed by this class into reactive values and back during get
98
+ class ReactiveArray<T> extends Array<R<T>> {
99
+ private options: Options;
100
+ // - Proxy binds itself to methods on get
101
+ // - Use 'self' to avoid going through proxy for internal loops
102
+ private self: ReactiveArray<T>;
34
103
  private signal: Signal<boolean>;
35
104
 
36
105
 
37
- constructor(data: T[]) {
106
+ constructor(data: R<T>[], options: Options = {}) {
38
107
  super(...data);
108
+ this.options = options;
109
+ this.self = this;
39
110
  this.signal = signal(false);
40
111
  }
41
112
 
@@ -49,28 +120,33 @@ class ReactiveArray<T> extends Array<T> {
49
120
  }
50
121
 
51
122
 
123
+ at(index: number) {
124
+ let value = super.at(index);
125
+
126
+ if (isInstanceOf(value, Reactive)) {
127
+ return value.get();
128
+ }
129
+
130
+ return value;
131
+ }
132
+
52
133
  dispatch<E extends keyof Events<T>>(event: E, data?: Events<T>[E]) {
53
134
  this.signal.dispatch(event, data);
54
135
  }
55
136
 
56
137
  dispose() {
57
138
  this.signal.dispose();
139
+ dispose(this.self as any);
58
140
  }
59
141
 
60
- fill(value: T, start?: number, end?: number) {
61
- super.fill(value, start, end);
62
-
63
- this.dispatch('fill', { value });
64
- this.trigger();
65
-
66
- return this;
67
- }
68
-
69
- map<U>(fn: (this: ReactiveArray<T>, value: T, i: number, values: this) => U) {
70
- let values: U[] = [];
142
+ // @ts-ignore
143
+ map<U>(fn: (this: T[], value: T, i: number) => U) {
144
+ let self = this.self,
145
+ values: U[] = [];
71
146
 
72
- for (let i = 0, n = this.length; i < n; i++) {
73
- values.push( fn.call(this, this[i], i, this) );
147
+ for (let i = 0, n = self.length; i < n; i++) {
148
+ // @ts-ignore
149
+ values.push( fn.call(this, self[i].value, i) );
74
150
  }
75
151
 
76
152
  return values;
@@ -88,27 +164,27 @@ class ReactiveArray<T> extends Array<T> {
88
164
  let item = super.pop();
89
165
 
90
166
  if (item !== undefined) {
91
- this.dispatch('pop', { item });
92
- this.trigger();
167
+ dispose(item);
168
+ this.signal.dispatch('pop', { item });
93
169
  }
94
170
 
95
171
  return item;
96
172
  }
97
173
 
98
- push(...items: T[]) {
99
- let n = super.push(...items);
174
+ // @ts-ignore
175
+ push(...input: T[]) {
176
+ let items = factory(input, this.options),
177
+ n = super.push(...items);
100
178
 
101
- this.dispatch('push', { items });
102
- this.trigger();
179
+ this.signal.dispatch('push', { items });
103
180
 
104
181
  return n;
105
182
  }
106
183
 
184
+ // @ts-ignore
107
185
  reverse() {
108
186
  super.reverse();
109
-
110
- this.dispatch('reverse');
111
- this.trigger();
187
+ this.signal.dispatch('reverse');
112
188
 
113
189
  return this;
114
190
  }
@@ -117,8 +193,8 @@ class ReactiveArray<T> extends Array<T> {
117
193
  let item = super.shift();
118
194
 
119
195
  if (item !== undefined) {
120
- this.dispatch('shift', { item });
121
- this.trigger();
196
+ dispose(item);
197
+ this.signal.dispatch('shift', { item });
122
198
  }
123
199
 
124
200
  return item;
@@ -126,96 +202,41 @@ class ReactiveArray<T> extends Array<T> {
126
202
 
127
203
  sort() {
128
204
  super.sort();
129
-
130
- this.dispatch('sort');
131
- this.trigger();
205
+ this.signal.dispatch('sort');
132
206
 
133
207
  return this;
134
208
  }
135
209
 
136
- splice(start: number, deleteCount: number = super.length, ...items: T[]) {
137
- let removed = super.splice(start, deleteCount, ...items);
210
+ // @ts-ignore
211
+ splice(start: number, deleteCount: number = super.length, ...input: T[]) {
212
+ let items = factory(input, this.options),
213
+ removed = super.splice(start, deleteCount, ...items);
138
214
 
139
215
  if (items.length > 0 || removed.length > 0) {
140
- this.dispatch('splice', {
216
+ dispose(removed);
217
+ this.signal.dispatch('splice', {
141
218
  deleteCount,
142
219
  items,
143
220
  start
144
221
  });
145
- this.trigger();
146
222
  }
147
223
 
148
224
  return removed;
149
225
  }
150
226
 
151
- track(index?: number) {
152
- this.signal.get();
153
- return index === undefined ? undefined : this[index];
154
- }
155
-
156
- trigger() {
157
- this.signal.set(!this.signal.value);
158
- }
159
-
160
- unshift(...items: T[]) {
161
- let length = super.unshift(...items);
162
-
163
- this.dispatch('unshift', { items });
164
- this.trigger();
165
-
166
- return length;
167
- }
168
- }
169
-
170
-
171
- // REMINDER:
172
- // - @ts-ignore flags are supressing a type mismatch error
173
- // - Input values are being transformed by this class into signals
174
- class ReactiveObjectArray<T extends Object> extends ReactiveArray<ReactiveObject<T>> {
175
- private options: Options;
176
-
177
-
178
- constructor(data: T[], options: Options = {}) {
179
- super( factory(data, options) );
180
- this.options = options;
181
- }
182
-
183
-
184
- fill() {
185
- return unsupported('fill');
186
- }
187
-
188
- reverse() {
189
- return unsupported('reverse');
190
- }
191
-
192
- pop() {
193
- return dispose(super.pop()) as ReactiveObject<T>| undefined;
194
- }
195
-
196
227
  // @ts-ignore
197
- push(...values: T[]) {
198
- return super.push(...factory(values, this.options));
199
- }
228
+ unshift(...input: T[]) {
229
+ let items = factory(input, this.options),
230
+ length = super.unshift(...items);
200
231
 
201
- shift() {
202
- return dispose(super.shift()) as ReactiveObject<T> | undefined;
203
- }
232
+ this.signal.dispatch('unshift', { items });
204
233
 
205
- sort() {
206
- return unsupported('sort');
207
- }
208
-
209
- // @ts-ignore
210
- splice(start: number, deleteCount: number = super.length, ...values: T[]) {
211
- return dispose( super.splice(start, deleteCount, ...factory(values, this.options)) );
212
- }
213
-
214
- // @ts-ignore
215
- unshift(...values: T[]) {
216
- return super.unshift(...factory(values, this.options));
234
+ return length;
217
235
  }
218
236
  }
219
237
 
220
238
 
221
- export { ReactiveArray, ReactiveObjectArray };
239
+ export default <T>(input: T[], options: Options = {}) => {
240
+ return new Proxy(new ReactiveArray(factory(input, options)), handler);
241
+ };
242
+ export { ReactiveArray };
@@ -1,40 +1,41 @@
1
- import { Computed, Object, Options, Prettify, Signal } from '~/types';
2
- import { ReactiveArray, ReactiveObjectArray } from './array';
3
- import { ReactiveObject } from './object';
1
+ import { Prettify } from '@esportsplus/typescript';
2
+ import { Options } from '~/types';
3
+ import { default as array, ReactiveArray } from './array';
4
+ import { default as object, ReactiveObject } from './object';
5
+ import { isArray, isObject } from '~/utilities';
4
6
 
5
7
 
6
8
  type Guard<T> =
7
- T extends Object
9
+ T extends Record<PropertyKey, unknown>
8
10
  ? { [K in keyof T]: Never<K, Guard<T[K]>> }
9
11
  : T extends unknown[]
10
- ? Guard<T[number]>[]
11
- : T;
12
+ ? T
13
+ : never;
12
14
 
13
15
  type Infer<T> =
14
16
  T extends (...args: unknown[]) => unknown
15
17
  ? ReturnType<T>
16
- : T extends unknown[]
17
- ? T extends Object[] ? ReactiveObjectArray<T[0]> : ReactiveArray<T[0]>
18
- : T extends Object
18
+ : T extends (infer U)[]
19
+ ? Prettify< Omit<U[], 'map'> & Pick<ReactiveArray<U>, 'dispatch' | 'dispose' | 'map' | 'on' | 'once'> >
20
+ : T extends Record<PropertyKey, unknown>
19
21
  ? { [K in keyof T]: T[K] }
20
22
  : T;
21
23
 
22
- type Never<K,V> = K extends keyof ReactiveObject<Object> ? never : V;
24
+ type Never<K,V> = K extends keyof ReactiveObject<Record<PropertyKey, unknown>> ? never : V;
23
25
 
24
- type Nodes<T> =
25
- T extends (...args: unknown[]) => unknown
26
- ? Computed<T>
27
- : T extends unknown[]
28
- ? T extends Object[] ? ReactiveObjectArray<T[0]> : ReactiveArray<T[0]>
29
- : Signal<T>;
30
26
 
31
- type Signals<T extends Object> = {
32
- [K in keyof T]: Nodes<T[K]>
33
- };
27
+ export default <T>(data: Guard<T>, options: Options = {}) => {
28
+ let value;
34
29
 
30
+ if (isArray(data)) {
31
+ value = array(data, options);
32
+ }
33
+ else if (isObject(data)) {
34
+ value = object(data as { [K in keyof T]: T[K] }, options);
35
+ }
36
+ else {
37
+ throw new Error('Reactivity: received invalid input for `reactive()`', data);
38
+ }
35
39
 
36
- export default <T extends Object>(data: Guard<T>, options?: Options) => {
37
- return new ReactiveObject(data, options) as any as Prettify<
38
- { [K in keyof T]: Infer<T[K]> } & Omit<ReactiveObject<T>, 'signals'> & { signals: Signals<T> }
39
- >;
40
+ return value as T extends Record<PropertyKey, unknown> ? { [K in keyof T]: Infer<T[K]> } : Infer<T>;
40
41
  };
@@ -1,14 +1,11 @@
1
1
  import { computed, signal } from '~/signal';
2
- import { Computed, Object, Options, Signal } from '~/types';
3
- import { defineProperty, isArray } from '~/utilities';
4
- import { ReactiveArray, ReactiveObjectArray } from './array';
2
+ import { Computed, Options, Signal } from '~/types';
3
+ import { defineProperty, isArray, isFunction } from '~/utilities';
4
+ import { default as array, ReactiveArray } from './array';
5
5
 
6
6
 
7
- type Node = Computed<any> | ReactiveArray<any> | ReactiveObjectArray<Object> | Signal<any>;
8
-
9
-
10
- class ReactiveObject<T extends Object> {
11
- signals: Record<PropertyKey, Node> = {};
7
+ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
8
+ signals: Record<PropertyKey, Computed<any> | ReactiveArray<any> | Signal<any>> = {};
12
9
 
13
10
 
14
11
  constructor(data: T, options: Options = {}) {
@@ -17,33 +14,23 @@ class ReactiveObject<T extends Object> {
17
14
  for (let key in data) {
18
15
  let input = data[key];
19
16
 
20
- if (typeof input === 'function') {
21
- let s = signals[key] = computed(input as Computed<T>['fn'], options);
17
+ if (isArray(input)) {
18
+ let s = signals[key] = array(input, options);
22
19
 
23
20
  defineProperty(this, key, {
24
21
  enumerable: true,
25
22
  get() {
26
- return s.get();
23
+ return s;
27
24
  }
28
25
  });
29
26
  }
30
- else if (isArray(input)) {
31
- let s: ReactiveArray<unknown> | ReactiveObjectArray<Object>,
32
- test = input[0];
33
-
34
- if (typeof test === 'object' && test !== null && test?.constructor?.name === 'Object') {
35
- s = signals[key] = new ReactiveObjectArray(input, options);
36
- }
37
- else {
38
- s = signals[key] = new ReactiveArray(input);
39
- }
27
+ else if (isFunction(input)) {
28
+ let s = signals[key] = computed(input as Computed<T>['fn'], options);
40
29
 
41
30
  defineProperty(this, key, {
42
31
  enumerable: true,
43
32
  get() {
44
- s.track();
45
-
46
- return s;
33
+ return s.get();
47
34
  }
48
35
  });
49
36
  }
@@ -64,6 +51,11 @@ class ReactiveObject<T extends Object> {
64
51
  }
65
52
 
66
53
 
54
+ get value() {
55
+ return this;
56
+ }
57
+
58
+
67
59
  dispose() {
68
60
  let signals = this.signals;
69
61
 
@@ -74,4 +66,7 @@ class ReactiveObject<T extends Object> {
74
66
  }
75
67
 
76
68
 
69
+ export default <T extends Record<PropertyKey, unknown>>(input: T, options: Options = {}) => {
70
+ return new ReactiveObject(input, options);
71
+ };
77
72
  export { ReactiveObject };
package/src/resource.ts CHANGED
@@ -3,9 +3,6 @@ import { signal } from './signal';
3
3
  import { Options, Signal } from './types';
4
4
 
5
5
 
6
- type Function<A extends unknown[], R extends Promise<unknown>> = (...args: A) => R;
7
-
8
-
9
6
  class Resource<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
10
7
  private arguments: Signal<A | null>;
11
8
  private okay: Signal<boolean | null>;
@@ -14,7 +11,7 @@ class Resource<A extends unknown[], R extends Promise<unknown>> extends CustomFu
14
11
  stop: boolean | null = null;
15
12
 
16
13
 
17
- constructor(fn: Function<A,R>, options: Options = {}) {
14
+ constructor(fn: (...args: A) => R, options: Options = {}) {
18
15
  super((...args: A) => {
19
16
  this.stop = null;
20
17
 
@@ -66,6 +63,6 @@ class Resource<A extends unknown[], R extends Promise<unknown>> extends CustomFu
66
63
  }
67
64
 
68
65
 
69
- export default <A extends unknown[], R extends Promise<unknown>>(fn: Function<A,R>, options: Options = {}) => {
66
+ export default <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R, options: Options = {}) => {
70
67
  return new Resource(fn, options);
71
68
  };
package/src/types.ts CHANGED
@@ -27,8 +27,6 @@ type Listener<D> = {
27
27
  <V>(event: { data?: D, value: V }): void;
28
28
  };
29
29
 
30
- type Object = Record<PropertyKey, unknown>;
31
-
32
30
  type Options = {
33
31
  changed?: Changed;
34
32
  };
@@ -52,4 +50,15 @@ type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
52
50
  type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;
53
51
 
54
52
 
55
- export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Scheduler, Signal, State, Type };
53
+ export type {
54
+ Changed, Computed,
55
+ Effect, Event,
56
+ Function,
57
+ Listener,
58
+ NeverAsync,
59
+ Options,
60
+ Prettify,
61
+ Root,
62
+ Scheduler, Signal, State,
63
+ Type
64
+ };
package/src/utilities.ts CHANGED
@@ -1,6 +1,26 @@
1
+ const { defineProperty } = Object;
2
+
1
3
  const { isArray } = Array;
2
4
 
3
- const { defineProperty } = Object;
5
+ const isFunction = (value: unknown): value is Function => {
6
+ return typeof value === 'function';
7
+ };
8
+
9
+ const isInstanceOf = <T>(instance: unknown, match: new (...args: any) => T): instance is T => {
10
+ return typeof instance === 'object' && instance !== null && instance.constructor === match;
11
+ };
12
+
13
+ const isNumber = (value: any): value is number => {
14
+ return !isNaN(value);
15
+ };
16
+
17
+ const isObject = (value: unknown): value is Record<PropertyKey, unknown> => {
18
+ return typeof value === 'object' && value !== null && value.constructor === Object;
19
+ };
20
+
21
+ const isString = (value: unknown): value is string => {
22
+ return typeof value === 'string';
23
+ };
4
24
 
5
25
 
6
- export { defineProperty, isArray };
26
+ export { defineProperty, isArray, isFunction, isInstanceOf, isNumber, isObject, isString };