@praxisjs/core 2.1.0 → 2.3.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/CHANGELOG.md +30 -0
- package/dist/component/internals.d.ts +12 -2
- package/dist/component/internals.d.ts.map +1 -1
- package/dist/component/internals.js +25 -7
- package/dist/component/internals.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -2
- package/dist/internal.js.map +1 -1
- package/dist/reactive/history.d.ts +2 -0
- package/dist/reactive/history.d.ts.map +1 -1
- package/dist/reactive/history.js +2 -1
- package/dist/reactive/history.js.map +1 -1
- package/dist/signal/batch.d.ts +1 -3
- package/dist/signal/batch.d.ts.map +1 -1
- package/dist/signal/batch.js +4 -16
- package/dist/signal/batch.js.map +1 -1
- package/dist/signal/computed.d.ts.map +1 -1
- package/dist/signal/computed.js +19 -61
- package/dist/signal/computed.js.map +1 -1
- package/dist/signal/effect.d.ts +6 -12
- package/dist/signal/effect.d.ts.map +1 -1
- package/dist/signal/effect.js +11 -74
- package/dist/signal/effect.js.map +1 -1
- package/dist/signal/graph.d.ts +111 -0
- package/dist/signal/graph.d.ts.map +1 -0
- package/dist/signal/graph.js +513 -0
- package/dist/signal/graph.js.map +1 -0
- package/dist/signal/index.d.ts +2 -1
- package/dist/signal/index.d.ts.map +1 -1
- package/dist/signal/index.js +1 -0
- package/dist/signal/index.js.map +1 -1
- package/dist/signal/peek.d.ts.map +1 -1
- package/dist/signal/peek.js +2 -9
- package/dist/signal/peek.js.map +1 -1
- package/dist/signal/persisted.d.ts +11 -1
- package/dist/signal/persisted.d.ts.map +1 -1
- package/dist/signal/persisted.js +39 -5
- package/dist/signal/persisted.js.map +1 -1
- package/dist/signal/selector.d.ts +5 -0
- package/dist/signal/selector.d.ts.map +1 -0
- package/dist/signal/selector.js +128 -0
- package/dist/signal/selector.js.map +1 -0
- package/dist/signal/signal.d.ts +0 -5
- package/dist/signal/signal.d.ts.map +1 -1
- package/dist/signal/signal.js +11 -81
- package/dist/signal/signal.js.map +1 -1
- package/package.json +13 -4
- package/src/component/internals.ts +35 -10
- package/src/internal.ts +27 -0
- package/src/reactive/history.ts +4 -1
- package/src/signal/batch.ts +5 -19
- package/src/signal/computed.ts +20 -63
- package/src/signal/effect.ts +12 -83
- package/src/signal/graph.ts +562 -0
- package/src/signal/index.ts +2 -1
- package/src/signal/peek.ts +2 -8
- package/src/signal/persisted.ts +54 -5
- package/src/signal/selector.ts +140 -0
- package/src/signal/signal.ts +11 -79
- 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 -73
- package/dist/__tests__/batch.test.js.map +0 -1
- package/dist/__tests__/component.test.d.ts +0 -2
- package/dist/__tests__/component.test.d.ts.map +0 -1
- package/dist/__tests__/component.test.js +0 -124
- package/dist/__tests__/component.test.js.map +0 -1
- package/dist/__tests__/computed.test.d.ts +0 -2
- package/dist/__tests__/computed.test.d.ts.map +0 -1
- package/dist/__tests__/computed.test.js +0 -252
- package/dist/__tests__/computed.test.js.map +0 -1
- package/dist/__tests__/effect.test.d.ts +0 -2
- package/dist/__tests__/effect.test.d.ts.map +0 -1
- package/dist/__tests__/effect.test.js +0 -189
- package/dist/__tests__/effect.test.js.map +0 -1
- package/dist/__tests__/peek.test.d.ts +0 -2
- package/dist/__tests__/peek.test.d.ts.map +0 -1
- package/dist/__tests__/peek.test.js +0 -26
- package/dist/__tests__/peek.test.js.map +0 -1
- package/dist/__tests__/persisted-signal.test.d.ts +0 -2
- package/dist/__tests__/persisted-signal.test.d.ts.map +0 -1
- package/dist/__tests__/persisted-signal.test.js +0 -156
- package/dist/__tests__/persisted-signal.test.js.map +0 -1
- package/dist/__tests__/reactive.test.d.ts +0 -2
- package/dist/__tests__/reactive.test.d.ts.map +0 -1
- package/dist/__tests__/reactive.test.js +0 -264
- package/dist/__tests__/reactive.test.js.map +0 -1
- package/dist/__tests__/resource-focus.test.d.ts +0 -2
- package/dist/__tests__/resource-focus.test.d.ts.map +0 -1
- package/dist/__tests__/resource-focus.test.js +0 -50
- package/dist/__tests__/resource-focus.test.js.map +0 -1
- package/dist/__tests__/resource-server-pass.test.d.ts +0 -2
- package/dist/__tests__/resource-server-pass.test.d.ts.map +0 -1
- package/dist/__tests__/resource-server-pass.test.js +0 -78
- package/dist/__tests__/resource-server-pass.test.js.map +0 -1
- package/dist/__tests__/resource.test.d.ts +0 -2
- package/dist/__tests__/resource.test.d.ts.map +0 -1
- package/dist/__tests__/resource.test.js +0 -336
- package/dist/__tests__/resource.test.js.map +0 -1
- package/dist/__tests__/signal.test.d.ts +0 -2
- package/dist/__tests__/signal.test.d.ts.map +0 -1
- package/dist/__tests__/signal.test.js +0 -186
- package/dist/__tests__/signal.test.js.map +0 -1
- package/dist/__tests__/synced.test.d.ts +0 -2
- package/dist/__tests__/synced.test.d.ts.map +0 -1
- package/dist/__tests__/synced.test.js +0 -142
- package/dist/__tests__/synced.test.js.map +0 -1
- package/dist/__tests__/track.test.d.ts +0 -2
- package/dist/__tests__/track.test.d.ts.map +0 -1
- package/dist/__tests__/track.test.js +0 -54
- package/dist/__tests__/track.test.js.map +0 -1
- package/src/__tests__/batch.test.ts +0 -87
- package/src/__tests__/component.test.ts +0 -165
- package/src/__tests__/computed.test.ts +0 -290
- package/src/__tests__/effect.test.ts +0 -226
- package/src/__tests__/peek.test.ts +0 -31
- package/src/__tests__/persisted-signal.test.ts +0 -194
- package/src/__tests__/reactive.test.ts +0 -305
- package/src/__tests__/resource-focus.test.ts +0 -61
- package/src/__tests__/resource-server-pass.test.ts +0 -90
- package/src/__tests__/resource.test.ts +0 -421
- package/src/__tests__/signal.test.ts +0 -213
- package/src/__tests__/synced.test.ts +0 -173
- package/src/__tests__/track.test.ts +0 -61
- package/tsconfig.json +0 -8
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { syncedSignal } from "../signal/synced";
|
|
3
|
-
import { effect } from "../signal/effect";
|
|
4
|
-
// BroadcastChannel mock
|
|
5
|
-
class MockBroadcastChannel {
|
|
6
|
-
static instances = [];
|
|
7
|
-
static byChannel = new Map();
|
|
8
|
-
onmessage = null;
|
|
9
|
-
name;
|
|
10
|
-
closed = false;
|
|
11
|
-
constructor(name) {
|
|
12
|
-
this.name = name;
|
|
13
|
-
MockBroadcastChannel.instances.push(this);
|
|
14
|
-
if (!MockBroadcastChannel.byChannel.has(name)) {
|
|
15
|
-
MockBroadcastChannel.byChannel.set(name, []);
|
|
16
|
-
}
|
|
17
|
-
MockBroadcastChannel.byChannel.get(name).push(this);
|
|
18
|
-
}
|
|
19
|
-
postMessage(data) {
|
|
20
|
-
// Deliver to all OTHER instances on same channel
|
|
21
|
-
MockBroadcastChannel.byChannel.get(this.name)?.forEach((ch) => {
|
|
22
|
-
if (ch !== this && ch.onmessage) {
|
|
23
|
-
ch.onmessage(new MessageEvent("message", { data }));
|
|
24
|
-
}
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
close() {
|
|
28
|
-
this.closed = true;
|
|
29
|
-
const list = MockBroadcastChannel.byChannel.get(this.name);
|
|
30
|
-
if (list) {
|
|
31
|
-
const idx = list.indexOf(this);
|
|
32
|
-
if (idx !== -1)
|
|
33
|
-
list.splice(idx, 1);
|
|
34
|
-
}
|
|
35
|
-
MockBroadcastChannel.instances.splice(MockBroadcastChannel.instances.indexOf(this), 1);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
beforeEach(() => {
|
|
39
|
-
MockBroadcastChannel.instances = [];
|
|
40
|
-
MockBroadcastChannel.byChannel = new Map();
|
|
41
|
-
vi.stubGlobal("BroadcastChannel", MockBroadcastChannel);
|
|
42
|
-
});
|
|
43
|
-
describe("syncedSignal", () => {
|
|
44
|
-
it("reads initial value", () => {
|
|
45
|
-
const s = syncedSignal("ch", 42);
|
|
46
|
-
expect(s()).toBe(42);
|
|
47
|
-
s.close();
|
|
48
|
-
});
|
|
49
|
-
it("set() updates the signal value", () => {
|
|
50
|
-
const s = syncedSignal("ch", 0);
|
|
51
|
-
s.set(10);
|
|
52
|
-
expect(s()).toBe(10);
|
|
53
|
-
s.close();
|
|
54
|
-
});
|
|
55
|
-
it("update() applies a transform", () => {
|
|
56
|
-
const s = syncedSignal("ch", 5);
|
|
57
|
-
s.update((v) => v * 2);
|
|
58
|
-
expect(s()).toBe(10);
|
|
59
|
-
s.close();
|
|
60
|
-
});
|
|
61
|
-
it("set() posts a JSON message to the channel", () => {
|
|
62
|
-
const s = syncedSignal("ch", 0);
|
|
63
|
-
const postSpy = vi.spyOn(MockBroadcastChannel.instances[0], "postMessage");
|
|
64
|
-
s.set(99);
|
|
65
|
-
expect(postSpy).toHaveBeenCalledWith(JSON.stringify(99));
|
|
66
|
-
s.close();
|
|
67
|
-
});
|
|
68
|
-
it("receiving a message from another tab updates the signal", () => {
|
|
69
|
-
const s = syncedSignal("ch", 0);
|
|
70
|
-
const channel = MockBroadcastChannel.instances[0];
|
|
71
|
-
channel.onmessage(new MessageEvent("message", { data: JSON.stringify(7) }));
|
|
72
|
-
expect(s()).toBe(7);
|
|
73
|
-
s.close();
|
|
74
|
-
});
|
|
75
|
-
it("does not echo own set() back to itself", () => {
|
|
76
|
-
const s = syncedSignal("ch", 0);
|
|
77
|
-
// Simulate two instances on the same channel
|
|
78
|
-
const s2 = syncedSignal("ch", 0);
|
|
79
|
-
const received = [];
|
|
80
|
-
effect(() => { received.push(s2()); });
|
|
81
|
-
s.set(5);
|
|
82
|
-
// s2 should receive the message because it came from a different instance
|
|
83
|
-
expect(s2()).toBe(5);
|
|
84
|
-
// s itself should NOT trigger its own onmessage handler (no echo)
|
|
85
|
-
const channel = MockBroadcastChannel.instances[0];
|
|
86
|
-
const receivedBySelf = [];
|
|
87
|
-
effect(() => { receivedBySelf.push(s()); });
|
|
88
|
-
const beforeCount = receivedBySelf.length;
|
|
89
|
-
// Manually simulate echo — should be ignored because posting=false after set
|
|
90
|
-
channel.onmessage(new MessageEvent("message", { data: JSON.stringify(5) }));
|
|
91
|
-
// The value stays 5 (no spurious extra notification tested via subscribe count)
|
|
92
|
-
expect(s()).toBe(5);
|
|
93
|
-
expect(receivedBySelf.length).toBe(beforeCount); // no extra effect run (Object.is equality)
|
|
94
|
-
s.close();
|
|
95
|
-
s2.close();
|
|
96
|
-
});
|
|
97
|
-
it("subscribe fires immediately then on each remote change", () => {
|
|
98
|
-
const s = syncedSignal("ch", 0);
|
|
99
|
-
const channel = MockBroadcastChannel.instances[0];
|
|
100
|
-
const received = [];
|
|
101
|
-
s.subscribe((v) => received.push(v));
|
|
102
|
-
expect(received).toEqual([0]);
|
|
103
|
-
channel.onmessage(new MessageEvent("message", { data: JSON.stringify(3) }));
|
|
104
|
-
expect(received).toEqual([0, 3]);
|
|
105
|
-
s.close();
|
|
106
|
-
});
|
|
107
|
-
it("close() shuts down the BroadcastChannel", () => {
|
|
108
|
-
const s = syncedSignal("ch", 0);
|
|
109
|
-
const channel = MockBroadcastChannel.instances[0];
|
|
110
|
-
expect(channel.closed).toBe(false);
|
|
111
|
-
s.close();
|
|
112
|
-
expect(channel.closed).toBe(true);
|
|
113
|
-
});
|
|
114
|
-
it("has __isSignal marker", () => {
|
|
115
|
-
const s = syncedSignal("ch", 0);
|
|
116
|
-
expect(s.__isSignal).toBe(true);
|
|
117
|
-
s.close();
|
|
118
|
-
});
|
|
119
|
-
it("does not update value when onmessage fires while posting (posting guard)", () => {
|
|
120
|
-
// Use a self-delivering channel that echoes to itself during postMessage.
|
|
121
|
-
// This covers the `if (posting) return;` branch in synced.ts.
|
|
122
|
-
class SelfEchoChannel {
|
|
123
|
-
onmessage = null;
|
|
124
|
-
postMessage(data) {
|
|
125
|
-
this.onmessage?.(new MessageEvent("message", { data }));
|
|
126
|
-
}
|
|
127
|
-
close() { }
|
|
128
|
-
}
|
|
129
|
-
vi.stubGlobal("BroadcastChannel", SelfEchoChannel);
|
|
130
|
-
const s = syncedSignal("echo-ch", 0);
|
|
131
|
-
const received = [];
|
|
132
|
-
s.subscribe((v) => received.push(v));
|
|
133
|
-
// set() triggers postMessage → onmessage fires → posting=true → early return
|
|
134
|
-
// Inner value should not be double-set
|
|
135
|
-
s.set(42);
|
|
136
|
-
expect(s()).toBe(42);
|
|
137
|
-
// Only two notifications: initial (0) + the set (42), no echo re-set
|
|
138
|
-
expect(received).toEqual([0, 42]);
|
|
139
|
-
s.close();
|
|
140
|
-
});
|
|
141
|
-
});
|
|
142
|
-
//# sourceMappingURL=synced.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"synced.test.js","sourceRoot":"","sources":["../../src/__tests__/synced.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"track.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/track.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { track, runEffect, activeEffect } from "../signal/effect";
|
|
3
|
-
import { signal } from "../signal/signal";
|
|
4
|
-
describe("track", () => {
|
|
5
|
-
it("runs the effect immediately", () => {
|
|
6
|
-
const fn = vi.fn();
|
|
7
|
-
track(fn);
|
|
8
|
-
expect(fn).toHaveBeenCalledOnce();
|
|
9
|
-
});
|
|
10
|
-
it("sets activeEffect during execution, resets after", () => {
|
|
11
|
-
let during = "not-set";
|
|
12
|
-
track(() => {
|
|
13
|
-
during = activeEffect;
|
|
14
|
-
});
|
|
15
|
-
expect(during).toBeTypeOf("function");
|
|
16
|
-
expect(activeEffect).toBeNull();
|
|
17
|
-
});
|
|
18
|
-
it("supports nested track calls — restores outer effect", () => {
|
|
19
|
-
const outer = vi.fn();
|
|
20
|
-
const inner = vi.fn();
|
|
21
|
-
let outerActive;
|
|
22
|
-
let innerActive;
|
|
23
|
-
track(() => {
|
|
24
|
-
outer();
|
|
25
|
-
outerActive = activeEffect;
|
|
26
|
-
track(() => {
|
|
27
|
-
inner();
|
|
28
|
-
innerActive = activeEffect;
|
|
29
|
-
});
|
|
30
|
-
// after inner track, activeEffect should be back to outer
|
|
31
|
-
expect(activeEffect).toBe(outerActive);
|
|
32
|
-
});
|
|
33
|
-
expect(outer).toHaveBeenCalledOnce();
|
|
34
|
-
expect(inner).toHaveBeenCalledOnce();
|
|
35
|
-
expect(innerActive).not.toBe(outerActive);
|
|
36
|
-
});
|
|
37
|
-
it("subscribes the tracked effect to signals read inside it", () => {
|
|
38
|
-
const s = signal(1);
|
|
39
|
-
const values = [];
|
|
40
|
-
track(() => values.push(s()));
|
|
41
|
-
s.set(2);
|
|
42
|
-
expect(values).toEqual([1, 2]);
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
describe("runEffect", () => {
|
|
46
|
-
it("sets activeEffect to the given value", () => {
|
|
47
|
-
const fn = () => { };
|
|
48
|
-
runEffect(fn);
|
|
49
|
-
expect(activeEffect).toBe(fn);
|
|
50
|
-
runEffect(null);
|
|
51
|
-
expect(activeEffect).toBeNull();
|
|
52
|
-
});
|
|
53
|
-
});
|
|
54
|
-
//# sourceMappingURL=track.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"track.test.js","sourceRoot":"","sources":["../../src/__tests__/track.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAElD,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,QAAQ,CAAC,OAAO,EAAE,GAAG,EAAE;IACrB,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACnB,KAAK,CAAC,EAAE,CAAC,CAAC;QACV,MAAM,CAAC,EAAE,CAAC,CAAC,oBAAoB,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,IAAI,MAAM,GAAY,SAAS,CAAC;QAChC,KAAK,CAAC,GAAG,EAAE;YACT,MAAM,GAAG,YAAY,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;QACtC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QACtB,IAAI,WAAoB,CAAC;QACzB,IAAI,WAAoB,CAAC;QAEzB,KAAK,CAAC,GAAG,EAAE;YACT,KAAK,EAAE,CAAC;YACR,WAAW,GAAG,YAAY,CAAC;YAC3B,KAAK,CAAC,GAAG,EAAE;gBACT,KAAK,EAAE,CAAC;gBACR,WAAW,GAAG,YAAY,CAAC;YAC7B,CAAC,CAAC,CAAC;YACH,0DAA0D;YAC1D,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,EAAE,CAAC;QACrC,MAAM,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACpB,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,KAAK,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACT,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACjC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,sCAAsC,EAAE,GAAG,EAAE;QAC9C,MAAM,EAAE,GAAG,GAAG,EAAE,GAAE,CAAC,CAAC;QACpB,SAAS,CAAC,EAAE,CAAC,CAAC;QACd,MAAM,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9B,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { batch } from "../signal/batch";
|
|
4
|
-
import { effect } from "../signal/effect";
|
|
5
|
-
import { signal } from "../signal/signal";
|
|
6
|
-
|
|
7
|
-
describe("batch", () => {
|
|
8
|
-
it("executes the wrapped function", () => {
|
|
9
|
-
const s = signal(0);
|
|
10
|
-
batch(() => {
|
|
11
|
-
s.set(5);
|
|
12
|
-
s.set(10);
|
|
13
|
-
});
|
|
14
|
-
expect(s()).toBe(10);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("does not throw when no effects are tracking", () => {
|
|
18
|
-
const s = signal(0);
|
|
19
|
-
expect(() => { batch(() => { s.set(1); }); }).not.toThrow();
|
|
20
|
-
expect(s()).toBe(1);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("propagates errors thrown inside the batch", () => {
|
|
24
|
-
expect(() =>
|
|
25
|
-
{ batch(() => {
|
|
26
|
-
throw new Error("inside batch");
|
|
27
|
-
}); },
|
|
28
|
-
).toThrow("inside batch");
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it("nested batch() — effects run exactly once after outer batch completes", () => {
|
|
32
|
-
const s = signal(0);
|
|
33
|
-
const runs: number[] = [];
|
|
34
|
-
effect(() => { runs.push(s()); });
|
|
35
|
-
// initial run captured
|
|
36
|
-
const before = runs.length;
|
|
37
|
-
|
|
38
|
-
batch(() => {
|
|
39
|
-
batch(() => {
|
|
40
|
-
s.set(1);
|
|
41
|
-
s.set(2);
|
|
42
|
-
});
|
|
43
|
-
s.set(3);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
// effect should have run exactly once for the final value
|
|
47
|
-
expect(runs.length).toBe(before + 1);
|
|
48
|
-
expect(runs[runs.length - 1]).toBe(3);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it("batch() that throws still cleans up state (no stuck batchQueue)", () => {
|
|
52
|
-
const s = signal(0);
|
|
53
|
-
const runs: number[] = [];
|
|
54
|
-
effect(() => { runs.push(s()); });
|
|
55
|
-
|
|
56
|
-
expect(() =>
|
|
57
|
-
batch(() => {
|
|
58
|
-
s.set(1);
|
|
59
|
-
throw new Error("batch boom");
|
|
60
|
-
}),
|
|
61
|
-
).toThrow("batch boom");
|
|
62
|
-
|
|
63
|
-
// After the failed batch, the queue must be cleared so subsequent
|
|
64
|
-
// signal changes work normally (not deferred indefinitely).
|
|
65
|
-
const countBefore = runs.length;
|
|
66
|
-
s.set(99);
|
|
67
|
-
expect(runs.length).toBe(countBefore + 1);
|
|
68
|
-
expect(runs[runs.length - 1]).toBe(99);
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it("effect added inside a running batch is included in the drain", () => {
|
|
72
|
-
const s = signal(0);
|
|
73
|
-
const seen: number[] = [];
|
|
74
|
-
|
|
75
|
-
batch(() => {
|
|
76
|
-
// Create an effect inside the batch — it runs immediately (initial run)
|
|
77
|
-
// and registers itself as a subscriber. Then we set the signal so it
|
|
78
|
-
// gets enqueued into the batch queue.
|
|
79
|
-
effect(() => { seen.push(s()); });
|
|
80
|
-
s.set(5);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
// The effect should have captured both the initial value and the batched value
|
|
84
|
-
expect(seen).toContain(0);
|
|
85
|
-
expect(seen).toContain(5);
|
|
86
|
-
});
|
|
87
|
-
});
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { RootComponent } from "../component/base";
|
|
4
|
-
import {
|
|
5
|
-
getComponentAnchor,
|
|
6
|
-
getComponentDefault,
|
|
7
|
-
getComponentDefaults,
|
|
8
|
-
getComponentProps,
|
|
9
|
-
getComponentRawProp,
|
|
10
|
-
isComponentMounted,
|
|
11
|
-
isStateDirty,
|
|
12
|
-
markStateDirty,
|
|
13
|
-
setComponentAnchor,
|
|
14
|
-
setComponentDefault,
|
|
15
|
-
setComponentMounted,
|
|
16
|
-
setComponentProps,
|
|
17
|
-
setStateDirty,
|
|
18
|
-
} from "../component/internals";
|
|
19
|
-
import { StatefulComponent } from "../component/stateful";
|
|
20
|
-
import { StatelessComponent } from "../component/stateless";
|
|
21
|
-
|
|
22
|
-
class ConcreteRoot extends RootComponent<{ name: string }> {
|
|
23
|
-
render() {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
class ConcreteStateful extends StatefulComponent {
|
|
29
|
-
render() {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
describe("RootComponent", () => {
|
|
35
|
-
it("stores props in component internals", () => {
|
|
36
|
-
const c = new ConcreteRoot({ name: "Alice" });
|
|
37
|
-
expect((getComponentProps(c) as { name: string }).name).toBe("Alice");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("does not expose old underscore internals as own properties", () => {
|
|
41
|
-
const c = new ConcreteRoot({ name: "Alice" });
|
|
42
|
-
expect(Object.prototype.hasOwnProperty.call(c, "_rawProps")).toBe(false);
|
|
43
|
-
expect(Object.prototype.hasOwnProperty.call(c, "_mounted")).toBe(false);
|
|
44
|
-
expect(Object.prototype.hasOwnProperty.call(c, "_anchor")).toBe(false);
|
|
45
|
-
expect(Object.prototype.hasOwnProperty.call(c, "_setProps")).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("mounted starts as false", () => {
|
|
49
|
-
const c = new ConcreteRoot({ name: "" });
|
|
50
|
-
expect(isComponentMounted(c)).toBe(false);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("anchor starts as undefined", () => {
|
|
54
|
-
const c = new ConcreteRoot({ name: "" });
|
|
55
|
-
expect(getComponentAnchor(c)).toBeUndefined();
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it("defaults to empty object when no props given", () => {
|
|
59
|
-
const c = new ConcreteRoot();
|
|
60
|
-
expect(getComponentProps(c)).toEqual({});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
describe("StatefulComponent", () => {
|
|
65
|
-
it("defaults start as empty object", () => {
|
|
66
|
-
const c = new ConcreteStateful();
|
|
67
|
-
expect(getComponentDefaults(c)).toEqual({});
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("stateDirty starts as false", () => {
|
|
71
|
-
const c = new ConcreteStateful();
|
|
72
|
-
expect(isStateDirty(c)).toBe(false);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("setComponentProps replaces raw props contents", () => {
|
|
76
|
-
const c = new ConcreteStateful({ x: 1 });
|
|
77
|
-
setComponentProps(c, { y: 2 });
|
|
78
|
-
const props = getComponentProps(c) as Record<string, unknown>;
|
|
79
|
-
expect(props.x).toBeUndefined();
|
|
80
|
-
expect(props.y).toBe(2);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("updates anchor, mounted, defaults, and dirty state through helpers", () => {
|
|
84
|
-
const c = new ConcreteStateful();
|
|
85
|
-
const anchor = {} as Comment;
|
|
86
|
-
|
|
87
|
-
setComponentAnchor(c, anchor);
|
|
88
|
-
setComponentMounted(c, true);
|
|
89
|
-
setComponentDefault(c, "label", "Save");
|
|
90
|
-
setStateDirty(c, true);
|
|
91
|
-
|
|
92
|
-
expect(getComponentAnchor(c)).toBe(anchor);
|
|
93
|
-
expect(isComponentMounted(c)).toBe(true);
|
|
94
|
-
expect(getComponentDefaults(c).label).toBe("Save");
|
|
95
|
-
expect(isStateDirty(c)).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it("reads raw props and defaults through single-value helpers", () => {
|
|
99
|
-
const c = new ConcreteStateful({ label: "From parent" });
|
|
100
|
-
setComponentDefault(c, "label", "Default");
|
|
101
|
-
|
|
102
|
-
expect(getComponentRawProp(c, "label")).toBe("From parent");
|
|
103
|
-
expect(getComponentDefault(c, "label")).toBe("Default");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("throws when component internals are read from a non-component", () => {
|
|
107
|
-
expect(() => getComponentProps({})).toThrow(TypeError);
|
|
108
|
-
expect(() => getComponentProps({})).toThrow(
|
|
109
|
-
"Expected a PraxisJS component instance.",
|
|
110
|
-
);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it("ignores dirty-state writes on non-component hosts", () => {
|
|
114
|
-
const host = {};
|
|
115
|
-
|
|
116
|
-
expect(() => markStateDirty(host)).not.toThrow();
|
|
117
|
-
expect(() => setStateDirty(host, true)).not.toThrow();
|
|
118
|
-
expect(isStateDirty(host)).toBe(false);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
class ConcreteStateless extends StatelessComponent<{ name: string }> {
|
|
123
|
-
render() {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
describe("StatelessComponent", () => {
|
|
129
|
-
it("stores props in component internals", () => {
|
|
130
|
-
const c = new ConcreteStateless({ name: "Alice" });
|
|
131
|
-
expect((getComponentProps(c) as { name: string }).name).toBe("Alice");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it("setComponentProps replaces raw props contents", () => {
|
|
135
|
-
const c = new ConcreteStateless({ name: "Alice" });
|
|
136
|
-
setComponentProps(c, { name: "Bob" });
|
|
137
|
-
expect((getComponentProps(c) as { name: string }).name).toBe("Bob");
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it("setComponentProps removes old keys", () => {
|
|
141
|
-
const c = new ConcreteStateless({ name: "Alice" });
|
|
142
|
-
setComponentProps(c, {});
|
|
143
|
-
expect((getComponentProps(c) as Record<string, unknown>).name).toBeUndefined();
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("accepts and exposes children via props.children", () => {
|
|
147
|
-
const c = new ConcreteStateless({ name: "x", children: "hello" });
|
|
148
|
-
expect(c.props.children).toBe("hello");
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
it("children is optional and defaults to undefined", () => {
|
|
152
|
-
const c = new ConcreteStateless({ name: "x" });
|
|
153
|
-
expect(c.props.children).toBeUndefined();
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it("works with default generic (no explicit T)", () => {
|
|
157
|
-
class Bare extends StatelessComponent {
|
|
158
|
-
render() {
|
|
159
|
-
return null;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
const c = new Bare({ children: "hi" });
|
|
163
|
-
expect(c.props.children).toBe("hi");
|
|
164
|
-
});
|
|
165
|
-
});
|