@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/index.d.ts CHANGED
@@ -1,10 +1,129 @@
1
1
  import { SchemaDefinition, KoraEventEmitter, Operation, VersionVector } from '@korajs/core';
2
2
  import * as _korajs_sync from '@korajs/sync';
3
- import { SyncStore, MessageSerializer, SyncMessage, ApplyResult } from '@korajs/sync';
3
+ import { SyncStore, MessageSerializer, SyncMessage, AwarenessUpdateMessage, YjsDocUpdateMessage, ApplyResult } from '@korajs/sync';
4
4
  import { S as ServerTransport, a as ServerMessageHandler, b as ServerCloseHandler, c as ServerErrorHandler } from './server-transport-CU1BLWgr.js';
5
5
  import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
6
6
  import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
7
7
 
8
+ /**
9
+ * Snapshot of server-side sync metrics at a point in time.
10
+ */
11
+ interface ServerMetricsSnapshot {
12
+ connectedClients: number;
13
+ connectedNodeIds: string[];
14
+ peakConnections: number;
15
+ connectionsTotal: number;
16
+ operationsReceived: number;
17
+ operationsSent: number;
18
+ bytesReceived: number;
19
+ bytesSent: number;
20
+ clients: ClientMetrics[];
21
+ uptime: number;
22
+ totalOperations: number;
23
+ errorCount: number;
24
+ schemaVersion: number;
25
+ }
26
+ /**
27
+ * Per-client metrics tracked by the server.
28
+ */
29
+ interface ClientMetrics {
30
+ sessionId: string;
31
+ nodeId: string | null;
32
+ state: SessionState$1;
33
+ connectedAt: number;
34
+ operationsReceived: number;
35
+ operationsSent: number;
36
+ authContext: AuthContext | null;
37
+ }
38
+ declare class ServerMetricsCollector {
39
+ private startedAt;
40
+ private peakConnections;
41
+ private connectionsTotal;
42
+ private operationsReceived;
43
+ private operationsSent;
44
+ private bytesReceived;
45
+ private bytesSent;
46
+ private errorCount;
47
+ private schemaVersion;
48
+ private readonly clientMetrics;
49
+ /** Record a new client connection. */
50
+ recordConnection(sessionId: string): void;
51
+ /** Record a client disconnection. */
52
+ recordDisconnection(sessionId: string): void;
53
+ /** Update the node ID after a handshake completes. */
54
+ recordHandshake(sessionId: string, nodeId: string): void;
55
+ /** Update session state. */
56
+ updateSessionState(sessionId: string, state: SessionState$1): void;
57
+ /** Record authentication context for a session. */
58
+ recordAuth(sessionId: string, authContext: AuthContext | null): void;
59
+ /** Record operations received from a client. */
60
+ recordReceived(sessionId: string, count: number, byteSize: number): void;
61
+ /** Record operations sent to a client. */
62
+ recordSent(sessionId: string, count: number, byteSize: number): void;
63
+ /** Record an error. */
64
+ recordError(): void;
65
+ /** Set the schema version. */
66
+ setSchemaVersion(version: number): void;
67
+ /** Return a full snapshot of current metrics. */
68
+ getSnapshot(totalOperations: number): ServerMetricsSnapshot;
69
+ /** Reset all metrics. */
70
+ reset(): void;
71
+ }
72
+
73
+ /**
74
+ * Log severity levels.
75
+ */
76
+ type LogLevel = 'info' | 'warn' | 'error';
77
+ /**
78
+ * A structured log entry with timestamp and metadata.
79
+ */
80
+ interface LogEntry {
81
+ timestamp: number;
82
+ level: LogLevel;
83
+ event: string;
84
+ sessionId?: string;
85
+ nodeId?: string;
86
+ duration?: number;
87
+ count?: number;
88
+ bytes?: number;
89
+ error?: string;
90
+ details?: Record<string, unknown>;
91
+ }
92
+ /**
93
+ * Logger interface for structured logging.
94
+ * Implementations control output format and destination.
95
+ */
96
+ interface Logger {
97
+ log(entry: LogEntry): void;
98
+ child?(context: Partial<LogEntry>): Logger;
99
+ }
100
+ /**
101
+ * Creates a JSON-lines logger that writes to stdout/stderr.
102
+ * Suitable for production use where log aggregators (Datadog, Loki, etc.) consume JSON.
103
+ */
104
+ declare function createJsonLogger(writer?: {
105
+ info: (s: string) => void;
106
+ error: (s: string) => void;
107
+ }): Logger;
108
+ /**
109
+ * Creates a pretty-printed logger for development use.
110
+ * Writes human-readable colored output to the terminal.
111
+ */
112
+ declare function createPrettyLogger(writer?: {
113
+ write: (s: string) => void;
114
+ }): Logger;
115
+ /**
116
+ * A logger that discards all entries (for testing or when logging is disabled).
117
+ */
118
+ declare function createSilentLogger(): Logger;
119
+ /**
120
+ * Creates a logger based on the NODE_ENV environment variable.
121
+ * - production: JSON lines to stdout/stderr
122
+ * - test: silent
123
+ * - otherwise: pretty-printed to stdout
124
+ */
125
+ declare function createDefaultLogger(): Logger;
126
+
8
127
  /**
9
128
  * A materialized record reconstructed from the operation log
10
129
  * or read from a materialized collection table.
@@ -48,6 +167,8 @@ interface ServerStore extends SyncStore {
48
167
  * @param schema - The schema definition describing all collections
49
168
  */
50
169
  setSchema(schema: SchemaDefinition): Promise<void>;
170
+ /** Schema used for materialized tables and server-side validation, if set. */
171
+ getSchema(): SchemaDefinition | null;
51
172
  /**
52
173
  * Get all records from a materialized collection.
53
174
  * When schema is set, reads directly from the collection table (O(1) indexed).
@@ -85,6 +206,23 @@ interface ServerStore extends SyncStore {
85
206
  * @returns Number of matching records
86
207
  */
87
208
  countCollection(collection: string, where?: Record<string, unknown>): Promise<number>;
209
+ /**
210
+ * Export all data as a portable backup binary.
211
+ * Ships operations, version vector, and metadata in a self-describing format
212
+ * with SHA-256 checksum.
213
+ */
214
+ exportBackup(): Promise<Uint8Array>;
215
+ /**
216
+ * Restore data from a portable backup binary.
217
+ * Operations are applied through applyRemoteOperation for safe merge.
218
+ *
219
+ * @param data - Backup binary
220
+ * @param merge - If true, merge with existing operations; if false, replace all
221
+ */
222
+ importBackup(data: Uint8Array, merge?: boolean): Promise<{
223
+ operationsRestored: number;
224
+ success: boolean;
225
+ }>;
88
226
  }
89
227
 
90
228
  /**
@@ -132,8 +270,22 @@ interface KoraSyncServerConfig {
132
270
  batchSize?: number;
133
271
  /** Schema version the server expects. Defaults to 1. */
134
272
  schemaVersion?: number;
273
+ /**
274
+ * Inclusive range of client schema versions accepted at handshake.
275
+ * Defaults to `{ min: schemaVersion, max: schemaVersion }`.
276
+ */
277
+ supportedSchemaVersions?: {
278
+ min: number;
279
+ max: number;
280
+ };
135
281
  /** WebSocket path (standalone mode). Defaults to '/'. */
136
282
  path?: string;
283
+ /** Structured logger. Defaults to pretty-print in dev, JSON lines in production. */
284
+ logger?: Logger;
285
+ /** Server metrics collector. Created automatically if omitted. */
286
+ metricsCollector?: ServerMetricsCollector;
287
+ /** Enable built-in dashboard and metrics endpoints. Defaults to true. */
288
+ enableDashboard?: boolean;
137
289
  }
138
290
  /**
139
291
  * Request envelope for the server-side HTTP sync endpoint.
@@ -161,6 +313,15 @@ interface HttpSyncResponse {
161
313
  /** Optional response headers */
162
314
  headers?: Record<string, string>;
163
315
  }
316
+ /**
317
+ * Session state machine states.
318
+ * - connected: initial state after transport connects
319
+ * - authenticated: auth check passed (or skipped)
320
+ * - syncing: exchanging delta operations during handshake
321
+ * - streaming: steady state, real-time operation relay
322
+ * - closed: session terminated
323
+ */
324
+ type SessionState$1 = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'closed';
164
325
  /**
165
326
  * Runtime status of a KoraSyncServer.
166
327
  */
@@ -173,6 +334,24 @@ interface ServerStatus {
173
334
  port: number | null;
174
335
  /** Total operations stored */
175
336
  totalOperations: number;
337
+ /** Server uptime in milliseconds */
338
+ uptime: number;
339
+ /** Server package version */
340
+ version: string;
341
+ /** Schema version the server expects */
342
+ schemaVersion: number;
343
+ /** Array of connected node IDs */
344
+ connectedNodeIds: string[];
345
+ /** Peak connections since server start */
346
+ peakConnections: number;
347
+ /** Total connections handled since server start */
348
+ connectionsTotal: number;
349
+ /** Operations received since server start */
350
+ operationsReceived: number;
351
+ /** Operations sent since server start */
352
+ operationsSent: number;
353
+ /** Error count since server start */
354
+ errorCount: number;
176
355
  }
177
356
 
178
357
  interface HttpPollResponse {
@@ -252,6 +431,14 @@ type SessionState = 'connected' | 'authenticated' | 'syncing' | 'streaming' | 'c
252
431
  * Callback invoked when a session has new operations to relay to other sessions.
253
432
  */
254
433
  type RelayCallback = (sourceSessionId: string, operations: Operation[]) => void;
434
+ /**
435
+ * Callback invoked when a session receives an awareness update to relay to other sessions.
436
+ */
437
+ type AwarenessRelayCallback = (sourceSessionId: string, message: AwarenessUpdateMessage) => void;
438
+ /**
439
+ * Callback invoked when a session receives a Yjs doc channel update to relay.
440
+ */
441
+ type YjsDocRelayCallback = (sourceSessionId: string, message: YjsDocUpdateMessage) => void;
255
442
  /**
256
443
  * Options for creating a ClientSession.
257
444
  */
@@ -272,10 +459,23 @@ interface ClientSessionOptions {
272
459
  batchSize?: number;
273
460
  /** Schema version the server expects */
274
461
  schemaVersion?: number;
462
+ /** Inclusive client schema versions accepted at handshake */
463
+ supportedSchemaVersions?: {
464
+ min: number;
465
+ max: number;
466
+ };
275
467
  /** Called when this session has operations to relay to other sessions */
276
468
  onRelay?: RelayCallback;
469
+ /** Called when this session receives an awareness update to broadcast */
470
+ onAwarenessUpdate?: AwarenessRelayCallback;
471
+ /** Called when this session receives a Yjs doc channel update to broadcast */
472
+ onYjsDocUpdate?: YjsDocRelayCallback;
277
473
  /** Called when this session closes */
278
474
  onClose?: (sessionId: string) => void;
475
+ /** Maximum serialized operation size in bytes. Defaults to 256 KiB. */
476
+ maxOperationBytes?: number;
477
+ /** Maximum operations accepted per minute for this session. Defaults to 600. */
478
+ maxOpsPerMinute?: number;
279
479
  }
280
480
  /**
281
481
  * Handles the sync protocol for a single connected client.
@@ -295,6 +495,8 @@ declare class ClientSession {
295
495
  private state;
296
496
  private clientNodeId;
297
497
  private authContext;
498
+ private syncQuerySubsets;
499
+ private resumeDeltaCursor;
298
500
  private readonly sessionId;
299
501
  private readonly transport;
300
502
  private readonly store;
@@ -303,8 +505,14 @@ declare class ClientSession {
303
505
  private readonly emitter;
304
506
  private readonly batchSize;
305
507
  private readonly schemaVersion;
508
+ private readonly supportedSchemaVersions;
306
509
  private readonly onRelay;
510
+ private readonly onAwarenessUpdate;
511
+ private readonly onYjsDocUpdate;
307
512
  private readonly onClose;
513
+ private readonly maxOperationBytes;
514
+ private readonly maxOpsPerMinute;
515
+ private readonly rateLimiter;
308
516
  constructor(options: ClientSessionOptions);
309
517
  /**
310
518
  * Start handling messages from the client transport.
@@ -324,10 +532,23 @@ declare class ClientSession {
324
532
  getClientNodeId(): string | null;
325
533
  getAuthContext(): AuthContext | null;
326
534
  isStreaming(): boolean;
327
- private handleMessage;
535
+ /**
536
+ * Get the transport for this session.
537
+ * Used by the awareness relay to send messages to this client.
538
+ */
539
+ getTransport(): ServerTransport;
540
+ private messageChain;
541
+ /** Send to the client when the transport is still connected; no-op otherwise. */
542
+ private sendToClient;
543
+ private enqueueMessage;
544
+ private handleMessageAsync;
545
+ private handleMessageFailure;
328
546
  private handleHandshake;
329
547
  private handleOperationBatch;
330
548
  private sendDelta;
549
+ private operationVisibleToClient;
550
+ private handleAwarenessUpdate;
551
+ private handleYjsDocUpdate;
331
552
  private sendError;
332
553
  private setSerializerWireFormat;
333
554
  private handleTransportClose;
@@ -369,15 +590,34 @@ declare class KoraSyncServer {
369
590
  private readonly maxConnections;
370
591
  private readonly batchSize;
371
592
  private readonly schemaVersion;
593
+ private readonly supportedSchemaVersions;
372
594
  private readonly port;
373
595
  private readonly host;
374
596
  private readonly path;
597
+ private readonly logger;
598
+ private readonly metrics;
599
+ private readonly awarenessRelay;
600
+ private readonly yjsDocRelay;
375
601
  private readonly sessions;
376
602
  private readonly httpClients;
377
603
  private readonly httpSessionToClient;
604
+ private readonly serverVersion;
378
605
  private wsServer;
379
606
  private running;
380
607
  constructor(config: KoraSyncServerConfig);
608
+ /**
609
+ * Subscribe to session-level events for metrics collection and logging.
610
+ * Called when a new session is created.
611
+ */
612
+ private attachSessionEvents;
613
+ /**
614
+ * Get the metrics collector for external access (e.g., HTTP endpoints).
615
+ */
616
+ getMetricsCollector(): ServerMetricsCollector;
617
+ /**
618
+ * Get the logger for external access (e.g., event streaming).
619
+ */
620
+ getLogger(): Logger;
381
621
  /**
382
622
  * Start the WebSocket server in standalone mode.
383
623
  *
@@ -412,6 +652,8 @@ declare class KoraSyncServer {
412
652
  getConnectionCount(): number;
413
653
  private handleRelay;
414
654
  private handleSessionClose;
655
+ private handleAwarenessRelay;
656
+ private handleYjsDocRelay;
415
657
  private getOrCreateHttpClient;
416
658
  }
417
659
 
@@ -430,6 +672,48 @@ interface ProductionServerConfig {
430
672
  syncPath?: string;
431
673
  /** Additional KoraSyncServer options */
432
674
  syncOptions?: Omit<KoraSyncServerConfig, 'store' | 'port' | 'host' | 'path'>;
675
+ /**
676
+ * Optional HTTP route handlers mounted before static file serving.
677
+ *
678
+ * This is intentionally framework-agnostic so packages such as
679
+ * `@korajs/auth` can plug into the production server without requiring
680
+ * Express, Hono, or another HTTP framework.
681
+ */
682
+ httpRoutes?: ProductionHttpRoute[];
683
+ /**
684
+ * Optional token protection for operational endpoints.
685
+ *
686
+ * When a token is omitted, the matching endpoint group remains public for
687
+ * backward compatibility. Production apps should set at least adminToken and
688
+ * backupToken.
689
+ */
690
+ operationalAuth?: ProductionOperationalAuth;
691
+ }
692
+ interface ProductionOperationalAuth {
693
+ /** Protects /__kora, /__kora/status, and /__kora/events. */
694
+ adminToken?: string;
695
+ /** Protects /__kora/metrics. Falls back to adminToken when omitted. */
696
+ metricsToken?: string;
697
+ /** Protects /__kora/backup/*. Falls back to adminToken when omitted. */
698
+ backupToken?: string;
699
+ }
700
+ interface ProductionHttpRouteRequest {
701
+ method: string;
702
+ path: string;
703
+ body?: unknown;
704
+ headers?: Record<string, string | string[] | undefined>;
705
+ query?: Record<string, string | string[] | undefined>;
706
+ ip?: string;
707
+ }
708
+ interface ProductionHttpRouteResponse {
709
+ status: number;
710
+ body?: unknown;
711
+ headers?: Record<string, string>;
712
+ }
713
+ interface ProductionHttpRoute {
714
+ /** URL path prefix, for example `/auth`. */
715
+ path: string;
716
+ handle(request: ProductionHttpRouteRequest): Promise<ProductionHttpRouteResponse>;
433
717
  }
434
718
  /**
435
719
  * A production server handle returned by createProductionServer.
@@ -442,22 +726,17 @@ interface ProductionServer {
442
726
  }
443
727
  /**
444
728
  * Creates a production server that serves both static files and WebSocket sync
445
- * on a single port. This is the recommended way to deploy a Kora app — one port
446
- * means one tunnel (ngrok, cloudflared) handles everything.
729
+ * on a single port, plus built-in dashboard and observability endpoints.
447
730
  *
448
731
  * @param config - Production server configuration
449
732
  * @returns A ProductionServer instance
450
733
  *
451
734
  * @example
452
735
  * ```typescript
453
- * import { createProductionServer, createSqliteServerStore } from '@korajs/server'
454
- *
455
736
  * const server = createProductionServer({
456
737
  * store: createSqliteServerStore({ filename: './kora-server.db' }),
457
738
  * })
458
- *
459
739
  * const url = await server.start()
460
- * console.log(`App running at ${url}`)
461
740
  * ```
462
741
  */
463
742
  declare function createProductionServer(config: ProductionServerConfig): ProductionServer;
@@ -502,6 +781,11 @@ interface TokenValidator {
502
781
  dev: string;
503
782
  type: string;
504
783
  } | null;
784
+ validateTokenWithRevocation?(token: string): Promise<{
785
+ sub: string;
786
+ dev: string;
787
+ type: string;
788
+ } | null>;
505
789
  }
506
790
  /**
507
791
  * Looks up a user by ID. Returns null if the user doesn't exist.
@@ -528,6 +812,8 @@ interface KoraAuthProviderOptions {
528
812
  /**
529
813
  * Token validator that verifies JWT signatures and returns claims.
530
814
  * Typically a `TokenManager` instance from `@korajs/auth/server`.
815
+ * If the validator also implements `validateTokenWithRevocation`, the sync
816
+ * server uses it so signed-out sessions and revoked devices are rejected.
531
817
  */
532
818
  tokenValidator: TokenValidator;
533
819
  /**
@@ -585,6 +871,88 @@ declare class KoraAuthProvider implements AuthProvider {
585
871
  authenticate(token: string): Promise<AuthContext | null>;
586
872
  }
587
873
 
874
+ /**
875
+ * Configuration for creating a MixedAuthProvider.
876
+ */
877
+ interface MixedAuthProviderOptions {
878
+ /**
879
+ * Primary auth provider that validates tokens from authenticated users.
880
+ * Typically a `KoraAuthProvider`, `TokenAuthProvider`, or the result of
881
+ * `authRoutes.toSyncAuthProvider()`.
882
+ */
883
+ primary: AuthProvider;
884
+ /**
885
+ * Scopes to apply to anonymous connections.
886
+ * Each key is a collection name; the value is a filter object
887
+ * (use `{}` for unrestricted access to that collection).
888
+ *
889
+ * @example
890
+ * ```typescript
891
+ * // Anonymous users can only sync the 'responses' collection
892
+ * anonymousScopes: { responses: {} }
893
+ *
894
+ * // Anonymous users can read published forms
895
+ * anonymousScopes: { forms: { status: 'published' } }
896
+ * ```
897
+ */
898
+ anonymousScopes: Record<string, Record<string, unknown>>;
899
+ /**
900
+ * Prefix for generated anonymous user IDs.
901
+ * A unique suffix is appended to each connection.
902
+ * @default 'anon'
903
+ */
904
+ anonymousPrefix?: string;
905
+ }
906
+ /**
907
+ * Auth provider that supports both authenticated and anonymous connections.
908
+ *
909
+ * When a client connects with a valid token, the primary auth provider
910
+ * handles authentication normally. When a client connects without a token
911
+ * (or with an invalid one), the connection is accepted as anonymous with
912
+ * restricted sync scopes.
913
+ *
914
+ * This is the recommended pattern for apps that need public data access
915
+ * alongside authenticated users — for example, a form builder where
916
+ * authenticated users create forms but anyone can submit responses.
917
+ *
918
+ * @example
919
+ * ```typescript
920
+ * import { MixedAuthProvider, KoraAuthProvider } from '@korajs/server'
921
+ *
922
+ * const auth = new MixedAuthProvider({
923
+ * primary: authRoutes.toSyncAuthProvider(),
924
+ * anonymousScopes: {
925
+ * // Anonymous users can only sync the 'responses' collection
926
+ * responses: {},
927
+ * },
928
+ * })
929
+ *
930
+ * const server = new KoraSyncServer({ store, auth })
931
+ * ```
932
+ *
933
+ * @example
934
+ * ```typescript
935
+ * // On the client, return an empty token for unauthenticated users:
936
+ * const app = createApp({
937
+ * schema,
938
+ * sync: {
939
+ * url: 'wss://my-server.com/kora',
940
+ * auth: async () => ({
941
+ * token: (await authClient.getAccessToken()) ?? '',
942
+ * }),
943
+ * },
944
+ * })
945
+ * ```
946
+ */
947
+ declare class MixedAuthProvider implements AuthProvider {
948
+ private readonly primary;
949
+ private readonly anonymousScopes;
950
+ private readonly anonymousPrefix;
951
+ private anonymousCounter;
952
+ constructor(options: MixedAuthProviderOptions);
953
+ authenticate(token: string): Promise<AuthContext>;
954
+ }
955
+
588
956
  /**
589
957
  * In-memory server store for testing and quick prototyping.
590
958
  * Not suitable for production — data does not survive process restart.
@@ -604,6 +972,7 @@ declare class MemoryServerStore implements ServerStore {
604
972
  constructor(nodeId?: string);
605
973
  getVersionVector(): VersionVector;
606
974
  getNodeId(): string;
975
+ getSchema(): SchemaDefinition | null;
607
976
  setSchema(schema: SchemaDefinition): Promise<void>;
608
977
  applyRemoteOperation(op: Operation): Promise<ApplyResult>;
609
978
  getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
@@ -613,6 +982,15 @@ declare class MemoryServerStore implements ServerStore {
613
982
  findRecord(collection: string, id: string): Promise<MaterializedRecord | null>;
614
983
  countCollection(collection: string, where?: Record<string, unknown>): Promise<number>;
615
984
  close(): Promise<void>;
985
+ /**
986
+ * Wipes all in-memory state. For tests and E2E isolation only.
987
+ */
988
+ resetForTests(): void;
989
+ exportBackup(): Promise<Uint8Array>;
990
+ importBackup(data: Uint8Array, merge?: boolean): Promise<{
991
+ operationsRestored: number;
992
+ success: boolean;
993
+ }>;
616
994
  /**
617
995
  * Get all stored operations (for test assertions).
618
996
  */
@@ -642,6 +1020,7 @@ declare class PostgresServerStore implements ServerStore {
642
1020
  constructor(db: PostgresJsDatabase, nodeId?: string);
643
1021
  getVersionVector(): VersionVector;
644
1022
  getNodeId(): string;
1023
+ getSchema(): SchemaDefinition | null;
645
1024
  setSchema(schema: SchemaDefinition): Promise<void>;
646
1025
  applyRemoteOperation(op: Operation): Promise<ApplyResult>;
647
1026
  getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
@@ -651,6 +1030,11 @@ declare class PostgresServerStore implements ServerStore {
651
1030
  findRecord(collection: string, id: string): Promise<MaterializedRecord | null>;
652
1031
  countCollection(collection: string, where?: Record<string, unknown>): Promise<number>;
653
1032
  close(): Promise<void>;
1033
+ exportBackup(): Promise<Uint8Array>;
1034
+ importBackup(data: Uint8Array, merge?: boolean): Promise<{
1035
+ operationsRestored: number;
1036
+ success: boolean;
1037
+ }>;
654
1038
  /**
655
1039
  * Rebuild a single record in the materialized collection table by replaying
656
1040
  * all operations for that record.
@@ -704,6 +1088,7 @@ declare class SqliteServerStore implements ServerStore {
704
1088
  constructor(db: BetterSQLite3Database, nodeId?: string);
705
1089
  getVersionVector(): VersionVector;
706
1090
  getNodeId(): string;
1091
+ getSchema(): SchemaDefinition | null;
707
1092
  setSchema(schema: SchemaDefinition): Promise<void>;
708
1093
  applyRemoteOperation(op: Operation): Promise<ApplyResult>;
709
1094
  getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
@@ -713,6 +1098,11 @@ declare class SqliteServerStore implements ServerStore {
713
1098
  findRecord(collection: string, id: string): Promise<MaterializedRecord | null>;
714
1099
  countCollection(collection: string, where?: Record<string, unknown>): Promise<number>;
715
1100
  close(): Promise<void>;
1101
+ exportBackup(): Promise<Uint8Array>;
1102
+ importBackup(data: Uint8Array, merge?: boolean): Promise<{
1103
+ operationsRestored: number;
1104
+ success: boolean;
1105
+ }>;
716
1106
  /**
717
1107
  * Rebuild a single record in the materialized collection table by replaying
718
1108
  * all operations for that record. Called within the applyRemoteOperation
@@ -782,6 +1172,49 @@ declare class NoAuthProvider implements AuthProvider {
782
1172
  authenticate(_token: string): Promise<AuthContext>;
783
1173
  }
784
1174
 
1175
+ /**
1176
+ * Server-side awareness relay. Broadcasts ephemeral awareness states
1177
+ * (cursor positions, user presence) between connected clients.
1178
+ *
1179
+ * Awareness data is never persisted. The relay simply forwards awareness
1180
+ * updates from one client to all other connected clients, and broadcasts
1181
+ * removal notifications when a client disconnects.
1182
+ */
1183
+ declare class AwarenessRelay {
1184
+ private readonly clients;
1185
+ /**
1186
+ * Register a client for awareness broadcasting.
1187
+ *
1188
+ * @param sessionId - Unique session identifier
1189
+ * @param clientId - Client-assigned awareness ID
1190
+ * @param transport - Transport for sending messages to this client
1191
+ */
1192
+ addClient(sessionId: string, clientId: number, transport: ServerTransport): void;
1193
+ /**
1194
+ * Remove a client and broadcast its removal to all remaining clients.
1195
+ *
1196
+ * @param sessionId - Session ID of the disconnecting client
1197
+ */
1198
+ removeClient(sessionId: string): void;
1199
+ /**
1200
+ * Handle an incoming awareness update from a client.
1201
+ * Stores the state and relays to all other connected clients.
1202
+ *
1203
+ * @param sessionId - Session ID of the sending client
1204
+ * @param message - The awareness update message
1205
+ */
1206
+ handleUpdate(sessionId: string, message: AwarenessUpdateMessage): void;
1207
+ /**
1208
+ * Get the number of registered awareness clients.
1209
+ */
1210
+ getClientCount(): number;
1211
+ /**
1212
+ * Remove all clients and clear all state.
1213
+ */
1214
+ clear(): void;
1215
+ private broadcastExcept;
1216
+ }
1217
+
785
1218
  /**
786
1219
  * Factory function to create a KoraSyncServer.
787
1220
  *
@@ -799,4 +1232,4 @@ declare class NoAuthProvider implements AuthProvider {
799
1232
  */
800
1233
  declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
801
1234
 
802
- export { type AuthContext, type AuthProvider, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type MaterializedRecord, MemoryServerStore, NoAuthProvider, PostgresServerStore, type ProductionServer, type ProductionServerConfig, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createKoraServer, createPostgresServerStore, createProductionServer, createSqliteServerStore };
1235
+ export { type AuthContext, type AuthProvider, AwarenessRelay, type AwarenessRelayCallback, type ClientMetrics, ClientSession, type ClientSessionOptions, type CollectionQueryOptions, type HttpPollResponse, HttpServerTransport, type HttpSyncRequest, type HttpSyncResponse, KoraAuthProvider, type KoraAuthProviderOptions, KoraSyncServer, type KoraSyncServerConfig, type LogEntry, type LogLevel, type Logger, type MaterializedRecord, MemoryServerStore, MixedAuthProvider, type MixedAuthProviderOptions, NoAuthProvider, PostgresServerStore, type ProductionHttpRoute, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, type RelayCallback, ServerCloseHandler, ServerErrorHandler, ServerMessageHandler, ServerMetricsCollector, type ServerMetricsSnapshot, type ServerStatus, type ServerStore, ServerTransport, type SessionState, SqliteServerStore, TokenAuthProvider, type TokenAuthProviderOptions, type WsServerConstructor, type WsServerLike, WsServerTransport, type WsServerTransportOptions, type WsWebSocket, createDefaultLogger, createJsonLogger, createKoraServer, createPostgresServerStore, createPrettyLogger, createProductionServer, createSilentLogger, createSqliteServerStore };