@4players/odin-common 1.1.0 → 1.2.0
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/CHANGELOG.md +12 -0
- package/lib/cjs/utility/rpc.js +187 -1
- package/lib/cjs/utility/selector.js +5 -5
- package/lib/cjs/utility/strand.js +7 -7
- package/lib/esm/utility/rpc.js +184 -1
- package/lib/esm/utility/selector.js +5 -5
- package/lib/esm/utility/strand.js +7 -7
- package/lib/rpc/commands.d.ts +217 -2
- package/lib/rpc/notifications.d.ts +779 -2
- package/lib/utility/json.spec.d.ts +1 -1
- package/lib/utility/result.spec.d.ts +4 -0
- package/lib/utility/rpc.d.ts +40 -1
- package/lib/utility/rpc.spec.d.ts +7 -0
- package/lib/utility/selector.d.ts +3 -3
- package/lib/utility/selector.spec.d.ts +4 -0
- package/lib/utility/strand.d.ts +2 -2
- package/lib/utility/strand.spec.d.ts +4 -0
- package/lib/utility/url.spec.d.ts +4 -0
- package/lib/utility/validation.spec.d.ts +3 -0
- package/package.json +1 -1
- package/testy.json +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
- Added both typed and untyped rpc connection class
|
|
6
|
+
|
|
7
|
+
## 1.1.2
|
|
8
|
+
|
|
9
|
+
- Added missing export
|
|
10
|
+
|
|
11
|
+
## 1.1.1
|
|
12
|
+
|
|
13
|
+
- Updated npm ignore patterns
|
|
14
|
+
|
|
3
15
|
## 1.1.0
|
|
4
16
|
|
|
5
17
|
- Added utility functions shared across browser frontend/backend apis
|
package/lib/cjs/utility/rpc.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
/* eslint-disable no-dupe-class-members */
|
|
3
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
+
});
|
|
11
|
+
};
|
|
2
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseRpcMessage = exports.parseRpc = exports.msgpackDecode = exports.msgpackEncode = void 0;
|
|
13
|
+
exports.isKnownRpcMessage = exports.parseRpcMessage = exports.parseRpc = exports.TypedRpcConnection = exports.RpcConnection = exports.msgpackDecode = exports.msgpackEncode = void 0;
|
|
4
14
|
const msgpack = require("@msgpack/msgpack");
|
|
5
15
|
const serialization_1 = require("../schema/serialization");
|
|
6
16
|
const result_1 = require("./result");
|
|
@@ -12,6 +22,181 @@ function msgpackDecode(buffer) {
|
|
|
12
22
|
return msgpack.decode(buffer);
|
|
13
23
|
}
|
|
14
24
|
exports.msgpackDecode = msgpackDecode;
|
|
25
|
+
class RpcConnection {
|
|
26
|
+
get closed() {
|
|
27
|
+
return this._Close.signal.aborted;
|
|
28
|
+
}
|
|
29
|
+
get closeSignal() {
|
|
30
|
+
return this._Close.signal;
|
|
31
|
+
}
|
|
32
|
+
constructor(webSocket) {
|
|
33
|
+
this.webSocket = webSocket;
|
|
34
|
+
this._SendQueue = [];
|
|
35
|
+
this._RecvQueue = [];
|
|
36
|
+
this._Requests = new Map();
|
|
37
|
+
this._Close = new AbortController();
|
|
38
|
+
this._NextId = 1;
|
|
39
|
+
this.webSocket.addEventListener('open', this._OnWebSocketOpen.bind(this));
|
|
40
|
+
this.webSocket.addEventListener('close', this.close.bind(this));
|
|
41
|
+
this.webSocket.addEventListener('message', this._OnWebSocketMessage.bind(this));
|
|
42
|
+
this.webSocket.binaryType = 'arraybuffer';
|
|
43
|
+
this._Close.signal.addEventListener('abort', this._Cleanup.bind(this), {
|
|
44
|
+
once: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
request(name, properties, timeout) {
|
|
48
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
49
|
+
if (this.closed) {
|
|
50
|
+
return Promise.resolve('closed');
|
|
51
|
+
}
|
|
52
|
+
const id = this._NextId;
|
|
53
|
+
const timeoutId = timeout === undefined
|
|
54
|
+
? undefined
|
|
55
|
+
: setTimeout(() => this._TimeoutRequest(id), timeout);
|
|
56
|
+
this._Send([0, id, name, properties]);
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
this._Requests.set(id, { resolve, timeoutId });
|
|
59
|
+
this._NextId += 1;
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
notify(name, properties) {
|
|
64
|
+
if (this.closed === false) {
|
|
65
|
+
this._Send([2, name, properties]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
recv() {
|
|
69
|
+
if (this.closed === false) {
|
|
70
|
+
return new Promise((resolve) => this._RecvQueue.push(resolve));
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return Promise.resolve('closed');
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
close() {
|
|
77
|
+
this._Close.abort();
|
|
78
|
+
}
|
|
79
|
+
_TimeoutRequest(requestId) {
|
|
80
|
+
const request = this._Requests.get(requestId);
|
|
81
|
+
if (request === undefined)
|
|
82
|
+
return;
|
|
83
|
+
this._Requests.delete(requestId);
|
|
84
|
+
clearTimeout(request.timeoutId);
|
|
85
|
+
request.resolve('timeout');
|
|
86
|
+
}
|
|
87
|
+
_Send(message) {
|
|
88
|
+
this.webSocket.send(msgpack.encode(message));
|
|
89
|
+
}
|
|
90
|
+
_OnWebSocketOpen(ev) {
|
|
91
|
+
for (const data in this._SendQueue) {
|
|
92
|
+
this.webSocket.send(data);
|
|
93
|
+
}
|
|
94
|
+
this._SendQueue.length = 0;
|
|
95
|
+
}
|
|
96
|
+
_OnWebSocketMessage(ev) {
|
|
97
|
+
const rpc = parseRpc(ev.data);
|
|
98
|
+
if (rpc.type !== 'Success') {
|
|
99
|
+
this.webSocket.close();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
switch (rpc.value[0]) {
|
|
103
|
+
case 0:
|
|
104
|
+
this._Send([1, rpc.value[1], 'not supported', undefined]);
|
|
105
|
+
break;
|
|
106
|
+
case 1:
|
|
107
|
+
this._OnResponse(rpc.value[1], rpc.value[2], rpc.value[3]);
|
|
108
|
+
break;
|
|
109
|
+
case 2:
|
|
110
|
+
this._OnEvent(rpc.value[1], rpc.value[2]);
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
_OnResponse(id, error, result) {
|
|
115
|
+
const request = this._Requests.get(id);
|
|
116
|
+
if (request === undefined)
|
|
117
|
+
return;
|
|
118
|
+
clearTimeout(request.timeoutId);
|
|
119
|
+
request.resolve(error === null ? (0, result_1.success)(result) : (0, result_1.failure)(error));
|
|
120
|
+
}
|
|
121
|
+
_OnEvent(name, properties) {
|
|
122
|
+
const receiver = this._RecvQueue.shift();
|
|
123
|
+
if (receiver !== undefined) {
|
|
124
|
+
receiver({ name, properties });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
_Cleanup() {
|
|
128
|
+
for (const request of this._Requests.values()) {
|
|
129
|
+
clearTimeout(request.timeoutId);
|
|
130
|
+
request.resolve('closed');
|
|
131
|
+
}
|
|
132
|
+
this._Requests.clear();
|
|
133
|
+
for (const accept of this._RecvQueue) {
|
|
134
|
+
accept('closed');
|
|
135
|
+
}
|
|
136
|
+
this._RecvQueue.length = 0;
|
|
137
|
+
this._SendQueue.length = 0;
|
|
138
|
+
this.webSocket.close();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.RpcConnection = RpcConnection;
|
|
142
|
+
class TypedRpcConnection extends RpcConnection {
|
|
143
|
+
constructor(webSocket, _Commands, _Events) {
|
|
144
|
+
super(webSocket);
|
|
145
|
+
this._Commands = _Commands;
|
|
146
|
+
this._Events = _Events;
|
|
147
|
+
}
|
|
148
|
+
request(name, properties, timeout) {
|
|
149
|
+
const _super = Object.create(null, {
|
|
150
|
+
request: { get: () => super.request }
|
|
151
|
+
});
|
|
152
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
153
|
+
properties = this._Commands[name].request.parse(properties);
|
|
154
|
+
const response = yield (timeout !== undefined
|
|
155
|
+
? _super.request.call(this, name, properties, timeout)
|
|
156
|
+
: _super.request.call(this, name, properties));
|
|
157
|
+
if (response === 'closed' ||
|
|
158
|
+
response === 'timeout' ||
|
|
159
|
+
response.type === 'Failure') {
|
|
160
|
+
return response;
|
|
161
|
+
}
|
|
162
|
+
const parsed = this._Commands[name].response.safeParse(response.value);
|
|
163
|
+
if (parsed.success) {
|
|
164
|
+
response.value = parsed.data;
|
|
165
|
+
return response;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
return (0, result_1.failure)(`response schema failed validation: ${parsed.error}`);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
notify(name, properties) {
|
|
173
|
+
properties = this._Commands[name].request.parse(properties);
|
|
174
|
+
super.notify(name, properties);
|
|
175
|
+
}
|
|
176
|
+
recv() {
|
|
177
|
+
const _super = Object.create(null, {
|
|
178
|
+
recv: { get: () => super.recv }
|
|
179
|
+
});
|
|
180
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
const event = yield _super.recv.call(this);
|
|
182
|
+
if (event === 'closed')
|
|
183
|
+
return event;
|
|
184
|
+
const parsed = parseRpcMessage(this._Events, event);
|
|
185
|
+
if (parsed !== undefined) {
|
|
186
|
+
return parsed;
|
|
187
|
+
}
|
|
188
|
+
if (!isKnownRpcMessage(this._Events, event.name)) {
|
|
189
|
+
console.warn(`dropping unknown event ${event.name}`);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
console.error(`schema validation for ${event.name} failed; closing connection`);
|
|
193
|
+
this.close();
|
|
194
|
+
}
|
|
195
|
+
return 'closed';
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
exports.TypedRpcConnection = TypedRpcConnection;
|
|
15
200
|
function parseRpc(buffer) {
|
|
16
201
|
try {
|
|
17
202
|
return (0, result_1.success)(serialization_1.MessagePackRpcSchema.parse(msgpackDecode(buffer)));
|
|
@@ -37,3 +222,4 @@ exports.parseRpcMessage = parseRpcMessage;
|
|
|
37
222
|
function isKnownRpcMessage(events, name) {
|
|
38
223
|
return name in events;
|
|
39
224
|
}
|
|
225
|
+
exports.isKnownRpcMessage = isKnownRpcMessage;
|
|
@@ -11,14 +11,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.Selector = void 0;
|
|
13
13
|
class Selector {
|
|
14
|
-
constructor(
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
14
|
+
constructor(_Generators) {
|
|
15
|
+
this._Generators = _Generators;
|
|
16
|
+
this._Futures = _Generators.map(Selector.addIndex);
|
|
17
17
|
}
|
|
18
18
|
next() {
|
|
19
19
|
return __awaiter(this, void 0, void 0, function* () {
|
|
20
|
-
const [result, index] = yield Promise.race(this.
|
|
21
|
-
this.
|
|
20
|
+
const [result, index] = yield Promise.race(this._Futures);
|
|
21
|
+
this._Futures[index] = Selector.addIndex(this._Generators[index], index);
|
|
22
22
|
return result;
|
|
23
23
|
});
|
|
24
24
|
}
|
|
@@ -12,8 +12,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.Strand = void 0;
|
|
13
13
|
class Strand {
|
|
14
14
|
constructor() {
|
|
15
|
-
this.
|
|
16
|
-
this.
|
|
15
|
+
this._Tasks = [];
|
|
16
|
+
this._Running = false;
|
|
17
17
|
}
|
|
18
18
|
enqueue(task) {
|
|
19
19
|
return new Promise((resolve, reject) => {
|
|
@@ -26,22 +26,22 @@ class Strand {
|
|
|
26
26
|
reject(error);
|
|
27
27
|
}
|
|
28
28
|
});
|
|
29
|
-
this.
|
|
30
|
-
if (!this.
|
|
29
|
+
this._Tasks.push(wrapped);
|
|
30
|
+
if (!this._Running) {
|
|
31
31
|
this.execute();
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
}
|
|
35
35
|
execute() {
|
|
36
36
|
return __awaiter(this, void 0, void 0, function* () {
|
|
37
|
-
this.
|
|
37
|
+
this._Running = true;
|
|
38
38
|
while (true) {
|
|
39
|
-
const task = this.
|
|
39
|
+
const task = this._Tasks.shift();
|
|
40
40
|
if (task === undefined)
|
|
41
41
|
break;
|
|
42
42
|
yield task();
|
|
43
43
|
}
|
|
44
|
-
this.
|
|
44
|
+
this._Running = false;
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
}
|
package/lib/esm/utility/rpc.js
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/* eslint-disable no-dupe-class-members */
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
1
11
|
import * as msgpack from '@msgpack/msgpack';
|
|
2
12
|
import { MessagePackRpcSchema } from '../schema/serialization';
|
|
3
13
|
import { failure, success } from './result';
|
|
@@ -7,6 +17,179 @@ export function msgpackEncode(value) {
|
|
|
7
17
|
export function msgpackDecode(buffer) {
|
|
8
18
|
return msgpack.decode(buffer);
|
|
9
19
|
}
|
|
20
|
+
export class RpcConnection {
|
|
21
|
+
get closed() {
|
|
22
|
+
return this._Close.signal.aborted;
|
|
23
|
+
}
|
|
24
|
+
get closeSignal() {
|
|
25
|
+
return this._Close.signal;
|
|
26
|
+
}
|
|
27
|
+
constructor(webSocket) {
|
|
28
|
+
this.webSocket = webSocket;
|
|
29
|
+
this._SendQueue = [];
|
|
30
|
+
this._RecvQueue = [];
|
|
31
|
+
this._Requests = new Map();
|
|
32
|
+
this._Close = new AbortController();
|
|
33
|
+
this._NextId = 1;
|
|
34
|
+
this.webSocket.addEventListener('open', this._OnWebSocketOpen.bind(this));
|
|
35
|
+
this.webSocket.addEventListener('close', this.close.bind(this));
|
|
36
|
+
this.webSocket.addEventListener('message', this._OnWebSocketMessage.bind(this));
|
|
37
|
+
this.webSocket.binaryType = 'arraybuffer';
|
|
38
|
+
this._Close.signal.addEventListener('abort', this._Cleanup.bind(this), {
|
|
39
|
+
once: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
request(name, properties, timeout) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
if (this.closed) {
|
|
45
|
+
return Promise.resolve('closed');
|
|
46
|
+
}
|
|
47
|
+
const id = this._NextId;
|
|
48
|
+
const timeoutId = timeout === undefined
|
|
49
|
+
? undefined
|
|
50
|
+
: setTimeout(() => this._TimeoutRequest(id), timeout);
|
|
51
|
+
this._Send([0, id, name, properties]);
|
|
52
|
+
return new Promise((resolve) => {
|
|
53
|
+
this._Requests.set(id, { resolve, timeoutId });
|
|
54
|
+
this._NextId += 1;
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
notify(name, properties) {
|
|
59
|
+
if (this.closed === false) {
|
|
60
|
+
this._Send([2, name, properties]);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
recv() {
|
|
64
|
+
if (this.closed === false) {
|
|
65
|
+
return new Promise((resolve) => this._RecvQueue.push(resolve));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
return Promise.resolve('closed');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
close() {
|
|
72
|
+
this._Close.abort();
|
|
73
|
+
}
|
|
74
|
+
_TimeoutRequest(requestId) {
|
|
75
|
+
const request = this._Requests.get(requestId);
|
|
76
|
+
if (request === undefined)
|
|
77
|
+
return;
|
|
78
|
+
this._Requests.delete(requestId);
|
|
79
|
+
clearTimeout(request.timeoutId);
|
|
80
|
+
request.resolve('timeout');
|
|
81
|
+
}
|
|
82
|
+
_Send(message) {
|
|
83
|
+
this.webSocket.send(msgpack.encode(message));
|
|
84
|
+
}
|
|
85
|
+
_OnWebSocketOpen(ev) {
|
|
86
|
+
for (const data in this._SendQueue) {
|
|
87
|
+
this.webSocket.send(data);
|
|
88
|
+
}
|
|
89
|
+
this._SendQueue.length = 0;
|
|
90
|
+
}
|
|
91
|
+
_OnWebSocketMessage(ev) {
|
|
92
|
+
const rpc = parseRpc(ev.data);
|
|
93
|
+
if (rpc.type !== 'Success') {
|
|
94
|
+
this.webSocket.close();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
switch (rpc.value[0]) {
|
|
98
|
+
case 0:
|
|
99
|
+
this._Send([1, rpc.value[1], 'not supported', undefined]);
|
|
100
|
+
break;
|
|
101
|
+
case 1:
|
|
102
|
+
this._OnResponse(rpc.value[1], rpc.value[2], rpc.value[3]);
|
|
103
|
+
break;
|
|
104
|
+
case 2:
|
|
105
|
+
this._OnEvent(rpc.value[1], rpc.value[2]);
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
_OnResponse(id, error, result) {
|
|
110
|
+
const request = this._Requests.get(id);
|
|
111
|
+
if (request === undefined)
|
|
112
|
+
return;
|
|
113
|
+
clearTimeout(request.timeoutId);
|
|
114
|
+
request.resolve(error === null ? success(result) : failure(error));
|
|
115
|
+
}
|
|
116
|
+
_OnEvent(name, properties) {
|
|
117
|
+
const receiver = this._RecvQueue.shift();
|
|
118
|
+
if (receiver !== undefined) {
|
|
119
|
+
receiver({ name, properties });
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
_Cleanup() {
|
|
123
|
+
for (const request of this._Requests.values()) {
|
|
124
|
+
clearTimeout(request.timeoutId);
|
|
125
|
+
request.resolve('closed');
|
|
126
|
+
}
|
|
127
|
+
this._Requests.clear();
|
|
128
|
+
for (const accept of this._RecvQueue) {
|
|
129
|
+
accept('closed');
|
|
130
|
+
}
|
|
131
|
+
this._RecvQueue.length = 0;
|
|
132
|
+
this._SendQueue.length = 0;
|
|
133
|
+
this.webSocket.close();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
export class TypedRpcConnection extends RpcConnection {
|
|
137
|
+
constructor(webSocket, _Commands, _Events) {
|
|
138
|
+
super(webSocket);
|
|
139
|
+
this._Commands = _Commands;
|
|
140
|
+
this._Events = _Events;
|
|
141
|
+
}
|
|
142
|
+
request(name, properties, timeout) {
|
|
143
|
+
const _super = Object.create(null, {
|
|
144
|
+
request: { get: () => super.request }
|
|
145
|
+
});
|
|
146
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
147
|
+
properties = this._Commands[name].request.parse(properties);
|
|
148
|
+
const response = yield (timeout !== undefined
|
|
149
|
+
? _super.request.call(this, name, properties, timeout)
|
|
150
|
+
: _super.request.call(this, name, properties));
|
|
151
|
+
if (response === 'closed' ||
|
|
152
|
+
response === 'timeout' ||
|
|
153
|
+
response.type === 'Failure') {
|
|
154
|
+
return response;
|
|
155
|
+
}
|
|
156
|
+
const parsed = this._Commands[name].response.safeParse(response.value);
|
|
157
|
+
if (parsed.success) {
|
|
158
|
+
response.value = parsed.data;
|
|
159
|
+
return response;
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
return failure(`response schema failed validation: ${parsed.error}`);
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
notify(name, properties) {
|
|
167
|
+
properties = this._Commands[name].request.parse(properties);
|
|
168
|
+
super.notify(name, properties);
|
|
169
|
+
}
|
|
170
|
+
recv() {
|
|
171
|
+
const _super = Object.create(null, {
|
|
172
|
+
recv: { get: () => super.recv }
|
|
173
|
+
});
|
|
174
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
175
|
+
const event = yield _super.recv.call(this);
|
|
176
|
+
if (event === 'closed')
|
|
177
|
+
return event;
|
|
178
|
+
const parsed = parseRpcMessage(this._Events, event);
|
|
179
|
+
if (parsed !== undefined) {
|
|
180
|
+
return parsed;
|
|
181
|
+
}
|
|
182
|
+
if (!isKnownRpcMessage(this._Events, event.name)) {
|
|
183
|
+
console.warn(`dropping unknown event ${event.name}`);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
console.error(`schema validation for ${event.name} failed; closing connection`);
|
|
187
|
+
this.close();
|
|
188
|
+
}
|
|
189
|
+
return 'closed';
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
10
193
|
export function parseRpc(buffer) {
|
|
11
194
|
try {
|
|
12
195
|
return success(MessagePackRpcSchema.parse(msgpackDecode(buffer)));
|
|
@@ -27,6 +210,6 @@ export function parseRpcMessage(events, rpc) {
|
|
|
27
210
|
properties: parsed.data,
|
|
28
211
|
};
|
|
29
212
|
}
|
|
30
|
-
function isKnownRpcMessage(events, name) {
|
|
213
|
+
export function isKnownRpcMessage(events, name) {
|
|
31
214
|
return name in events;
|
|
32
215
|
}
|
|
@@ -8,14 +8,14 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
export class Selector {
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
13
|
-
this.
|
|
11
|
+
constructor(_Generators) {
|
|
12
|
+
this._Generators = _Generators;
|
|
13
|
+
this._Futures = _Generators.map(Selector.addIndex);
|
|
14
14
|
}
|
|
15
15
|
next() {
|
|
16
16
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
-
const [result, index] = yield Promise.race(this.
|
|
18
|
-
this.
|
|
17
|
+
const [result, index] = yield Promise.race(this._Futures);
|
|
18
|
+
this._Futures[index] = Selector.addIndex(this._Generators[index], index);
|
|
19
19
|
return result;
|
|
20
20
|
});
|
|
21
21
|
}
|
|
@@ -9,8 +9,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
export class Strand {
|
|
11
11
|
constructor() {
|
|
12
|
-
this.
|
|
13
|
-
this.
|
|
12
|
+
this._Tasks = [];
|
|
13
|
+
this._Running = false;
|
|
14
14
|
}
|
|
15
15
|
enqueue(task) {
|
|
16
16
|
return new Promise((resolve, reject) => {
|
|
@@ -23,22 +23,22 @@ export class Strand {
|
|
|
23
23
|
reject(error);
|
|
24
24
|
}
|
|
25
25
|
});
|
|
26
|
-
this.
|
|
27
|
-
if (!this.
|
|
26
|
+
this._Tasks.push(wrapped);
|
|
27
|
+
if (!this._Running) {
|
|
28
28
|
this.execute();
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
}
|
|
32
32
|
execute() {
|
|
33
33
|
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
-
this.
|
|
34
|
+
this._Running = true;
|
|
35
35
|
while (true) {
|
|
36
|
-
const task = this.
|
|
36
|
+
const task = this._Tasks.shift();
|
|
37
37
|
if (task === undefined)
|
|
38
38
|
break;
|
|
39
39
|
yield task();
|
|
40
40
|
}
|
|
41
|
-
this.
|
|
41
|
+
this._Running = false;
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
}
|