rask-ui 0.21.0 → 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.
- package/dist/asyncState.d.ts +16 -0
- package/dist/asyncState.d.ts.map +1 -0
- package/dist/asyncState.js +24 -0
- package/dist/component.d.ts.map +1 -1
- package/dist/component.js +6 -4
- package/dist/context.d.ts +5 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +29 -0
- package/dist/createAsync.test.d.ts +2 -0
- package/dist/createAsync.test.d.ts.map +1 -0
- package/dist/createAsync.test.js +110 -0
- package/dist/createContext.d.ts +36 -20
- package/dist/createContext.d.ts.map +1 -1
- package/dist/createContext.js +58 -37
- package/dist/createMutation.test.d.ts +2 -0
- package/dist/createMutation.test.d.ts.map +1 -0
- package/dist/createMutation.test.js +168 -0
- package/dist/createQuery.test.d.ts +2 -0
- package/dist/createQuery.test.d.ts.map +1 -0
- package/dist/createQuery.test.js +156 -0
- package/dist/createRef.d.ts +6 -0
- package/dist/createRef.d.ts.map +1 -0
- package/dist/createRef.js +8 -0
- package/dist/createState.d.ts +0 -2
- package/dist/createState.d.ts.map +1 -1
- package/dist/createState.js +5 -40
- package/dist/createState.test.d.ts.map +1 -0
- package/dist/createState.test.js +111 -0
- package/dist/createView.d.ts +44 -18
- package/dist/createView.d.ts.map +1 -1
- package/dist/createView.js +48 -57
- package/dist/createView.test.d.ts.map +1 -0
- package/dist/{tests/createView.test.js → createView.test.js} +40 -40
- package/dist/error.d.ts +14 -3
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +15 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/jsx.d.ts +256 -10
- package/dist/observation.test.d.ts.map +1 -0
- package/dist/observation.test.js +150 -0
- package/dist/suspense.d.ts +25 -0
- package/dist/suspense.d.ts.map +1 -0
- package/dist/suspense.js +97 -0
- package/dist/test-setup.d.ts +16 -0
- package/dist/test-setup.d.ts.map +1 -0
- package/dist/test-setup.js +40 -0
- package/dist/test.d.ts +2 -0
- package/dist/test.d.ts.map +1 -0
- package/dist/test.js +24 -0
- package/dist/useCatchError.d.ts +6 -1
- package/dist/useCatchError.d.ts.map +1 -1
- package/dist/useCatchError.js +5 -4
- package/package.json +2 -2
- package/swc-plugin/target/wasm32-wasip1/release/swc_plugin_rask_component.wasm +0 -0
- package/dist/createComputed.d.ts +0 -4
- package/dist/createComputed.d.ts.map +0 -1
- package/dist/createComputed.js +0 -69
- package/dist/createEffect.d.ts +0 -2
- package/dist/createEffect.d.ts.map +0 -1
- package/dist/createEffect.js +0 -29
- package/dist/createRouter.d.ts +0 -8
- package/dist/createRouter.d.ts.map +0 -1
- package/dist/createRouter.js +0 -27
- package/dist/createTask.d.ts +0 -31
- package/dist/createTask.d.ts.map +0 -1
- package/dist/createTask.js +0 -79
- package/dist/patchInferno.d.ts +0 -6
- package/dist/patchInferno.d.ts.map +0 -1
- package/dist/patchInferno.js +0 -53
- package/dist/scheduler.d.ts +0 -4
- package/dist/scheduler.d.ts.map +0 -1
- package/dist/scheduler.js +0 -107
- package/dist/tests/batch.test.d.ts +0 -2
- package/dist/tests/batch.test.d.ts.map +0 -1
- package/dist/tests/batch.test.js +0 -244
- package/dist/tests/createComputed.test.d.ts +0 -2
- package/dist/tests/createComputed.test.d.ts.map +0 -1
- package/dist/tests/createComputed.test.js +0 -257
- package/dist/tests/createContext.test.d.ts +0 -2
- package/dist/tests/createContext.test.d.ts.map +0 -1
- package/dist/tests/createContext.test.js +0 -136
- package/dist/tests/createEffect.test.d.ts +0 -2
- package/dist/tests/createEffect.test.d.ts.map +0 -1
- package/dist/tests/createEffect.test.js +0 -467
- package/dist/tests/createState.test.d.ts.map +0 -1
- package/dist/tests/createState.test.js +0 -144
- package/dist/tests/createTask.test.d.ts +0 -2
- package/dist/tests/createTask.test.d.ts.map +0 -1
- package/dist/tests/createTask.test.js +0 -322
- package/dist/tests/createView.test.d.ts.map +0 -1
- package/dist/tests/error.test.d.ts +0 -2
- package/dist/tests/error.test.d.ts.map +0 -1
- package/dist/tests/error.test.js +0 -168
- package/dist/tests/observation.test.d.ts.map +0 -1
- package/dist/tests/observation.test.js +0 -341
- package/dist/useComputed.d.ts +0 -5
- package/dist/useComputed.d.ts.map +0 -1
- package/dist/useComputed.js +0 -69
- package/dist/useQuery.d.ts +0 -25
- package/dist/useQuery.d.ts.map +0 -1
- package/dist/useQuery.js +0 -25
- package/dist/useSuspendAsync.d.ts +0 -18
- package/dist/useSuspendAsync.d.ts.map +0 -1
- package/dist/useSuspendAsync.js +0 -37
- package/dist/useTask.d.ts +0 -25
- package/dist/useTask.d.ts.map +0 -1
- package/dist/useTask.js +0 -70
- /package/dist/{tests/createState.test.d.ts → createState.test.d.ts} +0 -0
- /package/dist/{tests/createView.test.d.ts → createView.test.d.ts} +0 -0
- /package/dist/{tests/observation.test.d.ts → observation.test.d.ts} +0 -0
package/dist/createTask.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { createCleanup, getCurrentComponent } from "./component";
|
|
2
|
-
import { assignState, createState } from "./createState";
|
|
3
|
-
export function createTask(task) {
|
|
4
|
-
const currentComponent = getCurrentComponent();
|
|
5
|
-
if (!currentComponent || currentComponent.isRendering) {
|
|
6
|
-
throw new Error("Only use createTask in component setup");
|
|
7
|
-
}
|
|
8
|
-
const state = createState({
|
|
9
|
-
isRunning: false,
|
|
10
|
-
result: null,
|
|
11
|
-
error: null,
|
|
12
|
-
params: null,
|
|
13
|
-
});
|
|
14
|
-
let currentAbortController;
|
|
15
|
-
const fetch = (params) => {
|
|
16
|
-
currentAbortController?.abort();
|
|
17
|
-
const abortController = (currentAbortController = new AbortController());
|
|
18
|
-
const promise = task(params, abortController.signal);
|
|
19
|
-
promise
|
|
20
|
-
.then((result) => {
|
|
21
|
-
if (abortController.signal.aborted) {
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
assignState(state, {
|
|
25
|
-
isRunning: false,
|
|
26
|
-
result,
|
|
27
|
-
error: null,
|
|
28
|
-
params: null,
|
|
29
|
-
});
|
|
30
|
-
})
|
|
31
|
-
.catch((error) => {
|
|
32
|
-
if (abortController.signal.aborted) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
assignState(state, {
|
|
36
|
-
isRunning: false,
|
|
37
|
-
result: null,
|
|
38
|
-
error: String(error),
|
|
39
|
-
params: null,
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
return promise;
|
|
43
|
-
};
|
|
44
|
-
createCleanup(() => currentAbortController?.abort());
|
|
45
|
-
return {
|
|
46
|
-
get isRunning() {
|
|
47
|
-
return state.isRunning;
|
|
48
|
-
},
|
|
49
|
-
get result() {
|
|
50
|
-
return state.result;
|
|
51
|
-
},
|
|
52
|
-
get error() {
|
|
53
|
-
return state.error;
|
|
54
|
-
},
|
|
55
|
-
get params() {
|
|
56
|
-
return state.params;
|
|
57
|
-
},
|
|
58
|
-
run(params) {
|
|
59
|
-
const promise = fetch(params);
|
|
60
|
-
assignState(state, {
|
|
61
|
-
isRunning: true,
|
|
62
|
-
result: null,
|
|
63
|
-
error: null,
|
|
64
|
-
params: (params || null),
|
|
65
|
-
});
|
|
66
|
-
return promise;
|
|
67
|
-
},
|
|
68
|
-
rerun(params) {
|
|
69
|
-
const promise = fetch(params);
|
|
70
|
-
assignState(state, {
|
|
71
|
-
isRunning: true,
|
|
72
|
-
result: state.result,
|
|
73
|
-
error: null,
|
|
74
|
-
params: (params || null),
|
|
75
|
-
});
|
|
76
|
-
return promise;
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
}
|
package/dist/patchInferno.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Temporarily patches document.addEventListener during render to capture
|
|
3
|
-
* and wrap Inferno's delegated event listeners with syncBatch
|
|
4
|
-
*/
|
|
5
|
-
export declare function patchInfernoEventHandling(renderFn: () => void): void;
|
|
6
|
-
//# sourceMappingURL=patchInferno.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"patchInferno.d.ts","sourceRoot":"","sources":["../src/patchInferno.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,IAAI,QA0D7D"}
|
package/dist/patchInferno.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { syncBatch } from "./batch";
|
|
2
|
-
/**
|
|
3
|
-
* Temporarily patches document.addEventListener during render to capture
|
|
4
|
-
* and wrap Inferno's delegated event listeners with syncBatch
|
|
5
|
-
*/
|
|
6
|
-
export function patchInfernoEventHandling(renderFn) {
|
|
7
|
-
const originalAddEventListener = document.addEventListener.bind(document);
|
|
8
|
-
const patchedEvents = new Set();
|
|
9
|
-
// Inferno's delegated events
|
|
10
|
-
const INFERNO_EVENTS = [
|
|
11
|
-
"click",
|
|
12
|
-
"dblclick",
|
|
13
|
-
"focusin",
|
|
14
|
-
"focusout",
|
|
15
|
-
"keydown",
|
|
16
|
-
"keypress",
|
|
17
|
-
"keyup",
|
|
18
|
-
"mousedown",
|
|
19
|
-
"mousemove",
|
|
20
|
-
"mouseup",
|
|
21
|
-
"touchend",
|
|
22
|
-
"touchmove",
|
|
23
|
-
"touchstart",
|
|
24
|
-
"change",
|
|
25
|
-
"input",
|
|
26
|
-
"submit",
|
|
27
|
-
];
|
|
28
|
-
// Temporarily replace addEventListener
|
|
29
|
-
document.addEventListener = function (type, listener, options) {
|
|
30
|
-
// Only wrap Inferno's delegated event listeners
|
|
31
|
-
if (INFERNO_EVENTS.includes(type) &&
|
|
32
|
-
typeof listener === "function" &&
|
|
33
|
-
!patchedEvents.has(type)) {
|
|
34
|
-
patchedEvents.add(type);
|
|
35
|
-
const wrappedListener = function (event) {
|
|
36
|
-
syncBatch(() => {
|
|
37
|
-
listener.call(this, event);
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
return originalAddEventListener(type, wrappedListener, options);
|
|
41
|
-
}
|
|
42
|
-
// @ts-ignore
|
|
43
|
-
return originalAddEventListener(type, listener, options);
|
|
44
|
-
};
|
|
45
|
-
try {
|
|
46
|
-
// Call render - Inferno will synchronously attach its listeners
|
|
47
|
-
renderFn();
|
|
48
|
-
}
|
|
49
|
-
finally {
|
|
50
|
-
// Restore original addEventListener
|
|
51
|
-
document.addEventListener = originalAddEventListener;
|
|
52
|
-
}
|
|
53
|
-
}
|
package/dist/scheduler.d.ts
DELETED
package/dist/scheduler.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../src/scheduler.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,SAExB;AAeD,wBAAgB,uBAAuB,SAWtC;AAmED,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,WAAoB,cAoBjE"}
|
package/dist/scheduler.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
let depth = 0; // batching scope nesting
|
|
2
|
-
let dirty = false; // has any state update been enqueued?
|
|
3
|
-
let scheduled = false; // async flush scheduled?
|
|
4
|
-
export function markDirty() {
|
|
5
|
-
dirty = true;
|
|
6
|
-
}
|
|
7
|
-
function performWork() {
|
|
8
|
-
// TODO: call your Inferno render/commit once.
|
|
9
|
-
// infernoRender(vnode, container);
|
|
10
|
-
}
|
|
11
|
-
function flushNow() {
|
|
12
|
-
scheduled = false;
|
|
13
|
-
if (!dirty)
|
|
14
|
-
return;
|
|
15
|
-
dirty = false;
|
|
16
|
-
performWork();
|
|
17
|
-
}
|
|
18
|
-
// Called by setters after enqueueing their state change
|
|
19
|
-
export function enqueueUpdateFromSetter() {
|
|
20
|
-
dirty = true;
|
|
21
|
-
if (depth > 0) {
|
|
22
|
-
// We're inside a batched input event; we'll flush on exit (same frame).
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
if (!scheduled) {
|
|
26
|
-
scheduled = true;
|
|
27
|
-
queueMicrotask(flushNow); // one flush per task
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
// Batch-scope control used by the global capture listeners
|
|
31
|
-
function enter() {
|
|
32
|
-
depth++;
|
|
33
|
-
}
|
|
34
|
-
function exit() {
|
|
35
|
-
if (--depth === 0) {
|
|
36
|
-
// End of the event propagation; commit now (before next paint).
|
|
37
|
-
flushNow();
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
// eventBatching.ts
|
|
41
|
-
const INTERACTIVE_EVENTS = [
|
|
42
|
-
// Pointer + mouse
|
|
43
|
-
"click",
|
|
44
|
-
"dblclick",
|
|
45
|
-
"contextmenu",
|
|
46
|
-
"mousedown",
|
|
47
|
-
"mouseup",
|
|
48
|
-
"mousemove",
|
|
49
|
-
"pointerdown",
|
|
50
|
-
"pointerup",
|
|
51
|
-
"pointermove",
|
|
52
|
-
"touchstart",
|
|
53
|
-
"touchmove",
|
|
54
|
-
"touchend",
|
|
55
|
-
"touchcancel",
|
|
56
|
-
"dragstart",
|
|
57
|
-
"drag",
|
|
58
|
-
"dragend",
|
|
59
|
-
"dragenter",
|
|
60
|
-
"dragleave",
|
|
61
|
-
"dragover",
|
|
62
|
-
"drop",
|
|
63
|
-
"wheel",
|
|
64
|
-
// Keyboard
|
|
65
|
-
"keydown",
|
|
66
|
-
"keypress",
|
|
67
|
-
"keyup",
|
|
68
|
-
// Focus & input
|
|
69
|
-
"focus",
|
|
70
|
-
"blur",
|
|
71
|
-
"focusin",
|
|
72
|
-
"focusout",
|
|
73
|
-
"input",
|
|
74
|
-
"beforeinput",
|
|
75
|
-
"change",
|
|
76
|
-
"compositionstart",
|
|
77
|
-
"compositionupdate",
|
|
78
|
-
"compositionend",
|
|
79
|
-
// Forms
|
|
80
|
-
"submit",
|
|
81
|
-
"reset",
|
|
82
|
-
// Selection / clipboard
|
|
83
|
-
"select",
|
|
84
|
-
"selectionchange",
|
|
85
|
-
"copy",
|
|
86
|
-
"cut",
|
|
87
|
-
"paste",
|
|
88
|
-
];
|
|
89
|
-
export function installGlobalBatching(target = window) {
|
|
90
|
-
const handlers = [];
|
|
91
|
-
INTERACTIVE_EVENTS.forEach((type) => {
|
|
92
|
-
const onCapture = () => {
|
|
93
|
-
enter();
|
|
94
|
-
// Close the scope after all handlers (capture→target→bubble) have run.
|
|
95
|
-
queueMicrotask(exit);
|
|
96
|
-
};
|
|
97
|
-
target.addEventListener(type, onCapture, { capture: true });
|
|
98
|
-
handlers.push([onCapture, { capture: true }]);
|
|
99
|
-
});
|
|
100
|
-
// Return a disposer so you can remove on unmount
|
|
101
|
-
return () => {
|
|
102
|
-
INTERACTIVE_EVENTS.forEach((type, i) => {
|
|
103
|
-
const [fn, opts] = handlers[i];
|
|
104
|
-
target.removeEventListener(type, fn, opts);
|
|
105
|
-
});
|
|
106
|
-
};
|
|
107
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"batch.test.d.ts","sourceRoot":"","sources":["../../src/tests/batch.test.ts"],"names":[],"mappings":""}
|
package/dist/tests/batch.test.js
DELETED
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { syncBatch } from "../batch";
|
|
3
|
-
import { createState } from "../createState";
|
|
4
|
-
import { Observer } from "../observation";
|
|
5
|
-
describe("syncBatch", () => {
|
|
6
|
-
it("should batch multiple state changes into a single notification", () => {
|
|
7
|
-
const state = createState({ count: 0, name: "Alice" });
|
|
8
|
-
let notifyCount = 0;
|
|
9
|
-
const observer = new Observer(() => {
|
|
10
|
-
notifyCount++;
|
|
11
|
-
});
|
|
12
|
-
const dispose = observer.observe();
|
|
13
|
-
state.count; // Track count
|
|
14
|
-
state.name; // Track name
|
|
15
|
-
dispose();
|
|
16
|
-
// Make multiple changes in a batch
|
|
17
|
-
syncBatch(() => {
|
|
18
|
-
state.count = 1;
|
|
19
|
-
state.name = "Bob";
|
|
20
|
-
state.count = 2;
|
|
21
|
-
});
|
|
22
|
-
// Should only notify once despite multiple changes, and synchronously
|
|
23
|
-
expect(notifyCount).toBe(1);
|
|
24
|
-
expect(state.count).toBe(2);
|
|
25
|
-
expect(state.name).toBe("Bob");
|
|
26
|
-
observer.dispose();
|
|
27
|
-
});
|
|
28
|
-
it("should handle nested batches correctly", () => {
|
|
29
|
-
const state = createState({ count: 0 });
|
|
30
|
-
let notifyCount = 0;
|
|
31
|
-
const observer = new Observer(() => {
|
|
32
|
-
notifyCount++;
|
|
33
|
-
});
|
|
34
|
-
const dispose = observer.observe();
|
|
35
|
-
state.count; // Track
|
|
36
|
-
dispose();
|
|
37
|
-
syncBatch(() => {
|
|
38
|
-
state.count = 1;
|
|
39
|
-
syncBatch(() => {
|
|
40
|
-
state.count = 2;
|
|
41
|
-
});
|
|
42
|
-
state.count = 3;
|
|
43
|
-
});
|
|
44
|
-
// Should still only notify once for nested batches
|
|
45
|
-
expect(notifyCount).toBe(1);
|
|
46
|
-
expect(state.count).toBe(3);
|
|
47
|
-
observer.dispose();
|
|
48
|
-
});
|
|
49
|
-
it("should handle multiple observers with syncBatch", () => {
|
|
50
|
-
const state = createState({ count: 0 });
|
|
51
|
-
let notifyCount1 = 0;
|
|
52
|
-
let notifyCount2 = 0;
|
|
53
|
-
const observer1 = new Observer(() => {
|
|
54
|
-
notifyCount1++;
|
|
55
|
-
});
|
|
56
|
-
const observer2 = new Observer(() => {
|
|
57
|
-
notifyCount2++;
|
|
58
|
-
});
|
|
59
|
-
const dispose1 = observer1.observe();
|
|
60
|
-
state.count; // Track in observer1
|
|
61
|
-
dispose1();
|
|
62
|
-
const dispose2 = observer2.observe();
|
|
63
|
-
state.count; // Track in observer2
|
|
64
|
-
dispose2();
|
|
65
|
-
syncBatch(() => {
|
|
66
|
-
state.count = 1;
|
|
67
|
-
state.count = 2;
|
|
68
|
-
state.count = 3;
|
|
69
|
-
});
|
|
70
|
-
// Both observers should be notified exactly once
|
|
71
|
-
expect(notifyCount1).toBe(1);
|
|
72
|
-
expect(notifyCount2).toBe(1);
|
|
73
|
-
observer1.dispose();
|
|
74
|
-
observer2.dispose();
|
|
75
|
-
});
|
|
76
|
-
it("should maintain correct state values after syncBatch", () => {
|
|
77
|
-
const state = createState({
|
|
78
|
-
count: 0,
|
|
79
|
-
name: "Alice",
|
|
80
|
-
items: [1, 2, 3],
|
|
81
|
-
});
|
|
82
|
-
syncBatch(() => {
|
|
83
|
-
state.count = 10;
|
|
84
|
-
state.name = "Bob";
|
|
85
|
-
state.items.push(4);
|
|
86
|
-
state.items[0] = 100;
|
|
87
|
-
});
|
|
88
|
-
expect(state.count).toBe(10);
|
|
89
|
-
expect(state.name).toBe("Bob");
|
|
90
|
-
expect(state.items).toEqual([100, 2, 3, 4]);
|
|
91
|
-
});
|
|
92
|
-
it("should not flush if exception thrown within syncBatch", () => {
|
|
93
|
-
const state = createState({ count: 0 });
|
|
94
|
-
let notifyCount = 0;
|
|
95
|
-
const observer = new Observer(() => {
|
|
96
|
-
notifyCount++;
|
|
97
|
-
});
|
|
98
|
-
const dispose = observer.observe();
|
|
99
|
-
state.count; // Track
|
|
100
|
-
dispose();
|
|
101
|
-
try {
|
|
102
|
-
syncBatch(() => {
|
|
103
|
-
state.count = 1;
|
|
104
|
-
throw new Error("Test error");
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
catch (e) {
|
|
108
|
-
// Expected error
|
|
109
|
-
}
|
|
110
|
-
// Should NOT have flushed since the batch was interrupted
|
|
111
|
-
expect(notifyCount).toBe(0);
|
|
112
|
-
// But state change still occurred
|
|
113
|
-
expect(state.count).toBe(1);
|
|
114
|
-
observer.dispose();
|
|
115
|
-
});
|
|
116
|
-
it("should deduplicate notifications for the same observer", () => {
|
|
117
|
-
const state = createState({ count: 0, name: "Alice" });
|
|
118
|
-
let notifyCount = 0;
|
|
119
|
-
const observer = new Observer(() => {
|
|
120
|
-
notifyCount++;
|
|
121
|
-
});
|
|
122
|
-
const dispose = observer.observe();
|
|
123
|
-
state.count; // Track
|
|
124
|
-
state.name; // Track
|
|
125
|
-
dispose();
|
|
126
|
-
syncBatch(() => {
|
|
127
|
-
state.count = 1; // Triggers observer
|
|
128
|
-
state.name = "Bob"; // Triggers same observer again
|
|
129
|
-
state.count = 2; // Triggers observer yet again
|
|
130
|
-
});
|
|
131
|
-
// Should deduplicate and only notify once
|
|
132
|
-
expect(notifyCount).toBe(1);
|
|
133
|
-
observer.dispose();
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
describe("queue (async batching)", () => {
|
|
137
|
-
it("should queue updates and flush on microtask", async () => {
|
|
138
|
-
const state = createState({ count: 0 });
|
|
139
|
-
let notifyCount = 0;
|
|
140
|
-
const observer = new Observer(() => {
|
|
141
|
-
notifyCount++;
|
|
142
|
-
});
|
|
143
|
-
const dispose = observer.observe();
|
|
144
|
-
state.count; // Track
|
|
145
|
-
dispose();
|
|
146
|
-
// Make changes that will be queued
|
|
147
|
-
state.count = 1;
|
|
148
|
-
state.count = 2;
|
|
149
|
-
state.count = 3;
|
|
150
|
-
// Not yet notified (queued)
|
|
151
|
-
expect(notifyCount).toBe(0);
|
|
152
|
-
// Wait for microtask flush
|
|
153
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
154
|
-
// Should have notified once after flush
|
|
155
|
-
expect(notifyCount).toBe(1);
|
|
156
|
-
expect(state.count).toBe(3);
|
|
157
|
-
observer.dispose();
|
|
158
|
-
});
|
|
159
|
-
it("should batch multiple async updates into one notification", async () => {
|
|
160
|
-
const state = createState({ count: 0, name: "Alice" });
|
|
161
|
-
let notifyCount = 0;
|
|
162
|
-
const observer = new Observer(() => {
|
|
163
|
-
notifyCount++;
|
|
164
|
-
});
|
|
165
|
-
const dispose = observer.observe();
|
|
166
|
-
state.count;
|
|
167
|
-
state.name;
|
|
168
|
-
dispose();
|
|
169
|
-
state.count = 1;
|
|
170
|
-
state.name = "Bob";
|
|
171
|
-
state.count = 2;
|
|
172
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
173
|
-
// Should batch all updates into single notification
|
|
174
|
-
expect(notifyCount).toBe(1);
|
|
175
|
-
observer.dispose();
|
|
176
|
-
});
|
|
177
|
-
it("should handle separate async batches", async () => {
|
|
178
|
-
const state = createState({ count: 0 });
|
|
179
|
-
let notifyCount = 0;
|
|
180
|
-
const observer = new Observer(() => {
|
|
181
|
-
notifyCount++;
|
|
182
|
-
});
|
|
183
|
-
const dispose = observer.observe();
|
|
184
|
-
state.count;
|
|
185
|
-
dispose();
|
|
186
|
-
state.count = 1;
|
|
187
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
188
|
-
const afterFirst = notifyCount;
|
|
189
|
-
state.count = 2;
|
|
190
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
191
|
-
const afterSecond = notifyCount;
|
|
192
|
-
expect(afterFirst).toBe(1);
|
|
193
|
-
expect(afterSecond).toBe(2);
|
|
194
|
-
observer.dispose();
|
|
195
|
-
});
|
|
196
|
-
});
|
|
197
|
-
describe("syncBatch with nested async updates", () => {
|
|
198
|
-
it("should handle syncBatch inside async context", async () => {
|
|
199
|
-
const state = createState({ count: 0 });
|
|
200
|
-
let notifyCount = 0;
|
|
201
|
-
const observer = new Observer(() => {
|
|
202
|
-
notifyCount++;
|
|
203
|
-
});
|
|
204
|
-
const dispose = observer.observe();
|
|
205
|
-
state.count;
|
|
206
|
-
dispose();
|
|
207
|
-
// Async update
|
|
208
|
-
state.count = 1;
|
|
209
|
-
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
210
|
-
expect(notifyCount).toBe(1);
|
|
211
|
-
// Sync batch after async
|
|
212
|
-
syncBatch(() => {
|
|
213
|
-
state.count = 2;
|
|
214
|
-
state.count = 3;
|
|
215
|
-
});
|
|
216
|
-
expect(notifyCount).toBe(2); // +1 from sync batch
|
|
217
|
-
observer.dispose();
|
|
218
|
-
});
|
|
219
|
-
it("should handle async updates inside syncBatch callback", async () => {
|
|
220
|
-
const state = createState({ count: 0 });
|
|
221
|
-
let notifyCount = 0;
|
|
222
|
-
const observer = new Observer(() => {
|
|
223
|
-
notifyCount++;
|
|
224
|
-
});
|
|
225
|
-
const dispose = observer.observe();
|
|
226
|
-
state.count;
|
|
227
|
-
dispose();
|
|
228
|
-
syncBatch(() => {
|
|
229
|
-
state.count = 1;
|
|
230
|
-
// Trigger an async update from within syncBatch
|
|
231
|
-
setTimeout(() => {
|
|
232
|
-
state.count = 2;
|
|
233
|
-
}, 0);
|
|
234
|
-
});
|
|
235
|
-
// Sync batch should flush immediately
|
|
236
|
-
expect(notifyCount).toBe(1);
|
|
237
|
-
expect(state.count).toBe(1);
|
|
238
|
-
// Wait for async update
|
|
239
|
-
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
240
|
-
expect(notifyCount).toBe(2);
|
|
241
|
-
expect(state.count).toBe(2);
|
|
242
|
-
observer.dispose();
|
|
243
|
-
});
|
|
244
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createComputed.test.d.ts","sourceRoot":"","sources":["../../src/tests/createComputed.test.ts"],"names":[],"mappings":""}
|