@kaito-http/core 3.0.0-beta.15 → 3.0.0-beta.20
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/index.cjs +19 -12
- package/dist/index.d.cts +97 -25
- package/dist/index.d.ts +97 -25
- package/dist/index.js +19 -12
- package/dist/stream/stream.cjs +110 -0
- package/dist/stream/stream.d.cts +35 -0
- package/dist/stream/stream.d.ts +35 -0
- package/dist/stream/stream.js +80 -0
- package/package.json +18 -4
- package/src/error.ts +26 -0
- package/src/index.ts +7 -0
- package/src/request.ts +47 -0
- package/src/response.ts +72 -0
- package/src/route.ts +53 -0
- package/src/router/router.test.ts +269 -0
- package/src/router/router.ts +257 -0
- package/src/router/types.ts +1 -0
- package/src/server.ts +112 -0
- package/src/stream/stream.ts +158 -0
- package/src/util.ts +66 -0
package/dist/index.cjs
CHANGED
|
@@ -106,6 +106,9 @@ var KaitoResponse = class {
|
|
|
106
106
|
return this._headers;
|
|
107
107
|
}
|
|
108
108
|
status(status) {
|
|
109
|
+
if (status === void 0) {
|
|
110
|
+
return this._status;
|
|
111
|
+
}
|
|
109
112
|
this._status = status;
|
|
110
113
|
return this;
|
|
111
114
|
}
|
|
@@ -232,10 +235,10 @@ var Router = class _Router {
|
|
|
232
235
|
const request = new KaitoRequest(url, req);
|
|
233
236
|
const response = new KaitoResponse();
|
|
234
237
|
try {
|
|
235
|
-
const rootCtx = await server.getContext(request, response);
|
|
236
|
-
const ctx = await route.through(rootCtx);
|
|
237
238
|
const body = route.body ? await route.body.parse(await req.json()) : void 0;
|
|
238
239
|
const query = _Router.parseQuery(route.query, url);
|
|
240
|
+
const rootCtx = await server.getContext(request, response);
|
|
241
|
+
const ctx = await route.through(rootCtx);
|
|
239
242
|
const result = await route.run({
|
|
240
243
|
ctx,
|
|
241
244
|
body,
|
|
@@ -243,6 +246,9 @@ var Router = class _Router {
|
|
|
243
246
|
params
|
|
244
247
|
});
|
|
245
248
|
if (result instanceof Response) {
|
|
249
|
+
if (server.enableClientResponseHints) {
|
|
250
|
+
result.headers.set("x-kaito-is-response", "1");
|
|
251
|
+
}
|
|
246
252
|
return result;
|
|
247
253
|
}
|
|
248
254
|
return response.toResponse({
|
|
@@ -288,20 +294,21 @@ var Router = class _Router {
|
|
|
288
294
|
};
|
|
289
295
|
|
|
290
296
|
// src/server.ts
|
|
291
|
-
function createKaitoHandler(
|
|
292
|
-
const
|
|
297
|
+
function createKaitoHandler(userConfig) {
|
|
298
|
+
const config = {
|
|
299
|
+
enableClientResponseHints: true,
|
|
300
|
+
...userConfig
|
|
301
|
+
};
|
|
302
|
+
const handle = config.router.freeze(config);
|
|
293
303
|
return async (request) => {
|
|
294
|
-
let before = void 0;
|
|
295
304
|
if (config.before) {
|
|
296
305
|
const result = await config.before(request);
|
|
297
|
-
if (result instanceof Response)
|
|
298
|
-
return result;
|
|
299
|
-
}
|
|
300
|
-
before = result;
|
|
306
|
+
if (result instanceof Response) return result;
|
|
301
307
|
}
|
|
302
|
-
const response = await
|
|
303
|
-
if (
|
|
304
|
-
await config.
|
|
308
|
+
const response = await handle(request);
|
|
309
|
+
if (config.transform) {
|
|
310
|
+
const result = await config.transform(request, response);
|
|
311
|
+
if (result instanceof Response) return result;
|
|
305
312
|
}
|
|
306
313
|
return response;
|
|
307
314
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -34,10 +34,10 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
|
|
|
34
34
|
* ```ts
|
|
35
35
|
* const response = new KaitoResponse();
|
|
36
36
|
*
|
|
37
|
-
* response.status
|
|
38
|
-
* response.
|
|
37
|
+
* response.status(200);
|
|
38
|
+
* response.headers.set('Content-Type', 'application/json');
|
|
39
39
|
*
|
|
40
|
-
* console.log(response.headers); // Headers {
|
|
40
|
+
* console.log(response.headers); // Headers {'content-type': 'application/json'}
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
declare class KaitoResponse {
|
|
@@ -45,6 +45,16 @@ declare class KaitoResponse {
|
|
|
45
45
|
private _status;
|
|
46
46
|
constructor();
|
|
47
47
|
get headers(): Headers;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the status code of this KaitoResponse instance
|
|
50
|
+
* @returns The status code
|
|
51
|
+
*/
|
|
52
|
+
status(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the status code of this KaitoResponse instance
|
|
55
|
+
* @param status The status code to set
|
|
56
|
+
* @returns This KaitoResponse instance
|
|
57
|
+
*/
|
|
48
58
|
status(status: number): this;
|
|
49
59
|
/**
|
|
50
60
|
* Turn this KaitoResponse instance into a Response instance
|
|
@@ -54,26 +64,87 @@ declare class KaitoResponse {
|
|
|
54
64
|
toResponse<T>(body: APIResponse<T>): Response;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
type Before
|
|
58
|
-
type
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
} | {
|
|
63
|
-
before?: undefined;
|
|
64
|
-
};
|
|
65
|
-
type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
67
|
+
type Before = (req: Request) => Promise<Response | void | undefined>;
|
|
68
|
+
type ServerConfig<ContextFrom> = {
|
|
69
|
+
/**
|
|
70
|
+
* The root router to mount on this server.
|
|
71
|
+
*/
|
|
66
72
|
router: Router<ContextFrom, unknown, any>;
|
|
73
|
+
/**
|
|
74
|
+
* A function that is called to get the context for a request.
|
|
75
|
+
*
|
|
76
|
+
* This is useful for things like authentication, to pass in a database connection, etc.
|
|
77
|
+
*
|
|
78
|
+
* It's fine for this function to throw; if it does, the error is passed to the `onError` function.
|
|
79
|
+
*/
|
|
67
80
|
getContext: GetContext<ContextFrom>;
|
|
68
|
-
|
|
81
|
+
/**
|
|
82
|
+
* A function that is called when an error occurs inside a route handler.
|
|
83
|
+
*
|
|
84
|
+
* The result of this function is used to determine the response status and message, and is
|
|
85
|
+
* always sent to the client. You could include logic to check for production vs development
|
|
86
|
+
* environments here, and this would also be a good place to include error tracking
|
|
87
|
+
* like Sentry or Rollbar.
|
|
88
|
+
*
|
|
89
|
+
* @param arg - The error and the request
|
|
90
|
+
* @returns A KaitoError or an object with a status and message
|
|
91
|
+
*/
|
|
92
|
+
onError: (arg: {
|
|
69
93
|
error: Error;
|
|
70
94
|
req: KaitoRequest;
|
|
71
|
-
})
|
|
95
|
+
}) => Promise<KaitoError | {
|
|
72
96
|
status: number;
|
|
73
97
|
message: string;
|
|
74
98
|
}>;
|
|
99
|
+
/**
|
|
100
|
+
* A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* before: async req => {
|
|
105
|
+
* if (req.method === 'OPTIONS') {
|
|
106
|
+
* return new Response(null, {status: 204});
|
|
107
|
+
* }
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
before?: Before;
|
|
112
|
+
/**
|
|
113
|
+
* Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
|
|
114
|
+
*
|
|
115
|
+
* You can also return a new response in this function, or just mutate the current one.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* transform: async (req, res) => {
|
|
120
|
+
* res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
|
|
121
|
+
* res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
122
|
+
* res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
123
|
+
* res.headers.set('Access-Control-Max-Age', '86400');
|
|
124
|
+
* res.headers.set('Access-Control-Allow-Credentials', 'true');
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
|
|
129
|
+
/**
|
|
130
|
+
* Controls whether the server includes a header to indicate non-JSON responses.
|
|
131
|
+
*
|
|
132
|
+
* When a route handler returns a `Response` object, this setting determines if
|
|
133
|
+
* the server adds a header indicating the response should not be parsed as JSON.
|
|
134
|
+
*
|
|
135
|
+
* The `@kaito-http/client` package checks for this header's presence:
|
|
136
|
+
* - If present: Returns the raw Response object
|
|
137
|
+
* - If absent: Attempts to parse the response as JSON
|
|
138
|
+
*
|
|
139
|
+
* You might want to disable this feature when:
|
|
140
|
+
* 1. Using a custom client that doesn't recognize this header
|
|
141
|
+
* 2. Security requirements prevent exposing framework details in headers
|
|
142
|
+
*
|
|
143
|
+
* @default true
|
|
144
|
+
*/
|
|
145
|
+
enableClientResponseHints: boolean;
|
|
75
146
|
};
|
|
76
|
-
declare function createKaitoHandler<Context
|
|
147
|
+
declare function createKaitoHandler<Context>(userConfig: MakeOptional<ServerConfig<Context>, 'enableClientResponseHints'>): (request: Request) => Promise<Response>;
|
|
77
148
|
|
|
78
149
|
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextFrom, infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextFrom, ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
|
|
79
150
|
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
@@ -88,17 +159,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
|
88
159
|
private static parseQuery;
|
|
89
160
|
constructor(options: RouterState<R, ContextFrom, ContextTo>);
|
|
90
161
|
get routes(): Set<R>;
|
|
91
|
-
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
162
|
+
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>>;
|
|
92
163
|
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
93
|
-
freeze: (server: Omit<ServerConfig<ContextFrom
|
|
164
|
+
freeze: (server: Omit<ServerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
|
|
94
165
|
private readonly method;
|
|
95
|
-
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
96
|
-
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
97
|
-
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
98
|
-
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
99
|
-
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
100
|
-
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
101
|
-
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
166
|
+
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>>;
|
|
167
|
+
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>>;
|
|
168
|
+
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>>;
|
|
169
|
+
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>>;
|
|
170
|
+
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>>;
|
|
171
|
+
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>>;
|
|
172
|
+
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>>;
|
|
102
173
|
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
103
174
|
}
|
|
104
175
|
|
|
@@ -114,6 +185,7 @@ type SuccessfulAPIResponse<T> = {
|
|
|
114
185
|
};
|
|
115
186
|
type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
116
187
|
type AnyResponse = APIResponse<unknown>;
|
|
188
|
+
type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
|
|
117
189
|
type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
118
190
|
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
119
191
|
} : T extends `${string}:${infer Param}` ? {
|
|
@@ -172,4 +244,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
|
|
|
172
244
|
};
|
|
173
245
|
type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
|
|
174
246
|
|
|
175
|
-
export { type APIResponse, type
|
|
247
|
+
export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
|
package/dist/index.d.ts
CHANGED
|
@@ -34,10 +34,10 @@ type KaitoMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIO
|
|
|
34
34
|
* ```ts
|
|
35
35
|
* const response = new KaitoResponse();
|
|
36
36
|
*
|
|
37
|
-
* response.status
|
|
38
|
-
* response.
|
|
37
|
+
* response.status(200);
|
|
38
|
+
* response.headers.set('Content-Type', 'application/json');
|
|
39
39
|
*
|
|
40
|
-
* console.log(response.headers); // Headers {
|
|
40
|
+
* console.log(response.headers); // Headers {'content-type': 'application/json'}
|
|
41
41
|
* ```
|
|
42
42
|
*/
|
|
43
43
|
declare class KaitoResponse {
|
|
@@ -45,6 +45,16 @@ declare class KaitoResponse {
|
|
|
45
45
|
private _status;
|
|
46
46
|
constructor();
|
|
47
47
|
get headers(): Headers;
|
|
48
|
+
/**
|
|
49
|
+
* Gets the status code of this KaitoResponse instance
|
|
50
|
+
* @returns The status code
|
|
51
|
+
*/
|
|
52
|
+
status(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Sets the status code of this KaitoResponse instance
|
|
55
|
+
* @param status The status code to set
|
|
56
|
+
* @returns This KaitoResponse instance
|
|
57
|
+
*/
|
|
48
58
|
status(status: number): this;
|
|
49
59
|
/**
|
|
50
60
|
* Turn this KaitoResponse instance into a Response instance
|
|
@@ -54,26 +64,87 @@ declare class KaitoResponse {
|
|
|
54
64
|
toResponse<T>(body: APIResponse<T>): Response;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
type Before
|
|
58
|
-
type
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
} | {
|
|
63
|
-
before?: undefined;
|
|
64
|
-
};
|
|
65
|
-
type ServerConfig<ContextFrom, BeforeAfterContext> = ServerConfigWithBefore<BeforeAfterContext> & {
|
|
67
|
+
type Before = (req: Request) => Promise<Response | void | undefined>;
|
|
68
|
+
type ServerConfig<ContextFrom> = {
|
|
69
|
+
/**
|
|
70
|
+
* The root router to mount on this server.
|
|
71
|
+
*/
|
|
66
72
|
router: Router<ContextFrom, unknown, any>;
|
|
73
|
+
/**
|
|
74
|
+
* A function that is called to get the context for a request.
|
|
75
|
+
*
|
|
76
|
+
* This is useful for things like authentication, to pass in a database connection, etc.
|
|
77
|
+
*
|
|
78
|
+
* It's fine for this function to throw; if it does, the error is passed to the `onError` function.
|
|
79
|
+
*/
|
|
67
80
|
getContext: GetContext<ContextFrom>;
|
|
68
|
-
|
|
81
|
+
/**
|
|
82
|
+
* A function that is called when an error occurs inside a route handler.
|
|
83
|
+
*
|
|
84
|
+
* The result of this function is used to determine the response status and message, and is
|
|
85
|
+
* always sent to the client. You could include logic to check for production vs development
|
|
86
|
+
* environments here, and this would also be a good place to include error tracking
|
|
87
|
+
* like Sentry or Rollbar.
|
|
88
|
+
*
|
|
89
|
+
* @param arg - The error and the request
|
|
90
|
+
* @returns A KaitoError or an object with a status and message
|
|
91
|
+
*/
|
|
92
|
+
onError: (arg: {
|
|
69
93
|
error: Error;
|
|
70
94
|
req: KaitoRequest;
|
|
71
|
-
})
|
|
95
|
+
}) => Promise<KaitoError | {
|
|
72
96
|
status: number;
|
|
73
97
|
message: string;
|
|
74
98
|
}>;
|
|
99
|
+
/**
|
|
100
|
+
* A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```ts
|
|
104
|
+
* before: async req => {
|
|
105
|
+
* if (req.method === 'OPTIONS') {
|
|
106
|
+
* return new Response(null, {status: 204});
|
|
107
|
+
* }
|
|
108
|
+
* }
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
before?: Before;
|
|
112
|
+
/**
|
|
113
|
+
* Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
|
|
114
|
+
*
|
|
115
|
+
* You can also return a new response in this function, or just mutate the current one.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* transform: async (req, res) => {
|
|
120
|
+
* res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
|
|
121
|
+
* res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
|
122
|
+
* res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
123
|
+
* res.headers.set('Access-Control-Max-Age', '86400');
|
|
124
|
+
* res.headers.set('Access-Control-Allow-Credentials', 'true');
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
|
|
129
|
+
/**
|
|
130
|
+
* Controls whether the server includes a header to indicate non-JSON responses.
|
|
131
|
+
*
|
|
132
|
+
* When a route handler returns a `Response` object, this setting determines if
|
|
133
|
+
* the server adds a header indicating the response should not be parsed as JSON.
|
|
134
|
+
*
|
|
135
|
+
* The `@kaito-http/client` package checks for this header's presence:
|
|
136
|
+
* - If present: Returns the raw Response object
|
|
137
|
+
* - If absent: Attempts to parse the response as JSON
|
|
138
|
+
*
|
|
139
|
+
* You might want to disable this feature when:
|
|
140
|
+
* 1. Using a custom client that doesn't recognize this header
|
|
141
|
+
* 2. Security requirements prevent exposing framework details in headers
|
|
142
|
+
*
|
|
143
|
+
* @default true
|
|
144
|
+
*/
|
|
145
|
+
enableClientResponseHints: boolean;
|
|
75
146
|
};
|
|
76
|
-
declare function createKaitoHandler<Context
|
|
147
|
+
declare function createKaitoHandler<Context>(userConfig: MakeOptional<ServerConfig<Context>, 'enableClientResponseHints'>): (request: Request) => Promise<Response>;
|
|
77
148
|
|
|
78
149
|
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextFrom, infer ContextTo, infer Result, infer Path, infer Method, infer Query, infer BodyOutput> ? Route<ContextFrom, ContextTo, Result, `${Prefix}${Path}`, Method, Query, BodyOutput> : never;
|
|
79
150
|
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
@@ -88,17 +159,17 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
|
|
|
88
159
|
private static parseQuery;
|
|
89
160
|
constructor(options: RouterState<R, ContextFrom, ContextTo>);
|
|
90
161
|
get routes(): Set<R>;
|
|
91
|
-
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
162
|
+
add: <Result, Path extends string, Method extends KaitoMethod, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(method: Method, path: Path, route: (Method extends "GET" ? Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "body" | "path" | "method" | "through"> : Omit<Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>, "path" | "method" | "through">) | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>["run"]) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, Method, Query, Body>>;
|
|
92
163
|
readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
|
|
93
|
-
freeze: (server: Omit<ServerConfig<ContextFrom
|
|
164
|
+
freeze: (server: Omit<ServerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
|
|
94
165
|
private readonly method;
|
|
95
|
-
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
96
|
-
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
97
|
-
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
98
|
-
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
99
|
-
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
100
|
-
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
101
|
-
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result
|
|
166
|
+
get: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>, "body" | "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "GET", Query, Body>>;
|
|
167
|
+
post: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "POST", Query, Body>>;
|
|
168
|
+
put: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PUT", Query, Body>>;
|
|
169
|
+
patch: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "PATCH", Query, Body>>;
|
|
170
|
+
delete: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "DELETE", Query, Body>>;
|
|
171
|
+
head: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "HEAD", Query, Body>>;
|
|
172
|
+
options: <Result, Path extends string, Query extends AnyQueryDefinition = {}, Body extends Parsable = never>(path: Path, route: ((arg: RouteArgument<Path, ContextTo, { [Key in keyof Query]: InferParsable<Query[Key]>["output"]; }, InferParsable<Body>["output"]>) => Promise<Result>) | Omit<Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>, "path" | "method" | "through">) => Router<ContextFrom, ContextTo, R | Route<ContextFrom, ContextTo, Result, Path, "OPTIONS", Query, Body>>;
|
|
102
173
|
through: <NextContext>(transform: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
|
|
103
174
|
}
|
|
104
175
|
|
|
@@ -114,6 +185,7 @@ type SuccessfulAPIResponse<T> = {
|
|
|
114
185
|
};
|
|
115
186
|
type APIResponse<T> = ErroredAPIResponse | SuccessfulAPIResponse<T>;
|
|
116
187
|
type AnyResponse = APIResponse<unknown>;
|
|
188
|
+
type MakeOptional<T, K extends keyof T> = T extends T ? Omit<T, K> & Partial<Pick<T, K>> : never;
|
|
117
189
|
type ExtractRouteParams<T extends string> = string extends T ? Record<string, string> : T extends `${string}:${infer Param}/${infer Rest}` ? {
|
|
118
190
|
[k in Param | keyof ExtractRouteParams<Rest>]: string;
|
|
119
191
|
} : T extends `${string}:${infer Param}` ? {
|
|
@@ -172,4 +244,4 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
|
|
|
172
244
|
};
|
|
173
245
|
type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
|
|
174
246
|
|
|
175
|
-
export { type APIResponse, type
|
|
247
|
+
export { type APIResponse, type AnyQueryDefinition, type AnyResponse, type AnyRoute, type Before, type ErroredAPIResponse, type ExtractRouteParams, type GetContext, type InferParsable, type InferRoutes, KaitoError, type KaitoMethod, KaitoRequest, type MakeOptional, type Parsable, type Route, type RouteArgument, Router, type RouterState, type ServerConfig, type SuccessfulAPIResponse, type Through, WrappedError, createKaitoHandler, createUtilities, parsable };
|
package/dist/index.js
CHANGED
|
@@ -74,6 +74,9 @@ var KaitoResponse = class {
|
|
|
74
74
|
return this._headers;
|
|
75
75
|
}
|
|
76
76
|
status(status) {
|
|
77
|
+
if (status === void 0) {
|
|
78
|
+
return this._status;
|
|
79
|
+
}
|
|
77
80
|
this._status = status;
|
|
78
81
|
return this;
|
|
79
82
|
}
|
|
@@ -200,10 +203,10 @@ var Router = class _Router {
|
|
|
200
203
|
const request = new KaitoRequest(url, req);
|
|
201
204
|
const response = new KaitoResponse();
|
|
202
205
|
try {
|
|
203
|
-
const rootCtx = await server.getContext(request, response);
|
|
204
|
-
const ctx = await route.through(rootCtx);
|
|
205
206
|
const body = route.body ? await route.body.parse(await req.json()) : void 0;
|
|
206
207
|
const query = _Router.parseQuery(route.query, url);
|
|
208
|
+
const rootCtx = await server.getContext(request, response);
|
|
209
|
+
const ctx = await route.through(rootCtx);
|
|
207
210
|
const result = await route.run({
|
|
208
211
|
ctx,
|
|
209
212
|
body,
|
|
@@ -211,6 +214,9 @@ var Router = class _Router {
|
|
|
211
214
|
params
|
|
212
215
|
});
|
|
213
216
|
if (result instanceof Response) {
|
|
217
|
+
if (server.enableClientResponseHints) {
|
|
218
|
+
result.headers.set("x-kaito-is-response", "1");
|
|
219
|
+
}
|
|
214
220
|
return result;
|
|
215
221
|
}
|
|
216
222
|
return response.toResponse({
|
|
@@ -256,20 +262,21 @@ var Router = class _Router {
|
|
|
256
262
|
};
|
|
257
263
|
|
|
258
264
|
// src/server.ts
|
|
259
|
-
function createKaitoHandler(
|
|
260
|
-
const
|
|
265
|
+
function createKaitoHandler(userConfig) {
|
|
266
|
+
const config = {
|
|
267
|
+
enableClientResponseHints: true,
|
|
268
|
+
...userConfig
|
|
269
|
+
};
|
|
270
|
+
const handle = config.router.freeze(config);
|
|
261
271
|
return async (request) => {
|
|
262
|
-
let before = void 0;
|
|
263
272
|
if (config.before) {
|
|
264
273
|
const result = await config.before(request);
|
|
265
|
-
if (result instanceof Response)
|
|
266
|
-
return result;
|
|
267
|
-
}
|
|
268
|
-
before = result;
|
|
274
|
+
if (result instanceof Response) return result;
|
|
269
275
|
}
|
|
270
|
-
const response = await
|
|
271
|
-
if (
|
|
272
|
-
await config.
|
|
276
|
+
const response = await handle(request);
|
|
277
|
+
if (config.transform) {
|
|
278
|
+
const result = await config.transform(request, response);
|
|
279
|
+
if (result instanceof Response) return result;
|
|
273
280
|
}
|
|
274
281
|
return response;
|
|
275
282
|
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/stream/stream.ts
|
|
21
|
+
var stream_exports = {};
|
|
22
|
+
__export(stream_exports, {
|
|
23
|
+
KaitoSSEResponse: () => KaitoSSEResponse,
|
|
24
|
+
KaitoStreamResponse: () => KaitoStreamResponse,
|
|
25
|
+
SSEController: () => SSEController,
|
|
26
|
+
sse: () => sse,
|
|
27
|
+
sseEventToString: () => sseEventToString,
|
|
28
|
+
stream: () => stream
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(stream_exports);
|
|
31
|
+
var KaitoStreamResponse = class extends Response {
|
|
32
|
+
constructor(body) {
|
|
33
|
+
super(body, {
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "text/event-stream",
|
|
36
|
+
"Cache-Control": "no-cache",
|
|
37
|
+
Connection: "keep-alive"
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async *[Symbol.asyncIterator]() {
|
|
42
|
+
for await (const chunk of this.body) {
|
|
43
|
+
yield chunk;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var KaitoSSEResponse = class extends KaitoStreamResponse {
|
|
48
|
+
};
|
|
49
|
+
function stream(body) {
|
|
50
|
+
return new KaitoStreamResponse(new ReadableStream(body));
|
|
51
|
+
}
|
|
52
|
+
function sseEventToString(event) {
|
|
53
|
+
let result = "";
|
|
54
|
+
if (event.event) {
|
|
55
|
+
result += `event:${event.event}
|
|
56
|
+
`;
|
|
57
|
+
}
|
|
58
|
+
if (event.id) {
|
|
59
|
+
result += `id:${event.id}
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
if (event.retry) {
|
|
63
|
+
result += `retry:${event.retry}
|
|
64
|
+
`;
|
|
65
|
+
}
|
|
66
|
+
if (event.data) {
|
|
67
|
+
result += `data:${event.data}`;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
var SSEController = class {
|
|
72
|
+
controller;
|
|
73
|
+
constructor(controller) {
|
|
74
|
+
this.controller = controller;
|
|
75
|
+
}
|
|
76
|
+
enqueue(event) {
|
|
77
|
+
this.controller.enqueue(sseEventToString(event) + "\n\n");
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
this.controller.close();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
function sse(source) {
|
|
84
|
+
const start = source.start;
|
|
85
|
+
const pull = source.pull;
|
|
86
|
+
const cancel = source.cancel;
|
|
87
|
+
const readable = new ReadableStream({
|
|
88
|
+
...cancel ? { cancel } : {},
|
|
89
|
+
...start ? {
|
|
90
|
+
start: async (controller) => {
|
|
91
|
+
await start(new SSEController(controller));
|
|
92
|
+
}
|
|
93
|
+
} : {},
|
|
94
|
+
...pull ? {
|
|
95
|
+
pull: async (controller) => {
|
|
96
|
+
await pull(new SSEController(controller));
|
|
97
|
+
}
|
|
98
|
+
} : {}
|
|
99
|
+
});
|
|
100
|
+
return new KaitoSSEResponse(readable);
|
|
101
|
+
}
|
|
102
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
103
|
+
0 && (module.exports = {
|
|
104
|
+
KaitoSSEResponse,
|
|
105
|
+
KaitoStreamResponse,
|
|
106
|
+
SSEController,
|
|
107
|
+
sse,
|
|
108
|
+
sseEventToString,
|
|
109
|
+
stream
|
|
110
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
declare class KaitoStreamResponse<T> extends Response {
|
|
2
|
+
constructor(body: ReadableStream<T>);
|
|
3
|
+
[Symbol.asyncIterator](): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
|
|
4
|
+
}
|
|
5
|
+
declare class KaitoSSEResponse extends KaitoStreamResponse<string> {
|
|
6
|
+
}
|
|
7
|
+
declare function stream<T = string>(body: UnderlyingDefaultSource<T>): KaitoStreamResponse<T>;
|
|
8
|
+
type SSEEvent = ({
|
|
9
|
+
data: string;
|
|
10
|
+
event: string;
|
|
11
|
+
} | {
|
|
12
|
+
data: string;
|
|
13
|
+
event?: string | undefined;
|
|
14
|
+
} | {
|
|
15
|
+
data?: string | undefined;
|
|
16
|
+
event: string;
|
|
17
|
+
}) & {
|
|
18
|
+
retry?: number;
|
|
19
|
+
id?: string;
|
|
20
|
+
};
|
|
21
|
+
declare function sseEventToString(event: SSEEvent): string;
|
|
22
|
+
declare class SSEController {
|
|
23
|
+
private readonly controller;
|
|
24
|
+
constructor(controller: ReadableStreamDefaultController<string>);
|
|
25
|
+
enqueue(event: SSEEvent): void;
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
28
|
+
interface SSESource {
|
|
29
|
+
cancel?: UnderlyingSourceCancelCallback;
|
|
30
|
+
start?(controller: SSEController): Promise<void>;
|
|
31
|
+
pull?(controller: SSEController): Promise<void>;
|
|
32
|
+
}
|
|
33
|
+
declare function sse(source: SSESource): KaitoSSEResponse;
|
|
34
|
+
|
|
35
|
+
export { KaitoSSEResponse, KaitoStreamResponse, SSEController, type SSEEvent, type SSESource, sse, sseEventToString, stream };
|