@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/README.md +43 -0
- package/dist/index.cjs +1691 -113
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +442 -9
- package/dist/index.d.ts +442 -9
- package/dist/index.js +1517 -100
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +1 -1
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.js +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/server-backup-MOICK6MI.js +116 -0
- package/dist/server-backup-MOICK6MI.js.map +1 -0
- package/package.json +4 -3
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
|
-
|
|
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
|
|
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 };
|