@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.
Files changed (96) hide show
  1. package/dist/client-DJZfuakf.d.cts +27 -0
  2. package/dist/{client.d.ts → client-TPsVZH_B.d.ts} +7 -4
  3. package/dist/index.cjs +335 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +55 -0
  6. package/dist/index.d.ts +55 -7
  7. package/dist/index.js +305 -6
  8. package/dist/index.js.map +1 -0
  9. package/dist/iso.cjs +151 -0
  10. package/dist/iso.cjs.map +1 -0
  11. package/dist/iso.d.cts +52 -0
  12. package/dist/iso.d.ts +35 -19
  13. package/dist/iso.js +99 -101
  14. package/dist/iso.js.map +1 -0
  15. package/dist/{presence-manager.d.ts → presence-manager-4LlEyuGp.d.cts} +3 -1
  16. package/dist/presence-manager-4LlEyuGp.d.ts +7 -0
  17. package/dist/shared-worker-client.cjs +271 -0
  18. package/dist/shared-worker-client.cjs.map +1 -0
  19. package/dist/shared-worker-client.d.cts +26 -0
  20. package/dist/shared-worker-client.d.ts +8 -4
  21. package/dist/shared-worker-client.js +243 -24
  22. package/dist/shared-worker-client.js.map +1 -0
  23. package/dist/shared-worker-server.cjs +363 -0
  24. package/dist/shared-worker-server.cjs.map +1 -0
  25. package/dist/shared-worker-server.d.cts +31 -0
  26. package/dist/shared-worker-server.d.ts +9 -6
  27. package/dist/shared-worker-server.js +332 -58
  28. package/dist/shared-worker-server.js.map +1 -0
  29. package/dist/stream-types-Q_EqNLtO.d.cts +46 -0
  30. package/dist/stream-types-Q_EqNLtO.d.ts +46 -0
  31. package/dist/transport.cjs +19 -0
  32. package/dist/transport.cjs.map +1 -0
  33. package/dist/transport.d.cts +29 -0
  34. package/dist/transport.d.ts +3 -1
  35. package/dist/transport.js +1 -1
  36. package/dist/transport.js.map +1 -0
  37. package/dist/web-socket-server.cjs +469 -0
  38. package/dist/web-socket-server.cjs.map +1 -0
  39. package/dist/web-socket-server.d.cts +14 -0
  40. package/dist/web-socket-server.d.ts +10 -7
  41. package/dist/web-socket-server.js +437 -52
  42. package/dist/web-socket-server.js.map +1 -0
  43. package/dist/web-socket-transport.cjs +52 -0
  44. package/dist/web-socket-transport.cjs.map +1 -0
  45. package/dist/web-socket-transport.d.cts +16 -0
  46. package/dist/web-socket-transport.d.ts +5 -2
  47. package/dist/web-socket-transport.js +27 -25
  48. package/dist/web-socket-transport.js.map +1 -0
  49. package/package.json +26 -17
  50. package/dist/client-handler.d.ts +0 -27
  51. package/dist/client-handler.js +0 -187
  52. package/dist/client-message.d.ts +0 -57
  53. package/dist/client-message.js +0 -59
  54. package/dist/client.js +0 -133
  55. package/dist/messageport-transport.d.ts +0 -13
  56. package/dist/messageport-transport.js +0 -28
  57. package/dist/node-web-socket-transport.d.ts +0 -16
  58. package/dist/node-web-socket-transport.js +0 -33
  59. package/dist/presence-manager.js +0 -1
  60. package/dist/queue.d.ts +0 -9
  61. package/dist/queue.js +0 -32
  62. package/dist/rate-limiter.d.ts +0 -7
  63. package/dist/rate-limiter.js +0 -24
  64. package/dist/reactive-map-adapter.d.ts +0 -24
  65. package/dist/reactive-map-adapter.js +0 -43
  66. package/dist/reactive-set-adapter.d.ts +0 -24
  67. package/dist/reactive-set-adapter.js +0 -41
  68. package/dist/server-message.d.ts +0 -48
  69. package/dist/server-message.js +0 -52
  70. package/dist/shared-worker-client-handler.d.ts +0 -27
  71. package/dist/shared-worker-client-handler.js +0 -149
  72. package/dist/stream-adapter.d.ts +0 -23
  73. package/dist/stream-adapter.js +0 -35
  74. package/dist/stream-types.d.ts +0 -44
  75. package/dist/stream-types.js +0 -1
  76. package/dist/tests/context.test.d.ts +0 -1
  77. package/dist/tests/context.test.js +0 -252
  78. package/dist/tests/iso.test.d.ts +0 -1
  79. package/dist/tests/iso.test.js +0 -186
  80. package/dist/tests/messages.test.d.ts +0 -1
  81. package/dist/tests/messages.test.js +0 -152
  82. package/dist/tests/mutations.test.d.ts +0 -1
  83. package/dist/tests/mutations.test.js +0 -122
  84. package/dist/tests/queue.test.d.ts +0 -1
  85. package/dist/tests/queue.test.js +0 -84
  86. package/dist/tests/reactive-map-adapter.test.d.ts +0 -1
  87. package/dist/tests/reactive-map-adapter.test.js +0 -190
  88. package/dist/tests/reactive-set-adapter.test.d.ts +0 -1
  89. package/dist/tests/reactive-set-adapter.test.js +0 -157
  90. package/dist/tests/stream-adapter.test.d.ts +0 -1
  91. package/dist/tests/stream-adapter.test.js +0 -119
  92. package/dist/tests/weak-list.test.d.ts +0 -1
  93. package/dist/tests/weak-list.test.js +0 -100
  94. package/dist/tsconfig.tsbuildinfo +0 -1
  95. package/dist/weak-list.d.ts +0 -5
  96. 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 {};
@@ -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 {};