spiceflow 1.1.12 → 1.1.14
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/dist/client/errors.d.ts +1 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js +1 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts +0 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -24
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +8 -17
- package/dist/client/types.d.ts.map +1 -1
- package/dist/cors.test.js +42 -0
- package/dist/cors.test.js.map +1 -1
- package/dist/middleware.test.js +46 -0
- package/dist/middleware.test.js.map +1 -1
- package/dist/openapi.d.ts +1 -1
- package/dist/spiceflow.d.ts +10 -10
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +51 -47
- package/dist/spiceflow.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.js +13 -8
- package/dist/utils.js.map +1 -1
- package/package.json +3 -2
- package/src/client/errors.ts +1 -1
- package/src/client/index.ts +8 -27
- package/src/client/types.ts +73 -90
- package/src/cors.test.ts +48 -0
- package/src/middleware.test.ts +53 -0
- package/src/spiceflow.ts +70 -66
- package/src/types.ts +6 -6
- package/src/utils.ts +13 -13
- package/dist/client/ws.d.ts +0 -15
- package/dist/client/ws.d.ts.map +0 -1
- package/dist/client/ws.js +0 -49
- package/dist/client/ws.js.map +0 -1
- package/src/client/ws.ts +0 -97
package/src/client/types.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import type { Spiceflow } from '../spiceflow.js'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import { EdenFetchError } from './errors.js'
|
|
6
|
-
import { EdenWS } from './ws.js'
|
|
3
|
+
|
|
4
|
+
import { SpiceflowFetchError } from './errors.js'
|
|
7
5
|
|
|
8
6
|
export type Prettify<T> = {
|
|
9
7
|
[K in keyof T]: T[K]
|
|
@@ -36,22 +34,22 @@ type ReplaceGeneratorWithAsyncGenerator<
|
|
|
36
34
|
? And<Not<IsNever<A>>, void extends B ? true : false> extends true
|
|
37
35
|
? AsyncGenerator<A, B, C>
|
|
38
36
|
: And<IsNever<A>, void extends B ? false : true> extends true
|
|
39
|
-
|
|
40
|
-
|
|
37
|
+
? B
|
|
38
|
+
: AsyncGenerator<A, B, C> | B
|
|
41
39
|
: RecordType[K] extends AsyncGenerator<infer A, infer B, infer C>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
? And<Not<IsNever<A>>, void extends B ? true : false> extends true
|
|
41
|
+
? AsyncGenerator<A, B, C>
|
|
42
|
+
: And<IsNever<A>, void extends B ? false : true> extends true
|
|
43
|
+
? B
|
|
44
|
+
: AsyncGenerator<A, B, C> | B
|
|
45
|
+
: RecordType[K]
|
|
48
46
|
} & {}
|
|
49
47
|
|
|
50
48
|
type MaybeArray<T> = T | T[]
|
|
51
49
|
type MaybePromise<T> = T | Promise<T>
|
|
52
50
|
|
|
53
51
|
export namespace SpiceflowClient {
|
|
54
|
-
interface
|
|
52
|
+
interface ClientParam {
|
|
55
53
|
fetch?: RequestInit
|
|
56
54
|
}
|
|
57
55
|
|
|
@@ -60,91 +58,76 @@ export namespace SpiceflowClient {
|
|
|
60
58
|
_routes: infer Schema extends Record<string, any>
|
|
61
59
|
}
|
|
62
60
|
? Prettify<Sign<Schema>>
|
|
63
|
-
: 'Please install Spiceflow before using
|
|
61
|
+
: 'Please install Spiceflow before using the client'
|
|
64
62
|
|
|
65
63
|
export type Sign<in out Route extends Record<string, any>> = {
|
|
66
|
-
[K in keyof Route as K extends `:${string}`
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
: {
|
|
77
|
-
query: Route['subscribe']['query']
|
|
78
|
-
}) extends infer Param
|
|
64
|
+
[K in keyof Route as K extends `:${string}` ? never : K]: Route[K] extends {
|
|
65
|
+
body: infer Body
|
|
66
|
+
// headers: infer Headers
|
|
67
|
+
params: any
|
|
68
|
+
query: infer Query
|
|
69
|
+
response: infer Response extends Record<number, unknown>
|
|
70
|
+
}
|
|
71
|
+
? { headers?: Record<string, unknown> } & (undefined extends Query
|
|
72
|
+
? { query?: Record<string, unknown> }
|
|
73
|
+
: { query: Query }) extends infer Param
|
|
79
74
|
? {} extends Param
|
|
80
|
-
?
|
|
81
|
-
|
|
82
|
-
: never
|
|
83
|
-
: Route[K] extends {
|
|
84
|
-
body: infer Body
|
|
85
|
-
// headers: infer Headers
|
|
86
|
-
params: any
|
|
87
|
-
query: infer Query
|
|
88
|
-
response: infer Response extends Record<number, unknown>
|
|
89
|
-
}
|
|
90
|
-
? { headers?: Record<string, unknown> } & (undefined extends Query
|
|
91
|
-
? { query?: Record<string, unknown> }
|
|
92
|
-
: { query: Query }) extends infer Param
|
|
93
|
-
? {} extends Param
|
|
94
|
-
? undefined extends Body
|
|
95
|
-
? K extends 'get' | 'head'
|
|
96
|
-
? (
|
|
97
|
-
options?: Prettify<Param & TreatyParam>,
|
|
98
|
-
) => Promise<
|
|
99
|
-
TreatyResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
100
|
-
>
|
|
101
|
-
: (
|
|
102
|
-
body?: Body,
|
|
103
|
-
options?: Prettify<Param & TreatyParam>,
|
|
104
|
-
) => Promise<
|
|
105
|
-
TreatyResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
106
|
-
>
|
|
107
|
-
: (
|
|
108
|
-
body: Body extends Record<string, unknown>
|
|
109
|
-
? ReplaceBlobWithFiles<Body>
|
|
110
|
-
: Body,
|
|
111
|
-
options?: Prettify<Param & TreatyParam>,
|
|
112
|
-
) => Promise<
|
|
113
|
-
TreatyResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
114
|
-
>
|
|
115
|
-
: K extends 'get' | 'head'
|
|
75
|
+
? undefined extends Body
|
|
76
|
+
? K extends 'get' | 'head'
|
|
116
77
|
? (
|
|
117
|
-
options
|
|
78
|
+
options?: Prettify<Param & ClientParam>,
|
|
118
79
|
) => Promise<
|
|
119
|
-
|
|
80
|
+
ClientResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
120
81
|
>
|
|
121
82
|
: (
|
|
122
|
-
body
|
|
123
|
-
|
|
124
|
-
: Body,
|
|
125
|
-
options: Prettify<Param & TreatyParam>,
|
|
83
|
+
body?: Body,
|
|
84
|
+
options?: Prettify<Param & ClientParam>,
|
|
126
85
|
) => Promise<
|
|
127
|
-
|
|
86
|
+
ClientResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
128
87
|
>
|
|
129
|
-
|
|
130
|
-
|
|
88
|
+
: (
|
|
89
|
+
body: Body extends Record<string, unknown>
|
|
90
|
+
? ReplaceBlobWithFiles<Body>
|
|
91
|
+
: Body,
|
|
92
|
+
options?: Prettify<Param & ClientParam>,
|
|
93
|
+
) => Promise<
|
|
94
|
+
ClientResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
95
|
+
>
|
|
96
|
+
: K extends 'get' | 'head'
|
|
97
|
+
? (
|
|
98
|
+
options: Prettify<Param & ClientParam>,
|
|
99
|
+
) => Promise<
|
|
100
|
+
ClientResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
101
|
+
>
|
|
102
|
+
: (
|
|
103
|
+
body: Body extends Record<string, unknown>
|
|
104
|
+
? ReplaceBlobWithFiles<Body>
|
|
105
|
+
: Body,
|
|
106
|
+
options: Prettify<Param & ClientParam>,
|
|
107
|
+
) => Promise<
|
|
108
|
+
ClientResponse<ReplaceGeneratorWithAsyncGenerator<Response>>
|
|
109
|
+
>
|
|
110
|
+
: never
|
|
111
|
+
: CreateParams<Route[K]>
|
|
131
112
|
}
|
|
132
113
|
|
|
133
|
-
type CreateParams<Route extends Record<string, any>> =
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
114
|
+
type CreateParams<Route extends Record<string, any>> = Extract<
|
|
115
|
+
keyof Route,
|
|
116
|
+
`:${string}`
|
|
117
|
+
> extends infer Path extends string
|
|
118
|
+
? IsNever<Path> extends true
|
|
119
|
+
? Prettify<Sign<Route>>
|
|
120
|
+
: // ! DO NOT USE PRETTIFY ON THIS LINE, OTHERWISE FUNCTION CALLING WILL BE OMITTED
|
|
121
|
+
(((params: {
|
|
122
|
+
[param in Path extends `:${infer Param}`
|
|
123
|
+
? Param extends `${infer Param}?`
|
|
124
|
+
? Param
|
|
125
|
+
: Param
|
|
126
|
+
: never]: string | number
|
|
127
|
+
}) => Prettify<Sign<Route[Path]>> & CreateParams<Route[Path]>) &
|
|
128
|
+
Prettify<Sign<Route>>) &
|
|
129
|
+
(Path extends `:${string}?` ? CreateParams<Route[Path]> : {})
|
|
130
|
+
: never
|
|
148
131
|
|
|
149
132
|
export interface Config {
|
|
150
133
|
// fetch?: Omit<RequestInit, 'headers' | 'method'>
|
|
@@ -164,7 +147,7 @@ export namespace SpiceflowClient {
|
|
|
164
147
|
// [K in keyof T]: Awaited<T[K]>
|
|
165
148
|
// }
|
|
166
149
|
|
|
167
|
-
export type
|
|
150
|
+
export type ClientResponse<Res extends Record<number, unknown>> =
|
|
168
151
|
| {
|
|
169
152
|
data: Res[200]
|
|
170
153
|
error: null
|
|
@@ -175,9 +158,9 @@ export namespace SpiceflowClient {
|
|
|
175
158
|
| {
|
|
176
159
|
data: null
|
|
177
160
|
error: Exclude<keyof Res, 200> extends never
|
|
178
|
-
?
|
|
161
|
+
? SpiceflowFetchError<number, any>
|
|
179
162
|
: {
|
|
180
|
-
[Status in keyof Res]:
|
|
163
|
+
[Status in keyof Res]: SpiceflowFetchError<Status, Res[Status]>
|
|
181
164
|
}[Exclude<keyof Res, 200>]
|
|
182
165
|
response: Response
|
|
183
166
|
status: number
|
package/src/cors.test.ts
CHANGED
|
@@ -48,3 +48,51 @@ describe('cors middleware', () => {
|
|
|
48
48
|
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
49
49
|
})
|
|
50
50
|
})
|
|
51
|
+
|
|
52
|
+
test('CORS headers are set when an error is thrown', async () => {
|
|
53
|
+
let errorRouteCallCount = 0;
|
|
54
|
+
const errorApp = new Spiceflow().use(cors()).get('/error', () => {
|
|
55
|
+
errorRouteCallCount++;
|
|
56
|
+
throw new Error('Test error')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
const res = await errorApp.handle(request('error'))
|
|
60
|
+
expect(res.status).toBe(500)
|
|
61
|
+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
62
|
+
expect(await res.text()).toContain('Test error')
|
|
63
|
+
expect(errorRouteCallCount).toBe(1)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('CORS headers are set for OPTIONS request when an error is thrown', async () => {
|
|
67
|
+
let errorRouteCallCount = 0;
|
|
68
|
+
const errorApp = new Spiceflow().use(cors()).options('/error', () => {
|
|
69
|
+
errorRouteCallCount++;
|
|
70
|
+
throw new Error('Test error')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const res = await errorApp.handle(request('error', 'OPTIONS'))
|
|
74
|
+
expect(res.status).toBe(204)
|
|
75
|
+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
76
|
+
expect(res.headers.get('Access-Control-Allow-Methods')).toBe('GET,HEAD,PUT,POST,DELETE,PATCH')
|
|
77
|
+
expect(errorRouteCallCount).toBe(1)
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
// TODO should middleware errors be handled? errors can be a way to short circuit other middlewares
|
|
81
|
+
test('CORS headers are set when an error is thrown in middleware', async () => {
|
|
82
|
+
let errorRouteCallCount = 0;
|
|
83
|
+
const errorApp = new Spiceflow()
|
|
84
|
+
.use((c) => {
|
|
85
|
+
throw new Error('middleware error')
|
|
86
|
+
})
|
|
87
|
+
.use(cors())
|
|
88
|
+
.get('/error', () => {
|
|
89
|
+
errorRouteCallCount++;
|
|
90
|
+
throw new Error('Test error')
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const res = await errorApp.handle(request('error'))
|
|
94
|
+
expect(res.status).toBe(500)
|
|
95
|
+
expect(res.headers.get('Access-Control-Allow-Origin')).toBe('*')
|
|
96
|
+
expect(await res.text()).toContain('middleware error')
|
|
97
|
+
expect(errorRouteCallCount).toBe(0)
|
|
98
|
+
})
|
package/src/middleware.test.ts
CHANGED
|
@@ -276,3 +276,56 @@ test('mutating response returned by next without returning it works', async () =
|
|
|
276
276
|
expect(res.headers.get('X-Custom-Header')).toBe('Modified')
|
|
277
277
|
expect(res.headers.get('X-Another-Header')).toBe('Added')
|
|
278
278
|
})
|
|
279
|
+
|
|
280
|
+
test('middleware returning response and middleware adding header', async () => {
|
|
281
|
+
const res = await new Spiceflow()
|
|
282
|
+
.use(async (ctx, next) => {
|
|
283
|
+
// This middleware calls next() and adds a header
|
|
284
|
+
const response = await next()
|
|
285
|
+
if (response) {
|
|
286
|
+
response.headers.set('X-Added-Header', 'HeaderValue')
|
|
287
|
+
}
|
|
288
|
+
return response
|
|
289
|
+
})
|
|
290
|
+
.use(async (ctx, next) => {
|
|
291
|
+
// This middleware returns a response directly
|
|
292
|
+
return new Response('Response from first middleware', { status: 200 })
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
.get('/test', () => {
|
|
296
|
+
// This handler should not be called
|
|
297
|
+
return 'This should not be returned'
|
|
298
|
+
})
|
|
299
|
+
.handle(new Request('http://localhost/test'))
|
|
300
|
+
|
|
301
|
+
expect(res.status).toBe(200)
|
|
302
|
+
expect(await res.text()).toBe('Response from first middleware')
|
|
303
|
+
expect(res.headers.get('X-Added-Header')).toBe('HeaderValue')
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
test('middleware returning response and middleware adding header with mounted Spiceflow', async () => {
|
|
307
|
+
const res = await new Spiceflow()
|
|
308
|
+
.use(async (ctx, next) => {
|
|
309
|
+
// This middleware calls next() and adds a header
|
|
310
|
+
const response = await next()
|
|
311
|
+
if (response) {
|
|
312
|
+
response.headers.set('X-Added-Header', 'HeaderValue')
|
|
313
|
+
}
|
|
314
|
+
return response
|
|
315
|
+
})
|
|
316
|
+
.use(
|
|
317
|
+
new Spiceflow({ scoped: false }).use(async (ctx, next) => {
|
|
318
|
+
// This middleware returns a response directly
|
|
319
|
+
return new Response('Response from mounted Spiceflow', { status: 200 })
|
|
320
|
+
}),
|
|
321
|
+
)
|
|
322
|
+
.get('/test', () => {
|
|
323
|
+
// This handler should not be called
|
|
324
|
+
return 'This should not be returned'
|
|
325
|
+
})
|
|
326
|
+
.handle(new Request('http://localhost/test'))
|
|
327
|
+
|
|
328
|
+
expect(res.status).toBe(200)
|
|
329
|
+
expect(await res.text()).toBe('Response from mounted Spiceflow')
|
|
330
|
+
expect(res.headers.get('X-Added-Header')).toBe('HeaderValue')
|
|
331
|
+
})
|
package/src/spiceflow.ts
CHANGED
|
@@ -7,7 +7,7 @@ export { Type as t }
|
|
|
7
7
|
import addFormats from 'ajv-formats'
|
|
8
8
|
import {
|
|
9
9
|
ComposeSpiceflowResponse,
|
|
10
|
-
|
|
10
|
+
CreateClient,
|
|
11
11
|
DefinitionBase,
|
|
12
12
|
ErrorHandler,
|
|
13
13
|
HTTPMethod,
|
|
@@ -311,7 +311,7 @@ export class Spiceflow<
|
|
|
311
311
|
Definitions,
|
|
312
312
|
Metadata,
|
|
313
313
|
Routes &
|
|
314
|
-
|
|
314
|
+
CreateClient<
|
|
315
315
|
JoinPath<BasePath, Path>,
|
|
316
316
|
{
|
|
317
317
|
post: {
|
|
@@ -358,7 +358,7 @@ export class Spiceflow<
|
|
|
358
358
|
Definitions,
|
|
359
359
|
Metadata,
|
|
360
360
|
Routes &
|
|
361
|
-
|
|
361
|
+
CreateClient<
|
|
362
362
|
JoinPath<BasePath, Path>,
|
|
363
363
|
{
|
|
364
364
|
get: {
|
|
@@ -404,7 +404,7 @@ export class Spiceflow<
|
|
|
404
404
|
Definitions,
|
|
405
405
|
Metadata,
|
|
406
406
|
Routes &
|
|
407
|
-
|
|
407
|
+
CreateClient<
|
|
408
408
|
JoinPath<BasePath, Path>,
|
|
409
409
|
{
|
|
410
410
|
put: {
|
|
@@ -451,7 +451,7 @@ export class Spiceflow<
|
|
|
451
451
|
Definitions,
|
|
452
452
|
Metadata,
|
|
453
453
|
Routes &
|
|
454
|
-
|
|
454
|
+
CreateClient<
|
|
455
455
|
JoinPath<BasePath, Path>,
|
|
456
456
|
{
|
|
457
457
|
patch: {
|
|
@@ -498,7 +498,7 @@ export class Spiceflow<
|
|
|
498
498
|
Definitions,
|
|
499
499
|
Metadata,
|
|
500
500
|
Routes &
|
|
501
|
-
|
|
501
|
+
CreateClient<
|
|
502
502
|
JoinPath<BasePath, Path>,
|
|
503
503
|
{
|
|
504
504
|
delete: {
|
|
@@ -545,7 +545,7 @@ export class Spiceflow<
|
|
|
545
545
|
Definitions,
|
|
546
546
|
Metadata,
|
|
547
547
|
Routes &
|
|
548
|
-
|
|
548
|
+
CreateClient<
|
|
549
549
|
JoinPath<BasePath, Path>,
|
|
550
550
|
{
|
|
551
551
|
options: {
|
|
@@ -592,7 +592,7 @@ export class Spiceflow<
|
|
|
592
592
|
Definitions,
|
|
593
593
|
Metadata,
|
|
594
594
|
Routes &
|
|
595
|
-
|
|
595
|
+
CreateClient<
|
|
596
596
|
JoinPath<BasePath, Path>,
|
|
597
597
|
{
|
|
598
598
|
[method in string]: {
|
|
@@ -641,7 +641,7 @@ export class Spiceflow<
|
|
|
641
641
|
Definitions,
|
|
642
642
|
Metadata,
|
|
643
643
|
Routes &
|
|
644
|
-
|
|
644
|
+
CreateClient<
|
|
645
645
|
JoinPath<BasePath, Path>,
|
|
646
646
|
{
|
|
647
647
|
head: {
|
|
@@ -675,7 +675,7 @@ export class Spiceflow<
|
|
|
675
675
|
Metadata,
|
|
676
676
|
BasePath extends ``
|
|
677
677
|
? Routes & NewSpiceflow['_routes']
|
|
678
|
-
: Routes &
|
|
678
|
+
: Routes & CreateClient<BasePath, NewSpiceflow['_routes']>
|
|
679
679
|
>
|
|
680
680
|
use<const Schema extends RouteSchema>(
|
|
681
681
|
handler: MiddlewareHandler<
|
|
@@ -707,6 +707,7 @@ export class Spiceflow<
|
|
|
707
707
|
|
|
708
708
|
async handle(request: Request): Promise<Response> {
|
|
709
709
|
let u = new URL(request.url, 'http://localhost')
|
|
710
|
+
const self = this
|
|
710
711
|
let path = u.pathname + u.search
|
|
711
712
|
const defaultContext = {
|
|
712
713
|
redirect,
|
|
@@ -715,45 +716,60 @@ export class Spiceflow<
|
|
|
715
716
|
}
|
|
716
717
|
const root = this.topLevelApp || this
|
|
717
718
|
let onErrorHandlers: OnError[] = []
|
|
718
|
-
try {
|
|
719
|
-
// Get all middleware and method specific routes in order
|
|
720
|
-
|
|
721
|
-
const route = this.match(request.method, path)
|
|
722
|
-
|
|
723
|
-
const appsInScope = this.getAppsInScope(route.app)
|
|
724
|
-
onErrorHandlers = appsInScope.flatMap((x) => x.onErrorHandlers)
|
|
725
|
-
let {
|
|
726
|
-
params: _params,
|
|
727
|
-
app: { defaultState },
|
|
728
|
-
} = route
|
|
729
|
-
const middlewares = appsInScope.flatMap((x) => x.middlewares)
|
|
730
|
-
// console.log({ onReqHandlers })
|
|
731
|
-
let state = structuredClone(defaultState)
|
|
732
|
-
|
|
733
|
-
let content = route?.internalRoute?.hooks?.content
|
|
734
|
-
|
|
735
|
-
if (route.internalRoute?.validateBody) {
|
|
736
|
-
// TODO don't clone the request
|
|
737
|
-
let typedRequest =
|
|
738
|
-
request instanceof SpiceflowRequest
|
|
739
|
-
? request
|
|
740
|
-
: new SpiceflowRequest(u, request)
|
|
741
|
-
typedRequest.validateBody = route.internalRoute?.validateBody
|
|
742
|
-
request = typedRequest
|
|
743
|
-
}
|
|
744
719
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
720
|
+
const route = this.match(request.method, path)
|
|
721
|
+
|
|
722
|
+
const appsInScope = this.getAppsInScope(route.app)
|
|
723
|
+
onErrorHandlers = appsInScope.flatMap((x) => x.onErrorHandlers)
|
|
724
|
+
let {
|
|
725
|
+
params: _params,
|
|
726
|
+
app: { defaultState },
|
|
727
|
+
} = route
|
|
728
|
+
const middlewares = appsInScope.flatMap((x) => x.middlewares)
|
|
729
|
+
|
|
730
|
+
let state = structuredClone(defaultState)
|
|
731
|
+
|
|
732
|
+
let content = route?.internalRoute?.hooks?.content
|
|
733
|
+
|
|
734
|
+
if (route.internalRoute?.validateBody) {
|
|
735
|
+
// TODO don't clone the request
|
|
736
|
+
let typedRequest =
|
|
737
|
+
request instanceof SpiceflowRequest
|
|
738
|
+
? request
|
|
739
|
+
: new SpiceflowRequest(u, request)
|
|
740
|
+
typedRequest.validateBody = route.internalRoute?.validateBody
|
|
741
|
+
request = typedRequest
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
let index = 0
|
|
745
|
+
let context = {
|
|
746
|
+
...defaultContext,
|
|
747
|
+
request,
|
|
748
|
+
state,
|
|
749
|
+
path,
|
|
750
|
+
query: parseQuery.parse((u.search || '').slice(1)),
|
|
751
|
+
params: _params,
|
|
752
|
+
redirect,
|
|
753
|
+
} satisfies MiddlewareContext<any>
|
|
754
|
+
let handlerResponse: Response | undefined
|
|
755
|
+
async function getResForError(err: any) {
|
|
756
|
+
if (isResponse(err)) return err
|
|
757
|
+
let res = await self.runErrorHandlers({
|
|
758
|
+
onErrorHandlers,
|
|
759
|
+
error: err,
|
|
748
760
|
request,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
761
|
+
})
|
|
762
|
+
if (isResponse(res)) return res
|
|
763
|
+
|
|
764
|
+
let status = err?.status ?? 500
|
|
765
|
+
res ||= new Response(err?.message || 'Internal Server Error', {
|
|
766
|
+
status,
|
|
767
|
+
})
|
|
768
|
+
return res
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const next = async () => {
|
|
772
|
+
try {
|
|
757
773
|
if (index < middlewares.length) {
|
|
758
774
|
const middleware = middlewares[index]
|
|
759
775
|
index++
|
|
@@ -781,7 +797,7 @@ export class Spiceflow<
|
|
|
781
797
|
route.internalRoute?.validateParams,
|
|
782
798
|
)
|
|
783
799
|
|
|
784
|
-
const res = route.internalRoute?.handler(context)
|
|
800
|
+
const res = await route.internalRoute?.handler(context)
|
|
785
801
|
if (isAsyncIterable(res)) {
|
|
786
802
|
handlerResponse = await this.handleStream({
|
|
787
803
|
generator: res,
|
|
@@ -790,28 +806,16 @@ export class Spiceflow<
|
|
|
790
806
|
})
|
|
791
807
|
return handlerResponse
|
|
792
808
|
}
|
|
793
|
-
|
|
794
809
|
handlerResponse = await turnHandlerResultIntoResponse(res)
|
|
795
810
|
return handlerResponse
|
|
811
|
+
} catch (err) {
|
|
812
|
+
handlerResponse = await getResForError(err)
|
|
813
|
+
return await next()
|
|
796
814
|
}
|
|
797
|
-
const response = await next()
|
|
798
|
-
|
|
799
|
-
return response
|
|
800
|
-
} catch (err: any) {
|
|
801
|
-
if (isResponse(err)) return err
|
|
802
|
-
let res = await this.runErrorHandlers({
|
|
803
|
-
onErrorHandlers,
|
|
804
|
-
error: err,
|
|
805
|
-
request,
|
|
806
|
-
})
|
|
807
|
-
if (isResponse(res)) return res
|
|
808
|
-
|
|
809
|
-
let status = err?.status ?? 500
|
|
810
|
-
res ||= new Response(err?.message || 'Internal Server Error', {
|
|
811
|
-
status,
|
|
812
|
-
})
|
|
813
|
-
return res
|
|
814
815
|
}
|
|
816
|
+
const response = await next()
|
|
817
|
+
|
|
818
|
+
return response
|
|
815
819
|
}
|
|
816
820
|
|
|
817
821
|
private async runErrorHandlers({
|
package/src/types.ts
CHANGED
|
@@ -636,25 +636,25 @@ export type BaseMacro = Record<
|
|
|
636
636
|
>
|
|
637
637
|
export type BaseMacroFn = Record<string, (...a: any) => unknown>
|
|
638
638
|
|
|
639
|
-
type
|
|
639
|
+
type _CreateClient<
|
|
640
640
|
Path extends string,
|
|
641
641
|
Property extends Record<string, unknown> = {},
|
|
642
642
|
> = Path extends `${infer Start}/${infer Rest}`
|
|
643
643
|
? {
|
|
644
|
-
[x in Start]:
|
|
644
|
+
[x in Start]: _CreateClient<Rest, Property>
|
|
645
645
|
}
|
|
646
646
|
: {
|
|
647
647
|
[x in Path]: Property
|
|
648
648
|
}
|
|
649
649
|
|
|
650
|
-
export type
|
|
650
|
+
export type CreateClient<
|
|
651
651
|
Path extends string,
|
|
652
652
|
Property extends Record<string, unknown> = {},
|
|
653
653
|
> = Path extends `/${infer Rest}`
|
|
654
|
-
?
|
|
654
|
+
? _CreateClient<Rest, Property>
|
|
655
655
|
: Path extends ''
|
|
656
|
-
?
|
|
657
|
-
:
|
|
656
|
+
? _CreateClient<'index', Property>
|
|
657
|
+
: _CreateClient<Path, Property>
|
|
658
658
|
|
|
659
659
|
export type ComposeSpiceflowResponse<Response, Handle> = Handle extends (
|
|
660
660
|
...a: any[]
|
package/src/utils.ts
CHANGED
|
@@ -113,20 +113,20 @@ export function isResponse(result: any): result is Response {
|
|
|
113
113
|
if (result instanceof Response) {
|
|
114
114
|
return true
|
|
115
115
|
}
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
// if (
|
|
117
|
+
// result &&
|
|
118
|
+
// typeof result === 'object' &&
|
|
119
|
+
// 'status' in result &&
|
|
120
|
+
// 'headers' in result &&
|
|
121
|
+
// 'body' in result
|
|
122
|
+
// ) {
|
|
123
|
+
// console.warn(
|
|
124
|
+
// 'spiceflow WARNING: you returned a Response that does not satisfy instanceof Response, probably because of some dumb polyfill\n',
|
|
125
|
+
// result,
|
|
126
|
+
// )
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
}
|
|
128
|
+
// return true
|
|
129
|
+
// }
|
|
130
130
|
|
|
131
131
|
return false
|
|
132
132
|
}
|
package/dist/client/ws.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { InputSchema } from '../types.js';
|
|
2
|
-
import type { SpiceflowClient } from './types.js';
|
|
3
|
-
export declare class EdenWS<in out Schema extends InputSchema<any> = {}> {
|
|
4
|
-
url: string;
|
|
5
|
-
ws: WebSocket;
|
|
6
|
-
constructor(url: string);
|
|
7
|
-
send(data: Schema['body'] | Schema['body'][]): this;
|
|
8
|
-
on<K extends keyof WebSocketEventMap>(type: K, listener: (event: SpiceflowClient.WSEvent<K, Schema['response']>) => void, options?: boolean | AddEventListenerOptions): this;
|
|
9
|
-
off<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): this;
|
|
10
|
-
subscribe(onMessage: (event: SpiceflowClient.WSEvent<'message', Schema['response']>) => void, options?: boolean | AddEventListenerOptions): this;
|
|
11
|
-
addEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (event: SpiceflowClient.WSEvent<K, Schema['response']>) => void, options?: boolean | AddEventListenerOptions): this;
|
|
12
|
-
removeEventListener<K extends keyof WebSocketEventMap>(type: K, listener: (this: WebSocket, ev: WebSocketEventMap[K]) => any, options?: boolean | EventListenerOptions): this;
|
|
13
|
-
close(): this;
|
|
14
|
-
}
|
|
15
|
-
//# sourceMappingURL=ws.d.ts.map
|
package/dist/client/ws.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/client/ws.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAGjD,qBAAa,MAAM,CAAC,EAAE,CAAC,GAAG,CAAC,MAAM,SAAS,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE;IAG1C,GAAG,EAAE,MAAM;IAF9B,EAAE,EAAE,SAAS,CAAA;gBAEM,GAAG,EAAE,MAAM;IAI9B,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE;IAc5C,EAAE,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAClC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,EACzE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB;IAK7C,GAAG,CAAC,CAAC,SAAS,MAAM,iBAAiB,EACnC,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAC5D,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB;IAO1C,SAAS,CACP,SAAS,EAAE,CACT,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,KAC1D,IAAI,EACT,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB;IAK7C,gBAAgB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EAChD,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,CAAC,OAAO,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,KAAK,IAAI,EACzE,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB;IAoB7C,mBAAmB,CAAC,CAAC,SAAS,MAAM,iBAAiB,EACnD,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,GAAG,EAC5D,OAAO,CAAC,EAAE,OAAO,GAAG,oBAAoB;IAO1C,KAAK;CAKN"}
|