@derivation/rpc 0.3.5 → 0.5.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/client-DJZfuakf.d.cts +27 -0
- package/dist/{client.d.ts → client-TPsVZH_B.d.ts} +7 -4
- package/dist/index.cjs +335 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +55 -0
- package/dist/index.d.ts +55 -7
- package/dist/index.js +305 -6
- package/dist/index.js.map +1 -0
- package/dist/iso.cjs +151 -0
- package/dist/iso.cjs.map +1 -0
- package/dist/iso.d.cts +52 -0
- package/dist/iso.d.ts +35 -19
- package/dist/iso.js +99 -101
- package/dist/iso.js.map +1 -0
- package/dist/{presence-manager.d.ts → presence-manager-4LlEyuGp.d.cts} +3 -1
- package/dist/presence-manager-4LlEyuGp.d.ts +7 -0
- package/dist/shared-worker-client.cjs +271 -0
- package/dist/shared-worker-client.cjs.map +1 -0
- package/dist/shared-worker-client.d.cts +26 -0
- package/dist/shared-worker-client.d.ts +8 -4
- package/dist/shared-worker-client.js +243 -24
- package/dist/shared-worker-client.js.map +1 -0
- package/dist/shared-worker-server.cjs +363 -0
- package/dist/shared-worker-server.cjs.map +1 -0
- package/dist/shared-worker-server.d.cts +31 -0
- package/dist/shared-worker-server.d.ts +9 -6
- package/dist/shared-worker-server.js +332 -58
- package/dist/shared-worker-server.js.map +1 -0
- package/dist/stream-types-Q_EqNLtO.d.cts +46 -0
- package/dist/stream-types-Q_EqNLtO.d.ts +46 -0
- package/dist/transport.cjs +19 -0
- package/dist/transport.cjs.map +1 -0
- package/dist/transport.d.cts +29 -0
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +1 -1
- package/dist/transport.js.map +1 -0
- package/dist/web-socket-server.cjs +469 -0
- package/dist/web-socket-server.cjs.map +1 -0
- package/dist/web-socket-server.d.cts +14 -0
- package/dist/web-socket-server.d.ts +10 -7
- package/dist/web-socket-server.js +437 -52
- package/dist/web-socket-server.js.map +1 -0
- package/dist/web-socket-transport.cjs +52 -0
- package/dist/web-socket-transport.cjs.map +1 -0
- package/dist/web-socket-transport.d.cts +16 -0
- package/dist/web-socket-transport.d.ts +5 -2
- package/dist/web-socket-transport.js +27 -25
- package/dist/web-socket-transport.js.map +1 -0
- package/package.json +26 -17
- package/dist/client-handler.d.ts +0 -27
- package/dist/client-handler.js +0 -187
- package/dist/client-message.d.ts +0 -57
- package/dist/client-message.js +0 -59
- package/dist/client.js +0 -133
- package/dist/messageport-transport.d.ts +0 -13
- package/dist/messageport-transport.js +0 -28
- package/dist/node-web-socket-transport.d.ts +0 -16
- package/dist/node-web-socket-transport.js +0 -33
- package/dist/presence-manager.js +0 -1
- package/dist/queue.d.ts +0 -9
- package/dist/queue.js +0 -32
- package/dist/rate-limiter.d.ts +0 -7
- package/dist/rate-limiter.js +0 -24
- package/dist/reactive-map-adapter.d.ts +0 -24
- package/dist/reactive-map-adapter.js +0 -43
- package/dist/reactive-set-adapter.d.ts +0 -24
- package/dist/reactive-set-adapter.js +0 -41
- package/dist/server-message.d.ts +0 -48
- package/dist/server-message.js +0 -52
- package/dist/shared-worker-client-handler.d.ts +0 -27
- package/dist/shared-worker-client-handler.js +0 -149
- package/dist/stream-adapter.d.ts +0 -23
- package/dist/stream-adapter.js +0 -35
- package/dist/stream-types.d.ts +0 -44
- package/dist/stream-types.js +0 -1
- package/dist/tests/context.test.d.ts +0 -1
- package/dist/tests/context.test.js +0 -252
- package/dist/tests/iso.test.d.ts +0 -1
- package/dist/tests/iso.test.js +0 -186
- package/dist/tests/messages.test.d.ts +0 -1
- package/dist/tests/messages.test.js +0 -152
- package/dist/tests/mutations.test.d.ts +0 -1
- package/dist/tests/mutations.test.js +0 -122
- package/dist/tests/queue.test.d.ts +0 -1
- package/dist/tests/queue.test.js +0 -84
- package/dist/tests/reactive-map-adapter.test.d.ts +0 -1
- package/dist/tests/reactive-map-adapter.test.js +0 -190
- package/dist/tests/reactive-set-adapter.test.d.ts +0 -1
- package/dist/tests/reactive-set-adapter.test.js +0 -157
- package/dist/tests/stream-adapter.test.d.ts +0 -1
- package/dist/tests/stream-adapter.test.js +0 -119
- package/dist/tests/weak-list.test.d.ts +0 -1
- package/dist/tests/weak-list.test.js +0 -100
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/weak-list.d.ts +0 -5
- package/dist/weak-list.js +0 -19
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { ClientMessage, CallMessageSchema, parseClientMessage, } from '../client-message.js';
|
|
3
|
-
import { ServerMessage, ResultMessageSchema, } from '../server-message.js';
|
|
4
|
-
describe('Mutation Messages', () => {
|
|
5
|
-
describe('ClientMessage.call', () => {
|
|
6
|
-
it('should create a valid call message', () => {
|
|
7
|
-
const msg = ClientMessage.call(1, 'addUser', { name: 'Alice', age: 30 });
|
|
8
|
-
expect(msg).toEqual({
|
|
9
|
-
type: 'call',
|
|
10
|
-
id: 1,
|
|
11
|
-
name: 'addUser',
|
|
12
|
-
args: { name: 'Alice', age: 30 },
|
|
13
|
-
});
|
|
14
|
-
});
|
|
15
|
-
it('should validate against schema', () => {
|
|
16
|
-
const msg = ClientMessage.call(42, 'myMutation', { x: 10 });
|
|
17
|
-
expect(() => CallMessageSchema.parse(msg)).not.toThrow();
|
|
18
|
-
});
|
|
19
|
-
it('should be parseable by parseClientMessage', () => {
|
|
20
|
-
const data = {
|
|
21
|
-
type: 'call',
|
|
22
|
-
id: 5,
|
|
23
|
-
name: 'updateUser',
|
|
24
|
-
args: { id: 123, name: 'Bob' },
|
|
25
|
-
};
|
|
26
|
-
const result = parseClientMessage(data);
|
|
27
|
-
expect(result).toEqual(data);
|
|
28
|
-
expect(result.type).toBe('call');
|
|
29
|
-
});
|
|
30
|
-
it('should reject call without required fields', () => {
|
|
31
|
-
const invalidData = {
|
|
32
|
-
type: 'call',
|
|
33
|
-
id: 1,
|
|
34
|
-
// missing name and args
|
|
35
|
-
};
|
|
36
|
-
expect(() => parseClientMessage(invalidData)).toThrow();
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
describe('ServerMessage.resultSuccess', () => {
|
|
40
|
-
it('should create a valid success result message', () => {
|
|
41
|
-
const value = { userId: 42, status: 'created' };
|
|
42
|
-
const msg = ServerMessage.resultSuccess(1, value);
|
|
43
|
-
expect(msg).toEqual({
|
|
44
|
-
type: 'result',
|
|
45
|
-
id: 1,
|
|
46
|
-
success: true,
|
|
47
|
-
value,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
it('should validate against schema', () => {
|
|
51
|
-
const msg = ServerMessage.resultSuccess(1, { data: 'test' });
|
|
52
|
-
expect(() => ResultMessageSchema.parse(msg)).not.toThrow();
|
|
53
|
-
});
|
|
54
|
-
it('should handle null values', () => {
|
|
55
|
-
const msg = ServerMessage.resultSuccess(1, null);
|
|
56
|
-
expect(msg.value).toBe(null);
|
|
57
|
-
expect(() => ResultMessageSchema.parse(msg)).not.toThrow();
|
|
58
|
-
});
|
|
59
|
-
it('should handle primitive values', () => {
|
|
60
|
-
const msgString = ServerMessage.resultSuccess(1, 'hello');
|
|
61
|
-
expect(msgString.value).toBe('hello');
|
|
62
|
-
const msgNumber = ServerMessage.resultSuccess(2, 42);
|
|
63
|
-
expect(msgNumber.value).toBe(42);
|
|
64
|
-
const msgBoolean = ServerMessage.resultSuccess(3, true);
|
|
65
|
-
expect(msgBoolean.value).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
describe('ServerMessage.resultError', () => {
|
|
69
|
-
it('should create a valid error result message', () => {
|
|
70
|
-
const msg = ServerMessage.resultError(1, 'User not found');
|
|
71
|
-
expect(msg).toEqual({
|
|
72
|
-
type: 'result',
|
|
73
|
-
id: 1,
|
|
74
|
-
success: false,
|
|
75
|
-
error: 'User not found',
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
it('should validate against schema', () => {
|
|
79
|
-
const msg = ServerMessage.resultError(1, 'Something went wrong');
|
|
80
|
-
expect(() => ResultMessageSchema.parse(msg)).not.toThrow();
|
|
81
|
-
});
|
|
82
|
-
it('should handle empty error messages', () => {
|
|
83
|
-
const msg = ServerMessage.resultError(1, '');
|
|
84
|
-
expect(msg.error).toBe('');
|
|
85
|
-
expect(() => ResultMessageSchema.parse(msg)).not.toThrow();
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
describe('MutationResult type', () => {
|
|
90
|
-
it('should represent successful results', () => {
|
|
91
|
-
const result = {
|
|
92
|
-
success: true,
|
|
93
|
-
value: 42,
|
|
94
|
-
};
|
|
95
|
-
expect(result.success).toBe(true);
|
|
96
|
-
if (result.success) {
|
|
97
|
-
expect(result.value).toBe(42);
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
it('should represent error results', () => {
|
|
101
|
-
const result = {
|
|
102
|
-
success: false,
|
|
103
|
-
error: 'Failed to compute',
|
|
104
|
-
};
|
|
105
|
-
expect(result.success).toBe(false);
|
|
106
|
-
if (!result.success) {
|
|
107
|
-
expect(result.error).toBe('Failed to compute');
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
it('should work with complex types', () => {
|
|
111
|
-
const successResult = {
|
|
112
|
-
success: true,
|
|
113
|
-
value: { id: 1, name: 'Alice' },
|
|
114
|
-
};
|
|
115
|
-
const errorResult = {
|
|
116
|
-
success: false,
|
|
117
|
-
error: 'User creation failed',
|
|
118
|
-
};
|
|
119
|
-
expect(successResult.success).toBe(true);
|
|
120
|
-
expect(errorResult.success).toBe(false);
|
|
121
|
-
});
|
|
122
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/dist/tests/queue.test.js
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { Queue } from '../queue.js';
|
|
3
|
-
describe('Queue', () => {
|
|
4
|
-
it('should start empty', () => {
|
|
5
|
-
const q = new Queue();
|
|
6
|
-
expect(q.isEmpty()).toBe(true);
|
|
7
|
-
expect(q.length).toBe(0);
|
|
8
|
-
});
|
|
9
|
-
it('should push and pop single item', () => {
|
|
10
|
-
const q = new Queue();
|
|
11
|
-
q.push(1);
|
|
12
|
-
expect(q.length).toBe(1);
|
|
13
|
-
expect(q.pop()).toBe(1);
|
|
14
|
-
expect(q.isEmpty()).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
it('should maintain FIFO order', () => {
|
|
17
|
-
const q = new Queue();
|
|
18
|
-
q.push(1);
|
|
19
|
-
q.push(2);
|
|
20
|
-
q.push(3);
|
|
21
|
-
expect(q.pop()).toBe(1);
|
|
22
|
-
expect(q.pop()).toBe(2);
|
|
23
|
-
expect(q.pop()).toBe(3);
|
|
24
|
-
expect(q.pop()).toBe(undefined);
|
|
25
|
-
});
|
|
26
|
-
it('should handle interleaved push and pop', () => {
|
|
27
|
-
const q = new Queue();
|
|
28
|
-
q.push('a');
|
|
29
|
-
q.push('b');
|
|
30
|
-
expect(q.pop()).toBe('a');
|
|
31
|
-
q.push('c');
|
|
32
|
-
expect(q.pop()).toBe('b');
|
|
33
|
-
expect(q.pop()).toBe('c');
|
|
34
|
-
expect(q.isEmpty()).toBe(true);
|
|
35
|
-
});
|
|
36
|
-
it('should reverse front to back when back is empty', () => {
|
|
37
|
-
const q = new Queue();
|
|
38
|
-
// Fill front
|
|
39
|
-
q.push(1);
|
|
40
|
-
q.push(2);
|
|
41
|
-
q.push(3);
|
|
42
|
-
// Pop empties back, triggers reverse
|
|
43
|
-
expect(q.pop()).toBe(1);
|
|
44
|
-
expect(q.pop()).toBe(2);
|
|
45
|
-
// Add more to front
|
|
46
|
-
q.push(4);
|
|
47
|
-
q.push(5);
|
|
48
|
-
// Continue popping - should get 3 (from back), then 4, 5 (after reverse)
|
|
49
|
-
expect(q.pop()).toBe(3);
|
|
50
|
-
expect(q.pop()).toBe(4);
|
|
51
|
-
expect(q.pop()).toBe(5);
|
|
52
|
-
expect(q.isEmpty()).toBe(true);
|
|
53
|
-
});
|
|
54
|
-
it('should handle large number of operations', () => {
|
|
55
|
-
const q = new Queue();
|
|
56
|
-
const n = 1000;
|
|
57
|
-
// Push n items
|
|
58
|
-
for (let i = 0; i < n; i++) {
|
|
59
|
-
q.push(i);
|
|
60
|
-
}
|
|
61
|
-
expect(q.length).toBe(n);
|
|
62
|
-
// Pop n items in order
|
|
63
|
-
for (let i = 0; i < n; i++) {
|
|
64
|
-
expect(q.pop()).toBe(i);
|
|
65
|
-
}
|
|
66
|
-
expect(q.isEmpty()).toBe(true);
|
|
67
|
-
});
|
|
68
|
-
it('should maintain length correctly through operations', () => {
|
|
69
|
-
const q = new Queue();
|
|
70
|
-
expect(q.length).toBe(0);
|
|
71
|
-
q.push(1);
|
|
72
|
-
expect(q.length).toBe(1);
|
|
73
|
-
q.push(2);
|
|
74
|
-
q.push(3);
|
|
75
|
-
expect(q.length).toBe(3);
|
|
76
|
-
q.pop();
|
|
77
|
-
expect(q.length).toBe(2);
|
|
78
|
-
q.pop();
|
|
79
|
-
q.pop();
|
|
80
|
-
expect(q.length).toBe(0);
|
|
81
|
-
q.pop(); // Pop from empty
|
|
82
|
-
expect(q.length).toBe(0);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { Graph } from 'derivation';
|
|
3
|
-
import { ZMap, Reactive, ZMapOperations, ZMapChangeInput } from '@derivation/composable';
|
|
4
|
-
import { ReactiveMapSourceAdapter, ReactiveMapSinkAdapter, sink } from '../reactive-map-adapter.js';
|
|
5
|
-
import * as iso from '../iso.js';
|
|
6
|
-
describe('ReactiveMapSourceAdapter', () => {
|
|
7
|
-
it('should provide snapshot as array of [key, value, weight] tuples', () => {
|
|
8
|
-
const graph = new Graph();
|
|
9
|
-
let zmap = new ZMap();
|
|
10
|
-
zmap = zmap.add('a', 1, 10).add('b', 2, 20);
|
|
11
|
-
const reactiveMap = Reactive.create(graph, new ZMapOperations(), new ZMapChangeInput(graph), zmap);
|
|
12
|
-
const adapter = new ReactiveMapSourceAdapter(reactiveMap, iso.id(), iso.id());
|
|
13
|
-
const snapshot = adapter.Snapshot;
|
|
14
|
-
expect(snapshot).toEqual([['a', 1, 10], ['b', 2, 20]]);
|
|
15
|
-
});
|
|
16
|
-
it('should transform keys and values using isomorphisms', () => {
|
|
17
|
-
const graph = new Graph();
|
|
18
|
-
let zmap = new ZMap();
|
|
19
|
-
zmap = zmap.add(1, 10, 5).add(2, 20, 15);
|
|
20
|
-
const reactiveMap = Reactive.create(graph, new ZMapOperations(), new ZMapChangeInput(graph), zmap);
|
|
21
|
-
const numToString = {
|
|
22
|
-
to: (n) => n.toString(),
|
|
23
|
-
from: (s) => parseInt(s, 10),
|
|
24
|
-
};
|
|
25
|
-
const adapter = new ReactiveMapSourceAdapter(reactiveMap, numToString, numToString);
|
|
26
|
-
const snapshot = adapter.Snapshot;
|
|
27
|
-
expect(snapshot).toEqual([['1', '10', 5], ['2', '20', 15]]);
|
|
28
|
-
});
|
|
29
|
-
it('should provide last change after modifications', () => {
|
|
30
|
-
const graph = new Graph();
|
|
31
|
-
const input = new ZMapChangeInput(graph);
|
|
32
|
-
const reactiveMap = Reactive.create(graph, new ZMapOperations(), input, new ZMap());
|
|
33
|
-
const adapter = new ReactiveMapSourceAdapter(reactiveMap, iso.id(), iso.id());
|
|
34
|
-
// Push a change
|
|
35
|
-
input.add('key', 'value', 1);
|
|
36
|
-
graph.step();
|
|
37
|
-
const lastChange = adapter.LastChange;
|
|
38
|
-
expect(lastChange).toEqual([['key', 'value', 1]]);
|
|
39
|
-
});
|
|
40
|
-
it('should return the underlying reactive map', () => {
|
|
41
|
-
const graph = new Graph();
|
|
42
|
-
const reactiveMap = Reactive.create(graph, new ZMapOperations(), new ZMapChangeInput(graph), new ZMap());
|
|
43
|
-
const adapter = new ReactiveMapSourceAdapter(reactiveMap, iso.id(), iso.id());
|
|
44
|
-
expect(adapter.Stream).toBe(reactiveMap);
|
|
45
|
-
});
|
|
46
|
-
it('should handle empty maps', () => {
|
|
47
|
-
const graph = new Graph();
|
|
48
|
-
const reactiveMap = Reactive.create(graph, new ZMapOperations(), new ZMapChangeInput(graph), new ZMap());
|
|
49
|
-
const adapter = new ReactiveMapSourceAdapter(reactiveMap, iso.id(), iso.id());
|
|
50
|
-
expect(adapter.Snapshot).toEqual([]);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
describe('ReactiveMapSinkAdapter', () => {
|
|
54
|
-
it('should build a reactive map source', () => {
|
|
55
|
-
const graph = new Graph();
|
|
56
|
-
const adapter = new ReactiveMapSinkAdapter(graph, iso.id(), iso.id(), []);
|
|
57
|
-
const { stream: source } = adapter.build();
|
|
58
|
-
expect(source).toBeDefined();
|
|
59
|
-
expect([...source.snapshot.getEntries()]).toEqual([]);
|
|
60
|
-
});
|
|
61
|
-
it('should apply changes to a reactive map', () => {
|
|
62
|
-
const graph = new Graph();
|
|
63
|
-
const adapter = new ReactiveMapSinkAdapter(graph, iso.id(), iso.id(), []);
|
|
64
|
-
const { stream: source, input } = adapter.build();
|
|
65
|
-
// Apply changes
|
|
66
|
-
const change = [['a', 1, 5], ['b', 2, 10]];
|
|
67
|
-
adapter.apply(change, input);
|
|
68
|
-
graph.step();
|
|
69
|
-
// Check snapshot
|
|
70
|
-
const entries = [...source.snapshot.getEntries()];
|
|
71
|
-
expect(entries).toContainEqual(['a', 1, 5]);
|
|
72
|
-
expect(entries).toContainEqual(['b', 2, 10]);
|
|
73
|
-
});
|
|
74
|
-
it('should transform keys and values using isomorphisms', () => {
|
|
75
|
-
const graph = new Graph();
|
|
76
|
-
const strToNum = {
|
|
77
|
-
to: (n) => n.toString(),
|
|
78
|
-
from: (s) => parseInt(s, 10),
|
|
79
|
-
};
|
|
80
|
-
const adapter = new ReactiveMapSinkAdapter(graph, strToNum, strToNum, []);
|
|
81
|
-
const { stream: source, input } = adapter.build();
|
|
82
|
-
// Apply change with string keys and values
|
|
83
|
-
const change = [['5', '10', 1], ['15', '20', 2]];
|
|
84
|
-
adapter.apply(change, input);
|
|
85
|
-
graph.step();
|
|
86
|
-
// Should be converted to numbers
|
|
87
|
-
const entries = [...source.snapshot.getEntries()];
|
|
88
|
-
expect(entries).toContainEqual([5, 10, 1]);
|
|
89
|
-
expect(entries).toContainEqual([15, 20, 2]);
|
|
90
|
-
});
|
|
91
|
-
it('should handle adding entries with different weights', () => {
|
|
92
|
-
const graph = new Graph();
|
|
93
|
-
const adapter = new ReactiveMapSinkAdapter(graph, iso.id(), iso.id(), []);
|
|
94
|
-
const { stream: source, input } = adapter.build();
|
|
95
|
-
// Add same key with different weights
|
|
96
|
-
adapter.apply([['key', 'value1', 5]], input);
|
|
97
|
-
graph.step();
|
|
98
|
-
adapter.apply([['key', 'value2', 3]], input);
|
|
99
|
-
graph.step();
|
|
100
|
-
const entries = [...source.snapshot.getEntries()];
|
|
101
|
-
// Should contain both entries with their weights
|
|
102
|
-
expect(entries.length).toBeGreaterThan(0);
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
describe('sink function', () => {
|
|
106
|
-
it('should create a sink initialized with snapshot', () => {
|
|
107
|
-
const graph = new Graph();
|
|
108
|
-
const sinkFn = sink(graph, iso.id(), iso.id());
|
|
109
|
-
const snapshot = [['a', 1, 10], ['b', 2, 20]];
|
|
110
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
111
|
-
const { stream: source } = sinkAdapter.build();
|
|
112
|
-
const entries = [...source.snapshot.getEntries()];
|
|
113
|
-
expect(entries).toContainEqual(['a', 1, 10]);
|
|
114
|
-
expect(entries).toContainEqual(['b', 2, 20]);
|
|
115
|
-
});
|
|
116
|
-
it('should allow applying changes after initialization', () => {
|
|
117
|
-
const graph = new Graph();
|
|
118
|
-
const sinkFn = sink(graph, iso.id(), iso.id());
|
|
119
|
-
const snapshot = [['x', 'y', 1]];
|
|
120
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
121
|
-
const { stream: source, input } = sinkAdapter.build();
|
|
122
|
-
// Initial check
|
|
123
|
-
let entries = [...source.snapshot.getEntries()];
|
|
124
|
-
expect(entries).toContainEqual(['x', 'y', 1]);
|
|
125
|
-
// Apply a change
|
|
126
|
-
const change = [['a', 'b', 2], ['c', 'd', 3]];
|
|
127
|
-
sinkAdapter.apply(change, input);
|
|
128
|
-
graph.step();
|
|
129
|
-
entries = [...source.snapshot.getEntries()];
|
|
130
|
-
expect(entries.length).toBeGreaterThan(1);
|
|
131
|
-
expect(entries).toContainEqual(['a', 'b', 2]);
|
|
132
|
-
expect(entries).toContainEqual(['c', 'd', 3]);
|
|
133
|
-
});
|
|
134
|
-
it('should transform snapshot using isomorphisms', () => {
|
|
135
|
-
const graph = new Graph();
|
|
136
|
-
const strToNum = {
|
|
137
|
-
to: (n) => n.toString(),
|
|
138
|
-
from: (s) => parseInt(s, 10),
|
|
139
|
-
};
|
|
140
|
-
const sinkFn = sink(graph, strToNum, strToNum);
|
|
141
|
-
// Snapshot with string keys and values
|
|
142
|
-
const snapshot = [['10', '20', 5], ['30', '40', 10]];
|
|
143
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
144
|
-
const { stream: source } = sinkAdapter.build();
|
|
145
|
-
// Should be converted to numbers
|
|
146
|
-
const entries = [...source.snapshot.getEntries()];
|
|
147
|
-
expect(entries).toContainEqual([10, 20, 5]);
|
|
148
|
-
expect(entries).toContainEqual([30, 40, 10]);
|
|
149
|
-
});
|
|
150
|
-
it('should handle empty snapshot', () => {
|
|
151
|
-
const graph = new Graph();
|
|
152
|
-
const sinkFn = sink(graph, iso.id(), iso.id());
|
|
153
|
-
const sinkAdapter = sinkFn([]);
|
|
154
|
-
const { stream: source } = sinkAdapter.build();
|
|
155
|
-
expect([...source.snapshot.getEntries()]).toEqual([]);
|
|
156
|
-
});
|
|
157
|
-
it('should create new source instances on each build call', () => {
|
|
158
|
-
const graph = new Graph();
|
|
159
|
-
const sinkFn = sink(graph, iso.id(), iso.id());
|
|
160
|
-
const sinkAdapter = sinkFn([['test', 1, 1]]);
|
|
161
|
-
const { stream: source1 } = sinkAdapter.build();
|
|
162
|
-
const { stream: source2 } = sinkAdapter.build();
|
|
163
|
-
// Each build creates a new instance - use strict equality to avoid vitest's deep comparison
|
|
164
|
-
expect(source1 === source2).toBe(false);
|
|
165
|
-
// But both have the same initial snapshot
|
|
166
|
-
expect([...source1.snapshot.getEntries()]).toEqual([['test', 1, 1]]);
|
|
167
|
-
expect([...source2.snapshot.getEntries()]).toEqual([['test', 1, 1]]);
|
|
168
|
-
});
|
|
169
|
-
it('should work with complex key and value types', () => {
|
|
170
|
-
const graph = new Graph();
|
|
171
|
-
const keyIso = iso.object({
|
|
172
|
-
id: iso.id(),
|
|
173
|
-
});
|
|
174
|
-
const valueIso = iso.object({
|
|
175
|
-
data: iso.id(),
|
|
176
|
-
});
|
|
177
|
-
const sinkFn = sink(graph, keyIso, valueIso);
|
|
178
|
-
const snapshot = [
|
|
179
|
-
[{ id: 1 }, { data: 'first' }, 5],
|
|
180
|
-
[{ id: 2 }, { data: 'second' }, 10],
|
|
181
|
-
];
|
|
182
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
183
|
-
const { stream: source } = sinkAdapter.build();
|
|
184
|
-
const entries = [...source.snapshot.getEntries()];
|
|
185
|
-
expect(entries.length).toBe(2);
|
|
186
|
-
expect(entries[0][0]).toEqual({ id: 1 });
|
|
187
|
-
expect(entries[0][1]).toEqual({ data: 'first' });
|
|
188
|
-
expect(entries[0][2]).toBe(5);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { Graph } from 'derivation';
|
|
3
|
-
import { ZSet, Reactive, ZSetOperations, ZSetChangeInput } from '@derivation/composable';
|
|
4
|
-
import { ReactiveSetSourceAdapter, ReactiveSetSinkAdapter, sink } from '../reactive-set-adapter.js';
|
|
5
|
-
import * as iso from '../iso.js';
|
|
6
|
-
describe('ReactiveSetSourceAdapter', () => {
|
|
7
|
-
it('should provide snapshot as array of [item, weight] tuples', () => {
|
|
8
|
-
const graph = new Graph();
|
|
9
|
-
let zset = new ZSet();
|
|
10
|
-
zset = zset.add('a', 1).add('b', 2);
|
|
11
|
-
const reactiveSet = Reactive.create(graph, new ZSetOperations(), new ZSetChangeInput(graph), zset);
|
|
12
|
-
const adapter = new ReactiveSetSourceAdapter(reactiveSet, iso.id());
|
|
13
|
-
const snapshot = adapter.Snapshot;
|
|
14
|
-
expect(snapshot).toEqual([['a', 1], ['b', 2]]);
|
|
15
|
-
});
|
|
16
|
-
it('should transform elements using isomorphism', () => {
|
|
17
|
-
const graph = new Graph();
|
|
18
|
-
let zset = new ZSet();
|
|
19
|
-
zset = zset.add(1, 2).add(3, 4);
|
|
20
|
-
const reactiveSet = Reactive.create(graph, new ZSetOperations(), new ZSetChangeInput(graph), zset);
|
|
21
|
-
const numToString = {
|
|
22
|
-
to: (n) => n.toString(),
|
|
23
|
-
from: (s) => parseInt(s, 10),
|
|
24
|
-
};
|
|
25
|
-
const adapter = new ReactiveSetSourceAdapter(reactiveSet, numToString);
|
|
26
|
-
const snapshot = adapter.Snapshot;
|
|
27
|
-
expect(snapshot).toEqual([['1', 2], ['3', 4]]);
|
|
28
|
-
});
|
|
29
|
-
it('should provide last change after modifications', () => {
|
|
30
|
-
const graph = new Graph();
|
|
31
|
-
const input = new ZSetChangeInput(graph);
|
|
32
|
-
const reactiveSet = Reactive.create(graph, new ZSetOperations(), input, new ZSet());
|
|
33
|
-
const adapter = new ReactiveSetSourceAdapter(reactiveSet, iso.id());
|
|
34
|
-
// Push a change
|
|
35
|
-
let zset = new ZSet();
|
|
36
|
-
zset = zset.add('x', 1);
|
|
37
|
-
input.push(zset);
|
|
38
|
-
graph.step();
|
|
39
|
-
const lastChange = adapter.LastChange;
|
|
40
|
-
expect(lastChange).toEqual([['x', 1]]);
|
|
41
|
-
});
|
|
42
|
-
it('should return the underlying reactive set', () => {
|
|
43
|
-
const graph = new Graph();
|
|
44
|
-
const reactiveSet = Reactive.create(graph, new ZSetOperations(), new ZSetChangeInput(graph), new ZSet());
|
|
45
|
-
const adapter = new ReactiveSetSourceAdapter(reactiveSet, iso.id());
|
|
46
|
-
expect(adapter.Stream).toBe(reactiveSet);
|
|
47
|
-
});
|
|
48
|
-
it('should handle empty sets', () => {
|
|
49
|
-
const graph = new Graph();
|
|
50
|
-
const reactiveSet = Reactive.create(graph, new ZSetOperations(), new ZSetChangeInput(graph), new ZSet());
|
|
51
|
-
const adapter = new ReactiveSetSourceAdapter(reactiveSet, iso.id());
|
|
52
|
-
expect(adapter.Snapshot).toEqual([]);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe('ReactiveSetSinkAdapter', () => {
|
|
56
|
-
it('should build a reactive set source', () => {
|
|
57
|
-
const graph = new Graph();
|
|
58
|
-
const adapter = new ReactiveSetSinkAdapter(graph, iso.compose(iso.zset(iso.id()), iso.zsetToArray()), []);
|
|
59
|
-
const { stream: source } = adapter.build();
|
|
60
|
-
expect(source).toBeDefined();
|
|
61
|
-
expect([...source.snapshot.getEntries()]).toEqual([]);
|
|
62
|
-
});
|
|
63
|
-
it('should apply changes to a reactive set', () => {
|
|
64
|
-
const graph = new Graph();
|
|
65
|
-
const isoComposed = iso.compose(iso.zset(iso.id()), iso.zsetToArray());
|
|
66
|
-
const adapter = new ReactiveSetSinkAdapter(graph, isoComposed, []);
|
|
67
|
-
const { stream: source, input } = adapter.build();
|
|
68
|
-
// Apply a change to add items
|
|
69
|
-
const change = [['a', 1], ['b', 2]];
|
|
70
|
-
adapter.apply(change, input);
|
|
71
|
-
graph.step();
|
|
72
|
-
// Check snapshot after change
|
|
73
|
-
const entries = [...source.snapshot.getEntries()];
|
|
74
|
-
expect(entries).toContainEqual(['a', 1]);
|
|
75
|
-
expect(entries).toContainEqual(['b', 2]);
|
|
76
|
-
});
|
|
77
|
-
it('should transform elements using isomorphism', () => {
|
|
78
|
-
const graph = new Graph();
|
|
79
|
-
const numToString = {
|
|
80
|
-
to: (n) => n.toString(),
|
|
81
|
-
from: (s) => parseInt(s, 10),
|
|
82
|
-
};
|
|
83
|
-
const isoComposed = iso.compose(iso.zset(numToString), iso.zsetToArray());
|
|
84
|
-
const adapter = new ReactiveSetSinkAdapter(graph, isoComposed, []);
|
|
85
|
-
const { stream: source, input } = adapter.build();
|
|
86
|
-
// Apply change with string values
|
|
87
|
-
const change = [['5', 1], ['10', 2]];
|
|
88
|
-
adapter.apply(change, input);
|
|
89
|
-
graph.step();
|
|
90
|
-
// Should be converted to numbers
|
|
91
|
-
const entries = [...source.snapshot.getEntries()];
|
|
92
|
-
expect(entries).toContainEqual([5, 1]);
|
|
93
|
-
expect(entries).toContainEqual([10, 2]);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
describe('sink function', () => {
|
|
97
|
-
it('should create a sink initialized with snapshot', () => {
|
|
98
|
-
const graph = new Graph();
|
|
99
|
-
const sinkFn = sink(graph, iso.id());
|
|
100
|
-
const snapshot = [['item1', 1], ['item2', 2]];
|
|
101
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
102
|
-
const { stream: source } = sinkAdapter.build();
|
|
103
|
-
const entries = [...source.snapshot.getEntries()];
|
|
104
|
-
expect(entries).toContainEqual(['item1', 1]);
|
|
105
|
-
expect(entries).toContainEqual(['item2', 2]);
|
|
106
|
-
});
|
|
107
|
-
it('should allow applying changes after initialization', () => {
|
|
108
|
-
const graph = new Graph();
|
|
109
|
-
const sinkFn = sink(graph, iso.id());
|
|
110
|
-
const snapshot = [['a', 1]];
|
|
111
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
112
|
-
const { stream: source, input } = sinkAdapter.build();
|
|
113
|
-
// Apply a change
|
|
114
|
-
const change = [['b', 2], ['c', 3]];
|
|
115
|
-
sinkAdapter.apply(change, input);
|
|
116
|
-
graph.step();
|
|
117
|
-
const entries = [...source.snapshot.getEntries()];
|
|
118
|
-
expect(entries.length).toBeGreaterThan(1);
|
|
119
|
-
expect(entries).toContainEqual(['b', 2]);
|
|
120
|
-
expect(entries).toContainEqual(['c', 3]);
|
|
121
|
-
});
|
|
122
|
-
it('should transform snapshot using isomorphism', () => {
|
|
123
|
-
const graph = new Graph();
|
|
124
|
-
const stringToNum = {
|
|
125
|
-
to: (n) => n.toString(),
|
|
126
|
-
from: (s) => parseInt(s, 10),
|
|
127
|
-
};
|
|
128
|
-
const sinkFn = sink(graph, stringToNum);
|
|
129
|
-
// Snapshot with string values
|
|
130
|
-
const snapshot = [['42', 1], ['99', 2]];
|
|
131
|
-
const sinkAdapter = sinkFn(snapshot);
|
|
132
|
-
const { stream: source } = sinkAdapter.build();
|
|
133
|
-
// Should be converted to numbers
|
|
134
|
-
const entries = [...source.snapshot.getEntries()];
|
|
135
|
-
expect(entries).toContainEqual([42, 1]);
|
|
136
|
-
expect(entries).toContainEqual([99, 2]);
|
|
137
|
-
});
|
|
138
|
-
it('should handle empty snapshot', () => {
|
|
139
|
-
const graph = new Graph();
|
|
140
|
-
const sinkFn = sink(graph, iso.id());
|
|
141
|
-
const sinkAdapter = sinkFn([]);
|
|
142
|
-
const { stream: source } = sinkAdapter.build();
|
|
143
|
-
expect([...source.snapshot.getEntries()]).toEqual([]);
|
|
144
|
-
});
|
|
145
|
-
it('should create new source instances on each build call', () => {
|
|
146
|
-
const graph = new Graph();
|
|
147
|
-
const sinkFn = sink(graph, iso.id());
|
|
148
|
-
const sinkAdapter = sinkFn([['test', 1]]);
|
|
149
|
-
const { stream: source1 } = sinkAdapter.build();
|
|
150
|
-
const { stream: source2 } = sinkAdapter.build();
|
|
151
|
-
// Each build creates a new instance - use strict equality to avoid vitest's deep comparison
|
|
152
|
-
expect(source1 === source2).toBe(false);
|
|
153
|
-
// But both have the same initial snapshot
|
|
154
|
-
expect([...source1.snapshot.getEntries()]).toEqual([['test', 1]]);
|
|
155
|
-
expect([...source2.snapshot.getEntries()]).toEqual([['test', 1]]);
|
|
156
|
-
});
|
|
157
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|