11import { ChildProcess } from 'child_process'
22import initChild from './init-child.js'
3- import IPOSMessaging from './messaging.js'
4- import { deserialize , serialize } from './serialize.js'
3+ import IPOSMessaging , { iposMessagingMessage , iposMessagingType } from './messaging.js'
54import intercept from './intercept.js'
65
76export default class IPOS {
@@ -28,6 +27,10 @@ export default class IPOS {
2827 this . fieldsReverseMap = new Map ( )
2928 this . processMessagingMap = new Map ( )
3029
30+ if ( process . send ) {
31+ this . messaging = new IPOSMessaging ( process )
32+ }
33+
3134 // proxy makes all "target.fields" available as "actual" fields
3235 this . proxy = new Proxy ( this , {
3336 get ( target , name : string ) {
@@ -36,38 +39,90 @@ export default class IPOS {
3639 } else if ( target . fields . has ( name ) ) {
3740 return target . fields . get ( name )
3841 }
39- }
42+ } ,
43+ set ( target , name : string , value : any ) : boolean {
44+ if ( Reflect . has ( target , name ) ) {
45+ throw Error ( `Cannot change inherent property \`${ name } \`` )
46+ } else if ( ! target . fields . has ( name ) ) {
47+ throw Error ( `Cannot set unknown field \`${ name } \`. Initialise a field with \`.create()\`` )
48+ } else {
49+ target . create ( name , value )
50+ return true
51+ }
52+ } ,
4053 } )
4154 return this . proxy
4255 }
4356
57+ protected mountListeners ( messaging : IPOSMessaging ) {
58+ messaging . listenForType ( 'update' , ( message ) => this . performUpdate ( message ) )
59+ messaging . listenForType ( 'set' , ( message ) => this . performSet ( message ) )
60+ messaging . listenForType ( 'delete' , ( message ) => this . performDelete ( message ) )
61+ }
62+
63+ protected sendToAll ( type : iposMessagingType , data ?: { } ) {
64+ this . messaging ?. send ( type , data )
65+ this . processMessagingMap . forEach ( processMessaging => {
66+ processMessaging . send ( type , data )
67+ } )
68+ }
69+
70+ /********************* GET **********************/
4471 public get ( key : string ) : any {
4572 return this . fields . get ( key )
4673 }
4774
48- // todo: also accept and update non-object values
49- public create ( key : string , value : object ) : void {
75+ /******************** CREATE ********************/
76+ public create ( key : string , value : any ) : void {
5077 this . createStealthy ( key , value )
51- // todo: send update message
78+ this . sendToAll ( 'set' , { key , value } )
5279 }
5380
5481 protected createStealthy ( key : string , value : object ) : void {
55- // console.log('create', key)
5682 if ( typeof value === 'object' )
5783 value = intercept ( value , ( object , method , ...args ) =>
5884 this . sendMethodCall ( object , method , ...args )
5985 )
6086
6187 this . fields . set ( key , value )
6288 this . fieldsReverseMap . set ( value , key )
63- // todo: send update message
6489 }
6590
91+ protected performSet ( message : iposMessagingMessage ) {
92+ if ( ! message . key || ! message . value ) return
93+ this . createStealthy ( message . key , message . value )
94+ }
95+
96+ /******************** UPDATE ********************/
97+ protected performUpdate ( message : iposMessagingMessage ) {
98+ if ( ! message . do || ! message . on ) return
99+ this . get ( message . on ) [ message . do ] ( ...( message . with ?? [ ] ) )
100+ }
101+
102+ private sendMethodCall ( object : object , method : string , ...args : any ) {
103+ this . sendToAll ( 'update' , {
104+ do : method ,
105+ on : this . fieldsReverseMap . get ( object ) ,
106+ with : Array . from ( args )
107+ } )
108+ }
109+
110+ /******************** DELETE ********************/
66111 public delete ( key : string ) : boolean {
112+ this . sendToAll ( 'delete' , { key} )
113+ return this . deleteStealthy ( key )
114+ }
115+
116+ public deleteStealthy ( key : string ) : boolean {
67117 return this . fields . delete ( key )
68- // todo: send update message
69118 }
70119
120+ public performDelete ( message : iposMessagingMessage ) {
121+ if ( ! message . key ) return
122+ return this . deleteStealthy ( message . key )
123+ }
124+
125+ /******************* PROCESS ********************/
71126 public addProcess ( process : ChildProcess ) : Promise < void > {
72127 if ( ! process . send )
73128 throw new Error ( `Process must have an ipc channel. Activate by passing "stdio: [<stdin>, <stdout>, <stderr>, 'ipc']" as an option.` )
@@ -79,37 +134,23 @@ export default class IPOS {
79134 if ( registered ) return
80135 registered = true
81136
137+ this . mountListeners ( messaging )
82138 this . processMessagingMap . set ( process , messaging )
83139 this . syncProcess ( process )
84- resolve ( )
140+ . then ( ( ) =>
141+ resolve ( )
142+ )
85143 } )
86144 return promise
87145 }
88146
89- private syncProcess ( process : ChildProcess ) {
90- this . processMessagingMap . get ( process ) ?. send ( 'sync' , {
91- fields : JSON . stringify ( IPOS . serialize ( this . fields ) )
92- } )
93- }
94-
95- private sendMethodCall ( object : object , method : string , ...args : any ) {
96- this . processMessagingMap . forEach ( processMessaging => {
97- processMessaging . send ( 'update' , {
98- do : method ,
99- on : this . fieldsReverseMap . get ( object ) ,
100- with : serialize ( args )
101- } )
147+ private syncProcess ( process : ChildProcess ) : Promise < void > {
148+ let resolve : Function
149+ const promise : Promise < void > = new Promise ( res => resolve = res )
150+ this . processMessagingMap . get ( process ) ?. send ( 'sync' , { fields : this . fields } )
151+ this . processMessagingMap . get ( process ) ?. listenOnceForType ( 'sync_ok' , ( ) => {
152+ resolve ( )
102153 } )
103- }
104-
105- /**
106- * Serializes types that "JSON.stringify()" doesn't properly handle
107- */
108- public static serialize ( value : any ) : any | void {
109- return serialize ( value )
110- }
111-
112- public static deserialize ( value : string | number | Array < any > | { $$iposType ?: string , data : any } ) : any | void {
113- return deserialize ( value )
154+ return promise
114155 }
115156}
0 commit comments