@esportsplus/reactivity 0.22.1 → 0.23.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 (59) hide show
  1. package/.github/workflows/bump.yml +2 -2
  2. package/.github/workflows/dependabot.yml +1 -1
  3. package/.github/workflows/publish.yml +2 -2
  4. package/build/index.d.ts +1 -1
  5. package/build/index.js +1 -1
  6. package/build/reactive/array.d.ts +3 -0
  7. package/build/reactive/array.js +32 -2
  8. package/build/reactive/index.d.ts +17 -14
  9. package/build/reactive/index.js +7 -25
  10. package/build/system.js +1 -1
  11. package/build/transformer/core/detector.d.ts +2 -0
  12. package/build/transformer/core/detector.js +6 -0
  13. package/build/transformer/core/index.d.ts +10 -0
  14. package/build/transformer/core/index.js +55 -0
  15. package/build/transformer/core/transforms/auto-dispose.d.ts +3 -0
  16. package/build/transformer/core/transforms/auto-dispose.js +116 -0
  17. package/build/transformer/core/transforms/reactive-array.d.ts +4 -0
  18. package/build/transformer/core/transforms/reactive-array.js +89 -0
  19. package/build/transformer/core/transforms/reactive-object.d.ts +4 -0
  20. package/build/transformer/core/transforms/reactive-object.js +155 -0
  21. package/build/transformer/core/transforms/reactive-primitives.d.ts +4 -0
  22. package/build/transformer/core/transforms/reactive-primitives.js +325 -0
  23. package/build/transformer/core/transforms/utilities.d.ts +9 -0
  24. package/build/transformer/core/transforms/utilities.js +57 -0
  25. package/build/transformer/plugins/esbuild.d.ts +5 -0
  26. package/build/transformer/plugins/esbuild.js +30 -0
  27. package/build/transformer/plugins/tsc.d.ts +3 -0
  28. package/build/transformer/plugins/tsc.js +4 -0
  29. package/build/transformer/plugins/vite.d.ts +5 -0
  30. package/build/transformer/plugins/vite.js +28 -0
  31. package/build/types.d.ts +14 -4
  32. package/package.json +34 -3
  33. package/readme.md +276 -2
  34. package/src/constants.ts +1 -1
  35. package/src/index.ts +1 -1
  36. package/src/reactive/array.ts +49 -2
  37. package/src/reactive/index.ts +33 -57
  38. package/src/system.ts +14 -5
  39. package/src/transformer/core/detector.ts +12 -0
  40. package/src/transformer/core/index.ts +82 -0
  41. package/src/transformer/core/transforms/auto-dispose.ts +194 -0
  42. package/src/transformer/core/transforms/reactive-array.ts +140 -0
  43. package/src/transformer/core/transforms/reactive-object.ts +244 -0
  44. package/src/transformer/core/transforms/reactive-primitives.ts +459 -0
  45. package/src/transformer/core/transforms/utilities.ts +95 -0
  46. package/src/transformer/plugins/esbuild.ts +46 -0
  47. package/src/transformer/plugins/tsc.ts +8 -0
  48. package/src/transformer/plugins/vite.ts +41 -0
  49. package/src/types.ts +24 -5
  50. package/test/arrays.ts +146 -0
  51. package/test/effects.ts +168 -0
  52. package/test/index.ts +8 -0
  53. package/test/nested.ts +201 -0
  54. package/test/objects.ts +106 -0
  55. package/test/primitives.ts +171 -0
  56. package/test/vite.config.ts +40 -0
  57. package/build/reactive/object.d.ts +0 -7
  58. package/build/reactive/object.js +0 -79
  59. package/src/reactive/object.ts +0 -116
package/test/arrays.ts ADDED
@@ -0,0 +1,146 @@
1
+ // Test: Reactive Arrays
2
+ import { effect, reactive } from '@esportsplus/reactivity';
3
+
4
+
5
+ // =============================================================================
6
+ // Basic Array Operations
7
+ // =============================================================================
8
+
9
+ console.log('=== Basic Array Operations ===');
10
+
11
+ let numbers = reactive([1, 2, 3]);
12
+
13
+ console.log('Initial:', [...numbers]);
14
+ console.log('Length:', numbers.length);
15
+
16
+ // Push
17
+ numbers.push(4, 5);
18
+ console.log('After push(4, 5):', [...numbers]);
19
+
20
+ // Pop
21
+ let popped = numbers.pop();
22
+ console.log('Popped:', popped);
23
+ console.log('After pop:', [...numbers]);
24
+
25
+ // Shift
26
+ let shifted = numbers.shift();
27
+ console.log('Shifted:', shifted);
28
+ console.log('After shift:', [...numbers]);
29
+
30
+ // Unshift
31
+ numbers.unshift(0);
32
+ console.log('After unshift(0):', [...numbers]);
33
+
34
+ // Index assignment
35
+ numbers[0] = 100;
36
+ console.log('After [0] = 100:', [...numbers]);
37
+
38
+
39
+ // =============================================================================
40
+ // Splice
41
+ // =============================================================================
42
+
43
+ console.log('\n=== Splice ===');
44
+
45
+ let items = reactive(['a', 'b', 'c', 'd', 'e']);
46
+ console.log('Initial:', [...items]);
47
+
48
+ // Remove 2 items starting at index 1
49
+ let removed = items.splice(1, 2);
50
+ console.log('Removed:', removed);
51
+ console.log('After splice(1, 2):', [...items]);
52
+
53
+ // Insert items
54
+ items.splice(1, 0, 'x', 'y');
55
+ console.log('After splice(1, 0, x, y):', [...items]);
56
+
57
+
58
+ // =============================================================================
59
+ // Sort and Reverse
60
+ // =============================================================================
61
+
62
+ console.log('\n=== Sort and Reverse ===');
63
+
64
+ let sortable = reactive([3, 1, 4, 1, 5, 9, 2, 6]);
65
+ console.log('Initial:', [...sortable]);
66
+
67
+ sortable.sort((a, b) => a - b);
68
+ console.log('After sort (asc):', [...sortable]);
69
+
70
+ sortable.reverse();
71
+ console.log('After reverse:', [...sortable]);
72
+
73
+
74
+ // =============================================================================
75
+ // Concat
76
+ // =============================================================================
77
+
78
+ console.log('\n=== Concat ===');
79
+
80
+ let base = reactive([1, 2]);
81
+ console.log('Initial:', [...base]);
82
+
83
+ base.concat([3, 4]);
84
+ console.log('After concat([3, 4]):', [...base]);
85
+
86
+ base.concat(5, [6, 7]);
87
+ console.log('After concat(5, [6, 7]):', [...base]);
88
+
89
+
90
+ // =============================================================================
91
+ // Reactive Length in Effects
92
+ // =============================================================================
93
+
94
+ console.log('\n=== Reactive Length in Effects ===');
95
+
96
+ let tracked = reactive([1, 2, 3]);
97
+ let lengthReads = 0;
98
+
99
+ effect(() => {
100
+ lengthReads++;
101
+ console.log(`Effect #${lengthReads}: length = ${tracked.length}`);
102
+ });
103
+
104
+ tracked.push(4);
105
+ tracked.pop();
106
+ tracked.splice(0, 1);
107
+
108
+ console.log('Total length reads:', lengthReads);
109
+
110
+
111
+ // =============================================================================
112
+ // Array Events
113
+ // =============================================================================
114
+
115
+ console.log('\n=== Array Events ===');
116
+
117
+ let observed = reactive([1, 2, 3]);
118
+
119
+ observed.on('push', (data) => {
120
+ console.log('Push event:', data);
121
+ });
122
+
123
+ observed.on('pop', (data) => {
124
+ console.log('Pop event:', data);
125
+ });
126
+
127
+ observed.on('set', (data) => {
128
+ console.log('Set event:', data);
129
+ });
130
+
131
+ observed.push(4, 5);
132
+ observed.pop();
133
+ observed[0] = 100;
134
+
135
+
136
+ // =============================================================================
137
+ // Clear and Dispose
138
+ // =============================================================================
139
+
140
+ console.log('\n=== Clear and Dispose ===');
141
+
142
+ let clearable = reactive([1, 2, 3, 4, 5]);
143
+ console.log('Before clear:', [...clearable], 'length:', clearable.length);
144
+
145
+ clearable.clear();
146
+ console.log('After clear:', [...clearable], 'length:', clearable.length);
@@ -0,0 +1,168 @@
1
+ // Test: Effects and Cleanup
2
+ import { effect, onCleanup, reactive, root } from '@esportsplus/reactivity';
3
+
4
+
5
+ // =============================================================================
6
+ // Basic Effect with Object
7
+ // =============================================================================
8
+
9
+ console.log('=== Basic Effect ===');
10
+
11
+ let state = reactive({
12
+ count: 0
13
+ });
14
+ let effectRuns = 0;
15
+
16
+ let dispose = effect(() => {
17
+ effectRuns++;
18
+ console.log(`Effect run #${effectRuns}: count = ${state.count}`);
19
+ });
20
+
21
+ state.count = 1;
22
+ state.count = 2;
23
+ state.count = 3;
24
+
25
+ console.log('Total runs before dispose:', effectRuns);
26
+
27
+ dispose();
28
+
29
+ state.count = 4;
30
+ state.count = 5;
31
+
32
+ console.log('Total runs after dispose:', effectRuns);
33
+
34
+
35
+ // =============================================================================
36
+ // Effect with Cleanup
37
+ // =============================================================================
38
+
39
+ console.log('\n=== Effect with Cleanup ===');
40
+
41
+ let data = reactive({
42
+ value: 0
43
+ });
44
+ let cleanupCalls = 0;
45
+
46
+ let cleanup = effect((onCleanup) => {
47
+ let current = data.value;
48
+ console.log('Effect running with value:', current);
49
+
50
+ onCleanup(() => {
51
+ cleanupCalls++;
52
+ console.log(`Cleanup called (${cleanupCalls}x) for value:`, current);
53
+ });
54
+ });
55
+
56
+ data.value = 1;
57
+ data.value = 2;
58
+
59
+ cleanup();
60
+
61
+ console.log('Total cleanup calls:', cleanupCalls);
62
+
63
+
64
+ // =============================================================================
65
+ // Effect with Computed
66
+ // =============================================================================
67
+
68
+ console.log('\n=== Effect with Computed ===');
69
+
70
+ let counter = reactive({
71
+ count: 10,
72
+ doubled: () => counter.count * 2
73
+ });
74
+
75
+ let computedReads = 0;
76
+
77
+ effect(() => {
78
+ computedReads++;
79
+ console.log(`Effect #${computedReads}: doubled = ${counter.doubled}`);
80
+ });
81
+
82
+ counter.count = 20;
83
+ counter.count = 30;
84
+
85
+ console.log('Total computed reads:', computedReads);
86
+
87
+
88
+ // =============================================================================
89
+ // Root Scope
90
+ // =============================================================================
91
+
92
+ console.log('\n=== Root Scope ===');
93
+
94
+ let rootDisposed = false;
95
+
96
+ let result = root((dispose) => {
97
+ let obj = reactive({
98
+ a: 1,
99
+ b: () => obj.a * 2
100
+ });
101
+
102
+ effect(() => {
103
+ console.log('Root effect: a =', obj.a, 'b =', obj.b);
104
+ });
105
+
106
+ obj.a = 5;
107
+
108
+ onCleanup(() => {
109
+ rootDisposed = true;
110
+ console.log('Root cleanup called');
111
+ });
112
+
113
+ dispose();
114
+
115
+ return 'root result';
116
+ });
117
+
118
+ console.log('Root returned:', result);
119
+ console.log('Root disposed:', rootDisposed);
120
+
121
+
122
+ // =============================================================================
123
+ // Multiple Object Dependencies
124
+ // =============================================================================
125
+
126
+ console.log('\n=== Multiple Dependencies ===');
127
+
128
+ let obj1 = reactive({ x: 1 });
129
+ let obj2 = reactive({ y: 2 });
130
+ let obj3 = reactive({ z: 3 });
131
+ let multiRuns = 0;
132
+
133
+ effect(() => {
134
+ multiRuns++;
135
+ let sum = obj1.x + obj2.y + obj3.z;
136
+ console.log(`Multi-dep effect #${multiRuns}: x=${obj1.x}, y=${obj2.y}, z=${obj3.z}, sum=${sum}`);
137
+ });
138
+
139
+ obj1.x = 10;
140
+ obj2.y = 20;
141
+ obj3.z = 30;
142
+
143
+ console.log('Total multi-dep runs:', multiRuns);
144
+
145
+
146
+ // =============================================================================
147
+ // Effect with Array
148
+ // =============================================================================
149
+
150
+ console.log('\n=== Effect with Array ===');
151
+
152
+ let list = reactive({
153
+ items: [1, 2, 3],
154
+ total: () => list.items.reduce((a, b) => a + b, 0)
155
+ });
156
+
157
+ let arrayRuns = 0;
158
+
159
+ effect(() => {
160
+ arrayRuns++;
161
+ console.log(`Array effect #${arrayRuns}: length=${list.items.length}, total=${list.total}`);
162
+ });
163
+
164
+ list.items.push(4);
165
+ list.items.pop();
166
+ list.items[0] = 10;
167
+
168
+ console.log('Total array runs:', arrayRuns);
package/test/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ // Test Index - Import all test modules
2
+ // Run individual files for isolated testing
3
+
4
+ export * from './primitives';
5
+ export * from './objects';
6
+ export * from './arrays';
7
+ export * from './effects';
8
+ export * from './nested';
package/test/nested.ts ADDED
@@ -0,0 +1,201 @@
1
+ // Test: Nested Reactive Structures
2
+ import { effect, reactive } from '@esportsplus/reactivity';
3
+
4
+
5
+ // =============================================================================
6
+ // Object with Computed accessing other objects
7
+ // =============================================================================
8
+
9
+ console.log('=== Nested Object Access ===');
10
+
11
+ let config = reactive({
12
+ debug: true,
13
+ theme: 'dark'
14
+ });
15
+
16
+ let settings = reactive({
17
+ notifications: true,
18
+ volume: 80
19
+ });
20
+
21
+ let user = reactive({
22
+ name: 'Alice',
23
+ getTheme: () => config.theme,
24
+ getVolume: () => settings.volume
25
+ });
26
+
27
+ console.log('Initial config:', { debug: config.debug, theme: config.theme });
28
+ console.log('Initial user:', user.name, 'theme:', user.getTheme, 'volume:', user.getVolume);
29
+
30
+ config.theme = 'light';
31
+ settings.volume = 50;
32
+
33
+ console.log('After updates:');
34
+ console.log(' user theme:', user.getTheme);
35
+ console.log(' user volume:', user.getVolume);
36
+
37
+
38
+ // =============================================================================
39
+ // Object with Array of Objects
40
+ // =============================================================================
41
+
42
+ console.log('\n=== Object with Array of Reactive Objects ===');
43
+
44
+ let todo1 = reactive({ done: false, text: 'Learn reactivity' });
45
+ let todo2 = reactive({ done: true, text: 'Build app' });
46
+ let todo3 = reactive({ done: false, text: 'Test everything' });
47
+
48
+ let store = reactive({
49
+ todos: [todo1, todo2, todo3],
50
+ completedCount: () => store.todos.filter(t => t.done).length
51
+ });
52
+
53
+ console.log('Initial todos:');
54
+ for (let i = 0; i < store.todos.length; i++) {
55
+ let todo = store.todos[i];
56
+ console.log(` [${todo.done ? 'x' : ' '}] ${todo.text}`);
57
+ }
58
+ console.log('Completed:', store.completedCount);
59
+
60
+ todo1.done = true;
61
+ console.log('After marking todo1 done:');
62
+ console.log('Completed:', store.completedCount);
63
+
64
+ let todo4 = reactive({ done: false, text: 'Deploy' });
65
+ store.todos.push(todo4);
66
+
67
+ console.log('After adding todo4:');
68
+ for (let i = 0; i < store.todos.length; i++) {
69
+ let todo = store.todos[i];
70
+ console.log(` [${todo.done ? 'x' : ' '}] ${todo.text}`);
71
+ }
72
+
73
+
74
+ // =============================================================================
75
+ // Array of Arrays (using reactive arrays)
76
+ // =============================================================================
77
+
78
+ console.log('\n=== Array of Arrays ===');
79
+
80
+ let row1 = reactive([1, 2, 3]);
81
+ let row2 = reactive([4, 5, 6]);
82
+ let row3 = reactive([7, 8, 9]);
83
+
84
+ let matrix = reactive([row1, row2, row3]);
85
+
86
+ console.log('Initial matrix:');
87
+ for (let row of matrix) {
88
+ console.log(' ', [...row]);
89
+ }
90
+
91
+ row1[0] = 100;
92
+ row2.push(60);
93
+
94
+ let row4 = reactive([10, 11, 12]);
95
+ matrix.push(row4);
96
+
97
+ console.log('After updates:');
98
+ for (let row of matrix) {
99
+ console.log(' ', [...row]);
100
+ }
101
+
102
+
103
+ // =============================================================================
104
+ // Cross-Object Computed Dependencies
105
+ // =============================================================================
106
+
107
+ console.log('\n=== Cross-Object Computed Dependencies ===');
108
+
109
+ let data = reactive({
110
+ items: [10, 20, 30],
111
+ multiplier: 2
112
+ });
113
+
114
+ let calcObj = reactive({
115
+ sum: () => data.items.reduce((a: number, b: number) => a + b, 0),
116
+ total: () => calcObj.sum * data.multiplier
117
+ });
118
+
119
+ console.log('Initial sum:', calcObj.sum);
120
+ console.log('Initial total:', calcObj.total);
121
+
122
+ data.items.push(40);
123
+ console.log('After push(40) - sum:', calcObj.sum, 'total:', calcObj.total);
124
+
125
+ data.multiplier = 3;
126
+ console.log('After multiplier = 3 - sum:', calcObj.sum, 'total:', calcObj.total);
127
+
128
+
129
+ // =============================================================================
130
+ // Effects Tracking Multiple Objects
131
+ // =============================================================================
132
+
133
+ console.log('\n=== Effects Tracking Multiple Objects ===');
134
+
135
+ let effectRuns = 0;
136
+
137
+ let obj1 = reactive({ value: 1 });
138
+ let obj2 = reactive({ value: 2 });
139
+ let obj3 = reactive({ combined: () => obj1.value + obj2.value });
140
+
141
+ effect(() => {
142
+ effectRuns++;
143
+ console.log(`Effect #${effectRuns}: obj1=${obj1.value}, obj2=${obj2.value}, combined=${obj3.combined}`);
144
+ });
145
+
146
+ obj1.value = 10;
147
+ obj2.value = 20;
148
+
149
+ console.log('Total effect runs:', effectRuns);
150
+
151
+
152
+ // =============================================================================
153
+ // Primitives Referencing Objects
154
+ // =============================================================================
155
+
156
+ console.log('\n=== Primitives Referencing Objects ===');
157
+
158
+ let source = reactive({
159
+ base: 10,
160
+ items: [1, 2, 3]
161
+ });
162
+
163
+ let derived = reactive(() => source.base * 2);
164
+ let itemSum = reactive(() => source.items.reduce((a: number, b: number) => a + b, 0));
165
+
166
+ console.log('Initial derived:', derived);
167
+ console.log('Initial itemSum:', itemSum);
168
+
169
+ source.base = 20;
170
+ source.items.push(4);
171
+
172
+ console.log('After updates:');
173
+ console.log(' derived:', derived);
174
+ console.log(' itemSum:', itemSum);
175
+
176
+
177
+ // =============================================================================
178
+ // Dispose Individual Objects
179
+ // =============================================================================
180
+
181
+ console.log('\n=== Dispose Individual Objects ===');
182
+
183
+ let parent = reactive({
184
+ value: 42,
185
+ doubled: () => parent.value * 2
186
+ });
187
+
188
+ let child = reactive({
189
+ parentValue: () => parent.value,
190
+ ownValue: 10
191
+ });
192
+
193
+ console.log('Before dispose - parent.value:', parent.value, 'child.parentValue:', child.parentValue);
194
+
195
+ parent.dispose();
196
+ console.log('Parent disposed');
197
+
198
+ // Child still works with its own value
199
+ console.log('Child ownValue:', child.ownValue);
200
+ child.dispose();
201
+ console.log('Child disposed');
@@ -0,0 +1,106 @@
1
+ // Test: Reactive Objects
2
+ import { effect, reactive } from '@esportsplus/reactivity';
3
+
4
+
5
+ // =============================================================================
6
+ // Basic Object
7
+ // =============================================================================
8
+
9
+ console.log('=== Basic Reactive Object ===');
10
+
11
+ let user = reactive({
12
+ age: 25,
13
+ email: 'test@example.com',
14
+ name: 'John'
15
+ });
16
+
17
+ console.log('Initial user:', { age: user.age, email: user.email, name: user.name });
18
+
19
+ user.age = 26;
20
+ user.name = 'Jane';
21
+
22
+ console.log('After updates:', { age: user.age, email: user.email, name: user.name });
23
+
24
+
25
+ // =============================================================================
26
+ // Object with Computed Properties
27
+ // =============================================================================
28
+
29
+ console.log('\n=== Object with Computed Properties ===');
30
+
31
+ let counter = reactive({
32
+ count: 0,
33
+ doubled: () => counter.count * 2,
34
+ message: () => `Count is ${counter.count}`
35
+ });
36
+
37
+ console.log('Initial:', { count: counter.count, doubled: counter.doubled, message: counter.message });
38
+
39
+ counter.count = 5;
40
+ console.log('After count = 5:', { count: counter.count, doubled: counter.doubled, message: counter.message });
41
+
42
+ counter.count = 10;
43
+ console.log('After count = 10:', { count: counter.count, doubled: counter.doubled, message: counter.message });
44
+
45
+
46
+ // =============================================================================
47
+ // Object with Arrays
48
+ // =============================================================================
49
+
50
+ console.log('\n=== Object with Arrays ===');
51
+
52
+ let state = reactive({
53
+ items: [1, 2, 3],
54
+ total: () => state.items.reduce((a, b) => a + b, 0)
55
+ });
56
+
57
+ console.log('Initial items:', [...state.items]);
58
+ console.log('Initial total:', state.total);
59
+
60
+ state.items.push(4, 5);
61
+ console.log('After push(4, 5):', [...state.items]);
62
+ console.log('Updated total:', state.total);
63
+
64
+ state.items[0] = 10;
65
+ console.log('After items[0] = 10:', [...state.items]);
66
+
67
+
68
+ // =============================================================================
69
+ // Effects with Objects
70
+ // =============================================================================
71
+
72
+ console.log('\n=== Effects with Objects ===');
73
+
74
+ let effectRuns = 0;
75
+ let data = reactive({
76
+ a: 1,
77
+ b: 2,
78
+ sum: () => data.a + data.b
79
+ });
80
+
81
+ effect(() => {
82
+ effectRuns++;
83
+ console.log(`Effect #${effectRuns}: a=${data.a}, b=${data.b}, sum=${data.sum}`);
84
+ });
85
+
86
+ data.a = 10;
87
+ data.b = 20;
88
+
89
+ console.log('Total effect runs:', effectRuns);
90
+
91
+
92
+ // =============================================================================
93
+ // Dispose
94
+ // =============================================================================
95
+
96
+ console.log('\n=== Dispose ===');
97
+
98
+ let disposable = reactive({
99
+ value: 42,
100
+ computed: () => disposable.value * 2
101
+ });
102
+
103
+ console.log('Before dispose:', disposable.value, disposable.computed);
104
+
105
+ disposable.dispose();
106
+ console.log('Disposed successfully');