spiceflow 1.0.1 → 1.0.3
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 +148 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +2 -6
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client.test.js +26 -22
- package/dist/client.test.js.map +1 -1
- package/dist/elysia-fork/context.d.ts +6 -22
- package/dist/elysia-fork/context.d.ts.map +1 -1
- package/dist/elysia-fork/error.d.ts +2 -226
- package/dist/elysia-fork/error.d.ts.map +1 -1
- package/dist/elysia-fork/error.js +13 -166
- package/dist/elysia-fork/error.js.map +1 -1
- package/dist/elysia-fork/types.d.ts +20 -30
- package/dist/elysia-fork/types.d.ts.map +1 -1
- package/dist/elysia-fork/types.js +1 -2
- package/dist/elysia-fork/types.js.map +1 -1
- package/dist/elysia-fork/utils.d.ts +1 -62
- package/dist/elysia-fork/utils.d.ts.map +1 -1
- package/dist/openapi.d.ts +67 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +250 -0
- package/dist/openapi.js.map +1 -0
- package/dist/spiceflow.d.ts +35 -23
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +214 -111
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js +135 -31
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/zod.test.d.ts +2 -0
- package/dist/zod.test.d.ts.map +1 -0
- package/dist/zod.test.js +61 -0
- package/dist/zod.test.js.map +1 -0
- package/package.json +7 -3
- package/src/client/types.ts +14 -19
- package/src/client.test.ts +34 -25
- package/src/elysia-fork/context.ts +19 -49
- package/src/elysia-fork/error.ts +6 -259
- package/src/elysia-fork/types.ts +115 -188
- package/src/openapi.ts +426 -0
- package/src/spiceflow.test.ts +188 -51
- package/src/spiceflow.ts +312 -183
- package/src/zod.test.ts +73 -0
package/src/spiceflow.test.ts
CHANGED
|
@@ -1,27 +1,109 @@
|
|
|
1
1
|
import { test, describe, expect } from 'vitest'
|
|
2
2
|
import { Type } from '@sinclair/typebox'
|
|
3
|
-
import { Spiceflow } from './spiceflow'
|
|
3
|
+
import { bfs, Spiceflow } from './spiceflow'
|
|
4
|
+
import { z } from 'zod'
|
|
4
5
|
|
|
5
6
|
test('works', async () => {
|
|
6
7
|
const res = await new Spiceflow()
|
|
7
8
|
.post('/xxx', () => 'hi')
|
|
8
9
|
.handle(new Request('http://localhost/xxx', { method: 'POST' }))
|
|
9
10
|
expect(res.status).toBe(200)
|
|
10
|
-
expect(await res.
|
|
11
|
+
expect(await res.json()).toEqual('hi')
|
|
11
12
|
})
|
|
12
13
|
test('dynamic route', async () => {
|
|
13
14
|
const res = await new Spiceflow()
|
|
14
15
|
.post('/ids/:id', () => 'hi')
|
|
15
16
|
.handle(new Request('http://localhost/ids/xxx', { method: 'POST' }))
|
|
16
17
|
expect(res.status).toBe(200)
|
|
17
|
-
expect(await res.
|
|
18
|
+
expect(await res.json()).toEqual('hi')
|
|
18
19
|
})
|
|
19
20
|
test('GET dynamic route', async () => {
|
|
20
21
|
const res = await new Spiceflow()
|
|
21
22
|
.get('/ids/:id', () => 'hi')
|
|
22
23
|
.handle(new Request('http://localhost/ids/xxx', { method: 'GET' }))
|
|
23
24
|
expect(res.status).toBe(200)
|
|
24
|
-
expect(await res.
|
|
25
|
+
expect(await res.json()).toEqual('hi')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('GET with query, untyped', async () => {
|
|
29
|
+
const res = await new Spiceflow()
|
|
30
|
+
.get('/query', ({ query }) => {
|
|
31
|
+
return query.id
|
|
32
|
+
})
|
|
33
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
34
|
+
expect(res.status).toBe(200)
|
|
35
|
+
expect(await res.json()).toEqual('hi')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('GET with query, zod, fails validation', async () => {
|
|
39
|
+
const res = await new Spiceflow()
|
|
40
|
+
.get(
|
|
41
|
+
'/query',
|
|
42
|
+
({ query }) => {
|
|
43
|
+
return query.id
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
query: z.object({
|
|
47
|
+
id: z.number(),
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
52
|
+
expect(res.status).toBe(422)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('GET with query and zod', async () => {
|
|
56
|
+
const res = await new Spiceflow()
|
|
57
|
+
.get(
|
|
58
|
+
'/query',
|
|
59
|
+
({ query }) => {
|
|
60
|
+
return query.id
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
void query.sdfsd
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
query: z.object({
|
|
66
|
+
id: z.string(),
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
71
|
+
expect(res.status).toBe(200)
|
|
72
|
+
expect(await res.json()).toEqual('hi')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('GET dynamic route, params are typed', async () => {
|
|
76
|
+
const res = await new Spiceflow()
|
|
77
|
+
.get('/ids/:id', ({ params }) => {
|
|
78
|
+
let id = params.id
|
|
79
|
+
// @ts-expect-error
|
|
80
|
+
params.sdfsd
|
|
81
|
+
return id
|
|
82
|
+
})
|
|
83
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
84
|
+
expect(res.status).toBe(200)
|
|
85
|
+
expect(await res.json()).toEqual('hi')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('GET dynamic route, params are typed with schema', async () => {
|
|
89
|
+
const res = await new Spiceflow()
|
|
90
|
+
.get(
|
|
91
|
+
'/ids/:id',
|
|
92
|
+
({ params }) => {
|
|
93
|
+
let id = params.id
|
|
94
|
+
// @ts-expect-error
|
|
95
|
+
params.sdfsd
|
|
96
|
+
return id
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
params: z.object({
|
|
100
|
+
id: z.string(),
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
105
|
+
expect(res.status).toBe(200)
|
|
106
|
+
expect(await res.json()).toEqual('hi')
|
|
25
107
|
})
|
|
26
108
|
|
|
27
109
|
test('missing route is not found', async () => {
|
|
@@ -48,22 +130,22 @@ test('body is parsed as json', async () => {
|
|
|
48
130
|
const res = await new Spiceflow()
|
|
49
131
|
.state('id', '')
|
|
50
132
|
|
|
51
|
-
.post('/post', (c) => {
|
|
52
|
-
body = c.
|
|
53
|
-
// console.log({
|
|
133
|
+
.post('/post', async (c) => {
|
|
134
|
+
body = await c.request.json()
|
|
135
|
+
// console.log({request})
|
|
54
136
|
return body
|
|
55
137
|
})
|
|
56
138
|
.handle(
|
|
57
139
|
new Request('http://localhost/post', {
|
|
58
140
|
method: 'POST',
|
|
59
141
|
headers: {
|
|
60
|
-
'content-type': 'application/json'
|
|
142
|
+
'content-type': 'application/json',
|
|
61
143
|
},
|
|
62
|
-
body: JSON.stringify({ name: 'John' })
|
|
63
|
-
})
|
|
144
|
+
body: JSON.stringify({ name: 'John' }),
|
|
145
|
+
}),
|
|
64
146
|
)
|
|
65
147
|
expect(res.status).toBe(200)
|
|
66
|
-
expect(await res.
|
|
148
|
+
expect(await res.json()).toEqual({ name: 'John' })
|
|
67
149
|
})
|
|
68
150
|
|
|
69
151
|
test('validate body works, request success', async () => {
|
|
@@ -71,25 +153,26 @@ test('validate body works, request success', async () => {
|
|
|
71
153
|
|
|
72
154
|
.post(
|
|
73
155
|
'/post',
|
|
74
|
-
({
|
|
75
|
-
// console.log({
|
|
156
|
+
async ({ request }) => {
|
|
157
|
+
// console.log({request})
|
|
158
|
+
let body = await request.json()
|
|
76
159
|
expect(body).toEqual({ name: 'John' })
|
|
77
160
|
return 'ok'
|
|
78
161
|
},
|
|
79
162
|
{
|
|
80
163
|
body: Type.Object({
|
|
81
|
-
name: Type.String()
|
|
82
|
-
})
|
|
83
|
-
}
|
|
164
|
+
name: Type.String(),
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
84
167
|
)
|
|
85
168
|
.handle(
|
|
86
169
|
new Request('http://localhost/post', {
|
|
87
170
|
method: 'POST',
|
|
88
171
|
headers: {
|
|
89
|
-
'content-type': 'application/json'
|
|
172
|
+
'content-type': 'application/json',
|
|
90
173
|
},
|
|
91
|
-
body: JSON.stringify({ name: 'John' })
|
|
92
|
-
})
|
|
174
|
+
body: JSON.stringify({ name: 'John' }),
|
|
175
|
+
}),
|
|
93
176
|
)
|
|
94
177
|
expect(res.status).toBe(200)
|
|
95
178
|
expect(await res.text()).toMatchInlineSnapshot(`""ok""`)
|
|
@@ -100,29 +183,30 @@ test('validate body works, request fails', async () => {
|
|
|
100
183
|
|
|
101
184
|
.post(
|
|
102
185
|
'/post',
|
|
103
|
-
({
|
|
104
|
-
// console.log({
|
|
186
|
+
async ({ request, redirect }) => {
|
|
187
|
+
// console.log({request})
|
|
188
|
+
let body = await request.json()
|
|
105
189
|
expect(body).toEqual({ name: 'John' })
|
|
106
190
|
},
|
|
107
191
|
{
|
|
108
192
|
body: Type.Object({
|
|
109
193
|
name: Type.String(),
|
|
110
|
-
requiredField: Type.String()
|
|
111
|
-
})
|
|
112
|
-
}
|
|
194
|
+
requiredField: Type.String(),
|
|
195
|
+
}),
|
|
196
|
+
},
|
|
113
197
|
)
|
|
114
198
|
.handle(
|
|
115
199
|
new Request('http://localhost/post', {
|
|
116
200
|
method: 'POST',
|
|
117
201
|
headers: {
|
|
118
|
-
'content-type': 'application/json'
|
|
202
|
+
'content-type': 'application/json',
|
|
119
203
|
},
|
|
120
|
-
body: JSON.stringify({ name: 'John' })
|
|
121
|
-
})
|
|
204
|
+
body: JSON.stringify({ name: 'John' }),
|
|
205
|
+
}),
|
|
122
206
|
)
|
|
123
|
-
expect(res.status).toBe(
|
|
207
|
+
expect(res.status).toBe(422)
|
|
124
208
|
expect(await res.text()).toMatchInlineSnapshot(
|
|
125
|
-
`"data must have required property 'requiredField'"
|
|
209
|
+
`"data must have required property 'requiredField'"`,
|
|
126
210
|
)
|
|
127
211
|
})
|
|
128
212
|
|
|
@@ -163,43 +247,96 @@ test('basPath works', async () => {
|
|
|
163
247
|
.get('/ids/:id', () => 'hi')
|
|
164
248
|
.handle(new Request('http://localhost/one/ids/xxx', { method: 'GET' }))
|
|
165
249
|
expect(res.status).toBe(200)
|
|
166
|
-
expect(await res.
|
|
250
|
+
expect(await res.json()).toEqual('hi')
|
|
167
251
|
})
|
|
168
252
|
|
|
253
|
+
test('basPath works with use', async () => {
|
|
254
|
+
let app = new Spiceflow({ basePath: '/one' }).use(
|
|
255
|
+
new Spiceflow({})
|
|
256
|
+
.get('/two', () => 'hi')
|
|
257
|
+
.use(
|
|
258
|
+
new Spiceflow({ basePath: '/three' }).get('/four', () => 'hi'),
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
{
|
|
262
|
+
const res = await app.handle(
|
|
263
|
+
new Request('http://localhost/one/two', { method: 'GET' }),
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
expect(res.status).toBe(200)
|
|
267
|
+
expect(await res.json()).toEqual('hi')
|
|
268
|
+
}
|
|
269
|
+
{
|
|
270
|
+
const res = await app.handle(
|
|
271
|
+
new Request('http://localhost/one/three/four', { method: 'GET' }),
|
|
272
|
+
)
|
|
273
|
+
expect(res.status).toBe(200)
|
|
274
|
+
expect(await res.json()).toEqual('hi')
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
test('getRouteAndParents', async () => {
|
|
279
|
+
let app = new Spiceflow({ basePath: '/one' })
|
|
280
|
+
.get('/ids/:id', () => 'hi')
|
|
281
|
+
.use(
|
|
282
|
+
new Spiceflow({ basePath: '/two' }).use(
|
|
283
|
+
new Spiceflow({ basePath: '/three' }).get('/four', () => 'hi'),
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
let routers = bfs(app['routerTree'])
|
|
288
|
+
let last = routers[routers.length - 1]
|
|
289
|
+
|
|
290
|
+
expect(app['getRouteAndParents'](last).map((x) => x.prefix))
|
|
291
|
+
.toMatchInlineSnapshot(`
|
|
292
|
+
[
|
|
293
|
+
"/three",
|
|
294
|
+
"/two",
|
|
295
|
+
"/one",
|
|
296
|
+
]
|
|
297
|
+
`)
|
|
298
|
+
})
|
|
169
299
|
test('use with 2 basPath works', async () => {
|
|
170
300
|
let oneOnReq = false
|
|
171
301
|
let twoOnReq = false
|
|
302
|
+
let onReqCalled: string[] = []
|
|
172
303
|
const app = await new Spiceflow()
|
|
304
|
+
.onRequest(({ request }) => {
|
|
305
|
+
onReqCalled.push('root')
|
|
306
|
+
})
|
|
173
307
|
.use(
|
|
174
308
|
new Spiceflow({ basePath: '/one' })
|
|
175
309
|
.onRequest(({ request }) => {
|
|
176
310
|
oneOnReq = true
|
|
311
|
+
onReqCalled.push('one')
|
|
177
312
|
})
|
|
178
|
-
.get('/ids/:id', ({ params }) => params.id)
|
|
313
|
+
.get('/ids/:id', ({ params }) => params.id),
|
|
179
314
|
)
|
|
180
315
|
.use(
|
|
181
316
|
new Spiceflow({ basePath: '/two' })
|
|
182
317
|
.onRequest((c) => {
|
|
183
318
|
twoOnReq = true
|
|
319
|
+
onReqCalled.push('two')
|
|
184
320
|
})
|
|
185
|
-
.get('/ids/:id', ({ params }) => params.id)
|
|
321
|
+
.get('/ids/:id', ({ params }) => params.id),
|
|
186
322
|
)
|
|
187
323
|
|
|
188
324
|
{
|
|
189
325
|
const res = await app.handle(
|
|
190
|
-
new Request('http://localhost/one/ids/one')
|
|
326
|
+
new Request('http://localhost/one/ids/one'),
|
|
191
327
|
)
|
|
192
328
|
expect(res.status).toBe(200)
|
|
193
329
|
|
|
194
|
-
expect(await res.
|
|
330
|
+
expect(await res.json()).toEqual('one')
|
|
195
331
|
}
|
|
332
|
+
expect(onReqCalled).toEqual(['root', 'one'])
|
|
196
333
|
{
|
|
197
334
|
const res = await app.handle(
|
|
198
|
-
new Request('http://localhost/two/ids/two')
|
|
335
|
+
new Request('http://localhost/two/ids/two'),
|
|
199
336
|
)
|
|
200
337
|
expect(res.status).toBe(200)
|
|
201
338
|
|
|
202
|
-
expect(await res.
|
|
339
|
+
expect(await res.json()).toEqual('two')
|
|
203
340
|
}
|
|
204
341
|
expect(oneOnReq).toBe(true)
|
|
205
342
|
expect(twoOnReq).toBe(true)
|
|
@@ -210,31 +347,31 @@ test('use with nested basPath works', async () => {
|
|
|
210
347
|
.use(
|
|
211
348
|
new Spiceflow({ basePath: '/one' }).get(
|
|
212
349
|
'/ids/:id',
|
|
213
|
-
({ params }) => params.id
|
|
214
|
-
)
|
|
350
|
+
({ params }) => params.id,
|
|
351
|
+
),
|
|
215
352
|
)
|
|
216
353
|
.use(
|
|
217
354
|
new Spiceflow({ basePath: '/two' }).use(
|
|
218
355
|
new Spiceflow({ basePath: '/nested' }).get(
|
|
219
356
|
'/ids/:id',
|
|
220
|
-
({ params }) => params.id
|
|
221
|
-
)
|
|
222
|
-
)
|
|
357
|
+
({ params }) => params.id,
|
|
358
|
+
),
|
|
359
|
+
),
|
|
223
360
|
)
|
|
224
361
|
{
|
|
225
362
|
const res = await app.handle(
|
|
226
|
-
new Request('http://localhost/zero/one/ids/one')
|
|
363
|
+
new Request('http://localhost/zero/one/ids/one'),
|
|
227
364
|
)
|
|
228
365
|
expect(res.status).toBe(200)
|
|
229
|
-
expect(await res.
|
|
366
|
+
expect(await res.json()).toEqual('one')
|
|
230
367
|
}
|
|
231
368
|
|
|
232
369
|
{
|
|
233
370
|
const res = await app.handle(
|
|
234
|
-
new Request('http://localhost/zero/two/nested/ids/nested')
|
|
371
|
+
new Request('http://localhost/zero/two/nested/ids/nested'),
|
|
235
372
|
)
|
|
236
373
|
expect(res.status).toBe(200)
|
|
237
|
-
expect(await res.
|
|
374
|
+
expect(await res.json()).toEqual('nested')
|
|
238
375
|
}
|
|
239
376
|
})
|
|
240
377
|
|
|
@@ -273,18 +410,18 @@ test('errors inside basPath works', async () => {
|
|
|
273
410
|
})
|
|
274
411
|
.get('/ids/:id', ({ params }) => {
|
|
275
412
|
throw new Error('error message')
|
|
276
|
-
})
|
|
277
|
-
)
|
|
413
|
+
}),
|
|
414
|
+
),
|
|
278
415
|
)
|
|
279
416
|
|
|
280
417
|
{
|
|
281
418
|
const res = await app.handle(
|
|
282
|
-
new Request('http://localhost/zero/two/nested/ids/nested')
|
|
419
|
+
new Request('http://localhost/zero/two/nested/ids/nested'),
|
|
283
420
|
)
|
|
284
|
-
expect(onErrorTriggered).toEqual(['
|
|
285
|
-
expect(onReqTriggered).toEqual(['
|
|
421
|
+
expect(onErrorTriggered).toEqual(['root', 'two', 'nested'])
|
|
422
|
+
expect(onReqTriggered).toEqual(['root', 'two', 'nested'])
|
|
286
423
|
expect(res.status).toBe(500)
|
|
287
424
|
expect(await res.text()).toBe('error message')
|
|
288
|
-
// expect(await res.
|
|
425
|
+
// expect(await res.json()).toEqual('nested'))
|
|
289
426
|
}
|
|
290
427
|
})
|