@manifest-editor/server-vault 1.4.3 → 2.0.1-next.0.d468080
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/index.cjs +218 -225
- package/dist/index.d.cts +63 -60
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +88 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +209 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +14 -19
- package/dist/index.d.ts +0 -85
- package/dist/index.js +0 -204
package/dist/index.cjs
CHANGED
|
@@ -1,240 +1,233 @@
|
|
|
1
|
-
|
|
1
|
+
//#region rolldown:runtime
|
|
2
2
|
var __create = Object.create;
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
-
var __export = (target, all) => {
|
|
10
|
-
for (var name in all)
|
|
11
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
-
};
|
|
13
8
|
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
-
var __publicField = (obj, key, value) => {
|
|
31
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
32
|
-
return value;
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
10
|
+
key = keys[i];
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
12
|
+
get: ((k) => from[k]).bind(null, key),
|
|
13
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
33
17
|
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
value: mod,
|
|
20
|
+
enumerable: true
|
|
21
|
+
}) : target, mod));
|
|
22
|
+
|
|
23
|
+
//#endregion
|
|
24
|
+
let __iiif_helpers_vault = require("@iiif/helpers/vault");
|
|
25
|
+
let ws = require("ws");
|
|
26
|
+
ws = __toESM(ws);
|
|
27
|
+
let uuid = require("uuid");
|
|
28
|
+
let timers = require("timers");
|
|
29
|
+
let http = require("http");
|
|
30
|
+
|
|
31
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/typeof.js
|
|
32
|
+
function _typeof(o) {
|
|
33
|
+
"@babel/helpers - typeof";
|
|
34
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
35
|
+
return typeof o$1;
|
|
36
|
+
} : function(o$1) {
|
|
37
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
38
|
+
}, _typeof(o);
|
|
39
|
+
}
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/toPrimitive.js
|
|
43
|
+
function toPrimitive(t, r) {
|
|
44
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
45
|
+
var e = t[Symbol.toPrimitive];
|
|
46
|
+
if (void 0 !== e) {
|
|
47
|
+
var i = e.call(t, r || "default");
|
|
48
|
+
if ("object" != _typeof(i)) return i;
|
|
49
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
50
|
+
}
|
|
51
|
+
return ("string" === r ? String : Number)(t);
|
|
52
|
+
}
|
|
42
53
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
var
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/toPropertyKey.js
|
|
56
|
+
function toPropertyKey(t) {
|
|
57
|
+
var i = toPrimitive(t, "string");
|
|
58
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/defineProperty.js
|
|
63
|
+
function _defineProperty(e, r, t) {
|
|
64
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
65
|
+
value: t,
|
|
66
|
+
enumerable: !0,
|
|
67
|
+
configurable: !0,
|
|
68
|
+
writable: !0
|
|
69
|
+
}) : e[r] = t, e;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
//#region src/server-vault.ts
|
|
74
|
+
const GenesisId = "@genesis";
|
|
49
75
|
var ServerVault = class {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
this.ws.on("connection", this.setupWebSocket);
|
|
144
|
-
this.pingInterval = (0, import_timers.setInterval)(() => {
|
|
145
|
-
this.ws.clients.forEach((socket) => {
|
|
146
|
-
if (socket.isAlive === false) {
|
|
147
|
-
return socket.terminate();
|
|
148
|
-
}
|
|
149
|
-
socket.isAlive = false;
|
|
150
|
-
socket.ping();
|
|
151
|
-
});
|
|
152
|
-
}, options.pingTimeout || 3e4);
|
|
153
|
-
this.ws.on("close", () => {
|
|
154
|
-
(0, import_timers.clearInterval)(this.pingInterval);
|
|
155
|
-
if (options.onClose) {
|
|
156
|
-
options.onClose();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
this.ws.on("error", (err) => {
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
async close() {
|
|
163
|
-
clearTimeout(this.pingInterval);
|
|
164
|
-
return new Promise((resolve, error) => {
|
|
165
|
-
this.ws.close((err) => {
|
|
166
|
-
if (err) {
|
|
167
|
-
error(err);
|
|
168
|
-
}
|
|
169
|
-
resolve();
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
}
|
|
76
|
+
constructor(options = {}) {
|
|
77
|
+
_defineProperty(this, "vault", void 0);
|
|
78
|
+
_defineProperty(this, "ws", void 0);
|
|
79
|
+
_defineProperty(this, "lastActionId", GenesisId);
|
|
80
|
+
_defineProperty(this, "pingInterval", void 0);
|
|
81
|
+
_defineProperty(this, "_handleMessage", /* @__PURE__ */ new Map());
|
|
82
|
+
_defineProperty(this, "_handleOpen", /* @__PURE__ */ new Map());
|
|
83
|
+
_defineProperty(this, "_handleClose", /* @__PURE__ */ new Map());
|
|
84
|
+
_defineProperty(this, "setupWebSocket", (socket) => {
|
|
85
|
+
socket.isAlive = true;
|
|
86
|
+
socket.on("pong", () => {
|
|
87
|
+
socket.isAlive = true;
|
|
88
|
+
});
|
|
89
|
+
socket.on("open", this.handleOpen(socket));
|
|
90
|
+
socket.on("message", this.handleMessage(socket));
|
|
91
|
+
socket.on("close", this.handleClose(socket));
|
|
92
|
+
socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
93
|
+
});
|
|
94
|
+
_defineProperty(this, "handleMessage", (socket) => {
|
|
95
|
+
if (this._handleMessage.has(socket)) return this._handleMessage.get(socket);
|
|
96
|
+
const handler = (data, isBinary) => {
|
|
97
|
+
const parsed = JSON.parse(data.toString());
|
|
98
|
+
if (parsed._type === "init-request") socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
99
|
+
if (parsed._type === "vault-action") {
|
|
100
|
+
try {
|
|
101
|
+
this.vault.dispatch(parsed.action);
|
|
102
|
+
} catch (e) {
|
|
103
|
+
socket.send(this.createMessage("vault-action-rejection", { action: parsed._id }));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
this.lastActionId = parsed._id;
|
|
107
|
+
this.rebroadcast(socket, data, parsed, isBinary);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
this._handleMessage.set(socket, handler);
|
|
111
|
+
return handler;
|
|
112
|
+
});
|
|
113
|
+
_defineProperty(this, "send", (data) => {
|
|
114
|
+
this.ws.clients.forEach(function each(client) {
|
|
115
|
+
if (client.readyState === ws.default.OPEN) client.send(data, { binary: true });
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
_defineProperty(this, "rebroadcast", (source, data, action, isBinary = true) => {
|
|
119
|
+
this.ws.clients.forEach((client) => {
|
|
120
|
+
if (client !== source && client.readyState === ws.default.OPEN) client.send(data, { binary: isBinary });
|
|
121
|
+
else client.send(this.createMessage("vault-action-confirmation", { action: action._id }));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
_defineProperty(this, "handleOpen", (socket) => {
|
|
125
|
+
if (this._handleOpen.has(socket)) return this._handleOpen.get(socket);
|
|
126
|
+
const handler = () => {};
|
|
127
|
+
this._handleOpen.set(socket, handler);
|
|
128
|
+
return handler;
|
|
129
|
+
});
|
|
130
|
+
_defineProperty(this, "handleClose", (socket) => {
|
|
131
|
+
if (this._handleClose.has(socket)) return this._handleClose.get(socket);
|
|
132
|
+
const handler = () => {};
|
|
133
|
+
this._handleClose.set(socket, handler);
|
|
134
|
+
return handler;
|
|
135
|
+
});
|
|
136
|
+
_defineProperty(this, "createMessage", (type, message) => {
|
|
137
|
+
return JSON.stringify({
|
|
138
|
+
_id: (0, uuid.v4)(),
|
|
139
|
+
_type: type,
|
|
140
|
+
_lastActionId: this.lastActionId,
|
|
141
|
+
...message
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
this.vault = new __iiif_helpers_vault.Vault();
|
|
145
|
+
this.ws = new ws.WebSocketServer({ noServer: true });
|
|
146
|
+
this.ws.on("connection", this.setupWebSocket);
|
|
147
|
+
this.pingInterval = (0, timers.setInterval)(() => {
|
|
148
|
+
this.ws.clients.forEach((socket) => {
|
|
149
|
+
if (socket.isAlive === false) return socket.terminate();
|
|
150
|
+
socket.isAlive = false;
|
|
151
|
+
socket.ping();
|
|
152
|
+
});
|
|
153
|
+
}, options.pingTimeout || 3e4);
|
|
154
|
+
this.ws.on("close", () => {
|
|
155
|
+
(0, timers.clearInterval)(this.pingInterval);
|
|
156
|
+
if (options.onClose) options.onClose();
|
|
157
|
+
});
|
|
158
|
+
this.ws.on("error", (err) => {});
|
|
159
|
+
}
|
|
160
|
+
async close() {
|
|
161
|
+
clearTimeout(this.pingInterval);
|
|
162
|
+
return new Promise((resolve, error) => {
|
|
163
|
+
this.ws.close((err) => {
|
|
164
|
+
if (err) error(err);
|
|
165
|
+
resolve();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
173
169
|
};
|
|
174
170
|
|
|
175
|
-
|
|
176
|
-
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/server.ts
|
|
177
173
|
var MultiVaultServer = class {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.server.listen(port, hostname, backlog, listeningListener);
|
|
234
|
-
}
|
|
174
|
+
constructor(server) {
|
|
175
|
+
_defineProperty(this, "server", void 0);
|
|
176
|
+
_defineProperty(this, "vaults", /* @__PURE__ */ new Map());
|
|
177
|
+
_defineProperty(this, "handleRequest", (request, response) => {
|
|
178
|
+
if (request.url === "/vault") {
|
|
179
|
+
const vaults = Array.from(this.vaults.keys());
|
|
180
|
+
const json = JSON.stringify({ vaults });
|
|
181
|
+
response.writeHead(200, {
|
|
182
|
+
"Content-Length": Buffer.byteLength(json),
|
|
183
|
+
"Content-Type": "application/json"
|
|
184
|
+
});
|
|
185
|
+
response.write(json);
|
|
186
|
+
response.end();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
response.writeHead(404);
|
|
190
|
+
response.end();
|
|
191
|
+
});
|
|
192
|
+
_defineProperty(this, "handleUpgrade", (request, socket, head) => {
|
|
193
|
+
console.log("ws.url", request.url);
|
|
194
|
+
const pathname = request.url;
|
|
195
|
+
if (pathname && pathname.startsWith("/vault")) {
|
|
196
|
+
const vaultId = pathname.replace("/vault/", "");
|
|
197
|
+
let vault = this.vaults.get(vaultId);
|
|
198
|
+
if (!vault) {
|
|
199
|
+
const newVault = new ServerVault();
|
|
200
|
+
console.log("new vault", vaultId);
|
|
201
|
+
this.vaults.set(vaultId, newVault);
|
|
202
|
+
vault = newVault;
|
|
203
|
+
}
|
|
204
|
+
if (vault) {
|
|
205
|
+
const theVault = vault;
|
|
206
|
+
theVault.ws.handleUpgrade(request, socket, head, (ws$1) => {
|
|
207
|
+
console.log("new client", vaultId);
|
|
208
|
+
theVault.ws.emit("connection", ws$1, request);
|
|
209
|
+
theVault.ws.on("close", () => {
|
|
210
|
+
if (theVault.ws.clients.size === 0) {
|
|
211
|
+
console.log("destroying vault", vaultId);
|
|
212
|
+
this.vaults.delete(vaultId);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
} else socket.destroy();
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
220
|
+
socket.destroy();
|
|
221
|
+
});
|
|
222
|
+
this.server = server || (0, http.createServer)();
|
|
223
|
+
this.server.on("upgrade", this.handleUpgrade);
|
|
224
|
+
this.server.on("request", this.handleRequest);
|
|
225
|
+
}
|
|
226
|
+
listen(port, hostname, backlog, listeningListener) {
|
|
227
|
+
this.server.listen(port, hostname, backlog, listeningListener);
|
|
228
|
+
}
|
|
235
229
|
};
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
});
|
|
230
|
+
|
|
231
|
+
//#endregion
|
|
232
|
+
exports.MultiVaultServer = MultiVaultServer;
|
|
233
|
+
exports.ServerVault = ServerVault;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,85 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { BatchAction } from
|
|
3
|
-
import { Collection, Manifest } from
|
|
4
|
-
import WebSocket, {
|
|
5
|
-
import {
|
|
6
|
-
import { Socket } from
|
|
1
|
+
import { AllActions, IIIFStore, Vault } from "@iiif/helpers/vault";
|
|
2
|
+
import { BatchAction } from "@iiif/helpers/vault/actions";
|
|
3
|
+
import { Collection, Manifest } from "@iiif/presentation-3";
|
|
4
|
+
import WebSocket, { RawData, WebSocketServer } from "ws";
|
|
5
|
+
import { IncomingMessage, Server } from "http";
|
|
6
|
+
import { Socket } from "net";
|
|
7
7
|
|
|
8
|
+
//#region src/server-vault.d.ts
|
|
8
9
|
type RemoteAction = {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
_id: string;
|
|
11
|
+
_type: "vault-action";
|
|
12
|
+
_lastActionId: string;
|
|
13
|
+
action: AllActions | BatchAction;
|
|
13
14
|
};
|
|
14
15
|
type ClientInitAction = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
_id: string;
|
|
17
|
+
_type: "init-response";
|
|
18
|
+
_lastActionId: string;
|
|
19
|
+
data: IIIFStore;
|
|
19
20
|
};
|
|
20
21
|
type ClientInitRequestAction = {
|
|
21
|
-
|
|
22
|
+
_type: "init-request";
|
|
22
23
|
};
|
|
23
24
|
type RemoteActionConfirmation = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
_id: string;
|
|
26
|
+
_type: "vault-action-confirmation";
|
|
27
|
+
_lastActionId: string;
|
|
28
|
+
action: string;
|
|
28
29
|
};
|
|
29
30
|
type RemoteActionRejection = {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
_id: string;
|
|
32
|
+
_type: "vault-action-rejection";
|
|
33
|
+
_lastActionId: string;
|
|
34
|
+
action: string;
|
|
34
35
|
};
|
|
35
36
|
type InitialiseWithResourceAction = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
_id: string;
|
|
38
|
+
_type: "initialise-with-resource";
|
|
39
|
+
_lastActionId: string;
|
|
40
|
+
resource: Collection | Manifest;
|
|
40
41
|
};
|
|
41
42
|
type EmptyVaultResponse = {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
_id: string;
|
|
44
|
+
_type: "empty-vault-response";
|
|
45
|
+
_lastActionId: string;
|
|
45
46
|
};
|
|
46
47
|
type ClientAction = RemoteAction | ClientInitRequestAction | InitialiseWithResourceAction;
|
|
47
48
|
type ServerAction = RemoteAction | RemoteActionConfirmation | RemoteActionRejection | ClientInitAction | EmptyVaultResponse;
|
|
48
49
|
declare module "ws" {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
interface WebSocket {
|
|
51
|
+
isAlive: boolean;
|
|
52
|
+
}
|
|
52
53
|
}
|
|
53
54
|
interface ServerVaultOptions {
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
pingTimeout?: number;
|
|
56
|
+
onClose?: () => void;
|
|
56
57
|
}
|
|
57
58
|
declare class ServerVault {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
59
|
+
vault: Vault;
|
|
60
|
+
ws: WebSocketServer;
|
|
61
|
+
lastActionId: string;
|
|
62
|
+
pingInterval: NodeJS.Timeout;
|
|
63
|
+
private _handleMessage;
|
|
64
|
+
private _handleOpen;
|
|
65
|
+
private _handleClose;
|
|
66
|
+
constructor(options?: ServerVaultOptions);
|
|
67
|
+
close(): Promise<void>;
|
|
68
|
+
setupWebSocket: (socket: WebSocket.WebSocket) => void;
|
|
69
|
+
handleMessage: (socket: WebSocket) => any;
|
|
70
|
+
send: (data: RawData) => void;
|
|
71
|
+
rebroadcast: (source: WebSocket, data: RawData, action: RemoteAction, isBinary?: boolean) => void;
|
|
72
|
+
handleOpen: (socket: WebSocket) => any;
|
|
73
|
+
handleClose: (socket: WebSocket) => any;
|
|
74
|
+
createMessage: (type: string, message: any) => string;
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/server.d.ts
|
|
76
78
|
declare class MultiVaultServer {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
server: Server;
|
|
80
|
+
vaults: Map<string, ServerVault>;
|
|
81
|
+
constructor(server?: Server);
|
|
82
|
+
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
|
|
83
|
+
handleRequest: (request: IncomingMessage, response: any) => void;
|
|
84
|
+
handleUpgrade: (request: IncomingMessage, socket: Socket, head: Buffer) => void;
|
|
83
85
|
}
|
|
84
|
-
|
|
85
|
-
export {
|
|
86
|
+
//#endregion
|
|
87
|
+
export { ClientAction, MultiVaultServer, ServerAction, ServerVault, ServerVaultOptions };
|
|
88
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;KAQK,YAAA;;;;EAAA,MAAA,EAIK,UAJO,GAIM,WAAb;AAAwB,CAAA;AASjB,KAJZ,gBAAA,GAOA;EAIA,GAAA,EAAA,MAAA;EAOA,KAAA,EAAA,eAAA;EAOA,aAAA,EAAA,MAAA;EAOA,IAAA,EA5BG,SA4BH;AAML,CAAA;KA/BK,uBAAA,GA+BsB;EAAe,KAAA,EAAA,cAAA;CAA0B;KA3B/D,wBAAA,GA2B2F;EAEpF,GAAA,EAAA,MAAA;EACR,KAAA,EAAA,2BAAA;EACA,aAAA,EAAA,MAAA;EACA,MAAA,EAAA,MAAA;CACA;KA1BC,qBAAA,GA2BD;EAAkB,GAAA,EAAA,MAAA;EAAC,KAAA,EAAA,wBAAA;EAAA,aAAA,EAAA,MAAA;QAAA,EAAA,MAAA;CAAA;KApBlB,4BAAA,GAoBkB;EASN,GAAA,EAAA,MAAA;EAKJ,KAAA,EAAA,0BAAW;EACf,aAAA,EAAA,MAAA;EACH,QAAA,EAhCM,UAgCN,GAhCmB,QAgCnB;CAEU;KA/BX,kBAAA,GAqCkB;EA8BV,GAAA,EAAA,MAAA;EAae,KAAA,EAAU,sBAAA;EAYX,aAAA,EAAA,MAAA;CAkCX;AAQS,KAhIb,YAAA,GAAe,YAgIF,GAhIiB,uBAgIjB,GAhI2C,4BAgI3C;AAAiB,KA9H9B,YAAA,GACR,YA6HsC,GA5HtC,wBA4HsC,GA3HtC,qBA2HsC,GA1HtC,gBA0HsC,GAzHtC,kBAyHsC;eAAiB,IAAA,CAAA;EAcnC,UAAA,SAAA,CAAA;IAaC,OAAA,EAAA,OAAA;EAAS;;UA3IjB,kBAAA;;ECnEJ,OAAA,CAAA,EAAA,GAAA,GAAA,IAAgB;;AAEP,cDsET,WAAA,CCtES;EAAZ,KAAA,EDuED,KCvEC;EACa,EAAA,EDuEjB,eCvEiB;EAUK,YAAA,EAAA,MAAA;EAoBA,YAAA,ED2CZ,MAAA,CAAO,OC3CK;EAAyB,QAAA,cAAA;EAAc,QAAA,WAAA;EAAM,QAAA,YAAA;wBDiDlD;WA8BV;2BAae,SAAA,CAAU;0BAYX;eAkCX;wBAQS,iBAAiB,iBAAiB;uBAcnC;wBAaC;;;;;cC9MZ,gBAAA;UACH;UACA,YAAY;uBACC;;EDClB,aAAA,EAAY,CAAA,OAAA,ECSW,eDLL,EAAA,QAAW,EAAA,GAAA,EAAA,GAAA,IAAA;EAK7B,aAAA,EAAA,CAAA,OAAgB,ECoBO,eDhBX,EAAA,MAAA,ECgBoC,MDhBpC,EAAA,IAAA,ECgBkD,MDhBlD,EAAA,GAAA,IAAA;AAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AllActions, IIIFStore, Vault } from "@iiif/helpers/vault";
|
|
2
|
+
import WebSocket, { RawData, WebSocketServer } from "ws";
|
|
3
|
+
import { IncomingMessage, Server } from "http";
|
|
4
|
+
import { BatchAction } from "@iiif/helpers/vault/actions";
|
|
5
|
+
import { Collection, Manifest } from "@iiif/presentation-3";
|
|
6
|
+
import { Socket } from "net";
|
|
7
|
+
|
|
8
|
+
//#region src/server-vault.d.ts
|
|
9
|
+
type RemoteAction = {
|
|
10
|
+
_id: string;
|
|
11
|
+
_type: "vault-action";
|
|
12
|
+
_lastActionId: string;
|
|
13
|
+
action: AllActions | BatchAction;
|
|
14
|
+
};
|
|
15
|
+
type ClientInitAction = {
|
|
16
|
+
_id: string;
|
|
17
|
+
_type: "init-response";
|
|
18
|
+
_lastActionId: string;
|
|
19
|
+
data: IIIFStore;
|
|
20
|
+
};
|
|
21
|
+
type ClientInitRequestAction = {
|
|
22
|
+
_type: "init-request";
|
|
23
|
+
};
|
|
24
|
+
type RemoteActionConfirmation = {
|
|
25
|
+
_id: string;
|
|
26
|
+
_type: "vault-action-confirmation";
|
|
27
|
+
_lastActionId: string;
|
|
28
|
+
action: string;
|
|
29
|
+
};
|
|
30
|
+
type RemoteActionRejection = {
|
|
31
|
+
_id: string;
|
|
32
|
+
_type: "vault-action-rejection";
|
|
33
|
+
_lastActionId: string;
|
|
34
|
+
action: string;
|
|
35
|
+
};
|
|
36
|
+
type InitialiseWithResourceAction = {
|
|
37
|
+
_id: string;
|
|
38
|
+
_type: "initialise-with-resource";
|
|
39
|
+
_lastActionId: string;
|
|
40
|
+
resource: Collection | Manifest;
|
|
41
|
+
};
|
|
42
|
+
type EmptyVaultResponse = {
|
|
43
|
+
_id: string;
|
|
44
|
+
_type: "empty-vault-response";
|
|
45
|
+
_lastActionId: string;
|
|
46
|
+
};
|
|
47
|
+
type ClientAction = RemoteAction | ClientInitRequestAction | InitialiseWithResourceAction;
|
|
48
|
+
type ServerAction = RemoteAction | RemoteActionConfirmation | RemoteActionRejection | ClientInitAction | EmptyVaultResponse;
|
|
49
|
+
declare module "ws" {
|
|
50
|
+
interface WebSocket {
|
|
51
|
+
isAlive: boolean;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
interface ServerVaultOptions {
|
|
55
|
+
pingTimeout?: number;
|
|
56
|
+
onClose?: () => void;
|
|
57
|
+
}
|
|
58
|
+
declare class ServerVault {
|
|
59
|
+
vault: Vault;
|
|
60
|
+
ws: WebSocketServer;
|
|
61
|
+
lastActionId: string;
|
|
62
|
+
pingInterval: NodeJS.Timeout;
|
|
63
|
+
private _handleMessage;
|
|
64
|
+
private _handleOpen;
|
|
65
|
+
private _handleClose;
|
|
66
|
+
constructor(options?: ServerVaultOptions);
|
|
67
|
+
close(): Promise<void>;
|
|
68
|
+
setupWebSocket: (socket: WebSocket.WebSocket) => void;
|
|
69
|
+
handleMessage: (socket: WebSocket) => any;
|
|
70
|
+
send: (data: RawData) => void;
|
|
71
|
+
rebroadcast: (source: WebSocket, data: RawData, action: RemoteAction, isBinary?: boolean) => void;
|
|
72
|
+
handleOpen: (socket: WebSocket) => any;
|
|
73
|
+
handleClose: (socket: WebSocket) => any;
|
|
74
|
+
createMessage: (type: string, message: any) => string;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/server.d.ts
|
|
78
|
+
declare class MultiVaultServer {
|
|
79
|
+
server: Server;
|
|
80
|
+
vaults: Map<string, ServerVault>;
|
|
81
|
+
constructor(server?: Server);
|
|
82
|
+
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
|
|
83
|
+
handleRequest: (request: IncomingMessage, response: any) => void;
|
|
84
|
+
handleUpgrade: (request: IncomingMessage, socket: Socket, head: Buffer) => void;
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
export { ClientAction, MultiVaultServer, ServerAction, ServerVault, ServerVaultOptions };
|
|
88
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"sourcesContent":[],"mappings":";;;;;;;;KAQK,YAAA;;;;EAAA,MAAA,EAIK,UAJO,GAIM,WAAb;AAAwB,CAAA;AASjB,KAJZ,gBAAA,GAOA;EAIA,GAAA,EAAA,MAAA;EAOA,KAAA,EAAA,eAAA;EAOA,aAAA,EAAA,MAAA;EAOA,IAAA,EA5BG,SA4BH;AAML,CAAA;KA/BK,uBAAA,GA+BsB;EAAe,KAAA,EAAA,cAAA;CAA0B;KA3B/D,wBAAA,GA2B2F;EAEpF,GAAA,EAAA,MAAA;EACR,KAAA,EAAA,2BAAA;EACA,aAAA,EAAA,MAAA;EACA,MAAA,EAAA,MAAA;CACA;KA1BC,qBAAA,GA2BD;EAAkB,GAAA,EAAA,MAAA;EAAC,KAAA,EAAA,wBAAA;EAAA,aAAA,EAAA,MAAA;QAAA,EAAA,MAAA;CAAA;KApBlB,4BAAA,GAoBkB;EASN,GAAA,EAAA,MAAA;EAKJ,KAAA,EAAA,0BAAW;EACf,aAAA,EAAA,MAAA;EACH,QAAA,EAhCM,UAgCN,GAhCmB,QAgCnB;CAEU;KA/BX,kBAAA,GAqCkB;EA8BV,GAAA,EAAA,MAAA;EAae,KAAA,EAAU,sBAAA;EAYX,aAAA,EAAA,MAAA;CAkCX;AAQS,KAhIb,YAAA,GAAe,YAgIF,GAhIiB,uBAgIjB,GAhI2C,4BAgI3C;AAAiB,KA9H9B,YAAA,GACR,YA6HsC,GA5HtC,wBA4HsC,GA3HtC,qBA2HsC,GA1HtC,gBA0HsC,GAzHtC,kBAyHsC;eAAiB,IAAA,CAAA;EAcnC,UAAA,SAAA,CAAA;IAaC,OAAA,EAAA,OAAA;EAAS;;UA3IjB,kBAAA;;ECnEJ,OAAA,CAAA,EAAA,GAAA,GAAA,IAAgB;;AAEP,cDsET,WAAA,CCtES;EAAZ,KAAA,EDuED,KCvEC;EACa,EAAA,EDuEjB,eCvEiB;EAUK,YAAA,EAAA,MAAA;EAoBA,YAAA,ED2CZ,MAAA,CAAO,OC3CK;EAAyB,QAAA,cAAA;EAAc,QAAA,WAAA;EAAM,QAAA,YAAA;wBDiDlD;WA8BV;2BAae,SAAA,CAAU;0BAYX;eAkCX;wBAQS,iBAAiB,iBAAiB;uBAcnC;wBAaC;;;;;cC9MZ,gBAAA;UACH;UACA,YAAY;uBACC;;EDClB,aAAA,EAAY,CAAA,OAAA,ECSW,eDLL,EAAA,QAAW,EAAA,GAAA,EAAA,GAAA,IAAA;EAK7B,aAAA,EAAA,CAAA,OAAgB,ECoBO,eDhBX,EAAA,MAAA,ECgBoC,MDhBpC,EAAA,IAAA,ECgBkD,MDhBlD,EAAA,GAAA,IAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Vault } from "@iiif/helpers/vault";
|
|
2
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { clearInterval, setInterval } from "timers";
|
|
5
|
+
import { createServer } from "http";
|
|
6
|
+
|
|
7
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/typeof.js
|
|
8
|
+
function _typeof(o) {
|
|
9
|
+
"@babel/helpers - typeof";
|
|
10
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o$1) {
|
|
11
|
+
return typeof o$1;
|
|
12
|
+
} : function(o$1) {
|
|
13
|
+
return o$1 && "function" == typeof Symbol && o$1.constructor === Symbol && o$1 !== Symbol.prototype ? "symbol" : typeof o$1;
|
|
14
|
+
}, _typeof(o);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/toPrimitive.js
|
|
19
|
+
function toPrimitive(t, r) {
|
|
20
|
+
if ("object" != _typeof(t) || !t) return t;
|
|
21
|
+
var e = t[Symbol.toPrimitive];
|
|
22
|
+
if (void 0 !== e) {
|
|
23
|
+
var i = e.call(t, r || "default");
|
|
24
|
+
if ("object" != _typeof(i)) return i;
|
|
25
|
+
throw new TypeError("@@toPrimitive must return a primitive value.");
|
|
26
|
+
}
|
|
27
|
+
return ("string" === r ? String : Number)(t);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/toPropertyKey.js
|
|
32
|
+
function toPropertyKey(t) {
|
|
33
|
+
var i = toPrimitive(t, "string");
|
|
34
|
+
return "symbol" == _typeof(i) ? i : i + "";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
//#region \0@oxc-project+runtime@0.97.0/helpers/defineProperty.js
|
|
39
|
+
function _defineProperty(e, r, t) {
|
|
40
|
+
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
41
|
+
value: t,
|
|
42
|
+
enumerable: !0,
|
|
43
|
+
configurable: !0,
|
|
44
|
+
writable: !0
|
|
45
|
+
}) : e[r] = t, e;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/server-vault.ts
|
|
50
|
+
const GenesisId = "@genesis";
|
|
51
|
+
var ServerVault = class {
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
_defineProperty(this, "vault", void 0);
|
|
54
|
+
_defineProperty(this, "ws", void 0);
|
|
55
|
+
_defineProperty(this, "lastActionId", GenesisId);
|
|
56
|
+
_defineProperty(this, "pingInterval", void 0);
|
|
57
|
+
_defineProperty(this, "_handleMessage", /* @__PURE__ */ new Map());
|
|
58
|
+
_defineProperty(this, "_handleOpen", /* @__PURE__ */ new Map());
|
|
59
|
+
_defineProperty(this, "_handleClose", /* @__PURE__ */ new Map());
|
|
60
|
+
_defineProperty(this, "setupWebSocket", (socket) => {
|
|
61
|
+
socket.isAlive = true;
|
|
62
|
+
socket.on("pong", () => {
|
|
63
|
+
socket.isAlive = true;
|
|
64
|
+
});
|
|
65
|
+
socket.on("open", this.handleOpen(socket));
|
|
66
|
+
socket.on("message", this.handleMessage(socket));
|
|
67
|
+
socket.on("close", this.handleClose(socket));
|
|
68
|
+
socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
69
|
+
});
|
|
70
|
+
_defineProperty(this, "handleMessage", (socket) => {
|
|
71
|
+
if (this._handleMessage.has(socket)) return this._handleMessage.get(socket);
|
|
72
|
+
const handler = (data, isBinary) => {
|
|
73
|
+
const parsed = JSON.parse(data.toString());
|
|
74
|
+
if (parsed._type === "init-request") socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
75
|
+
if (parsed._type === "vault-action") {
|
|
76
|
+
try {
|
|
77
|
+
this.vault.dispatch(parsed.action);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
socket.send(this.createMessage("vault-action-rejection", { action: parsed._id }));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.lastActionId = parsed._id;
|
|
83
|
+
this.rebroadcast(socket, data, parsed, isBinary);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
this._handleMessage.set(socket, handler);
|
|
87
|
+
return handler;
|
|
88
|
+
});
|
|
89
|
+
_defineProperty(this, "send", (data) => {
|
|
90
|
+
this.ws.clients.forEach(function each(client) {
|
|
91
|
+
if (client.readyState === WebSocket.OPEN) client.send(data, { binary: true });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
_defineProperty(this, "rebroadcast", (source, data, action, isBinary = true) => {
|
|
95
|
+
this.ws.clients.forEach((client) => {
|
|
96
|
+
if (client !== source && client.readyState === WebSocket.OPEN) client.send(data, { binary: isBinary });
|
|
97
|
+
else client.send(this.createMessage("vault-action-confirmation", { action: action._id }));
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
_defineProperty(this, "handleOpen", (socket) => {
|
|
101
|
+
if (this._handleOpen.has(socket)) return this._handleOpen.get(socket);
|
|
102
|
+
const handler = () => {};
|
|
103
|
+
this._handleOpen.set(socket, handler);
|
|
104
|
+
return handler;
|
|
105
|
+
});
|
|
106
|
+
_defineProperty(this, "handleClose", (socket) => {
|
|
107
|
+
if (this._handleClose.has(socket)) return this._handleClose.get(socket);
|
|
108
|
+
const handler = () => {};
|
|
109
|
+
this._handleClose.set(socket, handler);
|
|
110
|
+
return handler;
|
|
111
|
+
});
|
|
112
|
+
_defineProperty(this, "createMessage", (type, message) => {
|
|
113
|
+
return JSON.stringify({
|
|
114
|
+
_id: v4(),
|
|
115
|
+
_type: type,
|
|
116
|
+
_lastActionId: this.lastActionId,
|
|
117
|
+
...message
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
this.vault = new Vault();
|
|
121
|
+
this.ws = new WebSocketServer({ noServer: true });
|
|
122
|
+
this.ws.on("connection", this.setupWebSocket);
|
|
123
|
+
this.pingInterval = setInterval(() => {
|
|
124
|
+
this.ws.clients.forEach((socket) => {
|
|
125
|
+
if (socket.isAlive === false) return socket.terminate();
|
|
126
|
+
socket.isAlive = false;
|
|
127
|
+
socket.ping();
|
|
128
|
+
});
|
|
129
|
+
}, options.pingTimeout || 3e4);
|
|
130
|
+
this.ws.on("close", () => {
|
|
131
|
+
clearInterval(this.pingInterval);
|
|
132
|
+
if (options.onClose) options.onClose();
|
|
133
|
+
});
|
|
134
|
+
this.ws.on("error", (err) => {});
|
|
135
|
+
}
|
|
136
|
+
async close() {
|
|
137
|
+
clearTimeout(this.pingInterval);
|
|
138
|
+
return new Promise((resolve, error) => {
|
|
139
|
+
this.ws.close((err) => {
|
|
140
|
+
if (err) error(err);
|
|
141
|
+
resolve();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/server.ts
|
|
149
|
+
var MultiVaultServer = class {
|
|
150
|
+
constructor(server) {
|
|
151
|
+
_defineProperty(this, "server", void 0);
|
|
152
|
+
_defineProperty(this, "vaults", /* @__PURE__ */ new Map());
|
|
153
|
+
_defineProperty(this, "handleRequest", (request, response) => {
|
|
154
|
+
if (request.url === "/vault") {
|
|
155
|
+
const vaults = Array.from(this.vaults.keys());
|
|
156
|
+
const json = JSON.stringify({ vaults });
|
|
157
|
+
response.writeHead(200, {
|
|
158
|
+
"Content-Length": Buffer.byteLength(json),
|
|
159
|
+
"Content-Type": "application/json"
|
|
160
|
+
});
|
|
161
|
+
response.write(json);
|
|
162
|
+
response.end();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
response.writeHead(404);
|
|
166
|
+
response.end();
|
|
167
|
+
});
|
|
168
|
+
_defineProperty(this, "handleUpgrade", (request, socket, head) => {
|
|
169
|
+
console.log("ws.url", request.url);
|
|
170
|
+
const pathname = request.url;
|
|
171
|
+
if (pathname && pathname.startsWith("/vault")) {
|
|
172
|
+
const vaultId = pathname.replace("/vault/", "");
|
|
173
|
+
let vault = this.vaults.get(vaultId);
|
|
174
|
+
if (!vault) {
|
|
175
|
+
const newVault = new ServerVault();
|
|
176
|
+
console.log("new vault", vaultId);
|
|
177
|
+
this.vaults.set(vaultId, newVault);
|
|
178
|
+
vault = newVault;
|
|
179
|
+
}
|
|
180
|
+
if (vault) {
|
|
181
|
+
const theVault = vault;
|
|
182
|
+
theVault.ws.handleUpgrade(request, socket, head, (ws) => {
|
|
183
|
+
console.log("new client", vaultId);
|
|
184
|
+
theVault.ws.emit("connection", ws, request);
|
|
185
|
+
theVault.ws.on("close", () => {
|
|
186
|
+
if (theVault.ws.clients.size === 0) {
|
|
187
|
+
console.log("destroying vault", vaultId);
|
|
188
|
+
this.vaults.delete(vaultId);
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
} else socket.destroy();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
196
|
+
socket.destroy();
|
|
197
|
+
});
|
|
198
|
+
this.server = server || createServer();
|
|
199
|
+
this.server.on("upgrade", this.handleUpgrade);
|
|
200
|
+
this.server.on("request", this.handleRequest);
|
|
201
|
+
}
|
|
202
|
+
listen(port, hostname, backlog, listeningListener) {
|
|
203
|
+
this.server.listen(port, hostname, backlog, listeningListener);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
//#endregion
|
|
208
|
+
export { MultiVaultServer, ServerVault };
|
|
209
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["parsed: ClientAction"],"sources":["../src/server-vault.ts","../src/server.ts"],"sourcesContent":["// Server vault.\nimport { AllActions, IIIFStore, Vault } from \"@iiif/helpers/vault\";\nimport { BatchAction } from \"@iiif/helpers/vault/actions\";\nimport { Collection, Manifest } from \"@iiif/presentation-3\";\nimport WebSocket, { RawData, WebSocketServer } from \"ws\";\nimport { v4 } from \"uuid\";\nimport { clearInterval, setInterval } from \"timers\";\n\ntype RemoteAction = {\n _id: string;\n _type: \"vault-action\";\n _lastActionId: string;\n action: AllActions | BatchAction;\n};\n\nconst GenesisId = \"@genesis\";\n\ntype ClientInitAction = {\n _id: string;\n _type: \"init-response\";\n _lastActionId: string;\n data: IIIFStore;\n};\n\ntype ClientInitRequestAction = {\n _type: \"init-request\";\n};\n\ntype RemoteActionConfirmation = {\n _id: string;\n _type: \"vault-action-confirmation\";\n _lastActionId: string;\n action: string;\n};\n\ntype RemoteActionRejection = {\n _id: string;\n _type: \"vault-action-rejection\";\n _lastActionId: string;\n action: string;\n};\n\ntype InitialiseWithResourceAction = {\n _id: string;\n _type: \"initialise-with-resource\";\n _lastActionId: string;\n resource: Collection | Manifest;\n};\n\ntype EmptyVaultResponse = {\n _id: string;\n _type: \"empty-vault-response\";\n _lastActionId: string;\n};\n\nexport type ClientAction = RemoteAction | ClientInitRequestAction | InitialiseWithResourceAction;\n\nexport type ServerAction =\n | RemoteAction\n | RemoteActionConfirmation\n | RemoteActionRejection\n | ClientInitAction\n | EmptyVaultResponse;\n\n// Extend ws module interface WebSocket.WebSocket with custom typescript property\ndeclare module \"ws\" {\n interface WebSocket {\n isAlive: boolean;\n }\n}\n\nexport interface ServerVaultOptions {\n pingTimeout?: number;\n onClose?: () => void;\n}\n\nexport class ServerVault {\n vault: Vault;\n ws: WebSocketServer;\n lastActionId: string = GenesisId;\n pingInterval: NodeJS.Timeout;\n\n private _handleMessage = new Map();\n private _handleOpen = new Map();\n private _handleClose = new Map();\n\n constructor(options: ServerVaultOptions = {}) {\n this.vault = new Vault();\n this.ws = new WebSocketServer({ noServer: true });\n\n this.ws.on(\"connection\", this.setupWebSocket);\n\n this.pingInterval = setInterval(() => {\n this.ws.clients.forEach((socket) => {\n if ((socket as any).isAlive === false) {\n return socket.terminate();\n }\n\n (socket as any).isAlive = false;\n socket.ping();\n });\n }, options.pingTimeout || 30000);\n\n this.ws.on(\"close\", () => {\n clearInterval(this.pingInterval);\n\n if (options.onClose) {\n options.onClose();\n }\n });\n\n this.ws.on(\"error\", (err) => {\n // @todo error handling.\n });\n }\n\n async close() {\n clearTimeout(this.pingInterval);\n\n return new Promise<void>((resolve, error) => {\n this.ws.close((err) => {\n if (err) {\n error(err);\n }\n resolve();\n });\n });\n }\n\n setupWebSocket = (socket: WebSocket.WebSocket) => {\n socket.isAlive = true;\n socket.on(\"pong\", () => {\n socket.isAlive = true;\n });\n socket.on(\"open\", this.handleOpen(socket));\n socket.on(\"message\", this.handleMessage(socket));\n socket.on(\"close\", this.handleClose(socket));\n // Send the initial state to the client.\n socket.send(this.createMessage(\"init-response\", { data: this.vault.getState() }));\n };\n\n handleMessage = (socket: WebSocket) => {\n if (this._handleMessage.has(socket)) {\n return this._handleMessage.get(socket);\n }\n const handler = (data: RawData, isBinary: boolean) => {\n const parsed: ClientAction = JSON.parse(data.toString());\n\n if (parsed._type === \"init-request\") {\n socket.send(this.createMessage(\"init-response\", { data: this.vault.getState() }));\n }\n\n if (parsed._type === \"vault-action\") {\n try {\n this.vault.dispatch(parsed.action);\n } catch (e) {\n // @todo error handling.\n socket.send(\n this.createMessage(\"vault-action-rejection\", {\n action: parsed._id,\n })\n );\n return;\n }\n\n this.lastActionId = parsed._id;\n this.rebroadcast(socket, data, parsed, isBinary);\n }\n };\n\n this._handleMessage.set(socket, handler);\n\n return handler;\n };\n\n send = (data: RawData) => {\n this.ws.clients.forEach(function each(client) {\n if (client.readyState === WebSocket.OPEN) {\n client.send(data, { binary: true });\n }\n });\n };\n\n rebroadcast = (source: WebSocket, data: RawData, action: RemoteAction, isBinary = true) => {\n this.ws.clients.forEach((client) => {\n if (client !== source && client.readyState === WebSocket.OPEN) {\n client.send(data, { binary: isBinary });\n } else {\n client.send(\n this.createMessage(\"vault-action-confirmation\", {\n action: action._id,\n })\n );\n }\n });\n };\n\n handleOpen = (socket: WebSocket) => {\n if (this._handleOpen.has(socket)) {\n return this._handleOpen.get(socket);\n }\n const handler = () => {\n // Handle open.\n };\n\n this._handleOpen.set(socket, handler);\n\n return handler;\n };\n\n handleClose = (socket: WebSocket) => {\n if (this._handleClose.has(socket)) {\n return this._handleClose.get(socket);\n }\n const handler = () => {\n // Close handler.\n };\n\n this._handleClose.set(socket, handler);\n\n return handler;\n };\n\n createMessage = (type: string, message: any) => {\n return JSON.stringify({\n _id: v4(),\n _type: type,\n _lastActionId: this.lastActionId,\n ...message,\n });\n };\n}\n","import { createServer, IncomingMessage, Server } from \"http\";\nimport { Socket } from \"net\";\nimport { ServerVault } from \"./server-vault\";\n\nexport class MultiVaultServer {\n server: Server;\n vaults: Map<string, ServerVault> = new Map();\n constructor(server?: Server) {\n this.server = server || createServer();\n this.server.on(\"upgrade\", this.handleUpgrade);\n this.server.on(\"request\", this.handleRequest);\n }\n\n listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this.server.listen(port, hostname, backlog, listeningListener);\n }\n\n handleRequest = (request: IncomingMessage, response: any) => {\n if (request.url === \"/vault\") {\n // Need to respond with a JSON body with a list of available vaults.\n const vaults = Array.from(this.vaults.keys());\n const json = JSON.stringify({ vaults });\n\n response.writeHead(200, {\n \"Content-Length\": Buffer.byteLength(json),\n \"Content-Type\": \"application/json\",\n });\n response.write(json);\n response.end();\n return;\n }\n\n // If the pathname is not recognised, destroy the socket and send 404.\n response.writeHead(404);\n response.end();\n };\n\n handleUpgrade = (request: IncomingMessage, socket: Socket, head: Buffer) => {\n console.log(\"ws.url\", request.url);\n const pathname = request.url;\n\n // @todo some logic for handling the pathname. For now, just let any incoming be an ID.\n if (pathname && (pathname as string).startsWith(\"/vault\")) {\n // Get the vault ID from the URL.\n const vaultId = (pathname as string).replace(\"/vault/\", \"\");\n\n let vault = this.vaults.get(vaultId);\n\n // If no vault exists for this ID, create one.\n if (!vault) {\n const newVault = new ServerVault();\n console.log(\"new vault\", vaultId);\n this.vaults.set(vaultId, newVault);\n vault = newVault;\n }\n\n if (vault) {\n const theVault = vault;\n // If it exists, add the socket to the vault.\n theVault.ws.handleUpgrade(request, socket, head, (ws) => {\n console.log(\"new client\", vaultId);\n theVault.ws.emit(\"connection\", ws, request);\n theVault.ws.on(\"close\", () => {\n if (theVault.ws.clients.size === 0) {\n console.log(\"destroying vault\", vaultId);\n this.vaults.delete(vaultId);\n }\n });\n });\n } else {\n socket.destroy();\n }\n return;\n }\n\n // If the pathname is not recognised, destroy the socket and send 404.\n socket.write(\"HTTP/1.1 404 Not Found\\r\\n\\r\\n\");\n socket.destroy();\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manifest-editor/server-vault",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.1-next.0.d468080",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bugs": "https://github.com/digirati-co-uk/iiif-manifest-editor/issues",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/digirati-co-uk/iiif-manifest-editor.git",
|
|
8
|
+
"url": "git+https://github.com/digirati-co-uk/iiif-manifest-editor.git",
|
|
9
9
|
"directory": "packages/server-vault"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
@@ -23,32 +23,27 @@
|
|
|
23
23
|
"timers"
|
|
24
24
|
],
|
|
25
25
|
"main": "./dist/index.cjs",
|
|
26
|
-
"module": "./dist/index.
|
|
27
|
-
"types": "./dist/index.d.
|
|
26
|
+
"module": "./dist/index.mjs",
|
|
27
|
+
"types": "./dist/index.d.cts",
|
|
28
28
|
"files": [
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"exports": {
|
|
32
32
|
".": {
|
|
33
|
-
"import":
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"require": {
|
|
38
|
-
"types": "./dist/index.d.cts",
|
|
39
|
-
"default": "./dist/index.cjs"
|
|
40
|
-
}
|
|
41
|
-
}
|
|
33
|
+
"import": "./dist/index.mjs",
|
|
34
|
+
"require": "./dist/index.cjs"
|
|
35
|
+
},
|
|
36
|
+
"./package.json": "./package.json"
|
|
42
37
|
},
|
|
43
38
|
"scripts": {
|
|
44
|
-
"build": "
|
|
45
|
-
"dev": "
|
|
39
|
+
"build": "tsdown src/index.ts",
|
|
40
|
+
"dev": "tsdown src/index.ts --watch",
|
|
46
41
|
"lint": "publint",
|
|
47
42
|
"typecheck": "tsc --noEmit",
|
|
48
|
-
"prepack": "
|
|
43
|
+
"prepack": "tsdown src/index.ts"
|
|
49
44
|
},
|
|
50
45
|
"dependencies": {
|
|
51
|
-
"@iiif/helpers": "
|
|
46
|
+
"@iiif/helpers": "1.5.7",
|
|
52
47
|
"@iiif/presentation-3": "^2.2.3",
|
|
53
48
|
"uuid": "^9.0.1",
|
|
54
49
|
"ws": "^8.16.0"
|
|
@@ -58,10 +53,10 @@
|
|
|
58
53
|
"@types/node": "^20.11.24",
|
|
59
54
|
"@types/uuid": "^9.0.8",
|
|
60
55
|
"@types/ws": "8.5.10",
|
|
61
|
-
"
|
|
56
|
+
"tsdown": "0.16.4"
|
|
62
57
|
},
|
|
63
58
|
"publishConfig": {
|
|
64
59
|
"access": "public"
|
|
65
60
|
},
|
|
66
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "d4680800d5b3f82a2eb3dbac35b703844f298b6d"
|
|
67
62
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { Vault, AllActions, IIIFStore } from '@iiif/helpers/vault';
|
|
2
|
-
import { BatchAction } from '@iiif/helpers/vault/actions';
|
|
3
|
-
import { Collection, Manifest } from '@iiif/presentation-3';
|
|
4
|
-
import WebSocket, { WebSocketServer, RawData } from 'ws';
|
|
5
|
-
import { Server, IncomingMessage } from 'http';
|
|
6
|
-
import { Socket } from 'net';
|
|
7
|
-
|
|
8
|
-
type RemoteAction = {
|
|
9
|
-
_id: string;
|
|
10
|
-
_type: "vault-action";
|
|
11
|
-
_lastActionId: string;
|
|
12
|
-
action: AllActions | BatchAction;
|
|
13
|
-
};
|
|
14
|
-
type ClientInitAction = {
|
|
15
|
-
_id: string;
|
|
16
|
-
_type: "init-response";
|
|
17
|
-
_lastActionId: string;
|
|
18
|
-
data: IIIFStore;
|
|
19
|
-
};
|
|
20
|
-
type ClientInitRequestAction = {
|
|
21
|
-
_type: "init-request";
|
|
22
|
-
};
|
|
23
|
-
type RemoteActionConfirmation = {
|
|
24
|
-
_id: string;
|
|
25
|
-
_type: "vault-action-confirmation";
|
|
26
|
-
_lastActionId: string;
|
|
27
|
-
action: string;
|
|
28
|
-
};
|
|
29
|
-
type RemoteActionRejection = {
|
|
30
|
-
_id: string;
|
|
31
|
-
_type: "vault-action-rejection";
|
|
32
|
-
_lastActionId: string;
|
|
33
|
-
action: string;
|
|
34
|
-
};
|
|
35
|
-
type InitialiseWithResourceAction = {
|
|
36
|
-
_id: string;
|
|
37
|
-
_type: "initialise-with-resource";
|
|
38
|
-
_lastActionId: string;
|
|
39
|
-
resource: Collection | Manifest;
|
|
40
|
-
};
|
|
41
|
-
type EmptyVaultResponse = {
|
|
42
|
-
_id: string;
|
|
43
|
-
_type: "empty-vault-response";
|
|
44
|
-
_lastActionId: string;
|
|
45
|
-
};
|
|
46
|
-
type ClientAction = RemoteAction | ClientInitRequestAction | InitialiseWithResourceAction;
|
|
47
|
-
type ServerAction = RemoteAction | RemoteActionConfirmation | RemoteActionRejection | ClientInitAction | EmptyVaultResponse;
|
|
48
|
-
declare module "ws" {
|
|
49
|
-
interface WebSocket {
|
|
50
|
-
isAlive: boolean;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
interface ServerVaultOptions {
|
|
54
|
-
pingTimeout?: number;
|
|
55
|
-
onClose?: () => void;
|
|
56
|
-
}
|
|
57
|
-
declare class ServerVault {
|
|
58
|
-
vault: Vault;
|
|
59
|
-
ws: WebSocketServer;
|
|
60
|
-
lastActionId: string;
|
|
61
|
-
pingInterval: NodeJS.Timeout;
|
|
62
|
-
private _handleMessage;
|
|
63
|
-
private _handleOpen;
|
|
64
|
-
private _handleClose;
|
|
65
|
-
constructor(options?: ServerVaultOptions);
|
|
66
|
-
close(): Promise<void>;
|
|
67
|
-
setupWebSocket: (socket: WebSocket.WebSocket) => void;
|
|
68
|
-
handleMessage: (socket: WebSocket) => any;
|
|
69
|
-
send: (data: RawData) => void;
|
|
70
|
-
rebroadcast: (source: WebSocket, data: RawData, action: RemoteAction, isBinary?: boolean) => void;
|
|
71
|
-
handleOpen: (socket: WebSocket) => any;
|
|
72
|
-
handleClose: (socket: WebSocket) => any;
|
|
73
|
-
createMessage: (type: string, message: any) => string;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
declare class MultiVaultServer {
|
|
77
|
-
server: Server;
|
|
78
|
-
vaults: Map<string, ServerVault>;
|
|
79
|
-
constructor(server?: Server);
|
|
80
|
-
listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void): void;
|
|
81
|
-
handleRequest: (request: IncomingMessage, response: any) => void;
|
|
82
|
-
handleUpgrade: (request: IncomingMessage, socket: Socket, head: Buffer) => void;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export { type ClientAction, MultiVaultServer, type ServerAction, ServerVault, type ServerVaultOptions };
|
package/dist/index.js
DELETED
|
@@ -1,204 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
-
var __publicField = (obj, key, value) => {
|
|
4
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
-
return value;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
// src/server-vault.ts
|
|
9
|
-
import { Vault } from "@iiif/helpers/vault";
|
|
10
|
-
import WebSocket, { WebSocketServer } from "ws";
|
|
11
|
-
import { v4 } from "uuid";
|
|
12
|
-
import { clearInterval, setInterval } from "timers";
|
|
13
|
-
var GenesisId = "@genesis";
|
|
14
|
-
var ServerVault = class {
|
|
15
|
-
constructor(options = {}) {
|
|
16
|
-
__publicField(this, "vault");
|
|
17
|
-
__publicField(this, "ws");
|
|
18
|
-
__publicField(this, "lastActionId", GenesisId);
|
|
19
|
-
__publicField(this, "pingInterval");
|
|
20
|
-
__publicField(this, "_handleMessage", /* @__PURE__ */ new Map());
|
|
21
|
-
__publicField(this, "_handleOpen", /* @__PURE__ */ new Map());
|
|
22
|
-
__publicField(this, "_handleClose", /* @__PURE__ */ new Map());
|
|
23
|
-
__publicField(this, "setupWebSocket", (socket) => {
|
|
24
|
-
socket.isAlive = true;
|
|
25
|
-
socket.on("pong", () => {
|
|
26
|
-
socket.isAlive = true;
|
|
27
|
-
});
|
|
28
|
-
socket.on("open", this.handleOpen(socket));
|
|
29
|
-
socket.on("message", this.handleMessage(socket));
|
|
30
|
-
socket.on("close", this.handleClose(socket));
|
|
31
|
-
socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
32
|
-
});
|
|
33
|
-
__publicField(this, "handleMessage", (socket) => {
|
|
34
|
-
if (this._handleMessage.has(socket)) {
|
|
35
|
-
return this._handleMessage.get(socket);
|
|
36
|
-
}
|
|
37
|
-
const handler = (data, isBinary) => {
|
|
38
|
-
const parsed = JSON.parse(data.toString());
|
|
39
|
-
if (parsed._type === "init-request") {
|
|
40
|
-
socket.send(this.createMessage("init-response", { data: this.vault.getState() }));
|
|
41
|
-
}
|
|
42
|
-
if (parsed._type === "vault-action") {
|
|
43
|
-
try {
|
|
44
|
-
this.vault.dispatch(parsed.action);
|
|
45
|
-
} catch (e) {
|
|
46
|
-
socket.send(
|
|
47
|
-
this.createMessage("vault-action-rejection", {
|
|
48
|
-
action: parsed._id
|
|
49
|
-
})
|
|
50
|
-
);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
this.lastActionId = parsed._id;
|
|
54
|
-
this.rebroadcast(socket, data, parsed, isBinary);
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
this._handleMessage.set(socket, handler);
|
|
58
|
-
return handler;
|
|
59
|
-
});
|
|
60
|
-
__publicField(this, "send", (data) => {
|
|
61
|
-
this.ws.clients.forEach(function each(client) {
|
|
62
|
-
if (client.readyState === WebSocket.OPEN) {
|
|
63
|
-
client.send(data, { binary: true });
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
__publicField(this, "rebroadcast", (source, data, action, isBinary = true) => {
|
|
68
|
-
this.ws.clients.forEach((client) => {
|
|
69
|
-
if (client !== source && client.readyState === WebSocket.OPEN) {
|
|
70
|
-
client.send(data, { binary: isBinary });
|
|
71
|
-
} else {
|
|
72
|
-
client.send(
|
|
73
|
-
this.createMessage("vault-action-confirmation", {
|
|
74
|
-
action: action._id
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
__publicField(this, "handleOpen", (socket) => {
|
|
81
|
-
if (this._handleOpen.has(socket)) {
|
|
82
|
-
return this._handleOpen.get(socket);
|
|
83
|
-
}
|
|
84
|
-
const handler = () => {
|
|
85
|
-
};
|
|
86
|
-
this._handleOpen.set(socket, handler);
|
|
87
|
-
return handler;
|
|
88
|
-
});
|
|
89
|
-
__publicField(this, "handleClose", (socket) => {
|
|
90
|
-
if (this._handleClose.has(socket)) {
|
|
91
|
-
return this._handleClose.get(socket);
|
|
92
|
-
}
|
|
93
|
-
const handler = () => {
|
|
94
|
-
};
|
|
95
|
-
this._handleClose.set(socket, handler);
|
|
96
|
-
return handler;
|
|
97
|
-
});
|
|
98
|
-
__publicField(this, "createMessage", (type, message) => {
|
|
99
|
-
return JSON.stringify({
|
|
100
|
-
_id: v4(),
|
|
101
|
-
_type: type,
|
|
102
|
-
_lastActionId: this.lastActionId,
|
|
103
|
-
...message
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
this.vault = new Vault();
|
|
107
|
-
this.ws = new WebSocketServer({ noServer: true });
|
|
108
|
-
this.ws.on("connection", this.setupWebSocket);
|
|
109
|
-
this.pingInterval = setInterval(() => {
|
|
110
|
-
this.ws.clients.forEach((socket) => {
|
|
111
|
-
if (socket.isAlive === false) {
|
|
112
|
-
return socket.terminate();
|
|
113
|
-
}
|
|
114
|
-
socket.isAlive = false;
|
|
115
|
-
socket.ping();
|
|
116
|
-
});
|
|
117
|
-
}, options.pingTimeout || 3e4);
|
|
118
|
-
this.ws.on("close", () => {
|
|
119
|
-
clearInterval(this.pingInterval);
|
|
120
|
-
if (options.onClose) {
|
|
121
|
-
options.onClose();
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
this.ws.on("error", (err) => {
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
async close() {
|
|
128
|
-
clearTimeout(this.pingInterval);
|
|
129
|
-
return new Promise((resolve, error) => {
|
|
130
|
-
this.ws.close((err) => {
|
|
131
|
-
if (err) {
|
|
132
|
-
error(err);
|
|
133
|
-
}
|
|
134
|
-
resolve();
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
// src/server.ts
|
|
141
|
-
import { createServer } from "http";
|
|
142
|
-
var MultiVaultServer = class {
|
|
143
|
-
constructor(server) {
|
|
144
|
-
__publicField(this, "server");
|
|
145
|
-
__publicField(this, "vaults", /* @__PURE__ */ new Map());
|
|
146
|
-
__publicField(this, "handleRequest", (request, response) => {
|
|
147
|
-
if (request.url === "/vault") {
|
|
148
|
-
const vaults = Array.from(this.vaults.keys());
|
|
149
|
-
const json = JSON.stringify({ vaults });
|
|
150
|
-
response.writeHead(200, {
|
|
151
|
-
"Content-Length": Buffer.byteLength(json),
|
|
152
|
-
"Content-Type": "application/json"
|
|
153
|
-
});
|
|
154
|
-
response.write(json);
|
|
155
|
-
response.end();
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
response.writeHead(404);
|
|
159
|
-
response.end();
|
|
160
|
-
});
|
|
161
|
-
__publicField(this, "handleUpgrade", (request, socket, head) => {
|
|
162
|
-
console.log("ws.url", request.url);
|
|
163
|
-
const pathname = request.url;
|
|
164
|
-
if (pathname && pathname.startsWith("/vault")) {
|
|
165
|
-
const vaultId = pathname.replace("/vault/", "");
|
|
166
|
-
let vault = this.vaults.get(vaultId);
|
|
167
|
-
if (!vault) {
|
|
168
|
-
const newVault = new ServerVault();
|
|
169
|
-
console.log("new vault", vaultId);
|
|
170
|
-
this.vaults.set(vaultId, newVault);
|
|
171
|
-
vault = newVault;
|
|
172
|
-
}
|
|
173
|
-
if (vault) {
|
|
174
|
-
const theVault = vault;
|
|
175
|
-
theVault.ws.handleUpgrade(request, socket, head, (ws) => {
|
|
176
|
-
console.log("new client", vaultId);
|
|
177
|
-
theVault.ws.emit("connection", ws, request);
|
|
178
|
-
theVault.ws.on("close", () => {
|
|
179
|
-
if (theVault.ws.clients.size === 0) {
|
|
180
|
-
console.log("destroying vault", vaultId);
|
|
181
|
-
this.vaults.delete(vaultId);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
} else {
|
|
186
|
-
socket.destroy();
|
|
187
|
-
}
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
|
|
191
|
-
socket.destroy();
|
|
192
|
-
});
|
|
193
|
-
this.server = server || createServer();
|
|
194
|
-
this.server.on("upgrade", this.handleUpgrade);
|
|
195
|
-
this.server.on("request", this.handleRequest);
|
|
196
|
-
}
|
|
197
|
-
listen(port, hostname, backlog, listeningListener) {
|
|
198
|
-
this.server.listen(port, hostname, backlog, listeningListener);
|
|
199
|
-
}
|
|
200
|
-
};
|
|
201
|
-
export {
|
|
202
|
-
MultiVaultServer,
|
|
203
|
-
ServerVault
|
|
204
|
-
};
|