atomirx 0.0.2 → 0.0.5
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/README.md +868 -161
- package/coverage/src/core/onCreateHook.ts.html +72 -70
- package/dist/core/atom.d.ts +83 -6
- package/dist/core/batch.d.ts +3 -3
- package/dist/core/derived.d.ts +69 -22
- package/dist/core/effect.d.ts +52 -52
- package/dist/core/getAtomState.d.ts +29 -0
- package/dist/core/hook.d.ts +1 -1
- package/dist/core/onCreateHook.d.ts +37 -23
- package/dist/core/onErrorHook.d.ts +49 -0
- package/dist/core/promiseCache.d.ts +23 -32
- package/dist/core/select.d.ts +208 -29
- package/dist/core/types.d.ts +107 -22
- package/dist/core/withReady.d.ts +115 -0
- package/dist/core/withReady.test.d.ts +1 -0
- package/dist/index-CBVj1kSj.js +1350 -0
- package/dist/index-Cxk9v0um.cjs +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.js +18 -15
- package/dist/react/index.cjs +10 -10
- package/dist/react/index.d.ts +2 -1
- package/dist/react/index.js +422 -377
- package/dist/react/rx.d.ts +114 -25
- package/dist/react/useAction.d.ts +5 -4
- package/dist/react/{useValue.d.ts → useSelector.d.ts} +56 -25
- package/dist/react/useSelector.test.d.ts +1 -0
- package/package.json +1 -1
- package/src/core/atom.test.ts +307 -43
- package/src/core/atom.ts +144 -22
- package/src/core/batch.test.ts +10 -10
- package/src/core/batch.ts +3 -3
- package/src/core/define.test.ts +12 -11
- package/src/core/define.ts +1 -1
- package/src/core/derived.test.ts +906 -72
- package/src/core/derived.ts +192 -81
- package/src/core/effect.test.ts +651 -45
- package/src/core/effect.ts +102 -98
- package/src/core/getAtomState.ts +69 -0
- package/src/core/hook.test.ts +5 -5
- package/src/core/hook.ts +1 -1
- package/src/core/onCreateHook.ts +38 -23
- package/src/core/onErrorHook.test.ts +350 -0
- package/src/core/onErrorHook.ts +52 -0
- package/src/core/promiseCache.test.ts +5 -3
- package/src/core/promiseCache.ts +76 -71
- package/src/core/select.ts +405 -130
- package/src/core/selector.test.ts +574 -32
- package/src/core/types.ts +107 -29
- package/src/core/withReady.test.ts +534 -0
- package/src/core/withReady.ts +191 -0
- package/src/core/withUse.ts +1 -1
- package/src/index.test.ts +4 -4
- package/src/index.ts +21 -7
- package/src/react/index.ts +2 -1
- package/src/react/rx.test.tsx +173 -18
- package/src/react/rx.tsx +274 -43
- package/src/react/useAction.test.ts +12 -14
- package/src/react/useAction.ts +11 -9
- package/src/react/{useValue.test.ts → useSelector.test.ts} +16 -16
- package/src/react/{useValue.ts → useSelector.ts} +64 -33
- package/v2.md +44 -44
- package/dist/index-2ok7ilik.js +0 -1217
- package/dist/index-B_5SFzfl.cjs +0 -1
- /package/dist/{react/useValue.test.d.ts → core/onErrorHook.test.d.ts} +0 -0
package/src/core/atom.test.ts
CHANGED
|
@@ -6,7 +6,7 @@ describe("atom", () => {
|
|
|
6
6
|
describe("basic functionality", () => {
|
|
7
7
|
it("should create an atom with initial value", () => {
|
|
8
8
|
const count$ = atom(0);
|
|
9
|
-
expect(count$.
|
|
9
|
+
expect(count$.get()).toBe(0);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
it("should have SYMBOL_ATOM marker", () => {
|
|
@@ -17,51 +17,51 @@ describe("atom", () => {
|
|
|
17
17
|
it("should set value directly", () => {
|
|
18
18
|
const count$ = atom(0);
|
|
19
19
|
count$.set(5);
|
|
20
|
-
expect(count$.
|
|
20
|
+
expect(count$.get()).toBe(5);
|
|
21
21
|
});
|
|
22
22
|
|
|
23
23
|
it("should set value with reducer", () => {
|
|
24
24
|
const count$ = atom(0);
|
|
25
25
|
count$.set((prev) => prev + 1);
|
|
26
|
-
expect(count$.
|
|
26
|
+
expect(count$.get()).toBe(1);
|
|
27
27
|
count$.set((prev) => prev * 2);
|
|
28
|
-
expect(count$.
|
|
28
|
+
expect(count$.get()).toBe(2);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it("should reset to initial value", () => {
|
|
32
32
|
const count$ = atom(10);
|
|
33
33
|
count$.set(42);
|
|
34
|
-
expect(count$.
|
|
34
|
+
expect(count$.get()).toBe(42);
|
|
35
35
|
count$.reset();
|
|
36
|
-
expect(count$.
|
|
36
|
+
expect(count$.get()).toBe(10);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it("should store objects", () => {
|
|
40
40
|
const user$ = atom({ name: "John", age: 30 });
|
|
41
|
-
expect(user$.
|
|
41
|
+
expect(user$.get()).toEqual({ name: "John", age: 30 });
|
|
42
42
|
user$.set({ name: "Jane", age: 25 });
|
|
43
|
-
expect(user$.
|
|
43
|
+
expect(user$.get()).toEqual({ name: "Jane", age: 25 });
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
it("should store null and undefined", () => {
|
|
47
47
|
const nullable$ = atom<string | null>(null);
|
|
48
|
-
expect(nullable$.
|
|
48
|
+
expect(nullable$.get()).toBe(null);
|
|
49
49
|
nullable$.set("hello");
|
|
50
|
-
expect(nullable$.
|
|
50
|
+
expect(nullable$.get()).toBe("hello");
|
|
51
51
|
nullable$.set(null);
|
|
52
|
-
expect(nullable$.
|
|
52
|
+
expect(nullable$.get()).toBe(null);
|
|
53
53
|
|
|
54
54
|
const undef$ = atom<string | undefined>(undefined);
|
|
55
|
-
expect(undef$.
|
|
55
|
+
expect(undef$.get()).toBe(undefined);
|
|
56
56
|
undef$.set("world");
|
|
57
|
-
expect(undef$.
|
|
57
|
+
expect(undef$.get()).toBe("world");
|
|
58
58
|
});
|
|
59
59
|
|
|
60
60
|
it("should store arrays", () => {
|
|
61
61
|
const items$ = atom<number[]>([1, 2, 3]);
|
|
62
|
-
expect(items$.
|
|
62
|
+
expect(items$.get()).toEqual([1, 2, 3]);
|
|
63
63
|
items$.set((prev) => [...prev, 4]);
|
|
64
|
-
expect(items$.
|
|
64
|
+
expect(items$.get()).toEqual([1, 2, 3, 4]);
|
|
65
65
|
});
|
|
66
66
|
});
|
|
67
67
|
|
|
@@ -223,25 +223,25 @@ describe("atom", () => {
|
|
|
223
223
|
it("should store Promise as-is", () => {
|
|
224
224
|
const promise = Promise.resolve(42);
|
|
225
225
|
const data$ = atom(promise);
|
|
226
|
-
expect(data$.
|
|
226
|
+
expect(data$.get()).toBe(promise);
|
|
227
227
|
});
|
|
228
228
|
|
|
229
229
|
it("should store a new Promise on set", () => {
|
|
230
230
|
const promise1 = Promise.resolve(1);
|
|
231
231
|
const promise2 = Promise.resolve(2);
|
|
232
232
|
const data$ = atom(promise1);
|
|
233
|
-
expect(data$.
|
|
233
|
+
expect(data$.get()).toBe(promise1);
|
|
234
234
|
data$.set(promise2);
|
|
235
|
-
expect(data$.
|
|
235
|
+
expect(data$.get()).toBe(promise2);
|
|
236
236
|
});
|
|
237
237
|
|
|
238
238
|
it("should reset to original Promise object", () => {
|
|
239
239
|
const originalPromise = Promise.resolve(42);
|
|
240
240
|
const data$ = atom(originalPromise);
|
|
241
241
|
data$.set(Promise.resolve(100));
|
|
242
|
-
expect(data$.
|
|
242
|
+
expect(data$.get()).not.toBe(originalPromise);
|
|
243
243
|
data$.reset();
|
|
244
|
-
expect(data$.
|
|
244
|
+
expect(data$.get()).toBe(originalPromise);
|
|
245
245
|
});
|
|
246
246
|
});
|
|
247
247
|
|
|
@@ -255,7 +255,7 @@ describe("atom", () => {
|
|
|
255
255
|
});
|
|
256
256
|
}).toThrow(error);
|
|
257
257
|
// Value should remain unchanged
|
|
258
|
-
expect(count$.
|
|
258
|
+
expect(count$.get()).toBe(0);
|
|
259
259
|
});
|
|
260
260
|
});
|
|
261
261
|
|
|
@@ -264,7 +264,7 @@ describe("atom", () => {
|
|
|
264
264
|
const initializer = vi.fn(() => 42);
|
|
265
265
|
const count$ = atom(initializer);
|
|
266
266
|
expect(initializer).toHaveBeenCalledTimes(1);
|
|
267
|
-
expect(count$.
|
|
267
|
+
expect(count$.get()).toBe(42);
|
|
268
268
|
});
|
|
269
269
|
|
|
270
270
|
it("should only call initializer once", () => {
|
|
@@ -272,8 +272,8 @@ describe("atom", () => {
|
|
|
272
272
|
const obj$ = atom(initializer);
|
|
273
273
|
expect(initializer).toHaveBeenCalledTimes(1);
|
|
274
274
|
// Access value multiple times
|
|
275
|
-
obj$.
|
|
276
|
-
obj$.
|
|
275
|
+
obj$.get();
|
|
276
|
+
obj$.get();
|
|
277
277
|
expect(initializer).toHaveBeenCalledTimes(1);
|
|
278
278
|
});
|
|
279
279
|
|
|
@@ -284,33 +284,33 @@ describe("atom", () => {
|
|
|
284
284
|
return callCount; // Returns different value each call
|
|
285
285
|
};
|
|
286
286
|
const count$ = atom(initializer);
|
|
287
|
-
expect(count$.
|
|
287
|
+
expect(count$.get()).toBe(1); // First call returns 1
|
|
288
288
|
expect(callCount).toBe(1);
|
|
289
289
|
|
|
290
290
|
count$.set(100);
|
|
291
|
-
expect(count$.
|
|
291
|
+
expect(count$.get()).toBe(100);
|
|
292
292
|
|
|
293
293
|
count$.reset();
|
|
294
|
-
expect(count$.
|
|
294
|
+
expect(count$.get()).toBe(2); // Re-runs initializer, gets fresh value
|
|
295
295
|
expect(callCount).toBe(2); // Initializer called again
|
|
296
296
|
});
|
|
297
297
|
|
|
298
298
|
it("should work with lazy initializer returning object", () => {
|
|
299
299
|
const obj$ = atom(() => ({ count: 0, items: [] as number[] }));
|
|
300
|
-
expect(obj$.
|
|
300
|
+
expect(obj$.get()).toEqual({ count: 0, items: [] });
|
|
301
301
|
obj$.set((prev) => ({ ...prev, count: 1 }));
|
|
302
|
-
expect(obj$.
|
|
302
|
+
expect(obj$.get()).toEqual({ count: 1, items: [] });
|
|
303
303
|
});
|
|
304
304
|
|
|
305
305
|
it("should work with lazy initializer returning Promise", () => {
|
|
306
306
|
const promise = Promise.resolve(42);
|
|
307
307
|
const data$ = atom(() => promise);
|
|
308
|
-
expect(data$.
|
|
308
|
+
expect(data$.get()).toBe(promise);
|
|
309
309
|
});
|
|
310
310
|
|
|
311
311
|
it("should still work with direct value (non-function)", () => {
|
|
312
312
|
const count$ = atom(10);
|
|
313
|
-
expect(count$.
|
|
313
|
+
expect(count$.get()).toBe(10);
|
|
314
314
|
});
|
|
315
315
|
});
|
|
316
316
|
|
|
@@ -326,44 +326,308 @@ describe("atom", () => {
|
|
|
326
326
|
});
|
|
327
327
|
});
|
|
328
328
|
|
|
329
|
+
describe("AtomContext (init function with context)", () => {
|
|
330
|
+
describe("signal", () => {
|
|
331
|
+
it("should pass context with signal to init function", () => {
|
|
332
|
+
let receivedSignal: AbortSignal | undefined;
|
|
333
|
+
atom((ctx) => {
|
|
334
|
+
receivedSignal = ctx.signal;
|
|
335
|
+
return 42;
|
|
336
|
+
});
|
|
337
|
+
expect(receivedSignal).toBeInstanceOf(AbortSignal);
|
|
338
|
+
expect(receivedSignal!.aborted).toBe(false);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it("should abort signal when set() is called", () => {
|
|
342
|
+
let receivedSignal: AbortSignal | undefined;
|
|
343
|
+
const count$ = atom((ctx) => {
|
|
344
|
+
receivedSignal = ctx.signal;
|
|
345
|
+
return 0;
|
|
346
|
+
});
|
|
347
|
+
expect(receivedSignal!.aborted).toBe(false);
|
|
348
|
+
|
|
349
|
+
count$.set(1);
|
|
350
|
+
expect(receivedSignal!.aborted).toBe(true);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
it("should abort signal when reset() is called", () => {
|
|
354
|
+
const signals: AbortSignal[] = [];
|
|
355
|
+
const count$ = atom((ctx) => {
|
|
356
|
+
signals.push(ctx.signal);
|
|
357
|
+
return 0;
|
|
358
|
+
});
|
|
359
|
+
expect(signals[0].aborted).toBe(false);
|
|
360
|
+
|
|
361
|
+
count$.reset();
|
|
362
|
+
// Original signal should be aborted
|
|
363
|
+
expect(signals[0].aborted).toBe(true);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
it("should provide fresh signal on reset", () => {
|
|
367
|
+
const signals: AbortSignal[] = [];
|
|
368
|
+
const count$ = atom((ctx) => {
|
|
369
|
+
signals.push(ctx.signal);
|
|
370
|
+
return signals.length;
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(signals).toHaveLength(1);
|
|
374
|
+
expect(signals[0].aborted).toBe(false);
|
|
375
|
+
|
|
376
|
+
count$.reset();
|
|
377
|
+
expect(signals).toHaveLength(2);
|
|
378
|
+
expect(signals[0].aborted).toBe(true);
|
|
379
|
+
expect(signals[1].aborted).toBe(false);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it("should not create new signal on subsequent set() calls", () => {
|
|
383
|
+
let signalRef: AbortSignal | undefined;
|
|
384
|
+
const count$ = atom((ctx) => {
|
|
385
|
+
signalRef = ctx.signal;
|
|
386
|
+
return 0;
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const firstSignal = signalRef;
|
|
390
|
+
count$.set(1);
|
|
391
|
+
expect(firstSignal!.aborted).toBe(true);
|
|
392
|
+
|
|
393
|
+
// After set, the signal is already aborted, no new context is created
|
|
394
|
+
// because set() doesn't re-run the initializer
|
|
395
|
+
count$.set(2);
|
|
396
|
+
expect(firstSignal!.aborted).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it("should abort with reason", () => {
|
|
400
|
+
let receivedSignal: AbortSignal | undefined;
|
|
401
|
+
const count$ = atom((ctx) => {
|
|
402
|
+
receivedSignal = ctx.signal;
|
|
403
|
+
return 0;
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
count$.set(1);
|
|
407
|
+
expect(receivedSignal!.aborted).toBe(true);
|
|
408
|
+
expect(receivedSignal!.reason).toBe("value changed");
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
it("should abort with 'reset' reason on reset", () => {
|
|
412
|
+
const signals: AbortSignal[] = [];
|
|
413
|
+
const count$ = atom((ctx) => {
|
|
414
|
+
signals.push(ctx.signal);
|
|
415
|
+
return 0;
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
count$.reset();
|
|
419
|
+
// Original signal should be aborted with 'reset' reason
|
|
420
|
+
expect(signals[0].aborted).toBe(true);
|
|
421
|
+
expect(signals[0].reason).toBe("reset");
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
describe("onCleanup", () => {
|
|
426
|
+
it("should call cleanup when set() is called", () => {
|
|
427
|
+
const cleanup = vi.fn();
|
|
428
|
+
const count$ = atom((ctx) => {
|
|
429
|
+
ctx.onCleanup(cleanup);
|
|
430
|
+
return 0;
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
expect(cleanup).not.toHaveBeenCalled();
|
|
434
|
+
count$.set(1);
|
|
435
|
+
expect(cleanup).toHaveBeenCalledTimes(1);
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
it("should call cleanup when reset() is called", () => {
|
|
439
|
+
const cleanup = vi.fn();
|
|
440
|
+
const count$ = atom((ctx) => {
|
|
441
|
+
ctx.onCleanup(cleanup);
|
|
442
|
+
return 0;
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
expect(cleanup).not.toHaveBeenCalled();
|
|
446
|
+
count$.reset();
|
|
447
|
+
expect(cleanup).toHaveBeenCalledTimes(1);
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
it("should call multiple cleanups in FIFO order", () => {
|
|
451
|
+
const order: number[] = [];
|
|
452
|
+
const count$ = atom((ctx) => {
|
|
453
|
+
ctx.onCleanup(() => order.push(1));
|
|
454
|
+
ctx.onCleanup(() => order.push(2));
|
|
455
|
+
ctx.onCleanup(() => order.push(3));
|
|
456
|
+
return 0;
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
count$.set(1);
|
|
460
|
+
expect(order).toEqual([1, 2, 3]);
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
it("should clear cleanups after calling them", () => {
|
|
464
|
+
const cleanup = vi.fn();
|
|
465
|
+
const count$ = atom((ctx) => {
|
|
466
|
+
ctx.onCleanup(cleanup);
|
|
467
|
+
return 0;
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
count$.set(1);
|
|
471
|
+
expect(cleanup).toHaveBeenCalledTimes(1);
|
|
472
|
+
|
|
473
|
+
// Second set should not call cleanup again (cleanup was from init, not from set)
|
|
474
|
+
count$.set(2);
|
|
475
|
+
expect(cleanup).toHaveBeenCalledTimes(1);
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
it("should call cleanup from reset, then register new cleanups on next reset", () => {
|
|
479
|
+
let cleanupCallCount = 0;
|
|
480
|
+
const count$ = atom((ctx) => {
|
|
481
|
+
ctx.onCleanup(() => cleanupCallCount++);
|
|
482
|
+
return cleanupCallCount;
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
expect(cleanupCallCount).toBe(0);
|
|
486
|
+
|
|
487
|
+
count$.reset();
|
|
488
|
+
expect(cleanupCallCount).toBe(1); // First cleanup called
|
|
489
|
+
|
|
490
|
+
count$.reset();
|
|
491
|
+
expect(cleanupCallCount).toBe(2); // Second cleanup called (registered in previous reset)
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it("should work with async operations that check signal", async () => {
|
|
495
|
+
let cleanupCalled = false;
|
|
496
|
+
let operationAborted = false;
|
|
497
|
+
|
|
498
|
+
const data$ = atom((ctx) => {
|
|
499
|
+
ctx.onCleanup(() => {
|
|
500
|
+
cleanupCalled = true;
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
// Simulate an async operation that respects the signal
|
|
504
|
+
const timeoutId = setTimeout(() => {
|
|
505
|
+
if (!ctx.signal.aborted) {
|
|
506
|
+
// Would do something here
|
|
507
|
+
} else {
|
|
508
|
+
operationAborted = true;
|
|
509
|
+
}
|
|
510
|
+
}, 100);
|
|
511
|
+
|
|
512
|
+
ctx.onCleanup(() => clearTimeout(timeoutId));
|
|
513
|
+
|
|
514
|
+
return "initial";
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
// Trigger abort
|
|
518
|
+
data$.set("changed");
|
|
519
|
+
|
|
520
|
+
expect(cleanupCalled).toBe(true);
|
|
521
|
+
|
|
522
|
+
// Wait for the timeout to potentially fire
|
|
523
|
+
await new Promise((r) => setTimeout(r, 150));
|
|
524
|
+
|
|
525
|
+
// The operation should have been cleaned up/aborted
|
|
526
|
+
expect(operationAborted).toBe(false); // timeout was cleared by cleanup
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
describe("combined signal and cleanup", () => {
|
|
531
|
+
it("should abort signal and call cleanup on set", () => {
|
|
532
|
+
let signal: AbortSignal | undefined;
|
|
533
|
+
const cleanup = vi.fn();
|
|
534
|
+
|
|
535
|
+
const count$ = atom((ctx) => {
|
|
536
|
+
signal = ctx.signal;
|
|
537
|
+
ctx.onCleanup(cleanup);
|
|
538
|
+
return 0;
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
expect(signal!.aborted).toBe(false);
|
|
542
|
+
expect(cleanup).not.toHaveBeenCalled();
|
|
543
|
+
|
|
544
|
+
count$.set(1);
|
|
545
|
+
|
|
546
|
+
expect(signal!.aborted).toBe(true);
|
|
547
|
+
expect(cleanup).toHaveBeenCalledTimes(1);
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it("should abort signal and call cleanup on reset, then provide fresh context", () => {
|
|
551
|
+
const signals: AbortSignal[] = [];
|
|
552
|
+
const cleanups: number[] = [];
|
|
553
|
+
let callCount = 0;
|
|
554
|
+
|
|
555
|
+
const count$ = atom((ctx) => {
|
|
556
|
+
callCount++;
|
|
557
|
+
signals.push(ctx.signal);
|
|
558
|
+
ctx.onCleanup(() => cleanups.push(callCount));
|
|
559
|
+
return callCount;
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
expect(signals).toHaveLength(1);
|
|
563
|
+
expect(cleanups).toEqual([]);
|
|
564
|
+
|
|
565
|
+
count$.reset();
|
|
566
|
+
|
|
567
|
+
expect(signals).toHaveLength(2);
|
|
568
|
+
expect(signals[0].aborted).toBe(true);
|
|
569
|
+
expect(signals[1].aborted).toBe(false);
|
|
570
|
+
expect(cleanups).toEqual([1]); // cleanup from first init was called
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
describe("non-function initializer (no context)", () => {
|
|
575
|
+
it("should work normally with direct value", () => {
|
|
576
|
+
const count$ = atom(42);
|
|
577
|
+
expect(count$.get()).toBe(42);
|
|
578
|
+
|
|
579
|
+
count$.set(100);
|
|
580
|
+
expect(count$.get()).toBe(100);
|
|
581
|
+
|
|
582
|
+
count$.reset();
|
|
583
|
+
expect(count$.get()).toBe(42);
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
it("should not have cleanup issues with direct value", () => {
|
|
587
|
+
const count$ = atom(0);
|
|
588
|
+
// These should not throw
|
|
589
|
+
count$.set(1);
|
|
590
|
+
count$.set(2);
|
|
591
|
+
count$.reset();
|
|
592
|
+
count$.reset();
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
});
|
|
596
|
+
|
|
329
597
|
describe("plugin system (use)", () => {
|
|
330
598
|
it("should support .use() for extensions", () => {
|
|
331
599
|
// Note: Don't use ...source spread as it copies values, not getters
|
|
332
600
|
// Instead, access source properties through the reference
|
|
333
601
|
const base$ = atom(0);
|
|
334
602
|
const count$ = base$.use((source) => ({
|
|
335
|
-
get
|
|
336
|
-
return source.value;
|
|
337
|
-
},
|
|
603
|
+
get: source.get,
|
|
338
604
|
set: source.set,
|
|
339
605
|
reset: source.reset,
|
|
340
606
|
on: source.on,
|
|
341
607
|
increment: () => source.set((v) => v + 1),
|
|
342
608
|
}));
|
|
343
609
|
|
|
344
|
-
expect(count$.
|
|
610
|
+
expect(count$.get()).toBe(0);
|
|
345
611
|
count$.increment();
|
|
346
|
-
expect(count$.
|
|
612
|
+
expect(count$.get()).toBe(1);
|
|
347
613
|
});
|
|
348
614
|
|
|
349
615
|
it("should support .use() with source reference pattern", () => {
|
|
350
616
|
// Simpler pattern: just reference the original atom
|
|
351
617
|
const base$ = atom(0);
|
|
352
618
|
const enhanced$ = base$.use(() => ({
|
|
353
|
-
get
|
|
354
|
-
return base$.value;
|
|
355
|
-
},
|
|
619
|
+
get: () => base$.get(),
|
|
356
620
|
increment: () => base$.set((v) => v + 1),
|
|
357
621
|
decrement: () => base$.set((v) => v - 1),
|
|
358
622
|
}));
|
|
359
623
|
|
|
360
|
-
expect(enhanced$.
|
|
624
|
+
expect(enhanced$.get()).toBe(0);
|
|
361
625
|
enhanced$.increment();
|
|
362
|
-
expect(enhanced$.
|
|
626
|
+
expect(enhanced$.get()).toBe(1);
|
|
363
627
|
enhanced$.increment();
|
|
364
|
-
expect(enhanced$.
|
|
628
|
+
expect(enhanced$.get()).toBe(2);
|
|
365
629
|
enhanced$.decrement();
|
|
366
|
-
expect(enhanced$.
|
|
630
|
+
expect(enhanced$.get()).toBe(1);
|
|
367
631
|
});
|
|
368
632
|
});
|
|
369
633
|
});
|