ustor 0.0.2 → 0.0.4
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/{src/index.js → dist/index.es.js} +3 -7
- package/dist/index.js +36 -0
- package/dist/index.min.js +1 -0
- package/dist/index.umd.js +1 -0
- package/package.json +4 -1
- package/.github/workflows/node.js.yml +0 -30
- package/rollup.config.js +0 -39
- package/test.spec.js +0 -370
package/dist/index.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
let api = {},
|
|
4
|
+
_s = Symbol("s"),
|
|
5
|
+
is = (v) => v[_s],
|
|
6
|
+
isObject = (v) => v && v.constructor === Object,
|
|
7
|
+
resolve = (v) => Array.isArray(v)
|
|
8
|
+
? store(v)
|
|
9
|
+
: isObject(v)
|
|
10
|
+
? Object.seal(store(v))
|
|
11
|
+
: v,
|
|
12
|
+
store = (values, proto) => {
|
|
13
|
+
if ((is(values) && !proto) || !(api.signal && api.get && api.set && api.is)) return values;
|
|
14
|
+
if (Array.isArray(values)) values.forEach((item, i) => values[i] = is(item) ? item : store(item));
|
|
15
|
+
if (isObject(values)) {
|
|
16
|
+
let state = Object.create(proto || Object.getPrototypeOf(values));
|
|
17
|
+
Object.entries(Object.getOwnPropertyDescriptors(values)).forEach(
|
|
18
|
+
([key, desc]) => {
|
|
19
|
+
let signal = api.is(desc.value) ? desc.value : api.signal(resolve(desc.value)),
|
|
20
|
+
memo = desc.get ? api.memo ? api.memo(desc.get.bind(state)) : desc.get.bind(state)?.() : signal;
|
|
21
|
+
Object.defineProperty(state, key, {
|
|
22
|
+
get: () => desc.get ? memo : api.get(memo),
|
|
23
|
+
set: desc.get ? desc.set?.bind(state) : v => api.set(signal, resolve(v)),
|
|
24
|
+
enumerable: true,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
);
|
|
28
|
+
Object.defineProperty(state, _s, { value: true });
|
|
29
|
+
return state;
|
|
30
|
+
}
|
|
31
|
+
return values;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
exports.api = api;
|
|
35
|
+
exports.is = is;
|
|
36
|
+
exports.store = store;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var template=function(e){let t={},r=Symbol("s"),a=e=>e[r],i=e=>e&&e.constructor===Object,s=e=>Array.isArray(e)?n(e):i(e)?Object.seal(n(e)):e,n=(e,c)=>{if(a(e)&&!c||!(t.signal&&t.get&&t.set&&t.is))return e;if(Array.isArray(e)&&e.forEach(((t,r)=>e[r]=a(t)?t:n(t))),i(e)){let a=Object.create(c||Object.getPrototypeOf(e));return Object.entries(Object.getOwnPropertyDescriptors(e)).forEach((([e,r])=>{let i=t.is(r.value)?r.value:t.signal(s(r.value)),n=r.get?t.memo?t.memo(r.get.bind(a)):r.get.bind(a)?.():i;Object.defineProperty(a,e,{get:()=>r.get?n:t.get(n),set:r.get?r.set?.bind(a):e=>t.set(i,s(e)),enumerable:!0})})),Object.defineProperty(a,r,{value:!0}),a}return e};return e.api=t,e.is=a,e.store=n,e}({});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).template={})}(this,(function(e){let t={},r=Symbol("s"),i=e=>e[r],o=e=>e&&e.constructor===Object,n=e=>Array.isArray(e)?s(e):o(e)?Object.seal(s(e)):e,s=(e,a)=>{if(i(e)&&!a||!(t.signal&&t.get&&t.set&&t.is))return e;if(Array.isArray(e)&&e.forEach(((t,r)=>e[r]=i(t)?t:s(t))),o(e)){let i=Object.create(a||Object.getPrototypeOf(e));return Object.entries(Object.getOwnPropertyDescriptors(e)).forEach((([e,r])=>{let o=t.is(r.value)?r.value:t.signal(n(r.value)),s=r.get?t.memo?t.memo(r.get.bind(i)):r.get.bind(i)?.():o;Object.defineProperty(i,e,{get:()=>r.get?s:t.get(s),set:r.get?r.set?.bind(i):e=>t.set(o,n(e)),enumerable:!0})})),Object.defineProperty(i,r,{value:!0}),i}return e};e.api=t,e.is=i,e.store=s}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ustor",
|
|
3
3
|
"description": "A lightweight reactive store library that provides a powerful reactivity system for managing state, compatible with any UI framework, including React, Solid.js, and Preact Signals.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"source": "./src/index.js",
|
|
7
7
|
"main": "./dist/index.js",
|
|
@@ -19,6 +19,9 @@
|
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://github.com/kethan/ustor.git"
|
|
21
21
|
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
22
25
|
"scripts": {
|
|
23
26
|
"build": "rollup --config rollup.config.js",
|
|
24
27
|
"test": "vitest"
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
|
2
|
-
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
3
|
-
|
|
4
|
-
name: test
|
|
5
|
-
|
|
6
|
-
on:
|
|
7
|
-
push:
|
|
8
|
-
branches: [main]
|
|
9
|
-
pull_request:
|
|
10
|
-
branches: [main]
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
build:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
|
|
16
|
-
strategy:
|
|
17
|
-
matrix:
|
|
18
|
-
node-version: [18.x]
|
|
19
|
-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
|
20
|
-
|
|
21
|
-
steps:
|
|
22
|
-
- uses: actions/checkout@v3
|
|
23
|
-
- name: Use Node.js ${{ matrix.node-version }}
|
|
24
|
-
uses: actions/setup-node@v3
|
|
25
|
-
with:
|
|
26
|
-
node-version: ${{ matrix.node-version }}
|
|
27
|
-
cache: "npm"
|
|
28
|
-
- run: npm ci
|
|
29
|
-
- run: npm run build --if-present
|
|
30
|
-
- run: npm test
|
package/rollup.config.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import terser from '@rollup/plugin-terser';
|
|
2
|
-
import bundleSize from 'rollup-plugin-bundle-size';
|
|
3
|
-
|
|
4
|
-
const resolve = (pkg, input = "src/index", output = "dist/index") => ({
|
|
5
|
-
input: `${input}.js`,
|
|
6
|
-
plugins: [
|
|
7
|
-
bundleSize()
|
|
8
|
-
],
|
|
9
|
-
output: [
|
|
10
|
-
{
|
|
11
|
-
file: `${output}.es.js`,
|
|
12
|
-
format: 'es',
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
file: `${output}.js`,
|
|
16
|
-
format: 'cjs',
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
file: `${output}.min.js`,
|
|
20
|
-
format: 'iife',
|
|
21
|
-
name: pkg,
|
|
22
|
-
strict: false,
|
|
23
|
-
compact: true,
|
|
24
|
-
plugins: [terser()]
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
file: `${output}.umd.js`,
|
|
28
|
-
format: 'umd',
|
|
29
|
-
name: pkg,
|
|
30
|
-
strict: false,
|
|
31
|
-
compact: true,
|
|
32
|
-
plugins: [terser()]
|
|
33
|
-
}
|
|
34
|
-
]
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
export default [
|
|
38
|
-
resolve("template")
|
|
39
|
-
]
|
package/test.spec.js
DELETED
|
@@ -1,370 +0,0 @@
|
|
|
1
|
-
import { store, api } from './src';
|
|
2
|
-
// import { signal, computed, effect, batch } from '@preact/signals-core';
|
|
3
|
-
import { createSignal, createEffect, createMemo, batch } from './node_modules/solid-js/dist/solid'
|
|
4
|
-
import { assert, describe, it } from 'vitest';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
//Solid.js
|
|
8
|
-
api.memo = createMemo;
|
|
9
|
-
api.signal = createSignal;
|
|
10
|
-
api.effect = createEffect;
|
|
11
|
-
api.batch = batch;
|
|
12
|
-
api.getMemo = v => v?.();
|
|
13
|
-
api.is = v => v?.[0]?.name?.includes("readSignal");
|
|
14
|
-
api.get = v => v?.[0]();
|
|
15
|
-
api.set = (signal, v) => signal?.[1](v);
|
|
16
|
-
|
|
17
|
-
// @preact/signals-core, usignal, ulive
|
|
18
|
-
// api.memo = computed;
|
|
19
|
-
// api.signal = signal;
|
|
20
|
-
// api.effect = effect;
|
|
21
|
-
// api.batch = batch;
|
|
22
|
-
// api.getMemo = v => v?.value;
|
|
23
|
-
// api.is = (v) => v?.peek;
|
|
24
|
-
// api.get = (v) => v?.value;
|
|
25
|
-
// api.set = (signal, v) => (signal.value = v);
|
|
26
|
-
|
|
27
|
-
describe('Store functionality', () => {
|
|
28
|
-
let i = api.signal(3);
|
|
29
|
-
let s;
|
|
30
|
-
let len;
|
|
31
|
-
let xy;
|
|
32
|
-
let s1;
|
|
33
|
-
it('should initialize store with signals and functions', () => {
|
|
34
|
-
let i = api.signal(3);
|
|
35
|
-
s = store({
|
|
36
|
-
x: 0,
|
|
37
|
-
y: api.signal(1),
|
|
38
|
-
z: { r: 2, i },
|
|
39
|
-
v: function () {
|
|
40
|
-
return 1;
|
|
41
|
-
},
|
|
42
|
-
w: [1, 2],
|
|
43
|
-
get xy() {
|
|
44
|
-
return this.x + this.y;
|
|
45
|
-
},
|
|
46
|
-
set xy([x, y]) {
|
|
47
|
-
this.x = x;
|
|
48
|
-
this.y = y;
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should treat functions as signals', () => {
|
|
54
|
-
assert.equal(s.v(), 1);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should subscribe to signals without explicit .value access', () => {
|
|
58
|
-
const zilog = [];
|
|
59
|
-
api.effect(() => zilog.push(s.z.i));
|
|
60
|
-
xy = api.memo(() => s.x + s.y);
|
|
61
|
-
assert.equal(api.getMemo(xy), 1);
|
|
62
|
-
s.x = 2;
|
|
63
|
-
s.y = 3;
|
|
64
|
-
assert.equal(api.getMemo(xy), 5);
|
|
65
|
-
s.y = 4;
|
|
66
|
-
assert.equal(api.getMemo(xy), 6);
|
|
67
|
-
assert.deepEqual(zilog, [3]);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should handle getter computed properties correctly', () => {
|
|
71
|
-
assert.equal(api.getMemo(s.xy), 6);
|
|
72
|
-
s.xy = [4, 2];
|
|
73
|
-
assert.equal(s.x, 4);
|
|
74
|
-
assert.equal(s.y, 2);
|
|
75
|
-
assert.equal(api.getMemo(s.xy), 6);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should subscribe to deep values and update results', () => {
|
|
79
|
-
len = api.memo(() => (s.z.r ** 2 + s.z.i ** 2) ** 0.5);
|
|
80
|
-
s.z.r = 3;
|
|
81
|
-
s.z.i = 4;
|
|
82
|
-
assert.equal(api.getMemo(len), 5);
|
|
83
|
-
s.z.r = 4;
|
|
84
|
-
s.z.i = 3;
|
|
85
|
-
assert.equal(api.getMemo(len), 5);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('should update internal objects/arrays and turn them into signals', () => {
|
|
89
|
-
s.z = { r: 5, i: 12 };
|
|
90
|
-
assert.equal(api.getMemo(len), 13);
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
it('should handle updating arrays', () => {
|
|
95
|
-
let mult = api.memo(() => s.w[0] * s.w[1]);
|
|
96
|
-
assert.equal(api.getMemo(mult), 2);
|
|
97
|
-
s.w = [3, 4];
|
|
98
|
-
assert.equal(api.getMemo(mult), 12);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
it('should handle bulk updates with batch', () => {
|
|
102
|
-
api.batch(() => Object.assign(s, { x: 1, y: 1, z: { r: 3, i: 4 } }));
|
|
103
|
-
assert.equal(api.getMemo(xy), 2);
|
|
104
|
-
assert.equal(api.getMemo(len), 5, 'len after update');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should retain the same type as the initial data for signals', () => {
|
|
108
|
-
assert.equal(s.constructor, Object);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should return the same instance when re-initializing store', () => {
|
|
112
|
-
s1 = store(s);
|
|
113
|
-
assert.equal(s, s1);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('should ensure that store is not enumerable', () => {
|
|
117
|
-
let s2 = store([]);
|
|
118
|
-
let log = [];
|
|
119
|
-
for (let i of s2) log.push(i);
|
|
120
|
-
assert.deepEqual(log, [], "doesn't iterate");
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('should detect descendants as instances', () => {
|
|
124
|
-
let s3 = Object.create(s1),
|
|
125
|
-
s3s = store(s3);
|
|
126
|
-
assert.equal(s3, s3s);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
it('should convert array items to signal structs', () => {
|
|
130
|
-
let s5 = store({ list: [{ x: 1 }, { x: 2 }] });
|
|
131
|
-
let sum = api.memo(() => s5.list.reduce((sum, item) => item.x + sum, 0));
|
|
132
|
-
assert.equal(api.getMemo(sum), 3);
|
|
133
|
-
s5.list[0].x = 2;
|
|
134
|
-
assert.equal(api.getMemo(sum), 4);
|
|
135
|
-
s5.list = [{ x: 3 }, { x: 3 }];
|
|
136
|
-
assert.equal(api.getMemo(sum), 6);
|
|
137
|
-
s5.list = [{ x: 3 }, { x: 3 }, { x: 4 }];
|
|
138
|
-
assert.equal(api.getMemo(sum), 10);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should retain reference for arrays', () => {
|
|
142
|
-
let list = [1, 2, 3];
|
|
143
|
-
let s6 = store({ list });
|
|
144
|
-
s6.list[1] = 4;
|
|
145
|
-
assert.deepEqual(list, [1, 4, 3])
|
|
146
|
-
});
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
describe('Store Functionality - Basic to Complex', () => {
|
|
150
|
-
// Simple Test Cases
|
|
151
|
-
it('should initialize a store with simple values', () => {
|
|
152
|
-
const s = store({ a: 1, b: 2 });
|
|
153
|
-
assert.equal(s.a, 1);
|
|
154
|
-
assert.equal(s.b, 2);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
it('should update store values', () => {
|
|
158
|
-
const s = store({ a: 1 });
|
|
159
|
-
s.a = 5;
|
|
160
|
-
assert.equal(s.a, 5);
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('should initialize store with signals', () => {
|
|
164
|
-
const s = store({ a: api.signal(3) });
|
|
165
|
-
assert.equal(s.a, 3);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should update signal value within store', () => {
|
|
169
|
-
const s = store({ a: api.signal(3) });
|
|
170
|
-
s.a = 10;
|
|
171
|
-
assert.equal(s.a, 10);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
it('should compute a derived value', () => {
|
|
175
|
-
const s = store({ a: api.signal(2), b: api.signal(3) });
|
|
176
|
-
const sum = api.memo(() => s.a + s.b);
|
|
177
|
-
assert.equal(api.getMemo(sum), 5);
|
|
178
|
-
s.a = 5;
|
|
179
|
-
assert.equal(api.getMemo(sum), 8);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
// Intermediate Test Cases
|
|
183
|
-
it('should handle nested store updates', () => {
|
|
184
|
-
const s = store({ nested: { a: api.signal(1) } });
|
|
185
|
-
assert.equal(s.nested.a, 1);
|
|
186
|
-
s.nested.a = 4;
|
|
187
|
-
assert.equal(s.nested.a, 4);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('should react to changes in nested signals', () => {
|
|
191
|
-
const s = store({ nested: { a: api.signal(1) } });
|
|
192
|
-
const double = api.memo(() => s.nested.a * 2);
|
|
193
|
-
assert.equal(api.getMemo(double), 2);
|
|
194
|
-
s.nested.a = 3;
|
|
195
|
-
assert.equal(api.getMemo(double), 6);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
it('should allow effect to react to signal changes', () => {
|
|
199
|
-
const s = store({ a: api.signal(1) });
|
|
200
|
-
let effectValue = 0;
|
|
201
|
-
api.effect(() => {
|
|
202
|
-
effectValue = s.a * 2;
|
|
203
|
-
});
|
|
204
|
-
assert.equal(effectValue, 2);
|
|
205
|
-
s.a = 4;
|
|
206
|
-
assert.equal(effectValue, 8);
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('should batch updates to prevent redundant computations', () => {
|
|
210
|
-
const s = store({ a: api.signal(1), b: api.signal(2) });
|
|
211
|
-
let computeCount = 0;
|
|
212
|
-
const sum = api.memo(() => {
|
|
213
|
-
computeCount++;
|
|
214
|
-
return s.a + s.b;
|
|
215
|
-
});
|
|
216
|
-
assert.equal(api.getMemo(sum), 3);
|
|
217
|
-
assert.equal(computeCount, 1);
|
|
218
|
-
api.batch(() => {
|
|
219
|
-
s.a = 3;
|
|
220
|
-
s.b = 4;
|
|
221
|
-
});
|
|
222
|
-
assert.equal(api.getMemo(sum), 7);
|
|
223
|
-
assert.equal(computeCount, 2);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// Complex Test Cases
|
|
227
|
-
it('should handle arrays within the store', () => {
|
|
228
|
-
const s = store({ list: [1, 2, 3] });
|
|
229
|
-
const sum = api.memo(() => s.list.reduce((acc, item) => acc + item, 0));
|
|
230
|
-
assert.equal(api.getMemo(sum), 6);
|
|
231
|
-
s.list = [1, 5, 3]
|
|
232
|
-
assert.equal(api.getMemo(sum), 9);
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should handle nested computed properties', () => {
|
|
236
|
-
const s = store({ a: api.signal(2), b: api.signal(3) });
|
|
237
|
-
const product = api.memo(() => s.a * s.b);
|
|
238
|
-
const doubleProduct = api.memo(() => api.getMemo(product) * 2);
|
|
239
|
-
assert.equal(api.getMemo(doubleProduct), 12);
|
|
240
|
-
s.a = 4;
|
|
241
|
-
assert.equal(api.getMemo(doubleProduct), 24);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('should allow dynamic property addition and maintain reactivity', () => {
|
|
245
|
-
const s = store({ a: api.signal(1) });
|
|
246
|
-
s.b = api.signal(2);
|
|
247
|
-
const sum = api.memo(() => s.a + api.get(s.b));
|
|
248
|
-
assert.equal(api.getMemo(sum), 3);
|
|
249
|
-
api.set(s.b, 5)
|
|
250
|
-
assert.equal(api.getMemo(sum), 6);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
it('should react to changes in deeply nested structures', () => {
|
|
254
|
-
const s = store({ nested: { deep: { value: api.signal(10) } } });
|
|
255
|
-
const deepValue = api.memo(() => s.nested.deep.value * 2);
|
|
256
|
-
assert.equal(api.getMemo(deepValue), 20);
|
|
257
|
-
s.nested.deep.value = 15;
|
|
258
|
-
assert.equal(api.getMemo(deepValue), 30);
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// Combination of Arrays and Objects
|
|
262
|
-
it('should handle arrays of objects within the store', () => {
|
|
263
|
-
const s = store({ list: [{ x: api.signal(1) }, { x: api.signal(2) }, { x: api.signal(3) }] });
|
|
264
|
-
const sum = api.memo(() => s.list.reduce((acc, item) => acc + item.x, 0));
|
|
265
|
-
assert.equal(api.getMemo(sum), 6);
|
|
266
|
-
s.list[1].x = 5;
|
|
267
|
-
assert.equal(api.getMemo(sum), 9);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('should handle objects with array properties and nested signals', () => {
|
|
271
|
-
const s = store({ a: api.signal(1), b: [2, 3] });
|
|
272
|
-
const sum = api.memo(() => s.a + s.b.reduce((acc, item) => acc + item, 0));
|
|
273
|
-
assert.equal(api.getMemo(sum), 6);
|
|
274
|
-
s.a = 2;
|
|
275
|
-
s.b = [2, 4]
|
|
276
|
-
assert.equal(api.getMemo(sum), 8);
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
it('should handle deeply nested arrays and objects', () => {
|
|
280
|
-
const s = store({ nested: [{ x: { y: api.signal(2) } }, { x: { y: api.signal(3) } }] });
|
|
281
|
-
const total = api.memo(() => s.nested.reduce((acc, item) => acc + item.x.y, 0));
|
|
282
|
-
assert.equal(api.getMemo(total), 5);
|
|
283
|
-
s.nested[0].x.y = 4;
|
|
284
|
-
assert.equal(api.getMemo(total), 7);
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
it('should update array elements that are objects with signals', () => {
|
|
288
|
-
const s = store({ list: [{ a: api.signal(1) }, { a: api.signal(2) }] });
|
|
289
|
-
const sum = api.memo(() => s.list.reduce((acc, item) => acc + item.a, 0));
|
|
290
|
-
assert.equal(api.getMemo(sum), 3);
|
|
291
|
-
s.list = [...s.list, { a: api.signal(4) }]
|
|
292
|
-
assert.equal(api.getMemo(sum), 7);
|
|
293
|
-
s.list[0].a = 5;
|
|
294
|
-
assert.equal(api.getMemo(sum), 11);
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
describe('Extreme Testing - Simple Reactivity', () => {
|
|
300
|
-
// Stress test rapid updates
|
|
301
|
-
it('should handle rapid signal updates', () => {
|
|
302
|
-
const s = store({ a: api.signal(0) });
|
|
303
|
-
let updates = 0;
|
|
304
|
-
api.effect(() => {
|
|
305
|
-
updates = s.a;
|
|
306
|
-
});
|
|
307
|
-
for (let i = 0; i < 10000; i++) {
|
|
308
|
-
s.a = i;
|
|
309
|
-
}
|
|
310
|
-
assert.equal(s.a, 9999);
|
|
311
|
-
assert.equal(updates, 9999);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
// Stress test deep nesting with correct access
|
|
315
|
-
it('should handle deeply nested signals within the store', () => {
|
|
316
|
-
let deep = { value: 1 };
|
|
317
|
-
for (let i = 0; i < 50; i++) {
|
|
318
|
-
deep = { nested: deep };
|
|
319
|
-
}
|
|
320
|
-
const s = store({ deep });
|
|
321
|
-
// // Access the 50th level of nesting for example
|
|
322
|
-
let nestedValue = s.deep;
|
|
323
|
-
for (let i = 0; i < 50; i++) {
|
|
324
|
-
nestedValue = nestedValue.nested;
|
|
325
|
-
}
|
|
326
|
-
assert.equal(nestedValue.value, 1);
|
|
327
|
-
nestedValue.value = 50; // Update deeply nested signal
|
|
328
|
-
|
|
329
|
-
assert.equal(s.deep.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.nested.value, 50); // Confirm update at the correct depth
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
// Test massive batch updates
|
|
333
|
-
it('should handle massive batch updates without redundant computations', () => {
|
|
334
|
-
const s = store({ a: api.signal(1), b: api.signal(1) });
|
|
335
|
-
let computeCount = 0;
|
|
336
|
-
const sum = api.memo(() => {
|
|
337
|
-
computeCount++;
|
|
338
|
-
return s.a + s.b;
|
|
339
|
-
});
|
|
340
|
-
assert.equal(api.getMemo(sum), 2);
|
|
341
|
-
api.batch(() => {
|
|
342
|
-
for (let i = 0; i < 10000; i++) {
|
|
343
|
-
s.a = i;
|
|
344
|
-
s.b = i + 1;
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
assert.equal(api.getMemo(sum), 19999);
|
|
348
|
-
assert.equal(computeCount, 2); // Only computed twice (initial and after batch)
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
// Test bulk effect cleanup
|
|
352
|
-
it('should clean up effects properly during bulk updates', () => {
|
|
353
|
-
const s = store({ a: api.signal(0) });
|
|
354
|
-
let activeEffects = 0;
|
|
355
|
-
const cleanupEffect = api.effect(() => {
|
|
356
|
-
s.a = activeEffects++;
|
|
357
|
-
// if(api.cleanup) api.cleanup(() => s.a = activeEffects--)
|
|
358
|
-
// else
|
|
359
|
-
// return () => s.a = activeEffects--;
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
for (let i = 0; i < 1000; i++) {
|
|
363
|
-
s.a = i;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
assert.equal(s.a, 999);
|
|
367
|
-
// cleanupEffect();
|
|
368
|
-
// assert.equal(s.a, 1);
|
|
369
|
-
});
|
|
370
|
-
});
|