spiceflow 1.1.7 → 1.1.9
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.
- package/README.md +177 -92
- package/dist/benchmark.benchmark.js.map +1 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +8 -12
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.js.map +1 -1
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js +1 -3
- package/dist/client/ws.js.map +1 -1
- package/dist/client.test.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js.map +1 -1
- package/dist/cors.test.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/middleware.test.js +65 -0
- package/dist/middleware.test.js.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +1 -1
- package/dist/openapi.js.map +1 -1
- package/dist/openapi.test.js.map +1 -1
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +12 -5
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/stream.test.js +6 -6
- package/dist/stream.test.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.test.js +56 -6
- package/dist/types.test.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/zod.test.js.map +1 -1
- package/package.json +1 -1
- package/src/benchmark.benchmark.ts +8 -8
- package/src/client/errors.ts +17 -17
- package/src/client/index.ts +437 -469
- package/src/client/types.ts +168 -191
- package/src/client/utils.ts +5 -5
- package/src/client/ws.ts +87 -89
- package/src/client.test.ts +176 -183
- package/src/context.ts +82 -82
- package/src/cors.test.ts +38 -38
- package/src/cors.ts +87 -92
- package/src/error.ts +13 -13
- package/src/middleware.test.ts +221 -149
- package/src/openapi.test.ts +97 -97
- package/src/openapi.ts +365 -365
- package/src/spiceflow.test.ts +461 -467
- package/src/spiceflow.ts +1117 -1157
- package/src/stream.test.ts +310 -310
- package/src/types.test.ts +59 -39
- package/src/types.ts +698 -701
- package/src/utils.ts +79 -79
- package/src/zod.test.ts +64 -64
package/src/spiceflow.test.ts
CHANGED
|
@@ -4,371 +4,369 @@ import { bfs, Spiceflow } from './spiceflow.js'
|
|
|
4
4
|
import { z } from 'zod'
|
|
5
5
|
|
|
6
6
|
test('works', async () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
const res = await new Spiceflow()
|
|
8
|
+
.post('/xxx', () => 'hi')
|
|
9
|
+
.handle(new Request('http://localhost/xxx', { method: 'POST' }))
|
|
10
|
+
expect(res.status).toBe(200)
|
|
11
|
+
expect(await res.json()).toEqual('hi')
|
|
12
12
|
})
|
|
13
13
|
test('dynamic route', async () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
const res = await new Spiceflow()
|
|
15
|
+
.post('/ids/:id', () => 'hi')
|
|
16
|
+
.handle(new Request('http://localhost/ids/xxx', { method: 'POST' }))
|
|
17
|
+
expect(res.status).toBe(200)
|
|
18
|
+
expect(await res.json()).toEqual('hi')
|
|
19
19
|
})
|
|
20
20
|
test('GET dynamic route', async () => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
const res = await new Spiceflow()
|
|
22
|
+
.get('/ids/:id', () => 'hi')
|
|
23
|
+
.post('/ids/:id', ({ params: { id } }) => id, {
|
|
24
|
+
params: z.object({ id: z.string() }),
|
|
25
|
+
})
|
|
26
|
+
.handle(new Request('http://localhost/ids/xxx', { method: 'GET' }))
|
|
27
|
+
expect(res.status).toBe(200)
|
|
28
|
+
expect(await res.json()).toEqual('hi')
|
|
29
29
|
})
|
|
30
30
|
|
|
31
31
|
test('onError does not fire on 404', async () => {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
let errorFired = false
|
|
33
|
+
const app = new Spiceflow()
|
|
34
|
+
.get('/test', () => 'Hello')
|
|
35
|
+
.onError(() => {
|
|
36
|
+
errorFired = true
|
|
37
|
+
return new Response('Error', { status: 500 })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
const res = await app.handle(
|
|
41
|
+
new Request('http://localhost/non-existent', { method: 'GET' }),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
expect(res.status).toBe(404)
|
|
45
|
+
expect(errorFired).toBe(false)
|
|
46
|
+
expect(await res.text()).toBe('Not Found')
|
|
47
47
|
})
|
|
48
48
|
|
|
49
49
|
test('onError fires on validation errors', async () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
50
|
+
let errorMessage = ''
|
|
51
|
+
const app = new Spiceflow()
|
|
52
|
+
.post(
|
|
53
|
+
'/test',
|
|
54
|
+
async ({ request }) => {
|
|
55
|
+
await request.json()
|
|
56
|
+
return 'Success'
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
body: z.object({
|
|
60
|
+
name: z.string(),
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
.onError(({ error }) => {
|
|
65
|
+
errorMessage = error.message
|
|
66
|
+
return new Response('Error', { status: 400 })
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const res = await app.handle(
|
|
70
|
+
new Request('http://localhost/test', {
|
|
71
|
+
method: 'POST',
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-Type': 'application/json',
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({ name: 1 }), // Invalid type for 'name'
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
expect(res.status).toBe(400)
|
|
80
|
+
expect(errorMessage).toContain('data/name must be string')
|
|
81
|
+
expect(await res.text()).toBe('Error')
|
|
82
82
|
})
|
|
83
83
|
|
|
84
84
|
test.todo('HEAD uses GET route, does not add body', async () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
const app = new Spiceflow().get('/ids/:id', () => {
|
|
86
|
+
console.trace('GET')
|
|
87
|
+
return {
|
|
88
|
+
message: 'hi',
|
|
89
|
+
length: 10,
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const res = await app.handle(
|
|
94
|
+
new Request('http://localhost/ids/xxx', { method: 'HEAD' }),
|
|
95
|
+
)
|
|
96
|
+
expect(res.status).toBe(200)
|
|
97
|
+
// expect(res.headers.get('Content-Length')).toBe('10')
|
|
98
|
+
expect(await res.text()).toBe('')
|
|
99
|
+
|
|
100
|
+
// Compare with GET to ensure HEAD is using GET route
|
|
101
|
+
const getRes = await app.handle(
|
|
102
|
+
new Request('http://localhost/ids/xxx', { method: 'GET' }),
|
|
103
|
+
)
|
|
104
|
+
expect(getRes.status).toBe(200)
|
|
105
|
+
expect(await getRes.json()).toEqual({ message: 'hi', length: 10 })
|
|
106
106
|
})
|
|
107
107
|
|
|
108
108
|
test('GET with query, untyped', async () => {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
109
|
+
const res = await new Spiceflow()
|
|
110
|
+
.get('/query', ({ query }) => {
|
|
111
|
+
return query.id
|
|
112
|
+
})
|
|
113
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
114
|
+
expect(res.status).toBe(200)
|
|
115
|
+
expect(await res.json()).toEqual('hi')
|
|
116
116
|
})
|
|
117
117
|
|
|
118
118
|
test('GET with query, zod, fails validation', async () => {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
119
|
+
const res = await new Spiceflow()
|
|
120
|
+
.get(
|
|
121
|
+
'/query',
|
|
122
|
+
({ query }) => {
|
|
123
|
+
return query.id
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
query: z.object({
|
|
127
|
+
id: z.number(),
|
|
128
|
+
}),
|
|
129
|
+
},
|
|
130
|
+
)
|
|
131
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
132
|
+
expect(res.status).toBe(422)
|
|
133
133
|
})
|
|
134
134
|
|
|
135
135
|
test('GET with query and zod', async () => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
136
|
+
const res = await new Spiceflow()
|
|
137
|
+
.get(
|
|
138
|
+
'/query',
|
|
139
|
+
({ query }) => {
|
|
140
|
+
return query.id
|
|
141
|
+
// @ts-expect-error
|
|
142
|
+
void query.sdfsd
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
query: z.object({
|
|
146
|
+
id: z.string(),
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
)
|
|
150
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
151
|
+
expect(res.status).toBe(200)
|
|
152
|
+
expect(await res.json()).toEqual('hi')
|
|
153
153
|
})
|
|
154
154
|
|
|
155
155
|
test('GET dynamic route, params are typed', async () => {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
156
|
+
const res = await new Spiceflow()
|
|
157
|
+
.get('/ids/:id', ({ params }) => {
|
|
158
|
+
let id = params.id
|
|
159
|
+
// @ts-expect-error
|
|
160
|
+
params.sdfsd
|
|
161
|
+
return id
|
|
162
|
+
})
|
|
163
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
164
|
+
expect(res.status).toBe(200)
|
|
165
|
+
expect(await res.json()).toEqual('hi')
|
|
166
166
|
})
|
|
167
167
|
|
|
168
168
|
test('GET dynamic route, params are typed with schema', async () => {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
169
|
+
const res = await new Spiceflow()
|
|
170
|
+
.get(
|
|
171
|
+
'/ids/:id',
|
|
172
|
+
({ params }) => {
|
|
173
|
+
let id = params.id
|
|
174
|
+
// @ts-expect-error
|
|
175
|
+
params.sdfsd
|
|
176
|
+
return id
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
params: z.object({
|
|
180
|
+
id: z.string(),
|
|
181
|
+
}),
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
185
|
+
expect(res.status).toBe(200)
|
|
186
|
+
expect(await res.json()).toEqual('hi')
|
|
187
187
|
})
|
|
188
188
|
|
|
189
189
|
test('missing route is not found', async () => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
190
|
+
const res = await new Spiceflow()
|
|
191
|
+
.get('/ids/:id', () => 'hi')
|
|
192
|
+
.handle(new Request('http://localhost/zxxx', { method: 'GET' }))
|
|
193
|
+
expect(res.status).toBe(404)
|
|
194
194
|
})
|
|
195
195
|
test('state works', async () => {
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
196
|
+
const res = await new Spiceflow()
|
|
197
|
+
.state('id', '')
|
|
198
|
+
.use(({ state, request }) => {
|
|
199
|
+
state.id = 'xxx'
|
|
200
|
+
})
|
|
201
|
+
.get('/get', ({ state: state }) => {
|
|
202
|
+
expect(state.id).toBe('xxx')
|
|
203
|
+
})
|
|
204
|
+
.handle(new Request('http://localhost/get'))
|
|
205
|
+
expect(res.status).toBe(200)
|
|
206
206
|
})
|
|
207
207
|
|
|
208
208
|
test('body is parsed as json', async () => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
209
|
+
let body
|
|
210
|
+
const res = await new Spiceflow()
|
|
211
|
+
.state('id', '')
|
|
212
|
+
|
|
213
|
+
.post('/post', async (c) => {
|
|
214
|
+
body = await c.request.json()
|
|
215
|
+
// console.log({request})
|
|
216
|
+
return body
|
|
217
|
+
})
|
|
218
|
+
.handle(
|
|
219
|
+
new Request('http://localhost/post', {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
headers: {
|
|
222
|
+
'content-type': 'application/json',
|
|
223
|
+
},
|
|
224
|
+
body: JSON.stringify({ name: 'John' }),
|
|
225
|
+
}),
|
|
226
|
+
)
|
|
227
|
+
expect(res.status).toBe(200)
|
|
228
|
+
expect(await res.json()).toEqual({ name: 'John' })
|
|
229
229
|
})
|
|
230
230
|
|
|
231
231
|
test('validate body works, request success', async () => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
232
|
+
const res = await new Spiceflow()
|
|
233
|
+
|
|
234
|
+
.post(
|
|
235
|
+
'/post',
|
|
236
|
+
async ({ request }) => {
|
|
237
|
+
// console.log({request})
|
|
238
|
+
let body = await request.json()
|
|
239
|
+
expect(body).toEqual({ name: 'John' })
|
|
240
|
+
return 'ok'
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
body: Type.Object({
|
|
244
|
+
name: Type.String(),
|
|
245
|
+
}),
|
|
246
|
+
},
|
|
247
|
+
)
|
|
248
|
+
.handle(
|
|
249
|
+
new Request('http://localhost/post', {
|
|
250
|
+
method: 'POST',
|
|
251
|
+
headers: {
|
|
252
|
+
'content-type': 'application/json',
|
|
253
|
+
},
|
|
254
|
+
body: JSON.stringify({ name: 'John' }),
|
|
255
|
+
}),
|
|
256
|
+
)
|
|
257
|
+
expect(res.status).toBe(200)
|
|
258
|
+
expect(await res.text()).toMatchInlineSnapshot(`""ok""`)
|
|
259
259
|
})
|
|
260
260
|
|
|
261
261
|
test('validate body works, request fails', async () => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
262
|
+
const res = await new Spiceflow()
|
|
263
|
+
|
|
264
|
+
.post(
|
|
265
|
+
'/post',
|
|
266
|
+
async ({ request, redirect }) => {
|
|
267
|
+
// console.log({request})
|
|
268
|
+
let body = await request.json()
|
|
269
|
+
expect(body).toEqual({ name: 'John' })
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
body: Type.Object({
|
|
273
|
+
name: Type.String(),
|
|
274
|
+
requiredField: Type.String(),
|
|
275
|
+
}),
|
|
276
|
+
},
|
|
277
|
+
)
|
|
278
|
+
.handle(
|
|
279
|
+
new Request('http://localhost/post', {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'content-type': 'application/json',
|
|
283
|
+
},
|
|
284
|
+
body: JSON.stringify({ name: 'John' }),
|
|
285
|
+
}),
|
|
286
|
+
)
|
|
287
|
+
expect(res.status).toBe(422)
|
|
288
|
+
expect(await res.text()).toMatchInlineSnapshot(
|
|
289
|
+
`"data must have required property 'requiredField'"`,
|
|
290
|
+
)
|
|
291
291
|
})
|
|
292
292
|
|
|
293
293
|
test('run use', async () => {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
294
|
+
const res = await new Spiceflow()
|
|
295
|
+
.use(({ request }) => {
|
|
296
|
+
expect(request.method).toBe('HEAD')
|
|
297
|
+
return new Response('ok', { status: 401 })
|
|
298
|
+
})
|
|
299
|
+
.use(({ request }) => {
|
|
300
|
+
expect(request.method).toBe('HEAD')
|
|
301
|
+
return 'second one'
|
|
302
|
+
})
|
|
303
|
+
.head('/ids/:id', () => 'hi')
|
|
304
|
+
.handle(new Request('http://localhost/ids/zxxx', { method: 'HEAD' }))
|
|
305
|
+
expect(res.status).toBe(401)
|
|
306
|
+
expect(await res.text()).toBe('ok')
|
|
307
307
|
})
|
|
308
308
|
|
|
309
309
|
test('run use', async () => {
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
310
|
+
const res = await new Spiceflow()
|
|
311
|
+
.use(({ request }) => {
|
|
312
|
+
expect(request.method).toBe('HEAD')
|
|
313
|
+
return new Response('ok', { status: 401 })
|
|
314
|
+
})
|
|
315
|
+
.use(({ request }) => {
|
|
316
|
+
expect(request.method).toBe('HEAD')
|
|
317
|
+
return 'second one'
|
|
318
|
+
})
|
|
319
|
+
.head('/ids/:id', () => 'hi')
|
|
320
|
+
.handle(new Request('http://localhost/ids/zxxx', { method: 'HEAD' }))
|
|
321
|
+
expect(res.status).toBe(401)
|
|
322
|
+
expect(await res.text()).toBe('ok')
|
|
323
323
|
})
|
|
324
324
|
|
|
325
325
|
test('basPath works', async () => {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
326
|
+
const res = await new Spiceflow({ basePath: '/one' })
|
|
327
|
+
.get('/ids/:id', () => 'hi')
|
|
328
|
+
.handle(new Request('http://localhost/one/ids/xxx', { method: 'GET' }))
|
|
329
|
+
expect(res.status).toBe(200)
|
|
330
|
+
expect(await res.json()).toEqual('hi')
|
|
331
331
|
})
|
|
332
332
|
|
|
333
333
|
test('basPath works with use', async () => {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
expect(await res.json()).toEqual('hi')
|
|
355
|
-
}
|
|
334
|
+
let app = new Spiceflow({ basePath: '/one' }).use(
|
|
335
|
+
new Spiceflow({})
|
|
336
|
+
.get('/two', () => 'hi')
|
|
337
|
+
.use(new Spiceflow({ basePath: '/three' }).get('/four', () => 'hi')),
|
|
338
|
+
)
|
|
339
|
+
{
|
|
340
|
+
const res = await app.handle(
|
|
341
|
+
new Request('http://localhost/one/two', { method: 'GET' }),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
expect(res.status).toBe(200)
|
|
345
|
+
expect(await res.json()).toEqual('hi')
|
|
346
|
+
}
|
|
347
|
+
{
|
|
348
|
+
const res = await app.handle(
|
|
349
|
+
new Request('http://localhost/one/three/four', { method: 'GET' }),
|
|
350
|
+
)
|
|
351
|
+
expect(res.status).toBe(200)
|
|
352
|
+
expect(await res.json()).toEqual('hi')
|
|
353
|
+
}
|
|
356
354
|
})
|
|
357
355
|
|
|
358
356
|
test('getRouteAndParents', async () => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
357
|
+
let app = new Spiceflow({ basePath: '/one' })
|
|
358
|
+
.get('/ids/:id', () => 'hi')
|
|
359
|
+
.use(
|
|
360
|
+
new Spiceflow({ basePath: '/two' }).use(
|
|
361
|
+
new Spiceflow({ basePath: '/three' }).get('/four', () => 'hi'),
|
|
362
|
+
),
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
let routers = bfs(app)
|
|
366
|
+
let last = routers[routers.length - 1]
|
|
367
|
+
|
|
368
|
+
expect(app['getAppAndParents'](last).map((x) => x.prefix))
|
|
369
|
+
.toMatchInlineSnapshot(`
|
|
372
370
|
[
|
|
373
371
|
"/one",
|
|
374
372
|
"/two",
|
|
@@ -378,23 +376,23 @@ test('getRouteAndParents', async () => {
|
|
|
378
376
|
})
|
|
379
377
|
|
|
380
378
|
test('getAppsInScope include all parent apps', async () => {
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
379
|
+
let app = new Spiceflow({ basePath: '/one' })
|
|
380
|
+
.get('/ids/:id', () => 'hi')
|
|
381
|
+
.use(
|
|
382
|
+
new Spiceflow({ basePath: '/two' }).use(
|
|
383
|
+
new Spiceflow({ basePath: '/three' }).use(
|
|
384
|
+
new Spiceflow({ basePath: '/four' })
|
|
385
|
+
.get('/five', () => 'hi')
|
|
386
|
+
.use(({ request }) => {}),
|
|
387
|
+
),
|
|
388
|
+
),
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
let routers = bfs(app)
|
|
392
|
+
let secondLast = routers[routers.length - 2]
|
|
393
|
+
|
|
394
|
+
expect(app['getAppsInScope'](secondLast).map((x) => x.prefix))
|
|
395
|
+
.toMatchInlineSnapshot(`
|
|
398
396
|
[
|
|
399
397
|
"/one",
|
|
400
398
|
"/two",
|
|
@@ -404,23 +402,23 @@ test('getAppsInScope include all parent apps', async () => {
|
|
|
404
402
|
})
|
|
405
403
|
|
|
406
404
|
test('getAppsInScope include all parent apps and non scoped apps', async () => {
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
405
|
+
let app = new Spiceflow({ basePath: '/one' })
|
|
406
|
+
.get('/ids/:id', () => 'hi')
|
|
407
|
+
.use(
|
|
408
|
+
new Spiceflow({ basePath: '/two' }).use(
|
|
409
|
+
new Spiceflow({ basePath: '/three' }).use(
|
|
410
|
+
new Spiceflow({ basePath: '/four', scoped: false })
|
|
411
|
+
.get('/five', () => 'hi')
|
|
412
|
+
.use(({ request }) => {}),
|
|
413
|
+
),
|
|
414
|
+
),
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
let routers = bfs(app)
|
|
418
|
+
let secondLast = routers[routers.length - 2]
|
|
419
|
+
|
|
420
|
+
expect(app['getAppsInScope'](secondLast).map((x) => x.prefix))
|
|
421
|
+
.toMatchInlineSnapshot(`
|
|
424
422
|
[
|
|
425
423
|
"/one",
|
|
426
424
|
"/two",
|
|
@@ -431,135 +429,131 @@ test('getAppsInScope include all parent apps and non scoped apps', async () => {
|
|
|
431
429
|
})
|
|
432
430
|
|
|
433
431
|
test('use with 2 basPath works', async () => {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
expect(await res.json()).toEqual('two')
|
|
475
|
-
}
|
|
476
|
-
expect(oneOnReq).toBe(true)
|
|
477
|
-
expect(twoOnReq).toBe(true)
|
|
432
|
+
let oneOnReq = false
|
|
433
|
+
let twoOnReq = false
|
|
434
|
+
let onReqCalled: string[] = []
|
|
435
|
+
const app = await new Spiceflow()
|
|
436
|
+
.use(({ request }) => {
|
|
437
|
+
onReqCalled.push('root')
|
|
438
|
+
})
|
|
439
|
+
.use(
|
|
440
|
+
new Spiceflow({ basePath: '/one' })
|
|
441
|
+
|
|
442
|
+
.use(({ request }) => {
|
|
443
|
+
oneOnReq = true
|
|
444
|
+
onReqCalled.push('one')
|
|
445
|
+
})
|
|
446
|
+
.get('/ids/:id', ({ params }) => params.id),
|
|
447
|
+
)
|
|
448
|
+
.use(
|
|
449
|
+
new Spiceflow({ basePath: '/two' })
|
|
450
|
+
.use((c) => {
|
|
451
|
+
twoOnReq = true
|
|
452
|
+
onReqCalled.push('two')
|
|
453
|
+
})
|
|
454
|
+
.get('/ids/:id', ({ params }) => params.id, {}),
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
{
|
|
458
|
+
const res = await app.handle(new Request('http://localhost/one/ids/one'))
|
|
459
|
+
expect(res.status).toBe(200)
|
|
460
|
+
|
|
461
|
+
expect(await res.json()).toEqual('one')
|
|
462
|
+
}
|
|
463
|
+
expect(onReqCalled).toEqual(['root', 'one'])
|
|
464
|
+
{
|
|
465
|
+
const res = await app.handle(new Request('http://localhost/two/ids/two'))
|
|
466
|
+
expect(res.status).toBe(200)
|
|
467
|
+
|
|
468
|
+
expect(await res.json()).toEqual('two')
|
|
469
|
+
}
|
|
470
|
+
expect(oneOnReq).toBe(true)
|
|
471
|
+
expect(twoOnReq).toBe(true)
|
|
478
472
|
})
|
|
479
473
|
|
|
480
474
|
test('use with nested basPath works', async () => {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
475
|
+
const app = await new Spiceflow({ basePath: '/zero' })
|
|
476
|
+
.use(
|
|
477
|
+
new Spiceflow({ basePath: '/one' }).get(
|
|
478
|
+
'/ids/:id',
|
|
479
|
+
({ params }) => params.id,
|
|
480
|
+
),
|
|
481
|
+
)
|
|
482
|
+
.use(
|
|
483
|
+
new Spiceflow({ basePath: '/two' }).use(
|
|
484
|
+
new Spiceflow({ basePath: '/nested' }).get(
|
|
485
|
+
'/ids/:id',
|
|
486
|
+
({ params }) => params.id,
|
|
487
|
+
),
|
|
488
|
+
),
|
|
489
|
+
)
|
|
490
|
+
{
|
|
491
|
+
const res = await app.handle(
|
|
492
|
+
new Request('http://localhost/zero/one/ids/one'),
|
|
493
|
+
)
|
|
494
|
+
expect(res.status).toBe(200)
|
|
495
|
+
expect(await res.json()).toEqual('one')
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
{
|
|
499
|
+
const res = await app.handle(
|
|
500
|
+
new Request('http://localhost/zero/two/nested/ids/nested'),
|
|
501
|
+
)
|
|
502
|
+
expect(res.status).toBe(200)
|
|
503
|
+
expect(await res.json()).toEqual('nested')
|
|
504
|
+
}
|
|
511
505
|
})
|
|
512
506
|
|
|
513
507
|
test('errors inside basPath works', async () => {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
508
|
+
let onErrorTriggered = [] as string[]
|
|
509
|
+
let onReqTriggered = [] as string[]
|
|
510
|
+
let handlerCalledNTimes = 0
|
|
511
|
+
const app = await new Spiceflow({ basePath: '/zero' })
|
|
512
|
+
.onError(({ error }) => {
|
|
513
|
+
onErrorTriggered.push('root')
|
|
514
|
+
// return new Response('root', { status: 500 })
|
|
515
|
+
})
|
|
516
|
+
.use(({ request }) => {
|
|
517
|
+
onReqTriggered.push('root')
|
|
518
|
+
// return new Response('root', { status: 500 })
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
.use(
|
|
522
|
+
new Spiceflow({ basePath: '/two' })
|
|
523
|
+
.onError(({ error }) => {
|
|
524
|
+
onErrorTriggered.push('two')
|
|
525
|
+
// return new Response('two', { status: 500 })
|
|
526
|
+
})
|
|
527
|
+
.use(({ request }) => {
|
|
528
|
+
onReqTriggered.push('two')
|
|
529
|
+
// return new Response('two', { status: 500 })
|
|
530
|
+
})
|
|
531
|
+
.use(
|
|
532
|
+
new Spiceflow({ basePath: '/nested' })
|
|
533
|
+
.onError(({ error }) => {
|
|
534
|
+
onErrorTriggered.push('nested')
|
|
535
|
+
// return new Response('nested', { status: 500 })
|
|
536
|
+
})
|
|
537
|
+
.use(({ request }) => {
|
|
538
|
+
onReqTriggered.push('nested')
|
|
539
|
+
// return new Response('nested', { status: 500 })
|
|
540
|
+
})
|
|
541
|
+
.get('/ids/:id', ({ params }) => {
|
|
542
|
+
handlerCalledNTimes++
|
|
543
|
+
throw new Error('error message')
|
|
544
|
+
}),
|
|
545
|
+
),
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
{
|
|
549
|
+
const res = await app.handle(
|
|
550
|
+
new Request('http://localhost/zero/two/nested/ids/nested'),
|
|
551
|
+
)
|
|
552
|
+
expect(handlerCalledNTimes).toBe(1)
|
|
553
|
+
expect(onErrorTriggered).toEqual(['root', 'two', 'nested'])
|
|
554
|
+
expect(onReqTriggered).toEqual(['root', 'two', 'nested'])
|
|
555
|
+
expect(res.status).toBe(500)
|
|
556
|
+
expect(await res.text()).toBe('error message')
|
|
557
|
+
// expect(await res.json()).toEqual('nested'))
|
|
558
|
+
}
|
|
565
559
|
})
|