firefly-compiler 0.4.6 → 0.4.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 (90) hide show
  1. package/compiler/Compiler.ff +2 -0
  2. package/compiler/Deriver.ff +7 -0
  3. package/core/Array.ff +17 -4
  4. package/core/AssetSystem.ff +1 -1
  5. package/core/BrowserSystem.ff +27 -0
  6. package/core/List.ff +4 -0
  7. package/core/Lock.ff +2 -2
  8. package/core/Random.ff +148 -0
  9. package/core/SourceLocation.ff +27 -0
  10. package/core/Task.ff +3 -0
  11. package/core/WebSocket.ff +127 -0
  12. package/httpserver/HttpServer.ff +1 -1
  13. package/lux/Css.ff +648 -0
  14. package/lux/CssTest.ff +48 -0
  15. package/lux/Lux.ff +33 -18
  16. package/lux/Main.ff +4 -1
  17. package/output/js/ff/compiler/Builder.mjs +448 -444
  18. package/output/js/ff/compiler/Compiler.mjs +420 -416
  19. package/output/js/ff/compiler/Dependencies.mjs +393 -389
  20. package/output/js/ff/compiler/Deriver.mjs +1178 -1170
  21. package/output/js/ff/compiler/Dictionaries.mjs +1315 -1309
  22. package/output/js/ff/compiler/Environment.mjs +1025 -1015
  23. package/output/js/ff/compiler/Inference.mjs +4272 -4268
  24. package/output/js/ff/compiler/JsEmitter.mjs +5405 -5391
  25. package/output/js/ff/compiler/JsImporter.mjs +270 -266
  26. package/output/js/ff/compiler/LspHook.mjs +799 -793
  27. package/output/js/ff/compiler/Main.mjs +1691 -1675
  28. package/output/js/ff/compiler/Parser.mjs +4016 -4008
  29. package/output/js/ff/compiler/Patterns.mjs +935 -927
  30. package/output/js/ff/compiler/Resolver.mjs +2311 -2307
  31. package/output/js/ff/compiler/Substitution.mjs +1154 -1150
  32. package/output/js/ff/compiler/Syntax.mjs +12566 -12434
  33. package/output/js/ff/compiler/Token.mjs +3152 -3096
  34. package/output/js/ff/compiler/Tokenizer.mjs +597 -593
  35. package/output/js/ff/compiler/Unification.mjs +1762 -1752
  36. package/output/js/ff/compiler/Wildcards.mjs +612 -608
  37. package/output/js/ff/compiler/Workspace.mjs +695 -687
  38. package/output/js/ff/core/Any.mjs +147 -143
  39. package/output/js/ff/core/Array.mjs +573 -547
  40. package/output/js/ff/core/AssetSystem.mjs +278 -274
  41. package/output/js/ff/core/Atomic.mjs +158 -154
  42. package/output/js/ff/core/Bool.mjs +156 -152
  43. package/output/js/ff/core/Box.mjs +116 -112
  44. package/output/js/ff/core/BrowserSystem.mjs +181 -126
  45. package/output/js/ff/core/Buffer.mjs +399 -395
  46. package/output/js/ff/core/BuildSystem.mjs +300 -296
  47. package/output/js/ff/core/Channel.mjs +193 -189
  48. package/output/js/ff/core/Char.mjs +153 -149
  49. package/output/js/ff/core/Core.mjs +304 -300
  50. package/output/js/ff/core/Duration.mjs +120 -116
  51. package/output/js/ff/core/Equal.mjs +183 -179
  52. package/output/js/ff/core/Error.mjs +146 -142
  53. package/output/js/ff/core/FileHandle.mjs +154 -150
  54. package/output/js/ff/core/Float.mjs +229 -225
  55. package/output/js/ff/core/HttpClient.mjs +195 -191
  56. package/output/js/ff/core/Instant.mjs +113 -109
  57. package/output/js/ff/core/Int.mjs +269 -265
  58. package/output/js/ff/core/IntMap.mjs +284 -280
  59. package/output/js/ff/core/JsSystem.mjs +242 -238
  60. package/output/js/ff/core/JsValue.mjs +712 -708
  61. package/output/js/ff/core/List.mjs +2346 -2334
  62. package/output/js/ff/core/Lock.mjs +235 -231
  63. package/output/js/ff/core/Log.mjs +167 -163
  64. package/output/js/ff/core/Map.mjs +366 -362
  65. package/output/js/ff/core/NodeSystem.mjs +298 -294
  66. package/output/js/ff/core/Nothing.mjs +108 -104
  67. package/output/js/ff/core/Option.mjs +1023 -1015
  68. package/output/js/ff/core/Ordering.mjs +734 -730
  69. package/output/js/ff/core/Pair.mjs +337 -331
  70. package/output/js/ff/core/Path.mjs +549 -545
  71. package/output/js/ff/core/Random.mjs +340 -0
  72. package/output/js/ff/core/RbMap.mjs +1944 -1940
  73. package/output/js/ff/core/Serializable.mjs +432 -428
  74. package/output/js/ff/core/Set.mjs +258 -254
  75. package/output/js/ff/core/Show.mjs +209 -205
  76. package/output/js/ff/core/SourceLocation.mjs +273 -229
  77. package/output/js/ff/core/Stack.mjs +533 -529
  78. package/output/js/ff/core/Stream.mjs +1308 -1304
  79. package/output/js/ff/core/String.mjs +369 -365
  80. package/output/js/ff/core/StringMap.mjs +284 -280
  81. package/output/js/ff/core/Task.mjs +325 -319
  82. package/output/js/ff/core/Try.mjs +511 -507
  83. package/output/js/ff/core/Unit.mjs +155 -151
  84. package/output/js/ff/core/WebSocket.mjs +198 -0
  85. package/package.json +1 -1
  86. package/vscode/package.json +1 -1
  87. package/webserver/.firefly/include/package-lock.json +16 -0
  88. package/webserver/.firefly/include/package.json +5 -0
  89. package/webserver/.firefly/package.ff +2 -0
  90. package/webserver/WebServer.ff +608 -0
@@ -0,0 +1,608 @@
1
+ class WebRequest[T](
2
+ internalJsApp: JsValue
3
+ internalJsContext: JsValue
4
+ internalMethod: String
5
+ internalPath: String
6
+ internalQuery: String
7
+ internalHeaders: StringMap[String]
8
+ internalJsResponse: JsValue
9
+ internalResponse: T
10
+ internalResponseHeaders: StringMap[String]
11
+ internalResponseChunks: Stack[JsValue]
12
+ mutable internalResponseStatus: String
13
+ mutable internalCloseConnection: Bool
14
+ mutable internalResolveFlush: JsValue
15
+ mutable internalContentSize: Int
16
+ mutable internalResponded: Bool
17
+ )
18
+
19
+ capability WebResponse {}
20
+
21
+ data MultipartField {}
22
+
23
+ capability WebSocketConnection {}
24
+
25
+ listen(
26
+ system: NodeSystem
27
+ host: String
28
+ port: Int
29
+ handler: WebRequest[WebResponse] => Unit
30
+ ): Unit
31
+ target node async """
32
+ import * as uws from 'uWebSockets.js'
33
+ ff_core_Task.Task_throwIfAborted($task);
34
+ await new Promise((resolveServer, rejectServer) => {
35
+ const app = uws.App();
36
+ const handleRequest = (res, req, context) => {
37
+ let abortedByUws = false;
38
+ const method = req.getMethod();
39
+ const path = req.getUrl();
40
+ const query = req.getQuery() || '';
41
+ const headers = new Map();
42
+ req.forEach((key, value) => headers.set(key, value));
43
+ const chunks = internalStack_();
44
+ const task = ff_core_Task.Task_spawn($task, async task => {
45
+ try {
46
+ ff_core_Task.Task_throwIfAborted(task);
47
+ const webRequest = WebRequest(
48
+ app, context,
49
+ method, path, query, headers,
50
+ res, null,
51
+ new Map(), internalStack_(),
52
+ "200 OK", false, null, -1, false
53
+ );
54
+ await handler_(webRequest, task);
55
+ ff_core_Task.Task_throwIfAborted(task);
56
+ if(context == null && !webRequest.internalResponded_ && webRequest.internalContentSize_ >= 0) {
57
+ await WebRequest_flush$(webRequest, task);
58
+ ff_core_Task.Task_throwIfAborted(task);
59
+ }
60
+ if(context == null && !webRequest.internalResponded_) await new Promise((resolve, reject) => {
61
+ res.cork(() => {
62
+ try {
63
+ ff_core_Task.Task_throwIfAborted(task);
64
+ if(!webRequest.internalResolveFlush_) {
65
+ res.writeStatus(webRequest.internalResponseStatus_);
66
+ webRequest.internalResponseHeaders_.forEach((value, key) => res.writeHeader(key, value));
67
+ }
68
+ const chunk = internalChunksToChunk_(webRequest.internalResponseChunks_);
69
+ res.end(chunk, webRequest.internalCloseConnection_);
70
+ resolve();
71
+ } catch(e) {
72
+ reject(e);
73
+ }
74
+ });
75
+ });
76
+ } catch(e) {
77
+ if(!abortedByUws) console.error(e);
78
+ if(!abortedByUws && context == null) res.cork(() => {
79
+ if(!abortedByUws) res.writeStatus("500 Internal Server Error");
80
+ if(!abortedByUws) res.end();
81
+ });
82
+ }
83
+ });
84
+ res.onAborted(() => {
85
+ abortedByUws = true;
86
+ ff_core_Task.Task_abort(task);
87
+ });
88
+ }
89
+ app.any('/*', handleRequest);
90
+ app.ws("/*", {
91
+ compression: 0,
92
+ maxPayloadLength: 16 * 1024 * 1024,
93
+ idleTimeout: 10,
94
+ upgrade: handleRequest,
95
+ open: ws => ws.getUserData().onOpen(ws),
96
+ message: (ws, m, b) => ws.getUserData().onMessage(m, b),
97
+ close: ws => ws.getUserData().onClose(),
98
+ });
99
+ app.listen(host_, port_, listenSocket => {
100
+ if(listenSocket) {
101
+ const abort = () => {
102
+ $task.controller.signal.removeEventListener('abort', abort);
103
+ uws.us_listen_socket_close(listenSocket);
104
+ }
105
+ $task.controller.signal.addEventListener('abort', abort);
106
+ } else {
107
+ rejectServer();
108
+ }
109
+ });
110
+ });
111
+ """
112
+
113
+ nodeMain(system: NodeSystem) {
114
+ listen(system, "localhost", 8080) {request =>
115
+ if(request.readPath() != "/") {request.writeStatus("404 Not Found")} else:
116
+ request.writeHeader("IsExample", request.readQuery("foo").else {"None"})
117
+ request.writeContentSize(10 * 500000 * "Hello ".size() + "world!".size())
118
+ 1.to(10).each {_ => request.writeText(1.to(500000).map {_ => "Hello "}.join())}
119
+ request.flush()
120
+ request.writeText("world!")
121
+ }
122
+ }
123
+
124
+ extend self[T]: WebRequest[T] {
125
+
126
+ readMethod(): String {
127
+ self.internalMethod
128
+ }
129
+
130
+ readPath(): String {
131
+ self.internalPath
132
+ }
133
+
134
+ readRawQueryString(): String {
135
+ self.internalQuery
136
+ }
137
+
138
+ readQuery(name: String): Option[String]
139
+ target node sync """
140
+ if(self_.internalQuery_.length === 0) return ff_core_Option.None();
141
+ const ps = self_.internalQuery_.split('&');
142
+ const n = name_ + '=';
143
+ const r = ps.find(p => p === name_ || p.startsWith(n));
144
+ if(r == null) return ff_core_Option.None();
145
+ return ff_core_Option.Some(decodeURIComponent(r.slice(name_.length + 1)));
146
+ """
147
+
148
+ readHeader(lowerCaseName: String): Option[String] {
149
+ self.internalHeaders.get(lowerCaseName)
150
+ }
151
+
152
+ eachHeader(body: (String, String) => Unit) {
153
+ self.internalHeaders.each {body(_, _)}
154
+ }
155
+
156
+ }
157
+
158
+ extend self[T]: WebRequest[T] {
159
+
160
+ writeStatus(codeAndMessage: String) {
161
+ self.internalResponseStatus = codeAndMessage
162
+ }
163
+
164
+ writeHeader(name: String, value: String) {
165
+ self.internalResponseHeaders.set(name, value)
166
+ }
167
+
168
+ writeContentSize(contentSize: Int) {
169
+ if(contentSize >= 0) {
170
+ self.internalContentSize = contentSize
171
+ }
172
+ }
173
+
174
+ writeText(data: String) {
175
+ self.internalResponseChunks.push(internalTextChunk(data))
176
+ }
177
+
178
+ writeBuffer(data: Buffer) {
179
+ self.internalResponseChunks.push(internalBufferChunk(data))
180
+ }
181
+
182
+ writeStream(data: Stream[Buffer]) {
183
+ data.each {self.writeBuffer(_)}
184
+ }
185
+
186
+ }
187
+
188
+ extend self[T]: WebRequest[T] {
189
+
190
+ publishBuffer(topic: String, message: Buffer) // Should this be corked?
191
+ target js sync """
192
+ self_.internalJsApp_.publish(topic_, message_.buffer, true);
193
+ """
194
+
195
+ publishText(topic: String, message: String) // Should this be corked?
196
+ target js sync """
197
+ self_.internalJsApp_.publish(topic_, message_, false);
198
+ """
199
+
200
+ }
201
+
202
+ extend self[T]: WebRequest[T] {
203
+
204
+ closeConnection() {
205
+ self.internalCloseConnection = True
206
+ }
207
+
208
+ readRemoteAddress(): String
209
+ target js sync """
210
+ return new TextDecoder().decode(self_.internalJsResponse_.getRemoteAddressAsText())
211
+ """
212
+
213
+ readProxiedRemoteAddress(): String
214
+ target js sync """
215
+ return new TextDecoder().decode(self_.internalJsResponse_.getProxiedRemoteAddressAsText())
216
+ """
217
+
218
+ }
219
+
220
+ extend self: WebRequest[WebResponse] {
221
+
222
+ flush()
223
+ target js async """
224
+ if(!self_.internalResolveFlush_) {
225
+ self_.internalResolveFlush_ = () => {};
226
+ self_.internalJsResponse_.cork(() => {
227
+ self_.internalJsResponse_.writeStatus(self_.internalResponseStatus_);
228
+ self_.internalResponseHeaders_.forEach((value, key) =>
229
+ self_.internalJsResponse_.writeHeader(key, value)
230
+ );
231
+ self_.internalJsResponse_.onWritable(() => {
232
+ self_.internalResolveFlush_();
233
+ return true;
234
+ });
235
+ });
236
+ }
237
+ while(self_.internalResponseChunks_.array.length !== 0) {
238
+ await new Promise((resolve, reject) => {
239
+ self_.internalJsResponse_.cork(() => {
240
+ let backpressure = false;
241
+ let i = 0;
242
+ for(i = 0; i < self_.internalResponseChunks_.array.length && !backpressure; i++) {
243
+ let chunk = self_.internalResponseChunks_.array[i];
244
+ if(chunk && (chunk.byteLength || chunk.length)) {
245
+ if(typeof chunk !== 'string') {
246
+ chunk = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
247
+ } else if(self_.internalContentSize_ >= 0) {
248
+ chunk = new TextEncoder().encode(chunk);
249
+ }
250
+ if(self_.internalContentSize_ >= 0) {
251
+ const lastOffset = self_.internalJsResponse_.getWriteOffset();
252
+ const [ok, responded] = self_.internalJsResponse_.tryEnd(chunk, self_.internalContentSize_);
253
+ if(responded) self_.internalResponded_ = true;
254
+ if(!ok) {
255
+ const written = self_.internalJsResponse_.getWriteOffset() - lastOffset;
256
+ backpressure = true;
257
+ self_.internalResponseChunks_.array[i] =
258
+ new Uint8Array(chunk.buffer, chunk.byteOffset + written, chunk.byteLength - written);
259
+ i -= 1;
260
+ }
261
+ } else {
262
+ backpressure = !self_.internalJsResponse_.write(chunk);
263
+ }
264
+ }
265
+ }
266
+ if(i > 0) self_.internalResponseChunks_.array.splice(0, i);
267
+ if(!backpressure) {
268
+ resolve();
269
+ } else {
270
+ self_.internalResolveFlush_ = resolve;
271
+ }
272
+ });
273
+ });
274
+ }
275
+ """
276
+
277
+ flushText(data: String) {
278
+ self.writeText(data)
279
+ self.flush()
280
+ }
281
+
282
+ flushBuffer(data: Buffer) {
283
+ self.writeBuffer(data)
284
+ self.flush()
285
+ }
286
+
287
+ flushStream(data: Stream[Buffer]) {
288
+ data.each {self.flushBuffer(_)}
289
+ }
290
+
291
+ }
292
+
293
+ extend self: WebRequest[WebResponse] {
294
+
295
+ readText(encoding: String = "utf8"): String {
296
+ self.readStream(1073741823).toString(encoding)
297
+ }
298
+
299
+ readBuffer(): Buffer {
300
+ self.readStream(1073741823).toBuffer()
301
+ }
302
+
303
+ readStream(maxPendingBytes: Int = 65536): Stream[Buffer]
304
+ target js async """
305
+ const front = [];
306
+ const back = [];
307
+ let pendingBytes = 0
308
+ let closed = false;
309
+ let callResolve = null;
310
+ let callReject = null;
311
+ self_.internalJsResponse_.onData((chunk, isLast) => {
312
+ if(closed) return;
313
+ if(isLast) closed = true;
314
+ else chunk = cunk.slice();
315
+ pendingBytes += chunk.byteLength;
316
+ back.push(new DataView(chunk));
317
+ if(pendingBytes >= maxPendingBytes_) self_.internalJsResponse_.pause();
318
+ if(callResolve) callResolve();
319
+ });
320
+ const abort = () => {
321
+ $task.controller.signal.removeEventListener('abort', abort);
322
+ if(callReject) callReject();
323
+ }
324
+ $task.controller.signal.addEventListener('abort', abort);
325
+ return ff_core_Stream.Stream(
326
+ async () => {
327
+ while(true) {
328
+ ff_core_Task.Task_throwIfAborted($task);
329
+ if(front.length === 0) {
330
+ while(back.length !== 0) front.push(back.pop());
331
+ }
332
+ if(front.length !== 0) {
333
+ const chunk = front.pop();
334
+ const paused = pendingBytes >= maxPendingBytes_;
335
+ pendingBytes -= chunk.byteLength;
336
+ if(!closed && paused && pendingBytes < maxPendingBytes_) self_.internalJsResponse_.unpause();
337
+ return ff_core_Option.Some(chunk);
338
+ } else if(closed) {
339
+ return ff_core_Option.None();
340
+ } else {
341
+ await new Promise((resolve, reject) => {
342
+ callResolve = resolve;
343
+ callReject = reject;
344
+ });
345
+ callResolve = null;
346
+ callReject = null;
347
+ }
348
+ }
349
+ },
350
+ async () => {
351
+ closed = true;
352
+ ff_core_Task.Task_throwIfAborted($task);
353
+ if(callResolve) callResolve();
354
+ }
355
+ )
356
+ """
357
+
358
+ }
359
+
360
+ extend self[T]: WebRequest[T] {
361
+
362
+ needsWebSocket(): Bool
363
+ target js sync """
364
+ return self_.internalJsContext_ != null;
365
+ """
366
+
367
+ }
368
+
369
+ extend self: WebRequest[WebResponse] {
370
+
371
+ openWebSocket(): WebSocketConnection
372
+ target js async """
373
+ ff_core_Task.Task_throwIfAborted($task);
374
+ const userData = {};
375
+ const wrapper = {front: [], back: [], listeners: [], task: $task};
376
+ const promise = new Promise((resolve, reject) => {
377
+ const abort = () => {wrapper.webSocket = null; reject()};
378
+ userData.onOpen = ws => {wrapper.webSocket = ws; resolve(wrapper)};
379
+ userData.onClose = () => {
380
+ $task.controller.signal.removeEventListener('abort', abort);
381
+ abort();
382
+ for(let i = 0; i < wrapper.listeners.length; i++) wrapper.listeners[i]();
383
+ wrapper.listeners.length = 0;
384
+ };
385
+ userData.onMessage = (message, isBinary) => {
386
+ wrapper.back.push(isBinary ? new DataView(message) : new TextDecoder().decode(message));
387
+ for(let i = 0; i < wrapper.listeners.length; i++) wrapper.listeners[i]();
388
+ wrapper.listeners.length = 0;
389
+ };
390
+ $task.controller.signal.addEventListener('abort', abort);
391
+ });
392
+ self_.internalJsResponse_.cork(() => {
393
+ ff_core_Task.Task_throwIfAborted($task);
394
+ self_.internalJsResponse_.upgrade(
395
+ userData,
396
+ self_.internalHeaders_.get('sec-websocket-key'),
397
+ self_.internalHeaders_.get('sec-websocket-protocol'),
398
+ self_.internalHeaders_.get('sec-websocket-extensions'),
399
+ self_.internalJsContext_
400
+ )
401
+ });
402
+ return await promise;
403
+ """
404
+
405
+ }
406
+
407
+ extend self: WebSocketConnection {
408
+
409
+ readText(encoding: String = "utf8"): Option[String] {
410
+ self.readAny {_} {_.toString(encoding)}
411
+ }
412
+
413
+ readBuffer(): Option[Buffer] {
414
+ self.readAny {_.toBuffer()} {_}
415
+ }
416
+
417
+ readAny[T](fromText: String => T, fromBuffer: Buffer => T): Option[T]
418
+ target node async """
419
+ ff_core_Task.Task_throwIfAborted($task);
420
+ while(self_.front.length === 0) {
421
+ while(self_.back.length !== 0) {
422
+ self_.front.push(self_.back.pop());
423
+ }
424
+ if(self_.front.length !== 0) break;
425
+ if(self_.webSocket == null) return ff_core_Option.None();
426
+ let abort = null;
427
+ try {
428
+ await new Promise((resolve, reject) => {
429
+ self_.listeners.push(resolve);
430
+ if($task !== self_.task) {
431
+ abort = () => {
432
+ self_.listeners = self_.listeners.filter(l => l != resolve);
433
+ reject();
434
+ };
435
+ $task.controller.signal.addEventListener('abort', abort);
436
+ }
437
+ });
438
+ } finally {
439
+ if($task !== self_.task) {
440
+ $task.controller.signal.removeEventListener('abort', abort);
441
+ }
442
+ }
443
+ }
444
+ const data = self_.front.pop();
445
+ if(typeof data === 'string') return ff_core_Option.Some(await fromText_(data));
446
+ return ff_core_Option.Some(await fromBuffer_(data));
447
+ """
448
+
449
+ readStream(): Stream[Buffer] {
450
+ Stream {self.readBuffer()} {self.close()}
451
+ }
452
+
453
+ writeBuffer(message: Buffer)
454
+ target node async """
455
+ await internalSend_$(self_, () => self_.webSocket.send(message_.buffer, true) !== 2, $task);
456
+ """
457
+
458
+ writeText(message: String)
459
+ target node async """
460
+ await internalSend_$(self_, () => self_.webSocket.send(message_, false) !== 2, $task);
461
+ """
462
+
463
+ publishBuffer(topic: String, message: Buffer)
464
+ target node async """
465
+ await internalSend_$(self_, () => self_.webSocket.publish(topic_, message_.buffer, true), $task);
466
+ """
467
+
468
+ publishText(topic: String, message: String)
469
+ target node async """
470
+ await internalSend_$(self_, () => self_.webSocket.publish(topic_, message_, false), $task);
471
+ """
472
+
473
+ subscribe(topic: String)
474
+ target node async """
475
+ if(self_.webSocket == null) throw new Error("WebSocket is closed");
476
+ self_.webSocket.subscribe(topic_);
477
+ """
478
+
479
+ unsubscribe(topic: String)
480
+ target node async """
481
+ if(self_.webSocket == null) throw new Error("WebSocket is closed");
482
+ self_.webSocket.unsubscribe(topic_);
483
+ """
484
+
485
+ isSubscribedTo(topic: String): Bool
486
+ target node async """
487
+ if(self_.webSocket == null) throw new Error("WebSocket is closed");
488
+ return self_.webSocket.isSubscribed(topic_);
489
+ """
490
+
491
+ subscriptions(): Array[String]
492
+ target node async """
493
+ if(self_.webSocket == null) throw new Error("WebSocket is closed");
494
+ return self_.webSocket.getTopics();
495
+ """
496
+
497
+ close(): Unit
498
+ target node async """
499
+ if(self_.webSocket != null) {
500
+ self_.webSocket.close();
501
+ self_.webSocket = null;
502
+ }
503
+ """
504
+
505
+ }
506
+
507
+ internalSend[T](self: WebSocketConnection, send: () => Bool): JsValue
508
+ target node async """
509
+ let abort = null;
510
+ try {
511
+ await new Promise((resolve, reject) => {
512
+ if(self_.webSocket == null) new Error("WebSocket is closed");
513
+ else self_.webSocket.cork(() => {
514
+ if($task !== self_.task) {
515
+ abort = () => {
516
+ self_.listeners = self_.listeners.filter(l => l != resolve);
517
+ reject();
518
+ };
519
+ $task.controller.signal.addEventListener('abort', abort);
520
+ }
521
+ if(self_.webSocket == null) {
522
+ reject(new Error("WebSocket is closed"));
523
+ } else if(!send_()) {
524
+ reject(new Error("Dropped message due to backpressure"));
525
+ } else {
526
+ resolve();
527
+ }
528
+ })
529
+ });
530
+ } finally {
531
+ if($task !== self_.task) {
532
+ $task.controller.signal.removeEventListener('abort', abort);
533
+ }
534
+ }
535
+ """
536
+
537
+ internalStack[T](): Stack[T] {
538
+ Stack.make()
539
+ }
540
+
541
+ internalTextChunk(data: String): JsValue
542
+ target js sync """
543
+ return data_;
544
+ """
545
+
546
+ internalBufferChunk(data: Buffer): JsValue
547
+ target js sync """
548
+ return data_;
549
+ """
550
+
551
+ internalChunksToChunk(chunks: Stack[JsValue]): JsValue
552
+ target js sync """
553
+ const chunks = chunks_.array;
554
+ if(chunks.length === 0) return;
555
+ const firstIsString = typeof chunks[0] === 'string';
556
+ if(firstIsString && chunks.length === 1) return chunks[0];
557
+ if(firstIsString && chunks.every(c => typeof c === 'string')) {
558
+ let result = chunks[0];
559
+ for(let i = 1; i < chunks.length; i++) {
560
+ result += chunks[i];
561
+ }
562
+ return result;
563
+ }
564
+ let totalLength = 0;
565
+ for(let i = 0; i < chunks.length; i++) {
566
+ if(typeof chunks[i] === 'string') chunks[i] = new TextEncoder().encode(chunks[i]);
567
+ totalLength += chunks[i].byteLength;
568
+ }
569
+ const result = new Uint8Array(totalLength);
570
+ let offset = 0;
571
+ for(let i = 0; i < chunks.length; i++) {
572
+ const chunk = chunks[i];
573
+ const uint8Array = new Uint8Array(chunk.buffer, chunk.byteOffset, chunk.byteLength);
574
+ result.set(uint8Array, offset);
575
+ offset += chunk.byteLength;
576
+ }
577
+ return result;
578
+ """
579
+
580
+ parseMultipartFields(data: Buffer, contentType: String): Option[Array[MultipartField]]
581
+ target js sync """
582
+ const parts = getParts(data_, contentType_);
583
+ return parts == null ? ff_core_Option.None() : ff_core_Option.Some(parts);
584
+ """
585
+
586
+ extend self: MultipartField {
587
+
588
+ data(): Buffer
589
+ target js sync """
590
+ return new DataView(self_.data);
591
+ """
592
+
593
+ name(): String
594
+ target js sync """
595
+ return self_.name;
596
+ """
597
+
598
+ type(): Option[String]
599
+ target js sync """
600
+ return self_.type == null ? ff_core_Option.None() : ff_core_Option.Some(self_.type);
601
+ """
602
+
603
+ filename(): Option[String]
604
+ target js sync """
605
+ return self_.filename == null ? ff_core_Option.None() : ff_core_Option.Some(self_.filename);
606
+ """
607
+
608
+ }