spiceflow 1.1.8 → 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.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 +4 -2
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/stream.test.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.test.js +2 -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 +201 -201
- 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 -1161
- package/src/stream.test.ts +310 -310
- package/src/types.test.ts +46 -50
- package/src/types.ts +698 -701
- package/src/utils.ts +79 -79
- package/src/zod.test.ts +64 -64
package/src/client/index.ts
CHANGED
|
@@ -13,518 +13,486 @@ import { EdenFetchError } from './errors.js'
|
|
|
13
13
|
import { parseStringifiedValue } from './utils.js'
|
|
14
14
|
|
|
15
15
|
const method = [
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
'get',
|
|
17
|
+
'post',
|
|
18
|
+
'put',
|
|
19
|
+
'delete',
|
|
20
|
+
'patch',
|
|
21
|
+
'options',
|
|
22
|
+
'head',
|
|
23
|
+
'connect',
|
|
24
|
+
'subscribe',
|
|
25
25
|
] as const
|
|
26
26
|
|
|
27
27
|
const isServer = typeof FileList === 'undefined'
|
|
28
28
|
|
|
29
29
|
const isFile = (v: any) => {
|
|
30
|
-
|
|
30
|
+
if (isServer) return v instanceof Blob
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
return v instanceof FileList || v instanceof File
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// FormData is 1 level deep
|
|
36
36
|
const hasFile = (obj: Record<string, any>) => {
|
|
37
|
-
|
|
37
|
+
if (!obj) return false
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
for (const key in obj) {
|
|
40
|
+
if (isFile(obj[key])) return true
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
if (Array.isArray(obj[key]) && (obj[key] as unknown[]).find(isFile))
|
|
43
|
+
return true
|
|
44
|
+
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
return false
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const createNewFile = (v: File) =>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
50
|
+
isServer
|
|
51
|
+
? v
|
|
52
|
+
: new Promise<File>((resolve) => {
|
|
53
|
+
const reader = new FileReader()
|
|
54
|
+
|
|
55
|
+
reader.onload = () => {
|
|
56
|
+
const file = new File([reader.result!], v.name, {
|
|
57
|
+
lastModified: v.lastModified,
|
|
58
|
+
type: v.type,
|
|
59
|
+
})
|
|
60
|
+
resolve(file)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
reader.readAsArrayBuffer(v)
|
|
64
|
+
})
|
|
65
65
|
|
|
66
66
|
const processHeaders = (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
h: SpiceflowClient.Config['headers'],
|
|
68
|
+
path: string,
|
|
69
|
+
options: RequestInit = {},
|
|
70
|
+
headers: Record<string, string> = {},
|
|
71
71
|
): Record<string, string> => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
72
|
+
if (Array.isArray(h)) {
|
|
73
|
+
for (const value of h)
|
|
74
|
+
if (!Array.isArray(value))
|
|
75
|
+
headers = processHeaders(value, path, options, headers)
|
|
76
|
+
else {
|
|
77
|
+
const key = value[0]
|
|
78
|
+
if (typeof key === 'string')
|
|
79
|
+
headers[key.toLowerCase()] = value[1] as string
|
|
80
|
+
else
|
|
81
|
+
for (const [k, value] of key)
|
|
82
|
+
headers[k.toLowerCase()] = value as string
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return headers
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!h) return headers
|
|
89
|
+
|
|
90
|
+
switch (typeof h) {
|
|
91
|
+
case 'function':
|
|
92
|
+
if (typeof Headers !== 'undefined' && h instanceof Headers)
|
|
93
|
+
return processHeaders(h, path, options, headers)
|
|
94
|
+
|
|
95
|
+
const v = h(path, options)
|
|
96
|
+
if (v) return processHeaders(v, path, options, headers)
|
|
97
|
+
return headers
|
|
98
|
+
|
|
99
|
+
case 'object':
|
|
100
|
+
if (typeof Headers !== 'undefined' && h instanceof Headers) {
|
|
101
|
+
h.forEach((value, key) => {
|
|
102
|
+
headers[key.toLowerCase()] = value
|
|
103
|
+
})
|
|
104
|
+
return headers
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const [key, value] of Object.entries(h))
|
|
108
|
+
headers[key.toLowerCase()] = value as string
|
|
109
|
+
|
|
110
|
+
return headers
|
|
111
|
+
|
|
112
|
+
default:
|
|
113
|
+
return headers
|
|
114
|
+
}
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
interface SSEEvent {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
event: string
|
|
119
|
+
data: any
|
|
120
|
+
id?: string
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
export class TextDecoderStream extends TransformStream<Uint8Array, string> {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
124
|
+
constructor() {
|
|
125
|
+
const decoder = new TextDecoder('utf-8', {
|
|
126
|
+
fatal: true,
|
|
127
|
+
ignoreBOM: true,
|
|
128
|
+
})
|
|
129
|
+
super({
|
|
130
|
+
transform(
|
|
131
|
+
chunk: Uint8Array,
|
|
132
|
+
controller: TransformStreamDefaultController<string>,
|
|
133
|
+
) {
|
|
134
|
+
const decoded = decoder.decode(chunk, { stream: true })
|
|
135
|
+
if (decoded.length > 0) {
|
|
136
|
+
controller.enqueue(decoded)
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
flush(controller: TransformStreamDefaultController<string>) {
|
|
140
|
+
const output = decoder.decode()
|
|
141
|
+
if (output.length > 0) {
|
|
142
|
+
controller.enqueue(output)
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
export async function* streamSSEResponse(
|
|
150
|
-
|
|
150
|
+
response: Response,
|
|
151
151
|
): AsyncGenerator<SSEEvent> {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
152
|
+
const body = response.body
|
|
153
|
+
if (!body) return
|
|
154
|
+
|
|
155
|
+
const eventStream = response.body
|
|
156
|
+
.pipeThrough(new TextDecoderStream())
|
|
157
|
+
.pipeThrough(new EventSourceParserStream())
|
|
158
|
+
|
|
159
|
+
let reader = eventStream.getReader()
|
|
160
|
+
while (true) {
|
|
161
|
+
const { done, value: event } = await reader.read()
|
|
162
|
+
if (done) break
|
|
163
|
+
if (event?.event === 'error') {
|
|
164
|
+
throw new EdenFetchError(500, event.data)
|
|
165
|
+
}
|
|
166
|
+
if (event) {
|
|
167
|
+
yield tryParsingJson(event.data)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
function tryParsingJson(data: string): any {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
try {
|
|
174
|
+
return JSON.parse(data)
|
|
175
|
+
} catch (error) {
|
|
176
|
+
return null
|
|
177
|
+
}
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
const createProxy = (
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
181
|
+
domain: string,
|
|
182
|
+
config: SpiceflowClient.Config,
|
|
183
|
+
paths: string[] = [],
|
|
184
|
+
instance?: Spiceflow<any, any, any, any, any, any>,
|
|
185
185
|
): any =>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
default:
|
|
478
|
-
data = await response
|
|
479
|
-
.text()
|
|
480
|
-
.then(parseStringifiedValue)
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
if (response.status >= 300 || response.status < 200) {
|
|
484
|
-
error = new EdenFetchError(response.status, data)
|
|
485
|
-
data = null
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
return {
|
|
489
|
-
data,
|
|
490
|
-
error,
|
|
491
|
-
response,
|
|
492
|
-
status: response.status,
|
|
493
|
-
headers: response.headers,
|
|
494
|
-
}
|
|
495
|
-
})()
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
if (typeof body === 'object')
|
|
499
|
-
return createProxy(
|
|
500
|
-
domain,
|
|
501
|
-
config,
|
|
502
|
-
[...paths, Object.values(body)[0] as string],
|
|
503
|
-
instance,
|
|
504
|
-
)
|
|
505
|
-
|
|
506
|
-
return createProxy(domain, config, paths)
|
|
507
|
-
},
|
|
508
|
-
}) as any
|
|
186
|
+
new Proxy(() => {}, {
|
|
187
|
+
get(_, param: string): any {
|
|
188
|
+
return createProxy(
|
|
189
|
+
domain,
|
|
190
|
+
config,
|
|
191
|
+
param === 'index' ? paths : [...paths, param],
|
|
192
|
+
instance,
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
apply(_, __, [body, options]) {
|
|
196
|
+
if (
|
|
197
|
+
!body ||
|
|
198
|
+
options ||
|
|
199
|
+
(typeof body === 'object' && Object.keys(body).length !== 1) ||
|
|
200
|
+
method.includes(paths.at(-1) as any)
|
|
201
|
+
) {
|
|
202
|
+
const methodPaths = [...paths]
|
|
203
|
+
const method = methodPaths.pop()
|
|
204
|
+
const path = '/' + methodPaths.join('/')
|
|
205
|
+
|
|
206
|
+
let { fetch: fetcher = fetch, headers, onRequest, onResponse } = config
|
|
207
|
+
|
|
208
|
+
const isGetOrHead =
|
|
209
|
+
method === 'get' || method === 'head' || method === 'subscribe'
|
|
210
|
+
|
|
211
|
+
headers = processHeaders(headers, path, options)
|
|
212
|
+
|
|
213
|
+
const query = isGetOrHead
|
|
214
|
+
? (body as Record<string, string | string[] | undefined>)?.query
|
|
215
|
+
: options?.query
|
|
216
|
+
|
|
217
|
+
let q = ''
|
|
218
|
+
if (query) {
|
|
219
|
+
const append = (key: string, value: string) => {
|
|
220
|
+
q +=
|
|
221
|
+
(q ? '&' : '?') +
|
|
222
|
+
`${encodeURIComponent(key)}=${encodeURIComponent(value)}`
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
for (const [key, value] of Object.entries(query)) {
|
|
226
|
+
if (Array.isArray(value)) {
|
|
227
|
+
for (const v of value) append(key, v)
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (typeof value === 'object') {
|
|
232
|
+
append(key, JSON.stringify(value))
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
append(key, `${value}`)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// if (method === 'subscribe') {
|
|
241
|
+
// const url =
|
|
242
|
+
// domain.replace(
|
|
243
|
+
// /^([^]+):\/\//,
|
|
244
|
+
// domain.startsWith('https://')
|
|
245
|
+
// ? 'wss://'
|
|
246
|
+
// : domain.startsWith('http://')
|
|
247
|
+
// ? 'ws://'
|
|
248
|
+
// : locals.find((v) =>
|
|
249
|
+
// (domain as string).includes(v)
|
|
250
|
+
// )
|
|
251
|
+
// ? 'ws://'
|
|
252
|
+
// : 'wss://'
|
|
253
|
+
// ) +
|
|
254
|
+
// path +
|
|
255
|
+
// q
|
|
256
|
+
|
|
257
|
+
// return new EdenWS(url)
|
|
258
|
+
// }
|
|
259
|
+
|
|
260
|
+
return (async () => {
|
|
261
|
+
let fetchInit = {
|
|
262
|
+
method: method?.toUpperCase(),
|
|
263
|
+
body,
|
|
264
|
+
// ...conf,
|
|
265
|
+
headers,
|
|
266
|
+
} satisfies RequestInit
|
|
267
|
+
|
|
268
|
+
fetchInit.headers = {
|
|
269
|
+
...headers,
|
|
270
|
+
...processHeaders(
|
|
271
|
+
// For GET and HEAD, options is moved to body (1st param)
|
|
272
|
+
isGetOrHead ? body?.headers : options?.headers,
|
|
273
|
+
path,
|
|
274
|
+
fetchInit,
|
|
275
|
+
),
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const fetchOpts =
|
|
279
|
+
isGetOrHead && typeof body === 'object'
|
|
280
|
+
? body.fetch
|
|
281
|
+
: options?.fetch
|
|
282
|
+
|
|
283
|
+
fetchInit = {
|
|
284
|
+
...fetchInit,
|
|
285
|
+
...fetchOpts,
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (isGetOrHead) delete fetchInit.body
|
|
289
|
+
|
|
290
|
+
if (onRequest) {
|
|
291
|
+
if (!Array.isArray(onRequest)) onRequest = [onRequest]
|
|
292
|
+
|
|
293
|
+
for (const value of onRequest) {
|
|
294
|
+
const temp = await value(path, fetchInit)
|
|
295
|
+
|
|
296
|
+
if (typeof temp === 'object')
|
|
297
|
+
fetchInit = {
|
|
298
|
+
...fetchInit,
|
|
299
|
+
...temp,
|
|
300
|
+
headers: {
|
|
301
|
+
...fetchInit.headers,
|
|
302
|
+
...processHeaders(temp.headers, path, fetchInit),
|
|
303
|
+
},
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ? Duplicate because end-user might add a body in onRequest
|
|
309
|
+
if (isGetOrHead) delete fetchInit.body
|
|
310
|
+
|
|
311
|
+
if (hasFile(body)) {
|
|
312
|
+
const formData = new FormData()
|
|
313
|
+
|
|
314
|
+
// FormData is 1 level deep
|
|
315
|
+
for (const [key, field] of Object.entries(fetchInit.body)) {
|
|
316
|
+
if (isServer) {
|
|
317
|
+
formData.append(key, field as any)
|
|
318
|
+
|
|
319
|
+
continue
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (field instanceof File) {
|
|
323
|
+
formData.append(key, await createNewFile(field as any))
|
|
324
|
+
|
|
325
|
+
continue
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (field instanceof FileList) {
|
|
329
|
+
for (let i = 0; i < field.length; i++)
|
|
330
|
+
formData.append(
|
|
331
|
+
key as any,
|
|
332
|
+
await createNewFile((field as any)[i]),
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
continue
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
if (Array.isArray(field)) {
|
|
339
|
+
for (let i = 0; i < field.length; i++) {
|
|
340
|
+
const value = (field as any)[i]
|
|
341
|
+
|
|
342
|
+
formData.append(
|
|
343
|
+
key as any,
|
|
344
|
+
value instanceof File ? await createNewFile(value) : value,
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
continue
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
formData.append(key, field as string)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// We don't do this because we need to let the browser set the content type with the correct boundary
|
|
355
|
+
// fetchInit.headers['content-type'] = 'multipart/form-data'
|
|
356
|
+
fetchInit.body = formData
|
|
357
|
+
} else if (typeof body === 'object') {
|
|
358
|
+
;(fetchInit.headers as Record<string, string>)['content-type'] =
|
|
359
|
+
'application/json'
|
|
360
|
+
|
|
361
|
+
fetchInit.body = JSON.stringify(body)
|
|
362
|
+
} else if (body !== undefined && body !== null) {
|
|
363
|
+
;(fetchInit.headers as Record<string, string>)['content-type'] =
|
|
364
|
+
'text/plain'
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (isGetOrHead) delete fetchInit.body
|
|
368
|
+
|
|
369
|
+
if (onRequest) {
|
|
370
|
+
if (!Array.isArray(onRequest)) onRequest = [onRequest]
|
|
371
|
+
|
|
372
|
+
for (const value of onRequest) {
|
|
373
|
+
const temp = await value(path, fetchInit)
|
|
374
|
+
|
|
375
|
+
if (typeof temp === 'object')
|
|
376
|
+
fetchInit = {
|
|
377
|
+
...fetchInit,
|
|
378
|
+
...temp,
|
|
379
|
+
headers: {
|
|
380
|
+
...fetchInit.headers,
|
|
381
|
+
...processHeaders(temp.headers, path, fetchInit),
|
|
382
|
+
} as Record<string, string>,
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const url = domain + path + q
|
|
388
|
+
const response = await (instance?.handle(
|
|
389
|
+
new Request(url, fetchInit),
|
|
390
|
+
) ?? fetcher!(url, fetchInit))
|
|
391
|
+
|
|
392
|
+
let data = null as any
|
|
393
|
+
let error = null as any
|
|
394
|
+
|
|
395
|
+
if (onResponse) {
|
|
396
|
+
if (!Array.isArray(onResponse)) onResponse = [onResponse]
|
|
397
|
+
|
|
398
|
+
for (const value of onResponse)
|
|
399
|
+
try {
|
|
400
|
+
const temp = await value(response.clone())
|
|
401
|
+
|
|
402
|
+
if (temp !== undefined && temp !== null) {
|
|
403
|
+
data = temp
|
|
404
|
+
break
|
|
405
|
+
}
|
|
406
|
+
} catch (err) {
|
|
407
|
+
if (err instanceof EdenFetchError) error = err
|
|
408
|
+
else error = new EdenFetchError(422, err)
|
|
409
|
+
|
|
410
|
+
break
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (data !== null) {
|
|
415
|
+
return {
|
|
416
|
+
data,
|
|
417
|
+
error,
|
|
418
|
+
response,
|
|
419
|
+
status: response.status,
|
|
420
|
+
headers: response.headers,
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
switch (response.headers.get('Content-Type')?.split(';')[0]) {
|
|
425
|
+
case 'text/event-stream':
|
|
426
|
+
data = streamSSEResponse(response)
|
|
427
|
+
break
|
|
428
|
+
|
|
429
|
+
case 'application/json':
|
|
430
|
+
data = await response.json()
|
|
431
|
+
break
|
|
432
|
+
case 'application/octet-stream':
|
|
433
|
+
data = await response.arrayBuffer()
|
|
434
|
+
break
|
|
435
|
+
|
|
436
|
+
case 'multipart/form-data':
|
|
437
|
+
const temp = await response.formData()
|
|
438
|
+
|
|
439
|
+
data = {}
|
|
440
|
+
temp.forEach((value, key) => {
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
data[key] = value
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
break
|
|
446
|
+
|
|
447
|
+
default:
|
|
448
|
+
data = await response.text().then(parseStringifiedValue)
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (response.status >= 300 || response.status < 200) {
|
|
452
|
+
error = new EdenFetchError(response.status, data)
|
|
453
|
+
data = null
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
return {
|
|
457
|
+
data,
|
|
458
|
+
error,
|
|
459
|
+
response,
|
|
460
|
+
status: response.status,
|
|
461
|
+
headers: response.headers,
|
|
462
|
+
}
|
|
463
|
+
})()
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (typeof body === 'object')
|
|
467
|
+
return createProxy(
|
|
468
|
+
domain,
|
|
469
|
+
config,
|
|
470
|
+
[...paths, Object.values(body)[0] as string],
|
|
471
|
+
instance,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
return createProxy(domain, config, paths)
|
|
475
|
+
},
|
|
476
|
+
}) as any
|
|
509
477
|
|
|
510
478
|
export const createSpiceflowClient = <
|
|
511
|
-
|
|
479
|
+
const App extends Spiceflow<any, any, any, any, any, any>,
|
|
512
480
|
>(
|
|
513
|
-
|
|
514
|
-
|
|
481
|
+
domain: string | App,
|
|
482
|
+
config: SpiceflowClient.Config = {},
|
|
515
483
|
): SpiceflowClient.Create<App> => {
|
|
516
|
-
|
|
517
|
-
|
|
484
|
+
if (typeof domain === 'string') {
|
|
485
|
+
if (domain.endsWith('/')) domain = domain.slice(0, -1)
|
|
518
486
|
|
|
519
|
-
|
|
520
|
-
|
|
487
|
+
return createProxy(domain, config)
|
|
488
|
+
}
|
|
521
489
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
490
|
+
if (typeof window !== 'undefined')
|
|
491
|
+
console.warn(
|
|
492
|
+
'Spiceflow instance server found on client side, this is not recommended for security reason. Use generic type instead.',
|
|
493
|
+
)
|
|
526
494
|
|
|
527
|
-
|
|
495
|
+
return createProxy('http://e.ly', config, [], domain)
|
|
528
496
|
}
|
|
529
497
|
|
|
530
498
|
export type { SpiceflowClient as Treaty }
|