polfan-server-js-client 0.0.6 → 0.0.8

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 (177) hide show
  1. package/.eslintignore +0 -0
  2. package/.eslintrc.json +0 -0
  3. package/.idea/php.xml +18 -0
  4. package/.idea/vcs.xml +0 -1
  5. package/CONTRIBUTING.md +0 -15
  6. package/LICENSE +0 -21
  7. package/README.md +16 -16
  8. package/babel.config.js +7 -0
  9. package/build/index.js +1 -1
  10. package/build/index.js.map +1 -1
  11. package/build/types/Client.d.ts +35 -8
  12. package/build/types/ObservableInterface.d.ts +8 -1
  13. package/build/types/connections/ConnectionAssets.d.ts +16 -0
  14. package/build/types/connections/RestApiConnection.d.ts +14 -0
  15. package/build/types/connections/WebApiConnection.d.ts +17 -12
  16. package/build/types/connections/WebSocketConnection.d.ts +16 -14
  17. package/build/types/dtos/Dto.d.ts +1 -1
  18. package/build/types/dtos/protocol/Envelope.d.ts +2 -2
  19. package/build/types/index.d.ts +5 -19
  20. package/package.json +1 -1
  21. package/scripts/getPackageJson.js +25 -0
  22. package/scripts/testMock.js +1 -0
  23. package/src/Client.ts +239 -0
  24. package/src/ObservableInterface.ts +39 -0
  25. package/src/connections/ConnectionAssets.ts +19 -0
  26. package/src/connections/RestApiConnection.ts +47 -0
  27. package/src/connections/WebApiConnection.ts +78 -0
  28. package/src/connections/WebSocketConnection.ts +95 -0
  29. package/src/dtos/Dto.ts +45 -0
  30. package/src/dtos/Message.ts +16 -0
  31. package/src/dtos/Permission.ts +12 -0
  32. package/src/dtos/Role.ts +12 -0
  33. package/src/dtos/Room.ts +15 -0
  34. package/src/dtos/RoomMember.ts +13 -0
  35. package/src/dtos/RoomSummary.ts +12 -0
  36. package/src/dtos/Space.ts +15 -0
  37. package/src/dtos/SpaceMember.ts +14 -0
  38. package/src/dtos/Topic.ts +12 -0
  39. package/src/dtos/User.ts +15 -0
  40. package/src/dtos/UserState.ts +16 -0
  41. package/src/dtos/protocol/Envelope.ts +12 -0
  42. package/src/dtos/protocol/commands/AssignRole.ts +12 -0
  43. package/src/dtos/protocol/commands/CreateMessage.ts +12 -0
  44. package/src/dtos/protocol/commands/CreateRole.ts +12 -0
  45. package/src/dtos/protocol/commands/CreateRoom.ts +12 -0
  46. package/src/dtos/protocol/commands/CreateSpace.ts +10 -0
  47. package/src/dtos/protocol/commands/CreateTopic.ts +12 -0
  48. package/src/dtos/protocol/commands/DeassignRole.ts +12 -0
  49. package/src/dtos/protocol/commands/DeleteRole.ts +11 -0
  50. package/src/dtos/protocol/commands/DeleteRoom.ts +10 -0
  51. package/src/dtos/protocol/commands/DeleteSpace.ts +10 -0
  52. package/src/dtos/protocol/commands/DeleteTopic.ts +10 -0
  53. package/src/dtos/protocol/commands/GetComputedPermissions.ts +13 -0
  54. package/src/dtos/protocol/commands/GetRolePermissions.ts +13 -0
  55. package/src/dtos/protocol/commands/GetRoomMembers.ts +10 -0
  56. package/src/dtos/protocol/commands/GetSession.ts +8 -0
  57. package/src/dtos/protocol/commands/GetSpaceMembers.ts +10 -0
  58. package/src/dtos/protocol/commands/GetSpaceRooms.ts +10 -0
  59. package/src/dtos/protocol/commands/GetUserPermissions.ts +13 -0
  60. package/src/dtos/protocol/commands/JoinRoom.ts +10 -0
  61. package/src/dtos/protocol/commands/JoinSpace.ts +10 -0
  62. package/src/dtos/protocol/commands/LeaveRoom.ts +10 -0
  63. package/src/dtos/protocol/commands/LeaveSpace.ts +10 -0
  64. package/src/dtos/protocol/commands/SetRolePermissions.ts +16 -0
  65. package/src/dtos/protocol/commands/SetUserPermissions.ts +16 -0
  66. package/src/dtos/protocol/events/Bye.ts +10 -0
  67. package/src/dtos/protocol/events/Error.ts +11 -0
  68. package/src/dtos/protocol/events/NewMessage.ts +13 -0
  69. package/src/dtos/protocol/events/NewRole.ts +14 -0
  70. package/src/dtos/protocol/events/NewRoom.ts +14 -0
  71. package/src/dtos/protocol/events/NewTopic.ts +14 -0
  72. package/src/dtos/protocol/events/Ok.ts +8 -0
  73. package/src/dtos/protocol/events/Permissions.ts +13 -0
  74. package/src/dtos/protocol/events/RoleDeleted.ts +11 -0
  75. package/src/dtos/protocol/events/RoomDeleted.ts +10 -0
  76. package/src/dtos/protocol/events/RoomJoined.ts +13 -0
  77. package/src/dtos/protocol/events/RoomLeft.ts +10 -0
  78. package/src/dtos/protocol/events/RoomMemberJoined.ts +13 -0
  79. package/src/dtos/protocol/events/RoomMemberLeft.ts +10 -0
  80. package/src/dtos/protocol/events/RoomMembers.ts +13 -0
  81. package/src/dtos/protocol/events/Session.ts +17 -0
  82. package/src/dtos/protocol/events/SpaceDeleted.ts +10 -0
  83. package/src/dtos/protocol/events/SpaceJoined.ts +13 -0
  84. package/src/dtos/protocol/events/SpaceLeft.ts +10 -0
  85. package/src/dtos/protocol/events/SpaceMemberJoined.ts +13 -0
  86. package/src/dtos/protocol/events/SpaceMemberLeft.ts +10 -0
  87. package/src/dtos/protocol/events/SpaceMemberUpdate.ts +13 -0
  88. package/src/dtos/protocol/events/SpaceMembers.ts +13 -0
  89. package/src/dtos/protocol/events/SpaceRooms.ts +13 -0
  90. package/src/dtos/protocol/events/TopicDeleted.ts +10 -0
  91. package/src/index.ts +14 -0
  92. package/src/protocol.ts +113 -0
  93. package/tsconfig.json +2 -2
  94. package/webpack.config.js +69 -0
  95. package/build/types/Token.d.ts +0 -5
  96. package/build/types/connections/ConnectionInterface.d.ts +0 -17
  97. package/build/types/dtos/protocol/EnvelopeMeta.d.ts +0 -6
  98. package/old/dist/Client.d.ts +0 -76
  99. package/old/dist/ObservableInterface.d.ts +0 -9
  100. package/old/dist/Token.d.ts +0 -5
  101. package/old/dist/connections/ConnectionInterface.d.ts +0 -17
  102. package/old/dist/connections/WebApiConnection.d.ts +0 -15
  103. package/old/dist/connections/WebSocketConnection.d.ts +0 -19
  104. package/old/dist/dtos/Dto.d.ts +0 -16
  105. package/old/dist/dtos/Message.d.ts +0 -9
  106. package/old/dist/dtos/Permission.d.ts +0 -7
  107. package/old/dist/dtos/Role.d.ts +0 -7
  108. package/old/dist/dtos/Room.d.ts +0 -8
  109. package/old/dist/dtos/RoomMember.d.ts +0 -6
  110. package/old/dist/dtos/RoomSummary.d.ts +0 -7
  111. package/old/dist/dtos/Space.d.ts +0 -8
  112. package/old/dist/dtos/SpaceMember.d.ts +0 -7
  113. package/old/dist/dtos/Topic.d.ts +0 -7
  114. package/old/dist/dtos/User.d.ts +0 -9
  115. package/old/dist/dtos/UserState.d.ts +0 -8
  116. package/old/dist/dtos/protocol/Envelope.d.ts +0 -7
  117. package/old/dist/dtos/protocol/EnvelopeMeta.d.ts +0 -6
  118. package/old/dist/dtos/protocol/ProtocolMessage.d.ts +0 -7
  119. package/old/dist/dtos/protocol/ProtocolMetaData.d.ts +0 -6
  120. package/old/dist/dtos/protocol/commands/AssignRole.d.ts +0 -7
  121. package/old/dist/dtos/protocol/commands/CreateMessage.d.ts +0 -7
  122. package/old/dist/dtos/protocol/commands/CreateRole.d.ts +0 -7
  123. package/old/dist/dtos/protocol/commands/CreateRoom.d.ts +0 -7
  124. package/old/dist/dtos/protocol/commands/CreateSpace.d.ts +0 -5
  125. package/old/dist/dtos/protocol/commands/CreateTopic.d.ts +0 -7
  126. package/old/dist/dtos/protocol/commands/DeassignRole.d.ts +0 -7
  127. package/old/dist/dtos/protocol/commands/DeleteRole.d.ts +0 -6
  128. package/old/dist/dtos/protocol/commands/DeleteRoom.d.ts +0 -5
  129. package/old/dist/dtos/protocol/commands/DeleteSpace.d.ts +0 -5
  130. package/old/dist/dtos/protocol/commands/DeleteTopic.d.ts +0 -5
  131. package/old/dist/dtos/protocol/commands/GetComputedPermissions.d.ts +0 -8
  132. package/old/dist/dtos/protocol/commands/GetRolePermissions.d.ts +0 -8
  133. package/old/dist/dtos/protocol/commands/GetRoomMembers.d.ts +0 -5
  134. package/old/dist/dtos/protocol/commands/GetSession.d.ts +0 -4
  135. package/old/dist/dtos/protocol/commands/GetSpaceMembers.d.ts +0 -5
  136. package/old/dist/dtos/protocol/commands/GetSpaceRooms.d.ts +0 -5
  137. package/old/dist/dtos/protocol/commands/GetUserPermissions.d.ts +0 -8
  138. package/old/dist/dtos/protocol/commands/JoinRoom.d.ts +0 -5
  139. package/old/dist/dtos/protocol/commands/JoinSpace.d.ts +0 -5
  140. package/old/dist/dtos/protocol/commands/LeaveRoom.d.ts +0 -5
  141. package/old/dist/dtos/protocol/commands/LeaveSpace.d.ts +0 -5
  142. package/old/dist/dtos/protocol/commands/SetRolePermissions.d.ts +0 -9
  143. package/old/dist/dtos/protocol/commands/SetUserPermissions.d.ts +0 -9
  144. package/old/dist/dtos/protocol/events/Bye.d.ts +0 -5
  145. package/old/dist/dtos/protocol/events/Error.d.ts +0 -6
  146. package/old/dist/dtos/protocol/events/NewMessage.d.ts +0 -6
  147. package/old/dist/dtos/protocol/events/NewRole.d.ts +0 -7
  148. package/old/dist/dtos/protocol/events/NewRoom.d.ts +0 -7
  149. package/old/dist/dtos/protocol/events/NewTopic.d.ts +0 -7
  150. package/old/dist/dtos/protocol/events/Ok.d.ts +0 -4
  151. package/old/dist/dtos/protocol/events/Permissions.d.ts +0 -6
  152. package/old/dist/dtos/protocol/events/RoleDeleted.d.ts +0 -6
  153. package/old/dist/dtos/protocol/events/RoomDeleted.d.ts +0 -5
  154. package/old/dist/dtos/protocol/events/RoomJoined.d.ts +0 -6
  155. package/old/dist/dtos/protocol/events/RoomLeft.d.ts +0 -5
  156. package/old/dist/dtos/protocol/events/RoomMemberJoined.d.ts +0 -6
  157. package/old/dist/dtos/protocol/events/RoomMemberLeft.d.ts +0 -5
  158. package/old/dist/dtos/protocol/events/RoomMembers.d.ts +0 -6
  159. package/old/dist/dtos/protocol/events/Session.d.ts +0 -9
  160. package/old/dist/dtos/protocol/events/SpaceDeleted.d.ts +0 -5
  161. package/old/dist/dtos/protocol/events/SpaceJoined.d.ts +0 -6
  162. package/old/dist/dtos/protocol/events/SpaceLeft.d.ts +0 -5
  163. package/old/dist/dtos/protocol/events/SpaceMemberJoined.d.ts +0 -6
  164. package/old/dist/dtos/protocol/events/SpaceMemberLeft.d.ts +0 -5
  165. package/old/dist/dtos/protocol/events/SpaceMemberUpdate.d.ts +0 -6
  166. package/old/dist/dtos/protocol/events/SpaceMembers.d.ts +0 -6
  167. package/old/dist/dtos/protocol/events/SpaceRooms.d.ts +0 -6
  168. package/old/dist/dtos/protocol/events/TopicDeleted.d.ts +0 -5
  169. package/old/dist/index.d.ts +0 -27
  170. package/old/dist/index.js +0 -1916
  171. package/old/dist/index.js.map +0 -1
  172. package/old/dist/protocol.d.ts +0 -7
  173. package/old/dist/pserv-js-client.js +0 -2
  174. package/old/dist/pserv-js-client.js.map +0 -1
  175. package/old/package-lock.json +0 -2654
  176. package/old/package.json +0 -22
  177. package/old/tsconfig.json +0 -12
@@ -1,23 +1,9 @@
1
- import { Client } from "./Client";
1
+ import { Client, ClientEvent } from "./Client";
2
2
  import { WebApiConnection } from "./connections/WebApiConnection";
3
- import { getToken } from "./Token";
4
3
  import { WebSocketConnection } from "./connections/WebSocketConnection";
4
+ import { RestApiConnection } from "./connections/RestApiConnection";
5
+ import { ChatConnectionEvent } from "./connections/ConnectionAssets";
6
+ import { events, commands } from "./protocol";
5
7
  import { Dto } from "./dtos/Dto";
6
8
  import { Envelope } from "./dtos/protocol/Envelope";
7
- import { EnvelopeMeta } from "./dtos/protocol/EnvelopeMeta";
8
- declare const connections: {
9
- WebApi: typeof WebApiConnection;
10
- WebSocket: typeof WebSocketConnection;
11
- };
12
- declare const data: {
13
- Dto: typeof Dto;
14
- Envelope: typeof Envelope;
15
- EnvelopeMeta: typeof EnvelopeMeta;
16
- events: {
17
- [x: string]: typeof Dto;
18
- };
19
- commands: {
20
- [x: string]: typeof Dto;
21
- };
22
- };
23
- export { Client, connections, data, getToken };
9
+ export { Client, ClientEvent, WebSocketConnection, WebApiConnection, RestApiConnection, ChatConnectionEvent, Dto, Envelope, events, commands, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polfan-server-js-client",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "JavaScript client library for handling communication with Polfan chat server.",
5
5
  "author": "Jarosław Żak",
6
6
  "license": "MIT",
@@ -0,0 +1,25 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * A module to get package informations from package.json
6
+ * @module getPackageJson
7
+ * @param {...string} keys from package.json if no arguments passed it returns package.json content as object
8
+ * @returns {object} with given keys or content of package.json as object
9
+ */
10
+
11
+ /**
12
+ * Returns package info
13
+ */
14
+ const getPackageJson = function(...args) {
15
+ const packageJSON = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json')));
16
+ if (!args.length) {
17
+ return packageJSON;
18
+ }
19
+ return args.reduce((out, key) => {
20
+ out[key] = packageJSON[key];
21
+ return out;
22
+ }, {});
23
+ };
24
+
25
+ module.exports = getPackageJson;
@@ -0,0 +1 @@
1
+ module.exports = {};
package/src/Client.ts ADDED
@@ -0,0 +1,239 @@
1
+ import {ChatConnectionEvent, ChatConnectionInterface} from "./connections/ConnectionAssets";
2
+ import {EventTarget} from "./ObservableInterface";
3
+ import {Envelope} from "./dtos/protocol/Envelope";
4
+ import {JoinRoom} from "./dtos/protocol/commands/JoinRoom";
5
+ import {Dto} from "./dtos/Dto";
6
+ import {RoomJoined} from "./dtos/protocol/events/RoomJoined";
7
+ import {LeaveRoom} from "./dtos/protocol/commands/LeaveRoom";
8
+ import {RoomLeft} from "./dtos/protocol/events/RoomLeft";
9
+ import {CreateRoom} from "./dtos/protocol/commands/CreateRoom";
10
+ import {Error as ErrorEvent} from "./dtos/protocol/events/Error";
11
+ import {SpaceRooms} from "./dtos/protocol/events/SpaceRooms";
12
+ import {DeleteRoom} from "./dtos/protocol/commands/DeleteRoom";
13
+ import {RoomDeleted} from "./dtos/protocol/events/RoomDeleted";
14
+ import {GetRoomMembers} from "./dtos/protocol/commands/GetRoomMembers";
15
+ import {RoomMembers} from "./dtos/protocol/events/RoomMembers";
16
+ import {JoinSpace} from "./dtos/protocol/commands/JoinSpace";
17
+ import {SpaceJoined} from "./dtos/protocol/events/SpaceJoined";
18
+ import {LeaveSpace} from "./dtos/protocol/commands/LeaveSpace";
19
+ import {SpaceLeft} from "./dtos/protocol/events/SpaceLeft";
20
+ import {CreateSpace} from "./dtos/protocol/commands/CreateSpace";
21
+ import {DeleteSpace} from "./dtos/protocol/commands/DeleteSpace";
22
+ import {SpaceDeleted} from "./dtos/protocol/events/SpaceDeleted";
23
+ import {GetSpaceMembers} from "./dtos/protocol/commands/GetSpaceMembers";
24
+ import {SpaceMembers} from "./dtos/protocol/events/SpaceMembers";
25
+ import {GetSpaceRooms} from "./dtos/protocol/commands/GetSpaceRooms";
26
+ import {GetSession} from "./dtos/protocol/commands/GetSession";
27
+ import {Session} from "./dtos/protocol/events/Session";
28
+ import {AssignRole} from "./dtos/protocol/commands/AssignRole";
29
+ import {SpaceMemberUpdate} from "./dtos/protocol/events/SpaceMemberUpdate";
30
+ import {DeassignRole} from "./dtos/protocol/commands/DeassignRole";
31
+ import {CreateRole} from "./dtos/protocol/commands/CreateRole";
32
+ import {NewRole} from "./dtos/protocol/events/NewRole";
33
+ import {DeleteRole} from "./dtos/protocol/commands/DeleteRole";
34
+ import {RoleDeleted} from "./dtos/protocol/events/RoleDeleted";
35
+ import {SetRolePermissions} from "./dtos/protocol/commands/SetRolePermissions";
36
+ import {Permissions} from "./dtos/protocol/events/Permissions";
37
+ import {SetUserPermissions} from "./dtos/protocol/commands/SetUserPermissions";
38
+ import {CreateTopic} from "./dtos/protocol/commands/CreateTopic";
39
+ import {NewTopic} from "./dtos/protocol/events/NewTopic";
40
+ import {DeleteTopic} from "./dtos/protocol/commands/DeleteTopic";
41
+ import {TopicDeleted} from "./dtos/protocol/events/TopicDeleted";
42
+ import {CreateMessage} from "./dtos/protocol/commands/CreateMessage";
43
+ import {NewMessage} from "./dtos/protocol/events/NewMessage";
44
+ import {GetComputedPermissions} from "./dtos/protocol/commands/GetComputedPermissions";
45
+ import {GetRolePermissions} from "./dtos/protocol/commands/GetRolePermissions";
46
+ import {GetUserPermissions} from "./dtos/protocol/commands/GetUserPermissions";
47
+ import {commands, events} from "./protocol";
48
+ import {WebSocketConnection} from "./connections/WebSocketConnection";
49
+ import {RestApiConnection} from "./connections/RestApiConnection";
50
+
51
+ type ArrayOfPromiseResolvers = [(value: any) => void, (reason?: any) => void];
52
+ type CmdResult<EventDto> = EventDto | ErrorEvent;
53
+ type CommandsToEventsType<CommandT> =
54
+ // General commands
55
+ CommandT extends GetSession ? CmdResult<Session> :
56
+ CommandT extends SetUserPermissions ? CmdResult<Permissions> :
57
+ CommandT extends GetUserPermissions ? CmdResult<Permissions> :
58
+ CommandT extends GetComputedPermissions ? CmdResult<Permissions> :
59
+ // Space commands
60
+ CommandT extends JoinSpace ? CmdResult<SpaceJoined> :
61
+ CommandT extends LeaveSpace ? CmdResult<SpaceLeft> :
62
+ CommandT extends CreateSpace ? CmdResult<SpaceJoined> :
63
+ CommandT extends DeleteSpace ? CmdResult<SpaceDeleted> :
64
+ CommandT extends GetSpaceMembers ? CmdResult<SpaceMembers> :
65
+ CommandT extends GetSpaceRooms ? CmdResult<SpaceRooms> :
66
+ CommandT extends CreateRole ? CmdResult<NewRole> :
67
+ CommandT extends DeleteRole ? CmdResult<RoleDeleted> :
68
+ CommandT extends AssignRole ? CmdResult<SpaceMemberUpdate> :
69
+ CommandT extends DeassignRole ? CmdResult<SpaceMemberUpdate> :
70
+ CommandT extends SetRolePermissions ? CmdResult<Permissions> :
71
+ CommandT extends GetRolePermissions ? CmdResult<Permissions> :
72
+ // Room commands
73
+ CommandT extends JoinRoom ? CmdResult<RoomJoined> :
74
+ CommandT extends LeaveRoom ? CmdResult<RoomLeft> :
75
+ CommandT extends CreateRoom ? CmdResult<RoomJoined> :
76
+ CommandT extends DeleteRoom ? CmdResult<RoomDeleted> :
77
+ CommandT extends GetRoomMembers ? CmdResult<RoomMembers> :
78
+ // Topic commands
79
+ CommandT extends CreateTopic ? CmdResult<NewTopic> :
80
+ CommandT extends DeleteTopic ? CmdResult<TopicDeleted> :
81
+ CommandT extends CreateMessage ? CmdResult<NewMessage> :
82
+ any;
83
+
84
+ function guessCommandType(obj: any): string {
85
+ for (const type in commands) {
86
+ if (obj instanceof commands[type]) {
87
+ return type;
88
+ }
89
+ }
90
+ return Object.getPrototypeOf(obj).constructor.name;
91
+ }
92
+
93
+ export enum ClientEvent {
94
+ message = 'message',
95
+ renewal = 'renewalStart',
96
+ renewalSuccess = 'renewalSuccess',
97
+ renewalError = 'renewalError',
98
+ }
99
+
100
+ export interface TokenInterface {
101
+ token: string,
102
+ expiration: string
103
+ }
104
+
105
+ export interface MyAccountInterface {
106
+ id: number;
107
+ nick: string;
108
+ avatar: string;
109
+ }
110
+
111
+ export class Client extends EventTarget {
112
+ public static readonly defaultClientName = 'polfan-server-js-client';
113
+ public static readonly defaultWebSocketUrl = 'ws://pserv.shado.p5.tiktalik.io:1600/ws';
114
+ public static readonly defaultWebApiUrl = 'http://pserv.shado.p5.tiktalik.io:1600/api';
115
+ public static readonly defaultRestApiUrl = 'https://polfan.pl/webservice/api';
116
+ public static readonly defaultAvatarUrlPrefix = 'https://polfan.pl/modules/users/avatars/';
117
+
118
+ public static async getToken(
119
+ login: string,
120
+ password: string,
121
+ clientName: string = Client.defaultClientName
122
+ ): Promise<TokenInterface> {
123
+ const connection = new RestApiConnection({url: Client.defaultRestApiUrl});
124
+ const response = await connection.send('POST', 'auth/tokens', {
125
+ login, password, client_name: clientName
126
+ });
127
+ if (response.ok) {
128
+ return response.data;
129
+ }
130
+ throw new Error(`Cannot create user token: ${response.data.errors[0]}`);
131
+ }
132
+
133
+ public static createByToken(token: string): Client {
134
+ return new Client(
135
+ new WebSocketConnection({token, url: Client.defaultWebSocketUrl}),
136
+ new RestApiConnection({token, url: Client.defaultRestApiUrl}),
137
+ );
138
+ }
139
+
140
+ public static createByTemporaryNick(temporaryNick: string): Client {
141
+ return new Client(
142
+ new WebSocketConnection({temporaryNick, url: Client.defaultWebSocketUrl}),
143
+ new RestApiConnection({url: Client.defaultRestApiUrl}),
144
+ );
145
+ }
146
+
147
+ protected commandsCount = 0;
148
+ protected awaitingResponse: Map<string, ArrayOfPromiseResolvers> = new Map<string, ArrayOfPromiseResolvers>();
149
+ protected eventsMap: {[x: string]: typeof Dto};
150
+
151
+ public constructor(
152
+ public readonly chatConnection: ChatConnectionInterface,
153
+ public readonly restConnection: RestApiConnection,
154
+ ) {
155
+ super();
156
+ this.setCustomEventMap({}); // Set default event map.
157
+ this.chatConnection.on(ChatConnectionEvent.message, (payload: any) => this.onMessage(payload));
158
+ this.chatConnection.on(ChatConnectionEvent.destroy, (reconnect: boolean) => this.onDisconnect(reconnect));
159
+ }
160
+
161
+ /**
162
+ * Send command to chat server.
163
+ * @param commandPayload Command payload object.
164
+ * @param commandType Command type; if not specified, it will be guessed from the command payload class name.
165
+ * @return Promise which resolves to the event returned by server (including `Error`)
166
+ * in response to command and rejects with connection error.
167
+ */
168
+ public async sendCommand<CommandT extends Dto = any>(
169
+ commandPayload: CommandT,
170
+ commandType?: string
171
+ ): Promise<CommandsToEventsType<CommandT>> {
172
+ const message = this.createEnvelope(commandType ?? guessCommandType(commandPayload), commandPayload);
173
+ this.chatConnection.send(message.toRaw());
174
+ return new Promise(
175
+ (...args) => this.awaitingResponse.set(message.ref as string, args)
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Set custom DTO classes for events.
181
+ */
182
+ public setCustomEventMap(customMap: {[x: string]: typeof Dto}): this {
183
+ this.eventsMap = {...events, ...customMap};
184
+ return this;
185
+ }
186
+
187
+ public destroy(): this {
188
+ this.chatConnection.destroy();
189
+ return this;
190
+ }
191
+
192
+ public async getMe(): Promise<MyAccountInterface> {
193
+ const response = await this.restConnection.send('GET', 'auth/me');
194
+ if (response.ok) {
195
+ return response.data;
196
+ }
197
+ throw new Error(`Cannot get current user account: ${response.data.errors[0]}`);
198
+ }
199
+
200
+ private onMessage(message: any) {
201
+ const dto = this.createEvent(message);
202
+
203
+ const [resolve] = this.awaitingResponse.get(message.ref ?? '') ?? [];
204
+ if (resolve) {
205
+ resolve(dto ?? message.data);
206
+ this.awaitingResponse.delete(message.ref as string);
207
+ }
208
+
209
+ this.emit(ClientEvent.message, message);
210
+ this.emit(message.type ?? 'unknown', message, dto);
211
+ }
212
+
213
+ private onDisconnect(reconnect: boolean): void {
214
+ this.awaitingResponse.forEach(([resolve, reject], key: string) => {
215
+ reject('Disconnected');
216
+ this.awaitingResponse.delete(key);
217
+ });
218
+ if (reconnect) {
219
+ this.emit(ClientEvent.renewal);
220
+ this.chatConnection.once(ChatConnectionEvent.ready, () => this.emit(ClientEvent.renewalSuccess));
221
+ this.chatConnection.once(ChatConnectionEvent.error, () => this.emit(ClientEvent.renewalError));
222
+ }
223
+ }
224
+
225
+ private createEnvelope(commandType: string, dto: Dto): Envelope {
226
+ return new Envelope({
227
+ type: commandType,
228
+ ref: (++this.commandsCount).toString(),
229
+ data: dto
230
+ });
231
+ }
232
+
233
+ private createEvent(message: Envelope): Dto | null {
234
+ if ((message.type ?? false) && this.eventsMap.hasOwnProperty(message.type)) {
235
+ return new (this.eventsMap[message.type] as any)(message.data);
236
+ }
237
+ return null;
238
+ }
239
+ }
@@ -0,0 +1,39 @@
1
+ export type EventHandler<T = any> = (...args: T[]) => void;
2
+ type HandlersMap = Map<string, EventHandler[]>;
3
+
4
+ export interface ObservableInterface {
5
+ on(eventName: string, handler: EventHandler): ObservableInterface;
6
+ once(eventName: string, handler: EventHandler): ObservableInterface;
7
+ }
8
+
9
+ export abstract class EventTarget implements ObservableInterface {
10
+ protected events: HandlersMap = new Map<string, EventHandler[]>();
11
+ protected onceEvents: HandlersMap = new Map<string, EventHandler[]>();
12
+
13
+ public on(eventName: string, handler: EventHandler): EventTarget {
14
+ this.addHandler(this.events, eventName, handler);
15
+ return this;
16
+ }
17
+
18
+ public once(eventName: string, handler: EventHandler): EventTarget {
19
+ this.addHandler(this.onceEvents, eventName, handler);
20
+ return this;
21
+ }
22
+
23
+ public emit(eventName: string, ...args: any): EventTarget {
24
+ this.callHandlers(this.events, eventName, args);
25
+ this.callHandlers(this.onceEvents, eventName, args);
26
+ this.onceEvents.delete(eventName);
27
+ return this;
28
+ }
29
+
30
+ private addHandler(map: HandlersMap, eventName: string, handler: EventHandler): void {
31
+ const handlers = map.get(eventName) ?? [];
32
+ handlers.push(handler);
33
+ map.set(eventName, handlers);
34
+ }
35
+
36
+ private callHandlers(map: HandlersMap, eventName: string, args: any[]): void {
37
+ map.get(eventName)?.forEach(callback => callback(...args));
38
+ }
39
+ }
@@ -0,0 +1,19 @@
1
+ import {ObservableInterface} from "../ObservableInterface";
2
+
3
+ export enum ChatConnectionEvent {
4
+ message = 'message',
5
+ destroy = 'destroy',
6
+ ready = 'ready',
7
+ error = 'error',
8
+ }
9
+
10
+ export interface ConnectionOptionsInterface {
11
+ url: string;
12
+ token?: string;
13
+ temporaryNick?: string;
14
+ }
15
+
16
+ export interface ChatConnectionInterface extends ObservableInterface {
17
+ send(data: any): void;
18
+ destroy(): void;
19
+ }
@@ -0,0 +1,47 @@
1
+ import {ConnectionOptionsInterface} from "./ConnectionAssets";
2
+
3
+ export interface RestResponseInterface {
4
+ ok: boolean;
5
+ status: number;
6
+ data: any;
7
+ }
8
+
9
+ export class RestApiConnection {
10
+ public constructor(protected options: ConnectionOptionsInterface) {
11
+ if (this.options.temporaryNick) {
12
+ throw new Error('Temporary nick is not supported authentication method for REST API');
13
+ }
14
+ }
15
+
16
+ public async send(method: string, uri: string, data: any = undefined): Promise<RestResponseInterface> {
17
+ const headers: any = {
18
+ 'Content-Type': 'application/json',
19
+ Accept: 'application/json'
20
+ };
21
+
22
+ if (this.options.token) {
23
+ headers.Authorization = `Bearer ${this.options.token}`;
24
+ }
25
+
26
+ const body = data === undefined ? undefined : JSON.stringify(data);
27
+ const result = await fetch(this.getUrl(uri), {method, body, headers});
28
+
29
+ return {
30
+ ok: result.ok,
31
+ status: result.status,
32
+ data: await result.json(),
33
+ };
34
+ }
35
+
36
+ protected getUrl(uri: string): string {
37
+ return this.removeEndingSlash(this.options.url) + '/' + this.removeStartingSlash(uri);
38
+ }
39
+
40
+ private removeStartingSlash(text: string): string {
41
+ return text.replace(/^\/+/, '');
42
+ }
43
+
44
+ private removeEndingSlash(text: string): string {
45
+ return text.replace(/\/+$/, '');
46
+ }
47
+ }
@@ -0,0 +1,78 @@
1
+ import {ChatConnectionInterface, ChatConnectionEvent, ConnectionOptionsInterface} from "./ConnectionAssets";
2
+ import {EventTarget} from "../ObservableInterface";
3
+
4
+ export interface WebApiConnectionOptionsInterface extends ConnectionOptionsInterface {
5
+ attemptsToSend?: number;
6
+ attemptDelayMs?: number;
7
+ }
8
+
9
+ export class WebApiConnection extends EventTarget implements ChatConnectionInterface {
10
+ protected sendStack: {data: any, attempts: number, lastTimeoutId: any}[];
11
+
12
+ public constructor(
13
+ protected readonly options: WebApiConnectionOptionsInterface
14
+ ) {
15
+ super();
16
+ if (!this.options.token && !this.options.temporaryNick) {
17
+ throw new Error('Token or temporary nick is required for authentication');
18
+ }
19
+ }
20
+
21
+ public send(data: any): void {
22
+ this.sendStack.push({data: data, attempts: 0, lastTimeoutId: null});
23
+ this.makeApiCall(this.sendStack.length - 1);
24
+ }
25
+
26
+ public destroy(): void {
27
+ // Cancel all awaiting requests
28
+ this.sendStack.forEach(item => {
29
+ if (item.lastTimeoutId) {
30
+ clearTimeout(item.lastTimeoutId);
31
+ }
32
+ });
33
+ this.sendStack = [];
34
+ this.emit(ChatConnectionEvent.destroy, false);
35
+ }
36
+
37
+ protected async onMessage(reqId: number, response: Response): Promise<void> {
38
+ this.sendStack.splice(reqId, 1);
39
+ this.emit(ChatConnectionEvent.message, await response.json());
40
+ }
41
+
42
+ protected onError(reqId: number, body: string): void {
43
+ if (this.sendStack[reqId].attempts >= (this.options.attemptsToSend ?? 10)) {
44
+ this.sendStack.splice(reqId, 1);
45
+ this.emit(ChatConnectionEvent.error, new Error(
46
+ `Cannot send ${body}; aborting after reaching the maximum connection errors`)
47
+ );
48
+ return;
49
+ }
50
+ this.sendStack[reqId].lastTimeoutId = setTimeout(
51
+ () => this.makeApiCall(reqId),
52
+ this.options.attemptDelayMs ?? 3000
53
+ );
54
+ }
55
+
56
+ protected makeApiCall(reqId: number): void {
57
+ this.sendStack[reqId].attempts++;
58
+ const bodyJson = JSON.stringify(this.sendStack[reqId].data);
59
+ const headers: any = {
60
+ 'Content-Type': 'application/json',
61
+ Accept: 'application/json'
62
+ };
63
+
64
+ if (this.options.token) {
65
+ headers.Authorization = `Bearer ${this.options.token}`;
66
+ } else if (this.options.temporaryNick) {
67
+ headers.Authorization = `Temp ${this.options.temporaryNick}`;
68
+ }
69
+
70
+ fetch(this.options.url, {
71
+ headers,
72
+ body: bodyJson,
73
+ method: 'POST',
74
+ })
75
+ .then(response => this.onMessage(reqId, response))
76
+ .catch(() => this.onError(reqId, bodyJson));
77
+ }
78
+ }
@@ -0,0 +1,95 @@
1
+ import {EventTarget} from "../ObservableInterface";
2
+ import {
3
+ ChatConnectionEvent,
4
+ ChatConnectionInterface,
5
+ ConnectionOptionsInterface,
6
+ } from "./ConnectionAssets";
7
+
8
+ export interface WebSocketConnectionInterface extends ConnectionOptionsInterface {
9
+ awaitQueueSendDelayMs?: number;
10
+ connectingTimeoutMs?: number;
11
+ }
12
+
13
+ export class WebSocketConnection extends EventTarget implements ChatConnectionInterface {
14
+ protected ws: WebSocket|null = null;
15
+ protected sendQueue: any[] = [];
16
+ protected connectingTimeoutId: any;
17
+ protected authenticated: boolean;
18
+
19
+ public constructor(private readonly options: WebSocketConnectionInterface) {
20
+ super();
21
+ if (!this.options.token && !this.options.temporaryNick) {
22
+ throw new Error('Token or temporary nick is required');
23
+ }
24
+ }
25
+
26
+ public send(data: any): void {
27
+ if (!this.ws || this.ws.readyState === this.ws.CLOSED) {
28
+ this.connect();
29
+ }
30
+ if (this.ws.readyState === this.ws.CONNECTING || !this.authenticated) {
31
+ this.sendQueue.push(data);
32
+ return;
33
+ }
34
+ const dataJson = JSON.stringify(data);
35
+ if (this.ws.readyState !== this.ws.OPEN) {
36
+ this.emit(ChatConnectionEvent.error, new Error(`Cannot send ${dataJson}; invalid connection state`));
37
+ return;
38
+ }
39
+ this.ws.send(dataJson);
40
+ }
41
+
42
+ public destroy(): void {
43
+ this.sendQueue = [];
44
+ this.ws?.close();
45
+ }
46
+
47
+ private connect(): void {
48
+ this.authenticated = false;
49
+ const authString = this.options.token ? `token=${this.options.token}` : `nick=${this.options.temporaryNick}`;
50
+ this.ws = new WebSocket(`${this.options.url}?${authString}`);
51
+ this.ws.onclose = ev => this.onClose(ev);
52
+ this.ws.onmessage = ev => this.onMessage(ev);
53
+ this.connectingTimeoutId = setTimeout(
54
+ () => this.triggerConnectionTimeout(),
55
+ this.options.connectingTimeoutMs ?? 10000
56
+ );
57
+ }
58
+
59
+ private onMessage(event: MessageEvent): void {
60
+ const payload = JSON.parse(event.data);
61
+ // Login successfully
62
+ if (!this.authenticated && payload.type && payload.type !== 'Error') {
63
+ this.authenticated = true;
64
+ this.emit(ChatConnectionEvent.ready);
65
+ this.sendFromQueue();
66
+ }
67
+ this.emit(ChatConnectionEvent.message, payload);
68
+ }
69
+
70
+ private onClose(event: CloseEvent): void {
71
+ clearTimeout(this.connectingTimeoutId);
72
+ const reconnect = event.code !== 1000; // Connection was closed because of error
73
+ if (reconnect) {
74
+ this.connect();
75
+ }
76
+ this.emit(ChatConnectionEvent.destroy, reconnect);
77
+ }
78
+
79
+ private sendFromQueue(): void {
80
+ // Send awaiting data to server
81
+ let lastDelay = 0;
82
+ for (const dataIndex in this.sendQueue) {
83
+ const data = this.sendQueue[dataIndex];
84
+ setTimeout(() => this.send(data), lastDelay);
85
+ lastDelay += this.options.awaitQueueSendDelayMs ?? 500;
86
+ }
87
+ this.sendQueue = [];
88
+ clearTimeout(this.connectingTimeoutId);
89
+ }
90
+
91
+ private triggerConnectionTimeout(): void {
92
+ this.destroy();
93
+ this.emit(ChatConnectionEvent.error, new Error('Connection timeout'));
94
+ }
95
+ }
@@ -0,0 +1,45 @@
1
+ export function cast<T extends typeof Dto>(data: any, dto: T): T|undefined {
2
+ if (data instanceof dto) {
3
+ return data as any;
4
+ }
5
+ return new (dto as any)(data);
6
+ }
7
+
8
+ export function castArray<T extends typeof Dto>(data: any[], dto: T): T[] {
9
+ if (!Array.isArray(data)) throw new Error(`Passed data is not an array of ${dto.name}`);
10
+ return data.map(item => cast(item, dto));
11
+ }
12
+
13
+ type ExcludeMethods<T> = Pick<T, { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]>;
14
+ export type DtoData<T> = { [K in keyof ExcludeMethods<T>]: any };
15
+
16
+ export abstract class Dto {
17
+ public constructor(...args: any[]) {
18
+ }
19
+
20
+ public toJson(overrideBy: any = {}): string {
21
+ return JSON.stringify(this.toRaw(overrideBy));
22
+ }
23
+
24
+ public toRaw(overrideBy: any = {}): DtoData<this> {
25
+ const object: {[key: string]: any} = {};
26
+ Object.keys(this).forEach(key => {
27
+ if (overrideBy.hasOwnProperty(key)) {
28
+ object[key] = overrideBy[key];
29
+ } else if ((this as any)[key] instanceof Dto) {
30
+ object[key] = (this as any)[key].toRaw();
31
+ } else {
32
+ object[key] = (this as any)[key]
33
+ }
34
+ });
35
+ return object as DtoData<this>;
36
+ }
37
+
38
+ public clone(overrideBy: any = {}): this {
39
+ return new (this.constructor as any)(this.toRaw(overrideBy));
40
+ }
41
+
42
+ protected fill(data: any, overrideBy: any = {}) {
43
+ Object.assign(this, {...data, ...overrideBy});
44
+ }
45
+ }
@@ -0,0 +1,16 @@
1
+ import {cast, Dto, DtoData} from "./Dto";
2
+ import {User} from "./User";
3
+
4
+ export class Message extends Dto {
5
+ public readonly id: string;
6
+ public readonly author: User;
7
+ public readonly topicId: string;
8
+ public readonly content: string;
9
+
10
+ public constructor(data: DtoData<Message>) {
11
+ super();
12
+ this.fill(data, {
13
+ author: cast(data.author, User),
14
+ });
15
+ }
16
+ }
@@ -0,0 +1,12 @@
1
+ import {Dto, DtoData} from "./Dto";
2
+
3
+ export class Permission extends Dto {
4
+ public readonly name: string;
5
+ public readonly value: boolean;
6
+ public readonly skip: boolean;
7
+
8
+ public constructor(data: DtoData<Permission>) {
9
+ super(data);
10
+ this.fill(data);
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ import {Dto, DtoData} from "./Dto";
2
+
3
+ export class Role extends Dto {
4
+ public readonly id: string;
5
+ public readonly name: string;
6
+ public readonly color?: string;
7
+
8
+ public constructor(data: DtoData<Role>) {
9
+ super();
10
+ this.fill(data);
11
+ }
12
+ }