effect-start 0.9.0
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/LICENSE +21 -0
- package/README.md +109 -0
- package/package.json +57 -0
- package/src/Bundle.ts +167 -0
- package/src/BundleFiles.ts +174 -0
- package/src/BundleHttp.test.ts +160 -0
- package/src/BundleHttp.ts +259 -0
- package/src/Commander.test.ts +1378 -0
- package/src/Commander.ts +672 -0
- package/src/Datastar.test.ts +267 -0
- package/src/Datastar.ts +68 -0
- package/src/Effect_HttpRouter.test.ts +570 -0
- package/src/EncryptedCookies.test.ts +427 -0
- package/src/EncryptedCookies.ts +451 -0
- package/src/FileHttpRouter.test.ts +207 -0
- package/src/FileHttpRouter.ts +122 -0
- package/src/FileRouter.ts +405 -0
- package/src/FileRouterCodegen.test.ts +598 -0
- package/src/FileRouterCodegen.ts +251 -0
- package/src/FileRouter_files.test.ts +64 -0
- package/src/FileRouter_path.test.ts +132 -0
- package/src/FileRouter_tree.test.ts +126 -0
- package/src/FileSystemExtra.ts +102 -0
- package/src/HttpAppExtra.ts +127 -0
- package/src/Hyper.ts +194 -0
- package/src/HyperHtml.test.ts +90 -0
- package/src/HyperHtml.ts +139 -0
- package/src/HyperNode.ts +37 -0
- package/src/JsModule.test.ts +14 -0
- package/src/JsModule.ts +116 -0
- package/src/PublicDirectory.test.ts +280 -0
- package/src/PublicDirectory.ts +108 -0
- package/src/Route.test.ts +873 -0
- package/src/Route.ts +992 -0
- package/src/Router.ts +80 -0
- package/src/SseHttpResponse.ts +55 -0
- package/src/Start.ts +133 -0
- package/src/StartApp.ts +43 -0
- package/src/StartHttp.ts +42 -0
- package/src/StreamExtra.ts +146 -0
- package/src/TestHttpClient.test.ts +54 -0
- package/src/TestHttpClient.ts +100 -0
- package/src/bun/BunBundle.test.ts +277 -0
- package/src/bun/BunBundle.ts +309 -0
- package/src/bun/BunBundle_imports.test.ts +50 -0
- package/src/bun/BunFullstackServer.ts +45 -0
- package/src/bun/BunFullstackServer_httpServer.ts +541 -0
- package/src/bun/BunImportTrackerPlugin.test.ts +77 -0
- package/src/bun/BunImportTrackerPlugin.ts +97 -0
- package/src/bun/BunTailwindPlugin.test.ts +335 -0
- package/src/bun/BunTailwindPlugin.ts +322 -0
- package/src/bun/BunVirtualFilesPlugin.ts +59 -0
- package/src/bun/index.ts +4 -0
- package/src/client/Overlay.ts +34 -0
- package/src/client/ScrollState.ts +120 -0
- package/src/client/index.ts +101 -0
- package/src/index.ts +24 -0
- package/src/jsx-datastar.d.ts +63 -0
- package/src/jsx-runtime.ts +23 -0
- package/src/jsx.d.ts +4402 -0
- package/src/testing.ts +55 -0
- package/src/x/cloudflare/CloudflareTunnel.ts +110 -0
- package/src/x/cloudflare/index.ts +1 -0
- package/src/x/datastar/Datastar.test.ts +267 -0
- package/src/x/datastar/Datastar.ts +68 -0
- package/src/x/datastar/index.ts +4 -0
- package/src/x/datastar/jsx-datastar.d.ts +63 -0
package/src/Route.ts
ADDED
|
@@ -0,0 +1,992 @@
|
|
|
1
|
+
import * as HttpMethod from "@effect/platform/HttpMethod"
|
|
2
|
+
import * as HttpServerRequest from "@effect/platform/HttpServerRequest"
|
|
3
|
+
import * as HttpServerRespondable from "@effect/platform/HttpServerRespondable"
|
|
4
|
+
import * as HttpServerResponse from "@effect/platform/HttpServerResponse"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
import * as Pipeable from "effect/Pipeable"
|
|
7
|
+
import * as Predicate from "effect/Predicate"
|
|
8
|
+
import * as Schema from "effect/Schema"
|
|
9
|
+
import type { YieldWrap } from "effect/Utils"
|
|
10
|
+
import * as HyperHtml from "./HyperHtml.ts"
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
pipe,
|
|
14
|
+
} from "effect/Function"
|
|
15
|
+
|
|
16
|
+
type RouteModule = typeof import("./Route.ts")
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 'this' argument type for {@link RouteBuilder} functions.
|
|
20
|
+
* Its value depend on how the function is called as described below.
|
|
21
|
+
*/
|
|
22
|
+
type Self =
|
|
23
|
+
/**
|
|
24
|
+
* Called as {@link RouteSet} method:
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* let route: Route
|
|
29
|
+
*
|
|
30
|
+
* route.text("Hello")
|
|
31
|
+
*
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
| RouteSet.Default
|
|
35
|
+
| RouteSet<Route.Empty, RouteSchemas>
|
|
36
|
+
/**
|
|
37
|
+
* Called from namespaced import.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import * as Route from "./Route.ts"
|
|
42
|
+
*
|
|
43
|
+
* let route: Route
|
|
44
|
+
*
|
|
45
|
+
* Route.text("Hello")
|
|
46
|
+
*
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
| RouteModule
|
|
50
|
+
/**
|
|
51
|
+
* Called directly from exported function.
|
|
52
|
+
* Disencouraged but possible.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { text } from "./Route.ts"
|
|
57
|
+
*
|
|
58
|
+
* text("Hello")
|
|
59
|
+
*
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
| undefined
|
|
63
|
+
|
|
64
|
+
const TypeId: unique symbol = Symbol.for("effect-start/Route")
|
|
65
|
+
const RouteSetTypeId: unique symbol = Symbol.for("effect-start/RouteSet")
|
|
66
|
+
|
|
67
|
+
export type RouteMethod =
|
|
68
|
+
| "*"
|
|
69
|
+
| HttpMethod.HttpMethod
|
|
70
|
+
|
|
71
|
+
export type RoutePath = `/${string}`
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Route media type used for content negotiation.
|
|
75
|
+
* This allows to create routes that serve different media types
|
|
76
|
+
* for the same path & method, depending on the `Accept` header
|
|
77
|
+
* of the request.
|
|
78
|
+
*/
|
|
79
|
+
type RouteMedia =
|
|
80
|
+
| "*"
|
|
81
|
+
| "text/plain"
|
|
82
|
+
| "text/html"
|
|
83
|
+
| "application/json"
|
|
84
|
+
|
|
85
|
+
export type RouteHandler<
|
|
86
|
+
A = unknown,
|
|
87
|
+
E = any,
|
|
88
|
+
R = any,
|
|
89
|
+
> =
|
|
90
|
+
/**
|
|
91
|
+
* A handler that contains raw value.
|
|
92
|
+
* Can be consumed from other handlers to build more complex responses.
|
|
93
|
+
* For example, a Route can render markdown for API/AI consumption
|
|
94
|
+
* and another Route can wrap it in HTML for browsers.
|
|
95
|
+
*/
|
|
96
|
+
| RouteHandler.Value<A, E, R>
|
|
97
|
+
/**
|
|
98
|
+
* A handler returns `HttpServerResponse`.
|
|
99
|
+
* Should not be consumed with caution: if body is a stream,
|
|
100
|
+
* consuming it in another handler may break the stream.
|
|
101
|
+
*/
|
|
102
|
+
| RouteHandler.Encoded<E, R>
|
|
103
|
+
|
|
104
|
+
export namespace RouteHandler {
|
|
105
|
+
export type Value<
|
|
106
|
+
A = unknown,
|
|
107
|
+
E = any,
|
|
108
|
+
R = any,
|
|
109
|
+
> = Effect.Effect<
|
|
110
|
+
{
|
|
111
|
+
[HttpServerRespondable.symbol]: () => Effect.Effect<
|
|
112
|
+
HttpServerResponse.HttpServerResponse,
|
|
113
|
+
E,
|
|
114
|
+
R
|
|
115
|
+
>
|
|
116
|
+
raw: A
|
|
117
|
+
},
|
|
118
|
+
E,
|
|
119
|
+
R
|
|
120
|
+
>
|
|
121
|
+
|
|
122
|
+
export type Encoded<E = any, R = any> = Effect.Effect<
|
|
123
|
+
HttpServerResponse.HttpServerResponse,
|
|
124
|
+
E,
|
|
125
|
+
R
|
|
126
|
+
>
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Helper type for a value that can be a single item or an array.
|
|
131
|
+
*/
|
|
132
|
+
export type OneOrMany<T> = T | T[] | readonly T[]
|
|
133
|
+
|
|
134
|
+
export type RouteSchemas = {
|
|
135
|
+
readonly PathParams?: Schema.Struct<any>
|
|
136
|
+
readonly UrlParams?: Schema.Struct<any>
|
|
137
|
+
readonly Payload?: Schema.Schema.Any
|
|
138
|
+
readonly Success?: Schema.Schema.Any
|
|
139
|
+
readonly Error?: Schema.Schema.Any
|
|
140
|
+
readonly Headers?: Schema.Struct<any>
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export namespace RouteSchemas {
|
|
144
|
+
export type Empty = {
|
|
145
|
+
readonly PathParams?: never
|
|
146
|
+
readonly UrlParams?: never
|
|
147
|
+
readonly Payload?: never
|
|
148
|
+
readonly Success?: never
|
|
149
|
+
readonly Error?: never
|
|
150
|
+
readonly Headers?: never
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface Route<
|
|
155
|
+
out Method extends RouteMethod = "*",
|
|
156
|
+
out Media extends RouteMedia = "*",
|
|
157
|
+
out Handler extends RouteHandler = RouteHandler,
|
|
158
|
+
out Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
159
|
+
> extends RouteSet<[Route.Default], Schemas> {
|
|
160
|
+
[TypeId]: typeof TypeId
|
|
161
|
+
readonly method: Method
|
|
162
|
+
readonly media: Media
|
|
163
|
+
readonly handler: Handler
|
|
164
|
+
readonly schemas: Schemas
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Describes a single route that varies by method & media type.
|
|
169
|
+
*
|
|
170
|
+
* Implements {@link RouteSet} interface that contains itself.
|
|
171
|
+
*/
|
|
172
|
+
export namespace Route {
|
|
173
|
+
export type Data<
|
|
174
|
+
Method extends RouteMethod = RouteMethod,
|
|
175
|
+
Media extends RouteMedia = RouteMedia,
|
|
176
|
+
Handler extends RouteHandler = RouteHandler,
|
|
177
|
+
Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
178
|
+
> = {
|
|
179
|
+
readonly method: Method
|
|
180
|
+
readonly media: Media
|
|
181
|
+
readonly handler: Handler
|
|
182
|
+
readonly schemas: Schemas
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export type Default = Route<
|
|
186
|
+
RouteMethod,
|
|
187
|
+
RouteMedia,
|
|
188
|
+
RouteHandler,
|
|
189
|
+
RouteSchemas
|
|
190
|
+
>
|
|
191
|
+
|
|
192
|
+
export type Tuple = readonly [Default, ...Default[]]
|
|
193
|
+
|
|
194
|
+
export type Empty = readonly []
|
|
195
|
+
|
|
196
|
+
export type Proto =
|
|
197
|
+
& Pipeable.Pipeable
|
|
198
|
+
& {
|
|
199
|
+
[TypeId]: typeof TypeId
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Consists of function to build {@link RouteSet}.
|
|
205
|
+
* This should include all exported functions in this module ({@link RouteModule})
|
|
206
|
+
* that have `this` as {@link Self}.
|
|
207
|
+
*
|
|
208
|
+
* Method functions, like {@link post}, modify the method of existing routes.
|
|
209
|
+
* Media functions, like {@link json}, create new routes with specific media type.
|
|
210
|
+
*/
|
|
211
|
+
type RouteBuilder = {
|
|
212
|
+
post: typeof post
|
|
213
|
+
get: typeof get
|
|
214
|
+
put: typeof put
|
|
215
|
+
patch: typeof patch
|
|
216
|
+
del: typeof del
|
|
217
|
+
options: typeof options
|
|
218
|
+
head: typeof head
|
|
219
|
+
|
|
220
|
+
text: typeof text
|
|
221
|
+
html: typeof html
|
|
222
|
+
json: typeof json
|
|
223
|
+
|
|
224
|
+
schemaPathParams: typeof schemaPathParams
|
|
225
|
+
schemaUrlParams: typeof schemaUrlParams
|
|
226
|
+
schemaPayload: typeof schemaPayload
|
|
227
|
+
schemaSuccess: typeof schemaSuccess
|
|
228
|
+
schemaError: typeof schemaError
|
|
229
|
+
schemaHeaders: typeof schemaHeaders
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Set of one or many {@link Route} with chainable builder functions
|
|
234
|
+
* to modify the set or add new routes.
|
|
235
|
+
*/
|
|
236
|
+
export type RouteSet<
|
|
237
|
+
M extends ReadonlyArray<Route.Default>,
|
|
238
|
+
Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
239
|
+
> =
|
|
240
|
+
& Pipeable.Pipeable
|
|
241
|
+
& RouteSet.Instance<M, Schemas>
|
|
242
|
+
& {
|
|
243
|
+
[RouteSetTypeId]: typeof RouteSetTypeId
|
|
244
|
+
}
|
|
245
|
+
& RouteBuilder
|
|
246
|
+
|
|
247
|
+
export namespace RouteSet {
|
|
248
|
+
export type Instance<
|
|
249
|
+
M extends ReadonlyArray<Route.Default> = Route.Tuple,
|
|
250
|
+
Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
251
|
+
> = {
|
|
252
|
+
set: M
|
|
253
|
+
schema: Schemas
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export type Default = RouteSet<Route.Tuple, RouteSchemas>
|
|
257
|
+
|
|
258
|
+
export type Proto =
|
|
259
|
+
& {
|
|
260
|
+
[RouteSetTypeId]: typeof RouteSetTypeId
|
|
261
|
+
}
|
|
262
|
+
& RouteBuilder
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export const post = makeMethodModifier("POST")
|
|
266
|
+
export const get = makeMethodModifier("GET")
|
|
267
|
+
export const put = makeMethodModifier("PUT")
|
|
268
|
+
export const patch = makeMethodModifier("PATCH")
|
|
269
|
+
export const del = makeMethodModifier("DELETE")
|
|
270
|
+
export const options = makeMethodModifier("OPTIONS")
|
|
271
|
+
export const head = makeMethodModifier("HEAD")
|
|
272
|
+
|
|
273
|
+
export const text = makeMediaFunction(
|
|
274
|
+
"GET",
|
|
275
|
+
"text/plain",
|
|
276
|
+
makeValueHandler<string>(HttpServerResponse.text),
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
export const html = makeMediaFunction(
|
|
280
|
+
"GET",
|
|
281
|
+
"text/html",
|
|
282
|
+
makeValueHandler<string | JsxObject>((raw) => {
|
|
283
|
+
// Check if it's a JSX element (has type and props properties)
|
|
284
|
+
if (isJsxObject(raw)) {
|
|
285
|
+
return HttpServerResponse.html(HyperHtml.renderToString(raw))
|
|
286
|
+
}
|
|
287
|
+
return HttpServerResponse.html(raw as string)
|
|
288
|
+
}),
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
export const json = makeMediaFunction(
|
|
292
|
+
"GET",
|
|
293
|
+
"application/json",
|
|
294
|
+
makeValueHandler<JsonValue>((raw) => HttpServerResponse.unsafeJson(raw)),
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Schema type that accepts string-encoded input.
|
|
299
|
+
* Used for path parameters which are always strings.
|
|
300
|
+
*/
|
|
301
|
+
type StringEncodedSchema =
|
|
302
|
+
| Schema.Schema<any, string, any>
|
|
303
|
+
// TODO: we accept PropertySignature to support Schema.optional
|
|
304
|
+
// not great but Effect 4 should be better about it
|
|
305
|
+
| Schema.PropertySignature.All
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Schema type that accepts string or string array encoded input.
|
|
309
|
+
* Used for URL params and headers which can have multiple values.
|
|
310
|
+
*/
|
|
311
|
+
type StringOrArrayEncodedSchema =
|
|
312
|
+
| Schema.Schema<any, OneOrMany<string>, any>
|
|
313
|
+
| Schema.PropertySignature.All
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Helper type to extract the Encoded type from a Schema.
|
|
317
|
+
*/
|
|
318
|
+
type GetEncoded<S> = S extends { Encoded: infer E } ? E : never
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Check if a schema's encoded type is string.
|
|
322
|
+
*/
|
|
323
|
+
type IsStringEncoded<S> = S extends Schema.PropertySignature.All ? true
|
|
324
|
+
: GetEncoded<S> extends string ? true
|
|
325
|
+
: false
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Check if a schema's encoded type is string or string array.
|
|
329
|
+
*/
|
|
330
|
+
type IsStringOrArrayEncoded<S> = S extends Schema.PropertySignature.All ? true
|
|
331
|
+
: GetEncoded<S> extends OneOrMany<string> ? true
|
|
332
|
+
: false
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Validate that all fields have string-encoded schemas.
|
|
336
|
+
*/
|
|
337
|
+
type ValidateStringEncodedFields<T extends Record<PropertyKey, any>> = {
|
|
338
|
+
[K in keyof T]: IsStringEncoded<T[K]> extends true ? T[K]
|
|
339
|
+
: StringEncodedSchema
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Validate that all fields have string or array-encoded schemas.
|
|
344
|
+
*/
|
|
345
|
+
type ValidateStringOrArrayEncodedFields<T extends Record<PropertyKey, any>> = {
|
|
346
|
+
[K in keyof T]: IsStringOrArrayEncoded<T[K]> extends true ? T[K]
|
|
347
|
+
: StringOrArrayEncodedSchema
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function makeSingleStringSchemaModifier<
|
|
351
|
+
K extends string,
|
|
352
|
+
>(key: K) {
|
|
353
|
+
return function<
|
|
354
|
+
S extends Self,
|
|
355
|
+
const Fields extends Record<PropertyKey, any>,
|
|
356
|
+
>(
|
|
357
|
+
this: S,
|
|
358
|
+
fieldsOrSchema: Fields extends Schema.Struct<any> ? Fields
|
|
359
|
+
: ValidateStringEncodedFields<Fields>,
|
|
360
|
+
): S extends RouteSet<infer Routes, infer Schemas> ? RouteSet<
|
|
361
|
+
Routes,
|
|
362
|
+
& Schemas
|
|
363
|
+
& {
|
|
364
|
+
[P in K]: Fields extends Schema.Struct<infer F> ? Schema.Struct<F>
|
|
365
|
+
: Schema.Struct<
|
|
366
|
+
Fields extends Record<PropertyKey, infer _> ? Fields : never
|
|
367
|
+
>
|
|
368
|
+
}
|
|
369
|
+
>
|
|
370
|
+
: RouteSet<
|
|
371
|
+
[],
|
|
372
|
+
{
|
|
373
|
+
[P in K]: Fields extends Schema.Struct<infer F> ? Schema.Struct<F>
|
|
374
|
+
: Schema.Struct<
|
|
375
|
+
Fields extends Record<PropertyKey, infer _> ? Fields : never
|
|
376
|
+
>
|
|
377
|
+
}
|
|
378
|
+
>
|
|
379
|
+
{
|
|
380
|
+
const baseRoutes = isRouteSet(this)
|
|
381
|
+
? this.set
|
|
382
|
+
: []
|
|
383
|
+
const baseSchema = isRouteSet(this)
|
|
384
|
+
? this.schema
|
|
385
|
+
: {} as RouteSchemas.Empty
|
|
386
|
+
|
|
387
|
+
const schema = Schema.isSchema(fieldsOrSchema)
|
|
388
|
+
? fieldsOrSchema
|
|
389
|
+
: Schema.Struct(fieldsOrSchema as Schema.Struct.Fields)
|
|
390
|
+
|
|
391
|
+
return makeSet(
|
|
392
|
+
baseRoutes as any,
|
|
393
|
+
{
|
|
394
|
+
...baseSchema,
|
|
395
|
+
[key]: schema,
|
|
396
|
+
} as any,
|
|
397
|
+
) as any
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function makeMultiStringSchemaModifier<
|
|
402
|
+
K extends string,
|
|
403
|
+
>(key: K) {
|
|
404
|
+
return function<
|
|
405
|
+
S extends Self,
|
|
406
|
+
const Fields extends Record<PropertyKey, any>,
|
|
407
|
+
>(
|
|
408
|
+
this: S,
|
|
409
|
+
fieldsOrSchema: Fields extends Schema.Struct<any> ? Fields
|
|
410
|
+
: ValidateStringOrArrayEncodedFields<Fields>,
|
|
411
|
+
): S extends RouteSet<infer Routes, infer Schemas> ? RouteSet<
|
|
412
|
+
Routes,
|
|
413
|
+
& Schemas
|
|
414
|
+
& {
|
|
415
|
+
[P in K]: Fields extends Schema.Struct<infer F> ? Schema.Struct<F>
|
|
416
|
+
: Schema.Struct<
|
|
417
|
+
Fields extends Record<PropertyKey, infer _> ? Fields : never
|
|
418
|
+
>
|
|
419
|
+
}
|
|
420
|
+
>
|
|
421
|
+
: RouteSet<
|
|
422
|
+
[],
|
|
423
|
+
{
|
|
424
|
+
[P in K]: Fields extends Schema.Struct<infer F> ? Schema.Struct<F>
|
|
425
|
+
: Schema.Struct<
|
|
426
|
+
Fields extends Record<PropertyKey, infer _> ? Fields : never
|
|
427
|
+
>
|
|
428
|
+
}
|
|
429
|
+
>
|
|
430
|
+
{
|
|
431
|
+
const baseRoutes = isRouteSet(this)
|
|
432
|
+
? this.set
|
|
433
|
+
: []
|
|
434
|
+
const baseSchema = isRouteSet(this)
|
|
435
|
+
? this.schema
|
|
436
|
+
: {} as RouteSchemas.Empty
|
|
437
|
+
|
|
438
|
+
const schema = Schema.isSchema(fieldsOrSchema)
|
|
439
|
+
? fieldsOrSchema
|
|
440
|
+
: Schema.Struct(fieldsOrSchema as Schema.Struct.Fields)
|
|
441
|
+
|
|
442
|
+
return makeSet(
|
|
443
|
+
baseRoutes as any,
|
|
444
|
+
{
|
|
445
|
+
...baseSchema,
|
|
446
|
+
[key]: schema,
|
|
447
|
+
} as any,
|
|
448
|
+
) as any
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function makeUnionSchemaModifier<
|
|
453
|
+
K extends "Payload" | "Success" | "Error",
|
|
454
|
+
>(key: K) {
|
|
455
|
+
return function<
|
|
456
|
+
S extends Self,
|
|
457
|
+
Fields extends Schema.Struct.Fields | Schema.Schema.Any,
|
|
458
|
+
>(
|
|
459
|
+
this: S,
|
|
460
|
+
fieldsOrSchema: Fields,
|
|
461
|
+
): S extends RouteSet<infer Routes, infer Schemas> ? RouteSet<
|
|
462
|
+
Routes,
|
|
463
|
+
& Schemas
|
|
464
|
+
& {
|
|
465
|
+
[P in K]: Fields extends Schema.Schema.Any ? Fields
|
|
466
|
+
: Fields extends Schema.Struct.Fields ? Schema.Struct<Fields>
|
|
467
|
+
: never
|
|
468
|
+
}
|
|
469
|
+
>
|
|
470
|
+
: RouteSet<
|
|
471
|
+
[],
|
|
472
|
+
{
|
|
473
|
+
[P in K]: Fields extends Schema.Schema.Any ? Fields
|
|
474
|
+
: Fields extends Schema.Struct.Fields ? Schema.Struct<Fields>
|
|
475
|
+
: never
|
|
476
|
+
}
|
|
477
|
+
>
|
|
478
|
+
{
|
|
479
|
+
const baseRoutes = isRouteSet(this)
|
|
480
|
+
? this.set
|
|
481
|
+
: []
|
|
482
|
+
const baseSchema = isRouteSet(this)
|
|
483
|
+
? this.schema
|
|
484
|
+
: {} as RouteSchemas.Empty
|
|
485
|
+
|
|
486
|
+
const schema = Schema.isSchema(fieldsOrSchema)
|
|
487
|
+
? fieldsOrSchema
|
|
488
|
+
: Schema.Struct(fieldsOrSchema as Schema.Struct.Fields)
|
|
489
|
+
|
|
490
|
+
return makeSet(
|
|
491
|
+
baseRoutes as any,
|
|
492
|
+
{
|
|
493
|
+
...baseSchema,
|
|
494
|
+
[key]: schema,
|
|
495
|
+
} as any,
|
|
496
|
+
) as any
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
export const schemaPathParams = makeSingleStringSchemaModifier("PathParams")
|
|
501
|
+
export const schemaUrlParams = makeMultiStringSchemaModifier("UrlParams")
|
|
502
|
+
export const schemaHeaders = makeMultiStringSchemaModifier("Headers")
|
|
503
|
+
export const schemaPayload = makeUnionSchemaModifier("Payload")
|
|
504
|
+
export const schemaSuccess = makeUnionSchemaModifier("Success")
|
|
505
|
+
export const schemaError = makeUnionSchemaModifier("Error")
|
|
506
|
+
|
|
507
|
+
const SetProto = {
|
|
508
|
+
[RouteSetTypeId]: RouteSetTypeId,
|
|
509
|
+
|
|
510
|
+
post,
|
|
511
|
+
get,
|
|
512
|
+
put,
|
|
513
|
+
patch,
|
|
514
|
+
del,
|
|
515
|
+
options,
|
|
516
|
+
head,
|
|
517
|
+
|
|
518
|
+
text,
|
|
519
|
+
html,
|
|
520
|
+
json,
|
|
521
|
+
|
|
522
|
+
schemaPathParams,
|
|
523
|
+
schemaUrlParams,
|
|
524
|
+
schemaPayload,
|
|
525
|
+
schemaSuccess,
|
|
526
|
+
schemaError,
|
|
527
|
+
schemaHeaders,
|
|
528
|
+
} satisfies RouteSet.Proto
|
|
529
|
+
|
|
530
|
+
const RouteProto = Object.assign(
|
|
531
|
+
Object.create(SetProto),
|
|
532
|
+
{
|
|
533
|
+
[TypeId]: TypeId,
|
|
534
|
+
|
|
535
|
+
pipe() {
|
|
536
|
+
return Pipeable.pipeArguments(this, arguments)
|
|
537
|
+
},
|
|
538
|
+
} satisfies Route.Proto,
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
export function isRoute(input: unknown): input is Route {
|
|
542
|
+
return Predicate.hasProperty(input, TypeId)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function isRouteSet(
|
|
546
|
+
input: unknown,
|
|
547
|
+
): input is RouteSet.Default {
|
|
548
|
+
return Predicate.hasProperty(input, RouteSetTypeId)
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
export type JsonValue =
|
|
552
|
+
| string
|
|
553
|
+
| number
|
|
554
|
+
| boolean
|
|
555
|
+
| null
|
|
556
|
+
| JsonValue[]
|
|
557
|
+
| {
|
|
558
|
+
[key: string]: JsonValue
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Constructs a URL from HttpServerRequest.
|
|
563
|
+
* Handles relative URLs by using headers to determine the base URL.
|
|
564
|
+
*/
|
|
565
|
+
function makeUrlFromRequest(
|
|
566
|
+
request: HttpServerRequest.HttpServerRequest,
|
|
567
|
+
): URL {
|
|
568
|
+
const origin = request.headers.origin
|
|
569
|
+
?? request.headers.host
|
|
570
|
+
?? "http://localhost"
|
|
571
|
+
const protocol = request.headers["x-forwarded-proto"] ?? "http"
|
|
572
|
+
const host = request.headers.host ?? "localhost"
|
|
573
|
+
const base = origin.startsWith("http")
|
|
574
|
+
? origin
|
|
575
|
+
: `${protocol}://${host}`
|
|
576
|
+
return new URL(request.url, base)
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
type RouteContextDecoded = {
|
|
580
|
+
readonly pathParams?: Record<string, any>
|
|
581
|
+
readonly urlParams?: Record<string, any>
|
|
582
|
+
readonly payload?: any
|
|
583
|
+
readonly headers?: Record<string, any>
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Decode RouteSchemas to make context in media handlers easier to read:
|
|
588
|
+
* - Converts keys from PascalCase to camelCase
|
|
589
|
+
* - Decodes schema types to their Type representation
|
|
590
|
+
*/
|
|
591
|
+
export type DecodeRouteSchemas<Schemas extends RouteSchemas> =
|
|
592
|
+
& (Schemas["PathParams"] extends Schema.Struct<any> ? {
|
|
593
|
+
pathParams: Schema.Schema.Type<Schemas["PathParams"]>
|
|
594
|
+
}
|
|
595
|
+
: {})
|
|
596
|
+
& (Schemas["UrlParams"] extends Schema.Struct<any> ? {
|
|
597
|
+
urlParams: Schema.Schema.Type<Schemas["UrlParams"]>
|
|
598
|
+
}
|
|
599
|
+
: {})
|
|
600
|
+
& (Schemas["Payload"] extends Schema.Schema.Any ? {
|
|
601
|
+
payload: Schema.Schema.Type<Schemas["Payload"]>
|
|
602
|
+
}
|
|
603
|
+
: {})
|
|
604
|
+
& (Schemas["Headers"] extends Schema.Struct<any> ? {
|
|
605
|
+
headers: Schema.Schema.Type<Schemas["Headers"]>
|
|
606
|
+
}
|
|
607
|
+
: {})
|
|
608
|
+
|
|
609
|
+
/**
|
|
610
|
+
* Context passed to route handler generator functions.
|
|
611
|
+
*/
|
|
612
|
+
export type RouteContext<
|
|
613
|
+
Input extends RouteContextDecoded = {},
|
|
614
|
+
> = {
|
|
615
|
+
request: HttpServerRequest.HttpServerRequest
|
|
616
|
+
get url(): URL
|
|
617
|
+
} & Input
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Extracts fields from a Schema.Struct or returns never if not a struct.
|
|
621
|
+
*/
|
|
622
|
+
type ExtractStructFields<S> = S extends Schema.Struct<infer Fields> ? Fields
|
|
623
|
+
: never
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Merges two RouteSchemas types.
|
|
627
|
+
* For PathParams, UrlParams, and Headers: merges struct fields.
|
|
628
|
+
* For Payload, Success, and Error: creates Schema.Union.
|
|
629
|
+
*/
|
|
630
|
+
type MergeSchemas<
|
|
631
|
+
A extends RouteSchemas,
|
|
632
|
+
B extends RouteSchemas,
|
|
633
|
+
> = {
|
|
634
|
+
readonly PathParams: [A["PathParams"], B["PathParams"]] extends [
|
|
635
|
+
Schema.Struct<infer AFields>,
|
|
636
|
+
Schema.Struct<infer BFields>,
|
|
637
|
+
] ? Schema.Struct<AFields & BFields>
|
|
638
|
+
: A["PathParams"] extends Schema.Struct<any> ? A["PathParams"]
|
|
639
|
+
: B["PathParams"] extends Schema.Struct<any> ? B["PathParams"]
|
|
640
|
+
: never
|
|
641
|
+
readonly UrlParams: [A["UrlParams"], B["UrlParams"]] extends [
|
|
642
|
+
Schema.Struct<infer AFields>,
|
|
643
|
+
Schema.Struct<infer BFields>,
|
|
644
|
+
] ? Schema.Struct<AFields & BFields>
|
|
645
|
+
: A["UrlParams"] extends Schema.Struct<any> ? A["UrlParams"]
|
|
646
|
+
: B["UrlParams"] extends Schema.Struct<any> ? B["UrlParams"]
|
|
647
|
+
: never
|
|
648
|
+
readonly Payload: [A["Payload"], B["Payload"]] extends [
|
|
649
|
+
Schema.Schema.Any,
|
|
650
|
+
Schema.Schema.Any,
|
|
651
|
+
] ? Schema.Union<[A["Payload"], B["Payload"]]>
|
|
652
|
+
: A["Payload"] extends Schema.Schema.Any ? A["Payload"]
|
|
653
|
+
: B["Payload"] extends Schema.Schema.Any ? B["Payload"]
|
|
654
|
+
: never
|
|
655
|
+
readonly Success: [A["Success"], B["Success"]] extends [
|
|
656
|
+
Schema.Schema.Any,
|
|
657
|
+
Schema.Schema.Any,
|
|
658
|
+
] ? Schema.Union<[A["Success"], B["Success"]]>
|
|
659
|
+
: A["Success"] extends Schema.Schema.Any ? A["Success"]
|
|
660
|
+
: B["Success"] extends Schema.Schema.Any ? B["Success"]
|
|
661
|
+
: never
|
|
662
|
+
readonly Error: [A["Error"], B["Error"]] extends [
|
|
663
|
+
Schema.Schema.Any,
|
|
664
|
+
Schema.Schema.Any,
|
|
665
|
+
] ? Schema.Union<[A["Error"], B["Error"]]>
|
|
666
|
+
: A["Error"] extends Schema.Schema.Any ? A["Error"]
|
|
667
|
+
: B["Error"] extends Schema.Schema.Any ? B["Error"]
|
|
668
|
+
: never
|
|
669
|
+
readonly Headers: [A["Headers"], B["Headers"]] extends [
|
|
670
|
+
Schema.Struct<infer AFields>,
|
|
671
|
+
Schema.Struct<infer BFields>,
|
|
672
|
+
] ? Schema.Struct<AFields & BFields>
|
|
673
|
+
: A["Headers"] extends Schema.Struct<any> ? A["Headers"]
|
|
674
|
+
: B["Headers"] extends Schema.Struct<any> ? B["Headers"]
|
|
675
|
+
: never
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Runtime function to merge two RouteSchemas.
|
|
680
|
+
* For PathParams, UrlParams, and Headers: merges struct fields.
|
|
681
|
+
* For Payload, Success, and Error: creates Schema.Union.
|
|
682
|
+
*/
|
|
683
|
+
function mergeSchemas<
|
|
684
|
+
A extends RouteSchemas,
|
|
685
|
+
B extends RouteSchemas,
|
|
686
|
+
>(
|
|
687
|
+
a: A,
|
|
688
|
+
b: B,
|
|
689
|
+
): MergeSchemas<A, B> {
|
|
690
|
+
const result: any = {}
|
|
691
|
+
|
|
692
|
+
const structKeys: Array<keyof RouteSchemas> = [
|
|
693
|
+
"PathParams",
|
|
694
|
+
"UrlParams",
|
|
695
|
+
"Headers",
|
|
696
|
+
]
|
|
697
|
+
|
|
698
|
+
const unionKeys: Array<keyof RouteSchemas> = [
|
|
699
|
+
"Payload",
|
|
700
|
+
"Success",
|
|
701
|
+
"Error",
|
|
702
|
+
]
|
|
703
|
+
|
|
704
|
+
for (const key of structKeys) {
|
|
705
|
+
if (a[key] && b[key]) {
|
|
706
|
+
const aSchema = a[key]! as Schema.Struct<any>
|
|
707
|
+
const bSchema = b[key]! as Schema.Struct<any>
|
|
708
|
+
const mergedFields = {
|
|
709
|
+
...aSchema.fields,
|
|
710
|
+
...bSchema.fields,
|
|
711
|
+
}
|
|
712
|
+
result[key] = Schema.Struct(mergedFields)
|
|
713
|
+
} else if (a[key]) {
|
|
714
|
+
result[key] = a[key]
|
|
715
|
+
} else if (b[key]) {
|
|
716
|
+
result[key] = b[key]
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
for (const key of unionKeys) {
|
|
721
|
+
if (a[key] && b[key]) {
|
|
722
|
+
result[key] = Schema.Union(a[key]!, b[key]!)
|
|
723
|
+
} else if (a[key]) {
|
|
724
|
+
result[key] = a[key]
|
|
725
|
+
} else if (b[key]) {
|
|
726
|
+
result[key] = b[key]
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
return result
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function make<
|
|
734
|
+
Method extends RouteMethod = "*",
|
|
735
|
+
Media extends RouteMedia = "*",
|
|
736
|
+
Handler extends RouteHandler = never,
|
|
737
|
+
Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
738
|
+
>(
|
|
739
|
+
input: Route.Data<
|
|
740
|
+
Method,
|
|
741
|
+
Media,
|
|
742
|
+
Handler,
|
|
743
|
+
Schemas
|
|
744
|
+
>,
|
|
745
|
+
): Route<
|
|
746
|
+
Method,
|
|
747
|
+
Media,
|
|
748
|
+
Handler,
|
|
749
|
+
Schemas
|
|
750
|
+
> {
|
|
751
|
+
const route = Object.assign(
|
|
752
|
+
Object.create(RouteProto),
|
|
753
|
+
{
|
|
754
|
+
set: [],
|
|
755
|
+
// @ts-expect-error: assigned below
|
|
756
|
+
schemas: input.schemas,
|
|
757
|
+
method: input.method,
|
|
758
|
+
media: input.media,
|
|
759
|
+
handler: input.handler,
|
|
760
|
+
} satisfies Route.Data,
|
|
761
|
+
)
|
|
762
|
+
|
|
763
|
+
route.set = [
|
|
764
|
+
route,
|
|
765
|
+
]
|
|
766
|
+
route.schemas = input.schemas
|
|
767
|
+
|
|
768
|
+
return route
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function makeSet<
|
|
772
|
+
M extends ReadonlyArray<Route.Default>,
|
|
773
|
+
Schemas extends RouteSchemas = RouteSchemas.Empty,
|
|
774
|
+
>(
|
|
775
|
+
routes: M,
|
|
776
|
+
schema: Schemas = {} as Schemas,
|
|
777
|
+
): RouteSet<M, Schemas> {
|
|
778
|
+
return Object.assign(
|
|
779
|
+
Object.create(SetProto),
|
|
780
|
+
{
|
|
781
|
+
set: routes,
|
|
782
|
+
schema,
|
|
783
|
+
},
|
|
784
|
+
) as RouteSet<M, Schemas>
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Factory function that creates Route for a specific method & media.
|
|
789
|
+
* Supports both Effect values and generator functions that receive context.
|
|
790
|
+
*/
|
|
791
|
+
function makeMediaFunction<
|
|
792
|
+
Method extends HttpMethod.HttpMethod,
|
|
793
|
+
Media extends RouteMedia,
|
|
794
|
+
HandlerFn extends (
|
|
795
|
+
handler: any,
|
|
796
|
+
) => any,
|
|
797
|
+
>(
|
|
798
|
+
method: Method,
|
|
799
|
+
media: Media,
|
|
800
|
+
handlerFn: HandlerFn,
|
|
801
|
+
) {
|
|
802
|
+
return function<
|
|
803
|
+
S extends Self,
|
|
804
|
+
A,
|
|
805
|
+
E = never,
|
|
806
|
+
R = never,
|
|
807
|
+
>(
|
|
808
|
+
this: S,
|
|
809
|
+
handler: S extends RouteSet<infer _Routes, infer Schemas> ?
|
|
810
|
+
| Effect.Effect<A, E, R>
|
|
811
|
+
| ((
|
|
812
|
+
context: RouteContext<DecodeRouteSchemas<Schemas>>,
|
|
813
|
+
) =>
|
|
814
|
+
| Effect.Effect<A, E, R>
|
|
815
|
+
| Generator<YieldWrap<Effect.Effect<A, E, R>>, A, never>)
|
|
816
|
+
:
|
|
817
|
+
| Effect.Effect<A, E, R>
|
|
818
|
+
| ((
|
|
819
|
+
context: RouteContext<{}>,
|
|
820
|
+
) =>
|
|
821
|
+
| Effect.Effect<A, E, R>
|
|
822
|
+
| Generator<YieldWrap<Effect.Effect<A, E, R>>, A, never>),
|
|
823
|
+
): S extends RouteSet<infer Routes, infer Schemas> ? RouteSet<[
|
|
824
|
+
...Routes,
|
|
825
|
+
Route<
|
|
826
|
+
Method,
|
|
827
|
+
Media,
|
|
828
|
+
ReturnType<HandlerFn>,
|
|
829
|
+
Schemas
|
|
830
|
+
>,
|
|
831
|
+
], Schemas>
|
|
832
|
+
: RouteSet<[
|
|
833
|
+
Route<
|
|
834
|
+
Method,
|
|
835
|
+
Media,
|
|
836
|
+
ReturnType<HandlerFn>,
|
|
837
|
+
RouteSchemas.Empty
|
|
838
|
+
>,
|
|
839
|
+
], RouteSchemas.Empty>
|
|
840
|
+
{
|
|
841
|
+
const effect = typeof handler === "function"
|
|
842
|
+
? Effect.gen(function*() {
|
|
843
|
+
const request = yield* HttpServerRequest.HttpServerRequest
|
|
844
|
+
const context: RouteContext<any> = {
|
|
845
|
+
request,
|
|
846
|
+
get url() {
|
|
847
|
+
return makeUrlFromRequest(request)
|
|
848
|
+
},
|
|
849
|
+
}
|
|
850
|
+
const result = handler(context)
|
|
851
|
+
return yield* (typeof result === "object"
|
|
852
|
+
&& result !== null
|
|
853
|
+
&& Symbol.iterator in result
|
|
854
|
+
? Effect.gen(() => result as any)
|
|
855
|
+
: result as Effect.Effect<A, E, R>)
|
|
856
|
+
})
|
|
857
|
+
: handler
|
|
858
|
+
|
|
859
|
+
const baseRoutes = isRouteSet(this)
|
|
860
|
+
? this.set
|
|
861
|
+
: []
|
|
862
|
+
const baseSchema = isRouteSet(this)
|
|
863
|
+
? this.schema
|
|
864
|
+
: {} as RouteSchemas.Empty
|
|
865
|
+
|
|
866
|
+
return makeSet(
|
|
867
|
+
[
|
|
868
|
+
...baseRoutes,
|
|
869
|
+
make({
|
|
870
|
+
method,
|
|
871
|
+
media,
|
|
872
|
+
handler: handlerFn(effect as any) as any,
|
|
873
|
+
schemas: baseSchema as any,
|
|
874
|
+
}),
|
|
875
|
+
] as any,
|
|
876
|
+
baseSchema as any,
|
|
877
|
+
) as any
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Factory to create RouteHandler.Value.
|
|
883
|
+
* Useful for structural handlers like JSON
|
|
884
|
+
* or content that can be embedded in other formats,
|
|
885
|
+
* like text or HTML.
|
|
886
|
+
*/
|
|
887
|
+
function makeValueHandler<ExpectedRaw = string>(
|
|
888
|
+
responseFn: (raw: ExpectedRaw) => HttpServerResponse.HttpServerResponse,
|
|
889
|
+
) {
|
|
890
|
+
return <A extends ExpectedRaw, E = never, R = never>(
|
|
891
|
+
handler: Effect.Effect<A, E, R>,
|
|
892
|
+
): RouteHandler.Value<A, E, R> => {
|
|
893
|
+
return Effect.gen(function*() {
|
|
894
|
+
const raw = yield* handler
|
|
895
|
+
|
|
896
|
+
return {
|
|
897
|
+
[HttpServerRespondable.symbol]: () =>
|
|
898
|
+
Effect.succeed(responseFn(raw as ExpectedRaw)),
|
|
899
|
+
raw,
|
|
900
|
+
}
|
|
901
|
+
}) as RouteHandler.Value<A, E, R>
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Factory function that changes method in RouteSet.
|
|
907
|
+
*/
|
|
908
|
+
function makeMethodModifier<
|
|
909
|
+
M extends HttpMethod.HttpMethod,
|
|
910
|
+
>(method: M) {
|
|
911
|
+
return function<
|
|
912
|
+
S extends Self,
|
|
913
|
+
T extends Route.Tuple,
|
|
914
|
+
InSchemas extends RouteSchemas,
|
|
915
|
+
>(
|
|
916
|
+
this: S,
|
|
917
|
+
routes: RouteSet<T, InSchemas>,
|
|
918
|
+
): S extends RouteSet<infer B, infer BaseSchemas>
|
|
919
|
+
// append to existing RouteSet
|
|
920
|
+
? RouteSet<
|
|
921
|
+
[
|
|
922
|
+
...B,
|
|
923
|
+
...{
|
|
924
|
+
[K in keyof T]: T[K] extends Route<
|
|
925
|
+
infer _,
|
|
926
|
+
infer Media,
|
|
927
|
+
infer H,
|
|
928
|
+
infer RouteSchemas
|
|
929
|
+
> ? Route<
|
|
930
|
+
M,
|
|
931
|
+
Media,
|
|
932
|
+
H,
|
|
933
|
+
MergeSchemas<BaseSchemas, RouteSchemas>
|
|
934
|
+
>
|
|
935
|
+
: T[K]
|
|
936
|
+
},
|
|
937
|
+
],
|
|
938
|
+
BaseSchemas
|
|
939
|
+
>
|
|
940
|
+
// otherwise create new RouteSet
|
|
941
|
+
: RouteSet<
|
|
942
|
+
{
|
|
943
|
+
[K in keyof T]: T[K] extends Route<
|
|
944
|
+
infer _,
|
|
945
|
+
infer Media,
|
|
946
|
+
infer H,
|
|
947
|
+
infer RouteSchemas
|
|
948
|
+
> ? Route<
|
|
949
|
+
M,
|
|
950
|
+
Media,
|
|
951
|
+
H,
|
|
952
|
+
RouteSchemas
|
|
953
|
+
>
|
|
954
|
+
: T[K]
|
|
955
|
+
},
|
|
956
|
+
InSchemas
|
|
957
|
+
>
|
|
958
|
+
{
|
|
959
|
+
const baseRoutes = isRouteSet(this)
|
|
960
|
+
? this.set
|
|
961
|
+
: [] as const
|
|
962
|
+
const baseSchema = isRouteSet(this)
|
|
963
|
+
? this.schema
|
|
964
|
+
: {} as RouteSchemas.Empty
|
|
965
|
+
|
|
966
|
+
return makeSet(
|
|
967
|
+
[
|
|
968
|
+
...baseRoutes,
|
|
969
|
+
...routes.set.map(route => {
|
|
970
|
+
return make({
|
|
971
|
+
...route,
|
|
972
|
+
method,
|
|
973
|
+
schemas: mergeSchemas(baseSchema, route.schemas) as any,
|
|
974
|
+
})
|
|
975
|
+
}),
|
|
976
|
+
] as any,
|
|
977
|
+
baseSchema as any,
|
|
978
|
+
) as any
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
type JsxObject = {
|
|
983
|
+
type: any
|
|
984
|
+
props: any
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function isJsxObject(value: any) {
|
|
988
|
+
return typeof value === "object"
|
|
989
|
+
&& value !== null
|
|
990
|
+
&& "type" in value
|
|
991
|
+
&& "props" in value
|
|
992
|
+
}
|