@korajs/server 0.3.3 → 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/internal.cjs CHANGED
@@ -59,7 +59,7 @@ var MemoryServerTransport = class {
59
59
  }
60
60
  send(message) {
61
61
  if (!this.connected) {
62
- throw new Error("Cannot send message: server transport is not connected");
62
+ return;
63
63
  }
64
64
  this.sentMessages.push(message);
65
65
  this.sendToClient?.(message);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/internal.ts","../src/transport/memory-server-transport.ts"],"sourcesContent":["// Internal exports — shared within @kora packages but NOT part of the public API.\n// Other @kora packages can import from '@korajs/server/internal' if needed.\n\nexport {\n\tMemoryServerTransport,\n\tcreateServerTransportPair,\n} from './transport/memory-server-transport'\n","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":[]}
1
+ {"version":3,"sources":["../src/internal.ts","../src/transport/memory-server-transport.ts"],"sourcesContent":["// Internal exports — shared within @kora packages but NOT part of the public API.\n// Other @kora packages can import from '@korajs/server/internal' if needed.\n\nexport {\n\tMemoryServerTransport,\n\tcreateServerTransportPair,\n} from './transport/memory-server-transport'\n","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\treturn\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":[]}
package/dist/internal.js CHANGED
@@ -32,7 +32,7 @@ var MemoryServerTransport = class {
32
32
  }
33
33
  send(message) {
34
34
  if (!this.connected) {
35
- throw new Error("Cannot send message: server transport is not connected");
35
+ return;
36
36
  }
37
37
  this.sentMessages.push(message);
38
38
  this.sendToClient?.(message);
@@ -1 +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":[]}
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\treturn\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,116 @@
1
+ // src/store/server-backup.ts
2
+ var BACKUP_VERSION = 1;
3
+ function encodeSection(name, content) {
4
+ const nameBytes = new TextEncoder().encode(name);
5
+ const header = new Uint8Array(8);
6
+ const dv = new DataView(header.buffer);
7
+ dv.setUint32(0, nameBytes.length, true);
8
+ dv.setUint32(4, content.length, true);
9
+ const result = new Uint8Array(header.length + nameBytes.length + content.length);
10
+ result.set(header, 0);
11
+ result.set(nameBytes, 8);
12
+ result.set(content, 8 + nameBytes.length);
13
+ return result;
14
+ }
15
+ function encodeJsonSection(name, data) {
16
+ return encodeSection(name, new TextEncoder().encode(JSON.stringify(data)));
17
+ }
18
+ function parseSections(data) {
19
+ const sections = [];
20
+ const dv = new DataView(data.buffer, data.byteOffset, data.byteLength);
21
+ let offset = 0;
22
+ while (offset + 8 <= data.byteLength) {
23
+ const nameLen = dv.getUint32(offset, true);
24
+ const contentLen = dv.getUint32(offset + 4, true);
25
+ offset += 8;
26
+ if (offset + nameLen + contentLen > data.byteLength) break;
27
+ const name = new TextDecoder().decode(data.slice(offset, offset + nameLen));
28
+ offset += nameLen;
29
+ const content = data.slice(offset, offset + contentLen);
30
+ offset += contentLen;
31
+ sections.push({ name, content });
32
+ }
33
+ return sections;
34
+ }
35
+ function findSection(sections, name) {
36
+ for (const s of sections) {
37
+ if (s.name === name) return s.content;
38
+ }
39
+ return null;
40
+ }
41
+ function parseJsonSection(sections, name) {
42
+ const content = findSection(sections, name);
43
+ if (!content) return null;
44
+ return JSON.parse(new TextDecoder().decode(content));
45
+ }
46
+ async function computeSha256(data) {
47
+ const digestInput = new Uint8Array(data);
48
+ const hashBuffer = await crypto.subtle.digest("SHA-256", digestInput);
49
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
50
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
51
+ }
52
+ async function buildServerBackup(nodeId, operations, versionVector) {
53
+ const sections = [];
54
+ let allContentForChecksum = new Uint8Array(0);
55
+ const addSection = (name, data) => {
56
+ sections.push(data);
57
+ const newLen = allContentForChecksum.length + data.length;
58
+ const combined = new Uint8Array(newLen);
59
+ combined.set(allContentForChecksum, 0);
60
+ combined.set(data, allContentForChecksum.length);
61
+ allContentForChecksum = combined;
62
+ };
63
+ const vvObj = {};
64
+ for (const [nid, seq] of versionVector) {
65
+ vvObj[nid] = seq;
66
+ }
67
+ addSection("version_vector", encodeJsonSection("version_vector", vvObj));
68
+ const opLines = `${operations.map((op) => JSON.stringify(op)).join("\n")}
69
+ `;
70
+ addSection("operations", encodeSection("operations", new TextEncoder().encode(opLines)));
71
+ const checksumHex = await computeSha256(allContentForChecksum);
72
+ const manifest = {
73
+ version: BACKUP_VERSION,
74
+ createdAt: Date.now(),
75
+ nodeId,
76
+ schemaVersion: 1,
77
+ operationCount: operations.length,
78
+ collections: [],
79
+ includesRecords: false,
80
+ checksum: checksumHex
81
+ };
82
+ const manifestSection = encodeJsonSection("manifest", manifest);
83
+ const checksumSection = encodeSection("checksum", new TextEncoder().encode(checksumHex));
84
+ const totalLen = manifestSection.length + allContentForChecksum.length + checksumSection.length;
85
+ const result = new Uint8Array(totalLen);
86
+ let pos = 0;
87
+ result.set(manifestSection, pos);
88
+ pos += manifestSection.length;
89
+ result.set(allContentForChecksum, pos);
90
+ pos += allContentForChecksum.length;
91
+ result.set(checksumSection, pos);
92
+ return result;
93
+ }
94
+ function parseServerBackup(data) {
95
+ const sections = parseSections(data);
96
+ const opsContent = findSection(sections, "operations");
97
+ let operations = [];
98
+ if (opsContent) {
99
+ const text = new TextDecoder().decode(opsContent);
100
+ const lines = text.trim().split("\n").filter((l) => l.length > 0);
101
+ operations = lines.map((line) => JSON.parse(line));
102
+ }
103
+ const vvData = parseJsonSection(sections, "version_vector");
104
+ const versionVector = /* @__PURE__ */ new Map();
105
+ if (vvData) {
106
+ for (const [nid, seq] of Object.entries(vvData)) {
107
+ versionVector.set(nid, seq);
108
+ }
109
+ }
110
+ return { operations, versionVector };
111
+ }
112
+ export {
113
+ buildServerBackup,
114
+ parseServerBackup
115
+ };
116
+ //# sourceMappingURL=server-backup-MOICK6MI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store/server-backup.ts"],"sourcesContent":["import type { Operation } from '@korajs/core'\n\n/**\n * Server-side backup format: same portable section-based format as the client.\n *\n * Sections:\n * manifest — metadata (version, nodeId, operationCount, checksum)\n * version_vector — nodeId → maxSequenceNumber\n * operations — NDJSON of full Operation objects\n * checksum — SHA-256 of all content sections\n */\n\nconst BACKUP_VERSION = 1\n\nfunction encodeSection(name: string, content: Uint8Array): Uint8Array {\n\tconst nameBytes = new TextEncoder().encode(name)\n\tconst header = new Uint8Array(8)\n\tconst dv = new DataView(header.buffer)\n\tdv.setUint32(0, nameBytes.length, true)\n\tdv.setUint32(4, content.length, true)\n\n\tconst result = new Uint8Array(header.length + nameBytes.length + content.length)\n\tresult.set(header, 0)\n\tresult.set(nameBytes, 8)\n\tresult.set(content, 8 + nameBytes.length)\n\treturn result\n}\n\nfunction encodeJsonSection(name: string, data: unknown): Uint8Array {\n\treturn encodeSection(name, new TextEncoder().encode(JSON.stringify(data)))\n}\n\ninterface Section {\n\tname: string\n\tcontent: Uint8Array\n}\n\nfunction parseSections(data: Uint8Array): Section[] {\n\tconst sections: Section[] = []\n\tconst dv = new DataView(data.buffer, data.byteOffset, data.byteLength)\n\tlet offset = 0\n\n\twhile (offset + 8 <= data.byteLength) {\n\t\tconst nameLen = dv.getUint32(offset, true)\n\t\tconst contentLen = dv.getUint32(offset + 4, true)\n\t\toffset += 8\n\n\t\tif (offset + nameLen + contentLen > data.byteLength) break\n\n\t\tconst name = new TextDecoder().decode(data.slice(offset, offset + nameLen))\n\t\toffset += nameLen\n\n\t\tconst content = data.slice(offset, offset + contentLen)\n\t\toffset += contentLen\n\n\t\tsections.push({ name, content })\n\t}\n\n\treturn sections\n}\n\nfunction findSection(sections: Section[], name: string): Uint8Array | null {\n\tfor (const s of sections) {\n\t\tif (s.name === name) return s.content\n\t}\n\treturn null\n}\n\nfunction parseJsonSection<T>(sections: Section[], name: string): T | null {\n\tconst content = findSection(sections, name)\n\tif (!content) return null\n\treturn JSON.parse(new TextDecoder().decode(content)) as T\n}\n\nasync function computeSha256(data: Uint8Array): Promise<string> {\n\tconst digestInput = new Uint8Array(data)\n\tconst hashBuffer = await crypto.subtle.digest('SHA-256', digestInput)\n\tconst hashArray = Array.from(new Uint8Array(hashBuffer))\n\treturn hashArray.map((b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/**\n * Build a backup binary from server store data.\n */\nexport async function buildServerBackup(\n\tnodeId: string,\n\toperations: Operation[],\n\tversionVector: Map<string, number>,\n): Promise<Uint8Array> {\n\tconst sections: Uint8Array[] = []\n\tlet allContentForChecksum = new Uint8Array(0)\n\n\tconst addSection = (name: string, data: Uint8Array) => {\n\t\tsections.push(data)\n\t\tconst newLen = allContentForChecksum.length + data.length\n\t\tconst combined = new Uint8Array(newLen)\n\t\tcombined.set(allContentForChecksum, 0)\n\t\tcombined.set(data, allContentForChecksum.length)\n\t\tallContentForChecksum = combined\n\t}\n\n\t// Version vector\n\tconst vvObj: Record<string, number> = {}\n\tfor (const [nid, seq] of versionVector) {\n\t\tvvObj[nid] = seq\n\t}\n\taddSection('version_vector', encodeJsonSection('version_vector', vvObj))\n\n\t// Operations\n\tconst opLines = `${operations.map((op) => JSON.stringify(op)).join('\\n')}\\n`\n\taddSection('operations', encodeSection('operations', new TextEncoder().encode(opLines)))\n\n\t// Checksum\n\tconst checksumHex = await computeSha256(allContentForChecksum)\n\n\t// Manifest\n\tconst manifest = {\n\t\tversion: BACKUP_VERSION,\n\t\tcreatedAt: Date.now(),\n\t\tnodeId,\n\t\tschemaVersion: 1,\n\t\toperationCount: operations.length,\n\t\tcollections: [] as string[],\n\t\tincludesRecords: false,\n\t\tchecksum: checksumHex,\n\t}\n\n\tconst manifestSection = encodeJsonSection('manifest', manifest)\n\tconst checksumSection = encodeSection('checksum', new TextEncoder().encode(checksumHex))\n\n\tconst totalLen = manifestSection.length + allContentForChecksum.length + checksumSection.length\n\tconst result = new Uint8Array(totalLen)\n\tlet pos = 0\n\tresult.set(manifestSection, pos)\n\tpos += manifestSection.length\n\tresult.set(allContentForChecksum, pos)\n\tpos += allContentForChecksum.length\n\tresult.set(checksumSection, pos)\n\n\treturn result\n}\n\n/**\n * Parse a backup and return the operations and version vector.\n */\nexport function parseServerBackup(data: Uint8Array): {\n\toperations: Operation[]\n\tversionVector: Map<string, number>\n} {\n\tconst sections = parseSections(data)\n\n\t// Parse operations\n\tconst opsContent = findSection(sections, 'operations')\n\tlet operations: Operation[] = []\n\tif (opsContent) {\n\t\tconst text = new TextDecoder().decode(opsContent)\n\t\tconst lines = text\n\t\t\t.trim()\n\t\t\t.split('\\n')\n\t\t\t.filter((l) => l.length > 0)\n\t\toperations = lines.map((line) => JSON.parse(line) as Operation)\n\t}\n\n\t// Parse version vector\n\tconst vvData = parseJsonSection<Record<string, number>>(sections, 'version_vector')\n\tconst versionVector = new Map<string, number>()\n\tif (vvData) {\n\t\tfor (const [nid, seq] of Object.entries(vvData)) {\n\t\t\tversionVector.set(nid, seq)\n\t\t}\n\t}\n\n\treturn { operations, versionVector }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@korajs/server",
3
- "version": "0.3.3",
3
+ "version": "0.5.0",
4
4
  "description": "Kora.js self-hosted sync server",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -35,8 +35,9 @@
35
35
  "better-sqlite3": "^12.8.0",
36
36
  "drizzle-orm": "^0.45.2",
37
37
  "ws": "^8.18.0",
38
- "@korajs/core": "0.3.2",
39
- "@korajs/sync": "0.3.2"
38
+ "@korajs/merge": "0.5.0",
39
+ "@korajs/sync": "0.5.0",
40
+ "@korajs/core": "0.5.0"
40
41
  },
41
42
  "peerDependencies": {
42
43
  "postgres": "^3.4.0"