@geometra/server 1.0.0 → 1.0.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/README.md +5 -2
- package/dist/binary-frame.d.ts +13 -0
- package/dist/binary-frame.d.ts.map +1 -0
- package/dist/binary-frame.js +39 -0
- package/dist/binary-frame.js.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/protocol.d.ts +9 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +4 -0
- package/dist/protocol.js.map +1 -1
- package/dist/server.d.ts +30 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +95 -8
- package/dist/server.js.map +1 -1
- package/package.json +2 -2
- package/dist/__tests__/protocol-compat.test.d.ts +0 -2
- package/dist/__tests__/protocol-compat.test.d.ts.map +0 -1
- package/dist/__tests__/protocol-compat.test.js +0 -77
- package/dist/__tests__/protocol-compat.test.js.map +0 -1
- package/dist/__tests__/protocol-diff.test.d.ts +0 -2
- package/dist/__tests__/protocol-diff.test.d.ts.map +0 -1
- package/dist/__tests__/protocol-diff.test.js +0 -91
- package/dist/__tests__/protocol-diff.test.js.map +0 -1
- package/dist/__tests__/protocol-fixture.test.d.ts +0 -2
- package/dist/__tests__/protocol-fixture.test.d.ts.map +0 -1
- package/dist/__tests__/protocol-fixture.test.js +0 -20
- package/dist/__tests__/protocol-fixture.test.js.map +0 -1
- package/dist/__tests__/protocol-perf-smoke.test.d.ts +0 -2
- package/dist/__tests__/protocol-perf-smoke.test.d.ts.map +0 -1
- package/dist/__tests__/protocol-perf-smoke.test.js +0 -68
- package/dist/__tests__/protocol-perf-smoke.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -8,9 +8,12 @@ Server-side layout engine with WebSocket streaming for Geometra. Computes layout
|
|
|
8
8
|
npm install @geometra/server
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
## Key
|
|
11
|
+
## Key exports
|
|
12
12
|
|
|
13
|
-
- `createServer`
|
|
13
|
+
- `createServer` — Geometra layout server with WebSocket streaming
|
|
14
|
+
- `shouldDeferClientSend` — backpressure helper (used by the server and tests)
|
|
15
|
+
- `onTransportMetrics` (option) — per-broadcast deferred send count, coalesced patch delta, binary outbound count
|
|
16
|
+
- Binary frame helpers (`encodeBinaryFrameJson`, …) — optional GEOM v1 JSON envelopes; see repo `PROTOCOL_COMPATIBILITY.md`
|
|
14
17
|
|
|
15
18
|
## Usage
|
|
16
19
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
/** Magic prefix for optional v1 binary JSON envelopes (same JSON payload as text frames). */
|
|
3
|
+
export declare const BINARY_FRAME_MAGIC: Buffer<ArrayBuffer>;
|
|
4
|
+
/** True if buffer looks like a v1 binary envelope (GEOM + version 1). */
|
|
5
|
+
export declare function isBinaryFrameBuffer(data: Buffer | Uint8Array): boolean;
|
|
6
|
+
/** Wrap UTF-8 JSON bytes in a binary envelope for WebSocket binary frames. */
|
|
7
|
+
export declare function encodeBinaryFrameJson(jsonUtf8: string): Buffer;
|
|
8
|
+
/**
|
|
9
|
+
* Decode JSON string from a v1 binary envelope.
|
|
10
|
+
* Bytes after `header + payloadLength` are ignored so callers may pass a longer buffer.
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeBinaryFrameJson(data: Buffer): string;
|
|
13
|
+
//# sourceMappingURL=binary-frame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-frame.d.ts","sourceRoot":"","sources":["../src/binary-frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,6FAA6F;AAC7F,eAAO,MAAM,kBAAkB,qBAAwC,CAAA;AAKvE,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAStE;AAED,8EAA8E;AAC9E,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAO9D;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS1D"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
2
|
+
/** Magic prefix for optional v1 binary JSON envelopes (same JSON payload as text frames). */
|
|
3
|
+
export const BINARY_FRAME_MAGIC = Buffer.from([0x47, 0x45, 0x4f, 0x4d]); // GEOM
|
|
4
|
+
const FRAME_VERSION = 1;
|
|
5
|
+
const HEADER_BYTES = 9;
|
|
6
|
+
/** True if buffer looks like a v1 binary envelope (GEOM + version 1). */
|
|
7
|
+
export function isBinaryFrameBuffer(data) {
|
|
8
|
+
if (data.length < HEADER_BYTES)
|
|
9
|
+
return false;
|
|
10
|
+
return (data[0] === 0x47 &&
|
|
11
|
+
data[1] === 0x45 &&
|
|
12
|
+
data[2] === 0x4f &&
|
|
13
|
+
data[3] === 0x4d &&
|
|
14
|
+
data[4] === FRAME_VERSION);
|
|
15
|
+
}
|
|
16
|
+
/** Wrap UTF-8 JSON bytes in a binary envelope for WebSocket binary frames. */
|
|
17
|
+
export function encodeBinaryFrameJson(jsonUtf8) {
|
|
18
|
+
const payload = Buffer.from(jsonUtf8, 'utf8');
|
|
19
|
+
const header = Buffer.alloc(HEADER_BYTES);
|
|
20
|
+
BINARY_FRAME_MAGIC.copy(header, 0);
|
|
21
|
+
header.writeUInt8(FRAME_VERSION, 4);
|
|
22
|
+
header.writeUInt32LE(payload.length, 5);
|
|
23
|
+
return Buffer.concat([header, payload]);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Decode JSON string from a v1 binary envelope.
|
|
27
|
+
* Bytes after `header + payloadLength` are ignored so callers may pass a longer buffer.
|
|
28
|
+
*/
|
|
29
|
+
export function decodeBinaryFrameJson(data) {
|
|
30
|
+
if (!isBinaryFrameBuffer(data)) {
|
|
31
|
+
throw new Error('Not a GEOM binary frame');
|
|
32
|
+
}
|
|
33
|
+
const len = data.readUInt32LE(5);
|
|
34
|
+
if (HEADER_BYTES + len > data.length) {
|
|
35
|
+
throw new Error('Truncated binary frame payload');
|
|
36
|
+
}
|
|
37
|
+
return data.subarray(HEADER_BYTES, HEADER_BYTES + len).toString('utf8');
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=binary-frame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-frame.js","sourceRoot":"","sources":["../src/binary-frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAEpC,6FAA6F;AAC7F,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAA,CAAC,OAAO;AAE/E,MAAM,aAAa,GAAG,CAAC,CAAA;AACvB,MAAM,YAAY,GAAG,CAAC,CAAA;AAEtB,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,IAAyB;IAC3D,IAAI,IAAI,CAAC,MAAM,GAAG,YAAY;QAAE,OAAO,KAAK,CAAA;IAC5C,OAAO,CACL,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAChB,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAChB,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAChB,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI;QAChB,IAAI,CAAC,CAAC,CAAC,KAAK,aAAa,CAC1B,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;IAC7C,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;IACzC,kBAAkB,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IAClC,MAAM,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC,CAAA;IACnC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;IACvC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;IAC5C,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;IAChC,IAAI,YAAY,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACnD,CAAC;IACD,OAAO,IAAI,CAAC,QAAQ,CAAC,YAAY,EAAE,YAAY,GAAG,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;AACzE,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export { createServer } from './server.js';
|
|
2
|
-
export type { TexturaServer, TexturaServerOptions } from './server.js';
|
|
3
|
-
export { diffLayout } from './protocol.js';
|
|
1
|
+
export { createServer, shouldDeferClientSend } from './server.js';
|
|
2
|
+
export type { TexturaServer, TexturaServerOptions, ServerTransportMetrics } from './server.js';
|
|
3
|
+
export { diffLayout, CLOSE_AUTH_FAILED, CLOSE_FORBIDDEN } from './protocol.js';
|
|
4
4
|
export type { ServerMessage, ClientMessage, LayoutPatch } from './protocol.js';
|
|
5
|
+
export { encodeBinaryFrameJson, decodeBinaryFrameJson, isBinaryFrameBuffer } from './binary-frame.js';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACjE,YAAY,EAAE,aAAa,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC9E,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC9E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { createServer } from './server.js';
|
|
2
|
-
export { diffLayout } from './protocol.js';
|
|
1
|
+
export { createServer, shouldDeferClientSend } from './server.js';
|
|
2
|
+
export { diffLayout, CLOSE_AUTH_FAILED, CLOSE_FORBIDDEN } from './protocol.js';
|
|
3
|
+
export { encodeBinaryFrameJson, decodeBinaryFrameJson, isBinaryFrameBuffer } from './binary-frame.js';
|
|
3
4
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAEjE,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE9E,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import type { ComputedLayout } from 'textura';
|
|
|
2
2
|
import type { UIElement } from '@geometra/core';
|
|
3
3
|
/** Increment when the wire message shape changes in a non-backward-compatible way. */
|
|
4
4
|
export declare const PROTOCOL_VERSION = 1;
|
|
5
|
+
/** WebSocket close code: connection rejected by onConnection hook. */
|
|
6
|
+
export declare const CLOSE_AUTH_FAILED = 4001;
|
|
7
|
+
/** WebSocket close code: message rejected by onMessage hook. */
|
|
8
|
+
export declare const CLOSE_FORBIDDEN = 4003;
|
|
5
9
|
/**
|
|
6
10
|
* Protocol compatibility rule:
|
|
7
11
|
* - undefined means legacy v1 and is accepted.
|
|
@@ -23,6 +27,7 @@ export type ServerMessage = (VersionedMessage & {
|
|
|
23
27
|
}) | (VersionedMessage & {
|
|
24
28
|
type: 'error';
|
|
25
29
|
message: string;
|
|
30
|
+
code?: number;
|
|
26
31
|
});
|
|
27
32
|
/** Messages sent from client to server. */
|
|
28
33
|
export type ClientMessage = (VersionedMessage & {
|
|
@@ -47,6 +52,10 @@ export type ClientMessage = (VersionedMessage & {
|
|
|
47
52
|
type: 'resize';
|
|
48
53
|
width: number;
|
|
49
54
|
height: number;
|
|
55
|
+
/** Optional capability negotiation (v1+). */
|
|
56
|
+
capabilities?: {
|
|
57
|
+
binaryFraming?: boolean;
|
|
58
|
+
};
|
|
50
59
|
});
|
|
51
60
|
/** A patch describing a change to a single node's geometry. */
|
|
52
61
|
export interface LayoutPatch {
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,IAAI,CAAA;AAEjC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,cAAc,SAAmB,GAChC,OAAO,CAGT;AAED,UAAU,gBAAgB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GACrB,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAC/E,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,GAC9D,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,sFAAsF;AACtF,eAAO,MAAM,gBAAgB,IAAI,CAAA;AAEjC,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,OAAO,CAAA;AAErC,gEAAgE;AAChE,eAAO,MAAM,eAAe,OAAO,CAAA;AAEnC;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,cAAc,SAAmB,GAChC,OAAO,CAGT;AAED,UAAU,gBAAgB;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GACrB,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,GAC/E,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAAC,GAC9D,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAE1E,2CAA2C;AAC3C,MAAM,MAAM,aAAa,GACrB,CAAC,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC/E,CAAC,gBAAgB,GAAG;IAClB,IAAI,EAAE,KAAK,CAAA;IACX,SAAS,EAAE,WAAW,GAAG,SAAS,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,OAAO,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;CAChB,CAAC,GACF,CAAC,gBAAgB,GAAG;IAClB,IAAI,EAAE,aAAa,CAAA;IACnB,SAAS,EAAE,oBAAoB,GAAG,qBAAqB,GAAG,kBAAkB,CAAA;IAC5E,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,GACF,CAAC,gBAAgB,GAAG;IAClB,IAAI,EAAE,QAAQ,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,6CAA6C;IAC7C,YAAY,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC3C,CAAC,CAAA;AAEN,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,WAAW,EAAE,CAgBrE;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CACxB,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,cAAc,EACpB,IAAI,GAAE,MAAM,EAAO,GAClB,WAAW,EAAE,CAuBf"}
|
package/dist/protocol.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/** Increment when the wire message shape changes in a non-backward-compatible way. */
|
|
2
2
|
export const PROTOCOL_VERSION = 1;
|
|
3
|
+
/** WebSocket close code: connection rejected by onConnection hook. */
|
|
4
|
+
export const CLOSE_AUTH_FAILED = 4001;
|
|
5
|
+
/** WebSocket close code: message rejected by onMessage hook. */
|
|
6
|
+
export const CLOSE_FORBIDDEN = 4003;
|
|
3
7
|
/**
|
|
4
8
|
* Protocol compatibility rule:
|
|
5
9
|
* - undefined means legacy v1 and is accepted.
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAGA,sFAAsF;AACtF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,WAA+B,EAC/B,cAAc,GAAG,gBAAgB;IAEjC,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IAC1C,OAAO,WAAW,IAAI,cAAc,CAAA;AACtC,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
1
2
|
import type { UIElement } from '@geometra/core';
|
|
3
|
+
import type { ClientMessage } from './protocol.js';
|
|
2
4
|
export interface TexturaServerOptions {
|
|
3
5
|
/** Port to listen on. Default: 3100. */
|
|
4
6
|
port?: number;
|
|
@@ -8,6 +10,33 @@ export interface TexturaServerOptions {
|
|
|
8
10
|
height?: number;
|
|
9
11
|
/** Called on errors during layout computation or broadcasting. */
|
|
10
12
|
onError?: (error: unknown) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Called when a new WebSocket client connects. Receives the HTTP upgrade
|
|
15
|
+
* request (headers, URL, etc.). Return any truthy value to accept the
|
|
16
|
+
* connection — that value becomes the connection context passed to
|
|
17
|
+
* `onMessage` and `onDisconnect`. Return `null` to reject (closes with
|
|
18
|
+
* 4001). Throwing also rejects. Async handlers are supported.
|
|
19
|
+
*/
|
|
20
|
+
onConnection?: (request: IncomingMessage) => unknown | Promise<unknown>;
|
|
21
|
+
/** Called when an accepted client disconnects. Receives the connection context. */
|
|
22
|
+
onDisconnect?: (context: unknown) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Called before processing each client message. Return `false` to reject
|
|
25
|
+
* the message (sends a 4003 error to the client, event is not dispatched).
|
|
26
|
+
*/
|
|
27
|
+
onMessage?: (message: ClientMessage, context: unknown) => boolean;
|
|
28
|
+
/** Backpressure threshold; clients above this buffered amount are deferred. */
|
|
29
|
+
backpressureBytes?: number;
|
|
30
|
+
/** Per-broadcast transport telemetry (backpressure, coalescing, binary outbound). */
|
|
31
|
+
onTransportMetrics?: (metrics: ServerTransportMetrics) => void;
|
|
32
|
+
}
|
|
33
|
+
/** Emitted after each successful broadcast (not on early no-op returns). */
|
|
34
|
+
export interface ServerTransportMetrics {
|
|
35
|
+
deferredSends: number;
|
|
36
|
+
/** How many raw patches were merged by coalescing (0 when no previous layout). */
|
|
37
|
+
coalescedPatchDelta: number;
|
|
38
|
+
/** Count of clients that received a binary WebSocket frame this round. */
|
|
39
|
+
binaryOutboundFrames: number;
|
|
11
40
|
}
|
|
12
41
|
export interface TexturaServer {
|
|
13
42
|
/** Trigger a re-render for all connected clients. */
|
|
@@ -15,6 +44,7 @@ export interface TexturaServer {
|
|
|
15
44
|
/** Shut down the server. */
|
|
16
45
|
close(): void;
|
|
17
46
|
}
|
|
47
|
+
export declare function shouldDeferClientSend(bufferedAmount: number, backpressureBytes: number): boolean;
|
|
18
48
|
/**
|
|
19
49
|
* Create a Textura server that computes layout and streams geometry to clients.
|
|
20
50
|
*
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAI3C,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,gBAAgB,CAAA;AAE9D,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,eAAe,CAAA;AAGjE,MAAM,WAAW,oBAAoB;IACnC,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gCAAgC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kEAAkE;IAClE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvE,mFAAmF;IACnF,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IACzC;;;OAGG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAA;IACjE,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,qFAAqF;IACrF,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,IAAI,CAAA;CAC/D;AAED,4EAA4E;AAC5E,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,CAAA;IACrB,kFAAkF;IAClF,mBAAmB,EAAE,MAAM,CAAA;IAC3B,0EAA0E;IAC1E,oBAAoB,EAAE,MAAM,CAAA;CAC7B;AAED,MAAM,WAAW,aAAa;IAC5B,qDAAqD;IACrD,MAAM,IAAI,IAAI,CAAA;IACd,4BAA4B;IAC5B,KAAK,IAAI,IAAI,CAAA;CACd;AAED,wBAAgB,qBAAqB,CACnC,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,GACxB,OAAO,CAET;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,MAAM,SAAS,EACrB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,aAAa,CAAC,CA2NxB"}
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { WebSocketServer } from 'ws';
|
|
2
2
|
import { init, computeLayout } from 'textura';
|
|
3
3
|
import { toLayoutTree, dispatchHit, dispatchKeyboardEvent, dispatchCompositionEvent } from '@geometra/core';
|
|
4
|
-
import { diffLayout, PROTOCOL_VERSION, isProtocolCompatible } from './protocol.js';
|
|
4
|
+
import { diffLayout, coalescePatches, PROTOCOL_VERSION, isProtocolCompatible, CLOSE_AUTH_FAILED, CLOSE_FORBIDDEN } from './protocol.js';
|
|
5
|
+
import { encodeBinaryFrameJson } from './binary-frame.js';
|
|
6
|
+
export function shouldDeferClientSend(bufferedAmount, backpressureBytes) {
|
|
7
|
+
return bufferedAmount > backpressureBytes;
|
|
8
|
+
}
|
|
5
9
|
/**
|
|
6
10
|
* Create a Textura server that computes layout and streams geometry to clients.
|
|
7
11
|
*
|
|
@@ -14,16 +18,24 @@ export async function createServer(view, options = {}) {
|
|
|
14
18
|
let width = options.width ?? 800;
|
|
15
19
|
let height = options.height ?? 600;
|
|
16
20
|
const clients = new Set();
|
|
21
|
+
const needsResync = new Set();
|
|
22
|
+
/** Clients that negotiated optional binary JSON envelopes for server→client frames. */
|
|
23
|
+
const clientBinaryFraming = new Map();
|
|
24
|
+
const contexts = new Map();
|
|
17
25
|
let prevLayout = null;
|
|
18
26
|
let currentTree = null;
|
|
27
|
+
const backpressureBytes = Math.max(1024, options.backpressureBytes ?? 512 * 1024);
|
|
19
28
|
function computeAndBroadcast() {
|
|
20
29
|
try {
|
|
21
30
|
currentTree = view();
|
|
22
31
|
const layoutTree = toLayoutTree(currentTree);
|
|
23
32
|
const layout = computeLayout(layoutTree, { width, height });
|
|
24
33
|
let msg;
|
|
34
|
+
let coalescedPatchDelta = 0;
|
|
25
35
|
if (prevLayout) {
|
|
26
|
-
const
|
|
36
|
+
const rawPatches = diffLayout(prevLayout, layout);
|
|
37
|
+
const patches = coalescePatches(rawPatches);
|
|
38
|
+
coalescedPatchDelta = Math.max(0, rawPatches.length - patches.length);
|
|
27
39
|
if (patches.length === 0)
|
|
28
40
|
return;
|
|
29
41
|
// If patches are more than half the tree, just send full frame
|
|
@@ -38,12 +50,35 @@ export async function createServer(view, options = {}) {
|
|
|
38
50
|
msg = { type: 'frame', layout, tree: currentTree, protocolVersion: PROTOCOL_VERSION };
|
|
39
51
|
}
|
|
40
52
|
prevLayout = layout;
|
|
41
|
-
|
|
53
|
+
let deferredSends = 0;
|
|
54
|
+
let binaryOutboundFrames = 0;
|
|
42
55
|
for (const client of clients) {
|
|
43
56
|
if (client.readyState === client.OPEN) {
|
|
44
|
-
client.
|
|
57
|
+
if (shouldDeferClientSend(client.bufferedAmount, backpressureBytes)) {
|
|
58
|
+
deferredSends++;
|
|
59
|
+
needsResync.add(client);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const clientMsg = needsResync.has(client)
|
|
63
|
+
? { type: 'frame', layout, tree: currentTree, protocolVersion: PROTOCOL_VERSION }
|
|
64
|
+
: msg;
|
|
65
|
+
if (needsResync.has(client))
|
|
66
|
+
needsResync.delete(client);
|
|
67
|
+
const json = JSON.stringify(clientMsg);
|
|
68
|
+
if (clientBinaryFraming.get(client)) {
|
|
69
|
+
binaryOutboundFrames++;
|
|
70
|
+
client.send(encodeBinaryFrameJson(json), { binary: true });
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
client.send(json);
|
|
74
|
+
}
|
|
45
75
|
}
|
|
46
76
|
}
|
|
77
|
+
options.onTransportMetrics?.({
|
|
78
|
+
deferredSends,
|
|
79
|
+
coalescedPatchDelta,
|
|
80
|
+
binaryOutboundFrames,
|
|
81
|
+
});
|
|
47
82
|
}
|
|
48
83
|
catch (err) {
|
|
49
84
|
if (options.onError) {
|
|
@@ -63,9 +98,10 @@ export async function createServer(view, options = {}) {
|
|
|
63
98
|
}
|
|
64
99
|
}
|
|
65
100
|
const wss = new WebSocketServer({ port });
|
|
66
|
-
|
|
101
|
+
function acceptConnection(ws) {
|
|
67
102
|
clients.add(ws);
|
|
68
|
-
|
|
103
|
+
needsResync.delete(ws);
|
|
104
|
+
clientBinaryFraming.delete(ws);
|
|
69
105
|
if (prevLayout && currentTree) {
|
|
70
106
|
const msg = {
|
|
71
107
|
type: 'frame',
|
|
@@ -73,7 +109,13 @@ export async function createServer(view, options = {}) {
|
|
|
73
109
|
tree: currentTree,
|
|
74
110
|
protocolVersion: PROTOCOL_VERSION,
|
|
75
111
|
};
|
|
76
|
-
|
|
112
|
+
const json = JSON.stringify(msg);
|
|
113
|
+
if (clientBinaryFraming.get(ws)) {
|
|
114
|
+
ws.send(encodeBinaryFrameJson(json), { binary: true });
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
ws.send(json);
|
|
118
|
+
}
|
|
77
119
|
}
|
|
78
120
|
else {
|
|
79
121
|
computeAndBroadcast();
|
|
@@ -90,9 +132,21 @@ export async function createServer(view, options = {}) {
|
|
|
90
132
|
ws.send(JSON.stringify(errorMsg));
|
|
91
133
|
return;
|
|
92
134
|
}
|
|
135
|
+
if (options.onMessage) {
|
|
136
|
+
const ctx = contexts.get(ws);
|
|
137
|
+
if (!options.onMessage(msg, ctx)) {
|
|
138
|
+
const errorMsg = {
|
|
139
|
+
type: 'error',
|
|
140
|
+
message: 'Forbidden',
|
|
141
|
+
code: CLOSE_FORBIDDEN,
|
|
142
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
143
|
+
};
|
|
144
|
+
ws.send(JSON.stringify(errorMsg));
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
93
148
|
if (msg.type === 'event' && currentTree && prevLayout) {
|
|
94
149
|
dispatchHit(currentTree, prevLayout, msg.eventType, msg.x, msg.y);
|
|
95
|
-
// After event handling, signals may have changed — re-render
|
|
96
150
|
computeAndBroadcast();
|
|
97
151
|
}
|
|
98
152
|
else if (msg.type === 'key' && currentTree && prevLayout) {
|
|
@@ -113,6 +167,9 @@ export async function createServer(view, options = {}) {
|
|
|
113
167
|
computeAndBroadcast();
|
|
114
168
|
}
|
|
115
169
|
else if (msg.type === 'resize') {
|
|
170
|
+
if (msg.capabilities?.binaryFraming) {
|
|
171
|
+
clientBinaryFraming.set(ws, true);
|
|
172
|
+
}
|
|
116
173
|
width = Math.max(1, msg.width);
|
|
117
174
|
height = Math.max(1, msg.height);
|
|
118
175
|
prevLayout = null;
|
|
@@ -124,8 +181,35 @@ export async function createServer(view, options = {}) {
|
|
|
124
181
|
}
|
|
125
182
|
});
|
|
126
183
|
ws.on('close', () => {
|
|
184
|
+
const ctx = contexts.get(ws);
|
|
127
185
|
clients.delete(ws);
|
|
186
|
+
needsResync.delete(ws);
|
|
187
|
+
clientBinaryFraming.delete(ws);
|
|
188
|
+
contexts.delete(ws);
|
|
189
|
+
if (options.onDisconnect && ctx !== undefined) {
|
|
190
|
+
options.onDisconnect(ctx);
|
|
191
|
+
}
|
|
128
192
|
});
|
|
193
|
+
}
|
|
194
|
+
wss.on('connection', (ws, request) => {
|
|
195
|
+
if (options.onConnection) {
|
|
196
|
+
Promise.resolve()
|
|
197
|
+
.then(() => options.onConnection(request))
|
|
198
|
+
.then((ctx) => {
|
|
199
|
+
if (ctx == null) {
|
|
200
|
+
ws.close(CLOSE_AUTH_FAILED, 'Authentication failed');
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
contexts.set(ws, ctx);
|
|
204
|
+
acceptConnection(ws);
|
|
205
|
+
})
|
|
206
|
+
.catch(() => {
|
|
207
|
+
ws.close(CLOSE_AUTH_FAILED, 'Authentication failed');
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
acceptConnection(ws);
|
|
212
|
+
}
|
|
129
213
|
});
|
|
130
214
|
// Initial render
|
|
131
215
|
computeAndBroadcast();
|
|
@@ -136,6 +220,9 @@ export async function createServer(view, options = {}) {
|
|
|
136
220
|
close() {
|
|
137
221
|
wss.close();
|
|
138
222
|
clients.clear();
|
|
223
|
+
needsResync.clear();
|
|
224
|
+
clientBinaryFraming.clear();
|
|
225
|
+
contexts.clear();
|
|
139
226
|
},
|
|
140
227
|
};
|
|
141
228
|
}
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geometra/server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Server-side layout engine with WebSocket geometry streaming",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"check": "tsc --noEmit"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@geometra/core": "^1.0.
|
|
36
|
+
"@geometra/core": "^1.0.1",
|
|
37
37
|
"ws": "^8.18.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-compat.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol-compat.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import WebSocket from 'ws';
|
|
3
|
-
import { box } from '@geometra/core';
|
|
4
|
-
import { createServer } from '../server.js';
|
|
5
|
-
function pickPort() {
|
|
6
|
-
return 39000 + Math.floor(Math.random() * 2000);
|
|
7
|
-
}
|
|
8
|
-
describe('protocol compatibility', () => {
|
|
9
|
-
it('accepts unversioned client messages as v1-compatible', async () => {
|
|
10
|
-
const port = pickPort();
|
|
11
|
-
const server = await createServer(() => box({ width: 40, height: 20 }, []), { port, width: 200, height: 100 });
|
|
12
|
-
await new Promise((resolve, reject) => {
|
|
13
|
-
const ws = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
14
|
-
let sentResize = false;
|
|
15
|
-
const timeout = setTimeout(() => reject(new Error('timed out waiting for unversioned compatibility response')), 5000);
|
|
16
|
-
ws.on('message', (raw) => {
|
|
17
|
-
const msg = JSON.parse(String(raw));
|
|
18
|
-
if (msg.type === 'frame') {
|
|
19
|
-
if (!sentResize) {
|
|
20
|
-
// Unversioned resize should be accepted and trigger a new frame/patch without error.
|
|
21
|
-
ws.send(JSON.stringify({ type: 'resize', width: 180, height: 120 }));
|
|
22
|
-
sentResize = true;
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
clearTimeout(timeout);
|
|
26
|
-
ws.close();
|
|
27
|
-
resolve();
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
if (msg.type === 'error') {
|
|
31
|
-
clearTimeout(timeout);
|
|
32
|
-
reject(new Error(`unexpected protocol error: ${msg.message ?? 'unknown'}`));
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
ws.on('error', (err) => {
|
|
37
|
-
clearTimeout(timeout);
|
|
38
|
-
reject(err);
|
|
39
|
-
});
|
|
40
|
-
}).finally(() => {
|
|
41
|
-
server.close();
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
it('returns explicit error for newer client protocol versions', async () => {
|
|
45
|
-
const port = pickPort();
|
|
46
|
-
const server = await createServer(() => box({ width: 40, height: 20 }, []), { port, width: 200, height: 100 });
|
|
47
|
-
await new Promise((resolve, reject) => {
|
|
48
|
-
const ws = new WebSocket(`ws://127.0.0.1:${port}`);
|
|
49
|
-
const timeout = setTimeout(() => reject(new Error('timed out waiting for protocol mismatch error')), 5000);
|
|
50
|
-
ws.on('open', () => {
|
|
51
|
-
ws.send(JSON.stringify({
|
|
52
|
-
type: 'event',
|
|
53
|
-
eventType: 'onClick',
|
|
54
|
-
x: 1,
|
|
55
|
-
y: 1,
|
|
56
|
-
protocolVersion: 999,
|
|
57
|
-
}));
|
|
58
|
-
});
|
|
59
|
-
ws.on('message', (raw) => {
|
|
60
|
-
const msg = JSON.parse(String(raw));
|
|
61
|
-
if (msg.type !== 'error')
|
|
62
|
-
return;
|
|
63
|
-
clearTimeout(timeout);
|
|
64
|
-
expect(msg.message).toContain('newer than server protocol');
|
|
65
|
-
ws.close();
|
|
66
|
-
resolve();
|
|
67
|
-
});
|
|
68
|
-
ws.on('error', (err) => {
|
|
69
|
-
clearTimeout(timeout);
|
|
70
|
-
reject(err);
|
|
71
|
-
});
|
|
72
|
-
}).finally(() => {
|
|
73
|
-
server.close();
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
//# sourceMappingURL=protocol-compat.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-compat.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol-compat.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-diff.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol-diff.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { diffLayout, isProtocolCompatible, coalescePatches } from '../protocol.js';
|
|
3
|
-
function cloneLayout(layout) {
|
|
4
|
-
return {
|
|
5
|
-
x: layout.x,
|
|
6
|
-
y: layout.y,
|
|
7
|
-
width: layout.width,
|
|
8
|
-
height: layout.height,
|
|
9
|
-
children: layout.children.map(cloneLayout),
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
function applyPatches(layout, patches) {
|
|
13
|
-
for (const patch of patches) {
|
|
14
|
-
let node = layout;
|
|
15
|
-
for (const idx of patch.path) {
|
|
16
|
-
node = node.children[idx];
|
|
17
|
-
}
|
|
18
|
-
if (patch.x !== undefined)
|
|
19
|
-
node.x = patch.x;
|
|
20
|
-
if (patch.y !== undefined)
|
|
21
|
-
node.y = patch.y;
|
|
22
|
-
if (patch.width !== undefined)
|
|
23
|
-
node.width = patch.width;
|
|
24
|
-
if (patch.height !== undefined)
|
|
25
|
-
node.height = patch.height;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function makeBaseLayout() {
|
|
29
|
-
return {
|
|
30
|
-
x: 0,
|
|
31
|
-
y: 0,
|
|
32
|
-
width: 400,
|
|
33
|
-
height: 300,
|
|
34
|
-
children: [
|
|
35
|
-
{ x: 10, y: 20, width: 100, height: 40, children: [] },
|
|
36
|
-
{
|
|
37
|
-
x: 40,
|
|
38
|
-
y: 80,
|
|
39
|
-
width: 200,
|
|
40
|
-
height: 120,
|
|
41
|
-
children: [
|
|
42
|
-
{ x: 0, y: 0, width: 80, height: 20, children: [] },
|
|
43
|
-
{ x: 0, y: 28, width: 80, height: 20, children: [] },
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
],
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
describe('diffLayout', () => {
|
|
50
|
-
it('applies patches to reach next layout during burst updates', () => {
|
|
51
|
-
let prev = makeBaseLayout();
|
|
52
|
-
for (let i = 1; i <= 30; i++) {
|
|
53
|
-
const next = cloneLayout(prev);
|
|
54
|
-
next.width += i;
|
|
55
|
-
next.children[0].x += 1;
|
|
56
|
-
next.children[1].children[1].y += 2;
|
|
57
|
-
next.children[1].height += 1;
|
|
58
|
-
const patches = diffLayout(prev, next);
|
|
59
|
-
const patched = cloneLayout(prev);
|
|
60
|
-
applyPatches(patched, patches);
|
|
61
|
-
expect(patched).toEqual(next);
|
|
62
|
-
prev = next;
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
it('returns no patches when geometry is unchanged', () => {
|
|
66
|
-
const base = makeBaseLayout();
|
|
67
|
-
const same = cloneLayout(base);
|
|
68
|
-
const patches = diffLayout(base, same);
|
|
69
|
-
expect(patches).toEqual([]);
|
|
70
|
-
});
|
|
71
|
-
it('treats undefined/older protocol versions as compatible and newer as incompatible', () => {
|
|
72
|
-
expect(isProtocolCompatible(undefined, 1)).toBe(true);
|
|
73
|
-
expect(isProtocolCompatible(1, 1)).toBe(true);
|
|
74
|
-
expect(isProtocolCompatible(0, 1)).toBe(true);
|
|
75
|
-
expect(isProtocolCompatible(2, 1)).toBe(false);
|
|
76
|
-
});
|
|
77
|
-
it('coalesces repeated path updates with last-write wins semantics', () => {
|
|
78
|
-
const merged = coalescePatches([
|
|
79
|
-
{ path: [1, 2], x: 10 },
|
|
80
|
-
{ path: [1, 2], y: 20 },
|
|
81
|
-
{ path: [1, 2], x: 30, width: 40 },
|
|
82
|
-
{ path: [3], height: 9 },
|
|
83
|
-
{ path: [3], y: 5 },
|
|
84
|
-
]);
|
|
85
|
-
expect(merged).toEqual([
|
|
86
|
-
{ path: [1, 2], x: 30, y: 20, width: 40 },
|
|
87
|
-
{ path: [3], height: 9, y: 5 },
|
|
88
|
-
]);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
//# sourceMappingURL=protocol-diff.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-diff.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol-diff.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-fixture.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol-fixture.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { isProtocolCompatible } from '../protocol.js';
|
|
4
|
-
function readJSON(path) {
|
|
5
|
-
return JSON.parse(readFileSync(path, 'utf8'));
|
|
6
|
-
}
|
|
7
|
-
describe('server protocol fixtures', () => {
|
|
8
|
-
it('accepts shared v1 fixture shapes', () => {
|
|
9
|
-
const frame = readJSON(new URL('../../../../fixtures/protocol/v1/frame.json', import.meta.url).pathname);
|
|
10
|
-
const patch = readJSON(new URL('../../../../fixtures/protocol/v1/patch.json', import.meta.url).pathname);
|
|
11
|
-
const error = readJSON(new URL('../../../../fixtures/protocol/v1/error.json', import.meta.url).pathname);
|
|
12
|
-
expect(frame.type).toBe('frame');
|
|
13
|
-
expect(patch.type).toBe('patch');
|
|
14
|
-
expect(error.type).toBe('error');
|
|
15
|
-
expect(isProtocolCompatible(frame.protocolVersion, 1)).toBe(true);
|
|
16
|
-
expect(isProtocolCompatible(patch.protocolVersion, 1)).toBe(true);
|
|
17
|
-
expect(isProtocolCompatible(error.protocolVersion, 1)).toBe(true);
|
|
18
|
-
});
|
|
19
|
-
});
|
|
20
|
-
//# sourceMappingURL=protocol-fixture.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-fixture.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol-fixture.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAErD,SAAS,QAAQ,CAAC,IAAY;IAC5B,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;AAC/C,CAAC;AAED,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,6CAA6C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;QACxG,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,6CAA6C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;QACxG,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,GAAG,CAAC,6CAA6C,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAA;QAExG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAChC,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjE,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjE,MAAM,CAAC,oBAAoB,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-perf-smoke.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/protocol-perf-smoke.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { diffLayout } from '../protocol.js';
|
|
3
|
-
function nowMs() {
|
|
4
|
-
return performance.now();
|
|
5
|
-
}
|
|
6
|
-
function makeTree(depth, breadth, seed = 0) {
|
|
7
|
-
const node = {
|
|
8
|
-
x: seed % 17,
|
|
9
|
-
y: seed % 13,
|
|
10
|
-
width: 100 + (seed % 29),
|
|
11
|
-
height: 40 + (seed % 19),
|
|
12
|
-
children: [],
|
|
13
|
-
};
|
|
14
|
-
if (depth === 0)
|
|
15
|
-
return node;
|
|
16
|
-
for (let i = 0; i < breadth; i++) {
|
|
17
|
-
node.children.push(makeTree(depth - 1, breadth, seed + i + 1));
|
|
18
|
-
}
|
|
19
|
-
return node;
|
|
20
|
-
}
|
|
21
|
-
function mutateLayout(node) {
|
|
22
|
-
return {
|
|
23
|
-
x: node.x + 1,
|
|
24
|
-
y: node.y + 2,
|
|
25
|
-
width: node.width + 1,
|
|
26
|
-
height: node.height + 1,
|
|
27
|
-
children: node.children.map((c, i) => (i % 2 === 0
|
|
28
|
-
? mutateLayout(c)
|
|
29
|
-
: { ...c, x: c.x + 1, children: c.children.map(gc => ({ ...gc })) })),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
function mutateLayoutWorstCase(node) {
|
|
33
|
-
return {
|
|
34
|
-
x: node.x + 2,
|
|
35
|
-
y: node.y + 2,
|
|
36
|
-
width: node.width + 2,
|
|
37
|
-
height: node.height + 2,
|
|
38
|
-
children: node.children.map(mutateLayoutWorstCase),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
describe('protocol perf smoke', () => {
|
|
42
|
-
it('diffLayout handles rapid geometry bursts', () => {
|
|
43
|
-
let prev = makeTree(4, 4);
|
|
44
|
-
const start = nowMs();
|
|
45
|
-
for (let i = 0; i < 60; i++) {
|
|
46
|
-
const next = mutateLayout(prev);
|
|
47
|
-
const patches = diffLayout(prev, next);
|
|
48
|
-
expect(patches.length).toBeGreaterThan(0);
|
|
49
|
-
prev = next;
|
|
50
|
-
}
|
|
51
|
-
const elapsed = nowMs() - start;
|
|
52
|
-
expect(elapsed).toBeGreaterThan(0);
|
|
53
|
-
expect(elapsed).toBeLessThanOrEqual(500);
|
|
54
|
-
});
|
|
55
|
-
it('handles large tree worst-case churn within smoke threshold', () => {
|
|
56
|
-
let prev = makeTree(5, 4);
|
|
57
|
-
const start = nowMs();
|
|
58
|
-
for (let i = 0; i < 12; i++) {
|
|
59
|
-
const next = mutateLayoutWorstCase(prev);
|
|
60
|
-
const patches = diffLayout(prev, next);
|
|
61
|
-
expect(patches.length).toBeGreaterThan(100);
|
|
62
|
-
prev = next;
|
|
63
|
-
}
|
|
64
|
-
const elapsed = nowMs() - start;
|
|
65
|
-
expect(elapsed).toBeLessThanOrEqual(900);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
//# sourceMappingURL=protocol-perf-smoke.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"protocol-perf-smoke.test.js","sourceRoot":"","sources":["../../src/__tests__/protocol-perf-smoke.test.ts"],"names":[],"mappings":"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"}
|