Implement websocket server

This commit is contained in:
2022-07-14 01:15:16 -05:00
parent dc663ec8f5
commit 33a64b99ff
15 changed files with 546 additions and 27 deletions

View File

@@ -1,7 +1,12 @@
/**
* Base type for an API response format.
*/
import {BaseSerializer, Event, ObjectSerializer} from '../../support/bus'
import {Awaitable, ErrorWithContext, hasOwnProperty, JSONState, uuid4} from '../../util'
export interface APIResponse<T> {
eventName?: string,
eventUuid?: string,
success: boolean,
message?: string,
data?: T,
@@ -12,6 +17,61 @@ export interface APIResponse<T> {
}
}
export function isAPIResponse(what: unknown): what is APIResponse<unknown> {
return typeof what === 'object' && what !== null
&& hasOwnProperty(what, 'success')
&& typeof what.success === 'boolean'
&& (!hasOwnProperty(what, 'message') || typeof what.message === 'string')
&& (!hasOwnProperty(what, 'error') || (
typeof what.error === 'object' && what.error !== null
&& hasOwnProperty(what.error, 'name') && typeof what.error.name === 'string'
&& hasOwnProperty(what.error, 'message') && typeof what.error.message === 'string'
&& (!hasOwnProperty(what.error, 'stack') || (
Array.isArray(what.error.stack) && what.error.stack.every(x => typeof x === 'string')
)
)
)
)
}
export function apiEvent<T>(response: APIResponse<T>): APIResponse<T> & Event {
if ( !response.eventName ) {
response.eventName = '@extollo/lib:APIResponse'
}
if ( !response.eventUuid ) {
response.eventUuid = uuid4()
}
return response as APIResponse<T> & Event
}
/**
* Serializer implementation that can encode/decode APIResponse objects.
*/
@ObjectSerializer()
export class APIResponseSerializer extends BaseSerializer<APIResponse<unknown>, JSONState> {
protected decodeSerial(serial: JSONState): Awaitable<APIResponse<unknown>> {
if ( isAPIResponse(serial) ) {
return serial
}
throw new ErrorWithContext('Could not decode API response: object is malformed')
}
protected encodeActual(actual: APIResponse<unknown>): Awaitable<JSONState> {
return actual as unknown as JSONState
}
protected getName(): string {
return '@extollo/lib:APIResponseSerializer'
}
matchActual(some: APIResponse<unknown>): boolean {
return isAPIResponse(some)
}
}
/**
* Formats a mesage as a successful API response.
* @param {string} displayMessage
@@ -56,7 +116,7 @@ export function many<T>(records: T[]): APIResponse<{records: T[], total: number}
* @return APIResponse
* @param thrownError
*/
export function error(thrownError: string | Error): APIResponse<void> {
export function error(thrownError: string | Error): APIResponse<undefined> {
if ( typeof thrownError === 'string' ) {
return {
success: false,