@@ -17,6 +17,11 @@ import {
1717 mapNew ,
1818 mapSet ,
1919} from '../../common/map.ts' ;
20+ import type { Persister , Persists } from '../../@types/persisters/index.d.ts' ;
21+ import type {
22+ Receive ,
23+ Synchronizer ,
24+ } from '../../@types/synchronizers/index.d.ts' ;
2025import { WebSocket , WebSocketServer } from 'ws' ;
2126import {
2227 collClear ,
@@ -25,86 +30,124 @@ import {
2530 collSize ,
2631 collSize2 ,
2732} from '../../common/coll.ts' ;
28- import { ifNotUndefined , slice } from '../../common/other.ts' ;
33+ import {
34+ createPayload ,
35+ createRawPayload ,
36+ ifPayloadValid ,
37+ receivePayload ,
38+ } from '../common.ts' ;
2939import { IdSet2 } from '../../common/set.ts' ;
30- import { MESSAGE_SEPARATOR } from '../common.ts' ;
3140import type { MergeableStore } from '../../@types/mergeable-store/index.d.ts' ;
32- import type { Persister } from '../../@types/persisters/index.d.ts' ;
33- import { createMergeableStore } from '../../mergeable-store/index.ts' ;
41+ import { createCustomSynchronizer } from '../index.ts' ;
3442import { getListenerFunctions } from '../../common/listeners.ts' ;
43+ import { ifNotUndefined } from '../../common/other.ts' ;
3544import { objFreeze } from '../../common/obj.ts' ;
3645
46+ type ServerClient = {
47+ persister : Persister <
48+ Persists . MergeableStoreOnly | Persists . StoreOrMergeableStore
49+ > ;
50+ synchronizer : Synchronizer ;
51+ send : ( payload : string ) => void ;
52+ } ;
53+
3754const PATH_REGEX = / \/ ( [ ^ ? ] * ) / ;
55+ const SERVER_CLIENT_ID = 'S' ;
3856
3957export const createWsServer = ( (
4058 webSocketServer : WebSocketServer ,
41- createPersister ?: ( store : MergeableStore , path : Id ) => Persister | undefined ,
59+ createPersister ?: (
60+ path : Id ,
61+ ) =>
62+ | Persister < Persists . MergeableStoreOnly | Persists . StoreOrMergeableStore >
63+ | undefined ,
4264) => {
4365 const pathIdListeners : IdSet2 = mapNew ( ) ;
4466 const clientIdListeners : IdSet2 = mapNew ( ) ;
4567 const clientsByPath : IdMap2 < WebSocket > = mapNew ( ) ;
46- const persistersByPath : IdMap < Persister > = mapNew ( ) ;
68+ const serverClientsByPath : IdMap < ServerClient > = mapNew ( ) ;
4769
4870 const [ addListener , callListeners , delListenerImpl ] = getListenerFunctions (
4971 ( ) => wsServer ,
5072 ) ;
5173
52- const startPath = ( pathId : Id ) => {
53- callListeners ( pathIdListeners , undefined , pathId , 1 ) ;
74+ const startServerClient = ( pathId : Id ) =>
75+ ifNotUndefined ( createPersister ?.( pathId ) , async ( persister ) => {
76+ const serverClient = mapEnsure (
77+ serverClientsByPath ,
78+ pathId ,
79+ ( ) => ( { persister} ) as ServerClient ,
80+ ) ;
81+ const messageHandler = getMessageHandler ( SERVER_CLIENT_ID , pathId ) ;
82+
83+ serverClient . synchronizer = await createCustomSynchronizer (
84+ persister . getStore ( ) as MergeableStore ,
85+ ( toClientId , requestId , message , body ) =>
86+ messageHandler ( createPayload ( toClientId , requestId , message , body ) ) ,
87+ ( receive : Receive ) =>
88+ ( serverClient . send = ( payload ) => receivePayload ( payload , receive ) ) ,
89+ ( ) => { } ,
90+ 0.1 ,
91+ ) . startSync ( ) ;
92+ await persister . startAutoLoad ( ) ;
93+ await persister . startAutoSave ( ) ;
94+ } ) ;
95+
96+ const stopServerClient = ( pathId : Id ) =>
5497 ifNotUndefined (
55- createPersister ?. ( createMergeableStore ( ) , pathId ) ,
56- async ( persister ) => {
57- mapSet ( persistersByPath , pathId , persister ) ;
58- await persister . startAutoLoad ( ) ;
59- await persister . startAutoSave ( ) ;
98+ mapGet ( serverClientsByPath , pathId ) ,
99+ ( { persister, synchronizer } ) => {
100+ persister . destroy ( ) ;
101+ synchronizer ?. destroy ( ) ;
102+ collDel ( serverClientsByPath , pathId ) ;
60103 } ,
61104 ) ;
62- } ;
63105
64- const finishPath = ( pathId : Id ) => {
65- ifNotUndefined ( mapGet ( persistersByPath , pathId ) , ( persister ) =>
66- persister . destroy ( ) ,
67- ) ;
68- callListeners ( pathIdListeners , undefined , pathId , - 1 ) ;
106+ const getMessageHandler = ( clientId : Id , pathId : Id ) => {
107+ const clients = mapGet ( clientsByPath , pathId ) ;
108+ const serverClient = mapGet ( serverClientsByPath , pathId ) ;
109+ return ( payload : string ) =>
110+ ifPayloadValid ( payload , ( toClientId , remainder ) => {
111+ const forwardedPayload = createRawPayload ( clientId , remainder ) ;
112+ if ( toClientId === EMPTY_STRING ) {
113+ clientId !== SERVER_CLIENT_ID
114+ ? serverClient ?. send ( forwardedPayload )
115+ : 0 ;
116+ mapForEach ( clients , ( otherClientId , otherWebSocket ) =>
117+ otherClientId !== clientId
118+ ? otherWebSocket . send ( forwardedPayload )
119+ : 0 ,
120+ ) ;
121+ } else {
122+ ( toClientId === SERVER_CLIENT_ID
123+ ? serverClient
124+ : mapGet ( clients , toClientId )
125+ ) ?. send ( forwardedPayload ) ;
126+ }
127+ } ) ;
69128 } ;
70129
71130 webSocketServer . on ( 'connection' , ( webSocket , request ) =>
72131 ifNotUndefined ( request . url ?. match ( PATH_REGEX ) , ( [ , pathId ] ) =>
73132 ifNotUndefined ( request . headers [ 'sec-websocket-key' ] , ( clientId ) => {
74133 const clients = mapEnsure ( clientsByPath , pathId , mapNew < Id , WebSocket > ) ;
75- mapSet ( clients , clientId , webSocket ) ;
76-
77- if ( clients . size == 1 ) {
78- startPath ( pathId ) ;
134+ if ( collIsEmpty ( clients ) ) {
135+ callListeners ( pathIdListeners , undefined , pathId , 1 ) ;
136+ startServerClient ( pathId ) ;
79137 }
138+ mapSet ( clients , clientId , webSocket ) ;
80139 callListeners ( clientIdListeners , [ pathId ] , clientId , 1 ) ;
81140
82- webSocket . on ( 'message' , ( data ) => {
83- const payload = data . toString ( UTF8 ) ;
84- const splitAt = payload . indexOf ( MESSAGE_SEPARATOR ) ;
85- if ( splitAt !== - 1 ) {
86- const toClientId = slice ( payload , 0 , splitAt ) ;
87- const message = slice ( payload , splitAt + 1 ) ;
88- toClientId === EMPTY_STRING
89- ? mapForEach ( clients , ( otherClientId , otherWebSocket ) =>
90- otherClientId != clientId
91- ? otherWebSocket . send (
92- clientId + MESSAGE_SEPARATOR + message ,
93- )
94- : 0 ,
95- )
96- : mapGet ( clients , toClientId ) ?. send (
97- clientId + MESSAGE_SEPARATOR + message ,
98- ) ;
99- }
100- } ) ;
141+ const messageHandler = getMessageHandler ( clientId , pathId ) ;
142+ webSocket . on ( 'message' , ( data ) => messageHandler ( data . toString ( UTF8 ) ) ) ;
101143
102144 webSocket . on ( 'close' , ( ) => {
103145 collDel ( clients , clientId ) ;
104146 callListeners ( clientIdListeners , [ pathId ] , clientId , - 1 ) ;
105147 if ( collIsEmpty ( clients ) ) {
106148 collDel ( clientsByPath , pathId ) ;
107- finishPath ( pathId ) ;
149+ stopServerClient ( pathId ) ;
150+ callListeners ( pathIdListeners , undefined , pathId , - 1 ) ;
108151 }
109152 } ) ;
110153 } ) ,
0 commit comments