@korajs/server 1.0.0-beta.0 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +431 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +155 -3
- package/dist/index.d.ts +155 -3
- package/dist/index.js +416 -11
- package/dist/index.js.map +1 -1
- package/package.json +5 -4
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SchemaDefinition, KoraEventEmitter, Operation, VersionVector } from '@korajs/core';
|
|
1
|
+
import { SchemaDefinition, KoraEventEmitter, Operation, BlobRef, VersionVector } from '@korajs/core';
|
|
2
2
|
import * as _korajs_sync from '@korajs/sync';
|
|
3
|
-
import { SyncStore, MessageSerializer, SyncMessage, AwarenessUpdateMessage, YjsDocUpdateMessage, ApplyResult } from '@korajs/sync';
|
|
3
|
+
import { SyncStore, MessageSerializer, SyncMessage, AwarenessUpdateMessage, YjsDocUpdateMessage, BlobChunkRequestMessage, BlobChunkResponseMessage, ApplyResult } from '@korajs/sync';
|
|
4
4
|
import { S as ServerTransport, a as ServerMessageHandler, b as ServerCloseHandler, c as ServerErrorHandler } from './server-transport-CU1BLWgr.cjs';
|
|
5
5
|
import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
|
|
6
6
|
import { BetterSQLite3Database } from 'drizzle-orm/better-sqlite3';
|
|
@@ -286,6 +286,23 @@ interface KoraSyncServerConfig {
|
|
|
286
286
|
metricsCollector?: ServerMetricsCollector;
|
|
287
287
|
/** Enable built-in dashboard and metrics endpoints. Defaults to true. */
|
|
288
288
|
enableDashboard?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Resolve a blob chunk by content hash from server-side storage. Optional.
|
|
291
|
+
* When provided, the server answers blob chunk requests directly from its own
|
|
292
|
+
* store; when absent, the server relays chunk requests among connected peers
|
|
293
|
+
* without storing any blob bytes itself.
|
|
294
|
+
*/
|
|
295
|
+
resolveBlobChunk?: (hash: string) => Promise<Uint8Array | null>;
|
|
296
|
+
/**
|
|
297
|
+
* Persist a client-uploaded blob chunk (or manifest) centrally, keyed by its
|
|
298
|
+
* content hash. Optional. When provided, the server advertises central blob
|
|
299
|
+
* storage at handshake, clients upload the bytes behind their `blob` fields,
|
|
300
|
+
* and those bytes remain available to other devices after the authoring device
|
|
301
|
+
* goes offline. Pair with `resolveBlobChunk` (both backed by the same store)
|
|
302
|
+
* so uploaded blobs can then be served. `toServerBlobCallbacks` in
|
|
303
|
+
* `@korajs/store` derives both from a `ContentAddressedBlobStore`.
|
|
304
|
+
*/
|
|
305
|
+
persistBlobChunk?: (hash: string, bytes: Uint8Array) => Promise<void> | void;
|
|
289
306
|
}
|
|
290
307
|
/**
|
|
291
308
|
* Request envelope for the server-side HTTP sync endpoint.
|
|
@@ -439,6 +456,20 @@ type AwarenessRelayCallback = (sourceSessionId: string, message: AwarenessUpdate
|
|
|
439
456
|
* Callback invoked when a session receives a Yjs doc channel update to relay.
|
|
440
457
|
*/
|
|
441
458
|
type YjsDocRelayCallback = (sourceSessionId: string, message: YjsDocUpdateMessage) => void;
|
|
459
|
+
/**
|
|
460
|
+
* Callback invoked when a session receives a blob chunk request to route.
|
|
461
|
+
*/
|
|
462
|
+
type BlobChunkRequestCallback = (sourceSessionId: string, message: BlobChunkRequestMessage) => void;
|
|
463
|
+
/**
|
|
464
|
+
* Callback invoked when a session receives a blob chunk response to route back.
|
|
465
|
+
*/
|
|
466
|
+
type BlobChunkResponseCallback = (sourceSessionId: string, message: BlobChunkResponseMessage) => void;
|
|
467
|
+
/**
|
|
468
|
+
* Persist a blob chunk (or manifest) uploaded by a client, keyed by its content
|
|
469
|
+
* hash. Provided by the server operator; enables central blob storage so bytes
|
|
470
|
+
* survive the authoring device going offline.
|
|
471
|
+
*/
|
|
472
|
+
type PersistBlobChunk = (hash: string, bytes: Uint8Array) => Promise<void> | void;
|
|
442
473
|
/**
|
|
443
474
|
* Options for creating a ClientSession.
|
|
444
475
|
*/
|
|
@@ -470,6 +501,12 @@ interface ClientSessionOptions {
|
|
|
470
501
|
onAwarenessUpdate?: AwarenessRelayCallback;
|
|
471
502
|
/** Called when this session receives a Yjs doc channel update to broadcast */
|
|
472
503
|
onYjsDocUpdate?: YjsDocRelayCallback;
|
|
504
|
+
/** Called when this session receives a blob chunk request to route */
|
|
505
|
+
onBlobChunkRequest?: BlobChunkRequestCallback;
|
|
506
|
+
/** Called when this session receives a blob chunk response to route back */
|
|
507
|
+
onBlobChunkResponse?: BlobChunkResponseCallback;
|
|
508
|
+
/** Persist a client-uploaded blob chunk centrally (keyed by content hash) */
|
|
509
|
+
persistBlobChunk?: PersistBlobChunk;
|
|
473
510
|
/** Called when this session closes */
|
|
474
511
|
onClose?: (sessionId: string) => void;
|
|
475
512
|
/** Maximum serialized operation size in bytes. Defaults to 256 KiB. */
|
|
@@ -509,6 +546,9 @@ declare class ClientSession {
|
|
|
509
546
|
private readonly onRelay;
|
|
510
547
|
private readonly onAwarenessUpdate;
|
|
511
548
|
private readonly onYjsDocUpdate;
|
|
549
|
+
private readonly onBlobChunkRequest;
|
|
550
|
+
private readonly onBlobChunkResponse;
|
|
551
|
+
private readonly persistBlobChunk;
|
|
512
552
|
private readonly onClose;
|
|
513
553
|
private readonly maxOperationBytes;
|
|
514
554
|
private readonly maxOpsPerMinute;
|
|
@@ -542,6 +582,12 @@ declare class ClientSession {
|
|
|
542
582
|
private sendToClient;
|
|
543
583
|
private enqueueMessage;
|
|
544
584
|
private handleMessageAsync;
|
|
585
|
+
/**
|
|
586
|
+
* Persist a client-uploaded blob chunk (or manifest) centrally. The bytes are
|
|
587
|
+
* verified to hash to the declared hash before storing, so a corrupt or
|
|
588
|
+
* mislabeled upload is rejected rather than served later as trusted content.
|
|
589
|
+
*/
|
|
590
|
+
private handleBlobChunkPush;
|
|
545
591
|
private handleMessageFailure;
|
|
546
592
|
private handleHandshake;
|
|
547
593
|
private handleOperationBatch;
|
|
@@ -554,6 +600,18 @@ declare class ClientSession {
|
|
|
554
600
|
private handleTransportClose;
|
|
555
601
|
}
|
|
556
602
|
|
|
603
|
+
interface ApplyServerOperationResult {
|
|
604
|
+
/** Result of applying the primary operation */
|
|
605
|
+
result: ApplyResult;
|
|
606
|
+
/** Primary op when applied, plus any server-generated side-effect operations */
|
|
607
|
+
appliedOperations: Operation[];
|
|
608
|
+
/** Rejection reason when the operation was not applied */
|
|
609
|
+
rejection?: {
|
|
610
|
+
code: string;
|
|
611
|
+
message: string;
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
557
615
|
/**
|
|
558
616
|
* Minimal interface for a ws.WebSocketServer instance.
|
|
559
617
|
* Allows dependency injection for testing without importing ws directly.
|
|
@@ -598,6 +656,8 @@ declare class KoraSyncServer {
|
|
|
598
656
|
private readonly metrics;
|
|
599
657
|
private readonly awarenessRelay;
|
|
600
658
|
private readonly yjsDocRelay;
|
|
659
|
+
private readonly blobChunkRelay;
|
|
660
|
+
private readonly persistBlobChunk;
|
|
601
661
|
private readonly sessions;
|
|
602
662
|
private readonly httpClients;
|
|
603
663
|
private readonly httpSessionToClient;
|
|
@@ -646,6 +706,32 @@ declare class KoraSyncServer {
|
|
|
646
706
|
* Get the current server status.
|
|
647
707
|
*/
|
|
648
708
|
getStatus(): Promise<ServerStatus>;
|
|
709
|
+
/**
|
|
710
|
+
* Apply a server-originated operation (for example one created by a custom
|
|
711
|
+
* HTTP route) through the same validated pipeline that incoming client
|
|
712
|
+
* operations use — Tier 2 constraints, referential integrity, and cascade
|
|
713
|
+
* side effects — then relay every applied operation to connected clients.
|
|
714
|
+
*
|
|
715
|
+
* Because the operation did not come from a client session, it is relayed to
|
|
716
|
+
* ALL sessions (there is no source session to exclude). Each session still
|
|
717
|
+
* applies its own per-scope visibility filter in `relayOperations`, so a
|
|
718
|
+
* client only receives the operation if it falls within that client's scope.
|
|
719
|
+
*
|
|
720
|
+
* @param op - A fully-formed, server-originated operation to apply
|
|
721
|
+
* @returns The apply result, including any server-generated side-effect ops
|
|
722
|
+
*/
|
|
723
|
+
applyLocalOperation(op: Operation): Promise<ApplyServerOperationResult>;
|
|
724
|
+
/**
|
|
725
|
+
* Collect every blob reference still reachable from live records on the server.
|
|
726
|
+
*
|
|
727
|
+
* This is the live set for garbage-collecting the server's central blob store:
|
|
728
|
+
* pass the result to `collectBlobGarbage(blobStore, liveRefs)` from
|
|
729
|
+
* `@korajs/store` to reclaim bytes no record references any more. Only
|
|
730
|
+
* collections that declare a `blob` field are scanned.
|
|
731
|
+
*
|
|
732
|
+
* @returns Every live blob reference across all collections
|
|
733
|
+
*/
|
|
734
|
+
getLiveBlobRefs(): Promise<BlobRef[]>;
|
|
649
735
|
/**
|
|
650
736
|
* Get the number of currently connected clients.
|
|
651
737
|
*/
|
|
@@ -657,6 +743,65 @@ declare class KoraSyncServer {
|
|
|
657
743
|
private getOrCreateHttpClient;
|
|
658
744
|
}
|
|
659
745
|
|
|
746
|
+
/**
|
|
747
|
+
* Per-collection scope map from auth context.
|
|
748
|
+
*/
|
|
749
|
+
type ScopeMap = Record<string, Record<string, unknown>>;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* A mutation a custom HTTP route asks the server to apply on the data plane.
|
|
753
|
+
*
|
|
754
|
+
* `recordId` is optional for inserts (a UUID v7 is generated when omitted) and
|
|
755
|
+
* required for updates and deletes. For updates, `data` holds only the changed
|
|
756
|
+
* fields; the server reads the current record to capture the previous values so
|
|
757
|
+
* the change three-way merges correctly with concurrent client edits.
|
|
758
|
+
*/
|
|
759
|
+
interface RouteMutation {
|
|
760
|
+
collection: string;
|
|
761
|
+
type: 'insert' | 'update' | 'delete';
|
|
762
|
+
recordId?: string;
|
|
763
|
+
data?: Record<string, unknown> | null;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Scope the route derived from the request's authenticated actor. When present,
|
|
767
|
+
* it is enforced exactly like a sync session's scope: `apply()` rejects a
|
|
768
|
+
* mutation whose resulting record falls outside the scope, and `query()` /
|
|
769
|
+
* `findById()` only return records inside it. Omit it for genuinely public
|
|
770
|
+
* routes (for example a signup endpoint) where no per-actor isolation applies.
|
|
771
|
+
*/
|
|
772
|
+
interface RouteScopeOptions {
|
|
773
|
+
scope?: ScopeMap;
|
|
774
|
+
}
|
|
775
|
+
/** Result of {@link ProductionHttpRouteContext.apply}. */
|
|
776
|
+
type RouteApplyResult = {
|
|
777
|
+
ok: true;
|
|
778
|
+
operation: Operation;
|
|
779
|
+
record: MaterializedRecord | null;
|
|
780
|
+
} | {
|
|
781
|
+
ok: false;
|
|
782
|
+
code: string;
|
|
783
|
+
message: string;
|
|
784
|
+
};
|
|
785
|
+
/**
|
|
786
|
+
* Scoped, validated data-plane access handed to custom HTTP route handlers.
|
|
787
|
+
*
|
|
788
|
+
* Every mutation goes through the same pipeline as sync (Tier 2 constraints,
|
|
789
|
+
* referential integrity, materialization, and fan-out to connected clients), so
|
|
790
|
+
* REST endpoints cannot accidentally bypass validation, constraints, or tenant
|
|
791
|
+
* isolation the way hand-rolled store writes can.
|
|
792
|
+
*/
|
|
793
|
+
interface ProductionHttpRouteContext {
|
|
794
|
+
/**
|
|
795
|
+
* Apply a mutation through the validated server pipeline and relay the
|
|
796
|
+
* resulting operation(s) to connected clients.
|
|
797
|
+
*/
|
|
798
|
+
apply(mutation: RouteMutation, options?: RouteScopeOptions): Promise<RouteApplyResult>;
|
|
799
|
+
/** Read materialized records from a collection, optionally scoped. */
|
|
800
|
+
query(collection: string, options?: CollectionQueryOptions & RouteScopeOptions): Promise<MaterializedRecord[]>;
|
|
801
|
+
/** Read a single materialized record by id, optionally scoped. */
|
|
802
|
+
findById(collection: string, id: string, options?: RouteScopeOptions): Promise<MaterializedRecord | null>;
|
|
803
|
+
}
|
|
804
|
+
|
|
660
805
|
/**
|
|
661
806
|
* Configuration for the production server that serves both
|
|
662
807
|
* static files and WebSocket sync on a single port.
|
|
@@ -704,6 +849,13 @@ interface ProductionHttpRouteRequest {
|
|
|
704
849
|
headers?: Record<string, string | string[] | undefined>;
|
|
705
850
|
query?: Record<string, string | string[] | undefined>;
|
|
706
851
|
ip?: string;
|
|
852
|
+
/**
|
|
853
|
+
* Scoped, validated data-plane access. Use `kora.apply()` to mutate through
|
|
854
|
+
* the same pipeline as sync (constraints, referential integrity, fan-out) and
|
|
855
|
+
* `kora.query()` / `kora.findById()` to read materialized state, instead of
|
|
856
|
+
* writing to the store directly and bypassing those guarantees.
|
|
857
|
+
*/
|
|
858
|
+
kora: ProductionHttpRouteContext;
|
|
707
859
|
}
|
|
708
860
|
interface ProductionHttpRouteResponse {
|
|
709
861
|
status: number;
|
|
@@ -1232,4 +1384,4 @@ declare class AwarenessRelay {
|
|
|
1232
1384
|
*/
|
|
1233
1385
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
1234
1386
|
|
|
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 };
|
|
1387
|
+
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 ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, type RelayCallback, type RouteApplyResult, type RouteMutation, type RouteScopeOptions, 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 };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SchemaDefinition, KoraEventEmitter, Operation, VersionVector } from '@korajs/core';
|
|
1
|
+
import { SchemaDefinition, KoraEventEmitter, Operation, BlobRef, VersionVector } from '@korajs/core';
|
|
2
2
|
import * as _korajs_sync from '@korajs/sync';
|
|
3
|
-
import { SyncStore, MessageSerializer, SyncMessage, AwarenessUpdateMessage, YjsDocUpdateMessage, ApplyResult } from '@korajs/sync';
|
|
3
|
+
import { SyncStore, MessageSerializer, SyncMessage, AwarenessUpdateMessage, YjsDocUpdateMessage, BlobChunkRequestMessage, BlobChunkResponseMessage, 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';
|
|
@@ -286,6 +286,23 @@ interface KoraSyncServerConfig {
|
|
|
286
286
|
metricsCollector?: ServerMetricsCollector;
|
|
287
287
|
/** Enable built-in dashboard and metrics endpoints. Defaults to true. */
|
|
288
288
|
enableDashboard?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Resolve a blob chunk by content hash from server-side storage. Optional.
|
|
291
|
+
* When provided, the server answers blob chunk requests directly from its own
|
|
292
|
+
* store; when absent, the server relays chunk requests among connected peers
|
|
293
|
+
* without storing any blob bytes itself.
|
|
294
|
+
*/
|
|
295
|
+
resolveBlobChunk?: (hash: string) => Promise<Uint8Array | null>;
|
|
296
|
+
/**
|
|
297
|
+
* Persist a client-uploaded blob chunk (or manifest) centrally, keyed by its
|
|
298
|
+
* content hash. Optional. When provided, the server advertises central blob
|
|
299
|
+
* storage at handshake, clients upload the bytes behind their `blob` fields,
|
|
300
|
+
* and those bytes remain available to other devices after the authoring device
|
|
301
|
+
* goes offline. Pair with `resolveBlobChunk` (both backed by the same store)
|
|
302
|
+
* so uploaded blobs can then be served. `toServerBlobCallbacks` in
|
|
303
|
+
* `@korajs/store` derives both from a `ContentAddressedBlobStore`.
|
|
304
|
+
*/
|
|
305
|
+
persistBlobChunk?: (hash: string, bytes: Uint8Array) => Promise<void> | void;
|
|
289
306
|
}
|
|
290
307
|
/**
|
|
291
308
|
* Request envelope for the server-side HTTP sync endpoint.
|
|
@@ -439,6 +456,20 @@ type AwarenessRelayCallback = (sourceSessionId: string, message: AwarenessUpdate
|
|
|
439
456
|
* Callback invoked when a session receives a Yjs doc channel update to relay.
|
|
440
457
|
*/
|
|
441
458
|
type YjsDocRelayCallback = (sourceSessionId: string, message: YjsDocUpdateMessage) => void;
|
|
459
|
+
/**
|
|
460
|
+
* Callback invoked when a session receives a blob chunk request to route.
|
|
461
|
+
*/
|
|
462
|
+
type BlobChunkRequestCallback = (sourceSessionId: string, message: BlobChunkRequestMessage) => void;
|
|
463
|
+
/**
|
|
464
|
+
* Callback invoked when a session receives a blob chunk response to route back.
|
|
465
|
+
*/
|
|
466
|
+
type BlobChunkResponseCallback = (sourceSessionId: string, message: BlobChunkResponseMessage) => void;
|
|
467
|
+
/**
|
|
468
|
+
* Persist a blob chunk (or manifest) uploaded by a client, keyed by its content
|
|
469
|
+
* hash. Provided by the server operator; enables central blob storage so bytes
|
|
470
|
+
* survive the authoring device going offline.
|
|
471
|
+
*/
|
|
472
|
+
type PersistBlobChunk = (hash: string, bytes: Uint8Array) => Promise<void> | void;
|
|
442
473
|
/**
|
|
443
474
|
* Options for creating a ClientSession.
|
|
444
475
|
*/
|
|
@@ -470,6 +501,12 @@ interface ClientSessionOptions {
|
|
|
470
501
|
onAwarenessUpdate?: AwarenessRelayCallback;
|
|
471
502
|
/** Called when this session receives a Yjs doc channel update to broadcast */
|
|
472
503
|
onYjsDocUpdate?: YjsDocRelayCallback;
|
|
504
|
+
/** Called when this session receives a blob chunk request to route */
|
|
505
|
+
onBlobChunkRequest?: BlobChunkRequestCallback;
|
|
506
|
+
/** Called when this session receives a blob chunk response to route back */
|
|
507
|
+
onBlobChunkResponse?: BlobChunkResponseCallback;
|
|
508
|
+
/** Persist a client-uploaded blob chunk centrally (keyed by content hash) */
|
|
509
|
+
persistBlobChunk?: PersistBlobChunk;
|
|
473
510
|
/** Called when this session closes */
|
|
474
511
|
onClose?: (sessionId: string) => void;
|
|
475
512
|
/** Maximum serialized operation size in bytes. Defaults to 256 KiB. */
|
|
@@ -509,6 +546,9 @@ declare class ClientSession {
|
|
|
509
546
|
private readonly onRelay;
|
|
510
547
|
private readonly onAwarenessUpdate;
|
|
511
548
|
private readonly onYjsDocUpdate;
|
|
549
|
+
private readonly onBlobChunkRequest;
|
|
550
|
+
private readonly onBlobChunkResponse;
|
|
551
|
+
private readonly persistBlobChunk;
|
|
512
552
|
private readonly onClose;
|
|
513
553
|
private readonly maxOperationBytes;
|
|
514
554
|
private readonly maxOpsPerMinute;
|
|
@@ -542,6 +582,12 @@ declare class ClientSession {
|
|
|
542
582
|
private sendToClient;
|
|
543
583
|
private enqueueMessage;
|
|
544
584
|
private handleMessageAsync;
|
|
585
|
+
/**
|
|
586
|
+
* Persist a client-uploaded blob chunk (or manifest) centrally. The bytes are
|
|
587
|
+
* verified to hash to the declared hash before storing, so a corrupt or
|
|
588
|
+
* mislabeled upload is rejected rather than served later as trusted content.
|
|
589
|
+
*/
|
|
590
|
+
private handleBlobChunkPush;
|
|
545
591
|
private handleMessageFailure;
|
|
546
592
|
private handleHandshake;
|
|
547
593
|
private handleOperationBatch;
|
|
@@ -554,6 +600,18 @@ declare class ClientSession {
|
|
|
554
600
|
private handleTransportClose;
|
|
555
601
|
}
|
|
556
602
|
|
|
603
|
+
interface ApplyServerOperationResult {
|
|
604
|
+
/** Result of applying the primary operation */
|
|
605
|
+
result: ApplyResult;
|
|
606
|
+
/** Primary op when applied, plus any server-generated side-effect operations */
|
|
607
|
+
appliedOperations: Operation[];
|
|
608
|
+
/** Rejection reason when the operation was not applied */
|
|
609
|
+
rejection?: {
|
|
610
|
+
code: string;
|
|
611
|
+
message: string;
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
557
615
|
/**
|
|
558
616
|
* Minimal interface for a ws.WebSocketServer instance.
|
|
559
617
|
* Allows dependency injection for testing without importing ws directly.
|
|
@@ -598,6 +656,8 @@ declare class KoraSyncServer {
|
|
|
598
656
|
private readonly metrics;
|
|
599
657
|
private readonly awarenessRelay;
|
|
600
658
|
private readonly yjsDocRelay;
|
|
659
|
+
private readonly blobChunkRelay;
|
|
660
|
+
private readonly persistBlobChunk;
|
|
601
661
|
private readonly sessions;
|
|
602
662
|
private readonly httpClients;
|
|
603
663
|
private readonly httpSessionToClient;
|
|
@@ -646,6 +706,32 @@ declare class KoraSyncServer {
|
|
|
646
706
|
* Get the current server status.
|
|
647
707
|
*/
|
|
648
708
|
getStatus(): Promise<ServerStatus>;
|
|
709
|
+
/**
|
|
710
|
+
* Apply a server-originated operation (for example one created by a custom
|
|
711
|
+
* HTTP route) through the same validated pipeline that incoming client
|
|
712
|
+
* operations use — Tier 2 constraints, referential integrity, and cascade
|
|
713
|
+
* side effects — then relay every applied operation to connected clients.
|
|
714
|
+
*
|
|
715
|
+
* Because the operation did not come from a client session, it is relayed to
|
|
716
|
+
* ALL sessions (there is no source session to exclude). Each session still
|
|
717
|
+
* applies its own per-scope visibility filter in `relayOperations`, so a
|
|
718
|
+
* client only receives the operation if it falls within that client's scope.
|
|
719
|
+
*
|
|
720
|
+
* @param op - A fully-formed, server-originated operation to apply
|
|
721
|
+
* @returns The apply result, including any server-generated side-effect ops
|
|
722
|
+
*/
|
|
723
|
+
applyLocalOperation(op: Operation): Promise<ApplyServerOperationResult>;
|
|
724
|
+
/**
|
|
725
|
+
* Collect every blob reference still reachable from live records on the server.
|
|
726
|
+
*
|
|
727
|
+
* This is the live set for garbage-collecting the server's central blob store:
|
|
728
|
+
* pass the result to `collectBlobGarbage(blobStore, liveRefs)` from
|
|
729
|
+
* `@korajs/store` to reclaim bytes no record references any more. Only
|
|
730
|
+
* collections that declare a `blob` field are scanned.
|
|
731
|
+
*
|
|
732
|
+
* @returns Every live blob reference across all collections
|
|
733
|
+
*/
|
|
734
|
+
getLiveBlobRefs(): Promise<BlobRef[]>;
|
|
649
735
|
/**
|
|
650
736
|
* Get the number of currently connected clients.
|
|
651
737
|
*/
|
|
@@ -657,6 +743,65 @@ declare class KoraSyncServer {
|
|
|
657
743
|
private getOrCreateHttpClient;
|
|
658
744
|
}
|
|
659
745
|
|
|
746
|
+
/**
|
|
747
|
+
* Per-collection scope map from auth context.
|
|
748
|
+
*/
|
|
749
|
+
type ScopeMap = Record<string, Record<string, unknown>>;
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* A mutation a custom HTTP route asks the server to apply on the data plane.
|
|
753
|
+
*
|
|
754
|
+
* `recordId` is optional for inserts (a UUID v7 is generated when omitted) and
|
|
755
|
+
* required for updates and deletes. For updates, `data` holds only the changed
|
|
756
|
+
* fields; the server reads the current record to capture the previous values so
|
|
757
|
+
* the change three-way merges correctly with concurrent client edits.
|
|
758
|
+
*/
|
|
759
|
+
interface RouteMutation {
|
|
760
|
+
collection: string;
|
|
761
|
+
type: 'insert' | 'update' | 'delete';
|
|
762
|
+
recordId?: string;
|
|
763
|
+
data?: Record<string, unknown> | null;
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Scope the route derived from the request's authenticated actor. When present,
|
|
767
|
+
* it is enforced exactly like a sync session's scope: `apply()` rejects a
|
|
768
|
+
* mutation whose resulting record falls outside the scope, and `query()` /
|
|
769
|
+
* `findById()` only return records inside it. Omit it for genuinely public
|
|
770
|
+
* routes (for example a signup endpoint) where no per-actor isolation applies.
|
|
771
|
+
*/
|
|
772
|
+
interface RouteScopeOptions {
|
|
773
|
+
scope?: ScopeMap;
|
|
774
|
+
}
|
|
775
|
+
/** Result of {@link ProductionHttpRouteContext.apply}. */
|
|
776
|
+
type RouteApplyResult = {
|
|
777
|
+
ok: true;
|
|
778
|
+
operation: Operation;
|
|
779
|
+
record: MaterializedRecord | null;
|
|
780
|
+
} | {
|
|
781
|
+
ok: false;
|
|
782
|
+
code: string;
|
|
783
|
+
message: string;
|
|
784
|
+
};
|
|
785
|
+
/**
|
|
786
|
+
* Scoped, validated data-plane access handed to custom HTTP route handlers.
|
|
787
|
+
*
|
|
788
|
+
* Every mutation goes through the same pipeline as sync (Tier 2 constraints,
|
|
789
|
+
* referential integrity, materialization, and fan-out to connected clients), so
|
|
790
|
+
* REST endpoints cannot accidentally bypass validation, constraints, or tenant
|
|
791
|
+
* isolation the way hand-rolled store writes can.
|
|
792
|
+
*/
|
|
793
|
+
interface ProductionHttpRouteContext {
|
|
794
|
+
/**
|
|
795
|
+
* Apply a mutation through the validated server pipeline and relay the
|
|
796
|
+
* resulting operation(s) to connected clients.
|
|
797
|
+
*/
|
|
798
|
+
apply(mutation: RouteMutation, options?: RouteScopeOptions): Promise<RouteApplyResult>;
|
|
799
|
+
/** Read materialized records from a collection, optionally scoped. */
|
|
800
|
+
query(collection: string, options?: CollectionQueryOptions & RouteScopeOptions): Promise<MaterializedRecord[]>;
|
|
801
|
+
/** Read a single materialized record by id, optionally scoped. */
|
|
802
|
+
findById(collection: string, id: string, options?: RouteScopeOptions): Promise<MaterializedRecord | null>;
|
|
803
|
+
}
|
|
804
|
+
|
|
660
805
|
/**
|
|
661
806
|
* Configuration for the production server that serves both
|
|
662
807
|
* static files and WebSocket sync on a single port.
|
|
@@ -704,6 +849,13 @@ interface ProductionHttpRouteRequest {
|
|
|
704
849
|
headers?: Record<string, string | string[] | undefined>;
|
|
705
850
|
query?: Record<string, string | string[] | undefined>;
|
|
706
851
|
ip?: string;
|
|
852
|
+
/**
|
|
853
|
+
* Scoped, validated data-plane access. Use `kora.apply()` to mutate through
|
|
854
|
+
* the same pipeline as sync (constraints, referential integrity, fan-out) and
|
|
855
|
+
* `kora.query()` / `kora.findById()` to read materialized state, instead of
|
|
856
|
+
* writing to the store directly and bypassing those guarantees.
|
|
857
|
+
*/
|
|
858
|
+
kora: ProductionHttpRouteContext;
|
|
707
859
|
}
|
|
708
860
|
interface ProductionHttpRouteResponse {
|
|
709
861
|
status: number;
|
|
@@ -1232,4 +1384,4 @@ declare class AwarenessRelay {
|
|
|
1232
1384
|
*/
|
|
1233
1385
|
declare function createKoraServer(config: KoraSyncServerConfig): KoraSyncServer;
|
|
1234
1386
|
|
|
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 };
|
|
1387
|
+
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 ProductionHttpRouteContext, type ProductionHttpRouteRequest, type ProductionHttpRouteResponse, type ProductionServer, type ProductionServerConfig, type RelayCallback, type RouteApplyResult, type RouteMutation, type RouteScopeOptions, 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 };
|