@mml-io/3d-web-experience-server 0.21.6 → 0.23.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.
@@ -1,5 +1,4 @@
|
|
1
|
-
import {
|
2
|
-
import { UserData, UserIdentity, UserNetworkingServer } from "@mml-io/3d-web-user-networking";
|
1
|
+
import { UserData, UserNetworkingServer, UserNetworkingServerError } from "@mml-io/3d-web-user-networking";
|
3
2
|
import express from "express";
|
4
3
|
import enableWs from "express-ws";
|
5
4
|
import { MMLDocumentsServer } from "./MMLDocumentsServer";
|
@@ -8,13 +7,12 @@ type UserAuthenticator = {
|
|
8
7
|
getClientIdForSessionToken: (sessionToken: string) => {
|
9
8
|
id: number;
|
10
9
|
} | null;
|
11
|
-
onClientConnect(clientId: number, sessionToken: string, userIdentityPresentedOnConnection?:
|
12
|
-
onClientUserIdentityUpdate(clientId: number, userIdentity:
|
10
|
+
onClientConnect(clientId: number, sessionToken: string, userIdentityPresentedOnConnection?: UserData): Promise<UserData | true | Error> | UserData | true | Error;
|
11
|
+
onClientUserIdentityUpdate(clientId: number, userIdentity: UserData): UserData | true | Error;
|
13
12
|
onClientDisconnect(clientId: number): void;
|
14
13
|
};
|
15
14
|
export declare const defaultSessionTokenPlaceholder = "SESSION.TOKEN.PLACEHOLDER";
|
16
15
|
export type Networked3dWebExperienceServerConfig = {
|
17
|
-
connectionLimit?: number;
|
18
16
|
networkPath: string;
|
19
17
|
webClientServing: {
|
20
18
|
indexUrl: string;
|
@@ -24,7 +22,7 @@ export type Networked3dWebExperienceServerConfig = {
|
|
24
22
|
clientUrl: string;
|
25
23
|
clientWatchWebsocketPath?: string;
|
26
24
|
};
|
27
|
-
|
25
|
+
enableChat?: boolean;
|
28
26
|
assetServing?: {
|
29
27
|
assetsDir: string;
|
30
28
|
assetsUrl: string;
|
@@ -39,11 +37,10 @@ export type Networked3dWebExperienceServerConfig = {
|
|
39
37
|
export declare class Networked3dWebExperienceServer {
|
40
38
|
private config;
|
41
39
|
userNetworkingServer: UserNetworkingServer;
|
42
|
-
chatNetworkingServer?: ChatNetworkingServer;
|
43
40
|
mmlDocumentsServer?: MMLDocumentsServer;
|
44
41
|
constructor(config: Networked3dWebExperienceServerConfig);
|
45
42
|
updateUserCharacter(clientId: number, userData: UserData): void;
|
46
|
-
dispose(
|
43
|
+
dispose(error?: UserNetworkingServerError): void;
|
47
44
|
registerExpressRoutes(app: enableWs.Application): void;
|
48
45
|
}
|
49
46
|
export {};
|
package/build/index.js
CHANGED
@@ -11,10 +11,12 @@ var getMmlDocumentContent = (documentPath) => {
|
|
11
11
|
var MMLDocumentsServer = class {
|
12
12
|
constructor(directory, watchPattern) {
|
13
13
|
this.directory = directory;
|
14
|
-
this.documents = /* @__PURE__ */ new Map();
|
15
14
|
this.watchPattern = watchPattern;
|
16
15
|
this.watch();
|
17
16
|
}
|
17
|
+
documents = /* @__PURE__ */ new Map();
|
18
|
+
watcher;
|
19
|
+
watchPattern;
|
18
20
|
dispose() {
|
19
21
|
for (const { document } of this.documents.values()) {
|
20
22
|
document.dispose();
|
@@ -90,13 +92,6 @@ var MMLDocumentsServer = class {
|
|
90
92
|
|
91
93
|
// src/Networked3dWebExperienceServer.ts
|
92
94
|
import {
|
93
|
-
CHAT_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
94
|
-
CHAT_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
|
95
|
-
ChatNetworkingServer
|
96
|
-
} from "@mml-io/3d-web-text-chat";
|
97
|
-
import {
|
98
|
-
USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
99
|
-
USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
|
100
95
|
UserNetworkingServer
|
101
96
|
} from "@mml-io/3d-web-user-networking";
|
102
97
|
import cors from "cors";
|
@@ -128,15 +123,8 @@ var Networked3dWebExperienceServer = class {
|
|
128
123
|
const { documentsWatchPath, documentsDirectoryRoot } = this.config.mmlServing;
|
129
124
|
this.mmlDocumentsServer = new MMLDocumentsServer(documentsDirectoryRoot, documentsWatchPath);
|
130
125
|
}
|
131
|
-
if (this.config.chatNetworkPath) {
|
132
|
-
this.chatNetworkingServer = new ChatNetworkingServer({
|
133
|
-
getChatUserIdentity: (sessionToken) => {
|
134
|
-
return this.config.userAuthenticator.getClientIdForSessionToken(sessionToken);
|
135
|
-
}
|
136
|
-
});
|
137
|
-
}
|
138
126
|
this.userNetworkingServer = new UserNetworkingServer({
|
139
|
-
|
127
|
+
legacyAdapterEnabled: true,
|
140
128
|
onClientConnect: (clientId, sessionToken, userIdentityPresentedOnConnection) => {
|
141
129
|
return this.config.userAuthenticator.onClientConnect(
|
142
130
|
clientId,
|
@@ -149,33 +137,17 @@ var Networked3dWebExperienceServer = class {
|
|
149
137
|
},
|
150
138
|
onClientDisconnect: (clientId) => {
|
151
139
|
this.config.userAuthenticator.onClientDisconnect(clientId);
|
152
|
-
if (this.chatNetworkingServer) {
|
153
|
-
this.chatNetworkingServer.disconnectClientId(clientId);
|
154
|
-
}
|
155
140
|
}
|
156
141
|
});
|
157
142
|
}
|
143
|
+
userNetworkingServer;
|
144
|
+
mmlDocumentsServer;
|
158
145
|
updateUserCharacter(clientId, userData) {
|
159
146
|
console.log(`Initiate server-side update of client ${clientId}`);
|
160
147
|
this.userNetworkingServer.updateUserCharacter(clientId, userData);
|
161
148
|
}
|
162
|
-
dispose(
|
163
|
-
this.userNetworkingServer.dispose(
|
164
|
-
errorMessage ? {
|
165
|
-
type: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
166
|
-
errorType: USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
|
167
|
-
message: errorMessage
|
168
|
-
} : void 0
|
169
|
-
);
|
170
|
-
if (this.chatNetworkingServer) {
|
171
|
-
this.chatNetworkingServer.dispose(
|
172
|
-
errorMessage ? {
|
173
|
-
type: CHAT_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,
|
174
|
-
errorType: CHAT_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,
|
175
|
-
message: errorMessage
|
176
|
-
} : void 0
|
177
|
-
);
|
178
|
-
}
|
149
|
+
dispose(error) {
|
150
|
+
this.userNetworkingServer.dispose(error);
|
179
151
|
if (this.mmlDocumentsServer) {
|
180
152
|
this.mmlDocumentsServer.dispose();
|
181
153
|
}
|
@@ -184,12 +156,6 @@ var Networked3dWebExperienceServer = class {
|
|
184
156
|
app.ws(this.config.networkPath, (ws) => {
|
185
157
|
this.userNetworkingServer.connectClient(ws);
|
186
158
|
});
|
187
|
-
if (this.config.chatNetworkPath && this.chatNetworkingServer) {
|
188
|
-
const chatServer = this.chatNetworkingServer;
|
189
|
-
app.ws(this.config.chatNetworkPath, (ws) => {
|
190
|
-
chatServer.connectClient(ws);
|
191
|
-
});
|
192
|
-
}
|
193
159
|
const webClientServing = this.config.webClientServing;
|
194
160
|
if (webClientServing) {
|
195
161
|
app.get(webClientServing.indexUrl, async (req, res) => {
|
package/build/index.js.map
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"version": 3,
|
3
3
|
"sources": ["../src/MMLDocumentsServer.ts", "../src/Networked3dWebExperienceServer.ts", "../src/websocketDirectoryChangeListener.ts"],
|
4
|
-
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport url from \"node:url\";\n\nimport { EditableNetworkedDOM, LocalObservableDOMFactory } from \"@mml-io/networked-dom-server\";\nimport { watch, FSWatcher } from \"chokidar\";\nimport micromatch from \"micromatch\";\nimport WebSocket from \"ws\";\n\nconst getMmlDocumentContent = (documentPath: string) => {\n return fs.readFileSync(documentPath, { encoding: \"utf8\", flag: \"r\" });\n};\n\nexport class MMLDocumentsServer {\n private documents = new Map<\n string,\n {\n documentPath: string;\n document: EditableNetworkedDOM;\n }\n >();\n private watcher: FSWatcher;\n private watchPattern: string;\n\n constructor(\n private directory: string,\n watchPattern: string,\n ) {\n this.watchPattern = watchPattern;\n this.watch();\n }\n\n public dispose() {\n for (const { document } of this.documents.values()) {\n document.dispose();\n }\n this.documents.clear();\n this.watcher.close();\n }\n\n public handle(filename: string, ws: WebSocket) {\n const document = this.documents.get(filename)?.document;\n if (!document) {\n ws.close();\n return;\n }\n\n document.addWebSocket(ws as any);\n ws.on(\"close\", () => {\n document.removeWebSocket(ws as any);\n });\n }\n\n private watch() {\n this.watcher = watch(this.directory, {\n ignoreInitial: false,\n ignored: (checkPath, stats) => {\n if (!stats || !stats.isFile()) {\n return false;\n }\n return !micromatch.isMatch(checkPath, this.watchPattern);\n },\n persistent: true,\n });\n this.watcher\n .on(\"add\", (fullPath, stats) => {\n if (!stats || !stats.isFile()) {\n return;\n }\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been added`);\n const contents = getMmlDocumentContent(fullPath);\n const document = new EditableNetworkedDOM(\n url.pathToFileURL(fullPath).toString(),\n LocalObservableDOMFactory,\n );\n document.load(contents);\n\n const currentData = {\n documentPath: fullPath,\n document,\n };\n this.documents.set(relativePath, currentData);\n })\n .on(\"change\", (fullPath) => {\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been changed`);\n const contents = getMmlDocumentContent(fullPath);\n const documentState = this.documents.get(relativePath);\n if (!documentState) {\n console.error(`MML Document '${relativePath}' not found`);\n return;\n }\n documentState.document.load(contents);\n })\n .on(\"unlink\", (fullPath) => {\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been removed`);\n const documentState = this.documents.get(relativePath);\n if (!documentState) {\n console.error(`MML Document '${relativePath}' not found`);\n return;\n }\n documentState.document.dispose();\n this.documents.delete(relativePath);\n })\n .on(\"error\", (error) => {\n console.error(\"Error whilst watching directory\", error);\n });\n }\n}\n", "import {\n CHAT_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,\n CHAT_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,\n ChatNetworkingServer,\n} from \"@mml-io/3d-web-text-chat\";\nimport {\n USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,\n USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,\n UserData,\n UserIdentity,\n UserNetworkingServer,\n} from \"@mml-io/3d-web-user-networking\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport enableWs from \"express-ws\";\nimport WebSocket from \"ws\";\n\nimport { MMLDocumentsServer } from \"./MMLDocumentsServer\";\nimport { websocketDirectoryChangeListener } from \"./websocketDirectoryChangeListener\";\n\ntype UserAuthenticator = {\n generateAuthorizedSessionToken(req: express.Request): Promise<string | null>;\n getClientIdForSessionToken: (sessionToken: string) => {\n id: number;\n } | null;\n onClientConnect(\n clientId: number,\n sessionToken: string,\n userIdentityPresentedOnConnection?: UserIdentity,\n ): Promise<UserData | null> | UserData | null;\n onClientUserIdentityUpdate(clientId: number, userIdentity: UserIdentity): UserData | null;\n onClientDisconnect(clientId: number): void;\n};\n\nexport const defaultSessionTokenPlaceholder = \"SESSION.TOKEN.PLACEHOLDER\";\n\nexport type Networked3dWebExperienceServerConfig = {\n connectionLimit?: number;\n networkPath: string;\n webClientServing: {\n indexUrl: string;\n indexContent: string;\n sessionTokenPlaceholder?: string;\n\n clientBuildDir: string;\n clientUrl: string;\n clientWatchWebsocketPath?: string;\n };\n chatNetworkPath?: string;\n assetServing?: {\n assetsDir: string;\n assetsUrl: string;\n };\n mmlServing?: {\n documentsWatchPath: string;\n documentsDirectoryRoot: string;\n documentsUrl: string;\n };\n userAuthenticator: UserAuthenticator;\n};\n\nexport class Networked3dWebExperienceServer {\n public userNetworkingServer: UserNetworkingServer;\n\n public chatNetworkingServer?: ChatNetworkingServer;\n\n public mmlDocumentsServer?: MMLDocumentsServer;\n\n constructor(private config: Networked3dWebExperienceServerConfig) {\n if (this.config.mmlServing) {\n const { documentsWatchPath, documentsDirectoryRoot } = this.config.mmlServing;\n this.mmlDocumentsServer = new MMLDocumentsServer(documentsDirectoryRoot, documentsWatchPath);\n }\n\n if (this.config.chatNetworkPath) {\n this.chatNetworkingServer = new ChatNetworkingServer({\n getChatUserIdentity: (sessionToken: string) => {\n return this.config.userAuthenticator.getClientIdForSessionToken(sessionToken);\n },\n });\n }\n\n this.userNetworkingServer = new UserNetworkingServer({\n connectionLimit: config.connectionLimit,\n onClientConnect: (\n clientId: number,\n sessionToken: string,\n userIdentityPresentedOnConnection?: UserIdentity,\n ): Promise<UserData | null> | UserData | null => {\n return this.config.userAuthenticator.onClientConnect(\n clientId,\n sessionToken,\n userIdentityPresentedOnConnection,\n );\n },\n onClientUserIdentityUpdate: (\n clientId: number,\n userIdentity: UserIdentity,\n ): UserData | null => {\n // Called whenever a user connects or updates their character/identity\n return this.config.userAuthenticator.onClientUserIdentityUpdate(clientId, userIdentity);\n },\n onClientDisconnect: (clientId: number): void => {\n this.config.userAuthenticator.onClientDisconnect(clientId);\n // Disconnect the corresponding chat client to avoid later conflicts of client ids\n if (this.chatNetworkingServer) {\n this.chatNetworkingServer.disconnectClientId(clientId);\n }\n },\n });\n }\n\n public updateUserCharacter(clientId: number, userData: UserData) {\n console.log(`Initiate server-side update of client ${clientId}`);\n this.userNetworkingServer.updateUserCharacter(clientId, userData);\n }\n\n public dispose(errorMessage?: string) {\n this.userNetworkingServer.dispose(\n errorMessage\n ? {\n type: USER_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,\n errorType: USER_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,\n message: errorMessage,\n }\n : undefined,\n );\n if (this.chatNetworkingServer) {\n this.chatNetworkingServer.dispose(\n errorMessage\n ? {\n type: CHAT_NETWORKING_SERVER_ERROR_MESSAGE_TYPE,\n errorType: CHAT_NETWORKING_SERVER_SHUTDOWN_ERROR_TYPE,\n message: errorMessage,\n }\n : undefined,\n );\n }\n if (this.mmlDocumentsServer) {\n this.mmlDocumentsServer.dispose();\n }\n }\n\n registerExpressRoutes(app: enableWs.Application) {\n app.ws(this.config.networkPath, (ws) => {\n this.userNetworkingServer.connectClient(ws);\n });\n\n if (this.config.chatNetworkPath && this.chatNetworkingServer) {\n const chatServer = this.chatNetworkingServer;\n app.ws(this.config.chatNetworkPath, (ws) => {\n chatServer.connectClient(ws);\n });\n }\n\n const webClientServing = this.config.webClientServing;\n if (webClientServing) {\n app.get(webClientServing.indexUrl, async (req: express.Request, res: express.Response) => {\n const token = await this.config.userAuthenticator.generateAuthorizedSessionToken(req);\n if (!token) {\n res.send(\"Error: Could not generate token\");\n return;\n }\n const authorizedDemoIndexContent = webClientServing.indexContent.replace(\n webClientServing.sessionTokenPlaceholder || defaultSessionTokenPlaceholder,\n token,\n );\n res.send(authorizedDemoIndexContent);\n });\n\n app.use(webClientServing.clientUrl, express.static(webClientServing.clientBuildDir));\n if (webClientServing.clientWatchWebsocketPath) {\n websocketDirectoryChangeListener(app, {\n directory: webClientServing.clientBuildDir,\n websocketPath: webClientServing.clientWatchWebsocketPath,\n });\n }\n }\n\n const mmlDocumentsServer = this.mmlDocumentsServer;\n const mmlServing = this.config.mmlServing;\n // Handle example document sockets\n if (mmlServing && mmlDocumentsServer) {\n app.ws(`${mmlServing.documentsUrl}*`, (ws: WebSocket, req: express.Request) => {\n const path = req.params[0];\n console.log(\"document requested\", { path });\n mmlDocumentsServer.handle(path, ws);\n });\n }\n\n if (this.config.assetServing) {\n // Serve assets with CORS allowing all origins\n app.use(\n this.config.assetServing.assetsUrl,\n cors(),\n express.static(this.config.assetServing.assetsDir),\n );\n }\n }\n}\n", "import { watch } from \"chokidar\";\nimport enableWs from \"express-ws\";\nimport WebSocket from \"ws\";\n\nexport function websocketDirectoryChangeListener(\n app: enableWs.Application,\n options: {\n directory: string;\n websocketPath: string;\n },\n) {\n const listeningClients = new Set<WebSocket>();\n watch(options.directory).on(\"all\", () => {\n for (const client of listeningClients) {\n client.send(\"change\");\n }\n });\n // Create an event-source that updates whenever the build folder gets modified\n app.ws(options.websocketPath, (ws: WebSocket) => {\n listeningClients.add(ws);\n ws.on(\"close\", () => {\n listeningClients.delete(ws);\n });\n });\n}\n"],
|
5
|
-
"mappings": ";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAEhB,SAAS,sBAAsB,iCAAiC;AAChE,SAAS,aAAwB;AACjC,OAAO,gBAAgB;AAGvB,IAAM,wBAAwB,CAAC,iBAAyB;AACtD,SAAO,GAAG,aAAa,cAAc,EAAE,UAAU,QAAQ,MAAM,IAAI,CAAC;AACtE;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAW9B,YACU,WACR,cACA;AAFQ;
|
4
|
+
"sourcesContent": ["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport url from \"node:url\";\n\nimport { EditableNetworkedDOM, LocalObservableDOMFactory } from \"@mml-io/networked-dom-server\";\nimport { watch, FSWatcher } from \"chokidar\";\nimport micromatch from \"micromatch\";\nimport WebSocket from \"ws\";\n\nconst getMmlDocumentContent = (documentPath: string) => {\n return fs.readFileSync(documentPath, { encoding: \"utf8\", flag: \"r\" });\n};\n\nexport class MMLDocumentsServer {\n private documents = new Map<\n string,\n {\n documentPath: string;\n document: EditableNetworkedDOM;\n }\n >();\n private watcher: FSWatcher;\n private watchPattern: string;\n\n constructor(\n private directory: string,\n watchPattern: string,\n ) {\n this.watchPattern = watchPattern;\n this.watch();\n }\n\n public dispose() {\n for (const { document } of this.documents.values()) {\n document.dispose();\n }\n this.documents.clear();\n this.watcher.close();\n }\n\n public handle(filename: string, ws: WebSocket) {\n const document = this.documents.get(filename)?.document;\n if (!document) {\n ws.close();\n return;\n }\n\n document.addWebSocket(ws as any);\n ws.on(\"close\", () => {\n document.removeWebSocket(ws as any);\n });\n }\n\n private watch() {\n this.watcher = watch(this.directory, {\n ignoreInitial: false,\n ignored: (checkPath, stats) => {\n if (!stats || !stats.isFile()) {\n return false;\n }\n return !micromatch.isMatch(checkPath, this.watchPattern);\n },\n persistent: true,\n });\n this.watcher\n .on(\"add\", (fullPath, stats) => {\n if (!stats || !stats.isFile()) {\n return;\n }\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been added`);\n const contents = getMmlDocumentContent(fullPath);\n const document = new EditableNetworkedDOM(\n url.pathToFileURL(fullPath).toString(),\n LocalObservableDOMFactory,\n );\n document.load(contents);\n\n const currentData = {\n documentPath: fullPath,\n document,\n };\n this.documents.set(relativePath, currentData);\n })\n .on(\"change\", (fullPath) => {\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been changed`);\n const contents = getMmlDocumentContent(fullPath);\n const documentState = this.documents.get(relativePath);\n if (!documentState) {\n console.error(`MML Document '${relativePath}' not found`);\n return;\n }\n documentState.document.load(contents);\n })\n .on(\"unlink\", (fullPath) => {\n const relativePath = path.relative(this.directory, fullPath);\n console.log(`MML Document '${relativePath}' has been removed`);\n const documentState = this.documents.get(relativePath);\n if (!documentState) {\n console.error(`MML Document '${relativePath}' not found`);\n return;\n }\n documentState.document.dispose();\n this.documents.delete(relativePath);\n })\n .on(\"error\", (error) => {\n console.error(\"Error whilst watching directory\", error);\n });\n }\n}\n", "import {\n UserData,\n UserNetworkingServer,\n UserNetworkingServerError,\n} from \"@mml-io/3d-web-user-networking\";\nimport cors from \"cors\";\nimport express from \"express\";\nimport enableWs from \"express-ws\";\nimport ws from \"ws\";\n\nimport { MMLDocumentsServer } from \"./MMLDocumentsServer\";\nimport { websocketDirectoryChangeListener } from \"./websocketDirectoryChangeListener\";\n\ntype UserAuthenticator = {\n generateAuthorizedSessionToken(req: express.Request): Promise<string | null>;\n getClientIdForSessionToken: (sessionToken: string) => {\n id: number;\n } | null;\n onClientConnect(\n clientId: number,\n sessionToken: string,\n userIdentityPresentedOnConnection?: UserData,\n ): Promise<UserData | true | Error> | UserData | true | Error;\n onClientUserIdentityUpdate(clientId: number, userIdentity: UserData): UserData | true | Error;\n onClientDisconnect(clientId: number): void;\n};\n\nexport const defaultSessionTokenPlaceholder = \"SESSION.TOKEN.PLACEHOLDER\";\n\nexport type Networked3dWebExperienceServerConfig = {\n networkPath: string;\n webClientServing: {\n indexUrl: string;\n indexContent: string;\n sessionTokenPlaceholder?: string;\n\n clientBuildDir: string;\n clientUrl: string;\n clientWatchWebsocketPath?: string;\n };\n enableChat?: boolean;\n assetServing?: {\n assetsDir: string;\n assetsUrl: string;\n };\n mmlServing?: {\n documentsWatchPath: string;\n documentsDirectoryRoot: string;\n documentsUrl: string;\n };\n userAuthenticator: UserAuthenticator;\n};\n\nexport class Networked3dWebExperienceServer {\n public userNetworkingServer: UserNetworkingServer;\n\n public mmlDocumentsServer?: MMLDocumentsServer;\n\n constructor(private config: Networked3dWebExperienceServerConfig) {\n if (this.config.mmlServing) {\n const { documentsWatchPath, documentsDirectoryRoot } = this.config.mmlServing;\n this.mmlDocumentsServer = new MMLDocumentsServer(documentsDirectoryRoot, documentsWatchPath);\n }\n\n this.userNetworkingServer = new UserNetworkingServer({\n legacyAdapterEnabled: true,\n onClientConnect: (\n clientId: number,\n sessionToken: string,\n userIdentityPresentedOnConnection?: UserData,\n ): Promise<UserData | true | Error> | UserData | true | Error => {\n return this.config.userAuthenticator.onClientConnect(\n clientId,\n sessionToken,\n userIdentityPresentedOnConnection,\n );\n },\n onClientUserIdentityUpdate: (\n clientId: number,\n userIdentity: UserData,\n ): UserData | true | Error => {\n // Called whenever a user connects or updates their character/identity\n return this.config.userAuthenticator.onClientUserIdentityUpdate(clientId, userIdentity);\n },\n onClientDisconnect: (clientId: number): void => {\n this.config.userAuthenticator.onClientDisconnect(clientId);\n },\n });\n }\n\n public updateUserCharacter(clientId: number, userData: UserData) {\n console.log(`Initiate server-side update of client ${clientId}`);\n this.userNetworkingServer.updateUserCharacter(clientId, userData);\n }\n\n public dispose(error?: UserNetworkingServerError) {\n this.userNetworkingServer.dispose(error);\n if (this.mmlDocumentsServer) {\n this.mmlDocumentsServer.dispose();\n }\n }\n\n registerExpressRoutes(app: enableWs.Application) {\n app.ws(this.config.networkPath, (ws) => {\n this.userNetworkingServer.connectClient(ws as unknown as WebSocket);\n });\n\n const webClientServing = this.config.webClientServing;\n if (webClientServing) {\n app.get(webClientServing.indexUrl, async (req: express.Request, res: express.Response) => {\n const token = await this.config.userAuthenticator.generateAuthorizedSessionToken(req);\n if (!token) {\n res.send(\"Error: Could not generate token\");\n return;\n }\n const authorizedDemoIndexContent = webClientServing.indexContent.replace(\n webClientServing.sessionTokenPlaceholder || defaultSessionTokenPlaceholder,\n token,\n );\n res.send(authorizedDemoIndexContent);\n });\n\n app.use(webClientServing.clientUrl, express.static(webClientServing.clientBuildDir));\n if (webClientServing.clientWatchWebsocketPath) {\n websocketDirectoryChangeListener(app, {\n directory: webClientServing.clientBuildDir,\n websocketPath: webClientServing.clientWatchWebsocketPath,\n });\n }\n }\n\n const mmlDocumentsServer = this.mmlDocumentsServer;\n const mmlServing = this.config.mmlServing;\n // Handle example document sockets\n if (mmlServing && mmlDocumentsServer) {\n app.ws(`${mmlServing.documentsUrl}*`, (ws: ws.WebSocket, req: express.Request) => {\n const path = req.params[0];\n console.log(\"document requested\", { path });\n mmlDocumentsServer.handle(path, ws);\n });\n }\n\n if (this.config.assetServing) {\n // Serve assets with CORS allowing all origins\n app.use(\n this.config.assetServing.assetsUrl,\n cors(),\n express.static(this.config.assetServing.assetsDir),\n );\n }\n }\n}\n", "import { watch } from \"chokidar\";\nimport enableWs from \"express-ws\";\nimport WebSocket from \"ws\";\n\nexport function websocketDirectoryChangeListener(\n app: enableWs.Application,\n options: {\n directory: string;\n websocketPath: string;\n },\n) {\n const listeningClients = new Set<WebSocket>();\n watch(options.directory).on(\"all\", () => {\n for (const client of listeningClients) {\n client.send(\"change\");\n }\n });\n // Create an event-source that updates whenever the build folder gets modified\n app.ws(options.websocketPath, (ws: WebSocket) => {\n listeningClients.add(ws);\n ws.on(\"close\", () => {\n listeningClients.delete(ws);\n });\n });\n}\n"],
|
5
|
+
"mappings": ";AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAEhB,SAAS,sBAAsB,iCAAiC;AAChE,SAAS,aAAwB;AACjC,OAAO,gBAAgB;AAGvB,IAAM,wBAAwB,CAAC,iBAAyB;AACtD,SAAO,GAAG,aAAa,cAAc,EAAE,UAAU,QAAQ,MAAM,IAAI,CAAC;AACtE;AAEO,IAAM,qBAAN,MAAyB;AAAA,EAW9B,YACU,WACR,cACA;AAFQ;AAGR,SAAK,eAAe;AACpB,SAAK,MAAM;AAAA,EACb;AAAA,EAhBQ,YAAY,oBAAI,IAMtB;AAAA,EACM;AAAA,EACA;AAAA,EAUD,UAAU;AACf,eAAW,EAAE,SAAS,KAAK,KAAK,UAAU,OAAO,GAAG;AAClD,eAAS,QAAQ;AAAA,IACnB;AACA,SAAK,UAAU,MAAM;AACrB,SAAK,QAAQ,MAAM;AAAA,EACrB;AAAA,EAEO,OAAO,UAAkB,IAAe;AAxCjD;AAyCI,UAAM,YAAW,UAAK,UAAU,IAAI,QAAQ,MAA3B,mBAA8B;AAC/C,QAAI,CAAC,UAAU;AACb,SAAG,MAAM;AACT;AAAA,IACF;AAEA,aAAS,aAAa,EAAS;AAC/B,OAAG,GAAG,SAAS,MAAM;AACnB,eAAS,gBAAgB,EAAS;AAAA,IACpC,CAAC;AAAA,EACH;AAAA,EAEQ,QAAQ;AACd,SAAK,UAAU,MAAM,KAAK,WAAW;AAAA,MACnC,eAAe;AAAA,MACf,SAAS,CAAC,WAAW,UAAU;AAC7B,YAAI,CAAC,SAAS,CAAC,MAAM,OAAO,GAAG;AAC7B,iBAAO;AAAA,QACT;AACA,eAAO,CAAC,WAAW,QAAQ,WAAW,KAAK,YAAY;AAAA,MACzD;AAAA,MACA,YAAY;AAAA,IACd,CAAC;AACD,SAAK,QACF,GAAG,OAAO,CAAC,UAAU,UAAU;AAC9B,UAAI,CAAC,SAAS,CAAC,MAAM,OAAO,GAAG;AAC7B;AAAA,MACF;AACA,YAAM,eAAe,KAAK,SAAS,KAAK,WAAW,QAAQ;AAC3D,cAAQ,IAAI,iBAAiB,YAAY,kBAAkB;AAC3D,YAAM,WAAW,sBAAsB,QAAQ;AAC/C,YAAM,WAAW,IAAI;AAAA,QACnB,IAAI,cAAc,QAAQ,EAAE,SAAS;AAAA,QACrC;AAAA,MACF;AACA,eAAS,KAAK,QAAQ;AAEtB,YAAM,cAAc;AAAA,QAClB,cAAc;AAAA,QACd;AAAA,MACF;AACA,WAAK,UAAU,IAAI,cAAc,WAAW;AAAA,IAC9C,CAAC,EACA,GAAG,UAAU,CAAC,aAAa;AAC1B,YAAM,eAAe,KAAK,SAAS,KAAK,WAAW,QAAQ;AAC3D,cAAQ,IAAI,iBAAiB,YAAY,oBAAoB;AAC7D,YAAM,WAAW,sBAAsB,QAAQ;AAC/C,YAAM,gBAAgB,KAAK,UAAU,IAAI,YAAY;AACrD,UAAI,CAAC,eAAe;AAClB,gBAAQ,MAAM,iBAAiB,YAAY,aAAa;AACxD;AAAA,MACF;AACA,oBAAc,SAAS,KAAK,QAAQ;AAAA,IACtC,CAAC,EACA,GAAG,UAAU,CAAC,aAAa;AAC1B,YAAM,eAAe,KAAK,SAAS,KAAK,WAAW,QAAQ;AAC3D,cAAQ,IAAI,iBAAiB,YAAY,oBAAoB;AAC7D,YAAM,gBAAgB,KAAK,UAAU,IAAI,YAAY;AACrD,UAAI,CAAC,eAAe;AAClB,gBAAQ,MAAM,iBAAiB,YAAY,aAAa;AACxD;AAAA,MACF;AACA,oBAAc,SAAS,QAAQ;AAC/B,WAAK,UAAU,OAAO,YAAY;AAAA,IACpC,CAAC,EACA,GAAG,SAAS,CAAC,UAAU;AACtB,cAAQ,MAAM,mCAAmC,KAAK;AAAA,IACxD,CAAC;AAAA,EACL;AACF;;;AC9GA;AAAA,EAEE;AAAA,OAEK;AACP,OAAO,UAAU;AACjB,OAAO,aAAa;;;ACNpB,SAAS,SAAAA,cAAa;AAIf,SAAS,iCACd,KACA,SAIA;AACA,QAAM,mBAAmB,oBAAI,IAAe;AAC5C,EAAAA,OAAM,QAAQ,SAAS,EAAE,GAAG,OAAO,MAAM;AACvC,eAAW,UAAU,kBAAkB;AACrC,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,EACF,CAAC;AAED,MAAI,GAAG,QAAQ,eAAe,CAAC,OAAkB;AAC/C,qBAAiB,IAAI,EAAE;AACvB,OAAG,GAAG,SAAS,MAAM;AACnB,uBAAiB,OAAO,EAAE;AAAA,IAC5B,CAAC;AAAA,EACH,CAAC;AACH;;;ADGO,IAAM,iCAAiC;AA0BvC,IAAM,iCAAN,MAAqC;AAAA,EAK1C,YAAoB,QAA8C;AAA9C;AAClB,QAAI,KAAK,OAAO,YAAY;AAC1B,YAAM,EAAE,oBAAoB,uBAAuB,IAAI,KAAK,OAAO;AACnE,WAAK,qBAAqB,IAAI,mBAAmB,wBAAwB,kBAAkB;AAAA,IAC7F;AAEA,SAAK,uBAAuB,IAAI,qBAAqB;AAAA,MACnD,sBAAsB;AAAA,MACtB,iBAAiB,CACf,UACA,cACA,sCAC+D;AAC/D,eAAO,KAAK,OAAO,kBAAkB;AAAA,UACnC;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,MACA,4BAA4B,CAC1B,UACA,iBAC4B;AAE5B,eAAO,KAAK,OAAO,kBAAkB,2BAA2B,UAAU,YAAY;AAAA,MACxF;AAAA,MACA,oBAAoB,CAAC,aAA2B;AAC9C,aAAK,OAAO,kBAAkB,mBAAmB,QAAQ;AAAA,MAC3D;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAlCO;AAAA,EAEA;AAAA,EAkCA,oBAAoB,UAAkB,UAAoB;AAC/D,YAAQ,IAAI,yCAAyC,QAAQ,EAAE;AAC/D,SAAK,qBAAqB,oBAAoB,UAAU,QAAQ;AAAA,EAClE;AAAA,EAEO,QAAQ,OAAmC;AAChD,SAAK,qBAAqB,QAAQ,KAAK;AACvC,QAAI,KAAK,oBAAoB;AAC3B,WAAK,mBAAmB,QAAQ;AAAA,IAClC;AAAA,EACF;AAAA,EAEA,sBAAsB,KAA2B;AAC/C,QAAI,GAAG,KAAK,OAAO,aAAa,CAAC,OAAO;AACtC,WAAK,qBAAqB,cAAc,EAA0B;AAAA,IACpE,CAAC;AAED,UAAM,mBAAmB,KAAK,OAAO;AACrC,QAAI,kBAAkB;AACpB,UAAI,IAAI,iBAAiB,UAAU,OAAO,KAAsB,QAA0B;AACxF,cAAM,QAAQ,MAAM,KAAK,OAAO,kBAAkB,+BAA+B,GAAG;AACpF,YAAI,CAAC,OAAO;AACV,cAAI,KAAK,iCAAiC;AAC1C;AAAA,QACF;AACA,cAAM,6BAA6B,iBAAiB,aAAa;AAAA,UAC/D,iBAAiB,2BAA2B;AAAA,UAC5C;AAAA,QACF;AACA,YAAI,KAAK,0BAA0B;AAAA,MACrC,CAAC;AAED,UAAI,IAAI,iBAAiB,WAAW,QAAQ,OAAO,iBAAiB,cAAc,CAAC;AACnF,UAAI,iBAAiB,0BAA0B;AAC7C,yCAAiC,KAAK;AAAA,UACpC,WAAW,iBAAiB;AAAA,UAC5B,eAAe,iBAAiB;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,UAAM,qBAAqB,KAAK;AAChC,UAAM,aAAa,KAAK,OAAO;AAE/B,QAAI,cAAc,oBAAoB;AACpC,UAAI,GAAG,GAAG,WAAW,YAAY,KAAK,CAAC,IAAkB,QAAyB;AAChF,cAAMC,QAAO,IAAI,OAAO,CAAC;AACzB,gBAAQ,IAAI,sBAAsB,EAAE,MAAAA,MAAK,CAAC;AAC1C,2BAAmB,OAAOA,OAAM,EAAE;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,QAAI,KAAK,OAAO,cAAc;AAE5B,UAAI;AAAA,QACF,KAAK,OAAO,aAAa;AAAA,QACzB,KAAK;AAAA,QACL,QAAQ,OAAO,KAAK,OAAO,aAAa,SAAS;AAAA,MACnD;AAAA,IACF;AAAA,EACF;AACF;",
|
6
6
|
"names": ["watch", "path"]
|
7
7
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@mml-io/3d-web-experience-server",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.23.0",
|
4
4
|
"publishConfig": {
|
5
5
|
"access": "public"
|
6
6
|
},
|
@@ -11,6 +11,7 @@
|
|
11
11
|
"/build"
|
12
12
|
],
|
13
13
|
"scripts": {
|
14
|
+
"depcheck": "depcheck --quiet",
|
14
15
|
"build": "tsx ./build.ts --build",
|
15
16
|
"iterate": "tsx ./build.ts --watch",
|
16
17
|
"type-check": "tsc --noEmit",
|
@@ -18,26 +19,22 @@
|
|
18
19
|
"lint-fix": "eslint \"./{src,test}/**/*.{js,jsx,ts,tsx}\" --fix"
|
19
20
|
},
|
20
21
|
"dependencies": {
|
21
|
-
"@mml-io/3d-web-
|
22
|
-
"@mml-io/
|
23
|
-
"
|
24
|
-
"@mml-io/3d-web-voice-chat": "^0.21.6",
|
25
|
-
"@mml-io/networked-dom-server": "0.19.7",
|
26
|
-
"chokidar": "^4.0.3",
|
22
|
+
"@mml-io/3d-web-user-networking": "^0.23.0",
|
23
|
+
"@mml-io/networked-dom-server": "0.20.0",
|
24
|
+
"chokidar": "4.0.3",
|
27
25
|
"cors": "^2.8.5",
|
28
26
|
"express": "^4.21.2",
|
29
27
|
"express-ws": "^5.0.2",
|
30
28
|
"micromatch": "^4.0.8",
|
31
|
-
"three": "0.163.0",
|
32
29
|
"ws": "^8.18.0"
|
33
30
|
},
|
34
31
|
"devDependencies": {
|
35
32
|
"@types/cors": "2.8.17",
|
36
33
|
"@types/express": "^5.0.0",
|
37
34
|
"@types/express-ws": "^3.0.5",
|
35
|
+
"@types/jest": "^29.5.12",
|
38
36
|
"@types/micromatch": "^4.0.9",
|
39
|
-
"@types/node": "^22.13.1"
|
40
|
-
"@types/three": "0.163.0"
|
37
|
+
"@types/node": "^22.13.1"
|
41
38
|
},
|
42
|
-
"gitHead": "
|
39
|
+
"gitHead": "1111b42ca884d9b6253b9657fa64cec3aeda4de1"
|
43
40
|
}
|