@korajs/server 0.1.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.
@@ -0,0 +1,150 @@
1
+ import "./chunk-DGUM43GV.js";
2
+
3
+ // src/transport/memory-server-transport.ts
4
+ var MemoryServerTransport = class {
5
+ connected = true;
6
+ messageHandler = null;
7
+ closeHandler = null;
8
+ errorHandler = null;
9
+ sentMessages = [];
10
+ sendToClient = null;
11
+ clientDisconnect = null;
12
+ /**
13
+ * Wire up the send-to-client function and client disconnect notifier.
14
+ * Called by createServerTransportPair.
15
+ */
16
+ _link(sendToClient, clientDisconnect) {
17
+ this.sendToClient = sendToClient;
18
+ this.clientDisconnect = clientDisconnect;
19
+ }
20
+ /**
21
+ * Deliver a message from the client side into this transport.
22
+ * Called by the linked client transport.
23
+ */
24
+ _receiveFromClient(message) {
25
+ this.messageHandler?.(message);
26
+ }
27
+ /**
28
+ * Notify this transport that the client disconnected.
29
+ */
30
+ _notifyClientDisconnected() {
31
+ if (!this.connected) return;
32
+ this.connected = false;
33
+ this.closeHandler?.(1e3, "client disconnected");
34
+ }
35
+ send(message) {
36
+ if (!this.connected) {
37
+ throw new Error("Cannot send message: server transport is not connected");
38
+ }
39
+ this.sentMessages.push(message);
40
+ this.sendToClient?.(message);
41
+ }
42
+ onMessage(handler) {
43
+ this.messageHandler = handler;
44
+ }
45
+ onClose(handler) {
46
+ this.closeHandler = handler;
47
+ }
48
+ onError(handler) {
49
+ this.errorHandler = handler;
50
+ }
51
+ isConnected() {
52
+ return this.connected;
53
+ }
54
+ close(code, reason) {
55
+ if (!this.connected) return;
56
+ this.connected = false;
57
+ this.clientDisconnect?.();
58
+ }
59
+ // --- Testing helpers ---
60
+ /** Get all messages sent through this transport. */
61
+ getSentMessages() {
62
+ return [...this.sentMessages];
63
+ }
64
+ /** Simulate a disconnect from the client side. */
65
+ simulateDisconnect() {
66
+ this._notifyClientDisconnected();
67
+ }
68
+ };
69
+ var MemoryClientTransport = class {
70
+ connected = true;
71
+ messageHandler = null;
72
+ closeHandler = null;
73
+ errorHandler = null;
74
+ sentMessages = [];
75
+ sendToServer = null;
76
+ serverNotifyDisconnect = null;
77
+ _link(sendToServer, serverNotifyDisconnect) {
78
+ this.sendToServer = sendToServer;
79
+ this.serverNotifyDisconnect = serverNotifyDisconnect;
80
+ }
81
+ _receiveFromServer(message) {
82
+ this.messageHandler?.(message);
83
+ }
84
+ _notifyServerDisconnected() {
85
+ if (!this.connected) return;
86
+ this.connected = false;
87
+ this.closeHandler?.("server disconnected");
88
+ }
89
+ async connect(_url) {
90
+ this.connected = true;
91
+ }
92
+ async disconnect() {
93
+ if (!this.connected) return;
94
+ this.connected = false;
95
+ this.serverNotifyDisconnect?.();
96
+ }
97
+ send(message) {
98
+ if (!this.connected) {
99
+ throw new Error("Cannot send message: client transport is not connected");
100
+ }
101
+ this.sentMessages.push(message);
102
+ this.sendToServer?.(message);
103
+ }
104
+ onMessage(handler) {
105
+ this.messageHandler = handler;
106
+ }
107
+ onClose(handler) {
108
+ this.closeHandler = handler;
109
+ }
110
+ onError(handler) {
111
+ this.errorHandler = handler;
112
+ }
113
+ isConnected() {
114
+ return this.connected;
115
+ }
116
+ getSentMessages() {
117
+ return [...this.sentMessages];
118
+ }
119
+ simulateIncoming(message) {
120
+ this.messageHandler?.(message);
121
+ }
122
+ simulateDisconnect(reason) {
123
+ this.connected = false;
124
+ this.closeHandler?.(reason);
125
+ }
126
+ simulateError(error) {
127
+ this.errorHandler?.(error);
128
+ }
129
+ clearSentMessages() {
130
+ this.sentMessages.length = 0;
131
+ }
132
+ };
133
+ function createServerTransportPair() {
134
+ const client = new MemoryClientTransport();
135
+ const server = new MemoryServerTransport();
136
+ client._link(
137
+ (msg) => server._receiveFromClient(msg),
138
+ () => server._notifyClientDisconnected()
139
+ );
140
+ server._link(
141
+ (msg) => client._receiveFromServer(msg),
142
+ () => client._notifyServerDisconnected()
143
+ );
144
+ return { client, server };
145
+ }
146
+ export {
147
+ MemoryServerTransport,
148
+ createServerTransportPair
149
+ };
150
+ //# sourceMappingURL=internal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transport/memory-server-transport.ts"],"sourcesContent":["import type { SyncMessage } from '@korajs/sync'\nimport type {\n\tServerCloseHandler,\n\tServerErrorHandler,\n\tServerMessageHandler,\n\tServerTransport,\n} from './server-transport'\n\n/**\n * In-memory server transport for testing. Wraps the server side of a\n * MemoryTransport pair, adapting it to the ServerTransport interface.\n *\n * Messages sent through this transport arrive at the linked client MemoryTransport.\n */\nexport class MemoryServerTransport implements ServerTransport {\n\tprivate connected = true\n\tprivate messageHandler: ServerMessageHandler | null = null\n\tprivate closeHandler: ServerCloseHandler | null = null\n\tprivate errorHandler: ServerErrorHandler | null = null\n\tprivate readonly sentMessages: SyncMessage[] = []\n\tprivate sendToClient: ((message: SyncMessage) => void) | null = null\n\tprivate clientDisconnect: (() => void) | null = null\n\n\t/**\n\t * Wire up the send-to-client function and client disconnect notifier.\n\t * Called by createServerTransportPair.\n\t */\n\t_link(sendToClient: (message: SyncMessage) => void, clientDisconnect: () => void): void {\n\t\tthis.sendToClient = sendToClient\n\t\tthis.clientDisconnect = clientDisconnect\n\t}\n\n\t/**\n\t * Deliver a message from the client side into this transport.\n\t * Called by the linked client transport.\n\t */\n\t_receiveFromClient(message: SyncMessage): void {\n\t\tthis.messageHandler?.(message)\n\t}\n\n\t/**\n\t * Notify this transport that the client disconnected.\n\t */\n\t_notifyClientDisconnected(): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.closeHandler?.(1000, 'client disconnected')\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new Error('Cannot send message: server transport is not connected')\n\t\t}\n\t\tthis.sentMessages.push(message)\n\t\tthis.sendToClient?.(message)\n\t}\n\n\tonMessage(handler: ServerMessageHandler): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: ServerCloseHandler): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: ServerErrorHandler): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tclose(code?: number, reason?: string): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.clientDisconnect?.()\n\t}\n\n\t// --- Testing helpers ---\n\n\t/** Get all messages sent through this transport. */\n\tgetSentMessages(): SyncMessage[] {\n\t\treturn [...this.sentMessages]\n\t}\n\n\t/** Simulate a disconnect from the client side. */\n\tsimulateDisconnect(): void {\n\t\tthis._notifyClientDisconnected()\n\t}\n}\n\n/**\n * Minimal client-side transport that pairs with MemoryServerTransport.\n * Implements the same interface shape as @korajs/sync MemoryTransport but\n * specifically designed for server testing without importing internal modules.\n */\nclass MemoryClientTransport {\n\tprivate connected = true\n\tprivate messageHandler: ((message: SyncMessage) => void) | null = null\n\tprivate closeHandler: ((reason: string) => void) | null = null\n\tprivate errorHandler: ((error: Error) => void) | null = null\n\tprivate readonly sentMessages: SyncMessage[] = []\n\tprivate sendToServer: ((message: SyncMessage) => void) | null = null\n\tprivate serverNotifyDisconnect: (() => void) | null = null\n\n\t_link(sendToServer: (message: SyncMessage) => void, serverNotifyDisconnect: () => void): void {\n\t\tthis.sendToServer = sendToServer\n\t\tthis.serverNotifyDisconnect = serverNotifyDisconnect\n\t}\n\n\t_receiveFromServer(message: SyncMessage): void {\n\t\tthis.messageHandler?.(message)\n\t}\n\n\t_notifyServerDisconnected(): void {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.closeHandler?.('server disconnected')\n\t}\n\n\tasync connect(_url: string): Promise<void> {\n\t\tthis.connected = true\n\t}\n\n\tasync disconnect(): Promise<void> {\n\t\tif (!this.connected) return\n\t\tthis.connected = false\n\t\tthis.serverNotifyDisconnect?.()\n\t}\n\n\tsend(message: SyncMessage): void {\n\t\tif (!this.connected) {\n\t\t\tthrow new Error('Cannot send message: client transport is not connected')\n\t\t}\n\t\tthis.sentMessages.push(message)\n\t\tthis.sendToServer?.(message)\n\t}\n\n\tonMessage(handler: (message: SyncMessage) => void): void {\n\t\tthis.messageHandler = handler\n\t}\n\n\tonClose(handler: (reason: string) => void): void {\n\t\tthis.closeHandler = handler\n\t}\n\n\tonError(handler: (error: Error) => void): void {\n\t\tthis.errorHandler = handler\n\t}\n\n\tisConnected(): boolean {\n\t\treturn this.connected\n\t}\n\n\tgetSentMessages(): SyncMessage[] {\n\t\treturn [...this.sentMessages]\n\t}\n\n\tsimulateIncoming(message: SyncMessage): void {\n\t\tthis.messageHandler?.(message)\n\t}\n\n\tsimulateDisconnect(reason: string): void {\n\t\tthis.connected = false\n\t\tthis.closeHandler?.(reason)\n\t}\n\n\tsimulateError(error: Error): void {\n\t\tthis.errorHandler?.(error)\n\t}\n\n\tclearSentMessages(): void {\n\t\tthis.sentMessages.length = 0\n\t}\n}\n\n/**\n * Create a linked pair of memory transports for server testing.\n * Messages sent on `client` arrive at `server` and vice versa.\n * The client transport implements SyncTransport-compatible interface.\n */\nexport function createServerTransportPair(): {\n\tclient: MemoryClientTransport\n\tserver: MemoryServerTransport\n} {\n\tconst client = new MemoryClientTransport()\n\tconst server = new MemoryServerTransport()\n\n\t// Wire: client.send → server._receiveFromClient\n\t// Wire: server.send → client._receiveFromServer\n\tclient._link(\n\t\t(msg) => server._receiveFromClient(msg),\n\t\t() => server._notifyClientDisconnected(),\n\t)\n\tserver._link(\n\t\t(msg) => client._receiveFromServer(msg),\n\t\t() => client._notifyServerDisconnected(),\n\t)\n\n\treturn { client, server }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,35 @@
1
+ import { SyncMessage } from '@korajs/sync';
2
+
3
+ /**
4
+ * Handler for incoming sync messages on the server side.
5
+ */
6
+ type ServerMessageHandler = (message: SyncMessage) => void;
7
+ /**
8
+ * Handler for connection close events on the server side.
9
+ */
10
+ type ServerCloseHandler = (code: number, reason: string) => void;
11
+ /**
12
+ * Handler for transport errors on the server side.
13
+ */
14
+ type ServerErrorHandler = (error: Error) => void;
15
+ /**
16
+ * Server-side transport interface. Unlike SyncTransport (client-side),
17
+ * the server does not connect outward — it receives connections.
18
+ * Each ServerTransport represents one already-established client connection.
19
+ */
20
+ interface ServerTransport {
21
+ /** Send a sync message to the connected client */
22
+ send(message: SyncMessage): void;
23
+ /** Register a handler for incoming messages from the client */
24
+ onMessage(handler: ServerMessageHandler): void;
25
+ /** Register a handler for connection close events */
26
+ onClose(handler: ServerCloseHandler): void;
27
+ /** Register a handler for transport errors */
28
+ onError(handler: ServerErrorHandler): void;
29
+ /** Returns true if the transport connection is active */
30
+ isConnected(): boolean;
31
+ /** Close the connection to the client */
32
+ close(code?: number, reason?: string): void;
33
+ }
34
+
35
+ export type { ServerTransport as S, ServerMessageHandler as a, ServerCloseHandler as b, ServerErrorHandler as c };
@@ -0,0 +1,35 @@
1
+ import { SyncMessage } from '@korajs/sync';
2
+
3
+ /**
4
+ * Handler for incoming sync messages on the server side.
5
+ */
6
+ type ServerMessageHandler = (message: SyncMessage) => void;
7
+ /**
8
+ * Handler for connection close events on the server side.
9
+ */
10
+ type ServerCloseHandler = (code: number, reason: string) => void;
11
+ /**
12
+ * Handler for transport errors on the server side.
13
+ */
14
+ type ServerErrorHandler = (error: Error) => void;
15
+ /**
16
+ * Server-side transport interface. Unlike SyncTransport (client-side),
17
+ * the server does not connect outward — it receives connections.
18
+ * Each ServerTransport represents one already-established client connection.
19
+ */
20
+ interface ServerTransport {
21
+ /** Send a sync message to the connected client */
22
+ send(message: SyncMessage): void;
23
+ /** Register a handler for incoming messages from the client */
24
+ onMessage(handler: ServerMessageHandler): void;
25
+ /** Register a handler for connection close events */
26
+ onClose(handler: ServerCloseHandler): void;
27
+ /** Register a handler for transport errors */
28
+ onError(handler: ServerErrorHandler): void;
29
+ /** Returns true if the transport connection is active */
30
+ isConnected(): boolean;
31
+ /** Close the connection to the client */
32
+ close(code?: number, reason?: string): void;
33
+ }
34
+
35
+ export type { ServerTransport as S, ServerMessageHandler as a, ServerCloseHandler as b, ServerErrorHandler as c };
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@korajs/server",
3
+ "version": "0.1.0",
4
+ "description": "Kora.js self-hosted sync server",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./internal": {
21
+ "import": {
22
+ "types": "./dist/internal.d.ts",
23
+ "default": "./dist/internal.js"
24
+ },
25
+ "require": {
26
+ "types": "./dist/internal.d.cts",
27
+ "default": "./dist/internal.cjs"
28
+ }
29
+ }
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "dependencies": {
35
+ "better-sqlite3": "^12.8.0",
36
+ "drizzle-orm": "^0.45.2",
37
+ "ws": "^8.18.0",
38
+ "@korajs/core": "0.1.0",
39
+ "@korajs/sync": "0.1.0"
40
+ },
41
+ "peerDependencies": {
42
+ "postgres": "^3.4.0"
43
+ },
44
+ "peerDependenciesMeta": {
45
+ "postgres": {
46
+ "optional": true
47
+ }
48
+ },
49
+ "devDependencies": {
50
+ "@fast-check/vitest": "0.2.0",
51
+ "@types/better-sqlite3": "^7.6.13",
52
+ "@types/ws": "^8.5.13",
53
+ "tsup": "^8.3.6",
54
+ "typescript": "^5.7.3",
55
+ "vitest": "^3.0.4"
56
+ },
57
+ "license": "MIT",
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "test": "vitest run",
62
+ "test:watch": "vitest",
63
+ "typecheck": "tsc --noEmit",
64
+ "lint": "biome check src/",
65
+ "clean": "rm -rf dist .turbo coverage"
66
+ }
67
+ }