vortez 5.0.2 → 6.0.0-dev.1

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 (143) hide show
  1. package/build/Template/Template.d.ts +0 -5
  2. package/build/Template/Template.js +5 -5
  3. package/build/Template/Template.js.map +1 -1
  4. package/build/Vortez.d.ts +2 -2
  5. package/build/Vortez.js +2 -2
  6. package/build/Vortez.js.map +1 -1
  7. package/build/beta/Mail.d.ts +2 -8
  8. package/build/beta/Mail.js +7 -2
  9. package/build/beta/Mail.js.map +1 -1
  10. package/build/server/Cookie.d.ts +9 -5
  11. package/build/server/Cookie.js +28 -24
  12. package/build/server/Cookie.js.map +1 -1
  13. package/build/server/LoggerManager.d.ts +1 -1
  14. package/build/server/LoggerManager.js +5 -5
  15. package/build/server/LoggerManager.js.map +1 -1
  16. package/build/server/Response.js +11 -11
  17. package/build/server/Response.js.map +1 -1
  18. package/build/server/Server.js +4 -4
  19. package/build/server/Server.js.map +1 -1
  20. package/build/server/ServerDebug.d.ts +1 -1
  21. package/build/server/ServerDebug.js +1 -1
  22. package/build/server/ServerDebug.js.map +1 -1
  23. package/build/server/config/Config.d.ts +7 -7
  24. package/build/server/config/Config.js +4 -3
  25. package/build/server/config/Config.js.map +1 -1
  26. package/build/server/config/Loader.js +5 -5
  27. package/build/server/config/Loader.js.map +1 -1
  28. package/build/server/router/HttpRule.d.ts +2 -2
  29. package/build/server/router/HttpRule.js +1 -1
  30. package/build/server/router/HttpRule.js.map +1 -1
  31. package/build/server/router/Router.d.ts +1 -1
  32. package/build/server/router/Router.js +5 -5
  33. package/build/server/router/Router.js.map +1 -1
  34. package/build/server/router/Rule.d.ts +1 -1
  35. package/build/server/router/WsRule.d.ts +2 -2
  36. package/build/server/router/WsRule.js +2 -2
  37. package/build/server/router/WsRule.js.map +1 -1
  38. package/build/server/router/algorithm/Algorithm.d.ts +1 -1
  39. package/build/server/router/algorithm/Algorithm.js +2 -2
  40. package/build/server/router/algorithm/Algorithm.js.map +1 -1
  41. package/build/server/router/algorithm/FIFO.d.ts +1 -1
  42. package/build/server/router/algorithm/FIFO.js +2 -2
  43. package/build/server/router/algorithm/FIFO.js.map +1 -1
  44. package/build/server/router/algorithm/Tree.d.ts +1 -1
  45. package/build/server/router/algorithm/Tree.js.map +1 -1
  46. package/build/server/router/middleware/Middleware.d.ts +5 -5
  47. package/build/server/router/middleware/WsMiddleware.d.ts +4 -4
  48. package/build/server/router/middleware/WsMiddleware.js +24 -23
  49. package/build/server/router/middleware/WsMiddleware.js.map +1 -1
  50. package/build/server/security/PathSecurity.js +5 -5
  51. package/build/server/security/PathSecurity.js.map +1 -1
  52. package/build/server/websocket/Codec.d.ts +128 -0
  53. package/build/server/websocket/Codec.js +192 -0
  54. package/build/server/websocket/Codec.js.map +1 -0
  55. package/build/server/websocket/Websocket.d.ts +38 -84
  56. package/build/server/websocket/Websocket.js +41 -221
  57. package/build/server/websocket/Websocket.js.map +1 -1
  58. package/build/server/websocket/WebsocketBase.d.ts +130 -0
  59. package/build/server/websocket/WebsocketBase.js +272 -0
  60. package/build/server/websocket/WebsocketBase.js.map +1 -0
  61. package/build/server/websocket/WebsocketCSInit.d.ts +57 -0
  62. package/build/server/websocket/WebsocketCSInit.js +95 -0
  63. package/build/server/websocket/WebsocketCSInit.js.map +1 -0
  64. package/build/server/websocket/WebsocketSSInit.d.ts +24 -0
  65. package/build/server/websocket/WebsocketSSInit.js +46 -0
  66. package/build/server/websocket/WebsocketSSInit.js.map +1 -0
  67. package/build/server/websocket/frame/Frame.d.ts +31 -0
  68. package/build/server/websocket/frame/Frame.js +45 -0
  69. package/build/server/websocket/frame/Frame.js.map +1 -0
  70. package/build/server/websocket/frame/Header.d.ts +31 -0
  71. package/build/server/websocket/frame/Header.js +31 -0
  72. package/build/server/websocket/frame/Header.js.map +1 -0
  73. package/build/server/websocket/frame/HeaderParser.d.ts +58 -0
  74. package/build/server/websocket/frame/HeaderParser.js +109 -0
  75. package/build/server/websocket/frame/HeaderParser.js.map +1 -0
  76. package/build/server/websocket/handshake/CSHandshaker.d.ts +46 -0
  77. package/build/server/websocket/handshake/CSHandshaker.js +122 -0
  78. package/build/server/websocket/handshake/CSHandshaker.js.map +1 -0
  79. package/build/server/websocket/handshake/SSHandshaker.d.ts +57 -0
  80. package/build/server/websocket/handshake/SSHandshaker.js +113 -0
  81. package/build/server/websocket/handshake/SSHandshaker.js.map +1 -0
  82. package/build/server/websocket/messageAssembler/Message.d.ts +21 -0
  83. package/build/server/websocket/messageAssembler/Message.js +20 -0
  84. package/build/server/websocket/messageAssembler/Message.js.map +1 -0
  85. package/build/server/websocket/messageAssembler/MessageAssembler.d.ts +25 -0
  86. package/build/server/websocket/messageAssembler/MessageAssembler.js +47 -0
  87. package/build/server/websocket/messageAssembler/MessageAssembler.js.map +1 -0
  88. package/changes.md +28 -0
  89. package/package.json +6 -1
  90. package/build/logger/Debug.d.ts +0 -174
  91. package/build/logger/Debug.js +0 -295
  92. package/build/logger/Debug.js.map +0 -1
  93. package/build/logger/Logger.d.ts +0 -38
  94. package/build/logger/Logger.js +0 -48
  95. package/build/logger/Logger.js.map +0 -1
  96. package/build/server/websocket/Chunk.d.ts +0 -36
  97. package/build/server/websocket/Chunk.js +0 -81
  98. package/build/server/websocket/Chunk.js.map +0 -1
  99. package/build/utilities/ConsoleUI.d.ts +0 -89
  100. package/build/utilities/ConsoleUI.js +0 -142
  101. package/build/utilities/ConsoleUI.js.map +0 -1
  102. package/build/utilities/DebugUI.d.ts +0 -66
  103. package/build/utilities/DebugUI.js +0 -98
  104. package/build/utilities/DebugUI.js.map +0 -1
  105. package/build/utilities/Encoding.d.ts +0 -22
  106. package/build/utilities/Encoding.js +0 -26
  107. package/build/utilities/Encoding.js.map +0 -1
  108. package/build/utilities/Env.d.ts +0 -81
  109. package/build/utilities/Env.js +0 -140
  110. package/build/utilities/Env.js.map +0 -1
  111. package/build/utilities/File.d.ts +0 -10
  112. package/build/utilities/File.js +0 -19
  113. package/build/utilities/File.js.map +0 -1
  114. package/build/utilities/Flatten.d.ts +0 -73
  115. package/build/utilities/Flatten.js +0 -76
  116. package/build/utilities/Flatten.js.map +0 -1
  117. package/build/utilities/Object.d.ts +0 -16
  118. package/build/utilities/Object.js +0 -48
  119. package/build/utilities/Object.js.map +0 -1
  120. package/build/utilities/Path.d.ts +0 -44
  121. package/build/utilities/Path.js +0 -69
  122. package/build/utilities/Path.js.map +0 -1
  123. package/build/utilities/Time.d.ts +0 -21
  124. package/build/utilities/Time.js +0 -25
  125. package/build/utilities/Time.js.map +0 -1
  126. package/build/utilities/Utilities.d.ts +0 -118
  127. package/build/utilities/Utilities.js +0 -112
  128. package/build/utilities/Utilities.js.map +0 -1
  129. package/build/utilities/schema/Introspection.d.ts +0 -24
  130. package/build/utilities/schema/Introspection.js +0 -87
  131. package/build/utilities/schema/Introspection.js.map +0 -1
  132. package/build/utilities/schema/JSONSchema.d.ts +0 -68
  133. package/build/utilities/schema/JSONSchema.js +0 -13
  134. package/build/utilities/schema/JSONSchema.js.map +0 -1
  135. package/build/utilities/schema/Schema.d.ts +0 -253
  136. package/build/utilities/schema/Schema.js +0 -241
  137. package/build/utilities/schema/Schema.js.map +0 -1
  138. package/build/utilities/schema/SchemaError.d.ts +0 -10
  139. package/build/utilities/schema/SchemaError.js +0 -13
  140. package/build/utilities/schema/SchemaError.js.map +0 -1
  141. package/build/utilities/schema/Validator.d.ts +0 -94
  142. package/build/utilities/schema/Validator.js +0 -246
  143. package/build/utilities/schema/Validator.js.map +0 -1
@@ -0,0 +1,31 @@
1
+ export declare class Header implements Header.Header {
2
+ fin: boolean;
3
+ rsv: Header.Rsv;
4
+ opcode: number;
5
+ mask: boolean;
6
+ size: number | bigint;
7
+ maskKeys: Buffer | null;
8
+ headerSize: number;
9
+ constructor(data: Header.Header);
10
+ /**
11
+ * Reads the first complete frame header from a buffer and returns it.
12
+ * @throws `RangeError` If the buffer does not contain a complete frame header.
13
+ *
14
+ * Note: This method does not validate the frame header beyond checking for completeness.
15
+ * It assumes that the buffer starts with a valid frame header.
16
+ */
17
+ static fromBuffer(buffer: Buffer): Header;
18
+ }
19
+ export declare namespace Header {
20
+ type Rsv = [rsv1: boolean, rsv2: boolean, rsv3: boolean];
21
+ interface Header {
22
+ fin: boolean;
23
+ rsv: Rsv;
24
+ opcode: number;
25
+ mask: boolean;
26
+ size: number | bigint;
27
+ maskKeys: Buffer | null;
28
+ headerSize: number;
29
+ }
30
+ }
31
+ export default Header;
@@ -0,0 +1,31 @@
1
+ import HeaderParser from "./HeaderParser.js";
2
+ export class Header {
3
+ fin;
4
+ rsv;
5
+ opcode;
6
+ mask;
7
+ size;
8
+ maskKeys;
9
+ headerSize;
10
+ constructor(data) {
11
+ this.fin = data.fin;
12
+ this.rsv = data.rsv;
13
+ this.opcode = data.opcode;
14
+ this.mask = data.mask;
15
+ this.size = data.size;
16
+ this.maskKeys = data.maskKeys;
17
+ this.headerSize = data.headerSize;
18
+ }
19
+ /**
20
+ * Reads the first complete frame header from a buffer and returns it.
21
+ * @throws `RangeError` If the buffer does not contain a complete frame header.
22
+ *
23
+ * Note: This method does not validate the frame header beyond checking for completeness.
24
+ * It assumes that the buffer starts with a valid frame header.
25
+ */
26
+ static fromBuffer(buffer) {
27
+ return HeaderParser.header(buffer);
28
+ }
29
+ }
30
+ export default Header;
31
+ //# sourceMappingURL=Header.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Header.js","sourceRoot":"","sources":["../../../../src/server/websocket/frame/Header.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAE7C,MAAM,OAAO,MAAM;IACR,GAAG,CAAU;IACb,GAAG,CAAa;IAChB,MAAM,CAAS;IACf,IAAI,CAAU;IACd,IAAI,CAAkB;IACtB,QAAQ,CAAgB;IACxB,UAAU,CAAS;IAC1B,YAAmB,IAAmB;QAClC,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC9B,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IACtC,CAAC;IACD;;;;;;OAMG;IACI,MAAM,CAAC,UAAU,CAAC,MAAc;QACnC,OAAO,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;CACJ;AAaD,eAAe,MAAM,CAAC"}
@@ -0,0 +1,58 @@
1
+ import Header from "./Header.js";
2
+ export declare class HeaderParser {
3
+ /**
4
+ * Reads the first complete frame header from a buffer and returns it.
5
+ * @throws `RangeError` If the buffer does not contain a complete frame header.
6
+ *
7
+ * Note: This method does not validate the frame header beyond checking for completeness.
8
+ * It assumes that the buffer starts with a valid frame header.
9
+ */
10
+ static header(buffer: Buffer): Header;
11
+ /**
12
+ * Extracts the FIN bit from the first byte of the frame header.
13
+ *
14
+ * @param buffer - The buffer containing the frame header (must be at least 1 byte long).
15
+ * @returns - `true` if the FIN bit is set, otherwise `false`.
16
+ */
17
+ static fin(buffer: Buffer): boolean;
18
+ static rsv(buffer: Buffer): Header.Rsv;
19
+ /**
20
+ * Extracts the OpCode from the first byte of the frame header.
21
+ *
22
+ * @param buffer - The buffer containing the frame header (must be at least 1 byte long).
23
+ * @returns - The OpCode as a number (0-15).
24
+ *
25
+ * @remarks The OpCode is located in the least significant 4 bits of the first byte of the frame header.
26
+ */
27
+ static opCode(buffer: Buffer): number;
28
+ /**
29
+ * Extracts the Mask bit from the second byte of the frame header.
30
+ *
31
+ * @param buffer - The buffer containing the frame header (must be at least 2 bytes long).
32
+ * @returns - `true` if the Mask bit is set, otherwise `false`.
33
+ *
34
+ * @remarks The Mask bit is located in the most significant bit (bit 7) of the second byte of the frame header.
35
+ */
36
+ static mask(buffer: Buffer): boolean;
37
+ /**
38
+ * Extracts the Payload Length from the second byte of the frame header.
39
+ *
40
+ * @param buffer - The buffer containing the frame header (must be at least 2 bytes long).
41
+ * @returns - The Payload Length as a number (0-125) or bigint (for extended payload lengths).
42
+ */
43
+ static payloadLen(buffer: Buffer): number | bigint;
44
+ /**
45
+ * Extracts the Masking Keys from the frame header if the Mask bit is set.
46
+ *
47
+ * @param buffer - The buffer containing the frame header (must be long enough to contain the masking keys if the Mask bit is set).
48
+ * @param mask - A boolean indicating whether the Mask bit is set.
49
+ * @param offset - The byte offset in the buffer where the masking keys start (typically 2 for non-extended payloads, 4 for 16-bit extended payloads, and 10 for 64-bit extended payloads).
50
+ * @returns - A Buffer containing the 4 bytes of masking keys if the Mask bit is set, otherwise `null`.
51
+ *
52
+ * @remarks The masking keys are located immediately after the payload length field in the frame header if the Mask bit is set. The offset parameter should be calculated based on the presence of extended payload length fields.
53
+ * For example, if the payload length is 126 (indicating a 16-bit extended payload length), the masking keys would start at byte offset 4. If the payload length is 127 (indicating a 64-bit extended payload length), the masking keys would start at byte offset 10.
54
+ */
55
+ static maskKeys(buffer: Buffer, mask: boolean, offset: number): Buffer | null;
56
+ }
57
+ export declare namespace HeaderParser { }
58
+ export default HeaderParser;
@@ -0,0 +1,109 @@
1
+ import Header from "./Header.js";
2
+ export class HeaderParser {
3
+ /**
4
+ * Reads the first complete frame header from a buffer and returns it.
5
+ * @throws `RangeError` If the buffer does not contain a complete frame header.
6
+ *
7
+ * Note: This method does not validate the frame header beyond checking for completeness.
8
+ * It assumes that the buffer starts with a valid frame header.
9
+ */
10
+ static header(buffer) {
11
+ if (buffer.length < 2)
12
+ throw new RangeError('Frame header requires at least 2 bytes');
13
+ const fin = this.fin(buffer);
14
+ const rsv = this.rsv(buffer);
15
+ const opCode = this.opCode(buffer);
16
+ const mask = this.mask(buffer);
17
+ let size = this.payloadLen(buffer);
18
+ let maskKeys = null;
19
+ let headerSize = 2;
20
+ if (size === 0x7e) {
21
+ if (buffer.length < 4)
22
+ throw new RangeError('Frame requires 2 bytes for extended size');
23
+ size = buffer.readUint16BE(2);
24
+ headerSize = mask ? 8 : 4;
25
+ if (buffer.length < headerSize)
26
+ throw new RangeError('Frame header is incomplete');
27
+ maskKeys = mask ? this.maskKeys(buffer, mask, 4) : null;
28
+ }
29
+ else if (size === 0x7f) {
30
+ if (buffer.length < 10)
31
+ throw new RangeError('Frame requires 8 bytes for extended size');
32
+ size = buffer.readBigUint64BE(2);
33
+ headerSize = mask ? 14 : 10;
34
+ if (buffer.length < headerSize)
35
+ throw new RangeError('Frame header is incomplete');
36
+ maskKeys = mask ? this.maskKeys(buffer, mask, 10) : null;
37
+ }
38
+ else {
39
+ headerSize = mask ? 6 : 2;
40
+ if (buffer.length < headerSize)
41
+ throw new RangeError('Frame header is incomplete');
42
+ maskKeys = mask ? this.maskKeys(buffer, mask, 2) : null;
43
+ }
44
+ return new Header({ fin, rsv, opcode: opCode, mask, size, maskKeys, headerSize });
45
+ }
46
+ /**
47
+ * Extracts the FIN bit from the first byte of the frame header.
48
+ *
49
+ * @param buffer - The buffer containing the frame header (must be at least 1 byte long).
50
+ * @returns - `true` if the FIN bit is set, otherwise `false`.
51
+ */
52
+ static fin(buffer) {
53
+ return Boolean((buffer[0] >>> 0x7) & 0x01);
54
+ }
55
+ static rsv(buffer) {
56
+ return [
57
+ Boolean((buffer[0] >>> 0x6) & 0x01),
58
+ Boolean((buffer[0] >>> 0x5) & 0x01),
59
+ Boolean((buffer[0] >>> 0x4) & 0x01)
60
+ ];
61
+ }
62
+ /**
63
+ * Extracts the OpCode from the first byte of the frame header.
64
+ *
65
+ * @param buffer - The buffer containing the frame header (must be at least 1 byte long).
66
+ * @returns - The OpCode as a number (0-15).
67
+ *
68
+ * @remarks The OpCode is located in the least significant 4 bits of the first byte of the frame header.
69
+ */
70
+ static opCode(buffer) {
71
+ return buffer[0] & 0x0f;
72
+ }
73
+ /**
74
+ * Extracts the Mask bit from the second byte of the frame header.
75
+ *
76
+ * @param buffer - The buffer containing the frame header (must be at least 2 bytes long).
77
+ * @returns - `true` if the Mask bit is set, otherwise `false`.
78
+ *
79
+ * @remarks The Mask bit is located in the most significant bit (bit 7) of the second byte of the frame header.
80
+ */
81
+ static mask(buffer) {
82
+ return Boolean((buffer[1] >>> 0x7) & 0x01);
83
+ }
84
+ /**
85
+ * Extracts the Payload Length from the second byte of the frame header.
86
+ *
87
+ * @param buffer - The buffer containing the frame header (must be at least 2 bytes long).
88
+ * @returns - The Payload Length as a number (0-125) or bigint (for extended payload lengths).
89
+ */
90
+ static payloadLen(buffer) {
91
+ return buffer[1] & 0x7f;
92
+ }
93
+ /**
94
+ * Extracts the Masking Keys from the frame header if the Mask bit is set.
95
+ *
96
+ * @param buffer - The buffer containing the frame header (must be long enough to contain the masking keys if the Mask bit is set).
97
+ * @param mask - A boolean indicating whether the Mask bit is set.
98
+ * @param offset - The byte offset in the buffer where the masking keys start (typically 2 for non-extended payloads, 4 for 16-bit extended payloads, and 10 for 64-bit extended payloads).
99
+ * @returns - A Buffer containing the 4 bytes of masking keys if the Mask bit is set, otherwise `null`.
100
+ *
101
+ * @remarks The masking keys are located immediately after the payload length field in the frame header if the Mask bit is set. The offset parameter should be calculated based on the presence of extended payload length fields.
102
+ * For example, if the payload length is 126 (indicating a 16-bit extended payload length), the masking keys would start at byte offset 4. If the payload length is 127 (indicating a 64-bit extended payload length), the masking keys would start at byte offset 10.
103
+ */
104
+ static maskKeys(buffer, mask, offset) {
105
+ return mask ? buffer.subarray(offset, offset + 4) : null;
106
+ }
107
+ }
108
+ export default HeaderParser;
109
+ //# sourceMappingURL=HeaderParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HeaderParser.js","sourceRoot":"","sources":["../../../../src/server/websocket/frame/HeaderParser.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,MAAM,OAAO,YAAY;IACrB;;;;;;OAMG;IACI,MAAM,CAAC,MAAM,CAAC,MAAc;QAC/B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,wCAAwC,CAAC,CAAC;QACtF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,IAAI,GAAoB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACpD,IAAI,QAAQ,GAAkB,IAAI,CAAC;QACnC,IAAI,UAAU,GAAG,CAAC,CAAC;QAEnB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAChB,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;gBAAE,MAAM,IAAI,UAAU,CAAC,0CAA0C,CAAC,CAAC;YACxF,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC9B,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,MAAM,CAAC,MAAM,GAAG,UAAU;gBAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,CAAC;YACnF,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5D,CAAC;aAAM,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE;gBAAE,MAAM,IAAI,UAAU,CAAC,0CAA0C,CAAC,CAAC;YACzF,IAAI,GAAG,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;YACjC,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5B,IAAI,MAAM,CAAC,MAAM,GAAG,UAAU;gBAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,CAAC;YACnF,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC7D,CAAC;aAAM,CAAC;YACJ,UAAU,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,MAAM,CAAC,MAAM,GAAG,UAAU;gBAAE,MAAM,IAAI,UAAU,CAAC,4BAA4B,CAAC,CAAC;YACnF,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5D,CAAC;QACD,OAAO,IAAI,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC;IACtF,CAAC;IACD;;;;;OAKG;IACI,MAAM,CAAC,GAAG,CAAC,MAAc;QAC5B,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/C,CAAC;IACM,MAAM,CAAC,GAAG,CAAC,MAAc;QAC5B,OAAO;YACH,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC;YACnC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC;YACnC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC;SACtC,CAAC;IACN,CAAC;IACD;;;;;;;OAOG;IACI,MAAM,CAAC,MAAM,CAAC,MAAc;QAC/B,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC5B,CAAC;IACD;;;;;;;OAOG;IACI,MAAM,CAAC,IAAI,CAAC,MAAc;QAC7B,OAAO,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD;;;;;OAKG;IACI,MAAM,CAAC,UAAU,CAAC,MAAc;QACnC,OAAO,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAC5B,CAAC;IACD;;;;;;;;;;QAUI;IACG,MAAM,CAAC,QAAQ,CAAC,MAAc,EAAE,IAAa,EAAE,MAAc;QAChE,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7D,CAAC;CACJ;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { Duplex } from 'stream';
2
+ import { IncomingHttpHeaders } from 'http';
3
+ import { Events } from '@netfeez/common';
4
+ export declare class CSHandShaker extends Events<CSHandShaker.EventMap> {
5
+ private readonly socket;
6
+ private readonly host;
7
+ private readonly path;
8
+ private secWebSocketKey;
9
+ constructor(socket: Duplex, host: string, path?: string);
10
+ /**
11
+ * Initiates the WebSocket handshake process by sending a properly formatted HTTP request to the server and setting up a data handler to process the server's response.
12
+ * This method will emit a 'finish' event with the status of the handshake ('open' if successful, 'closed' if failed) once the server's response has been processed, or an 'error' event if any issues occur during the handshake process.
13
+ *
14
+ * @remarks The handshake process involves sending an HTTP GET request with the necessary headers to request an upgrade to a WebSocket connection. The method also sets up a data handler to listen for the server's response, which will determine whether the handshake was successful based on the status code and headers received. Proper error handling is implemented to ensure that any issues during the handshake are appropriately emitted as events for further handling by the caller.
15
+ */
16
+ start(): void;
17
+ /**
18
+ * Generates an DataHandler function for processing the server's response to the WebSocket handshake request. This handler will parse the HTTP response from the server, extract the status code and headers, and emit a 'response' event with this information for further processing by the caller.
19
+ * @param emitter - An event emitter to emit the 'response' event with the parsed status code and headers.
20
+ * @param parent - The instance of CSHandShaker that will emit any errors encountered during the parsing process.
21
+ * @returns A function that can be used as a data handler for the socket's 'data' event to process the server's handshake response.
22
+ */
23
+ private static getDataHandler;
24
+ /**
25
+ * Generates the HTTP request message for initiating the WebSocket handshake with the server.
26
+ * This method constructs a valid HTTP GET request with the necessary headers to request an upgrade to a WebSocket connection, including a randomly generated Sec-WebSocket-Key and the appropriate Upgrade and Connection headers.
27
+ * @param host - The host and port of the server to which the handshake request will be sent (e.g., "example.com:8080").
28
+ * @param path - The path on the server for the WebSocket endpoint (default is "/").
29
+ */
30
+ private static requestMessage;
31
+ /**
32
+ * Computes the expected Sec-WebSocket-Accept value for a given key.
33
+ */
34
+ private static computeAcceptKey;
35
+ }
36
+ export declare namespace CSHandShaker {
37
+ type Status = 'handshake' | 'open' | 'closed';
38
+ type EventMap = {
39
+ finish: [status: CSHandShaker.Status];
40
+ error: [error: Error];
41
+ };
42
+ type ServerSocketEventMap = {
43
+ response: [status: number, headers: IncomingHttpHeaders];
44
+ };
45
+ }
46
+ export default CSHandShaker;
@@ -0,0 +1,122 @@
1
+ import { createHash, randomBytes } from 'crypto';
2
+ import { Events } from '@netfeez/common';
3
+ export class CSHandShaker extends Events {
4
+ socket;
5
+ host;
6
+ path;
7
+ secWebSocketKey;
8
+ constructor(socket, host, path = '/') {
9
+ super();
10
+ this.socket = socket;
11
+ this.host = host;
12
+ this.path = path;
13
+ this.secWebSocketKey = randomBytes(16).toString('base64');
14
+ }
15
+ /**
16
+ * Initiates the WebSocket handshake process by sending a properly formatted HTTP request to the server and setting up a data handler to process the server's response.
17
+ * This method will emit a 'finish' event with the status of the handshake ('open' if successful, 'closed' if failed) once the server's response has been processed, or an 'error' event if any issues occur during the handshake process.
18
+ *
19
+ * @remarks The handshake process involves sending an HTTP GET request with the necessary headers to request an upgrade to a WebSocket connection. The method also sets up a data handler to listen for the server's response, which will determine whether the handshake was successful based on the status code and headers received. Proper error handling is implemented to ensure that any issues during the handshake are appropriately emitted as events for further handling by the caller.
20
+ */
21
+ start() {
22
+ try {
23
+ const request = CSHandShaker.requestMessage(this.host, this.path, this.secWebSocketKey);
24
+ const emitter = new Events.Emitter();
25
+ const handler = CSHandShaker.getDataHandler(emitter, this);
26
+ this.socket.on('data', handler);
27
+ emitter.once('response', (status, headers) => {
28
+ this.socket.off('data', handler);
29
+ if (status === 101) {
30
+ const accept = headers['sec-websocket-accept'];
31
+ const expected = CSHandShaker.computeAcceptKey(this.secWebSocketKey);
32
+ if (accept !== expected) {
33
+ this.emit('error', new Error('Invalid Sec-WebSocket-Accept header in handshake'));
34
+ setImmediate(() => this.emit('finish', 'closed'));
35
+ return;
36
+ }
37
+ setImmediate(() => this.emit('finish', 'open'));
38
+ }
39
+ else {
40
+ setImmediate(() => this.emit('finish', 'closed'));
41
+ }
42
+ });
43
+ this.socket.write(request);
44
+ }
45
+ catch (error) {
46
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
47
+ }
48
+ }
49
+ /**
50
+ * Generates an DataHandler function for processing the server's response to the WebSocket handshake request. This handler will parse the HTTP response from the server, extract the status code and headers, and emit a 'response' event with this information for further processing by the caller.
51
+ * @param emitter - An event emitter to emit the 'response' event with the parsed status code and headers.
52
+ * @param parent - The instance of CSHandShaker that will emit any errors encountered during the parsing process.
53
+ * @returns A function that can be used as a data handler for the socket's 'data' event to process the server's handshake response.
54
+ */
55
+ static getDataHandler(emitter, parent) {
56
+ let buffer = Buffer.alloc(0);
57
+ return (data) => {
58
+ try {
59
+ buffer = Buffer.concat([buffer, data]);
60
+ const index = buffer.indexOf('\r\n\r\n');
61
+ if (index === -1)
62
+ return;
63
+ const headerBuffer = buffer.subarray(0, index);
64
+ const leftovers = buffer.subarray(index + 4);
65
+ if (leftovers.length > 0) {
66
+ parent.socket.unshift(leftovers);
67
+ }
68
+ const header = headerBuffer.toString('utf-8');
69
+ const headerLines = header.split('\r\n');
70
+ const statusLine = headerLines[0];
71
+ const statusMatch = statusLine.match(/^HTTP\/\d\.\d (\d{3})/);
72
+ if (!statusMatch) {
73
+ emitter.emit('response', 0, {});
74
+ parent.emit('error', new Error('Invalid handshake response from server'));
75
+ return;
76
+ }
77
+ const statusCode = parseInt(statusMatch[1], 10);
78
+ const headers = {};
79
+ for (let i = 1; i < headerLines.length; i++) {
80
+ const line = headerLines[i];
81
+ const [key, value] = line.split(':').map(s => s.trim());
82
+ headers[key.toLowerCase()] = value;
83
+ }
84
+ emitter.emit('response', statusCode, headers);
85
+ }
86
+ catch (error) {
87
+ parent.emit('error', error instanceof Error ? error : new Error(String(error)));
88
+ }
89
+ };
90
+ }
91
+ /**
92
+ * Generates the HTTP request message for initiating the WebSocket handshake with the server.
93
+ * This method constructs a valid HTTP GET request with the necessary headers to request an upgrade to a WebSocket connection, including a randomly generated Sec-WebSocket-Key and the appropriate Upgrade and Connection headers.
94
+ * @param host - The host and port of the server to which the handshake request will be sent (e.g., "example.com:8080").
95
+ * @param path - The path on the server for the WebSocket endpoint (default is "/").
96
+ */
97
+ static requestMessage(host, path = '/', secWebSocketKey) {
98
+ path = encodeURIComponent(path);
99
+ path = path.replace(/%2F/g, '/');
100
+ path = path.replace(/^\/?/, '/');
101
+ return [
102
+ `GET ${path} HTTP/1.1`,
103
+ `Host: ${host}`,
104
+ 'Upgrade: websocket',
105
+ 'Connection: Upgrade',
106
+ `Sec-WebSocket-Key: ${secWebSocketKey}`,
107
+ 'Sec-WebSocket-Version: 13',
108
+ ''
109
+ ].join('\r\n');
110
+ }
111
+ /**
112
+ * Computes the expected Sec-WebSocket-Accept value for a given key.
113
+ */
114
+ static computeAcceptKey(secWebSocketKey) {
115
+ const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
116
+ return createHash('sha1')
117
+ .update(secWebSocketKey + GUID)
118
+ .digest('base64');
119
+ }
120
+ }
121
+ export default CSHandShaker;
122
+ //# sourceMappingURL=CSHandshaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CSHandshaker.js","sourceRoot":"","sources":["../../../../src/server/websocket/handshake/CSHandshaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { Duplex } from 'stream';
2
+ import { Events } from '@netfeez/common';
3
+ import Request from '../../Request.js';
4
+ export declare class SSHandshaker extends Events<SSHandshaker.EventMap> {
5
+ private readonly socket;
6
+ private readonly Request;
7
+ static readonly WEBSOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
8
+ vStatus: SSHandshaker.Status;
9
+ constructor(socket: Duplex, Request: Request);
10
+ /**
11
+ * Accepts the WebSocket handshake by validating the client's request and sending the appropriate HTTP response to establish the WebSocket connection. This method should be called after verifying that the client's handshake request is valid and meets the necessary criteria for accepting the connection.
12
+ * - It generates the Sec-WebSocket-Accept key based on the client's Sec-WebSocket-Key and constructs the HTTP response with the required headers to complete the handshake.
13
+ * - After sending the response, it updates the internal status to 'open' and emits a 'finish' event with the new status.
14
+ *
15
+ * @throws Will throw an error if the client's handshake request does not contain a valid Sec-WebSocket-Key or if any other issue occurs during the acceptance process. The error will be emitted as an 'error' event for handling by the caller.
16
+ */
17
+ accept(): void;
18
+ /**
19
+ * Rejects the WebSocket handshake by sending an HTTP response with the specified status code and reason, and then closes the connection.
20
+ * @param code - The HTTP status code to indicate the reason for rejection (e.g., 400 for Bad Request).
21
+ * @param reason - A human-readable string explaining the reason for rejection.
22
+ *
23
+ * @remarks The generated response will have a JSON body containing the provided code and reason, and will include any specified cookies in the headers. This response can be sent back to the client to indicate that the handshake request was rejected, along with the reason for rejection.
24
+ */
25
+ reject(code?: number, reason?: string): void;
26
+ /**
27
+ * Generates the Sec-WebSocket-Accept key for the server handshake response based on the client's Sec-WebSocket-Key.
28
+ * @param key - The Sec-WebSocket-Key received from the client during the handshake request.
29
+ * @returns The computed Sec-WebSocket-Accept key to be sent in the handshake response.
30
+ */
31
+ static generateAcceptKey(key: string): string;
32
+ /**
33
+ * Generates an HTTP response message to accept a WebSocket handshake request with the appropriate headers.
34
+ * @param key - The Sec-WebSocket-Key received from the client during the handshake request.
35
+ * @param cookies - Optional cookies to be included in the response.
36
+ * @returns A complete HTTP response message as a string to be sent back to the client.
37
+ */
38
+ private static acceptMessage;
39
+ /**
40
+ * Generates an HTTP response message to reject a WebSocket handshake request with a specific status code and reason.
41
+ * @param code - The HTTP status code to indicate the reason for rejection (e.g., 400 for Bad Request).
42
+ * @param reason - A human-readable string explaining the reason for rejection.
43
+ * @param cookies - Optional cookies to be included in the response.
44
+ * @returns A complete HTTP response message as a string to be sent back to the client.
45
+ *
46
+ * @remarks The generated response will have a JSON body containing the provided code and reason, and will include any specified cookies in the headers. This response can be sent back to the client to indicate that the handshake request was rejected, along with the reason for rejection.
47
+ */
48
+ private static rejectMessage;
49
+ }
50
+ export declare namespace SSHandshaker {
51
+ type Status = 'handshake' | 'open' | 'closed';
52
+ type EventMap = {
53
+ finish: [status: SSHandshaker.Status];
54
+ error: [error: Error];
55
+ };
56
+ }
57
+ export default SSHandshaker;
@@ -0,0 +1,113 @@
1
+ import CRYPTO from 'crypto';
2
+ import { Events } from '@netfeez/common';
3
+ export class SSHandshaker extends Events {
4
+ socket;
5
+ Request;
6
+ static WEBSOCKET_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
7
+ vStatus = 'handshake';
8
+ constructor(socket, Request) {
9
+ super();
10
+ this.socket = socket;
11
+ this.Request = Request;
12
+ }
13
+ /**
14
+ * Accepts the WebSocket handshake by validating the client's request and sending the appropriate HTTP response to establish the WebSocket connection. This method should be called after verifying that the client's handshake request is valid and meets the necessary criteria for accepting the connection.
15
+ * - It generates the Sec-WebSocket-Accept key based on the client's Sec-WebSocket-Key and constructs the HTTP response with the required headers to complete the handshake.
16
+ * - After sending the response, it updates the internal status to 'open' and emits a 'finish' event with the new status.
17
+ *
18
+ * @throws Will throw an error if the client's handshake request does not contain a valid Sec-WebSocket-Key or if any other issue occurs during the acceptance process. The error will be emitted as an 'error' event for handling by the caller.
19
+ */
20
+ accept() {
21
+ try {
22
+ const key = this.Request.headers['sec-websocket-key'];
23
+ if (!key)
24
+ throw new Error('Server handshake requires a key');
25
+ const response = SSHandshaker.acceptMessage(key, this.Request.cookies);
26
+ this.socket.write(response);
27
+ this.vStatus = 'open';
28
+ }
29
+ catch (error) {
30
+ this.vStatus = 'closed';
31
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
32
+ }
33
+ finally {
34
+ setImmediate(() => this.emit('finish', this.vStatus));
35
+ }
36
+ }
37
+ /**
38
+ * Rejects the WebSocket handshake by sending an HTTP response with the specified status code and reason, and then closes the connection.
39
+ * @param code - The HTTP status code to indicate the reason for rejection (e.g., 400 for Bad Request).
40
+ * @param reason - A human-readable string explaining the reason for rejection.
41
+ *
42
+ * @remarks The generated response will have a JSON body containing the provided code and reason, and will include any specified cookies in the headers. This response can be sent back to the client to indicate that the handshake request was rejected, along with the reason for rejection.
43
+ */
44
+ reject(code = 400, reason = 'Bad Request') {
45
+ try {
46
+ const response = SSHandshaker.rejectMessage(code, reason, this.Request.cookies);
47
+ this.socket.write(response);
48
+ this.socket.end();
49
+ this.vStatus = 'closed';
50
+ }
51
+ catch (error) {
52
+ this.emit('error', error instanceof Error ? error : new Error(String(error)));
53
+ }
54
+ finally {
55
+ setImmediate(() => this.emit('finish', this.vStatus));
56
+ }
57
+ }
58
+ /**
59
+ * Generates the Sec-WebSocket-Accept key for the server handshake response based on the client's Sec-WebSocket-Key.
60
+ * @param key - The Sec-WebSocket-Key received from the client during the handshake request.
61
+ * @returns The computed Sec-WebSocket-Accept key to be sent in the handshake response.
62
+ */
63
+ static generateAcceptKey(key) {
64
+ return CRYPTO.createHash('sha1')
65
+ .update(key + SSHandshaker.WEBSOCKET_GUID)
66
+ .digest('base64');
67
+ }
68
+ /**
69
+ * Generates an HTTP response message to accept a WebSocket handshake request with the appropriate headers.
70
+ * @param key - The Sec-WebSocket-Key received from the client during the handshake request.
71
+ * @param cookies - Optional cookies to be included in the response.
72
+ * @returns A complete HTTP response message as a string to be sent back to the client.
73
+ */
74
+ static acceptMessage(key, cookies) {
75
+ const acceptKey = SSHandshaker.generateAcceptKey(key);
76
+ const setters = cookies
77
+ ? cookies.setters.map((setter) => `Set-Cookie: ${setter}`)
78
+ : [];
79
+ return [
80
+ 'HTTP/1.1 101 Switching Protocols',
81
+ 'Upgrade: websocket',
82
+ 'Connection: Upgrade',
83
+ `Sec-WebSocket-Accept: ${acceptKey}`,
84
+ ...setters,
85
+ '\r\n'
86
+ ].join('\r\n');
87
+ }
88
+ /**
89
+ * Generates an HTTP response message to reject a WebSocket handshake request with a specific status code and reason.
90
+ * @param code - The HTTP status code to indicate the reason for rejection (e.g., 400 for Bad Request).
91
+ * @param reason - A human-readable string explaining the reason for rejection.
92
+ * @param cookies - Optional cookies to be included in the response.
93
+ * @returns A complete HTTP response message as a string to be sent back to the client.
94
+ *
95
+ * @remarks The generated response will have a JSON body containing the provided code and reason, and will include any specified cookies in the headers. This response can be sent back to the client to indicate that the handshake request was rejected, along with the reason for rejection.
96
+ */
97
+ static rejectMessage(code, reason, cookies) {
98
+ const body = JSON.stringify({ code, reason }, null, 4);
99
+ const setters = cookies
100
+ ? cookies.setters.map((setter) => `Set-Cookie: ${setter}`)
101
+ : [];
102
+ return [
103
+ 'HTTP/1.1 400 Bad Request',
104
+ 'Content-Type: application/json',
105
+ `Content-Length: ${Buffer.byteLength(body)}`,
106
+ ...setters,
107
+ '\r\n',
108
+ body
109
+ ].join('\r\n');
110
+ }
111
+ }
112
+ export default SSHandshaker;
113
+ //# sourceMappingURL=SSHandshaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SSHandshaker.js","sourceRoot":"","sources":["../../../../src/server/websocket/handshake/SSHandshaker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type Frame from '../frame/Frame.js';
2
+ export declare class Message {
3
+ readonly frames: Frame[];
4
+ readonly opcode: number;
5
+ readonly payload: Buffer;
6
+ constructor(frames: Frame[]);
7
+ get isText(): boolean;
8
+ get isBinary(): boolean;
9
+ get isClose(): boolean;
10
+ get isPing(): boolean;
11
+ get isPong(): boolean;
12
+ get isControl(): boolean;
13
+ }
14
+ export declare namespace Message {
15
+ type Role = 'server' | 'client';
16
+ interface Message {
17
+ opcode: number;
18
+ payload: Buffer;
19
+ }
20
+ }
21
+ export default Message;
@@ -0,0 +1,20 @@
1
+ export class Message {
2
+ frames;
3
+ opcode;
4
+ payload;
5
+ constructor(frames) {
6
+ if (frames.length === 0)
7
+ throw new Error('No frames to assemble message');
8
+ this.frames = frames;
9
+ this.opcode = frames[0].header.opcode;
10
+ this.payload = Buffer.concat(frames.map(frame => frame.payload));
11
+ }
12
+ get isText() { return this.opcode === 0x1; }
13
+ get isBinary() { return this.opcode === 0x2; }
14
+ get isClose() { return this.opcode === 0x8; }
15
+ get isPing() { return this.opcode === 0x9; }
16
+ get isPong() { return this.opcode === 0xA; }
17
+ get isControl() { return this.opcode >= 0x8; }
18
+ }
19
+ export default Message;
20
+ //# sourceMappingURL=Message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Message.js","sourceRoot":"","sources":["../../../../src/server/websocket/messageAssembler/Message.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,OAAO;IACA,MAAM,CAAU;IAChB,MAAM,CAAS;IACf,OAAO,CAAS;IAEhC,YAAY,MAAe;QACvB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC1E,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,CAAC;IACD,IAAW,MAAM,KAAc,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5D,IAAW,QAAQ,KAAc,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC9D,IAAW,OAAO,KAAc,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC7D,IAAW,MAAM,KAAc,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5D,IAAW,MAAM,KAAc,OAAO,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC;IAC5D,IAAW,SAAS,KAAc,OAAO,IAAI,CAAC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC;CACjE;AAQD,eAAe,OAAO,CAAC"}