@furystack/rest-service 13.0.0 → 14.1.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.
Files changed (311) hide show
  1. package/CHANGELOG.md +124 -0
  2. package/README.md +47 -28
  3. package/esm/actions/error-action.d.ts +7 -2
  4. package/esm/actions/error-action.d.ts.map +1 -1
  5. package/esm/actions/error-action.js +7 -2
  6. package/esm/actions/error-action.js.map +1 -1
  7. package/esm/actions/get-current-user.d.ts +1 -4
  8. package/esm/actions/get-current-user.d.ts.map +1 -1
  9. package/esm/actions/get-current-user.js +1 -4
  10. package/esm/actions/get-current-user.js.map +1 -1
  11. package/esm/actions/get-current-user.spec.js +10 -6
  12. package/esm/actions/get-current-user.spec.js.map +1 -1
  13. package/esm/actions/is-authenticated.d.ts +3 -4
  14. package/esm/actions/is-authenticated.d.ts.map +1 -1
  15. package/esm/actions/is-authenticated.js +3 -4
  16. package/esm/actions/is-authenticated.js.map +1 -1
  17. package/esm/actions/is-authenticated.spec.js +10 -6
  18. package/esm/actions/is-authenticated.spec.js.map +1 -1
  19. package/esm/actions/logout-action.spec.js +11 -9
  20. package/esm/actions/logout-action.spec.js.map +1 -1
  21. package/esm/actions/logout.d.ts +2 -6
  22. package/esm/actions/logout.d.ts.map +1 -1
  23. package/esm/actions/logout.js +3 -7
  24. package/esm/actions/logout.js.map +1 -1
  25. package/esm/actions/not-found-action.d.ts +1 -3
  26. package/esm/actions/not-found-action.d.ts.map +1 -1
  27. package/esm/actions/not-found-action.js +1 -3
  28. package/esm/actions/not-found-action.js.map +1 -1
  29. package/esm/actions/password-login-action.d.ts +5 -12
  30. package/esm/actions/password-login-action.d.ts.map +1 -1
  31. package/esm/actions/password-login-action.js +6 -13
  32. package/esm/actions/password-login-action.js.map +1 -1
  33. package/esm/actions/password-login-action.spec.js +23 -28
  34. package/esm/actions/password-login-action.spec.js.map +1 -1
  35. package/esm/authenticate.d.ts +5 -11
  36. package/esm/authenticate.d.ts.map +1 -1
  37. package/esm/authenticate.js +6 -12
  38. package/esm/authenticate.js.map +1 -1
  39. package/esm/authenticate.spec.js +41 -33
  40. package/esm/authenticate.spec.js.map +1 -1
  41. package/esm/authentication-providers/authentication-provider.d.ts +17 -0
  42. package/esm/authentication-providers/authentication-provider.d.ts.map +1 -1
  43. package/esm/authentication-providers/basic-auth-provider.d.ts +7 -0
  44. package/esm/authentication-providers/basic-auth-provider.d.ts.map +1 -1
  45. package/esm/authentication-providers/basic-auth-provider.js +7 -0
  46. package/esm/authentication-providers/basic-auth-provider.js.map +1 -1
  47. package/esm/authentication-providers/cookie-auth-provider.d.ts.map +1 -1
  48. package/esm/authentication-providers/cookie-auth-provider.js +8 -0
  49. package/esm/authentication-providers/cookie-auth-provider.js.map +1 -1
  50. package/esm/authorize.d.ts +5 -17
  51. package/esm/authorize.d.ts.map +1 -1
  52. package/esm/authorize.js +5 -17
  53. package/esm/authorize.js.map +1 -1
  54. package/esm/authorize.spec.js +34 -29
  55. package/esm/authorize.spec.js.map +1 -1
  56. package/esm/endpoint-generators/create-delete-endpoint.d.ts +6 -11
  57. package/esm/endpoint-generators/create-delete-endpoint.d.ts.map +1 -1
  58. package/esm/endpoint-generators/create-delete-endpoint.js +7 -11
  59. package/esm/endpoint-generators/create-delete-endpoint.js.map +1 -1
  60. package/esm/endpoint-generators/create-delete-endpoint.spec.js +14 -21
  61. package/esm/endpoint-generators/create-delete-endpoint.spec.js.map +1 -1
  62. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts +11 -11
  63. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -1
  64. package/esm/endpoint-generators/create-get-collection-endpoint.js +13 -12
  65. package/esm/endpoint-generators/create-get-collection-endpoint.js.map +1 -1
  66. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js +43 -70
  67. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -1
  68. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts +6 -11
  69. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -1
  70. package/esm/endpoint-generators/create-get-entity-endpoint.js +7 -11
  71. package/esm/endpoint-generators/create-get-entity-endpoint.js.map +1 -1
  72. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +16 -33
  73. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
  74. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts +1 -1
  75. package/esm/endpoint-generators/create-get-openapi-document-action.d.ts.map +1 -1
  76. package/esm/endpoint-generators/create-get-openapi-document-action.js +1 -1
  77. package/esm/endpoint-generators/create-get-openapi-document-action.js.map +1 -1
  78. package/esm/endpoint-generators/create-get-schema-action.d.ts +1 -1
  79. package/esm/endpoint-generators/create-get-schema-action.d.ts.map +1 -1
  80. package/esm/endpoint-generators/create-get-schema-action.js.map +1 -1
  81. package/esm/endpoint-generators/create-patch-endpoint.d.ts +7 -13
  82. package/esm/endpoint-generators/create-patch-endpoint.d.ts.map +1 -1
  83. package/esm/endpoint-generators/create-patch-endpoint.js +8 -13
  84. package/esm/endpoint-generators/create-patch-endpoint.js.map +1 -1
  85. package/esm/endpoint-generators/create-patch-endpoint.spec.js +10 -17
  86. package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
  87. package/esm/endpoint-generators/create-post-endpoint.d.ts +7 -13
  88. package/esm/endpoint-generators/create-post-endpoint.d.ts.map +1 -1
  89. package/esm/endpoint-generators/create-post-endpoint.js +8 -13
  90. package/esm/endpoint-generators/create-post-endpoint.js.map +1 -1
  91. package/esm/endpoint-generators/create-post-endpoint.spec.js +41 -14
  92. package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
  93. package/esm/endpoint-generators/utils.d.ts +17 -2
  94. package/esm/endpoint-generators/utils.d.ts.map +1 -1
  95. package/esm/endpoint-generators/utils.js +33 -20
  96. package/esm/endpoint-generators/utils.js.map +1 -1
  97. package/esm/get-schema-from-api.d.ts +1 -1
  98. package/esm/get-schema-from-api.d.ts.map +1 -1
  99. package/esm/get-schema-from-api.js +1 -0
  100. package/esm/get-schema-from-api.js.map +1 -1
  101. package/esm/get-schema-from-api.spec.js.map +1 -1
  102. package/esm/helpers.d.ts +54 -88
  103. package/esm/helpers.d.ts.map +1 -1
  104. package/esm/helpers.js +90 -92
  105. package/esm/helpers.js.map +1 -1
  106. package/esm/helpers.spec.d.ts +2 -1
  107. package/esm/helpers.spec.d.ts.map +1 -1
  108. package/esm/helpers.spec.js +41 -38
  109. package/esm/helpers.spec.js.map +1 -1
  110. package/esm/http-authentication-settings.d.ts +56 -12
  111. package/esm/http-authentication-settings.d.ts.map +1 -1
  112. package/esm/http-authentication-settings.js +27 -29
  113. package/esm/http-authentication-settings.js.map +1 -1
  114. package/esm/http-server-pool.d.ts +64 -0
  115. package/esm/http-server-pool.d.ts.map +1 -0
  116. package/esm/http-server-pool.js +95 -0
  117. package/esm/http-server-pool.js.map +1 -0
  118. package/esm/http-server-pool.spec.d.ts +2 -0
  119. package/esm/http-server-pool.spec.d.ts.map +1 -0
  120. package/esm/http-server-pool.spec.js +193 -0
  121. package/esm/http-server-pool.spec.js.map +1 -0
  122. package/esm/http-user-context.d.ts +34 -44
  123. package/esm/http-user-context.d.ts.map +1 -1
  124. package/esm/http-user-context.js +75 -95
  125. package/esm/http-user-context.js.map +1 -1
  126. package/esm/http-user-context.spec.d.ts +8 -2
  127. package/esm/http-user-context.spec.d.ts.map +1 -1
  128. package/esm/http-user-context.spec.js +226 -332
  129. package/esm/http-user-context.spec.js.map +1 -1
  130. package/esm/identity-cache-keys.d.ts +28 -0
  131. package/esm/identity-cache-keys.d.ts.map +1 -0
  132. package/esm/identity-cache-keys.js +13 -0
  133. package/esm/identity-cache-keys.js.map +1 -0
  134. package/esm/identity-cache-keys.spec.d.ts +2 -0
  135. package/esm/identity-cache-keys.spec.d.ts.map +1 -0
  136. package/esm/identity-cache-keys.spec.js +21 -0
  137. package/esm/identity-cache-keys.spec.js.map +1 -0
  138. package/esm/identity-event-bus.d.ts +102 -0
  139. package/esm/identity-event-bus.d.ts.map +1 -0
  140. package/esm/identity-event-bus.js +151 -0
  141. package/esm/identity-event-bus.js.map +1 -0
  142. package/esm/identity-event-bus.spec.d.ts +2 -0
  143. package/esm/identity-event-bus.spec.d.ts.map +1 -0
  144. package/esm/identity-event-bus.spec.js +392 -0
  145. package/esm/identity-event-bus.spec.js.map +1 -0
  146. package/esm/index.d.ts +9 -4
  147. package/esm/index.d.ts.map +1 -1
  148. package/esm/index.js +9 -4
  149. package/esm/index.js.map +1 -1
  150. package/esm/login-response-strategy.d.ts +11 -10
  151. package/esm/login-response-strategy.d.ts.map +1 -1
  152. package/esm/login-response-strategy.js +7 -6
  153. package/esm/login-response-strategy.js.map +1 -1
  154. package/esm/login-response-strategy.spec.js +25 -29
  155. package/esm/login-response-strategy.spec.js.map +1 -1
  156. package/esm/openapi/generate-openapi-document.spec.js.map +1 -1
  157. package/esm/proxy-manager.spec.js +152 -176
  158. package/esm/proxy-manager.spec.js.map +1 -1
  159. package/esm/proxy-runtime.d.ts +27 -0
  160. package/esm/proxy-runtime.d.ts.map +1 -0
  161. package/esm/proxy-runtime.js +45 -0
  162. package/esm/proxy-runtime.js.map +1 -0
  163. package/esm/proxy-runtime.spec.d.ts +2 -0
  164. package/esm/proxy-runtime.spec.d.ts.map +1 -0
  165. package/esm/proxy-runtime.spec.js +39 -0
  166. package/esm/proxy-runtime.spec.js.map +1 -0
  167. package/esm/request-action-implementation.d.ts +25 -0
  168. package/esm/request-action-implementation.d.ts.map +1 -1
  169. package/esm/request-action-implementation.js +9 -0
  170. package/esm/request-action-implementation.js.map +1 -1
  171. package/esm/rest-api-runtime.d.ts +76 -0
  172. package/esm/rest-api-runtime.d.ts.map +1 -0
  173. package/esm/rest-api-runtime.js +131 -0
  174. package/esm/rest-api-runtime.js.map +1 -0
  175. package/esm/rest-api-runtime.spec.d.ts +2 -0
  176. package/esm/rest-api-runtime.spec.d.ts.map +1 -0
  177. package/esm/rest-api-runtime.spec.js +58 -0
  178. package/esm/rest-api-runtime.spec.js.map +1 -0
  179. package/esm/rest-service.integration.spec.d.ts +1 -1
  180. package/esm/rest-service.integration.spec.d.ts.map +1 -1
  181. package/esm/rest-service.integration.spec.js +57 -80
  182. package/esm/rest-service.integration.spec.js.map +1 -1
  183. package/esm/rest.integration.test.js +26 -84
  184. package/esm/rest.integration.test.js.map +1 -1
  185. package/esm/server-telemetry.d.ts +49 -0
  186. package/esm/server-telemetry.d.ts.map +1 -0
  187. package/esm/server-telemetry.js +25 -0
  188. package/esm/server-telemetry.js.map +1 -0
  189. package/esm/server-telemetry.spec.d.ts +2 -0
  190. package/esm/server-telemetry.spec.d.ts.map +1 -0
  191. package/esm/server-telemetry.spec.js +52 -0
  192. package/esm/server-telemetry.spec.js.map +1 -0
  193. package/esm/static-server-manager.spec.js +60 -125
  194. package/esm/static-server-manager.spec.js.map +1 -1
  195. package/esm/static-site-runtime.d.ts +21 -0
  196. package/esm/static-site-runtime.d.ts.map +1 -0
  197. package/esm/static-site-runtime.js +48 -0
  198. package/esm/static-site-runtime.js.map +1 -0
  199. package/esm/static-site-runtime.spec.d.ts +2 -0
  200. package/esm/static-site-runtime.spec.d.ts.map +1 -0
  201. package/esm/static-site-runtime.spec.js +30 -0
  202. package/esm/static-site-runtime.spec.js.map +1 -0
  203. package/esm/user-resolution-cache.d.ts +65 -0
  204. package/esm/user-resolution-cache.d.ts.map +1 -0
  205. package/esm/user-resolution-cache.js +101 -0
  206. package/esm/user-resolution-cache.js.map +1 -0
  207. package/esm/user-resolution-cache.spec.d.ts +2 -0
  208. package/esm/user-resolution-cache.spec.d.ts.map +1 -0
  209. package/esm/user-resolution-cache.spec.js +220 -0
  210. package/esm/user-resolution-cache.spec.js.map +1 -0
  211. package/esm/user-store.d.ts +39 -0
  212. package/esm/user-store.d.ts.map +1 -0
  213. package/esm/user-store.js +62 -0
  214. package/esm/user-store.js.map +1 -0
  215. package/esm/user-store.spec.d.ts +2 -0
  216. package/esm/user-store.spec.d.ts.map +1 -0
  217. package/esm/user-store.spec.js +72 -0
  218. package/esm/user-store.spec.js.map +1 -0
  219. package/esm/validate.d.ts +13 -12
  220. package/esm/validate.d.ts.map +1 -1
  221. package/esm/validate.integration.spec.js +27 -28
  222. package/esm/validate.integration.spec.js.map +1 -1
  223. package/esm/validate.js +12 -0
  224. package/esm/validate.js.map +1 -1
  225. package/package.json +14 -12
  226. package/src/actions/error-action.ts +7 -3
  227. package/src/actions/get-current-user.spec.ts +11 -10
  228. package/src/actions/get-current-user.ts +1 -4
  229. package/src/actions/is-authenticated.spec.ts +12 -7
  230. package/src/actions/is-authenticated.ts +3 -4
  231. package/src/actions/logout-action.spec.ts +12 -12
  232. package/src/actions/logout.ts +3 -7
  233. package/src/actions/not-found-action.ts +1 -3
  234. package/src/actions/password-login-action.spec.ts +30 -30
  235. package/src/actions/password-login-action.ts +6 -13
  236. package/src/authenticate.spec.ts +45 -51
  237. package/src/authenticate.ts +6 -12
  238. package/src/authentication-providers/authentication-provider.ts +17 -0
  239. package/src/authentication-providers/basic-auth-provider.ts +7 -0
  240. package/src/authentication-providers/cookie-auth-provider.ts +8 -0
  241. package/src/authorize.spec.ts +37 -39
  242. package/src/authorize.ts +6 -17
  243. package/src/endpoint-generators/create-delete-endpoint.spec.ts +16 -24
  244. package/src/endpoint-generators/create-delete-endpoint.ts +11 -17
  245. package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +45 -78
  246. package/src/endpoint-generators/create-get-collection-endpoint.ts +17 -19
  247. package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +17 -33
  248. package/src/endpoint-generators/create-get-entity-endpoint.ts +11 -17
  249. package/src/endpoint-generators/create-get-openapi-document-action.ts +2 -2
  250. package/src/endpoint-generators/create-get-schema-action.ts +1 -1
  251. package/src/endpoint-generators/create-patch-endpoint.spec.ts +11 -18
  252. package/src/endpoint-generators/create-patch-endpoint.ts +12 -23
  253. package/src/endpoint-generators/create-post-endpoint.spec.ts +45 -14
  254. package/src/endpoint-generators/create-post-endpoint.ts +13 -23
  255. package/src/endpoint-generators/utils.ts +36 -28
  256. package/src/get-schema-from-api.spec.ts +1 -1
  257. package/src/get-schema-from-api.ts +2 -1
  258. package/src/helpers.spec.ts +51 -41
  259. package/src/helpers.ts +140 -109
  260. package/src/http-authentication-settings.ts +78 -24
  261. package/src/http-server-pool.spec.ts +220 -0
  262. package/src/http-server-pool.ts +169 -0
  263. package/src/http-user-context.spec.ts +246 -378
  264. package/src/http-user-context.ts +122 -92
  265. package/src/identity-cache-keys.spec.ts +24 -0
  266. package/src/identity-cache-keys.ts +31 -0
  267. package/src/identity-event-bus.spec.ts +285 -0
  268. package/src/identity-event-bus.ts +230 -0
  269. package/src/index.ts +9 -4
  270. package/src/login-response-strategy.spec.ts +32 -31
  271. package/src/login-response-strategy.ts +14 -12
  272. package/src/openapi/generate-openapi-document.spec.ts +2 -2
  273. package/src/proxy-manager.spec.ts +158 -188
  274. package/src/proxy-runtime.spec.ts +50 -0
  275. package/src/proxy-runtime.ts +75 -0
  276. package/src/request-action-implementation.ts +25 -0
  277. package/src/rest-api-runtime.spec.ts +77 -0
  278. package/src/rest-api-runtime.ts +204 -0
  279. package/src/rest-service.integration.spec.ts +65 -85
  280. package/src/rest.integration.test.ts +27 -87
  281. package/src/server-telemetry.spec.ts +56 -0
  282. package/src/server-telemetry.ts +43 -0
  283. package/src/static-server-manager.spec.ts +63 -151
  284. package/src/static-site-runtime.spec.ts +39 -0
  285. package/src/static-site-runtime.ts +75 -0
  286. package/src/user-resolution-cache.spec.ts +241 -0
  287. package/src/user-resolution-cache.ts +164 -0
  288. package/src/user-store.spec.ts +86 -0
  289. package/src/user-store.ts +69 -0
  290. package/src/validate.integration.spec.ts +40 -35
  291. package/src/validate.ts +14 -12
  292. package/esm/api-manager.d.ts +0 -104
  293. package/esm/api-manager.d.ts.map +0 -1
  294. package/esm/api-manager.js +0 -179
  295. package/esm/api-manager.js.map +0 -1
  296. package/esm/proxy-manager.d.ts +0 -53
  297. package/esm/proxy-manager.d.ts.map +0 -1
  298. package/esm/proxy-manager.js +0 -84
  299. package/esm/proxy-manager.js.map +0 -1
  300. package/esm/server-manager.d.ts +0 -48
  301. package/esm/server-manager.d.ts.map +0 -1
  302. package/esm/server-manager.js +0 -100
  303. package/esm/server-manager.js.map +0 -1
  304. package/esm/static-server-manager.d.ts +0 -19
  305. package/esm/static-server-manager.d.ts.map +0 -1
  306. package/esm/static-server-manager.js +0 -65
  307. package/esm/static-server-manager.js.map +0 -1
  308. package/src/api-manager.ts +0 -304
  309. package/src/proxy-manager.ts +0 -102
  310. package/src/server-manager.ts +0 -135
  311. package/src/static-server-manager.ts +0 -90
@@ -1,5 +1,5 @@
1
1
  import { getPort } from '@furystack/core/port-generator';
2
- import { Injector } from '@furystack/inject';
2
+ import { createInjector } from '@furystack/inject';
3
3
  import { createClient } from '@furystack/rest-client-fetch';
4
4
  import { usingAsync } from '@furystack/utils';
5
5
  import { describe, expect, it } from 'vitest';
@@ -8,7 +8,7 @@ import { JsonResult } from './request-action-implementation.js';
8
8
  const createEchoApiServer = async () => {
9
9
  const port = getPort();
10
10
  const root = '/api';
11
- const injector = new Injector();
11
+ const injector = createInjector();
12
12
  await useRestService({
13
13
  injector,
14
14
  port,
@@ -40,115 +40,57 @@ const createEchoApiServer = async () => {
40
40
  };
41
41
  };
42
42
  describe('REST Integration tests with FETCH client', () => {
43
- it('Should execute a single parameterless GET query', async () => {
43
+ it('executes a parameterless GET request', async () => {
44
44
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
45
- const result = await client({
46
- method: 'GET',
47
- action: '/plain',
48
- });
45
+ const result = await client({ method: 'GET', action: '/plain' });
49
46
  expect(result.response.status).toBe(200);
50
47
  expect(result.result).toEqual({});
51
48
  });
52
49
  });
53
- it('Should execute a request with headers', async () => {
50
+ it('passes headers through', async () => {
54
51
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
55
- const value = 'value';
56
- const result = await client({
57
- method: 'GET',
58
- action: '/headers',
59
- headers: {
60
- value,
61
- },
62
- });
52
+ const result = await client({ method: 'GET', action: '/headers', headers: { value: 'hello' } });
63
53
  expect(result.response.status).toBe(200);
64
- expect(result.result.headers.value).toEqual(value);
54
+ expect(result.result.headers.value).toBe('hello');
65
55
  });
66
56
  });
67
- it('Should execute a request with query', async () => {
57
+ it('serialises url params', async () => {
68
58
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
69
- const value = 'value2';
70
- const result = await client({
71
- method: 'GET',
72
- action: '/query',
73
- query: {
74
- someObject: {
75
- foo: value,
76
- },
77
- },
78
- });
79
- expect(result.response.status).toBe(200);
80
- expect(result.result.query.someObject.foo).toEqual(value);
59
+ const result = await client({ method: 'GET', action: '/urlParams/:id', url: { id: '42' } });
60
+ expect(result.result.url.id).toBe('42');
81
61
  });
82
62
  });
83
- it('Should execute a request with URL parameters', async () => {
63
+ it('serialises a structured query', async () => {
84
64
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
85
- const value = 'value3';
86
65
  const result = await client({
87
66
  method: 'GET',
88
- action: '/urlParams/:id',
89
- url: {
90
- id: value,
91
- },
92
- });
93
- expect(result.response.status).toBe(200);
94
- expect(result.result.url.id).toEqual(value);
95
- });
96
- });
97
- it('should execute a request for a segment', async () => {
98
- await usingAsync(await createEchoApiServer(), async ({ client }) => {
99
- const result = await client({
100
- method: 'GET',
101
- action: '/segment',
102
- });
103
- expect(result.response.status).toBe(200);
104
- expect(result.result.name).toEqual('segment');
105
- });
106
- });
107
- it('should execute a request for a subsegment', async () => {
108
- await usingAsync(await createEchoApiServer(), async ({ client }) => {
109
- const result = await client({
110
- method: 'GET',
111
- action: '/segment/subsegment',
67
+ action: '/query',
68
+ query: { someObject: { foo: 'bar' } },
112
69
  });
113
- expect(result.response.status).toBe(200);
114
- expect(result.result.name).toEqual('segment-subsegment');
70
+ expect(result.result.query.someObject.foo).toBe('bar');
115
71
  });
116
72
  });
117
- it('should execute a request for a subsegment with URL parameters', async () => {
73
+ it('dispatches plain and multi-segment URLs correctly', async () => {
118
74
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
119
- const value = 'value4';
120
- const result = await client({
75
+ expect((await client({ method: 'GET', action: '/segment' })).result.name).toBe('segment');
76
+ expect((await client({ method: 'GET', action: '/segment/subsegment' })).result.name).toBe('segment-subsegment');
77
+ const subsegment = await client({
121
78
  method: 'GET',
122
79
  action: '/segment/:id/subsegment',
123
- url: {
124
- id: value,
125
- },
80
+ url: { id: 'abc' },
126
81
  });
127
- expect(result.response.status).toBe(200);
128
- expect(result.result.url.id).toEqual(value);
129
- expect(result.result.url.name).toEqual('segment-subsegment');
82
+ expect(subsegment.result.url.id).toBe('abc');
83
+ expect(subsegment.result.url.name).toBe('segment-subsegment');
130
84
  });
131
85
  });
132
- it('should evaluate optional parameters in the URL', async () => {
86
+ it('forwards POST bodies', async () => {
133
87
  await usingAsync(await createEchoApiServer(), async ({ client }) => {
134
88
  const result = await client({
135
- method: 'GET',
136
- action: '/segment{/:optionalId}?/optionalSubsegment/',
137
- url: {},
138
- });
139
- expect(result.response.status).toBe(200);
140
- expect(result.result.url.optionalId).toBeUndefined();
141
- expect(result.result.name).toEqual('optional-id');
142
- const result2 = await client({
143
- method: 'GET',
144
- action: '/segment{/:optionalId}?/optionalSubsegment/',
145
- url: {
146
- optionalId: 'value',
147
- },
89
+ method: 'POST',
90
+ action: '/body',
91
+ body: { foo: 'hi', bar: 42 },
148
92
  });
149
- expect(result2.response.status).toBe(200);
150
- expect(result2.result.url.optionalId).toEqual('value');
151
- expect(result2.result.name).toEqual('optional-id');
93
+ expect(result.result.body).toEqual({ foo: 'hi', bar: 42 });
152
94
  });
153
95
  });
154
96
  });
@@ -1 +1 @@
1
- {"version":3,"file":"rest.integration.test.js","sourceRoot":"","sources":["../src/rest.integration.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"rest.integration.test.js","sourceRoot":"","sources":["../src/rest.integration.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import { type Token } from '@furystack/inject';
2
+ import { EventHub, type ListenerErrorPayload } from '@furystack/utils';
3
+ import type { IncomingMessage, ServerResponse } from 'http';
4
+ import type { Data, WebSocket } from 'ws';
5
+ /**
6
+ * Union of every server-lifecycle event the rest-service runtime emits.
7
+ * The HTTP server pool, proxy runtime and websocket-api all forward into
8
+ * this single hub so subscribers can observe every source from one place.
9
+ */
10
+ export type ServerTelemetryEvents = {
11
+ onServerListening: {
12
+ url: string;
13
+ port: number;
14
+ hostName?: string;
15
+ };
16
+ onServerClosed: {
17
+ url: string;
18
+ };
19
+ /** Tuple-shaped to match the underlying Node `request` callback signature. */
20
+ onRequestFailed: [unknown, IncomingMessage, ServerResponse<IncomingMessage>];
21
+ onProxyFailed: {
22
+ from: string;
23
+ to: string;
24
+ error: unknown;
25
+ };
26
+ onWebSocketProxyFailed: {
27
+ from: string;
28
+ to: string;
29
+ error: unknown;
30
+ };
31
+ onWebSocketActionFailed: {
32
+ error: unknown;
33
+ data?: Data;
34
+ socket?: WebSocket;
35
+ };
36
+ onListenerError: ListenerErrorPayload;
37
+ };
38
+ /**
39
+ * Application-facing telemetry surface. Extends {@link EventHub} so
40
+ * subscribers use the standard `addListener` / `subscribe` API.
41
+ */
42
+ export declare class ServerTelemetry extends EventHub<ServerTelemetryEvents> {
43
+ }
44
+ /**
45
+ * DI token for the shared {@link ServerTelemetry} instance. Scoped so each
46
+ * injector tree (in particular, each test) gets an isolated event stream.
47
+ */
48
+ export declare const ServerTelemetryToken: Token<ServerTelemetry, 'scoped'>;
49
+ //# sourceMappingURL=server-telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-telemetry.d.ts","sourceRoot":"","sources":["../src/server-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAAE,KAAK,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,MAAM,CAAA;AAC3D,OAAO,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,IAAI,CAAA;AAEzC;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,iBAAiB,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnE,cAAc,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/B,8EAA8E;IAC9E,eAAe,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,cAAc,CAAC,eAAe,CAAC,CAAC,CAAA;IAC5E,aAAa,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IAC3D,sBAAsB,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAA;IACpE,uBAAuB,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAC;QAAC,MAAM,CAAC,EAAE,SAAS,CAAA;KAAE,CAAA;IAC5E,eAAe,EAAE,oBAAoB,CAAA;CACtC,CAAA;AAED;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,QAAQ,CAAC,qBAAqB,CAAC;CAAG;AAEvE;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,CAWhE,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { defineService } from '@furystack/inject';
2
+ import { EventHub } from '@furystack/utils';
3
+ /**
4
+ * Application-facing telemetry surface. Extends {@link EventHub} so
5
+ * subscribers use the standard `addListener` / `subscribe` API.
6
+ */
7
+ export class ServerTelemetry extends EventHub {
8
+ }
9
+ /**
10
+ * DI token for the shared {@link ServerTelemetry} instance. Scoped so each
11
+ * injector tree (in particular, each test) gets an isolated event stream.
12
+ */
13
+ export const ServerTelemetryToken = defineService({
14
+ name: 'furystack/rest-service/ServerTelemetry',
15
+ lifetime: 'scoped',
16
+ factory: ({ onDispose }) => {
17
+ const telemetry = new ServerTelemetry();
18
+ // Disposal is delegated to the injector via `onDispose`; the telemetry
19
+ // outlives this factory invocation and is torn down on scope teardown.
20
+ // eslint-disable-next-line furystack/prefer-using-wrapper
21
+ onDispose(() => telemetry[Symbol.dispose]());
22
+ return telemetry;
23
+ },
24
+ });
25
+ //# sourceMappingURL=server-telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-telemetry.js","sourceRoot":"","sources":["../src/server-telemetry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAc,MAAM,mBAAmB,CAAA;AAC7D,OAAO,EAAE,QAAQ,EAA6B,MAAM,kBAAkB,CAAA;AAoBtE;;;GAGG;AACH,MAAM,OAAO,eAAgB,SAAQ,QAA+B;CAAG;AAEvE;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAqC,aAAa,CAAC;IAClF,IAAI,EAAE,wCAAwC;IAC9C,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;QACzB,MAAM,SAAS,GAAG,IAAI,eAAe,EAAE,CAAA;QACvC,uEAAuE;QACvE,uEAAuE;QACvE,0DAA0D;QAC1D,SAAS,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QAC5C,OAAO,SAAS,CAAA;IAClB,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server-telemetry.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-telemetry.spec.d.ts","sourceRoot":"","sources":["../src/server-telemetry.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,52 @@
1
+ import { createInjector } from '@furystack/inject';
2
+ import { usingAsync } from '@furystack/utils';
3
+ import { describe, expect, it, vi } from 'vitest';
4
+ import { ServerTelemetry, ServerTelemetryToken } from './server-telemetry.js';
5
+ describe('ServerTelemetry', () => {
6
+ it('ServerTelemetryToken resolves to a ServerTelemetry EventHub and is cached per scope', async () => {
7
+ await usingAsync(createInjector(), async (injector) => {
8
+ const a = injector.get(ServerTelemetryToken);
9
+ const b = injector.get(ServerTelemetryToken);
10
+ expect(a).toBe(b);
11
+ expect(a).toBeInstanceOf(ServerTelemetry);
12
+ });
13
+ });
14
+ it('Emitted events are dispatched to subscribers', async () => {
15
+ await usingAsync(createInjector(), async (injector) => {
16
+ const telemetry = injector.get(ServerTelemetryToken);
17
+ const onListening = vi.fn();
18
+ telemetry.subscribe('onServerListening', onListening);
19
+ telemetry.emit('onServerListening', { url: 'http://localhost:1', port: 1 });
20
+ expect(onListening).toHaveBeenCalledWith({ url: 'http://localhost:1', port: 1 });
21
+ });
22
+ });
23
+ it('Disposes cleanly with the injector and stops routing events', async () => {
24
+ const injector = createInjector();
25
+ const telemetry = injector.get(ServerTelemetryToken);
26
+ const onClosed = vi.fn();
27
+ telemetry.subscribe('onServerClosed', onClosed);
28
+ await injector[Symbol.asyncDispose]();
29
+ // After the owning injector is disposed, emit on the captured reference
30
+ // should still work (the EventHub is disposed separately via onDispose,
31
+ // but listeners are cleared). The key guarantee: previously-attached
32
+ // listeners receive nothing new after dispose.
33
+ telemetry.emit('onServerClosed', { url: 'http://localhost:2' });
34
+ expect(onClosed).not.toHaveBeenCalled();
35
+ });
36
+ it('Scopes have isolated telemetry instances', async () => {
37
+ await usingAsync(createInjector(), async (root) => {
38
+ const scope1 = root.createScope();
39
+ const scope2 = root.createScope();
40
+ try {
41
+ const t1 = scope1.get(ServerTelemetryToken);
42
+ const t2 = scope2.get(ServerTelemetryToken);
43
+ expect(t1).not.toBe(t2);
44
+ }
45
+ finally {
46
+ await scope1[Symbol.asyncDispose]();
47
+ await scope2[Symbol.asyncDispose]();
48
+ }
49
+ });
50
+ });
51
+ });
52
+ //# sourceMappingURL=server-telemetry.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-telemetry.spec.js","sourceRoot":"","sources":["../src/server-telemetry.spec.ts"],"names":[],"mappings":"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"}
@@ -1,182 +1,118 @@
1
- import { Injector } from '@furystack/inject';
2
- import { sleepAsync, usingAsync } from '@furystack/utils';
3
- import { ServerManager } from './server-manager.js';
4
- import { StaticServerManager } from './static-server-manager.js';
5
- import { describe, it, expect, vi } from 'vitest';
6
1
  import { getPort } from '@furystack/core/port-generator';
7
- describe('StaticServerManager', () => {
2
+ import { createInjector } from '@furystack/inject';
3
+ import { sleepAsync, usingAsync } from '@furystack/utils';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+ import { useStaticFiles } from './helpers.js';
6
+ import { HttpServerPoolToken } from './http-server-pool.js';
7
+ import { buildStaticSiteServerApi } from './static-site-runtime.js';
8
+ describe('useStaticFiles / buildStaticSiteServerApi', () => {
8
9
  describe('Top level routing', () => {
9
- it('Should return a 404 without fallback', async () => {
10
- await usingAsync(new Injector(), async (injector) => {
11
- const staticServerManager = injector.getInstance(StaticServerManager);
10
+ it('responds 404 without a fallback', async () => {
11
+ await usingAsync(createInjector(), async (i) => {
12
12
  const port = getPort();
13
- await staticServerManager.addStaticSite({
14
- baseUrl: '/',
15
- path: '.',
16
- port,
17
- });
13
+ await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port });
18
14
  const result = await fetch(`http://127.0.0.1:${port}/not-found.html`);
19
15
  expect(result.ok).toBe(false);
20
16
  expect(result.status).toBe(404);
21
- expect(result?.headers.get('content-type')).toBe('text/plain');
22
- const body = await result.text();
23
- expect(body).toBe('Not found');
17
+ expect(result.headers.get('content-type')).toBe('text/plain');
18
+ expect(await result.text()).toBe('Not found');
24
19
  });
25
20
  });
26
- it('Should return a fallback', async () => {
27
- await usingAsync(new Injector(), async (injector) => {
28
- const staticServerManager = injector.getInstance(StaticServerManager);
21
+ it('falls back to the configured file and returns its content-type', async () => {
22
+ await usingAsync(createInjector(), async (i) => {
29
23
  const port = getPort();
30
- await staticServerManager.addStaticSite({
24
+ await useStaticFiles({
25
+ injector: i,
31
26
  baseUrl: '/',
32
27
  path: '.',
33
28
  fallback: 'package.json',
34
29
  port,
35
- headers: {
36
- 'custom-header': 'custom-value',
37
- },
30
+ headers: { 'custom-header': 'custom-value' },
38
31
  });
39
32
  const result = await fetch(`http://localhost:${port}/not-found.html`);
40
33
  expect(result.headers.get('content-type')).toBe('application/json');
41
34
  expect(result.headers.get('custom-header')).toBe('custom-value');
42
35
  });
43
36
  });
44
- it('Should return a fallback for root files', async () => {
45
- await usingAsync(new Injector(), async (injector) => {
46
- const staticServerManager = injector.getInstance(StaticServerManager);
37
+ it('falls back for the root url when no index exists', async () => {
38
+ await usingAsync(createInjector(), async (i) => {
47
39
  const port = getPort();
48
- await staticServerManager.addStaticSite({
40
+ await useStaticFiles({
41
+ injector: i,
49
42
  baseUrl: '/',
50
43
  path: '.',
51
44
  fallback: 'package.json',
52
45
  port,
53
- headers: {
54
- 'custom-header': 'custom-value',
55
- },
46
+ headers: { 'custom-header': 'custom-value' },
56
47
  });
57
48
  const result = await fetch(`http://localhost:${port}`);
58
49
  expect(result.headers.get('content-type')).toBe('application/json');
59
50
  expect(result.headers.get('custom-header')).toBe('custom-value');
60
51
  });
61
52
  });
62
- it('Should return a defined file from a root directory', async () => {
63
- await usingAsync(new Injector(), async (injector) => {
64
- const staticServerManager = injector.getInstance(StaticServerManager);
53
+ it('serves an existing file from the root directory', async () => {
54
+ await usingAsync(createInjector(), async (i) => {
65
55
  const port = getPort();
66
- await staticServerManager.addStaticSite({
67
- baseUrl: '/',
68
- path: '.',
69
- port,
70
- headers: {
71
- 'custom-header': 'custom-value',
72
- },
73
- });
56
+ await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port });
74
57
  const result = await fetch(`http://localhost:${port}/README.md`);
75
58
  expect(result.headers.get('content-type')).toBe('text/markdown');
76
- expect(result.headers.get('custom-header')).toBe('custom-value');
77
59
  });
78
60
  });
79
- it('Should return a defined file from a subdirectory', async () => {
80
- await usingAsync(new Injector(), async (injector) => {
81
- const staticServerManager = injector.getInstance(StaticServerManager);
61
+ it('serves files from subdirectories', async () => {
62
+ await usingAsync(createInjector(), async (i) => {
82
63
  const port = getPort();
83
- await staticServerManager.addStaticSite({
84
- baseUrl: '/',
85
- path: '.',
86
- port,
87
- });
64
+ await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port });
88
65
  const result = await fetch(`http://localhost:${port}/packages/utils/README.md`);
89
66
  expect(result.headers.get('content-type')).toBe('text/markdown');
90
67
  });
91
68
  });
92
69
  });
93
70
  describe('Non-top level routing', () => {
94
- it('Should not handle a request when the path is not matching', async () => {
95
- await usingAsync(new Injector(), async (injector) => {
96
- const staticServerManager = injector.getInstance(StaticServerManager);
71
+ it('leaves requests outside the base url to the next api in the chain', async () => {
72
+ await usingAsync(createInjector(), async (i) => {
97
73
  const port = getPort();
98
- await staticServerManager.addStaticSite({
99
- baseUrl: '/bundle',
100
- path: '.',
101
- port,
102
- });
103
- const server = [...injector.getInstance(ServerManager).servers.values()][0];
104
- server.apis[0].onRequest = vi.fn();
74
+ const serverApi = await useStaticFiles({ injector: i, baseUrl: '/bundle', path: '.', port });
75
+ const record = await i.get(HttpServerPoolToken).acquire({ port });
76
+ const onRequest = vi.fn();
77
+ // Swap in a spy to observe non-matching requests never reach the handler.
78
+ serverApi.onRequest = onRequest;
79
+ record.apis[0] = serverApi;
105
80
  fetch(`http://localhost:${port}/bundleToAnotherFolder/not-found.html`).catch(() => {
106
- /** should fall, ignore */
81
+ // Expected to fail: the server has no matching API and drops the socket.
107
82
  });
108
83
  await sleepAsync(100);
109
- expect(server.apis[0].onRequest).not.toHaveBeenCalled();
84
+ expect(onRequest).not.toHaveBeenCalled();
110
85
  });
111
86
  });
112
- it('Should return a 404 without fallback', async () => {
113
- await usingAsync(new Injector(), async (injector) => {
114
- const staticServerManager = injector.getInstance(StaticServerManager);
87
+ it('returns 404 under a sub-base url when the file is missing and no fallback is set', async () => {
88
+ await usingAsync(createInjector(), async (i) => {
115
89
  const port = getPort();
116
- await staticServerManager.addStaticSite({
117
- baseUrl: '/bundle',
118
- path: '.',
119
- port,
120
- });
90
+ await useStaticFiles({ injector: i, baseUrl: '/bundle', path: '.', port });
121
91
  const result = await fetch(`http://localhost:${port}/bundle/not-found.html`);
122
92
  expect(result.ok).toBe(false);
123
93
  expect(result.status).toBe(404);
124
94
  expect(result.headers.get('content-type')).toBe('text/plain');
125
- const body = await result.text();
126
- expect(body).toBe('Not found');
95
+ expect(await result.text()).toBe('Not found');
127
96
  });
128
97
  });
129
- it('Should return a fallback', async () => {
130
- await usingAsync(new Injector(), async (injector) => {
131
- const staticServerManager = injector.getInstance(StaticServerManager);
98
+ it('serves the configured fallback under a sub-base url', async () => {
99
+ await usingAsync(createInjector(), async (i) => {
132
100
  const port = getPort();
133
- await staticServerManager.addStaticSite({
134
- baseUrl: '/bundle',
135
- path: '.',
136
- fallback: 'package.json',
137
- port,
138
- });
101
+ await useStaticFiles({ injector: i, baseUrl: '/bundle', path: '.', fallback: 'package.json', port });
139
102
  const result = await fetch(`http://localhost:${port}/bundle/not-found.html`);
140
103
  expect(result.headers.get('content-type')).toBe('application/json');
141
104
  });
142
105
  });
143
- it('Should return a defined file from a root directory', async () => {
144
- await usingAsync(new Injector(), async (injector) => {
145
- const staticServerManager = injector.getInstance(StaticServerManager);
146
- const port = getPort();
147
- await staticServerManager.addStaticSite({
148
- baseUrl: '/',
149
- path: '.',
150
- port,
151
- });
152
- const result = await fetch(`http://localhost:${port}/README.md`);
153
- expect(result.headers.get('content-type')).toBe('text/markdown');
154
- });
155
- });
156
- it('Should return a defined file from a subdirectory', async () => {
157
- await usingAsync(new Injector(), async (injector) => {
158
- const staticServerManager = injector.getInstance(StaticServerManager);
159
- const port = getPort();
160
- await staticServerManager.addStaticSite({
161
- baseUrl: '/',
162
- path: '.',
163
- port,
164
- });
165
- const result = await fetch(`http://localhost:${port}/packages/utils/README.md`);
166
- expect(result.headers.get('content-type')).toBe('text/markdown');
167
- });
168
- });
169
106
  });
170
107
  describe('shouldExec', () => {
171
- it('Should NOT match for requests with not defined method', () => {
172
- const staticServerManager = new StaticServerManager();
173
- expect(staticServerManager.shouldExec('/')({ req: { url: '/' } })).toBe(false);
108
+ const api = (baseUrl) => buildStaticSiteServerApi({ baseUrl, path: '.', port: 0 });
109
+ it('does not match when the request method is missing', () => {
110
+ expect(api('/').shouldExec({ req: { url: '/' }, res: {} })).toBe(false);
174
111
  });
175
- it('Should NOT match for non-GET requests', () => {
176
- const staticServerManager = new StaticServerManager();
177
- expect(staticServerManager.shouldExec('/')({ req: { method: 'POST', url: '/' } })).toBe(false);
112
+ it('does not match non-GET methods', () => {
113
+ expect(api('/').shouldExec({ req: { method: 'POST', url: '/' }, res: {} })).toBe(false);
178
114
  });
179
- [
115
+ const positiveCases = [
180
116
  ['/', '/'],
181
117
  ['/', '/index.html'],
182
118
  ['/', '/subdir'],
@@ -186,17 +122,16 @@ describe('StaticServerManager', () => {
186
122
  ['/subdir', '/subdir/'],
187
123
  ['/subdir', '/subdir/file.js'],
188
124
  ['/subdir', '/subdir/s2/file.js'],
189
- ].forEach(([root, url]) => it(`Should match for '${root}' root and '${url}' url`, () => {
190
- const staticServerManager = new StaticServerManager();
191
- const shouldExec = staticServerManager.shouldExec(root)({ req: { method: 'GET', url } });
192
- expect(shouldExec).toBe(true);
125
+ ];
126
+ positiveCases.forEach(([baseUrl, url]) => it(`matches GET '${url}' against base '${baseUrl}'`, () => {
127
+ expect(api(baseUrl).shouldExec({ req: { method: 'GET', url }, res: {} })).toBe(true);
193
128
  }));
194
- it('Should not exec different paths for non-top level root directory', () => {
195
- const staticServerManager = new StaticServerManager();
196
- expect(staticServerManager.shouldExec('/subdir')({ req: { method: 'GET', url: '/' } })).toBe(false);
197
- expect(staticServerManager.shouldExec('/subdir')({ req: { method: 'GET', url: '/other/index.html' } })).toBe(false);
198
- expect(staticServerManager.shouldExec('/subdir')({ req: { method: 'GET', url: '/subdir2' } })).toBe(false);
199
- expect(staticServerManager.shouldExec('/subdir')({ req: { method: 'GET', url: '/subdir2/index.html' } })).toBe(false);
129
+ it('does not match unrelated urls under a non-root base', () => {
130
+ const subdir = api('/subdir');
131
+ expect(subdir.shouldExec({ req: { method: 'GET', url: '/' }, res: {} })).toBe(false);
132
+ expect(subdir.shouldExec({ req: { method: 'GET', url: '/other/index.html' }, res: {} })).toBe(false);
133
+ expect(subdir.shouldExec({ req: { method: 'GET', url: '/subdir2' }, res: {} })).toBe(false);
134
+ expect(subdir.shouldExec({ req: { method: 'GET', url: '/subdir2/index.html' }, res: {} })).toBe(false);
200
135
  });
201
136
  });
202
137
  });