@dangao/bun-server 1.7.0 → 1.7.1

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.
Files changed (37) hide show
  1. package/README.md +72 -3
  2. package/dist/cache/cache-module.d.ts +18 -0
  3. package/dist/cache/cache-module.d.ts.map +1 -1
  4. package/dist/cache/index.d.ts +3 -1
  5. package/dist/cache/index.d.ts.map +1 -1
  6. package/dist/cache/interceptors.d.ts +41 -0
  7. package/dist/cache/interceptors.d.ts.map +1 -0
  8. package/dist/cache/service-proxy.d.ts +62 -0
  9. package/dist/cache/service-proxy.d.ts.map +1 -0
  10. package/dist/controller/controller.d.ts +8 -0
  11. package/dist/controller/controller.d.ts.map +1 -1
  12. package/dist/core/application.d.ts +5 -0
  13. package/dist/core/application.d.ts.map +1 -1
  14. package/dist/di/container.d.ts +18 -1
  15. package/dist/di/container.d.ts.map +1 -1
  16. package/dist/di/index.d.ts +1 -1
  17. package/dist/di/index.d.ts.map +1 -1
  18. package/dist/di/types.d.ts +22 -0
  19. package/dist/di/types.d.ts.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +3163 -2862
  23. package/docs/symbol-interface-pattern.md +431 -0
  24. package/docs/zh/symbol-interface-pattern.md +431 -0
  25. package/package.json +1 -1
  26. package/src/cache/cache-module.ts +37 -0
  27. package/src/cache/index.ts +16 -1
  28. package/src/cache/interceptors.ts +295 -0
  29. package/src/cache/service-proxy.ts +219 -0
  30. package/src/controller/controller.ts +30 -6
  31. package/src/core/application.ts +25 -1
  32. package/src/di/container.ts +57 -7
  33. package/src/di/index.ts +7 -1
  34. package/src/di/types.ts +29 -0
  35. package/src/index.ts +7 -0
  36. package/tests/cache/cache-decorators.test.ts +284 -0
  37. package/tests/controller/path-combination.test.ts +353 -0
@@ -203,5 +203,358 @@ describe('Controller Path Combination', () => {
203
203
  const rootResponse = await fetch(`http://localhost:${port}/`);
204
204
  expect(rootResponse.status).toBe(404);
205
205
  });
206
+
207
+ test('should correctly combine root controller "/" with method path "/health"', async () => {
208
+ // 这是 metrics-rate-limit-app.ts 示例中的场景
209
+ // @Controller('/') + @GET('/health') 应该映射到 /health,而不是 //health
210
+ @Controller('/')
211
+ class HealthController {
212
+ @GET('/health')
213
+ public health() {
214
+ return { status: 'ok' };
215
+ }
216
+
217
+ @GET('/')
218
+ public index() {
219
+ return { message: 'index' };
220
+ }
221
+ }
222
+
223
+ app.registerController(HealthController);
224
+ await app.listen();
225
+
226
+ // /health 应该正常访问(修复前会得到 404,因为注册的路径是 //health)
227
+ const healthResponse = await fetch(`http://localhost:${port}/health`);
228
+ expect(healthResponse.status).toBe(200);
229
+ const healthData = await healthResponse.json();
230
+ expect(healthData.status).toBe('ok');
231
+
232
+ // / 应该正常访问
233
+ const rootResponse = await fetch(`http://localhost:${port}/`);
234
+ expect(rootResponse.status).toBe(200);
235
+ const rootData = await rootResponse.json();
236
+ expect(rootData.message).toBe('index');
237
+
238
+ // //health 不应该被访问到(或者应该重定向/返回 404)
239
+ // 注:大多数 HTTP 服务器会规范化 //health 为 /health,所以这里可能返回 200
240
+ });
241
+
242
+ test('should correctly combine root controller "/" with multiple method paths', async () => {
243
+ @Controller('/')
244
+ class RootController {
245
+ @GET('/api/status')
246
+ public status() {
247
+ return { status: 'running' };
248
+ }
249
+
250
+ @GET('/api/info')
251
+ public info() {
252
+ return { info: 'test' };
253
+ }
254
+
255
+ @POST('/api/data')
256
+ public data() {
257
+ return { received: true };
258
+ }
259
+ }
260
+
261
+ app.registerController(RootController);
262
+ await app.listen();
263
+
264
+ // 所有路径都应该正常工作
265
+ const statusResponse = await fetch(`http://localhost:${port}/api/status`);
266
+ expect(statusResponse.status).toBe(200);
267
+ expect((await statusResponse.json()).status).toBe('running');
268
+
269
+ const infoResponse = await fetch(`http://localhost:${port}/api/info`);
270
+ expect(infoResponse.status).toBe(200);
271
+ expect((await infoResponse.json()).info).toBe('test');
272
+
273
+ const dataResponse = await fetch(`http://localhost:${port}/api/data`, {
274
+ method: 'POST',
275
+ });
276
+ expect(dataResponse.status).toBe(200);
277
+ expect((await dataResponse.json()).received).toBe(true);
278
+ });
279
+ });
280
+
281
+ /**
282
+ * 路径规范化测试
283
+ * 根据图片中的测试矩阵,验证各种路径组合是否都能正确映射到 /api/base
284
+ *
285
+ * 配置组合 | 拼接结果 | 规范化后路径 | 是否命中 /api/base
286
+ * [/ + /api/base] | //api/base | /api/base | 是
287
+ * [// + /api/base] | ///api/base | /api/base | 是
288
+ * [/api + /base] | /api/base | /api/base | 是
289
+ * [/api/ + base] | /api/base | /api/base | 是
290
+ * [/api/base + ""] | /api/base | /api/base | 是
291
+ */
292
+ describe('Controller Path Normalization', () => {
293
+ let app: Application;
294
+ let port: number;
295
+
296
+ beforeEach(() => {
297
+ port = getTestPort();
298
+ app = new Application({ port });
299
+ });
300
+
301
+ afterEach(async () => {
302
+ if (app) {
303
+ await app.stop();
304
+ }
305
+ RouteRegistry.getInstance().clear();
306
+ ControllerRegistry.getInstance().clear();
307
+ });
308
+
309
+ // 场景 1: [/ + /api/base] -> //api/base -> /api/base
310
+ test('should normalize "/" + "/api/base" to "/api/base"', async () => {
311
+ @Controller('/')
312
+ class TestController {
313
+ @GET('/api/base')
314
+ public handler() {
315
+ return { success: true, scenario: 1 };
316
+ }
317
+ }
318
+
319
+ app.registerController(TestController);
320
+ await app.listen();
321
+
322
+ const response = await fetch(`http://localhost:${port}/api/base`);
323
+ expect(response.status).toBe(200);
324
+ const data = await response.json();
325
+ expect(data.success).toBe(true);
326
+ expect(data.scenario).toBe(1);
327
+ });
328
+
329
+ // 场景 2: [// + /api/base] -> ///api/base -> /api/base
330
+ test('should normalize "//" + "/api/base" to "/api/base"', async () => {
331
+ @Controller('//')
332
+ class TestController {
333
+ @GET('/api/base')
334
+ public handler() {
335
+ return { success: true, scenario: 2 };
336
+ }
337
+ }
338
+
339
+ app.registerController(TestController);
340
+ await app.listen();
341
+
342
+ const response = await fetch(`http://localhost:${port}/api/base`);
343
+ expect(response.status).toBe(200);
344
+ const data = await response.json();
345
+ expect(data.success).toBe(true);
346
+ expect(data.scenario).toBe(2);
347
+ });
348
+
349
+ // 场景 3: [/api + /base] -> /api/base -> /api/base
350
+ test('should normalize "/api" + "/base" to "/api/base"', async () => {
351
+ @Controller('/api')
352
+ class TestController {
353
+ @GET('/base')
354
+ public handler() {
355
+ return { success: true, scenario: 3 };
356
+ }
357
+ }
358
+
359
+ app.registerController(TestController);
360
+ await app.listen();
361
+
362
+ const response = await fetch(`http://localhost:${port}/api/base`);
363
+ expect(response.status).toBe(200);
364
+ const data = await response.json();
365
+ expect(data.success).toBe(true);
366
+ expect(data.scenario).toBe(3);
367
+ });
368
+
369
+ // 场景 4: [/api/ + base] -> /api/base -> /api/base
370
+ test('should normalize "/api/" + "base" to "/api/base"', async () => {
371
+ @Controller('/api/')
372
+ class TestController {
373
+ @GET('base')
374
+ public handler() {
375
+ return { success: true, scenario: 4 };
376
+ }
377
+ }
378
+
379
+ app.registerController(TestController);
380
+ await app.listen();
381
+
382
+ const response = await fetch(`http://localhost:${port}/api/base`);
383
+ expect(response.status).toBe(200);
384
+ const data = await response.json();
385
+ expect(data.success).toBe(true);
386
+ expect(data.scenario).toBe(4);
387
+ });
388
+
389
+ // 场景 5: [/api/base + ""] -> /api/base -> /api/base
390
+ test('should normalize "/api/base" + "" to "/api/base"', async () => {
391
+ @Controller('/api/base')
392
+ class TestController {
393
+ @GET('')
394
+ public handler() {
395
+ return { success: true, scenario: 5 };
396
+ }
397
+ }
398
+
399
+ app.registerController(TestController);
400
+ await app.listen();
401
+
402
+ const response = await fetch(`http://localhost:${port}/api/base`);
403
+ expect(response.status).toBe(200);
404
+ const data = await response.json();
405
+ expect(data.success).toBe(true);
406
+ expect(data.scenario).toBe(5);
407
+ });
408
+
409
+ // 额外场景: [/api/base + /] -> /api/base/ 或 /api/base
410
+ test('should normalize "/api/base" + "/" to "/api/base"', async () => {
411
+ @Controller('/api/base')
412
+ class TestController {
413
+ @GET('/')
414
+ public handler() {
415
+ return { success: true, scenario: 6 };
416
+ }
417
+ }
418
+
419
+ app.registerController(TestController);
420
+ await app.listen();
421
+
422
+ const response = await fetch(`http://localhost:${port}/api/base`);
423
+ expect(response.status).toBe(200);
424
+ const data = await response.json();
425
+ expect(data.success).toBe(true);
426
+ expect(data.scenario).toBe(6);
427
+ });
428
+
429
+ // 场景: [/// + /api/base] -> /api/base (多个前导斜杠)
430
+ test('should normalize "///" + "/api/base" to "/api/base"', async () => {
431
+ @Controller('///')
432
+ class TestController {
433
+ @GET('/api/base')
434
+ public handler() {
435
+ return { success: true, scenario: 'triple-slash' };
436
+ }
437
+ }
438
+
439
+ app.registerController(TestController);
440
+ await app.listen();
441
+
442
+ const response = await fetch(`http://localhost:${port}/api/base`);
443
+ expect(response.status).toBe(200);
444
+ const data = await response.json();
445
+ expect(data.success).toBe(true);
446
+ });
447
+
448
+ // 场景: [/api// + //base] -> /api/base (多个连续斜杠)
449
+ test('should normalize "/api//" + "//base" to "/api/base"', async () => {
450
+ @Controller('/api//')
451
+ class TestController {
452
+ @GET('//base')
453
+ public handler() {
454
+ return { success: true, scenario: 'double-slashes' };
455
+ }
456
+ }
457
+
458
+ app.registerController(TestController);
459
+ await app.listen();
460
+
461
+ const response = await fetch(`http://localhost:${port}/api/base`);
462
+ expect(response.status).toBe(200);
463
+ const data = await response.json();
464
+ expect(data.success).toBe(true);
465
+ });
466
+
467
+ // 场景: ["" + /api/base] -> /api/base (空基础路径)
468
+ test('should normalize "" + "/api/base" to "/api/base"', async () => {
469
+ @Controller('')
470
+ class TestController {
471
+ @GET('/api/base')
472
+ public handler() {
473
+ return { success: true, scenario: 'empty-base' };
474
+ }
475
+ }
476
+
477
+ app.registerController(TestController);
478
+ await app.listen();
479
+
480
+ const response = await fetch(`http://localhost:${port}/api/base`);
481
+ expect(response.status).toBe(200);
482
+ const data = await response.json();
483
+ expect(data.success).toBe(true);
484
+ });
485
+
486
+ // 测试所有场景在同一应用中同时工作
487
+ test('should handle all path normalization scenarios simultaneously', async () => {
488
+ @Controller('/')
489
+ class RootController {
490
+ @GET('/health')
491
+ public health() {
492
+ return { endpoint: 'health' };
493
+ }
494
+ }
495
+
496
+ @Controller('/api')
497
+ class ApiController {
498
+ @GET('/users')
499
+ public users() {
500
+ return { endpoint: 'users' };
501
+ }
502
+
503
+ @GET('products')
504
+ public products() {
505
+ return { endpoint: 'products' };
506
+ }
507
+ }
508
+
509
+ @Controller('/api/')
510
+ class ApiSlashController {
511
+ @GET('orders')
512
+ public orders() {
513
+ return { endpoint: 'orders' };
514
+ }
515
+ }
516
+
517
+ @Controller('/v1/api')
518
+ class V1Controller {
519
+ @GET('')
520
+ public root() {
521
+ return { endpoint: 'v1-root' };
522
+ }
523
+
524
+ @GET('/')
525
+ public index() {
526
+ return { endpoint: 'v1-index' };
527
+ }
528
+ }
529
+
530
+ app.registerController(RootController);
531
+ app.registerController(ApiController);
532
+ app.registerController(ApiSlashController);
533
+ app.registerController(V1Controller);
534
+ await app.listen();
535
+
536
+ // 验证所有端点
537
+ const healthRes = await fetch(`http://localhost:${port}/health`);
538
+ expect(healthRes.status).toBe(200);
539
+ expect((await healthRes.json()).endpoint).toBe('health');
540
+
541
+ const usersRes = await fetch(`http://localhost:${port}/api/users`);
542
+ expect(usersRes.status).toBe(200);
543
+ expect((await usersRes.json()).endpoint).toBe('users');
544
+
545
+ const productsRes = await fetch(`http://localhost:${port}/api/products`);
546
+ expect(productsRes.status).toBe(200);
547
+ expect((await productsRes.json()).endpoint).toBe('products');
548
+
549
+ const ordersRes = await fetch(`http://localhost:${port}/api/orders`);
550
+ expect(ordersRes.status).toBe(200);
551
+ expect((await ordersRes.json()).endpoint).toBe('orders');
552
+
553
+ const v1Res = await fetch(`http://localhost:${port}/v1/api`);
554
+ expect(v1Res.status).toBe(200);
555
+ // v1-root 或 v1-index,取决于哪个先注册
556
+ const v1Data = await v1Res.json();
557
+ expect(['v1-root', 'v1-index']).toContain(v1Data.endpoint);
558
+ });
206
559
  });
207
560