@aawwaaa/astralcore-syncer 1.0.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/dist/event.js ADDED
@@ -0,0 +1,54 @@
1
+ export class EventBus {
2
+ listeners = new Map();
3
+ rpcs = [];
4
+ async emit(event, ...args) {
5
+ const handlers = this.listeners.get(event);
6
+ if (handlers) {
7
+ for (const handler of handlers.values()) {
8
+ try {
9
+ await handler(...args);
10
+ }
11
+ catch (e) {
12
+ console.error("An error occurred when event ", event, " triggers with ", args, ": ", e);
13
+ }
14
+ }
15
+ }
16
+ this.rpcs.forEach(rpc => {
17
+ try {
18
+ rpc(event, ...args);
19
+ }
20
+ catch (e) {
21
+ console.error("An error occurred when RPC ", rpc, " triggers with ", event, " and ", args, ": ", e);
22
+ }
23
+ });
24
+ }
25
+ on(event, handler) {
26
+ const handlers = this.listeners.get(event);
27
+ if (handlers) {
28
+ handlers.push(handler);
29
+ }
30
+ else {
31
+ this.listeners.set(event, [handler]);
32
+ }
33
+ return () => {
34
+ const list = this.listeners.get(event);
35
+ if (list.indexOf(handler) == -1)
36
+ return;
37
+ list.splice(list.indexOf(handler), 1);
38
+ };
39
+ }
40
+ updater(event, handler) {
41
+ handler();
42
+ return this.on(event, handler);
43
+ }
44
+ off(event, handler) {
45
+ const list = this.listeners.get(event);
46
+ if (!list)
47
+ return;
48
+ const index = list.indexOf(handler);
49
+ if (index === -1)
50
+ return;
51
+ list.splice(index, 1);
52
+ }
53
+ }
54
+ //# sourceMappingURL=event.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../src/event.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,QAAQ;IACX,SAAS,GAAoE,IAAI,GAAG,EAAE,CAAC;IAC/F,IAAI,GAAgC,EAAE,CAAC;IAEvC,KAAK,CAAC,IAAI,CAAyB,KAAQ,EAAE,GAAG,IAAe;QAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxC,IAAI,CAAC;oBACH,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAA;gBACxB,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBACX,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,KAAK,EAAE,iBAAiB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;gBACzF,CAAC;YACH,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE;YACtB,IAAI,CAAC;gBACH,GAAG,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAA;YACrB,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YACrG,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IACD,EAAE,CAAyB,KAAQ,EAAE,OAAqD;QACxF,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,QAAQ,EAAE,CAAC;YACb,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,GAAG,EAAE;YACV,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAA;YACvC,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAE,OAAO;YACxC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAA;QACvC,CAAC,CAAA;IACH,CAAC;IAED,OAAO,CAAyB,KAAQ,EAAE,OAAmB;QAC3D,OAAO,EAAE,CAAA;QACT,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAChC,CAAC;IAED,GAAG,CAAyB,KAAQ,EAAE,OAAY;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,OAAO;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,OAAO;QACzB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,273 @@
1
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
2
+ if (value !== null && value !== void 0) {
3
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
4
+ var dispose, inner;
5
+ if (async) {
6
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
7
+ dispose = value[Symbol.asyncDispose];
8
+ }
9
+ if (dispose === void 0) {
10
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
11
+ dispose = value[Symbol.dispose];
12
+ if (async) inner = dispose;
13
+ }
14
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
15
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
16
+ env.stack.push({ value: value, dispose: dispose, async: async });
17
+ }
18
+ else if (async) {
19
+ env.stack.push({ async: true });
20
+ }
21
+ return value;
22
+ };
23
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
24
+ return function (env) {
25
+ function fail(e) {
26
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
27
+ env.hasError = true;
28
+ }
29
+ var r, s = 0;
30
+ function next() {
31
+ while (r = env.stack.pop()) {
32
+ try {
33
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
34
+ if (r.dispose) {
35
+ var result = r.dispose.call(r.value);
36
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
37
+ }
38
+ else s |= 1;
39
+ }
40
+ catch (e) {
41
+ fail(e);
42
+ }
43
+ }
44
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
45
+ if (env.hasError) throw env.error;
46
+ }
47
+ return next();
48
+ };
49
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
50
+ var e = new Error(message);
51
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
+ });
53
+ import { resourceEnvironment } from "../../environment.js";
54
+ import { EventBus } from "../../event.js";
55
+ import { freeResourcesInObject } from "../../object.js";
56
+ import { typedValueUnpack, typedValuePack } from "../../protocol.js";
57
+ const PING_INTERVAL = 5000;
58
+ export class ClientWebsocketRemoteManager {
59
+ websocketPath;
60
+ event = new EventBus();
61
+ clientid;
62
+ websocket;
63
+ messageQueue = [];
64
+ connected = false;
65
+ retryTimer;
66
+ pingTimer;
67
+ _resolveEstablished = () => void 0;
68
+ _resolvedEstablished = false;
69
+ connectEstablished;
70
+ invokes = {};
71
+ pingId = 0;
72
+ pingBegin = new Map();
73
+ lastPingId = -1;
74
+ lastPingDelay = 0;
75
+ _preparePromise() {
76
+ if (this._resolvedEstablished) {
77
+ this._resolvedEstablished = false;
78
+ this.connectEstablished = new Promise(r => this._resolveEstablished = () => {
79
+ r();
80
+ this._resolvedEstablished = true;
81
+ });
82
+ }
83
+ }
84
+ resourceManager;
85
+ constructor(websocketPath) {
86
+ this.websocketPath = websocketPath;
87
+ this._preparePromise();
88
+ }
89
+ init() {
90
+ this.clientid = Math.random().toString();
91
+ this.connect();
92
+ return this;
93
+ }
94
+ connectData() {
95
+ return {
96
+ command: "connect", id: this.clientid,
97
+ loadedResources: [...resourceEnvironment().resource.resourceMap.keys()].map(a => [a])
98
+ };
99
+ }
100
+ connect() {
101
+ console.log('[ClientRemoteManager] Connecting to server...');
102
+ clearTimeout(this.retryTimer);
103
+ this.websocket = new WebSocket(this.websocketPath);
104
+ this.connected = false;
105
+ this.websocket.onopen = () => {
106
+ this.websocket.send(JSON.stringify(this.connectData()));
107
+ };
108
+ this.websocket.onmessage = (e) => {
109
+ try {
110
+ const json = JSON.parse(e.data);
111
+ if (json.command != "established") {
112
+ throw new Error("Invalid message: " + e.data);
113
+ }
114
+ this.connected = true;
115
+ this.handleConnected();
116
+ }
117
+ catch (e) {
118
+ console.error("Error during connecting ", e);
119
+ this.scheduleReconnect();
120
+ }
121
+ };
122
+ this.websocket.onerror = (e) => {
123
+ console.error("Error during connecting ", e);
124
+ this.scheduleReconnect();
125
+ };
126
+ this.websocket.onclose = () => {
127
+ this.scheduleReconnect();
128
+ };
129
+ }
130
+ handleConnected() {
131
+ console.log('[ClientRemoteManager] Connection established');
132
+ this.connected = true;
133
+ clearTimeout(this.retryTimer);
134
+ this.websocket.onmessage = (e) => {
135
+ try {
136
+ const json = JSON.parse(e.data);
137
+ this.handleMessage(json);
138
+ }
139
+ catch (e) {
140
+ console.error("Error during handling message ", e);
141
+ }
142
+ };
143
+ for (const msg of this.messageQueue) {
144
+ this.sendMessage(msg);
145
+ }
146
+ this.messageQueue = [];
147
+ this.pingTimer = setInterval(() => {
148
+ const id = this.pingId++;
149
+ this.pingBegin.set(id, Date.now());
150
+ this.sendMessage({
151
+ command: "ping", id: id.toString()
152
+ });
153
+ }, PING_INTERVAL);
154
+ this._resolveEstablished();
155
+ this.event.emit("connected");
156
+ }
157
+ scheduleReconnect() {
158
+ console.log('[ClientRemoteManager] Connection lost, scheduling reconnect...');
159
+ this.websocket?.close();
160
+ this.websocket = null;
161
+ clearInterval(this.pingTimer);
162
+ this.lastPingId = -1;
163
+ this.lastPingDelay = 0;
164
+ this.connected = false;
165
+ this._preparePromise();
166
+ this.retryTimer = setTimeout(() => {
167
+ this.connect();
168
+ });
169
+ this.event.emit("disconnected");
170
+ }
171
+ close() {
172
+ console.log('[ClientRemoteManager] Connection closed');
173
+ this.scheduleReconnect = () => void 0;
174
+ this.connect = () => void 0;
175
+ if (this.connected)
176
+ this.websocket.close();
177
+ this.event.emit("disconnected");
178
+ }
179
+ sendMessage(message) {
180
+ if (!this.connected) {
181
+ this.messageQueue.push(message);
182
+ return;
183
+ }
184
+ this.websocket.send(JSON.stringify(message));
185
+ }
186
+ handleMessage(message) {
187
+ switch (message.command) {
188
+ case 'established':
189
+ throw new Error("Unreachable code.");
190
+ case 'invoke_response':
191
+ void (async () => {
192
+ if (message.id == "$serverside")
193
+ return;
194
+ const handler = this.invokes[message.id];
195
+ if (!handler) {
196
+ throw new Error("Invaild response id, not found! " + message.id);
197
+ }
198
+ const res = await typedValueUnpack(message.result);
199
+ handler(res, message.isError);
200
+ return;
201
+ })();
202
+ break;
203
+ case 'emit':
204
+ void this.handleEmit(message.resource, message.event, message.data);
205
+ break;
206
+ case 'resource':
207
+ this.resourceManager.handleResource(message.ref, message.exists, message.data);
208
+ break;
209
+ case 'pong':
210
+ const id = +message.id;
211
+ if (!this.pingBegin.has(id)) {
212
+ throw new Error("Got a pong without ping, " + message.id);
213
+ }
214
+ const begin = this.pingBegin.get(id);
215
+ const delta = Date.now() - begin;
216
+ this.lastPingId = id;
217
+ this.lastPingDelay = delta;
218
+ this.event.emit("pong", delta);
219
+ break;
220
+ case 'fatal_error':
221
+ this.close();
222
+ console.error("Server issued a fatal error, disconnecting: " + message.message);
223
+ break;
224
+ }
225
+ }
226
+ async handleEmit(ref, event, argsTv) {
227
+ const env_1 = { stack: [], error: void 0, hasError: false };
228
+ try {
229
+ const res = __addDisposableResource(env_1, await resourceEnvironment().resource.resolve(ref) ?? undefined, false);
230
+ if (!res) {
231
+ if (event == "$remove")
232
+ return; // no difference
233
+ throw new Error("Resource not found: " + ref);
234
+ }
235
+ const args = await typedValueUnpack(argsTv);
236
+ res.rpcEmit(event, ...args);
237
+ freeResourcesInObject(args);
238
+ }
239
+ catch (e_1) {
240
+ env_1.error = e_1;
241
+ env_1.hasError = true;
242
+ }
243
+ finally {
244
+ __disposeResources(env_1);
245
+ }
246
+ }
247
+ isClient() { return true; }
248
+ isServer() { return false; }
249
+ rpcInvoke(resource, method, ...args) {
250
+ const id = Math.random().toString();
251
+ this.sendMessage({
252
+ command: "invoke", id,
253
+ resource: resource.path(),
254
+ method, args: typedValuePack(args),
255
+ });
256
+ freeResourcesInObject(args);
257
+ return new Promise((res, rej) => {
258
+ this.invokes[id] = (ret, isError) => {
259
+ if (isError)
260
+ rej(ret);
261
+ else
262
+ res(ret);
263
+ };
264
+ });
265
+ }
266
+ rpcEmit(resource, event, ...args) {
267
+ throw new Error('Unreachable code.');
268
+ }
269
+ getCaller() {
270
+ throw new Error('Unreachable code.');
271
+ }
272
+ }
273
+ //# sourceMappingURL=remote-websocket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-websocket.js","sourceRoot":"","sources":["../../../src/impl/client/remote-websocket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { ResourceManager, ResourceDefiner, stringifyResourceRef } from "../../object.js";
2
+ export class ClientRemoteResourceManager extends ResourceManager {
3
+ remote;
4
+ constructor(remote) {
5
+ super();
6
+ this.remote = remote;
7
+ remote.resourceManager = this;
8
+ }
9
+ removeResource(res) {
10
+ throw new Error("Unreachable code.");
11
+ }
12
+ pendingResources = new Map();
13
+ loadResource(id) {
14
+ const [definer, realId] = ResourceDefiner.fromId(id);
15
+ if (!definer) {
16
+ throw new Error("Unknown resource type: " + id);
17
+ }
18
+ // FIXME: optimize - batch requests
19
+ this.remote.sendMessage({
20
+ command: "resource_request",
21
+ resources: [[id]]
22
+ });
23
+ return new Promise((resolve) => {
24
+ this.pendingResources.set(id, (existed, data) => {
25
+ if (!existed)
26
+ return resolve(null);
27
+ const res = definer.init(realId);
28
+ void (async () => {
29
+ await res.resLoad(data);
30
+ res.ready();
31
+ })();
32
+ return resolve(res);
33
+ });
34
+ });
35
+ }
36
+ unloadResource(res) {
37
+ this.resourceMap.delete(res.path()[0]);
38
+ this.remote.sendMessage({
39
+ command: "resource_unloaded",
40
+ resources: [res.path().slice(0, 1)]
41
+ });
42
+ }
43
+ handleResource(ref, existed, data) {
44
+ const key = stringifyResourceRef(ref);
45
+ if (!this.pendingResources.has(key)) {
46
+ throw new Error("Got a resource without request, " + key);
47
+ }
48
+ this.pendingResources.get(key)(existed, data);
49
+ this.pendingResources.delete(key);
50
+ }
51
+ }
52
+ //# sourceMappingURL=resource-remote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-remote.js","sourceRoot":"","sources":["../../../src/impl/client/resource-remote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAY,eAAe,EAAe,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAGhH,MAAM,OAAO,2BAA4B,SAAQ,eAAe;IAC3C;IAAnB,YAAmB,MAAoC;QACrD,KAAK,EAAE,CAAA;QADU,WAAM,GAAN,MAAM,CAA8B;QAErD,MAAM,CAAC,eAAe,GAAG,IAAI,CAAA;IAC/B,CAAC;IAED,cAAc,CAAC,GAAuB;QACpC,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACvC,CAAC;IACD,gBAAgB,GAAuD,IAAI,GAAG,EAAE,CAAA;IAEhF,YAAY,CAAC,EAAU;QACrB,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACpD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,yBAAyB,GAAG,EAAE,CAAC,CAAA;QACjD,CAAC;QACD,mCAAmC;QACnC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YACtB,OAAO,EAAE,kBAAkB;YAC3B,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;SAClB,CAAC,CAAA;QACF,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE;gBAC9C,IAAI,CAAC,OAAO;oBAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;gBACnC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAChC,KAAK,CAAC,KAAK,IAAI,EAAE;oBACf,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;oBACvB,GAAG,CAAC,KAAK,EAAE,CAAA;gBACb,CAAC,CAAC,EAAE,CAAA;gBACJ,OAAO,OAAO,CAAC,GAAG,CAAC,CAAA;YACrB,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;IACJ,CAAC;IACD,cAAc,CAAC,GAAuB;QACpC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;QACtC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC;YACtB,OAAO,EAAE,mBAAmB;YAC5B,SAAS,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;SACpC,CAAC,CAAA;IACJ,CAAC;IAED,cAAc,CAAC,GAAgB,EAAE,OAAgB,EAAE,IAAS;QAC1D,MAAM,GAAG,GAAG,oBAAoB,CAAC,GAAG,CAAC,CAAA;QACrC,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,GAAG,CAAC,CAAA;QAC3D,CAAC;QACD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;QAC9C,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACnC,CAAC;CAEF"}
@@ -0,0 +1,271 @@
1
+ import { stringifyResourceRef, freeResourcesInObject } from "../../object.js";
2
+ import { AsyncLocalStorage } from "node:async_hooks";
3
+ import { typedValueUnpack, typedValuePack } from "../../protocol.js";
4
+ import { resourceEnvironment } from "../../environment.js";
5
+ const callerStorage = new AsyncLocalStorage();
6
+ export class ServerWebsocketRemoteManager {
7
+ connections = [];
8
+ isClient() { return false; }
9
+ isServer() { return true; }
10
+ async rpcInvoke(resource, method, ...args) {
11
+ throw new Error("Unreachable code.");
12
+ }
13
+ rpcEmit(resource, event, ...args) {
14
+ for (const connection of this.connections) {
15
+ connection.emit(resource, event, ...args);
16
+ }
17
+ }
18
+ getCaller() {
19
+ const caller = callerStorage.getStore();
20
+ return caller ? caller : null;
21
+ }
22
+ runInContext(caller, callback) {
23
+ return callerStorage.run(caller, callback);
24
+ }
25
+ _createOrGetConnection(id, msg) {
26
+ const connection = this.connections.find((connection) => connection.id === id);
27
+ if (connection) {
28
+ return connection;
29
+ }
30
+ const newConnection = new Connection(this, id);
31
+ newConnection.connect(msg);
32
+ this.connections.push(newConnection);
33
+ return newConnection;
34
+ }
35
+ _removeConnection(id) {
36
+ const index = this.connections.findIndex((connection) => connection.id === id);
37
+ if (index !== -1) {
38
+ this.connections.splice(index, 1);
39
+ }
40
+ }
41
+ handleWebSocket(websocket) {
42
+ const timeout = setTimeout(() => {
43
+ websocket.close();
44
+ }, 3000);
45
+ websocket.once("message", (message) => {
46
+ clearTimeout(timeout);
47
+ try {
48
+ const obj = JSON.parse(message.toString());
49
+ if (obj.command !== "connect") {
50
+ throw new Error("Invalid command");
51
+ }
52
+ const connection = this._createOrGetConnection(obj.id, obj);
53
+ connection.setWebSocket(websocket);
54
+ connection.sendMessage({
55
+ command: "established",
56
+ id: obj.id
57
+ });
58
+ // if (login) {
59
+ // connection.handleInvoke(Vars.res.ref(Vars.user), "verify", {
60
+ // type: "array",
61
+ // value: [
62
+ // {
63
+ // type: "basic",
64
+ // value: login.username
65
+ // },
66
+ // {
67
+ // type: "basic",
68
+ // value: login.token
69
+ // }
70
+ // ]
71
+ // }, "$serverside")
72
+ // }
73
+ }
74
+ catch (error) {
75
+ websocket.send(JSON.stringify({ command: "error", message: "Invalid message" }));
76
+ console.error("Failed to handle WebSocket message:", error);
77
+ websocket.close();
78
+ }
79
+ });
80
+ }
81
+ }
82
+ export class Connection {
83
+ remote;
84
+ id;
85
+ messageQueue = [];
86
+ websocket;
87
+ _reconnectTimeout;
88
+ loadedResource = {};
89
+ // user: User | null = null
90
+ constructor(remote, id) {
91
+ this.remote = remote;
92
+ this.id = id;
93
+ }
94
+ status() {
95
+ return `
96
+ Connection ${this.id}
97
+ object_refs: ${Object.keys(this.loadedResource).length}
98
+ `;
99
+ }
100
+ setWebSocket(websocket) {
101
+ if (this._reconnectTimeout) {
102
+ clearTimeout(this._reconnectTimeout);
103
+ this._reconnectTimeout = undefined;
104
+ }
105
+ this.websocket = websocket;
106
+ if (this.messageQueue.length > 0) {
107
+ for (const message of this.messageQueue) {
108
+ this.sendMessage(message);
109
+ }
110
+ this.messageQueue = [];
111
+ }
112
+ websocket.on("message", (message) => {
113
+ try {
114
+ const obj = JSON.parse(message.toString());
115
+ this.handleMessage(obj);
116
+ }
117
+ catch (e) {
118
+ console.error("Failed to handle WebSocket message ", message, ":", e);
119
+ this.sendMessage({
120
+ command: "fatal_error",
121
+ message: e instanceof Error ? e.toString() : "Unknown error",
122
+ });
123
+ }
124
+ });
125
+ websocket.on("close", () => {
126
+ this.websocket = undefined;
127
+ this._reconnectTimeout = setTimeout(() => {
128
+ this.close();
129
+ }, 5000);
130
+ });
131
+ }
132
+ close() {
133
+ for (const obj of Object.values(this.loadedResource)) {
134
+ obj.refRemove();
135
+ }
136
+ // if (this.user) {
137
+ // this.user.refRemove()
138
+ // }
139
+ this.remote._removeConnection(this.id);
140
+ }
141
+ connect(msg) {
142
+ for (const ref of msg.loadedResources) {
143
+ if (!ref)
144
+ continue;
145
+ if (!this.loadedResource[ref[0]]) {
146
+ this.handleResourceRequest(ref, false);
147
+ }
148
+ }
149
+ }
150
+ sendMessage(message) {
151
+ if (!this.websocket) {
152
+ this.messageQueue.push(message);
153
+ return;
154
+ }
155
+ this.websocket.send(JSON.stringify(message));
156
+ }
157
+ async handleResourceRequest(ref, send = true) {
158
+ if (ref && ref.length != 1) {
159
+ console.error("Got an unexpected resource request: ", ref);
160
+ return;
161
+ }
162
+ const res = await resourceEnvironment().resource.resolve(ref);
163
+ if (res) {
164
+ const existed = this.loadedResource[stringifyResourceRef(ref)];
165
+ if (existed) {
166
+ existed.refRemove(); // resolve added a ref
167
+ }
168
+ this.loadedResource[stringifyResourceRef(ref)] = res;
169
+ }
170
+ if (send)
171
+ this.sendMessage({
172
+ command: "resource",
173
+ ref, exists: res != null,
174
+ data: res?.resSave(true)
175
+ });
176
+ }
177
+ handleResourceUnload(ref) {
178
+ const existed = this.loadedResource[stringifyResourceRef(ref)];
179
+ if (existed) {
180
+ existed.refRemove(); // resolve added a ref
181
+ }
182
+ delete this.loadedResource[stringifyResourceRef(ref)];
183
+ }
184
+ async handleInvoke(ref, method, argsTv, id) {
185
+ const args = await typedValueUnpack(argsTv);
186
+ if (!Array.isArray(args)) {
187
+ this.sendMessage({
188
+ command: "invoke_response",
189
+ id, result: typedValuePack(new Error("Not a valid args list.")),
190
+ isError: true
191
+ });
192
+ return;
193
+ }
194
+ const res = await resourceEnvironment().resource.resolve(ref);
195
+ if (!res) {
196
+ this.sendMessage({
197
+ command: "invoke_response",
198
+ id, result: typedValuePack(new Error("Resource not found: " + ref)),
199
+ isError: true
200
+ });
201
+ return;
202
+ }
203
+ let ret, isError;
204
+ try {
205
+ ret = await this.remote.runInContext(this.caller(), async () => {
206
+ return await res.rpcInvoke(method, ...args);
207
+ });
208
+ isError = false;
209
+ }
210
+ catch (e) {
211
+ ret = e;
212
+ isError = true;
213
+ }
214
+ finally {
215
+ freeResourcesInObject(args);
216
+ }
217
+ this.sendMessage({
218
+ command: "invoke_response",
219
+ id, result: typedValuePack(ret), isError
220
+ });
221
+ freeResourcesInObject(ret);
222
+ res.refRemove();
223
+ }
224
+ handleMessage(message) {
225
+ switch (message.command) {
226
+ case "connect":
227
+ throw new Error("Unreachable code! Check the frontend!");
228
+ case "invoke":
229
+ void this.handleInvoke(message.resource, message.method, message.args, message.id)
230
+ .catch(e => console.error("Error while handling invoke: ", message, "\n", e));
231
+ break;
232
+ case "resource_request":
233
+ for (const ref of message.resources) {
234
+ void this.handleResourceRequest(ref)
235
+ .catch(e => console.error("Error while handling request: ", message, "\n", e));
236
+ }
237
+ break;
238
+ case "resource_unloaded":
239
+ for (const ref of message.resources) {
240
+ void this.handleResourceUnload(ref);
241
+ }
242
+ break;
243
+ case "ping":
244
+ this.sendMessage({
245
+ command: "pong",
246
+ id: message.id
247
+ });
248
+ break;
249
+ }
250
+ }
251
+ /**
252
+ * borrow ref
253
+ */
254
+ emit(resource, event, ...args) {
255
+ const first = resource.path()[0];
256
+ if (!this.loadedResource[first])
257
+ return; // not for me
258
+ this.sendMessage({
259
+ command: "emit",
260
+ resource: resource.path(),
261
+ event: event,
262
+ data: typedValuePack(args)
263
+ });
264
+ }
265
+ caller() {
266
+ return {
267
+ connection: this
268
+ };
269
+ }
270
+ }
271
+ //# sourceMappingURL=remote-websocket.js.map