firefly-compiler 0.4.7 → 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.
- package/compiler/Compiler.ff +2 -0
- package/compiler/Deriver.ff +7 -0
- package/core/Array.ff +17 -4
- package/core/AssetSystem.ff +1 -1
- package/core/BrowserSystem.ff +27 -0
- package/core/List.ff +4 -0
- package/core/Lock.ff +2 -2
- package/core/Random.ff +148 -0
- package/core/SourceLocation.ff +27 -0
- package/core/Task.ff +3 -0
- package/core/WebSocket.ff +127 -0
- package/httpserver/HttpServer.ff +1 -1
- package/lux/Css.ff +648 -0
- package/lux/CssTest.ff +48 -0
- package/lux/Lux.ff +33 -18
- package/lux/Main.ff +4 -1
- package/output/js/ff/compiler/Builder.mjs +4 -0
- package/output/js/ff/compiler/Compiler.mjs +5 -1
- package/output/js/ff/compiler/Dependencies.mjs +4 -0
- package/output/js/ff/compiler/Deriver.mjs +10 -2
- package/output/js/ff/compiler/Dictionaries.mjs +6 -0
- package/output/js/ff/compiler/Environment.mjs +10 -0
- package/output/js/ff/compiler/Inference.mjs +4 -0
- package/output/js/ff/compiler/JsEmitter.mjs +14 -0
- package/output/js/ff/compiler/JsImporter.mjs +4 -0
- package/output/js/ff/compiler/LspHook.mjs +6 -0
- package/output/js/ff/compiler/Main.mjs +16 -0
- package/output/js/ff/compiler/Parser.mjs +8 -0
- package/output/js/ff/compiler/Patterns.mjs +8 -0
- package/output/js/ff/compiler/Resolver.mjs +4 -0
- package/output/js/ff/compiler/Substitution.mjs +4 -0
- package/output/js/ff/compiler/Syntax.mjs +132 -0
- package/output/js/ff/compiler/Token.mjs +56 -0
- package/output/js/ff/compiler/Tokenizer.mjs +4 -0
- package/output/js/ff/compiler/Unification.mjs +10 -0
- package/output/js/ff/compiler/Wildcards.mjs +4 -0
- package/output/js/ff/compiler/Workspace.mjs +8 -0
- package/output/js/ff/core/Any.mjs +4 -0
- package/output/js/ff/core/Array.mjs +27 -1
- package/output/js/ff/core/AssetSystem.mjs +6 -2
- package/output/js/ff/core/Atomic.mjs +4 -0
- package/output/js/ff/core/Bool.mjs +4 -0
- package/output/js/ff/core/Box.mjs +4 -0
- package/output/js/ff/core/BrowserSystem.mjs +55 -0
- package/output/js/ff/core/Buffer.mjs +4 -0
- package/output/js/ff/core/BuildSystem.mjs +4 -0
- package/output/js/ff/core/Channel.mjs +4 -0
- package/output/js/ff/core/Char.mjs +4 -0
- package/output/js/ff/core/Core.mjs +4 -0
- package/output/js/ff/core/Duration.mjs +4 -0
- package/output/js/ff/core/Equal.mjs +4 -0
- package/output/js/ff/core/Error.mjs +4 -0
- package/output/js/ff/core/FileHandle.mjs +4 -0
- package/output/js/ff/core/Float.mjs +4 -0
- package/output/js/ff/core/HttpClient.mjs +4 -0
- package/output/js/ff/core/Instant.mjs +4 -0
- package/output/js/ff/core/Int.mjs +4 -0
- package/output/js/ff/core/IntMap.mjs +4 -0
- package/output/js/ff/core/JsSystem.mjs +4 -0
- package/output/js/ff/core/JsValue.mjs +4 -0
- package/output/js/ff/core/List.mjs +12 -0
- package/output/js/ff/core/Lock.mjs +6 -2
- package/output/js/ff/core/Log.mjs +4 -0
- package/output/js/ff/core/Map.mjs +4 -0
- package/output/js/ff/core/NodeSystem.mjs +4 -0
- package/output/js/ff/core/Nothing.mjs +4 -0
- package/output/js/ff/core/Option.mjs +8 -0
- package/output/js/ff/core/Ordering.mjs +4 -0
- package/output/js/ff/core/Pair.mjs +6 -0
- package/output/js/ff/core/Path.mjs +4 -0
- package/output/js/ff/core/Random.mjs +340 -0
- package/output/js/ff/core/RbMap.mjs +4 -0
- package/output/js/ff/core/Serializable.mjs +4 -0
- package/output/js/ff/core/Set.mjs +4 -0
- package/output/js/ff/core/Show.mjs +4 -0
- package/output/js/ff/core/SourceLocation.mjs +45 -1
- package/output/js/ff/core/Stack.mjs +4 -0
- package/output/js/ff/core/Stream.mjs +4 -0
- package/output/js/ff/core/String.mjs +4 -0
- package/output/js/ff/core/StringMap.mjs +4 -0
- package/output/js/ff/core/Task.mjs +7 -1
- package/output/js/ff/core/Try.mjs +4 -0
- package/output/js/ff/core/Unit.mjs +4 -0
- package/output/js/ff/core/WebSocket.mjs +198 -0
- package/package.json +1 -1
- package/vscode/package.json +1 -1
- package/webserver/.firefly/include/package-lock.json +16 -0
- package/webserver/.firefly/include/package.json +5 -0
- package/webserver/.firefly/package.ff +2 -0
- package/webserver/WebServer.ff +608 -0
package/compiler/Compiler.ff
CHANGED
|
@@ -94,6 +94,7 @@ coreImports: List[DImport] =
|
|
|
94
94
|
"Ordering"
|
|
95
95
|
"Pair"
|
|
96
96
|
"Path"
|
|
97
|
+
"Random"
|
|
97
98
|
"Serializable"
|
|
98
99
|
"Set"
|
|
99
100
|
"Show"
|
|
@@ -105,6 +106,7 @@ coreImports: List[DImport] =
|
|
|
105
106
|
"Task"
|
|
106
107
|
"Try"
|
|
107
108
|
"Unit"
|
|
109
|
+
"WebSocket"
|
|
108
110
|
].map {moduleName =>
|
|
109
111
|
DImport(
|
|
110
112
|
at = Location("<prelude>", 1, 1)
|
package/compiler/Deriver.ff
CHANGED
|
@@ -516,6 +516,12 @@ extend self: Deriver {
|
|
|
516
516
|
let variantName = modulePrefix + "." + variant.name
|
|
517
517
|
let fields = [...declaration.commonFields, ...variant.fields]
|
|
518
518
|
let updateChecksum = self.makeUpdateChecksum(at, variantName, declaration, variant)
|
|
519
|
+
let autoResize = self.makeMethodCall(
|
|
520
|
+
at = at
|
|
521
|
+
target = EVariable(at, "serialization")
|
|
522
|
+
methodName = "autoResize"
|
|
523
|
+
arguments = [EInt(at, "1")]
|
|
524
|
+
)
|
|
519
525
|
let setVariantIndex = self.makeMethodCall(
|
|
520
526
|
at = at
|
|
521
527
|
target = EField(at, False, EVariable(at, "serialization"), "buffer")
|
|
@@ -533,6 +539,7 @@ extend self: Deriver {
|
|
|
533
539
|
patterns = [wildcardPattern, PVariantAs(at, variantName, at, Some("v"))]
|
|
534
540
|
guards = []
|
|
535
541
|
body = [
|
|
542
|
+
autoResize
|
|
536
543
|
setVariantIndex
|
|
537
544
|
EAssignField(at, "+", EVariable(at, "serialization"), "offset", EInt(at, "1"))
|
|
538
545
|
...fieldSerializations
|
package/core/Array.ff
CHANGED
|
@@ -14,6 +14,13 @@ fillBy[T](size: Int, body: Int => T): Array[T]
|
|
|
14
14
|
target js sync """
|
|
15
15
|
return Array.from({length: size_}, (_, i) => body_(i));
|
|
16
16
|
"""
|
|
17
|
+
target js async """
|
|
18
|
+
const array = new Array(size_);
|
|
19
|
+
for(let i = 0; i < size_; i++) {
|
|
20
|
+
array[i] = await(body_(_i));
|
|
21
|
+
}
|
|
22
|
+
return array;
|
|
23
|
+
"""
|
|
17
24
|
|
|
18
25
|
range(size: Int): Array[Int]
|
|
19
26
|
target js sync """
|
|
@@ -46,13 +53,19 @@ extend self[T]: Array[T] {
|
|
|
46
53
|
return self_[index_]
|
|
47
54
|
"""
|
|
48
55
|
|
|
49
|
-
first(): Option[T] {
|
|
56
|
+
first(): Option[T] {self.get(0)}
|
|
57
|
+
|
|
58
|
+
last(): Option[T] {self.get(self.size() - 1)}
|
|
59
|
+
|
|
60
|
+
grabFirst(): T {self.grab(0)}
|
|
50
61
|
|
|
51
|
-
|
|
62
|
+
grabLast(): T {self.grab(self.size() - 1)}
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
takeFirst(count: Int = 1): Array[T]
|
|
65
|
+
target js sync "return self_.slice(0, count_)"
|
|
54
66
|
|
|
55
|
-
|
|
67
|
+
takeLast(count: Int = 1): Array[T]
|
|
68
|
+
target js sync "return self_.slice(-count_)"
|
|
56
69
|
|
|
57
70
|
dropFirst(count: Int = 1): Array[T]
|
|
58
71
|
target js sync "return self_.slice(count_)"
|
package/core/AssetSystem.ff
CHANGED
|
@@ -22,7 +22,7 @@ extend self: AssetSystem {
|
|
|
22
22
|
assets(path: String): AssetSystem {
|
|
23
23
|
let prefix = if(path.endsWith("/")) {path} else {path + "/"}
|
|
24
24
|
let streams = self.files.pairs().collect {
|
|
25
|
-
| Pair(p, s) {p.startsWith(prefix)} => Some(Pair(p.dropFirst(prefix.size()), s))
|
|
25
|
+
| Pair(p, s) {p.startsWith(prefix)} => Some(Pair(p.dropFirst(prefix.size() - 1), s))
|
|
26
26
|
| _ => None
|
|
27
27
|
}
|
|
28
28
|
AssetSystem(streams.toMap())
|
package/core/BrowserSystem.ff
CHANGED
|
@@ -5,10 +5,37 @@ extend self: BrowserSystem {
|
|
|
5
5
|
httpClient(): HttpClient
|
|
6
6
|
target js async "return null"
|
|
7
7
|
|
|
8
|
+
webSocket(url: String): WebSocket {
|
|
9
|
+
WebSocket.internalOpenBrowserWebSocket(self, url)
|
|
10
|
+
}
|
|
11
|
+
|
|
8
12
|
mainTask(): Task
|
|
9
13
|
target js async "return self_.task_"
|
|
10
14
|
|
|
11
15
|
js(): JsSystem
|
|
12
16
|
target js async "return typeof globalThis !== 'undefined' ? globalThis : window"
|
|
17
|
+
|
|
18
|
+
url(): String
|
|
19
|
+
target js async """
|
|
20
|
+
return location.href;
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
urlPath(): String
|
|
24
|
+
target js async """
|
|
25
|
+
return location.pathname;
|
|
26
|
+
"""
|
|
13
27
|
|
|
28
|
+
urlQuery(name: String): Option[String]
|
|
29
|
+
target js async """
|
|
30
|
+
const param = new URLSearchParams(location.search).get(name_)
|
|
31
|
+
if(param == null) return ff_core_Option.None();
|
|
32
|
+
return ff_core_Option.Some(param);
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
urlFragment(): Option[String]
|
|
36
|
+
target js async """
|
|
37
|
+
if(!location.hash.startsWith('#')) return ff_core_Option.None();
|
|
38
|
+
return ff_core_Option.Some(location.hash.slice(1));
|
|
39
|
+
"""
|
|
40
|
+
|
|
14
41
|
}
|
package/core/List.ff
CHANGED
package/core/Lock.ff
CHANGED
|
@@ -76,7 +76,7 @@ extend self: LockCondition {
|
|
|
76
76
|
}
|
|
77
77
|
const level = self_.lock.level
|
|
78
78
|
self_.lock.level = 1
|
|
79
|
-
await
|
|
79
|
+
await ff_core_Lock.Lock_release$(self_.lock)
|
|
80
80
|
try {
|
|
81
81
|
await new Promise((resolve, reject) => {
|
|
82
82
|
$task.controller.signal.addEventListener('abort', reject)
|
|
@@ -92,7 +92,7 @@ extend self: LockCondition {
|
|
|
92
92
|
let acquired = false
|
|
93
93
|
while(!acquired) {
|
|
94
94
|
try {
|
|
95
|
-
await
|
|
95
|
+
await ff_core_Lock.Lock_acquire$(self_.lock)
|
|
96
96
|
self_.lock.level = level
|
|
97
97
|
acquired = true
|
|
98
98
|
} catch(e) {
|
package/core/Random.ff
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
class Random {}
|
|
2
|
+
|
|
3
|
+
// Using Alea PRNG by Johannes Baagøe <baagoe@baagoe.com>, 2010
|
|
4
|
+
// Typical use: Random.seedInstant(system.task().now())
|
|
5
|
+
|
|
6
|
+
seedInt(seed: Int): Random {
|
|
7
|
+
seedFloat(seed.toFloat())
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
seedFloat(seed: Float): Random {
|
|
11
|
+
let buffer = Buffer.make(8)
|
|
12
|
+
buffer.setFloat64(0, seed)
|
|
13
|
+
seedBuffer(buffer)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
seedInstant(seed: Instant): Random {
|
|
17
|
+
seedFloat(seed.since1970.seconds)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
seedBuffer(buffer: Buffer): Random
|
|
21
|
+
target js sync """
|
|
22
|
+
var n = 0xefc8249d;
|
|
23
|
+
function mash(data) {
|
|
24
|
+
for(var i = 0; i < data.byteLength; i++) {
|
|
25
|
+
n += data.getUint8(i);
|
|
26
|
+
var h = 0.02519603282416938 * n;
|
|
27
|
+
n = h >>> 0;
|
|
28
|
+
h -= n;
|
|
29
|
+
h *= n;
|
|
30
|
+
n = h >>> 0;
|
|
31
|
+
h -= n;
|
|
32
|
+
n += h * 0x100000000; // 2^32
|
|
33
|
+
}
|
|
34
|
+
return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
|
|
35
|
+
}
|
|
36
|
+
var space = new DataView(new Uint8Array([32]).buffer);
|
|
37
|
+
var r = {
|
|
38
|
+
s0: mash(space),
|
|
39
|
+
s1: mash(space),
|
|
40
|
+
s2: mash(space),
|
|
41
|
+
c: 1,
|
|
42
|
+
spareGauss: NaN
|
|
43
|
+
};
|
|
44
|
+
r.s0 -= mash(buffer_);
|
|
45
|
+
if(r.s0 < 0) r.s0 += 1;
|
|
46
|
+
r.s1 -= mash(buffer_);
|
|
47
|
+
if(r.s1 < 0) r.s1 += 1;
|
|
48
|
+
r.s2 -= mash(buffer_);
|
|
49
|
+
if(r.s2 < 0) r.s2 += 1;
|
|
50
|
+
return r;
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
extend self: Random {
|
|
54
|
+
|
|
55
|
+
copy(): Random
|
|
56
|
+
target js sync """
|
|
57
|
+
return {...self_};
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
nextInt(from: Int, until: Int): Int
|
|
61
|
+
target js sync """
|
|
62
|
+
return Random_nextFloat(self_, from_, until_) | 0;
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
nextFloat(from: Float, until: Float): Float
|
|
66
|
+
target js sync """
|
|
67
|
+
var t = 2091639 * self_.s0 + self_.c * 2.3283064365386963e-10; // 2^-32
|
|
68
|
+
self_.s0 = self_.s1;
|
|
69
|
+
self_.s1 = self_.s2;
|
|
70
|
+
var uniform = self_.s2 = t - (self_.c = t | 0);
|
|
71
|
+
return from_ + uniform * (until_ - from_);
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
nextBool(): Bool {
|
|
75
|
+
self.nextInt(0, 2) == 0
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
nextBytes(buffer: Buffer, start: Int, stop: Int): Unit {
|
|
79
|
+
start.until(stop).each {i =>
|
|
80
|
+
buffer.setUint8(i, self.nextInt(0, 256))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
nextGauss(mean: Float, standardDeviation: Float): Float
|
|
85
|
+
target js sync """
|
|
86
|
+
if(!isNaN(self_.spareGauss)) {
|
|
87
|
+
const result = self_.spareGauss * standardDeviation_ + mean_;
|
|
88
|
+
self_.spareGauss = NaN;
|
|
89
|
+
return result;
|
|
90
|
+
} else {
|
|
91
|
+
let u = 0.5, v = 0.5, s = 0.5;
|
|
92
|
+
do {
|
|
93
|
+
u = Random_nextFloat(self_, 0.0, 1.0) * 2 - 1;
|
|
94
|
+
v = Random_nextFloat(self_, 0.0, 1.0) * 2 - 1;
|
|
95
|
+
s = u * u + v * v;
|
|
96
|
+
} while(s >= 1 || s == 0);
|
|
97
|
+
s = Math.sqrt(-2.0 * Math.log(s) / s);
|
|
98
|
+
self_.spareGauss = v * s;
|
|
99
|
+
return mean_ + standardDeviation_ * u * s;
|
|
100
|
+
}
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
shuffleStack[T](stack: Stack[T]): Unit {
|
|
104
|
+
0.until(stack.size() - 1).each {i =>
|
|
105
|
+
let j = self.nextInt(0, stack.size() - i) + i
|
|
106
|
+
let value = stack.grab(i)
|
|
107
|
+
stack.set(i, stack.grab(j))
|
|
108
|
+
stack.set(j, value)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
shuffleArray[T](array: Array[T]): Array[T] {
|
|
113
|
+
let stack = array.toStack()
|
|
114
|
+
self.shuffleStack(array.toStack())
|
|
115
|
+
stack.drain()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
shuffleList[T](list: List[T]): List[T] {
|
|
119
|
+
let stack = list.toStack()
|
|
120
|
+
self.shuffleStack(list.toStack())
|
|
121
|
+
stack.toList()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
sampleStack[T](count: Int, stack: Stack[T], body: T => Unit): Unit {
|
|
125
|
+
self.shuffleArray(stack.toArray()).takeFirst(count).each {body(_)}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
sampleArray[T](count: Int, array: Array[T]): Array[T] {
|
|
129
|
+
self.shuffleArray(array).takeFirst(count)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
sampleList[T](count: Int, list: List[T]): List[T] {
|
|
133
|
+
self.shuffleList(list).takeFirst(count)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
grabStack[T](stack: Stack[T]): T {
|
|
137
|
+
stack.grab(self.nextInt(0, stack.size()))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
grabArray[T](array: Array[T]): T {
|
|
141
|
+
array.grab(self.nextInt(0, array.size()))
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
grabList[T](list: Stack[T]): T {
|
|
145
|
+
list.grab(self.nextInt(0, list.size()))
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
}
|
package/core/SourceLocation.ff
CHANGED
|
@@ -39,3 +39,30 @@ extend self: SourceLocation {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
instance SourceLocation: Show {
|
|
44
|
+
show(self: SourceLocation): String {
|
|
45
|
+
self.location
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
instance SourceLocation: Equal {
|
|
50
|
+
equals(a: SourceLocation, b: SourceLocation): Bool {
|
|
51
|
+
a.location == b.location
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
instance SourceLocation: Order {
|
|
56
|
+
compare(a: SourceLocation, b: SourceLocation): Ordering {
|
|
57
|
+
Ordering.compare(a.location, b.location)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
instance SourceLocation: Serializable {
|
|
62
|
+
serializeUsing(serialization: Serialization, self: SourceLocation) {
|
|
63
|
+
Serializable.serializeUsing(serialization, self.location)
|
|
64
|
+
}
|
|
65
|
+
deserializeUsing(serialization: Serialization): SourceLocation {
|
|
66
|
+
SourceLocation(Serializable.deserializeUsing(serialization))
|
|
67
|
+
}
|
|
68
|
+
}
|
package/core/Task.ff
CHANGED
|
@@ -46,6 +46,9 @@ extend self: Task {
|
|
|
46
46
|
target js async """
|
|
47
47
|
self_.controller.abort()
|
|
48
48
|
"""
|
|
49
|
+
target js sync """
|
|
50
|
+
self_.controller.abort()
|
|
51
|
+
"""
|
|
49
52
|
|
|
50
53
|
// Create a blocking channel with the specified capacity. The channel is not tied to the task.
|
|
51
54
|
channel[T](capacity: Int = 0): Channel[T]
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
capability WebSocket {}
|
|
2
|
+
|
|
3
|
+
extend self: WebSocket {
|
|
4
|
+
|
|
5
|
+
readText(encoding: String = "utf8"): Option[String] {
|
|
6
|
+
self.readAny {_} {_.toString(encoding)}
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
readBuffer(): Option[Buffer] {
|
|
10
|
+
self.readAny {_.toBuffer()} {_}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
readAny[T](fromText: String => T, fromBuffer: Buffer => T): Option[T]
|
|
14
|
+
target browser async """
|
|
15
|
+
ff_core_Task.Task_throwIfAborted($task);
|
|
16
|
+
while(self_.ffFront.length === 0) {
|
|
17
|
+
while(self_.ffBack.length !== 0) {
|
|
18
|
+
self_.ffFront.push(self_.ffBack.pop());
|
|
19
|
+
}
|
|
20
|
+
if(self_.ffFront.length !== 0) break;
|
|
21
|
+
if(self_.readyState === 3) return ff_core_Option.None();
|
|
22
|
+
let abort = null;
|
|
23
|
+
try {
|
|
24
|
+
await new Promise((resolve, reject) => {
|
|
25
|
+
self_.ffListeners.push(resolve);
|
|
26
|
+
if($task !== self_.ffTask) {
|
|
27
|
+
abort = () => {
|
|
28
|
+
self_.ffListeners = self_.ffListeners.filter(l => l != resolve);
|
|
29
|
+
reject();
|
|
30
|
+
};
|
|
31
|
+
$task.controller.signal.addEventListener('abort', abort);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
} finally {
|
|
35
|
+
if($task !== self_.ffTask) {
|
|
36
|
+
$task.controller.signal.removeEventListener('abort', abort);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const message = self_.ffFront.pop();
|
|
41
|
+
if(!(message instanceof MessageEvent)) throw new Error("WebSocket error");
|
|
42
|
+
const data = message.data;
|
|
43
|
+
if(typeof data === 'string') return ff_core_Option.Some(await fromText_(data));
|
|
44
|
+
return ff_core_Option.Some(await fromBuffer_(new DataView(data)));
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
writeBuffer(data: Buffer): Unit
|
|
48
|
+
target browser async """
|
|
49
|
+
ff_core_Task.Task_throwIfAborted($task);
|
|
50
|
+
self_.send(data_);
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
writeText(data: String): Unit
|
|
54
|
+
target browser async """
|
|
55
|
+
ff_core_Task.Task_throwIfAborted($task);
|
|
56
|
+
self_.send(data_);
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
close(code: Int = 1000, reason: String = ""): Unit
|
|
60
|
+
target browser async """
|
|
61
|
+
if(self_.readyState === 2 || self_.readyState === 3) return;
|
|
62
|
+
await new Promise((resolve, reject) => {
|
|
63
|
+
self_.addEventListener('close', resolve);
|
|
64
|
+
self_.close(code_, reason !== "" ? reason : void 0);
|
|
65
|
+
});
|
|
66
|
+
return;
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
isOpen(): Bool
|
|
70
|
+
target browser async """
|
|
71
|
+
return self_.readyState === 1;
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
isClosing(): Bool
|
|
75
|
+
target browser async """
|
|
76
|
+
return self_.readyState === 2;
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
isClosed(): Bool
|
|
80
|
+
target browser async """
|
|
81
|
+
return self_.readyState === 3;
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
internalOpenBrowserWebSocket(browserSystem: BrowserSystem, url: String): WebSocket
|
|
87
|
+
target browser async """
|
|
88
|
+
if(typeof location !== 'undefined' && !url_.includes("://")) {
|
|
89
|
+
if(location.href && location.href.startsWith("http")) {
|
|
90
|
+
url_ = new URL(url_, location.href).href.replace(/^http/, 'ws');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const socket = new WebSocket(url_);
|
|
94
|
+
socket.binaryType = "arraybuffer";
|
|
95
|
+
socket.ffFront = [];
|
|
96
|
+
socket.ffBack = [];
|
|
97
|
+
socket.ffListeners = [];
|
|
98
|
+
socket.ffTask = $task;
|
|
99
|
+
$task.controller.signal.addEventListener('message', m => socket.ffBack.push(m));
|
|
100
|
+
const abort = () => socket.close();
|
|
101
|
+
try {
|
|
102
|
+
await new Promise((resolve, reject) => {
|
|
103
|
+
socket.onopen = resolve;
|
|
104
|
+
socket.onerror = reject;
|
|
105
|
+
$task.controller.signal.addEventListener('abort', abort);
|
|
106
|
+
});
|
|
107
|
+
} finally {
|
|
108
|
+
socket.onopen = null;
|
|
109
|
+
socket.onerror = null;
|
|
110
|
+
}
|
|
111
|
+
socket.addEventListener('close', () => {
|
|
112
|
+
$task.controller.signal.removeEventListener('abort', abort);
|
|
113
|
+
for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
|
|
114
|
+
socket.ffListeners.length = 0;
|
|
115
|
+
});
|
|
116
|
+
socket.addEventListener('message', m => {
|
|
117
|
+
socket.ffBack.push(m);
|
|
118
|
+
for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
|
|
119
|
+
socket.ffListeners.length = 0;
|
|
120
|
+
});
|
|
121
|
+
socket.addEventListener('error', e => {
|
|
122
|
+
socket.ffBack.push(e);
|
|
123
|
+
for(let i = 0; i < socket.ffListeners.length; i++) socket.ffListeners[i]();
|
|
124
|
+
socket.ffListeners.length = 0;
|
|
125
|
+
});
|
|
126
|
+
return socket;
|
|
127
|
+
"""
|
package/httpserver/HttpServer.ff
CHANGED
|
@@ -146,7 +146,7 @@ extend self: HttpResponse {
|
|
|
146
146
|
target node async """
|
|
147
147
|
import * as http from 'http'
|
|
148
148
|
ff_core_Task.Task_throwIfAborted($task)
|
|
149
|
-
if(!self_.write(buffer_)) {
|
|
149
|
+
if(!self_.write(Buffer.from(buffer_.buffer))) {
|
|
150
150
|
let reject = null;
|
|
151
151
|
await new Promise((resolve, reject) => {
|
|
152
152
|
const abort = () => {
|