@esportsplus/reactivity 0.0.29 → 0.1.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 (63) hide show
  1. package/build/bench.d.ts +1 -0
  2. package/build/bench.js +46 -0
  3. package/build/constants.d.ts +8 -0
  4. package/build/constants.js +8 -0
  5. package/build/context/node.d.ts +1 -1
  6. package/build/context/node.js +1 -1
  7. package/build/context/nodes.d.ts +1 -1
  8. package/build/context/nodes.js +1 -1
  9. package/build/effect.d.ts +2 -6
  10. package/build/effect.js +1 -2
  11. package/build/index.d.ts +2 -4
  12. package/build/index.js +2 -4
  13. package/build/macro.d.ts +10 -7
  14. package/build/macro.js +17 -5
  15. package/build/promise.d.ts +25 -5
  16. package/build/promise.js +37 -30
  17. package/build/reactive/array.d.ts +60 -0
  18. package/build/reactive/array.js +136 -0
  19. package/build/reactive/index.d.ts +11 -0
  20. package/build/reactive/index.js +4 -0
  21. package/build/reactive/object.d.ts +14 -0
  22. package/build/reactive/object.js +60 -0
  23. package/build/reactive/object2.d.ts +10 -0
  24. package/build/reactive/object2.js +65 -0
  25. package/build/reactive/types.d.ts +33 -0
  26. package/build/reactive/types.js +1 -0
  27. package/build/reactive-array.d.ts +1 -0
  28. package/build/reactive-array.js +106 -0
  29. package/build/reactive.js +2 -2
  30. package/build/resource.d.ts +16 -0
  31. package/build/resource.js +55 -0
  32. package/build/signal.d.ts +17 -12
  33. package/build/signal.js +71 -37
  34. package/build/testing/node.d.ts +13 -0
  35. package/build/testing/node.js +21 -0
  36. package/build/testing/nodes.d.ts +13 -0
  37. package/build/testing/nodes.js +33 -0
  38. package/build/testing/reactive.d.ts +0 -0
  39. package/build/testing/reactive.js +1 -0
  40. package/build/trigger.d.ts +15 -0
  41. package/build/trigger.js +30 -0
  42. package/build/types.d.ts +15 -17
  43. package/build/utilities.d.ts +3 -0
  44. package/build/utilities.js +3 -0
  45. package/package.json +5 -2
  46. package/src/constants.ts +17 -0
  47. package/src/index.ts +3 -5
  48. package/src/macro.ts +29 -9
  49. package/src/reactive/array.ts +219 -0
  50. package/src/reactive/index.ts +26 -0
  51. package/src/reactive/object.ts +93 -0
  52. package/src/resource.ts +79 -0
  53. package/src/signal.ts +91 -52
  54. package/src/types.ts +13 -19
  55. package/src/utilities.ts +6 -0
  56. package/tsconfig.json +1 -1
  57. package/src/context/index.ts +0 -5
  58. package/src/context/node.ts +0 -36
  59. package/src/context/nodes.ts +0 -52
  60. package/src/effect.ts +0 -5
  61. package/src/promise.ts +0 -48
  62. package/src/reactive.ts +0 -47
  63. package/src/symbols.ts +0 -29
package/build/types.d.ts CHANGED
@@ -1,36 +1,34 @@
1
- import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, SIGNAL } from './symbols';
1
+ import { Prettify } from '@esportsplus/typescript';
2
+ import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, SIGNAL } from './constants';
2
3
  import Signal from './signal';
3
4
  type Changed = (a: unknown, b: unknown) => boolean;
4
5
  type Computed<T> = {
5
- fn: T extends Promise<unknown> ? never : ((this: Context, previous: T) => T);
6
+ fn: T extends Promise<unknown> ? never : ((previous: T) => T);
6
7
  value: ReturnType<Computed<T>['fn']>;
7
8
  } & Omit<Signal<T>, 'fn' | 'value'>;
8
- type Context = {
9
- dispose(): void;
10
- on(event: Event, listener: Listener): void;
11
- once(event: Event, listener: Listener): void;
12
- reset(): void;
13
- };
14
9
  type Effect<T> = {
15
- fn: (this: Context, previous: T) => T;
10
+ fn: (node: Effect<T>) => void;
16
11
  root: NonNullable<Signal<T>['root']>;
17
12
  task: NonNullable<Signal<T>['task']>;
18
- } & Omit<Computed<T>, 'fn' | 'root' | 'task'>;
19
- type Event = symbol;
20
- type Listener = {
13
+ value: void;
14
+ } & Omit<Signal<T>, 'fn' | 'root' | 'task' | 'value'>;
15
+ type Event = string;
16
+ type Listener<D> = {
21
17
  once?: boolean;
22
- <T>(value: T): void;
18
+ <V>(event: {
19
+ data?: D;
20
+ value: V;
21
+ }): void;
23
22
  };
23
+ type Object = Record<PropertyKey, unknown>;
24
24
  type Options = {
25
25
  changed?: Changed;
26
+ value?: unknown;
26
27
  };
27
28
  type Root = {
28
29
  scheduler: Scheduler;
29
30
  };
30
- type Prettify<T> = {
31
- [K in keyof T]: T[K];
32
- } & {};
33
31
  type Scheduler = (fn: (...args: unknown[]) => Promise<unknown> | unknown) => unknown;
34
32
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
35
33
  type Type = typeof COMPUTED | typeof EFFECT | typeof SIGNAL;
36
- export { Changed, Computed, Context, Effect, Event, Listener, Options, Prettify, Root, Scheduler, Signal, State, Type };
34
+ export { Changed, Computed, Effect, Event, Listener, Object, Options, Prettify, Root, Scheduler, Signal, State, Type };
@@ -0,0 +1,3 @@
1
+ declare const isArray: (arg: any) => arg is any[];
2
+ declare const defineProperty: <T>(o: T, p: PropertyKey, attributes: PropertyDescriptor & ThisType<any>) => T;
3
+ export { defineProperty, isArray };
@@ -0,0 +1,3 @@
1
+ const { isArray } = Array;
2
+ const { defineProperty } = Object;
3
+ export { defineProperty, isArray };
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "author": "ICJR",
3
+ "dependencies": {
4
+ "@esportsplus/custom-function": "^0.0.1"
5
+ },
3
6
  "devDependencies": {
4
- "@esportsplus/rspack": "^0.0.15"
7
+ "@esportsplus/typescript": "^0.0.3"
5
8
  },
6
9
  "main": "build/index.js",
7
10
  "name": "@esportsplus/reactivity",
@@ -13,5 +16,5 @@
13
16
  "prepublishOnly": "npm run build"
14
17
  },
15
18
  "types": "build/index.d.ts",
16
- "version": "0.0.29"
19
+ "version": "0.1.0"
17
20
  }
@@ -0,0 +1,17 @@
1
+ const CLEAN = 0;
2
+
3
+ const CHECK = 1;
4
+
5
+ const DIRTY = 2;
6
+
7
+ const DISPOSED = 3;
8
+
9
+
10
+ const COMPUTED = 0;
11
+
12
+ const EFFECT = 1;
13
+
14
+ const SIGNAL = 2;
15
+
16
+
17
+ export { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, SIGNAL };
package/src/index.ts CHANGED
@@ -1,7 +1,5 @@
1
- export { default as effect } from './effect';
2
1
  export { default as macro } from './macro';
3
- export { default as promise } from './promise';
2
+ export { default as resource } from './resource';
4
3
  export * as core from './signal';
5
- export { root } from './signal';
6
- export { default as reactive } from './reactive';
7
- export { DISPOSE, RESET, UPDATE } from './symbols';
4
+ export { effect, root } from './signal';
5
+ export { default as reactive } from './reactive';
package/src/macro.ts CHANGED
@@ -1,15 +1,35 @@
1
+ import CustomFunction from '@esportsplus/custom-function';
1
2
  import { computed, read } from './signal';
2
3
  import { Computed } from './types';
3
- import context from './context';
4
4
 
5
5
 
6
- export default <T extends <A, R>(...args: A[]) => R>(fn: Computed<T>['fn'], options: Parameters<typeof computed>[1] = {}) => {
7
- let node = computed(fn, options);
6
+ type Fn<A extends unknown[], R> = Parameters<typeof computed<(...args: A) => R>>[0];
8
7
 
9
- return context.node(
10
- (...args: Parameters<ReturnType<typeof fn>>) => {
11
- return (read(node) as ReturnType<typeof fn>)(...args);
12
- },
13
- node
14
- );
8
+ type Options = Parameters<typeof computed>[1];
9
+
10
+
11
+ class Macro<A extends unknown[], R> extends CustomFunction {
12
+ #factory: Computed< ReturnType<Fn<A,R>> >;
13
+
14
+
15
+ constructor(fn: Fn<A,R>, options: Options = {}) {
16
+ super((...args: A) => {
17
+ return read(this.#factory)(...args);
18
+ });
19
+ this.#factory = computed(fn, options);
20
+ }
21
+
22
+
23
+ dispose() {
24
+ this.#factory.dispose();
25
+ }
26
+
27
+ reset() {
28
+ this.#factory.reset();
29
+ }
30
+ }
31
+
32
+
33
+ export default <A extends unknown[], R>(fn: Fn<A,R>, options: Options = {}) => {
34
+ return new Macro(fn, options);
15
35
  };
@@ -0,0 +1,219 @@
1
+ import { dispose, read, signal, write } from '~/signal';
2
+ import { Listener, Options, Signal } from '~/types';
3
+ import { ReactiveObject } from './object';
4
+
5
+
6
+ type Events<T> = {
7
+ fill: { value: T };
8
+ pop: { item: T };
9
+ push: { items: T[] };
10
+ reverse: undefined;
11
+ shift: { item: T };
12
+ sort: undefined;
13
+ splice: { deleteCount: number, items: T[], start: number };
14
+ unshift: { items: T[] };
15
+ };
16
+
17
+ type Node<T extends Record<PropertyKey, unknown>> = ReactiveObject<T>;
18
+
19
+
20
+ function factory<T extends Record<PropertyKey, unknown>>(data: T[], options: Options = {}) {
21
+ let signals = [];
22
+
23
+ for (let i = 0, n = data.length; i < n; i++) {
24
+ signals.push( new ReactiveObject(data[i], options) );
25
+ }
26
+
27
+ return signals;
28
+ }
29
+
30
+ function unsupported(method: string): never {
31
+ throw new Error(`Reactivity: '${method}' is not supported on reactive object array`);
32
+ }
33
+
34
+
35
+ // TODO:
36
+ // - patch method ( diff )
37
+ class ReactiveArray<T> extends Array<T> {
38
+ #signal: Signal<boolean>;
39
+
40
+
41
+ constructor(data: T[]) {
42
+ super(...data);
43
+ this.#signal = signal(false);
44
+ }
45
+
46
+
47
+ set length(n: number) {
48
+ if (n > this.length) {
49
+ return;
50
+ }
51
+
52
+ this.splice(n);
53
+ }
54
+
55
+
56
+ private trigger() {
57
+ write(this.#signal, !this.#signal.value);
58
+ }
59
+
60
+
61
+ dispatch<E extends keyof Events<T>>(event: E, data?: Events<T>[E]) {
62
+ this.#signal.dispatch(event, data);
63
+ }
64
+
65
+ dispose() {
66
+ this.#signal.dispose();
67
+ }
68
+
69
+ fill(value: T, start?: number, end?: number) {
70
+ super.fill(value, start, end);
71
+
72
+ this.dispatch('fill', { value });
73
+ this.trigger();
74
+
75
+ return this;
76
+ }
77
+
78
+ on<E extends keyof Events<T>>(event: E, listener: Listener<Events<T>[E]>) {
79
+ this.#signal.on(event, listener);
80
+ }
81
+
82
+ once<E extends keyof Events<T>>(event: E, listener: Listener<Events<T>[E]>) {
83
+ this.#signal.once(event, listener);
84
+ }
85
+
86
+ pop() {
87
+ let item = super.pop();
88
+
89
+ if (item !== undefined) {
90
+ this.dispatch('pop', { item });
91
+ this.trigger();
92
+ }
93
+
94
+ return item;
95
+ }
96
+
97
+ push(...items: T[]) {
98
+ let n = super.push(...items);
99
+
100
+ this.dispatch('push', { items });
101
+ this.trigger();
102
+
103
+ return n;
104
+ }
105
+
106
+ reset() {
107
+ this.#signal.reset();
108
+ }
109
+
110
+ reverse() {
111
+ super.reverse();
112
+
113
+ this.dispatch('reverse');
114
+ this.trigger();
115
+
116
+ return this;
117
+ }
118
+
119
+ shift() {
120
+ let item = super.shift();
121
+
122
+ if (item !== undefined) {
123
+ this.dispatch('shift', { item });
124
+ this.trigger();
125
+ }
126
+
127
+ return item;
128
+ }
129
+
130
+ sort() {
131
+ super.sort();
132
+
133
+ this.dispatch('sort');
134
+ this.trigger();
135
+
136
+ return this;
137
+ }
138
+
139
+ splice(start: number, deleteCount: number = super.length, ...items: T[]) {
140
+ let removed = super.splice(start, deleteCount, ...items);
141
+
142
+ if (items.length > 0 || removed.length > 0) {
143
+ this.dispatch('splice', {
144
+ deleteCount,
145
+ items,
146
+ start
147
+ });
148
+ this.trigger();
149
+ }
150
+
151
+ return removed;
152
+ }
153
+
154
+ track() {
155
+ read(this.#signal);
156
+ }
157
+
158
+ unshift(...items: T[]) {
159
+ let length = super.unshift(...items);
160
+
161
+ this.dispatch('unshift', { items });
162
+ this.trigger();
163
+
164
+ return length;
165
+ }
166
+ }
167
+
168
+
169
+ // REMINDER:
170
+ // - @ts-ignore flags are supressing a type mismatch error
171
+ // - Input values are being transformed by this class into nodes
172
+ class ReactiveObjectArray<T extends Record<PropertyKey, unknown>> extends ReactiveArray<Node<T>> {
173
+ #options: Options;
174
+
175
+
176
+ constructor(data: T[], options: Options = {}) {
177
+ super( factory(data, options) );
178
+ this.#options = options;
179
+ }
180
+
181
+
182
+ fill() {
183
+ return unsupported('fill');
184
+ }
185
+
186
+ reverse() {
187
+ return unsupported('reverse');
188
+ }
189
+
190
+ pop() {
191
+ return dispose(super.pop()) as Node<T> | undefined;
192
+ }
193
+
194
+ // @ts-ignore
195
+ push(...values: T[]) {
196
+ return super.push(...factory(values, this.#options));
197
+ }
198
+
199
+ shift() {
200
+ return dispose(super.shift()) as Node<T> | undefined;
201
+ }
202
+
203
+ sort() {
204
+ return unsupported('sort');
205
+ }
206
+
207
+ // @ts-ignore
208
+ splice(start: number, deleteCount: number = super.length, ...values: T[]) {
209
+ return dispose( super.splice(start, deleteCount, ...factory(values, this.#options)) );
210
+ }
211
+
212
+ // @ts-ignore
213
+ unshift(...values: T[]) {
214
+ return super.unshift(...factory(values, this.#options));
215
+ }
216
+ }
217
+
218
+
219
+ export { ReactiveArray, ReactiveObjectArray };
@@ -0,0 +1,26 @@
1
+ import { Object, Options, Prettify } from '~/types';
2
+ import { ReactiveObject } from './object';
3
+
4
+
5
+ type Guard<T> =
6
+ T extends Object
7
+ ? { [K in keyof T]: Never<K, Guard<T[K]>> }
8
+ : T extends unknown[]
9
+ ? Guard<T[number]>[]
10
+ : T;
11
+
12
+ type Infer<T> =
13
+ T extends (...args: unknown[]) => unknown
14
+ ? ReturnType<T>
15
+ : T extends unknown[]
16
+ ? Infer<T[number]>[]
17
+ : T extends Object
18
+ ? { [K in keyof T]: T[K] }
19
+ : T;
20
+
21
+ type Never<K,V> = K extends keyof ReactiveObject<Object> ? never : V;
22
+
23
+
24
+ export default <T extends Object>(data: Guard<T>, options: Options = {}) => {
25
+ return new ReactiveObject(data, options) as any as Prettify< { [K in keyof T]: Infer<T[K]> } & ReactiveObject<T> >;
26
+ };
@@ -0,0 +1,93 @@
1
+ import { computed, read, signal, write } from '~/signal';
2
+ import { Computed, Options, Signal } from '~/types';
3
+ import { defineProperty, isArray } from '~/utilities';
4
+ import { ReactiveArray, ReactiveObjectArray } from './array';
5
+
6
+
7
+ type Node<T> =
8
+ T extends (...args: unknown[]) => unknown
9
+ ? Computed<T>
10
+ : T extends unknown[]
11
+ ? T extends Object[] ? ReactiveObjectArray<T[0]> : ReactiveArray<T>
12
+ : Signal<T>;
13
+
14
+ type Nodes<T extends Object> = { [K in keyof T]: Node<T[K]> };
15
+
16
+ type Object = Record<PropertyKey, unknown>;
17
+
18
+
19
+ class ReactiveObject<T extends Object> {
20
+ nodes: Nodes<T>;
21
+
22
+
23
+ constructor(data: T, options: Options = {}) {
24
+ let nodes: Object = {};
25
+
26
+ for (let key in data) {
27
+ let input = data[key];
28
+
29
+ if (typeof input === 'function') {
30
+ let node = nodes[key] = computed(input as Computed<unknown>['fn'], options);
31
+
32
+ defineProperty(this, key, {
33
+ get() {
34
+ return read(node);
35
+ }
36
+ });
37
+ }
38
+ else if (isArray(input)) {
39
+ let node: ReactiveArray<unknown> | ReactiveObjectArray<Object>,
40
+ test = input[0];
41
+
42
+ if (typeof test === 'object' && test !== null && test.constructor.name === 'Object') {
43
+ node = nodes[key] = new ReactiveObjectArray(input, options);
44
+ }
45
+ else {
46
+ node = nodes[key] = new ReactiveArray(input);
47
+ }
48
+
49
+ defineProperty(this, key, {
50
+ get() {
51
+ node.track();
52
+
53
+ return node;
54
+ }
55
+ });
56
+ }
57
+ else {
58
+ let node = nodes[key] = signal(input, options);
59
+
60
+ defineProperty(this, key, {
61
+ get() {
62
+ return read(node);
63
+ },
64
+ set(value) {
65
+ write(node, value);
66
+ }
67
+ });
68
+ }
69
+ }
70
+
71
+ this.nodes = nodes as typeof this.nodes;
72
+ }
73
+
74
+
75
+ dispose() {
76
+ let nodes = this.nodes;
77
+
78
+ for (let key in nodes) {
79
+ nodes[key].dispose();
80
+ }
81
+ }
82
+
83
+ reset() {
84
+ let nodes = this.nodes;
85
+
86
+ for (let key in nodes) {
87
+ nodes[key].reset();
88
+ }
89
+ }
90
+ }
91
+
92
+
93
+ export { ReactiveObject };
@@ -0,0 +1,79 @@
1
+ import CustomFunction from '@esportsplus/custom-function';
2
+ import { computed, read, signal, write } from './signal';
3
+ import { Signal } from './types';
4
+
5
+
6
+ type Fn<A extends unknown[], R extends Promise<unknown>> = (...args: A) => R;
7
+
8
+ type Options = Parameters<typeof computed>[1];
9
+
10
+
11
+ class Resource<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
12
+ #data: Signal<Awaited<R>>;
13
+ #input: Signal<A | null>;
14
+ #ok: Signal<boolean | null>;
15
+
16
+ stop: boolean | null = null;
17
+
18
+
19
+ constructor(fn: Fn<A,R>, options: Options = {}) {
20
+ super((...args: A) => {
21
+ this.stop = null;
22
+
23
+ write(this.#input, args);
24
+ write(this.#ok, null);
25
+
26
+ fn(...args)
27
+ .then((value) => {
28
+ if (this.stop === true) {
29
+ return;
30
+ }
31
+
32
+ write(this.#data, value);
33
+ write(this.#ok, true);
34
+ })
35
+ .catch(() => {
36
+ if (this.stop === true) {
37
+ return;
38
+ }
39
+
40
+ write(this.#data, undefined);
41
+ write(this.#ok, false);
42
+ });
43
+ });
44
+ this.#data = signal(options.value as Awaited<R>, options),
45
+ this.#input = signal(null, options),
46
+ this.#ok = signal(null, options)
47
+ }
48
+
49
+
50
+ get data() {
51
+ return read(this.#data);
52
+ }
53
+
54
+ get ok() {
55
+ return read(this.#ok);
56
+ }
57
+
58
+ get input() {
59
+ return read(this.#input);
60
+ }
61
+
62
+
63
+ dispose() {
64
+ this.#data.dispose();
65
+ this.#input.dispose();
66
+ this.#ok.dispose();
67
+ }
68
+
69
+ reset() {
70
+ this.#data.reset();
71
+ this.#input.reset();
72
+ this.#ok.reset();
73
+ }
74
+ }
75
+
76
+
77
+ export default <A extends unknown[], R extends Promise<unknown>>(fn: Fn<A,R>, options: Options = {}) => {
78
+ return new Resource(fn, options);
79
+ };