opal 1.6.1 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/build.yml +17 -0
  3. data/CHANGELOG.md +35 -1
  4. data/Gemfile +1 -0
  5. data/HACKING.md +47 -26
  6. data/benchmark/benchmarks +415 -103
  7. data/benchmark/bm_call_overhead.yml +28 -0
  8. data/benchmark/run.rb +61 -40
  9. data/docs/cdp_common.json +3364 -0
  10. data/docs/cdp_common.md +18 -0
  11. data/docs/{headless_chrome.md → headless_browsers.md} +31 -12
  12. data/lib/opal/ast/builder.rb +1 -1
  13. data/lib/opal/builder.rb +6 -1
  14. data/lib/opal/builder_processors.rb +5 -3
  15. data/lib/opal/cache.rb +1 -7
  16. data/lib/opal/cli_options.rb +72 -58
  17. data/lib/opal/cli_runners/chrome.rb +47 -9
  18. data/lib/opal/cli_runners/chrome_cdp_interface.rb +238 -112
  19. data/lib/opal/cli_runners/compiler.rb +146 -13
  20. data/lib/opal/cli_runners/deno.rb +32 -0
  21. data/lib/opal/cli_runners/firefox.rb +350 -0
  22. data/lib/opal/cli_runners/firefox_cdp_interface.rb +212 -0
  23. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.cmd +17 -0
  24. data/lib/opal/cli_runners/node_modules/.bin/chrome-remote-interface.ps1 +28 -0
  25. data/lib/opal/cli_runners/node_modules/.package-lock.json +41 -0
  26. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/LICENSE +1 -1
  27. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/README.md +322 -182
  28. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/bin/client.js +99 -114
  29. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/chrome-remote-interface.js +1 -11
  30. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/index.js +16 -11
  31. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/api.js +41 -33
  32. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/chrome.js +224 -214
  33. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/devtools.js +71 -191
  34. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/external-request.js +26 -6
  35. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/protocol.json +20788 -9049
  36. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/lib/websocket-wrapper.js +10 -3
  37. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/package.json +59 -123
  38. data/lib/opal/cli_runners/node_modules/chrome-remote-interface/webpack.config.js +25 -32
  39. data/lib/opal/cli_runners/node_modules/commander/History.md +298 -0
  40. data/lib/opal/cli_runners/node_modules/commander/LICENSE +22 -0
  41. data/lib/opal/cli_runners/node_modules/commander/Readme.md +217 -61
  42. data/lib/opal/cli_runners/node_modules/commander/index.js +431 -145
  43. data/lib/opal/cli_runners/node_modules/commander/package.json +16 -79
  44. data/lib/opal/cli_runners/node_modules/ws/README.md +334 -98
  45. data/lib/opal/cli_runners/node_modules/ws/browser.js +8 -0
  46. data/lib/opal/cli_runners/node_modules/ws/index.js +5 -10
  47. data/lib/opal/cli_runners/node_modules/ws/lib/buffer-util.js +129 -0
  48. data/lib/opal/cli_runners/node_modules/ws/lib/constants.js +10 -0
  49. data/lib/opal/cli_runners/node_modules/ws/lib/event-target.js +184 -0
  50. data/lib/opal/cli_runners/node_modules/ws/lib/extension.js +223 -0
  51. data/lib/opal/cli_runners/node_modules/ws/lib/limiter.js +55 -0
  52. data/lib/opal/cli_runners/node_modules/ws/lib/permessage-deflate.js +518 -0
  53. data/lib/opal/cli_runners/node_modules/ws/lib/receiver.js +607 -0
  54. data/lib/opal/cli_runners/node_modules/ws/lib/sender.js +409 -0
  55. data/lib/opal/cli_runners/node_modules/ws/lib/stream.js +180 -0
  56. data/lib/opal/cli_runners/node_modules/ws/lib/validation.js +104 -0
  57. data/lib/opal/cli_runners/node_modules/ws/lib/websocket-server.js +447 -0
  58. data/lib/opal/cli_runners/node_modules/ws/lib/websocket.js +1195 -0
  59. data/lib/opal/cli_runners/node_modules/ws/package.json +40 -106
  60. data/lib/opal/cli_runners/package-lock.json +62 -0
  61. data/lib/opal/cli_runners/package.json +1 -1
  62. data/lib/opal/cli_runners.rb +26 -4
  63. data/lib/opal/nodes/args/prepare_post_args.rb +2 -2
  64. data/lib/opal/nodes/def.rb +8 -8
  65. data/lib/opal/nodes/iter.rb +12 -12
  66. data/lib/opal/nodes/logic.rb +1 -1
  67. data/lib/opal/nodes/masgn.rb +2 -2
  68. data/lib/opal/parser/with_ruby_lexer.rb +1 -1
  69. data/lib/opal/paths.rb +14 -0
  70. data/lib/opal/rewriter.rb +2 -0
  71. data/lib/opal/rewriters/forward_args.rb +52 -4
  72. data/lib/opal/rewriters/targeted_patches.rb +94 -0
  73. data/lib/opal/version.rb +1 -1
  74. data/opal/corelib/basic_object.rb +1 -1
  75. data/opal/corelib/boolean.rb +2 -2
  76. data/opal/corelib/class.rb +11 -0
  77. data/opal/corelib/constants.rb +3 -3
  78. data/opal/corelib/enumerable.rb +4 -0
  79. data/opal/corelib/enumerator.rb +1 -1
  80. data/opal/corelib/hash.rb +2 -2
  81. data/opal/corelib/helpers.rb +1 -1
  82. data/opal/corelib/kernel.rb +3 -3
  83. data/opal/corelib/method.rb +1 -1
  84. data/opal/corelib/module.rb +29 -8
  85. data/opal/corelib/proc.rb +7 -5
  86. data/opal/corelib/runtime.js +141 -78
  87. data/opal/corelib/set.rb +252 -0
  88. data/opal/corelib/string.rb +2 -1
  89. data/opal/corelib/time.rb +2 -2
  90. data/opal/opal.rb +1 -0
  91. data/opal.gemspec +1 -0
  92. data/spec/filters/bugs/array.rb +22 -13
  93. data/spec/filters/bugs/base64.rb +5 -5
  94. data/spec/filters/bugs/basicobject.rb +16 -8
  95. data/spec/filters/bugs/bigdecimal.rb +161 -160
  96. data/spec/filters/bugs/binding.rb +10 -10
  97. data/spec/filters/bugs/class.rb +8 -8
  98. data/spec/filters/bugs/complex.rb +2 -1
  99. data/spec/filters/bugs/date.rb +79 -81
  100. data/spec/filters/bugs/datetime.rb +29 -29
  101. data/spec/filters/bugs/delegate.rb +1 -3
  102. data/spec/filters/bugs/encoding.rb +69 -69
  103. data/spec/filters/bugs/enumerable.rb +22 -20
  104. data/spec/filters/bugs/enumerator.rb +88 -85
  105. data/spec/filters/bugs/exception.rb +46 -40
  106. data/spec/filters/bugs/file.rb +32 -32
  107. data/spec/filters/bugs/float.rb +26 -21
  108. data/spec/filters/bugs/freeze.rb +88 -0
  109. data/spec/filters/bugs/hash.rb +39 -38
  110. data/spec/filters/bugs/integer.rb +57 -44
  111. data/spec/filters/bugs/io.rb +1 -1
  112. data/spec/filters/bugs/kernel.rb +349 -269
  113. data/spec/filters/bugs/language.rb +220 -188
  114. data/spec/filters/bugs/main.rb +5 -3
  115. data/spec/filters/bugs/marshal.rb +38 -38
  116. data/spec/filters/bugs/math.rb +2 -1
  117. data/spec/filters/bugs/method.rb +73 -62
  118. data/spec/filters/bugs/module.rb +163 -143
  119. data/spec/filters/bugs/numeric.rb +6 -6
  120. data/spec/filters/bugs/objectspace.rb +16 -16
  121. data/spec/filters/bugs/openstruct.rb +1 -1
  122. data/spec/filters/bugs/pack_unpack.rb +51 -51
  123. data/spec/filters/bugs/pathname.rb +7 -7
  124. data/spec/filters/bugs/proc.rb +63 -63
  125. data/spec/filters/bugs/random.rb +7 -6
  126. data/spec/filters/bugs/range.rb +12 -9
  127. data/spec/filters/bugs/rational.rb +8 -7
  128. data/spec/filters/bugs/regexp.rb +49 -48
  129. data/spec/filters/bugs/ruby-32.rb +56 -0
  130. data/spec/filters/bugs/set.rb +30 -30
  131. data/spec/filters/bugs/singleton.rb +4 -4
  132. data/spec/filters/bugs/string.rb +187 -99
  133. data/spec/filters/bugs/stringio.rb +7 -0
  134. data/spec/filters/bugs/stringscanner.rb +68 -68
  135. data/spec/filters/bugs/struct.rb +11 -9
  136. data/spec/filters/bugs/symbol.rb +1 -1
  137. data/spec/filters/bugs/time.rb +78 -63
  138. data/spec/filters/bugs/trace_point.rb +4 -4
  139. data/spec/filters/bugs/unboundmethod.rb +32 -17
  140. data/spec/filters/bugs/warnings.rb +8 -12
  141. data/spec/filters/unsupported/array.rb +24 -107
  142. data/spec/filters/unsupported/basicobject.rb +12 -12
  143. data/spec/filters/unsupported/bignum.rb +27 -52
  144. data/spec/filters/unsupported/class.rb +1 -2
  145. data/spec/filters/unsupported/delegator.rb +3 -3
  146. data/spec/filters/unsupported/enumerable.rb +2 -9
  147. data/spec/filters/unsupported/enumerator.rb +2 -11
  148. data/spec/filters/unsupported/file.rb +1 -1
  149. data/spec/filters/unsupported/float.rb +28 -47
  150. data/spec/filters/unsupported/hash.rb +8 -14
  151. data/spec/filters/unsupported/integer.rb +75 -91
  152. data/spec/filters/unsupported/kernel.rb +17 -35
  153. data/spec/filters/unsupported/language.rb +11 -19
  154. data/spec/filters/unsupported/marshal.rb +22 -41
  155. data/spec/filters/unsupported/matchdata.rb +28 -52
  156. data/spec/filters/unsupported/math.rb +1 -1
  157. data/spec/filters/unsupported/privacy.rb +229 -285
  158. data/spec/filters/unsupported/range.rb +1 -5
  159. data/spec/filters/unsupported/regexp.rb +40 -66
  160. data/spec/filters/unsupported/set.rb +2 -2
  161. data/spec/filters/unsupported/singleton.rb +4 -4
  162. data/spec/filters/unsupported/string.rb +305 -508
  163. data/spec/filters/unsupported/struct.rb +3 -4
  164. data/spec/filters/unsupported/symbol.rb +15 -18
  165. data/spec/filters/unsupported/thread.rb +1 -7
  166. data/spec/filters/unsupported/time.rb +159 -202
  167. data/spec/filters/unsupported/usage_of_files.rb +170 -259
  168. data/spec/lib/builder_spec.rb +4 -4
  169. data/spec/lib/rewriters/forward_args_spec.rb +32 -12
  170. data/spec/mspec-opal/runner.rb +2 -0
  171. data/spec/ruby_specs +4 -0
  172. data/stdlib/deno/base.rb +28 -0
  173. data/stdlib/deno/file.rb +340 -0
  174. data/stdlib/{headless_chrome.rb → headless_browser/base.rb} +1 -1
  175. data/stdlib/headless_browser/file.rb +15 -0
  176. data/stdlib/headless_browser.rb +4 -0
  177. data/stdlib/native.rb +1 -1
  178. data/stdlib/nodejs/file.rb +5 -0
  179. data/stdlib/opal/platform.rb +8 -6
  180. data/stdlib/opal-platform.rb +14 -8
  181. data/stdlib/set.rb +1 -258
  182. data/tasks/benchmarking.rake +62 -19
  183. data/tasks/performance.rake +1 -1
  184. data/tasks/testing.rake +5 -3
  185. data/test/nodejs/test_file.rb +29 -10
  186. data/test/opal/http_server.rb +28 -11
  187. data/test/opal/unsupported_and_bugs.rb +2 -1
  188. metadata +89 -50
  189. data/lib/opal/cli_runners/node_modules/ultron/LICENSE +0 -22
  190. data/lib/opal/cli_runners/node_modules/ultron/index.js +0 -138
  191. data/lib/opal/cli_runners/node_modules/ultron/package.json +0 -112
  192. data/lib/opal/cli_runners/node_modules/ws/SECURITY.md +0 -33
  193. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.fallback.js +0 -56
  194. data/lib/opal/cli_runners/node_modules/ws/lib/BufferUtil.js +0 -15
  195. data/lib/opal/cli_runners/node_modules/ws/lib/ErrorCodes.js +0 -28
  196. data/lib/opal/cli_runners/node_modules/ws/lib/EventTarget.js +0 -158
  197. data/lib/opal/cli_runners/node_modules/ws/lib/Extensions.js +0 -69
  198. data/lib/opal/cli_runners/node_modules/ws/lib/PerMessageDeflate.js +0 -339
  199. data/lib/opal/cli_runners/node_modules/ws/lib/Receiver.js +0 -520
  200. data/lib/opal/cli_runners/node_modules/ws/lib/Sender.js +0 -438
  201. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.fallback.js +0 -9
  202. data/lib/opal/cli_runners/node_modules/ws/lib/Validation.js +0 -17
  203. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocket.js +0 -705
  204. data/lib/opal/cli_runners/node_modules/ws/lib/WebSocketServer.js +0 -336
  205. data/spec/filters/bugs/boolean.rb +0 -3
  206. data/spec/filters/bugs/matrix.rb +0 -3
  207. data/spec/filters/unsupported/fixnum.rb +0 -15
  208. data/spec/filters/unsupported/freeze.rb +0 -102
  209. data/spec/filters/unsupported/pathname.rb +0 -4
  210. data/spec/filters/unsupported/proc.rb +0 -4
  211. data/spec/filters/unsupported/random.rb +0 -5
  212. data/spec/filters/unsupported/taint.rb +0 -162
@@ -0,0 +1,129 @@
1
+ 'use strict';
2
+
3
+ const { EMPTY_BUFFER } = require('./constants');
4
+
5
+ /**
6
+ * Merges an array of buffers into a new buffer.
7
+ *
8
+ * @param {Buffer[]} list The array of buffers to concat
9
+ * @param {Number} totalLength The total length of buffers in the list
10
+ * @return {Buffer} The resulting buffer
11
+ * @public
12
+ */
13
+ function concat(list, totalLength) {
14
+ if (list.length === 0) return EMPTY_BUFFER;
15
+ if (list.length === 1) return list[0];
16
+
17
+ const target = Buffer.allocUnsafe(totalLength);
18
+ let offset = 0;
19
+
20
+ for (let i = 0; i < list.length; i++) {
21
+ const buf = list[i];
22
+ target.set(buf, offset);
23
+ offset += buf.length;
24
+ }
25
+
26
+ if (offset < totalLength) return target.slice(0, offset);
27
+
28
+ return target;
29
+ }
30
+
31
+ /**
32
+ * Masks a buffer using the given mask.
33
+ *
34
+ * @param {Buffer} source The buffer to mask
35
+ * @param {Buffer} mask The mask to use
36
+ * @param {Buffer} output The buffer where to store the result
37
+ * @param {Number} offset The offset at which to start writing
38
+ * @param {Number} length The number of bytes to mask.
39
+ * @public
40
+ */
41
+ function _mask(source, mask, output, offset, length) {
42
+ for (let i = 0; i < length; i++) {
43
+ output[offset + i] = source[i] ^ mask[i & 3];
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Unmasks a buffer using the given mask.
49
+ *
50
+ * @param {Buffer} buffer The buffer to unmask
51
+ * @param {Buffer} mask The mask to use
52
+ * @public
53
+ */
54
+ function _unmask(buffer, mask) {
55
+ // Required until https://github.com/nodejs/node/issues/9006 is resolved.
56
+ const length = buffer.length;
57
+ for (let i = 0; i < length; i++) {
58
+ buffer[i] ^= mask[i & 3];
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Converts a buffer to an `ArrayBuffer`.
64
+ *
65
+ * @param {Buffer} buf The buffer to convert
66
+ * @return {ArrayBuffer} Converted buffer
67
+ * @public
68
+ */
69
+ function toArrayBuffer(buf) {
70
+ if (buf.byteLength === buf.buffer.byteLength) {
71
+ return buf.buffer;
72
+ }
73
+
74
+ return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
75
+ }
76
+
77
+ /**
78
+ * Converts `data` to a `Buffer`.
79
+ *
80
+ * @param {*} data The data to convert
81
+ * @return {Buffer} The buffer
82
+ * @throws {TypeError}
83
+ * @public
84
+ */
85
+ function toBuffer(data) {
86
+ toBuffer.readOnly = true;
87
+
88
+ if (Buffer.isBuffer(data)) return data;
89
+
90
+ let buf;
91
+
92
+ if (data instanceof ArrayBuffer) {
93
+ buf = Buffer.from(data);
94
+ } else if (ArrayBuffer.isView(data)) {
95
+ buf = Buffer.from(data.buffer, data.byteOffset, data.byteLength);
96
+ } else {
97
+ buf = Buffer.from(data);
98
+ toBuffer.readOnly = false;
99
+ }
100
+
101
+ return buf;
102
+ }
103
+
104
+ try {
105
+ const bufferUtil = require('bufferutil');
106
+ const bu = bufferUtil.BufferUtil || bufferUtil;
107
+
108
+ module.exports = {
109
+ concat,
110
+ mask(source, mask, output, offset, length) {
111
+ if (length < 48) _mask(source, mask, output, offset, length);
112
+ else bu.mask(source, mask, output, offset, length);
113
+ },
114
+ toArrayBuffer,
115
+ toBuffer,
116
+ unmask(buffer, mask) {
117
+ if (buffer.length < 32) _unmask(buffer, mask);
118
+ else bu.unmask(buffer, mask);
119
+ }
120
+ };
121
+ } catch (e) /* istanbul ignore next */ {
122
+ module.exports = {
123
+ concat,
124
+ mask: _mask,
125
+ toArrayBuffer,
126
+ toBuffer,
127
+ unmask: _unmask
128
+ };
129
+ }
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ BINARY_TYPES: ['nodebuffer', 'arraybuffer', 'fragments'],
5
+ GUID: '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
6
+ kStatusCode: Symbol('status-code'),
7
+ kWebSocket: Symbol('websocket'),
8
+ EMPTY_BUFFER: Buffer.alloc(0),
9
+ NOOP: () => {}
10
+ };
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Class representing an event.
5
+ *
6
+ * @private
7
+ */
8
+ class Event {
9
+ /**
10
+ * Create a new `Event`.
11
+ *
12
+ * @param {String} type The name of the event
13
+ * @param {Object} target A reference to the target to which the event was
14
+ * dispatched
15
+ */
16
+ constructor(type, target) {
17
+ this.target = target;
18
+ this.type = type;
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Class representing a message event.
24
+ *
25
+ * @extends Event
26
+ * @private
27
+ */
28
+ class MessageEvent extends Event {
29
+ /**
30
+ * Create a new `MessageEvent`.
31
+ *
32
+ * @param {(String|Buffer|ArrayBuffer|Buffer[])} data The received data
33
+ * @param {WebSocket} target A reference to the target to which the event was
34
+ * dispatched
35
+ */
36
+ constructor(data, target) {
37
+ super('message', target);
38
+
39
+ this.data = data;
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Class representing a close event.
45
+ *
46
+ * @extends Event
47
+ * @private
48
+ */
49
+ class CloseEvent extends Event {
50
+ /**
51
+ * Create a new `CloseEvent`.
52
+ *
53
+ * @param {Number} code The status code explaining why the connection is being
54
+ * closed
55
+ * @param {String} reason A human-readable string explaining why the
56
+ * connection is closing
57
+ * @param {WebSocket} target A reference to the target to which the event was
58
+ * dispatched
59
+ */
60
+ constructor(code, reason, target) {
61
+ super('close', target);
62
+
63
+ this.wasClean = target._closeFrameReceived && target._closeFrameSent;
64
+ this.reason = reason;
65
+ this.code = code;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Class representing an open event.
71
+ *
72
+ * @extends Event
73
+ * @private
74
+ */
75
+ class OpenEvent extends Event {
76
+ /**
77
+ * Create a new `OpenEvent`.
78
+ *
79
+ * @param {WebSocket} target A reference to the target to which the event was
80
+ * dispatched
81
+ */
82
+ constructor(target) {
83
+ super('open', target);
84
+ }
85
+ }
86
+
87
+ /**
88
+ * Class representing an error event.
89
+ *
90
+ * @extends Event
91
+ * @private
92
+ */
93
+ class ErrorEvent extends Event {
94
+ /**
95
+ * Create a new `ErrorEvent`.
96
+ *
97
+ * @param {Object} error The error that generated this event
98
+ * @param {WebSocket} target A reference to the target to which the event was
99
+ * dispatched
100
+ */
101
+ constructor(error, target) {
102
+ super('error', target);
103
+
104
+ this.message = error.message;
105
+ this.error = error;
106
+ }
107
+ }
108
+
109
+ /**
110
+ * This provides methods for emulating the `EventTarget` interface. It's not
111
+ * meant to be used directly.
112
+ *
113
+ * @mixin
114
+ */
115
+ const EventTarget = {
116
+ /**
117
+ * Register an event listener.
118
+ *
119
+ * @param {String} type A string representing the event type to listen for
120
+ * @param {Function} listener The listener to add
121
+ * @param {Object} [options] An options object specifies characteristics about
122
+ * the event listener
123
+ * @param {Boolean} [options.once=false] A `Boolean`` indicating that the
124
+ * listener should be invoked at most once after being added. If `true`,
125
+ * the listener would be automatically removed when invoked.
126
+ * @public
127
+ */
128
+ addEventListener(type, listener, options) {
129
+ if (typeof listener !== 'function') return;
130
+
131
+ function onMessage(data) {
132
+ listener.call(this, new MessageEvent(data, this));
133
+ }
134
+
135
+ function onClose(code, message) {
136
+ listener.call(this, new CloseEvent(code, message, this));
137
+ }
138
+
139
+ function onError(error) {
140
+ listener.call(this, new ErrorEvent(error, this));
141
+ }
142
+
143
+ function onOpen() {
144
+ listener.call(this, new OpenEvent(this));
145
+ }
146
+
147
+ const method = options && options.once ? 'once' : 'on';
148
+
149
+ if (type === 'message') {
150
+ onMessage._listener = listener;
151
+ this[method](type, onMessage);
152
+ } else if (type === 'close') {
153
+ onClose._listener = listener;
154
+ this[method](type, onClose);
155
+ } else if (type === 'error') {
156
+ onError._listener = listener;
157
+ this[method](type, onError);
158
+ } else if (type === 'open') {
159
+ onOpen._listener = listener;
160
+ this[method](type, onOpen);
161
+ } else {
162
+ this[method](type, listener);
163
+ }
164
+ },
165
+
166
+ /**
167
+ * Remove an event listener.
168
+ *
169
+ * @param {String} type A string representing the event type to remove
170
+ * @param {Function} listener The listener to remove
171
+ * @public
172
+ */
173
+ removeEventListener(type, listener) {
174
+ const listeners = this.listeners(type);
175
+
176
+ for (let i = 0; i < listeners.length; i++) {
177
+ if (listeners[i] === listener || listeners[i]._listener === listener) {
178
+ this.removeListener(type, listeners[i]);
179
+ }
180
+ }
181
+ }
182
+ };
183
+
184
+ module.exports = EventTarget;
@@ -0,0 +1,223 @@
1
+ 'use strict';
2
+
3
+ //
4
+ // Allowed token characters:
5
+ //
6
+ // '!', '#', '$', '%', '&', ''', '*', '+', '-',
7
+ // '.', 0-9, A-Z, '^', '_', '`', a-z, '|', '~'
8
+ //
9
+ // tokenChars[32] === 0 // ' '
10
+ // tokenChars[33] === 1 // '!'
11
+ // tokenChars[34] === 0 // '"'
12
+ // ...
13
+ //
14
+ // prettier-ignore
15
+ const tokenChars = [
16
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15
17
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31
18
+ 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, // 32 - 47
19
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63
20
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79
21
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, // 80 - 95
22
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111
23
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0 // 112 - 127
24
+ ];
25
+
26
+ /**
27
+ * Adds an offer to the map of extension offers or a parameter to the map of
28
+ * parameters.
29
+ *
30
+ * @param {Object} dest The map of extension offers or parameters
31
+ * @param {String} name The extension or parameter name
32
+ * @param {(Object|Boolean|String)} elem The extension parameters or the
33
+ * parameter value
34
+ * @private
35
+ */
36
+ function push(dest, name, elem) {
37
+ if (dest[name] === undefined) dest[name] = [elem];
38
+ else dest[name].push(elem);
39
+ }
40
+
41
+ /**
42
+ * Parses the `Sec-WebSocket-Extensions` header into an object.
43
+ *
44
+ * @param {String} header The field value of the header
45
+ * @return {Object} The parsed object
46
+ * @public
47
+ */
48
+ function parse(header) {
49
+ const offers = Object.create(null);
50
+
51
+ if (header === undefined || header === '') return offers;
52
+
53
+ let params = Object.create(null);
54
+ let mustUnescape = false;
55
+ let isEscaping = false;
56
+ let inQuotes = false;
57
+ let extensionName;
58
+ let paramName;
59
+ let start = -1;
60
+ let end = -1;
61
+ let i = 0;
62
+
63
+ for (; i < header.length; i++) {
64
+ const code = header.charCodeAt(i);
65
+
66
+ if (extensionName === undefined) {
67
+ if (end === -1 && tokenChars[code] === 1) {
68
+ if (start === -1) start = i;
69
+ } else if (code === 0x20 /* ' ' */ || code === 0x09 /* '\t' */) {
70
+ if (end === -1 && start !== -1) end = i;
71
+ } else if (code === 0x3b /* ';' */ || code === 0x2c /* ',' */) {
72
+ if (start === -1) {
73
+ throw new SyntaxError(`Unexpected character at index ${i}`);
74
+ }
75
+
76
+ if (end === -1) end = i;
77
+ const name = header.slice(start, end);
78
+ if (code === 0x2c) {
79
+ push(offers, name, params);
80
+ params = Object.create(null);
81
+ } else {
82
+ extensionName = name;
83
+ }
84
+
85
+ start = end = -1;
86
+ } else {
87
+ throw new SyntaxError(`Unexpected character at index ${i}`);
88
+ }
89
+ } else if (paramName === undefined) {
90
+ if (end === -1 && tokenChars[code] === 1) {
91
+ if (start === -1) start = i;
92
+ } else if (code === 0x20 || code === 0x09) {
93
+ if (end === -1 && start !== -1) end = i;
94
+ } else if (code === 0x3b || code === 0x2c) {
95
+ if (start === -1) {
96
+ throw new SyntaxError(`Unexpected character at index ${i}`);
97
+ }
98
+
99
+ if (end === -1) end = i;
100
+ push(params, header.slice(start, end), true);
101
+ if (code === 0x2c) {
102
+ push(offers, extensionName, params);
103
+ params = Object.create(null);
104
+ extensionName = undefined;
105
+ }
106
+
107
+ start = end = -1;
108
+ } else if (code === 0x3d /* '=' */ && start !== -1 && end === -1) {
109
+ paramName = header.slice(start, i);
110
+ start = end = -1;
111
+ } else {
112
+ throw new SyntaxError(`Unexpected character at index ${i}`);
113
+ }
114
+ } else {
115
+ //
116
+ // The value of a quoted-string after unescaping must conform to the
117
+ // token ABNF, so only token characters are valid.
118
+ // Ref: https://tools.ietf.org/html/rfc6455#section-9.1
119
+ //
120
+ if (isEscaping) {
121
+ if (tokenChars[code] !== 1) {
122
+ throw new SyntaxError(`Unexpected character at index ${i}`);
123
+ }
124
+ if (start === -1) start = i;
125
+ else if (!mustUnescape) mustUnescape = true;
126
+ isEscaping = false;
127
+ } else if (inQuotes) {
128
+ if (tokenChars[code] === 1) {
129
+ if (start === -1) start = i;
130
+ } else if (code === 0x22 /* '"' */ && start !== -1) {
131
+ inQuotes = false;
132
+ end = i;
133
+ } else if (code === 0x5c /* '\' */) {
134
+ isEscaping = true;
135
+ } else {
136
+ throw new SyntaxError(`Unexpected character at index ${i}`);
137
+ }
138
+ } else if (code === 0x22 && header.charCodeAt(i - 1) === 0x3d) {
139
+ inQuotes = true;
140
+ } else if (end === -1 && tokenChars[code] === 1) {
141
+ if (start === -1) start = i;
142
+ } else if (start !== -1 && (code === 0x20 || code === 0x09)) {
143
+ if (end === -1) end = i;
144
+ } else if (code === 0x3b || code === 0x2c) {
145
+ if (start === -1) {
146
+ throw new SyntaxError(`Unexpected character at index ${i}`);
147
+ }
148
+
149
+ if (end === -1) end = i;
150
+ let value = header.slice(start, end);
151
+ if (mustUnescape) {
152
+ value = value.replace(/\\/g, '');
153
+ mustUnescape = false;
154
+ }
155
+ push(params, paramName, value);
156
+ if (code === 0x2c) {
157
+ push(offers, extensionName, params);
158
+ params = Object.create(null);
159
+ extensionName = undefined;
160
+ }
161
+
162
+ paramName = undefined;
163
+ start = end = -1;
164
+ } else {
165
+ throw new SyntaxError(`Unexpected character at index ${i}`);
166
+ }
167
+ }
168
+ }
169
+
170
+ if (start === -1 || inQuotes) {
171
+ throw new SyntaxError('Unexpected end of input');
172
+ }
173
+
174
+ if (end === -1) end = i;
175
+ const token = header.slice(start, end);
176
+ if (extensionName === undefined) {
177
+ push(offers, token, params);
178
+ } else {
179
+ if (paramName === undefined) {
180
+ push(params, token, true);
181
+ } else if (mustUnescape) {
182
+ push(params, paramName, token.replace(/\\/g, ''));
183
+ } else {
184
+ push(params, paramName, token);
185
+ }
186
+ push(offers, extensionName, params);
187
+ }
188
+
189
+ return offers;
190
+ }
191
+
192
+ /**
193
+ * Builds the `Sec-WebSocket-Extensions` header field value.
194
+ *
195
+ * @param {Object} extensions The map of extensions and parameters to format
196
+ * @return {String} A string representing the given object
197
+ * @public
198
+ */
199
+ function format(extensions) {
200
+ return Object.keys(extensions)
201
+ .map((extension) => {
202
+ let configurations = extensions[extension];
203
+ if (!Array.isArray(configurations)) configurations = [configurations];
204
+ return configurations
205
+ .map((params) => {
206
+ return [extension]
207
+ .concat(
208
+ Object.keys(params).map((k) => {
209
+ let values = params[k];
210
+ if (!Array.isArray(values)) values = [values];
211
+ return values
212
+ .map((v) => (v === true ? k : `${k}=${v}`))
213
+ .join('; ');
214
+ })
215
+ )
216
+ .join('; ');
217
+ })
218
+ .join(', ');
219
+ })
220
+ .join(', ');
221
+ }
222
+
223
+ module.exports = { format, parse };
@@ -0,0 +1,55 @@
1
+ 'use strict';
2
+
3
+ const kDone = Symbol('kDone');
4
+ const kRun = Symbol('kRun');
5
+
6
+ /**
7
+ * A very simple job queue with adjustable concurrency. Adapted from
8
+ * https://github.com/STRML/async-limiter
9
+ */
10
+ class Limiter {
11
+ /**
12
+ * Creates a new `Limiter`.
13
+ *
14
+ * @param {Number} [concurrency=Infinity] The maximum number of jobs allowed
15
+ * to run concurrently
16
+ */
17
+ constructor(concurrency) {
18
+ this[kDone] = () => {
19
+ this.pending--;
20
+ this[kRun]();
21
+ };
22
+ this.concurrency = concurrency || Infinity;
23
+ this.jobs = [];
24
+ this.pending = 0;
25
+ }
26
+
27
+ /**
28
+ * Adds a job to the queue.
29
+ *
30
+ * @param {Function} job The job to run
31
+ * @public
32
+ */
33
+ add(job) {
34
+ this.jobs.push(job);
35
+ this[kRun]();
36
+ }
37
+
38
+ /**
39
+ * Removes a job from the queue and runs it if possible.
40
+ *
41
+ * @private
42
+ */
43
+ [kRun]() {
44
+ if (this.pending === this.concurrency) return;
45
+
46
+ if (this.jobs.length) {
47
+ const job = this.jobs.shift();
48
+
49
+ this.pending++;
50
+ job(this[kDone]);
51
+ }
52
+ }
53
+ }
54
+
55
+ module.exports = Limiter;