@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,27 +1,23 @@
1
1
  import { getPort } from '@furystack/core/port-generator'
2
- import { Injector } from '@furystack/inject'
3
- import { getDataSetFor } from '@furystack/repository'
2
+ import { createInjector } from '@furystack/inject'
4
3
  import type { PostEndpoint } from '@furystack/rest'
5
4
  import { usingAsync } from '@furystack/utils'
6
5
  import { describe, expect, it } from 'vitest'
7
6
  import { useRestService } from '../helpers.js'
8
7
  import { createPostEndpoint } from './create-post-endpoint.js'
9
- import { MockClass, setupContext } from './utils.js'
8
+ import type { MockClass } from './utils.js'
9
+ import { MockDataSet, MockStore, setupContext } from './utils.js'
10
10
 
11
11
  describe('createPostEndpoint', () => {
12
- it('Should create the entity and report the success', async () => {
13
- await usingAsync(new Injector(), async (i) => {
12
+ it('creates the entity and responds 201', async () => {
13
+ await usingAsync(createInjector(), async (i) => {
14
14
  setupContext(i)
15
15
  const port = getPort()
16
16
  await useRestService<{ POST: { '/': PostEndpoint<MockClass, 'id'> } }>({
17
17
  injector: i,
18
18
  root: '/api',
19
19
  port,
20
- api: {
21
- POST: {
22
- '/': createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
23
- },
24
- },
20
+ api: { POST: { '/': createPostEndpoint(MockDataSet) } },
25
21
  })
26
22
  const entityToPost = { id: 'mock', value: 'posted' }
27
23
  const response = await fetch(`http://127.0.0.1:${port}/api`, {
@@ -29,10 +25,45 @@ describe('createPostEndpoint', () => {
29
25
  body: JSON.stringify(entityToPost),
30
26
  })
31
27
  expect(response.status).toBe(201)
32
- const body = (await response.json()) as { id: string; value: string }
33
- expect(body).toEqual(entityToPost)
34
- const posted = await getDataSetFor(i, MockClass, 'id').get(i, entityToPost.id)
35
- expect(posted?.value).toBe('posted')
28
+ expect(await response.json()).toEqual(entityToPost)
29
+ expect((await i.get(MockDataSet).get(i, entityToPost.id))?.value).toBe('posted')
30
+ })
31
+ })
32
+
33
+ it('responds 404 when the underlying data set returned no created entity', async () => {
34
+ await usingAsync(createInjector(), async (i) => {
35
+ setupContext(i)
36
+ // Swap the dataset binding for a stub whose `add` comes back empty --
37
+ // this is the only way to exercise the `!created.length` branch.
38
+ i.bind(MockDataSet, () => ({ add: async () => ({ created: [] as MockClass[] }) }) as never)
39
+ const port = getPort()
40
+ await useRestService<{ POST: { '/': PostEndpoint<MockClass, 'id'> } }>({
41
+ injector: i,
42
+ root: '/api',
43
+ port,
44
+ api: { POST: { '/': createPostEndpoint(MockDataSet) } },
45
+ })
46
+ const response = await fetch(`http://127.0.0.1:${port}/api`, {
47
+ method: 'POST',
48
+ body: JSON.stringify({ id: 'mock', value: 'x' }),
49
+ })
50
+ expect(response.status).toBe(404)
51
+ expect(await response.json()).toEqual({ message: 'Entity not found' })
52
+ })
53
+ })
54
+ })
55
+
56
+ describe('endpoint-generators/utils', () => {
57
+ it('MockStore throws from its default factory when setupContext was not called', async () => {
58
+ await usingAsync(createInjector(), async (i) => {
59
+ expect(() => i.get(MockStore)).toThrow(/MockStore is not configured/)
60
+ })
61
+ })
62
+
63
+ it('setupContext binds MockStore in-memory so the default factory is bypassed', async () => {
64
+ await usingAsync(createInjector(), async (i) => {
65
+ setupContext(i)
66
+ expect(i.get(MockStore)).toBeDefined()
36
67
  })
37
68
  })
38
69
  })
@@ -1,39 +1,29 @@
1
- import type { Constructable } from '@furystack/inject'
1
+ import type { WithOptionalId } from '@furystack/core'
2
+ import type { DataSetToken } from '@furystack/repository'
2
3
  import type { PostEndpoint } from '@furystack/rest'
3
4
  import { RequestError } from '@furystack/rest'
4
- import '@furystack/repository'
5
- import type { RequestAction } from '../request-action-implementation.js'
6
- import { JsonResult } from '../request-action-implementation.js'
7
- import type { WithOptionalId } from '@furystack/core'
8
- import { getRepository } from '@furystack/repository'
9
5
  import { readPostBody } from '../read-post-body.js'
6
+ import { JsonResult, type RequestAction } from '../request-action-implementation.js'
7
+
10
8
  /**
11
- * Creates a POST endpoint for updating entities
12
- * @param options The options for endpoint creation
13
- * @param options.model The Model class
14
- * @param options.primaryKey The field name used as primary key
15
- * @returns a boolean that indicates the success
9
+ * Creates a POST endpoint that inserts a new entity into the DataSet
10
+ * resolved from `dataSet`. Returns `201 Created` on success; throws a
11
+ * `RequestError(404)` when the underlying store returns no created entity.
16
12
  */
17
13
  export const createPostEndpoint = <
18
14
  T extends object,
19
15
  TPrimaryKey extends keyof T,
20
16
  TWritableData = WithOptionalId<T, TPrimaryKey>,
21
- >(options: {
22
- model: Constructable<T>
23
- primaryKey: TPrimaryKey
24
- }) => {
25
- const endpoint: RequestAction<PostEndpoint<T, TPrimaryKey, TWritableData>> = async ({ injector, request }) => {
26
- const dataSet = getRepository(injector).getDataSetFor<T, TPrimaryKey, TWritableData>(
27
- options.model,
28
- options.primaryKey,
29
- )
30
-
17
+ >(
18
+ dataSet: DataSetToken<T, TPrimaryKey, TWritableData>,
19
+ ): RequestAction<PostEndpoint<T, TPrimaryKey, TWritableData>> => {
20
+ return async ({ injector, request }) => {
21
+ const ds = injector.get(dataSet)
31
22
  const entityToCreate = await readPostBody<TWritableData>(request)
32
- const { created } = await dataSet.add(injector, entityToCreate)
23
+ const { created } = await ds.add(injector, entityToCreate)
33
24
  if (!created || !created.length) {
34
25
  throw new RequestError('Entity not found', 404)
35
26
  }
36
27
  return JsonResult(created[0], 201)
37
28
  }
38
- return endpoint
39
29
  }
@@ -1,37 +1,45 @@
1
+ import { defineStore, InMemoryStore, User } from '@furystack/core'
1
2
  import type { Injector } from '@furystack/inject'
2
- import { addStore, InMemoryStore, User } from '@furystack/core'
3
+ import { defineDataSet } from '@furystack/repository'
3
4
  import { DefaultSession } from '../models/default-session.js'
4
- import '@furystack/repository'
5
- import '../helpers.js'
6
- import { getRepository } from '@furystack/repository'
5
+ import { SessionStore, UserStore } from '../user-store.js'
7
6
 
7
+ /**
8
+ * Mock entity used by endpoint-generator specs.
9
+ */
8
10
  export class MockClass {
9
11
  declare id: string
10
12
  declare value: string
11
13
  }
12
14
 
13
- export const setupContext = (i: Injector) => {
14
- addStore(
15
- i,
16
- new InMemoryStore({
17
- model: MockClass,
18
- primaryKey: 'id',
19
- }),
20
- )
21
- .addStore(
22
- new InMemoryStore({
23
- model: User,
24
- primaryKey: 'username',
25
- }),
26
- )
27
- .addStore(
28
- new InMemoryStore({
29
- model: DefaultSession,
30
- primaryKey: 'sessionId',
31
- }),
32
- )
33
- getRepository(i)
34
- .createDataSet(MockClass, 'id')
35
- .createDataSet(User, 'username')
36
- .createDataSet(DefaultSession, 'sessionId')
15
+ /**
16
+ * Store token for {@link MockClass}. The default factory throws so each spec
17
+ * explicitly opts into an in-memory backing via `setupContext`.
18
+ */
19
+ export const MockStore = defineStore({
20
+ name: 'test/MockStore',
21
+ model: MockClass,
22
+ primaryKey: 'id',
23
+ factory: () => {
24
+ throw new Error('MockStore is not configured in this injector')
25
+ },
26
+ })
27
+
28
+ /**
29
+ * Data set token over {@link MockStore}.
30
+ */
31
+ export const MockDataSet = defineDataSet({
32
+ name: 'test/MockDataSet',
33
+ store: MockStore,
34
+ })
35
+
36
+ /**
37
+ * Binds in-memory implementations of {@link UserStore}, {@link SessionStore}
38
+ * and {@link MockStore} on the provided injector. Endpoint-generator specs
39
+ * and integration tests call this at setup time.
40
+ */
41
+ export const setupContext = (i: Injector): void => {
42
+ i.bind(UserStore, () => new InMemoryStore({ model: User, primaryKey: 'username' }))
43
+ i.bind(SessionStore, () => new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
44
+ i.bind(MockStore, () => new InMemoryStore({ model: MockClass, primaryKey: 'id' }))
37
45
  }
@@ -1,6 +1,6 @@
1
1
  import { describe, expect, it } from 'vitest'
2
- import type { RestApiImplementation } from './api-manager.js'
3
2
  import { defaultSchema, getSchemaFromApi } from './get-schema-from-api.js'
3
+ import type { RestApiImplementation } from './rest-api-runtime.js'
4
4
  import { JsonResult, type RequestAction } from './request-action-implementation.js'
5
5
  import validationSchema from './validate.integration.spec.schema.json' with { type: 'json' }
6
6
  import { Validate } from './validate.js'
@@ -1,6 +1,6 @@
1
1
  import type { ApiEndpointDefinition, ApiEndpointSchema, Method, RestApi, Schema } from '@furystack/rest'
2
- import type { RestApiImplementation } from './api-manager.js'
3
2
  import type { RequestAction } from './request-action-implementation.js'
3
+ import type { RestApiImplementation } from './rest-api-runtime.js'
4
4
 
5
5
  export const defaultSchema: Schema = {
6
6
  definitions: {
@@ -61,6 +61,7 @@ export const getSchemaFromApi = <T extends RestApiImplementation<RestApi>>({
61
61
  if (!endpoints[methodKey]) {
62
62
  endpoints[methodKey] = {}
63
63
  }
64
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion -- false positive: TS narrows `endpointList` to a union the iterator can't index without this cast
64
65
  Object.entries(endpointList as Record<string, RequestAction<{ result: unknown }>>).forEach(
65
66
  ([url, requestAction]) => {
66
67
  if (method && url && requestAction) {
@@ -1,56 +1,57 @@
1
- import { InMemoryStore, User, addStore } from '@furystack/core'
1
+ import type { User } from '@furystack/core'
2
+ import { InMemoryStore, User as UserModel } from '@furystack/core'
2
3
  import { getPort } from '@furystack/core/port-generator'
3
- import { Injector } from '@furystack/inject'
4
- import { getRepository } from '@furystack/repository'
5
- import { PasswordCredential, PasswordResetToken, usePasswordPolicy } from '@furystack/security'
4
+ import { createInjector, type Injector } from '@furystack/inject'
5
+ import {
6
+ PasswordCredential,
7
+ PasswordCredentialStore,
8
+ PasswordResetToken,
9
+ PasswordResetTokenStore,
10
+ usePasswordPolicy,
11
+ } from '@furystack/security'
6
12
  import { usingAsync } from '@furystack/utils'
7
13
  import { describe, expect, it } from 'vitest'
8
- import { ApiManager } from './api-manager.js'
9
14
  import { useHttpAuthentication, useProxy, useRestService, useStaticFiles } from './helpers.js'
10
15
  import { HttpAuthenticationSettings } from './http-authentication-settings.js'
16
+ import { HttpServerPoolToken } from './http-server-pool.js'
11
17
  import { DefaultSession } from './models/default-session.js'
12
- import { ProxyManager } from './proxy-manager.js'
13
- import { StaticServerManager } from './static-server-manager.js'
18
+ import { SessionStore, UserStore } from './user-store.js'
14
19
 
15
20
  const setupAuthStores = (i: Injector) => {
16
- addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' }))
17
- .addStore(new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
18
- .addStore(new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }))
19
- .addStore(new InMemoryStore({ model: PasswordResetToken, primaryKey: 'token' }))
20
- const repo = getRepository(i)
21
- repo.createDataSet(User, 'username')
22
- repo.createDataSet(DefaultSession, 'sessionId')
23
- repo.createDataSet(PasswordCredential, 'userName')
24
- repo.createDataSet(PasswordResetToken, 'token')
21
+ i.bind(UserStore, () => new InMemoryStore({ model: UserModel, primaryKey: 'username' }))
22
+ i.bind(SessionStore, () => new InMemoryStore({ model: DefaultSession, primaryKey: 'sessionId' }))
23
+ i.bind(PasswordCredentialStore, () => new InMemoryStore({ model: PasswordCredential, primaryKey: 'userName' }))
24
+ i.bind(PasswordResetTokenStore, () => new InMemoryStore({ model: PasswordResetToken, primaryKey: 'token' }))
25
25
  usePasswordPolicy(i)
26
26
  }
27
27
 
28
- describe('Injector extensions', () => {
28
+ describe('rest-service setup helpers', () => {
29
29
  describe('useHttpAuthentication', () => {
30
- it('Should set up HTTP Authentication', async () => {
31
- await usingAsync(new Injector(), async (i) => {
30
+ it('binds HttpAuthenticationSettings on the injector', async () => {
31
+ await usingAsync(createInjector(), async (i) => {
32
32
  setupAuthStores(i)
33
33
  useHttpAuthentication(i)
34
- expect(i.cachedSingletons.get(HttpAuthenticationSettings)).toBeDefined()
34
+ const settings = i.get(HttpAuthenticationSettings)
35
+ expect(settings).toBeDefined()
35
36
  })
36
37
  })
37
38
 
38
- it('Should register basic-auth and cookie-auth providers by default', async () => {
39
- await usingAsync(new Injector(), async (i) => {
39
+ it('registers basic-auth and cookie-auth providers by default', async () => {
40
+ await usingAsync(createInjector(), async (i) => {
40
41
  setupAuthStores(i)
41
42
  useHttpAuthentication(i)
42
- const settings = i.getInstance(HttpAuthenticationSettings)
43
+ const settings = i.get(HttpAuthenticationSettings)
43
44
  const providerNames = settings.authenticationProviders.map((p) => p.name)
44
45
  expect(providerNames).toContain('basic-auth')
45
46
  expect(providerNames).toContain('cookie-auth')
46
47
  })
47
48
  })
48
49
 
49
- it('Should not register basic-auth provider when disabled', async () => {
50
- await usingAsync(new Injector(), async (i) => {
50
+ it('omits the basic-auth provider when enableBasicAuth is false', async () => {
51
+ await usingAsync(createInjector(), async (i) => {
51
52
  setupAuthStores(i)
52
53
  useHttpAuthentication(i, { enableBasicAuth: false })
53
- const settings = i.getInstance(HttpAuthenticationSettings)
54
+ const settings = i.get(HttpAuthenticationSettings)
54
55
  const providerNames = settings.authenticationProviders.map((p) => p.name)
55
56
  expect(providerNames).not.toContain('basic-auth')
56
57
  expect(providerNames).toContain('cookie-auth')
@@ -59,41 +60,50 @@ describe('Injector extensions', () => {
59
60
  })
60
61
 
61
62
  describe('useRestService()', () => {
62
- it('Should set up a REST service', async () => {
63
- await usingAsync(new Injector(), async (i) => {
63
+ it('acquires a pooled HTTP server and attaches a serverApi', async () => {
64
+ await usingAsync(createInjector(), async (i) => {
64
65
  const port = getPort()
65
-
66
- await useRestService({ injector: i, api: {}, root: '/', port })
67
- expect(i.cachedSingletons.get(ApiManager)).toBeDefined()
66
+ const serverApi = await useRestService({ injector: i, api: {}, root: '/', port })
67
+ expect(serverApi).toBeDefined()
68
+ const pool = i.get(HttpServerPoolToken)
69
+ const record = await pool.acquire({ port })
70
+ expect(record.apis).toContain(serverApi)
68
71
  })
69
72
  })
70
73
  })
71
74
 
72
75
  describe('useStaticFiles()', () => {
73
- it('Should set up a static file server', async () => {
74
- await usingAsync(new Injector(), async (i) => {
76
+ it('attaches a static-file serverApi to the pooled HTTP server', async () => {
77
+ await usingAsync(createInjector(), async (i) => {
75
78
  const port = getPort()
76
-
77
- await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port })
78
- expect(i.cachedSingletons.get(StaticServerManager)).toBeDefined()
79
+ const serverApi = await useStaticFiles({ injector: i, baseUrl: '/', path: '.', port })
80
+ expect(serverApi).toBeDefined()
81
+ const pool = i.get(HttpServerPoolToken)
82
+ const record = await pool.acquire({ port })
83
+ expect(record.apis).toContain(serverApi)
79
84
  })
80
85
  })
81
86
  })
82
87
 
83
88
  describe('useProxy()', () => {
84
- it('Should set up a proxy server', async () => {
85
- await usingAsync(new Injector(), async (i) => {
89
+ it('attaches a proxy serverApi to the pooled HTTP server', async () => {
90
+ await usingAsync(createInjector(), async (i) => {
86
91
  const sourcePort = getPort()
87
92
  const targetPort = getPort()
88
-
89
- await useProxy({
93
+ const serverApi = await useProxy({
90
94
  injector: i,
91
95
  sourceBaseUrl: '/api',
92
96
  targetBaseUrl: `http://localhost:${targetPort}`,
93
97
  sourcePort,
94
98
  })
95
- expect(i.cachedSingletons.get(ProxyManager)).toBeDefined()
99
+ expect(serverApi).toBeDefined()
100
+ const pool = i.get(HttpServerPoolToken)
101
+ const record = await pool.acquire({ port: sourcePort })
102
+ expect(record.apis).toContain(serverApi)
96
103
  })
97
104
  })
98
105
  })
99
106
  })
107
+
108
+ // Re-export to keep ESLint happy about unused User import elsewhere.
109
+ export type { User }