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.
Files changed (42) hide show
  1. package/README.md +148 -1
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/types.d.ts +2 -6
  4. package/dist/client/types.d.ts.map +1 -1
  5. package/dist/client.test.js +26 -22
  6. package/dist/client.test.js.map +1 -1
  7. package/dist/elysia-fork/context.d.ts +6 -22
  8. package/dist/elysia-fork/context.d.ts.map +1 -1
  9. package/dist/elysia-fork/error.d.ts +2 -226
  10. package/dist/elysia-fork/error.d.ts.map +1 -1
  11. package/dist/elysia-fork/error.js +13 -166
  12. package/dist/elysia-fork/error.js.map +1 -1
  13. package/dist/elysia-fork/types.d.ts +20 -30
  14. package/dist/elysia-fork/types.d.ts.map +1 -1
  15. package/dist/elysia-fork/types.js +1 -2
  16. package/dist/elysia-fork/types.js.map +1 -1
  17. package/dist/elysia-fork/utils.d.ts +1 -62
  18. package/dist/elysia-fork/utils.d.ts.map +1 -1
  19. package/dist/openapi.d.ts +67 -0
  20. package/dist/openapi.d.ts.map +1 -0
  21. package/dist/openapi.js +250 -0
  22. package/dist/openapi.js.map +1 -0
  23. package/dist/spiceflow.d.ts +35 -23
  24. package/dist/spiceflow.d.ts.map +1 -1
  25. package/dist/spiceflow.js +214 -111
  26. package/dist/spiceflow.js.map +1 -1
  27. package/dist/spiceflow.test.js +135 -31
  28. package/dist/spiceflow.test.js.map +1 -1
  29. package/dist/zod.test.d.ts +2 -0
  30. package/dist/zod.test.d.ts.map +1 -0
  31. package/dist/zod.test.js +61 -0
  32. package/dist/zod.test.js.map +1 -0
  33. package/package.json +7 -3
  34. package/src/client/types.ts +14 -19
  35. package/src/client.test.ts +34 -25
  36. package/src/elysia-fork/context.ts +19 -49
  37. package/src/elysia-fork/error.ts +6 -259
  38. package/src/elysia-fork/types.ts +115 -188
  39. package/src/openapi.ts +426 -0
  40. package/src/spiceflow.test.ts +188 -51
  41. package/src/spiceflow.ts +312 -183
  42. package/src/zod.test.ts +73 -0
@@ -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.text()).toBe(JSON.stringify('hi'))
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.text()).toBe(JSON.stringify('hi'))
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.text()).toBe(JSON.stringify('hi'))
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.body
53
- // console.log({ body })
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.text()).toBe(JSON.stringify({ name: 'John' }))
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
- ({ body }) => {
75
- // console.log({ body })
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
- ({ body, redirect, error }) => {
104
- // console.log({ body })
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(400)
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.text()).toBe(JSON.stringify('hi'))
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.text()).toBe(JSON.stringify('one'))
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.text()).toBe(JSON.stringify('two'))
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.text()).toBe(JSON.stringify('one'))
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.text()).toBe(JSON.stringify('nested'))
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(['nested', 'two', 'root'])
285
- expect(onReqTriggered).toEqual(['nested', 'two', 'root'])
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.text()).toBe(JSON.stringify('nested'))
425
+ // expect(await res.json()).toEqual('nested'))
289
426
  }
290
427
  })