@geometra/client 0.3.2 → 1.0.0-rc.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geometra/client",
3
- "version": "0.3.2",
3
+ "version": "1.0.0-rc.1",
4
4
  "description": "Thin client that receives server-computed geometry and paints via renderer",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,6 +33,6 @@
33
33
  "check": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@geometra/core": "^0.3.2"
36
+ "@geometra/core": "^1.0.0-rc.1"
37
37
  }
38
38
  }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=client-protocol.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-protocol.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client-protocol.test.ts"],"names":[],"mappings":""}
@@ -1,50 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { applyServerMessage } from '../client.js';
3
- function layout(x = 0, y = 0, width = 100, height = 50) {
4
- return { x, y, width, height, children: [] };
5
- }
6
- function tree() {
7
- return {
8
- kind: 'box',
9
- props: {},
10
- children: [],
11
- };
12
- }
13
- function createRendererSpy() {
14
- const renders = [];
15
- const renderer = {
16
- render: (nextLayout, nextTree) => {
17
- renders.push({ layout: nextLayout, tree: nextTree });
18
- },
19
- destroy: () => { },
20
- };
21
- return { renderer, renders };
22
- }
23
- describe('applyServerMessage', () => {
24
- it('surfaces server errors and recovers on subsequent frame', () => {
25
- const { renderer, renders } = createRendererSpy();
26
- const state = { layout: null, tree: null };
27
- const errors = [];
28
- applyServerMessage(state, renderer, { type: 'error', message: 'server exploded' }, (err) => {
29
- errors.push(String(err));
30
- });
31
- expect(errors[0]).toContain('server exploded');
32
- expect(renders.length).toBe(0);
33
- const nextLayout = layout(10, 20, 120, 40);
34
- const nextTree = tree();
35
- applyServerMessage(state, renderer, { type: 'frame', layout: nextLayout, tree: nextTree });
36
- expect(state.layout).toEqual(nextLayout);
37
- expect(state.tree).toEqual(nextTree);
38
- expect(renders.length).toBe(1);
39
- });
40
- it('surfaces protocol mismatch and ignores incompatible frame', () => {
41
- const { renderer, renders } = createRendererSpy();
42
- const state = { layout: null, tree: null };
43
- const errors = [];
44
- applyServerMessage(state, renderer, { type: 'frame', layout: layout(), tree: tree(), protocolVersion: 999 }, (err) => errors.push(String(err)));
45
- expect(errors[0]).toContain('newer than client protocol');
46
- expect(state.layout).toBeNull();
47
- expect(renders.length).toBe(0);
48
- });
49
- });
50
- //# sourceMappingURL=client-protocol.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-protocol.test.js","sourceRoot":"","sources":["../../src/__tests__/client-protocol.test.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=client-reconnect.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-reconnect.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client-reconnect.test.ts"],"names":[],"mappings":""}
@@ -1,87 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createClient } from '../client.js';
3
- async function waitFor(predicate, timeoutMs, label) {
4
- const start = Date.now();
5
- while (Date.now() - start < timeoutMs) {
6
- if (predicate())
7
- return;
8
- await new Promise(resolve => setTimeout(resolve, 50));
9
- }
10
- throw new Error(`Timed out waiting for ${label}`);
11
- }
12
- describe('client reconnect integration', () => {
13
- it('reconnects and resyncs state after server restart', async () => {
14
- class MockWebSocket {
15
- static CONNECTING = 0;
16
- static OPEN = 1;
17
- static CLOSING = 2;
18
- static CLOSED = 3;
19
- readyState = MockWebSocket.OPEN;
20
- listeners = new Map();
21
- constructor(_url) {
22
- sockets.push(this);
23
- setTimeout(() => this.emit('open'), 0);
24
- }
25
- addEventListener(type, cb) {
26
- const current = this.listeners.get(type) ?? [];
27
- current.push(cb);
28
- this.listeners.set(type, current);
29
- }
30
- removeEventListener(type, cb) {
31
- const current = this.listeners.get(type) ?? [];
32
- this.listeners.set(type, current.filter(fn => fn !== cb));
33
- }
34
- send(_data) { }
35
- close() {
36
- this.readyState = MockWebSocket.CLOSED;
37
- this.emit('close');
38
- }
39
- emit(type, event = {}) {
40
- for (const cb of this.listeners.get(type) ?? [])
41
- cb(event);
42
- }
43
- }
44
- const sockets = [];
45
- globalThis.WebSocket = MockWebSocket;
46
- const renders = [];
47
- const renderer = {
48
- render: (layout, _tree) => {
49
- renders.push({ ...layout, children: layout.children });
50
- },
51
- destroy: () => { },
52
- };
53
- const client = createClient({
54
- url: 'ws://mock.test',
55
- renderer,
56
- reconnect: true,
57
- forwardKeyboard: false,
58
- forwardComposition: false,
59
- forwardResize: false,
60
- keyboardTarget: {},
61
- });
62
- await waitFor(() => sockets.length === 1, 2000, 'first socket');
63
- sockets[0].emit('message', {
64
- data: JSON.stringify({
65
- type: 'frame',
66
- layout: { x: 0, y: 0, width: 40, height: 20, children: [] },
67
- tree: { kind: 'box', props: {}, children: [] },
68
- protocolVersion: 1,
69
- }),
70
- });
71
- await waitFor(() => renders.length > 0, 4000, 'initial frame');
72
- expect(client.layout?.width).toBe(40);
73
- sockets[0].close();
74
- await waitFor(() => sockets.length === 2, 4000, 'reconnect socket');
75
- sockets[1].emit('message', {
76
- data: JSON.stringify({
77
- type: 'frame',
78
- layout: { x: 0, y: 0, width: 77, height: 33, children: [] },
79
- tree: { kind: 'box', props: {}, children: [] },
80
- protocolVersion: 1,
81
- }),
82
- });
83
- await waitFor(() => client.layout?.width === 77 && client.layout?.height === 33, 6000, 'reconnect resync frame');
84
- client.close();
85
- }, 20000);
86
- });
87
- //# sourceMappingURL=client-reconnect.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client-reconnect.test.js","sourceRoot":"","sources":["../../src/__tests__/client-reconnect.test.ts"],"names":[],"mappings":"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"}