diff --git a/Common/.eslintignore b/Common/.eslintignore index 0e375e28..7f095c7a 100644 --- a/Common/.eslintignore +++ b/Common/.eslintignore @@ -1,7 +1,8 @@ -dist +build node_modules types package-lock.json package.json tsconfig.json -.eslintrc.js \ No newline at end of file +.eslintrc.js +src/Messages/signalling_messages.ts diff --git a/Common/.eslintrc.js b/Common/.eslintrc.js index 55691de9..4fd94217 100644 --- a/Common/.eslintrc.js +++ b/Common/.eslintrc.js @@ -3,6 +3,17 @@ module.exports = { root: true, parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'] + plugins: ['@typescript-eslint', 'eslint-plugin-tsdoc'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + rules: { + "tsdoc/syntax": "warn", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ] + } }; diff --git a/Common/README.md b/Common/README.md index 2bb9f781..05c530b0 100644 --- a/Common/README.md +++ b/Common/README.md @@ -1,21 +1,11 @@ -## lib-pixelstreamingcommon +# lib-pixelstreamingcommon The common library for the browser/client side of Pixel Streaming experiences. This library exposes common functionality for frontend applications of the pixel streaming ecosystem. -See [lib-pixelstreamingfrontend](/Frontend/library) for an example of how to implement this library. +For examples of how to implement this library. +- See [lib-pixelstreamingfrontend](/Frontend/library) +- See [lib-pixelstreamingsignalling](/Signalling) +- Also see [Wilbur](/SignallingWebServer) which uses the signalling library to implement a full signalling server. -Currently exposed. -### Logger -A small helper class for handling logging across the pixel streaming infrastructure. - -### ITransport -An interface to a transport protocol that is in charge of sending and receiving signalling messages. Users can make use of supplied transport protocols or implement their own transport protocol via extending this interface. They can then pass the transport into the constructor of `SignallingProtocol` which is explained below. - -### WebSocketTransport -An `ITransport` implementation that sends signalling messages over a websocket. This is currently the only transport in use by the official pixel streaming frontend. - -### SignallingProtocol -This is the object where the user should send/receive messages. Currently there are specific functions for specific messages sent, but in the future this should change to be more generic. You can specify your own transport protocol by passing an implementation of `ITransport` to this class, allowing you to send and receive messages through any protocol you wish. - -### Adding it to your project -`npm i @epicgames-ps/lib-pixelstreamingcommon-ue5.5` +## Documentation +The API is documented [here](docs). diff --git a/Common/docs/.nojekyll b/Common/docs/.nojekyll new file mode 100644 index 00000000..e2ac6616 --- /dev/null +++ b/Common/docs/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/Common/docs/README.md b/Common/docs/README.md new file mode 100644 index 00000000..7c0b2bca --- /dev/null +++ b/Common/docs/README.md @@ -0,0 +1,17 @@ +@epicgames-ps/lib-pixelstreamingcommon-ue5.5 + +# @epicgames-ps/lib-pixelstreamingcommon-ue5.5 + +## Table of contents + +### Modules + +- [Logger/Logger](modules/Logger_Logger.md) +- [Messages/base\_message](modules/Messages_base_message.md) +- [Messages/message\_helpers](modules/Messages_message_helpers.md) +- [Messages/message\_registry](modules/Messages_message_registry.md) +- [Protocol/SignallingProtocol](modules/Protocol_SignallingProtocol.md) +- [Transport/ITransport](modules/Transport_ITransport.md) +- [Transport/WebSocketTransport](modules/Transport_WebSocketTransport.md) +- [Transport/WebSocketTransportNJS](modules/Transport_WebSocketTransportNJS.md) +- [pixelstreamingcommon](modules/pixelstreamingcommon.md) diff --git a/Common/docs/classes/Logger_Logger.Logger.md b/Common/docs/classes/Logger_Logger.Logger.md new file mode 100644 index 00000000..8e4ec2f2 --- /dev/null +++ b/Common/docs/classes/Logger_Logger.Logger.md @@ -0,0 +1,206 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Logger/Logger](../modules/Logger_Logger.md) / Logger + +# Class: Logger + +[Logger/Logger](../modules/Logger_Logger.md).Logger + +A basic console logger utilized by the Pixel Streaming frontend to allow +logging to the browser console. + +## Table of contents + +### Constructors + +- [constructor](Logger_Logger.Logger.md#constructor) + +### Properties + +- [verboseLogLevel](Logger_Logger.Logger.md#verboseloglevel) + +### Methods + +- [CommonLog](Logger_Logger.Logger.md#commonlog) +- [Error](Logger_Logger.Logger.md#error) +- [GetStackTrace](Logger_Logger.Logger.md#getstacktrace) +- [Info](Logger_Logger.Logger.md#info) +- [Log](Logger_Logger.Logger.md#log) +- [SetLoggerVerbosity](Logger_Logger.Logger.md#setloggerverbosity) +- [Warning](Logger_Logger.Logger.md#warning) + +## Constructors + +### constructor + +• **new Logger**(): [`Logger`](Logger_Logger.Logger.md) + +#### Returns + +[`Logger`](Logger_Logger.Logger.md) + +## Properties + +### verboseLogLevel + +▪ `Static` **verboseLogLevel**: `number` = `5` + +#### Defined in + +[Logger/Logger.ts:8](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L8) + +## Methods + +### CommonLog + +▸ **CommonLog**(`level`, `stack`, `message`): `void` + +The common log function that all other log functions call to. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `level` | `string` | the level of this log message. | +| `stack` | `string` | an optional stack trace string from where the log message was called. | +| `message` | `string` | the message to be logged. | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:87](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L87) + +___ + +### Error + +▸ **Error**(`stack`, `message`): `void` + +The standard logging output + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `stack` | `string` | the stack trace | +| `message` | `string` | the message to be logged | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:68](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L68) + +___ + +### GetStackTrace + +▸ **GetStackTrace**(): `string` + +Captures the stack and returns it + +#### Returns + +`string` + +the current stack + +#### Defined in + +[Logger/Logger.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L14) + +___ + +### Info + +▸ **Info**(`stack`, `message`, `verbosity?`): `void` + +The standard logging output + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `stack` | `string` | the stack trace | +| `message` | `string` | the message to be logged | +| `verbosity?` | `number` | the verbosity level | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:55](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L55) + +___ + +### Log + +▸ **Log**(`stack`, `message`, `verbosity?`): `void` + +The standard logging output + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `stack` | `string` | the stack trace | +| `message` | `string` | the message to be logged | +| `verbosity?` | `number` | the verbosity level | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:41](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L41) + +___ + +### SetLoggerVerbosity + +▸ **SetLoggerVerbosity**(`verboseLogLevel`): `void` + +Set the log verbosity level + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `verboseLogLevel` | `number` | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L29) + +___ + +### Warning + +▸ **Warning**(`stack`, `message`): `void` + +The standard logging output + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `stack` | `string` | the stack trace | +| `message` | `string` | the message to be logged | + +#### Returns + +`void` + +#### Defined in + +[Logger/Logger.ts:77](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Logger/Logger.ts#L77) diff --git a/Common/docs/classes/Protocol_SignallingProtocol.SignallingProtocol.md b/Common/docs/classes/Protocol_SignallingProtocol.SignallingProtocol.md new file mode 100644 index 00000000..78ccb405 --- /dev/null +++ b/Common/docs/classes/Protocol_SignallingProtocol.SignallingProtocol.md @@ -0,0 +1,187 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Protocol/SignallingProtocol](../modules/Protocol_SignallingProtocol.md) / SignallingProtocol + +# Class: SignallingProtocol + +[Protocol/SignallingProtocol](../modules/Protocol_SignallingProtocol.md).SignallingProtocol + +Signalling protocol for handling messages from the signalling server. + +Listen on this emitter for messages. Message type is the name of the event to listen for. +Example: + ``` + signallingProtocol.on('config', (message: Messages.config) => console.log(`Got a config message: ${message}`))); + ``` + +The transport in this class will also emit on message events. + +Events emitted on transport: + message: + Emitted any time a message is received by the transport. Listen on this if + you wish to capture all messages, rather than specific messages on + 'messageHandlers'. + + out: + Emitted when sending a message out on the transport. Similar to 'message' but + only for when messages are sent from this endpoint. Useful for debugging. + +## Hierarchy + +- `EventEmitter` + + ↳ **`SignallingProtocol`** + +## Table of contents + +### Constructors + +- [constructor](Protocol_SignallingProtocol.SignallingProtocol.md#constructor) + +### Properties + +- [transport](Protocol_SignallingProtocol.SignallingProtocol.md#transport) + +### Accessors + +- [SIGNALLING\_VERSION](Protocol_SignallingProtocol.SignallingProtocol.md#signalling_version) + +### Methods + +- [connect](Protocol_SignallingProtocol.SignallingProtocol.md#connect) +- [disconnect](Protocol_SignallingProtocol.SignallingProtocol.md#disconnect) +- [isConnected](Protocol_SignallingProtocol.SignallingProtocol.md#isconnected) +- [sendMessage](Protocol_SignallingProtocol.SignallingProtocol.md#sendmessage) + +## Constructors + +### constructor + +• **new SignallingProtocol**(`transport`): [`SignallingProtocol`](Protocol_SignallingProtocol.SignallingProtocol.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `transport` | [`ITransport`](../interfaces/Transport_ITransport.ITransport.md) | + +#### Returns + +[`SignallingProtocol`](Protocol_SignallingProtocol.SignallingProtocol.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[Protocol/SignallingProtocol.ts:36](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L36) + +## Properties + +### transport + +• **transport**: [`ITransport`](../interfaces/Transport_ITransport.ITransport.md) + +#### Defined in + +[Protocol/SignallingProtocol.ts:34](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L34) + +## Accessors + +### SIGNALLING\_VERSION + +• `get` **SIGNALLING_VERSION**(): `string` + +#### Returns + +`string` + +#### Defined in + +[Protocol/SignallingProtocol.ts:31](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L31) + +## Methods + +### connect + +▸ **connect**(`url`): `boolean` + +Asks the transport to connect to the given URL. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `url` | `string` | The url to connect to. | + +#### Returns + +`boolean` + +True if the connection call succeeded. + +#### Defined in + +[Protocol/SignallingProtocol.ts:58](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L58) + +___ + +### disconnect + +▸ **disconnect**(`code?`, `reason?`): `void` + +Asks the transport to disconnect from any connection it might have. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `code?` | `number` | An optional disconnection code. | +| `reason?` | `string` | An optional descriptive string for the disconnect reason. | + +#### Returns + +`void` + +#### Defined in + +[Protocol/SignallingProtocol.ts:67](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L67) + +___ + +### isConnected + +▸ **isConnected**(): `boolean` + +Returns true if the transport is connected and ready to send/receive messages. + +#### Returns + +`boolean` + +True if the protocol is connected. + +#### Defined in + +[Protocol/SignallingProtocol.ts:75](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L75) + +___ + +### sendMessage + +▸ **sendMessage**(`msg`): `void` + +Passes a message to the transport to send to the other end. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | The message to send. | + +#### Returns + +`void` + +#### Defined in + +[Protocol/SignallingProtocol.ts:83](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Protocol/SignallingProtocol.ts#L83) diff --git a/Common/docs/classes/Transport_WebSocketTransport.WebSocketTransport.md b/Common/docs/classes/Transport_WebSocketTransport.WebSocketTransport.md new file mode 100644 index 00000000..5a27764b --- /dev/null +++ b/Common/docs/classes/Transport_WebSocketTransport.WebSocketTransport.md @@ -0,0 +1,327 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Transport/WebSocketTransport](../modules/Transport_WebSocketTransport.md) / WebSocketTransport + +# Class: WebSocketTransport + +[Transport/WebSocketTransport](../modules/Transport_WebSocketTransport.md).WebSocketTransport + +The controller for the WebSocket and all associated methods + +## Hierarchy + +- `EventEmitter` + + ↳ **`WebSocketTransport`** + +## Implements + +- [`ITransport`](../interfaces/Transport_ITransport.ITransport.md) + +## Table of contents + +### Constructors + +- [constructor](Transport_WebSocketTransport.WebSocketTransport.md#constructor) + +### Properties + +- [WS\_OPEN\_STATE](Transport_WebSocketTransport.WebSocketTransport.md#ws_open_state) +- [onMessage](Transport_WebSocketTransport.WebSocketTransport.md#onmessage) +- [webSocket](Transport_WebSocketTransport.WebSocketTransport.md#websocket) + +### Methods + +- [close](Transport_WebSocketTransport.WebSocketTransport.md#close) +- [connect](Transport_WebSocketTransport.WebSocketTransport.md#connect) +- [disconnect](Transport_WebSocketTransport.WebSocketTransport.md#disconnect) +- [handleOnClose](Transport_WebSocketTransport.WebSocketTransport.md#handleonclose) +- [handleOnError](Transport_WebSocketTransport.WebSocketTransport.md#handleonerror) +- [handleOnMessage](Transport_WebSocketTransport.WebSocketTransport.md#handleonmessage) +- [handleOnMessageBinary](Transport_WebSocketTransport.WebSocketTransport.md#handleonmessagebinary) +- [handleOnOpen](Transport_WebSocketTransport.WebSocketTransport.md#handleonopen) +- [isConnected](Transport_WebSocketTransport.WebSocketTransport.md#isconnected) +- [sendMessage](Transport_WebSocketTransport.WebSocketTransport.md#sendmessage) + +## Constructors + +### constructor + +• **new WebSocketTransport**(): [`WebSocketTransport`](Transport_WebSocketTransport.WebSocketTransport.md) + +#### Returns + +[`WebSocketTransport`](Transport_WebSocketTransport.WebSocketTransport.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[Transport/WebSocketTransport.ts:22](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L22) + +## Properties + +### WS\_OPEN\_STATE + +• **WS\_OPEN\_STATE**: `number` = `1` + +#### Defined in + +[Transport/WebSocketTransport.ts:19](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L19) + +___ + +### onMessage + +• **onMessage**: (`msg`: [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)) => `void` + +Callback filled in by the SignallingProtocol and should be called by the transport when a new message arrives. + +#### Type declaration + +▸ (`msg`): `void` + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | + +##### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[onMessage](../interfaces/Transport_ITransport.ITransport.md#onmessage) + +#### Defined in + +[Transport/WebSocketTransport.ts:35](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L35) + +___ + +### webSocket + +• **webSocket**: `WebSocket` + +#### Defined in + +[Transport/WebSocketTransport.ts:20](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L20) + +## Methods + +### close + +▸ **close**(): `void` + +Closes the Websocket connection + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:175](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L175) + +___ + +### connect + +▸ **connect**(`connectionURL`): `boolean` + +Connect to the signaling server + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `connectionURL` | `string` | The Address of the signaling server | + +#### Returns + +`boolean` + +If there is a connection + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[connect](../interfaces/Transport_ITransport.ITransport.md#connect) + +#### Defined in + +[Transport/WebSocketTransport.ts:42](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L42) + +___ + +### disconnect + +▸ **disconnect**(`code?`, `reason?`): `void` + +Disconnect this transport. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `code?` | `number` | An optional disconnect code. | +| `reason?` | `string` | A descriptive string for the disconnect reason. | + +#### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[disconnect](../interfaces/Transport_ITransport.ITransport.md#disconnect) + +#### Defined in + +[Transport/WebSocketTransport.ts:63](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L63) + +___ + +### handleOnClose + +▸ **handleOnClose**(`event`): `void` + +Handles when the Websocket is closed + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `CloseEvent` | Close Event | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:161](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L161) + +___ + +### handleOnError + +▸ **handleOnError**(): `void` + +Handles when there is an error on the websocket + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:152](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L152) + +___ + +### handleOnMessage + +▸ **handleOnMessage**(`event`): `void` + +Handles what happens when a message is received + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `MessageEvent`\<`any`\> | Message Received | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:112](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L112) + +___ + +### handleOnMessageBinary + +▸ **handleOnMessageBinary**(`event`): `void` + +Handles what happens when a message is received in binary form + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `MessageEvent`\<`any`\> | Message Received | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:79](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L79) + +___ + +### handleOnOpen + +▸ **handleOnOpen**(): `void` + +Handles when the Websocket is opened + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransport.ts:140](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L140) + +___ + +### isConnected + +▸ **isConnected**(): `boolean` + +Should return true when the transport is connected and ready to send/receive messages. + +#### Returns + +`boolean` + +True if the transport is connected. + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[isConnected](../interfaces/Transport_ITransport.ITransport.md#isconnected) + +#### Defined in + +[Transport/WebSocketTransport.ts:71](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L71) + +___ + +### sendMessage + +▸ **sendMessage**(`msg`): `void` + +Sends a message over the websocket. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | The message to send. | + +#### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[sendMessage](../interfaces/Transport_ITransport.ITransport.md#sendmessage) + +#### Defined in + +[Transport/WebSocketTransport.ts:30](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransport.ts#L30) diff --git a/Common/docs/classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md b/Common/docs/classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md new file mode 100644 index 00000000..865d2b7c --- /dev/null +++ b/Common/docs/classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md @@ -0,0 +1,336 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Transport/WebSocketTransportNJS](../modules/Transport_WebSocketTransportNJS.md) / WebSocketTransportNJS + +# Class: WebSocketTransportNJS + +[Transport/WebSocketTransportNJS](../modules/Transport_WebSocketTransportNJS.md).WebSocketTransportNJS + +An implementation of WebSocketTransport from pixelstreamingcommon that supports node.js websockets +This is needed because of the slight differences between the 'ws' node.js package and the websockets +supported in the browsers. +Do not use this code in a browser use 'WebSocketTransport' instead. + +## Hierarchy + +- `EventEmitter` + + ↳ **`WebSocketTransportNJS`** + +## Implements + +- [`ITransport`](../interfaces/Transport_ITransport.ITransport.md) + +## Table of contents + +### Constructors + +- [constructor](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#constructor) + +### Properties + +- [WS\_OPEN\_STATE](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#ws_open_state) +- [onMessage](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#onmessage) +- [webSocket](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#websocket) +- [webSocketServer](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#websocketserver) + +### Methods + +- [close](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#close) +- [connect](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#connect) +- [disconnect](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#disconnect) +- [handleOnClose](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#handleonclose) +- [handleOnError](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#handleonerror) +- [handleOnMessage](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#handleonmessage) +- [handleOnOpen](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#handleonopen) +- [isConnected](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#isconnected) +- [sendMessage](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md#sendmessage) + +## Constructors + +### constructor + +• **new WebSocketTransportNJS**(`existingSocket?`): [`WebSocketTransportNJS`](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `existingSocket?` | `WebSocket` | + +#### Returns + +[`WebSocketTransportNJS`](Transport_WebSocketTransportNJS.WebSocketTransportNJS.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:19](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L19) + +## Properties + +### WS\_OPEN\_STATE + +• **WS\_OPEN\_STATE**: `number` = `1` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:15](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L15) + +___ + +### onMessage + +• **onMessage**: (`msg`: [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)) => `void` + +Callback filled in by the SignallingProtocol and should be called by the transport when a new message arrives. + +#### Type declaration + +▸ (`msg`): `void` + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | + +##### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[onMessage](../interfaces/Transport_ITransport.ITransport.md#onmessage) + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:37](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L37) + +___ + +### webSocket + +• **webSocket**: `WebSocket` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:16](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L16) + +___ + +### webSocketServer + +• **webSocketServer**: `Server`\ + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L17) + +## Methods + +### close + +▸ **close**(): `void` + +Closes the Websocket connection + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:109](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L109) + +___ + +### connect + +▸ **connect**(`connectionURL`): `boolean` + +Connect to the signaling server + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `connectionURL` | `string` | The Address of the signaling server | + +#### Returns + +`boolean` + +If there is a connection + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[connect](../interfaces/Transport_ITransport.ITransport.md#connect) + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:44](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L44) + +___ + +### disconnect + +▸ **disconnect**(`code?`, `reason?`): `void` + +Disconnect this transport. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `code?` | `number` | An optional disconnect code. | +| `reason?` | `string` | A descriptive string for the disconnect reason. | + +#### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[disconnect](../interfaces/Transport_ITransport.ITransport.md#disconnect) + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:55](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L55) + +___ + +### handleOnClose + +▸ **handleOnClose**(`event`): `void` + +Handles when the Websocket is closed + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `CloseEvent` | Close Event | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:102](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L102) + +___ + +### handleOnError + +▸ **handleOnError**(`event`): `void` + +Handles when there is an error on the websocket + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `ErrorEvent` | Error Payload | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:94](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L94) + +___ + +### handleOnMessage + +▸ **handleOnMessage**(`event`): `void` + +Handles what happens when a message is received + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `MessageEvent` | Message Received | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:71](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L71) + +___ + +### handleOnOpen + +▸ **handleOnOpen**(`event`): `void` + +Handles when the Websocket is opened + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `event` | `Event` | Not Used | + +#### Returns + +`void` + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:86](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L86) + +___ + +### isConnected + +▸ **isConnected**(): `boolean` + +Should return true when the transport is connected and ready to send/receive messages. + +#### Returns + +`boolean` + +True if the transport is connected. + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[isConnected](../interfaces/Transport_ITransport.ITransport.md#isconnected) + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:63](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L63) + +___ + +### sendMessage + +▸ **sendMessage**(`msg`): `void` + +Sends a message over the websocket. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | The message to send. | + +#### Returns + +`void` + +#### Implementation of + +[ITransport](../interfaces/Transport_ITransport.ITransport.md).[sendMessage](../interfaces/Transport_ITransport.ITransport.md#sendmessage) + +#### Defined in + +[Transport/WebSocketTransportNJS.ts:32](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/WebSocketTransportNJS.ts#L32) diff --git a/Common/docs/interfaces/Messages_base_message.BaseMessage.md b/Common/docs/interfaces/Messages_base_message.BaseMessage.md new file mode 100644 index 00000000..077e7920 --- /dev/null +++ b/Common/docs/interfaces/Messages_base_message.BaseMessage.md @@ -0,0 +1,34 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Messages/base\_message](../modules/Messages_base_message.md) / BaseMessage + +# Interface: BaseMessage + +[Messages/base\_message](../modules/Messages_base_message.md).BaseMessage + +All Pixel Streaming messages should adhere to this interface. + +## Table of contents + +### Properties + +- [playerId](Messages_base_message.BaseMessage.md#playerid) +- [type](Messages_base_message.BaseMessage.md#type) + +## Properties + +### playerId + +• `Optional` **playerId**: `string` + +#### Defined in + +[Messages/base_message.ts:8](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Messages/base_message.ts#L8) + +___ + +### type + +• **type**: `string` + +#### Defined in + +[Messages/base_message.ts:6](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Messages/base_message.ts#L6) diff --git a/Common/docs/interfaces/Transport_ITransport.ITransport.md b/Common/docs/interfaces/Transport_ITransport.ITransport.md new file mode 100644 index 00000000..8ca0357a --- /dev/null +++ b/Common/docs/interfaces/Transport_ITransport.ITransport.md @@ -0,0 +1,148 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / [Transport/ITransport](../modules/Transport_ITransport.md) / ITransport + +# Interface: ITransport + +[Transport/ITransport](../modules/Transport_ITransport.md).ITransport + +An interface to a transport protocol that is in charge of sending and receiving signalling messages. +Implement this interface to support your custom transport. You can then supply an instance of your +transport to the constructor of SignallingProtocol during startup. + +## Hierarchy + +- `EventEmitter` + + ↳ **`ITransport`** + +## Implemented by + +- [`WebSocketTransport`](../classes/Transport_WebSocketTransport.WebSocketTransport.md) +- [`WebSocketTransportNJS`](../classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md) + +## Table of contents + +### Properties + +- [onMessage](Transport_ITransport.ITransport.md#onmessage) + +### Methods + +- [connect](Transport_ITransport.ITransport.md#connect) +- [disconnect](Transport_ITransport.ITransport.md#disconnect) +- [isConnected](Transport_ITransport.ITransport.md#isconnected) +- [sendMessage](Transport_ITransport.ITransport.md#sendmessage) + +## Properties + +### onMessage + +• **onMessage**: (`msg`: [`BaseMessage`](Messages_base_message.BaseMessage.md)) => `void` + +Callback filled in by the SignallingProtocol and should be called by the transport when a new message arrives. + +#### Type declaration + +▸ (`msg`): `void` + +Callback filled in by the SignallingProtocol and should be called by the transport when a new message arrives. + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `msg` | [`BaseMessage`](Messages_base_message.BaseMessage.md) | + +##### Returns + +`void` + +#### Defined in + +[Transport/ITransport.ts:19](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/ITransport.ts#L19) + +## Methods + +### connect + +▸ **connect**(`url`): `boolean` + +Connect to a given URL. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `url` | `string` | The URL for the transport to connect to. | + +#### Returns + +`boolean` + +True if the connection was successful. + +#### Defined in + +[Transport/ITransport.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/ITransport.ts#L26) + +___ + +### disconnect + +▸ **disconnect**(`code?`, `reason?`): `void` + +Disconnect this transport. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `code?` | `number` | An optional disconnect code. | +| `reason?` | `string` | A descriptive string for the disconnect reason. | + +#### Returns + +`void` + +#### Defined in + +[Transport/ITransport.ts:33](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/ITransport.ts#L33) + +___ + +### isConnected + +▸ **isConnected**(): `boolean` + +Should return true when the transport is connected and ready to send/receive messages. + +#### Returns + +`boolean` + +True if the transport is connected. + +#### Defined in + +[Transport/ITransport.ts:39](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/ITransport.ts#L39) + +___ + +### sendMessage + +▸ **sendMessage**(`msg`): `void` + +Called when the protocol wants to send a message over the transport. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `msg` | [`BaseMessage`](Messages_base_message.BaseMessage.md) | The message to send over the transport. | + +#### Returns + +`void` + +#### Defined in + +[Transport/ITransport.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Transport/ITransport.ts#L14) diff --git a/Common/docs/messages.md b/Common/docs/messages.md new file mode 100644 index 00000000..5aeaa2cc --- /dev/null +++ b/Common/docs/messages.md @@ -0,0 +1,612 @@ +# Protocol Documentation + + +## Table of Contents + +- [signalling_messages.proto](#signalling_messages-proto) + - [answer](#-answer) + - [base_message](#-base_message) + - [config](#-config) + - [dataChannelRequest](#-dataChannelRequest) + - [disconnectPlayer](#-disconnectPlayer) + - [endpointId](#-endpointId) + - [endpointIdConfirm](#-endpointIdConfirm) + - [iceCandidate](#-iceCandidate) + - [iceCandidateData](#-iceCandidateData) + - [identify](#-identify) + - [layerPreference](#-layerPreference) + - [listStreamers](#-listStreamers) + - [offer](#-offer) + - [peerConnectionOptions](#-peerConnectionOptions) + - [peerDataChannels](#-peerDataChannels) + - [peerDataChannelsReady](#-peerDataChannelsReady) + - [ping](#-ping) + - [playerConnected](#-playerConnected) + - [playerCount](#-playerCount) + - [playerDisconnected](#-playerDisconnected) + - [pong](#-pong) + - [startStreaming](#-startStreaming) + - [stats](#-stats) + - [stopStreaming](#-stopStreaming) + - [streamerDataChannels](#-streamerDataChannels) + - [streamerDisconnected](#-streamerDisconnected) + - [streamerIdChanged](#-streamerIdChanged) + - [streamerList](#-streamerList) + - [subscribe](#-subscribe) + - [unsubscribe](#-unsubscribe) + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## signalling_messages.proto + + + + + +### answer +This is a response to an `offer` message. It contains the answer `SDP`. +Part of the normal subscribe flow. A peer will subscribe to a streamer +and depending on whether `offer_to_receive` is set, one peer will make +an offer and the other should answer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'answer' | +| sdp | [string](#string) | | The WebRTC SDP payload | +| playerId | [string](#string) | optional | If being sent to a player this should be set to a valid player ID. | + + + + + + + + +### base_message +This is just a helper message type that allows us to use a "base interface" +in code to describe that all messages should at least have a 'type field'. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | The type of the message. | + + + + + + + + +### config +A config message is sent to each connecting peer when it connects to +describe to them the setup of the signalling server they're +connecting to. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'config' | +| peerConnectionOptions | [peerConnectionOptions](#peerConnectionOptions) | | The user defined peer connnection options | +| protocolVersion | [string](#string) | optional | The signalling protocol version the signalling server is using | + + + + + + + + +### dataChannelRequest +Message is forwarded to a connected SFU. Tells the SFU that the player +requests data channels to the streamer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'dataChannelRequest' | + + + + + + + + +### disconnectPlayer +Message is consumed by the Signalling Server. Requests that the +signalling server disconnect the given player matching the player ID. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'disconnectPlayer' | +| playerId | [string](#string) | | The ID of the player to disconnect. | +| reason | [string](#string) | optional | An optional reason string to send to the player. | + + + + + + + + +### endpointId +Message is consumed by the Signalling Server. Specifies an id for the +streamer. This is used to uniquely identify multiple streamers connected +to the same Signalling Server. +Note: to preserve backward compatibility when Streamer IDs were optional, +when a Streamer first connects it is assigned a temporary ID which +allows use of older Streamers if needed. +Note: Streamer IDs must be unique and so if the ID provided here clashes +with an existing ID, the ID may be altered slightly (usually just an +appended number). The streamer will be sent an `endpointIdConfirm` +message to notify it of it's final ID. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'endpointId' | +| id | [string](#string) | | The requested ID of the streamer. | +| protocolVersion | [string](#string) | optional | The signalling protocol version the streamer is using | + + + + + + + + +### endpointIdConfirm +A response to `endpointId` that will notify the streamer of its final +ID given. Since streamer IDs must be unique the requested ID may not be +available and may need to be altered. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'endpointIdConfirm' | +| committedId | [string](#string) | | The final ID of the streamer. | + + + + + + + + +### iceCandidate +A single ICE candidate entry from WebRTC. Notifies a peer of a possible +connection option to another peer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'iceCandidate' | +| candidate | [iceCandidateData](#iceCandidateData) | | The ICE candidate data from WebRTC | +| playerId | [string](#string) | optional | If being sent to a player this should be a valid player ID. | + + + + + + + + +### iceCandidateData +A submessage that contains data from a WebRTC ICE candidate. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| candidate | [string](#string) | | | +| sdpMid | [string](#string) | | | +| sdpMLineIndex | [int32](#int32) | | | +| usernameFragment | [string](#string) | optional | | + + + + + + + + +### identify +A request for a new streamer to give itself an ID. The flow for these +messages should be connect->identify->endpointId->endpointIdConfirm + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'identify' | + + + + + + + + +### layerPreference +Message is forwarded to a connected SFU. Sends a preferred layer index to a +connected SFU for a specified player. Useful for switching between SFU +quality layers to force a certain resolution/quality option either as part +of UX or testing. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'layerPreference' | +| spatialLayer | [int32](#int32) | | The requested spatial layer | +| temporalLayer | [int32](#int32) | | The requested temporal layer | +| playerId | [string](#string) | | The player ID this preference refers to | + + + + + + + + +### listStreamers +A request to the signalling server to send the player a list of +available streamers it could possibly subscribe to. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'listStreamers' | + + + + + + + + +### offer +An offer message can be an offer of a WebRTC stream, or an offer to +receive a WebRTC stream, depending on the configuration of the player. +The default behaviour is that when a player subscribes to a streamer +the streamer will offer the stream to the new player. +An alternative configuration exists where a player can be configured +to offer to receive and in that case the player will subscribe to a +streamer and then offer to receive the stream. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'offer' | +| sdp | [string](#string) | | The SDP payload from WebRTC | +| playerId | [string](#string) | optional | If being sent to a player this should be a valid player ID | +| sfu | [bool](#bool) | optional | Indiates that this offer is coming from an SFU. | + + + + + + + + +### peerConnectionOptions +This is a user defined structure that is sent as part of the `config` +message. Left empty here because everything is optional. + + + + + + + + +### peerDataChannels +Message is forwarded to a player. Sends information to the player about what +data channels to use for sending/receiving with the streamer. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'peerDataChannels' | +| playerId | [string](#string) | | The player ID this message refers to. | +| sendStreamId | [int32](#int32) | | The channel ID to use for sending data. | +| recvStreamId | [int32](#int32) | | The channel ID to use for receiving data. | + + + + + + + + +### peerDataChannelsReady +Message is forwarded to a connected SFU. Tells the SFU that the player is +ready for data channels to be negotiated. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'peerDataChannelsReady' | + + + + + + + + +### ping +A keepalive ping message used to test that the connection is still open. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'ping' | +| time | [int32](#int32) | | The current time | + + + + + + + + +### playerConnected +A message sent to a streamer to notify it that a player has just +subscribed to it. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'playerConnected' | +| dataChannel | [bool](#bool) | | True if the player should be given a datachannel for stream control purposes. | +| sfu | [bool](#bool) | | True if the player connected is an SFU | +| sendOffer | [bool](#bool) | | True if the streamer should send an offer. False if the player is offering to receive | +| playerId | [string](#string) | | The ID of the player that connected. | + + + + + + + + +### playerCount +DEPRECATED Message is sent to players to indicate how many currently connected players +there are on this signalling server. (Note: This is mostly old behaviour and +is not influenced by multi streamers or who is subscribed to what streamer. +It just reports the number of players it knows about.) + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'playerCount' | +| count | [int32](#int32) | | The number of players connected. | + + + + + + + + +### playerDisconnected +Message is used to notify a streamer that a player has +unsubscribed/disconnected from the stream. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'playerDisconnected' | +| playerId | [string](#string) | | The ID of the player that disconnected. | + + + + + + + + +### pong +Message is a reply to `ping` from a streamer. Replies with the time from the +ping message. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'pong' | +| time | [int32](#int32) | | The echoed time from the ping message | + + + + + + + + +### startStreaming +Sent by the SFU to indicate that it is now streaming. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'startStreaming' | + + + + + + + + +### stats +DEPRECATED Message is consumed by the signalling server. Will print out the provided +stats data on the console. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'stats' | +| data | [string](#string) | | The stats data to echo. | + + + + + + + + +### stopStreaming +Sent by the SFU to indicate that it is now no longer streaming. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'stopStreaming' | + + + + + + + + +### streamerDataChannels +Message is forwarded to the streamer. Sends a request to the streamer to +open up data channels for a given player. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'streamerDataChannels' | +| sfuId | [string](#string) | | The SFU the player is connected to | +| sendStreamId | [int32](#int32) | | The channel ID to use for sending data. | +| recvStreamId | [int32](#int32) | | The channel ID to use for receiving data. | + + + + + + + + +### streamerDisconnected +Message is used to notify players when a Streamer disconnects from the +signalling server. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'streamerDisconnected' | + + + + + + + + +### streamerIdChanged +Message is used to communicate to players that the streamer it is currently +subscribed to is changing its ID. This allows players to keep track of it's +currently subscribed streamer and allow auto reconnects to the correct +streamer. This happens if a streamer sends an `endpointID` message after it +already has an ID assigned. (Can happen if it is late to respond to the +`identify` message and is auto assigned a legacy ID.) + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'streamerIdChanged' | +| newID | [string](#string) | | The new ID of the streamer. | + + + + + + + + +### streamerList +Message is a reply to `listStreamers` from a player. Replies with a list of +currently active streamers connected to this server. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'streamerList' | +| ids | [string](#string) | repeated | A list of streamer IDs active on the server. | + + + + + + + + +### subscribe +Message is consumed by the signalling server. Tells the signalling server +that the player requests to subscribe to the given stream. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'subscribe' | +| streamerId | [string](#string) | | The ID of the streamer the player wishes to subscribe to. | + + + + + + + + +### unsubscribe +Message is consumed by the signalling server. Tells the signalling server +that the player wishes to unsubscribe from the current stream. The player +must have previously used the `subscribe` message for this to have any effect. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| type | [string](#string) | | Should always be 'unsubscribe' | + + + + + + + + + + + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/Common/docs/modules/Logger_Logger.md b/Common/docs/modules/Logger_Logger.md new file mode 100644 index 00000000..d1620b90 --- /dev/null +++ b/Common/docs/modules/Logger_Logger.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Logger/Logger + +# Module: Logger/Logger + +## Table of contents + +### Classes + +- [Logger](../classes/Logger_Logger.Logger.md) diff --git a/Common/docs/modules/Messages_base_message.md b/Common/docs/modules/Messages_base_message.md new file mode 100644 index 00000000..f0247733 --- /dev/null +++ b/Common/docs/modules/Messages_base_message.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Messages/base\_message + +# Module: Messages/base\_message + +## Table of contents + +### Interfaces + +- [BaseMessage](../interfaces/Messages_base_message.BaseMessage.md) diff --git a/Common/docs/modules/Messages_message_helpers.md b/Common/docs/modules/Messages_message_helpers.md new file mode 100644 index 00000000..a26c0489 --- /dev/null +++ b/Common/docs/modules/Messages_message_helpers.md @@ -0,0 +1,62 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Messages/message\_helpers + +# Module: Messages/message\_helpers + +## Table of contents + +### Functions + +- [createMessage](Messages_message_helpers.md#createmessage) +- [validateMessage](Messages_message_helpers.md#validatemessage) + +## Functions + +### createMessage + +▸ **createMessage**(`messageType`, `params?`): [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) + +A helper for creating signalling messages. Takes in optional given parameters and +includes them in a message object with the 'type' field set properly for the message +type supplied. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `messageType` | `IMessageType`\<[`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)\> | A message type from MessageRegistry that indicates the type of message to create. | +| `params?` | `object` | An optional object whose fields are added to the newly created message. | + +#### Returns + +[`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) + +The resulting message object. + +#### Defined in + +[Messages/message_helpers.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Messages/message_helpers.ts#L14) + +___ + +### validateMessage + +▸ **validateMessage**(`msg`): `IMessageType`\<[`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)\> \| ``null`` + +Tests that the supplied message is valid. That is contains all expected fields and +doesn't contain any unknown fields. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `msg` | [`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md) | The message object to test. | + +#### Returns + +`IMessageType`\<[`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)\> \| ``null`` + +The message type from MessageRegistry of the supplied message object if it's valid, or null if invalid. + +#### Defined in + +[Messages/message_helpers.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Messages/message_helpers.ts#L29) diff --git a/Common/docs/modules/Messages_message_registry.md b/Common/docs/modules/Messages_message_registry.md new file mode 100644 index 00000000..24ac48ce --- /dev/null +++ b/Common/docs/modules/Messages_message_registry.md @@ -0,0 +1,23 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Messages/message\_registry + +# Module: Messages/message\_registry + +## Table of contents + +### Variables + +- [MessageRegistry](Messages_message_registry.md#messageregistry) + +## Variables + +### MessageRegistry + +• `Const` **MessageRegistry**: `Record`\<`string`, `IMessageType`\<[`BaseMessage`](../interfaces/Messages_base_message.BaseMessage.md)\>\> + +A map of all the supported signalling messages in the Pixel Streaming +signalling protocol. This allows mapping of signalling message names +to actual message types. + +#### Defined in + +[Messages/message_registry.ts:10](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Common/src/Messages/message_registry.ts#L10) diff --git a/Common/docs/modules/Protocol_SignallingProtocol.md b/Common/docs/modules/Protocol_SignallingProtocol.md new file mode 100644 index 00000000..ee00019d --- /dev/null +++ b/Common/docs/modules/Protocol_SignallingProtocol.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Protocol/SignallingProtocol + +# Module: Protocol/SignallingProtocol + +## Table of contents + +### Classes + +- [SignallingProtocol](../classes/Protocol_SignallingProtocol.SignallingProtocol.md) diff --git a/Common/docs/modules/Transport_ITransport.md b/Common/docs/modules/Transport_ITransport.md new file mode 100644 index 00000000..90eacc6f --- /dev/null +++ b/Common/docs/modules/Transport_ITransport.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Transport/ITransport + +# Module: Transport/ITransport + +## Table of contents + +### Interfaces + +- [ITransport](../interfaces/Transport_ITransport.ITransport.md) diff --git a/Common/docs/modules/Transport_WebSocketTransport.md b/Common/docs/modules/Transport_WebSocketTransport.md new file mode 100644 index 00000000..befb2cea --- /dev/null +++ b/Common/docs/modules/Transport_WebSocketTransport.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Transport/WebSocketTransport + +# Module: Transport/WebSocketTransport + +## Table of contents + +### Classes + +- [WebSocketTransport](../classes/Transport_WebSocketTransport.WebSocketTransport.md) diff --git a/Common/docs/modules/Transport_WebSocketTransportNJS.md b/Common/docs/modules/Transport_WebSocketTransportNJS.md new file mode 100644 index 00000000..e4ec1368 --- /dev/null +++ b/Common/docs/modules/Transport_WebSocketTransportNJS.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / Transport/WebSocketTransportNJS + +# Module: Transport/WebSocketTransportNJS + +## Table of contents + +### Classes + +- [WebSocketTransportNJS](../classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md) diff --git a/Common/docs/modules/pixelstreamingcommon.md b/Common/docs/modules/pixelstreamingcommon.md new file mode 100644 index 00000000..156a52ae --- /dev/null +++ b/Common/docs/modules/pixelstreamingcommon.md @@ -0,0 +1,64 @@ +[@epicgames-ps/lib-pixelstreamingcommon-ue5.5](../README.md) / pixelstreamingcommon + +# Module: pixelstreamingcommon + +## Table of contents + +### References + +- [BaseMessage](pixelstreamingcommon.md#basemessage) +- [ITransport](pixelstreamingcommon.md#itransport) +- [Logger](pixelstreamingcommon.md#logger) +- [MessageHelpers](pixelstreamingcommon.md#messagehelpers) +- [MessageRegistry](pixelstreamingcommon.md#messageregistry) +- [SignallingProtocol](pixelstreamingcommon.md#signallingprotocol) +- [WebSocketTransport](pixelstreamingcommon.md#websockettransport) +- [WebSocketTransportNJS](pixelstreamingcommon.md#websockettransportnjs) + +## References + +### BaseMessage + +Re-exports [BaseMessage](../interfaces/Messages_base_message.BaseMessage.md) + +___ + +### ITransport + +Re-exports [ITransport](../interfaces/Transport_ITransport.ITransport.md) + +___ + +### Logger + +Re-exports [Logger](../classes/Logger_Logger.Logger.md) + +___ + +### MessageHelpers + +Renames and re-exports [Messages/message_helpers](Messages_message_helpers.md) + +___ + +### MessageRegistry + +Re-exports [MessageRegistry](Messages_message_registry.md#messageregistry) + +___ + +### SignallingProtocol + +Re-exports [SignallingProtocol](../classes/Protocol_SignallingProtocol.SignallingProtocol.md) + +___ + +### WebSocketTransport + +Re-exports [WebSocketTransport](../classes/Transport_WebSocketTransport.WebSocketTransport.md) + +___ + +### WebSocketTransportNJS + +Re-exports [WebSocketTransportNJS](../classes/Transport_WebSocketTransportNJS.WebSocketTransportNJS.md) diff --git a/Common/package-lock.json b/Common/package-lock.json index 43cc2413..abcb4662 100644 --- a/Common/package-lock.json +++ b/Common/package-lock.json @@ -1,12 +1,12 @@ { "name": "@epicgames-ps/lib-pixelstreamingcommon-ue5.5", - "version": "0.0.8", - "lockfileVersion": 2, + "version": "0.0.9", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@epicgames-ps/lib-pixelstreamingcommon-ue5.5", - "version": "0.0.8", + "version": "0.0.9", "license": "MIT", "dependencies": { "@protobuf-ts/plugin": "^2.9.3", @@ -20,13 +20,16 @@ "@typescript-eslint/parser": "^5.16.0", "cspell": "^4.1.0", "eslint": "^8.11.0", + "eslint-plugin-tsdoc": "^0.2.17", "jest": "^27.5.1", "jest-environment-jsdom": "27.5.1", "prettier": "2.8.3", "rimraf": "^5.0.5", "ts-jest": "27.1.5", "ts-loader": "^9.4.2", - "typedoc": "^0.23.24", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", "typescript": "^4.9.4" } }, @@ -890,6 +893,28 @@ "integrity": "sha512-yIuGeeZtQA2gqpGefGjZqBl8iGJpIYWz0QzDqsscNi2qfSnLsbjM0RkRbTehM8y9gGGe7xfgUP5adxceJa5Krg==", "dev": true }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1488,6 +1513,37 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1630,6 +1686,30 @@ "node": ">= 6" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -2350,6 +2430,12 @@ "node": ">= 8" } }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -2782,6 +2868,12 @@ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -3030,6 +3122,15 @@ "node": ">=8" } }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/diff-sequences": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", @@ -3262,6 +3363,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -3661,20 +3772,6 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3842,6 +3939,27 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -4866,6 +4984,12 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5000,9 +5124,9 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", "dev": true }, "node_modules/jsonfile": { @@ -5242,6 +5366,15 @@ "node": "*" } }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/minipass": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", @@ -5273,8 +5406,7 @@ "version": "2.6.2", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true + "dev": true }, "node_modules/node-int64": { "version": "0.4.0", @@ -6012,9 +6144,9 @@ } }, "node_modules/shiki": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", - "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "dependencies": { "ansi-sequence-parser": "^1.1.0", @@ -6477,6 +6609,58 @@ "node": ">= 8" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -6541,24 +6725,36 @@ } }, "node_modules/typedoc": { - "version": "0.23.28", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.28.tgz", - "integrity": "sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz", + "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==", "dev": true, "dependencies": { "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^7.1.3", - "shiki": "^0.14.1" + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" }, "bin": { "typedoc": "bin/typedoc" }, "engines": { - "node": ">= 14.14" + "node": ">= 16" }, "peerDependencies": { - "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x" + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" } }, "node_modules/typedoc/node_modules/brace-expansion": { @@ -6571,15 +6767,15 @@ } }, "node_modules/typedoc/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -6598,6 +6794,19 @@ "node": ">=4.2.0" } }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -6673,6 +6882,12 @@ "requires-port": "^1.0.0" } }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", @@ -6882,6 +7097,12 @@ "node": ">= 8" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -7018,6 +7239,15 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -7030,5325 +7260,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", - "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", - "dev": true - }, - "@babel/core": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", - "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.3", - "@babel/helpers": "^7.23.5", - "@babel/parser": "^7.23.5", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "dependencies": { - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", - "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", - "dev": true, - "requires": { - "@babel/types": "^7.23.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", - "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", - "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", - "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.5", - "@babel/types": "^7.23.5" - } - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", - "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.23.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", - "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", - "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.23.5", - "@babel/generator": "^7.23.5", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.5", - "@babel/types": "^7.23.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", - "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cspell/dict-aws": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-1.0.14.tgz", - "integrity": "sha512-K21CfB4ZpKYwwDQiPfic2zJA/uxkbsd4IQGejEvDAhE3z8wBs6g6BwwqdVO767M9NgZqc021yAVpr79N5pWe3w==", - "dev": true - }, - "@cspell/dict-bash": { - "version": "1.0.18", - "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-1.0.18.tgz", - "integrity": "sha512-kJIqQ+FD2TCSgaaP5XLEDgy222+pVWTc+VhveNO++gnTWU3BCVjkD5LjfW7g/CmGONnz+nwXDueWspProaSdJw==", - "dev": true - }, - "@cspell/dict-companies": { - "version": "1.0.40", - "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-1.0.40.tgz", - "integrity": "sha512-Aw07qiTroqSST2P5joSrC4uOA05zTXzI2wMb+me3q4Davv1D9sCkzXY0TGoC2vzhNv5ooemRi9KATGaBSdU1sw==", - "dev": true - }, - "@cspell/dict-cpp": { - "version": "1.1.40", - "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-1.1.40.tgz", - "integrity": "sha512-sscfB3woNDNj60/yGXAdwNtIRWZ89y35xnIaJVDMk5TPMMpaDvuk0a34iOPIq0g4V+Y8e3RyAg71SH6ADwSjGw==", - "dev": true - }, - "@cspell/dict-cryptocurrencies": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-1.0.10.tgz", - "integrity": "sha512-47ABvDJOkaST/rXipNMfNvneHUzASvmL6K/CbOFpYKfsd0x23Jc9k1yaOC7JAm82XSC/8a7+3Yu+Fk2jVJNnsA==", - "dev": true - }, - "@cspell/dict-csharp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-1.0.11.tgz", - "integrity": "sha512-nub+ZCiTgmT87O+swI+FIAzNwaZPWUGckJU4GN402wBq420V+F4ZFqNV7dVALJrGaWH7LvADRtJxi6cZVHJKeA==", - "dev": true - }, - "@cspell/dict-css": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-1.0.13.tgz", - "integrity": "sha512-HU8RbFRoGanFH85mT01Ot/Ay48ixr/gG25VPLtdq56QTrmPsw79gxYm/5Qay16eQbpoPIxaj5CAWNam+DX4GbA==", - "dev": true - }, - "@cspell/dict-django": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-1.0.26.tgz", - "integrity": "sha512-mn9bd7Et1L2zuibc08GVHTiD2Go3/hdjyX5KLukXDklBkq06r+tb0OtKtf1zKodtFDTIaYekGADhNhA6AnKLkg==", - "dev": true - }, - "@cspell/dict-dotnet": { - "version": "1.0.32", - "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-1.0.32.tgz", - "integrity": "sha512-9H9vXrgJB4KF8xsyTToXO53cXD33iyfrpT4mhCds+YLUw3P3x3E9myszgJzshnrxYBvQZ+QMII57Qr6SjZVk4Q==", - "dev": true - }, - "@cspell/dict-elixir": { - "version": "1.0.26", - "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-1.0.26.tgz", - "integrity": "sha512-hz1yETUiRJM7yjN3mITSnxcmZaEyaBbyJhpZPpg+cKUil+xhHeZ2wwfbRc83QHGmlqEuDWbdCFqKSpCDJYpYhg==", - "dev": true - }, - "@cspell/dict-en_us": { - "version": "1.2.45", - "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-1.2.45.tgz", - "integrity": "sha512-UPwR4rfiJCxnS+Py+EK9E4AUj3aPZE4p/yBRSHN+5aBQConlI0lLDtMceH5wlupA/sQTU1ERZGPJA9L96jVSyQ==", - "dev": true - }, - "@cspell/dict-en-gb": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb/-/dict-en-gb-1.1.33.tgz", - "integrity": "sha512-tKSSUf9BJEV+GJQAYGw5e+ouhEe2ZXE620S7BLKe3ZmpnjlNG9JqlnaBhkIMxKnNFkLY2BP/EARzw31AZnOv4g==", - "dev": true - }, - "@cspell/dict-filetypes": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-1.1.8.tgz", - "integrity": "sha512-EllahNkhzvLWo0ptwu0l3oEeAJOQSUpZnDfnKRIh6mJVehuSovNHwA9vrdZ8jBUjuqcfaN2e7c32zN0D/qvWJQ==", - "dev": true - }, - "@cspell/dict-fonts": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-1.0.14.tgz", - "integrity": "sha512-VhIX+FVYAnqQrOuoFEtya6+H72J82cIicz9QddgknsTqZQ3dvgp6lmVnsQXPM3EnzA8n1peTGpLDwHzT7ociLA==", - "dev": true - }, - "@cspell/dict-fullstack": { - "version": "1.0.39", - "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-1.0.39.tgz", - "integrity": "sha512-Mbi+zWdiP9yzL+X4YD9Tgcm5YQ95Ql+Y3vF2LRnOY6g2QWaijTRN1rgksVuxzpFqHi//+bx2uoUb0XEKBYDi8g==", - "dev": true - }, - "@cspell/dict-golang": { - "version": "1.1.24", - "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-1.1.24.tgz", - "integrity": "sha512-qq3Cjnx2U1jpeWAGJL1GL0ylEhUMqyaR36Xij6Y6Aq4bViCRp+HRRqk0x5/IHHbOrti45h3yy7ii1itRFo+Xkg==", - "dev": true - }, - "@cspell/dict-haskell": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-1.0.13.tgz", - "integrity": "sha512-kvl8T84cnYRPpND/P3D86P6WRSqebsbk0FnMfy27zo15L5MLAb3d3MOiT1kW3vEWfQgzUD7uddX/vUiuroQ8TA==", - "dev": true - }, - "@cspell/dict-html": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-1.1.9.tgz", - "integrity": "sha512-vvnYia0tyIS5Fdoz+gEQm77MGZZE66kOJjuNpIYyRHCXFAhWdYz3SmkRm6YKJSWSvuO+WBJYTKDvkOxSh3Fx/w==", - "dev": true - }, - "@cspell/dict-html-symbol-entities": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-1.0.23.tgz", - "integrity": "sha512-PV0UBgcBFbBLf/m1wfkVMM8w96kvfHoiCGLWO6BR3Q9v70IXoE4ae0+T+f0CkxcEkacMqEQk/I7vuE9MzrjaNw==", - "dev": true - }, - "@cspell/dict-java": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-1.0.23.tgz", - "integrity": "sha512-LcOg9srYLDoNGd8n3kbfDBlZD+LOC9IVcnFCdua1b/luCHNVmlgBx7e677qPu7olpMYOD5TQIVW2OmM1+/6MFA==", - "dev": true - }, - "@cspell/dict-latex": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-1.0.25.tgz", - "integrity": "sha512-cEgg91Migqcp1SdVV7dUeMxbPDhxdNo6Fgq2eygAXQjIOFK520FFvh/qxyBvW90qdZbIRoU2AJpchyHfGuwZFA==", - "dev": true - }, - "@cspell/dict-lorem-ipsum": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-1.0.22.tgz", - "integrity": "sha512-yqzspR+2ADeAGUxLTfZ4pXvPl7FmkENMRcGDECmddkOiuEwBCWMZdMP5fng9B0Q6j91hQ8w9CLvJKBz10TqNYg==", - "dev": true - }, - "@cspell/dict-lua": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-1.0.16.tgz", - "integrity": "sha512-YiHDt8kmHJ8nSBy0tHzaxiuitYp+oJ66ffCYuFWTNB3//Y0SI4OGHU3omLsQVeXIfCeVrO4DrVvRDoCls9B5zQ==", - "dev": true - }, - "@cspell/dict-node": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-1.0.12.tgz", - "integrity": "sha512-RPNn/7CSkflAWk0sbSoOkg0ORrgBARUjOW3QjB11KwV1gSu8f5W/ij/S50uIXtlrfoBLqd4OyE04jyON+g/Xfg==", - "dev": true - }, - "@cspell/dict-npm": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-1.0.16.tgz", - "integrity": "sha512-RwkuZGcYBxL3Yux3cSG/IOWGlQ1e9HLCpHeyMtTVGYKAIkFAVUnGrz20l16/Q7zUG7IEktBz5O42kAozrEnqMQ==", - "dev": true - }, - "@cspell/dict-php": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-1.0.25.tgz", - "integrity": "sha512-RoBIP5MRdByyPaXcznZMfOY1JdCMYPPLua5E9gkq0TJO7bX5mC9hyAKfYBSWVQunZydd82HZixjb5MPkDFU1uw==", - "dev": true - }, - "@cspell/dict-powershell": { - "version": "1.0.19", - "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-1.0.19.tgz", - "integrity": "sha512-zF/raM/lkhXeHf4I43OtK0gP9rBeEJFArscTVwLWOCIvNk21MJcNoTYoaGw+c056+Q+hJL0psGLO7QN+mxYH1A==", - "dev": true - }, - "@cspell/dict-python": { - "version": "1.0.38", - "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-1.0.38.tgz", - "integrity": "sha512-KuyOQaby9NID/pn7EkXilpUxjVIvvyLzhr7BPsDS6FcvUE8Yhss6bJowEDHSv6pa+W2387phoqbDf2rTicquAA==", - "dev": true - }, - "@cspell/dict-ruby": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-1.0.15.tgz", - "integrity": "sha512-I76hJA///lc1pgmDTGUFHN/O8KLIZIU/8TgIYIGI6Ix/YzSEvWNdQYbANn6JbCynS0X+7IbZ2Ft+QqvmGtIWuA==", - "dev": true - }, - "@cspell/dict-rust": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-1.0.23.tgz", - "integrity": "sha512-lR4boDzs79YD6+30mmiSGAMMdwh7HTBAPUFSB0obR3Kidibfc3GZ+MHWZXay5dxZ4nBKM06vyjtanF9VJ8q1Iw==", - "dev": true - }, - "@cspell/dict-scala": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-1.0.21.tgz", - "integrity": "sha512-5V/R7PRbbminTpPS3ywgdAalI9BHzcEjEj9ug4kWYvBIGwSnS7T6QCFCiu+e9LvEGUqQC+NHgLY4zs1NaBj2vA==", - "dev": true - }, - "@cspell/dict-software-terms": { - "version": "1.0.48", - "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-1.0.48.tgz", - "integrity": "sha512-pfF3Ys2gRffu5ElqkH7FQMDMi/iZMyOzpGMb3FSH0PJ2AnRQ5rRNWght1h2L36YxvXl0mWVaFrrfwiOyRIc8ZQ==", - "dev": true - }, - "@cspell/dict-typescript": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-1.0.20.tgz", - "integrity": "sha512-yIuGeeZtQA2gqpGefGjZqBl8iGJpIYWz0QzDqsscNi2qfSnLsbjM0RkRbTehM8y9gGGe7xfgUP5adxceJa5Krg==", - "dev": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", - "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", - "dev": true - }, - "@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "requires": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true - }, - "ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "requires": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - } - }, - "strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "requires": { - "ansi-regex": "^6.0.1" - } - }, - "wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "requires": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - } - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-27.5.1.tgz", - "integrity": "sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-27.5.1.tgz", - "integrity": "sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/reporters": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^27.5.1", - "jest-config": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-resolve-dependencies": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "jest-watcher": "^27.5.1", - "micromatch": "^4.0.4", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "@jest/environment": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz", - "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1" - } - }, - "@jest/fake-timers": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", - "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@sinonjs/fake-timers": "^8.0.1", - "@types/node": "*", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - } - }, - "@jest/globals": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-27.5.1.tgz", - "integrity": "sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/types": "^27.5.1", - "expect": "^27.5.1" - } - }, - "@jest/reporters": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-27.5.1.tgz", - "integrity": "sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-haste-map": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^8.1.0" - } - }, - "@jest/source-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-27.5.1.tgz", - "integrity": "sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9", - "source-map": "^0.6.0" - } - }, - "@jest/test-result": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-27.5.1.tgz", - "integrity": "sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-27.5.1.tgz", - "integrity": "sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-runtime": "^27.5.1" - } - }, - "@jest/transform": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-27.5.1.tgz", - "integrity": "sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^27.5.1", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-util": "^27.5.1", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - } - }, - "@jest/types": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz", - "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^16.0.0", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/source-map": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", - "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true - }, - "@protobuf-ts/plugin": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin/-/plugin-2.9.3.tgz", - "integrity": "sha512-tHYACv+nnIV2eoiMxeZhrgMqGiUktzUzrhfgnROg/rr8TecPLp9v5/yqNibN+bad5k7d57aqlTuQKhFl+J4W/g==", - "requires": { - "@protobuf-ts/plugin-framework": "^2.9.3", - "@protobuf-ts/protoc": "^2.9.3", - "@protobuf-ts/runtime": "^2.9.3", - "@protobuf-ts/runtime-rpc": "^2.9.3", - "typescript": "^3.9" - }, - "dependencies": { - "typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" - } - } - }, - "@protobuf-ts/plugin-framework": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@protobuf-ts/plugin-framework/-/plugin-framework-2.9.3.tgz", - "integrity": "sha512-iqdkhAu7fGPvBCVOoAEEFJ1/oaGIBoNIMgSv2WonTNJVHxv5FvvAfWFn6nG/eta34fHRZT38ZXTaYcMUkv8AiQ==", - "requires": { - "@protobuf-ts/runtime": "^2.9.3", - "typescript": "^3.9" - }, - "dependencies": { - "typescript": { - "version": "3.9.10", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.10.tgz", - "integrity": "sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q==" - } - } - }, - "@protobuf-ts/protoc": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.9.3.tgz", - "integrity": "sha512-TJ0Ycx/CIBqpB4wpKt6K05kjXj6zv36s/qpdCT/wdJBhpayOVBqLF5NpLp3WIiw1PmIxvqalB6QHKjvnLzGKLA==" - }, - "@protobuf-ts/runtime": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime/-/runtime-2.9.3.tgz", - "integrity": "sha512-nivzCpg/qYD0RX2OmHOahJALb8ndjGmUhNBcTJ0BbXoqKwCSM6vYA+vegzS3rhuaPgbyC7Ec8idlnizzUfIRuw==" - }, - "@protobuf-ts/runtime-rpc": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@protobuf-ts/runtime-rpc/-/runtime-rpc-2.9.3.tgz", - "integrity": "sha512-WelHpctvZeG8yhbb7tnsrLzotq9xjMCXuGuhJ8qMyEdNoBBEodbXseofAYFTebo2/PN2LzyEq3X6vwr5f8jqTA==", - "requires": { - "@protobuf-ts/runtime": "^2.9.3" - } - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true - }, - "@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/eslint": { - "version": "8.44.8", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.8.tgz", - "integrity": "sha512-4K8GavROwhrYl2QXDXm0Rv9epkA8GBFu0EI+XrrnnuCl7u8CWBRusX7fXJfanhZTDWSAL24gDI/UqXyUM0Injw==", - "dev": true, - "peer": true, - "requires": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "@types/estree": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", - "dev": true, - "peer": true - }, - "@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-fUy7YRpT+rHXto1YlL+J9rs0uLGyiqVt3ZOTQR+4ROc47yNl8WLdVLgUloBRhOxP1PZvguHl44T3H0wAWxahYQ==", - "dev": true, - "requires": { - "jest-matcher-utils": "^27.0.0", - "pretty-format": "^27.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/node": { - "version": "20.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", - "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "@types/semver": { - "version": "7.5.6", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", - "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "@types/webxr": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.10.tgz", - "integrity": "sha512-n3u5sqXQJhf1CS68mw3Wf16FQ4cRPNBBwdYLFzq3UddiADOim1Pn3Y6PBdDilz1vOJF3ybLxJ8ZEDlLIzrOQZg==", - "dev": true - }, - "@types/ws": { - "version": "8.5.10", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", - "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", - "requires": { - "@types/node": "*" - } - }, - "@types/yargs": { - "version": "16.0.9", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.9.tgz", - "integrity": "sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", - "dev": true - }, - "@webassemblyjs/ast": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", - "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/helper-numbers": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6" - } - }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", - "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-api-error": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", - "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-buffer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", - "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-numbers": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", - "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", - "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", - "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6" - } - }, - "@webassemblyjs/ieee754": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", - "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "@webassemblyjs/leb128": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", - "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", - "dev": true, - "peer": true, - "requires": { - "@xtuc/long": "4.2.2" - } - }, - "@webassemblyjs/utf8": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", - "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", - "dev": true, - "peer": true - }, - "@webassemblyjs/wasm-edit": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", - "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/helper-wasm-section": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-opt": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6", - "@webassemblyjs/wast-printer": "1.11.6" - } - }, - "@webassemblyjs/wasm-gen": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", - "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wasm-opt": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", - "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-buffer": "1.11.6", - "@webassemblyjs/wasm-gen": "1.11.6", - "@webassemblyjs/wasm-parser": "1.11.6" - } - }, - "@webassemblyjs/wasm-parser": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", - "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@webassemblyjs/helper-api-error": "1.11.6", - "@webassemblyjs/helper-wasm-bytecode": "1.11.6", - "@webassemblyjs/ieee754": "1.11.6", - "@webassemblyjs/leb128": "1.11.6", - "@webassemblyjs/utf8": "1.11.6" - } - }, - "@webassemblyjs/wast-printer": { - "version": "1.11.6", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", - "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", - "dev": true, - "peer": true, - "requires": { - "@webassemblyjs/ast": "1.11.6", - "@xtuc/long": "4.2.2" - } - }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "peer": true - }, - "@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "peer": true - }, - "abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "dev": true - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - }, - "dependencies": { - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - } - } - }, - "acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peer": true, - "requires": {} - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz", - "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==", - "dev": true - }, - "agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "requires": { - "debug": "4" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "peer": true, - "requires": {} - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-sequence-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", - "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", - "dev": true - }, - "babel-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz", - "integrity": "sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==", - "dev": true, - "requires": { - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-27.5.1.tgz", - "integrity": "sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.0.0", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-27.5.1.tgz", - "integrity": "sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^27.5.1", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browserslist": { - "version": "4.22.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", - "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001565", - "electron-to-chromium": "^1.4.601", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001566", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", - "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "chrome-trace-event": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", - "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", - "dev": true, - "peer": true - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "commander": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true - }, - "comment-json": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.3.tgz", - "integrity": "sha512-SsxdiOf064DWoZLH799Ata6u7iV658A11PlWtZATDlXPpKGJnbJZ5Z24ybixAi+LUUqJ/GKowAejtC5GFUG7Tw==", - "dev": true, - "requires": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - } - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "cspell": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/cspell/-/cspell-4.2.8.tgz", - "integrity": "sha512-eqan8+lCU9bSp8Tl4+SR/ccBnuPyMmp7evck/RlMdFTjLh/s+3vQ5hQyBzbzK8w2MMqL84CymW7BwIOKjpylSg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "commander": "^7.0.0", - "comment-json": "^4.0.6", - "cspell-glob": "^0.1.25", - "cspell-lib": "^4.3.12", - "fs-extra": "^9.1.0", - "gensequence": "^3.1.1", - "get-stdin": "^8.0.0", - "glob": "^7.1.6", - "minimatch": "^3.0.4" - } - }, - "cspell-glob": { - "version": "0.1.25", - "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-0.1.25.tgz", - "integrity": "sha512-/XaSHrGBpMJa+duFz3GKOWfrijrfdHT7a/XGgIcq3cymCSpOH+DPho42sl0jLI/hjM+8yv2m8aEoxRT8yVSnlg==", - "dev": true, - "requires": { - "micromatch": "^4.0.2" - } - }, - "cspell-io": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-4.1.7.tgz", - "integrity": "sha512-V0/tUu9FnIS3v+vAvDT6NNa14Nc/zUNX8+YUUOfFAiDJJTdqefmvcWjOJBIMYBf3wIk9iWLmLbMM+bNHqr7DSQ==", - "dev": true, - "requires": { - "iconv-lite": "^0.6.2", - "iterable-to-stream": "^1.0.1" - } - }, - "cspell-lib": { - "version": "4.3.12", - "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-4.3.12.tgz", - "integrity": "sha512-yCCb6MoW1K8Tsr/WVEQoO4dfYhH9bCsjQayccb8MlyDaNNuWJHuX+gUGHsZSXSuChSh8PrTWKXJzs13/uM977g==", - "dev": true, - "requires": { - "@cspell/dict-aws": "^1.0.13", - "@cspell/dict-bash": "^1.0.11", - "@cspell/dict-companies": "^1.0.35", - "@cspell/dict-cpp": "^1.1.37", - "@cspell/dict-cryptocurrencies": "^1.0.10", - "@cspell/dict-csharp": "^1.0.10", - "@cspell/dict-css": "^1.0.10", - "@cspell/dict-django": "^1.0.25", - "@cspell/dict-dotnet": "^1.0.24", - "@cspell/dict-elixir": "^1.0.23", - "@cspell/dict-en_us": "^1.2.39", - "@cspell/dict-en-gb": "^1.1.27", - "@cspell/dict-filetypes": "^1.1.5", - "@cspell/dict-fonts": "^1.0.13", - "@cspell/dict-fullstack": "^1.0.36", - "@cspell/dict-golang": "^1.1.24", - "@cspell/dict-haskell": "^1.0.12", - "@cspell/dict-html": "^1.1.5", - "@cspell/dict-html-symbol-entities": "^1.0.23", - "@cspell/dict-java": "^1.0.22", - "@cspell/dict-latex": "^1.0.23", - "@cspell/dict-lorem-ipsum": "^1.0.22", - "@cspell/dict-lua": "^1.0.16", - "@cspell/dict-node": "^1.0.10", - "@cspell/dict-npm": "^1.0.10", - "@cspell/dict-php": "^1.0.23", - "@cspell/dict-powershell": "^1.0.14", - "@cspell/dict-python": "^1.0.32", - "@cspell/dict-ruby": "^1.0.12", - "@cspell/dict-rust": "^1.0.22", - "@cspell/dict-scala": "^1.0.21", - "@cspell/dict-software-terms": "^1.0.24", - "@cspell/dict-typescript": "^1.0.16", - "comment-json": "^4.1.0", - "configstore": "^5.0.1", - "cspell-io": "^4.1.7", - "cspell-trie-lib": "^4.2.8", - "cspell-util-bundle": "^4.1.11", - "fs-extra": "^9.1.0", - "gensequence": "^3.1.1", - "minimatch": "^3.0.4", - "resolve-from": "^5.0.0", - "resolve-global": "^1.0.0", - "vscode-uri": "^3.0.2" - } - }, - "cspell-trie-lib": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-4.2.8.tgz", - "integrity": "sha512-Nt3c0gxOYXIc3/yhALDukpje1BgR6guvlUKWQO2zb0r7qRWpwUw2j2YM4dWbHQeH/3Hx5ei4Braa6cMaiJ5YBw==", - "dev": true, - "requires": { - "gensequence": "^3.1.1" - } - }, - "cspell-util-bundle": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/cspell-util-bundle/-/cspell-util-bundle-4.1.11.tgz", - "integrity": "sha512-or3OGKydZs1NwweMIgnA48k8H3F5zK4e5lonjUhpEzLYQZ2nB23decdoqZ8ogFC8pFTA40tZKDsMJ0b+65gX4Q==", - "dev": true - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", - "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "dot-prop": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", - "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", - "dev": true, - "requires": { - "is-obj": "^2.0.0" - } - }, - "eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "electron-to-chromium": { - "version": "1.4.604", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.604.tgz", - "integrity": "sha512-JAJ4lyLJYudlgJPYJicimU9R+qZ/3iyeyQS99bfT7PWi7psYWeN84lPswTjpHxQueU34PKxM/IJzQS6poYlovQ==", - "dev": true - }, - "emittery": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", - "integrity": "sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enhanced-resolve": { - "version": "5.15.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", - "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-module-lexer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", - "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", - "dev": true, - "peer": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint": { - "version": "8.55.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", - "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.55.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "peer": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-27.5.1.tgz", - "integrity": "sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "requires": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "dependencies": { - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } - } - }, - "flatted": { - "version": "3.2.9", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", - "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", - "dev": true - }, - "foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "dependencies": { - "signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true - } - } - }, - "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, - "fs-extra": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", - "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", - "dev": true, - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "gensequence": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-3.1.1.tgz", - "integrity": "sha512-ys3h0hiteRwmY6BsvSttPmkhC0vEQHPJduANBRtH/dlDPZ0UBIb/dXy80IcckXyuQ6LKg+PloRqvGER9IS7F7g==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stdin": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", - "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "peer": true - }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true - }, - "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "dev": true, - "requires": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - } - }, - "https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "requires": { - "agent-base": "6", - "debug": "4" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - }, - "ignore": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", - "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "requires": { - "hasown": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "dependencies": { - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - } - } - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterable-to-stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/iterable-to-stream/-/iterable-to-stream-1.0.1.tgz", - "integrity": "sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA==", - "dev": true - }, - "jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "requires": { - "@isaacs/cliui": "^8.0.2", - "@pkgjs/parseargs": "^0.11.0" - } - }, - "jest": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", - "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "import-local": "^3.0.2", - "jest-cli": "^27.5.1" - } - }, - "jest-changed-files": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-27.5.1.tgz", - "integrity": "sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "execa": "^5.0.0", - "throat": "^6.0.1" - } - }, - "jest-circus": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-27.5.1.tgz", - "integrity": "sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3", - "throat": "^6.0.1" - } - }, - "jest-cli": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-27.5.1.tgz", - "integrity": "sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==", - "dev": true, - "requires": { - "@jest/core": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "prompts": "^2.0.1", - "yargs": "^16.2.0" - } - }, - "jest-config": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-27.5.1.tgz", - "integrity": "sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==", - "dev": true, - "requires": { - "@babel/core": "^7.8.0", - "@jest/test-sequencer": "^27.5.1", - "@jest/types": "^27.5.1", - "babel-jest": "^27.5.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.9", - "jest-circus": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-jasmine2": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runner": "^27.5.1", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", - "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-docblock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-27.5.1.tgz", - "integrity": "sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-27.5.1.tgz", - "integrity": "sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-environment-jsdom": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-27.5.1.tgz", - "integrity": "sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1", - "jsdom": "^16.6.0" - } - }, - "jest-environment-node": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-27.5.1.tgz", - "integrity": "sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "jest-mock": "^27.5.1", - "jest-util": "^27.5.1" - } - }, - "jest-get-type": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", - "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", - "dev": true - }, - "jest-haste-map": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-27.5.1.tgz", - "integrity": "sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^27.5.1", - "jest-serializer": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "micromatch": "^4.0.4", - "walker": "^1.0.7" - } - }, - "jest-jasmine2": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-27.5.1.tgz", - "integrity": "sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^27.5.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "pretty-format": "^27.5.1", - "throat": "^6.0.1" - } - }, - "jest-leak-detector": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-27.5.1.tgz", - "integrity": "sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==", - "dev": true, - "requires": { - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-matcher-utils": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", - "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "pretty-format": "^27.5.1" - } - }, - "jest-message-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz", - "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^27.5.1", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^27.5.1", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz", - "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-27.5.1.tgz", - "integrity": "sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==", - "dev": true - }, - "jest-resolve": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz", - "integrity": "sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^27.5.1", - "jest-validate": "^27.5.1", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-27.5.1.tgz", - "integrity": "sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-snapshot": "^27.5.1" - } - }, - "jest-runner": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-27.5.1.tgz", - "integrity": "sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==", - "dev": true, - "requires": { - "@jest/console": "^27.5.1", - "@jest/environment": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.8.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^27.5.1", - "jest-environment-jsdom": "^27.5.1", - "jest-environment-node": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-leak-detector": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-runtime": "^27.5.1", - "jest-util": "^27.5.1", - "jest-worker": "^27.5.1", - "source-map-support": "^0.5.6", - "throat": "^6.0.1" - } - }, - "jest-runtime": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-27.5.1.tgz", - "integrity": "sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==", - "dev": true, - "requires": { - "@jest/environment": "^27.5.1", - "@jest/fake-timers": "^27.5.1", - "@jest/globals": "^27.5.1", - "@jest/source-map": "^27.5.1", - "@jest/test-result": "^27.5.1", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-mock": "^27.5.1", - "jest-regex-util": "^27.5.1", - "jest-resolve": "^27.5.1", - "jest-snapshot": "^27.5.1", - "jest-util": "^27.5.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - } - }, - "jest-serializer": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-27.5.1.tgz", - "integrity": "sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.9" - } - }, - "jest-snapshot": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-27.5.1.tgz", - "integrity": "sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==", - "dev": true, - "requires": { - "@babel/core": "^7.7.2", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.0.0", - "@jest/transform": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^27.5.1", - "graceful-fs": "^4.2.9", - "jest-diff": "^27.5.1", - "jest-get-type": "^27.5.1", - "jest-haste-map": "^27.5.1", - "jest-matcher-utils": "^27.5.1", - "jest-message-util": "^27.5.1", - "jest-util": "^27.5.1", - "natural-compare": "^1.4.0", - "pretty-format": "^27.5.1", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz", - "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-27.5.1.tgz", - "integrity": "sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==", - "dev": true, - "requires": { - "@jest/types": "^27.5.1", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^27.5.1", - "leven": "^3.1.0", - "pretty-format": "^27.5.1" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-27.5.1.tgz", - "integrity": "sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==", - "dev": true, - "requires": { - "@jest/test-result": "^27.5.1", - "@jest/types": "^27.5.1", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^27.5.1", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsdom": { - "version": "16.7.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", - "integrity": "sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==", - "dev": true, - "requires": { - "abab": "^2.0.5", - "acorn": "^8.2.4", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.3.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.1", - "domexception": "^2.0.1", - "escodegen": "^2.0.0", - "form-data": "^3.0.0", - "html-encoding-sniffer": "^2.0.1", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.0", - "parse5": "6.0.1", - "saxes": "^5.0.1", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.0.0", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.1.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.5.0", - "ws": "^7.4.6", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "dev": true, - "requires": {} - } - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==", - "dev": true - }, - "jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^2.0.0" - } - }, - "keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "peer": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "lunr": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", - "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "marked": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", - "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", - "dev": true - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true - }, - "mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "requires": { - "mime-db": "1.52.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "peer": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nwsapi": { - "version": "2.2.7", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", - "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "requires": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true - } - } - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz", - "integrity": "sha512-tJ/oJ4amDihPoufT5sM0Z1SKEuKay8LfVAMlbbhnnkvt6BUserZylqo2PN+p9KeljLr0OHa2rXHU1T8reeoTrw==", - "dev": true - }, - "pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true - }, - "querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "peer": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-global": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz", - "integrity": "sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==", - "dev": true, - "requires": { - "global-dirs": "^0.1.1" - } - }, - "resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", - "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", - "dev": true, - "requires": { - "glob": "^10.3.7" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "requires": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - } - }, - "minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "peer": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "peer": true, - "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "serialize-javascript": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", - "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", - "dev": true, - "peer": true, - "requires": { - "randombytes": "^2.1.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "shiki": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.5.tgz", - "integrity": "sha512-1gCAYOcmCFONmErGTrS1fjzJLA7MGZmKzrBNX7apqSwhyITJg2O102uFzXUeBxNnEkDA9vHIKLyeKq0V083vIw==", - "dev": true, - "requires": { - "ansi-sequence-parser": "^1.1.0", - "jsonc-parser": "^3.2.0", - "vscode-oniguruma": "^1.7.0", - "vscode-textmate": "^8.0.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "string-width-cjs": { - "version": "npm:string-width@4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-ansi-cjs": { - "version": "npm:strip-ansi@6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "terser": { - "version": "5.25.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.25.0.tgz", - "integrity": "sha512-we0I9SIsfvNUMP77zC9HG+MylwYYsGFSBG8qm+13oud2Yh+O104y614FRbyjpxys16jZwot72Fpi827YvGzuqg==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "peer": true - } - } - }, - "terser-webpack-plugin": { - "version": "5.3.9", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz", - "integrity": "sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==", - "dev": true, - "peer": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.17", - "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.1", - "terser": "^5.16.8" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "throat": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz", - "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", - "dev": true, - "requires": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "dependencies": { - "universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true - } - } - }, - "tr46": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.1.0.tgz", - "integrity": "sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "ts-jest": { - "version": "27.1.5", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-27.1.5.tgz", - "integrity": "sha512-Xv6jBQPoBEvBq/5i2TeSG9tt/nqkbpcurrEG1b+2yfBrcJelOZF9Ml6dmyMh7bcW9JyFbRYpR5rxROSlBLTZHA==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^27.0.0", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "20.x" - } - }, - "ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", - "dev": true, - "requires": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - } - } - }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "requires": { - "is-typedarray": "^1.0.0" - } - }, - "typedoc": { - "version": "0.23.28", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.23.28.tgz", - "integrity": "sha512-9x1+hZWTHEQcGoP7qFmlo4unUoVJLB0H/8vfO/7wqTnZxg4kPuji9y3uRzEu0ZKez63OJAUmiGhUrtukC6Uj3w==", - "dev": true, - "requires": { - "lunr": "^2.3.9", - "marked": "^4.2.12", - "minimatch": "^7.1.3", - "shiki": "^0.14.1" - }, - "dependencies": { - "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", - "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true - }, - "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "v8-to-istanbul": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", - "integrity": "sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "dependencies": { - "source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true - } - } - }, - "vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", - "dev": true - }, - "vscode-textmate": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", - "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", - "dev": true - }, - "vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "dev": true - }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "dev": true, - "peer": true, - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "webpack": { - "version": "5.89.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", - "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", - "dev": true, - "peer": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^1.0.0", - "@webassemblyjs/ast": "^1.11.5", - "@webassemblyjs/wasm-edit": "^1.11.5", - "@webassemblyjs/wasm-parser": "^1.11.5", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", - "browserslist": "^4.14.5", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.15.0", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.7", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" - } - }, - "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "dev": true, - "peer": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.7.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.7.0.tgz", - "integrity": "sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==", - "dev": true, - "requires": { - "lodash": "^4.7.0", - "tr46": "^2.1.0", - "webidl-conversions": "^6.1.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrap-ansi-cjs": { - "version": "npm:wrap-ansi@7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "requires": {} - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "requires": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/Common/package.json b/Common/package.json index 9f1dfc86..bac88084 100644 --- a/Common/package.json +++ b/Common/package.json @@ -7,11 +7,14 @@ "types": "build/types/pixelstreamingcommon.d.ts", "sideEffects": false, "scripts": { - "build_proto": "npx protoc --ts_out src/Messages --proto_path protobuf protobuf/signalling_messages.proto", - "compile": "rimraf ./build && tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", - "build": "npm run build_proto && npm run compile", - "test": "echo \"Error: no test specified\" && exit 1", - "lint": "eslint . --ext .js,.jsx,.ts,.tsx" + "compile": "tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", + "build": "rimraf ./build && npm run build-proto && npm run compile", + "build-dev": "npm run build-proto && npm run compile", + "build-proto": "protoc --experimental_allow_proto3_optional --ts_out src/Messages --proto_path protobuf protobuf/signalling_messages.proto", + "build-proto-docs": "protoc --doc_out=docs --doc_opt=markdown,messages.md --proto_path protobuf protobuf/signalling_messages.proto", + "build-docs": "typedoc --tsconfig tsconfig.base.json", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "test": "echo \"Error: no test specified\" && exit 1" }, "devDependencies": { "@types/jest": "27.5.1", @@ -20,13 +23,16 @@ "@typescript-eslint/parser": "^5.16.0", "cspell": "^4.1.0", "eslint": "^8.11.0", + "eslint-plugin-tsdoc": "^0.2.17", "jest": "^27.5.1", "jest-environment-jsdom": "27.5.1", "prettier": "2.8.3", "rimraf": "^5.0.5", "ts-jest": "27.1.5", "ts-loader": "^9.4.2", - "typedoc": "^0.23.24", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", "typescript": "^4.9.4" }, "author": "Epic Games", diff --git a/Common/protobuf/signalling_messages.proto b/Common/protobuf/signalling_messages.proto index a36a0155..6696b1d3 100644 --- a/Common/protobuf/signalling_messages.proto +++ b/Common/protobuf/signalling_messages.proto @@ -1,140 +1,363 @@ syntax = "proto3"; -// intermediate message to determine message type +/** + * This is just a helper message type that allows us to use a "base interface" + * in code to describe that all messages should at least have a 'type field'. + */ message base_message { + // The type of the message. string type = 1; } -message answer { - string type = 1; - string sdp = 2; - optional string playerId = 3; -} - +/** + * This is a user defined structure that is sent as part of the `config` + * message. Left empty here because everything is optional. + */ message peerConnectionOptions { } +/** + * A config message is sent to each connecting peer when it connects to + * describe to them the setup of the signalling server they're + * connecting to. + */ message config { + // Should always be 'config' string type = 1; + // The user defined peer connnection options peerConnectionOptions peerConnectionOptions = 2; + // The signalling protocol version the signalling server is using optional string protocolVersion = 3; } -message dataChannelRequest { - string type = 1; -} - -message disconnectPlayer { +/** + * A request for a new streamer to give itself an ID. The flow for these + * messages should be connect->identify->endpointId->endpointIdConfirm + */ +message identify { + // Should always be 'identify' string type = 1; - string playerId = 2; - optional string reason = 3; } +/** + * Message is consumed by the Signalling Server. Specifies an id for the + * streamer. This is used to uniquely identify multiple streamers connected + * to the same Signalling Server. + * Note: to preserve backward compatibility when Streamer IDs were optional, + * when a Streamer first connects it is assigned a temporary ID which + * allows use of older Streamers if needed. + * Note: Streamer IDs must be unique and so if the ID provided here clashes + * with an existing ID, the ID may be altered slightly (usually just an + * appended number). The streamer will be sent an `endpointIdConfirm` + * message to notify it of it's final ID. + */ message endpointId { + // Should always be 'endpointId' string type = 1; + // The requested ID of the streamer. string id = 2; + // The signalling protocol version the streamer is using optional string protocolVersion = 3; } -message iceCandidateData { - string candidate = 1; - string sdpMid = 2; - int32 sdpMLineIndex = 3; - optional string usernameFragment = 4; +/** + * A response to `endpointId` that will notify the streamer of its final + * ID given. Since streamer IDs must be unique the requested ID may not be + * available and may need to be altered. + */ +message endpointIdConfirm { + // Should always be 'endpointIdConfirm' + string type = 1; + // The final ID of the streamer. + string committedId = 2; } -message iceCandidate { +/** + * Message is used to communicate to players that the streamer it is currently + * subscribed to is changing its ID. This allows players to keep track of it's + * currently subscribed streamer and allow auto reconnects to the correct + * streamer. This happens if a streamer sends an `endpointID` message after it + * already has an ID assigned. (Can happen if it is late to respond to the + * `identify` message and is auto assigned a legacy ID.) + */ +message streamerIdChanged { + // Should always be 'streamerIdChanged' string type = 1; - iceCandidateData candidate = 2; - optional string playerId = 3; + // The new ID of the streamer. + string newID = 2; } -message identify { +/** + * A request to the signalling server to send the player a list of + * available streamers it could possibly subscribe to. + */ +message listStreamers { + // Should always be 'listStreamers' string type = 1; } -message layerPreference { +/** + * Message is a reply to `listStreamers` from a player. Replies with a list of + * currently active streamers connected to this server. + */ +message streamerList { + // Should always be 'streamerList' string type = 1; - int32 spatialLayer = 2; - int32 temporalLayer = 3; - string playerId = 4; + // A list of streamer IDs active on the server. + repeated string ids = 2; } -message listStreamers { +/** + * Message is consumed by the signalling server. Tells the signalling server + * that the player requests to subscribe to the given stream. + */ +message subscribe { + // Should always be 'subscribe' + string type = 1; + // The ID of the streamer the player wishes to subscribe to. + string streamerId = 2; +} + +/** + * Message is consumed by the signalling server. Tells the signalling server + * that the player wishes to unsubscribe from the current stream. The player + * must have previously used the `subscribe` message for this to have any effect. + */ +message unsubscribe { + // Should always be 'unsubscribe' string type = 1; } +/** + * A message sent to a streamer to notify it that a player has just + * subscribed to it. + */ +message playerConnected { + // Should always be 'playerConnected' + string type = 1; + // True if the player should be given a datachannel for stream control purposes. + bool dataChannel = 2; + // True if the player connected is an SFU + bool sfu = 3; + // True if the streamer should send an offer. False if the player is offering to receive + bool sendOffer = 4; + // The ID of the player that connected. + string playerId = 5; +} + +/** + * Message is used to notify a streamer that a player has + * unsubscribed/disconnected from the stream. + */ +message playerDisconnected { + // Should always be 'playerDisconnected' + string type = 1; + // The ID of the player that disconnected. + string playerId = 2; +} + +/** + * An offer message can be an offer of a WebRTC stream, or an offer to + * receive a WebRTC stream, depending on the configuration of the player. + * The default behaviour is that when a player subscribes to a streamer + * the streamer will offer the stream to the new player. + * An alternative configuration exists where a player can be configured + * to offer to receive and in that case the player will subscribe to a + * streamer and then offer to receive the stream. + */ message offer { + // Should always be 'offer' string type = 1; + // The SDP payload from WebRTC string sdp = 2; + // If being sent to a player this should be a valid player ID optional string playerId = 3; + // Indiates that this offer is coming from an SFU. optional bool sfu = 4; } -message peerDataChannelsReady { +/** + * This is a response to an `offer` message. It contains the answer `SDP`. + * Part of the normal subscribe flow. A peer will subscribe to a streamer + * and depending on whether `offer_to_receive` is set, one peer will make + * an offer and the other should answer. + */ +message answer { + // Should always be 'answer' + string type = 1; + // The WebRTC SDP payload + string sdp = 2; + // If being sent to a player this should be set to a valid player ID. + optional string playerId = 3; +} + +/** + * A submessage that contains data from a WebRTC ICE candidate. + */ +message iceCandidateData { + string candidate = 1; + string sdpMid = 2; + int32 sdpMLineIndex = 3; + optional string usernameFragment = 4; +} + +/** + * A single ICE candidate entry from WebRTC. Notifies a peer of a possible + * connection option to another peer. + */ +message iceCandidate { + // Should always be 'iceCandidate' + string type = 1; + // The ICE candidate data from WebRTC + iceCandidateData candidate = 2; + // If being sent to a player this should be a valid player ID. + optional string playerId = 3; +} + +/** + * Message is consumed by the Signalling Server. Requests that the + * signalling server disconnect the given player matching the player ID. + */ +message disconnectPlayer { + // Should always be 'disconnectPlayer' string type = 1; + // The ID of the player to disconnect. + string playerId = 2; + // An optional reason string to send to the player. + optional string reason = 3; } +/** + * A keepalive ping message used to test that the connection is still open. + */ message ping { + // Should always be 'ping' string type = 1; + // The current time int32 time = 2; } -message playerConnected { +/** + * Message is a reply to `ping` from a streamer. Replies with the time from the + * ping message. + */ +message pong { + // Should always be 'pong' string type = 1; - bool dataChannel = 2; - bool sfu = 3; - bool sendOffer = 4; - optional string playerId = 5; + // The echoed time from the ping message + int32 time = 2; } -message playerCount { +/** + * Message is used to notify players when a Streamer disconnects from the + * signalling server. + */ +message streamerDisconnected { + // Should always be 'streamerDisconnected' string type = 1; - int32 count = 2; } -message playerDisconnected { +/** + * Message is forwarded to a connected SFU. Sends a preferred layer index to a + * connected SFU for a specified player. Useful for switching between SFU + * quality layers to force a certain resolution/quality option either as part + * of UX or testing. + */ +message layerPreference { + // Should always be 'layerPreference' string type = 1; - string playerId = 2; + // The requested spatial layer + int32 spatialLayer = 2; + // The requested temporal layer + int32 temporalLayer = 3; + // The player ID this preference refers to + string playerId = 4; } -message pong { +/** + * Message is forwarded to a connected SFU. Tells the SFU that the player + * requests data channels to the streamer. + */ +message dataChannelRequest { + // Should always be 'dataChannelRequest' string type = 1; - int32 time = 2; } -message stats { +/** + * Message is forwarded to a player. Sends information to the player about what + * data channels to use for sending/receiving with the streamer. + */ +message peerDataChannels { + // Should always be 'peerDataChannels' string type = 1; - string data = 2; + // The player ID this message refers to. + string playerId = 2; + // The channel ID to use for sending data. + int32 sendStreamId = 3; + // The channel ID to use for receiving data. + int32 recvStreamId = 4; } -message streamerDataChannels { +/** + * Message is forwarded to a connected SFU. Tells the SFU that the player is + * ready for data channels to be negotiated. + */ +message peerDataChannelsReady { + // Should always be 'peerDataChannelsReady' string type = 1; - int32 sendStreamId = 2; - int32 recvStreamId = 3; - string playerId = 4; } -message streamerDisconnected { +/** + * Message is forwarded to the streamer. Sends a request to the streamer to + * open up data channels for a given player. + */ +message streamerDataChannels { + // Should always be 'streamerDataChannels' string type = 1; - string streamerId = 2; // NEW + // The SFU the player is connected to + string sfuId = 2; + // The channel ID to use for sending data. + int32 sendStreamId = 3; + // The channel ID to use for receiving data. + int32 recvStreamId = 4; } -message streamerList { +/** + * Sent by the SFU to indicate that it is now streaming. + */ +message startStreaming { + // Should always be 'startStreaming' string type = 1; - repeated string ids = 2; // CHECK } -message subscribe { +/** + * Sent by the SFU to indicate that it is now no longer streaming. + */ +message stopStreaming { + // Should always be 'stopStreaming' string type = 1; - string streamerId = 2; } -message unsubscribe { +/** + * DEPRECATED Message is sent to players to indicate how many currently connected players + * there are on this signalling server. (Note: This is mostly old behaviour and + * is not influenced by multi streamers or who is subscribed to what streamer. + * It just reports the number of players it knows about.) + */ +message playerCount { + // Should always be 'playerCount' string type = 1; + // The number of players connected. + int32 count = 2; } -message streamerIdChanged { +/** + * DEPRECATED Message is consumed by the signalling server. Will print out the provided + * stats data on the console. + */ +message stats { + // Should always be 'stats' string type = 1; - string newID = 2; + // The stats data to echo. + string data = 2; } diff --git a/Common/src/Logger/Logger.ts b/Common/src/Logger/Logger.ts index 63105d07..83143839 100644 --- a/Common/src/Logger/Logger.ts +++ b/Common/src/Logger/Logger.ts @@ -1,5 +1,9 @@ // Copyright Epic Games, Inc. All Rights Reserved. +/** + * A basic console logger utilized by the Pixel Streaming frontend to allow + * logging to the browser console. + */ export class Logger { static verboseLogLevel = 5; @@ -35,7 +39,7 @@ export class Logger { * @param verbosity - the verbosity level */ static Log(stack: string, message: string, verbosity?: number) { - if (verbosity > this.verboseLogLevel) { + if (verbosity !== undefined && verbosity > this.verboseLogLevel) { return; } @@ -49,7 +53,7 @@ export class Logger { * @param verbosity - the verbosity level */ static Info(stack: string, message: string, verbosity?: number) { - if (verbosity > this.verboseLogLevel) { + if (verbosity !== undefined && verbosity > this.verboseLogLevel) { return; } @@ -74,6 +78,12 @@ export class Logger { this.CommonLog("Warning", null, message); } + /** + * The common log function that all other log functions call to. + * @param level - the level of this log message. + * @param stack - an optional stack trace string from where the log message was called. + * @param message - the message to be logged. + */ static CommonLog(level: string, stack: null | string, message: string) { if (stack) { console.log(`[${level}] - ${message}\nCaller: ${stack}`); diff --git a/Common/src/Messages/base_message.ts b/Common/src/Messages/base_message.ts index 2471fb68..1fa30a98 100644 --- a/Common/src/Messages/base_message.ts +++ b/Common/src/Messages/base_message.ts @@ -1,3 +1,9 @@ +/** + * All Pixel Streaming messages should adhere to this interface. + */ export interface BaseMessage { + // the type of the message type: string; + // the player id that this message is related to (usually describes the target of the message) + playerId?: string; } diff --git a/Common/src/Messages/message_helpers.ts b/Common/src/Messages/message_helpers.ts index ba8c73e1..25f75d52 100644 --- a/Common/src/Messages/message_helpers.ts +++ b/Common/src/Messages/message_helpers.ts @@ -3,7 +3,15 @@ import { BaseMessage } from './base_message'; import { Logger } from '../Logger/Logger'; import { MessageRegistry } from './message_registry'; -export function createMessage(messageType: IMessageType, params?: any) { +/** + * A helper for creating signalling messages. Takes in optional given parameters and + * includes them in a message object with the 'type' field set properly for the message + * type supplied. + * @param messageType - A message type from MessageRegistry that indicates the type of message to create. + * @param params - An optional object whose fields are added to the newly created message. + * @returns The resulting message object. + */ +export function createMessage(messageType: IMessageType, params?: object) { const message = messageType.create(); message.type = messageType.typeName; if (params) { @@ -12,8 +20,14 @@ export function createMessage(messageType: IMessageType, params?: a return message; } -export function validateMessage(msg: any): IMessageType | null { - let valid: boolean = true; +/** + * Tests that the supplied message is valid. That is contains all expected fields and + * doesn't contain any unknown fields. + * @param msg - The message object to test. + * @returns The message type from MessageRegistry of the supplied message object if it's valid, or null if invalid. + */ +export function validateMessage(msg: BaseMessage): IMessageType | null { + let valid = true; if (!msg.type) { Logger.Error(Logger.GetStackTrace(), `Parsed message has no type. Rejected. ${JSON.stringify(msg)}`); @@ -27,9 +41,9 @@ export function validateMessage(msg: any): IMessageType | null { } if (messageType.fields) { - for (let field of messageType.fields) { + for (const field of messageType.fields) { if (!field.opt) { - if (!msg.hasOwnProperty(field.name)) { + if (!Object.prototype.hasOwnProperty.call(msg, field.name)) { Logger.Error(Logger.GetStackTrace(), `Message "${msg.type}"" is missing required field "${field.name}". Rejected.`); valid = false; } diff --git a/Common/src/Messages/message_registry.ts b/Common/src/Messages/message_registry.ts index 44f4f279..4cad87dd 100644 --- a/Common/src/Messages/message_registry.ts +++ b/Common/src/Messages/message_registry.ts @@ -2,27 +2,38 @@ import { IMessageType } from "@protobuf-ts/runtime"; import * as Messages from './signalling_messages'; import { BaseMessage } from './base_message'; +/** + * A map of all the supported signalling messages in the Pixel Streaming + * signalling protocol. This allows mapping of signalling message names + * to actual message types. + */ export const MessageRegistry: Record> = { 'answer': Messages.answer, 'config': Messages.config, - 'dataChannelRequest': Messages.dataChannelRequest, 'disconnectPlayer': Messages.disconnectPlayer, 'endpointId': Messages.endpointId, + 'endpointIdConfirm': Messages.endpointIdConfirm, 'iceCandidate': Messages.iceCandidate, 'identify': Messages.identify, - 'layerPreference': Messages.layerPreference, 'listStreamers': Messages.listStreamers, 'offer': Messages.offer, - 'peerDataChannelsReady': Messages.peerDataChannelsReady, 'ping': Messages.ping, 'playerConnected': Messages.playerConnected, 'playerCount': Messages.playerCount, 'playerDisconnected': Messages.playerDisconnected, 'pong': Messages.pong, 'stats': Messages.stats, - 'streamerDataChannels': Messages.streamerDataChannels, 'streamerDisconnected': Messages.streamerDisconnected, 'streamerList': Messages.streamerList, 'subscribe': Messages.subscribe, 'unsubscribe': Messages.unsubscribe, + + 'layerPreference': Messages.layerPreference, + + 'dataChannelRequest': Messages.dataChannelRequest, + 'peerDataChannels': Messages.peerDataChannels, + 'peerDataChannelsReady': Messages.peerDataChannelsReady, + 'streamerDataChannels': Messages.streamerDataChannels, + 'startStreaming': Messages.startStreaming, + 'stopStreaming': Messages.stopStreaming, } diff --git a/Common/src/Messages/signalling_messages.ts b/Common/src/Messages/signalling_messages.ts index ec26879e..e568eb87 100644 --- a/Common/src/Messages/signalling_messages.ts +++ b/Common/src/Messages/signalling_messages.ts @@ -11,376 +11,690 @@ import type { PartialMessage } from "@protobuf-ts/runtime"; import { reflectionMergePartial } from "@protobuf-ts/runtime"; import { MessageType } from "@protobuf-ts/runtime"; /** - * intermediate message to determine message type + * * + * This is just a helper message type that allows us to use a "base interface" + * in code to describe that all messages should at least have a 'type field'. * * @generated from protobuf message base_message */ export interface base_message { /** + * The type of the message. + * * @generated from protobuf field: string type = 1; */ type: string; } /** - * @generated from protobuf message answer - */ -export interface answer { - /** - * @generated from protobuf field: string type = 1; - */ - type: string; - /** - * @generated from protobuf field: string sdp = 2; - */ - sdp: string; - /** - * @generated from protobuf field: optional string playerId = 3; - */ - playerId?: string; -} -/** + * * + * This is a user defined structure that is sent as part of the `config` + * message. Left empty here because everything is optional. + * * @generated from protobuf message peerConnectionOptions */ export interface peerConnectionOptions { } /** + * * + * A config message is sent to each connecting peer when it connects to + * describe to them the setup of the signalling server they're + * connecting to. + * * @generated from protobuf message config */ export interface config { /** + * Should always be 'config' + * * @generated from protobuf field: string type = 1; */ type: string; /** + * The user defined peer connnection options + * * @generated from protobuf field: peerConnectionOptions peerConnectionOptions = 2; */ peerConnectionOptions?: peerConnectionOptions; /** + * The signalling protocol version the signalling server is using + * * @generated from protobuf field: optional string protocolVersion = 3; */ protocolVersion?: string; } /** - * @generated from protobuf message dataChannelRequest + * * + * A request for a new streamer to give itself an ID. The flow for these + * messages should be connect->identify->endpointId->endpointIdConfirm + * + * @generated from protobuf message identify */ -export interface dataChannelRequest { +export interface identify { /** + * Should always be 'identify' + * * @generated from protobuf field: string type = 1; */ type: string; } /** - * @generated from protobuf message disconnectPlayer + * * + * Message is consumed by the Signalling Server. Specifies an id for the + * streamer. This is used to uniquely identify multiple streamers connected + * to the same Signalling Server. + * Note: to preserve backward compatibility when Streamer IDs were optional, + * when a Streamer first connects it is assigned a temporary ID which + * allows use of older Streamers if needed. + * Note: Streamer IDs must be unique and so if the ID provided here clashes + * with an existing ID, the ID may be altered slightly (usually just an + * appended number). The streamer will be sent an `endpointIdConfirm` + * message to notify it of it's final ID. + * + * @generated from protobuf message endpointId */ -export interface disconnectPlayer { +export interface endpointId { /** + * Should always be 'endpointId' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: string playerId = 2; + * The requested ID of the streamer. + * + * @generated from protobuf field: string id = 2; */ - playerId: string; + id: string; /** - * @generated from protobuf field: optional string reason = 3; + * The signalling protocol version the streamer is using + * + * @generated from protobuf field: optional string protocolVersion = 3; */ - reason?: string; + protocolVersion?: string; } /** - * @generated from protobuf message endpointId + * * + * A response to `endpointId` that will notify the streamer of its final + * ID given. Since streamer IDs must be unique the requested ID may not be + * available and may need to be altered. + * + * @generated from protobuf message endpointIdConfirm */ -export interface endpointId { +export interface endpointIdConfirm { /** + * Should always be 'endpointIdConfirm' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: string id = 2; - */ - id: string; - /** - * @generated from protobuf field: optional string protocolVersion = 3; + * The final ID of the streamer. + * + * @generated from protobuf field: string committedId = 2; */ - protocolVersion?: string; + committedId: string; } /** - * @generated from protobuf message iceCandidateData + * * + * Message is used to communicate to players that the streamer it is currently + * subscribed to is changing its ID. This allows players to keep track of it's + * currently subscribed streamer and allow auto reconnects to the correct + * streamer. This happens if a streamer sends an `endpointID` message after it + * already has an ID assigned. (Can happen if it is late to respond to the + * `identify` message and is auto assigned a legacy ID.) + * + * @generated from protobuf message streamerIdChanged */ -export interface iceCandidateData { - /** - * @generated from protobuf field: string candidate = 1; - */ - candidate: string; +export interface streamerIdChanged { /** - * @generated from protobuf field: string sdpMid = 2; + * Should always be 'streamerIdChanged' + * + * @generated from protobuf field: string type = 1; */ - sdpMid: string; + type: string; /** - * @generated from protobuf field: int32 sdpMLineIndex = 3; + * The new ID of the streamer. + * + * @generated from protobuf field: string newID = 2; */ - sdpMLineIndex: number; + newID: string; +} +/** + * * + * A request to the signalling server to send the player a list of + * available streamers it could possibly subscribe to. + * + * @generated from protobuf message listStreamers + */ +export interface listStreamers { /** - * @generated from protobuf field: optional string usernameFragment = 4; + * Should always be 'listStreamers' + * + * @generated from protobuf field: string type = 1; */ - usernameFragment?: string; + type: string; } /** - * @generated from protobuf message iceCandidate + * * + * Message is a reply to `listStreamers` from a player. Replies with a list of + * currently active streamers connected to this server. + * + * @generated from protobuf message streamerList */ -export interface iceCandidate { +export interface streamerList { /** + * Should always be 'streamerList' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: iceCandidateData candidate = 2; + * A list of streamer IDs active on the server. + * + * @generated from protobuf field: repeated string ids = 2; */ - candidate?: iceCandidateData; + ids: string[]; +} +/** + * * + * Message is consumed by the signalling server. Tells the signalling server + * that the player requests to subscribe to the given stream. + * + * @generated from protobuf message subscribe + */ +export interface subscribe { /** - * @generated from protobuf field: optional string playerId = 3; + * Should always be 'subscribe' + * + * @generated from protobuf field: string type = 1; */ - playerId?: string; + type: string; + /** + * The ID of the streamer the player wishes to subscribe to. + * + * @generated from protobuf field: string streamerId = 2; + */ + streamerId: string; } /** - * @generated from protobuf message identify + * * + * Message is consumed by the signalling server. Tells the signalling server + * that the player wishes to unsubscribe from the current stream. The player + * must have previously used the `subscribe` message for this to have any effect. + * + * @generated from protobuf message unsubscribe */ -export interface identify { +export interface unsubscribe { /** + * Should always be 'unsubscribe' + * * @generated from protobuf field: string type = 1; */ type: string; } /** - * @generated from protobuf message layerPreference + * * + * A message sent to a streamer to notify it that a player has just + * subscribed to it. + * + * @generated from protobuf message playerConnected */ -export interface layerPreference { +export interface playerConnected { /** + * Should always be 'playerConnected' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: int32 spatialLayer = 2; + * True if the player should be given a datachannel for stream control purposes. + * + * @generated from protobuf field: bool dataChannel = 2; */ - spatialLayer: number; + dataChannel: boolean; /** - * @generated from protobuf field: int32 temporalLayer = 3; + * True if the player connected is an SFU + * + * @generated from protobuf field: bool sfu = 3; */ - temporalLayer: number; + sfu: boolean; /** - * @generated from protobuf field: string playerId = 4; + * True if the streamer should send an offer. False if the player is offering to receive + * + * @generated from protobuf field: bool sendOffer = 4; + */ + sendOffer: boolean; + /** + * The ID of the player that connected. + * + * @generated from protobuf field: string playerId = 5; */ playerId: string; } /** - * @generated from protobuf message listStreamers + * * + * Message is used to notify a streamer that a player has + * unsubscribed/disconnected from the stream. + * + * @generated from protobuf message playerDisconnected */ -export interface listStreamers { +export interface playerDisconnected { /** + * Should always be 'playerDisconnected' + * * @generated from protobuf field: string type = 1; */ type: string; + /** + * The ID of the player that disconnected. + * + * @generated from protobuf field: string playerId = 2; + */ + playerId: string; } /** + * * + * An offer message can be an offer of a WebRTC stream, or an offer to + * receive a WebRTC stream, depending on the configuration of the player. + * The default behaviour is that when a player subscribes to a streamer + * the streamer will offer the stream to the new player. + * An alternative configuration exists where a player can be configured + * to offer to receive and in that case the player will subscribe to a + * streamer and then offer to receive the stream. + * * @generated from protobuf message offer */ export interface offer { /** + * Should always be 'offer' + * * @generated from protobuf field: string type = 1; */ type: string; /** + * The SDP payload from WebRTC + * * @generated from protobuf field: string sdp = 2; */ sdp: string; /** + * If being sent to a player this should be a valid player ID + * * @generated from protobuf field: optional string playerId = 3; */ playerId?: string; /** + * Indiates that this offer is coming from an SFU. + * * @generated from protobuf field: optional bool sfu = 4; */ sfu?: boolean; } /** - * @generated from protobuf message peerDataChannelsReady + * * + * This is a response to an `offer` message. It contains the answer `SDP`. + * Part of the normal subscribe flow. A peer will subscribe to a streamer + * and depending on whether `offer_to_receive` is set, one peer will make + * an offer and the other should answer. + * + * @generated from protobuf message answer */ -export interface peerDataChannelsReady { +export interface answer { /** + * Should always be 'answer' + * * @generated from protobuf field: string type = 1; */ type: string; + /** + * The WebRTC SDP payload + * + * @generated from protobuf field: string sdp = 2; + */ + sdp: string; + /** + * If being sent to a player this should be set to a valid player ID. + * + * @generated from protobuf field: optional string playerId = 3; + */ + playerId?: string; } /** - * @generated from protobuf message ping + * * + * A submessage that contains data from a WebRTC ICE candidate. + * + * @generated from protobuf message iceCandidateData */ -export interface ping { +export interface iceCandidateData { /** - * @generated from protobuf field: string type = 1; + * @generated from protobuf field: string candidate = 1; */ - type: string; + candidate: string; /** - * @generated from protobuf field: int32 time = 2; + * @generated from protobuf field: string sdpMid = 2; */ - time: number; + sdpMid: string; + /** + * @generated from protobuf field: int32 sdpMLineIndex = 3; + */ + sdpMLineIndex: number; + /** + * @generated from protobuf field: optional string usernameFragment = 4; + */ + usernameFragment?: string; } /** - * @generated from protobuf message playerConnected + * * + * A single ICE candidate entry from WebRTC. Notifies a peer of a possible + * connection option to another peer. + * + * @generated from protobuf message iceCandidate */ -export interface playerConnected { +export interface iceCandidate { /** + * Should always be 'iceCandidate' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: bool dataChannel = 2; - */ - dataChannel: boolean; - /** - * @generated from protobuf field: bool sfu = 3; - */ - sfu: boolean; - /** - * @generated from protobuf field: bool sendOffer = 4; + * The ICE candidate data from WebRTC + * + * @generated from protobuf field: iceCandidateData candidate = 2; */ - sendOffer: boolean; + candidate?: iceCandidateData; /** - * @generated from protobuf field: optional string playerId = 5; + * If being sent to a player this should be a valid player ID. + * + * @generated from protobuf field: optional string playerId = 3; */ playerId?: string; } /** - * @generated from protobuf message playerCount + * * + * Message is consumed by the Signalling Server. Requests that the + * signalling server disconnect the given player matching the player ID. + * + * @generated from protobuf message disconnectPlayer */ -export interface playerCount { +export interface disconnectPlayer { /** + * Should always be 'disconnectPlayer' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: int32 count = 2; + * The ID of the player to disconnect. + * + * @generated from protobuf field: string playerId = 2; */ - count: number; + playerId: string; + /** + * An optional reason string to send to the player. + * + * @generated from protobuf field: optional string reason = 3; + */ + reason?: string; } /** - * @generated from protobuf message playerDisconnected + * * + * A keepalive ping message used to test that the connection is still open. + * + * @generated from protobuf message ping */ -export interface playerDisconnected { +export interface ping { /** + * Should always be 'ping' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: string playerId = 2; + * The current time + * + * @generated from protobuf field: int32 time = 2; */ - playerId: string; + time: number; } /** + * * + * Message is a reply to `ping` from a streamer. Replies with the time from the + * ping message. + * * @generated from protobuf message pong */ export interface pong { /** + * Should always be 'pong' + * * @generated from protobuf field: string type = 1; */ type: string; /** + * The echoed time from the ping message + * * @generated from protobuf field: int32 time = 2; */ time: number; } /** - * @generated from protobuf message stats + * * + * Message is used to notify players when a Streamer disconnects from the + * signalling server. + * + * @generated from protobuf message streamerDisconnected */ -export interface stats { +export interface streamerDisconnected { /** + * Should always be 'streamerDisconnected' + * * @generated from protobuf field: string type = 1; */ type: string; - /** - * @generated from protobuf field: string data = 2; - */ - data: string; } /** - * @generated from protobuf message streamerDataChannels + * * + * Message is forwarded to a connected SFU. Sends a preferred layer index to a + * connected SFU for a specified player. Useful for switching between SFU + * quality layers to force a certain resolution/quality option either as part + * of UX or testing. + * + * @generated from protobuf message layerPreference */ -export interface streamerDataChannels { +export interface layerPreference { /** + * Should always be 'layerPreference' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: int32 sendStreamId = 2; + * The requested spatial layer + * + * @generated from protobuf field: int32 spatialLayer = 2; */ - sendStreamId: number; + spatialLayer: number; /** - * @generated from protobuf field: int32 recvStreamId = 3; + * The requested temporal layer + * + * @generated from protobuf field: int32 temporalLayer = 3; */ - recvStreamId: number; + temporalLayer: number; /** + * The player ID this preference refers to + * * @generated from protobuf field: string playerId = 4; */ playerId: string; } /** - * @generated from protobuf message streamerDisconnected + * * + * Message is forwarded to a connected SFU. Tells the SFU that the player + * requests data channels to the streamer. + * + * @generated from protobuf message dataChannelRequest */ -export interface streamerDisconnected { +export interface dataChannelRequest { /** + * Should always be 'dataChannelRequest' + * * @generated from protobuf field: string type = 1; */ type: string; - /** - * @generated from protobuf field: string streamerId = 2; - */ - streamerId: string; // NEW } /** - * @generated from protobuf message streamerList + * * + * Message is forwarded to a player. Sends information to the player about what + * data channels to use for sending/receiving with the streamer. + * + * @generated from protobuf message peerDataChannels */ -export interface streamerList { +export interface peerDataChannels { /** + * Should always be 'peerDataChannels' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: repeated string ids = 2; + * The player ID this message refers to. + * + * @generated from protobuf field: string playerId = 2; */ - ids: string[]; // CHECK -} -/** - * @generated from protobuf message subscribe - */ -export interface subscribe { + playerId: string; /** - * @generated from protobuf field: string type = 1; + * The channel ID to use for sending data. + * + * @generated from protobuf field: int32 sendStreamId = 3; */ - type: string; + sendStreamId: number; /** - * @generated from protobuf field: string streamerId = 2; + * The channel ID to use for receiving data. + * + * @generated from protobuf field: int32 recvStreamId = 4; */ - streamerId: string; + recvStreamId: number; } /** - * @generated from protobuf message unsubscribe + * * + * Message is forwarded to a connected SFU. Tells the SFU that the player is + * ready for data channels to be negotiated. + * + * @generated from protobuf message peerDataChannelsReady */ -export interface unsubscribe { +export interface peerDataChannelsReady { /** + * Should always be 'peerDataChannelsReady' + * * @generated from protobuf field: string type = 1; */ type: string; } /** - * @generated from protobuf message streamerIdChanged + * * + * Message is forwarded to the streamer. Sends a request to the streamer to + * open up data channels for a given player. + * + * @generated from protobuf message streamerDataChannels */ -export interface streamerIdChanged { +export interface streamerDataChannels { /** + * Should always be 'streamerDataChannels' + * * @generated from protobuf field: string type = 1; */ type: string; /** - * @generated from protobuf field: string newID = 2; + * The SFU the player is connected to + * + * @generated from protobuf field: string sfuId = 2; */ - newID: string; + sfuId: string; + /** + * The channel ID to use for sending data. + * + * @generated from protobuf field: int32 sendStreamId = 3; + */ + sendStreamId: number; + /** + * The channel ID to use for receiving data. + * + * @generated from protobuf field: int32 recvStreamId = 4; + */ + recvStreamId: number; +} +/** + * * + * Sent by the SFU to indicate that it is now streaming. + * + * @generated from protobuf message startStreaming + */ +export interface startStreaming { + /** + * Should always be 'startStreaming' + * + * @generated from protobuf field: string type = 1; + */ + type: string; +} +/** + * * + * Sent by the SFU to indicate that it is now no longer streaming. + * + * @generated from protobuf message stopStreaming + */ +export interface stopStreaming { + /** + * Should always be 'stopStreaming' + * + * @generated from protobuf field: string type = 1; + */ + type: string; +} +/** + * * + * DEPRECATED Message is sent to players to indicate how many currently connected players + * there are on this signalling server. (Note: This is mostly old behaviour and + * is not influenced by multi streamers or who is subscribed to what streamer. + * It just reports the number of players it knows about.) + * + * @generated from protobuf message playerCount + */ +export interface playerCount { + /** + * Should always be 'playerCount' + * + * @generated from protobuf field: string type = 1; + */ + type: string; + /** + * The number of players connected. + * + * @generated from protobuf field: int32 count = 2; + */ + count: number; +} +/** + * * + * DEPRECATED Message is consumed by the signalling server. Will print out the provided + * stats data on the console. + * + * @generated from protobuf message stats + */ +export interface stats { + /** + * Should always be 'stats' + * + * @generated from protobuf field: string type = 1; + */ + type: string; + /** + * The stats data to echo. + * + * @generated from protobuf field: string data = 2; + */ + data: string; } // @generated message type with reflection information, may provide speed optimized methods class base_message$Type extends MessageType { @@ -430,23 +744,47 @@ class base_message$Type extends MessageType { */ export const base_message = new base_message$Type(); // @generated message type with reflection information, may provide speed optimized methods -class answer$Type extends MessageType { +class peerConnectionOptions$Type extends MessageType { constructor() { - super("answer", [ + super("peerConnectionOptions", []); + } + create(value?: PartialMessage): peerConnectionOptions { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: peerConnectionOptions): peerConnectionOptions { + return target ?? this.create(); + } + internalBinaryWrite(message: peerConnectionOptions, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message peerConnectionOptions + */ +export const peerConnectionOptions = new peerConnectionOptions$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class config$Type extends MessageType { + constructor() { + super("config", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "sdp", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "peerConnectionOptions", kind: "message", T: () => peerConnectionOptions }, + { no: 3, name: "protocolVersion", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): answer { + create(value?: PartialMessage): config { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.sdp = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: answer): answer { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: config): config { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -454,11 +792,11 @@ class answer$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string sdp */ 2: - message.sdp = reader.string(); + case /* peerConnectionOptions peerConnectionOptions */ 2: + message.peerConnectionOptions = peerConnectionOptions.internalBinaryRead(reader, reader.uint32(), options, message.peerConnectionOptions); break; - case /* optional string playerId */ 3: - message.playerId = reader.string(); + case /* optional string protocolVersion */ 3: + message.protocolVersion = reader.string(); break; default: let u = options.readUnknownField; @@ -471,16 +809,16 @@ class answer$Type extends MessageType { } return message; } - internalBinaryWrite(message: answer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: config, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string sdp = 2; */ - if (message.sdp !== "") - writer.tag(2, WireType.LengthDelimited).string(message.sdp); - /* optional string playerId = 3; */ - if (message.playerId !== undefined) - writer.tag(3, WireType.LengthDelimited).string(message.playerId); + /* peerConnectionOptions peerConnectionOptions = 2; */ + if (message.peerConnectionOptions) + peerConnectionOptions.internalBinaryWrite(message.peerConnectionOptions, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); + /* optional string protocolVersion = 3; */ + if (message.protocolVersion !== undefined) + writer.tag(3, WireType.LengthDelimited).string(message.protocolVersion); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -488,24 +826,46 @@ class answer$Type extends MessageType { } } /** - * @generated MessageType for protobuf message answer + * @generated MessageType for protobuf message config */ -export const answer = new answer$Type(); +export const config = new config$Type(); // @generated message type with reflection information, may provide speed optimized methods -class peerConnectionOptions$Type extends MessageType { +class identify$Type extends MessageType { constructor() { - super("peerConnectionOptions", []); + super("identify", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); } - create(value?: PartialMessage): peerConnectionOptions { + create(value?: PartialMessage): identify { const message = globalThis.Object.create((this.messagePrototype!)); + message.type = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: peerConnectionOptions): peerConnectionOptions { - return target ?? this.create(); + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: identify): identify { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string type */ 1: + message.type = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; } - internalBinaryWrite(message: peerConnectionOptions, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: identify, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string type = 1; */ + if (message.type !== "") + writer.tag(1, WireType.LengthDelimited).string(message.type); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -513,26 +873,196 @@ class peerConnectionOptions$Type extends MessageType { } } /** - * @generated MessageType for protobuf message peerConnectionOptions + * @generated MessageType for protobuf message identify */ -export const peerConnectionOptions = new peerConnectionOptions$Type(); +export const identify = new identify$Type(); // @generated message type with reflection information, may provide speed optimized methods -class config$Type extends MessageType { +class endpointId$Type extends MessageType { constructor() { - super("config", [ + super("endpointId", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "peerConnectionOptions", kind: "message", T: () => peerConnectionOptions }, + { no: 2, name: "id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, { no: 3, name: "protocolVersion", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): config { + create(value?: PartialMessage): endpointId { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.id = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: endpointId): endpointId { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string type */ 1: + message.type = reader.string(); + break; + case /* string id */ 2: + message.id = reader.string(); + break; + case /* optional string protocolVersion */ 3: + message.protocolVersion = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: endpointId, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string type = 1; */ + if (message.type !== "") + writer.tag(1, WireType.LengthDelimited).string(message.type); + /* string id = 2; */ + if (message.id !== "") + writer.tag(2, WireType.LengthDelimited).string(message.id); + /* optional string protocolVersion = 3; */ + if (message.protocolVersion !== undefined) + writer.tag(3, WireType.LengthDelimited).string(message.protocolVersion); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message endpointId + */ +export const endpointId = new endpointId$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class endpointIdConfirm$Type extends MessageType { + constructor() { + super("endpointIdConfirm", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "committedId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): endpointIdConfirm { + const message = globalThis.Object.create((this.messagePrototype!)); + message.type = ""; + message.committedId = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: endpointIdConfirm): endpointIdConfirm { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string type */ 1: + message.type = reader.string(); + break; + case /* string committedId */ 2: + message.committedId = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: endpointIdConfirm, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string type = 1; */ + if (message.type !== "") + writer.tag(1, WireType.LengthDelimited).string(message.type); + /* string committedId = 2; */ + if (message.committedId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.committedId); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message endpointIdConfirm + */ +export const endpointIdConfirm = new endpointIdConfirm$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class streamerIdChanged$Type extends MessageType { + constructor() { + super("streamerIdChanged", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "newID", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): streamerIdChanged { + const message = globalThis.Object.create((this.messagePrototype!)); + message.type = ""; + message.newID = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerIdChanged): streamerIdChanged { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* string type */ 1: + message.type = reader.string(); + break; + case /* string newID */ 2: + message.newID = reader.string(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: streamerIdChanged, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string type = 1; */ + if (message.type !== "") + writer.tag(1, WireType.LengthDelimited).string(message.type); + /* string newID = 2; */ + if (message.newID !== "") + writer.tag(2, WireType.LengthDelimited).string(message.newID); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message streamerIdChanged + */ +export const streamerIdChanged = new streamerIdChanged$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class listStreamers$Type extends MessageType { + constructor() { + super("listStreamers", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + ]); + } + create(value?: PartialMessage): listStreamers { + const message = globalThis.Object.create((this.messagePrototype!)); + message.type = ""; + if (value !== undefined) + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: config): config { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: listStreamers): listStreamers { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -540,12 +1070,6 @@ class config$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* peerConnectionOptions peerConnectionOptions */ 2: - message.peerConnectionOptions = peerConnectionOptions.internalBinaryRead(reader, reader.uint32(), options, message.peerConnectionOptions); - break; - case /* optional string protocolVersion */ 3: - message.protocolVersion = reader.string(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -557,16 +1081,10 @@ class config$Type extends MessageType { } return message; } - internalBinaryWrite(message: config, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: listStreamers, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* peerConnectionOptions peerConnectionOptions = 2; */ - if (message.peerConnectionOptions) - peerConnectionOptions.internalBinaryWrite(message.peerConnectionOptions, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); - /* optional string protocolVersion = 3; */ - if (message.protocolVersion !== undefined) - writer.tag(3, WireType.LengthDelimited).string(message.protocolVersion); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -574,24 +1092,26 @@ class config$Type extends MessageType { } } /** - * @generated MessageType for protobuf message config + * @generated MessageType for protobuf message listStreamers */ -export const config = new config$Type(); +export const listStreamers = new listStreamers$Type(); // @generated message type with reflection information, may provide speed optimized methods -class dataChannelRequest$Type extends MessageType { +class streamerList$Type extends MessageType { constructor() { - super("dataChannelRequest", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("streamerList", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): dataChannelRequest { + create(value?: PartialMessage): streamerList { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.ids = []; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: dataChannelRequest): dataChannelRequest { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerList): streamerList { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -599,6 +1119,9 @@ class dataChannelRequest$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; + case /* repeated string ids */ 2: + message.ids.push(reader.string()); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -610,10 +1133,13 @@ class dataChannelRequest$Type extends MessageType { } return message; } - internalBinaryWrite(message: dataChannelRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: streamerList, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); + /* repeated string ids = 2; */ + for (let i = 0; i < message.ids.length; i++) + writer.tag(2, WireType.LengthDelimited).string(message.ids[i]); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -621,27 +1147,26 @@ class dataChannelRequest$Type extends MessageType { } } /** - * @generated MessageType for protobuf message dataChannelRequest + * @generated MessageType for protobuf message streamerList */ -export const dataChannelRequest = new dataChannelRequest$Type(); +export const streamerList = new streamerList$Type(); // @generated message type with reflection information, may provide speed optimized methods -class disconnectPlayer$Type extends MessageType { +class subscribe$Type extends MessageType { constructor() { - super("disconnectPlayer", [ + super("subscribe", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "reason", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "streamerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): disconnectPlayer { + create(value?: PartialMessage): subscribe { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.playerId = ""; + message.streamerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: disconnectPlayer): disconnectPlayer { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: subscribe): subscribe { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -649,11 +1174,8 @@ class disconnectPlayer$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string playerId */ 2: - message.playerId = reader.string(); - break; - case /* optional string reason */ 3: - message.reason = reader.string(); + case /* string streamerId */ 2: + message.streamerId = reader.string(); break; default: let u = options.readUnknownField; @@ -666,16 +1188,13 @@ class disconnectPlayer$Type extends MessageType { } return message; } - internalBinaryWrite(message: disconnectPlayer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: subscribe, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string playerId = 2; */ - if (message.playerId !== "") - writer.tag(2, WireType.LengthDelimited).string(message.playerId); - /* optional string reason = 3; */ - if (message.reason !== undefined) - writer.tag(3, WireType.LengthDelimited).string(message.reason); + /* string streamerId = 2; */ + if (message.streamerId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.streamerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -683,27 +1202,24 @@ class disconnectPlayer$Type extends MessageType { } } /** - * @generated MessageType for protobuf message disconnectPlayer + * @generated MessageType for protobuf message subscribe */ -export const disconnectPlayer = new disconnectPlayer$Type(); +export const subscribe = new subscribe$Type(); // @generated message type with reflection information, may provide speed optimized methods -class endpointId$Type extends MessageType { +class unsubscribe$Type extends MessageType { constructor() { - super("endpointId", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "id", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "protocolVersion", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + super("unsubscribe", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): endpointId { + create(value?: PartialMessage): unsubscribe { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.id = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: endpointId): endpointId { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: unsubscribe): unsubscribe { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -711,12 +1227,6 @@ class endpointId$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string id */ 2: - message.id = reader.string(); - break; - case /* optional string protocolVersion */ 3: - message.protocolVersion = reader.string(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -728,16 +1238,10 @@ class endpointId$Type extends MessageType { } return message; } - internalBinaryWrite(message: endpointId, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: unsubscribe, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string id = 2; */ - if (message.id !== "") - writer.tag(2, WireType.LengthDelimited).string(message.id); - /* optional string protocolVersion = 3; */ - if (message.protocolVersion !== undefined) - writer.tag(3, WireType.LengthDelimited).string(message.protocolVersion); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -745,44 +1249,50 @@ class endpointId$Type extends MessageType { } } /** - * @generated MessageType for protobuf message endpointId + * @generated MessageType for protobuf message unsubscribe */ -export const endpointId = new endpointId$Type(); +export const unsubscribe = new unsubscribe$Type(); // @generated message type with reflection information, may provide speed optimized methods -class iceCandidateData$Type extends MessageType { +class playerConnected$Type extends MessageType { constructor() { - super("iceCandidateData", [ - { no: 1, name: "candidate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "sdpMid", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "sdpMLineIndex", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 4, name: "usernameFragment", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + super("playerConnected", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "dataChannel", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 3, name: "sfu", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 4, name: "sendOffer", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, + { no: 5, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): iceCandidateData { + create(value?: PartialMessage): playerConnected { const message = globalThis.Object.create((this.messagePrototype!)); - message.candidate = ""; - message.sdpMid = ""; - message.sdpMLineIndex = 0; + message.type = ""; + message.dataChannel = false; + message.sfu = false; + message.sendOffer = false; + message.playerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: iceCandidateData): iceCandidateData { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerConnected): playerConnected { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); switch (fieldNo) { - case /* string candidate */ 1: - message.candidate = reader.string(); + case /* string type */ 1: + message.type = reader.string(); break; - case /* string sdpMid */ 2: - message.sdpMid = reader.string(); + case /* bool dataChannel */ 2: + message.dataChannel = reader.bool(); break; - case /* int32 sdpMLineIndex */ 3: - message.sdpMLineIndex = reader.int32(); + case /* bool sfu */ 3: + message.sfu = reader.bool(); break; - case /* optional string usernameFragment */ 4: - message.usernameFragment = reader.string(); + case /* bool sendOffer */ 4: + message.sendOffer = reader.bool(); + break; + case /* string playerId */ 5: + message.playerId = reader.string(); break; default: let u = options.readUnknownField; @@ -795,19 +1305,22 @@ class iceCandidateData$Type extends MessageType { } return message; } - internalBinaryWrite(message: iceCandidateData, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string candidate = 1; */ - if (message.candidate !== "") - writer.tag(1, WireType.LengthDelimited).string(message.candidate); - /* string sdpMid = 2; */ - if (message.sdpMid !== "") - writer.tag(2, WireType.LengthDelimited).string(message.sdpMid); - /* int32 sdpMLineIndex = 3; */ - if (message.sdpMLineIndex !== 0) - writer.tag(3, WireType.Varint).int32(message.sdpMLineIndex); - /* optional string usernameFragment = 4; */ - if (message.usernameFragment !== undefined) - writer.tag(4, WireType.LengthDelimited).string(message.usernameFragment); + internalBinaryWrite(message: playerConnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string type = 1; */ + if (message.type !== "") + writer.tag(1, WireType.LengthDelimited).string(message.type); + /* bool dataChannel = 2; */ + if (message.dataChannel !== false) + writer.tag(2, WireType.Varint).bool(message.dataChannel); + /* bool sfu = 3; */ + if (message.sfu !== false) + writer.tag(3, WireType.Varint).bool(message.sfu); + /* bool sendOffer = 4; */ + if (message.sendOffer !== false) + writer.tag(4, WireType.Varint).bool(message.sendOffer); + /* string playerId = 5; */ + if (message.playerId !== "") + writer.tag(5, WireType.LengthDelimited).string(message.playerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -815,26 +1328,26 @@ class iceCandidateData$Type extends MessageType { } } /** - * @generated MessageType for protobuf message iceCandidateData + * @generated MessageType for protobuf message playerConnected */ -export const iceCandidateData = new iceCandidateData$Type(); +export const playerConnected = new playerConnected$Type(); // @generated message type with reflection information, may provide speed optimized methods -class iceCandidate$Type extends MessageType { +class playerDisconnected$Type extends MessageType { constructor() { - super("iceCandidate", [ + super("playerDisconnected", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "candidate", kind: "message", T: () => iceCandidateData }, - { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): iceCandidate { + create(value?: PartialMessage): playerDisconnected { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.playerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: iceCandidate): iceCandidate { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerDisconnected): playerDisconnected { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -842,10 +1355,7 @@ class iceCandidate$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* iceCandidateData candidate */ 2: - message.candidate = iceCandidateData.internalBinaryRead(reader, reader.uint32(), options, message.candidate); - break; - case /* optional string playerId */ 3: + case /* string playerId */ 2: message.playerId = reader.string(); break; default: @@ -859,16 +1369,13 @@ class iceCandidate$Type extends MessageType { } return message; } - internalBinaryWrite(message: iceCandidate, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: playerDisconnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* iceCandidateData candidate = 2; */ - if (message.candidate) - iceCandidateData.internalBinaryWrite(message.candidate, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); - /* optional string playerId = 3; */ - if (message.playerId !== undefined) - writer.tag(3, WireType.LengthDelimited).string(message.playerId); + /* string playerId = 2; */ + if (message.playerId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.playerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -876,24 +1383,28 @@ class iceCandidate$Type extends MessageType { } } /** - * @generated MessageType for protobuf message iceCandidate + * @generated MessageType for protobuf message playerDisconnected */ -export const iceCandidate = new iceCandidate$Type(); +export const playerDisconnected = new playerDisconnected$Type(); // @generated message type with reflection information, may provide speed optimized methods -class identify$Type extends MessageType { +class offer$Type extends MessageType { constructor() { - super("identify", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("offer", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "sdp", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }, + { no: 4, name: "sfu", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ } ]); } - create(value?: PartialMessage): identify { + create(value?: PartialMessage): offer { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.sdp = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: identify): identify { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: offer): offer { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -901,6 +1412,15 @@ class identify$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; + case /* string sdp */ 2: + message.sdp = reader.string(); + break; + case /* optional string playerId */ 3: + message.playerId = reader.string(); + break; + case /* optional bool sfu */ 4: + message.sfu = reader.bool(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -912,10 +1432,19 @@ class identify$Type extends MessageType { } return message; } - internalBinaryWrite(message: identify, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: offer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); + /* string sdp = 2; */ + if (message.sdp !== "") + writer.tag(2, WireType.LengthDelimited).string(message.sdp); + /* optional string playerId = 3; */ + if (message.playerId !== undefined) + writer.tag(3, WireType.LengthDelimited).string(message.playerId); + /* optional bool sfu = 4; */ + if (message.sfu !== undefined) + writer.tag(4, WireType.Varint).bool(message.sfu); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -923,30 +1452,27 @@ class identify$Type extends MessageType { } } /** - * @generated MessageType for protobuf message identify + * @generated MessageType for protobuf message offer */ -export const identify = new identify$Type(); +export const offer = new offer$Type(); // @generated message type with reflection information, may provide speed optimized methods -class layerPreference$Type extends MessageType { +class answer$Type extends MessageType { constructor() { - super("layerPreference", [ + super("answer", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "spatialLayer", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 3, name: "temporalLayer", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 4, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "sdp", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): layerPreference { + create(value?: PartialMessage): answer { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.spatialLayer = 0; - message.temporalLayer = 0; - message.playerId = ""; + message.sdp = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: layerPreference): layerPreference { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: answer): answer { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -954,13 +1480,10 @@ class layerPreference$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* int32 spatialLayer */ 2: - message.spatialLayer = reader.int32(); - break; - case /* int32 temporalLayer */ 3: - message.temporalLayer = reader.int32(); + case /* string sdp */ 2: + message.sdp = reader.string(); break; - case /* string playerId */ 4: + case /* optional string playerId */ 3: message.playerId = reader.string(); break; default: @@ -974,19 +1497,16 @@ class layerPreference$Type extends MessageType { } return message; } - internalBinaryWrite(message: layerPreference, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: answer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* int32 spatialLayer = 2; */ - if (message.spatialLayer !== 0) - writer.tag(2, WireType.Varint).int32(message.spatialLayer); - /* int32 temporalLayer = 3; */ - if (message.temporalLayer !== 0) - writer.tag(3, WireType.Varint).int32(message.temporalLayer); - /* string playerId = 4; */ - if (message.playerId !== "") - writer.tag(4, WireType.LengthDelimited).string(message.playerId); + /* string sdp = 2; */ + if (message.sdp !== "") + writer.tag(2, WireType.LengthDelimited).string(message.sdp); + /* optional string playerId = 3; */ + if (message.playerId !== undefined) + writer.tag(3, WireType.LengthDelimited).string(message.playerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -994,30 +1514,44 @@ class layerPreference$Type extends MessageType { } } /** - * @generated MessageType for protobuf message layerPreference + * @generated MessageType for protobuf message answer */ -export const layerPreference = new layerPreference$Type(); +export const answer = new answer$Type(); // @generated message type with reflection information, may provide speed optimized methods -class listStreamers$Type extends MessageType { +class iceCandidateData$Type extends MessageType { constructor() { - super("listStreamers", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("iceCandidateData", [ + { no: 1, name: "candidate", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "sdpMid", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "sdpMLineIndex", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 4, name: "usernameFragment", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): listStreamers { + create(value?: PartialMessage): iceCandidateData { const message = globalThis.Object.create((this.messagePrototype!)); - message.type = ""; + message.candidate = ""; + message.sdpMid = ""; + message.sdpMLineIndex = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: listStreamers): listStreamers { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: iceCandidateData): iceCandidateData { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); switch (fieldNo) { - case /* string type */ 1: - message.type = reader.string(); + case /* string candidate */ 1: + message.candidate = reader.string(); + break; + case /* string sdpMid */ 2: + message.sdpMid = reader.string(); + break; + case /* int32 sdpMLineIndex */ 3: + message.sdpMLineIndex = reader.int32(); + break; + case /* optional string usernameFragment */ 4: + message.usernameFragment = reader.string(); break; default: let u = options.readUnknownField; @@ -1030,10 +1564,19 @@ class listStreamers$Type extends MessageType { } return message; } - internalBinaryWrite(message: listStreamers, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { - /* string type = 1; */ - if (message.type !== "") - writer.tag(1, WireType.LengthDelimited).string(message.type); + internalBinaryWrite(message: iceCandidateData, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* string candidate = 1; */ + if (message.candidate !== "") + writer.tag(1, WireType.LengthDelimited).string(message.candidate); + /* string sdpMid = 2; */ + if (message.sdpMid !== "") + writer.tag(2, WireType.LengthDelimited).string(message.sdpMid); + /* int32 sdpMLineIndex = 3; */ + if (message.sdpMLineIndex !== 0) + writer.tag(3, WireType.Varint).int32(message.sdpMLineIndex); + /* optional string usernameFragment = 4; */ + if (message.usernameFragment !== undefined) + writer.tag(4, WireType.LengthDelimited).string(message.usernameFragment); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1041,28 +1584,26 @@ class listStreamers$Type extends MessageType { } } /** - * @generated MessageType for protobuf message listStreamers + * @generated MessageType for protobuf message iceCandidateData */ -export const listStreamers = new listStreamers$Type(); +export const iceCandidateData = new iceCandidateData$Type(); // @generated message type with reflection information, may provide speed optimized methods -class offer$Type extends MessageType { +class iceCandidate$Type extends MessageType { constructor() { - super("offer", [ + super("iceCandidate", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "sdp", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }, - { no: 4, name: "sfu", kind: "scalar", opt: true, T: 8 /*ScalarType.BOOL*/ } + { no: 2, name: "candidate", kind: "message", T: () => iceCandidateData }, + { no: 3, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): offer { + create(value?: PartialMessage): iceCandidate { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.sdp = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: offer): offer { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: iceCandidate): iceCandidate { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1070,15 +1611,12 @@ class offer$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string sdp */ 2: - message.sdp = reader.string(); + case /* iceCandidateData candidate */ 2: + message.candidate = iceCandidateData.internalBinaryRead(reader, reader.uint32(), options, message.candidate); break; case /* optional string playerId */ 3: message.playerId = reader.string(); break; - case /* optional bool sfu */ 4: - message.sfu = reader.bool(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1090,19 +1628,16 @@ class offer$Type extends MessageType { } return message; } - internalBinaryWrite(message: offer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: iceCandidate, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string sdp = 2; */ - if (message.sdp !== "") - writer.tag(2, WireType.LengthDelimited).string(message.sdp); + /* iceCandidateData candidate = 2; */ + if (message.candidate) + iceCandidateData.internalBinaryWrite(message.candidate, writer.tag(2, WireType.LengthDelimited).fork(), options).join(); /* optional string playerId = 3; */ if (message.playerId !== undefined) writer.tag(3, WireType.LengthDelimited).string(message.playerId); - /* optional bool sfu = 4; */ - if (message.sfu !== undefined) - writer.tag(4, WireType.Varint).bool(message.sfu); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1110,24 +1645,27 @@ class offer$Type extends MessageType { } } /** - * @generated MessageType for protobuf message offer + * @generated MessageType for protobuf message iceCandidate */ -export const offer = new offer$Type(); +export const iceCandidate = new iceCandidate$Type(); // @generated message type with reflection information, may provide speed optimized methods -class peerDataChannelsReady$Type extends MessageType { +class disconnectPlayer$Type extends MessageType { constructor() { - super("peerDataChannelsReady", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("disconnectPlayer", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "reason", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): peerDataChannelsReady { + create(value?: PartialMessage): disconnectPlayer { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.playerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: peerDataChannelsReady): peerDataChannelsReady { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: disconnectPlayer): disconnectPlayer { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1135,6 +1673,12 @@ class peerDataChannelsReady$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; + case /* string playerId */ 2: + message.playerId = reader.string(); + break; + case /* optional string reason */ 3: + message.reason = reader.string(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -1146,10 +1690,16 @@ class peerDataChannelsReady$Type extends MessageType { } return message; } - internalBinaryWrite(message: peerDataChannelsReady, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: disconnectPlayer, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); + /* string playerId = 2; */ + if (message.playerId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.playerId); + /* optional string reason = 3; */ + if (message.reason !== undefined) + writer.tag(3, WireType.LengthDelimited).string(message.reason); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1157,9 +1707,9 @@ class peerDataChannelsReady$Type extends MessageType { } } /** - * @generated MessageType for protobuf message peerDataChannelsReady + * @generated MessageType for protobuf message disconnectPlayer */ -export const peerDataChannelsReady = new peerDataChannelsReady$Type(); +export const disconnectPlayer = new disconnectPlayer$Type(); // @generated message type with reflection information, may provide speed optimized methods class ping$Type extends MessageType { constructor() { @@ -1216,27 +1766,22 @@ class ping$Type extends MessageType { */ export const ping = new ping$Type(); // @generated message type with reflection information, may provide speed optimized methods -class playerConnected$Type extends MessageType { +class pong$Type extends MessageType { constructor() { - super("playerConnected", [ + super("pong", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "dataChannel", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 3, name: "sfu", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 4, name: "sendOffer", kind: "scalar", T: 8 /*ScalarType.BOOL*/ }, - { no: 5, name: "playerId", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "time", kind: "scalar", T: 5 /*ScalarType.INT32*/ } ]); } - create(value?: PartialMessage): playerConnected { + create(value?: PartialMessage): pong { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.dataChannel = false; - message.sfu = false; - message.sendOffer = false; + message.time = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerConnected): playerConnected { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: pong): pong { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1244,17 +1789,8 @@ class playerConnected$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* bool dataChannel */ 2: - message.dataChannel = reader.bool(); - break; - case /* bool sfu */ 3: - message.sfu = reader.bool(); - break; - case /* bool sendOffer */ 4: - message.sendOffer = reader.bool(); - break; - case /* optional string playerId */ 5: - message.playerId = reader.string(); + case /* int32 time */ 2: + message.time = reader.int32(); break; default: let u = options.readUnknownField; @@ -1267,22 +1803,13 @@ class playerConnected$Type extends MessageType { } return message; } - internalBinaryWrite(message: playerConnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: pong, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* bool dataChannel = 2; */ - if (message.dataChannel !== false) - writer.tag(2, WireType.Varint).bool(message.dataChannel); - /* bool sfu = 3; */ - if (message.sfu !== false) - writer.tag(3, WireType.Varint).bool(message.sfu); - /* bool sendOffer = 4; */ - if (message.sendOffer !== false) - writer.tag(4, WireType.Varint).bool(message.sendOffer); - /* optional string playerId = 5; */ - if (message.playerId !== undefined) - writer.tag(5, WireType.LengthDelimited).string(message.playerId); + /* int32 time = 2; */ + if (message.time !== 0) + writer.tag(2, WireType.Varint).int32(message.time); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1290,26 +1817,24 @@ class playerConnected$Type extends MessageType { } } /** - * @generated MessageType for protobuf message playerConnected + * @generated MessageType for protobuf message pong */ -export const playerConnected = new playerConnected$Type(); +export const pong = new pong$Type(); // @generated message type with reflection information, may provide speed optimized methods -class playerCount$Type extends MessageType { +class streamerDisconnected$Type extends MessageType { constructor() { - super("playerCount", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "count", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + super("streamerDisconnected", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): playerCount { + create(value?: PartialMessage): streamerDisconnected { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.count = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerCount): playerCount { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerDisconnected): streamerDisconnected { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1317,9 +1842,6 @@ class playerCount$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* int32 count */ 2: - message.count = reader.int32(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1331,13 +1853,10 @@ class playerCount$Type extends MessageType { } return message; } - internalBinaryWrite(message: playerCount, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: streamerDisconnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* int32 count = 2; */ - if (message.count !== 0) - writer.tag(2, WireType.Varint).int32(message.count); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1345,26 +1864,30 @@ class playerCount$Type extends MessageType { } } /** - * @generated MessageType for protobuf message playerCount + * @generated MessageType for protobuf message streamerDisconnected */ -export const playerCount = new playerCount$Type(); +export const streamerDisconnected = new streamerDisconnected$Type(); // @generated message type with reflection information, may provide speed optimized methods -class playerDisconnected$Type extends MessageType { +class layerPreference$Type extends MessageType { constructor() { - super("playerDisconnected", [ + super("layerPreference", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "spatialLayer", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 3, name: "temporalLayer", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 4, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): playerDisconnected { + create(value?: PartialMessage): layerPreference { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.spatialLayer = 0; + message.temporalLayer = 0; message.playerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerDisconnected): playerDisconnected { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: layerPreference): layerPreference { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1372,7 +1895,13 @@ class playerDisconnected$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string playerId */ 2: + case /* int32 spatialLayer */ 2: + message.spatialLayer = reader.int32(); + break; + case /* int32 temporalLayer */ 3: + message.temporalLayer = reader.int32(); + break; + case /* string playerId */ 4: message.playerId = reader.string(); break; default: @@ -1386,13 +1915,19 @@ class playerDisconnected$Type extends MessageType { } return message; } - internalBinaryWrite(message: playerDisconnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: layerPreference, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string playerId = 2; */ + /* int32 spatialLayer = 2; */ + if (message.spatialLayer !== 0) + writer.tag(2, WireType.Varint).int32(message.spatialLayer); + /* int32 temporalLayer = 3; */ + if (message.temporalLayer !== 0) + writer.tag(3, WireType.Varint).int32(message.temporalLayer); + /* string playerId = 4; */ if (message.playerId !== "") - writer.tag(2, WireType.LengthDelimited).string(message.playerId); + writer.tag(4, WireType.LengthDelimited).string(message.playerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1400,26 +1935,24 @@ class playerDisconnected$Type extends MessageType { } } /** - * @generated MessageType for protobuf message playerDisconnected + * @generated MessageType for protobuf message layerPreference */ -export const playerDisconnected = new playerDisconnected$Type(); +export const layerPreference = new layerPreference$Type(); // @generated message type with reflection information, may provide speed optimized methods -class pong$Type extends MessageType { +class dataChannelRequest$Type extends MessageType { constructor() { - super("pong", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "time", kind: "scalar", T: 5 /*ScalarType.INT32*/ } + super("dataChannelRequest", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): pong { + create(value?: PartialMessage): dataChannelRequest { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.time = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: pong): pong { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: dataChannelRequest): dataChannelRequest { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1427,9 +1960,6 @@ class pong$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* int32 time */ 2: - message.time = reader.int32(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1441,13 +1971,10 @@ class pong$Type extends MessageType { } return message; } - internalBinaryWrite(message: pong, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: dataChannelRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* int32 time = 2; */ - if (message.time !== 0) - writer.tag(2, WireType.Varint).int32(message.time); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1455,26 +1982,30 @@ class pong$Type extends MessageType { } } /** - * @generated MessageType for protobuf message pong + * @generated MessageType for protobuf message dataChannelRequest */ -export const pong = new pong$Type(); +export const dataChannelRequest = new dataChannelRequest$Type(); // @generated message type with reflection information, may provide speed optimized methods -class stats$Type extends MessageType { +class peerDataChannels$Type extends MessageType { constructor() { - super("stats", [ + super("peerDataChannels", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "data", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "sendStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 4, name: "recvStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ } ]); } - create(value?: PartialMessage): stats { + create(value?: PartialMessage): peerDataChannels { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.data = ""; + message.playerId = ""; + message.sendStreamId = 0; + message.recvStreamId = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: stats): stats { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: peerDataChannels): peerDataChannels { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1482,8 +2013,14 @@ class stats$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string data */ 2: - message.data = reader.string(); + case /* string playerId */ 2: + message.playerId = reader.string(); + break; + case /* int32 sendStreamId */ 3: + message.sendStreamId = reader.int32(); + break; + case /* int32 recvStreamId */ 4: + message.recvStreamId = reader.int32(); break; default: let u = options.readUnknownField; @@ -1496,13 +2033,19 @@ class stats$Type extends MessageType { } return message; } - internalBinaryWrite(message: stats, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: peerDataChannels, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string data = 2; */ - if (message.data !== "") - writer.tag(2, WireType.LengthDelimited).string(message.data); + /* string playerId = 2; */ + if (message.playerId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.playerId); + /* int32 sendStreamId = 3; */ + if (message.sendStreamId !== 0) + writer.tag(3, WireType.Varint).int32(message.sendStreamId); + /* int32 recvStreamId = 4; */ + if (message.recvStreamId !== 0) + writer.tag(4, WireType.Varint).int32(message.recvStreamId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1510,30 +2053,24 @@ class stats$Type extends MessageType { } } /** - * @generated MessageType for protobuf message stats + * @generated MessageType for protobuf message peerDataChannels */ -export const stats = new stats$Type(); +export const peerDataChannels = new peerDataChannels$Type(); // @generated message type with reflection information, may provide speed optimized methods -class streamerDataChannels$Type extends MessageType { +class peerDataChannelsReady$Type extends MessageType { constructor() { - super("streamerDataChannels", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "sendStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 3, name: "recvStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, - { no: 4, name: "playerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("peerDataChannelsReady", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): streamerDataChannels { + create(value?: PartialMessage): peerDataChannelsReady { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.sendStreamId = 0; - message.recvStreamId = 0; - message.playerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerDataChannels): streamerDataChannels { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: peerDataChannelsReady): peerDataChannelsReady { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1541,15 +2078,6 @@ class streamerDataChannels$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* int32 sendStreamId */ 2: - message.sendStreamId = reader.int32(); - break; - case /* int32 recvStreamId */ 3: - message.recvStreamId = reader.int32(); - break; - case /* string playerId */ 4: - message.playerId = reader.string(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1561,19 +2089,10 @@ class streamerDataChannels$Type extends MessageType { } return message; } - internalBinaryWrite(message: streamerDataChannels, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: peerDataChannelsReady, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* int32 sendStreamId = 2; */ - if (message.sendStreamId !== 0) - writer.tag(2, WireType.Varint).int32(message.sendStreamId); - /* int32 recvStreamId = 3; */ - if (message.recvStreamId !== 0) - writer.tag(3, WireType.Varint).int32(message.recvStreamId); - /* string playerId = 4; */ - if (message.playerId !== "") - writer.tag(4, WireType.LengthDelimited).string(message.playerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1581,26 +2100,30 @@ class streamerDataChannels$Type extends MessageType { } } /** - * @generated MessageType for protobuf message streamerDataChannels + * @generated MessageType for protobuf message peerDataChannelsReady */ -export const streamerDataChannels = new streamerDataChannels$Type(); +export const peerDataChannelsReady = new peerDataChannelsReady$Type(); // @generated message type with reflection information, may provide speed optimized methods -class streamerDisconnected$Type extends MessageType { +class streamerDataChannels$Type extends MessageType { constructor() { - super("streamerDisconnected", [ + super("streamerDataChannels", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "streamerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "sfuId", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 3, name: "sendStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ }, + { no: 4, name: "recvStreamId", kind: "scalar", T: 5 /*ScalarType.INT32*/ } ]); } - create(value?: PartialMessage): streamerDisconnected { + create(value?: PartialMessage): streamerDataChannels { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.streamerId = ""; + message.sfuId = ""; + message.sendStreamId = 0; + message.recvStreamId = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerDisconnected): streamerDisconnected { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerDataChannels): streamerDataChannels { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1608,8 +2131,14 @@ class streamerDisconnected$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string streamerId */ 2: - message.streamerId = reader.string(); + case /* string sfuId */ 2: + message.sfuId = reader.string(); + break; + case /* int32 sendStreamId */ 3: + message.sendStreamId = reader.int32(); + break; + case /* int32 recvStreamId */ 4: + message.recvStreamId = reader.int32(); break; default: let u = options.readUnknownField; @@ -1622,13 +2151,19 @@ class streamerDisconnected$Type extends MessageType { } return message; } - internalBinaryWrite(message: streamerDisconnected, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: streamerDataChannels, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string streamerId = 2; */ - if (message.streamerId !== "") - writer.tag(2, WireType.LengthDelimited).string(message.streamerId); + /* string sfuId = 2; */ + if (message.sfuId !== "") + writer.tag(2, WireType.LengthDelimited).string(message.sfuId); + /* int32 sendStreamId = 3; */ + if (message.sendStreamId !== 0) + writer.tag(3, WireType.Varint).int32(message.sendStreamId); + /* int32 recvStreamId = 4; */ + if (message.recvStreamId !== 0) + writer.tag(4, WireType.Varint).int32(message.recvStreamId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1636,26 +2171,24 @@ class streamerDisconnected$Type extends MessageType { } } /** - * @generated MessageType for protobuf message streamerDisconnected + * @generated MessageType for protobuf message streamerDataChannels */ -export const streamerDisconnected = new streamerDisconnected$Type(); +export const streamerDataChannels = new streamerDataChannels$Type(); // @generated message type with reflection information, may provide speed optimized methods -class streamerList$Type extends MessageType { +class startStreaming$Type extends MessageType { constructor() { - super("streamerList", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ } + super("startStreaming", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): streamerList { + create(value?: PartialMessage): startStreaming { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.ids = []; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerList): streamerList { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: startStreaming): startStreaming { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1663,9 +2196,6 @@ class streamerList$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* repeated string ids */ 2: - message.ids.push(reader.string()); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1677,13 +2207,10 @@ class streamerList$Type extends MessageType { } return message; } - internalBinaryWrite(message: streamerList, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: startStreaming, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* repeated string ids = 2; */ - for (let i = 0; i < message.ids.length; i++) - writer.tag(2, WireType.LengthDelimited).string(message.ids[i]); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1691,26 +2218,24 @@ class streamerList$Type extends MessageType { } } /** - * @generated MessageType for protobuf message streamerList + * @generated MessageType for protobuf message startStreaming */ -export const streamerList = new streamerList$Type(); +export const startStreaming = new startStreaming$Type(); // @generated message type with reflection information, may provide speed optimized methods -class subscribe$Type extends MessageType { +class stopStreaming$Type extends MessageType { constructor() { - super("subscribe", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "streamerId", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("stopStreaming", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): subscribe { + create(value?: PartialMessage): stopStreaming { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.streamerId = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: subscribe): subscribe { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: stopStreaming): stopStreaming { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1718,9 +2243,6 @@ class subscribe$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string streamerId */ 2: - message.streamerId = reader.string(); - break; default: let u = options.readUnknownField; if (u === "throw") @@ -1732,13 +2254,10 @@ class subscribe$Type extends MessageType { } return message; } - internalBinaryWrite(message: subscribe, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: stopStreaming, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string streamerId = 2; */ - if (message.streamerId !== "") - writer.tag(2, WireType.LengthDelimited).string(message.streamerId); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1746,24 +2265,26 @@ class subscribe$Type extends MessageType { } } /** - * @generated MessageType for protobuf message subscribe + * @generated MessageType for protobuf message stopStreaming */ -export const subscribe = new subscribe$Type(); +export const stopStreaming = new stopStreaming$Type(); // @generated message type with reflection information, may provide speed optimized methods -class unsubscribe$Type extends MessageType { +class playerCount$Type extends MessageType { constructor() { - super("unsubscribe", [ - { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + super("playerCount", [ + { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, + { no: 2, name: "count", kind: "scalar", T: 5 /*ScalarType.INT32*/ } ]); } - create(value?: PartialMessage): unsubscribe { + create(value?: PartialMessage): playerCount { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; + message.count = 0; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: unsubscribe): unsubscribe { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: playerCount): playerCount { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1771,6 +2292,9 @@ class unsubscribe$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; + case /* int32 count */ 2: + message.count = reader.int32(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -1782,10 +2306,13 @@ class unsubscribe$Type extends MessageType { } return message; } - internalBinaryWrite(message: unsubscribe, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: playerCount, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); + /* int32 count = 2; */ + if (message.count !== 0) + writer.tag(2, WireType.Varint).int32(message.count); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1793,26 +2320,26 @@ class unsubscribe$Type extends MessageType { } } /** - * @generated MessageType for protobuf message unsubscribe + * @generated MessageType for protobuf message playerCount */ -export const unsubscribe = new unsubscribe$Type(); +export const playerCount = new playerCount$Type(); // @generated message type with reflection information, may provide speed optimized methods -class streamerIdChanged$Type extends MessageType { +class stats$Type extends MessageType { constructor() { - super("streamerIdChanged", [ + super("stats", [ { no: 1, name: "type", kind: "scalar", T: 9 /*ScalarType.STRING*/ }, - { no: 2, name: "newID", kind: "scalar", T: 9 /*ScalarType.STRING*/ } + { no: 2, name: "data", kind: "scalar", T: 9 /*ScalarType.STRING*/ } ]); } - create(value?: PartialMessage): streamerIdChanged { + create(value?: PartialMessage): stats { const message = globalThis.Object.create((this.messagePrototype!)); message.type = ""; - message.newID = ""; + message.data = ""; if (value !== undefined) - reflectionMergePartial(this, message, value); + reflectionMergePartial(this, message, value); return message; } - internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: streamerIdChanged): streamerIdChanged { + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: stats): stats { let message = target ?? this.create(), end = reader.pos + length; while (reader.pos < end) { let [fieldNo, wireType] = reader.tag(); @@ -1820,8 +2347,8 @@ class streamerIdChanged$Type extends MessageType { case /* string type */ 1: message.type = reader.string(); break; - case /* string newID */ 2: - message.newID = reader.string(); + case /* string data */ 2: + message.data = reader.string(); break; default: let u = options.readUnknownField; @@ -1834,13 +2361,13 @@ class streamerIdChanged$Type extends MessageType { } return message; } - internalBinaryWrite(message: streamerIdChanged, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + internalBinaryWrite(message: stats, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { /* string type = 1; */ if (message.type !== "") writer.tag(1, WireType.LengthDelimited).string(message.type); - /* string newID = 2; */ - if (message.newID !== "") - writer.tag(2, WireType.LengthDelimited).string(message.newID); + /* string data = 2; */ + if (message.data !== "") + writer.tag(2, WireType.LengthDelimited).string(message.data); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); @@ -1848,6 +2375,6 @@ class streamerIdChanged$Type extends MessageType { } } /** - * @generated MessageType for protobuf message streamerIdChanged + * @generated MessageType for protobuf message stats */ -export const streamerIdChanged = new streamerIdChanged$Type(); +export const stats = new stats$Type(); diff --git a/Common/src/Protocol/SignallingProtocol.ts b/Common/src/Protocol/SignallingProtocol.ts index 4f526dd4..55afc888 100644 --- a/Common/src/Protocol/SignallingProtocol.ts +++ b/Common/src/Protocol/SignallingProtocol.ts @@ -1,6 +1,5 @@ // Copyright Epic Games, Inc. All Rights Reserved. -import { Logger } from '../Logger/Logger'; import { ITransport } from '../Transport/ITransport'; import { EventEmitter } from 'events'; import { BaseMessage } from '../Messages/base_message'; @@ -9,47 +8,34 @@ import * as MessageHelpers from '../Messages/message_helpers'; /** * Signalling protocol for handling messages from the signalling server. + * + * Listen on this emitter for messages. Message type is the name of the event to listen for. + * Example: + * ``` + * signallingProtocol.on('config', (message: Messages.config) => console.log(`Got a config message: ${message}`))); + * ``` + * + * The transport in this class will also emit on message events. + * + * Events emitted on transport: + * message: + * Emitted any time a message is received by the transport. Listen on this if + * you wish to capture all messages, rather than specific messages on + * 'messageHandlers'. + * + * out: + * Emitted when sending a message out on the transport. Similar to 'message' but + * only for when messages are sent from this endpoint. Useful for debugging. */ -export class SignallingProtocol { - private transport: ITransport; +export class SignallingProtocol extends EventEmitter { + static get SIGNALLING_VERSION(): string { return '1.1.1'; } - /** - * Listen on this emitter for transport events. - * - * Events emitted: - * open: - * Emitted when the transport connection opens and is ready to handle messages. - * - * error: - * Emitted when there is an error on the transport has an error and must close. - * - * close: - * Emitted when the transport connection closes and can no longer send or - * receive messages. Will also be emitted after an error. - * - * message: - * Emitted any time a message is received by the transport. Listen on this if - * you wish to capture all messages, rather than specific messages on - * 'messageHandlers'. - */ - transportEvents: EventEmitter; - - /** - * Listen on this emitter for messages. Message type is the name of the event to listen for. - * - * Example: - * messageHandlers.addListener('config', (message: Messages.config) => console.log(`Got a config message: ${message}`))); - */ - messageHandlers: EventEmitter; + // The transport in use by this protocol object. + transport: ITransport; constructor(transport: ITransport) { + super(); this.transport = transport; - this.transportEvents = new EventEmitter(); - this.messageHandlers = new EventEmitter(); - - transport.events.addListener('open', (event: Event) => this.transportEvents.emit('open', event)); - transport.events.addListener('error', (event: Event) => this.transportEvents.emit('error', event)); - transport.events.addListener('close', (event: CloseEvent) => this.transportEvents.emit('close', event)); transport.onMessage = (msg: BaseMessage) => { // auto handle ping messages @@ -59,78 +45,43 @@ export class SignallingProtocol { } // call the handlers - this.messageHandlers.emit(msg.type, msg); // emit this for listeners listening for specific messages - this.transportEvents.emit('message', msg); // emit this for listeners listening to any message + transport.emit('message', msg); // emit this for listeners listening to any message + this.emit(msg.type, msg); // emit this for listeners listening for specific messages }; } /** * Asks the transport to connect to the given URL. + * @param url - The url to connect to. + * @returns True if the connection call succeeded. */ - connect(url: string) { + connect(url: string): boolean { return this.transport.connect(url); } /** * Asks the transport to disconnect from any connection it might have. + * @param code - An optional disconnection code. + * @param reason - An optional descriptive string for the disconnect reason. */ - disconnect() { - this.transport.disconnect(); + disconnect(code?: number, reason?: string): void { + this.transport.disconnect(code, reason); } /** * Returns true if the transport is connected and ready to send/receive messages. + * @returns True if the protocol is connected. */ - isConnected() { + isConnected(): boolean { return this.transport.isConnected(); } /** * Passes a message to the transport to send to the other end. + * @param msg - The message to send. */ - sendMessage(msg: BaseMessage) { + sendMessage(msg: BaseMessage): void { this.transport.sendMessage(msg); - } - - // the following are just wrappers for sendMessage and should be deprioritized. - - requestStreamerList() { - const payload = MessageHelpers.createMessage(Messages.listStreamers); - this.transport.sendMessage(payload); - } - - sendSubscribe(streamerid: string) { - const payload = MessageHelpers.createMessage(Messages.subscribe, { streamerid: streamerid }); - this.transport.sendMessage(payload); - } - - sendUnsubscribe() { - const payload = MessageHelpers.createMessage(Messages.unsubscribe); - this.transport.sendMessage(payload); - } - - sendWebRtcOffer(extraParams: any) { - const payload = MessageHelpers.createMessage(Messages.offer, extraParams); - this.transport.sendMessage(payload); - } - - sendWebRtcAnswer(extraParams: any) { - const payload = MessageHelpers.createMessage(Messages.answer, extraParams); - this.transport.sendMessage(payload); - } - - sendWebRtcDatachannelRequest() { - const payload = MessageHelpers.createMessage(Messages.dataChannelRequest); - this.transport.sendMessage(payload); - } - - sendSFURecvDataChannelReady() { - const payload = MessageHelpers.createMessage(Messages.peerDataChannelsReady); - this.transport.sendMessage(payload); - } - - sendIceCandidate(candidate: RTCIceCandidate) { - const payload = MessageHelpers.createMessage(Messages.iceCandidate, { candidate: candidate }); - this.transport.sendMessage(payload); + this.transport.emit('out', msg); // emit this for listeners listening to outgoing messages } } diff --git a/Common/src/Transport/ITransport.ts b/Common/src/Transport/ITransport.ts index e0dc3ee6..4c386a22 100644 --- a/Common/src/Transport/ITransport.ts +++ b/Common/src/Transport/ITransport.ts @@ -6,14 +6,10 @@ import { EventEmitter } from 'events'; * Implement this interface to support your custom transport. You can then supply an instance of your * transport to the constructor of SignallingProtocol during startup. */ -export interface ITransport { - /** - * Dispatch open/error/close events on this to indicate transport events to the protocol. - */ - events: EventEmitter; - +export interface ITransport extends EventEmitter { /** * Called when the protocol wants to send a message over the transport. + * @param msg - The message to send over the transport. */ sendMessage(msg: BaseMessage): void; @@ -24,16 +20,21 @@ export interface ITransport { /** * Connect to a given URL. + * @param url - The URL for the transport to connect to. + * @returns True if the connection was successful. */ connect(url: string): boolean; /** * Disconnect this transport. + * @param code - An optional disconnect code. + * @param reason - A descriptive string for the disconnect reason. */ - disconnect(): void; + disconnect(code?: number, reason?: string): void; /** * Should return true when the transport is connected and ready to send/receive messages. + * @returns True if the transport is connected. */ isConnected(): boolean; } diff --git a/Common/src/Transport/WebSocketTransport.ts b/Common/src/Transport/WebSocketTransport.ts index 95dd20c7..10ee3666 100644 --- a/Common/src/Transport/WebSocketTransport.ts +++ b/Common/src/Transport/WebSocketTransport.ts @@ -4,7 +4,6 @@ import { Logger } from '../Logger/Logger'; import { ITransport } from './ITransport'; import { EventEmitter } from 'events'; import { BaseMessage } from '../Messages/base_message'; -import * as MessageHelpers from '../Messages/message_helpers'; // declare the new method for the websocket interface declare global { @@ -16,25 +15,29 @@ declare global { /** * The controller for the WebSocket and all associated methods */ -export class WebSocketTransport implements ITransport { +export class WebSocketTransport extends EventEmitter implements ITransport { WS_OPEN_STATE = 1; webSocket: WebSocket; - events: EventEmitter; constructor() { - this.events = new EventEmitter(); + super(); } + /** + * Sends a message over the websocket. + * @param msg - The message to send. + */ sendMessage(msg: BaseMessage): void { this.webSocket.send(JSON.stringify(msg)); } + // A handler for when messages are received. onMessage: (msg: BaseMessage) => void; /** * Connect to the signaling server * @param connectionURL - The Address of the signaling server - * @returns - If there is a connection + * @returns If there is a connection */ connect(connectionURL: string): boolean { Logger.Log(Logger.GetStackTrace(), connectionURL, 6); @@ -52,10 +55,19 @@ export class WebSocketTransport implements ITransport { } } - disconnect(): void { - this.webSocket.close(); + /** + * Disconnect this transport. + * @param code - An optional disconnect code. + * @param reason - A descriptive string for the disconnect reason. + */ + disconnect(code?: number, reason?: string): void { + this.webSocket.close(code, reason); } + /** + * Should return true when the transport is connected and ready to send/receive messages. + * @returns True if the transport is connected. + */ isConnected(): boolean { return this.webSocket && this.webSocket.readyState != WebSocket.CLOSED } @@ -64,7 +76,7 @@ export class WebSocketTransport implements ITransport { * Handles what happens when a message is received in binary form * @param event - Message Received */ - handleOnMessageBinary(event: MessageEvent) { + handleOnMessageBinary(event: MessageEvent): void { // if the event is empty return if (!event || !event.data) { return; @@ -97,7 +109,7 @@ export class WebSocketTransport implements ITransport { * Handles what happens when a message is received * @param event - Message Received */ - handleOnMessage(event: MessageEvent) { + handleOnMessage(event: MessageEvent): void { // Check if websocket message is binary, if so, stringify it. if (event.data && event.data instanceof Blob) { this.handleOnMessageBinary(event); @@ -124,32 +136,29 @@ export class WebSocketTransport implements ITransport { /** * Handles when the Websocket is opened - * @param event - Not Used */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handleOnOpen() { + handleOnOpen(): void { Logger.Log( Logger.GetStackTrace(), 'Connected to the signalling server via WebSocket', 6 ); - this.events.emit('open'); + this.emit('open'); } /** * Handles when there is an error on the websocket - * @param event - Error Payload */ - handleOnError() { + handleOnError(): void { //Logger.Error(Logger.GetStackTrace(), 'WebSocket error'); - this.events.emit('error'); + this.emit('error'); } /** * Handles when the Websocket is closed * @param event - Close Event */ - handleOnClose(event: CloseEvent) { + handleOnClose(event: CloseEvent): void { Logger.Log( Logger.GetStackTrace(), 'Disconnected to the signalling server via WebSocket: ' + @@ -157,13 +166,13 @@ export class WebSocketTransport implements ITransport { ' - ' + event.reason ); - this.events.emit('close', event); + this.emit('close', event); } /** * Closes the Websocket connection */ - close() { + close(): void { this.webSocket?.close(); } } diff --git a/Common/src/Transport/WebSocketTransportNJS.ts b/Common/src/Transport/WebSocketTransportNJS.ts index 8fa6fb4b..645ee5ba 100644 --- a/Common/src/Transport/WebSocketTransportNJS.ts +++ b/Common/src/Transport/WebSocketTransportNJS.ts @@ -9,41 +9,57 @@ import { EventEmitter } from 'events'; * An implementation of WebSocketTransport from pixelstreamingcommon that supports node.js websockets * This is needed because of the slight differences between the 'ws' node.js package and the websockets * supported in the browsers. - * If you are using this code in a browser use 'WebSocketTransport' instead. + * Do not use this code in a browser use 'WebSocketTransport' instead. */ -export class WebSocketTransportNJS implements ITransport { +export class WebSocketTransportNJS extends EventEmitter implements ITransport { WS_OPEN_STATE = 1; webSocket: WebSocket; - events: EventEmitter; + webSocketServer: WebSocket.Server; - constructor() { - this.events = new EventEmitter(); + constructor(existingSocket?: WebSocket) { + super(); + if (existingSocket) { + this.webSocket = existingSocket; + this.setupSocketHandlers(); + this.emit('open'); + } } + /** + * Sends a message over the websocket. + * @param msg - The message to send. + */ sendMessage(msg: BaseMessage): void { this.webSocket.send(JSON.stringify(msg)); } + // A handler for when messages are received. onMessage: (msg: BaseMessage) => void; /** * Connect to the signaling server * @param connectionURL - The Address of the signaling server - * @returns - If there is a connection + * @returns If there is a connection */ connect(connectionURL: string): boolean { this.webSocket = new WebSocket(connectionURL); - this.webSocket.addEventListener("open", event => this.handleOnOpen(event)); - this.webSocket.addEventListener("error", event => this.handleOnError(event)); - this.webSocket.addEventListener("close", event => this.handleOnClose(event)); - this.webSocket.addEventListener("message", event => this.handleOnMessage(event)); + this.setupSocketHandlers(); return true; } - disconnect(): void { - this.webSocket.close(); + /** + * Disconnect this transport. + * @param code - An optional disconnect code. + * @param reason - A descriptive string for the disconnect reason. + */ + disconnect(code?: number, reason?: string): void { + this.webSocket.close(code, reason); } + /** + * Should return true when the transport is connected and ready to send/receive messages. + * @returns True if the transport is connected. + */ isConnected(): boolean { return this.webSocket && this.webSocket.readyState != WebSocket.CLOSED } @@ -52,7 +68,7 @@ export class WebSocketTransportNJS implements ITransport { * Handles what happens when a message is received * @param event - Message Received */ - handleOnMessage(event: WebSocket.MessageEvent) { + handleOnMessage(event: WebSocket.MessageEvent): void { let parsedMessage; try { parsedMessage = JSON.parse(event.data as string); @@ -67,31 +83,37 @@ export class WebSocketTransportNJS implements ITransport { * Handles when the Websocket is opened * @param event - Not Used */ - // eslint-disable-next-line @typescript-eslint/no-unused-vars - handleOnOpen(event: WebSocket.Event) { - this.events.emit('open', event); + handleOnOpen(event: WebSocket.Event): void { + this.emit('open', event); } /** * Handles when there is an error on the websocket * @param event - Error Payload */ - handleOnError(event: WebSocket.ErrorEvent) { - this.events.emit('error', event); + handleOnError(event: WebSocket.ErrorEvent): void { + this.emit('error', event); } /** * Handles when the Websocket is closed * @param event - Close Event */ - handleOnClose(event: WebSocket.CloseEvent) { - this.events.emit('close', event); + handleOnClose(event: WebSocket.CloseEvent): void { + this.emit('close', event); } /** * Closes the Websocket connection */ - close() { + close(): void { this.webSocket?.close(); } + + private setupSocketHandlers(): void { + this.webSocket.addEventListener("open", this.handleOnOpen.bind(this)); + this.webSocket.addEventListener("error", this.handleOnError.bind(this)); + this.webSocket.addEventListener("close", this.handleOnClose.bind(this)); + this.webSocket.addEventListener("message", this.handleOnMessage.bind(this)); + } } diff --git a/Common/tsconfig.base.json b/Common/tsconfig.base.json index be58135d..98bab704 100644 --- a/Common/tsconfig.base.json +++ b/Common/tsconfig.base.json @@ -8,4 +8,15 @@ "allowJs": true }, "include": ["./src/*.ts"], + "typedocOptions": { + "entryPoints": ["src/**/*.ts"], + "out": "docs", + "exclude": ["src/Messages/signalling_messages.ts"], + "excludeExternals": true, + "excludePrivate": true, + "excludeInternal": true, + "cleanOutputDir": false, + "plugin": "typedoc-plugin-markdown", + "readme": "none" + } } diff --git a/Frontend/implementations/typescript/webpack.common.js b/Frontend/implementations/typescript/webpack.common.js index d5d0e0fe..6d700965 100644 --- a/Frontend/implementations/typescript/webpack.common.js +++ b/Frontend/implementations/typescript/webpack.common.js @@ -59,7 +59,7 @@ module.exports = { filename: '[name].js', library: 'epicgames-frontend', libraryTarget: 'umd', - path: path.resolve(__dirname, '../../../SignallingWebServer/Public'), + path: process.env.WEBPACK_OUTPUT_PATH ? path.resolve(process.env.WEBPACK_OUTPUT_PATH) : path.resolve(__dirname, '../../../SignallingWebServer/www'), clean: true, globalObject: 'this', hashFunction: 'xxhash64', diff --git a/Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts b/Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts index c41bf824..b60567e4 100644 --- a/Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts +++ b/Frontend/library/src/WebRtcPlayer/WebRtcPlayerController.ts @@ -192,44 +192,44 @@ export class WebRtcPlayerController { // set up websocket methods this.transport = new WebSocketTransport(); this.protocol = new SignallingProtocol(this.transport); - this.protocol.messageHandlers.addListener(Messages.config.typeName, (msg: BaseMessage) => + this.protocol.addListener(Messages.config.typeName, (msg: BaseMessage) => this.handleOnConfigMessage(msg as Messages.config) ); - this.protocol.messageHandlers.addListener(Messages.streamerList.typeName, (msg: BaseMessage) => + this.protocol.addListener(Messages.streamerList.typeName, (msg: BaseMessage) => this.handleStreamerListMessage(msg as Messages.streamerList) ); - this.protocol.messageHandlers.addListener(Messages.streamerIdChanged.typeName, (msg: BaseMessage) => + this.protocol.addListener(Messages.streamerIdChanged.typeName, (msg: BaseMessage) => this.handleStreamerIDChangedMessage(msg as Messages.streamerIdChanged) ); - this.protocol.messageHandlers.addListener(Messages.playerCount.typeName, (msg: BaseMessage) => { + this.protocol.addListener(Messages.playerCount.typeName, (msg: BaseMessage) => { const playerCountMessage = msg as Messages.playerCount; this.pixelStreaming._onPlayerCount(playerCountMessage.count); }); - this.protocol.messageHandlers.addListener(Messages.answer.typeName, (msg: BaseMessage) => + this.protocol.addListener(Messages.answer.typeName, (msg: BaseMessage) => this.handleWebRtcAnswer(msg as Messages.answer) ); - this.protocol.messageHandlers.addListener(Messages.offer.typeName, (msg: BaseMessage) => + this.protocol.addListener(Messages.offer.typeName, (msg: BaseMessage) => this.handleWebRtcOffer(msg as Messages.offer) ); - this.protocol.messageHandlers.addListener(Messages.streamerDataChannels.typeName, (msg: BaseMessage) => - this.handleWebRtcSFUPeerDatachannels(msg as Messages.streamerDataChannels) + this.protocol.addListener(Messages.peerDataChannels.typeName, (msg: BaseMessage) => + this.handleWebRtcSFUPeerDatachannels(msg as Messages.peerDataChannels) ); - this.protocol.messageHandlers.addListener(Messages.iceCandidate.typeName, (msg: BaseMessage) => { + this.protocol.addListener(Messages.iceCandidate.typeName, (msg: BaseMessage) => { const iceCandidateMessage = msg as Messages.iceCandidate; this.handleIceCandidate(iceCandidateMessage.candidate); }); - this.protocol.transportEvents.addListener('open', () => { + this.protocol.transport.addListener('open', () => { const BrowserSendsOffer = this.config.isFlagEnabled(Flags.BrowserSendOffer); if (!BrowserSendsOffer) { const message = MessageHelpers.createMessage(Messages.listStreamers); this.protocol.sendMessage(message); } }); - this.protocol.transportEvents.addListener('error', () => { + this.protocol.transport.addListener('error', () => { // dont really need to do anything here since the close event should follow. Logger.Error(Logger.GetStackTrace(), `Got a transport error.`); }); - this.protocol.transportEvents.addListener('close', (event: CloseEvent) => { + this.protocol.transport.addListener('close', (event: CloseEvent) => { // when we refresh the page during a stream we get the going away code. // in that case we don't want to reconnect since we're navigating away. // https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code @@ -1386,7 +1386,7 @@ export class WebRtcPlayerController { this.isReconnecting = true; this.reconnectAttempt++; setTimeout(() => { - this.protocol.requestStreamerList(); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.listStreamers)); }, reconnectDelay); } else { // We've exhausted our reconnect attempts, return to main screen @@ -1488,7 +1488,7 @@ export class WebRtcPlayerController { * Handle when the SFU provides the peer with its data channels * @param DataChannels - The message from the SFU containing the data channels ids */ - handleWebRtcSFUPeerDatachannels(DataChannels: Messages.streamerDataChannels) { + handleWebRtcSFUPeerDatachannels(DataChannels: Messages.peerDataChannels) { const SendOptions: RTCDataChannelInit = { ordered: true, negotiated: true, @@ -1517,7 +1517,7 @@ export class WebRtcPlayerController { RecvOptions ); this.recvDataChannelController.handleOnOpen = () => - this.protocol.sendSFURecvDataChannelReady(); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.peerDataChannelsReady)); // If we're uni-directional, only the recv data channel should handle incoming messages this.recvDataChannelController.handleOnMessage = ( ev: MessageEvent @@ -1570,7 +1570,7 @@ export class WebRtcPlayerController { handleSendIceCandidate(iceEvent: RTCPeerConnectionIceEvent) { Logger.Log(Logger.GetStackTrace(), 'OnIceCandidate', 6); if (iceEvent.candidate && iceEvent.candidate.candidate) { - this.protocol.sendIceCandidate(iceEvent.candidate); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.iceCandidate, { candidate: iceEvent.candidate })); } } @@ -1610,7 +1610,7 @@ export class WebRtcPlayerController { maxBitrateBps: 1000 * this.config.getNumericSettingValue(NumericParameters.WebRTCMaxBitrate) }; - this.protocol.sendWebRtcOffer(extraParams); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.offer, extraParams)); } /** @@ -1630,10 +1630,10 @@ export class WebRtcPlayerController { maxBitrateBps: 1000 * this.config.getNumericSettingValue(NumericParameters.WebRTCMaxBitrate) }; - this.protocol.sendWebRtcAnswer(extraParams); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.answer, extraParams)); if (this.isUsingSFU) { - this.protocol.sendWebRtcDatachannelRequest(); + this.protocol.sendMessage(MessageHelpers.createMessage(Messages.dataChannelRequest)); } } diff --git a/Matchmaker/.gitignore b/Matchmaker/.gitignore index f00ee426..c42335f2 100644 --- a/Matchmaker/.gitignore +++ b/Matchmaker/.gitignore @@ -1 +1,2 @@ -html/custom/ \ No newline at end of file +html/custom/ +certificates/ diff --git a/Matchmaker/matchmaker.js b/Matchmaker/matchmaker.js index b9ff3bdb..08e680fd 100644 --- a/Matchmaker/matchmaker.js +++ b/Matchmaker/matchmaker.js @@ -71,9 +71,6 @@ if (config.UseHTTPS) { if (req.get('Host')) { var hostAddressParts = req.get('Host').split(':'); var hostAddress = hostAddressParts[0]; - if (httpsPort != 443) { - hostAddress = `${hostAddress}:${httpsPort}`; - } return res.redirect(['https://', hostAddress, req.originalUrl].join('')); } else { console.error(`unable to get host name from header. Requestor ${req.ip}, url path: '${req.originalUrl}', available headers ${JSON.stringify(req.headers)}`); @@ -152,7 +149,8 @@ if(enableRedirectionLinks) { app.get('/', (req, res) => { cirrusServer = getAvailableCirrusServer(); if (cirrusServer != undefined) { - res.redirect(`http://${cirrusServer.address}:${cirrusServer.port}/`); + let prefix = cirrusServer.https ? 'https://' : 'http://'; + res.redirect(`${prefix}${cirrusServer.address}:${cirrusServer.port}/`); //console.log(req); console.log(`Redirect to ${cirrusServer.address}:${cirrusServer.port}`); } else { @@ -164,7 +162,8 @@ if(enableRedirectionLinks) { app.get('/custom_html/:htmlFilename', (req, res) => { cirrusServer = getAvailableCirrusServer(); if (cirrusServer != undefined) { - res.redirect(`http://${cirrusServer.address}:${cirrusServer.port}/custom_html/${req.params.htmlFilename}`); + let prefix = cirrusServer.https ? 'https://' : 'http://'; + res.redirect(`${prefix}${cirrusServer.address}:${cirrusServer.port}/custom_html/${req.params.htmlFilename}`); console.log(`Redirect to ${cirrusServer.address}:${cirrusServer.port}`); } else { sendRetryResponse(res); @@ -200,6 +199,7 @@ const matchmaker = net.createServer((connection) => { cirrusServer = { address: message.address, port: message.port, + https: message.https, numConnectedClients: 0, lastPingReceived: Date.now() }; diff --git a/SS_Test/run_tests.sh b/SS_Test/run_tests.sh index d59227e0..ecc2a247 100755 --- a/SS_Test/run_tests.sh +++ b/SS_Test/run_tests.sh @@ -1,7 +1,6 @@ #!/bin/bash docker compose up --build --abort-on-container-exit --exit-code-from tester -echo if [[ $? -ne 0 ]]; then echo "Testing failed." diff --git a/SS_Test/src/index.ts b/SS_Test/src/index.ts index 33a71810..ef2bc0f8 100644 --- a/SS_Test/src/index.ts +++ b/SS_Test/src/index.ts @@ -38,13 +38,13 @@ async function main(): Promise { streamer.addEventExpect('open', (event: WebSocket.Event) => {}); streamer.addExpect(Messages.config, (msg: Messages.config) => {}); streamer.addExpect(Messages.identify, (msg: Messages.identify) => streamer.sendMessage(Messages.endpointId, { id: config.streamerId })); + streamer.addExpect(Messages.endpointIdConfirm, (event: Messages.endpointIdConfirm) => {}); let playerId: string | null = null; let player: SignallingConnection = context.newConnection('Player', config.playerURL); player.addEventExpect('open', (event: WebSocket.Event) => {}); player.addExpect(Messages.config, (msg: Messages.config) => {}); - player.addExpect(Messages.playerCount, (msg: Messages.playerCount) => {}); if (!await context.validateStep(3000, [streamer, player])) { onFailedPhase('initial connection', context); @@ -118,8 +118,7 @@ async function main(): Promise { player = context.newConnection('Player', config.playerURL); player.addEventExpect('open', (event: WebSocket.Event) => {}); - player.addExpect(Messages.config, (msg: Messages.config) => {}); - player.addExpect(Messages.playerCount, (msg: Messages.playerCount) => player.sendMessage(Messages.listStreamers)); + player.addExpect(Messages.config, (msg: Messages.config) => player.sendMessage(Messages.listStreamers)); player.addExpect(Messages.streamerList, (msg: Messages.streamerList) => player.sendMessage(Messages.subscribe, { streamerId: config.streamerId })); streamer.addExpect(Messages.playerConnected, (msg: Messages.playerConnected) => { playerId = msg.playerId!; diff --git a/SS_Test/src/signalling_tester.ts b/SS_Test/src/signalling_tester.ts index b09ee862..19e205d3 100644 --- a/SS_Test/src/signalling_tester.ts +++ b/SS_Test/src/signalling_tester.ts @@ -85,19 +85,19 @@ export class TestContext { const errors = unhandledEvents.filter((event) => event.type == 'error') as ErrorEvent[]; for (const expected of unsatisfiedMessages) { - this._errors.push(`Failed to receive expected message: ${expected.messageType.typeName}`); + this._errors.push(`(${connection.name}) Failed to receive expected message: ${expected.messageType.typeName}`); } for (const expected of unsatisfiedSocketEvents) { - this._errors.push(`Failed to receive expected socket event: ${expected.eventType}`); + this._errors.push(`(${connection.name}) Failed to receive expected socket event: ${expected.eventType}`); } for (const message of unhandledMessages) { - this._errors.push(`Got message that was not expected/handled:: ${message.message.type}`); + this._errors.push(`(${connection.name}) Got message that was not expected/handled:: ${message.message.type}`); } for (const message of unhandledSocketEvents) { - this._errors.push(`Event not handled:: ${message.eventType}`); + this._errors.push(`(${connection.name}) Event not handled:: ${message.eventType}`); } for (const error of errors) { @@ -158,10 +158,10 @@ export class SignallingConnection { this.processTimer = null; this.protocol = new SignallingProtocol(new WebSocketTransportNJS()); - this.protocol.transportEvents.addListener("open", event => this.onConnectionOpen(event)); - this.protocol.transportEvents.addListener("error", event => this.onConnectionError(event)); - this.protocol.transportEvents.addListener("close", event => this.onConnectionClose(event)); - this.protocol.transportEvents.addListener("message", message => this.onMessage(message)); + this.protocol.transport.addListener("open", event => this.onConnectionOpen(event)); + this.protocol.transport.addListener("error", event => this.onConnectionError(event)); + this.protocol.transport.addListener("close", event => this.onConnectionClose(event)); + this.protocol.transport.addListener("message", message => this.onMessage(message)); this.logCallback(this, `Connecting to Signalling Server at ${url}`); this.protocol.connect(url); @@ -385,6 +385,8 @@ export class SignallingConnection { throw new Error('Unhandled internal event type'); } } + eventsToRemove.sort(); + expectingToRemove.sort(); for (var i = eventsToRemove.length - 1; i >= 0; --i) { this.eventQueue.splice(eventsToRemove[i], 1); } diff --git a/SS_Test/tsconfig.json b/SS_Test/tsconfig.json index 6e7696cc..3a879564 100644 --- a/SS_Test/tsconfig.json +++ b/SS_Test/tsconfig.json @@ -6,7 +6,7 @@ "esModuleInterop": true, "target": "es5", "moduleResolution": "node", - "sourceMap": false, + "sourceMap": true, "allowJs": true, "declaration": false }, diff --git a/Signalling/.eslintignore b/Signalling/.eslintignore new file mode 100644 index 00000000..c2eee6a8 --- /dev/null +++ b/Signalling/.eslintignore @@ -0,0 +1,6 @@ +build +node_modules +package-lock.json +package.json +tsconfig.json +.eslintrc.js \ No newline at end of file diff --git a/Signalling/.eslintrc.js b/Signalling/.eslintrc.js new file mode 100644 index 00000000..38c95aea --- /dev/null +++ b/Signalling/.eslintrc.js @@ -0,0 +1,23 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + 'eslint-plugin-tsdoc' + ], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + rules: { + "tsdoc/syntax": "warn", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ] + } +}; diff --git a/Signalling/.gitignore b/Signalling/.gitignore new file mode 100644 index 00000000..3e2e84b0 --- /dev/null +++ b/Signalling/.gitignore @@ -0,0 +1,2 @@ +build/ +node_modules/ diff --git a/Signalling/README.md b/Signalling/README.md new file mode 100644 index 00000000..f75124bd --- /dev/null +++ b/Signalling/README.md @@ -0,0 +1,11 @@ +# @epicgames-ps/lib-pixelstreamingsignalling + +A library for developers wanting to implement tools that signal Pixel Streaming applications + +## Building + +`npm run build` + +## Documentation + +The API documentation is [here](docs/) and covers details of all exported components of the library. diff --git a/Signalling/docs/.nojekyll b/Signalling/docs/.nojekyll new file mode 100644 index 00000000..e2ac6616 --- /dev/null +++ b/Signalling/docs/.nojekyll @@ -0,0 +1 @@ +TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/Signalling/docs/README.md b/Signalling/docs/README.md new file mode 100644 index 00000000..a691914f --- /dev/null +++ b/Signalling/docs/README.md @@ -0,0 +1,20 @@ +@epicgames-ps/lib-pixelstreamingsignalling-ue5.5 + +# @epicgames-ps/lib-pixelstreamingsignalling-ue5.5 + +## Table of contents + +### Modules + +- [Logger](modules/Logger.md) +- [LoggingUtils](modules/LoggingUtils.md) +- [MatchmakerConnection](modules/MatchmakerConnection.md) +- [PlayerConnection](modules/PlayerConnection.md) +- [PlayerRegistry](modules/PlayerRegistry.md) +- [SFUConnection](modules/SFUConnection.md) +- [SignallingServer](modules/SignallingServer.md) +- [StreamerConnection](modules/StreamerConnection.md) +- [StreamerRegistry](modules/StreamerRegistry.md) +- [Utils](modules/Utils.md) +- [WebServer](modules/WebServer.md) +- [pixelstreamingsignalling](modules/pixelstreamingsignalling.md) diff --git a/Signalling/docs/classes/MatchmakerConnection.MatchmakerConnection.md b/Signalling/docs/classes/MatchmakerConnection.MatchmakerConnection.md new file mode 100644 index 00000000..3aca8ffa --- /dev/null +++ b/Signalling/docs/classes/MatchmakerConnection.MatchmakerConnection.md @@ -0,0 +1,55 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [MatchmakerConnection](../modules/MatchmakerConnection.md) / MatchmakerConnection + +# Class: MatchmakerConnection + +[MatchmakerConnection](../modules/MatchmakerConnection.md).MatchmakerConnection + +This class handles connecting and reconnecting to a matchmaker service and +will notify the matchmaker of streamer and player added/removed events that +are emitted from the respective registries. + +## Table of contents + +### Constructors + +- [constructor](MatchmakerConnection.MatchmakerConnection.md#constructor) + +### Properties + +- [config](MatchmakerConnection.MatchmakerConnection.md#config) + +## Constructors + +### constructor + +• **new MatchmakerConnection**(`config`, `streamerRegistry`, `playerRegistry`): [`MatchmakerConnection`](MatchmakerConnection.MatchmakerConnection.md) + +Initializes the matchmaker connection and attempts a connection to the given +address and port immediately. Will automatically try to reconnect on connection +loss. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `config` | [`IMatchmakerConfig`](../interfaces/MatchmakerConnection.IMatchmakerConfig.md) | The matchmaker configuration. | +| `streamerRegistry` | [`StreamerRegistry`](StreamerRegistry.StreamerRegistry.md) | The signalling server streamer registry. This is used to notify the matchmaker of streamers being added/removed. | +| `playerRegistry` | [`PlayerRegistry`](PlayerRegistry.PlayerRegistry.md) | The signalling server player registry. This is used to notify the matchmaker of players being added/removed. | + +#### Returns + +[`MatchmakerConnection`](MatchmakerConnection.MatchmakerConnection.md) + +#### Defined in + +[MatchmakerConnection.ts:49](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L49) + +## Properties + +### config + +• **config**: [`IMatchmakerConfig`](../interfaces/MatchmakerConnection.IMatchmakerConfig.md) + +#### Defined in + +[MatchmakerConnection.ts:36](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L36) diff --git a/Signalling/docs/classes/PlayerConnection.PlayerConnection.md b/Signalling/docs/classes/PlayerConnection.PlayerConnection.md new file mode 100644 index 00000000..5c56f776 --- /dev/null +++ b/Signalling/docs/classes/PlayerConnection.PlayerConnection.md @@ -0,0 +1,198 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [PlayerConnection](../modules/PlayerConnection.md) / PlayerConnection + +# Class: PlayerConnection + +[PlayerConnection](../modules/PlayerConnection.md).PlayerConnection + +A connection between the signalling server and a player connection. +This is where messages expected to be handled by the player come in +and where messages are sent to the player. + +Interesting internals: +playerId: The unique id string of this player. +transport: The ITransport where transport events can be subscribed to +protocol: The SignallingProtocol where signalling messages can be +subscribed to. + +## Implements + +- [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) +- [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) + +## Table of contents + +### Constructors + +- [constructor](PlayerConnection.PlayerConnection.md#constructor) + +### Properties + +- [playerId](PlayerConnection.PlayerConnection.md#playerid) +- [protocol](PlayerConnection.PlayerConnection.md#protocol) +- [remoteAddress](PlayerConnection.PlayerConnection.md#remoteaddress) +- [subscribedStreamer](PlayerConnection.PlayerConnection.md#subscribedstreamer) +- [transport](PlayerConnection.PlayerConnection.md#transport) + +### Methods + +- [getPlayerInfo](PlayerConnection.PlayerConnection.md#getplayerinfo) +- [getReadableIdentifier](PlayerConnection.PlayerConnection.md#getreadableidentifier) +- [sendMessage](PlayerConnection.PlayerConnection.md#sendmessage) + +## Constructors + +### constructor + +• **new PlayerConnection**(`server`, `ws`, `sendOffer`, `remoteAddress?`): [`PlayerConnection`](PlayerConnection.PlayerConnection.md) + +Initializes a new connection with given and sane values. Adds listeners for the +websocket close and error so it can react by unsubscribing and resetting itself. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `server` | [`SignallingServer`](SignallingServer.SignallingServer.md) | The signalling server object that spawned this player. | +| `ws` | `WebSocket` | The websocket coupled to this player connection. | +| `sendOffer` | `boolean` | True if the player is requesting to receive offers from streamers. | +| `remoteAddress?` | `string` | The remote address of this connection. Only used as display. | + +#### Returns + +[`PlayerConnection`](PlayerConnection.PlayerConnection.md) + +#### Defined in + +[PlayerConnection.ts:50](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L50) + +## Properties + +### playerId + +• **playerId**: `string` + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[playerId](../interfaces/PlayerRegistry.IPlayer.md#playerid) + +#### Defined in + +[PlayerConnection.ts:27](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L27) + +___ + +### protocol + +• **protocol**: `SignallingProtocol` + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[protocol](../interfaces/PlayerRegistry.IPlayer.md#protocol) + +#### Defined in + +[PlayerConnection.ts:31](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L31) + +___ + +### remoteAddress + +• `Optional` **remoteAddress**: `string` + +#### Defined in + +[PlayerConnection.ts:35](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L35) + +___ + +### subscribedStreamer + +• **subscribedStreamer**: [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[subscribedStreamer](../interfaces/PlayerRegistry.IPlayer.md#subscribedstreamer) + +#### Defined in + +[PlayerConnection.ts:33](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L33) + +___ + +### transport + +• **transport**: `ITransport` + +#### Defined in + +[PlayerConnection.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L29) + +## Methods + +### getPlayerInfo + +▸ **getPlayerInfo**(): [`IPlayerInfo`](../interfaces/PlayerRegistry.IPlayerInfo.md) + +Returns a descriptive object for the REST API inspection operations. + +#### Returns + +[`IPlayerInfo`](../interfaces/PlayerRegistry.IPlayerInfo.md) + +An IPlayerInfo object containing viewable information about this connection. + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[getPlayerInfo](../interfaces/PlayerRegistry.IPlayer.md#getplayerinfo) + +#### Defined in + +[PlayerConnection.ts:87](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L87) + +___ + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +Returns an identifier that is displayed in logs. + +#### Returns + +`string` + +A string describing this connection. + +#### Implementation of + +[IMessageLogger](../interfaces/LoggingUtils.IMessageLogger.md).[getReadableIdentifier](../interfaces/LoggingUtils.IMessageLogger.md#getreadableidentifier) + +#### Defined in + +[PlayerConnection.ts:72](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L72) + +___ + +### sendMessage + +▸ **sendMessage**(`message`): `void` + +Sends a signalling message to the player. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `message` | `BaseMessage` | The message to send. | + +#### Returns + +`void` + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[sendMessage](../interfaces/PlayerRegistry.IPlayer.md#sendmessage) + +#### Defined in + +[PlayerConnection.ts:78](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerConnection.ts#L78) diff --git a/Signalling/docs/classes/PlayerRegistry.PlayerRegistry.md b/Signalling/docs/classes/PlayerRegistry.PlayerRegistry.md new file mode 100644 index 00000000..d68e8ede --- /dev/null +++ b/Signalling/docs/classes/PlayerRegistry.PlayerRegistry.md @@ -0,0 +1,188 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [PlayerRegistry](../modules/PlayerRegistry.md) / PlayerRegistry + +# Class: PlayerRegistry + +[PlayerRegistry](../modules/PlayerRegistry.md).PlayerRegistry + +Handles all the player connections of a signalling server and +can be used to lookup connections by id etc. +Fires events when players are added or removed. +Events: + 'added': (playerId: string) Player was added. + 'removed': (playerId: string) Player was removed. + +## Hierarchy + +- `EventEmitter` + + ↳ **`PlayerRegistry`** + +## Table of contents + +### Constructors + +- [constructor](PlayerRegistry.PlayerRegistry.md#constructor) + +### Methods + +- [add](PlayerRegistry.PlayerRegistry.md#add) +- [count](PlayerRegistry.PlayerRegistry.md#count) +- [empty](PlayerRegistry.PlayerRegistry.md#empty) +- [get](PlayerRegistry.PlayerRegistry.md#get) +- [has](PlayerRegistry.PlayerRegistry.md#has) +- [listPlayers](PlayerRegistry.PlayerRegistry.md#listplayers) +- [remove](PlayerRegistry.PlayerRegistry.md#remove) + +## Constructors + +### constructor + +• **new PlayerRegistry**(): [`PlayerRegistry`](PlayerRegistry.PlayerRegistry.md) + +#### Returns + +[`PlayerRegistry`](PlayerRegistry.PlayerRegistry.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[PlayerRegistry.ts:44](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L44) + +## Methods + +### add + +▸ **add**(`player`): `void` + +Assigns a unique id to the player and adds it to the registry + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `player` | [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) | + +#### Returns + +`void` + +#### Defined in + +[PlayerRegistry.ts:54](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L54) + +___ + +### count + +▸ **count**(): `number` + +Gets the total number of connected players. + +#### Returns + +`number` + +#### Defined in + +[PlayerRegistry.ts:107](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L107) + +___ + +### empty + +▸ **empty**(): `boolean` + +Returns true when the registry is empty. + +#### Returns + +`boolean` + +#### Defined in + +[PlayerRegistry.ts:100](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L100) + +___ + +### get + +▸ **get**(`playerId`): [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) + +Gets a player from the registry using the player id. +Returns undefined if the player doesn't exist. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `playerId` | `string` | + +#### Returns + +[`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) + +#### Defined in + +[PlayerRegistry.ts:89](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L89) + +___ + +### has + +▸ **has**(`playerId`): `boolean` + +Tests if a player id exists in the registry. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `playerId` | `string` | + +#### Returns + +`boolean` + +#### Defined in + +[PlayerRegistry.ts:81](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L81) + +___ + +### listPlayers + +▸ **listPlayers**(): [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md)[] + +#### Returns + +[`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md)[] + +#### Defined in + +[PlayerRegistry.ts:93](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L93) + +___ + +### remove + +▸ **remove**(`player`): `void` + +Removes a player from the registry. Does nothing if the id +does not exist. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `player` | [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) | + +#### Returns + +`void` + +#### Defined in + +[PlayerRegistry.ts:66](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L66) diff --git a/Signalling/docs/classes/SFUConnection.SFUConnection.md b/Signalling/docs/classes/SFUConnection.SFUConnection.md new file mode 100644 index 00000000..1213f119 --- /dev/null +++ b/Signalling/docs/classes/SFUConnection.SFUConnection.md @@ -0,0 +1,268 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [SFUConnection](../modules/SFUConnection.md) / SFUConnection + +# Class: SFUConnection + +[SFUConnection](../modules/SFUConnection.md).SFUConnection + +A SFU connection to the signalling server. +An SFU can act as both a streamer and a player. It can subscribe to +streamers like a player, and other players can subscribe to the sfu. +Therefore the SFU will have a streamer id and a player id and be +registered in both streamer registries and player registries. + +Interesting internals: +playerId: The player id of this connectiom. +streamerId: The streamer id of this connection. +transport: The ITransport where transport events can be subscribed to +protocol: The SignallingProtocol where signalling messages can be +subscribed to. +streaming: True when the streamer is ready to accept subscriptions. + +## Hierarchy + +- `EventEmitter` + + ↳ **`SFUConnection`** + +## Implements + +- [`IPlayer`](../interfaces/PlayerRegistry.IPlayer.md) +- [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) +- [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) + +## Table of contents + +### Constructors + +- [constructor](SFUConnection.SFUConnection.md#constructor) + +### Properties + +- [playerId](SFUConnection.SFUConnection.md#playerid) +- [protocol](SFUConnection.SFUConnection.md#protocol) +- [remoteAddress](SFUConnection.SFUConnection.md#remoteaddress) +- [streamerId](SFUConnection.SFUConnection.md#streamerid) +- [streaming](SFUConnection.SFUConnection.md#streaming) +- [subscribedStreamer](SFUConnection.SFUConnection.md#subscribedstreamer) +- [transport](SFUConnection.SFUConnection.md#transport) + +### Methods + +- [getPlayerInfo](SFUConnection.SFUConnection.md#getplayerinfo) +- [getReadableIdentifier](SFUConnection.SFUConnection.md#getreadableidentifier) +- [getStreamerInfo](SFUConnection.SFUConnection.md#getstreamerinfo) +- [sendMessage](SFUConnection.SFUConnection.md#sendmessage) + +## Constructors + +### constructor + +• **new SFUConnection**(`server`, `ws`, `remoteAddress?`): [`SFUConnection`](SFUConnection.SFUConnection.md) + +Construct a new SFU connection. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `server` | [`SignallingServer`](SignallingServer.SignallingServer.md) | The signalling server object that spawned this sfu. | +| `ws` | `WebSocket` | The websocket coupled to this sfu connection. | +| `remoteAddress?` | `string` | The remote address of this connection. Only used as display. | + +#### Returns + +[`SFUConnection`](SFUConnection.SFUConnection.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[SFUConnection.ts:57](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L57) + +## Properties + +### playerId + +• **playerId**: `string` + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[playerId](../interfaces/PlayerRegistry.IPlayer.md#playerid) + +#### Defined in + +[SFUConnection.ts:32](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L32) + +___ + +### protocol + +• **protocol**: `SignallingProtocol` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[protocol](../interfaces/StreamerRegistry.IStreamer.md#protocol) + +#### Defined in + +[SFUConnection.ts:38](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L38) + +___ + +### remoteAddress + +• `Optional` **remoteAddress**: `string` + +#### Defined in + +[SFUConnection.ts:44](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L44) + +___ + +### streamerId + +• **streamerId**: `string` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[streamerId](../interfaces/StreamerRegistry.IStreamer.md#streamerid) + +#### Defined in + +[SFUConnection.ts:34](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L34) + +___ + +### streaming + +• **streaming**: `boolean` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[streaming](../interfaces/StreamerRegistry.IStreamer.md#streaming) + +#### Defined in + +[SFUConnection.ts:40](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L40) + +___ + +### subscribedStreamer + +• **subscribedStreamer**: [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[subscribedStreamer](../interfaces/PlayerRegistry.IPlayer.md#subscribedstreamer) + +#### Defined in + +[SFUConnection.ts:42](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L42) + +___ + +### transport + +• **transport**: `ITransport` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[transport](../interfaces/StreamerRegistry.IStreamer.md#transport) + +#### Defined in + +[SFUConnection.ts:36](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L36) + +## Methods + +### getPlayerInfo + +▸ **getPlayerInfo**(): [`IPlayerInfo`](../interfaces/PlayerRegistry.IPlayerInfo.md) + +Returns a descriptive object for the REST API inspection operations. + +#### Returns + +[`IPlayerInfo`](../interfaces/PlayerRegistry.IPlayerInfo.md) + +An IPlayerInfo object containing viewable information about this connection. + +#### Implementation of + +[IPlayer](../interfaces/PlayerRegistry.IPlayer.md).[getPlayerInfo](../interfaces/PlayerRegistry.IPlayer.md#getplayerinfo) + +#### Defined in + +[SFUConnection.ts:112](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L112) + +___ + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +Returns an identifier that is displayed in logs. + +#### Returns + +`string` + +A string describing this connection. + +#### Implementation of + +[IMessageLogger](../interfaces/LoggingUtils.IMessageLogger.md).[getReadableIdentifier](../interfaces/LoggingUtils.IMessageLogger.md#getreadableidentifier) + +#### Defined in + +[SFUConnection.ts:83](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L83) + +___ + +### getStreamerInfo + +▸ **getStreamerInfo**(): [`IStreamerInfo`](../interfaces/StreamerRegistry.IStreamerInfo.md) + +Returns a descriptive object for the REST API inspection operations. + +#### Returns + +[`IStreamerInfo`](../interfaces/StreamerRegistry.IStreamerInfo.md) + +An IStreamerInfo object containing viewable information about this connection. + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[getStreamerInfo](../interfaces/StreamerRegistry.IStreamer.md#getstreamerinfo) + +#### Defined in + +[SFUConnection.ts:98](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L98) + +___ + +### sendMessage + +▸ **sendMessage**(`message`): `void` + +Sends a signalling message to the SFU. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `message` | `BaseMessage` | The message to send. | + +#### Returns + +`void` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[sendMessage](../interfaces/StreamerRegistry.IStreamer.md#sendmessage) + +#### Defined in + +[SFUConnection.ts:89](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SFUConnection.ts#L89) diff --git a/Signalling/docs/classes/SignallingServer.SignallingServer.md b/Signalling/docs/classes/SignallingServer.SignallingServer.md new file mode 100644 index 00000000..b89f1237 --- /dev/null +++ b/Signalling/docs/classes/SignallingServer.SignallingServer.md @@ -0,0 +1,96 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [SignallingServer](../modules/SignallingServer.md) / SignallingServer + +# Class: SignallingServer + +[SignallingServer](../modules/SignallingServer.md).SignallingServer + +The main signalling server object. +Contains a streamer and player registry and handles setting up of websockets +to listen for incoming connections. + +## Table of contents + +### Constructors + +- [constructor](SignallingServer.SignallingServer.md#constructor) + +### Properties + +- [config](SignallingServer.SignallingServer.md#config) +- [playerRegistry](SignallingServer.SignallingServer.md#playerregistry) +- [protocolConfig](SignallingServer.SignallingServer.md#protocolconfig) +- [startTime](SignallingServer.SignallingServer.md#starttime) +- [streamerRegistry](SignallingServer.SignallingServer.md#streamerregistry) + +## Constructors + +### constructor + +• **new SignallingServer**(`config`): [`SignallingServer`](SignallingServer.SignallingServer.md) + +Initializes the server object and sets up listening sockets for streamers +players and optionally SFU connections. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `config` | [`IServerConfig`](../interfaces/SignallingServer.IServerConfig.md) | A collection of options for this server. | + +#### Returns + +[`SignallingServer`](SignallingServer.SignallingServer.md) + +#### Defined in + +[SignallingServer.ts:86](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L86) + +## Properties + +### config + +• **config**: [`IServerConfig`](../interfaces/SignallingServer.IServerConfig.md) + +#### Defined in + +[SignallingServer.ts:75](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L75) + +___ + +### playerRegistry + +• **playerRegistry**: [`PlayerRegistry`](PlayerRegistry.PlayerRegistry.md) + +#### Defined in + +[SignallingServer.ts:78](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L78) + +___ + +### protocolConfig + +• **protocolConfig**: `any` + +#### Defined in + +[SignallingServer.ts:76](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L76) + +___ + +### startTime + +• **startTime**: `Date` + +#### Defined in + +[SignallingServer.ts:79](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L79) + +___ + +### streamerRegistry + +• **streamerRegistry**: [`StreamerRegistry`](StreamerRegistry.StreamerRegistry.md) + +#### Defined in + +[SignallingServer.ts:77](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L77) diff --git a/Signalling/docs/classes/StreamerConnection.StreamerConnection.md b/Signalling/docs/classes/StreamerConnection.StreamerConnection.md new file mode 100644 index 00000000..a361d8b9 --- /dev/null +++ b/Signalling/docs/classes/StreamerConnection.StreamerConnection.md @@ -0,0 +1,212 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [StreamerConnection](../modules/StreamerConnection.md) / StreamerConnection + +# Class: StreamerConnection + +[StreamerConnection](../modules/StreamerConnection.md).StreamerConnection + +A connection between the signalling server and a streamer connection. +This is where messages expected to be handled by the streamer come in +and where messages are sent to the streamer. + +Interesting internals: +streamerId: The unique id string of this streamer. +transport: The ITransport where transport events can be subscribed to +protocol: The SignallingProtocol where signalling messages can be +subscribed to. +streaming: True when the streamer is ready to accept subscriptions. + +## Hierarchy + +- `EventEmitter` + + ↳ **`StreamerConnection`** + +## Implements + +- [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) +- [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) + +## Table of contents + +### Constructors + +- [constructor](StreamerConnection.StreamerConnection.md#constructor) + +### Properties + +- [protocol](StreamerConnection.StreamerConnection.md#protocol) +- [remoteAddress](StreamerConnection.StreamerConnection.md#remoteaddress) +- [streamerId](StreamerConnection.StreamerConnection.md#streamerid) +- [streaming](StreamerConnection.StreamerConnection.md#streaming) +- [transport](StreamerConnection.StreamerConnection.md#transport) + +### Methods + +- [getReadableIdentifier](StreamerConnection.StreamerConnection.md#getreadableidentifier) +- [getStreamerInfo](StreamerConnection.StreamerConnection.md#getstreamerinfo) +- [sendMessage](StreamerConnection.StreamerConnection.md#sendmessage) + +## Constructors + +### constructor + +• **new StreamerConnection**(`server`, `ws`, `remoteAddress?`): [`StreamerConnection`](StreamerConnection.StreamerConnection.md) + +Initializes a new connection with given and sane values. Adds listeners for the +websocket close and error and will emit a disconnected event when disconneted. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `server` | [`SignallingServer`](SignallingServer.SignallingServer.md) | The signalling server object that spawned this streamer. | +| `ws` | `WebSocket` | The websocket coupled to this streamer connection. | +| `remoteAddress?` | `string` | The remote address of this connection. Only used as display. | + +#### Returns + +[`StreamerConnection`](StreamerConnection.StreamerConnection.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[StreamerConnection.ts:48](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L48) + +## Properties + +### protocol + +• **protocol**: `SignallingProtocol` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[protocol](../interfaces/StreamerRegistry.IStreamer.md#protocol) + +#### Defined in + +[StreamerConnection.ts:33](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L33) + +___ + +### remoteAddress + +• `Optional` **remoteAddress**: `string` + +#### Defined in + +[StreamerConnection.ts:37](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L37) + +___ + +### streamerId + +• **streamerId**: `string` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[streamerId](../interfaces/StreamerRegistry.IStreamer.md#streamerid) + +#### Defined in + +[StreamerConnection.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L29) + +___ + +### streaming + +• **streaming**: `boolean` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[streaming](../interfaces/StreamerRegistry.IStreamer.md#streaming) + +#### Defined in + +[StreamerConnection.ts:35](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L35) + +___ + +### transport + +• **transport**: `ITransport` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[transport](../interfaces/StreamerRegistry.IStreamer.md#transport) + +#### Defined in + +[StreamerConnection.ts:31](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L31) + +## Methods + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +Returns an identifier that is displayed in logs. + +#### Returns + +`string` + +A string describing this connection. + +#### Implementation of + +[IMessageLogger](../interfaces/LoggingUtils.IMessageLogger.md).[getReadableIdentifier](../interfaces/LoggingUtils.IMessageLogger.md#getreadableidentifier) + +#### Defined in + +[StreamerConnection.ts:68](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L68) + +___ + +### getStreamerInfo + +▸ **getStreamerInfo**(): [`IStreamerInfo`](../interfaces/StreamerRegistry.IStreamerInfo.md) + +Returns a descriptive object for the REST API inspection operations. + +#### Returns + +[`IStreamerInfo`](../interfaces/StreamerRegistry.IStreamerInfo.md) + +An IStreamerInfo object containing viewable information about this connection. + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[getStreamerInfo](../interfaces/StreamerRegistry.IStreamer.md#getstreamerinfo) + +#### Defined in + +[StreamerConnection.ts:83](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L83) + +___ + +### sendMessage + +▸ **sendMessage**(`message`): `void` + +Sends a signalling message to the player. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `message` | `BaseMessage` | The message to send. | + +#### Returns + +`void` + +#### Implementation of + +[IStreamer](../interfaces/StreamerRegistry.IStreamer.md).[sendMessage](../interfaces/StreamerRegistry.IStreamer.md#sendmessage) + +#### Defined in + +[StreamerConnection.ts:74](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerConnection.ts#L74) diff --git a/Signalling/docs/classes/StreamerRegistry.StreamerRegistry.md b/Signalling/docs/classes/StreamerRegistry.StreamerRegistry.md new file mode 100644 index 00000000..93f8f5ea --- /dev/null +++ b/Signalling/docs/classes/StreamerRegistry.StreamerRegistry.md @@ -0,0 +1,202 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [StreamerRegistry](../modules/StreamerRegistry.md) / StreamerRegistry + +# Class: StreamerRegistry + +[StreamerRegistry](../modules/StreamerRegistry.md).StreamerRegistry + +Handles all the streamer connections of a signalling server and +can be used to lookup connections by id etc. +Fires events when streamers are added or removed. +Events: + 'added': (playerId: string) Player was added. + 'removed': (playerId: string) Player was removed. + +## Hierarchy + +- `EventEmitter` + + ↳ **`StreamerRegistry`** + +## Table of contents + +### Constructors + +- [constructor](StreamerRegistry.StreamerRegistry.md#constructor) + +### Properties + +- [defaultStreamerIdPrefix](StreamerRegistry.StreamerRegistry.md#defaultstreameridprefix) +- [streamers](StreamerRegistry.StreamerRegistry.md#streamers) + +### Methods + +- [add](StreamerRegistry.StreamerRegistry.md#add) +- [count](StreamerRegistry.StreamerRegistry.md#count) +- [empty](StreamerRegistry.StreamerRegistry.md#empty) +- [find](StreamerRegistry.StreamerRegistry.md#find) +- [getFirstStreamerId](StreamerRegistry.StreamerRegistry.md#getfirststreamerid) +- [remove](StreamerRegistry.StreamerRegistry.md#remove) + +## Constructors + +### constructor + +• **new StreamerRegistry**(): [`StreamerRegistry`](StreamerRegistry.StreamerRegistry.md) + +#### Returns + +[`StreamerRegistry`](StreamerRegistry.StreamerRegistry.md) + +#### Overrides + +EventEmitter.constructor + +#### Defined in + +[StreamerRegistry.ts:44](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L44) + +## Properties + +### defaultStreamerIdPrefix + +• **defaultStreamerIdPrefix**: `string` = `"UnknownStreamer"` + +#### Defined in + +[StreamerRegistry.ts:42](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L42) + +___ + +### streamers + +• **streamers**: [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md)[] + +#### Defined in + +[StreamerRegistry.ts:41](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L41) + +## Methods + +### add + +▸ **add**(`streamer`): `boolean` + +Adds a streamer to the registry. If the streamer already has an id +it will be sanitized (checked against existing ids and altered if +there are collisions), or if it has no id it will be assigned a +default unique id. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `streamer` | [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) | + +#### Returns + +`boolean` + +True if the add was successful. + +#### Defined in + +[StreamerRegistry.ts:56](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L56) + +___ + +### count + +▸ **count**(): `number` + +Returns the total number of connected streamers. + +#### Returns + +`number` + +#### Defined in + +[StreamerRegistry.ts:121](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L121) + +___ + +### empty + +▸ **empty**(): `boolean` + +Returns true when the registry is empty. + +#### Returns + +`boolean` + +#### Defined in + +[StreamerRegistry.ts:114](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L114) + +___ + +### find + +▸ **find**(`streamerId`): [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) + +Attempts to find the given streamer id in the registry. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `streamerId` | `string` | + +#### Returns + +[`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) + +#### Defined in + +[StreamerRegistry.ts:94](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L94) + +___ + +### getFirstStreamerId + +▸ **getFirstStreamerId**(): `string` + +Used by players who haven't subscribed but try to send a message. +This is to cover legacy connections that do not know how to subscribe. +The player will be assigned the first streamer in the list. + +#### Returns + +`string` + +The first streamerId in the registry or null if there are none. + +#### Defined in + +[StreamerRegistry.ts:104](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L104) + +___ + +### remove + +▸ **remove**(`streamer`): `boolean` + +Removes a streamer from the registry. If the streamer isn't found +it does nothing. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `streamer` | [`IStreamer`](../interfaces/StreamerRegistry.IStreamer.md) | + +#### Returns + +`boolean` + +True if the streamer was removed. + +#### Defined in + +[StreamerRegistry.ts:80](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L80) diff --git a/Signalling/docs/classes/WebServer.WebServer.md b/Signalling/docs/classes/WebServer.WebServer.md new file mode 100644 index 00000000..f8423218 --- /dev/null +++ b/Signalling/docs/classes/WebServer.WebServer.md @@ -0,0 +1,60 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [WebServer](../modules/WebServer.md) / WebServer + +# Class: WebServer + +[WebServer](../modules/WebServer.md).WebServer + +An object to manage the initialization of a web server. Used to serve the +pixel streaming frontend. + +## Table of contents + +### Constructors + +- [constructor](WebServer.WebServer.md#constructor) + +### Properties + +- [httpServer](WebServer.WebServer.md#httpserver) +- [httpsServer](WebServer.WebServer.md#httpsserver) + +## Constructors + +### constructor + +• **new WebServer**(`app`, `config`): [`WebServer`](WebServer.WebServer.md) + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `app` | `any` | +| `config` | [`IWebServerConfig`](../interfaces/WebServer.IWebServerConfig.md) | + +#### Returns + +[`WebServer`](WebServer.WebServer.md) + +#### Defined in + +[WebServer.ts:52](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L52) + +## Properties + +### httpServer + +• **httpServer**: `Server`\ + +#### Defined in + +[WebServer.ts:49](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L49) + +___ + +### httpsServer + +• **httpsServer**: `Server`\ + +#### Defined in + +[WebServer.ts:50](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L50) diff --git a/Signalling/docs/interfaces/Logger.IConfig.md b/Signalling/docs/interfaces/Logger.IConfig.md new file mode 100644 index 00000000..ba6e9914 --- /dev/null +++ b/Signalling/docs/interfaces/Logger.IConfig.md @@ -0,0 +1,54 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [Logger](../modules/Logger.md) / IConfig + +# Interface: IConfig + +[Logger](../modules/Logger.md).IConfig + +## Table of contents + +### Properties + +- [logDir](Logger.IConfig.md#logdir) +- [logLevelConsole](Logger.IConfig.md#loglevelconsole) +- [logLevelFile](Logger.IConfig.md#loglevelfile) +- [logMessagesToConsole](Logger.IConfig.md#logmessagestoconsole) + +## Properties + +### logDir + +• `Optional` **logDir**: `string` + +#### Defined in + +[Logger.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L17) + +___ + +### logLevelConsole + +• `Optional` **logLevelConsole**: `string` + +#### Defined in + +[Logger.ts:23](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L23) + +___ + +### logLevelFile + +• `Optional` **logLevelFile**: `string` + +#### Defined in + +[Logger.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L26) + +___ + +### logMessagesToConsole + +• `Optional` **logMessagesToConsole**: `string` + +#### Defined in + +[Logger.ts:20](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L20) diff --git a/Signalling/docs/interfaces/LoggingUtils.IMessageLogger.md b/Signalling/docs/interfaces/LoggingUtils.IMessageLogger.md new file mode 100644 index 00000000..f3110496 --- /dev/null +++ b/Signalling/docs/interfaces/LoggingUtils.IMessageLogger.md @@ -0,0 +1,42 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [LoggingUtils](../modules/LoggingUtils.md) / IMessageLogger + +# Interface: IMessageLogger + +[LoggingUtils](../modules/LoggingUtils.md).IMessageLogger + +Most methods in here rely on connections implementing this interface so we can identify +who is sending or receiving etc. + +## Hierarchy + +- **`IMessageLogger`** + + ↳ [`IPlayer`](PlayerRegistry.IPlayer.md) + + ↳ [`IStreamer`](StreamerRegistry.IStreamer.md) + +## Implemented by + +- [`PlayerConnection`](../classes/PlayerConnection.PlayerConnection.md) +- [`SFUConnection`](../classes/SFUConnection.SFUConnection.md) +- [`StreamerConnection`](../classes/StreamerConnection.StreamerConnection.md) + +## Table of contents + +### Methods + +- [getReadableIdentifier](LoggingUtils.IMessageLogger.md#getreadableidentifier) + +## Methods + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +#### Returns + +`string` + +#### Defined in + +[LoggingUtils.ts:9](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L9) diff --git a/Signalling/docs/interfaces/MatchmakerConnection.IMatchmakerConfig.md b/Signalling/docs/interfaces/MatchmakerConnection.IMatchmakerConfig.md new file mode 100644 index 00000000..bbe4ca92 --- /dev/null +++ b/Signalling/docs/interfaces/MatchmakerConnection.IMatchmakerConfig.md @@ -0,0 +1,87 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [MatchmakerConnection](../modules/MatchmakerConnection.md) / IMatchmakerConfig + +# Interface: IMatchmakerConfig + +[MatchmakerConnection](../modules/MatchmakerConnection.md).IMatchmakerConfig + +## Table of contents + +### Properties + +- [address](MatchmakerConnection.IMatchmakerConfig.md#address) +- [https](MatchmakerConnection.IMatchmakerConfig.md#https) +- [keepAliveInterval](MatchmakerConnection.IMatchmakerConfig.md#keepaliveinterval) +- [port](MatchmakerConnection.IMatchmakerConfig.md#port) +- [publicIp](MatchmakerConnection.IMatchmakerConfig.md#publicip) +- [publicPort](MatchmakerConnection.IMatchmakerConfig.md#publicport) +- [retryInterval](MatchmakerConnection.IMatchmakerConfig.md#retryinterval) + +## Properties + +### address + +• **address**: `string` + +#### Defined in + +[MatchmakerConnection.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L14) + +___ + +### https + +• **https**: `boolean` + +#### Defined in + +[MatchmakerConnection.ts:20](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L20) + +___ + +### keepAliveInterval + +• **keepAliveInterval**: `number` + +#### Defined in + +[MatchmakerConnection.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L26) + +___ + +### port + +• **port**: `number` + +#### Defined in + +[MatchmakerConnection.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L17) + +___ + +### publicIp + +• **publicIp**: `string` + +#### Defined in + +[MatchmakerConnection.ts:8](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L8) + +___ + +### publicPort + +• **publicPort**: `number` + +#### Defined in + +[MatchmakerConnection.ts:11](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L11) + +___ + +### retryInterval + +• **retryInterval**: `number` + +#### Defined in + +[MatchmakerConnection.ts:23](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/MatchmakerConnection.ts#L23) diff --git a/Signalling/docs/interfaces/PlayerRegistry.IPlayer.md b/Signalling/docs/interfaces/PlayerRegistry.IPlayer.md new file mode 100644 index 00000000..331d3aaf --- /dev/null +++ b/Signalling/docs/interfaces/PlayerRegistry.IPlayer.md @@ -0,0 +1,115 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [PlayerRegistry](../modules/PlayerRegistry.md) / IPlayer + +# Interface: IPlayer + +[PlayerRegistry](../modules/PlayerRegistry.md).IPlayer + +An interface that describes a player that can be added to the +player registry. + +## Hierarchy + +- [`IMessageLogger`](LoggingUtils.IMessageLogger.md) + + ↳ **`IPlayer`** + +## Implemented by + +- [`PlayerConnection`](../classes/PlayerConnection.PlayerConnection.md) +- [`SFUConnection`](../classes/SFUConnection.SFUConnection.md) + +## Table of contents + +### Properties + +- [playerId](PlayerRegistry.IPlayer.md#playerid) +- [protocol](PlayerRegistry.IPlayer.md#protocol) +- [subscribedStreamer](PlayerRegistry.IPlayer.md#subscribedstreamer) + +### Methods + +- [getPlayerInfo](PlayerRegistry.IPlayer.md#getplayerinfo) +- [getReadableIdentifier](PlayerRegistry.IPlayer.md#getreadableidentifier) +- [sendMessage](PlayerRegistry.IPlayer.md#sendmessage) + +## Properties + +### playerId + +• **playerId**: `string` + +#### Defined in + +[PlayerRegistry.ts:12](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L12) + +___ + +### protocol + +• **protocol**: `SignallingProtocol` + +#### Defined in + +[PlayerRegistry.ts:13](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L13) + +___ + +### subscribedStreamer + +• **subscribedStreamer**: [`IStreamer`](StreamerRegistry.IStreamer.md) + +#### Defined in + +[PlayerRegistry.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L14) + +## Methods + +### getPlayerInfo + +▸ **getPlayerInfo**(): [`IPlayerInfo`](PlayerRegistry.IPlayerInfo.md) + +#### Returns + +[`IPlayerInfo`](PlayerRegistry.IPlayerInfo.md) + +#### Defined in + +[PlayerRegistry.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L17) + +___ + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +#### Returns + +`string` + +#### Inherited from + +[IMessageLogger](LoggingUtils.IMessageLogger.md).[getReadableIdentifier](LoggingUtils.IMessageLogger.md#getreadableidentifier) + +#### Defined in + +[LoggingUtils.ts:9](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L9) + +___ + +### sendMessage + +▸ **sendMessage**(`message`): `void` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message` | `BaseMessage` | + +#### Returns + +`void` + +#### Defined in + +[PlayerRegistry.ts:16](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L16) diff --git a/Signalling/docs/interfaces/PlayerRegistry.IPlayerInfo.md b/Signalling/docs/interfaces/PlayerRegistry.IPlayerInfo.md new file mode 100644 index 00000000..9f81295e --- /dev/null +++ b/Signalling/docs/interfaces/PlayerRegistry.IPlayerInfo.md @@ -0,0 +1,67 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [PlayerRegistry](../modules/PlayerRegistry.md) / IPlayerInfo + +# Interface: IPlayerInfo + +[PlayerRegistry](../modules/PlayerRegistry.md).IPlayerInfo + +Used by the API to describe the current state of the player. + +## Table of contents + +### Properties + +- [playerId](PlayerRegistry.IPlayerInfo.md#playerid) +- [remoteAddress](PlayerRegistry.IPlayerInfo.md#remoteaddress) +- [sendOffer](PlayerRegistry.IPlayerInfo.md#sendoffer) +- [subscribedTo](PlayerRegistry.IPlayerInfo.md#subscribedto) +- [type](PlayerRegistry.IPlayerInfo.md#type) + +## Properties + +### playerId + +• **playerId**: `string` + +#### Defined in + +[PlayerRegistry.ts:24](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L24) + +___ + +### remoteAddress + +• **remoteAddress**: `string` + +#### Defined in + +[PlayerRegistry.ts:28](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L28) + +___ + +### sendOffer + +• **sendOffer**: `boolean` + +#### Defined in + +[PlayerRegistry.ts:27](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L27) + +___ + +### subscribedTo + +• **subscribedTo**: `string` + +#### Defined in + +[PlayerRegistry.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L26) + +___ + +### type + +• **type**: `string` + +#### Defined in + +[PlayerRegistry.ts:25](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/PlayerRegistry.ts#L25) diff --git a/Signalling/docs/interfaces/SignallingServer.IServerConfig.md b/Signalling/docs/interfaces/SignallingServer.IServerConfig.md new file mode 100644 index 00000000..003e49a5 --- /dev/null +++ b/Signalling/docs/interfaces/SignallingServer.IServerConfig.md @@ -0,0 +1,189 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [SignallingServer](../modules/SignallingServer.md) / IServerConfig + +# Interface: IServerConfig + +[SignallingServer](../modules/SignallingServer.md).IServerConfig + +An interface describing the possible options to pass when creating +a new SignallingServer object. + +## Table of contents + +### Properties + +- [httpServer](SignallingServer.IServerConfig.md#httpserver) +- [httpsServer](SignallingServer.IServerConfig.md#httpsserver) +- [matchmakerAddress](SignallingServer.IServerConfig.md#matchmakeraddress) +- [matchmakerKeepAliveInterval](SignallingServer.IServerConfig.md#matchmakerkeepaliveinterval) +- [matchmakerPort](SignallingServer.IServerConfig.md#matchmakerport) +- [matchmakerRetryInterval](SignallingServer.IServerConfig.md#matchmakerretryinterval) +- [peerOptions](SignallingServer.IServerConfig.md#peeroptions) +- [playerPort](SignallingServer.IServerConfig.md#playerport) +- [playerWsOptions](SignallingServer.IServerConfig.md#playerwsoptions) +- [publicIp](SignallingServer.IServerConfig.md#publicip) +- [publicPort](SignallingServer.IServerConfig.md#publicport) +- [sfuPort](SignallingServer.IServerConfig.md#sfuport) +- [sfuWsOptions](SignallingServer.IServerConfig.md#sfuwsoptions) +- [streamerPort](SignallingServer.IServerConfig.md#streamerport) +- [streamerWsOptions](SignallingServer.IServerConfig.md#streamerwsoptions) +- [useMatchmaker](SignallingServer.IServerConfig.md#usematchmaker) + +## Properties + +### httpServer + +• `Optional` **httpServer**: `Server`\ + +#### Defined in + +[SignallingServer.ts:21](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L21) + +___ + +### httpsServer + +• `Optional` **httpsServer**: `Server`\ + +#### Defined in + +[SignallingServer.ts:24](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L24) + +___ + +### matchmakerAddress + +• `Optional` **matchmakerAddress**: `string` + +#### Defined in + +[SignallingServer.ts:51](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L51) + +___ + +### matchmakerKeepAliveInterval + +• `Optional` **matchmakerKeepAliveInterval**: `number` + +#### Defined in + +[SignallingServer.ts:60](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L60) + +___ + +### matchmakerPort + +• `Optional` **matchmakerPort**: `number` + +#### Defined in + +[SignallingServer.ts:54](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L54) + +___ + +### matchmakerRetryInterval + +• `Optional` **matchmakerRetryInterval**: `number` + +#### Defined in + +[SignallingServer.ts:57](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L57) + +___ + +### peerOptions + +• **peerOptions**: `any` + +#### Defined in + +[SignallingServer.ts:36](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L36) + +___ + +### playerPort + +• `Optional` **playerPort**: `number` + +#### Defined in + +[SignallingServer.ts:30](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L30) + +___ + +### playerWsOptions + +• `Optional` **playerWsOptions**: `any` + +#### Defined in + +[SignallingServer.ts:42](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L42) + +___ + +### publicIp + +• `Optional` **publicIp**: `string` + +#### Defined in + +[SignallingServer.ts:63](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L63) + +___ + +### publicPort + +• `Optional` **publicPort**: `number` + +#### Defined in + +[SignallingServer.ts:66](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L66) + +___ + +### sfuPort + +• `Optional` **sfuPort**: `number` + +#### Defined in + +[SignallingServer.ts:33](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L33) + +___ + +### sfuWsOptions + +• `Optional` **sfuWsOptions**: `any` + +#### Defined in + +[SignallingServer.ts:45](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L45) + +___ + +### streamerPort + +• **streamerPort**: `number` + +#### Defined in + +[SignallingServer.ts:27](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L27) + +___ + +### streamerWsOptions + +• `Optional` **streamerWsOptions**: `any` + +#### Defined in + +[SignallingServer.ts:39](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L39) + +___ + +### useMatchmaker + +• `Optional` **useMatchmaker**: `boolean` + +#### Defined in + +[SignallingServer.ts:48](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/SignallingServer.ts#L48) diff --git a/Signalling/docs/interfaces/StreamerRegistry.IStreamer.md b/Signalling/docs/interfaces/StreamerRegistry.IStreamer.md new file mode 100644 index 00000000..bdef2b72 --- /dev/null +++ b/Signalling/docs/interfaces/StreamerRegistry.IStreamer.md @@ -0,0 +1,128 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [StreamerRegistry](../modules/StreamerRegistry.md) / IStreamer + +# Interface: IStreamer + +[StreamerRegistry](../modules/StreamerRegistry.md).IStreamer + +An interface that describes a streamer that can be added to the +streamer registry. + +## Hierarchy + +- `EventEmitter` + +- [`IMessageLogger`](LoggingUtils.IMessageLogger.md) + + ↳ **`IStreamer`** + +## Implemented by + +- [`SFUConnection`](../classes/SFUConnection.SFUConnection.md) +- [`StreamerConnection`](../classes/StreamerConnection.StreamerConnection.md) + +## Table of contents + +### Properties + +- [protocol](StreamerRegistry.IStreamer.md#protocol) +- [streamerId](StreamerRegistry.IStreamer.md#streamerid) +- [streaming](StreamerRegistry.IStreamer.md#streaming) +- [transport](StreamerRegistry.IStreamer.md#transport) + +### Methods + +- [getReadableIdentifier](StreamerRegistry.IStreamer.md#getreadableidentifier) +- [getStreamerInfo](StreamerRegistry.IStreamer.md#getstreamerinfo) +- [sendMessage](StreamerRegistry.IStreamer.md#sendmessage) + +## Properties + +### protocol + +• **protocol**: `SignallingProtocol` + +#### Defined in + +[StreamerRegistry.ts:14](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L14) + +___ + +### streamerId + +• **streamerId**: `string` + +#### Defined in + +[StreamerRegistry.ts:12](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L12) + +___ + +### streaming + +• **streaming**: `boolean` + +#### Defined in + +[StreamerRegistry.ts:15](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L15) + +___ + +### transport + +• **transport**: `ITransport` + +#### Defined in + +[StreamerRegistry.ts:13](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L13) + +## Methods + +### getReadableIdentifier + +▸ **getReadableIdentifier**(): `string` + +#### Returns + +`string` + +#### Inherited from + +[IMessageLogger](LoggingUtils.IMessageLogger.md).[getReadableIdentifier](LoggingUtils.IMessageLogger.md#getreadableidentifier) + +#### Defined in + +[LoggingUtils.ts:9](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L9) + +___ + +### getStreamerInfo + +▸ **getStreamerInfo**(): [`IStreamerInfo`](StreamerRegistry.IStreamerInfo.md) + +#### Returns + +[`IStreamerInfo`](StreamerRegistry.IStreamerInfo.md) + +#### Defined in + +[StreamerRegistry.ts:18](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L18) + +___ + +### sendMessage + +▸ **sendMessage**(`message`): `void` + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `message` | `BaseMessage` | + +#### Returns + +`void` + +#### Defined in + +[StreamerRegistry.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L17) diff --git a/Signalling/docs/interfaces/StreamerRegistry.IStreamerInfo.md b/Signalling/docs/interfaces/StreamerRegistry.IStreamerInfo.md new file mode 100644 index 00000000..5ae4848c --- /dev/null +++ b/Signalling/docs/interfaces/StreamerRegistry.IStreamerInfo.md @@ -0,0 +1,67 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [StreamerRegistry](../modules/StreamerRegistry.md) / IStreamerInfo + +# Interface: IStreamerInfo + +[StreamerRegistry](../modules/StreamerRegistry.md).IStreamerInfo + +Used by the API to describe a streamer. + +## Table of contents + +### Properties + +- [remoteAddress](StreamerRegistry.IStreamerInfo.md#remoteaddress) +- [streamerId](StreamerRegistry.IStreamerInfo.md#streamerid) +- [streaming](StreamerRegistry.IStreamerInfo.md#streaming) +- [subscribers](StreamerRegistry.IStreamerInfo.md#subscribers) +- [type](StreamerRegistry.IStreamerInfo.md#type) + +## Properties + +### remoteAddress + +• **remoteAddress**: `string` + +#### Defined in + +[StreamerRegistry.ts:28](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L28) + +___ + +### streamerId + +• **streamerId**: `string` + +#### Defined in + +[StreamerRegistry.ts:25](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L25) + +___ + +### streaming + +• **streaming**: `boolean` + +#### Defined in + +[StreamerRegistry.ts:27](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L27) + +___ + +### subscribers + +• **subscribers**: [`IPlayerInfo`](PlayerRegistry.IPlayerInfo.md)[] + +#### Defined in + +[StreamerRegistry.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L29) + +___ + +### type + +• **type**: `string` + +#### Defined in + +[StreamerRegistry.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/StreamerRegistry.ts#L26) diff --git a/Signalling/docs/interfaces/WebServer.IWebServerConfig.md b/Signalling/docs/interfaces/WebServer.IWebServerConfig.md new file mode 100644 index 00000000..078f5e50 --- /dev/null +++ b/Signalling/docs/interfaces/WebServer.IWebServerConfig.md @@ -0,0 +1,101 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / [WebServer](../modules/WebServer.md) / IWebServerConfig + +# Interface: IWebServerConfig + +[WebServer](../modules/WebServer.md).IWebServerConfig + +An interface that describes the possible options to pass to +WebServer. + +## Table of contents + +### Properties + +- [homepageFile](WebServer.IWebServerConfig.md#homepagefile) +- [httpPort](WebServer.IWebServerConfig.md#httpport) +- [httpsPort](WebServer.IWebServerConfig.md#httpsport) +- [https\_redirect](WebServer.IWebServerConfig.md#https_redirect) +- [perMinuteRateLimit](WebServer.IWebServerConfig.md#perminuteratelimit) +- [root](WebServer.IWebServerConfig.md#root) +- [ssl\_cert](WebServer.IWebServerConfig.md#ssl_cert) +- [ssl\_key](WebServer.IWebServerConfig.md#ssl_key) + +## Properties + +### homepageFile + +• **homepageFile**: `string` + +#### Defined in + +[WebServer.ts:26](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L26) + +___ + +### httpPort + +• **httpPort**: `number` + +#### Defined in + +[WebServer.ts:20](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L20) + +___ + +### httpsPort + +• `Optional` **httpsPort**: `number` + +#### Defined in + +[WebServer.ts:32](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L32) + +___ + +### https\_redirect + +• `Optional` **https\_redirect**: `Boolean` + +#### Defined in + +[WebServer.ts:41](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L41) + +___ + +### perMinuteRateLimit + +• `Optional` **perMinuteRateLimit**: `number` + +#### Defined in + +[WebServer.ts:29](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L29) + +___ + +### root + +• **root**: `string` + +#### Defined in + +[WebServer.ts:23](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L23) + +___ + +### ssl\_cert + +• `Optional` **ssl\_cert**: `Buffer` + +#### Defined in + +[WebServer.ts:38](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L38) + +___ + +### ssl\_key + +• `Optional` **ssl\_key**: `Buffer` + +#### Defined in + +[WebServer.ts:35](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/WebServer.ts#L35) diff --git a/Signalling/docs/modules/Logger.md b/Signalling/docs/modules/Logger.md new file mode 100644 index 00000000..672e5561 --- /dev/null +++ b/Signalling/docs/modules/Logger.md @@ -0,0 +1,54 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / Logger + +# Module: Logger + +## Table of contents + +### Interfaces + +- [IConfig](../interfaces/Logger.IConfig.md) + +### Variables + +- [Logger](Logger.md#logger) + +### Functions + +- [InitLogging](Logger.md#initlogging) + +## Variables + +### Logger + +• **Logger**: `Logger` + +The actual logger object. This is just a winston logger. +You can use InitLogging to get a decent result, or you can +completely create your own winston logger and assign it. + +#### Defined in + +[Logger.ts:13](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L13) + +## Functions + +### InitLogging + +▸ **InitLogging**(`config`): `void` + +Call this as early as possible to setup the logging module with your +preferred settings. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `config` | [`IConfig`](../interfaces/Logger.IConfig.md) | The settings to init the logger with. See IConfig interface | + +#### Returns + +`void` + +#### Defined in + +[Logger.ts:34](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Logger.ts#L34) diff --git a/Signalling/docs/modules/LoggingUtils.md b/Signalling/docs/modules/LoggingUtils.md new file mode 100644 index 00000000..dddde186 --- /dev/null +++ b/Signalling/docs/modules/LoggingUtils.md @@ -0,0 +1,129 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / LoggingUtils + +# Module: LoggingUtils + +## Table of contents + +### Interfaces + +- [IMessageLogger](../interfaces/LoggingUtils.IMessageLogger.md) + +### Functions + +- [createHandlerListener](LoggingUtils.md#createhandlerlistener) +- [logForward](LoggingUtils.md#logforward) +- [logIncoming](LoggingUtils.md#logincoming) +- [logOutgoing](LoggingUtils.md#logoutgoing) + +## Functions + +### createHandlerListener + +▸ **createHandlerListener**(`obj`, `handler`): (`message`: `BaseMessage`) => `void` + +We don't want to log every incoming and outgoing messages. This is because some messages are simply +forwarded to other connections. This results in duplicated spam. So we only want to log incoming +messages that we handle internally, and any messages that we forward we only log once for the recv +and send events. +This creation method allows a simple way to enforce this. Any events we handle directly will +be preceded by the logging call. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `obj` | [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) | +| `handler` | (`message`: `any`) => `void` | + +#### Returns + +`fn` + +▸ (`message`): `void` + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `message` | `BaseMessage` | + +##### Returns + +`void` + +#### Defined in + +[LoggingUtils.ts:64](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L64) + +___ + +### logForward + +▸ **logForward**(`recvr`, `target`, `message`): `void` + +Call this for messages being forwarded to this connection. That is messages received on +one connection and being sent to another with only minor changes being made. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `recvr` | [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) | The connection the message was received on. | +| `target` | [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) | The connection the message is being sent to. | +| `message` | `BaseMessage` | - | + +#### Returns + +`void` + +#### Defined in + +[LoggingUtils.ts:46](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L46) + +___ + +### logIncoming + +▸ **logIncoming**(`recvr`, `message`): `void` + +Call to log messages received on a connection that we will handle here at the server. +Do not call this for messages being forwarded to another connection. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `recvr` | [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) | IMessageLogger The connection the message was received on. | +| `message` | `BaseMessage` | - | + +#### Returns + +`void` + +#### Defined in + +[LoggingUtils.ts:17](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L17) + +___ + +### logOutgoing + +▸ **logOutgoing**(`sender`, `message`): `void` + +Call to log messages created here at the server and being sent to the connection. +Do not call this for messages being forwarded to this connection. + +#### Parameters + +| Name | Type | Description | +| :------ | :------ | :------ | +| `sender` | [`IMessageLogger`](../interfaces/LoggingUtils.IMessageLogger.md) | IMessageLogger The connection the message is being sent to. | +| `message` | `BaseMessage` | - | + +#### Returns + +`void` + +#### Defined in + +[LoggingUtils.ts:31](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/LoggingUtils.ts#L31) diff --git a/Signalling/docs/modules/MatchmakerConnection.md b/Signalling/docs/modules/MatchmakerConnection.md new file mode 100644 index 00000000..6c885e2a --- /dev/null +++ b/Signalling/docs/modules/MatchmakerConnection.md @@ -0,0 +1,13 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / MatchmakerConnection + +# Module: MatchmakerConnection + +## Table of contents + +### Classes + +- [MatchmakerConnection](../classes/MatchmakerConnection.MatchmakerConnection.md) + +### Interfaces + +- [IMatchmakerConfig](../interfaces/MatchmakerConnection.IMatchmakerConfig.md) diff --git a/Signalling/docs/modules/PlayerConnection.md b/Signalling/docs/modules/PlayerConnection.md new file mode 100644 index 00000000..0609a94e --- /dev/null +++ b/Signalling/docs/modules/PlayerConnection.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / PlayerConnection + +# Module: PlayerConnection + +## Table of contents + +### Classes + +- [PlayerConnection](../classes/PlayerConnection.PlayerConnection.md) diff --git a/Signalling/docs/modules/PlayerRegistry.md b/Signalling/docs/modules/PlayerRegistry.md new file mode 100644 index 00000000..e865cb80 --- /dev/null +++ b/Signalling/docs/modules/PlayerRegistry.md @@ -0,0 +1,14 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / PlayerRegistry + +# Module: PlayerRegistry + +## Table of contents + +### Classes + +- [PlayerRegistry](../classes/PlayerRegistry.PlayerRegistry.md) + +### Interfaces + +- [IPlayer](../interfaces/PlayerRegistry.IPlayer.md) +- [IPlayerInfo](../interfaces/PlayerRegistry.IPlayerInfo.md) diff --git a/Signalling/docs/modules/SFUConnection.md b/Signalling/docs/modules/SFUConnection.md new file mode 100644 index 00000000..f51375ce --- /dev/null +++ b/Signalling/docs/modules/SFUConnection.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / SFUConnection + +# Module: SFUConnection + +## Table of contents + +### Classes + +- [SFUConnection](../classes/SFUConnection.SFUConnection.md) diff --git a/Signalling/docs/modules/SignallingServer.md b/Signalling/docs/modules/SignallingServer.md new file mode 100644 index 00000000..42981d1f --- /dev/null +++ b/Signalling/docs/modules/SignallingServer.md @@ -0,0 +1,13 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / SignallingServer + +# Module: SignallingServer + +## Table of contents + +### Classes + +- [SignallingServer](../classes/SignallingServer.SignallingServer.md) + +### Interfaces + +- [IServerConfig](../interfaces/SignallingServer.IServerConfig.md) diff --git a/Signalling/docs/modules/StreamerConnection.md b/Signalling/docs/modules/StreamerConnection.md new file mode 100644 index 00000000..1ef35ccc --- /dev/null +++ b/Signalling/docs/modules/StreamerConnection.md @@ -0,0 +1,9 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / StreamerConnection + +# Module: StreamerConnection + +## Table of contents + +### Classes + +- [StreamerConnection](../classes/StreamerConnection.StreamerConnection.md) diff --git a/Signalling/docs/modules/StreamerRegistry.md b/Signalling/docs/modules/StreamerRegistry.md new file mode 100644 index 00000000..f3c2fe0d --- /dev/null +++ b/Signalling/docs/modules/StreamerRegistry.md @@ -0,0 +1,14 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / StreamerRegistry + +# Module: StreamerRegistry + +## Table of contents + +### Classes + +- [StreamerRegistry](../classes/StreamerRegistry.StreamerRegistry.md) + +### Interfaces + +- [IStreamer](../interfaces/StreamerRegistry.IStreamer.md) +- [IStreamerInfo](../interfaces/StreamerRegistry.IStreamerInfo.md) diff --git a/Signalling/docs/modules/Utils.md b/Signalling/docs/modules/Utils.md new file mode 100644 index 00000000..fd6184bb --- /dev/null +++ b/Signalling/docs/modules/Utils.md @@ -0,0 +1,54 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / Utils + +# Module: Utils + +## Table of contents + +### Functions + +- [beautify](Utils.md#beautify) +- [stringify](Utils.md#stringify) + +## Functions + +### beautify + +▸ **beautify**(`obj`): `string` + +Circular reference save version of JSON.stringify with extra formatting. + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `obj` | `any` | + +#### Returns + +`string` + +#### Defined in + +[Utils.ts:13](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Utils.ts#L13) + +___ + +### stringify + +▸ **stringify**(`obj`): `string` + +Cirular reference safe version of JSON.stringify + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `obj` | `any` | + +#### Returns + +`string` + +#### Defined in + +[Utils.ts:6](https://github.com/mcottontensor/PixelStreamingInfrastructure/blob/1d8a258/Signalling/src/Utils.ts#L6) diff --git a/Signalling/docs/modules/WebServer.md b/Signalling/docs/modules/WebServer.md new file mode 100644 index 00000000..ecb28772 --- /dev/null +++ b/Signalling/docs/modules/WebServer.md @@ -0,0 +1,13 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / WebServer + +# Module: WebServer + +## Table of contents + +### Classes + +- [WebServer](../classes/WebServer.WebServer.md) + +### Interfaces + +- [IWebServerConfig](../interfaces/WebServer.IWebServerConfig.md) diff --git a/Signalling/docs/modules/pixelstreamingsignalling.md b/Signalling/docs/modules/pixelstreamingsignalling.md new file mode 100644 index 00000000..bde396df --- /dev/null +++ b/Signalling/docs/modules/pixelstreamingsignalling.md @@ -0,0 +1,155 @@ +[@epicgames-ps/lib-pixelstreamingsignalling-ue5.5](../README.md) / pixelstreamingsignalling + +# Module: pixelstreamingsignalling + +## Table of contents + +### References + +- [IConfig](pixelstreamingsignalling.md#iconfig) +- [IMessageLogger](pixelstreamingsignalling.md#imessagelogger) +- [IPlayer](pixelstreamingsignalling.md#iplayer) +- [IPlayerInfo](pixelstreamingsignalling.md#iplayerinfo) +- [IServerConfig](pixelstreamingsignalling.md#iserverconfig) +- [IStreamer](pixelstreamingsignalling.md#istreamer) +- [IStreamerInfo](pixelstreamingsignalling.md#istreamerinfo) +- [IWebServerConfig](pixelstreamingsignalling.md#iwebserverconfig) +- [InitLogging](pixelstreamingsignalling.md#initlogging) +- [Logger](pixelstreamingsignalling.md#logger) +- [PlayerConnection](pixelstreamingsignalling.md#playerconnection) +- [PlayerRegistry](pixelstreamingsignalling.md#playerregistry) +- [SFUConnection](pixelstreamingsignalling.md#sfuconnection) +- [SignallingServer](pixelstreamingsignalling.md#signallingserver) +- [StreamerConnection](pixelstreamingsignalling.md#streamerconnection) +- [StreamerRegistry](pixelstreamingsignalling.md#streamerregistry) +- [WebServer](pixelstreamingsignalling.md#webserver) +- [createHandlerListener](pixelstreamingsignalling.md#createhandlerlistener) +- [logForward](pixelstreamingsignalling.md#logforward) +- [logIncoming](pixelstreamingsignalling.md#logincoming) +- [logOutgoing](pixelstreamingsignalling.md#logoutgoing) + +## References + +### IConfig + +Re-exports [IConfig](../interfaces/Logger.IConfig.md) + +___ + +### IMessageLogger + +Re-exports [IMessageLogger](../interfaces/LoggingUtils.IMessageLogger.md) + +___ + +### IPlayer + +Re-exports [IPlayer](../interfaces/PlayerRegistry.IPlayer.md) + +___ + +### IPlayerInfo + +Re-exports [IPlayerInfo](../interfaces/PlayerRegistry.IPlayerInfo.md) + +___ + +### IServerConfig + +Re-exports [IServerConfig](../interfaces/SignallingServer.IServerConfig.md) + +___ + +### IStreamer + +Re-exports [IStreamer](../interfaces/StreamerRegistry.IStreamer.md) + +___ + +### IStreamerInfo + +Re-exports [IStreamerInfo](../interfaces/StreamerRegistry.IStreamerInfo.md) + +___ + +### IWebServerConfig + +Re-exports [IWebServerConfig](../interfaces/WebServer.IWebServerConfig.md) + +___ + +### InitLogging + +Re-exports [InitLogging](Logger.md#initlogging) + +___ + +### Logger + +Re-exports [Logger](Logger.md#logger) + +___ + +### PlayerConnection + +Re-exports [PlayerConnection](../classes/PlayerConnection.PlayerConnection.md) + +___ + +### PlayerRegistry + +Re-exports [PlayerRegistry](../classes/PlayerRegistry.PlayerRegistry.md) + +___ + +### SFUConnection + +Re-exports [SFUConnection](../classes/SFUConnection.SFUConnection.md) + +___ + +### SignallingServer + +Re-exports [SignallingServer](../classes/SignallingServer.SignallingServer.md) + +___ + +### StreamerConnection + +Re-exports [StreamerConnection](../classes/StreamerConnection.StreamerConnection.md) + +___ + +### StreamerRegistry + +Re-exports [StreamerRegistry](../classes/StreamerRegistry.StreamerRegistry.md) + +___ + +### WebServer + +Re-exports [WebServer](../classes/WebServer.WebServer.md) + +___ + +### createHandlerListener + +Re-exports [createHandlerListener](LoggingUtils.md#createhandlerlistener) + +___ + +### logForward + +Re-exports [logForward](LoggingUtils.md#logforward) + +___ + +### logIncoming + +Re-exports [logIncoming](LoggingUtils.md#logincoming) + +___ + +### logOutgoing + +Re-exports [logOutgoing](LoggingUtils.md#logoutgoing) diff --git a/Signalling/package-lock.json b/Signalling/package-lock.json new file mode 100644 index 00000000..146b2183 --- /dev/null +++ b/Signalling/package-lock.json @@ -0,0 +1,4155 @@ +{ + "name": "@epicgames-ps/lib-pixelstreamingsignalling-ue5.5", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@epicgames-ps/lib-pixelstreamingsignalling-ue5.5", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "commander": "^12.0.0", + "express": "^4.18.2", + "express-openapi": "^12.1.3", + "express-rate-limit": "^7.1.5", + "helmet": "^7.1.0", + "hsts": "^2.2.0", + "jsonc": "^2.0.0", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^4.7.1", + "ws": "^8.16.0" + }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.11.13", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.56.0", + "eslint-plugin-tsdoc": "^0.2.17", + "nodemon": "^3.0.3", + "openapi-typescript": "^6.7.4", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", + "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "node_modules/commander": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/difunc": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/difunc/-/difunc-0.0.4.tgz", + "integrity": "sha512-zBiL4ALDmviHdoLC0g0G6wVme5bwAow9WfhcZLLopXCAWgg3AEf7RYTs2xugszIGulRHzEVDF/SHl9oyQU07Pw==", + "dependencies": { + "esprima": "^4.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-normalize-query-params-middleware": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/express-normalize-query-params-middleware/-/express-normalize-query-params-middleware-0.5.1.tgz", + "integrity": "sha512-KUBjEukYL9KJkrphVX3ZgMHgMTdgaSJe+FIOeWwJIJpCw8UZQPIylt0MYddSyUwbms4LQ8RC4wmavcLUP9uduA==" + }, + "node_modules/express-openapi": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/express-openapi/-/express-openapi-12.1.3.tgz", + "integrity": "sha512-F570dVC5ENSkLu1SpDFPRQ13Y3a/7Udh0rfHyn3O1QrE81fPmlhnAo1JRgoNtbMRJ6goHNymxU1TVSllgFZBlQ==", + "dependencies": { + "express-normalize-query-params-middleware": "^0.5.0", + "openapi-framework": "^12.1.3", + "openapi-types": "^12.1.3" + } + }, + "node_modules/express-rate-limit": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.1.5.tgz", + "integrity": "sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.0.tgz", + "integrity": "sha512-noqGuLw158+DuD9UPRKHpJ2hGxpFyDlYYrfM0mWt4XhT4n0lwzTLh70Tkdyy4kyTmyTT9Bv7bWAJqw7cgkEXDg==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-routes": { + "name": "epic-fs-routes", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/epic-fs-routes/-/epic-fs-routes-1.0.0.tgz", + "integrity": "sha512-vWpJeL9K5PQFznGEdOnHXzq9Y4+fC6NVpJ+qtPPyhXeeXkTaq2trQipOj1zLOmWxT+4Koa16MNzOma3x7KQV/g==", + "dependencies": { + "typescript": "^5.3.3" + }, + "peerDependencies": { + "glob": ">=7.1.6" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.1.0.tgz", + "integrity": "sha512-g+HZqgfbpXdCkme/Cd/mZkV0aV3BZZZSugecH03kl38m/Kmdx8jKjBikpDj2cr+Iynv4KpYEviojNdTJActJAg==", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/hsts": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", + "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", + "dependencies": { + "depd": "2.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-dir/-/is-dir-1.0.0.tgz", + "integrity": "sha512-vLwCNpTNkFC5k7SBRxPubhOCryeulkOsSkjbGyZ8eOzZmzMS+hSEO/Kn9ZOVhFNAlRZTFc4ZKql48hESuYUPIQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jsonc/-/jsonc-2.0.0.tgz", + "integrity": "sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==", + "dependencies": { + "fast-safe-stringify": "^2.0.6", + "graceful-fs": "^4.1.15", + "mkdirp": "^0.5.1", + "parse-json": "^4.0.0", + "strip-bom": "^4.0.0", + "strip-json-comments": "^3.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nodemon": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/openapi-default-setter": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-default-setter/-/openapi-default-setter-12.1.3.tgz", + "integrity": "sha512-wHKwvEuOWwke5WcQn8pyCTXT5WQ+rm9FpJmDeEVECEBWjEyB/MVLYfXi+UQeSHTTu2Tg4VDHHmzbjOqN6hYeLQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-framework": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-framework/-/openapi-framework-12.1.3.tgz", + "integrity": "sha512-p30PHWVXda9gGxm+t/1X2XvEcufW1YhzeDQwc5SsgDnBXt8gkuu1SwrioGJ66wxVYEzfSRTTf/FMLhI49ut8fQ==", + "dependencies": { + "difunc": "0.0.4", + "fs-routes": "^12.1.3", + "glob": "*", + "is-dir": "^1.0.0", + "js-yaml": "^3.10.0", + "openapi-default-setter": "^12.1.3", + "openapi-request-coercer": "^12.1.3", + "openapi-request-validator": "^12.1.3", + "openapi-response-validator": "^12.1.3", + "openapi-schema-validator": "^12.1.3", + "openapi-security-handler": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-framework/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/openapi-framework/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/openapi-jsonschema-parameters": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.1.3.tgz", + "integrity": "sha512-aHypKxWHwu2lVqfCIOCZeJA/2NTDiP63aPwuoIC+5ksLK5/IQZ3oKTz7GiaIegz5zFvpMDxDvLR2DMQQSkOAug==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-request-coercer": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-coercer/-/openapi-request-coercer-12.1.3.tgz", + "integrity": "sha512-CT2ZDhBmAZpHhAzHhEN+/J5oMK3Ds99ayLLdXh2Aw1DCcn72EM8VuIGVwG5fSjvkMsgtn7FgltFosHqeM6PRFQ==", + "dependencies": { + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.1.3.tgz", + "integrity": "sha512-HW1sG00A9Hp2oS5g8CBvtaKvRAc4h5E4ksmuC5EJgmQ+eAUacL7g+WaYCrC7IfoQaZrjxDfeivNZUye/4D8pwA==", + "dependencies": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-request-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-response-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-response-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-schema-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz", + "integrity": "sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==", + "dependencies": { + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", + "lodash.merge": "^4.6.1", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-schema-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-schema-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-security-handler": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-security-handler/-/openapi-security-handler-12.1.3.tgz", + "integrity": "sha512-25UTAflxqqpjCLrN6rRhINeM1L+MCDixMltiAqtBa9Zz/i7UkWwYwdzqgZY3Cx3vRZElFD09brYxo5VleeP3HQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, + "node_modules/openapi-typescript": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.4.tgz", + "integrity": "sha512-EZyeW9Wy7UDCKv0iYmKrq2pVZtquXiD/YHiUClAKqiMi42nodx/EQH11K6fLqjt1IZlJmVokrAsExsBMM2RROQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "fast-glob": "^3.3.2", + "js-yaml": "^4.1.0", + "supports-color": "^9.4.0", + "undici": "^5.28.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedoc": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz", + "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" + } + }, + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", + "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", + "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", + "dependencies": { + "file-stream-rotator": "^0.6.1", + "object-hash": "^2.0.1", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" + } + }, + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/Signalling/package.json b/Signalling/package.json new file mode 100644 index 00000000..1e3628c7 --- /dev/null +++ b/Signalling/package.json @@ -0,0 +1,52 @@ +{ + "name": "@epicgames-ps/lib-pixelstreamingsignalling-ue5.5", + "version": "1.0.0", + "description": "Basic signalling library for developers wishing to build applications that signal a Pixel Streaming application.", + "main": "build/commonjs/pixelstreamingsignalling.js", + "module": "build/esm/pixelstreamingsignalling.js", + "types": "build/types/pixelstreamingsignalling.d.ts", + "scripts": { + "build": "tsc --project tsconfig.esm.json && tsc --project tsconfig.cjs.json", + "build-dev": "npm link ../Common && npm run build", + "build-all": "rimraf ./build && cd ../Common && npm run build-dev && cd ../Signalling && npm run build-dev", + "build-docs": "typedoc --tsconfig tsconfig.base.json", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Epic Games", + "license": "MIT", + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.11.13", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.56.0", + "eslint-plugin-tsdoc": "^0.2.17", + "nodemon": "^3.0.3", + "openapi-typescript": "^6.7.4", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.3.3" + }, + "dependencies": { + "commander": "^12.0.0", + "express": "^4.18.2", + "express-openapi": "^12.1.3", + "express-rate-limit": "^7.1.5", + "helmet": "^7.1.0", + "jsonc": "^2.0.0", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^4.7.1", + "ws": "^8.16.0" + }, + "overrides": { + "express-openapi": { + "fs-routes": "npm:epic-fs-routes@1.0.0" + } + }, + "repository": { + "type": "git", + "url": "https://github.com/EpicGames/PixelStreamingInfrastructure.git" + } +} diff --git a/Signalling/src/Logger.ts b/Signalling/src/Logger.ts new file mode 100644 index 00000000..3bd6fc02 --- /dev/null +++ b/Signalling/src/Logger.ts @@ -0,0 +1,142 @@ +import { stringify, beautify } from './Utils'; +import path from 'path'; +import winston from 'winston'; +import 'winston-daily-rotate-file'; + +const { combine, timestamp, printf, colorize, splat } = winston.format; + +/** + * The actual logger object. This is just a winston logger. + * You can use InitLogging to get a decent result, or you can + * completely create your own winston logger and assign it. + */ +export let Logger = createDefaultLogger(); + +export interface IConfig { + // the directory to store log files + logDir?: string; + + // if true, every message will be logged to the console in a condensed form + logMessagesToConsole?: string; + + // the minimum log level for console messages + logLevelConsole?: string; + + // the minimum log level for file messages + logLevelFile?: string; +} + +/** + * Call this as early as possible to setup the logging module with your + * preferred settings. + * @param config - The settings to init the logger with. See IConfig interface + */ +export function InitLogging(config: IConfig): void { + const logDir = config.logDir || 'logs'; + const logLevelConsole = config.logLevelConsole || 'info'; + const logLevelFile = config.logLevelFile || 'info'; + logMessagesToConsole = config.logMessagesToConsole || 'none'; + + Logger = winston.createLogger({ + transports: [ + createConsoleTransport(logLevelConsole), + createFileTransport(logDir, logLevelFile), + ], + }); +} + +let logMessagesToConsole = 'none'; + +function createDefaultLogger() { + return winston.createLogger({ + level: 'info', + format: winston.format.cli(), + transports: [new winston.transports.Console()], + }) +} + +function createConsoleTransport(logLevel: string) { + return new winston.transports.Console({ + level: logLevel, + format: combine( + createProtoMessageFilter()(), + timestamp({ format: 'HH:mm:ss.SSS' }), + colorize(), + splat(), + createConsoleFormat(), + ), + }); +} + +function createConsoleFormat() { + return printf((logObj: any) => { + const prefix = `[${logObj.timestamp}] ${logObj.level}: `; + if (typeof logObj.message === 'string') { + return prefix + logObj.message; + } else if (logObj.message.event && logObj.message.event == 'proto_message') { + const { direction, receiver, sender, target, protoMessage } = logObj.message; + switch (direction) { + case 'incoming': return prefix + `> ${receiver} :: ${formatMessageForConsole(protoMessage)}`; + case 'outgoing': return prefix + `< ${sender} :: ${formatMessageForConsole(protoMessage)}`; + case 'forward': return prefix + `${receiver} > ${target} :: ${formatMessageForConsole(protoMessage)}`; + default: return prefix + `Unknown proto direction: ${direction}`; + } + } + return ''; + }); +} + +function formatMessageForConsole(message: any) { + switch (logMessagesToConsole) { + case 'verbose': return stringify(message); + case 'formatted': return beautify(message); + default: return `[${message.type}]`; + } +} + +function createProtoMessageFilter() { + return winston.format((info: any, _opts: any) => { + if (typeof info.message !== 'string' + && info.message.event == 'proto_message' + && logMessagesToConsole == 'none') { + return false; + } + return info; + }); +} + +function createFileTransport(logDirPath: string, logLevel: string) { + return new winston.transports.DailyRotateFile({ + level: logLevel, + filename: path.join(logDirPath, 'server-%DATE%.log'), + datePattern: 'YYYY-MM-DD', + maxFiles: '14d', + format: combine( + timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), + splat(), + createFileFormat(), + ), + }); +} + +function createFileFormat() { + return printf((logObj: any) => { + if (typeof logObj.message === 'string') { + const { timestamp, level, message } = logObj; + return JSON.stringify({ + timestamp, + level, + event: 'message', + message, + }); + } else if (logObj.message.event && logObj.message.event == 'proto_message') { + const { timestamp, level, message } = logObj; + return JSON.stringify({ + timestamp, + level, + ...message + }); + } + return ''; + }); +} diff --git a/Signalling/src/LoggingUtils.ts b/Signalling/src/LoggingUtils.ts new file mode 100644 index 00000000..a540a010 --- /dev/null +++ b/Signalling/src/LoggingUtils.ts @@ -0,0 +1,69 @@ +import { BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { Logger } from './Logger'; + +/** + * Most methods in here rely on connections implementing this interface so we can identify + * who is sending or receiving etc. + */ +export interface IMessageLogger { + getReadableIdentifier(): string; +} + +/** + * Call to log messages received on a connection that we will handle here at the server. + * Do not call this for messages being forwarded to another connection. + * @param recvr - IMessageLogger The connection the message was received on. + */ +export function logIncoming(recvr: IMessageLogger, message: BaseMessage): void { + Logger.info({ + event: 'proto_message', + direction: 'incoming', + receiver: recvr.getReadableIdentifier(), + protoMessage: message + }); +} + +/** + * Call to log messages created here at the server and being sent to the connection. + * Do not call this for messages being forwarded to this connection. + * @param sender - IMessageLogger The connection the message is being sent to. + */ +export function logOutgoing(sender: IMessageLogger, message: BaseMessage): void { + Logger.info({ + event: 'proto_message', + direction: 'outgoing', + sender: sender.getReadableIdentifier(), + protoMessage: message + }); +} + +/** + * Call this for messages being forwarded to this connection. That is messages received on + * one connection and being sent to another with only minor changes being made. + * @param recvr - The connection the message was received on. + * @param target - The connection the message is being sent to. + */ +export function logForward(recvr: IMessageLogger, target: IMessageLogger, message: BaseMessage): void { + Logger.info({ + event: 'proto_message', + direction: 'forward', + receiver: recvr.getReadableIdentifier(), + target: target.getReadableIdentifier(), + protoMessage: message + }); +} + +/** + * We don't want to log every incoming and outgoing messages. This is because some messages are simply + * forwarded to other connections. This results in duplicated spam. So we only want to log incoming + * messages that we handle internally, and any messages that we forward we only log once for the recv + * and send events. + * This creation method allows a simple way to enforce this. Any events we handle directly will + * be preceded by the logging call. + */ +export function createHandlerListener(obj: IMessageLogger, handler: (message: any) => void) { + return (message: BaseMessage) => { + logIncoming(obj, message); + handler.bind(obj)(message); + }; +} diff --git a/Signalling/src/MatchmakerConnection.ts b/Signalling/src/MatchmakerConnection.ts new file mode 100644 index 00000000..595e30ef --- /dev/null +++ b/Signalling/src/MatchmakerConnection.ts @@ -0,0 +1,133 @@ +import net from 'net'; +import { StreamerRegistry } from './StreamerRegistry' +import { PlayerRegistry } from './PlayerRegistry' +import { Logger } from './Logger'; + +export interface IMatchmakerConfig { + // The public ip/hostname of the signalling server + publicIp: string; + + // The port that the signalling server is running on + publicPort: number; + + // The ip/hostname of the matchmaker application + address: string; + + // The port the matchmaker is listening on for signalling connections. + port: number; + + // Is the signalling server using https? + https: boolean; + + // Delay in seconds before attempting a reconnection after a connection loss. + retryInterval: number; + + // Delay in seconds between keep alive pings to the matchmaker. + keepAliveInterval: number; +} + +/** + * This class handles connecting and reconnecting to a matchmaker service and + * will notify the matchmaker of streamer and player added/removed events that + * are emitted from the respective registries. + */ +export class MatchmakerConnection { + // The supplied configuration for the matchmaker connection. + config: IMatchmakerConfig; + private socket: net.Socket; + private streamerRegistry: StreamerRegistry; + private playerRegistry: PlayerRegistry; + + /** + * Initializes the matchmaker connection and attempts a connection to the given + * address and port immediately. Will automatically try to reconnect on connection + * loss. + * @param config - The matchmaker configuration. + * @param streamerRegistry - The signalling server streamer registry. This is used to notify the matchmaker of streamers being added/removed. + * @param playerRegistry - The signalling server player registry. This is used to notify the matchmaker of players being added/removed. + */ + constructor(config: IMatchmakerConfig, streamerRegistry: StreamerRegistry, playerRegistry: PlayerRegistry) { + this.config = config; + this.socket = new net.Socket(); + this.streamerRegistry = streamerRegistry; + this.playerRegistry = playerRegistry; + + this.socket.on('connect', this.onConnected.bind(this)); + this.socket.on('error', this.onError.bind(this)); + this.socket.on('end', this.onEnded.bind(this)); + this.socket.on('close', this.onClosed.bind(this)); + + streamerRegistry.on('added', this.onStreamerAdded.bind(this)); + streamerRegistry.on('removed', this.onStreamerRemoved.bind(this)); + + playerRegistry.on('added', this.onPlayerAdded.bind(this)); + playerRegistry.on('removed', this.onPlayerRemoved.bind(this)); + + this.connect(); + } + + private connect(): void { + Logger.info(`Matchmaker connecting to ${this.config.address}:${this.config.port}`) + this.socket.connect(this.config.port, this.config.address); + } + + private onConnected(): void { + Logger.info(`Matchmaker connected.`); + this.startKeepAlive(); + + const message = { + type: 'connect', + address: this.config.publicIp, + port: this.config.publicPort, + https: this.config.https, + ready: !this.streamerRegistry.empty(), + playerConnected: !this.playerRegistry.empty() + }; + + this.socket.write(JSON.stringify(message)); + } + + private onError(): void { + Logger.info(`Matchmaker connection error.`); + } + + private onEnded(): void { + Logger.info(`Matchmaker connection ended.`); + } + + private onClosed(): void { + Logger.info(`Matchmaker connection closed`); + this.socket.setKeepAlive(true, 60000); // Keeps it alive for 60 seconds + setTimeout(() => { + Logger.info(`Attempting matchmaker reconnection...`); + this.connect(); + }, this.config.retryInterval * 1000); + } + + private startKeepAlive(): void { + setInterval(() => { + const message = { type: 'ping' }; + this.socket.write(JSON.stringify(message)); + }, this.config.keepAliveInterval * 1000); + } + + private onStreamerAdded() { + const message = { type: 'streamerConnected' }; + this.socket.write(JSON.stringify(message)); + } + + private onStreamerRemoved() { + const message = { type: 'streamerDisconnected' }; + this.socket.write(JSON.stringify(message)); + } + + private onPlayerAdded() { + const message = { type: 'clientConnected' }; + this.socket.write(JSON.stringify(message)); + } + + private onPlayerRemoved() { + const message = { type: 'clientDisconnected' }; + this.socket.write(JSON.stringify(message)); + } +} diff --git a/Signalling/src/PlayerConnection.ts b/Signalling/src/PlayerConnection.ts new file mode 100644 index 00000000..16a56910 --- /dev/null +++ b/Signalling/src/PlayerConnection.ts @@ -0,0 +1,203 @@ +import WebSocket from 'ws'; +import { ITransport, + WebSocketTransportNJS, + SignallingProtocol, + MessageHelpers, + Messages, + BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { IPlayer, IPlayerInfo } from './PlayerRegistry'; +import { IStreamer } from './StreamerRegistry'; +import { Logger } from './Logger'; +import * as LogUtils from './LoggingUtils'; +import { SignallingServer } from './SignallingServer'; + +/** + * A connection between the signalling server and a player connection. + * This is where messages expected to be handled by the player come in + * and where messages are sent to the player. + * + * Interesting internals: + * playerId: The unique id string of this player. + * transport: The ITransport where transport events can be subscribed to + * protocol: The SignallingProtocol where signalling messages can be + * subscribed to. + */ +export class PlayerConnection implements IPlayer, LogUtils.IMessageLogger { + // The unique id of this player connection. + playerId: string; + // The websocket transport used by this connection. + transport: ITransport; + // The protocol abstraction on this connection. Used for sending/receiving signalling messages. + protocol: SignallingProtocol; + // When the player is subscribed to a streamer this will be the streamer being subscribed to. + subscribedStreamer: IStreamer | null; + // A descriptive string describing the remote address of this connection. + remoteAddress?: string; + + private server: SignallingServer; + private sendOffer: boolean; + private streamerIdChangeListener: (newId: string) => void; + private streamerDisconnectedListener: () => void; + + /** + * Initializes a new connection with given and sane values. Adds listeners for the + * websocket close and error so it can react by unsubscribing and resetting itself. + * @param server - The signalling server object that spawned this player. + * @param ws - The websocket coupled to this player connection. + * @param sendOffer - True if the player is requesting to receive offers from streamers. + * @param remoteAddress - The remote address of this connection. Only used as display. + */ + constructor(server: SignallingServer, ws: WebSocket, sendOffer: boolean, remoteAddress?: string) { + this.server = server; + this.playerId = ''; + this.subscribedStreamer = null; + this.transport = new WebSocketTransportNJS(ws); + this.protocol = new SignallingProtocol(this.transport); + this.sendOffer = sendOffer; + this.remoteAddress = remoteAddress; + + this.transport.on('error', this.onTransportError.bind(this)); + this.transport.on('close', this.onTransportClose.bind(this)); + + this.streamerIdChangeListener = this.onStreamerIdChanged.bind(this); + this.streamerDisconnectedListener = this.onStreamerDisconnected.bind(this); + + this.registerMessageHandlers(); + } + + /** + * Returns an identifier that is displayed in logs. + * @returns A string describing this connection. + */ + getReadableIdentifier(): string { return this.playerId; } + + /** + * Sends a signalling message to the player. + * @param message - The message to send. + */ + sendMessage(message: BaseMessage): void { + LogUtils.logOutgoing(this, message); + this.protocol.sendMessage(message); + } + + /** + * Returns a descriptive object for the REST API inspection operations. + * @returns An IPlayerInfo object containing viewable information about this connection. + */ + getPlayerInfo(): IPlayerInfo { + return { + playerId: this.playerId, + type: 'Player', + subscribedTo: this.subscribedStreamer?.streamerId, + sendOffer: this.sendOffer, + remoteAddress: this.remoteAddress, + }; + } + + private registerMessageHandlers(): void { + this.protocol.on(Messages.subscribe.typeName, LogUtils.createHandlerListener(this, this.onSubscribeMessage)); + this.protocol.on(Messages.unsubscribe.typeName, LogUtils.createHandlerListener(this, this.onUnsubscribeMessage)); + this.protocol.on(Messages.listStreamers.typeName, LogUtils.createHandlerListener(this, this.onListStreamers)); + + this.protocol.on(Messages.offer.typeName, this.sendToStreamer.bind(this)); + this.protocol.on(Messages.answer.typeName, this.sendToStreamer.bind(this)); + this.protocol.on(Messages.iceCandidate.typeName, this.sendToStreamer.bind(this)); + this.protocol.on(Messages.dataChannelRequest.typeName, this.sendToStreamer.bind(this)); + this.protocol.on(Messages.peerDataChannelsReady.typeName, this.sendToStreamer.bind(this)); + } + + private sendToStreamer(message: BaseMessage): void { + if (!this.subscribedStreamer) { + Logger.warn(`Player ${this.playerId} tried to send to a streamer but they're not subscribed to any.`); + const streamerId = this.server.streamerRegistry.getFirstStreamerId(); + if (!streamerId) { + Logger.error('There are no streamers to force a subscription. Disconnecting.'); + this.disconnect(); + return; + } else { + Logger.warn(`Subscribing to ${streamerId}`); + this.subscribe(streamerId); + } + } + + LogUtils.logForward(this, this.subscribedStreamer!, message); + message.playerId = this.playerId; + this.subscribedStreamer!.protocol.sendMessage(message); + } + + private subscribe(streamerId: string) { + const streamer = this.server.streamerRegistry.find(streamerId); + if (!streamer) { + Logger.error(`subscribe: Player ${this.playerId} tried to subscribe to a non-existent streamer ${streamerId}`); + return; + } + + if (this.subscribedStreamer) { + Logger.warn(`subscribe: Player ${this.playerId} is resubscribing to a streamer but is already subscribed to ${this.subscribedStreamer.streamerId}`); + this.unsubscribe(); + } + + this.subscribedStreamer = streamer; + this.subscribedStreamer.on('id_changed', this.streamerIdChangeListener); + this.subscribedStreamer.on('disconnect', this.streamerDisconnectedListener); + + const connectedMessage = MessageHelpers.createMessage(Messages.playerConnected, { playerId: this.playerId, + dataChannel: true, + sfu: false, + sendOffer: this.sendOffer }); + this.sendToStreamer(connectedMessage); + } + + private unsubscribe() { + if (!this.subscribedStreamer) { + return; + } + + const disconnectedMessage = MessageHelpers.createMessage(Messages.playerDisconnected, { playerId: this.playerId }); + this.sendToStreamer(disconnectedMessage); + + this.subscribedStreamer.off('id_changed', this.streamerIdChangeListener); + this.subscribedStreamer.off('disconnect', this.streamerDisconnectedListener); + this.subscribedStreamer = null; + } + + private disconnect() { + this.unsubscribe(); + this.protocol.disconnect(); + } + + private onStreamerDisconnected(): void { + this.disconnect(); + } + + private onTransportError(error: ErrorEvent): void { + Logger.error(`Player (${this.playerId}) transport error ${error}`); + } + + private onTransportClose(_event: CloseEvent): void { + Logger.debug('PlayerConnection transport close.'); + this.disconnect(); + } + + private onSubscribeMessage(message: Messages.subscribe): void { + this.subscribe(message.streamerId); + } + + private onUnsubscribeMessage(_message: Messages.unsubscribe): void { + this.unsubscribe(); + } + + private onListStreamers(_message: Messages.listStreamers): void { + const listMessage = MessageHelpers.createMessage(Messages.streamerList, { ids: this.server.streamerRegistry.streamers.map(streamer => streamer.streamerId) }); + this.sendMessage(listMessage); + } + + private onStreamerIdChanged(newId: string) { + const renameMessage = MessageHelpers.createMessage(Messages.streamerIdChanged, { newID: newId }); + this.sendMessage(renameMessage); + } + + private onStreamerRemoved() { + this.disconnect(); + } +} diff --git a/Signalling/src/PlayerRegistry.ts b/Signalling/src/PlayerRegistry.ts new file mode 100644 index 00000000..32a9fd0f --- /dev/null +++ b/Signalling/src/PlayerRegistry.ts @@ -0,0 +1,116 @@ +import { SignallingProtocol, BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { EventEmitter } from 'events'; +import { Logger } from './Logger'; +import { IMessageLogger } from './LoggingUtils'; +import { IStreamer } from './StreamerRegistry'; + +/** + * An interface that describes a player that can be added to the + * player registry. + */ +export interface IPlayer extends IMessageLogger { + playerId: string; + protocol: SignallingProtocol; + subscribedStreamer: IStreamer | null; + + sendMessage(message: BaseMessage): void; + getPlayerInfo(): IPlayerInfo; +} + +/** + * Used by the API to describe the current state of the player. + */ +export interface IPlayerInfo { + playerId: string, + type: string, + subscribedTo: string | undefined, + sendOffer: boolean, + remoteAddress: string | undefined, +} + +/** + * Handles all the player connections of a signalling server and + * can be used to lookup connections by id etc. + * Fires events when players are added or removed. + * Events: + * 'added': (playerId: string) Player was added. + * 'removed': (playerId: string) Player was removed. + */ +export class PlayerRegistry extends EventEmitter { + private players: Map = new Map(); + private playerCount: number; + private nextPlayerId: number; + + constructor() { + super(); + this.players = new Map(); + this.playerCount = 0; + this.nextPlayerId = 0; + } + + /** + * Assigns a unique id to the player and adds it to the registry + */ + add(player: IPlayer): void { + player.playerId = this.getUniquePlayerId(); + this.players.set(player.playerId, player); + this.playerCount++; + this.emit('added', player.playerId); + Logger.info(`Registered new player: ${player.playerId}`); + } + + /** + * Removes a player from the registry. Does nothing if the id + * does not exist. + */ + remove(player: IPlayer): void { + if (!this.players.has(player.playerId)) { + return; + } + + this.emit('removed', player.playerId); + this.players.delete(player.playerId); + this.playerCount--; + + Logger.info(`Unregistered player: ${player.playerId}`); + } + + /** + * Tests if a player id exists in the registry. + */ + has(playerId: string): boolean { + return this.players.has(playerId); + } + + /** + * Gets a player from the registry using the player id. + * Returns undefined if the player doesn't exist. + */ + get(playerId: string): IPlayer | undefined { + return this.players.get(playerId); + } + + listPlayers(): IPlayer[] { + return Array.from(this.players.values()); + } + + /** + * Returns true when the registry is empty. + */ + empty(): boolean { + return this.players.size == 0; + } + + /** + * Gets the total number of connected players. + */ + count(): number { + return this.players.size; + } + + private getUniquePlayerId(): string { + const newPlayerId = `Player${this.nextPlayerId}`; + this.nextPlayerId++; + return newPlayerId; + } +} diff --git a/Signalling/src/SFUConnection.ts b/Signalling/src/SFUConnection.ts new file mode 100644 index 00000000..b1313ae4 --- /dev/null +++ b/Signalling/src/SFUConnection.ts @@ -0,0 +1,260 @@ +import WebSocket from 'ws'; +import { ITransport, + WebSocketTransportNJS, + SignallingProtocol, + MessageHelpers, + Messages, + BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { IPlayer, IPlayerInfo } from './PlayerRegistry'; +import { IStreamer, IStreamerInfo } from './StreamerRegistry'; +import { EventEmitter } from 'events'; +import { Logger } from './Logger'; +import * as LogUtils from './LoggingUtils'; +import { SignallingServer } from './SignallingServer'; + +/** + * A SFU connection to the signalling server. + * An SFU can act as both a streamer and a player. It can subscribe to + * streamers like a player, and other players can subscribe to the sfu. + * Therefore the SFU will have a streamer id and a player id and be + * registered in both streamer registries and player registries. + * + * Interesting internals: + * playerId: The player id of this connectiom. + * streamerId: The streamer id of this connection. + * transport: The ITransport where transport events can be subscribed to + * protocol: The SignallingProtocol where signalling messages can be + * subscribed to. + * streaming: True when the streamer is ready to accept subscriptions. + */ +export class SFUConnection extends EventEmitter implements IPlayer, IStreamer, LogUtils.IMessageLogger { + // The player id related to this SFU connection. + playerId: string; + // The streamer id related to this SFU connection. + streamerId: string; + // The websocket transport used by this connection. + transport: ITransport; + // The protocol abstraction on this connection. Used for sending/receiving signalling messages. + protocol: SignallingProtocol; + // True when this SFU is subscribed to another streamer and is ready to be subscribed to itself. + streaming: boolean; + // When the player is subscribed to a streamer this will be the streamer being subscribed to. + subscribedStreamer: IStreamer | null; + // A descriptive string describing the remote address of this connection. + remoteAddress?: string; + + private server: SignallingServer; + private layerPreferenceListener: (message: Messages.layerPreference) => void; + private streamerIdChangeListener: (newId: string) => void; + private streamerDisconnectedListener: () => void; + + /** + * Construct a new SFU connection. + * @param server - The signalling server object that spawned this sfu. + * @param ws - The websocket coupled to this sfu connection. + * @param remoteAddress - The remote address of this connection. Only used as display. + */ + constructor(server: SignallingServer, ws: WebSocket, remoteAddress?: string) { + super(); + + this.server = server; + this.transport = new WebSocketTransportNJS(ws); + this.protocol = new SignallingProtocol(this.transport); + this.playerId = ''; + this.streamerId = ''; + this.streaming = false; + this.remoteAddress = remoteAddress; + this.subscribedStreamer = null; + + this.transport.on('error', this.onTransportError.bind(this)); + this.transport.on('close', this.onTransportClose.bind(this)); + + this.layerPreferenceListener = this.onLayerPreference.bind(this); + this.streamerIdChangeListener = this.onStreamerIdChanged.bind(this); + this.streamerDisconnectedListener = this.onStreamerDisconnected.bind(this); + + this.registerMessageHandlers(); + } + + /** + * Returns an identifier that is displayed in logs. + * @returns A string describing this connection. + */ + getReadableIdentifier(): string { return `(${this.streamerId}:${this.playerId})`; } + + /** + * Sends a signalling message to the SFU. + * @param message - The message to send. + */ + sendMessage(message: BaseMessage): void { + LogUtils.logOutgoing(this, message); + this.protocol.sendMessage(message); + } + + /** + * Returns a descriptive object for the REST API inspection operations. + * @returns An IStreamerInfo object containing viewable information about this connection. + */ + getStreamerInfo(): IStreamerInfo { + return { + streamerId: this.streamerId, + type: 'SFU', + streaming: this.streaming, + remoteAddress: this.remoteAddress, + subscribers: this.server.playerRegistry.listPlayers().filter(player => player.subscribedStreamer == this).map(player => player.getPlayerInfo()), + }; + } + + /** + * Returns a descriptive object for the REST API inspection operations. + * @returns An IPlayerInfo object containing viewable information about this connection. + */ + getPlayerInfo(): IPlayerInfo { + return { + playerId: this.playerId, + type: 'SFU', + remoteAddress: this.remoteAddress, + subscribedTo: this.subscribedStreamer?.streamerId, + sendOffer: true, + }; + } + + private registerMessageHandlers(): void { + this.protocol.on(Messages.subscribe.typeName, LogUtils.createHandlerListener(this, this.onSubscribeMessage)); + this.protocol.on(Messages.unsubscribe.typeName, LogUtils.createHandlerListener(this, this.onUnsubscribeMessage)); + this.protocol.on(Messages.listStreamers.typeName, LogUtils.createHandlerListener(this, this.onListStreamers)); + this.protocol.on(Messages.endpointId.typeName, LogUtils.createHandlerListener(this, this.onEndpointId)); + this.protocol.on(Messages.streamerDataChannels.typeName, LogUtils.createHandlerListener(this, this.onStreamerDataChannels)); + this.protocol.on(Messages.startStreaming.typeName, LogUtils.createHandlerListener(this, this.onStartStreaming)); + this.protocol.on(Messages.stopStreaming.typeName, LogUtils.createHandlerListener(this, this.onStopStreaming)); + + this.protocol.on(Messages.offer.typeName, this.sendToPlayer.bind(this)); + this.protocol.on(Messages.answer.typeName, this.sendToStreamer.bind(this)); + this.protocol.on(Messages.peerDataChannels.typeName, this.sendToPlayer.bind(this)); + } + + private subscribe(streamerId: string) { + const streamer = this.server.streamerRegistry.find(streamerId); + if (!streamer) { + Logger.error(`subscribe: SFU ${this.playerId} tried to subscribe to a non-existent streamer ${streamerId}`); + return; + } + + this.subscribedStreamer = streamer; + this.subscribedStreamer.on('layer_preference', this.layerPreferenceListener); + this.subscribedStreamer.on('id_changed', this.streamerIdChangeListener); + this.subscribedStreamer.on('disconnect', this.streamerDisconnectedListener); + + const connectedMessage = MessageHelpers.createMessage(Messages.playerConnected, { playerId: this.playerId, + dataChannel: true, + sfu: true, + sendOffer: true }); + this.sendToStreamer(connectedMessage); + } + + private unsubscribe() { + if (!this.subscribedStreamer) { + return; + } + + const disconnectedMessage = MessageHelpers.createMessage(Messages.playerDisconnected, { playerId: this.playerId }); + this.sendToStreamer(disconnectedMessage); + + this.subscribedStreamer.off('layer_preference', this.layerPreferenceListener); + this.subscribedStreamer.off('id_changed', this.streamerIdChangeListener); + this.subscribedStreamer.off('disconnect', this.streamerDisconnectedListener); + this.subscribedStreamer = null; + } + + private sendToStreamer(message: BaseMessage): void { + if (!this.subscribedStreamer) { + Logger.error(`SFU ${this.playerId} tried to send to a streamer but they're not subscribed to any.`) + return; + } + + LogUtils.logForward(this, this.subscribedStreamer, message); + + // normally we want to indicate what player this message came from + // but in some instances we might already have set this (streamerDataChannels) due to poor choices + message.playerId = message.playerId || this.playerId; + + this.subscribedStreamer.protocol.sendMessage(message); + } + + private sendToPlayer(message: BaseMessage): void { + if (!message.playerId) { + Logger.error(`SFU ${this.streamerId} trying to send a message to a player with no playerId. Ignored.`); + return; + } + const player = this.server.playerRegistry.get(message.playerId); + if (player) { + delete message.playerId; + LogUtils.logForward(this, player, message); + player.protocol.sendMessage(message); + } else { + Logger.error(`SFU attempted to forward to player ${message.playerId} which does not exist.`); + } + } + + private disconnect() { + this.unsubscribe(); + this.protocol.disconnect(); + this.emit('disconnect'); + } + + private onLayerPreference(message: Messages.layerPreference): void { + this.sendMessage(message); + } + + private onStreamerIdChanged(newId: string): void { + const renameMessage = MessageHelpers.createMessage(Messages.streamerIdChanged, { newID: newId }); + this.sendMessage(renameMessage); + } + + private onStreamerDisconnected(): void { + this.unsubscribe(); + this.sendMessage(MessageHelpers.createMessage(Messages.streamerDisconnected)); + this.streaming = false; + this.emit('disconnect'); + } + + private onTransportError(error: ErrorEvent): void { + Logger.error(`SFU (${this.playerId}) transport error ${error}`); + } + + private onTransportClose(_event: CloseEvent): void { + Logger.debug('SFUConnection transport close.'); + this.disconnect(); + } + + private onSubscribeMessage(message: Messages.subscribe): void { + this.subscribe(message.streamerId); + } + + private onUnsubscribeMessage(_message: Messages.unsubscribe): void { + this.unsubscribe(); + } + + private onListStreamers(_message: Messages.listStreamers): void { + const listMessage = MessageHelpers.createMessage(Messages.streamerList, { ids: this.server.streamerRegistry.streamers.map(streamer => streamer.streamerId) }); + this.sendMessage(listMessage); + } + + private onStreamerDataChannels(message: Messages.streamerDataChannels): void { + message.sfuId = this.playerId!; + this.sendToStreamer(message); + } + + private onEndpointId(_message: Messages.endpointId): void { + this.streaming = true; + } + + private onStartStreaming(_message: Messages.startStreaming): void { + this.streaming = true; + } + + private onStopStreaming(_message: Messages.stopStreaming): void { + this.streaming = false; + this.emit('disconnect'); + } +} diff --git a/Signalling/src/SignallingServer.ts b/Signalling/src/SignallingServer.ts new file mode 100644 index 00000000..5cabff02 --- /dev/null +++ b/Signalling/src/SignallingServer.ts @@ -0,0 +1,188 @@ +import http from 'http'; +import https from 'https'; +import WebSocket from 'ws'; +import url from 'url'; +import { StreamerConnection } from './StreamerConnection'; +import { PlayerConnection } from './PlayerConnection'; +import { SFUConnection } from './SFUConnection'; +import { Logger } from './Logger'; +import { StreamerRegistry } from './StreamerRegistry'; +import { PlayerRegistry } from './PlayerRegistry'; +import { IMatchmakerConfig, MatchmakerConnection } from './MatchmakerConnection'; +import { Messages, MessageHelpers, SignallingProtocol } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { stringify } from './Utils'; + +/** + * An interface describing the possible options to pass when creating + * a new SignallingServer object. + */ +export interface IServerConfig { + // An http server to use for player connections rather than a port. Not needed if playerPort or httpsServer supplied. + httpServer?: http.Server; + + // An https server to use for player connections rather than a port. Not needed if playerPort or httpServer supplied. + httpsServer?: https.Server; + + // The port to listen on for streamer connections. + streamerPort: number; + + // The port to listen on for player connections. Not needed if httpServer or httpsServer supplied. + playerPort?: number; + + // The port to listen on for SFU connections. If not supplied SFU connections will be disabled. + sfuPort?: number; + + // The peer configuration object to send to peers in the config message when they connect. + peerOptions: any; + + // Additional websocket options for the streamer listening websocket. + streamerWsOptions?: any; + + // Additional websocket options for the player listening websocket. + playerWsOptions?: any; + + // Additional websocket options for the SFU listening websocket. + sfuWsOptions?: any; + + // Enables the matchmaker connection. + useMatchmaker?: boolean; + + // The ip/hostname of the matchmaker application. + matchmakerAddress?: string; + + // The port that the matchmaker is listening for connections on. + matchmakerPort?: number; + + // Number of seconds to delay before a reconnection attempt when the matchmaker connection is lost. + matchmakerRetryInterval?: number; + + // Number of seconds between keep alive pings to the matchmaker. + matchmakerKeepAliveInterval?: number; + + // Sent to the matchmaker so it can tell clients the ip/hostname to connect to + publicIp?: string; + + // Sent to the matchmaker so it can tell clients what port to connect to + publicPort?: number; +} + +/** + * The main signalling server object. + * Contains a streamer and player registry and handles setting up of websockets + * to listen for incoming connections. + */ +export class SignallingServer { + config: IServerConfig; + protocolConfig: any; + streamerRegistry: StreamerRegistry; + playerRegistry: PlayerRegistry; + startTime: Date; + + /** + * Initializes the server object and sets up listening sockets for streamers + * players and optionally SFU connections. + * @param config - A collection of options for this server. + */ + constructor(config: IServerConfig) { + Logger.debug('Started SignallingServer with config: %s', stringify(config)); + + this.config = config; + this.streamerRegistry = new StreamerRegistry(); + this.playerRegistry = new PlayerRegistry(); + this.protocolConfig = { + protocolVersion: SignallingProtocol.SIGNALLING_VERSION, + peerConnectionOptions: this.config.peerOptions || {} + }; + this.startTime = new Date(); + + if (!config.playerPort && !config.httpServer && !config.httpsServer) { + Logger.error('No player port, http server or https server supplied to SignallingServer.'); + return; + } + + // Streamer connections + const streamerServer = new WebSocket.Server({ port: config.streamerPort, backlog: 1, ...config.streamerWsOptions}); + streamerServer.on('connection', this.onStreamerConnected.bind(this)); + Logger.info(`Listening for streamer connections on port ${config.streamerPort}`); + + // Player connections + const server = config.httpsServer || config.httpServer; + const playerServer = new WebSocket.Server({ + server: server, + port: server ? undefined : config.playerPort, + ...config.playerWsOptions }); + playerServer.on('connection', this.onPlayerConnected.bind(this)); + if (!config.httpServer && !config.httpsServer) { + Logger.info(`Listening for player connections on port ${config.playerPort}`); + } + + // Optional SFU connections + if (config.sfuPort) { + const sfuServer = new WebSocket.Server({ port: config.sfuPort, backlog: 1, ...config.sfuWsOptions }); + sfuServer.on('connection', this.onSFUConnected.bind(this)); + Logger.info(`Listening for SFU connections on port ${config.sfuPort}`); + } + + // Optional Matchmaker connections + if (config.useMatchmaker) { + const mmConfig: IMatchmakerConfig = { + publicIp: config.publicIp, + publicPort: config.publicPort, + address: config.matchmakerAddress, + port: config.matchmakerPort, + https: config.httpsServer != undefined, + retryInterval: config.matchmakerRetryInterval, + keepAliveInterval: config.matchmakerKeepAliveInterval, + }; + const _matchmakerConnection = new MatchmakerConnection(mmConfig, this.streamerRegistry, this.playerRegistry); + } + } + + private onStreamerConnected(ws: WebSocket, request: http.IncomingMessage) { + Logger.info(`New streamer connection: %s`, request.socket.remoteAddress); + + const newStreamer = new StreamerConnection(this, ws, request.socket.remoteAddress); + + // add it to the registry and when the transport closes, remove it. + this.streamerRegistry.add(newStreamer); + newStreamer.transport.on('close', () => { this.streamerRegistry.remove(newStreamer); }); + + newStreamer.sendMessage(MessageHelpers.createMessage(Messages.config, this.protocolConfig)); + } + + private onPlayerConnected(ws: WebSocket, request: http.IncomingMessage) { + Logger.info(`New player connection: %s (%s)`, request.socket.remoteAddress, request.url); + + // extract some options from the request url + let sendOffer = true; + if (request.url) { + const parsedUrl = url.parse(request.url); + const urlParams = new URLSearchParams(parsedUrl.search!); + const offerToReceive: boolean = (urlParams.get('OfferToReceive') === 'true'); + sendOffer = offerToReceive ? false : true; + } + + const newPlayer = new PlayerConnection(this, ws, sendOffer, request.socket.remoteAddress); + + // add it to the registry and when the transport closes, remove it + this.playerRegistry.add(newPlayer); + newPlayer.transport.on('close', () => { this.playerRegistry.remove(newPlayer); }); + + newPlayer.sendMessage(MessageHelpers.createMessage(Messages.config, this.protocolConfig)); + } + + private onSFUConnected(ws: WebSocket, request: http.IncomingMessage) { + Logger.info(`New SFU connection: %s`, request.socket.remoteAddress); + const newSFU = new SFUConnection(this, ws, request.socket.remoteAddress); + + // SFU acts as both a streamer and player + this.streamerRegistry.add(newSFU); + this.playerRegistry.add(newSFU); + newSFU.transport.on('close', () => { + this.streamerRegistry.remove(newSFU); + this.playerRegistry.remove(newSFU); + }); + + newSFU.sendMessage(MessageHelpers.createMessage(Messages.config, this.protocolConfig)); + } +} diff --git a/Signalling/src/StreamerConnection.ts b/Signalling/src/StreamerConnection.ts new file mode 100644 index 00000000..05d38721 --- /dev/null +++ b/Signalling/src/StreamerConnection.ts @@ -0,0 +1,146 @@ +import WebSocket from 'ws'; +import { ITransport, + SignallingProtocol, + WebSocketTransportNJS, + BaseMessage, + Messages, + MessageHelpers } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { IStreamer, IStreamerInfo } from './StreamerRegistry'; +import { EventEmitter } from 'events'; +import { stringify } from './Utils'; +import { Logger } from './Logger'; +import * as LogUtils from './LoggingUtils'; +import { SignallingServer } from './SignallingServer'; + +/** + * A connection between the signalling server and a streamer connection. + * This is where messages expected to be handled by the streamer come in + * and where messages are sent to the streamer. + * + * Interesting internals: + * streamerId: The unique id string of this streamer. + * transport: The ITransport where transport events can be subscribed to + * protocol: The SignallingProtocol where signalling messages can be + * subscribed to. + * streaming: True when the streamer is ready to accept subscriptions. + */ +export class StreamerConnection extends EventEmitter implements IStreamer, LogUtils.IMessageLogger { + // The streamer id related to this SFU connection. + streamerId: string; + // The websocket transport used by this connection. + transport: ITransport; + // The protocol abstraction on this connection. Used for sending/receiving signalling messages. + protocol: SignallingProtocol; + // True when this streamer is ready to be subscribed to. + streaming: boolean; + // A descriptive string describing the remote address of this connection. + remoteAddress?: string; + + private server: SignallingServer; + + /** + * Initializes a new connection with given and sane values. Adds listeners for the + * websocket close and error and will emit a disconnected event when disconneted. + * @param server - The signalling server object that spawned this streamer. + * @param ws - The websocket coupled to this streamer connection. + * @param remoteAddress - The remote address of this connection. Only used as display. + */ + constructor(server: SignallingServer, ws: WebSocket, remoteAddress?: string) { + super(); + + this.server = server; + this.streamerId = ''; + this.transport = new WebSocketTransportNJS(ws); + this.protocol = new SignallingProtocol(this.transport); + this.streaming = false; + this.remoteAddress = remoteAddress; + + this.transport.on('error', this.onTransportError.bind(this)); + this.transport.on('close', this.onTransportClose.bind(this)); + + this.registerMessageHandlers(); + } + + /** + * Returns an identifier that is displayed in logs. + * @returns A string describing this connection. + */ + getReadableIdentifier(): string { return this.streamerId; } + + /** + * Sends a signalling message to the player. + * @param message - The message to send. + */ + sendMessage(message: BaseMessage): void { + LogUtils.logOutgoing(this, message); + this.protocol.sendMessage(message); + } + + /** + * Returns a descriptive object for the REST API inspection operations. + * @returns An IStreamerInfo object containing viewable information about this connection. + */ + getStreamerInfo(): IStreamerInfo { + return { + streamerId: this.streamerId, + type: 'Streamer', + streaming: this.streaming, + remoteAddress: this.remoteAddress, + subscribers: this.server.playerRegistry.listPlayers().filter(player => player.subscribedStreamer == this).map(player => player.getPlayerInfo()), + }; + } + + private registerMessageHandlers(): void { + this.protocol.on(Messages.endpointId.typeName, LogUtils.createHandlerListener(this, this.onEndpointId)); + this.protocol.on(Messages.ping.typeName, LogUtils.createHandlerListener(this, this.onPing)); + this.protocol.on(Messages.disconnectPlayer.typeName, LogUtils.createHandlerListener(this, this.onDisconnectPlayerRequest)); + this.protocol.on(Messages.layerPreference.typeName, LogUtils.createHandlerListener(this, this.onLayerPreference)); + + this.protocol.on(Messages.offer.typeName, this.forwardMessage.bind(this)); + this.protocol.on(Messages.answer.typeName, this.forwardMessage.bind(this)); + this.protocol.on(Messages.iceCandidate.typeName, this.forwardMessage.bind(this)); + } + + private forwardMessage(message: BaseMessage): void { + if (!message.playerId) { + Logger.warn(`No playerId specified, cannot forward message: ${stringify(message)}`); + } else { + const player = this.server.playerRegistry.get(message.playerId); + if (player) { + delete message.playerId; + LogUtils.logForward(this, player, message); + player.protocol.sendMessage(message); + } + } + } + + private onTransportError(error: ErrorEvent): void { + Logger.error(`Streamer (${this.streamerId}) transport error ${error}`); + } + + private onTransportClose(): void { + Logger.debug('StreamerConnection transport close.'); + this.emit('disconnect'); + } + + private onEndpointId(_message: Messages.endpointId): void { + this.streaming = true; // we're ready to stream when we id ourselves + } + + private onPing(message: Messages.ping): void { + this.sendMessage(MessageHelpers.createMessage(Messages.pong, { time: message.time })); + } + + private onDisconnectPlayerRequest(message: Messages.disconnectPlayer): void { + if (message.playerId) { + const player = this.server.playerRegistry.get(message.playerId); + if (player) { + player.protocol.disconnect(1011, message.reason); + } + } + } + + private onLayerPreference(message: Messages.layerPreference): void { + this.emit('layer_preference', message); + } +} diff --git a/Signalling/src/StreamerRegistry.ts b/Signalling/src/StreamerRegistry.ts new file mode 100644 index 00000000..63bb5466 --- /dev/null +++ b/Signalling/src/StreamerRegistry.ts @@ -0,0 +1,163 @@ +import { ITransport, SignallingProtocol, Messages, MessageHelpers, BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.5'; +import { EventEmitter } from 'events'; +import { Logger } from './Logger'; +import { IMessageLogger } from './LoggingUtils'; +import { IPlayerInfo } from './PlayerRegistry'; + +/** + * An interface that describes a streamer that can be added to the + * streamer registry. + */ +export interface IStreamer extends EventEmitter, IMessageLogger { + streamerId: string; + transport: ITransport; + protocol: SignallingProtocol; + streaming: boolean; + + sendMessage(message: BaseMessage): void; + getStreamerInfo(): IStreamerInfo; +} + +/** + * Used by the API to describe a streamer. + */ +export interface IStreamerInfo { + streamerId: string, + type: string, + streaming: boolean, + remoteAddress: string | undefined, + subscribers: IPlayerInfo[], +} + +/** + * Handles all the streamer connections of a signalling server and + * can be used to lookup connections by id etc. + * Fires events when streamers are added or removed. + * Events: + * 'added': (playerId: string) Player was added. + * 'removed': (playerId: string) Player was removed. + */ +export class StreamerRegistry extends EventEmitter { + streamers: IStreamer[]; + defaultStreamerIdPrefix: string = "UnknownStreamer"; + + constructor() { + super(); + this.streamers = []; + } + + /** + * Adds a streamer to the registry. If the streamer already has an id + * it will be sanitized (checked against existing ids and altered if + * there are collisions), or if it has no id it will be assigned a + * default unique id. + * @returns True if the add was successful. + */ + add(streamer: IStreamer): boolean { + streamer.streamerId = this.sanitizeStreamerId(streamer.streamerId); + + if (this.find(streamer.streamerId)) { + Logger.error(`StreamerRegistry: Tried to register streamer ${streamer.streamerId} but that id already exists.`); + return false; + } + + this.streamers.push(streamer); + + // request that the new streamer id itself. + streamer.protocol.on(Messages.endpointId.typeName, this.onEndpointId.bind(this, streamer)); + streamer.sendMessage(MessageHelpers.createMessage(Messages.identify)); + + this.emit('added', streamer.streamerId); + + return true; + } + + /** + * Removes a streamer from the registry. If the streamer isn't found + * it does nothing. + * @returns True if the streamer was removed. + */ + remove(streamer: IStreamer): boolean { + const index = this.streamers.indexOf(streamer); + if (index == -1) { + Logger.debug(`StreamerRegistry: Tried to remove streamer ${streamer.streamerId} but it doesn't exist`); + return false; + } + this.streamers.splice(index, 1); + this.emit('removed', streamer.streamerId); + return true; + } + + /** + * Attempts to find the given streamer id in the registry. + */ + find(streamerId: string): IStreamer | undefined { + return this.streamers.find((streamer) => streamer.streamerId == streamerId); + } + + /** + * Used by players who haven't subscribed but try to send a message. + * This is to cover legacy connections that do not know how to subscribe. + * The player will be assigned the first streamer in the list. + * @returns The first streamerId in the registry or null if there are none. + */ + getFirstStreamerId(): string | null { + if (this.empty()) { + return null; + } + return this.streamers[0].streamerId; + } + + /** + * Returns true when the registry is empty. + */ + empty(): boolean { + return this.streamers.length == 0; + } + + /** + * Returns the total number of connected streamers. + */ + count(): number { + return this.streamers.length; + } + + private onEndpointId(streamer: IStreamer, message: Messages.endpointId): void { + const oldId = streamer.streamerId; + + // id might conflict or be invalid so here we sanitize it + streamer.streamerId = this.sanitizeStreamerId(message.id); + + Logger.debug(`StreamerRegistry: Streamer id change. ${oldId} -> ${streamer.streamerId}`); + streamer.emit('id_changed', streamer.streamerId); + + // because we might have sanitized the id, we confirm the id back to the streamer + streamer.sendMessage(MessageHelpers.createMessage(Messages.endpointIdConfirm, { committedId: streamer.streamerId })); + } + + private sanitizeStreamerId(id: string): string { + // create a default id if none supplied + if (!id) { + id = this.defaultStreamerIdPrefix; + } + + // search for existing streamerId and optionally append a numeric value + let maxPostfix = -1; + for (const streamer of this.streamers) { + const idMatchRegex = /^(.*?)(\d*)$/; + const [, baseId, postfix] = streamer.streamerId.match(idMatchRegex)!; + // if the id is numeric then base id will be empty and we need to compare with the postfix + if ((baseId != '' && baseId != id) || (baseId == '' && postfix != id)) { + continue; + } + const numPostfix = Number(postfix); + if (numPostfix > maxPostfix) { + maxPostfix = numPostfix + } + } + if (maxPostfix >= 0) { + return id + (maxPostfix + 1); + } + return id; + } +} diff --git a/Signalling/src/Utils.ts b/Signalling/src/Utils.ts new file mode 100644 index 00000000..3de7b709 --- /dev/null +++ b/Signalling/src/Utils.ts @@ -0,0 +1,15 @@ +import { jsonc } from 'jsonc'; + +/** + * Cirular reference safe version of JSON.stringify + */ +export function stringify(obj: any): string { + return jsonc.stringify(obj); +} + +/** + * Circular reference save version of JSON.stringify with extra formatting. + */ +export function beautify(obj: any): string { + return jsonc.stringify(obj, undefined, '\t'); +} diff --git a/Signalling/src/WebServer.ts b/Signalling/src/WebServer.ts new file mode 100644 index 00000000..5a2827f5 --- /dev/null +++ b/Signalling/src/WebServer.ts @@ -0,0 +1,126 @@ +import express from 'express'; +import path from 'path'; +import fs from 'fs'; +import http from 'http'; +import https from 'https'; +import helmet from 'helmet'; +import { Logger } from './Logger'; +import RateLimit from 'express-rate-limit'; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const hsts = require('hsts'); +/* eslint-enable @typescript-eslint/no-var-requires */ + +/** + * An interface that describes the possible options to pass to + * WebServer. + */ +export interface IWebServerConfig { + // The port to run the webserver on. 80 by default. + httpPort: number; + + // The root of the serve directory. Current working directory by default. + root: string; + + // The filename to direct connections to if none suppllied in the url. player.html by default. + homepageFile: string; + + // An optional rate limit to prevent overloading. + perMinuteRateLimit?: number; + + // When set an https server will be created + httpsPort?: number; + + // The ssl key data for https + ssl_key?: Buffer; + + // The ssl cert data for https + ssl_cert?: Buffer; + + // If true, connections to http will be redirected to https. + https_redirect?: Boolean; +} + +/** + * An object to manage the initialization of a web server. Used to serve the + * pixel streaming frontend. + */ +export class WebServer { + httpServer: http.Server; + httpsServer: https.Server; + + constructor(app: any, config: IWebServerConfig) { + Logger.debug('Starting WebServer with config: %s', config); + + // only listen on the http port if we're not using https or if we want to redirect + if (!config.httpsPort || config.https_redirect) { + this.httpServer = http.createServer(app); + this.httpServer.listen(config.httpPort, () => { + Logger.info(`Http server listening on port ${config.httpPort}`); + }); + } + + // if using https listen on the given ports and setup some details + if (config.httpsPort) { + const options = { key: config.ssl_key, cert: config.ssl_cert }; + this.httpsServer = https.createServer(options, app); + this.httpsServer.listen(config.httpsPort, () => { + Logger.info(`Https server listening on port ${config.httpsPort}`); + }); + + app.use(helmet()); + app.use(hsts({ + maxAge: 15552000 // 180 days in seconds + })); + + // Setup http -> https redirect if requested + if (config.https_redirect) { + app.use((req: any, res: any, next: any) => { + if (!req.secure) { + if (req.get('Host')) { + const hostAddressParts = req.get('Host').split(':'); + let hostAddress = hostAddressParts[0]; + if (config.httpsPort != 443) { + hostAddress = `${hostAddress}:${config.httpsPort}`; + } + return res.redirect(['https://', hostAddress, req.originalUrl].join('')); + } else { + Logger.error(`Unable to get host name from header. Requestor ${req.ip}, url path: '${req.originalUrl}', available headers ${JSON.stringify(req.headers)}`); + return res.status(400).send('Bad Request'); + } + } + next(); + }); + } + } + + app.use(express.static(config.root)); + + // Request has been sent to site root, send the homepage file + app.get('/', function (req: any, res: any) { + // Try a few paths, see if any resolve to a homepage file the user has set + const p = path.resolve(path.join(config.root, config.homepageFile)); + if (fs.existsSync(p)) { + // Send the file for browser to display it + res.sendFile(p); + return; + } + + // Catch file doesn't exist, and send back 404 if not + const error = 'Unable to locate file ' + config.homepageFile; + Logger.error(error); + res.status(404).send(error); + return; + }); + + if (config.perMinuteRateLimit) { + const limiter = RateLimit({ + windowMs: 60 * 1000, // 1 minute + max: config.perMinuteRateLimit + }); + + // apply rate limiter to all requests + app.use(limiter); + } + } +} diff --git a/Signalling/src/pixelstreamingsignalling.ts b/Signalling/src/pixelstreamingsignalling.ts new file mode 100644 index 00000000..8d690c35 --- /dev/null +++ b/Signalling/src/pixelstreamingsignalling.ts @@ -0,0 +1,10 @@ +export * from './Logger'; +export * from './LoggingUtils'; +export * from './PlayerConnection'; +export * from './PlayerRegistry'; +export * from './SFUConnection'; +export * from './SignallingServer'; +export * from './StreamerConnection'; +export * from './StreamerRegistry'; +export * from './WebServer'; +// keep Utils internal for now diff --git a/Signalling/tsconfig.base.json b/Signalling/tsconfig.base.json new file mode 100644 index 00000000..ff13abea --- /dev/null +++ b/Signalling/tsconfig.base.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "esModuleInterop": true, + "target": "ES6", + "moduleResolution": "node", + "sourceMap": true, + "allowJs": true + }, + "include": ["./src/*.ts"], + "typedocOptions": { + "entryPoints": ["src/**/*.ts"], + "out": "docs", + "excludeExternals": true, + "excludePrivate": true, + "excludeInternal": true, + "cleanOutputDir": false, + "plugin": "typedoc-plugin-markdown", + "readme": "none" + } +} diff --git a/Signalling/tsconfig.cjs.json b/Signalling/tsconfig.cjs.json new file mode 100644 index 00000000..ccee4d61 --- /dev/null +++ b/Signalling/tsconfig.cjs.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./build/commonjs", + "module": "commonjs", + "declaration": true, + "declarationDir": "./build/types" + } +} diff --git a/Signalling/tsconfig.esm.json b/Signalling/tsconfig.esm.json new file mode 100644 index 00000000..184e0dcd --- /dev/null +++ b/Signalling/tsconfig.esm.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.base.json", + "compilerOptions": { + "outDir": "./build/esm", + "module": "es2015" + } +} diff --git a/SignallingWebServer/.dockerignore b/SignallingWebServer/.dockerignore deleted file mode 100644 index 9fc28969..00000000 --- a/SignallingWebServer/.dockerignore +++ /dev/null @@ -1,4 +0,0 @@ -logs/*.log -node_modules -platform_scripts -tps diff --git a/SignallingWebServer/.eslintignore b/SignallingWebServer/.eslintignore new file mode 100644 index 00000000..3dbd671f --- /dev/null +++ b/SignallingWebServer/.eslintignore @@ -0,0 +1,9 @@ +build +doc +logs +node_modules +www +package-lock.json +package.json +tsconfig.json +.eslintrc.js \ No newline at end of file diff --git a/SignallingWebServer/.eslintrc.js b/SignallingWebServer/.eslintrc.js new file mode 100644 index 00000000..38c95aea --- /dev/null +++ b/SignallingWebServer/.eslintrc.js @@ -0,0 +1,23 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + 'eslint-plugin-tsdoc' + ], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'], + rules: { + "tsdoc/syntax": "warn", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "argsIgnorePattern": "^_", + "varsIgnorePattern": "^_", + "caughtErrorsIgnorePattern": "^_" + } + ] + } +}; diff --git a/SignallingWebServer/.gitignore b/SignallingWebServer/.gitignore new file mode 100644 index 00000000..26e5ffdd --- /dev/null +++ b/SignallingWebServer/.gitignore @@ -0,0 +1,5 @@ +build/ +node_modules/ +www/ +config.json +ssl/ diff --git a/SignallingWebServer/Dockerfile b/SignallingWebServer/Dockerfile index 838137bc..8f132ed2 100644 --- a/SignallingWebServer/Dockerfile +++ b/SignallingWebServer/Dockerfile @@ -1,11 +1,13 @@ # Use the current Long Term Support (LTS) version of Node.js FROM node:lts # Copy the signalling server and frontend source code from the build context +COPY /Common /Common +COPY /Signalling /Signalling COPY /SignallingWebServer /SignallingWebServer COPY /Frontend /Frontend -COPY /Common /Common # Install the dependencies for the signalling server and build the frontend RUN SignallingWebServer/platform_scripts/bash/setup.sh --build + # Expose TCP ports 80 and 443 for player WebSocket connections and web server HTTP(S) access EXPOSE 80 EXPOSE 443 @@ -20,12 +22,10 @@ EXPOSE 8889 # Expose TCP port 19302 for connections to Google's stun server EXPOSE 19302 -# Expose TCP port 9999 for connections from the Matchmaker -EXPOSE 9999 - # Expose TCP port 19302 for connections from coturn EXPOSE 3478 EXPOSE 3479 # Set the signalling server as the container's entrypoint -ENTRYPOINT ["/usr/local/bin/node", "/SignallingWebServer/cirrus.js"] +ENTRYPOINT ["bash", "SignallingWebServer/platform_scripts/bash/start.sh", "--nosudo"] + diff --git a/SignallingWebServer/Docs/SignallingProtocol.md b/SignallingWebServer/Docs/SignallingProtocol.md deleted file mode 100644 index ae3029f3..00000000 --- a/SignallingWebServer/Docs/SignallingProtocol.md +++ /dev/null @@ -1,353 +0,0 @@ -# Pixel Streaming Signalling Protocol - -The following is a complete reference to the current signalling server messaging protocol. These messages are sent as stringified JSON packets. Some parameters are JSON strings themselves and require escape sequences to be contained in the string parameter. - -## Version -1.1.0 - Current - -Major version number - breaking protocol change such as a required new message or field or deleting an existing message. -Minor version number - independent new message. -Hotfix version - a non-breaking new field in an existing message type. - -## Table of Contents -- [Terms](#terms) -- [Example Streaming Message Sequence](#example-message-sequence) -- [Player Sent Messages](#source-player) - - [answer](#player-answer) - - [dataChannelRequest](#player-datachannelrequest) - - [iceCandidate](#player-icecandidate) - - [listStreamers](#player-liststreamers) - - [offer](#player-offer) - - [peerDataChannelsReady](#player-peerdatachannelsready) - - [stats](#player-stats) - - [subscribe](#player-subscribe) - - [unsubscribe](#player-unsubscribe) -- [SFU Sent Messages](#source-sfu) - - [answer](#sfu-answer) - - [offer](#sfu-offer) - - [peerDataChannels](#sfu-peerdatachannels) - - [streamerDataChannels](#sfu-streamerdatachannels) -- [Signalling Server Sent Messages](#source-signalling) - - [config](#signalling-config) - - [identify](#signalling-identify) - - [streamerIDChanged](#signalling-streameridchanged) - - [playerConnected](#signalling-playerconnected) - - [playerCount](#signalling-playercount) - - [playerDisconnected](#signalling-playerdisconnected) - - [pong](#signalling-pong) - - [streamerDisconnected](#signalling-streamerDisconnected) - - [streamerList](#signalling-streamerlist) -- [Streamer Sent Message](#source-streamer) - - [answer](#streamer-answer) - - [disconnectPlayer](#streamer-disconnectplayer) - - [endpointId](#streamer-endpointid) - - [iceCandidate](#streamer-icecandidate) - - [layerPreference](#streamer-layerpreference) - - [offer](#streamer-offer) - - [ping](#streamer-ping) - -## Terms - -### Signalling Server - ->The server that is responsible for routing messageas and controlling new connections. - -### Streamer - -> The entity that is streaming the media. - -### Player - -> A consumer of the stream. Could be passive or active with the use of data streams. - -### SFU - -> Selective Forwarding Unit. This is a special type of Player that consumes the stream and then can forward the stream to new connected Players. This is useful when many users need to be consuming the stream and encoding resources on the Streamer are not adequate to keep up with the demand. -> Additionally allows the use of simulcast which allows the Streamer to stream a handful of different quality streams that the SFU can selectively forward to individual Players based on their connection quality. - -### SDP - -> Session Description Protocol. A message format that is used to negotiate a media connection between two endpoints. There are many resources for further details such as [here](https://www.tutorialspoint.com/webrtc/webrtc_session_description_protocol.htm) or [here](https://webrtchacks.com/sdp-anatomy/) - -### ICE Candidate - -> Interactive Connectivity Establishment. Describes protocols and routing needed for WebRTC to be able to communicate with a remote device. Further details can be read [here](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate) - -## Example Streaming Message Sequence - -```mermaid -sequenceDiagram -Streamer->>Signalling: Open Connection -Signalling->>Streamer: config -Signalling->>Streamer: identify -Streamer->>Signalling: endpointId -Player->>Signalling: Open Connection -Signalling->>Player: config -Signalling->>Player: playerCount -Player->>Signalling: listStreamers -Signalling->>Player: streamerList -Player->>Signalling: subscribe -Signalling->>Streamer: playerConnected -Streamer->>Signalling: offer -Signalling->>Player: offer -Player->>Signalling: answer -Signalling->>Streamer: answer -Streamer->Player: WebRTC negotiation -Note over Streamer, Player: Streaming Session -opt Player Disconnect - Player->>Signalling: Close Connection - Signalling->>Streamer: playerDisconnected -end -opt Streamer Disconnect - Streamer->>Signalling: Close Connection - Signalling->>Player: streamerDisconnected -end -``` - -## Player Sent Messages - -### answer - ->Message is forwarded to the currently subscribed to [Streamer](#term-streamer). Sends the [SDP](#term-sdp) answer back to the Streamer The [Player](#term-player) must have previously used the [subscribe](#player-subscribe) message to successfully subscribe to a connected Streamer and received an [offer](#streamer-offer) from the Streamer. - -| Param Name | Type | Description | -|-|-|-| -| sdp | string | The WebRTC SDP package in string format | - -### dataChannelRequest - ->Message is forwarded to a connected [SFU](#term-sfu). Tells the SFU that the [Player](#term-player) requests data channels to the Streamer. - -| Param Name | Type | Description | -|-|-|-| - -### iceCandidate - ->Message is forwarded to the currently subscribed to [Streamer](#term-streamer). Sends an [ICE](#term-icecandidate) candidate to the Streamer. This is part of the WebRTC negotiation and should come after the [answer](#streamer-answer) - -| Param Name | Type | Description | -|-|-|-| -| candidate | Object | The JSON object describing the ICE candidate | - -### listStreamers - -> Message is consumed by the [Signalling Server](#term-signallingserver). Requests the list of streamer ids currently connected to the Signalling Server. The server will reply with a [streamerList](#signalling-streamerlist) - -| Param Name | Type | Description | -|-|-|-| - -### offer - ->Message is forwarded to the currently subscribed to [Streamer](#term-streamer). Supplies an SDP offer to the Streamer to answer. The [Player](#term-player) must have previously used the [subscribe](#player-subscribe) message to successfully subscribe to a connected Streamer. - -| Param Name | Type | Description | -|-|-|-| -| sdp | string | The WebRTC SDP package in string format | - -### peerDataChannelsReady - ->Message is forwarded to a connected [SFU](#term-sfu). Tells the SFU that the [Player](#term-player) is ready for data channels to be negotiated. - -| Param Name | Type | Description | -|-|-|-| - -### stats - ->Message is consumed by the [Signalling Server](#term-signallingserver). Will print out the provided stats data on the console. - -| Param Name | Type | Description | -|-|-|-| -| data | string | The stats data to log. | - -### subscribe - -> Message is consumed by the [Signalling Server](#term-signallingserver). Tells the Signalling server that the player requests to subscribe to the given stream. - -| Param Name | Type | Description | -|-|-|-| -| streamerId | string | The id of the stream to subscribe to. | - -### unsubscribe - -> Message is consumed by the [Signalling Server](#term-signallingserver). Tells the Signalling Server that the [Player](#term-player) wishes to unsubscribe from the current stream. The player must have previously used the [subscribe](#player-subscribe) message for this to have any effect. - -| Param Name | Type | Description | -|-|-|-| - -## SFU Sent Messages - -### answer - ->Message is forwarded to the [Streamer](#term-streamer). Sends the [SDP](#term-sdp) answer back to the Streamer. This should be in response to a previous [offer](#streamer-offer) from the Streamer. - -| Param Name | Type | Description | -|-|-|-| -| sdp | string | The WebRTC SDP package in string format | - -### offer - ->Message is forwarded to a [Player](#term-player). Sends the [SDP](#term-sdp) offer to the specified Player. This begins the WebRTC negotiation between the [SFU](#term-sfu) and the Player. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to send the offer to. | -| sdp | string | The WebRTC SDP package in string format | - -### peerDataChannels - ->Message is forwarded to a [Player](#term-player). Sends information to the Player about what data channels to use for sending/receiving with the [Streamer](#term-streamer). - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The player id of the player to send the message to. | -| sendStreamId | number | The datachannel id for sending data. | -| recvStreamId | number | The datachannel id for receiving data. | - -### streamerDataChannels - ->Message is forwarded to the [Streamer](#term-streamer). Sends a request to the Streamer to open up data channels for a given [Player](#term-player). - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player the request is for. | -| sendStreamId | number | The datachannel id for sending data. | -| recvStreamId | number | The datachannel id for receiving data. | - -## Signalling Server Sent Messages - -### config - -> Message is used to send the peer connection options such as stun and turn servers to a connecting [Streamer](#term-streamer) - -| Param Name | Type | Description | -|-|-|-| -| peerConnectionOptions | Object | The object describing the peer connection options for this server. | - -### identify - -> Message is used to request an identity from a connecting [Streamer](#term-streamer). The Streamer should reply with an [endpointId](#streamer-endpointId) message. See note in [endpointId](#streamer-endpointid) about legacy Streamers and temporary ids. - -| Param Name | Type | Description | -|-|-|-| - -### streamerIDChanged - -> Message is used to communicate to [Player](#term-player)s that the [Streamer](#term-streamer) it is currently subscribed to is changing its ID. This allows Players to keep track of its currently subscribed Streamer and allow auto reconnects to the correct Streamer. This happens if a Streamer sends an [endpointID](#streamer-endpointid) message after it already has an ID assigned. (Can happen if it is late to respond to the [identify](#signalling-identify) message and is auto assigned a legacy ID.) - -| Param Name | Type | Description | -|-|-|-| -| newID | string | The new ID of the subscribed to Streamer | - -### playerConnected - -> Message is used to notify a [Streamer](#term-streamer) that a new [Player](#term-player) has subscribed to the stream. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the new connected player. | -| dataChannel | boolean | Indicates whether the player wants a datachannel or not. | -| sfu | boolean | Indicates if the player is an SFU or not. | -| sendOffer | boolean | Indicates if the new player want's an offer or not. | - -### playerCount - -> Message is sent to Players to indicate how many currently connected players there are on this signalling server. (Note: This is mostly old behaviour and is not influenced by multi streamers or who is subscribed to what Streamer. It just reports the number of players it knows about.) - -| Param Name | Type | Description | -|-|-|-| -| count | number | The number of players currently connected to this signalling server. | - -### playerDisconnected - -> Message is used to notify a [Streamer](#term-streamer) that a [Player](#term-player) has unsubscribed/disconnected from the stream. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the new connected player. | - -### pong - ->Message is a reply to [ping](#streamer-ping) from a [Streamer](#term-streamer). Replies with the time from the ping message. - -| Param Name | Description | -|-|-| -| time | The timestamp of the ping message. Will be returned in the pong message | - -### streamerDisconnected - -> Message is used to notify players when a [Streamer](#term-streamer) disconnects from the [Signalling Server](#term-signallingserver). - -| Param Name | Type | Description | -|-|-|-| - -### streamerList - ->Message is a reply to [listStreamers](#player-liststreamers) from a [Player](#term-player). Replies with a list of currently active [Streamers](#term-streamer) connected to this server. - -| Param Name | Type | Description | -|-|-|-| -| ids | Array\ | A list of string ids that are currently active on the signalling server. | - -## Streamer Sent Messages - -### answer - ->Message is forwarded to a [Player](#term-player). Sends the [SDP](#term-sdp) answer back to the player. This should be in response to an [offer](#player-offer) from the specified player. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to send the answer to. | -| sdp | string | The WebRTC SDP package in string format | - -### disconnectPlayer - ->Message is consumed by the [Signalling Server](#term-signallingserver). Requests that the Signalling Server disconnect a [Player](#term-player). - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to disconnect | -| reason | string | The reason for the disconnect | - -### endpointId - ->Message is consumed by the [Signalling Server](#term-signallingserver). Specifies an id for the [Streamer](#term-streamer). This is used to uniquely identify multiple streamers connected to the same Signalling Server. Note: to preserve backward compatibility when Streamer ids were optional, when a Streamer first connects it is assigned a temporary ID (\_\_LEGACY\_\_) which allows use of older Streamers if needed. - -| Param Name | Type | Description | -|-|-|-| -| id | string | The id of the Streamer | - -### iceCandidate - ->Message is forwarded to a [Player](#term-player). Sends an [ICE](#term-icecandidate) candidate to the specified player. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to send the ICE candidate to. | -| candidate | Object | The JSON string describing the ICE candidate | - -### layerPreference - ->Message is forwarded to a connected [SFU](#term-sfu). Sends a preferred layer index to a connected SFU for a specified [Player](#term-player). Useful for switching between SFU quality layers to force a certain resolution/quality option either as part of UX or testing. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to give the preference to | -| spatialLayer | number | The index of the spatial layer to prefer | -| temporalLayer | number | The index of the temporal layer to prefer | - -### offer - ->Message is forwarded to a [Player](#term-player). Sends the [SDP](#term-sdp) offer to the specified player. Begins the WebRTC negotiation with a player. - -| Param Name | Type | Description | -|-|-|-| -| playerId | string | The id of the player to send the offer to. | -| sdp | string | The WebRTC SDP package in string format | - -### ping - ->Message is consumed by the [Signalling Server](#term-signallingserver). A keepalive ping message that initiates a [pong](#signalling-pong) response. - -| Param Name | Type | Description | -|-|-|-| -| time | number | The timestamp of the ping message. Will be returned in the pong message | diff --git a/SignallingWebServer/README.md b/SignallingWebServer/README.md index ea97e371..d7330e48 100644 --- a/SignallingWebServer/README.md +++ b/SignallingWebServer/README.md @@ -1,33 +1,97 @@ -# Pixel Streaming Signalling Server - -The signalling server is a small intermediary application that sits between streamers and other peers. It handles the initial connection negotiations and some other small ongoing control messages between peers as well as acting as a simple web server for serving the [Frontend](/Frontend/README.md) web application. - -## Configuration - -Configuration of the signalling server is handled via the config.js file in the SignallingWebServer directory. The following are its supported options. -| Name | Type | Default | Description | -|-|-|-|-| -| UseFrontend | Boolean | false | Enables or disables the use of the Frontend. | -| UseMatchmaker | Boolean | false | Enables or disables the use of the [Matchmaker](/Matchmaker) application. | -| UseHTTPS | Boolean | false | Enables or disables ssl for the serving of the Frontend. | -| HTTPSCertFile | String | './certificates/client-cert.pem' | The path to the SSL cert file for when HTTPS is enabled. | -| HTTPSKeyFile | String | './certificates/client-key.pem' | The path to the SSL key file for when HTTPS is enabled. | -| LogToFile | Boolean | true | Enable or disable logging to a file in the 'logs' folder. | -| LogVerbose | Boolean | true | Enable or disable verbose logging. Adds a lot of extra information to logs. | -| HomepageFile | String | 'player.html' | The root file of the frontend web application. | -| AdditionalRoutes | Map | | Additional routes for the web application. | -| EnableWebserver | Boolean | true | Enables or disables the serving of the frontend through the internal web server. Disbable this if you are serving your own frontend. | -| MatchmakerAddress | String | | The IP/hostname of the matchmaker application. | -| MatchmakerPort | Number | 9999 | The port the matchmaker is listening on. | -| PublicIp | String | "localhost" | The public IP/hostname of the host that the signalling server is listening on. This is used by the matchmaker. | -| HttpPort | Number | 80 | The port for the internal webserver to listen on. | -| HttpsPort | Number | 443 | The port for the internal webserver to listen on when HTTPS is enabled. | -| StreamerPort | Number | 8888 | The port to listen on for new streamer connections. | -| SFUPort | Number | 8889 | The port to listen on for new SFU connections. | -| MaxPlayerCount | Number | -1 | A limit for connected players in total on this signalling server. -1 to disable limit. | -| DisableSSLCert | Boolean | true | When HTTPS is enabled and this is true, insecure certificates can be used. This is convenient for local testing but please DO NOT SHIP THIS IN PRODUCTION | +# Wilbur + +A Direct replacement for cirrus. + +Wilbur is a small intermediary application that sits between streamers and other peers. It handles the initial connection negotiations and some other small ongoing control messages between peers as well as acting as a simple web server for serving the [Frontend](/Frontend/README.md) web application. + +Differences of behaviour from the old cirrus are described [here](from_cirrus.md). + +## Building +Building is handled by `npm` and `tsc` as a very basic typescript project. The easiest method is to invoke +``` +npm run build +``` +Which will output built files into the `build` directory. ## Running +You can run with both `node` directly or the `npm start` script. +``` +npm start -- [arguments] +``` +or +``` +node build/index.js [arguments] +``` +Invoking `npm start -- --help` or `node build/index.js --help` will display the configuration options. +``` +Usage: node build/index.js [options] + +A basic signalling server application for Unreal Engine's Pixel Streaming applications. -Several scripts are supplied for Windows and Linux in the [platform_scripts](platform_scripts/) folder. These are the easiest way to get the server running under common situations. They can also be used as a reference for new situations. +Options: + -V, --version output the version number + --log_folder Sets the path for the log files. (default: "logs") + --log_level_console Sets the logging level for console messages. (choices: "debug", "info", "warning", + "error", default: "info") + --log_level_file Sets the logging level for log files. (choices: "debug", "info", "warning", + "error", default: "info") + --console_messages [detail] Displays incoming and outgoing signalling messages on the console. (choices: + "basic", "verbose", "formatted", preset: "basic") + --streamer_port Sets the listening port for streamer connections. (default: "8888") + --player_port Sets the listening port for player connections. (default: "80") + --sfu_port Sets the listening port for SFU connections. (default: "8889") + --serve Enables the webserver on player_port. (default: false) + --http_root Sets the path for the webserver root. (default: "www") + --homepage The default html file to serve on the web server. (default: "player.html") + --https Enables the webserver on https_port and enabling SSL (default: false) + --https_port Sets the listen port for the https server. (default: 443) + --ssl_key_path Sets the path for the SSL key file. (default: "ssl/key.pem") + --ssl_cert_path Sets the path for the SSL certificate file. (default: "ssl/cert.pem") + --https_redirect Enables the redirection of connection attempts on http to https. If this is not set + the webserver will only listen on https_port. Player websockets will still listen + on player_port. (default: false) + --rest_api Enables the rest API interface that can be accessed at + /api/api-definition (default: false) + --peer_options Additional JSON data to send in peerConnectionOptions of the config message. + --matchmaker Enable matchmaker connection. (default: false) + --matchmaker_address
Sets the matchmaker address to connect to. (default: "127.0.0.1") + --matchmaker_port Sets the matchmaker port to connect to. (default: "9999") + --matchmaker_retry Sets the delay before reconnecting to the matchmaker after a disconnect. (default: + "5") + --matchmaker_keepalive Sets the delay between matchmaker pings. (default: "30") + --public_ip The public IP address to be used to connect to this server. Only needed when using + matchmaker. (default: "127.0.0.1") + --log_config Will print the program configuration on startup. (default: false) + --stdin Allows stdin input while running. (default: false) + --no_config Skips the reading of the config file. Only CLI options will be used. (default: + false) + --config_file Sets the path of the config file. (default: "config.json") + --no_save On startup the given configuration is resaved out to config.json. This switch will + prevent this behaviour allowing the config.json file to remain untouched while + running with new configurations. (default: false) + -h, --help Display this help text. +``` +These CLI options can also be described in a `config.json` (default config file overridable with --config_file) by specifying the command option name and value in a simple JSON object. eg. +``` +{ + "log_folder": "logs", + "log_level_console": "info", + "log_level_file": "info", + "streamer_port": "8888", + "player_port": "80", + "sfu_port": "8889", + "serve": true, + "http_root": "www", + "homepage": "player.html", + "log_config": false, + "stdin": false +} +``` +Given these options, to start the server with the closest behaviour as the old cirrus, you would invoke, +``` +npm start -- --console_messages verbose --serve --http_root Public --homepage player.html +``` +Note that `Public` being used as the http root assumes your Frontend is in that directory from the old behaviour of the scripts. The new convenience scripts (`platform_scripts` directory) will now build the frontend into the `www` directory. +## Development +This implementation is built on the [Signalling](../Signalling) library which is supplied as a library for developing signalling applications. Visit its [documentation](../Signalling/docs) for more information. diff --git a/SignallingWebServer/apidoc/api-definition-base.yml b/SignallingWebServer/apidoc/api-definition-base.yml new file mode 100644 index 00000000..0908bfa0 --- /dev/null +++ b/SignallingWebServer/apidoc/api-definition-base.yml @@ -0,0 +1,29 @@ +openapi: "3.1.0" +info: + title: "Pixel Streaming Signalling Server API" + version: "1.0.0" +servers: + - url: "/api" +paths: {} +components: + schemas: + Streamer: + type: object + properties: + streamerId: + type: string + description: The streamer's unique identifier + streaming: + type: boolean + description: True if the streamer is accepting players + required: + - streamerId + - streaming + Player: + type: object + properties: + playerId: + type: string + description: The player's unique identifier + required: + - playerId diff --git a/SignallingWebServer/cirrus.js b/SignallingWebServer/cirrus.js deleted file mode 100644 index 7d5a67c9..00000000 --- a/SignallingWebServer/cirrus.js +++ /dev/null @@ -1,1272 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -//-- Server side logic. Serves pixel streaming WebRTC-based page, proxies data back to Streamer --// - -var express = require('express'); -var app = express(); - -const fs = require('fs'); -const path = require('path'); -const querystring = require('querystring'); -const bodyParser = require('body-parser'); -const logging = require('./modules/logging.js'); -logging.RegisterConsoleLogger(); - -// Command line argument --configFile needs to be checked before loading the config, all other command line arguments are dealt with through the config object - -const defaultConfig = { - UseFrontend: false, - UseMatchmaker: false, - UseHTTPS: false, - HTTPSCertFile: './certificates/client-cert.pem', - HTTPSKeyFile: './certificates/client-key.pem', - LogToFile: true, - LogVerbose: true, - HomepageFile: 'player.html', - AdditionalRoutes: new Map(), - EnableWebserver: true, - MatchmakerAddress: "", - MatchmakerPort: 9999, - PublicIp: "localhost", - HttpPort: 80, - HttpsPort: 443, - StreamerPort: 8888, - SFUPort: 8889, - MaxPlayerCount: -1, - DisableSSLCert: true -}; - -const argv = require('yargs').argv; -var configFile = (typeof argv.configFile != 'undefined') ? argv.configFile.toString() : path.join(__dirname, 'config.json'); -console.log(`configFile ${configFile}`); -const config = require('./modules/config.js').init(configFile, defaultConfig); - -if (config.LogToFile) { - logging.RegisterFileLogger('./logs/'); -} - -console.log("Config: " + JSON.stringify(config, null, '\t')); - -var http = require('http').Server(app); - -if (config.UseHTTPS) { - //HTTPS certificate details - const options = { - key: fs.readFileSync(path.join(__dirname, config.HTTPSKeyFile)), - cert: fs.readFileSync(path.join(__dirname, config.HTTPSCertFile)) - }; - - var https = require('https').Server(options, app); -} - -const helmet = require('helmet'); -var hsts = require('hsts'); -var net = require('net'); - -var FRONTEND_WEBSERVER = 'https://localhost'; -if (config.UseFrontend) { - var httpPort = 3000; - var httpsPort = 8000; - - if (config.UseHTTPS && config.DisableSSLCert) { - //Required for self signed certs otherwise just get an error back when sending request to frontend see https://stackoverflow.com/a/35633993 - console.logColor(logging.Orange, 'WARNING: config.DisableSSLCert is true. Unauthorized SSL certificates will be allowed! This is convenient for local testing but please DO NOT SHIP THIS IN PRODUCTION. To remove this warning please set DisableSSLCert to false in your config.json.'); - process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0" - } - - const httpsClient = require('./modules/httpsClient.js'); - var webRequest = new httpsClient(); -} else { - var httpPort = config.HttpPort; - var httpsPort = config.HttpsPort; -} - -var streamerPort = config.StreamerPort; // port to listen to Streamer connections -var sfuPort = config.SFUPort; - -var matchmakerAddress = '127.0.0.1'; -var matchmakerPort = 9999; -var matchmakerRetryInterval = 5; -var matchmakerKeepAliveInterval = 30; -var maxPlayerCount = -1; - -var gameSessionId; -var userSessionId; -var serverPublicIp; - -// `clientConfig` is send to Streamer and Players -// Example of STUN server setting -// let clientConfig = {peerConnectionOptions: { 'iceServers': [{'urls': ['stun:34.250.222.95:19302']}] }}; -var clientConfig = { type: 'config', peerConnectionOptions: {} }; - -// Parse public server address from command line -// --publicIp -try { - if (typeof config.PublicIp != 'undefined') { - serverPublicIp = config.PublicIp.toString(); - } - - if (typeof config.HttpPort != 'undefined') { - httpPort = config.HttpPort; - } - - if (typeof config.HttpsPort != 'undefined') { - httpsPort = config.HttpsPort; - } - - if (typeof config.StreamerPort != 'undefined') { - streamerPort = config.StreamerPort; - } - - if (typeof config.SFUPort != 'undefined') { - sfuPort = config.SFUPort; - } - - if (typeof config.FrontendUrl != 'undefined') { - FRONTEND_WEBSERVER = config.FrontendUrl; - } - - if (typeof config.peerConnectionOptions != 'undefined') { - clientConfig.peerConnectionOptions = JSON.parse(config.peerConnectionOptions); - console.log(`peerConnectionOptions = ${JSON.stringify(clientConfig.peerConnectionOptions)}`); - } else { - console.log("No peerConnectionConfig") - } - - if (typeof config.MatchmakerAddress != 'undefined') { - matchmakerAddress = config.MatchmakerAddress; - } - - if (typeof config.MatchmakerPort != 'undefined') { - matchmakerPort = config.MatchmakerPort; - } - - if (typeof config.MatchmakerRetryInterval != 'undefined') { - matchmakerRetryInterval = config.MatchmakerRetryInterval; - } - - if (typeof config.MaxPlayerCount != 'undefined') { - maxPlayerCount = config.MaxPlayerCount; - } -} catch (e) { - console.error(e); - process.exit(2); -} - -if (config.UseHTTPS) { - app.use(helmet()); - - app.use(hsts({ - maxAge: 15552000 // 180 days in seconds - })); - - //Setup http -> https redirect - console.log('Redirecting http->https'); - app.use(function (req, res, next) { - if (!req.secure) { - if (req.get('Host')) { - var hostAddressParts = req.get('Host').split(':'); - var hostAddress = hostAddressParts[0]; - if (httpsPort != 443) { - hostAddress = `${hostAddress}:${httpsPort}`; - } - return res.redirect(['https://', hostAddress, req.originalUrl].join('')); - } else { - console.error(`unable to get host name from header. Requestor ${req.ip}, url path: '${req.originalUrl}', available headers ${JSON.stringify(req.headers)}`); - return res.status(400).send('Bad Request'); - } - } - next(); - }); -} - -sendGameSessionData(); - -// set up rate limiter: maximum of five requests per minute -var RateLimit = require('express-rate-limit'); -var limiter = RateLimit({ - windowMs: 1*60*1000, // 1 minute - max: 60 -}); - -// apply rate limiter to all requests -app.use(limiter); - -if(config.EnableWebserver) { - //Setup folders - app.use(express.static(path.join(__dirname, '/Public'))) - app.use('/images', express.static(path.join(__dirname, './images'))) - app.use('/scripts', express.static(path.join(__dirname, '/scripts'))); - app.use('/', express.static(path.join(__dirname, '/custom_html'))) -} - -try { - for (var property in config.AdditionalRoutes) { - if (config.AdditionalRoutes.hasOwnProperty(property)) { - console.log(`Adding additional routes "${property}" -> "${config.AdditionalRoutes[property]}"`) - app.use(property, express.static(path.join(__dirname, config.AdditionalRoutes[property]))); - } - } -} catch (err) { - console.error(`reading config.AdditionalRoutes: ${err}`) -} - -if(config.EnableWebserver) { - - // Request has been sent to site root, send the homepage file - app.get('/', function (req, res) { - homepageFile = (typeof config.HomepageFile != 'undefined' && config.HomepageFile != '') ? config.HomepageFile.toString() : defaultConfig.HomepageFile; - - let pathsToTry = [ path.join(__dirname, homepageFile), path.join(__dirname, '/Public', homepageFile), path.join(__dirname, '/custom_html', homepageFile), homepageFile ]; - - // Try a few paths, see if any resolve to a homepage file the user has set - for(let pathToTry of pathsToTry){ - if(fs.existsSync(pathToTry)){ - // Send the file for browser to display it - res.sendFile(pathToTry); - return; - } - } - - // Catch file doesn't exist, and send back 404 if not - console.error('Unable to locate file ' + homepageFile) - res.status(404).send('Unable to locate file ' + homepageFile); - return; - }); -} - -//Setup http and https servers -http.listen(httpPort, function () { - console.logColor(logging.Green, 'Http listening on *: ' + httpPort); -}); - -if (config.UseHTTPS) { - https.listen(httpsPort, function () { - console.logColor(logging.Green, 'Https listening on *: ' + httpsPort); - }); -} - -console.logColor(logging.Cyan, `Running Cirrus - The Pixel Streaming reference implementation signalling server for Unreal Engine 5.3.`); - -let nextPlayerId = 1; - -const StreamerType = { Regular: 0, SFU: 1 }; - -class Streamer { - constructor(initialId, ws, type) { - this.id = initialId; - this.ws = ws; - this.type = type; - this.idCommitted = false; - } - - // registers this streamers id - commitId(id) { - this.id = id; - this.idCommitted = true; - } - - // returns true if we have a valid id - isIdCommitted() { - return this.idCommitted; - } - - // links this streamer to a subscribed SFU player (player component of an SFU) - addSFUPlayer(sfuPlayerId) { - if (!!this.SFUPlayerId && this.SFUPlayerId != sfuPlayerId) { - console.error(`Streamer ${this.id} already has an SFU ${this.SFUPlayerId}. Trying to add ${sfuPlayerId} as SFU.`); - return; - } - this.SFUPlayerId = sfuPlayerId; - } - - // removes the previously subscribed SFU player - removeSFUPlayer() { - delete this.SFUPlayerId; - } - - // gets the player id of the subscribed SFU if any - getSFUPlayerId() { - return this.SFUPlayerId; - } - - // returns true if this streamer is forwarding another streamer - isSFU() { - return this.type == StreamerType.SFU; - } - - // links this streamer to a player, used for SFU connections since they have both components - setSFUPlayerComponent(playerComponent) { - if (!this.isSFU()) { - console.error(`Trying to add an SFU player component ${playerComponent.id} to streamer ${this.id} but it is not an SFU type.`); - return; - } - this.sfuPlayerComponent = playerComponent; - } - - // gets the player component for this sfu - getSFUPlayerComponent() { - if (!this.isSFU()) { - console.error(`Trying to get an SFU player component from streamer ${this.id} but it is not an SFU type.`); - return null; - } - return this.sfuPlayerComponent; - } -} - -const PlayerType = { Regular: 0, SFU: 1 }; -const WhoSendsOffer = { Streamer: 0, Browser: 1 }; - -class Player { - constructor(id, ws, type, whoSendsOffer) { - this.id = id; - this.ws = ws; - this.type = type; - this.whoSendsOffer = whoSendsOffer; - } - - isSFU() { - return this.type == PlayerType.SFU; - } - - subscribe(streamerId) { - if (!streamers.has(streamerId)) { - console.error(`subscribe: Player ${this.id} tried to subscribe to a non-existent streamer ${streamerId}`); - return; - } - this.streamerId = streamerId; - if (this.type == PlayerType.SFU) { - let streamer = streamers.get(this.streamerId); - streamer.addSFUPlayer(this.id); - } - const msg = { type: 'playerConnected', playerId: this.id, dataChannel: true, sfu: this.type == PlayerType.SFU, sendOffer: this.whoSendsOffer == WhoSendsOffer.Streamer }; - logOutgoing(this.streamerId, msg); - this.sendFrom(msg); - } - - unsubscribe() { - if (this.streamerId && streamers.has(this.streamerId)) { - if (this.type == PlayerType.SFU) { - let streamer = streamers.get(this.streamerId); - if (streamer.getSFUPlayerId() != this.id) { - console.error(`Trying to unsibscribe SFU player ${this.id} from streamer ${streamer.id} but the current SFUId does not match (${streamer.getSFUPlayerId()}).`) - } else { - streamer.removeSFUPlayer(); - } - } - const msg = { type: 'playerDisconnected', playerId: this.id }; - logOutgoing(this.streamerId, msg); - this.sendFrom(msg); - } - this.streamerId = null; - } - - sendFrom(message) { - if (!this.streamerId) { - if(streamers.size > 0) { - this.streamerId = streamers.entries().next().value[0]; - console.logColor(logging.Orange, `Player ${this.id} attempted to send an outgoing message without having subscribed first. Defaulting to ${this.streamerId}`); - } else { - console.logColor(logging.Orange, `Player ${this.id} attempted to send an outgoing message without having subscribed first. No streamer connected so this message isn't going anywhere!`) - return; - } - } - - // normally we want to indicate what player this message came from - // but in some instances we might already have set this (streamerDataChannels) due to poor choices - if (!message.playerId) { - message.playerId = this.id; - } - const msgString = JSON.stringify(message); - - let streamer = streamers.get(this.streamerId); - if (!streamer) { - console.error(`sendFrom: Player ${this.id} subscribed to non-existent streamer: ${this.streamerId}`); - } else { - streamer.ws.send(msgString); - } - } - - sendTo(message) { - const msgString = JSON.stringify(message); - this.ws.send(msgString); - } - - setSFUStreamerComponent(streamerComponent) { - if (!this.isSFU()) { - console.error(`Trying to add an SFU streamer component ${streamerComponent.id} to player ${this.id} but it is not an SFU type.`); - return; - } - this.sfuStreamerComponent = streamerComponent; - } - - getSFUStreamerComponent() { - if (!this.isSFU()) { - console.error(`Trying to get an SFU streamer component from player ${this.id} but it is not an SFU type.`); - return null; - } - return this.sfuStreamerComponent; - } -}; - -let streamers = new Map(); // streamerId <-> streamer -let players = new Map(); // playerId <-> player/peer/viewer -const LegacyStreamerPrefix = "__LEGACY_STREAMER__"; // old streamers that dont know how to ID will be assigned this id prefix. -const LegacySFUPrefix = "__LEGACY_SFU__"; // same as streamer version but for SFUs -const streamerIdTimeoutSecs = 5; - -// gets the SFU subscribed to this streamer if any. -function getSFUForStreamer(streamerId) { - if (!streamers.has(streamerId)) { - return null; - } - const streamer = streamers.get(streamerId); - const sfuPlayerId = streamer.getSFUPlayerId(); - if (!sfuPlayerId) { - return null; - } - return players.get(sfuPlayerId); -} - -function logIncoming(sourceName, msg) { - if (config.LogVerbose) - console.logColor(logging.Blue, "\x1b[37m%s ->\x1b[34m %s", sourceName, JSON.stringify(msg)); - else - console.logColor(logging.Blue, "\x1b[37m%s ->\x1b[34m %s", sourceName, msg.type); -} - -function logOutgoing(destName, msg) { - if (config.LogVerbose) - console.logColor(logging.Green, "\x1b[37m%s <-\x1b[32m %s", destName, JSON.stringify(msg)); - else - console.logColor(logging.Green, "\x1b[37m%s <-\x1b[32m %s", destName, msg.type); -} - -function logForward(srcName, destName, msg) { - if (config.LogVerbose) - console.logColor(logging.Cyan, "\x1b[37m%s -> %s\x1b[36m %s", srcName, destName, JSON.stringify(msg)); - else - console.logColor(logging.Cyan, "\x1b[37m%s -> %s\x1b[36m %s", srcName, destName, msg.type); -} - -let WebSocket = require('ws'); - -let sfuMessageHandlers = new Map(); -let playerMessageHandlers = new Map(); - -function sanitizePlayerId(playerId) { - if (playerId && typeof playerId === 'number') { - playerId = playerId.toString(); - } - return playerId; -} - -function getPlayerIdFromMessage(msg) { - return sanitizePlayerId(msg.playerId); -} - -let uniqueLegacyStreamerPostfix = 0; -function getUniqueLegacyStreamerId() { - const finalId = LegacyStreamerPrefix + uniqueLegacyStreamerPostfix; - ++uniqueLegacyStreamerPostfix; - return finalId; -} - -let uniqueLegacySFUPostfix = 0; -function getUniqueLegacySFUId() { - const finalId = LegacySFUPrefix + uniqueLegacySFUPostfix; - ++uniqueLegacySFUPostfix; - return finalId; -} - -function requestStreamerId(streamer) { - // first we ask the streamer to id itself. - // if it doesnt reply within a time limit we assume it's an older streamer - // and assign it an id. - - // request id - const msg = { type: "identify" }; - logOutgoing(streamer.id, msg); - streamer.ws.send(JSON.stringify(msg)); - - streamer.idTimer = setTimeout(function() { - // streamer did not respond in time. give it a legacy id. - const newLegacyId = getUniqueLegacyStreamerId(); - if (newLegacyId.length == 0) { - const error = `Ran out of legacy ids.`; - console.error(error); - streamer.ws.close(1008, error); - } else { - registerStreamer(newLegacyId, streamer); - } - - }, streamerIdTimeoutSecs * 1000); -} - -function sanitizeStreamerId(id) { - let maxPostfix = -1; - for (let [streamerId, streamer] of streamers) { - const idMatchRegex = /^(.*?)(\d*)$/; - const [, baseId, postfix] = streamerId.match(idMatchRegex); - // if the id is numeric then base id will be empty and we need to compare with the postfix - if ((baseId != '' && baseId != id) || (baseId == '' && postfix != id)) { - continue; - } - const numPostfix = Number(postfix); - if (numPostfix > maxPostfix) { - maxPostfix = numPostfix - } - } - if (maxPostfix >= 0) { - return id + (maxPostfix + 1); - } - return id; -} - -function registerStreamer(id, streamer) { - // remove any existing streamer id - if (!!streamer.id) { - // notify any connected peers of rename - const renameMessage = { type: "streamerIDChanged", newID: id }; - let clone = new Map(players); - for (let player of clone.values()) { - if (player.streamerId == streamer.id) { - logOutgoing(player.id, renameMessage); - player.sendTo(renameMessage); - player.streamerId = id; // reassign the subscription - } - } - streamers.delete(streamer.id); - } - // make sure the id is unique - const uniqueId = sanitizeStreamerId(id); - streamer.commitId(uniqueId); - if (!!streamer.idTimer) { - clearTimeout(streamer.idTimer); - delete streamer.idTimer; - } - streamers.set(uniqueId, streamer); - console.logColor(logging.Green, `Registered new streamer: ${streamer.id}`); -} - -function onStreamerDisconnected(streamer) { - if (!!streamer.idTimer) { - clearTimeout(streamer.idTimer); - } - - if (!streamer.id || !streamers.has(streamer.id)) { - return; - } - - sendStreamerDisconnectedToMatchmaker(); - let sfuPlayer = getSFUForStreamer(streamer.id); - if (sfuPlayer) { - const msg = { type: "streamerDisconnected" }; - logOutgoing(sfuPlayer.id, msg); - sfuPlayer.sendTo(msg); - disconnectAllPlayers(sfuPlayer.id); - } - disconnectAllPlayers(streamer.id); - streamers.delete(streamer.id); -} - -function onStreamerMessageId(streamer, msg) { - logIncoming(streamer.id, msg); - - let streamerId = msg.id; - registerStreamer(streamerId, streamer); -} - -function onStreamerMessagePing(streamer, msg) { - logIncoming(streamer.id, msg); - - const pongMsg = JSON.stringify({ type: "pong", time: msg.time}); - streamer.ws.send(pongMsg); -} - -function onStreamerMessageDisconnectPlayer(streamer, msg) { - logIncoming(streamer.id, msg); - - const playerId = getPlayerIdFromMessage(msg); - const player = players.get(playerId); - if (player) { - player.ws.close(1011 /* internal error */, msg.reason); - } -} - -function onStreamerMessageLayerPreference(streamer, msg) { - let sfuPlayer = getSFUForStreamer(streamer.id); - if (sfuPlayer) { - logOutgoing(sfuPlayer.id, msg); - sfuPlayer.sendTo(msg); - } -} - -function forwardStreamerMessageToPlayer(streamer, msg) { - const playerId = getPlayerIdFromMessage(msg); - const player = players.get(playerId); - if (player) { - delete msg.playerId; - logForward(streamer.id, playerId, msg); - player.sendTo(msg); - } else { - console.warn("No playerId specified, cannot forward message: %s", msg); - } -} - -let streamerMessageHandlers = new Map(); -streamerMessageHandlers.set('endpointId', onStreamerMessageId); -streamerMessageHandlers.set('ping', onStreamerMessagePing); -streamerMessageHandlers.set('offer', forwardStreamerMessageToPlayer); -streamerMessageHandlers.set('answer', forwardStreamerMessageToPlayer); -streamerMessageHandlers.set('iceCandidate', forwardStreamerMessageToPlayer); -streamerMessageHandlers.set('disconnectPlayer', onStreamerMessageDisconnectPlayer); -streamerMessageHandlers.set('layerPreference', onStreamerMessageLayerPreference); - -console.logColor(logging.Green, `WebSocket listening for Streamer connections on :${streamerPort}`) -let streamerServer = new WebSocket.Server({ port: streamerPort, backlog: 1 }); -streamerServer.on('connection', function (ws, req) { - console.logColor(logging.Green, `Streamer connected: ${req.connection.remoteAddress}`); - sendStreamerConnectedToMatchmaker(); - - const temporaryId = req.connection.remoteAddress; - let streamer = new Streamer(temporaryId, ws, StreamerType.Regular); - - ws.on('message', (msgRaw) => { - var msg; - try { - msg = JSON.parse(msgRaw); - } catch(err) { - console.error(`Cannot parse Streamer message: ${msgRaw}\nError: ${err}`); - ws.close(1008, 'Cannot parse'); - return; - } - - let handler = streamerMessageHandlers.get(msg.type); - if (!handler || (typeof handler != 'function')) { - if (config.LogVerbose) { - console.logColor(logging.White, "\x1b[37m-> %s\x1b[34m: %s", streamer.id, msgRaw); - } - console.error(`unsupported Streamer message type: ${msg.type}`); - ws.close(1008, 'Unsupported message type'); - return; - } - handler(streamer, msg); - }); - - ws.on('close', function(code, reason) { - console.error(`streamer ${streamer.id} disconnected: ${code} - ${reason}`); - onStreamerDisconnected(streamer); - }); - - ws.on('error', function(error) { - console.error(`streamer ${streamer.id} connection error: ${error}`); - onStreamerDisconnected(streamer); - try { - ws.close(1006 /* abnormal closure */, `streamer ${streamer.id} connection error: ${error}`); - } catch(err) { - console.error(`ERROR: ws.on error: ${err.message}`); - } - }); - - const configStr = JSON.stringify(clientConfig); - logOutgoing(streamer.id, configStr) - ws.send(configStr); - - requestStreamerId(streamer); -}); - -function forwardSFUMessageToPlayer(sfuPlayer, msg) { - const playerId = getPlayerIdFromMessage(msg); - const player = players.get(playerId); - if (player) { - logForward(sfuPlayer.getSFUStreamerComponent().id, playerId, msg); - player.sendTo(msg); - } -} - -function forwardSFUMessageToStreamer(sfuPlayer, msg) { - logForward(sfuPlayer.getSFUStreamerComponent().id, sfuPlayer.streamerId, msg); - msg.sfuId = sfuPlayer.id; - sfuPlayer.sendFrom(msg); -} - -function onPeerDataChannelsSFUMessage(sfuPlayer, msg) { - // sfu is telling a peer what stream id to use for a data channel - const playerId = getPlayerIdFromMessage(msg); - const player = players.get(playerId); - if (player) { - logForward(sfuPlayer.getSFUStreamerComponent().id, playerId, msg); - player.sendTo(msg); - player.datachannel = true; - } -} - -// basically a duplicate of the streamer id request but this one does not register the streamer -function requestSFUStreamerId(sfuPlayer) { - // request id - const msg = { type: "identify" }; - const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent(); - logOutgoing(sfuStreamerComponent.id, msg); - sfuStreamerComponent.ws.send(JSON.stringify(msg)); - - sfuStreamerComponent.idTimer = setTimeout(function() { - // streamer did not respond in time. give it a legacy id. - const newLegacyId = getUniqueSFUId(); - if (newLegacyId.length == 0) { - const error = `Ran out of legacy ids.`; - console.error(error); - sfuPlayer.ws.close(1008, error); - } else { - sfuStreamerComponent.id = newLegacyId; - } - }, streamerIdTimeoutSecs * 1000); -} - -function onSFUMessageId(sfuPlayer, msg) { - const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent(); - logIncoming(sfuStreamerComponent.id, msg); - sfuStreamerComponent.id = msg.id; - - if (!!sfuStreamerComponent.idTimer) { - clearTimeout(sfuStreamerComponent.idTimer); - delete sfuStreamerComponent.idTimer; - } -} - -function onSFUMessageStartStreaming(sfuPlayer, msg) { - const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent(); - logIncoming(sfuStreamerComponent.id, msg); - if (streamers.has(sfuStreamerComponent.id)) { - console.error(`SFU ${sfuStreamerComponent.id} is already registered as a streamer and streaming.`) - return; - } - - registerStreamer(sfuStreamerComponent.id, sfuStreamerComponent); -} - -function onSFUMessageStopStreaming(sfuPlayer, msg) { - const sfuStreamerComponent = sfuPlayer.getSFUStreamerComponent(); - logIncoming(sfuStreamerComponent.id, msg); -if (!streamers.has(sfuStreamerComponent.id)) { - console.error(`SFU ${sfuStreamerComponent.id} is not registered as a streamer or streaming.`) - return; - } - - onStreamerDisconnected(sfuStreamerComponent); -} - -function onSFUDisconnected(sfuPlayer) { - console.log("disconnecting SFU from streamer"); - disconnectAllPlayers(sfuPlayer.id); - onStreamerDisconnected(sfuPlayer.getSFUStreamerComponent()); - sfuPlayer.unsubscribe(); - sfuPlayer.ws.close(4000, "SFU Disconnected"); - players.delete(sfuPlayer.id); - streamers.delete(sfuPlayer.id); -} - -sfuMessageHandlers.set('listStreamers', onPlayerMessageListStreamers); -sfuMessageHandlers.set('subscribe', onPlayerMessageSubscribe); -sfuMessageHandlers.set('unsubscribe', onPlayerMessageUnsubscribe); -sfuMessageHandlers.set('offer', forwardSFUMessageToPlayer); -sfuMessageHandlers.set('answer', forwardSFUMessageToStreamer); -sfuMessageHandlers.set('streamerDataChannels', forwardSFUMessageToStreamer); -sfuMessageHandlers.set('peerDataChannels', onPeerDataChannelsSFUMessage); -sfuMessageHandlers.set('endpointId', onSFUMessageId); -sfuMessageHandlers.set('startStreaming', onSFUMessageStartStreaming); -sfuMessageHandlers.set('stopStreaming', onSFUMessageStopStreaming); - -console.logColor(logging.Green, `WebSocket listening for SFU connections on :${sfuPort}`); -let sfuServer = new WebSocket.Server({ port: sfuPort }); -sfuServer.on('connection', function (ws, req) { - - let playerId = sanitizePlayerId(nextPlayerId++); - console.logColor(logging.Green, `SFU (${req.connection.remoteAddress}) connected `); - - let streamerComponent = new Streamer(req.connection.remoteAddress, ws, StreamerType.SFU); - let playerComponent = new Player(playerId, ws, PlayerType.SFU, WhoSendsOffer.Streamer); - - streamerComponent.setSFUPlayerComponent(playerComponent); - playerComponent.setSFUStreamerComponent(streamerComponent); - - players.set(playerId, playerComponent); - - ws.on('message', (msgRaw) => { - var msg; - try { - msg = JSON.parse(msgRaw); - } catch (err) { - console.error(`Cannot parse SFU message: ${msgRaw}\nError: ${err}`); - ws.close(1008, 'Cannot parse'); - return; - } - - let sfuPlayer = players.get(playerId); - if (!sfuPlayer) { - console.error(`Received a message from an SFU not in the player list ${playerId}`); - ws.close(1001, 'Broken'); - return; - } - - let handler = sfuMessageHandlers.get(msg.type); - if (!handler || (typeof handler != 'function')) { - if (config.LogVerbose) { - console.logColor(logging.White, "\x1b[37m-> %s\x1b[34m: %s", sfuPlayer.id, msgRaw); - } - console.error(`unsupported SFU message type: ${msg.type}`); - ws.close(1008, 'Unsupported message type'); - return; - } - handler(sfuPlayer, msg); - }); - - ws.on('close', function(code, reason) { - console.error(`SFU disconnected: ${code} - ${reason}`); - onSFUDisconnected(playerComponent); - }); - - ws.on('error', function(error) { - console.error(`SFU connection error: ${error}`); - onSFUDisconnected(playerComponent); - try { - ws.close(1006 /* abnormal closure */, `SFU connection error: ${error}`); - } catch(err) { - console.error(`ERROR: ws.on error: ${err.message}`); - } - }); - - requestStreamerId(playerComponent.getSFUStreamerComponent()); -}); - -let playerCount = 0; - -function sendPlayersCount() { - const msg = { type: 'playerCount', count: players.size }; - logOutgoing("[players]", msg); - for (let player of players.values()) { - player.sendTo(msg); - } -} - -function onPlayerMessageSubscribe(player, msg) { - logIncoming(player.id, msg); - player.subscribe(msg.streamerId); -} - -function onPlayerMessageUnsubscribe(player, msg) { - logIncoming(player.id, msg); - player.unsubscribe(); -} - -function onPlayerMessageStats(player, msg) { - console.log(`player ${playerId}: stats\n${msg.data}`); -} - -function onPlayerMessageListStreamers(player, msg) { - logIncoming(player.id, msg); - - let reply = { type: 'streamerList', ids: [] }; - for (let [streamerId, streamer] of streamers) { - reply.ids.push(streamerId); - } - - logOutgoing(player.id, reply); - player.sendTo(reply); -} - -function forwardPlayerMessage(player, msg) { - logForward(player.id, player.streamerId, msg); - player.sendFrom(msg); -} - -function onPlayerDisconnected(playerId) { - const player = players.get(playerId); - player.unsubscribe(); - players.delete(playerId); - --playerCount; - sendPlayersCount(); - sendPlayerDisconnectedToFrontend(); - sendPlayerDisconnectedToMatchmaker(); -} - -playerMessageHandlers.set('subscribe', onPlayerMessageSubscribe); -playerMessageHandlers.set('unsubscribe', onPlayerMessageUnsubscribe); -playerMessageHandlers.set('stats', onPlayerMessageStats); -playerMessageHandlers.set('offer', forwardPlayerMessage); -playerMessageHandlers.set('answer', forwardPlayerMessage); -playerMessageHandlers.set('iceCandidate', forwardPlayerMessage); -playerMessageHandlers.set('listStreamers', onPlayerMessageListStreamers); -// sfu related messages -playerMessageHandlers.set('dataChannelRequest', forwardPlayerMessage); -playerMessageHandlers.set('peerDataChannelsReady', forwardPlayerMessage); - -console.logColor(logging.Green, `WebSocket listening for Players connections on :${httpPort}`); -let playerServer = new WebSocket.Server({ server: config.UseHTTPS ? https : http}); -playerServer.on('connection', function (ws, req) { - var url = require('url'); - const parsedUrl = url.parse(req.url); - const urlParams = new URLSearchParams(parsedUrl.search); - const whoSendsOffer = urlParams.has('OfferToReceive') && urlParams.get('OfferToReceive') !== 'false' ? WhoSendsOffer.Browser : WhoSendsOffer.Streamer; - - if (playerCount + 1 > maxPlayerCount && maxPlayerCount !== -1) - { - console.logColor(logging.Red, `new connection would exceed number of allowed concurrent connections. Max: ${maxPlayerCount}, Current ${playerCount}`); - ws.close(1013, `too many connections. max: ${maxPlayerCount}, current: ${playerCount}`); - return; - } - - ++playerCount; - let playerId = sanitizePlayerId(nextPlayerId++); - console.logColor(logging.Green, `player ${playerId} (${req.connection.remoteAddress}) connected`); - let player = new Player(playerId, ws, PlayerType.Regular, whoSendsOffer); - players.set(playerId, player); - - ws.on('message', (msgRaw) =>{ - var msg; - try { - msg = JSON.parse(msgRaw); - } catch (err) { - console.error(`Cannot parse player ${playerId} message: ${msgRaw}\nError: ${err}`); - ws.close(1008, 'Cannot parse'); - return; - } - - let player = players.get(playerId); - if (!player) { - console.error(`Received a message from a player not in the player list ${playerId}`); - ws.close(1001, 'Broken'); - return; - } - - let handler = playerMessageHandlers.get(msg.type); - if (!handler || (typeof handler != 'function')) { - if (config.LogVerbose) { - console.logColor(logging.White, "\x1b[37m-> %s\x1b[34m: %s", playerId, msgRaw); - } - console.error(`unsupported player message type: ${msg.type}`); - ws.close(1008, 'Unsupported message type'); - return; - } - handler(player, msg); - }); - - ws.on('close', function(code, reason) { - console.logColor(logging.Yellow, `player ${playerId} connection closed: ${code} - ${reason}`); - onPlayerDisconnected(playerId); - }); - - ws.on('error', function(error) { - console.error(`player ${playerId} connection error: ${error}`); - ws.close(1006 /* abnormal closure */, `player ${playerId} connection error: ${error}`); - onPlayerDisconnected(playerId); - - console.logColor(logging.Red, `Trying to reconnect...`); - reconnect(); - }); - - sendPlayerConnectedToFrontend(); - sendPlayerConnectedToMatchmaker(); - - const configStr = JSON.stringify(clientConfig); - logOutgoing(player.id, configStr) - player.ws.send(configStr); - - sendPlayersCount(); -}); - -function disconnectAllPlayers(streamerId) { - console.log(`unsubscribing all players on ${streamerId}`); - let clone = new Map(players); - for (let player of clone.values()) { - if (player.streamerId == streamerId) { - // disconnect players but just unsubscribe the SFU - const sfuPlayer = getSFUForStreamer(streamerId); - if (sfuPlayer && player.id == sfuPlayer.id) { - sfuPlayer.unsubscribe(); - } else { - player.ws.close(); - } - } - } -} - -/** - * Function that handles the connection to the matchmaker. - */ - -if (config.UseMatchmaker) { - var matchmaker = new net.Socket(); - - matchmaker.on('connect', function() { - console.log(`Cirrus connected to Matchmaker ${matchmakerAddress}:${matchmakerPort}`); - - // message.playerConnected is a new variable sent from the SS to help track whether or not a player - // is already connected when a 'connect' message is sent (i.e., reconnect). This happens when the MM - // and the SS get disconnected unexpectedly (was happening often at scale for some reason). - var playerConnected = false; - - // Set the playerConnected flag to tell the MM if there is already a player active (i.e., don't send a new one here) - if( players && players.size > 0) { - playerConnected = true; - } - - // Add the new playerConnected flag to the message body to the MM - message = { - type: 'connect', - address: typeof serverPublicIp === 'undefined' ? '127.0.0.1' : serverPublicIp, - port: config.UseHTTPS ? httpsPort : httpPort, - ready: streamers.size > 0, - playerConnected: playerConnected - }; - - matchmaker.write(JSON.stringify(message)); - }); - - matchmaker.on('error', (err) => { - console.log(`Matchmaker connection error ${JSON.stringify(err)}`); - }); - - matchmaker.on('end', () => { - console.log('Matchmaker connection ended'); - }); - - matchmaker.on('close', (hadError) => { - console.logColor(logging.Blue, 'Setting Keep Alive to true'); - matchmaker.setKeepAlive(true, 60000); // Keeps it alive for 60 seconds - - console.log(`Matchmaker connection closed (hadError=${hadError})`); - - reconnect(); - }); - - // Attempt to connect to the Matchmaker - function connect() { - matchmaker.connect(matchmakerPort, matchmakerAddress); - } - - // Try to reconnect to the Matchmaker after a given period of time - function reconnect() { - console.log(`Try reconnect to Matchmaker in ${matchmakerRetryInterval} seconds`) - setTimeout(function() { - connect(); - }, matchmakerRetryInterval * 1000); - } - - function registerMMKeepAlive() { - setInterval(function() { - message = { - type: 'ping' - }; - matchmaker.write(JSON.stringify(message)); - }, matchmakerKeepAliveInterval * 1000); - } - - connect(); - registerMMKeepAlive(); -} - -//Keep trying to send gameSessionId in case the server isn't ready yet -function sendGameSessionData() { - //If we are not using the frontend web server don't try and make requests to it - if (!config.UseFrontend) - return; - webRequest.get(`${FRONTEND_WEBSERVER}/server/requestSessionId`, - function (response, body) { - if (response.statusCode === 200) { - gameSessionId = body; - console.log('SessionId: ' + gameSessionId); - } - else { - console.error('Status code: ' + response.statusCode); - console.error(body); - } - }, - function (err) { - //Repeatedly try in cases where the connection timed out or never connected - if (err.code === "ECONNRESET") { - //timeout - sendGameSessionData(); - } else if (err.code === 'ECONNREFUSED') { - console.error('Frontend server not running, unable to setup game session'); - } else { - console.error(err); - } - }); -} - -function sendUserSessionData(serverPort) { - //If we are not using the frontend web server don't try and make requests to it - if (!config.UseFrontend) - return; - webRequest.get(`${FRONTEND_WEBSERVER}/server/requestUserSessionId?gameSessionId=${gameSessionId}&serverPort=${serverPort}&appName=${querystring.escape(clientConfig.AppName)}&appDescription=${querystring.escape(clientConfig.AppDescription)}${(typeof serverPublicIp === 'undefined' ? '' : '&serverHost=' + serverPublicIp)}`, - function (response, body) { - if (response.statusCode === 410) { - sendUserSessionData(serverPort); - } else if (response.statusCode === 200) { - userSessionId = body; - console.log('UserSessionId: ' + userSessionId); - } else { - console.error('Status code: ' + response.statusCode); - console.error(body); - } - }, - function (err) { - //Repeatedly try in cases where the connection timed out or never connected - if (err.code === "ECONNRESET") { - //timeout - sendUserSessionData(serverPort); - } else if (err.code === 'ECONNREFUSED') { - console.error('Frontend server not running, unable to setup user session'); - } else { - console.error(err); - } - }); -} - -function sendServerDisconnect() { - //If we are not using the frontend web server don't try and make requests to it - if (!config.UseFrontend) - return; - try { - webRequest.get(`${FRONTEND_WEBSERVER}/server/serverDisconnected?gameSessionId=${gameSessionId}&appName=${querystring.escape(clientConfig.AppName)}`, - function (response, body) { - if (response.statusCode === 200) { - console.log('serverDisconnected acknowledged by Frontend'); - } else { - console.error('Status code: ' + response.statusCode); - console.error(body); - } - }, - function (err) { - //Repeatedly try in cases where the connection timed out or never connected - if (err.code === "ECONNRESET") { - //timeout - sendServerDisconnect(); - } else if (err.code === 'ECONNREFUSED') { - console.error('Frontend server not running, unable to setup user session'); - } else { - console.error(err); - } - }); - } catch(err) { - console.logColor(logging.Red, `ERROR::: sendServerDisconnect error: ${err.message}`); - } -} - -function sendPlayerConnectedToFrontend() { - //If we are not using the frontend web server don't try and make requests to it - if (!config.UseFrontend) - return; - try { - webRequest.get(`${FRONTEND_WEBSERVER}/server/clientConnected?gameSessionId=${gameSessionId}&appName=${querystring.escape(clientConfig.AppName)}`, - function (response, body) { - if (response.statusCode === 200) { - console.log('clientConnected acknowledged by Frontend'); - } else { - console.error('Status code: ' + response.statusCode); - console.error(body); - } - }, - function (err) { - //Repeatedly try in cases where the connection timed out or never connected - if (err.code === "ECONNRESET") { - //timeout - sendPlayerConnectedToFrontend(); - } else if (err.code === 'ECONNREFUSED') { - console.error('Frontend server not running, unable to setup game session'); - } else { - console.error(err); - } - }); - } catch(err) { - console.logColor(logging.Red, `ERROR::: sendPlayerConnectedToFrontend error: ${err.message}`); - } -} - -function sendPlayerDisconnectedToFrontend() { - //If we are not using the frontend web server don't try and make requests to it - if (!config.UseFrontend) - return; - try { - webRequest.get(`${FRONTEND_WEBSERVER}/server/clientDisconnected?gameSessionId=${gameSessionId}&appName=${querystring.escape(clientConfig.AppName)}`, - function (response, body) { - if (response.statusCode === 200) { - console.log('clientDisconnected acknowledged by Frontend'); - } - else { - console.error('Status code: ' + response.statusCode); - console.error(body); - } - }, - function (err) { - //Repeatedly try in cases where the connection timed out or never connected - if (err.code === "ECONNRESET") { - //timeout - sendPlayerDisconnectedToFrontend(); - } else if (err.code === 'ECONNREFUSED') { - console.error('Frontend server not running, unable to setup game session'); - } else { - console.error(err); - } - }); - } catch(err) { - console.logColor(logging.Red, `ERROR::: sendPlayerDisconnectedToFrontend error: ${err.message}`); - } -} - -function sendStreamerConnectedToMatchmaker() { - if (!config.UseMatchmaker) - return; - try { - message = { - type: 'streamerConnected' - }; - matchmaker.write(JSON.stringify(message)); - } catch (err) { - console.logColor(logging.Red, `ERROR sending streamerConnected: ${err.message}`); - } -} - -function sendStreamerDisconnectedToMatchmaker() { - if (!config.UseMatchmaker) - return; - try { - message = { - type: 'streamerDisconnected' - }; - matchmaker.write(JSON.stringify(message)); - } catch (err) { - console.logColor(logging.Red, `ERROR sending streamerDisconnected: ${err.message}`); - } -} - -// The Matchmaker will not re-direct clients to this Cirrus server if any client -// is connected. -function sendPlayerConnectedToMatchmaker() { - if (!config.UseMatchmaker) - return; - try { - message = { - type: 'clientConnected' - }; - matchmaker.write(JSON.stringify(message)); - } catch (err) { - console.logColor(logging.Red, `ERROR sending clientConnected: ${err.message}`); - } -} - -// The Matchmaker is interested when nobody is connected to a Cirrus server -// because then it can re-direct clients to this re-cycled Cirrus server. -function sendPlayerDisconnectedToMatchmaker() { - if (!config.UseMatchmaker) - return; - try { - message = { - type: 'clientDisconnected' - }; - matchmaker.write(JSON.stringify(message)); - } catch (err) { - console.logColor(logging.Red, `ERROR sending clientDisconnected: ${err.message}`); - } -} diff --git a/SignallingWebServer/config.json b/SignallingWebServer/config.json deleted file mode 100644 index fe302289..00000000 --- a/SignallingWebServer/config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "UseFrontend": false, - "UseMatchmaker": false, - "UseHTTPS": false, - "LogToFile": true, - "LogVerbose": true, - "HomepageFile": "player.html", - "AdditionalRoutes": {}, - "EnableWebserver": true, - "MatchmakerAddress": "", - "MatchmakerPort": 9999, - "PublicIp": "localhost", - "HttpPort": 80, - "HttpsPort": 443, - "StreamerPort": 8888, - "SFUPort": 8889, - "MaxPlayerCount": -1 -} \ No newline at end of file diff --git a/SignallingWebServer/from_cirrus.md b/SignallingWebServer/from_cirrus.md new file mode 100644 index 00000000..f998ca7a --- /dev/null +++ b/SignallingWebServer/from_cirrus.md @@ -0,0 +1,12 @@ +# Cirrus to Wilbur Guide + +This is just a small brief to describe the differences between the now deprecated `cirrus` signalling server and the new `wilbur` signalling server. + +- Configuration is handled by CLI options or the same options in a `config.json` +- By default web serving is disabled but can be enabled by supplying --serve +- Convenience scripts in `platform_scripts' will append --serve and try to preproduce old cirrus behaviour. +- Frontend will be placed in `www` instead of `Public` when using convenience scripts. +- Messages are now described by protobufs in the [Common](../Common/protobuf/signalling_messages.proto) library. +- The server is built on top of the [Common](../Common) library which is provided as a tool for developers to build their own applications. +- Logs from wilbur are now structured JSON so they can be easily ingested into external tools. +- Messages sent and received are no longer echoed to the terminal by default. The --console_messages argument can control this behaviour. diff --git a/SignallingWebServer/modules/authentication/db/index.js b/SignallingWebServer/modules/authentication/db/index.js deleted file mode 100644 index 5fd0f687..00000000 --- a/SignallingWebServer/modules/authentication/db/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -exports.users = require('./users'); diff --git a/SignallingWebServer/modules/authentication/db/users.js b/SignallingWebServer/modules/authentication/db/users.js deleted file mode 100644 index 586ccb92..00000000 --- a/SignallingWebServer/modules/authentication/db/users.js +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -const fs = require('fs'); -const path = require('path'); - -// Read in users from file -let records = []; -let usersFile = path.join(__dirname, './users.json'); -if (fs.existsSync(usersFile)) { - console.log(`Reading users from '${usersFile}'`) - var content = fs.readFileSync(usersFile, 'utf8'); - try { - records = JSON.parse(content); - } catch(e) { - console.log(`ERROR: Failed to parse users from file '${usersFile}'`) - } -} - -exports.findById = function(id, cb) { - var idx = id - 1; - if (records[idx]) { - cb(null, records[idx]); - } else { - cb(new Error('User ' + id + ' does not exist')); - } -} - -exports.findByUsername = function(username, cb) { - for (var i = 0, len = records.length; i < len; i++) { - var record = records[i]; - if (record.username === username) { - return cb(null, record); - } - } - return cb(null, null); -} diff --git a/SignallingWebServer/modules/authentication/index.js b/SignallingWebServer/modules/authentication/index.js deleted file mode 100644 index d19bf66b..00000000 --- a/SignallingWebServer/modules/authentication/index.js +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -module.exports = { - init: require('./init') -} \ No newline at end of file diff --git a/SignallingWebServer/modules/authentication/init.js b/SignallingWebServer/modules/authentication/init.js deleted file mode 100644 index 7dec8c8a..00000000 --- a/SignallingWebServer/modules/authentication/init.js +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -// Adapted from -// * https://blog.risingstack.com/node-hero-node-js-authentication-passport-js/ -// * https://github.com/RisingStack/nodehero-authentication/tree/master/app -// * https://github.com/passport/express-4.x-local-example - - -const passport = require('passport'); -const session = require('express-session'); -const bcrypt = require('bcryptjs'); -const LocalStrategy = require('passport-local').Strategy; -const path = require('path'); -const fs = require('fs'); -const csrf = require('lusca').csrf; -var db = require('./db'); - -function initPassport (app) { - - // Generate session secret if it doesn't already exist and save it to file for use next time - let config = {}; - let configPath = path.join(__dirname, './config.json'); - if (fs.existsSync(configPath)) { - let content = fs.readFileSync(configPath, 'utf8'); - try { - config = JSON.parse(content); - } catch (e) { - console.log(`Error with config file '${configPath}': ${e}`); - } - } - - if(!config.sessionSecret){ - config.sessionSecret = bcrypt.genSaltSync(12); - let content = JSON.stringify(config); - fs.writeFileSync(configPath, content); - } - - app.use(csrf()); - - // Setup session id settings - app.use(session({ - secret: config.sessionSecret, - resave: false, - saveUninitialized: false, - cookie: { - secure: true, - maxAge: 24 * 60 * 60 * 1000 /* 1 day */ - //maxAge: 5 * 1000 /* 5 seconds */ - } - })); - - app.use(passport.initialize()); - app.use(passport.session()); - - passport.serializeUser(function(user, cb) { - cb(null, user.id); - }); - - passport.deserializeUser(function(id, cb) { - db.users.findById(id, function (err, user) { - if (err) { return cb(err); } - cb(null, user); - }); - }); - - console.log('Setting up auth'); - passport.use(new LocalStrategy( - (username, password, callback) => { - db.users.findByUsername(username, (err, user) => { - if (err) { - console.log(`Unable to login '${username}', error ${err}`); - return callback(err); - } - - // User not found - if (!user) { - console.log(`User '${username}' not found`); - return callback(null, false); - } - - // Always use hashed passwords and fixed time comparison - bcrypt.compare(password, user.passwordHash, (err, isValid) => { - if (err) { - console.log(`Error comparing password for user '${username}': ${err}`); - return callback(err); - } - if (!isValid) { - console.log(`Password incorrect for user '${username}'`) - return callback(null, false); - } - - console.log(`User '${username}' logged in`); - return callback(null, user); - }); - }) - } - )); - - passport.authenticationMiddleware = function authenticationMiddleware (redirectUrl) { - return function (req, res, next) { - if (req.isAuthenticated()) { - return next(); - } - - // Set redirectTo property so that user can be redirected back there after logging in - //console.log(`Original request path '${req.originalUrl}'`); - req.session.redirectTo = req.originalUrl; - res.redirect(redirectUrl); - } - } -} - -module.exports = initPassport; \ No newline at end of file diff --git a/SignallingWebServer/modules/config.js b/SignallingWebServer/modules/config.js deleted file mode 100644 index e4f33cad..00000000 --- a/SignallingWebServer/modules/config.js +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -//-- Provides configuration information from file and combines it with default values and command line arguments --// -//-- Hierachy of values: Default Values < Config File < Command Line arguments --// - -const fs = require('fs'); -const path = require('path'); -const argv = require('yargs').argv; - -function initConfig(configFile, defaultConfig){ - defaultConfig = defaultConfig || {}; - - // Using object spread syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals - let config = {...defaultConfig}; - try { - let configData = fs.readFileSync(configFile, 'UTF8'); - fileConfig = JSON.parse(configData); - config = {...config, ...fileConfig} - - try { - accessSync('configFile', constants.W_OK); - // Update config file with any additional defaults (does not override existing values if default has changed) - fs.writeFileSync(configFile, JSON.stringify(config, null, '\t'), 'UTF8'); - } catch (err) { - console.log("Config file is readonly, skipping writing config..."); - } - - } catch(err) { - if (err.code === 'ENOENT') { - console.log("No config file found, writing defaults to log file " + configFile); - fs.writeFileSync(configFile, JSON.stringify(config, null, '\t'), 'UTF8'); - } else if (err instanceof SyntaxError) { - console.log(`ERROR: Invalid JSON in ${configFile}, ignoring file config, ${err}`) - } else { - console.log(`ERROR: ${err}`); - } - } - - try { - //Make a copy of the command line args and remove the unneccessary ones - //The _ value is an array of any elements without a key - let commandLineConfig = {...argv} - delete commandLineConfig._; - delete commandLineConfig.help; - delete commandLineConfig.version; - delete commandLineConfig['$0']; - config = {...config, ...commandLineConfig} - } catch(err) { - console.log(`ERROR: ${err}`); - } - return config; -} - -module.exports = { - init: initConfig -} \ No newline at end of file diff --git a/SignallingWebServer/modules/httpsClient.js b/SignallingWebServer/modules/httpsClient.js deleted file mode 100644 index 4b1fe009..00000000 --- a/SignallingWebServer/modules/httpsClient.js +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. -var querystring = require('querystring') -const https = require('https'); -const assert = require('assert'); - -function cleanUrl(aUrl){ - let url = aUrl; - if(url.startsWith("https://")) - url = url.substring("https://".length); - - return url -} - -function createOptions(requestType, url){ - let index = url.indexOf('/'); - - let urlParts = url.split('/', 2) - - return { - hostname: (index === -1) ? url.substring(0) : url.substring(0, index), - port: 443, - path: (index === -1) ? '' : url.substring(index), - method: requestType, - timeout: 30000, - }; -} - -function makeHttpsCall(options, aCallback, aError){ - //console.log(JSON.stringify(options)); - const req = https.request(options, function(response){ - let data = ''; - - //console.log('statusCode:', response.statusCode); - //console.log('headers:', response.headers); - - // A chunk of data has been received. - response.on('data', (chunk) => { - data += chunk; - }); - - // The whole response has been received. Print out the result. - response.on('end', () => { - if(typeof aCallback != "undefined") - aCallback(response, data); - }); - }); - - req.on('timeout', function () { - console.log("Request timed out. " + (options.timeout / 1000) + " seconds expired"); - - // Source: https://github.com/nodejs/node/blob/master/test/parallel/test-http-client-timeout-option.js#L27 - req.destroy(); - }); - - req.on("error", (err) => { - if(typeof aError != "undefined") { - aError(err); - } else { - console.log("Error: " + err.message); - } - }); - - return req; -} - -module.exports = class HttpClient { - get(aUrl, aCallback, aError) { - let url = cleanUrl(aUrl); - - let options = createOptions('GET', url); - - const req = makeHttpsCall(options, aCallback, aError); - - req.end(); - } - - post(aUrl, body, aCallback, aError) { - let url = cleanUrl(aUrl); - - let options = createOptions('POST', url); - - let postBody = querystring.stringify(body); - - //Add extra options for POST request type - options.headers = { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': postBody.length - }; - - const req = makeHttpsCall(options, aCallback, aError); - - req.write(postBody); - req.end(); - } -} \ No newline at end of file diff --git a/SignallingWebServer/modules/logging.js b/SignallingWebServer/modules/logging.js deleted file mode 100644 index b2d85064..00000000 --- a/SignallingWebServer/modules/logging.js +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright Epic Games, Inc. All Rights Reserved. - -const fs = require('fs'); -const { Console } = require('console'); - -var loggers=[]; -var logFunctions=[]; -var logColorFunctions=[]; - -const AllAttributesOff = '\x1b[0m'; -const BoldOn = '\x1b[1m'; -const Black = '\x1b[30m'; -const Red = '\x1b[31m'; -const Green = '\x1b[32m'; -const Yellow = '\x1b[33m'; -const Blue = '\x1b[34m'; -const Magenta = '\x1b[35m'; -const Cyan = '\x1b[36m'; -const White = '\x1b[37m'; -const Orange = '\x1b[38;5;215m'; - -console.log = function(msg, ...args) { - logFunctions.forEach((logFunction) => { - logFunction(msg, ...args); - }); -} - -console.warn = function(msg, ...args) { - logColorFunctions.forEach((logColorFunction) => { - logColorFunction(Yellow, msg, ...args); - }); -} - -console.error = function(msg, ...args) { - logColorFunctions.forEach((logColorFunction) => { - logColorFunction(Red, msg, ...args); - }); -} - -console.logColor = function(color, msg, ...args) { - logColorFunctions.forEach((logColorFunction) => { - logColorFunction(color, msg, ...args); - }); -} - -/** - * Pad the start of the given number with zeros so it takes up the number of digits. - * e.g. zeroPad(5, 3) = '005' and zeroPad(23, 2) = '23'. - */ -function zeroPad(number, digits) { - let string = number.toString(); - while (string.length < digits) { - string = '0' + string; - } - return string; -} - -/** - * Create a string of the form 'YEAR.MONTH.DATE.HOURS.MINUTES.SECONDS'. - */ -function dateTimeToString() { - let date = new Date(); - return `${date.getFullYear()}.${zeroPad(date.getMonth(), 2)}.${zeroPad(date.getDate(), 2)}.${zeroPad(date.getHours(), 2)}.${zeroPad(date.getMinutes(), 2)}.${zeroPad(date.getSeconds(), 2)}`; -} - -/** - * Create a string of the form 'HOURS.MINUTES.SECONDS.MILLISECONDS'. - */ -function timeToString() { - let date = new Date(); - return `${zeroPad(date.getHours(), 2)}:${zeroPad(date.getMinutes(), 2)}:${zeroPad(date.getSeconds(), 2)}.${zeroPad(date.getMilliseconds(), 3)}`; -} - -function RegisterFileLogger(path) { - if(path == null) - path = './logs/'; - - if (!fs.existsSync(path)) - fs.mkdirSync(path); - - var output = fs.createWriteStream(`${path}${dateTimeToString()}.log`); - var fileLogger = new Console(output); - logFunctions.push(function(msg, ...args) { - fileLogger.log(`${timeToString()} ${msg}`, ...args); - }); - - logColorFunctions.push(function(color, msg, ...args) { - fileLogger.log(`${timeToString()} ${msg}`, ...args); - }); - loggers.push(fileLogger); -} - -function RegisterConsoleLogger() { - var consoleLogger = new Console(process.stdout, process.stderr) - logFunctions.push(function(msg, ...args) { - consoleLogger.log(`${timeToString()} ${msg}`, ...args); - }); - - logColorFunctions.push(function(color, msg, ...args) { - consoleLogger.log(`${BoldOn}${color}${timeToString()} ${msg}${AllAttributesOff}`, ...args); - }); - loggers.push(consoleLogger); -} - -module.exports = { - //Functions - RegisterFileLogger, - RegisterConsoleLogger, - - //Variables - AllAttributesOff, - BoldOn, - Black, - Red, - Green, - Yellow, - Blue, - Magenta, - Cyan, - White, - Orange -} \ No newline at end of file diff --git a/SignallingWebServer/package-lock.json b/SignallingWebServer/package-lock.json index f495c7ad..f563c018 100644 --- a/SignallingWebServer/package-lock.json +++ b/SignallingWebServer/package-lock.json @@ -1,1342 +1,1518 @@ { - "name": "@epicgames-ps/cirrus-webserver", - "version": "0.0.1", - "lockfileVersion": 2, + "name": "@epicgames-ps/wilbur", + "version": "1.0.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@epicgames-ps/cirrus-webserver", - "version": "0.0.1", + "name": "@epicgames-ps/wilbur", + "version": "1.0.0", + "license": "MIT", "dependencies": { - "bcryptjs": "^2.4.3", + "commander": "^12.0.0", "express": "^4.18.2", - "express-rate-limit": "^6.7.0", - "express-session": "^1.15.6", - "helmet": "^3.21.3", - "lusca": "^1.7.0", - "passport": "^0.6.0", - "passport-local": "^1.0.0", - "run-script-os": "^1.1.6", - "ws": "^7.1.2", - "y18n": "^5.0.5", - "yargs": "^15.3.0" - } - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "express-openapi": "^12.1.3", + "express-rate-limit": "^7.1.5", + "jsonc": "^2.0.0", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^4.7.1", + "ws": "^8.16.0" }, + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.11.13", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.56.0", + "eslint-plugin-tsdoc": "^0.2.17", + "nodemon": "^3.0.3", + "openapi-typescript": "^6.7.4", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.3.3" + } + }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=0.10.0" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "engines": { - "node": ">=8" + "node": ">=0.1.90" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">=12" } }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } }, - "node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/bowser": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", - "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/camelize": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { - "color-name": "~1.1.4" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=7.0.0" + "node": "*" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dependencies": { - "safe-buffer": "5.2.1" - }, + "node_modules/@eslint/js": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", + "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/content-security-policy-builder": { + "node_modules/@fastify/busboy": { "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "dev": true, "engines": { - "node": ">=4.0.0" + "node": ">=14" } }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">= 0.6" + "node": ">=10.10.0" } }, - "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", - "engines": { - "node": ">= 0.6" + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" - }, - "node_modules/dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, "dependencies": { - "ms": "2.0.0" + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, "engines": { - "node": ">= 0.8" + "node": ">=12" } }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==", + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, "engines": { - "node": ">=4.0.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, - "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "engines": { - "node": ">= 0.8" + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "engines": { - "node": ">= 0.6" + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" } }, - "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.1", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.5.0", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.2.0", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.1", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.7", - "proxy-addr": "~2.0.7", - "qs": "6.11.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.18.0", - "serve-static": "1.15.0", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">= 0.10.0" + "node": ">= 8" } }, - "node_modules/express-rate-limit": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", - "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { - "node": ">= 12.9.0" - }, - "peerDependencies": { - "express": "^4 || ^5" + "node": ">= 8" } }, - "node_modules/express-session": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", - "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">= 8" } }, - "node_modules/express-session/node_modules/cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "optional": true, "engines": { - "node": ">= 0.6" + "node": ">=14" } }, - "node_modules/feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==", - "engines": { - "node": ">=4.0.0" + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" } }, - "node_modules/finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, "dependencies": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" + "@types/node": "*" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" } }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "engines": { - "node": ">= 0.6" + "node_modules/@types/express-serve-static-core": { + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" } }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "engines": { - "node": ">= 0.6" + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", + "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", + "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" } }, - "node_modules/get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, + "node_modules/@types/ws": { + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "@types/node": "*" } }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, "dependencies": { - "function-bind": "^1.1.1" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "engines": { - "node": ">= 0.4" + "node": "^16.0.0 || >=18.0.0" }, "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/helmet": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", - "integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==", - "dependencies": { - "depd": "2.0.0", - "dont-sniff-mimetype": "1.1.0", - "feature-policy": "0.3.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.10.0", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/helmet-csp": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", - "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", - "dependencies": { - "bowser": "2.9.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==", - "engines": { - "node": ">=4.0.0" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" - }, - "node_modules/hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "peer": true, "dependencies": { - "depd": "2.0.0" + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=4.0.0" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", + "dev": true, "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { - "node": ">= 0.8" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "engines": { - "node": ">= 0.10" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/@typescript-eslint/types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", + "dev": true, "engines": { - "node": ">=8" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", + "dev": true, "dependencies": { - "p-locate": "^4.1.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": ">=8" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "node_modules/lusca": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/lusca/-/lusca-1.7.0.tgz", - "integrity": "sha512-msnrplCfY7zaqlZBDEloCIKld+RUeMZVeWzSPaGUKeRXFlruNSdKg2XxCyR+zj6BqzcXhXlRnvcvx6rAGgsvMA==", + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, "dependencies": { - "tsscmp": "^1.0.5" + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" }, "engines": { - "node": ">=0.8.x" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" } }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.21.0", + "eslint-visitor-keys": "^3.4.1" + }, "engines": { - "node": ">= 0.6" + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/merge-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", - "engines": { - "node": ">= 0.6" - } + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "bin": { - "mime": "cli.js" + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "engines": { "node": ">= 0.6" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 0.6" + "node": ">=0.4.0" } }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "engines": { - "node": ">= 0.6" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==", + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=0.4.0" } }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, "dependencies": { - "ee-first": "1.1.1" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "engines": { - "node": ">= 0.8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { - "p-try": "^2.0.0" + "ajv": "^8.0.0" }, - "engines": { - "node": ">=6" + "peerDependencies": { + "ajv": "^8.0.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "peerDependenciesMeta": { + "ajv": { + "optional": true + } } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", "dependencies": { - "p-limit": "^2.2.0" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">=8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "engines": { "node": ">=6" } }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "engines": { - "node": ">= 0.8" + "node": ">=8" } }, - "node_modules/passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", + "dev": true + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" + "color-convert": "^2.0.1" }, "engines": { - "node": ">= 0.4.0" + "node": ">=8" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { - "passport-strategy": "1.x.x" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 8" } }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=", - "engines": { - "node": ">= 0.4.0" - } + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, "engines": { - "node": ">= 0.10" + "node": ">=8" } }, - "node_modules/qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "dependencies": { - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=", - "engines": { - "node": ">= 0.8" - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, "engines": { - "node": ">= 0.6" + "node": ">=8" } }, - "node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "node_modules/body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", "dependencies": { "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" - } - }, - "node_modules/referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==", - "engines": { - "node": ">=4.0.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "engines": { - "node": ">=0.10.0" + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" } }, - "node_modules/require-main-filename": { + "node_modules/body-parser/node_modules/ms": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", - "bin": { - "run-os": "index.js", - "run-script-os": "index.js" + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "node_modules/send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "fill-range": "^7.0.1" }, "engines": { - "node": ">= 0.8.0" + "node": ">=8" } }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "dependencies": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" - }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "engines": { - "node": ">= 0.8.0" + "node": ">= 0.8" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, "engines": { - "node": ">= 0.8" + "node": ">=6" } }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" }, "engines": { - "node": ">=8" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, "engines": { - "node": ">=0.6" + "node": ">= 6" } }, - "node_modules/tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", - "engines": { - "node": ">=0.6.x" + "node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" } }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "color-name": "~1.1.4" }, "engines": { - "node": ">= 0.6" + "node": ">=7.0.0" } }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" } }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "engines": { - "node": ">= 0.8" + "node_modules/color/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" } }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", - "engines": { - "node": ">= 0.4.0" + "node_modules/color/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" } }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", + "node_modules/commander": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", "engines": { - "node": ">= 0.8" + "node": ">=18" } }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==", + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", "engines": { - "node": ">=4.0.0" + "node": ">= 0.6" } }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" + "ms": "2.1.2" }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=6" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/yargs/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - } - }, - "dependencies": { - "accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "requires": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" } }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" } }, - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + "node_modules/difunc": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/difunc/-/difunc-0.0.4.tgz", + "integrity": "sha512-zBiL4ALDmviHdoLC0g0G6wVme5bwAow9WfhcZLLopXCAWgg3AEf7RYTs2xugszIGulRHzEVDF/SHl9oyQU07Pw==", + "dependencies": { + "esprima": "^4.0.0" + } }, - "bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms=" + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "bowser": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.9.0.tgz", - "integrity": "sha512-2ld76tuLBNFekRgmJfT2+3j5MIrP6bFict8WAIT3beq+srz1gcKNAdNKMqHqauQt63NmAa88HfP1/Ypa9Er3HA==" + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, - "bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, - "call-bind": { + "node_modules/encodeurl": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dependencies": { + "is-arrayish": "^0.2.1" + } }, - "camelize": { + "node_modules/es-define-property": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", - "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" } }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" } }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" }, - "content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "requires": { - "safe-buffer": "5.2.1" + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "content-security-policy-builder": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-security-policy-builder/-/content-security-policy-builder-2.1.0.tgz", - "integrity": "sha512-/MtLWhJVvJNkA9dVLAp6fg9LxD2gfI6R2Fi1hPmfjYXSahJJzcfvoeDOxSyp4NvxMuwWv3WMssE9o31DoULHrQ==" - }, - "content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==" - }, - "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "node_modules/eslint": { + "version": "8.56.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", + "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.56.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } }, - "dasherize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", - "integrity": "sha1-bYCcnNDPe7iVLYD8hPoT1H3bEwg=" + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } }, - "destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==" + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "dont-sniff-mimetype": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", - "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } }, - "encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==" + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "etag": { + "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==" + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } }, - "express": { + "node_modules/express": { "version": "4.18.2", "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", - "requires": { + "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.1", @@ -1368,594 +1544,2590 @@ "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" } }, - "express-rate-limit": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.7.0.tgz", - "integrity": "sha512-vhwIdRoqcYB/72TK3tRZI+0ttS8Ytrk24GfmsxDXK9o9IhHNO5bXRiXQSExPQ4GbaE5tvIS7j1SGrxsuWs+sGA==", - "requires": {} + "node_modules/express-normalize-query-params-middleware": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/express-normalize-query-params-middleware/-/express-normalize-query-params-middleware-0.5.1.tgz", + "integrity": "sha512-KUBjEukYL9KJkrphVX3ZgMHgMTdgaSJe+FIOeWwJIJpCw8UZQPIylt0MYddSyUwbms4LQ8RC4wmavcLUP9uduA==" }, - "express-session": { - "version": "1.17.2", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz", - "integrity": "sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==", - "requires": { - "cookie": "0.4.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, + "node_modules/express-openapi": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/express-openapi/-/express-openapi-12.1.3.tgz", + "integrity": "sha512-F570dVC5ENSkLu1SpDFPRQ13Y3a/7Udh0rfHyn3O1QrE81fPmlhnAo1JRgoNtbMRJ6goHNymxU1TVSllgFZBlQ==", "dependencies": { - "cookie": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", - "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" - } + "express-normalize-query-params-middleware": "^0.5.0", + "openapi-framework": "^12.1.3", + "openapi-types": "^12.1.3" } }, - "feature-policy": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", - "integrity": "sha512-ZtijOTFN7TzCujt1fnNhfWPFPSHeZkesff9AXZj+UEjYBynWNUIYpC87Ve4wHzyexQsImicLu7WsC2LHq7/xrQ==" + "node_modules/express-rate-limit": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.1.5.tgz", + "integrity": "sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": "4 || 5 || ^5.0.0-beta.1" + } }, - "finalhandler": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", - "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", - "requires": { - "debug": "2.6.9", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-stream-rotator": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz", + "integrity": "sha512-u+dBid4PvZw17PmDeRcNOtCP9CCK/9lRN2w+r1xIS7yOL9JFrIBKTvrYsxT4P0pGtThYTn++QS5ChHaUov3+zQ==", + "dependencies": { + "moment": "^2.29.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "requires": { - "locate-path": "^5.0.0", + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "forwarded": { + "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==" + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } }, - "fresh": { + "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==" + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "node_modules/fs-routes": { + "name": "epic-fs-routes", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/epic-fs-routes/-/epic-fs-routes-1.0.0.tgz", + "integrity": "sha512-vWpJeL9K5PQFznGEdOnHXzq9Y4+fC6NVpJ+qtPPyhXeeXkTaq2trQipOj1zLOmWxT+4Koa16MNzOma3x7KQV/g==", + "dependencies": { + "typescript": "^5.3.3" + }, + "peerDependencies": { + "glob": ">=7.1.6" + } }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "get-intrinsic": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", - "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.3" + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } }, - "helmet": { - "version": "3.23.3", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.23.3.tgz", - "integrity": "sha512-U3MeYdzPJQhtvqAVBPntVgAvNSOJyagwZwyKsFdyRa8TV3pOKVFljalPOCxbw5Wwf2kncGhmP0qHjyazIdNdSA==", - "requires": { - "depd": "2.0.0", - "dont-sniff-mimetype": "1.1.0", - "feature-policy": "0.3.0", - "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.10.0", - "hide-powered-by": "1.1.0", - "hpkp": "2.0.0", - "hsts": "2.2.0", - "nocache": "2.1.0", - "referrer-policy": "1.2.0", - "x-xss-protection": "1.3.0" - } - }, - "helmet-crossdomain": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/helmet-crossdomain/-/helmet-crossdomain-0.4.0.tgz", - "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } }, - "helmet-csp": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.10.0.tgz", - "integrity": "sha512-Rz953ZNEFk8sT2XvewXkYN0Ho4GEZdjAZy4stjiEQV3eN7GDxg1QKmYggH7otDyIA7uGA6XnUMVSgeJwbR5X+w==", - "requires": { - "bowser": "2.9.0", - "camelize": "1.0.0", - "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0" + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "hide-powered-by": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hide-powered-by/-/hide-powered-by-1.1.0.tgz", - "integrity": "sha512-Io1zA2yOA1YJslkr+AJlWSf2yWFkKjvkcL9Ni1XSUqnGLr/qRQe2UI3Cn/J9MsJht7yEVCe0SscY1HgVMujbgg==" + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "hpkp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hpkp/-/hpkp-2.0.0.tgz", - "integrity": "sha1-EOFCJk52IVpdMMROxD3mTe5tFnI=" + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "hsts": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hsts/-/hsts-2.2.0.tgz", - "integrity": "sha512-ToaTnQ2TbJkochoVcdXYm4HOCliNozlviNsg+X2XQLQvZNI/kCHR9rZxVYpJB3UPcHz80PgxRyWQ7PdU1r+VBQ==", - "requires": { - "depd": "2.0.0" + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "http-errors": { + "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "requires": { + "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" } }, - "iconv-lite": { + "node_modules/iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { + "dependencies": { "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ipaddr.js": { + "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "requires": { - "p-locate": "^4.1.0" + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "lusca": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/lusca/-/lusca-1.7.0.tgz", - "integrity": "sha512-msnrplCfY7zaqlZBDEloCIKld+RUeMZVeWzSPaGUKeRXFlruNSdKg2XxCyR+zj6BqzcXhXlRnvcvx6rAGgsvMA==", - "requires": { - "tsscmp": "^1.0.5" - } + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-dir/-/is-dir-1.0.0.tgz", + "integrity": "sha512-vLwCNpTNkFC5k7SBRxPubhOCryeulkOsSkjbGyZ8eOzZmzMS+hSEO/Kn9ZOVhFNAlRZTFc4ZKql48hESuYUPIQ==" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, - "media-typer": { + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/jsonc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/jsonc/-/jsonc-2.0.0.tgz", + "integrity": "sha512-B281bLCT2TRMQa+AQUQY5AGcqSOXBOKaYGP4wDzoA/+QswUfN8sODektbPEs9Baq7LGKun5jQbNFpzwGuVYKhw==", + "dependencies": { + "fast-safe-stringify": "^2.0.6", + "graceful-fs": "^4.1.15", + "mkdirp": "^0.5.1", + "parse-json": "^4.0.0", + "strip-bom": "^4.0.0", + "strip-json-comments": "^3.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/logform": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.0.tgz", + "integrity": "sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ==", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==" + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } }, - "merge-descriptors": { + "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", - "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" }, - "methods": { + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } }, - "mime": { + "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } }, - "mime-db": { + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "requires": { + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "negotiator": { + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } }, - "nocache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/nocache/-/nocache-2.1.0.tgz", - "integrity": "sha512-0L9FvHG3nfnnmaEQPjT9xhfN4ISk0A8/2j4M37Np4mcDesJjHgEUfgPhdCyZuFI954tjokaIj/A3NdpFNdEh4Q==" + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/nodemon": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", + "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==", + "engines": { + "node": ">= 6" + } }, - "object-inspect": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", - "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==" + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "on-finished": { + "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "requires": { + "dependencies": { "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/openapi-default-setter": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-default-setter/-/openapi-default-setter-12.1.3.tgz", + "integrity": "sha512-wHKwvEuOWwke5WcQn8pyCTXT5WQ+rm9FpJmDeEVECEBWjEyB/MVLYfXi+UQeSHTTu2Tg4VDHHmzbjOqN6hYeLQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-framework": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-framework/-/openapi-framework-12.1.3.tgz", + "integrity": "sha512-p30PHWVXda9gGxm+t/1X2XvEcufW1YhzeDQwc5SsgDnBXt8gkuu1SwrioGJ66wxVYEzfSRTTf/FMLhI49ut8fQ==", + "dependencies": { + "difunc": "0.0.4", + "fs-routes": "^12.1.3", + "glob": "*", + "is-dir": "^1.0.0", + "js-yaml": "^3.10.0", + "openapi-default-setter": "^12.1.3", + "openapi-request-coercer": "^12.1.3", + "openapi-request-validator": "^12.1.3", + "openapi-response-validator": "^12.1.3", + "openapi-schema-validator": "^12.1.3", + "openapi-security-handler": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-framework/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/openapi-framework/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/openapi-jsonschema-parameters": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-jsonschema-parameters/-/openapi-jsonschema-parameters-12.1.3.tgz", + "integrity": "sha512-aHypKxWHwu2lVqfCIOCZeJA/2NTDiP63aPwuoIC+5ksLK5/IQZ3oKTz7GiaIegz5zFvpMDxDvLR2DMQQSkOAug==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-request-coercer": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-coercer/-/openapi-request-coercer-12.1.3.tgz", + "integrity": "sha512-CT2ZDhBmAZpHhAzHhEN+/J5oMK3Ds99ayLLdXh2Aw1DCcn72EM8VuIGVwG5fSjvkMsgtn7FgltFosHqeM6PRFQ==", + "dependencies": { + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-request-validator/-/openapi-request-validator-12.1.3.tgz", + "integrity": "sha512-HW1sG00A9Hp2oS5g8CBvtaKvRAc4h5E4ksmuC5EJgmQ+eAUacL7g+WaYCrC7IfoQaZrjxDfeivNZUye/4D8pwA==", + "dependencies": { + "ajv": "^8.3.0", + "ajv-formats": "^2.1.0", + "content-type": "^1.0.4", + "openapi-jsonschema-parameters": "^12.1.3", + "openapi-types": "^12.1.3", + "ts-log": "^2.1.4" + } + }, + "node_modules/openapi-request-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-request-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-response-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-response-validator/-/openapi-response-validator-12.1.3.tgz", + "integrity": "sha512-beZNb6r1SXAg1835S30h9XwjE596BYzXQFAEZlYAoO2imfxAu5S7TvNFws5k/MMKMCOFTzBXSjapqEvAzlblrQ==", + "dependencies": { + "ajv": "^8.4.0", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-response-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-response-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-schema-validator": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-schema-validator/-/openapi-schema-validator-12.1.3.tgz", + "integrity": "sha512-xTHOmxU/VQGUgo7Cm0jhwbklOKobXby+/237EG967+3TQEYJztMgX9Q5UE2taZKwyKPUq0j11dngpGjUuxz1hQ==", + "dependencies": { + "ajv": "^8.1.0", + "ajv-formats": "^2.0.2", + "lodash.merge": "^4.6.1", + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-schema-validator/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/openapi-schema-validator/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/openapi-security-handler": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-security-handler/-/openapi-security-handler-12.1.3.tgz", + "integrity": "sha512-25UTAflxqqpjCLrN6rRhINeM1L+MCDixMltiAqtBa9Zz/i7UkWwYwdzqgZY3Cx3vRZElFD09brYxo5VleeP3HQ==", + "dependencies": { + "openapi-types": "^12.1.3" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, + "node_modules/openapi-typescript": { + "version": "6.7.4", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-6.7.4.tgz", + "integrity": "sha512-EZyeW9Wy7UDCKv0iYmKrq2pVZtquXiD/YHiUClAKqiMi42nodx/EQH11K6fLqjt1IZlJmVokrAsExsBMM2RROQ==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "fast-glob": "^3.3.2", + "js-yaml": "^4.1.0", + "supports-color": "^9.4.0", + "undici": "^5.28.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" + } + }, + "node_modules/openapi-typescript/node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-stable-stringify": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz", + "integrity": "sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g==", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/send/node_modules/debug/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", + "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", + "dependencies": { + "define-data-property": "^1.1.2", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, + "node_modules/shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", + "dev": true, + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, + "node_modules/side-channel": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz", + "integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==", + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "engines": { + "node": "*" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" } }, - "on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + "node_modules/ts-log": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/ts-log/-/ts-log-2.2.5.tgz", + "integrity": "sha512-PGcnJoTBnVGy6yYNFxWVNkdcAuAMstvutN9MgDJIV6L0oG8fB+ZNNy1T+wJzah8RPGor1mZuPQkVfXNDpy9eHA==" }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "requires": { - "p-try": "^2.0.0" + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "requires": { - "p-limit": "^2.2.0" + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "passport": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz", - "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==", - "requires": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", - "requires": { - "passport-strategy": "1.x.x" + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" } }, - "passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=" - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + "node_modules/typedoc": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.8.tgz", + "integrity": "sha512-mh8oLW66nwmeB9uTa0Bdcjfis+48bAjSH3uqdzSuSawfduROQLlXw//WSNZLYDdhmMVB7YcYZicq6e8T0d271A==", + "dev": true, + "dependencies": { + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 16" + }, + "peerDependencies": { + "typescript": "4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x" + } }, - "path-to-regexp": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", - "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + "node_modules/typedoc-plugin-markdown": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/typedoc-plugin-markdown/-/typedoc-plugin-markdown-3.17.1.tgz", + "integrity": "sha512-QzdU3fj0Kzw2XSdoL15ExLASt2WPqD7FbLeaqwT70+XjKyTshBnUlQA5nNREO1C2P8Uen0CDjsBLMsCQ+zd0lw==", + "dev": true, + "dependencies": { + "handlebars": "^4.7.7" + }, + "peerDependencies": { + "typedoc": ">=0.24.0" + } }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } }, - "proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "requires": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" } }, - "qs": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", - "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", - "requires": { - "side-channel": "^1.0.4" + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/undici": { + "version": "5.28.3", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", + "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", + "dev": true, + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" } }, - "random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs=" + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true }, - "range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" } }, - "referrer-policy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", - "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true }, - "run-script-os": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", - "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==" + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } }, - "safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } }, - "send": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", - "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", - "requires": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "node_modules/winston": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.11.0.tgz", + "integrity": "sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g==", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-daily-rotate-file": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/winston-daily-rotate-file/-/winston-daily-rotate-file-4.7.1.tgz", + "integrity": "sha512-7LGPiYGBPNyGHLn9z33i96zx/bd71pjBn9tqQzO3I4Tayv94WPmBNwKC7CO1wPHdP9uvu+Md/1nr6VSH9h0iaA==", "dependencies": { - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - } + "file-stream-rotator": "^0.6.1", + "object-hash": "^2.0.1", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "winston": "^3" } }, - "serve-static": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", - "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", - "requires": { - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.18.0" + "node_modules/winston-transport": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.7.0.tgz", + "integrity": "sha512-ajBj65K5I7denzer2IYW6+2bNIVqLGDHqDw3Ow8Ohh+vdW+rv4MZ6eiDvHoKhfJFZ2auyN8byXieDDJ96ViONg==", + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true }, - "setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "requires": { + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" - }, - "tsscmp": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", - "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "requires": { - "random-bytes": "~1.0.0" + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true }, - "utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + "node_modules/ws": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" } }, - "ws": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.5.tgz", - "integrity": "sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==", - "requires": {} - }, - "x-xss-protection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", - "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==" - } + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" } }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/SignallingWebServer/package.json b/SignallingWebServer/package.json index dbd48f02..dbee4d4f 100644 --- a/SignallingWebServer/package.json +++ b/SignallingWebServer/package.json @@ -1,37 +1,49 @@ { - "name": "@epicgames-ps/cirrus-webserver", - "version": "0.0.2", - "description": "The reference signalling and web server for Pixel Streaming: Cirrus", - "private": true, + "name": "@epicgames-ps/wilbur", + "version": "1.0.0", + "description": "A basic signalling server application for Unreal Engine's Pixel Streaming applications.", + "main": "index.js", "scripts": { - "store_password": "run-script-os", - "store_password:default": "./platform_scripts/bash/node/bin/node ./modules/authentication/db/store_password.js --usersFile=./modules/authentication/db/users.json", - "store_password:windows": "platform_scripts\\cmd\\node\\node.exe ./modules/authentication/db/store_password.js --usersFile=./modules/authentication/db/users.json", - "start-local": "run-script-os --", - "start-local:default": "./platform_scripts/bash/Start_Local.sh", - "start-local:windows": ".\\platform_scripts\\cmd\\Start_Local.bat", - "start-signalling-server": "run-script-os --", - "start-signalling-server:default": "./platform_scripts/bash/Start_SignallingServer.sh", - "start-signalling-server:windows": ".\\platform_scripts\\cmd\\Start_SignallingServer.bat", - "start-with-turn-signalling-server": "run-script-os --", - "start-with-turn-signalling-server:default": "./platform_scripts/bash/Start_WithTurn_SignallingServer.sh", - "start-wiht-turn-signalling-server:windows": ".\\platform_scripts\\cmd\\Start_WithTurn_SignallingServer.bat", - "start": "run-script-os", - "start:default": "if [ `id -u` -eq 0 ] || [ ! -z $NO_SUDO ]\nthen\n export process=\"./platform_scripts/bash/node/bin/node cirrus.js\"\nelse\n export process=\"sudo ./platform_scripts/bash/node/bin/node cirrus.js\"\nfi\n$process ", - "start:windows": "platform_scripts\\cmd\\node\\node.exe cirrus.js" + "build": "tsc", + "build-dev": "npm link ../Signalling && npm run build", + "build-all": "rimraf ./build && cd ../Signalling && npm run build-all && cd ../SignallingWebServer && npm run build-dev", + "start": "node ./build/index.js", + "lint": "eslint . --ext .js,.jsx,.ts,.tsx", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Epic Games", + "license": "MIT", + "devDependencies": { + "@types/express": "^4.17.21", + "@types/node": "^20.11.13", + "@types/ws": "^8.5.10", + "@typescript-eslint/eslint-plugin": "^6.21.0", + "eslint": "^8.56.0", + "eslint-plugin-tsdoc": "^0.2.17", + "nodemon": "^3.0.3", + "openapi-typescript": "^6.7.4", + "ts-node": "^10.9.2", + "typedoc": "^0.25.8", + "typedoc-plugin-markdown": "^3.17.1", + "typescript": "^5.3.3" }, "dependencies": { - "bcryptjs": "^2.4.3", + "commander": "^12.0.0", "express": "^4.18.2", - "express-rate-limit": "^6.7.0", - "express-session": "^1.15.6", - "helmet": "^3.21.3", - "lusca": "^1.7.0", - "passport": "^0.6.0", - "passport-local": "^1.0.0", - "run-script-os": "^1.1.6", - "ws": "^7.1.2", - "y18n": "^5.0.5", - "yargs": "^15.3.0" + "express-openapi": "^12.1.3", + "express-rate-limit": "^7.1.5", + "jsonc": "^2.0.0", + "winston": "^3.11.0", + "winston-daily-rotate-file": "^4.7.1", + "ws": "^8.16.0" + }, + "overrides": { + "express-openapi": { + "fs-routes": "npm:epic-fs-routes@1.0.0" + } + }, + "repository": { + "type": "git", + "url": "https://github.com/EpicGames/PixelStreamingInfrastructure.git" } } diff --git a/SignallingWebServer/platform_scripts/bash/README.txt b/SignallingWebServer/platform_scripts/bash/README.txt index b6b0a4e7..107c1715 100644 --- a/SignallingWebServer/platform_scripts/bash/README.txt +++ b/SignallingWebServer/platform_scripts/bash/README.txt @@ -1,13 +1,15 @@ How to use files in this directory: -- Run ./setup.sh to automatically install all required dependencies for your operating system. Note that setup.sh is called from every script designed to run; -- Run a local instance of the Cirrus server by using the ./run_local.sh script; -- Use the following scripts to run locally or on your cloud instance (note that TURN server is not expected to work locally due to the nature of its application): - - Start_SignallingServer.sh - start only the Signalling (STUN) server; - - Start_TURNServer.sh - start only the TURN server; - - Start_WithTURN_SignallingServer.sh - start a TURN server and the Cirrus server together. - +- setup.sh : Ensures the correct node is installed and builds the frontend if it isn't already +- start.sh : Starts the signalling server with basic settings +- common.sh : Contains a bunch of helper functions for the contained scripts. Shouldn't be run directly. + +The following are provided as handy shortcuts but mostly leverage start.bat functionality +- start_turn.sh : Starts the turn server only with basic settings +- start_with_stun.sh : Starts the signalling server with basic STUN settings +- start_with_turn.sh : Starts the TURN server and then the signalling server with STUN and TURN parameters + Tips: - Please note that scripts intended to run need to be executable: $ chmod +x *.sh will do that job. -- The local/cloud Start_*.sh shell scripts can be invoked with the --help command line option to see how those can be configured. The following options can be supplied: --publicip, --turn, --stun. Please read the --help. +- You can provide --help to start.sh to get a list of customizable arguments. diff --git a/SignallingWebServer/platform_scripts/bash/Start_SignallingServer.sh b/SignallingWebServer/platform_scripts/bash/Start_SignallingServer.sh deleted file mode 100755 index 9f949e71..00000000 --- a/SignallingWebServer/platform_scripts/bash/Start_SignallingServer.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" - -pushd "${BASH_LOCATION}" > /dev/null - -source common_utils.sh - -set_start_default_values "n" "y" # Set STUN server defaults only -use_args "$@" -call_setup_sh -print_parameters - -peerconnectionoptions='{\"iceServers\":[{\"urls\":[\"stun:${stunserver}\"]}]}' - -process="${BASH_LOCATION}/node/lib/node_modules/npm/bin/npm-cli.js run start:default --" -arguments="" - -if [ ! -z $IS_DEBUG ]; then - arguments+=" --inspect" -fi - -arguments+=" --peerConnectionOptions=\"${peerconnectionoptions}\" --PublicIp=${publicip}" -# Add arguments passed to script to arguments for executable -arguments+=" ${cirruscmd}" - -pushd ../.. -echo "Running: $process $arguments" -PATH="${BASH_LOCATION}/node/bin:$PATH" -start_process $process $arguments -popd - -popd \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/bash/Start_TURNServer.sh b/SignallingWebServer/platform_scripts/bash/Start_TURNServer.sh deleted file mode 100755 index a1f1c774..00000000 --- a/SignallingWebServer/platform_scripts/bash/Start_TURNServer.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" - -pushd "${BASH_LOCATION}" > /dev/null - -source turn_user_pwd.sh -source common_utils.sh - -set_start_default_values "y" "n" # TURN server defaults only -use_args "$@" -call_setup_sh -print_parameters - -localip=$(hostname -I | awk '{print $1}') -echo "Private IP: $localip" - -turnport="${turnserver##*:}" -if [ -z "${turnport}" ]; then - turnport=3478 -fi -echo "TURN port: ${turnport}" -echo "" - - -# Hmm, plain text -realm="PixelStreaming" -process="" -if [ "$(uname)" == "Darwin" ]; then - process="${BASH_LOCATION}/coturn/bin/turnserver" -else - process="turnserver" -fi -arguments="-c turnserver.conf --allowed-peer-ip=$localip -p ${turnport} -r $realm -X $publicip -E $localip -L $localip --no-cli --no-tls --no-dtls --pidfile /var/run/turnserver.pid -f -a -v -u ${turnusername}:${turnpassword}" - -# Add arguments passed to script to arguments for executable -arguments+=" ${cirruscmd}" - -pushd ../.. >/dev/null -echo "Running: $process $arguments" -# pause -start_process $process $arguments & -popd >/dev/null # ../.. - -popd >/dev/null # BASH_SOURCE diff --git a/SignallingWebServer/platform_scripts/bash/Start_WithTURN_SignallingServer.sh b/SignallingWebServer/platform_scripts/bash/Start_WithTURN_SignallingServer.sh deleted file mode 100755 index 1174d508..00000000 --- a/SignallingWebServer/platform_scripts/bash/Start_WithTURN_SignallingServer.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" - -pushd "${BASH_LOCATION}" > /dev/null - -source common_utils.sh - -set_start_default_values "y" "y" # Set both TURN and STUN server defaults -use_args "$@" -call_setup_sh -print_parameters - -bash Start_TURNServer.sh --turn "${turnserver}" - -peerconnectionoptions='{\"iceServers\":[{\"urls\":[\"stun:$stunserver\",\"turn:$turnserver\"],\"username\":\"PixelStreamingUser\",\"credential\":\"AnotherTURNintheroad\"}]}' - -process="${BASH_LOCATION}/node/lib/node_modules/npm/bin/npm-cli.js run start:default --" -arguments="" - -if [ ! -z $IS_DEBUG ]; then - arguments+=" --inspect" -fi - -arguments+=" --peerConnectionOptions=\"$peerconnectionoptions\" --PublicIp=$publicip" -# Add arguments passed to script to arguments for executable -arguments+=" ${cirruscmd}" - -pushd ../.. -echo "Running: $process $arguments" -PATH="${BASH_LOCATION}/node/bin:$PATH" -start_process $process $arguments -popd - -popd diff --git a/SignallingWebServer/platform_scripts/bash/common.sh b/SignallingWebServer/platform_scripts/bash/common.sh new file mode 100644 index 00000000..1cf08912 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/common.sh @@ -0,0 +1,364 @@ +#!/bin/bash + +NODE_VERSION=v18.17.0 +NPM="${SCRIPT_DIR}/node/bin/npm" + +# Prints the arguments and their descriptions to the console +function print_usage() { + echo " + Usage: + ${0} [--help] [--publicip ] [--turn ] [--stun ] [server options...] + Where: + --help Print this message and stop this script. + --debug Run all scripts with --inspect + --nosudo Run all scripts without \`sudo\` command useful for when run in containers. + --verbose Enable additional logging + --publicip Define public ip address (using default port) for turn server, syntax: --publicip ; it is used for + Default value: Retrieved from 'curl https://api.ipify.org' or if unsuccessful then set to 127.0.0.1. It is the IP address of the server and the default IP address of the TURN server + --turn TURN server to be used, syntax: --turn 127.0.0.1:19303 + Default value: as above, IP address downloaded from https://api.ipify.org; in case if download failure it is set to 127.0.0.1 + --turn-user Sets the turn username when using a turn server. + --turn-pass Sets the turn password when using a turn server. + --start-turn Will launch the turnserver process. + --stun STUN server to be used, syntax: --stun stun.l.google.com:19302 + Default value as above + --build Force a rebuild of the typescript frontend even if it already exists + --frontend-dir Sets the output path for the fontend build + + Other options: stored and passed to the server. All parameters printed once the script values are set. + Command line options might be omitted to run with defaults and it is a good practice to omit specific ones when just starting the TURN or the STUN server alone, not the whole set of scripts. + " + exit 1 +} + +function parse_args() { + IS_DEBUG=0 + NO_SUDO=0 + VERBOSE=0 + FORCE_BUILD=0 + DEFAULT_STUN=0 + DEFAULT_TURN=0 + while(($#)) ; do + case "$1" in + --debug ) IS_DEBUG=1; shift;; + --nosudo ) NO_SUDO=1; shift;; + --verbose ) VERBOSE=1; shift;; + --build ) FORCE_BUILD=1; shift;; + --default-stun ) DEFAULT_STUN=1; shift;; + --default-turn ) DEFAULT_TURN=1; shift;; + --stun ) STUN_SERVER="$2"; shift 2;; + --turn ) TURN_SERVER="$2"; shift 2;; + --turn-user ) TURN_USER="$2"; shift 2;; + --turn-pass ) TURN_PASS="$2"; shift 2;; + --start-turn ) START_TURN=1; shift;; + --publicip ) PUBLIC_IP="$2"; shift 2;; + --frontend-dir ) FRONTEND_DIR="$(realpath "$2")"; shift 2;; + --help ) print_usage;; + * ) SERVER_ARGS+=" $1"; shift;; + esac + done + if [[ ! -z "${SERVER_ARGS}" ]]; then + echo "Parameters being passed to server: ${SERVER_ARGS}" + fi +} + +function check_version() { #current_version #min_version + #check if same string + if [ -z "$2" ] || [ "$1" = "$2" ]; then + return 0 + fi + + local i current minimum + + IFS="." read -r -a current <<< $1 + IFS="." read -r -a minimum <<< $2 + + # fill empty fields in current with zeros + for ((i=${#current[@]}; i<${#minimum[@]}; i++)) + do + current[i]=0 + done + + for ((i=0; i<${#current[@]}; i++)) + do + if [[ -z ${minimum[i]} ]]; then + # fill empty fields in minimum with zeros + minimum[i]=0 + fi + + if ((10#${current[i]} > 10#${minimum[i]})); then + return 1 + fi + + if ((10#${current[i]} < 10#${minimum[i]})); then + return 2 + fi + done + + # if got this far string is the same once we added missing 0 + return 0 +} + +function check_and_install() { #dep_name #get_version_string #version_min #install_command + local is_installed=0 + + echo "Checking for required $1 install" + + local current=$(echo $2 | sed -E 's/[^0-9.]//g') + local minimum=$(echo $3 | sed -E 's/[^0-9.]//g') + + if [ $# -ne 4 ]; then + echo "check_and_install expects 4 args (dep_name get_version_string version_min install_command) got $#" + return -1 + fi + + if [ ! -z $current ]; then + echo "Current version: $current checking >= $minimum" + check_version "$current" "$minimum" + if [ "$?" -lt 2 ]; then + echo "$1 is installed." + return 0 + else + echo "Required install of $1 not found installing" + fi + fi + + if [ $is_installed -ne 1 ]; then + echo "$1 installation not found installing..." + + start_process $4 + + if [ $? -ge 1 ]; then + echo "Installation of $1 failed try running `export VERBOSE=1` then run this script again for more details" + fi + fi +} + +function setup_node() { + pushd "${SCRIPT_DIR}" > /dev/null + + # Azure specific fix to allow installing NodeJS from NodeSource + if test -f "/etc/apt/sources.list.d/azure-cli.list"; then + sudo touch /etc/apt/sources.list.d/nodesource.list + sudo touch /usr/share/keyrings/nodesource.gpg + sudo chmod 644 /etc/apt/sources.list.d/nodesource.list + sudo chmod 644 /usr/share/keyrings/nodesource.gpg + sudo chmod 644 /etc/apt/sources.list.d/azure-cli.list + fi + + popd > /dev/null + + # navigate to project root + pushd ${SCRIPT_DIR}/../.. > /dev/null + + node_version="" + if [[ -f "${SCRIPT_DIR}/node/bin/node" ]]; then + node_version=$("${SCRIPT_DIR}/node/bin/node" --version) + fi + + node_url="" + if [ "$(uname)" == "Darwin" ]; then + arch=$(uname -m) + if [[ $arch == x86_64* ]]; then + node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-darwin-x64.tar.gz" + elif [[ $arch == arm* ]]; then + node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-darwin-arm64.tar.gz" + else + echo 'Incompatible architecture. Only x86_64 and ARM64 are supported' + exit -1 + fi + else + node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-x64.tar.gz" + fi + check_and_install "node" "$node_version" "$NODE_VERSION" "curl $node_url --output node.tar.xz + && tar -xf node.tar.xz + && rm node.tar.xz + && mv node-v*-*-* \"${SCRIPT_DIR}/node\"" + + PATH="${SCRIPT_DIR}/node/bin:$PATH" + "${SCRIPT_DIR}/node/lib/node_modules/npm/bin/npm-cli.js" install + + popd > /dev/null +} + +function setup_frontend() { + set -e + + if [[ -z "$FRONTEND_DIR" ]]; then + FRONTEND_DIR="${SCRIPT_DIR}/../../www" + fi + + # tell webpack where to output the built frontend + export WEBPACK_OUTPUT_PATH=${FRONTEND_DIR} + + # navigate to root + pushd ${SCRIPT_DIR}/../../.. > /dev/null + OLDPATH=$PATH + export PATH="${SCRIPT_DIR}/node/bin:$PATH" + + # If player.html doesn't exist, or --build passed as arg, rebuild the frontend + echo Testing ${WEBPACK_OUTPUT_PATH}/player.html + if [ ! -f ${WEBPACK_OUTPUT_PATH}/player.html ] || [ "$FORCE_BUILD" == "1" ] ; then + echo "Building Typescript Frontend." + # Using our bundled NodeJS, build the web frontend files + pushd ${SCRIPT_DIR}/../../../Common > /dev/null + ${SCRIPT_DIR}/node/bin/npm install + ${SCRIPT_DIR}/node/bin/npm run build + popd > /dev/null + pushd ${SCRIPT_DIR}/../../../Frontend/library > /dev/null + ${SCRIPT_DIR}/node/bin/npm install + ${SCRIPT_DIR}/node/bin/npm link ../../Common + ${SCRIPT_DIR}/node/bin/npm run build + popd > /dev/null + pushd ${SCRIPT_DIR}/../../../Frontend/ui-library > /dev/null + ${SCRIPT_DIR}/node/bin/npm install + ${SCRIPT_DIR}/node/bin/npm link ../library + ${SCRIPT_DIR}/node/bin/npm run build + popd > /dev/null + pushd ${SCRIPT_DIR}/../../../Frontend/implementations/typescript > /dev/null + ${SCRIPT_DIR}/node/bin/npm install + ${SCRIPT_DIR}/node/bin/npm link ../../library ../../ui-library + ${SCRIPT_DIR}/node/bin/npm run build + popd > /dev/null + else + echo 'Skipping building Frontend because files already exist. Please run with "--build" to force a rebuild' + fi + + export PATH=$OLDPATH + popd > /dev/null # root + set +e +} + +function setup_coturn() { + if [ "$(uname)" == "Darwin" ]; then + if [ -d "${SCRIPT_DIR}/coturn" ]; then + echo 'CoTURN directory found...skipping install.' + else + echo 'CoTURN directory not found...beginning CoTURN download for Mac.' + coturn_url="" + if [[ $arch == x86_64* ]]; then + coturn_url="https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.6.2-coturn-mac-x86_64/turnserver.zip" + elif [[ $arch == arm* ]]; then + coturn_url="https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.6.2-coturn-mac-arm64/turnserver.zip" + fi + curl -L -o ./turnserver.zip "$coturn_url" + mkdir "${SCRIPT_DIR}/coturn" + tar -xf turnserver.zip -C "${SCRIPT_DIR}/coturn" + rm turnserver.zip + fi + else + #command #dep_name #get_version_string #version_min #install command + coturn_version=$(if command -v turnserver &> /dev/null; then echo 1; else echo 0; fi) + if [ $coturn_version -eq 0 ]; then + if ! command -v apt-get &> /dev/null; then + echo "Setup for the scripts is designed for use with distros that use the apt-get package manager" \ + "if you are seeing this message you will have to update \"${SCRIPT_DIR}/setup.sh\" with\n" \ + "a package manger and the equivalent packages for your distribution. Please follow the\n" \ + "instructions found at https://pkgs.org/search/?q=coturn to install Coturn for your specific distribution" + exit 1 + else + if [ `id -u` -eq 0 ]; then + check_and_install "coturn" "$coturn_version" "1" "apt-get install -y coturn" + else + check_and_install "coturn" "$coturn_version" "1" "sudo apt-get install -y coturn" + fi + fi + fi + fi +} + +function setup() { + echo "Checking Pixel Streaming Server dependencies." + setup_node + setup_frontend + setup_coturn +} + +# Sets PUBLIC_IP to the external ip +function set_public_ip() { + PUBLIC_IP=$(curl -s https://api.ipify.org) + if [[ -z "$PUBLIC_IP" ]]; then + PUBLIC_IP="127.0.0.1" + fi +} + +# Sets up the turn variables and optionally launches the turn server +# Assumes PUBLIC_IP = public ip of this host +function setup_turn_stun() { + if [[ "$DEFAULT_TURN" == "1" ]]; then + TURN_SERVER="${PUBLIC_IP}:19303" + TURN_USER="PixelStreamingUser" + TURN_PASS="AnotherTURNintheroad" + fi + + if [[ "$DEFAULT_STUN" == "1" ]]; then + STUN_SERVER="stun.l.google.com:19302" + fi + + LOCAL_IP="$(hostname -I | awk '{print $1}')" + TURN_PORT="${TURN_SERVER##*:}" + if [[ -z "${TURN_PORT}" ]]; then TURN_PORT=3478; fi + + TURN_PROCESS="" + if [[ "$(uname)" == "Darwin" ]]; then + TURN_PROCESS="${SCRIPT_DIR}/coturn/bin/turnserver" + else + TURN_PROCESS="sudo turnserver" + fi + + TURN_REALM="PixelStreaming" + TURN_ARGUMENTS="-c turnserver.conf --allowed-peer-ip=$LOCAL_IP -p ${TURN_PORT} -r ${TURN_REALM} -X ${PUBLIC_IP} -E ${LOCAL_IP} --no-cli --no-tls --no-dtls --pidfile /var/run/turnserver.pid -f -a -v -u ${TURN_USER}:${TURN_PASS}" + + if [[ "$1" == "bg" ]]; then + TURN_ARGUMENTS+=" &" + fi + + if [[ "${START_TURN}" == "1" && ! -z "${TURN_SERVER}" && ! -z "${TURN_USER}" && ! -z "${TURN_PASS}" ]]; then + start_process ${TURN_PROCESS} ${TURN_ARGUMENTS} $@ + fi +} + +# Prints out the configuration for the server +function print_config() { + echo "" + echo "Running with config:" + echo "Public IP address : ${PUBLIC_IP}" + if [[ -n "${STUN_SERVER}" ]]; then echo "STUN server : ${STUN_SERVER}" ; fi + if [[ -n "${TURN_SERVER}" ]]; then echo "TURN server : ${TURN_SERVER}" ; fi + echo "Server command line arguments: ${SERVER_ARGS}" + echo "" +} + +# launches a process confitionally removing sudo +function start_process() { + if [[ "${NO_SUDO}" == 1 ]]; then + eval $(echo "$@" | sed 's/sudo//g') + else + eval $@ + fi +} + +# Assumes the following are set +# SCRIPT_DIR = The path to the root of the PixelStreamingInfrastructure repo. +# NPM = The npm command path +# SERVER_ARGS The arguments to be passed to the server +function start_wilbur() { + pushd ${SCRIPT_DIR}/../../.. > /dev/null + + pushd Signalling > /dev/null + ${NPM} link ../Common + ${NPM} run build + popd > /dev/null + + pushd SignallingWebServer > /dev/null + ${NPM} link ../Signalling + ${NPM} run build + + echo "Starting wilbur signalling server use ctrl-c to exit" + echo "----------------------------------" + + start_process "sudo PATH=$PATH ${NPM} start -- ${SERVER_ARGS}" + + popd > /dev/null +} + diff --git a/SignallingWebServer/platform_scripts/bash/common_utils.sh b/SignallingWebServer/platform_scripts/bash/common_utils.sh deleted file mode 100755 index b2966b18..00000000 --- a/SignallingWebServer/platform_scripts/bash/common_utils.sh +++ /dev/null @@ -1,99 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -function log_msg() { #message - if [ ! -z $VERBOSE ]; then - echo $1 - fi -} - -function print_usage() { - echo " - Usage: - ${0} [--help] [--publicip ] [--turn ] [--stun ] [cirrus options...] - Where: - --help will print this message and stop this script. - --debug will run all scripts with --inspect - --nosudo will run all scripts without \`sudo\` command useful for when run in containers. - --verbose will enable additional logging - --package-manager specify an alternative package manager to apt-get - --publicip is used to define public ip address (using default port) for turn server, syntax: --publicip ; it is used for - default value: Retrieved from 'curl https://api.ipify.org' or if unsuccessful then set to 127.0.0.1. It is the IP address of the Cirrus server and the default IP address of the TURN server - --turn defines what TURN server to be used, syntax: --turn 127.0.0.1:19303 - default value: as above, IP address downloaded from https://api.ipify.org; in case if download failure it is set to 127.0.0.1 - --stun defined what STUN server to be used, syntax: --stun stun.l.google.com:19302 - default value as above - --build will force a rebuild of the typescript frontend even if it already exists - Other options: stored and passed to the Cirrus server. All parameters printed once the script values are set. - Command line options might be omitted to run with defaults and it is a good practice to omit specific ones when just starting the TURN or the STUN server alone, not the whole set of scripts. - " - exit 1 -} - -function print_parameters() { - echo "" - echo "${0} is running with the following parameters:" - echo "--------------------------------------" - if [[ -n "${stunserver}" ]]; then echo "STUN server : ${stunserver}" ; fi - if [[ -n "${turnserver}" ]]; then echo "TURN server : ${turnserver}" ; fi - echo "Public IP address : ${publicip}" - echo "Cirrus server command line arguments: ${cirruscmd}" - echo "" -} - -function set_start_default_values() { - # publicip and cirruscmd are always needed - publicip=$(curl -s https://api.ipify.org) - if [[ -z $publicip ]]; then - publicip="127.0.0.1" - fi - cirruscmd="" - - if [ "$1" = "y" ]; then - turnserver="${publicip}:19303" - fi - - if [ "$2" = "y" ]; then - stunserver="stun.l.google.com:19302" - fi -} - -function use_args() { - while(($#)) ; do - case "$1" in - --debug ) IS_DEBUG=1; shift;; - --nosudo ) NO_SUDO=1; shift;; - --verbose ) VERBOSE=1; shift;; - --build ) FORCE_BUILD=1; shift;; - --stun ) stunserver="$2"; shift 2;; - --turn ) turnserver="$2"; shift 2;; - --publicip ) publicip="$2"; turnserver="${publicip}:19303"; shift 2;; - --help ) print_usage;; - * ) echo "Unknown command, adding to cirrus command line: $1"; cirruscmd+=" $1"; shift;; - esac - done -} - -function call_setup_sh() { - bash "setup.sh" $* -} - -function start_process() { - export NO_SUDO=$NO_SUDO - if [ ! -z $NO_SUDO ]; then - log_msg "running with sudo removed" - eval $(echo "$@" | sed 's/sudo//g') - else - eval $@ - fi -} - -function get_version() { - local version=$1 - - if command -v $version; then - version=$($@) - fi - - echo $version | sed -E 's/[^0-9.]//g' -} diff --git a/SignallingWebServer/platform_scripts/bash/docker-build-cirrus.sh b/SignallingWebServer/platform_scripts/bash/docker-build-cirrus.sh deleted file mode 100755 index 00c3f37a..00000000 --- a/SignallingWebServer/platform_scripts/bash/docker-build-cirrus.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" - -pushd "${BASH_LOCATION}" > /dev/null - -# When run from SignallingWebServer/platform_scripts/bash, this uses the SignallingWebServer directory -# as the build context so the Cirrus files can be successfully copied into the container image -docker build --network=host -t 'cirrus-webserver:latest' -f "${BASH_LOCATION}/../../Dockerfile" "${BASH_LOCATION}/../../.." diff --git a/SignallingWebServer/platform_scripts/bash/docker-build-wilbur.sh b/SignallingWebServer/platform_scripts/bash/docker-build-wilbur.sh new file mode 100755 index 00000000..9bb23e25 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/docker-build-wilbur.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. +# +cd -P -- "$(dirname -- "$0")" +pushd "../../.." > /dev/null + +# When run from SignallingWebServer/platform_scripts/bash, this uses the SignallingWebServer +# directory as the build context so the Cirrus files can be successfully copied into the +# container image +docker build --network=host -t "wilbur:latest" -f SignallingWebServer/Dockerfile . + diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-local.sh b/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-local.sh deleted file mode 100755 index 959ca430..00000000 --- a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-local.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -# Start docker container by name using host networking -docker run --name cirrus_latest --network host --rm cirrus-webserver - -# Interactive start example -#docker run --name cirrus_latest --network host --rm -it --entrypoint /bin/bash cirrus-webserver diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-with-turn.sh b/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-with-turn.sh deleted file mode 100755 index e1098039..00000000 --- a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus-with-turn.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -# Suppress printing of directory stack -pushd () { - command pushd "$@" > /dev/null -} -popd () { - command popd "$@" > /dev/null -} - -# Stop both stun and turn -pushd "$(dirname ${BASH_SOURCE[0]})" -./docker-start-cirrus.sh --with-turn & -popd diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus.sh b/SignallingWebServer/platform_scripts/bash/docker-start-cirrus.sh deleted file mode 100755 index 9eb0f4e3..00000000 --- a/SignallingWebServer/platform_scripts/bash/docker-start-cirrus.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -source turn_user_pwd.sh - -USETURN="false" - -for arg do - shift - [ "${arg}" = "--with-turn" ] && USETURN="true" && continue - set -- "$@" "${arg}" -done - -# Get stun server data for passing to the container -source common_utils.sh -if [ "${USETURN}" = "true" ]; then - set_start_default_values "y" "y" # Both TURN and STUN server defaults -else - set_start_default_values "n" "y" # Only STUN server defaults -fi -use_args "$@" - -# Start docker container by name using host networking -if [ "${USETURN}" = "true" ]; then - peerConnectionOptions="{\""iceServers\"":[{\""urls\"":[\""stun:"${stunserver}"\"",\""turn":"${turnserver}\""],\""username\"":\""${turnusername}\"",\""credential\"":\""${turnpassword}\""}]}" -else - peerConnectionOptions="{\""iceServers\"":[{\""urls\"":[\""stun:"${stunserver}"\""]}]}" -fi - -docker run --name cirrus_latest --network host --rm --entrypoint /usr/local/bin/node cirrus-webserver /SignallingWebServer/cirrus.js --peerConnectionOptions="${peerConnectionOptions}" --publicIp="${publicip}" - diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-turn.sh b/SignallingWebServer/platform_scripts/bash/docker-start-turn.sh index 11473410..3659184f 100755 --- a/SignallingWebServer/platform_scripts/bash/docker-start-turn.sh +++ b/SignallingWebServer/platform_scripts/bash/docker-start-turn.sh @@ -1,46 +1,27 @@ #!/bin/bash # Copyright Epic Games, Inc. All Rights Reserved. +SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" # Get stun server data for passing to the container -source common_utils.sh -set_start_default_values "n" "y" # Only STUN server defaults -use_args "$@" +. ${SCRIPT_DIR}/common.sh -localip=$(hostname -I | awk '{print $1}') -echo "Private IP: $localip" +parse_args $@ +set_public_ip +START_TURN=0 +setup_turn_stun -turnport="${turnserver##*:}" -if [ -z "${turnport}" ]; then - turnport=3478 +# If we don't have a turnserver config in the signalling dir then just create one so that docker +# will not create an empty folder when we try to mount the volume +if [[ ! -f "${SCRIPT_DIR}/../../turnserver.conf" ]]; then + touch "${SCRIPT_DIR}/../../turnserver.conf" fi -echo "TURN port: ${turnport}" -echo "" - -turnusername="PixelStreamingUser" -turnpassword="AnotherTURNintheroad" -realm="PixelStreaming" -process="turnserver" -arguments="-c /turnconfig/turnserver.conf --allowed-peer-ip=$localip -p ${turnport} -r $realm -X $publicip -E $localip -L $localip --no-cli --no-tls --no-dtls --pidfile /var/run/turnserver.pid -f -a -v -u ${turnusername}:${turnpassword}" - -# Add arguments passed to script to arguments for executable -arguments+=" ${cirruscmd}" - -# Start docker container by name using host networking -echo "Running: ${process} ${arguments}" - -# Get the docker image -docker pull coturn/coturn - -# Copy the turn setup config to a path we can use as a volume -mkdir -p turnconfig -cp ../../turnserver.conf turnconfig/ # Start the TURN server #docker run --name coturn_latest --network host -it --entrypoint /bin/bash coturn/coturn #docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr --entrypoint "sudo mkdir -p /var/run" coturn/coturn "" #docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr --entrypoint "/bin/ls" coturn/coturn "/var/" -docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr -v $PWD/turnconfig:/turnconfig --entrypoint "${process}" coturn/coturn "${arguments}" +docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr -v "$PWD/turnserver.conf:${SCRIPT_DIR}/../../turnserver.conf" --entrypoint "turnserver" coturn/coturn "${TURN_ARGUMENTS}" #docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr --entrypoint "/bin/bash" coturn/coturn "ls -latr /var/run/" #docker run --name coturn_latest --network host --rm -a stdin -a stdout -a stderr --entrypoint "sudo chown ubuntu:ubuntu /var/run/turnserver.pid | sudo chmod +x /var/run/turnserver.pid | ${process}" coturn/coturn "${arguments}" diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-local.sh b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-local.sh new file mode 100755 index 00000000..a534d106 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-local.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. + +docker run --name wilbur_latest --network host --rm wilbur + diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-with-turn.sh b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-with-turn.sh new file mode 100755 index 00000000..d0c5b85e --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur-with-turn.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. + +docker run --name wilbur_latest --network host --rm wilbur --default-turn + diff --git a/SignallingWebServer/platform_scripts/bash/docker-start-wilbur.sh b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur.sh new file mode 100755 index 00000000..5dd503dc --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/docker-start-wilbur.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. +SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" + +# Get stun server data for passing to the container +. ${SCRIPT_DIR}/common.sh + +parse_args $@ +set_public_ip +if [[ -z "${STUN_SERVER}" ]]; then + DEFAULT_STUN=1 +fi +START_TURN=0 +setup_turn_stun + +# Start docker container by name using host networking +if [[ ! -z "${TURN_SERVER}" ]]; then + PEER_OPTIONS="{\""iceServers\"":[{\""urls\"":[\""stun:"${STUN_SERVER}"\"",\""turn":"${TURN_SERVER}\""],\""username\"":\""${TURN_USER}\"",\""credential\"":\""${TURN_PASS}\""}]}" +else + PEER_OPTIONS="{\""iceServers\"":[{\""urls\"":[\""stun:"${STUN_SERVER}"\""]}]}" +fi + +docker run --name wilbur_latest --network host --rm wilbur --peerConnectionOptions="${PEER_OPTIONS}" --publicIp="${PUBLIC_IP}" + diff --git a/SignallingWebServer/platform_scripts/bash/docker-stop-all.sh b/SignallingWebServer/platform_scripts/bash/docker-stop-all.sh index 15a3e869..ce7b93f0 100755 --- a/SignallingWebServer/platform_scripts/bash/docker-stop-all.sh +++ b/SignallingWebServer/platform_scripts/bash/docker-stop-all.sh @@ -1,16 +1,7 @@ #!/bin/bash # Copyright Epic Games, Inc. All Rights Reserved. -# Suppress printing of directory stack -pushd () { - command pushd "$@" > /dev/null -} -popd () { - command popd "$@" > /dev/null -} - -# Stop both stun and turn pushd "$(dirname ${BASH_SOURCE[0]})" -./docker-stop-cirrus.sh +./docker-stop-wilbur.sh ./docker-stop-turn.sh popd diff --git a/SignallingWebServer/platform_scripts/bash/docker-stop-cirrus.sh b/SignallingWebServer/platform_scripts/bash/docker-stop-cirrus.sh deleted file mode 100755 index 4f93ac12..00000000 --- a/SignallingWebServer/platform_scripts/bash/docker-stop-cirrus.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -# Stop the docker container -PSID=$(docker ps -a -q --filter="name=cirrus_latest") -if [ -z "$PSID" ]; then - echo "Docker stun is not running, no stopping will be done" - exit 1; -fi -echo "Stopping stun server ..." -docker stop cirrus_latest - diff --git a/SignallingWebServer/platform_scripts/bash/docker-stop-wilbur.sh b/SignallingWebServer/platform_scripts/bash/docker-stop-wilbur.sh new file mode 100755 index 00000000..947ec7d2 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/docker-stop-wilbur.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. + +# Stop the docker container +PSID=$(docker ps -a -q --filter="name=wilbur_latest") +if [ -z "$PSID" ]; then + echo "Docker wilbur is not running, no stopping will be done" + exit 1; +fi +echo "Stopping wilbur server ..." +docker stop wilbur_latest + diff --git a/SignallingWebServer/platform_scripts/bash/run_local.sh b/SignallingWebServer/platform_scripts/bash/run_local.sh deleted file mode 100755 index f60869e2..00000000 --- a/SignallingWebServer/platform_scripts/bash/run_local.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" - -pushd "${BASH_LOCATION}" > /dev/null - -source common_utils.sh - -set_start_default_values "n" "n" # No server specific defaults -use_args "$*" -call_setup_sh $* -print_parameters - -process="${BASH_LOCATION}/node/lib/node_modules/npm/bin/npm-cli.js run start:default --" -arguments="" - -if [ ! -z $IS_DEBUG ]; then - arguments+=" --inspect" -fi - -arguments+=" --publicIp=${publicip}" -arguments+=" ${cirruscmd}" - -pushd ../.. > /dev/null - -echo "" -echo "Starting Cirrus server use ctrl-c to exit" -echo "-----------------------------------------" -echo "" - -PATH="${BASH_LOCATION}/node/bin:$PATH" -start_process $process $arguments - -popd > /dev/null # ../.. - -popd > /dev/null # BASH_SOURCE - diff --git a/SignallingWebServer/platform_scripts/bash/setup.sh b/SignallingWebServer/platform_scripts/bash/setup.sh index 07518af5..6ad83944 100755 --- a/SignallingWebServer/platform_scripts/bash/setup.sh +++ b/SignallingWebServer/platform_scripts/bash/setup.sh @@ -1,204 +1,10 @@ #!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. -BASH_LOCATION="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" -NODE_VERSION=v18.17.0 - -pushd "${BASH_LOCATION}" > /dev/null - -source common_utils.sh - -use_args $* -# Azure specific fix to allow installing NodeJS from NodeSource -if test -f "/etc/apt/sources.list.d/azure-cli.list"; then - sudo touch /etc/apt/sources.list.d/nodesource.list - sudo touch /usr/share/keyrings/nodesource.gpg - sudo chmod 644 /etc/apt/sources.list.d/nodesource.list - sudo chmod 644 /usr/share/keyrings/nodesource.gpg - sudo chmod 644 /etc/apt/sources.list.d/azure-cli.list -fi - -function check_version() { #current_version #min_version - #check if same string - if [ -z "$2" ] || [ "$1" = "$2" ]; then - return 0 - fi - - local i current minimum - - IFS="." read -r -a current <<< $1 - IFS="." read -r -a minimum <<< $2 - - # fill empty fields in current with zeros - for ((i=${#current[@]}; i<${#minimum[@]}; i++)) - do - current[i]=0 - done - - for ((i=0; i<${#current[@]}; i++)) - do - if [[ -z ${minimum[i]} ]]; then - # fill empty fields in minimum with zeros - minimum[i]=0 - fi - - if ((10#${current[i]} > 10#${minimum[i]})); then - return 1 - fi - - if ((10#${current[i]} < 10#${minimum[i]})); then - return 2 - fi - done - - # if got this far string is the same once we added missing 0 - return 0 -} - -function check_and_install() { #dep_name #get_version_string #version_min #install_command - local is_installed=0 - - log_msg "Checking for required $1 install" - - local current=$(echo $2 | sed -E 's/[^0-9.]//g') - local minimum=$(echo $3 | sed -E 's/[^0-9.]//g') - if [ $# -ne 4 ]; then - log_msg "check_and_install expects 4 args (dep_name get_version_string version_min install_command) got $#" - return -1 - fi - - if [ ! -z $current ]; then - log_msg "Current version: $current checking >= $minimum" - check_version "$current" "$minimum" - if [ "$?" -lt 2 ]; then - log_msg "$1 is installed." - return 0 - else - log_msg "Required install of $1 not found installing" - fi - fi - - if [ $is_installed -ne 1 ]; then - echo "$1 installation not found installing..." - - start_process $4 - - if [ $? -ge 1 ]; then - echo "Installation of $1 failed try running `export VERBOSE=1` then run this script again for more details" - fi - fi -} - -function setup_frontend() { - set -e - # navigate to root - pushd ${BASH_LOCATION}/../../.. > /dev/null - export PATH="../../SignallingWebServer/platform_scripts/bash/node/bin:$PATH" - # If player.html doesn't exist, or --build passed as arg, rebuild the frontend - if [ ! -f SignallingWebServer/Public/player.html ] || [ ! -z "$FORCE_BUILD" ] ; then - echo "Building Typescript Frontend." - # Using our bundled NodeJS, build the web frontend files - pushd ${BASH_LOCATION}/../../../Common > /dev/null - ../SignallingWebServer/platform_scripts/bash/node/bin/npm install - ../SignallingWebServer/platform_scripts/bash/node/bin/npm run build - popd - pushd ${BASH_LOCATION}/../../../Frontend/library > /dev/null - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm install - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm link ../../Common - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm run build-dev - popd - pushd ${BASH_LOCATION}/../../../Frontend/ui-library > /dev/null - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm install - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm link ../library - ../../SignallingWebServer/platform_scripts/bash/node/bin/npm run build-dev - popd - - pushd ${BASH_LOCATION}/../../../Frontend/implementations/typescript > /dev/null - ../../../SignallingWebServer/platform_scripts/bash/node/bin/npm install - ../../../SignallingWebServer/platform_scripts/bash/node/bin/npm link ../../library ../../ui-library - ../../../SignallingWebServer/platform_scripts/bash/node/bin/npm run build-dev - popd - else - echo 'Skipping building Frontend because files already exist. Please run with "--build" to force a rebuild' - fi - - popd > /dev/null # root - set +e -} - - -echo "Checking Pixel Streaming Server dependencies." - -# navigate to SignallingWebServer root -pushd ${BASH_LOCATION}/../.. > /dev/null - -node_version="" -if [[ -f "${BASH_LOCATION}/node/bin/node" ]]; then - node_version=$("${BASH_LOCATION}/node/bin/node" --version) -fi - -node_url="" -if [ "$(uname)" == "Darwin" ]; then - arch=$(uname -m) - if [[ $arch == x86_64* ]]; then - node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-darwin-x64.tar.gz" - elif [[ $arch == arm* ]]; then - node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-darwin-arm64.tar.gz" - else - echo 'Incompatible architecture. Only x86_64 and ARM64 are supported' - exit -1 - fi -else - node_url="https://nodejs.org/dist/$NODE_VERSION/node-$NODE_VERSION-linux-x64.tar.gz" -fi -check_and_install "node" "$node_version" "$NODE_VERSION" "curl $node_url --output node.tar.xz - && tar -xf node.tar.xz - && rm node.tar.xz - && mv node-v*-*-* \"${BASH_LOCATION}/node\"" - -PATH="${BASH_LOCATION}/node/bin:$PATH" -"${BASH_LOCATION}/node/lib/node_modules/npm/bin/npm-cli.js" install - -popd > /dev/null # SignallingWebServer +# Copyright Epic Games, Inc. All Rights Reserved. +SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" -# Trigger Frontend Build if needed or requested -# This has to be done after check_and_install "node" -setup_frontend +. ${SCRIPT_DIR}/common.sh -popd > /dev/null # BASH_SOURCE +parse_args $@ +setup $@ -if [ "$(uname)" == "Darwin" ]; then - if [ -d "${BASH_LOCATION}/coturn" ]; then - echo 'CoTURN directory found...skipping install.' - else - echo 'CoTURN directory not found...beginning CoTURN download for Mac.' - coturn_url="" - if [[ $arch == x86_64* ]]; then - coturn_url="https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.6.2-coturn-mac-x86_64/turnserver.zip" - elif [[ $arch == arm* ]]; then - coturn_url="https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.6.2-coturn-mac-arm64/turnserver.zip" - fi - curl -L -o ./turnserver.zip "$coturn_url" - mkdir "${BASH_LOCATION}/coturn" - tar -xf turnserver.zip -C "${BASH_LOCATION}/coturn" - rm turnserver.zip - fi -else - #command #dep_name #get_version_string #version_min #install command - coturn_version=$(if command -v turnserver &> /dev/null; then echo 1; else echo 0; fi) - if [ $coturn_version -eq 0 ]; then - if ! command -v apt-get &> /dev/null; then - echo "Setup for the scripts is designed for use with distros that use the apt-get package manager" \ - "if you are seeing this message you will have to update \"${BASH_LOCATION}/setup.sh\" with\n" \ - "a package manger and the equivalent packages for your distribution. Please follow the\n" \ - "instructions found at https://pkgs.org/search/?q=coturn to install Coturn for your specific distribution" - exit 1 - else - if [ `id -u` -eq 0 ]; then - check_and_install "coturn" "$coturn_version" "1" "apt-get install -y coturn" - else - check_and_install "coturn" "$coturn_version" "1" "sudo apt-get install -y coturn" - fi - fi - fi -fi diff --git a/SignallingWebServer/platform_scripts/bash/start.sh b/SignallingWebServer/platform_scripts/bash/start.sh new file mode 100755 index 00000000..823814e3 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/start.sh @@ -0,0 +1,29 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. +SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" + +. ${SCRIPT_DIR}/common.sh + +parse_args $@ +setup $@ +set_public_ip +setup_turn_stun "bg" + +SERVER_ARGS+=" --serve --https_redirect --public_ip=${PUBLIC_IP}" +if [[ ! -z "$STUN_SERVER" && ! -z "$TURN_SERVER" ]]; then + PEER_OPTIONS='{\"iceServers\":[{\"urls\":[\"stun:'${STUN_SERVER}'\",\"turn:'${TURN_SERVER}'\"],\"username\":\"'${TURN_USER}'\",\"credential\":\"'${TURN_PASS}'\"}]}' +elif [[ ! -z "$STUN_SERVER" ]]; then + PEER_OPTIONS='{\"iceServers\":[{\"urls\":[\"stun:'${STUN_SERVER}'\"]}]}' +elif [[ ! -z "$TURN_SERVER" ]]; then + PEER_OPTIONS='{\"iceServers\":[{\"urls\":[\"turn:'${TURN_SERVER}'\"],\"username\":\"'${TURN_USER}'\",\"credentials\":\"'${TURN_PASS}'\"}]}' +fi +if [[ ! -z "$PEER_OPTIONS" ]]; then + SERVER_ARGS+=" --peer_options=\"${PEER_OPTIONS}\"" +fi +if [[ ! -z "$FRONTEND_DIR" ]]; then + SERVER_ARGS+=" --http_root=\"$FRONTEND_DIR\"" +fi + +print_config +start_wilbur + diff --git a/SignallingWebServer/platform_scripts/bash/start_turn.sh b/SignallingWebServer/platform_scripts/bash/start_turn.sh new file mode 100755 index 00000000..4ec5e354 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/start_turn.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# Copyright Epic Games, Inc. All Rights Reserved. + +SCRIPT_DIR="$(cd -P -- "$(dirname -- "$0")" && pwd -P)" + +. ${SCRIPT_DIR}/common.sh +parse_args $@ +setup $@ +set_public_ip +DEFAULT_TURN=1 +START_TURN=1 +setup_turn_stun + diff --git a/SignallingWebServer/platform_scripts/bash/start_with_stun.sh b/SignallingWebServer/platform_scripts/bash/start_with_stun.sh new file mode 100755 index 00000000..06c3e19d --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/start_with_stun.sh @@ -0,0 +1,2 @@ +#!/bin/bash +. $(dirname "$0")/start.sh --default-stun $* diff --git a/SignallingWebServer/platform_scripts/bash/start_with_turn.sh b/SignallingWebServer/platform_scripts/bash/start_with_turn.sh new file mode 100755 index 00000000..d0949cf1 --- /dev/null +++ b/SignallingWebServer/platform_scripts/bash/start_with_turn.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +. $(dirname "$0")/start.sh --default-stun --default-turn --start-turn %* + diff --git a/SignallingWebServer/platform_scripts/bash/turn_user_pwd.sh b/SignallingWebServer/platform_scripts/bash/turn_user_pwd.sh deleted file mode 100755 index 77687f2e..00000000 --- a/SignallingWebServer/platform_scripts/bash/turn_user_pwd.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# Copyright Epic Games, Inc. All Rights Reserved. - -# Plain text TURN setup -turnusername="PixelStreamingUser" -turnpassword="AnotherTURNintheroad" - diff --git a/SignallingWebServer/platform_scripts/cmd/README.txt b/SignallingWebServer/platform_scripts/cmd/README.txt index a114c30e..360779a1 100644 --- a/SignallingWebServer/platform_scripts/cmd/README.txt +++ b/SignallingWebServer/platform_scripts/cmd/README.txt @@ -1,14 +1,14 @@ How to use files in this directory: -- Files with .ps1 extension can be run with PowerShell[.exe] in Windows. Powershell needs to be started as Administrator to run setup.ps1 so it can run installation / installation check steps; -- Run .\setup.bat to automatically install all required dependencies for your operating system. Note that .\setup.bat is called from every script designed to run; -- Run a local instance of the Cirrus server by using the .\run_local.ps1 script; -- Use the following scripts to run locally or on your cloud instance (note that TURN server is not expected to work locally due to the nature of its application): - - Start_SignallingServer.ps1 - start only the Signalling (STUN) server; - - Start_TURNServer.ps1 - start only the TURN server; - - Start_WithTURN_SignallingServer.ps1 - start a TURN server and the Cirrus server together; +- setup.bat : Ensures the correct node is installed and builds the frontend if it isn't already +- start.bat : Starts the signalling server with basic settings +- common.bat : Contains a bunch of helper functions for the contained scripts. Shouldn't be run directly. + +The following are provided as handy shortcuts but mostly leverage start.bat functionality +- start_turn.bat : Starts the turn server only with basic settings +- start_with_stun.bat : Starts the signalling server with basic STUN settings +- start_with_turn.bat : Starts the TURN server and then the signalling server with STUN and TURN parameters Tips: -- The Start_Common.ps1 file contains shared functions for other Start_*.ps1 scripts and it is not supposed to run alone. -- The local/cloud Start_*.ps1 powershell scripts can be invoked with the --help command line option to see how those can be configured. The following options can be supplied: --publicip, --turn, --stun. Please read the --help. +- You can provide --help to start.bat to get a list of customizable arguments. diff --git a/SignallingWebServer/platform_scripts/cmd/Start_Common.ps1 b/SignallingWebServer/platform_scripts/cmd/Start_Common.ps1 deleted file mode 100644 index de48ec98..00000000 --- a/SignallingWebServer/platform_scripts/cmd/Start_Common.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright Epic Games, Inc. All Rights Reserved. - -# Parse $args into a string -$params = $args[0] -if ( $args.Count -gt 1 ) { - $params = $args[1..$($args.Count - 1)] - # Do setup as a common task, it is smart and will not reinstall if not required. - Start-Process -FilePath "$PSScriptRoot\setup.bat" -Wait -NoNewWindow -ArgumentList $params -} -else { - Start-Process -FilePath "$PSScriptRoot\setup.bat" -Wait -NoNewWindow -} -echo $params - -$global:ScriptName = $MyInvocation.MyCommand.Name -$global:PublicIP = $null -$global:StunServer = $null -$global:TurnServer = $null -$global:CirrusCmd = $null -$global:BuildFrontend = $null - -function print_usage { - echo " - Usage (in MS Windows Power Shell): - $global:ScriptName [--help] [--publicip ] [--turn ] [--stun ] [cirrus options...] - Where: - --help will print this message and stop this script. - --publicip is used to define public ip address (using default port) for turn server, syntax: --publicip ; it is used for - default value: Retrieved from 'curl https://api.ipify.org' or if unsuccessful then set to 127.0.0.1. It is the IP address of the Cirrus server and the default IP address of the TURN server - --turn defines what TURN server to be used, syntax: --turn 127.0.0.1:19303 - default value: as above, IP address downloaded from https://api.ipify.org; in case if download failure it is set to 127.0.0.1 - --stun defined what STUN server to be used, syntax: --stun stun.l.google.com:19302 - default value as above - Other options: stored and passed to the Cirrus server. All parameters printed once the script values are set. - Command line options might be omitted to run with defaults and it is a good practice to omit specific ones when just starting the TURN or the STUN server alone, not the whole set of scripts. - " - exit 1 -} - -function print_parameters { - echo "" - echo "$scriptname is running with the following parameters:" - echo "--------------------------------------" - if ($global:StunServer -ne $null) { echo "STUN server : $global:StunServer" } - if ($global:TurnServer -ne $null) { echo "TURN server : $global:TurnServer" } - echo "Public IP address : $global:PublicIP" - echo "Cirrus server command line arguments: $global:CirrusCmd" - echo "" -} - -function set_start_default_values($SetTurnServerVar, $SetStunServerVar) { - # publicip and cirruscmd are always needed - $global:PublicIP = Invoke-WebRequest -Uri "https://api.ipify.org" -UseBasicParsing - if ($global:PublicIP -eq $null -Or $global:PublicIP.length -eq 0) { - $global:PublicIP = "127.0.0.1" - } else { - $global:PublicIP = ($global:PublicIP).Content - } - $global:cirruscmd = "" - - if ($SetTurnServerVar -eq "y") { - $global:TurnServer = $global:PublicIP + ":19303" - } - if ($SetStunServerVar -eq "y") { - $global:StunServer = "stun.l.google.com:19302" - } -} - -function use_args($arg) { - $CmdArgs = $arg -split (" ") - while($CmdArgs.count -gt 0) { - $Cmd, $CmdArgs = $CmdArgs - if ($Cmd -eq "--stun") { - $global:StunServer, $CmdArgs = $CmdArgs - } elseif ($Cmd -eq "--turn") { - $global:TurnServer, $CmdArgs = $CmdArgs - } elseif ($Cmd -eq "--publicip") { - $global:PublicIP, $CmdArgs = $CmdArgs - $global:TurnServer = $global:publicip + ":19303" - } elseif ($Cmd -eq "--build") { - $global:BuildFrontend, $CmdArgs = $CmdArgs - } elseif ($Cmd -eq "--help") { - print_usage - } else { - echo "Unknown command, adding to cirrus command line: $Cmd" - $global:CirrusCmd += " $Cmd" - } - } -} diff --git a/SignallingWebServer/platform_scripts/cmd/Start_SignallingServer.ps1 b/SignallingWebServer/platform_scripts/cmd/Start_SignallingServer.ps1 deleted file mode 100644 index 83c94a8d..00000000 --- a/SignallingWebServer/platform_scripts/cmd/Start_SignallingServer.ps1 +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright Epic Games, Inc. All Rights Reserved. - -. "$PSScriptRoot\Start_Common.ps1" $args - -set_start_default_values "n" "y" # Set both TURN and STUN server defaults -use_args($args) -print_parameters - -$peerConnectionOptions = "{ \""iceServers\"": [{\""urls\"": [\""stun:" + $global:StunServer + "\""]}] }" - -$ProcessExe = "platform_scripts\cmd\node\node.exe" -$Arguments = @("cirrus", "--peerConnectionOptions=""$peerConnectionOptions""", "--PublicIp=$global:PublicIp") -# Add arguments passed to script to Arguments for executable -$Arguments += $global:CirrusCmd - -Push-Location $PSScriptRoot\..\..\ -Write-Output "Running: $ProcessExe $Arguments" -Start-Process -FilePath $ProcessExe -ArgumentList "$Arguments" -Wait -NoNewWindow -Pop-Location diff --git a/SignallingWebServer/platform_scripts/cmd/Start_TURNServer.ps1 b/SignallingWebServer/platform_scripts/cmd/Start_TURNServer.ps1 deleted file mode 100644 index aa2dfa55..00000000 --- a/SignallingWebServer/platform_scripts/cmd/Start_TURNServer.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright Epic Games, Inc. All Rights Reserved. - -. "$PSScriptRoot\Start_Common.ps1" $args - -set_start_default_values "y" "n" # Set both TURN and STUN server defaults -use_args($args) -print_parameters -#$LocalIp = Invoke-WebRequest -Uri "http://169.254.169.254/latest/meta-data/local-ipv4" -$LocalIP = (Test-Connection -ComputerName (hostname) -Count 1 | Select IPV4Address).IPV4Address.IPAddressToString - -Write-Output "Private IP: $LocalIP" - -$TurnPort="19303" -$Pos = $global:TurnServer.LastIndexOf(":") -if ($Pos -ne -1) { - $TurnPort = $global:TurnServer.Substring($Pos+ 1) -} -echo "TURN port: ${turnport}" -echo "" - -Push-Location $PSScriptRoot - -$TurnUsername = "PixelStreamingUser" -$TurnPassword = "AnotherTURNintheroad" -$Realm = "PixelStreaming" -$ProcessExe = ".\turnserver.exe" -$Arguments = "-c ..\..\..\turnserver.conf --allowed-peer-ip=$LocalIP -p $TurnPort -r $Realm -X $PublicIP -E $LocalIP -L $LocalIP --no-cli --no-tls --no-dtls --pidfile `"C:\coturn.pid`" -f -a -v -u $TurnUsername`:$TurnPassword" - -# Add arguments passed to script to Arguments for executable -$Arguments += $args - -Push-Location $PSScriptRoot\coturn\ -Write-Output "Running: $ProcessExe $Arguments" -# pause -Start-Process -FilePath $ProcessExe -ArgumentList $Arguments -NoNewWindow -Pop-Location - -Pop-Location \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/Start_WithTURN_SignallingServer.ps1 b/SignallingWebServer/platform_scripts/cmd/Start_WithTURN_SignallingServer.ps1 deleted file mode 100644 index 395926a9..00000000 --- a/SignallingWebServer/platform_scripts/cmd/Start_WithTURN_SignallingServer.ps1 +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright Epic Games, Inc. All Rights Reserved. - -. "$PSScriptRoot\Start_Common.ps1" $args - -set_start_default_values "y" "y" # Set both TURN and STUN server defaults -use_args($args) -print_parameters - -Push-Location $PSScriptRoot - -Start-Process -FilePath "PowerShell" -ArgumentList ".\Start_TURNServer.ps1" -WorkingDirectory "$PSScriptRoot" - -$peerConnectionOptions = "{ \""iceServers\"": [{\""urls\"": [\""stun:" + $global:StunServer + "\"",\""turn:" + $global:TurnServer + "\""], \""username\"": \""PixelStreamingUser\"", \""credential\"": \""AnotherTURNintheroad\""}] }" - -$ProcessExe = "platform_scripts\cmd\node\node.exe" -$Arguments = @("cirrus", "--peerConnectionOptions=""$peerConnectionOptions""", "--PublicIp=$global:PublicIp") -# Add arguments passed to script to Arguments for executable -$Arguments += $args - -Push-Location $PSScriptRoot\..\..\ -Write-Output "Running: $ProcessExe $Arguments" -Start-Process -FilePath $ProcessExe -ArgumentList $Arguments -Wait -NoNewWindow -Pop-Location - -Pop-Location \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/build.bat b/SignallingWebServer/platform_scripts/cmd/build.bat deleted file mode 100644 index 12b695f6..00000000 --- a/SignallingWebServer/platform_scripts/cmd/build.bat +++ /dev/null @@ -1,39 +0,0 @@ -@Rem Copyright Epic Games, Inc. All Rights Reserved. - -@echo off - -@Rem Set script directory as working directory. -pushd "%~dp0" - -title Building Cirrus.exe - -@Rem Run setup to ensure we have node and cirrus installed. -call setup.bat %* - -@Rem Look for a `nexe` directory next to this script -if exist nexe\ ( - echo nexe directory found...skipping install. -) else ( - echo nexe directory not found...beginning nexe install. - - @Rem Make `nexe directory` - mkdir nexe - - @Rem npm init and install nexe - pushd nexe - call ..\node\npm init -y - call ..\node\npm install nexe --save - popd -) - -@Rem Move to cirrus directory. -pushd ..\.. - -@Rem Build cirrus.exe using `nexe` using node 14.5.0 (as that is one of the latest prebuilts node versions in the nexe repo) -call platform_scripts\cmd\node\npx nexe cirrus.js --target "x64-14.15.3" -r "Public/*" -r "scripts/*" -r "images/*" -r "config.json" - -@Rem Pop cirrus directory. -popd ..\.. - -@Rem Pop working directory -popd \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/common.bat b/SignallingWebServer/platform_scripts/cmd/common.bat new file mode 100644 index 00000000..7195014e --- /dev/null +++ b/SignallingWebServer/platform_scripts/cmd/common.bat @@ -0,0 +1,286 @@ +@rem Copyright Epic Games, Inc. All Rights Reserved. +@echo off + +echo Helper library script. Not to be called directly. +goto :eof + +:Init +set SCRIPT_DIR=%~dp0 +set NODE_VERSION=v18.17.0 +set NPM="%SCRIPT_DIR%/node/npm" +set CONTINUE=1 +GOTO :eof + +:Usage +echo. +echo Usage: +echo %0 [--help] [--publicip ^] [--turn ^] [--stun ^] [server options...] +echo Where: +echo --help Print this message and stop this script. +echo --publicip ^ Define public ip address (using default port) for turn server +echo Default value: Retrieved from 'curl https://api.ipify.org' or if unsuccessful then set to 127.0.0.1. +echo --turn ^ TURN server to be used +echo --default-turn Uses IP address downloaded from https://api.ipify.org and sets up default turn parameters +echo --turn-user ^ Sets the turn username when using a turn server. +echo --turn-pass ^ Sets the turn password when using a turn server. +echo --start-turn Will launch the turnserver process. +echo --stun ^ STUN server to be used +echo --default-stun Uses IP address downloaded from https://api.ipify.org and sets up default stun parameters +echo --build Force a rebuild of the typescript frontend even if it already exists +echo --frontend-dir ^ Sets the output path for the fontend build +echo Other options: stored and passed to the server. +set CONTINUE=0 +exit /b + +:ParseArgs +set FORCE_BUILD=0 +set DEFAULT_STUN=0 +set DEFAULT_TURN=0 +set START_TURN=0 +set SERVER_ARGS= +set FRONTEND_DIR= +set TURN_SERVER= +set TURN_USER= +set TURN_PASS= +set STUN_SERVER= +set PUBLIC_IP= +:arg_loop +IF NOT "%1"=="" ( + set HANDLED=0 + IF "%1"=="--help" ( + CALL :Usage + GOTO :eof + ) + IF "%1"=="--publicip" ( + set HANDLED=1 + set PUBLIC_IP=%2 + SHIFT + ) + IF "%1"=="--turn" ( + set HANDLED=1 + set TURN_SERVER=%2 + SHIFT + ) + IF "%1"=="--default-turn" ( + set HANDLED=1 + set DEFAULT_TURN=1 + ) + IF "%1"=="--turn-user" ( + set HANDLED=1 + set TURN_USER=1 + ) + IF "%1"=="--turn-pass" ( + set HANDLED=1 + set TURN_PASS=1 + ) + if "%1"=="--start-turn" ( + set HANDLED=1 + set START_TURN=1 + ) + IF "%1"=="--stun" ( + set HANDLED=1 + set STUN_SERVER=%2 + SHIFT + ) + IF "%1"=="--default-stun" ( + set HANDLED=1 + set DEFAULT_STUN=1 + ) + IF "%1"=="--build" ( + set HANDLED=1 + set FORCE_BUILD=1 + ) + IF "%1"=="--frontend-dir" ( + set HANDLED=1 + set FRONTEND_DIR=%2 + SHIFT + ) + IF NOT "!HANDLED!"=="1" ( + set SERVER_ARGS=%SERVER_ARGS% %1 + ) + SHIFT + GOTO :arg_loop +) +exit /b + +:SetupNode +pushd %SCRIPT_DIR% +SET NODE_NAME=node-%NODE_VERSION%-win-x64 +if exist node\ ( + echo Node directory found...skipping install. +) else ( + echo Node directory not found...beginning NodeJS download for Windows. + + rem Download nodejs and follow redirects. + curl -L -o ./node.zip "https://nodejs.org/dist/%NODE_VERSION%/%NODE_NAME%.zip" + + tar -xf node.zip + ren "%NODE_NAME%\" "node" + del node.zip +) + +rem Print node version +echo Node version: & node\node.exe -v +popd +exit /b + +:SetupFrontend +rem Start in the repo dir +pushd %SCRIPT_DIR%..\..\..\ + +rem NOTE: We want to use our NodeJS (not system NodeJS!) to build the web frontend files. +rem Save our current directory (the NodeJS dir) in a variable +set NODE_DIR=%SCRIPT_DIR%node + +rem Prepend NODE_DIR to PATH temporarily +set "OLDPATH=%PATH%" +set "PATH=%PATH%;%NODE_DIR%" + +IF "%FRONTEND_DIR%"=="" ( + set FRONTEND_DIR="%SCRIPT_DIR%..\..\www" +) + +rem try to make it an absolute path +call :NormalizePath %FRONTEND_DIR% +set FRONTEND_DIR=%RETVAL% + +rem Set this for webpack +set WEBPACK_OUTPUT_PATH=%FRONTEND_DIR% + +IF NOT exist %FRONTEND_DIR%\player.html ( + set FORCE_BUILD=1 +) + +IF "%FORCE_BUILD%"=="1" ( + rem We could replace this all with a single npm script that does all this. we do have several build-all scripts already + rem but this does give a good reference about the dependency chain for all of this. + rem Note: npm link will also run npm install so we dont need that here + echo Building common library... + echo ---------------------------- + pushd %CD%\Common + call %SCRIPT_DIR%\node\npm install + call %SCRIPT_DIR%\node\npm run build + popd + echo Building frontend library... + echo ---------------------------- + pushd %CD%\Frontend\library + call %SCRIPT_DIR%\node\npm link ../../Common + call %SCRIPT_DIR%\node\npm run build + popd + echo Building frontend-ui library... + echo ---------------------------- + pushd %CD%\Frontend\ui-library + call %SCRIPT_DIR%\node\npm link ../library + call %SCRIPT_DIR%\node\npm run build + popd + echo Building Epic Games reference frontend... + echo ---------------------------- + pushd %CD%\Frontend\implementations\typescript + call %SCRIPT_DIR%\node\npm link ../../library ../../ui-library + call %SCRIPT_DIR%\node\npm run build + popd + popd +) else ( + echo Skipping rebuilding frontend... %FRONTEND_DIR% has content already, use --build to force a frontend rebuild. +) + +rem Restore path +set "PATH=%OLDPATH%" +exit /b + +:SetupCoturn +@Rem Look for CoTURN directory next to this script +pushd %SCRIPT_DIR% +if exist coturn\ ( + echo CoTURN directory found...skipping install. +) else ( + echo CoTURN directory not found...beginning CoTURN download for Windows. + + @Rem Download nodejs and follow redirects. + curl -L -o ./turnserver.zip "https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.5.2-coturn-windows/turnserver.zip" + + @Rem Unarchive the .zip to a directory called "turnserver" + mkdir coturn & tar -xf turnserver.zip -C coturn + + @Rem Delete the downloaded turnserver.zip + del turnserver.zip +) +popd +goto :eof + +:Setup +echo Checking Pixel Streaming Server dependencies +call :SetupNode +call :SetupFrontend +call :SetupCoturn +exit /b + +:SetPublicIP +FOR /f %%A IN ('powershell -command "(Invoke-Webrequest "http://api.ipify.org").content"') DO set PUBLIC_IP=%%A +Echo External IP is : %PUBLIC_IP% +exit /b + +:SetupTurnStun +IF "%DEFAULT_TURN%"=="1" ( + set TURN_SERVER=%PUBLIC_IP%:19303 + set TURN_USER=PixelStreamingUser + set TURN_PASS=AnotherTURNintheroad +) +IF "%DEFAULT_STUN%"=="1" ( + set STUN_SERVER=stun.l.google.com:19302 +) +FOR /f %%A IN ('powershell -command "(Test-Connection -ComputerName (hostname) -Count 1 | Select IPV4Address).IPV4Address.IPAddressToString"') DO set LOCAL_IP=%%A +FOR /F "tokens=1,2 delims=:" %%i in ("%TURN_SERVER%") do ( + set TURN_PORT=%j +) +IF "%TURN_PORT%"=="" ( set TURN_PORT=3478 ) + +set TURN_PROCESS=%SCRIPT_DIR%coturn\turnserver.exe +set TURN_REALM=PixelStreaming +set TURN_ARGS=-c ..\..\..\turnserver.conf --allowed-peer-ip=%LOCAL_IP% -p %TURN_PORT% -r %TURN_REALM% -X %PUBLIC_IP% -E %LOCAL_IP% -L %LOCAL_IP% --no-cli --no-tls --no-dtls --pidfile `"C:\coturn.pid`" -f -a -v -u %TURN_USER%:%TURN_PASS% + +if "%START_TURN%"=="1" ( + IF NOT "%TURN_SERVER%"=="" ( + IF NOT "%TURN_USER%"=="" ( + IF NOT "%TURN_PASS%"=="" ( + pushd %SCRIPT_DIR%coturn\ + IF "%1"=="bg" ( + start "%TURN_PROCESS%" %TURN_PROCESS% %TURN_ARGS% + ) else ( + call %TURN_PROCESS% %TURN_ARGS% + ) + popd + ) + ) + ) +) +exit /b + +:PrintConfig +echo. +echo Running with config: +echo Public IP address : %PUBLIC_IP% +IF NOT "%STUN_SERVER%"=="" ( echo STUN_SERVER : %STUN_SERVER% ) +IF NOT "%TURN_SERVER%"=="" ( echo TURN_SERVER : %TURN_SERVER% ) +echo Server command line arguments: %SERVER_ARGS% +echo. +exit /b + +:StartWilbur +pushd %SCRIPT_DIR%\..\..\ +echo Building signalling library... +echo ---------------------------- +pushd ..\Signalling +call %SCRIPT_DIR%\node\npm link ../Common +call %SCRIPT_DIR%\node\npm run build +popd +echo Building wilbur... +echo ---------------------------- +call %SCRIPT_DIR%\node\npm link ../Signalling +call %SCRIPT_DIR%\node\npm run build +call %NPM% run start -- %SERVER_ARGS% +exit /b + +:NormalizePath +set RETVAL=%~f1 +exit /b diff --git a/SignallingWebServer/platform_scripts/cmd/run_local.bat b/SignallingWebServer/platform_scripts/cmd/run_local.bat deleted file mode 100644 index 30afba42..00000000 --- a/SignallingWebServer/platform_scripts/cmd/run_local.bat +++ /dev/null @@ -1,25 +0,0 @@ -@Rem Copyright Epic Games, Inc. All Rights Reserved. - -@echo off - -@Rem Set script directory as working directory. -pushd "%~dp0" - -title Cirrus - -@Rem Run setup to ensure we have node and cirrus installed. -call setup.bat %* - -@Rem Move to cirrus directory. -pushd ..\.. - -@Rem Run node server and pass any argument along. -platform_scripts\cmd\node\node.exe cirrus %* - -@Rem Pop cirrus directory. -popd - -@Rem Pop script directory. -popd - -pause \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/setup.bat b/SignallingWebServer/platform_scripts/cmd/setup.bat index 4767ddc2..8662602e 100644 --- a/SignallingWebServer/platform_scripts/cmd/setup.bat +++ b/SignallingWebServer/platform_scripts/cmd/setup.bat @@ -1,23 +1,21 @@ @Rem Copyright Epic Games, Inc. All Rights Reserved. - @echo off +setlocal enabledelayedexpansion -@Rem Set script location as working directory for commands. -pushd "%~dp0" - -@Rem Ensure we have NodeJs available for calling. -call setup_node.bat - -@Rem Ensure we have frontend built. -call setup_frontend.bat %* +call :Init +call :ParseArgs %* -@Rem Ensure we have CoTURN available for calling. -call setup_coturn.bat +IF "%CONTINUE%"=="1" ( + call :Setup +) -@Rem Move to cirrus.js directory and install its package.json -pushd %~dp0\..\..\ -call platform_scripts\cmd\node\npm install --no-save -popd +goto :eof -@Rem Pop working directory -popd \ No newline at end of file +:Init +:ParseArgs +:Setup +:SetPublicIP +:SetupTurnStun +:PrintConfig +:StartWilbur +%~dp0common.bat %* diff --git a/SignallingWebServer/platform_scripts/cmd/setup_coturn.bat b/SignallingWebServer/platform_scripts/cmd/setup_coturn.bat deleted file mode 100644 index d2640b35..00000000 --- a/SignallingWebServer/platform_scripts/cmd/setup_coturn.bat +++ /dev/null @@ -1,25 +0,0 @@ -@Rem Copyright Epic Games, Inc. All Rights Reserved. - -@echo off - -@Rem Set script location as working directory for commands. -pushd "%~dp0" - -@Rem Look for CoTURN directory next to this script -if exist coturn\ ( - echo CoTURN directory found...skipping install. -) else ( - echo CoTURN directory not found...beginning CoTURN download for Windows. - - @Rem Download nodejs and follow redirects. - curl -L -o ./turnserver.zip "https://github.com/EpicGames/PixelStreamingInfrastructure/releases/download/v4.5.2-coturn-windows/turnserver.zip" - - @Rem Unarchive the .zip to a directory called "turnserver" - mkdir coturn & tar -xf turnserver.zip -C coturn - - @Rem Delete the downloaded turnserver.zip - del turnserver.zip -) - -@Rem Pop working directory -popd \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/setup_frontend.bat b/SignallingWebServer/platform_scripts/cmd/setup_frontend.bat deleted file mode 100644 index 902a5d4a..00000000 --- a/SignallingWebServer/platform_scripts/cmd/setup_frontend.bat +++ /dev/null @@ -1,85 +0,0 @@ -:main - @Rem Copyright Epic Games, Inc. All Rights Reserved. - @echo off - - @Rem Set root directory as working directory for commands. - pushd %~dp0\..\..\..\ - - @Rem By default don't build the frontend files - set "shouldbuild=false" - - @Rem Check if --build is passed as argument and we will always build frontend files. - :parse - IF "%~1"=="" GOTO endparse - IF "%~1"=="--build" set "shouldbuild=true" - SHIFT - GOTO parse - :endparse - - @Rem Look under /Public directory for player.html - if exist SignallingWebServer\Public\player.html ( - @Rem If --build is passed then we should build - if "%shouldbuild%" == "true" ( - call :buildFrontend - ) else ( - echo Skipping rebuilding frontend... SignallingWebServer/Public has content already, use --build to force a frontend rebuild. - ) - ) else ( - call :buildFrontend - ) - - @Rem Pop working directory - popd - - goto :eof - -:buildFrontend - echo Building frontend files... - - @Rem Look for a node directory next to this script - if not exist node call SignallingWebServer\platform_scripts\cmd\setup_node.bat - - @Rem NOTE: We want to use our NodeJS (not system NodeJS!) to build the web frontend files. - @Rem Save our current directory (the NodeJS dir) in a variable - set "NodeDir=%CD%\SignallingWebServer\platform_scripts\cmd\node" - - @rem Save the old path variable - set OLDPATH=%PATH% - @Rem Prepend NodeDir to PATH temporarily - set PATH=%PATH%;%NodeDir% - - @Rem Do npm install in the Frontend\lib directory (note we use start because that loads PATH) - echo ---------------------------- - echo Building common library... - pushd %CD%\Common - call ..\SignallingWebServer\platform_scripts\cmd\node\npm install - call ..\SignallingWebServer\platform_scripts\cmd\node\npm run build - popd - echo Building frontend library... - pushd %CD%\Frontend\library - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm install - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm link ../../Common - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm run build-dev - popd - pushd %CD%\Frontend\ui-library - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm install - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm link ../library - call ..\..\SignallingWebServer\platform_scripts\cmd\node\npm run build-dev - popd - echo End of build PS frontend lib step. - - @Rem Do npm install in the Frontend\implementations\typescript directory (note we use start because that loads PATH) - echo ---------------------------- - echo Building Epic Games reference frontend... - pushd %CD%\Frontend\implementations\typescript - call ..\..\..\SignallingWebServer\platform_scripts\cmd\node\npm install - call ..\..\..\SignallingWebServer\platform_scripts\cmd\node\npm link ../../library ../../ui-library - call ..\..\..\SignallingWebServer\platform_scripts\cmd\node\npm run build-dev - popd - echo End of build reference frontend step. - echo ---------------------------- - - @Rem Restore path - set PATH=%OLDPATH% - - goto :eof \ No newline at end of file diff --git a/SignallingWebServer/platform_scripts/cmd/setup_node.bat b/SignallingWebServer/platform_scripts/cmd/setup_node.bat deleted file mode 100644 index 747f0ef7..00000000 --- a/SignallingWebServer/platform_scripts/cmd/setup_node.bat +++ /dev/null @@ -1,35 +0,0 @@ -@Rem Copyright Epic Games, Inc. All Rights Reserved. - -@echo off - -@Rem Set script location as working directory for commands. -pushd "%~dp0" - -@Rem Name and version of node that we are downloading -SET NodeVersion=v18.17.0 -SET NodeName=node-%NodeVersion%-win-x64 - -@Rem Look for a node directory next to this script -if exist node\ ( - echo Node directory found...skipping install. -) else ( - echo Node directory not found...beginning NodeJS download for Windows. - - @Rem Download nodejs and follow redirects. - curl -L -o ./node.zip "https://nodejs.org/dist/%NodeVersion%/%NodeName%.zip" - - @Rem Unarchive the .zip - tar -xf node.zip - - @Rem Rename the extracted, versioned, directory that contains the NodeJS binaries to simply "node". - ren "%NodeName%\" "node" - - @Rem Delete the downloaded node.zip - del node.zip -) - -@Rem Print node version -echo Node version: & node\node.exe -v - -@Rem Pop working directory -popd diff --git a/SignallingWebServer/platform_scripts/cmd/start.bat b/SignallingWebServer/platform_scripts/cmd/start.bat new file mode 100644 index 00000000..02c27705 --- /dev/null +++ b/SignallingWebServer/platform_scripts/cmd/start.bat @@ -0,0 +1,48 @@ +@Rem Copyright Epic Games, Inc. All Rights Reserved. +@echo off +setlocal enabledelayedexpansion + +title Wilbur + +call :Init +call :ParseArgs %* + +IF "%CONTINUE%"=="1" ( + call :Setup + call :SetPublicIP + call :SetupTurnStun bg + + set PEER_OPTIONS= + set SERVER_ARGS=!SERVER_ARGS! --serve --https_redirect --public_ip=!PUBLIC_IP! + IF NOT "!STUN_SERVER!"=="" ( + IF NOT "!TURN_SERVER!"=="" ( + set PEER_OPTIONS={\\\"iceServers\\\":[{\\\"urls\\\":[\\\"stun:!STUN_SERVER!\\\",\\\"turn:!TURN_SERVER!\\\"],\\\"username\\\":\\\"!TURN_USER!\\\",\\\"credential\\\":\\\"!TURN_PASS!\\\"}]} + ) ELSE ( + set PEER_OPTIONS={\\\"iceServers\\\":[{\\\"urls\\\":[\\\"stun:!STUN_SERVER!\\\"]}]} + ) + ) ELSE IF NOT "!TURN_SERVER!"=="" ( + set PEER_OPTIONS={\\\"iceServers\\\":[{\\\"urls\\\":[\\\"turn:!TURN_SERVER!\\\"],\\\"username\\\":\\\"!TURN_USER!\\\",\\\"credentials\\\":\\\"!TURN_PASS!\\\"}]} + ) + + IF NOT "!PEER_OPTIONS!"=="" ( + set SERVER_ARGS=!SERVER_ARGS! --peer_options=\"!PEER_OPTIONS!\" + ) + IF NOT "!FRONTEND_DIR!"=="" ( + set SERVER_ARGS=!SERVER_ARGS! --http_root=\"!FRONTEND_DIR!\" + ) + + call :PrintConfig + call :StartWilbur + pause +) + +goto :eof + +:Init +:ParseArgs +:Setup +:SetPublicIP +:SetupTurnStun +:PrintConfig +:StartWilbur +%~dp0common.bat %* diff --git a/SignallingWebServer/platform_scripts/cmd/start_turn.bat b/SignallingWebServer/platform_scripts/cmd/start_turn.bat new file mode 100644 index 00000000..679e9e9d --- /dev/null +++ b/SignallingWebServer/platform_scripts/cmd/start_turn.bat @@ -0,0 +1,26 @@ +@Rem Copyright Epic Games, Inc. All Rights Reserved. +@echo off +setlocal enabledelayedexpansion + +title turnserver + +call :Init +call :ParseArgs %* +IF "%CONTINUE%"=="1" ( + call :Setup + call :SetPublicIP + set DEFAULT_TURN=1 + set START_TURN=1 + call :SetupTurnStun +) + +goto :eof + +:Init +:ParseArgs +:Setup +:SetPublicIP +:SetupTurnStun +:PrintConfig +:StartWilbur +%~dp0common.bat %* diff --git a/SignallingWebServer/platform_scripts/cmd/start_with_stun.bat b/SignallingWebServer/platform_scripts/cmd/start_with_stun.bat new file mode 100644 index 00000000..85ba7c71 --- /dev/null +++ b/SignallingWebServer/platform_scripts/cmd/start_with_stun.bat @@ -0,0 +1,5 @@ +@Rem Copyright Epic Games, Inc. All Rights Reserved. +@echo off +setlocal enabledelayedexpansion + +call %~dp0start.bat --default-stun %* diff --git a/SignallingWebServer/platform_scripts/cmd/start_with_turn.bat b/SignallingWebServer/platform_scripts/cmd/start_with_turn.bat new file mode 100644 index 00000000..7620e50b --- /dev/null +++ b/SignallingWebServer/platform_scripts/cmd/start_with_turn.bat @@ -0,0 +1,5 @@ +@Rem Copyright Epic Games, Inc. All Rights Reserved. +@echo off +setlocal enabledelayedexpansion + +call %~dp0start.bat --default-stun --default-turn --start-turn %* diff --git a/SignallingWebServer/src/InputHandler.ts b/SignallingWebServer/src/InputHandler.ts new file mode 100644 index 00000000..ad80650d --- /dev/null +++ b/SignallingWebServer/src/InputHandler.ts @@ -0,0 +1,60 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; +import { IProgramOptions, beautify } from './Utils'; + +interface IHandlerFunc { + desc: string, + func: (options: IProgramOptions, signallingServer: SignallingServer) => void, +} + +export function initInputHandler(options: IProgramOptions, signallingServer: SignallingServer) { + const stdin = process.stdin; + + stdin.setRawMode( true ); + stdin.resume(); + stdin.setEncoding('utf8'); + + const handlers: Record = { + 'c': { desc: 'Print configuration.', func: printConfig }, + 'i': { desc: 'Print current server info.', func: printServerInfo }, + 's': { desc: 'Print list of connected streamers.', func: printStreamerList }, + 'p': { desc: 'Print list of connected players.', func: printPlayerList }, + } + + // on any data into stdin + stdin.on('data', (keyBuffer) => { + const key = keyBuffer.toString(); + if (key == 'q' || key == '\u0003') { + process.exit(); + } else if (key == 'h') { + process.stdout.write('Help:\n'); + for (const [handlerKey, handlerInfo] of Object.entries(handlers)) { + process.stdout.write(`\t${handlerKey} - ${handlerInfo.desc}\n`); + } + process.stdout.write(`\th - Help.\n`); + process.stdout.write(`\tq - Quit.\n`); + } else { + const handler = handlers[key]; + if (!handler) { + process.stdout.write(`${key}: No handler.\n`); + } else { + handler.func(options, signallingServer); + } + } + }); +} + +function printConfig(options: IProgramOptions) { + process.stdout.write(`${beautify(options)}\n`); +} + +function printServerInfo(_options: IProgramOptions, _signallingServer: SignallingServer) { + process.stdout.write(`Info:\n\t\n`); +} + +function printStreamerList(_options: IProgramOptions, signallingServer: SignallingServer) { + process.stdout.write(`Streamer Ids: ${signallingServer.streamerRegistry.streamers.map(streamer => streamer.streamerId)}\n`); +} + +function printPlayerList(_options: IProgramOptions, signallingServer: SignallingServer) { + process.stdout.write(`Player Ids: ${signallingServer.playerRegistry.listPlayers().map(player => player.playerId)}\n`); +} diff --git a/SignallingWebServer/src/Utils.ts b/SignallingWebServer/src/Utils.ts new file mode 100644 index 00000000..db1c33d7 --- /dev/null +++ b/SignallingWebServer/src/Utils.ts @@ -0,0 +1,18 @@ +import { jsonc } from 'jsonc'; + +// A simple interface to describe the options from commander.js +export type IProgramOptions = Record; + +/** + * Cirular reference safe version of JSON.stringify + */ +export function stringify(obj: any): string { + return jsonc.stringify(obj); +} + +/** + * Circular reference save version of JSON.stringify with extra formatting. + */ +export function beautify(obj: any): string { + return jsonc.stringify(obj, undefined, '\t'); +} diff --git a/SignallingWebServer/src/index.ts b/SignallingWebServer/src/index.ts new file mode 100644 index 00000000..b2166d70 --- /dev/null +++ b/SignallingWebServer/src/index.ts @@ -0,0 +1,176 @@ +import express from 'express'; +import fs from 'fs'; +import path from 'path'; +import { SignallingServer, + IServerConfig, + WebServer, + InitLogging, + Logger, + IWebServerConfig } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; +import { stringify, beautify, IProgramOptions } from './Utils'; +import { initInputHandler } from './InputHandler'; +import { Command, Option } from 'commander'; +import { initialize } from 'express-openapi'; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const pjson = require('../package.json'); +/* eslint-enable @typescript-eslint/no-var-requires */ + +const program = new Command(); +program + .name('node build/index.js') + .description(pjson.description) + .version(pjson.version); + +// For any switch that doesn't take an argument, like --serve, its important to give it the default +// of false. Without the default, not supplying the default will mean the option is undefined in +// cli_options. When merged with the config_file options, if the app was previously run with --serve +// you will not be able to turn serving off without editing the config file. +// an alternative could be to use --serve , but then the options come through as strings +// as 'true' or 'false', which might not be terrible, but it's not as neat. +program + .option('--log_folder ', 'Sets the path for the log files.', 'logs') + .addOption(new Option('--log_level_console ', 'Sets the logging level for console messages.') + .choices(["debug","info","warning","error"]) + .default("info")) + .addOption(new Option('--log_level_file ', 'Sets the logging level for log files.') + .choices(["debug","info","warning","error"]) + .default("info")) + .addOption(new Option('--console_messages [detail]', 'Displays incoming and outgoing signalling messages on the console.') + .choices(["basic","verbose","formatted"]) + .preset("basic")) + .option('--streamer_port ', 'Sets the listening port for streamer connections.', '8888') + .option('--player_port ', 'Sets the listening port for player connections.', '80') + .option('--sfu_port ', 'Sets the listening port for SFU connections.', '8889') + .option('--serve', 'Enables the webserver on player_port.', false) + .option('--http_root ', 'Sets the path for the webserver root.', 'www') + .option('--homepage ', 'The default html file to serve on the web server.', 'player.html') + .option('--https', 'Enables the webserver on https_port and enabling SSL', false) + .addOption(new Option('--https_port ', 'Sets the listen port for the https server.') + .implies({https: true}) + .default(443)) + .option('--ssl_key_path ', 'Sets the path for the SSL key file.','ssl/key.pem') + .option('--ssl_cert_path ', 'Sets the path for the SSL certificate file.','ssl/cert.pem') + .option('--https_redirect', 'Enables the redirection of connection attempts on http to https. If this is not set the webserver will only listen on https_port. Player websockets will still listen on player_port.', false) + .option('--rest_api', 'Enables the rest API interface that can be accessed at /api/api-definition', false) + .addOption(new Option('--peer_options ', 'Additional JSON data to send in peerConnectionOptions of the config message.') + .argParser(JSON.parse)) + .option('--matchmaker', 'Enable matchmaker connection.', false) + .addOption(new Option('--matchmaker_address
', 'Sets the matchmaker address to connect to.') + .default('127.0.0.1') + .implies({ matchmaker: true })) + .addOption(new Option('--matchmaker_port ', 'Sets the matchmaker port to connect to.') + .default('9999') + .argParser(parseInt)) + .addOption(new Option('--matchmaker_retry ', 'Sets the delay before reconnecting to the matchmaker after a disconnect.').default("5").argParser(parseInt)) + .addOption(new Option('--matchmaker_keepalive ', 'Sets the delay between matchmaker pings.') + .default('30') + .argParser(parseInt)) + .option('--public_ip ', 'The public IP address to be used to connect to this server. Only needed when using matchmaker.', '127.0.0.1') + .option('--log_config', 'Will print the program configuration on startup.', false) + .option('--stdin', 'Allows stdin input while running.', false) + .option('--no_config', 'Skips the reading of the config file. Only CLI options will be used.', false) + .option('--config_file ', 'Sets the path of the config file.', 'config.json') + .option('--no_save', 'On startup the given configuration is resaved out to config.json. This switch will prevent this behaviour allowing the config.json file to remain untouched while running with new configurations.', false) + .helpOption('-h, --help', 'Display this help text.') + .allowUnknownOption() // ignore unknown options which will allow versions to be swapped out into existing scripts with maybe older/newer options + .parse(); + +// parsed command line options +const cli_options: IProgramOptions = program.opts(); + +// possible config file options +let config_file: any = {}; +if (!cli_options.no_config) { + // read any config file + try { + const configData = fs.readFileSync(cli_options.config_file, { encoding: 'utf8' }); + config_file = JSON.parse(configData); + } catch(error) { + // silently fail here since we havent started the logging system yet. + } +} + +// merge the configurations +const options: IProgramOptions = { ...config_file, ...cli_options }; + +// save out new configuration (unless disabled) +if (!options.no_save) { + + // dont save certain options + const save_options = { ...options }; + delete save_options.no_config; + delete save_options.config_file; + delete save_options.no_save; + + // save out the config file with the current settings + fs.writeFile(options.config_file, beautify(save_options), (error: any) => { + if (error) throw error; + }); +} + +InitLogging({ + logDir: options.log_folder, + logMessagesToConsole: options.console_messages, + logLevelConsole: options.log_level_console, + logLevelFile: options.log_level_file, +}); + +Logger.info(`${pjson.name} v${pjson.version} starting...`); +if (options.log_config) { + Logger.info(`Config: ${stringify(options)}`); +} + +const app = express(); + +const serverOpts: IServerConfig = { + streamerPort: options.streamer_port, + playerPort: options.player_port, + sfuPort: options.sfu_port, + peerOptions: options.peer_options, + useMatchmaker: options.matchmaker, + matchmakerAddress: options.matchmaker_address, + matchmakerPort: options.matchmaker_port, + matchmakerRetryInterval: options.matchmaker_retry, + matchmakerKeepAliveInterval: options.matchmaker_keepalive, + publicIp: options.public_ip, + publicPort: options.https ? options.https_port : options.player_port +} + +if (options.serve) { + const webserverOptions: IWebServerConfig = { + httpPort: options.player_port, + root: options.http_root, + homepageFile: options.homepage + }; + if (options.https) { + webserverOptions.httpsPort = options.https_port; + webserverOptions.ssl_key = fs.readFileSync(path.join(__dirname, '..', options.ssl_key_path)); + webserverOptions.ssl_cert = fs.readFileSync(path.join(__dirname, '..', options.ssl_cert_path)); + webserverOptions.https_redirect = options.https_redirect; + } + const webServer = new WebServer(app, webserverOptions); + if (!options.https || webserverOptions.https_redirect) { + serverOpts.httpServer = webServer.httpServer; + } + serverOpts.httpsServer = webServer.httpsServer; +} + +const signallingServer = new SignallingServer(serverOpts); + +if (options.stdin) { + initInputHandler(options, signallingServer); +} + +if (options.rest_api) { + initialize({ + app, + docsPath: "/api-definition", + exposeApiDocs: true, + apiDoc: "./apidoc/api-definition-base.yml", + paths: "./build/paths", + dependencies: { + signallingServer, + } + }); +} diff --git a/SignallingWebServer/src/paths/config.ts b/SignallingWebServer/src/paths/config.ts new file mode 100644 index 00000000..caef3b83 --- /dev/null +++ b/SignallingWebServer/src/paths/config.ts @@ -0,0 +1,38 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +export default function(signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + res.status(200).json({ config: signallingServer.config, protocolConfig: signallingServer.protocolConfig }); + } + + GET.apiDoc = { + summary: "Returns the current configuration of the server.", + operationId: "getConfig", + responses: { + 200: { + description: "The current configuration of the server.", + content: { + "application/json": { + schema: { + type: "object", + properties: { + "config": { + type: "object" + }, + "protocol": { + type: "object" + }, + }, + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/src/paths/players.ts b/SignallingWebServer/src/paths/players.ts new file mode 100644 index 00000000..d734c005 --- /dev/null +++ b/SignallingWebServer/src/paths/players.ts @@ -0,0 +1,33 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +export default function(signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + res.status(200).json(signallingServer.playerRegistry.listPlayers().map(player => player.getPlayerInfo())); + } + + GET.apiDoc = { + summary: "Returns list of players", + operationId: "getPlayers", + responses: { + 200: { + description: "List of player IDs", + content: { + "application/json": { + schema: { + type: "array", + items: { + type: "string", + }, + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/src/paths/players/{playerId}.ts b/SignallingWebServer/src/paths/players/{playerId}.ts new file mode 100644 index 00000000..224892b6 --- /dev/null +++ b/SignallingWebServer/src/paths/players/{playerId}.ts @@ -0,0 +1,44 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +export default function (signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + const player = signallingServer.playerRegistry.get(req.params.playerId); + if (!player) { + throw new Error(`No player ID matches ${req.params.playerId}.`); + } + res.status(200).json(player.getPlayerInfo()); + } + + GET.apiDoc = { + summary: "Returns a single player", + operationId: "getOnePlayer", + parameters: [ + { + name: "playerId", + in: "path", + required: true, + schema: { + type: "string", + }, + }, + ], + responses: { + 200: { + description: "Player data", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/Player", + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/src/paths/status.ts b/SignallingWebServer/src/paths/status.ts new file mode 100644 index 00000000..5f218e4d --- /dev/null +++ b/SignallingWebServer/src/paths/status.ts @@ -0,0 +1,55 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const pjson = require('../../package.json'); +/* eslint-enable @typescript-eslint/no-var-requires */ + +export default function(signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + const nowTime = new Date(); + const uptime = nowTime.getTime() - signallingServer.startTime.getTime(); + res.status(200).json({ + uptime: uptime, + streamer_count: signallingServer.streamerRegistry.count(), + player_count: signallingServer.playerRegistry.count(), + version: pjson.version, + }); + } + + GET.apiDoc = { + summary: "Returns the current status of the server.", + operationId: "getConfig", + responses: { + 200: { + description: "The current status of the server.", + content: { + "application/json": { + schema: { + type: "object", + properties: { + "uptime": { + type: "number" + }, + "streamer_count": { + type: "number" + }, + "player_count": { + type: "number" + }, + "version": { + type: "string" + }, + }, + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/src/paths/streamers.ts b/SignallingWebServer/src/paths/streamers.ts new file mode 100644 index 00000000..dcde219e --- /dev/null +++ b/SignallingWebServer/src/paths/streamers.ts @@ -0,0 +1,33 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +export default function(signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + res.status(200).json(signallingServer.streamerRegistry.streamers.map(streamer => streamer.getStreamerInfo())); + } + + GET.apiDoc = { + summary: "Returns list of streamers", + operationId: "getStreamers", + responses: { + 200: { + description: "List of streamers", + content: { + "application/json": { + schema: { + type: "array", + items: { + type: "string", + }, + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/src/paths/streamers/{streamerId}.ts b/SignallingWebServer/src/paths/streamers/{streamerId}.ts new file mode 100644 index 00000000..f082fc38 --- /dev/null +++ b/SignallingWebServer/src/paths/streamers/{streamerId}.ts @@ -0,0 +1,44 @@ +import { SignallingServer } from '@epicgames-ps/lib-pixelstreamingsignalling-ue5.5'; + +export default function (signallingServer: SignallingServer) { + const operations = { + GET, + }; + + function GET(req: any, res: any, _next: any) { + const streamer = signallingServer.streamerRegistry.find(req.params.streamerId); + if (!streamer) { + throw new Error(`No streamer id matches ${req.params.streamerId}.`); + } + res.status(200).json(streamer.getStreamerInfo()); + } + + GET.apiDoc = { + summary: "Returns a single streamer", + operationId: "getOneStreamer", + parameters: [ + { + name: "streamerId", + in: "path", + required: true, + schema: { + type: "string", + }, + }, + ], + responses: { + 200: { + description: "Streamer data", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/Streamer", + }, + }, + }, + }, + }, + }; + + return operations; +} diff --git a/SignallingWebServer/tps/Bootstrap.tps b/SignallingWebServer/tps/Bootstrap.tps deleted file mode 100644 index 156174c4..00000000 --- a/SignallingWebServer/tps/Bootstrap.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - bootstrap v3.x, v4.x - /Samples/PixelStreaming/WebServers/ - This is a requirement to using Bootstrap, providing better UI elements for the client web pages created for demoing pixelstreaming - https://github.com/twitter/bootstrap/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/Bootstrap_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/Express.tps b/SignallingWebServer/tps/Express.tps deleted file mode 100644 index 813a5dc6..00000000 --- a/SignallingWebServer/tps/Express.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - Express v4.16.2 - /Samples/PixelStreaming/WebServers/ - Express is a web framework for Node.js. - https://github.com/expressjs/express/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Plugins/Experimental/PixelStreaming/Source/Express_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/FontAwesome.tps b/SignallingWebServer/tps/FontAwesome.tps deleted file mode 100644 index 0a00d5db..00000000 --- a/SignallingWebServer/tps/FontAwesome.tps +++ /dev/null @@ -1,11 +0,0 @@ - - - FontAwesome Free v5.1 - /Samples/PixelStreaming/WebServers/ - Provides a consistent icon style to use in the sites for demoing pixelstreaming. - https://github.com/FortAwesome/Font-Awesome/blob/master/LICENSE.txt - - P4 - - None - \ No newline at end of file diff --git a/SignallingWebServer/tps/Helmet.tps b/SignallingWebServer/tps/Helmet.tps deleted file mode 100644 index eb45e856..00000000 --- a/SignallingWebServer/tps/Helmet.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - Helmet v.3.21.3 - /Samples/PixelStreaming/WebServers/SignallingWebServer - Helmet helps you secure your Express apps by setting various HTTP headers. - https://github.com/helmetjs/helmet/blob/v3.21.3/LICENSE - - Licensees - Git - P4 - - NONE (but keep license with code) - \ No newline at end of file diff --git a/SignallingWebServer/tps/JQuery.tps b/SignallingWebServer/tps/JQuery.tps deleted file mode 100644 index 0169eb36..00000000 --- a/SignallingWebServer/tps/JQuery.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - JQuery - /Samples/PixelStreaming/WebServers/ - This is a requirement to using Bootstrap, providing access to the DOM in the browser for easier and more advanced client side interactions and UI. Used for Project Cirrus. - https://github.com/jquery/jquery/blob/master/LICENSE.txt; https://js.foundation/pdf/ip-policy.pdf - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/JQuery_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/Popper.tps b/SignallingWebServer/tps/Popper.tps deleted file mode 100644 index 220de47a..00000000 --- a/SignallingWebServer/tps/Popper.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - Popper.js v1.14.3 - /Samples/PixelStreaming/WebServers/ - A requirement to using Bootstrap. - https://github.com/FezVrasta/popper.js/blob/master/LICENSE.md - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/Popper.js_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/Socket.io.tps b/SignallingWebServer/tps/Socket.io.tps deleted file mode 100644 index fb8706e9..00000000 --- a/SignallingWebServer/tps/Socket.io.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - Socket.io v2.0.4 - /Samples/PixelStreaming/WebServers/ - Enables real-time bidirectional event-based communication. - https://github.com/socketio/socket.io/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Plugins/Experimental/PixelStreaming/Source/Socket.io_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/WS.tps b/SignallingWebServer/tps/WS.tps deleted file mode 100644 index 2b6af9f3..00000000 --- a/SignallingWebServer/tps/WS.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - WS - /Samples/PixelStreaming/WebServers/SignallingWebServer/ - It's used by SignallingWebServer (based on Node.js web-server) that is part of our PixelStreaming project.We add a dependency to WS library to Node.js configuration and it's downloaded automatically. - https://github.com/websockets/ws/blob/HEAD/LICENSE - - Licensees - Git - P4 - - //depot/UE4/Engine/Source/ThirdParty/Licenses/WS_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/WebRTCadapter.tps b/SignallingWebServer/tps/WebRTCadapter.tps deleted file mode 100644 index 42025d29..00000000 --- a/SignallingWebServer/tps/WebRTCadapter.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - WebRTC adapter (adapter.js) v6.3.2 - /Samples/PixelStreaming/WebServers/ - Used as a cross browser interface for WebRTC. - https://github.com/webrtc/adapter/blob/master/LICENSE.md - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/WebRTCadapter_License.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/Yargs.tps b/SignallingWebServer/tps/Yargs.tps deleted file mode 100644 index b7eb1a8e..00000000 --- a/SignallingWebServer/tps/Yargs.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - Yargs v15.3.0 - /Samples/PixelStreaming/WebServers/SignallingWebServer/ - A module for Node.js, used to parse command line arguments, which is downloaded automatically by Node Package Manager. - https://github.com/yargs/yargs/blob/v15.3.0/LICENSE - - Licensees - Git - P4 - - None - \ No newline at end of file diff --git a/SignallingWebServer/tps/bcrypt-js.tps b/SignallingWebServer/tps/bcrypt-js.tps deleted file mode 100644 index ded83c79..00000000 --- a/SignallingWebServer/tps/bcrypt-js.tps +++ /dev/null @@ -1,11 +0,0 @@ - - - Bcrypt.js - /Samples/PixelStreaming/WebServers/SignallingWebServer/ - This is used to verify passwords match the ones stored using the bcrypt algorithm. The passwords are always stored using bcrypt and so we never know the unencrypted password. This allows us to implement a authentication system on the web server so that only people we give accounts to can access the web server. This use is only for prototype stage, production will use the Epic unreal account system - https://github.com/dcodeIO/bcrypt.js/blob/master/LICENSE - - P4 - - /UE4/Main/Engine/Source/ThirdParty/Licenses - \ No newline at end of file diff --git a/SignallingWebServer/tps/expression-session.tps b/SignallingWebServer/tps/expression-session.tps deleted file mode 100644 index fec8e7e5..00000000 --- a/SignallingWebServer/tps/expression-session.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - express-session v1.15.6 - /Samples/PixelStreaming/WebServers/ - Used to create session id's used to remember a person who has logged into a server across page loads so that they don't have to log in every time the reload or navigate to a different page hosted on a webserver - https://github.com/expressjs/session/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/express-session_license.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/passport-local.tps b/SignallingWebServer/tps/passport-local.tps deleted file mode 100644 index b47cab7d..00000000 --- a/SignallingWebServer/tps/passport-local.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - passport-local v1.0.0 - /Samples/PixelStreaming/WebServers/ - This is a implementation for the Passport middleware that allows you to store user credentials locally on the machine (passwords are stored with bcrypt and not reversible) to be used to authenticate users on a node.js webserver. This use is only for prototype stage, production will use the Epic unreal account system. - https://github.com/jaredhanson/passport-local/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/passport-local_license.txt - \ No newline at end of file diff --git a/SignallingWebServer/tps/passport.tps b/SignallingWebServer/tps/passport.tps deleted file mode 100644 index a2c7b54c..00000000 --- a/SignallingWebServer/tps/passport.tps +++ /dev/null @@ -1,13 +0,0 @@ - - - passport v0.4.0 - /Samples/PixelStreaming/WebServers/ - Is the authentication middleware that adds the ability to securely log in a user to the webserver. This is a generic framework that you add specific implementation frameworks (separate TPS's will be provided for these) to to provide authentication on a node.js webserver. - https://github.com/jaredhanson/passport/blob/master/LICENSE - - Licensees - Git - P4 - - /Engine/Source/ThirdParty/Licenses/passport_license.txt - \ No newline at end of file diff --git a/SignallingWebServer/tsconfig.json b/SignallingWebServer/tsconfig.json new file mode 100644 index 00000000..54d4ec16 --- /dev/null +++ b/SignallingWebServer/tsconfig.json @@ -0,0 +1,109 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig to read more about this file */ + + /* Projects */ + // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ + // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ + // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ + // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ + // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ + // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ + + /* Language and Environment */ + "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + // "jsx": "preserve", /* Specify what JSX code is generated. */ + // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ + // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ + // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ + // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ + // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ + // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ + // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ + + /* Modules */ + "module": "commonjs", /* Specify what module code is generated. */ + // "rootDir": "./", /* Specify the root folder within your source files. */ + // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ + // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ + // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ + // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ + // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ + // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ + // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ + // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ + // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ + // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ + // "resolveJsonModule": true, /* Enable importing .json files. */ + // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ + // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ + + /* JavaScript Support */ + // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ + // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ + // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ + + /* Emit */ + // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ + // "declarationMap": true, /* Create sourcemaps for d.ts files. */ + // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ + // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ + // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ + // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ + "outDir": "./build", /* Specify an output folder for all emitted files. */ + // "removeComments": true, /* Disable emitting comments. */ + // "noEmit": true, /* Disable emitting files from a compilation. */ + // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ + // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ + // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ + // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ + // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ + // "newLine": "crlf", /* Set the newline character for emitting files. */ + // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ + // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ + // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ + // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ + // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ + // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ + + /* Interop Constraints */ + // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ + // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ + // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ + "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ + "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + + /* Type Checking */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ + // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ + // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ + // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ + // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ + // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ + // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ + // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ + // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ + // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ + // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ + // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ + // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ + // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ + // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ + + /* Completeness */ + // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } +} diff --git a/SignallingWebServer/turnserver.conf b/SignallingWebServer/turnserver.conf deleted file mode 100644 index a8b7befe..00000000 --- a/SignallingWebServer/turnserver.conf +++ /dev/null @@ -1,15 +0,0 @@ -no-multicast-peers -denied-peer-ip=0.0.0.0-0.255.255.255 -denied-peer-ip=10.0.0.0-10.255.255.255 -denied-peer-ip=100.64.0.0-100.127.255.255 -denied-peer-ip=127.0.0.0-127.255.255.255 -denied-peer-ip=169.254.0.0-169.254.255.255 -denied-peer-ip=172.16.0.0-172.31.255.255 -denied-peer-ip=192.0.0.0-192.0.0.255 -denied-peer-ip=192.0.2.0-192.0.2.255 -denied-peer-ip=192.88.99.0-192.88.99.255 -denied-peer-ip=192.168.0.0-192.168.255.255 -denied-peer-ip=198.18.0.0-198.19.255.255 -denied-peer-ip=198.51.100.0-198.51.100.255 -denied-peer-ip=203.0.113.0-203.0.113.255 -denied-peer-ip=240.0.0.0-255.255.255.255