@mml-io/3d-web-experience-server 0.22.0 → 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 { ChatNetworkingServer } from "@mml-io/3d-web-text-chat";
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?: UserIdentity): Promise<UserData | null> | UserData | null;
12
- onClientUserIdentityUpdate(clientId: number, userIdentity: UserIdentity): UserData | null;
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
- chatNetworkPath?: string;
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(errorMessage?: string): void;
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
- connectionLimit: config.connectionLimit,
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(errorMessage) {
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) => {
@@ -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": "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",
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": "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",
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.22.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-client-core": "^0.22.0",
22
- "@mml-io/3d-web-text-chat": "^0.22.0",
23
- "@mml-io/3d-web-user-networking": "^0.22.0",
24
- "@mml-io/3d-web-voice-chat": "^0.22.0",
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": "c389fca2be51c09efd43b04d3b840712ef605c84"
39
+ "gitHead": "1111b42ca884d9b6253b9657fa64cec3aeda4de1"
43
40
  }