@esengine/server 4.2.0 → 4.4.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,3 +1,4 @@
1
+ import { createLogger } from './chunk-I4QQSQ72.js';
1
2
  import { __name, __publicField } from './chunk-T626JPC7.js';
2
3
  import * as path from 'path';
3
4
  import { createServer as createServer$1 } from 'http';
@@ -7,7 +8,66 @@ import * as fs from 'fs';
7
8
  import { pathToFileURL } from 'url';
8
9
 
9
10
  // src/http/router.ts
10
- async function createRequest(req) {
11
+ var logger = createLogger("HTTP");
12
+ function parseRoutePath(path3) {
13
+ const paramNames = [];
14
+ const isStatic = !path3.includes(":");
15
+ if (isStatic) {
16
+ return {
17
+ pattern: new RegExp(`^${escapeRegex(path3)}$`),
18
+ paramNames,
19
+ isStatic: true
20
+ };
21
+ }
22
+ const segments = path3.split("/").map((segment) => {
23
+ if (segment.startsWith(":")) {
24
+ const paramName = segment.slice(1);
25
+ paramNames.push(paramName);
26
+ return "([^/]+)";
27
+ }
28
+ return escapeRegex(segment);
29
+ });
30
+ return {
31
+ pattern: new RegExp(`^${segments.join("/")}$`),
32
+ paramNames,
33
+ isStatic: false
34
+ };
35
+ }
36
+ __name(parseRoutePath, "parseRoutePath");
37
+ function escapeRegex(str) {
38
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
39
+ }
40
+ __name(escapeRegex, "escapeRegex");
41
+ function matchRoute(routes, path3, method) {
42
+ for (const route of routes) {
43
+ if (!route.isStatic) continue;
44
+ if (route.method !== "*" && route.method !== method) continue;
45
+ if (route.pattern.test(path3)) {
46
+ return {
47
+ route,
48
+ params: {}
49
+ };
50
+ }
51
+ }
52
+ for (const route of routes) {
53
+ if (route.isStatic) continue;
54
+ if (route.method !== "*" && route.method !== method) continue;
55
+ const match = path3.match(route.pattern);
56
+ if (match) {
57
+ const params = {};
58
+ route.paramNames.forEach((name, index) => {
59
+ params[name] = decodeURIComponent(match[index + 1]);
60
+ });
61
+ return {
62
+ route,
63
+ params
64
+ };
65
+ }
66
+ }
67
+ return null;
68
+ }
69
+ __name(matchRoute, "matchRoute");
70
+ async function createRequest(req, params = {}) {
11
71
  const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
12
72
  const query = {};
13
73
  url.searchParams.forEach((value, key) => {
@@ -22,6 +82,7 @@ async function createRequest(req) {
22
82
  raw: req,
23
83
  method: req.method ?? "GET",
24
84
  path: url.pathname,
85
+ params,
25
86
  query,
26
87
  headers: req.headers,
27
88
  body,
@@ -67,6 +128,7 @@ function parseBody(req) {
67
128
  __name(parseBody, "parseBody");
68
129
  function createResponse(res) {
69
130
  let statusCode = 200;
131
+ let ended = false;
70
132
  const response = {
71
133
  raw: res,
72
134
  status(code) {
@@ -74,20 +136,28 @@ function createResponse(res) {
74
136
  return response;
75
137
  },
76
138
  header(name, value) {
77
- res.setHeader(name, value);
139
+ if (!ended) {
140
+ res.setHeader(name, value);
141
+ }
78
142
  return response;
79
143
  },
80
144
  json(data) {
145
+ if (ended) return;
146
+ ended = true;
81
147
  res.setHeader("Content-Type", "application/json; charset=utf-8");
82
148
  res.statusCode = statusCode;
83
149
  res.end(JSON.stringify(data));
84
150
  },
85
151
  text(data) {
152
+ if (ended) return;
153
+ ended = true;
86
154
  res.setHeader("Content-Type", "text/plain; charset=utf-8");
87
155
  res.statusCode = statusCode;
88
156
  res.end(data);
89
157
  },
90
158
  error(code, message) {
159
+ if (ended) return;
160
+ ended = true;
91
161
  res.setHeader("Content-Type", "application/json; charset=utf-8");
92
162
  res.statusCode = code;
93
163
  res.end(JSON.stringify({
@@ -98,58 +168,170 @@ function createResponse(res) {
98
168
  return response;
99
169
  }
100
170
  __name(createResponse, "createResponse");
171
+ function createOriginWhitelist(origins) {
172
+ const whitelist = {};
173
+ for (const origin of origins) {
174
+ whitelist[origin] = true;
175
+ }
176
+ return whitelist;
177
+ }
178
+ __name(createOriginWhitelist, "createOriginWhitelist");
101
179
  function applyCors(res, req, cors) {
102
- const origin = req.headers.origin;
103
- if (cors.origin === true || cors.origin === "*") {
104
- res.setHeader("Access-Control-Allow-Origin", origin ?? "*");
105
- } else if (typeof cors.origin === "string") {
180
+ const credentials = cors.credentials ?? false;
181
+ if (typeof cors.origin === "string" && cors.origin !== "*") {
106
182
  res.setHeader("Access-Control-Allow-Origin", cors.origin);
107
- } else if (Array.isArray(cors.origin) && origin && cors.origin.includes(origin)) {
108
- res.setHeader("Access-Control-Allow-Origin", origin);
109
- }
110
- if (cors.methods) {
111
- res.setHeader("Access-Control-Allow-Methods", cors.methods.join(", "));
112
- } else {
113
- res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
114
- }
115
- if (cors.allowedHeaders) {
116
- res.setHeader("Access-Control-Allow-Headers", cors.allowedHeaders.join(", "));
117
- } else {
118
- res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
119
- }
120
- if (cors.credentials) {
121
- res.setHeader("Access-Control-Allow-Credentials", "true");
183
+ if (credentials) {
184
+ res.setHeader("Access-Control-Allow-Credentials", "true");
185
+ }
186
+ } else if (Array.isArray(cors.origin)) {
187
+ const requestOrigin = req.headers.origin;
188
+ if (typeof requestOrigin === "string") {
189
+ const whitelist = createOriginWhitelist(cors.origin);
190
+ if (requestOrigin in whitelist) {
191
+ res.setHeader("Access-Control-Allow-Origin", requestOrigin);
192
+ if (credentials) {
193
+ res.setHeader("Access-Control-Allow-Credentials", "true");
194
+ }
195
+ }
196
+ }
197
+ } else if (!credentials) {
198
+ if (cors.origin === "*" || cors.origin === true) {
199
+ res.setHeader("Access-Control-Allow-Origin", "*");
200
+ }
122
201
  }
202
+ res.setHeader("Access-Control-Allow-Methods", cors.methods?.join(", ") ?? "GET, POST, PUT, DELETE, PATCH, OPTIONS");
203
+ res.setHeader("Access-Control-Allow-Headers", cors.allowedHeaders?.join(", ") ?? "Content-Type, Authorization");
123
204
  if (cors.maxAge) {
124
205
  res.setHeader("Access-Control-Max-Age", String(cors.maxAge));
125
206
  }
126
207
  }
127
208
  __name(applyCors, "applyCors");
128
- function createHttpRouter(routes, cors) {
209
+ async function executeMiddlewares(middlewares, req, res, finalHandler) {
210
+ let index = 0;
211
+ const next = /* @__PURE__ */ __name(async () => {
212
+ if (index < middlewares.length) {
213
+ const middleware = middlewares[index++];
214
+ await middleware(req, res, next);
215
+ } else {
216
+ await finalHandler();
217
+ }
218
+ }, "next");
219
+ await next();
220
+ }
221
+ __name(executeMiddlewares, "executeMiddlewares");
222
+ async function executeWithTimeout(handler, timeoutMs, res) {
223
+ let resolved = false;
224
+ const timeoutPromise = new Promise((_, reject) => {
225
+ setTimeout(() => {
226
+ if (!resolved) {
227
+ reject(new Error("Request timeout"));
228
+ }
229
+ }, timeoutMs);
230
+ });
231
+ try {
232
+ await Promise.race([
233
+ handler().then(() => {
234
+ resolved = true;
235
+ }),
236
+ timeoutPromise
237
+ ]);
238
+ } catch (error) {
239
+ if (error instanceof Error && error.message === "Request timeout") {
240
+ if (!res.writableEnded) {
241
+ res.statusCode = 408;
242
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
243
+ res.end(JSON.stringify({
244
+ error: "Request Timeout"
245
+ }));
246
+ }
247
+ } else {
248
+ throw error;
249
+ }
250
+ }
251
+ }
252
+ __name(executeWithTimeout, "executeWithTimeout");
253
+ function isHandlerDefinition(value) {
254
+ return typeof value === "object" && value !== null && "handler" in value && typeof value.handler === "function";
255
+ }
256
+ __name(isHandlerDefinition, "isHandlerDefinition");
257
+ function isRouteMethods(value) {
258
+ if (typeof value !== "object" || value === null) return false;
259
+ const methods = [
260
+ "GET",
261
+ "POST",
262
+ "PUT",
263
+ "DELETE",
264
+ "PATCH",
265
+ "OPTIONS"
266
+ ];
267
+ return Object.keys(value).some((key) => methods.includes(key));
268
+ }
269
+ __name(isRouteMethods, "isRouteMethods");
270
+ function extractHandler(methodHandler) {
271
+ if (isHandlerDefinition(methodHandler)) {
272
+ return {
273
+ handler: methodHandler.handler,
274
+ middlewares: methodHandler.middlewares ?? [],
275
+ timeout: methodHandler.timeout
276
+ };
277
+ }
278
+ return {
279
+ handler: methodHandler,
280
+ middlewares: [],
281
+ timeout: void 0
282
+ };
283
+ }
284
+ __name(extractHandler, "extractHandler");
285
+ function createHttpRouter(routes, options = {}) {
286
+ const globalMiddlewares = options.middlewares ?? [];
287
+ const globalTimeout = options.timeout;
129
288
  const parsedRoutes = [];
130
289
  for (const [path3, handlerOrMethods] of Object.entries(routes)) {
290
+ const { pattern, paramNames, isStatic } = parseRoutePath(path3);
131
291
  if (typeof handlerOrMethods === "function") {
132
292
  parsedRoutes.push({
133
293
  method: "*",
134
294
  path: path3,
135
- handler: handlerOrMethods
295
+ handler: handlerOrMethods,
296
+ pattern,
297
+ paramNames,
298
+ middlewares: [],
299
+ timeout: void 0,
300
+ isStatic
136
301
  });
137
- } else {
138
- for (const [method, handler] of Object.entries(handlerOrMethods)) {
139
- if (handler !== void 0) {
302
+ } else if (isRouteMethods(handlerOrMethods)) {
303
+ for (const [method, methodHandler] of Object.entries(handlerOrMethods)) {
304
+ if (methodHandler !== void 0) {
305
+ const { handler, middlewares, timeout } = extractHandler(methodHandler);
140
306
  parsedRoutes.push({
141
307
  method,
142
308
  path: path3,
143
- handler
309
+ handler,
310
+ pattern,
311
+ paramNames,
312
+ middlewares,
313
+ timeout,
314
+ isStatic
144
315
  });
145
316
  }
146
317
  }
318
+ } else if (isHandlerDefinition(handlerOrMethods)) {
319
+ const { handler, middlewares, timeout } = extractHandler(handlerOrMethods);
320
+ parsedRoutes.push({
321
+ method: "*",
322
+ path: path3,
323
+ handler,
324
+ pattern,
325
+ paramNames,
326
+ middlewares,
327
+ timeout,
328
+ isStatic
329
+ });
147
330
  }
148
331
  }
149
- const corsOptions = cors === true ? {
150
- origin: true,
151
- credentials: true
152
- } : cors === false ? null : cors ?? null;
332
+ const corsOptions = options.cors === true ? {
333
+ origin: "*"
334
+ } : options.cors === false ? null : options.cors ?? null;
153
335
  return /* @__PURE__ */ __name(async function handleRequest(req, res) {
154
336
  const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
155
337
  const path3 = url.pathname;
@@ -162,27 +344,50 @@ function createHttpRouter(routes, cors) {
162
344
  return true;
163
345
  }
164
346
  }
165
- const route = parsedRoutes.find((r) => r.path === path3 && (r.method === "*" || r.method === method));
166
- if (!route) {
347
+ const match = matchRoute(parsedRoutes, path3, method);
348
+ if (!match) {
167
349
  return false;
168
350
  }
351
+ const { route, params } = match;
169
352
  try {
170
- const httpReq = await createRequest(req);
353
+ const httpReq = await createRequest(req, params);
171
354
  const httpRes = createResponse(res);
172
- await route.handler(httpReq, httpRes);
355
+ const allMiddlewares = [
356
+ ...globalMiddlewares,
357
+ ...route.middlewares
358
+ ];
359
+ const timeout = route.timeout ?? globalTimeout;
360
+ const finalHandler = /* @__PURE__ */ __name(async () => {
361
+ await route.handler(httpReq, httpRes);
362
+ }, "finalHandler");
363
+ const executeHandler = /* @__PURE__ */ __name(async () => {
364
+ if (allMiddlewares.length > 0) {
365
+ await executeMiddlewares(allMiddlewares, httpReq, httpRes, finalHandler);
366
+ } else {
367
+ await finalHandler();
368
+ }
369
+ }, "executeHandler");
370
+ if (timeout && timeout > 0) {
371
+ await executeWithTimeout(executeHandler, timeout, res);
372
+ } else {
373
+ await executeHandler();
374
+ }
173
375
  return true;
174
376
  } catch (error) {
175
- console.error("[HTTP] Route handler error:", error);
176
- res.statusCode = 500;
177
- res.setHeader("Content-Type", "application/json");
178
- res.end(JSON.stringify({
179
- error: "Internal Server Error"
180
- }));
377
+ logger.error("Route handler error:", error);
378
+ if (!res.writableEnded) {
379
+ res.statusCode = 500;
380
+ res.setHeader("Content-Type", "application/json");
381
+ res.end(JSON.stringify({
382
+ error: "Internal Server Error"
383
+ }));
384
+ }
181
385
  return true;
182
386
  }
183
387
  }, "handleRequest");
184
388
  }
185
389
  __name(createHttpRouter, "createHttpRouter");
390
+ var logger2 = createLogger("Server");
186
391
  function fileNameToHandlerName(fileName) {
187
392
  const baseName = fileName.replace(/\.(ts|js|mts|mjs)$/, "");
188
393
  return baseName.split(/[-_]/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
@@ -224,7 +429,7 @@ async function loadApiHandlers(apiDir) {
224
429
  });
225
430
  }
226
431
  } catch (err) {
227
- console.warn(`[Server] Failed to load API handler: ${filePath}`, err);
432
+ logger2.warn(`Failed to load API handler: ${filePath}`, err);
228
433
  }
229
434
  }
230
435
  return handlers;
@@ -247,7 +452,7 @@ async function loadMsgHandlers(msgDir) {
247
452
  });
248
453
  }
249
454
  } catch (err) {
250
- console.warn(`[Server] Failed to load msg handler: ${filePath}`, err);
455
+ logger2.warn(`Failed to load msg handler: ${filePath}`, err);
251
456
  }
252
457
  }
253
458
  return handlers;
@@ -280,7 +485,7 @@ function scanDirectoryRecursive(dir, baseDir = dir) {
280
485
  }
281
486
  __name(scanDirectoryRecursive, "scanDirectoryRecursive");
282
487
  function filePathToRoute(relativePath, prefix) {
283
- let route = relativePath.replace(/\.(ts|js|mts|mjs)$/, "").replace(/\\/g, "/").replace(/\[([^\]]+)\]/g, ":$1");
488
+ let route = relativePath.replace(/\.(ts|js|mts|mjs)$/, "").replace(/\\/g, "/").replace(/\[(\w+)\]/g, ":$1");
284
489
  if (!route.startsWith("/")) {
285
490
  route = "/" + route;
286
491
  }
@@ -307,7 +512,7 @@ async function loadHttpHandlers(httpDir, prefix = "/api") {
307
512
  });
308
513
  }
309
514
  } catch (err) {
310
- console.warn(`[Server] Failed to load HTTP handler: ${filePath}`, err);
515
+ logger2.warn(`Failed to load HTTP handler: ${filePath}`, err);
311
516
  }
312
517
  }
313
518
  return handlers;
@@ -315,6 +520,7 @@ async function loadHttpHandlers(httpDir, prefix = "/api") {
315
520
  __name(loadHttpHandlers, "loadHttpHandlers");
316
521
 
317
522
  // src/room/RoomManager.ts
523
+ var logger3 = createLogger("Room");
318
524
  var _RoomManager = class _RoomManager {
319
525
  constructor(sendFn) {
320
526
  __publicField(this, "_definitions", /* @__PURE__ */ new Map());
@@ -340,7 +546,7 @@ var _RoomManager = class _RoomManager {
340
546
  async create(name, options) {
341
547
  const def = this._definitions.get(name);
342
548
  if (!def) {
343
- console.warn(`[RoomManager] Room type not found: ${name}`);
549
+ logger3.warn(`Room type not found: ${name}`);
344
550
  return null;
345
551
  }
346
552
  const roomId = this._generateRoomId();
@@ -359,7 +565,7 @@ var _RoomManager = class _RoomManager {
359
565
  });
360
566
  this._rooms.set(roomId, room);
361
567
  await room._create(options);
362
- console.log(`[Room] Created: ${name} (${roomId})`);
568
+ logger3.info(`Created: ${name} (${roomId})`);
363
569
  return room;
364
570
  }
365
571
  /**
@@ -375,7 +581,7 @@ var _RoomManager = class _RoomManager {
375
581
  const player = await room._addPlayer(playerId, conn);
376
582
  if (!player) return null;
377
583
  this._playerToRoom.set(playerId, room.id);
378
- console.log(`[Room] Player ${playerId} joined ${room.id}`);
584
+ logger3.info(`Player ${playerId} joined ${room.id}`);
379
585
  return {
380
586
  room,
381
587
  player
@@ -391,7 +597,7 @@ var _RoomManager = class _RoomManager {
391
597
  const player = await room._addPlayer(playerId, conn);
392
598
  if (!player) return null;
393
599
  this._playerToRoom.set(playerId, room.id);
394
- console.log(`[Room] Player ${playerId} joined ${room.id}`);
600
+ logger3.info(`Player ${playerId} joined ${room.id}`);
395
601
  return {
396
602
  room,
397
603
  player
@@ -409,7 +615,7 @@ var _RoomManager = class _RoomManager {
409
615
  await room._removePlayer(playerId, reason);
410
616
  }
411
617
  this._playerToRoom.delete(playerId);
412
- console.log(`[Room] Player ${playerId} left ${roomId}`);
618
+ logger3.info(`Player ${playerId} left ${roomId}`);
413
619
  }
414
620
  /**
415
621
  * @zh 处理消息
@@ -486,19 +692,20 @@ async function createServer(config = {}) {
486
692
  ...config
487
693
  };
488
694
  const cwd = process.cwd();
695
+ const logger4 = createLogger("Server");
489
696
  const apiHandlers = await loadApiHandlers(path.resolve(cwd, opts.apiDir));
490
697
  const msgHandlers = await loadMsgHandlers(path.resolve(cwd, opts.msgDir));
491
698
  const httpDir = config.httpDir ?? opts.httpDir;
492
699
  const httpPrefix = config.httpPrefix ?? opts.httpPrefix;
493
700
  const httpHandlers = await loadHttpHandlers(path.resolve(cwd, httpDir), httpPrefix);
494
701
  if (apiHandlers.length > 0) {
495
- console.log(`[Server] Loaded ${apiHandlers.length} API handlers`);
702
+ logger4.info(`Loaded ${apiHandlers.length} API handlers`);
496
703
  }
497
704
  if (msgHandlers.length > 0) {
498
- console.log(`[Server] Loaded ${msgHandlers.length} message handlers`);
705
+ logger4.info(`Loaded ${msgHandlers.length} message handlers`);
499
706
  }
500
707
  if (httpHandlers.length > 0) {
501
- console.log(`[Server] Loaded ${httpHandlers.length} HTTP handlers`);
708
+ logger4.info(`Loaded ${httpHandlers.length} HTTP handlers`);
502
709
  }
503
710
  const mergedHttpRoutes = {};
504
711
  for (const handler of httpHandlers) {
@@ -636,7 +843,9 @@ async function createServer(config = {}) {
636
843
  };
637
844
  }
638
845
  if (hasHttpRoutes) {
639
- const httpRouter = createHttpRouter(mergedHttpRoutes, config.cors ?? true);
846
+ const httpRouter = createHttpRouter(mergedHttpRoutes, {
847
+ cors: config.cors ?? true
848
+ });
640
849
  httpServer = createServer$1(async (req, res) => {
641
850
  const handled = await httpRouter(req, res);
642
851
  if (!handled) {
@@ -651,7 +860,7 @@ async function createServer(config = {}) {
651
860
  server: httpServer,
652
861
  createConnData: /* @__PURE__ */ __name(() => ({}), "createConnData"),
653
862
  onStart: /* @__PURE__ */ __name(() => {
654
- console.log(`[Server] Started on http://localhost:${opts.port}`);
863
+ logger4.info(`Started on http://localhost:${opts.port}`);
655
864
  opts.onStart?.(opts.port);
656
865
  }, "onStart"),
657
866
  onConnect: /* @__PURE__ */ __name(async (conn) => {
@@ -673,7 +882,7 @@ async function createServer(config = {}) {
673
882
  port: opts.port,
674
883
  createConnData: /* @__PURE__ */ __name(() => ({}), "createConnData"),
675
884
  onStart: /* @__PURE__ */ __name((p) => {
676
- console.log(`[Server] Started on ws://localhost:${p}`);
885
+ logger4.info(`Started on ws://localhost:${p}`);
677
886
  opts.onStart?.(p);
678
887
  }, "onStart"),
679
888
  onConnect: /* @__PURE__ */ __name(async (conn) => {
@@ -725,5 +934,5 @@ async function createServer(config = {}) {
725
934
  __name(createServer, "createServer");
726
935
 
727
936
  export { createHttpRouter, createServer };
728
- //# sourceMappingURL=chunk-BIAOJF7P.js.map
729
- //# sourceMappingURL=chunk-BIAOJF7P.js.map
937
+ //# sourceMappingURL=chunk-M7VONMZJ.js.map
938
+ //# sourceMappingURL=chunk-M7VONMZJ.js.map