@manifest-editor/server-vault 2.1.1-next.0.96fdf30 → 2.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/dist/index.cjs +19 -24
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +0 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +0 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +9 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -6
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -15,31 +16,28 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
16
|
}
|
|
16
17
|
return to;
|
|
17
18
|
};
|
|
18
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
19
20
|
value: mod,
|
|
20
21
|
enumerable: true
|
|
21
22
|
}) : target, mod));
|
|
22
|
-
|
|
23
23
|
//#endregion
|
|
24
|
-
let
|
|
24
|
+
let _iiif_helpers_vault = require("@iiif/helpers/vault");
|
|
25
25
|
let ws = require("ws");
|
|
26
|
-
ws = __toESM(ws);
|
|
26
|
+
ws = __toESM(ws, 1);
|
|
27
27
|
let uuid = require("uuid");
|
|
28
28
|
let timers = require("timers");
|
|
29
29
|
let http = require("http");
|
|
30
|
-
|
|
31
|
-
//#region \0@oxc-project+runtime@0.97.0/helpers/typeof.js
|
|
30
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/typeof.js
|
|
32
31
|
function _typeof(o) {
|
|
33
32
|
"@babel/helpers - typeof";
|
|
34
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o
|
|
35
|
-
return typeof o
|
|
36
|
-
} : function(o
|
|
37
|
-
return o
|
|
33
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
34
|
+
return typeof o;
|
|
35
|
+
} : function(o) {
|
|
36
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
38
37
|
}, _typeof(o);
|
|
39
38
|
}
|
|
40
|
-
|
|
41
39
|
//#endregion
|
|
42
|
-
//#region \0@oxc-project+runtime@0.
|
|
40
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/toPrimitive.js
|
|
43
41
|
function toPrimitive(t, r) {
|
|
44
42
|
if ("object" != _typeof(t) || !t) return t;
|
|
45
43
|
var e = t[Symbol.toPrimitive];
|
|
@@ -50,16 +48,14 @@ function toPrimitive(t, r) {
|
|
|
50
48
|
}
|
|
51
49
|
return ("string" === r ? String : Number)(t);
|
|
52
50
|
}
|
|
53
|
-
|
|
54
51
|
//#endregion
|
|
55
|
-
//#region \0@oxc-project+runtime@0.
|
|
52
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/toPropertyKey.js
|
|
56
53
|
function toPropertyKey(t) {
|
|
57
54
|
var i = toPrimitive(t, "string");
|
|
58
55
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
59
56
|
}
|
|
60
|
-
|
|
61
57
|
//#endregion
|
|
62
|
-
//#region \0@oxc-project+runtime@0.
|
|
58
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/defineProperty.js
|
|
63
59
|
function _defineProperty(e, r, t) {
|
|
64
60
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
65
61
|
value: t,
|
|
@@ -68,7 +64,6 @@ function _defineProperty(e, r, t) {
|
|
|
68
64
|
writable: !0
|
|
69
65
|
}) : e[r] = t, e;
|
|
70
66
|
}
|
|
71
|
-
|
|
72
67
|
//#endregion
|
|
73
68
|
//#region src/server-vault.ts
|
|
74
69
|
const GenesisId = "@genesis";
|
|
@@ -141,7 +136,7 @@ var ServerVault = class {
|
|
|
141
136
|
...message
|
|
142
137
|
});
|
|
143
138
|
});
|
|
144
|
-
this.vault = new
|
|
139
|
+
this.vault = new _iiif_helpers_vault.Vault();
|
|
145
140
|
this.ws = new ws.WebSocketServer({ noServer: true });
|
|
146
141
|
this.ws.on("connection", this.setupWebSocket);
|
|
147
142
|
this.pingInterval = (0, timers.setInterval)(() => {
|
|
@@ -167,7 +162,6 @@ var ServerVault = class {
|
|
|
167
162
|
});
|
|
168
163
|
}
|
|
169
164
|
};
|
|
170
|
-
|
|
171
165
|
//#endregion
|
|
172
166
|
//#region src/server.ts
|
|
173
167
|
var MultiVaultServer = class {
|
|
@@ -203,9 +197,9 @@ var MultiVaultServer = class {
|
|
|
203
197
|
}
|
|
204
198
|
if (vault) {
|
|
205
199
|
const theVault = vault;
|
|
206
|
-
theVault.ws.handleUpgrade(request, socket, head, (ws
|
|
200
|
+
theVault.ws.handleUpgrade(request, socket, head, (ws) => {
|
|
207
201
|
console.log("new client", vaultId);
|
|
208
|
-
theVault.ws.emit("connection", ws
|
|
202
|
+
theVault.ws.emit("connection", ws, request);
|
|
209
203
|
theVault.ws.on("close", () => {
|
|
210
204
|
if (theVault.ws.clients.size === 0) {
|
|
211
205
|
console.log("destroying vault", vaultId);
|
|
@@ -227,7 +221,8 @@ var MultiVaultServer = class {
|
|
|
227
221
|
this.server.listen(port, hostname, backlog, listeningListener);
|
|
228
222
|
}
|
|
229
223
|
};
|
|
230
|
-
|
|
231
224
|
//#endregion
|
|
232
225
|
exports.MultiVaultServer = MultiVaultServer;
|
|
233
|
-
exports.ServerVault = ServerVault;
|
|
226
|
+
exports.ServerVault = ServerVault;
|
|
227
|
+
|
|
228
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["WebSocket","v4","Vault","WebSocketServer","setInterval","createServer"],"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/dist/index.d.cts
CHANGED
|
@@ -4,7 +4,6 @@ import { Collection, Manifest } from "@iiif/presentation-3";
|
|
|
4
4
|
import WebSocket, { RawData, WebSocketServer } from "ws";
|
|
5
5
|
import { IncomingMessage, Server } from "http";
|
|
6
6
|
import { Socket } from "net";
|
|
7
|
-
|
|
8
7
|
//#region src/server-vault.d.ts
|
|
9
8
|
type RemoteAction = {
|
|
10
9
|
_id: string;
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"mappings":";;;;;;;KAQK;EACH;EACA;EACA;EACA,QAAQ,aAAa;;KAKlB;EACH;EACA;EACA;EACA,MAAM;;KAGH;EACH;;KAGG;EACH;EACA;EACA;EACA;;KAGG;EACH;EACA;EACA;EACA;;KAGG;EACH;EACA;EACA;EACA,UAAU,aAAa;;KAGpB;EACH;EACA;EACA;;KAGU,eAAe,eAAe,0BAA0B;KAExD,eACR,eACA,2BACA,wBACA,mBACA;;YAIQ;IACR;;;UAIa;EACf;EACA;;cAGW;EACX,OAAO;EACP,IAAI;EACJ;EACA,cAAc,OAAO;UAEb;UACA;UACA;EAER,YAAY,UAAS;EA8Bf,SAAK;EAaX,iBAAc,QAAY,UAAU;EAYpC,gBAAa,QAAY;EAkCzB,OAAI,MAAU;EAQd,cAAW,QAAY,WAAS,MAAQ,SAAO,QAAU,cAAY;EAcrE,aAAU,QAAY;EAatB,cAAW,QAAY;EAavB,gBAAa,cAAgB;;;;cC3NlB;EACX,QAAQ;EACR,QAAQ,YAAY;EACpB,YAAY,SAAS;EAMrB,OAAO,cAAc,mBAAmB,kBAAkB;EAI1D,gBAAa,SAAa,iBAAe;EAoBzC,gBAAa,SAAa,iBAAe,QAAU,QAAM,MAAQ"}
|
package/dist/index.d.mts
CHANGED
|
@@ -4,7 +4,6 @@ import { IncomingMessage, Server } from "http";
|
|
|
4
4
|
import { BatchAction } from "@iiif/helpers/vault/actions";
|
|
5
5
|
import { Collection, Manifest } from "@iiif/presentation-3";
|
|
6
6
|
import { Socket } from "net";
|
|
7
|
-
|
|
8
7
|
//#region src/server-vault.d.ts
|
|
9
8
|
type RemoteAction = {
|
|
10
9
|
_id: string;
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/server-vault.ts","../src/server.ts"],"mappings":";;;;;;;KAQK;EACH;EACA;EACA;EACA,QAAQ,aAAa;;KAKlB;EACH;EACA;EACA;EACA,MAAM;;KAGH;EACH;;KAGG;EACH;EACA;EACA;EACA;;KAGG;EACH;EACA;EACA;EACA;;KAGG;EACH;EACA;EACA;EACA,UAAU,aAAa;;KAGpB;EACH;EACA;EACA;;KAGU,eAAe,eAAe,0BAA0B;KAExD,eACR,eACA,2BACA,wBACA,mBACA;;YAIQ;IACR;;;UAIa;EACf;EACA;;cAGW;EACX,OAAO;EACP,IAAI;EACJ;EACA,cAAc,OAAO;UAEb;UACA;UACA;EAER,YAAY,UAAS;EA8Bf,SAAK;EAaX,iBAAc,QAAY,UAAU;EAYpC,gBAAa,QAAY;EAkCzB,OAAI,MAAU;EAQd,cAAW,QAAY,WAAS,MAAQ,SAAO,QAAU,cAAY;EAcrE,aAAU,QAAY;EAatB,cAAW,QAAY;EAavB,gBAAa,cAAgB;;;;cC3NlB;EACX,QAAQ;EACR,QAAQ,YAAY;EACpB,YAAY,SAAS;EAMrB,OAAO,cAAc,mBAAmB,kBAAkB;EAI1D,gBAAa,SAAa,iBAAe;EAoBzC,gBAAa,SAAa,iBAAe,QAAU,QAAM,MAAQ"}
|
package/dist/index.mjs
CHANGED
|
@@ -3,19 +3,17 @@ import WebSocket, { WebSocketServer } from "ws";
|
|
|
3
3
|
import { v4 } from "uuid";
|
|
4
4
|
import { clearInterval, setInterval } from "timers";
|
|
5
5
|
import { createServer } from "http";
|
|
6
|
-
|
|
7
|
-
//#region \0@oxc-project+runtime@0.97.0/helpers/typeof.js
|
|
6
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/typeof.js
|
|
8
7
|
function _typeof(o) {
|
|
9
8
|
"@babel/helpers - typeof";
|
|
10
|
-
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o
|
|
11
|
-
return typeof o
|
|
12
|
-
} : function(o
|
|
13
|
-
return o
|
|
9
|
+
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function(o) {
|
|
10
|
+
return typeof o;
|
|
11
|
+
} : function(o) {
|
|
12
|
+
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
|
|
14
13
|
}, _typeof(o);
|
|
15
14
|
}
|
|
16
|
-
|
|
17
15
|
//#endregion
|
|
18
|
-
//#region \0@oxc-project+runtime@0.
|
|
16
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/toPrimitive.js
|
|
19
17
|
function toPrimitive(t, r) {
|
|
20
18
|
if ("object" != _typeof(t) || !t) return t;
|
|
21
19
|
var e = t[Symbol.toPrimitive];
|
|
@@ -26,16 +24,14 @@ function toPrimitive(t, r) {
|
|
|
26
24
|
}
|
|
27
25
|
return ("string" === r ? String : Number)(t);
|
|
28
26
|
}
|
|
29
|
-
|
|
30
27
|
//#endregion
|
|
31
|
-
//#region \0@oxc-project+runtime@0.
|
|
28
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/toPropertyKey.js
|
|
32
29
|
function toPropertyKey(t) {
|
|
33
30
|
var i = toPrimitive(t, "string");
|
|
34
31
|
return "symbol" == _typeof(i) ? i : i + "";
|
|
35
32
|
}
|
|
36
|
-
|
|
37
33
|
//#endregion
|
|
38
|
-
//#region \0@oxc-project+runtime@0.
|
|
34
|
+
//#region \0@oxc-project+runtime@0.143.0/helpers/esm/defineProperty.js
|
|
39
35
|
function _defineProperty(e, r, t) {
|
|
40
36
|
return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
|
|
41
37
|
value: t,
|
|
@@ -44,7 +40,6 @@ function _defineProperty(e, r, t) {
|
|
|
44
40
|
writable: !0
|
|
45
41
|
}) : e[r] = t, e;
|
|
46
42
|
}
|
|
47
|
-
|
|
48
43
|
//#endregion
|
|
49
44
|
//#region src/server-vault.ts
|
|
50
45
|
const GenesisId = "@genesis";
|
|
@@ -143,7 +138,6 @@ var ServerVault = class {
|
|
|
143
138
|
});
|
|
144
139
|
}
|
|
145
140
|
};
|
|
146
|
-
|
|
147
141
|
//#endregion
|
|
148
142
|
//#region src/server.ts
|
|
149
143
|
var MultiVaultServer = class {
|
|
@@ -203,7 +197,7 @@ var MultiVaultServer = class {
|
|
|
203
197
|
this.server.listen(port, hostname, backlog, listeningListener);
|
|
204
198
|
}
|
|
205
199
|
};
|
|
206
|
-
|
|
207
200
|
//#endregion
|
|
208
201
|
export { MultiVaultServer, ServerVault };
|
|
202
|
+
|
|
209
203
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manifest-editor/server-vault",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bugs": "https://github.com/digirati-co-uk/iiif-manifest-editor/issues",
|
|
6
6
|
"repository": {
|
|
@@ -43,20 +43,19 @@
|
|
|
43
43
|
"prepack": "tsdown src/index.ts"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@iiif/helpers": "1.
|
|
46
|
+
"@iiif/helpers": "1.6.1",
|
|
47
47
|
"@iiif/presentation-3": "^2.2.3",
|
|
48
48
|
"uuid": "^9.0.1",
|
|
49
49
|
"ws": "^8.16.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@
|
|
53
|
-
"@types/node": "^20.11.24",
|
|
52
|
+
"@types/node": "22.20.1",
|
|
54
53
|
"@types/uuid": "^9.0.8",
|
|
55
54
|
"@types/ws": "8.5.10",
|
|
56
|
-
"tsdown": "0.
|
|
55
|
+
"tsdown": "0.22.14"
|
|
57
56
|
},
|
|
58
57
|
"publishConfig": {
|
|
59
58
|
"access": "public"
|
|
60
59
|
},
|
|
61
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "9ee7ce32bf7728205e22d852fb80061b86067471"
|
|
62
61
|
}
|