warpr 1.0.21 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/fesm2022/warpr.mjs +716 -0
  2. package/fesm2022/warpr.mjs.map +1 -0
  3. package/index.d.ts +5 -0
  4. package/lib/components/stream-host/stream-host.component.d.ts +26 -0
  5. package/lib/data/frames.d.ts +20 -0
  6. package/lib/data/signaling-messages.d.ts +29 -0
  7. package/lib/data/streaming-messages.d.ts +53 -0
  8. package/lib/infrastructure/array-stream.d.ts +21 -0
  9. package/lib/infrastructure/events.d.ts +25 -0
  10. package/lib/infrastructure/message-assembler.d.ts +6 -0
  11. package/lib/networking/messaging-client.d.ts +5 -0
  12. package/lib/networking/web-socket-client.d.ts +17 -0
  13. package/lib/services/messaging.service.d.ts +12 -0
  14. package/lib/services/streaming.service.d.ts +30 -0
  15. package/package.json +26 -15
  16. package/{src/public-api.ts → public-api.d.ts} +0 -4
  17. package/ng-package.json +0 -7
  18. package/src/lib/components/stream-host/stream-host.component.html +0 -5
  19. package/src/lib/components/stream-host/stream-host.component.scss +0 -31
  20. package/src/lib/components/stream-host/stream-host.component.spec.ts +0 -23
  21. package/src/lib/components/stream-host/stream-host.component.ts +0 -169
  22. package/src/lib/data/frames.ts +0 -37
  23. package/src/lib/data/signaling-messages.ts +0 -64
  24. package/src/lib/data/streaming-messages.ts +0 -136
  25. package/src/lib/infrastructure/array-stream.ts +0 -109
  26. package/src/lib/infrastructure/events.ts +0 -61
  27. package/src/lib/infrastructure/message-assembler.ts +0 -60
  28. package/src/lib/networking/messaging-client.ts +0 -7
  29. package/src/lib/networking/web-socket-client.ts +0 -55
  30. package/src/lib/services/messaging.service.spec.ts +0 -16
  31. package/src/lib/services/messaging.service.ts +0 -32
  32. package/src/lib/services/streaming.service.spec.ts +0 -16
  33. package/src/lib/services/streaming.service.ts +0 -145
  34. package/tsconfig.lib.json +0 -14
  35. package/tsconfig.lib.prod.json +0 -10
  36. package/tsconfig.spec.json +0 -14
@@ -1,169 +0,0 @@
1
- import { Component, ElementRef, ViewChild } from '@angular/core';
2
- import { StreamingService } from '../../services/streaming.service';
3
- import { EncodedFrame, FrameType, Size } from '../../data/frames';
4
- import { PointerInputMessage, PointerStates, ResizeSurfaceMessage } from '../../data/streaming-messages';
5
- import { CommonModule } from '@angular/common';
6
-
7
- @Component({
8
- selector: 'warpr-stream-host',
9
- imports: [CommonModule],
10
- templateUrl: './stream-host.component.html',
11
- styleUrl: './stream-host.component.scss'
12
- })
13
- export class StreamHostComponent {
14
-
15
- @ViewChild("root")
16
- private _root?: ElementRef<HTMLDivElement>;
17
-
18
- @ViewChild("renderTarget")
19
- private _canvas?: ElementRef<HTMLCanvasElement>;
20
- private _renderingContext: CanvasRenderingContext2D | null = null;
21
-
22
- private _decoder: VideoDecoder;
23
- private _framePending?: VideoFrame;
24
- private _videoSize = Size.Empty;
25
- private _displaySize = Size.Empty;
26
- private _isDecoderInitialized = false;
27
- private _pointerStates: PointerStates = {};
28
-
29
- public get IsFullScreenAvailable(): boolean {
30
- return document.fullscreenEnabled;
31
- }
32
-
33
- public constructor(
34
- private _streamingService: StreamingService) {
35
- this._streamingService.Connected.Subscribe((sender, eventArgs) => this.OnConnected());
36
- this._streamingService.FrameReceived.Subscribe((sender, eventArgs) => this.OnFrameReceived(eventArgs));
37
-
38
- this._decoder = new VideoDecoder({
39
- output: (frame) => this.OnFrameDecoded(frame),
40
- error: (error) => console.log(error)
41
- });
42
- }
43
-
44
- private async ngAfterViewInit() {
45
-
46
- console.log("Initializing stream host...");
47
-
48
- let canvas = this._canvas!.nativeElement;
49
- this._renderingContext = canvas.getContext("2d")!;
50
- canvas.onpointerdown = (event) => this.OnPointerEvent(event);
51
- canvas.onpointermove = (event) => this.OnPointerEvent(event);
52
- canvas.onpointerup = (event) => this.OnPointerEvent(event);
53
- canvas.onwheel = (event) => this.OnWheelEvent(event);
54
- canvas.oncontextmenu = (event) => event.preventDefault();
55
-
56
- if (this._renderingContext) {
57
- console.log("Stream host initialized.");
58
- }
59
- else {
60
- console.log("Stream host failed to initialize.");
61
- }
62
-
63
- requestAnimationFrame(() => this.RenderFrame());
64
- }
65
-
66
- private OnPointerEvent(event: PointerEvent, wheelDelta = 0) {
67
- //console.log(event);
68
- event.preventDefault();
69
-
70
- if (event.type == "pointerdown") this._canvas!.nativeElement.setPointerCapture(event.pointerId);
71
-
72
- let message = new PointerInputMessage(event, this._pointerStates, this._canvas!.nativeElement, wheelDelta);
73
- //console.log(message);
74
-
75
- this._streamingService.SendControlMessage(message);
76
- }
77
-
78
- private OnWheelEvent(event: WheelEvent) {
79
- //console.log(event);
80
- event.preventDefault();
81
-
82
- let pointerEvent: PointerEvent = new PointerEvent(event.type, event);
83
- this.OnPointerEvent(pointerEvent, event.deltaY);
84
- }
85
-
86
- private OnConnected() {
87
- this._displaySize = Size.Empty;
88
- }
89
-
90
- private OnFrameReceived(frame: EncodedFrame) {
91
- let chunk = new EncodedVideoChunk({
92
- data: frame.Bytes,
93
- type: frame.Type == FrameType.Key ? "key" : "delta",
94
- timestamp: frame.Index
95
- });
96
-
97
- //Initialize decoder
98
- let frameSize = new Size(frame.Width, frame.Height);
99
-
100
- if (!Size.AreEqual(frameSize, this._videoSize)) {
101
- console.log("Configuring decoder...");
102
- this._decoder.configure({
103
- codec: "hvc1.1.6.L93.B0",
104
- codedWidth: frame.Width,
105
- codedHeight: frame.Height,
106
- hardwareAcceleration: "prefer-hardware",
107
- optimizeForLatency: true
108
- });
109
- console.log("Decoder configured.");
110
- console.log(`Resolution is ${frame.Width} x ${frame.Height}.`);
111
-
112
- this._videoSize = frameSize;
113
- }
114
-
115
- //Decode frame
116
- if (!this._isDecoderInitialized && chunk.type != "key") return;
117
- this._isDecoderInitialized = true;
118
-
119
- this._decoder.decode(chunk);
120
- }
121
-
122
- private OnFrameDecoded(frame: VideoFrame) {
123
- if (this._framePending) this._framePending.close();
124
- this._framePending = frame;
125
- requestAnimationFrame(() => this.RenderFrame());
126
- }
127
-
128
- private RenderFrame() {
129
-
130
- let frame = this._framePending;
131
- this._framePending = undefined;
132
-
133
- if (frame) {
134
-
135
- //Set canvas resolution to video resolution
136
- let canvas = this._canvas!.nativeElement;
137
- canvas.width = frame.displayWidth;
138
- canvas.height = frame.displayHeight;
139
-
140
- //Check for control size change
141
- let canvasRect = canvas.getBoundingClientRect();
142
- let canvasSize = new Size(Math.round(canvasRect.width * window.devicePixelRatio), Math.round(canvasRect.height * window.devicePixelRatio));
143
- if (!Size.AreEqual(canvasSize, this._displaySize)) {
144
- this._displaySize = canvasSize;
145
-
146
- console.log(`Resized to ${canvasSize.Width} x ${canvasSize.Height}`);
147
- let message = new ResizeSurfaceMessage(canvasSize.Width, canvasSize.Height);
148
- this._streamingService.SendControlMessage(message);
149
- }
150
-
151
- //Draw new frame
152
- if (this._renderingContext) {
153
- this._renderingContext.drawImage(frame, 0, 0, frame.displayWidth, frame.displayHeight);
154
- }
155
-
156
- //Release memory
157
- frame.close();
158
- }
159
- }
160
-
161
- public ToggleFullScreen() {
162
- if (document.fullscreenElement === this._root?.nativeElement) {
163
- document.exitFullscreen();
164
- }
165
- else {
166
- this._root?.nativeElement.requestFullscreen();
167
- }
168
- }
169
- }
@@ -1,37 +0,0 @@
1
- import { ArrayStream } from "../infrastructure/array-stream";
2
-
3
- export enum FrameType {
4
- Unknown,
5
- Key,
6
- Delta
7
- }
8
-
9
- export class EncodedFrame {
10
- public Index: number;
11
- public Type: FrameType;
12
- public Width: number;
13
- public Height: number;
14
- public Bytes: ArrayBuffer;
15
-
16
- public constructor(buffer: ArrayBuffer) {
17
- let stream = new ArrayStream(buffer);
18
-
19
- this.Type = stream.ReadUInt32();
20
- this.Index = stream.ReadUInt32();
21
- this.Width = stream.ReadUInt32();
22
- this.Height = stream.ReadUInt32();
23
- this.Bytes = stream.ReadBuffer();
24
- }
25
- }
26
-
27
- export class Size {
28
- public constructor(
29
- public Width: number,
30
- public Height: number) { }
31
-
32
- public static Empty: Size = { Width: 0, Height: 0 };
33
-
34
- public static AreEqual(a: Size, b: Size) {
35
- return a.Width === b.Width && a.Height === b.Height;
36
- }
37
- }
@@ -1,64 +0,0 @@
1
-
2
- export enum WarprSignalingMessageType {
3
- ConnectionRequest = "ConnectionRequest",
4
- PairingCompleteMessage = "PairingCompleteMessage",
5
- PeerConnectionDescriptionMessage = "PeerConnectionDescriptionMessage",
6
- PeerConnectionCandidateMessage = "PeerConnectionCandidateMessage",
7
- StreamingSourcesMessage = "StreamingSourcesMessage",
8
- }
9
-
10
- export class ConnectionRequest {
11
- public readonly $type = WarprSignalingMessageType.ConnectionRequest;
12
- public SessionId?: string;
13
- }
14
-
15
- export class PairingCompleteMessage {
16
- public readonly $type = WarprSignalingMessageType.PairingCompleteMessage;
17
-
18
- public IceServers?: string[];
19
- }
20
-
21
- export function GetConfiguration(iceServers?: string[]): RTCConfiguration {
22
- let result: RTCConfiguration = {};
23
- result.iceServers = new Array<RTCIceServer>();
24
-
25
- if (iceServers) {
26
- let regex = /^(?<protocol>stun|turn):(?:(?<username>\w+):(?<password>\w+)@)?(?<url>[\w\.:?=&]+)$/im;
27
- for (let server of iceServers) {
28
- let match = regex.exec(server);
29
- if (!match || !match.groups) {
30
- console.warn(`Failed to parse ICE server URI ${server}.`);
31
- continue;
32
- }
33
-
34
- result.iceServers.push({
35
- urls: `${match.groups['protocol']}:${match.groups['url']}`,
36
- username: match.groups['username'],
37
- credential: match.groups['password']
38
- });
39
- }
40
- }
41
-
42
- return result;
43
- }
44
-
45
- export class PeerConnectionDescriptionMessage {
46
- public readonly $type = WarprSignalingMessageType.PeerConnectionDescriptionMessage;
47
- public Description?: string;
48
- }
49
-
50
- export class PeerConnectionCandidateMessage {
51
- public readonly $type = WarprSignalingMessageType.PeerConnectionCandidateMessage;
52
- public Candidate?: string;
53
- }
54
- export class StreamingSourcesMessage {
55
- public readonly $type = WarprSignalingMessageType.StreamingSourcesMessage;
56
- public Sources?: string[];
57
- }
58
-
59
- export type WarprSignalingMessage =
60
- ConnectionRequest |
61
- PairingCompleteMessage |
62
- PeerConnectionDescriptionMessage |
63
- PeerConnectionCandidateMessage |
64
- StreamingSourcesMessage;
@@ -1,136 +0,0 @@
1
- export enum WarprStreamingMessageType {
2
- PointerInputMessage = "PointerInputMessage",
3
- ResizeSurfaceMessage = "ResizeSurfaceMessage"
4
- }
5
-
6
- export type Point = {
7
- X: number;
8
- Y: number;
9
- }
10
-
11
- export enum PointerType {
12
- Unknown = "Unknown",
13
- Mouse = "Mouse",
14
- Pen = "Pen",
15
- Touch = "Touch"
16
- }
17
-
18
- export enum PointerAction {
19
- Unknown = "Unknown",
20
- Pressed = "Pressed",
21
- Moved = "Moved",
22
- Released = "Released",
23
- WheelChanged = "WheelChanged"
24
- }
25
-
26
- export enum PointerFlags {
27
- None = 0,
28
- LeftButton = 1,
29
- MiddleButton = 2,
30
- RightButton = 4,
31
- Wheel = 8,
32
- Captured = 16
33
- };
34
-
35
- export class PointerState {
36
- public Buttons = 0;
37
- }
38
-
39
- export type PointerStates = { [key: number]: PointerState };
40
-
41
- export class PointerInputMessage {
42
- public readonly $type = WarprStreamingMessageType.PointerInputMessage;
43
-
44
- public Id = -1;
45
- public Type = PointerType.Unknown;
46
- public Action = PointerAction.Unknown;
47
- public Position: Point = { X: 0, Y: 0 };
48
- public Flags = PointerFlags.None;
49
- public WheelDelta = 0;
50
-
51
- constructor(event: PointerEvent, states: PointerStates, target: HTMLCanvasElement, wheelDelta: number) {
52
-
53
- let state = states[event.pointerId];
54
- if (state === undefined) {
55
- state = new PointerState();
56
- states[event.pointerId] = state;
57
- }
58
-
59
- this.Id = event.pointerId;
60
-
61
- switch (event.pointerType) {
62
- case "mouse":
63
- this.Type = PointerType.Mouse;
64
- break;
65
- case "pen":
66
- this.Type = PointerType.Pen;
67
- break;
68
- case "touch":
69
- this.Type = PointerType.Touch;
70
- break;
71
- default:
72
- this.Type = PointerType.Mouse;
73
- break;
74
- }
75
-
76
- switch (event.type) {
77
- case "pointerdown":
78
- this.Action = PointerAction.Pressed;
79
- state.Buttons |= 1 << event.button
80
- break;
81
- case "pointerup":
82
- this.Action = PointerAction.Released;
83
- state.Buttons &= ~(1 << event.button)
84
- break;
85
- case "pointermove":
86
- this.Action = PointerAction.Moved;
87
- break;
88
- case "wheel":
89
- this.Action = PointerAction.WheelChanged;
90
- break;
91
- }
92
-
93
- if (state.Buttons & 1) this.Flags |= PointerFlags.LeftButton;
94
- if (state.Buttons & 2) this.Flags |= PointerFlags.MiddleButton;
95
- if (state.Buttons & 4) this.Flags |= PointerFlags.RightButton;
96
- if (wheelDelta != 0) this.Flags |= PointerFlags.Wheel;
97
- if (target.hasPointerCapture(event.pointerId)) this.Flags |= PointerFlags.Captured;
98
-
99
- this.Position = PointerInputMessage.GetRemotePosition(event, target);
100
- this.WheelDelta = -wheelDelta;
101
- }
102
-
103
- private static GetRemotePosition(event: PointerEvent, target: HTMLCanvasElement): Point {
104
- let offsetX = 0;
105
- let offsetY = 0;
106
- let scale = 1;
107
- let canvasAspectRatio = target.clientWidth / target.clientHeight;
108
- let videoAspectRatio = target.width / target.height;
109
-
110
- if (videoAspectRatio > canvasAspectRatio) {
111
- offsetY = 0.5 * (target.clientHeight - target.clientWidth / videoAspectRatio);
112
- scale = target.width / target.clientWidth;
113
- } else {
114
- offsetX = 0.5 * (target.clientWidth - target.clientHeight * videoAspectRatio);
115
- scale = target.height / target.clientHeight;
116
- }
117
-
118
- return {
119
- X: (event.x - offsetX) * scale,
120
- Y: (event.y - offsetY) * scale
121
- };
122
- }
123
- }
124
-
125
- export class ResizeSurfaceMessage {
126
- public readonly $type = WarprStreamingMessageType.ResizeSurfaceMessage;
127
-
128
- constructor(
129
- public Width: number,
130
- public Height: number
131
- ) { }
132
- }
133
-
134
- export type WarprStreamingMessage =
135
- PointerInputMessage |
136
- ResizeSurfaceMessage;
@@ -1,109 +0,0 @@
1
- export class ArrayStream {
2
- private _view: DataView;
3
- private _position: number;
4
-
5
- public constructor(buffer: ArrayBuffer) {
6
- this._view = new DataView(buffer);
7
- this._position = 0;
8
- }
9
-
10
- public get Position(): number {
11
- return this._position;
12
- }
13
-
14
- public set Position(value: number) {
15
- if (value < 0 || this._view.buffer.byteLength < value) {
16
- throw new RangeError("The provided value lies outside the stream's bounds.");
17
- }
18
-
19
- this._position = value;
20
- }
21
-
22
- public ReadBool(): boolean {
23
- let result = this._view.getUint8(this._position) != 0;
24
- this._position += 1;
25
- return result;
26
- }
27
-
28
- public ReadUInt8(): number {
29
- let result = this._view.getUint8(this._position);
30
- this._position += 1;
31
- return result;
32
- }
33
-
34
- public ReadUInt16(): number {
35
- let result = this._view.getUint16(this._position, true);
36
- this._position += 2;
37
- return result;
38
- }
39
-
40
- public ReadUInt32(): number {
41
- let result = this._view.getUint32(this._position, true);
42
- this._position += 4;
43
- return result;
44
- }
45
-
46
- public ReadUInt64(): bigint {
47
- let result = this._view.getBigUint64(this._position, true);
48
- this._position += 8;
49
- return result;
50
- }
51
-
52
- public ReadInt8(): number {
53
- let result = this._view.getInt8(this._position);
54
- this._position += 1;
55
- return result;
56
- }
57
-
58
- public ReadInt16(): number {
59
- let result = this._view.getInt16(this._position, true);
60
- this._position += 2;
61
- return result;
62
- }
63
-
64
- public ReadInt32(): number {
65
- let result = this._view.getInt32(this._position, true);
66
- this._position += 4;
67
- return result;
68
- }
69
-
70
- public ReadInt64(): bigint {
71
- let result = this._view.getBigInt64(this._position, true);
72
- this._position += 8;
73
- return result;
74
- }
75
-
76
- public ReadSingle(): number {
77
- let result = this._view.getFloat32(this._position, true);
78
- this._position += 4;
79
- return result;
80
- }
81
-
82
- public ReadDouble(): number {
83
- let result = this._view.getFloat64(this._position, true);
84
- this._position += 8;
85
- return result;
86
- }
87
-
88
- public ReadBuffer(): ArrayBuffer {
89
- let length = this.ReadUInt32();
90
- let result = this._view.buffer.slice(this._position, this._position + length);
91
- this._position += length;
92
- return result;
93
- }
94
-
95
- public ReadToEnd(): ArrayBuffer {
96
- let length = this._view.buffer.byteLength - this._position;
97
- let result = this._view.buffer.slice(this._position, this._position + length);
98
- this._position += length;
99
- return result;
100
- }
101
-
102
- public ReadString() {
103
- let length = this.ReadUInt32();
104
- let decoder = new TextDecoder();
105
- let result = decoder.decode(this._view.buffer.slice(this._position, this._position + length));
106
- this._position += length;
107
- return result;
108
- }
109
- }
@@ -1,61 +0,0 @@
1
- export type EventHandler<TSender, TEventArgs> = (sender: TSender, eventArgs: TEventArgs) => void;
2
-
3
- export interface IEventSource {
4
-
5
- Unsubscribe(token: symbol): void;
6
- }
7
-
8
- export class EventSubscription {
9
-
10
- constructor(
11
- private _owner: IEventSource,
12
- private _token: symbol) { }
13
-
14
- Unsubscribe() {
15
- this._owner.Unsubscribe(this._token);
16
- }
17
- }
18
-
19
- export class EventOwner {
20
-
21
- public readonly Token = Symbol();
22
-
23
- Raise<TSender, TEventArgs>(event: EventPublisher<TSender, TEventArgs>, sender: TSender, eventArgs: TEventArgs) {
24
- event.Raise(this, sender, eventArgs);
25
- }
26
- }
27
-
28
- export class EventArgs {
29
- public static readonly Empty = new EventArgs();
30
- }
31
-
32
- export class EventPublisher<TSender, TEventArgs> implements IEventSource {
33
-
34
- private readonly _token: symbol;
35
- private readonly _handlers = new Map<symbol, EventHandler<TSender, TEventArgs>>;
36
-
37
- constructor(owner: EventOwner) {
38
- this._token = owner.Token;
39
- }
40
-
41
- Subscribe(handler: EventHandler<TSender, TEventArgs>) {
42
- const token = Symbol();
43
- this._handlers.set(token, handler);
44
- return new EventSubscription(this, token);
45
- }
46
-
47
- Raise(owner: EventOwner, sender: TSender, eventArgs: TEventArgs): void {
48
- if (owner.Token === this._token) {
49
- for (let [symbol, handler] of this._handlers) {
50
- handler(sender, eventArgs);
51
- }
52
- }
53
- else {
54
- throw Error("Cannot raise event: this event source does not belong to the specified owner.");
55
- }
56
- }
57
-
58
- Unsubscribe(token: symbol): void {
59
- this._handlers.delete(token);
60
- }
61
- }
@@ -1,60 +0,0 @@
1
- import { ArrayStream } from "./array-stream";
2
-
3
- class MessageBuilder {
4
- public readonly FragmentCount: number;
5
- public FragmentsReady: number = 0;
6
- public Buffer: Uint8Array;
7
-
8
- constructor(
9
- public readonly Id: number,
10
- public readonly Size: number,
11
- public readonly FragmentSize: number
12
- ) {
13
- this.FragmentCount = Math.ceil(Size / FragmentSize);
14
- this.Buffer = new Uint8Array(Size);
15
- }
16
-
17
- AddFragment(index: number, buffer: Uint8Array): boolean {
18
- if (this.FragmentCount == this.FragmentsReady) throw RangeError("All message parts have been already collected.");
19
-
20
- this.Buffer.set(buffer, this.FragmentSize * index);
21
- this.FragmentsReady++;
22
-
23
- return this.FragmentCount == this.FragmentsReady;
24
- }
25
- }
26
-
27
- export class MessageAssembler {
28
-
29
- private _builders = new Map<number, MessageBuilder>();
30
-
31
- public constructor(private _maxBuilderCount = 3) { }
32
-
33
- public PushMessage(buffer: ArrayBuffer): ArrayBuffer | null {
34
- let stream = new ArrayStream(buffer);
35
- let messageIndex = stream.ReadUInt32();
36
- let messageSize = stream.ReadUInt32();
37
- let fragmentSize = stream.ReadUInt32();
38
- let fragmentIndex = stream.ReadUInt32();
39
- let fragment = new Uint8Array(stream.ReadToEnd());
40
-
41
- let builder = this._builders.get(messageIndex);
42
- if (builder === undefined) {
43
- builder = new MessageBuilder(messageIndex, messageSize, fragmentSize);
44
- this._builders.set(messageIndex, builder);
45
-
46
- if (this._builders.size > this._maxBuilderCount) {
47
- let [oldest] = this._builders.keys();
48
- this._builders.delete(oldest);
49
- console.log("Message lost.");
50
- }
51
- }
52
-
53
- if (builder.AddFragment(fragmentIndex, fragment)) {
54
- this._builders.delete(messageIndex);
55
- return builder.Buffer.buffer;
56
- } else {
57
- return null;
58
- }
59
- }
60
- }
@@ -1,7 +0,0 @@
1
- import { EventPublisher } from "../infrastructure/events";
2
-
3
- export interface IMessagingClient<TMessage> {
4
- readonly MessageReceived: EventPublisher<IMessagingClient<TMessage>, TMessage>;
5
-
6
- SendMessage(message: TMessage): void;
7
- }
@@ -1,55 +0,0 @@
1
- import { EventOwner, EventPublisher, EventArgs } from "../infrastructure/events";
2
- import { IMessagingClient } from "./messaging-client";
3
-
4
- export class WebSocketClient<TMessage> implements IMessagingClient<TMessage> {
5
- private readonly _events = new EventOwner();
6
-
7
- public readonly Connected = new EventPublisher<IMessagingClient<TMessage>, EventArgs>(this._events);
8
- public readonly MessageReceived = new EventPublisher<IMessagingClient<TMessage>, TMessage>(this._events);
9
- public readonly Disconnected = new EventPublisher<IMessagingClient<TMessage>, EventArgs>(this._events);
10
-
11
- private _socket?: WebSocket;
12
-
13
- constructor(private _uri: string) { }
14
-
15
- Connect(): void {
16
- console.log(`Connecting to ${this._uri}...`);
17
-
18
- let socket = new WebSocket(this._uri);
19
- socket.onopen = () => this.OnConnected();
20
- socket.onmessage = (e: MessageEvent<string>) => this.OnMessageReceived(e);
21
- socket.onclose = () => this.OnDisconnected();
22
- socket.onerror = (e: Event) => this.OnError(e);
23
- this._socket = socket;
24
- }
25
-
26
- SendMessage(message: TMessage): void {
27
- if (!this._socket) return;
28
-
29
- let content = JSON.stringify(message);
30
- console.debug(`Sending message: ${content}`);
31
-
32
- this._socket.send(content);
33
- }
34
-
35
- protected OnConnected(): void {
36
- console.log(`Connected to: ${this._uri}.`);
37
- this._events.Raise(this.Connected, this, EventArgs.Empty);
38
- }
39
-
40
- protected OnMessageReceived(eventArgs: MessageEvent<string>): void {
41
- console.debug('Message received: ' + eventArgs.data);
42
-
43
- let message = JSON.parse(eventArgs.data) as TMessage;
44
- this._events.Raise(this.MessageReceived, this, message);
45
- }
46
-
47
- protected OnDisconnected(): void {
48
- console.log(`Disconnected from: ${this._uri}.`);
49
- this._events.Raise(this.Disconnected, this, EventArgs.Empty);
50
- }
51
-
52
- protected OnError(error: Event): void {
53
- console.log('Connection failed: ' + error);
54
- }
55
- }