@derivation/rpc 0.3.3 → 0.3.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/dist/tests/context.test.d.ts +1 -0
- package/dist/tests/context.test.js +252 -0
- package/dist/tests/iso.test.d.ts +1 -0
- package/dist/tests/iso.test.js +186 -0
- package/dist/tests/messages.test.d.ts +1 -0
- package/dist/tests/messages.test.js +152 -0
- package/dist/tests/mutations.test.d.ts +1 -0
- package/dist/tests/mutations.test.js +122 -0
- package/dist/tests/queue.test.d.ts +1 -0
- package/dist/tests/queue.test.js +84 -0
- package/dist/tests/reactive-map-adapter.test.d.ts +1 -0
- package/dist/tests/reactive-map-adapter.test.js +190 -0
- package/dist/tests/reactive-set-adapter.test.d.ts +1 -0
- package/dist/tests/reactive-set-adapter.test.js +157 -0
- package/dist/tests/stream-adapter.test.d.ts +1 -0
- package/dist/tests/stream-adapter.test.js +119 -0
- package/dist/tests/weak-list.test.d.ts +1 -0
- package/dist/tests/weak-list.test.js +100 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +9 -4
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { Graph, inputValue } from 'derivation';
|
|
3
|
+
import { StreamSourceAdapter, StreamSinkAdapter, sink } from '../stream-adapter.js';
|
|
4
|
+
import * as iso from '../iso.js';
|
|
5
|
+
describe('StreamSourceAdapter', () => {
|
|
6
|
+
it('should provide snapshot of the stream value', () => {
|
|
7
|
+
const graph = new Graph();
|
|
8
|
+
const stream = inputValue(graph, { x: 10, y: 20 });
|
|
9
|
+
const adapter = new StreamSourceAdapter(stream, iso.id());
|
|
10
|
+
expect(adapter.Snapshot).toEqual({ x: 10, y: 20 });
|
|
11
|
+
});
|
|
12
|
+
it('should transform snapshot using isomorphism', () => {
|
|
13
|
+
const graph = new Graph();
|
|
14
|
+
const stream = inputValue(graph, { count: 42 });
|
|
15
|
+
const countToString = {
|
|
16
|
+
to: (obj) => ({ count: obj.count.toString() }),
|
|
17
|
+
from: (obj) => ({ count: parseInt(obj.count, 10) }),
|
|
18
|
+
};
|
|
19
|
+
const adapter = new StreamSourceAdapter(stream, countToString);
|
|
20
|
+
expect(adapter.Snapshot).toEqual({ count: '42' });
|
|
21
|
+
});
|
|
22
|
+
it('should provide last change as the current value', () => {
|
|
23
|
+
const graph = new Graph();
|
|
24
|
+
const stream = inputValue(graph, { a: 1 });
|
|
25
|
+
const adapter = new StreamSourceAdapter(stream, iso.id());
|
|
26
|
+
expect(adapter.LastChange).toEqual({ a: 1 });
|
|
27
|
+
stream.push({ a: 2 });
|
|
28
|
+
graph.step();
|
|
29
|
+
expect(adapter.LastChange).toEqual({ a: 2 });
|
|
30
|
+
});
|
|
31
|
+
it('should return the underlying stream', () => {
|
|
32
|
+
const graph = new Graph();
|
|
33
|
+
const stream = inputValue(graph, { test: true });
|
|
34
|
+
const adapter = new StreamSourceAdapter(stream, iso.id());
|
|
35
|
+
expect(adapter.Stream).toBe(stream);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
describe('StreamSinkAdapter', () => {
|
|
39
|
+
it('should apply changes to a stream', () => {
|
|
40
|
+
const graph = new Graph();
|
|
41
|
+
const adapter = new StreamSinkAdapter(graph, iso.id(), { x: 0 });
|
|
42
|
+
const { stream, input } = adapter.build();
|
|
43
|
+
adapter.apply({ x: 5 }, input);
|
|
44
|
+
graph.step();
|
|
45
|
+
expect(stream.value).toEqual({ x: 5 });
|
|
46
|
+
adapter.apply({ x: 10 }, input);
|
|
47
|
+
graph.step();
|
|
48
|
+
expect(stream.value).toEqual({ x: 10 });
|
|
49
|
+
});
|
|
50
|
+
it('should transform changes using isomorphism', () => {
|
|
51
|
+
const graph = new Graph();
|
|
52
|
+
const stringToNum = {
|
|
53
|
+
to: (obj) => ({ val: obj.val.toString() }),
|
|
54
|
+
from: (obj) => ({ val: parseInt(obj.val, 10) }),
|
|
55
|
+
};
|
|
56
|
+
const adapter = new StreamSinkAdapter(graph, stringToNum, { val: '0' });
|
|
57
|
+
const { stream, input } = adapter.build();
|
|
58
|
+
// Apply change with string, should be converted to number
|
|
59
|
+
adapter.apply({ val: '42' }, input);
|
|
60
|
+
graph.step();
|
|
61
|
+
expect(stream.value).toEqual({ val: 42 });
|
|
62
|
+
});
|
|
63
|
+
it('should build a new input stream each time', () => {
|
|
64
|
+
const graph = new Graph();
|
|
65
|
+
const adapter = new StreamSinkAdapter(graph, iso.id(), { id: 'test' });
|
|
66
|
+
const { stream: stream1 } = adapter.build();
|
|
67
|
+
const { stream: stream2 } = adapter.build();
|
|
68
|
+
expect(stream1).toBeDefined();
|
|
69
|
+
expect(stream2).toBeDefined();
|
|
70
|
+
expect(stream1).not.toBe(stream2);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('sink function', () => {
|
|
74
|
+
it('should create a sink that initializes with snapshot', () => {
|
|
75
|
+
const graph = new Graph();
|
|
76
|
+
const sinkFn = sink(graph, iso.id());
|
|
77
|
+
const snapshot = { name: 'initial' };
|
|
78
|
+
const sinkAdapter = sinkFn(snapshot);
|
|
79
|
+
const { stream } = sinkAdapter.build();
|
|
80
|
+
expect(stream.value).toEqual({ name: 'initial' });
|
|
81
|
+
});
|
|
82
|
+
it('should allow applying changes after initialization', () => {
|
|
83
|
+
const graph = new Graph();
|
|
84
|
+
const sinkFn = sink(graph, iso.id());
|
|
85
|
+
const sinkAdapter = sinkFn({ count: 0 });
|
|
86
|
+
const { stream, input } = sinkAdapter.build();
|
|
87
|
+
expect(stream.value).toEqual({ count: 0 });
|
|
88
|
+
sinkAdapter.apply({ count: 5 }, input);
|
|
89
|
+
graph.step();
|
|
90
|
+
expect(stream.value).toEqual({ count: 5 });
|
|
91
|
+
sinkAdapter.apply({ count: 10 }, input);
|
|
92
|
+
graph.step();
|
|
93
|
+
expect(stream.value).toEqual({ count: 10 });
|
|
94
|
+
});
|
|
95
|
+
it('should transform snapshot using isomorphism', () => {
|
|
96
|
+
const graph = new Graph();
|
|
97
|
+
const strToNum = {
|
|
98
|
+
to: (obj) => ({ value: obj.value.toString() }),
|
|
99
|
+
from: (obj) => ({ value: parseInt(obj.value, 10) }),
|
|
100
|
+
};
|
|
101
|
+
const sinkFn = sink(graph, strToNum);
|
|
102
|
+
const sinkAdapter = sinkFn({ value: '99' });
|
|
103
|
+
const { stream } = sinkAdapter.build();
|
|
104
|
+
// Should be converted from string to number
|
|
105
|
+
expect(stream.value).toEqual({ value: 99 });
|
|
106
|
+
});
|
|
107
|
+
it('should create new stream instances on each build call', () => {
|
|
108
|
+
const graph = new Graph();
|
|
109
|
+
const sinkFn = sink(graph, iso.id());
|
|
110
|
+
const sinkAdapter = sinkFn({ data: 'test' });
|
|
111
|
+
const { stream: stream1 } = sinkAdapter.build();
|
|
112
|
+
const { stream: stream2 } = sinkAdapter.build();
|
|
113
|
+
// Each build creates a new instance
|
|
114
|
+
expect(stream1).not.toBe(stream2);
|
|
115
|
+
// But both have the same initial value
|
|
116
|
+
expect(stream1.value).toEqual({ data: 'test' });
|
|
117
|
+
expect(stream2.value).toEqual({ data: 'test' });
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import WeakList from '../weak-list.js';
|
|
3
|
+
describe('WeakList', () => {
|
|
4
|
+
it('should store and iterate over objects', () => {
|
|
5
|
+
const list = new WeakList();
|
|
6
|
+
const obj1 = { id: 1 };
|
|
7
|
+
const obj2 = { id: 2 };
|
|
8
|
+
const obj3 = { id: 3 };
|
|
9
|
+
list.add(obj1);
|
|
10
|
+
list.add(obj2);
|
|
11
|
+
list.add(obj3);
|
|
12
|
+
const items = [...list];
|
|
13
|
+
expect(items).toHaveLength(3);
|
|
14
|
+
expect(items).toContain(obj1);
|
|
15
|
+
expect(items).toContain(obj2);
|
|
16
|
+
expect(items).toContain(obj3);
|
|
17
|
+
});
|
|
18
|
+
it('should automatically clean up collected references', () => {
|
|
19
|
+
const list = new WeakList();
|
|
20
|
+
// Add objects in a scope
|
|
21
|
+
{
|
|
22
|
+
const obj1 = { id: 1 };
|
|
23
|
+
const obj2 = { id: 2 };
|
|
24
|
+
list.add(obj1);
|
|
25
|
+
list.add(obj2);
|
|
26
|
+
}
|
|
27
|
+
// Keep one object alive
|
|
28
|
+
const obj3 = { id: 3 };
|
|
29
|
+
list.add(obj3);
|
|
30
|
+
// Force garbage collection if available
|
|
31
|
+
if (global.gc) {
|
|
32
|
+
global.gc();
|
|
33
|
+
}
|
|
34
|
+
// Iterate - this should clean up dead references
|
|
35
|
+
const items = [...list];
|
|
36
|
+
// obj3 should still be present
|
|
37
|
+
expect(items).toContain(obj3);
|
|
38
|
+
// Note: We can't reliably test that obj1 and obj2 are gone
|
|
39
|
+
// because GC timing is non-deterministic
|
|
40
|
+
});
|
|
41
|
+
it('should handle empty list', () => {
|
|
42
|
+
const list = new WeakList();
|
|
43
|
+
const items = [...list];
|
|
44
|
+
expect(items).toHaveLength(0);
|
|
45
|
+
});
|
|
46
|
+
it('should allow multiple iterations', () => {
|
|
47
|
+
const list = new WeakList();
|
|
48
|
+
const obj1 = { id: 1 };
|
|
49
|
+
const obj2 = { id: 2 };
|
|
50
|
+
list.add(obj1);
|
|
51
|
+
list.add(obj2);
|
|
52
|
+
const items1 = [...list];
|
|
53
|
+
expect(items1).toHaveLength(2);
|
|
54
|
+
const items2 = [...list];
|
|
55
|
+
expect(items2).toHaveLength(2);
|
|
56
|
+
expect(items2).toEqual(items1);
|
|
57
|
+
});
|
|
58
|
+
it('should work with different object types', () => {
|
|
59
|
+
class MyClass {
|
|
60
|
+
constructor(value) {
|
|
61
|
+
this.value = value;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const list = new WeakList();
|
|
65
|
+
const obj1 = new MyClass('test1');
|
|
66
|
+
const obj2 = new MyClass('test2');
|
|
67
|
+
list.add(obj1);
|
|
68
|
+
list.add(obj2);
|
|
69
|
+
const items = [...list];
|
|
70
|
+
expect(items).toHaveLength(2);
|
|
71
|
+
expect(items[0]).toBeInstanceOf(MyClass);
|
|
72
|
+
expect(items[1]).toBeInstanceOf(MyClass);
|
|
73
|
+
});
|
|
74
|
+
it('should maintain insertion order for live objects', () => {
|
|
75
|
+
const list = new WeakList();
|
|
76
|
+
const obj1 = { id: 1 };
|
|
77
|
+
const obj2 = { id: 2 };
|
|
78
|
+
const obj3 = { id: 3 };
|
|
79
|
+
list.add(obj1);
|
|
80
|
+
list.add(obj2);
|
|
81
|
+
list.add(obj3);
|
|
82
|
+
const items = [...list];
|
|
83
|
+
expect(items[0]).toBe(obj1);
|
|
84
|
+
expect(items[1]).toBe(obj2);
|
|
85
|
+
expect(items[2]).toBe(obj3);
|
|
86
|
+
});
|
|
87
|
+
it('should handle adding the same object multiple times', () => {
|
|
88
|
+
const list = new WeakList();
|
|
89
|
+
const obj1 = { id: 1 };
|
|
90
|
+
list.add(obj1);
|
|
91
|
+
list.add(obj1);
|
|
92
|
+
list.add(obj1);
|
|
93
|
+
const items = [...list];
|
|
94
|
+
// Should have 3 references to the same object
|
|
95
|
+
expect(items).toHaveLength(3);
|
|
96
|
+
expect(items[0]).toBe(obj1);
|
|
97
|
+
expect(items[1]).toBe(obj1);
|
|
98
|
+
expect(items[2]).toBe(obj1);
|
|
99
|
+
});
|
|
100
|
+
});
|