1
1
import * as fs from 'fs-extra'
2
- import WS from 'ws'
3
2
import { isFunction } from 'lodash/fp'
4
3
5
4
import * as paths from '../config/paths'
6
- import { onSignal } from '../utils/on-signal'
7
-
8
- export type Send = ( type : string , payload : any ) => void
9
- export type On = ( type : string ) => Promise < any >
10
-
11
- const isSocketOpened = ( socket : WS ) => socket . readyState === WS . OPEN
12
- const sender = ( socket ?: WS ) => ( type : string , payload : any ) => {
13
- if ( socket && isSocketOpened ( socket ) ) {
14
- socket . send ( JSON . stringify ( { type, payload } ) )
15
- }
16
- }
17
-
18
- export interface Action {
19
- type : string
20
- payload : any
21
- }
22
5
23
6
export interface Params {
24
7
state : Record < string , any >
@@ -27,103 +10,74 @@ export interface Params {
27
10
28
11
export interface State {
29
12
id : string
30
- init : ( params : Params ) => Promise < any >
31
- update : ( params : Params ) => any
32
- close : ( params : Params ) => any
13
+ start : ( params : Params ) => Promise < void >
14
+ close : ( ) => void
15
+ }
16
+
17
+ export interface Action {
18
+ type : string
19
+ payload : any
33
20
}
34
21
22
+ export type Listener = ( action : Action ) => void
23
+
35
24
export class DataServer {
36
- private client ?: WS . Server
37
25
private states : Set < State >
38
- private state : Record < string , any >
26
+ private state : Map < string , any >
27
+ private listeners : Set < Listener >
39
28
40
- constructor ( server ?: any , port ?: number , host ?: string ) {
29
+ constructor ( ) {
41
30
this . states = new Set ( )
42
- this . state = { }
43
-
44
- if ( server ) {
45
- this . client = new WS . Server ( {
46
- server,
47
- port,
48
- host,
49
- } )
50
- }
31
+ this . state = new Map ( )
32
+ this . listeners = new Set < Listener > ( )
51
33
}
52
34
53
35
public register ( states : State [ ] ) : DataServer {
54
36
for ( const state of states ) this . states . add ( state )
55
37
return this
56
38
}
57
39
58
- public async init ( ) : Promise < void > {
40
+ public async start ( ) : Promise < void > {
41
+ const setState = ( key : string , val : any ) => this . setState ( key , val )
42
+
59
43
await Promise . all (
60
44
Array . from ( this . states ) . map ( async state => {
61
- if ( ! isFunction ( state . init ) ) return
62
- return state . init ( {
63
- state : { ... this . state } ,
64
- setState : this . setState ( ) ,
45
+ if ( ! isFunction ( state . start ) ) return
46
+ return state . start ( {
47
+ setState ,
48
+ state : this . mapToObject ( this . state ) ,
65
49
} )
66
50
} )
67
51
)
68
-
69
- this . updateStateFile ( )
70
52
}
71
53
72
- public async listen ( ) : Promise < void > {
73
- if ( this . client ) {
74
- this . client . on ( 'connection' , socket => {
75
- const close = this . handleConnection ( socket )
76
- const handleClose = async ( ) => {
77
- await close ( )
78
- socket . terminate ( )
79
- }
80
-
81
- this . client ! . on ( 'close' , handleClose )
82
- onSignal ( handleClose )
83
- } )
54
+ public close ( ) : void {
55
+ for ( const state of this . states ) {
56
+ isFunction ( state . close ) && state . close ( )
84
57
}
85
58
}
86
59
87
- public async close ( ) : Promise < void > {
88
- await Promise . all (
89
- Array . from ( this . states ) . map (
90
- async state =>
91
- isFunction ( state . close ) &&
92
- state . close ( {
93
- state : { ...this . state } ,
94
- setState : this . setState ( ) ,
95
- } )
96
- )
97
- )
60
+ public onStateChange ( listener : Listener ) : ( ) => void {
61
+ this . listeners . add ( listener )
62
+ return ( ) => this . listeners . clear ( )
98
63
}
99
64
100
- private handleConnection ( socket : WS ) : ( ) => void {
101
- const states = Array . from ( this . states ) . map (
102
- async state =>
103
- isFunction ( state . update ) &&
104
- state . update ( {
105
- state : this . state ,
106
- setState : this . setState ( socket ) ,
107
- } )
108
- )
109
-
110
- return async ( ) => {
111
- const fns = await Promise . all ( states . filter ( Boolean ) )
112
- for ( const fn of fns ) isFunction ( fn ) && fn ( )
113
- }
65
+ private setState ( key : string , val : any ) : void {
66
+ this . state . set ( key , val )
67
+ this . writeDbFile ( )
68
+ this . listeners . forEach ( listener => {
69
+ listener ( { type : `state.${ key } ` , payload : val } )
70
+ } )
114
71
}
115
72
116
- private setState ( socket ?: WS ) : ( key : string , val : any ) => void {
117
- const send = sender ( socket )
118
-
119
- return ( key : string , val : any ) : void => {
120
- this . state [ key ] = val
121
- send ( `state.${ key } ` , val )
122
- this . updateStateFile ( )
123
- }
73
+ private async writeDbFile ( ) : Promise < void > {
74
+ fs . outputJSONSync ( paths . db , this . mapToObject ( this . state ) , { spaces : 2 } )
124
75
}
125
76
126
- private async updateStateFile ( ) : Promise < void > {
127
- await fs . outputJSON ( paths . db , this . state , { spaces : 2 } )
77
+ private mapToObject < T > ( map : Map < string , any > ) : T {
78
+ return Array . from ( map . entries ( ) ) . reduce (
79
+ ( obj , [ key , val ] ) => ( { ...obj , [ key ] : val } ) ,
80
+ { } as T
81
+ )
128
82
}
129
83
}
0 commit comments