-
Notifications
You must be signed in to change notification settings - Fork 2
/
papi.d.ts
2798 lines (2797 loc) · 123 KB
/
papi.d.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// <reference types="node" />
declare module 'shared/global-this.model' {
import { FunctionComponent } from 'react';
/**
* Variables that are defined in global scope. These must be defined in main.ts (main), index.ts (renderer), and extension-host.ts (extension host)
*/
global {
/** Type of process this is. Helps with running specific code based on which process you're in */
var processType: ProcessType;
/** Whether this process is packaged or running from sources */
var isPackaged: boolean;
/** Path to the app's resources directory. This is a string representation of the resources uri on frontend */
var resourcesPath: string;
/**
* A function that each React WebView extension must provide for Paranext to display it.
* Only used in WebView iframes
*/
var webViewComponent: FunctionComponent;
}
/** Type of Paranext process */
export enum ProcessType {
Main = 'main',
Renderer = 'renderer',
ExtensionHost = 'extension-host',
}
}
declare module 'shared/utils/util' {
export function newGuid(): string;
/**
* Create a nonce that is at least 128 bits long and should be (is not currently) cryptographically random.
* See nonce spec at https://w3c.github.io/webappsec-csp/#security-nonces
*
* WARNING: THIS IS NOT CURRENTLY CRYPTOGRAPHICALLY SECURE!
* TODO: Make this cryptographically random! Use some polymorphic library that works in all contexts?
* https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues only works in browser
*/
export function newNonce(): string;
/**
* Determine whether the object is a string
* @param o object to determine if it is a string
* @returns true if the object is a string; false otherwise
*/
export function isString(o: unknown): o is string;
/**
* Get a function that reduces calls to the function passed in
* @param fn The function to debounce
* @param delay How much delay in milliseconds after the most recent call to the debounced function to call the function
* @returns function that, when called, only calls the function passed in at maximum every delay ms
*/
export function debounce<T extends (...args: any[]) => void>(fn: T, delay?: number): T;
/**
* Groups each item in the array of items into a map according to the keySelector
* @param items array of items to group by
* @param keySelector function to run on each item to get the key for the group to which it belongs
* @param valueSelector function to run on each item to get the value it should have in the group (like map function). If not provided, uses the item itself
* @returns map of keys to groups of values corresponding to each item
*/
export function groupBy<T, K>(items: T[], keySelector: (item: T) => K): Map<K, Array<T>>;
export function groupBy<T, K, V>(
items: T[],
keySelector: (item: T) => K,
valueSelector: (item: T, key: K) => V,
): Map<K, Array<V>>;
/**
* Function to get an error message from the object (useful for getting error message in a catch block)
* @param error error object whose message to get
* @returns message of the error - if object has message, returns message. Otherwise tries to stringify
* @example
* try {...}
* catch (e) { logger.info(getErrorMessage(e)) }
*/
export function getErrorMessage(error: unknown): string;
/**
* Asynchronously waits for the specified number of milliseconds.
* (wraps setTimeout in a promise)
*/
export function wait(ms: number): Promise<void>;
/**
* Runs the specified function and will timeout if it takes longer than the specified wait time
* @param fn The function to run
* @param maxWaitTimeInMS The maximum amount of time to wait for the function to resolve
* @returns Promise that resolves to the resolved value of the function or null if it
* ran longer than the specified wait time
*/
export function waitForDuration<TResult>(
fn: () => Promise<TResult>,
maxWaitTimeInMS: number,
): Promise<Awaited<TResult> | null>;
/**
* Get all functions on an object and its prototype (so we don't miss any class methods or any
* object methods).
*
* Note: this does return some potentially unexpected function names. For example:
* `getAllObjectFunctionNames({})` returns `[constructor,__defineGetter__,__defineSetter__,
* hasOwnProperty,__lookupGetter__,__lookupSetter__,isPrototypeOf,propertyIsEnumerable,toString,
* valueOf,toLocaleString]`
* @param obj object whose functions to get
* @returns array of all function names on an object
*/
export function getAllObjectFunctionNames(obj: NonNullable<any>): string[];
}
declare module 'shared/utils/papi-util' {
import { ProcessType } from 'shared/global-this.model';
/** Function to run to dispose of something. Returns true if successfully unsubscribed */
export type Unsubscriber = () => boolean;
/**
* Returns an Unsubscriber function that combines all the unsubscribers passed in.
* @param unsubscribers all unsubscribers to aggregate into one unsubscriber
* @returns function that unsubscribes from all passed in unsubscribers when run
*/
export const aggregateUnsubscribers: (unsubscribers: Unsubscriber[]) => Unsubscriber;
/** Function to run to dispose of something that runs asynchronously. The promise resolves to true if successfully unsubscribed */
export type UnsubscriberAsync = () => Promise<boolean>;
/**
* Returns an UnsubscriberAsync function that combines all the unsubscribers passed in.
* @param unsubscribers all unsubscribers to aggregate into one unsubscriber
* @returns function that unsubscribes from all passed in unsubscribers when run
*/
export const aggregateUnsubscriberAsyncs: (
unsubscribers: UnsubscriberAsync[],
) => UnsubscriberAsync;
/**
* Creates a safe version of a register function that returns a Promise<UnsubscriberAsync>.
* @param unsafeRegisterFn function that does some kind of async registration and returns an unsubscriber and a promise that resolves when the registration is finished
* @param isInitialized whether the service associated with this safe UnsubscriberAsync function is initialized
* @param initialize promise that resolves when the service is finished initializing
* @returns safe version of an unsafe function that returns a promise to an UnsubscriberAsync (meaning it will wait to register until the service is initialized)
*/
export const createSafeRegisterFn: <TParam extends unknown[]>(
unsafeRegisterFn: (...args: TParam) => Promise<UnsubscriberAsync>,
isInitialized: boolean,
initialize: () => Promise<void>,
) => (...args: TParam) => Promise<UnsubscriberAsync>;
/**
* Type of object passed to a complex request handler that provides information about the request.
* This type is used as the public-facing interface for requests
*/
export type ComplexRequest<TParam = unknown> = {
contents: TParam;
};
type ComplexResponseSuccess<TReturn = unknown> = {
/** Whether the handler that created this response was successful in handling the request */
success: true;
/** Content with which to respond to the request. Must be provided unless the response failed or TReturn is undefined */
contents: TReturn;
};
type ComplexResponseFailure = {
/** Whether the handler that created this response was successful in handling the request */
success: false;
/**
* Content with which to respond to the request. Must be provided unless the response failed or TReturn is undefined
* Removed from failure so we do not change the type of contents for type safety. We could add errorContents one day if we really need it
*/
/** Error explaining the problem that is only populated if success is false */
errorMessage: string;
};
/**
* Type of object to create when handling a complex request where you desire to provide additional information beyond the contents of the response
* This type is used as the public-facing interface for responses
*/
export type ComplexResponse<TReturn = unknown> =
| ComplexResponseSuccess<TReturn>
| ComplexResponseFailure;
/** Type of request handler - indicates what type of parameters and what return type the handler has */
export enum RequestHandlerType {
Args = 'args',
Contents = 'contents',
Complex = 'complex',
}
/**
* Handler function for a command. Called when a command is executed.
* The function should accept the command's parameters as its parameters.
* The function should return a promise that resolves with the "return" value of the command.
*/
export type CommandHandler<TParam extends Array<unknown> = any[], TReturn = any> = (
...args: TParam
) => Promise<TReturn> | TReturn;
/** Check that two objects are deeply equal, comparing members of each object and such */
export function deepEqual(a: unknown, b: unknown): boolean;
/** Information about a request that tells us what to do with it */
export type RequestType = {
/** the general category of request */
category: string;
/** specific identifier for this type of request */
directive: string;
};
/**
* Create a request message requestType string from a category and a directive
* @param category the general category of request
* @param directive specific identifier for this type of request
* @returns full requestType for use in network calls
*/
export function serializeRequestType(category: string, directive: string): string;
/** Split a request message requestType string into its parts */
export function deserializeRequestType(requestType: string): RequestType;
/**
* HTML Encodes the provided string.
* Thanks to ChatGPT
* @param str string to HTML encode
* @returns HTML-encoded string
*/
export const htmlEncode: (str: string) => string;
/**
* Modules that someone might try to require in their extensions that we have similar apis for.
* When an extension requires these modules, an error throws that lets them know about our similar api.
*/
export const MODULE_SIMILAR_APIS: Readonly<{
[moduleName: string]:
| string
| {
[process in ProcessType | 'default']?: string;
}
| undefined;
}>;
/**
* Get a message that says the module import was rejected and to try a similar api if available.
* @param moduleName name of `require`d module that was rejected
* @returns string that says the import was rejected and a similar api to try
*/
export function getModuleSimilarApiMessage(moduleName: string): string;
}
declare module 'shared/models/papi-event.model' {
import { Unsubscriber, UnsubscriberAsync } from 'shared/utils/papi-util';
/** Callback function that accepts an event and should run when an event is emitted */
export type PapiEventHandler<T> = (event: T) => void;
/**
* Function that subscribes the provided callback to run when this event is emitted.
* @param callback function to run with the event when it is emitted
* @returns unsubscriber function to run to stop calling the passed-in function when the event is emitted
*/
export type PapiEvent<T> = (callback: PapiEventHandler<T>) => Unsubscriber;
/**
* A PapiEvent that subscribes asynchronously and resolves an asynchronous unsubscriber.
*
* Note: The callback itself is not asynchronous.
*/
export type PapiEventAsync<T> = (callback: PapiEventHandler<T>) => Promise<UnsubscriberAsync>;
}
declare module 'shared/models/papi-event-emitter.model' {
/**
* Interfaces, classes, and functions related to events and event emitters
*/
import { PapiEvent } from 'shared/models/papi-event.model';
/**
* Event manager - accepts subscriptions to an event and runs the subscription callbacks when the event is emitted
* Use eventEmitter.event(callback) to subscribe to the event.
* Use eventEmitter.emit(event) to run the subscriptions.
* Generally, this EventEmitter should be private, and its event should be public. That way, the emitter is not publicized,
* but anyone can subscribe to the event.
*/
export default class PapiEventEmitter<T> {
/**
* Subscribes a function to run when this event is emitted.
* @alias event
* @param callback function to run with the event when it is emitted
* @returns unsubscriber function to run to stop calling the passed-in function when the event is emitted
*/
subscribe: PapiEvent<T>;
/** All callback functions that will run when this event is emitted. Lazy loaded */
private subscriptions?;
/** Event for listeners to subscribe to. Lazy loaded */
private lazyEvent?;
/** Whether this emitter has been disposed */
private isDisposed;
/**
* Event for listeners to subscribe to. Subscribes a function to run when this event is emitted.
* Use like `const unsubscriber = event(callback)`
* @param callback function to run with the event when it is emitted
* @returns unsubscriber function to run to stop calling the passed-in function when the event is emitted
*/
get event(): PapiEvent<T>;
/** Disposes of this event, preparing it to release from memory */
dispose: () => void;
/**
* Runs the subscriptions for the event
* @param event event data to provide to subscribed callbacks
*/
emit: (event: T) => void;
/**
* Function that runs the subscriptions for the event.
* Added here so children can override emit and still call the base functionality.
* See NetworkEventEmitter.emit for example
*/
protected emitFn(event: T): void;
/** Check to make sure this emitter is not disposed. Throw if it is */
protected assertNotDisposed(): void;
/**
* Disposes of this event, preparing it to release from memory.
* Added here so children can override emit and still call the base functionality.
*/
protected disposeFn(): void;
}
}
declare module 'shared/data/internal-connection.model' {
/**
* Types that are internal to the communication we do through WebSocket.
* These types should not need to be used outside of NetworkConnectors and ConnectionService.ts
*/
import { ComplexRequest, ComplexResponse } from 'shared/utils/papi-util';
/** Represents when the client id has not been assigned by the server */
export const CLIENT_ID_UNASSIGNED = -1;
/** "Client id" for the server */
export const CLIENT_ID_SERVER = 0;
/** Represents when the connector info has not been populated by the server */
export const CONNECTOR_INFO_DISCONNECTED: Readonly<{
clientId: -1;
}>;
/** Information about the network connector */
export type NetworkConnectorInfo = Readonly<{
clientId: number;
}>;
/** Event emitted when client connections are established */
export type ClientConnectEvent = {
clientId: number;
didReconnect: boolean;
};
/** Event emitted when client connections are lost */
export type ClientDisconnectEvent = {
clientId: number;
};
/**
* Functions that run when network connector events occur.
* These should likely be emit functions from NetworkEventEmitters so the events inform all interested connections
*/
export type NetworkConnectorEventHandlers = {
/** Handles when a new connection is established */
didClientConnectHandler?: (event: ClientConnectEvent) => void;
/** Handles when a client disconnects */
didClientDisconnectHandler?: (event: ClientDisconnectEvent) => void;
};
/** Whether this connector is setting up or has finished setting up its connection and is ready to communicate on the network */
export enum ConnectionStatus {
/** This connector is not connected to the network */
Disconnected = 0,
/** This connector is attempting to connect to the network and retrieve connectorInfo */
Connecting = 1,
/** This connector has finished setting up its connection - has connectorInfo and such */
Connected = 2,
}
/** Request to do something and to respond */
export type InternalRequest<TParam = unknown> = {
senderId: number;
requestId: number;
} & ComplexRequest<TParam>;
/** Response to a request */
export type InternalResponse<TReturn = unknown> = {
/** The process that sent this Response */
senderId: number;
requestId: number;
/** The process that originally sent the Request that matches to this response */
requesterId: number;
} & ComplexResponse<TReturn>;
/** Handler for requests from the server. Used internally between network connector and Connection Service */
export type InternalRequestHandler = <TParam, TReturn>(
requestType: string,
request: InternalRequest<TParam>,
) => Promise<InternalResponse<TReturn>>;
/** Handler for requests from the server */
export type RequestHandler = <TParam, TReturn>(
requestType: string,
request: ComplexRequest<TParam>,
) => Promise<ComplexResponse<TReturn>>;
/** Function that returns a clientId to which to send the request based on the requestType */
export type RequestRouter = (requestType: string) => number;
/** Event to be sent out throughout all processes */
export type InternalEvent<T> = {
/** The process that emitted this Event */
senderId: number;
/** Contents of the event */
event: T;
};
/** Handler for events from on the network. Used internally between network connector and Connection Service */
export type InternalNetworkEventHandler = <T>(
eventType: string,
incomingEvent: InternalEvent<T>,
) => void;
/** Handler for events from on the network */
export type NetworkEventHandler = <T>(eventType: string, event: T) => void;
}
declare module 'shared/services/network-connector.interface' {
import {
ConnectionStatus,
InternalEvent,
InternalNetworkEventHandler,
InternalRequestHandler,
NetworkConnectorEventHandlers,
NetworkConnectorInfo,
RequestRouter,
} from 'shared/data/internal-connection.model';
/**
* Interface that defines the network connection functionality the server and the client must implement.
* Used by NetworkConnectorFactory to supply the right kind of NetworkConnector to ConnectionService
*/
export default interface INetworkConnector {
/** Information about the connector. Populated by the server while connecting */
connectorInfo: NetworkConnectorInfo;
/** Whether this connector is setting up or has finished setting up its connection and is ready to communicate on the network */
connectionStatus: ConnectionStatus;
/**
* Sets up the NetworkConnector by populating connector info, setting up event handlers, and doing one of the following:
* - On Client: connecting to the server.
* - On Server: opening an endpoint for clients to connect.
* MUST ALSO RUN notifyClientConnected() WHEN PROMISE RESOLVES
* @param localRequestHandler function that handles requests from the connection. Only called when this connector can handle the request
* @param requestRouter function that returns a clientId to which to send the request based on the requestType. If requestRouter returns this connector's clientId, localRequestHandler is used
* @param localEventHandler function that handles events from the server by accepting an eventType and an event and emitting the event locally
* @param networkConnectorEventHandlers functions that run when network connector events occur like when clients are disconnected
* @returns Promise that resolves with connector info when finished connecting
*/
connect: (
localRequestHandler: InternalRequestHandler,
requestRouter: RequestRouter,
localEventHandler: InternalNetworkEventHandler,
networkConnectorEventHandlers: NetworkConnectorEventHandlers,
) => Promise<NetworkConnectorInfo>;
/**
* Notify the server that this client has received its connectorInfo and is ready to go.
* MUST RUN AFTER connect() WHEN ITS PROMISE RESOLVES
* TODO: Is this necessary?
*/
notifyClientConnected: () => Promise<void>;
/**
* Disconnects from the connection:
* - On Client: disconnects from the server
* - On Server: disconnects from clients and closes its connection endpoint
*/
disconnect: () => void;
/**
* Send a request to the server/a client and resolve after receiving a response
* @param requestType the type of request
* @param contents contents to send in the request
* @returns promise that resolves with the response message
*/
request: InternalRequestHandler;
/**
* Sends an event to other processes. Does NOT run the local event subscriptions
* as they should be run by NetworkEventEmitter after sending on network.
* @param eventType unique network event type for coordinating between processes
* @param event event to emit on the network
*/
emitEventOnNetwork: <T>(eventType: string, event: InternalEvent<T>) => Promise<void>;
}
}
declare module 'shared/utils/internal-util' {
/**
* Utility functions specific to the internal technologies we are using.
*/
import { ProcessType } from 'shared/global-this.model';
/**
* Determine if running on a client process (renderer, extension-host) or on the server.
* @returns Returns true if running on a client, false otherwise
*/
export const isClient: () => boolean;
/**
* Determine if running on the server process (main)
* @returns Returns true if running on the server, false otherwise
*/
export const isServer: () => boolean;
/**
* Determine if running on the renderer process
* @returns Returns true if running on the renderer, false otherwise
*/
export const isRenderer: () => boolean;
/**
* Determine if running on the extension host
* @returns Returns true if running on the extension host, false otherwise
*/
export const isExtensionHost: () => boolean;
/**
* Gets which kind of process this is (main, renderer, extension-host)
* @returns ProcessType for this process
*/
export const getProcessType: () => ProcessType;
}
declare module 'shared/data/network-connector.model' {
/**
* Types that are relevant particularly to the implementation of communication on NetworkConnector.ts files
* Do not use these types outside of ClientNetworkConnector.ts and ServerNetworkConnector.ts
*/
import {
InternalEvent,
InternalRequest,
InternalResponse,
NetworkConnectorInfo,
} from 'shared/data/internal-connection.model';
/** Port to use for the webSocket */
export const WEBSOCKET_PORT = 8876;
/** Number of attempts a client will make to connect to the WebSocket server before failing */
export const WEBSOCKET_ATTEMPTS_MAX = 5;
/** Time in ms for the client to wait before attempting to connect to the WebSocket server again after a failure */
export const WEBSOCKET_ATTEMPTS_WAIT = 1000;
/** WebSocket message type that indicates how to handle it */
export enum MessageType {
InitClient = 'init-client',
ClientConnect = 'client-connect',
Request = 'request',
Response = 'response',
Event = 'event',
}
/** Message sent to the client to give it NetworkConnectorInfo */
export type InitClient = {
type: MessageType.InitClient;
senderId: number;
connectorInfo: NetworkConnectorInfo;
/** Guid unique to this connection. Used to verify important messages like reconnecting */
clientGuid: string;
};
/** Message responding to the server to let it know this connection is ready to receive messages */
export type ClientConnect = {
type: MessageType.ClientConnect;
senderId: number;
/** clientGuid for this client the last time it was connected to the server. Used when reconnecting (like if the browser refreshes):
* if the server has a connection with this clientGuid, it will unregister all requests on that client so the reconnecting client
* can register its request handlers again.
*/
reconnectingClientGuid?: string | null;
};
/** Request to do something and to respond */
export type WebSocketRequest<TParam = unknown> = {
type: MessageType.Request;
/** What kind of request this is. Certain command, etc */
requestType: string;
} & InternalRequest<TParam>;
/** Response to a request */
export type WebSocketResponse<TReturn = unknown> = {
type: MessageType.Response;
/** What kind of request this is. Certain command, etc */
requestType: string;
} & InternalResponse<TReturn>;
/** Event to be sent out throughout all processes */
export type WebSocketEvent<T> = {
type: MessageType.Event;
/** What kind of event this is */
eventType: string;
} & InternalEvent<T>;
/** Messages send by the WebSocket */
export type Message =
| InitClient
| ClientConnect
| WebSocketRequest
| WebSocketResponse
| WebSocketEvent<unknown>;
}
declare module 'shared/services/logger.service' {
import log from 'electron-log';
export const WARN_TAG = '<WARN>';
/**
* Format a string of a service message
* @param message message from the service
* @param serviceName name of the service to show in the log
* @param tag optional tag at the end of the service name
* @returns formatted string of a service message
*/
export function formatLog(message: string, serviceName: string, tag?: string): string;
const logger: log.Logger & {
default: log.Logger;
};
export default logger;
}
declare module 'client/services/web-socket.interface' {
/**
* Interface that defines the webSocket functionality the extension host and the renderer must implement.
* Used by WebSocketFactory to supply the right kind of WebSocket to ClientNetworkConnector.
* For now, we are just using the browser WebSocket type. We may need specific functionality that don't
* line up between the ws library's implementation and the browser implementation. We can adjust as needed at that point.
*/
export type IWebSocket = WebSocket;
}
declare module 'renderer/services/renderer-web-socket.model' {
/**
* The renderer's implementation of WebSocket is the browser-supplied WebSocket, which doesn't work in Node
*/
export default WebSocket;
}
declare module 'extension-host/services/extension-host-web-socket.model' {
import ws from 'ws';
/**
* extension-host client uses ws as its WebSocket client, but the renderer can't use it. So we need to exclude it from the renderer webpack bundle like this.
*/
export default ws;
}
declare module 'client/services/web-socket.factory' {
import { IWebSocket } from 'client/services/web-socket.interface';
/**
* Creates a WebSocket for the renderer or extension host depending on where you're running
* @returns WebSocket
*/
export const createWebSocket: (url: string) => Promise<IWebSocket>;
}
declare module 'client/services/client-network-connector.service' {
import {
ConnectionStatus,
InternalEvent,
InternalNetworkEventHandler,
InternalRequest,
InternalRequestHandler,
InternalResponse,
NetworkConnectorInfo,
RequestRouter,
} from 'shared/data/internal-connection.model';
import INetworkConnector from 'shared/services/network-connector.interface';
/**
* Handles the connection from the client to the server
*/
export default class ClientNetworkConnector implements INetworkConnector {
connectorInfo: NetworkConnectorInfo;
connectionStatus: ConnectionStatus;
/** The webSocket connected to the server */
private webSocket?;
/** All message subscriptions - emitters that emit an event each time a message with a specific message type comes in */
private messageEmitters;
/** Promise that resolves when the connection is finished or rejects if disconnected before the connection finishes */
private connectPromise?;
/** Function that removes this initClient handler from the connection */
private unsubscribeHandleInitClientMessage?;
/** Function that removes this response handler from the connection */
private unsubscribeHandleResponseMessage?;
/** Function that removes this handleRequest from the connection */
private unsubscribeHandleRequestMessage?;
/** Function that removes this handleEvent from the connection */
private unsubscribeHandleEventMessage?;
/**
* Function to call when we receive a request that is registered on this connector.
* Handles requests from the connection and returns a response to send back
*/
private localRequestHandler?;
/**
* Function to call when we are sending a request.
* Returns a clientId to which to send the request based on the requestType
*/
private requestRouter?;
/**
* Function to call when we receive an event.
* Handles events from the connection by emitting the event locally
*/
private localEventHandler?;
/** All requests that are waiting for a response */
private requests;
/** Unique Guid associated with this connection. Used to verify certain things with server */
private clientGuid;
connect: (
localRequestHandler: InternalRequestHandler,
requestRouter: RequestRouter,
localEventHandler: InternalNetworkEventHandler,
) => Promise<
Readonly<{
clientId: number;
}>
>;
notifyClientConnected: () => Promise<void>;
disconnect: () => void;
request: <TParam, TReturn>(
requestType: string,
request: InternalRequest<TParam>,
) => Promise<InternalResponse<TReturn>>;
emitEventOnNetwork: <T>(eventType: string, event: InternalEvent<T>) => Promise<void>;
/**
* Send a message to the server via webSocket. Throws if not connected
* @param message message to send
*/
private sendMessage;
/**
* Receives and appropriately publishes server webSocket messages
* @param event webSocket message information
* @param fromSelf whether this message is from this connector instead of from someone else
*/
private onMessage;
/**
* Subscribes a function to run on webSocket messages of a particular type
* @param messageType the type of message on which to subscribe the function
* @param callback function to run with the contents of the webSocket message
* @returns unsubscriber function to run to stop calling the passed-in function on webSocket messages
*/
private subscribe;
/**
* Function that handles webSocket messages of type Response.
* Resolves the request associated with the received response message
* @param response Response message to resolve
*/
private handleResponseMessage;
/**
* Function that handles incoming webSocket messages and locally sent messages of type Request.
* Runs the requestHandler provided in connect() and sends a message with the response
* @param requestMessage request message to handle
* @param isIncoming whether this message is coming from the server and we should definitely handle it locally
* or if it is a locally sent request and we should send to the server if we don't have a local handler
*/
private handleRequestMessage;
/**
* Function that handles incoming webSocket messages of type Event.
* Runs the eventHandler provided in connect()
* @param eventMessage event message to handle
*/
private handleEventMessage;
}
}
declare module 'main/services/server-network-connector.service' {
import {
ConnectionStatus,
InternalEvent,
InternalNetworkEventHandler,
InternalRequest,
InternalRequestHandler,
InternalResponse,
NetworkConnectorEventHandlers,
NetworkConnectorInfo,
RequestRouter,
} from 'shared/data/internal-connection.model';
import INetworkConnector from 'shared/services/network-connector.interface';
/**
* Handles the endpoint and connections from the server to the clients
*/
export default class ServerNetworkConnector implements INetworkConnector {
connectorInfo: NetworkConnectorInfo;
connectionStatus: ConnectionStatus;
/** The webSocket connected to the server */
private webSocketServer?;
/** The next client id to use for a new connection. Starts at 1 because the server is 0 */
private nextClientId;
/** The webSocket clients that are connected and information about them */
private clientSockets;
/** All message subscriptions - emitters that emit an event each time a message with a specific message type comes in */
private messageEmitters;
/** Promise that resolves when finished starting the server or rejects if disconnected before the server finishes */
private connectPromise?;
/** Function that removes this clientConnect handler from connections */
private unsubscribeHandleClientConnectMessage?;
/** Function that removes this response handler from connections */
private unsubscribeHandleResponseMessage?;
/** Function that removes this handleRequest from connections */
private unsubscribeHandleRequestMessage?;
/** Function that removes this handleEvent from the connection */
private unsubscribeHandleEventMessage?;
/**
* Function to call when we receive a request that is registered on this connector.
* Handles requests from connections and returns a response to send back
*/
private localRequestHandler?;
/**
* Function to call when we are sending a request.
* Returns a clientId to which to send the request based on the requestType
*/
private requestRouter?;
/**
* Function to call when we receive an event.
* Handles events from connections and emits the event locally
*/
private localEventHandler?;
/**
* Functions to run when network connector events occur like when clients are disconnected
*/
private networkConnectorEventHandlers?;
/** All requests that are waiting for a response */
private requests;
connect: (
localRequestHandler: InternalRequestHandler,
requestRouter: RequestRouter,
localEventHandler: InternalNetworkEventHandler,
networkConnectorEventHandlers: NetworkConnectorEventHandlers,
) => Promise<
Readonly<{
clientId: number;
}>
>;
notifyClientConnected: () => Promise<void>;
disconnect: () => void;
request: <TParam, TReturn>(
requestType: string,
request: InternalRequest<TParam>,
) => Promise<InternalResponse<TReturn>>;
emitEventOnNetwork: <T>(eventType: string, event: InternalEvent<T>) => Promise<void>;
/** Get the client socket for a certain clientId. Throws if not found */
private getClientSocket;
/**
* Attempts to get the client socket for a certain clientGuid. Returns undefined if not found.
* This does not throw because it will likely be very common that we do not have a clientId for a certain clientGuid
* as connecting clients will often supply old clientGuids.
*/
private getClientSocketFromGuid;
/** Get the clientId for a certain webSocket. Throws if not found */
private getClientIdFromSocket;
/**
* Send a message to a client via webSocket. Throws if not connected
* @param message message to send
* @param recipientId the client to which to send the message. TODO: determine if we can intuit this instead
*/
private sendMessage;
/**
* Receives and appropriately publishes webSocket messages
* @param event webSocket message information
* @param fromSelf whether this message is from this connector instead of from someone else
*/
private onMessage;
/**
* Subscribes a function to run on webSocket messages of a particular type
* @param messageType the type of message on which to subscribe the function
* @param callback function to run with the contents of the webSocket message
* @returns unsubscriber function to run to stop calling the passed-in function on webSocket messages
*/
private subscribe;
/**
* Registers an incoming webSocket connection and sends connection info with InitClient.
* Does not consider the client fully connected yet until they respond and tell us they connected with ClientConnect
*/
private onClientConnect;
/** Handles when client connection disconnects. Unregisters and such */
private onClientDisconnect;
/** Closes connection and unregisters a client webSocket when it has disconnected */
private disconnectClient;
/**
* Function that handles webSocket messages of type ClientConnect.
* Mark the connection fully connected and notify that a client connected or reconnected
* @param clientConnect message from the client about the connection
* @param connectorId clientId of the client who is sending this ClientConnect message
*/
private handleClientConnectMessage;
/**
* Function that handles webSocket messages of type Response.
* Resolves the request associated with the received response message or forwards to appropriate client
* @param response Response message to resolve
* @param responderId responding client
*/
private handleResponseMessage;
/**
* Function that handles incoming webSocket messages and locally sent messages of type Request.
* Handles the request and sends a response if we have a handler or forwards to the appropriate client
* @param requestMessage request to handle
* @param requesterId who sent this message
*/
private handleRequestMessage;
/**
* Function that handles incoming webSocket messages of type Event.
* Runs the eventHandler provided in connect() and forwards the event to other clients
* @param eventMessage event message to handle
*/
private handleEventMessage;
}
}
declare module 'shared/services/network-connector.factory' {
import INetworkConnector from 'shared/services/network-connector.interface';
/**
* Creates a NetworkConnector for the client or the server depending on where you're running
* @returns NetworkConnector
*/
export const createNetworkConnector: () => Promise<INetworkConnector>;
}
declare module 'shared/services/connection.service' {
/**
* Handles setting up a connection to the electron backend and exchanging simple messages.
* Do not use outside NetworkService.ts. For communication, use NetworkService.ts as it is an abstraction over this.
*/
import {
NetworkConnectorEventHandlers,
NetworkEventHandler,
RequestHandler,
RequestRouter,
} from 'shared/data/internal-connection.model';
import { ComplexResponse } from 'shared/utils/papi-util';
/**
* Send a request to the server and resolve after receiving a response
* @param requestType the type of request
* @param contents contents to send in the request
* @returns promise that resolves with the response message
*/
export const request: <TParam, TReturn>(
requestType: string,
contents: TParam,
) => Promise<ComplexResponse<TReturn>>;
/**
* Sends an event to other processes. Does NOT run the local event subscriptions
* as they should be run by NetworkEventEmitter after sending on network.
* @param eventType unique network event type for coordinating between processes
* @param event event to emit on the network
*/
export const emitEventOnNetwork: <T>(eventType: string, event: T) => Promise<void>;
/** Disconnects from the server */
export const disconnect: () => void;
/**
* Sets up the ConnectionService by connecting to the server and setting up event handlers
* @param localRequestHandler function that handles requests from the server by accepting a requestType and a ComplexRequest and returning a Promise of a Complex Response
* @param networkRequestRouter function that determines the appropriate clientId to which to send requests of the given type
* @param localEventHandler function that handles events from the server by accepting an eventType and an event and emitting the event locally
* @param connectorEventHandlers functions that run when network connector events occur like when clients are disconnected
* @returns Promise that resolves when finished connecting
*/
export const connect: (
localRequestHandler: RequestHandler,
networkRequestRouter: RequestRouter,
localEventHandler: NetworkEventHandler,
connectorEventHandlers: NetworkConnectorEventHandlers,
) => Promise<void>;
/** Gets this connection's clientId */
export const getClientId: () => number;
}
declare module 'shared/models/papi-network-event-emitter.model' {
import { PapiEventHandler } from 'shared/models/papi-event.model';
import PapiEventEmitter from 'shared/models/papi-event-emitter.model';
/**
* Networked version of EventEmitter - accepts subscriptions to an event and runs the subscription callbacks when the event is emitted.
* Events on NetworkEventEmitters can be emitted across processes. They are coordinated between processes by their type.
* Use eventEmitter.event(callback) to subscribe to the event.
* Use eventEmitter.emit(event) to run the subscriptions.
* Generally, this EventEmitter should be private, and its event should be public. That way, the emitter is not publicized,
* but anyone can subscribe to the event.
*
* WARNING: Do not use this class directly outside of NetworkService, or it will not do what you expect. Use NetworkService.createNetworkEventEmitter.
*
* WARNING: You cannot emit events with complex types on the network.
*/
export default class PapiNetworkEventEmitter<T> extends PapiEventEmitter<T> {
/** Callback that sends the event to other processes on the network when it is emitted */
private networkSubscriber;
/** Callback that runs when the emitter is disposed - should handle unlinking from the network */
private networkDisposer;
/**
* Creates a NetworkEventEmitter
* @param networkSubscriber callback that accepts the event and emits it to other processes
* @param networkDisposer callback that unlinks this emitter from the network
*/
constructor(
/** Callback that sends the event to other processes on the network when it is emitted */
networkSubscriber: PapiEventHandler<T>,
/** Callback that runs when the emitter is disposed - should handle unlinking from the network */
networkDisposer: () => void,
);
emit: (event: T) => void;
/**
* Runs only the subscriptions for the event that are on this process. Does not send over network
* @param event event data to provide to subscribed callbacks
*/
emitLocal(event: T): void;
dispose: () => void;
}
}
declare module 'shared/services/network.service' {
/**
* Handles requests, responses, subscriptions, etc. to the backend.
* Likely shouldn't need/want to expose this whole service on papi,
* but there are a few things that are exposed via papiNetworkService
*/
import { ClientConnectEvent, ClientDisconnectEvent } from 'shared/data/internal-connection.model';
import {
CommandHandler,
ComplexRequest,
ComplexResponse,
RequestHandlerType,
UnsubscriberAsync,
} from 'shared/utils/papi-util';
import PapiEventEmitter from 'shared/models/papi-event-emitter.model';
import { PapiEvent } from 'shared/models/papi-event.model';
/**
* Args handler function for a request. Called when a request is handled.
* The function should accept the spread of the contents array of the request as its parameters.
* The function should return an object that becomes the contents object of the response.
* This type of handler is a normal function.
*/
type ArgsRequestHandler<TParam extends Array<unknown> = any[], TReturn = any> = CommandHandler<
TParam,
TReturn
>;
/**
* Contents handler function for a request. Called when a request is handled.
* The function should accept the contents object of the request as its single parameter.
* The function should return an object that becomes the contents object of the response.
*/
type ContentsRequestHandler<TParam = any, TReturn = any> = (contents: TParam) => Promise<TReturn>;
/**
* Complex handler function for a request. Called when a request is handled.
* The function should accept a ComplexRequest object as its single parameter.
* The function should return a ComplexResponse object that becomes the response..
* This type of handler is the most flexible of the request handlers.
*/
type ComplexRequestHandler<TParam = any, TReturn = any> = (
request: ComplexRequest<TParam>,
) => Promise<ComplexResponse<TReturn>>;
/** Event that emits with clientId when a client connects */
export const onDidClientConnect: PapiEvent<ClientConnectEvent>;
/** Event that emits with clientId when a client disconnects */
export const onDidClientDisconnect: PapiEvent<ClientDisconnectEvent>;
/** Closes the network services gracefully */
export const shutdown: () => void;
/** Sets up the NetworkService. Runs only once */
export const initialize: () => Promise<void>;
/**
* Send a request on the network and resolve the response contents.
* @param requestType the type of request
* @param args arguments to send in the request (put in request.contents)
* @returns promise that resolves with the response message
*/
export const request: <TParam extends unknown[], TReturn>(
requestType: string,
...args: TParam
) => Promise<TReturn>;
/**
* Register a local request handler to run on requests.
* @param requestType the type of request on which to register the handler
* @param handler function to register to run on requests
* @param handlerType type of handler function - indicates what type of parameters and what return type the handler has
* @returns promise that resolves if the request successfully registered and unsubscriber function to run to stop the passed-in function from handling requests
*/