@furystack/rest-service 8.0.0 → 9.0.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 (113) hide show
  1. package/esm/actions/login.d.ts +0 -1
  2. package/esm/actions/login.d.ts.map +1 -1
  3. package/esm/actions/login.js +0 -1
  4. package/esm/actions/login.js.map +1 -1
  5. package/esm/add-cors-header.d.ts +14 -0
  6. package/esm/add-cors-header.d.ts.map +1 -0
  7. package/esm/add-cors-header.js +23 -0
  8. package/esm/add-cors-header.js.map +1 -0
  9. package/esm/add-cors-header.spec.js +7 -8
  10. package/esm/add-cors-header.spec.js.map +1 -1
  11. package/esm/api-manager.d.ts.map +1 -1
  12. package/esm/api-manager.js +4 -5
  13. package/esm/api-manager.js.map +1 -1
  14. package/esm/endpoint-generators/create-delete-endpoint.d.ts +0 -1
  15. package/esm/endpoint-generators/create-delete-endpoint.d.ts.map +1 -1
  16. package/esm/endpoint-generators/create-delete-endpoint.js +0 -1
  17. package/esm/endpoint-generators/create-delete-endpoint.js.map +1 -1
  18. package/esm/endpoint-generators/create-delete-endpoint.spec.js +4 -2
  19. package/esm/endpoint-generators/create-delete-endpoint.spec.js.map +1 -1
  20. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts +0 -1
  21. package/esm/endpoint-generators/create-get-collection-endpoint.d.ts.map +1 -1
  22. package/esm/endpoint-generators/create-get-collection-endpoint.js +0 -1
  23. package/esm/endpoint-generators/create-get-collection-endpoint.js.map +1 -1
  24. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js +16 -10
  25. package/esm/endpoint-generators/create-get-collection-endpoint.spec.js.map +1 -1
  26. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts +0 -1
  27. package/esm/endpoint-generators/create-get-entity-endpoint.d.ts.map +1 -1
  28. package/esm/endpoint-generators/create-get-entity-endpoint.js +0 -1
  29. package/esm/endpoint-generators/create-get-entity-endpoint.js.map +1 -1
  30. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js +10 -6
  31. package/esm/endpoint-generators/create-get-entity-endpoint.spec.js.map +1 -1
  32. package/esm/endpoint-generators/create-patch-endpoint.d.ts +0 -1
  33. package/esm/endpoint-generators/create-patch-endpoint.d.ts.map +1 -1
  34. package/esm/endpoint-generators/create-patch-endpoint.js +2 -2
  35. package/esm/endpoint-generators/create-patch-endpoint.js.map +1 -1
  36. package/esm/endpoint-generators/create-patch-endpoint.spec.js +4 -2
  37. package/esm/endpoint-generators/create-patch-endpoint.spec.js.map +1 -1
  38. package/esm/endpoint-generators/create-post-endpoint.d.ts +0 -1
  39. package/esm/endpoint-generators/create-post-endpoint.d.ts.map +1 -1
  40. package/esm/endpoint-generators/create-post-endpoint.js +2 -2
  41. package/esm/endpoint-generators/create-post-endpoint.js.map +1 -1
  42. package/esm/endpoint-generators/create-post-endpoint.spec.js +4 -2
  43. package/esm/endpoint-generators/create-post-endpoint.spec.js.map +1 -1
  44. package/esm/endpoint-generators/utils.d.ts.map +1 -1
  45. package/esm/endpoint-generators/utils.js +0 -2
  46. package/esm/endpoint-generators/utils.js.map +1 -1
  47. package/esm/http-user-context.d.ts.map +1 -1
  48. package/esm/http-user-context.js +0 -3
  49. package/esm/http-user-context.js.map +1 -1
  50. package/esm/http-user-context.spec.js +1 -2
  51. package/esm/http-user-context.spec.js.map +1 -1
  52. package/esm/incoming-message-extensions.d.ts +0 -2
  53. package/esm/incoming-message-extensions.d.ts.map +1 -1
  54. package/esm/incoming-message-extensions.js +1 -10
  55. package/esm/incoming-message-extensions.js.map +1 -1
  56. package/esm/index.d.ts +2 -2
  57. package/esm/index.d.ts.map +1 -1
  58. package/esm/index.js +2 -2
  59. package/esm/index.js.map +1 -1
  60. package/esm/models/default-session.d.ts.map +1 -1
  61. package/esm/models/default-session.js +0 -8
  62. package/esm/models/default-session.js.map +1 -1
  63. package/esm/read-post-body.d.ts +12 -0
  64. package/esm/read-post-body.d.ts.map +1 -0
  65. package/esm/read-post-body.js +33 -0
  66. package/esm/read-post-body.js.map +1 -0
  67. package/esm/rest-service.integration.spec.js +23 -30
  68. package/esm/rest-service.integration.spec.js.map +1 -1
  69. package/esm/rest.integration.test.js +1 -1
  70. package/esm/static-server-manager.d.ts.map +1 -1
  71. package/esm/static-server-manager.js +0 -1
  72. package/esm/static-server-manager.js.map +1 -1
  73. package/esm/static-server-manager.spec.js +2 -15
  74. package/esm/static-server-manager.spec.js.map +1 -1
  75. package/esm/validate.integration.spec.js +2 -2
  76. package/esm/validate.integration.spec.js.map +1 -1
  77. package/package.json +10 -10
  78. package/src/actions/login.ts +0 -1
  79. package/src/add-cors-header.spec.ts +7 -8
  80. package/src/add-cors-header.ts +32 -0
  81. package/src/api-manager.ts +5 -5
  82. package/src/endpoint-generators/create-delete-endpoint.spec.ts +4 -2
  83. package/src/endpoint-generators/create-delete-endpoint.ts +0 -1
  84. package/src/endpoint-generators/create-get-collection-endpoint.spec.ts +16 -10
  85. package/src/endpoint-generators/create-get-collection-endpoint.ts +0 -1
  86. package/src/endpoint-generators/create-get-entity-endpoint.spec.ts +10 -6
  87. package/src/endpoint-generators/create-get-entity-endpoint.ts +0 -1
  88. package/src/endpoint-generators/create-patch-endpoint.spec.ts +4 -2
  89. package/src/endpoint-generators/create-patch-endpoint.ts +2 -2
  90. package/src/endpoint-generators/create-post-endpoint.spec.ts +4 -2
  91. package/src/endpoint-generators/create-post-endpoint.ts +2 -2
  92. package/src/endpoint-generators/utils.ts +2 -2
  93. package/src/http-user-context.spec.ts +1 -2
  94. package/src/http-user-context.ts +3 -3
  95. package/src/incoming-message-extensions.ts +0 -15
  96. package/src/index.ts +2 -2
  97. package/src/models/default-session.ts +2 -2
  98. package/src/read-post-body.ts +36 -0
  99. package/src/rest-service.integration.spec.ts +24 -34
  100. package/src/rest.integration.test.ts +1 -1
  101. package/src/static-server-manager.spec.ts +2 -18
  102. package/src/static-server-manager.ts +1 -1
  103. package/src/validate.integration.spec.ts +2 -2
  104. package/esm/incoming-message-extensions.spec.d.ts +0 -2
  105. package/esm/incoming-message-extensions.spec.d.ts.map +0 -1
  106. package/esm/incoming-message-extensions.spec.js +0 -38
  107. package/esm/incoming-message-extensions.spec.js.map +0 -1
  108. package/esm/utils.d.ts +0 -25
  109. package/esm/utils.d.ts.map +0 -1
  110. package/esm/utils.js +0 -70
  111. package/esm/utils.js.map +0 -1
  112. package/src/incoming-message-extensions.spec.ts +0 -42
  113. package/src/utils.ts +0 -68
@@ -8,6 +8,7 @@ import type { FindOptions } from '@furystack/core'
8
8
  import { getDataSetFor, getRepository } from '@furystack/repository'
9
9
  import { useRestService } from '../helpers.js'
10
10
  import { describe, it, expect } from 'vitest'
11
+ import { getPort } from '@furystack/core/port-generator'
11
12
 
12
13
  const addMockEntities = async (i: Injector) =>
13
14
  await getRepository(i)
@@ -24,10 +25,11 @@ describe('createGetCollectionEndpoint', () => {
24
25
  it('Should return the collection without filter / order', async () => {
25
26
  await usingAsync(new Injector(), async (i) => {
26
27
  setupContext(i)
28
+ const port = getPort()
27
29
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
28
30
  injector: i,
29
31
  root: '/api',
30
- port: 1112,
32
+ port,
31
33
  api: {
32
34
  GET: {
33
35
  '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -39,7 +41,7 @@ describe('createGetCollectionEndpoint', () => {
39
41
  const count = await getDataSetFor(i, MockClass, 'id').count(i)
40
42
  const allEntities = await getDataSetFor(i, MockClass, 'id').find(i, {})
41
43
 
42
- const response = await fetch('http://127.0.0.1:1112/api/entities', { method: 'GET' })
44
+ const response = await fetch(`http://127.0.0.1:${port}/api/entities`, { method: 'GET' })
43
45
  expect(response.ok).toBe(true)
44
46
  const json: GetCollectionResult<MockClass> = await response.json()
45
47
  expect(response.status).toBe(200)
@@ -51,10 +53,11 @@ describe('createGetCollectionEndpoint', () => {
51
53
  it('Should return entities in order', async () => {
52
54
  await usingAsync(new Injector(), async (i) => {
53
55
  setupContext(i)
56
+ const port = getPort()
54
57
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
55
58
  injector: i,
56
59
  root: '/api',
57
- port: 1113,
60
+ port,
58
61
  api: {
59
62
  GET: {
60
63
  '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -65,7 +68,7 @@ describe('createGetCollectionEndpoint', () => {
65
68
  const findOptions: FindOptions<MockClass, Array<keyof MockClass>> = { order: { value: 'ASC' } }
66
69
  const count = await getDataSetFor(i, MockClass, 'id').count(i, findOptions.filter)
67
70
  const orderedEntities = await getDataSetFor(i, MockClass, 'id').find(i, findOptions)
68
- const response = await fetch(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
71
+ const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
69
72
  method: 'GET',
70
73
  })
71
74
  expect(response.ok).toBe(true)
@@ -79,10 +82,11 @@ describe('createGetCollectionEndpoint', () => {
79
82
  it('Should return entities with filtering', async () => {
80
83
  await usingAsync(new Injector(), async (i) => {
81
84
  setupContext(i)
85
+ const port = getPort()
82
86
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
83
87
  injector: i,
84
88
  root: '/api',
85
- port: 1113,
89
+ port,
86
90
  api: {
87
91
  GET: {
88
92
  '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -99,7 +103,7 @@ describe('createGetCollectionEndpoint', () => {
99
103
 
100
104
  expect(filteredEntities).not.toContainEqual({ id: 'mock2', value: '3' })
101
105
 
102
- const response = await fetch(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
106
+ const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
103
107
  method: 'GET',
104
108
  })
105
109
  expect(response.ok).toBe(true)
@@ -113,10 +117,11 @@ describe('createGetCollectionEndpoint', () => {
113
117
  it('Should return entities with selecting specific fields', async () => {
114
118
  await usingAsync(new Injector(), async (i) => {
115
119
  setupContext(i)
120
+ const port = getPort()
116
121
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
117
122
  injector: i,
118
123
  root: '/api',
119
- port: 1113,
124
+ port,
120
125
  api: {
121
126
  GET: {
122
127
  '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -133,7 +138,7 @@ describe('createGetCollectionEndpoint', () => {
133
138
 
134
139
  selectedEntities.forEach((e) => expect(e.value).toBeUndefined())
135
140
 
136
- const response = await fetch(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
141
+ const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
137
142
  method: 'GET',
138
143
  })
139
144
 
@@ -148,10 +153,11 @@ describe('createGetCollectionEndpoint', () => {
148
153
  it('Should return entities with top/skip', async () => {
149
154
  await usingAsync(new Injector(), async (i) => {
150
155
  setupContext(i)
156
+ const port = getPort()
151
157
  await useRestService<{ GET: { '/entities': GetCollectionEndpoint<MockClass> } }>({
152
158
  injector: i,
153
159
  root: '/api',
154
- port: 1113,
160
+ port,
155
161
  api: {
156
162
  GET: {
157
163
  '/entities': createGetCollectionEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -170,7 +176,7 @@ describe('createGetCollectionEndpoint', () => {
170
176
  expect(topSkipEntities).not.toContainEqual({ id: 'mock1', value: '4' })
171
177
  expect(topSkipEntities).not.toContainEqual({ id: 'mock4', value: '1' })
172
178
 
173
- const response = await fetch(`http://127.0.0.1:1113/api/entities?${serializeToQueryString({ findOptions })}`, {
179
+ const response = await fetch(`http://127.0.0.1:${port}/api/entities?${serializeToQueryString({ findOptions })}`, {
174
180
  method: 'GET',
175
181
  })
176
182
  expect(response.status).toBe(200)
@@ -1,6 +1,5 @@
1
1
  import type { Constructable } from '@furystack/inject'
2
2
  import type { GetCollectionEndpoint } from '@furystack/rest'
3
- import '@furystack/repository'
4
3
  import type { RequestAction } from '../request-action-implementation.js'
5
4
  import { JsonResult } from '../request-action-implementation.js'
6
5
  import { getRepository } from '@furystack/repository'
@@ -7,15 +7,17 @@ import { createGetEntityEndpoint } from './create-get-entity-endpoint.js'
7
7
  import { getDataSetFor } from '@furystack/repository'
8
8
  import { useRestService } from '../helpers.js'
9
9
  import { describe, it, expect } from 'vitest'
10
+ import { getPort } from '@furystack/core/port-generator'
10
11
 
11
12
  describe('createGetEntityEndpoint', () => {
12
13
  it('Should return the entity', async () => {
13
14
  await usingAsync(new Injector(), async (i) => {
14
15
  setupContext(i)
16
+ const port = getPort()
15
17
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
16
18
  injector: i,
17
19
  root: '/api',
18
- port: 1113,
20
+ port,
19
21
  api: {
20
22
  GET: {
21
23
  '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -25,7 +27,7 @@ describe('createGetEntityEndpoint', () => {
25
27
  const mockEntity: MockClass = { id: 'mock', value: 'mock' }
26
28
  await getDataSetFor(i, MockClass, 'id').add(i, mockEntity)
27
29
 
28
- const response = await fetch('http://127.0.0.1:1113/api/mock', { method: 'GET' })
30
+ const response = await fetch(`http://127.0.0.1:${port}/api/mock`, { method: 'GET' })
29
31
  expect(response.status).toBe(200)
30
32
  const body = await response.json()
31
33
  expect(body).toEqual(mockEntity)
@@ -35,10 +37,11 @@ describe('createGetEntityEndpoint', () => {
35
37
  it('Should return the entity with the selected fields', async () => {
36
38
  await usingAsync(new Injector(), async (i) => {
37
39
  setupContext(i)
40
+ const port = getPort()
38
41
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
39
42
  injector: i,
40
43
  root: '/api',
41
- port: 1114,
44
+ port,
42
45
  api: {
43
46
  GET: {
44
47
  '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -48,7 +51,7 @@ describe('createGetEntityEndpoint', () => {
48
51
  const mockEntity: MockClass = { id: 'mock', value: 'mock' }
49
52
  await getDataSetFor(i, MockClass, 'id').add(i, mockEntity)
50
53
 
51
- const response = await fetch(`http://127.0.0.1:1114/api/mock?${serializeToQueryString({ select: ['id'] })}`, {
54
+ const response = await fetch(`http://127.0.0.1:${port}/api/mock?${serializeToQueryString({ select: ['id'] })}`, {
52
55
  method: 'GET',
53
56
  })
54
57
  expect(response.status).toBe(200)
@@ -60,17 +63,18 @@ describe('createGetEntityEndpoint', () => {
60
63
  it('Should return 404 if no entity has been found', async () => {
61
64
  await usingAsync(new Injector(), async (i) => {
62
65
  setupContext(i)
66
+ const port = getPort()
63
67
  await useRestService<{ GET: { '/:id': GetEntityEndpoint<MockClass, 'id'> } }>({
64
68
  injector: i,
65
69
  root: '/api',
66
- port: 1115,
70
+ port,
67
71
  api: {
68
72
  GET: {
69
73
  '/:id': createGetEntityEndpoint({ model: MockClass, primaryKey: 'id' }),
70
74
  },
71
75
  },
72
76
  })
73
- const result = await fetch(`http://127.0.0.1:1115/api/mock`, { method: 'GET' })
77
+ const result = await fetch(`http://127.0.0.1:${port}/api/mock`, { method: 'GET' })
74
78
  expect(result.status).toBe(404)
75
79
  const body = await result.json()
76
80
  expect(body).toEqual({ message: 'Entity not found' })
@@ -1,7 +1,6 @@
1
1
  import type { Constructable } from '@furystack/inject'
2
2
  import type { GetEntityEndpoint } from '@furystack/rest'
3
3
  import { RequestError } from '@furystack/rest'
4
- import '@furystack/repository'
5
4
  import type { RequestAction } from '../request-action-implementation.js'
6
5
  import { JsonResult } from '../request-action-implementation.js'
7
6
  import { getRepository } from '@furystack/repository'
@@ -6,15 +6,17 @@ import { MockClass, setupContext } from './utils.js'
6
6
  import { getDataSetFor } from '@furystack/repository'
7
7
  import { useRestService } from '../helpers.js'
8
8
  import { describe, it, expect } from 'vitest'
9
+ import { getPort } from '@furystack/core/port-generator'
9
10
 
10
11
  describe('createPatchEndpoint', () => {
11
12
  it('Should update the entity and report the success', async () => {
12
13
  await usingAsync(new Injector(), async (i) => {
13
14
  setupContext(i)
15
+ const port = getPort()
14
16
  await useRestService<{ PATCH: { '/:id': PatchEndpoint<MockClass, 'id'> } }>({
15
17
  injector: i,
16
18
  root: '/api',
17
- port: 1116,
19
+ port,
18
20
  api: {
19
21
  PATCH: {
20
22
  '/:id': createPatchEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -26,7 +28,7 @@ describe('createPatchEndpoint', () => {
26
28
  const countBeforeDelete = await getDataSetFor(i, MockClass, 'id').count(i)
27
29
  expect(countBeforeDelete).toBe(1)
28
30
 
29
- const response = await fetch('http://127.0.0.1:1116/api/mock', {
31
+ const response = await fetch(`http://127.0.0.1:${port}/api/mock`, {
30
32
  method: 'PATCH',
31
33
  body: JSON.stringify({ value: 'updated' }),
32
34
  })
@@ -1,11 +1,11 @@
1
1
  import type { Constructable } from '@furystack/inject'
2
2
  import type { PatchEndpoint } from '@furystack/rest'
3
3
  import '@furystack/repository'
4
- import '../incoming-message-extensions.js'
5
4
  import type { RequestAction } from '../request-action-implementation.js'
6
5
  import { JsonResult } from '../request-action-implementation.js'
7
6
  import { getRepository } from '@furystack/repository'
8
7
  import type { WithOptionalId } from '@furystack/core'
8
+ import { readPostBody } from '../read-post-body.js'
9
9
 
10
10
  /**
11
11
  * Creates a PATCH endpoint for updating entities
@@ -28,7 +28,7 @@ export const createPatchEndpoint = <
28
28
  getUrlParams,
29
29
  }) => {
30
30
  const { id } = getUrlParams()
31
- const patchData = await request.readPostBody<T>()
31
+ const patchData = await readPostBody<T>(request)
32
32
  const dataSet = getRepository(injector).getDataSetFor(options.model, options.primaryKey)
33
33
  await dataSet.update(injector, id, patchData)
34
34
  return JsonResult({})
@@ -6,15 +6,17 @@ import { MockClass, setupContext } from './utils.js'
6
6
  import { useRestService } from '../helpers.js'
7
7
  import { getDataSetFor } from '@furystack/repository'
8
8
  import { describe, it, expect } from 'vitest'
9
+ import { getPort } from '@furystack/core/port-generator'
9
10
 
10
11
  describe('createPostEndpoint', () => {
11
12
  it('Should create the entity and report the success', async () => {
12
13
  await usingAsync(new Injector(), async (i) => {
13
14
  setupContext(i)
15
+ const port = getPort()
14
16
  await useRestService<{ POST: { '/': PostEndpoint<MockClass, 'id'> } }>({
15
17
  injector: i,
16
18
  root: '/api',
17
- port: 1117,
19
+ port,
18
20
  api: {
19
21
  POST: {
20
22
  '/': createPostEndpoint({ model: MockClass, primaryKey: 'id' }),
@@ -22,7 +24,7 @@ describe('createPostEndpoint', () => {
22
24
  },
23
25
  })
24
26
  const entityToPost = { id: 'mock', value: 'posted' }
25
- const response = await fetch('http://127.0.0.1:1117/api', {
27
+ const response = await fetch(`http://127.0.0.1:${port}/api`, {
26
28
  method: 'POST',
27
29
  body: JSON.stringify(entityToPost),
28
30
  })
@@ -2,11 +2,11 @@ import type { Constructable } from '@furystack/inject'
2
2
  import type { PostEndpoint } from '@furystack/rest'
3
3
  import { RequestError } from '@furystack/rest'
4
4
  import '@furystack/repository'
5
- import '../incoming-message-extensions.js'
6
5
  import type { RequestAction } from '../request-action-implementation.js'
7
6
  import { JsonResult } from '../request-action-implementation.js'
8
7
  import type { WithOptionalId } from '@furystack/core'
9
8
  import { getRepository } from '@furystack/repository'
9
+ import { readPostBody } from '../read-post-body.js'
10
10
  /**
11
11
  * Creates a POST endpoint for updating entities
12
12
  * @param options The options for endpoint creation
@@ -28,7 +28,7 @@ export const createPostEndpoint = <
28
28
  options.primaryKey,
29
29
  )
30
30
 
31
- const entityToCreate = await request.readPostBody<TWritableData>()
31
+ const entityToCreate = await readPostBody<TWritableData>(request)
32
32
  const { created } = await dataSet.add(injector, entityToCreate)
33
33
  if (!created || !created.length) {
34
34
  throw new RequestError('Entity not found', 404)
@@ -6,8 +6,8 @@ import '../helpers.js'
6
6
  import { getRepository } from '@furystack/repository'
7
7
 
8
8
  export class MockClass {
9
- id!: string
10
- value!: string
9
+ declare id: string
10
+ declare value: string
11
11
  }
12
12
 
13
13
  export const setupContext = (i: Injector) => {
@@ -19,8 +19,7 @@ export const prepareInjector = async (i: Injector) => {
19
19
  const setupUser = async (i: Injector, userName: string, password: string) => {
20
20
  const sm = i.getInstance(StoreManager)
21
21
  const pw = i.getInstance(PasswordAuthenticator)
22
- const hasher = pw.getHasher()
23
- const cred = await hasher.createCredential(userName, password)
22
+ const cred = await pw.hasher.createCredential(userName, password)
24
23
  await sm.getStoreFor(PasswordCredential, 'userName').add(cred)
25
24
  await sm.getStoreFor(User, 'username').add({ username: userName, roles: [] })
26
25
  }
@@ -159,11 +159,11 @@ export class HttpUserContext {
159
159
  }
160
160
 
161
161
  @Injected(HttpAuthenticationSettings)
162
- public readonly authentication!: HttpAuthenticationSettings<User, DefaultSession>
162
+ public declare readonly authentication: HttpAuthenticationSettings<User, DefaultSession>
163
163
 
164
164
  @Injected(StoreManager)
165
- private readonly storeManager!: StoreManager
165
+ private declare readonly storeManager: StoreManager
166
166
 
167
167
  @Injected(PasswordAuthenticator)
168
- private readonly authenticator!: PasswordAuthenticator
168
+ private declare readonly authenticator: PasswordAuthenticator
169
169
  }
@@ -1,20 +1,5 @@
1
- import http from 'http'
2
- import { Utils } from './utils.js'
3
-
4
1
  declare module 'http' {
5
2
  export interface IncomingMessage {
6
- readPostBodyRaw: () => Promise<string>
7
- readPostBody: <T>() => Promise<T>
8
3
  postBody: unknown
9
4
  }
10
5
  }
11
-
12
- http.IncomingMessage.prototype.readPostBody = async function <T>() {
13
- const utils = new Utils()
14
- return await utils.readPostBody<T>(this)
15
- }
16
-
17
- http.IncomingMessage.prototype.readPostBodyRaw = async function () {
18
- const utils = new Utils()
19
- return await utils.readPostBodyRaw(this)
20
- }
package/src/index.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  export * from './helpers.js'
2
+ export * from './add-cors-header.js'
2
3
  export * from './api-manager.js'
3
4
  export * from './actions/index.js'
4
5
  export * from './authenticate.js'
5
6
  export * from './authorize.js'
6
7
  export * from './http-authentication-settings.js'
7
8
  export * from './http-user-context.js'
8
- export * from './incoming-message-extensions.js'
9
9
  export * from './server-manager.js'
10
10
  export * from './server-response-extensions.js'
11
- export * from './utils.js'
12
11
  export * from './models/index.js'
13
12
  export * from './endpoint-generators/index.js'
14
13
  export * from './schema-validator/index.js'
15
14
  export * from './request-action-implementation.js'
16
15
  export * from './validate.js'
17
16
  export * from './mime-types.js'
17
+ export * from './read-post-body.js'
18
18
  export * from './static-server-manager.js'
@@ -5,10 +5,10 @@ export class DefaultSession {
5
5
  /**
6
6
  * The generated session identifier
7
7
  */
8
- public sessionId!: string
8
+ declare sessionId: string
9
9
 
10
10
  /**
11
11
  * The user's login name for the session
12
12
  */
13
- public username!: string
13
+ declare username: string
14
14
  }
@@ -0,0 +1,36 @@
1
+ import type { IncomingMessage } from 'http'
2
+ import './incoming-message-extensions.js'
3
+
4
+ export const readPostBodyRaw = async (incomingMessage: IncomingMessage) => {
5
+ if (!incomingMessage.readable) {
6
+ throw Error('Incoming message is not readable')
7
+ }
8
+
9
+ let body = ''
10
+ await new Promise<void>((resolve, reject) => {
11
+ incomingMessage.on('readable', () => {
12
+ const data = incomingMessage.read()
13
+ if (data) {
14
+ body += data
15
+ }
16
+ })
17
+ incomingMessage.on('end', () => {
18
+ resolve()
19
+ })
20
+ incomingMessage.on('error', (err) => {
21
+ reject(err)
22
+ })
23
+ })
24
+ return body
25
+ }
26
+
27
+ /**
28
+ * Reads the post's body and returns a promise with a parsed value
29
+ * @param incomingMessage The incoming message instance
30
+ * @returns the parsed object from the post body
31
+ */
32
+ export const readPostBody = async <T>(incomingMessage: IncomingMessage): Promise<T> => {
33
+ const body = incomingMessage.postBody || JSON.parse(await readPostBodyRaw(incomingMessage))
34
+ incomingMessage.postBody = body
35
+ return body
36
+ }
@@ -12,7 +12,7 @@ import { serializeValue } from '@furystack/rest'
12
12
  import { getPort } from '@furystack/core/port-generator'
13
13
 
14
14
  class UserWithPassword extends User {
15
- password!: string
15
+ declare password: string
16
16
  }
17
17
 
18
18
  interface IntegrationTestApi extends RestApi {
@@ -29,9 +29,9 @@ interface IntegrationTestApi extends RestApi {
29
29
  }
30
30
  }
31
31
 
32
- const prepareInjector = async (i: Injector) => {
32
+ const createIntegrationApi = async () => {
33
+ const i = new Injector()
33
34
  const port = getPort()
34
- const hostName = 'localhost'
35
35
  const root = 'test-api'
36
36
 
37
37
  addStore(i, new InMemoryStore({ model: User, primaryKey: 'username' })).addStore(
@@ -44,8 +44,7 @@ const prepareInjector = async (i: Injector) => {
44
44
  await useRestService<IntegrationTestApi>({
45
45
  injector: i,
46
46
  root,
47
- port: getPort(),
48
- hostName,
47
+ port,
49
48
  cors: {
50
49
  credentials: true,
51
50
  origins: ['http://localhost:8080'],
@@ -71,23 +70,22 @@ const prepareInjector = async (i: Injector) => {
71
70
  })
72
71
 
73
72
  return {
74
- apiUrl: `http://${hostName}:${port}/${root}`,
73
+ apiUrl: `http://127.0.0.1:${port}/${root}`,
75
74
  port,
76
- hostName,
75
+ dispose: i.dispose.bind(i),
77
76
  }
78
77
  }
79
78
 
80
79
  describe('@furystack/rest-service inregration tests', () => {
81
80
  it('Should be started and disposed', async () => {
82
- await usingAsync(new Injector(), async (i) => {
83
- await prepareInjector(i)
84
- })
81
+ await usingAsync(await createIntegrationApi(), async () => {})
85
82
  })
86
83
 
87
84
  it('Should respond with 404 when a route is not found', async () => {
88
- await usingAsync(new Injector(), async (i) => {
89
- const { apiUrl } = await prepareInjector(i)
90
- const result = await fetch(PathHelper.joinPaths(apiUrl, 'some-route-that-does-not-exists'))
85
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
86
+ const result = await fetch(PathHelper.joinPaths(apiUrl, 'some-route-that-does-not-exists'), {
87
+ method: 'GET',
88
+ })
91
89
  expect(result.ok).toBe(false)
92
90
  expect(result.status).toBe(404)
93
91
  const responseText = await result.json()
@@ -96,8 +94,7 @@ describe('@furystack/rest-service inregration tests', () => {
96
94
  })
97
95
 
98
96
  it('Should respond with 401 for unauthorized request errors', async () => {
99
- await usingAsync(new Injector(), async (i) => {
100
- const { apiUrl } = await prepareInjector(i)
97
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
101
98
  const result = await fetch(PathHelper.joinPaths(apiUrl, 'currentUser'))
102
99
  expect(result.ok).toBe(false)
103
100
  expect(result.status).toBe(401)
@@ -107,8 +104,7 @@ describe('@furystack/rest-service inregration tests', () => {
107
104
  })
108
105
 
109
106
  it('Should respond with 401 for unauthorized request errors', async () => {
110
- await usingAsync(new Injector(), async (i) => {
111
- const { apiUrl } = await prepareInjector(i)
107
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
112
108
  const result = await fetch(PathHelper.joinPaths(apiUrl, 'currentUser'))
113
109
  expect(result.ok).toBe(false)
114
110
  expect(result.status).toBe(401)
@@ -118,8 +114,7 @@ describe('@furystack/rest-service inregration tests', () => {
118
114
  })
119
115
 
120
116
  it('Should respond with the correct result body', async () => {
121
- await usingAsync(new Injector(), async (i) => {
122
- const { apiUrl } = await prepareInjector(i)
117
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
123
118
  const response = await fetch(PathHelper.joinPaths(apiUrl, 'isAuthenticated'))
124
119
  expect(response.status).toBe(200)
125
120
  const result = await response.json()
@@ -128,9 +123,8 @@ describe('@furystack/rest-service inregration tests', () => {
128
123
  })
129
124
 
130
125
  it('Should be able to read query parameters', async () => {
131
- await usingAsync(new Injector(), async (i) => {
132
- const { apiUrl } = await prepareInjector(i)
133
-
126
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
127
+ console.log('apiUrl', apiUrl)
134
128
  const response = await fetch(PathHelper.joinPaths(apiUrl, `testQuery?param1=${serializeValue('foo')}`))
135
129
  expect(response.status).toBe(200)
136
130
  const result = await response.json()
@@ -139,9 +133,7 @@ describe('@furystack/rest-service inregration tests', () => {
139
133
  })
140
134
 
141
135
  it('Should be able to read url parameters', async () => {
142
- await usingAsync(new Injector(), async (i) => {
143
- const { apiUrl } = await prepareInjector(i)
144
-
136
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
145
137
  const response = await fetch(PathHelper.joinPaths(apiUrl, 'testUrlParams/bar'))
146
138
  expect(response.status).toBe(200)
147
139
  const result = await response.json()
@@ -150,9 +142,7 @@ describe('@furystack/rest-service inregration tests', () => {
150
142
  })
151
143
 
152
144
  it('Should be able to read post body', async () => {
153
- await usingAsync(new Injector(), async (i) => {
154
- const { apiUrl } = await prepareInjector(i)
155
-
145
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
156
146
  const response = await fetch(PathHelper.joinPaths(apiUrl, 'testPostBody'), {
157
147
  method: 'POST',
158
148
  body: JSON.stringify({ value: 'baz' }),
@@ -164,20 +154,20 @@ describe('@furystack/rest-service inregration tests', () => {
164
154
  })
165
155
 
166
156
  it('Should respond with OK to OPTIONS requests', async () => {
167
- await usingAsync(new Injector(), async (i) => {
168
- const { apiUrl } = await prepareInjector(i)
169
-
157
+ await usingAsync(await createIntegrationApi(), async ({ apiUrl }) => {
170
158
  const response = await fetch(PathHelper.joinPaths(apiUrl, 'testPostBody'), {
171
159
  method: 'OPTIONS',
160
+ }).catch((e) => {
161
+ console.log(e)
162
+ throw e
172
163
  })
173
164
  expect(response.status).toBe(200)
174
165
  })
175
166
  })
176
167
 
177
168
  it('Should reject requests outside of the API Root', async () => {
178
- await usingAsync(new Injector(), async (i) => {
179
- const { hostName, port } = await prepareInjector(i)
180
- await expect(fetch(PathHelper.joinPaths(`http://${hostName}:${port}`, 'not-my-api-root'))).rejects.toThrowError(
169
+ await usingAsync(await createIntegrationApi(), async ({ port }) => {
170
+ await expect(fetch(PathHelper.joinPaths(`http://127.0.0.1:${port}`, 'not-my-api-root'))).rejects.toThrowError(
181
171
  'fetch failed',
182
172
  )
183
173
  })
@@ -56,7 +56,7 @@ const createEchoApiServer = async () => {
56
56
  },
57
57
  })
58
58
  const client = createClient<EchoApi>({
59
- endpointUrl: `http://localhost:${port}/api`,
59
+ endpointUrl: `http://127.0.0.1:${port}/api`,
60
60
  })
61
61
  return {
62
62
  dispose: injector.dispose.bind(injector),
@@ -3,23 +3,7 @@ import { sleepAsync, usingAsync } from '@furystack/utils'
3
3
  import { ServerManager } from './server-manager.js'
4
4
  import { StaticServerManager } from './static-server-manager.js'
5
5
  import { describe, it, expect, vi } from 'vitest'
6
-
7
- /**
8
- * Generator for an incremental port number
9
- * @param initialPort The initial port number
10
- * @yields a port for testing
11
- * @returns The Port number
12
- */
13
- function* portGenerator(initialPort = 17000) {
14
- let port = initialPort
15
-
16
- while (true) {
17
- yield port++
18
- }
19
- return port
20
- }
21
-
22
- const getPort = () => portGenerator().next().value
6
+ import { getPort } from '@furystack/core/port-generator'
23
7
 
24
8
  describe('StaticServerManager', () => {
25
9
  describe('Top level routing', () => {
@@ -33,7 +17,7 @@ describe('StaticServerManager', () => {
33
17
  port,
34
18
  })
35
19
 
36
- const result = await fetch(`http://localhost:${port}/not-found.html`)
20
+ const result = await fetch(`http://127.0.0.1:${port}/not-found.html`)
37
21
  expect(result.ok).toBe(false)
38
22
  expect(result.status).toBe(404)
39
23
  expect(result?.headers.get('content-type')).toBe('text/plain')
@@ -18,7 +18,7 @@ export interface StaticServerOptions {
18
18
  @Injectable({ lifetime: 'singleton' })
19
19
  export class StaticServerManager {
20
20
  @Injected(ServerManager)
21
- private readonly serverManager!: ServerManager
21
+ private declare readonly serverManager: ServerManager
22
22
 
23
23
  private async sendFile({
24
24
  fullPath,