@kaito-http/core 3.0.0-beta.16 → 3.0.0-beta.21
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 +27 -26
- package/dist/index.d.cts +80 -25
- package/dist/index.d.ts +80 -25
- package/dist/index.js +27 -26
- package/dist/stream/stream.cjs +142 -0
- package/dist/stream/stream.d.cts +39 -0
- package/dist/stream/stream.d.ts +39 -0
- package/dist/stream/stream.js +111 -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 +254 -0
- package/src/router/types.ts +1 -0
- package/src/server.ts +87 -0
- package/src/stream/stream.ts +159 -0
- package/src/util.ts +66 -0
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
sseFromAnyReadable: () => sseFromAnyReadable,
|
|
29
|
+
stream: () => stream
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(stream_exports);
|
|
32
|
+
var KaitoStreamResponse = class extends Response {
|
|
33
|
+
constructor(body) {
|
|
34
|
+
super(body, {
|
|
35
|
+
headers: {
|
|
36
|
+
"Content-Type": "text/event-stream",
|
|
37
|
+
"Cache-Control": "no-cache",
|
|
38
|
+
Connection: "keep-alive"
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
async *[Symbol.asyncIterator]() {
|
|
43
|
+
for await (const chunk of this.body) {
|
|
44
|
+
yield chunk;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var KaitoSSEResponse = class extends KaitoStreamResponse {
|
|
49
|
+
};
|
|
50
|
+
function stream(body) {
|
|
51
|
+
return new KaitoStreamResponse(new ReadableStream(body));
|
|
52
|
+
}
|
|
53
|
+
function sseEventToString(event) {
|
|
54
|
+
let result = "";
|
|
55
|
+
if (event.event) {
|
|
56
|
+
result += `event:${event.event}
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
if (event.id) {
|
|
60
|
+
result += `id:${event.id}
|
|
61
|
+
`;
|
|
62
|
+
}
|
|
63
|
+
if (event.retry) {
|
|
64
|
+
result += `retry:${event.retry}
|
|
65
|
+
`;
|
|
66
|
+
}
|
|
67
|
+
if (event.data !== void 0) {
|
|
68
|
+
result += `data:${JSON.stringify(event.data)}`;
|
|
69
|
+
}
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
var SSEController = class {
|
|
73
|
+
controller;
|
|
74
|
+
constructor(controller) {
|
|
75
|
+
this.controller = controller;
|
|
76
|
+
}
|
|
77
|
+
enqueue(event) {
|
|
78
|
+
this.controller.enqueue(sseEventToString(event) + "\n\n");
|
|
79
|
+
}
|
|
80
|
+
close() {
|
|
81
|
+
this.controller.close();
|
|
82
|
+
}
|
|
83
|
+
[Symbol.dispose]() {
|
|
84
|
+
this.close();
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
function sseFromSource(source) {
|
|
88
|
+
const start = source.start;
|
|
89
|
+
const pull = source.pull;
|
|
90
|
+
const cancel = source.cancel;
|
|
91
|
+
const readable = new ReadableStream({
|
|
92
|
+
...cancel ? { cancel } : {},
|
|
93
|
+
...start ? {
|
|
94
|
+
start: async (controller) => {
|
|
95
|
+
await start(new SSEController(controller));
|
|
96
|
+
}
|
|
97
|
+
} : {},
|
|
98
|
+
...pull ? {
|
|
99
|
+
pull: async (controller) => {
|
|
100
|
+
await pull(new SSEController(controller));
|
|
101
|
+
}
|
|
102
|
+
} : {}
|
|
103
|
+
});
|
|
104
|
+
return new KaitoSSEResponse(readable);
|
|
105
|
+
}
|
|
106
|
+
function sse(source) {
|
|
107
|
+
const evaluated = typeof source === "function" ? source() : source;
|
|
108
|
+
if ("next" in evaluated) {
|
|
109
|
+
const generator = evaluated;
|
|
110
|
+
return sseFromSource({
|
|
111
|
+
async start(controller) {
|
|
112
|
+
try {
|
|
113
|
+
for await (const event of generator) {
|
|
114
|
+
controller.enqueue(event);
|
|
115
|
+
}
|
|
116
|
+
} finally {
|
|
117
|
+
controller.close();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
} else {
|
|
122
|
+
return sseFromSource(evaluated);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function sseFromAnyReadable(stream2, transform) {
|
|
126
|
+
const transformer = new TransformStream({
|
|
127
|
+
transform: (chunk, controller) => {
|
|
128
|
+
controller.enqueue(transform(chunk));
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
return sse(stream2.pipeThrough(transformer));
|
|
132
|
+
}
|
|
133
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
134
|
+
0 && (module.exports = {
|
|
135
|
+
KaitoSSEResponse,
|
|
136
|
+
KaitoStreamResponse,
|
|
137
|
+
SSEController,
|
|
138
|
+
sse,
|
|
139
|
+
sseEventToString,
|
|
140
|
+
sseFromAnyReadable,
|
|
141
|
+
stream
|
|
142
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
declare class KaitoStreamResponse<R> extends Response {
|
|
2
|
+
constructor(body: ReadableStream<R>);
|
|
3
|
+
[Symbol.asyncIterator](): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
|
|
4
|
+
}
|
|
5
|
+
declare class KaitoSSEResponse<_ClientType> extends KaitoStreamResponse<string> {
|
|
6
|
+
}
|
|
7
|
+
declare function stream<R>(body: UnderlyingDefaultSource<R>): KaitoStreamResponse<R>;
|
|
8
|
+
type SSEEvent<T, E extends string> = ({
|
|
9
|
+
data: T;
|
|
10
|
+
event?: E | undefined;
|
|
11
|
+
} | {
|
|
12
|
+
data?: T | undefined;
|
|
13
|
+
event: E;
|
|
14
|
+
}) & {
|
|
15
|
+
retry?: number;
|
|
16
|
+
id?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Converts an SSE Event into a string, ready for sending to the client
|
|
20
|
+
* @param event The SSE Event
|
|
21
|
+
* @returns A stringified version
|
|
22
|
+
*/
|
|
23
|
+
declare function sseEventToString(event: SSEEvent<unknown, string>): string;
|
|
24
|
+
declare class SSEController<U, E extends string> implements Disposable {
|
|
25
|
+
private readonly controller;
|
|
26
|
+
constructor(controller: ReadableStreamDefaultController<string>);
|
|
27
|
+
enqueue(event: SSEEvent<U, E>): void;
|
|
28
|
+
close(): void;
|
|
29
|
+
[Symbol.dispose](): void;
|
|
30
|
+
}
|
|
31
|
+
interface SSESource<U, E extends string> {
|
|
32
|
+
cancel?: UnderlyingSourceCancelCallback;
|
|
33
|
+
start?(controller: SSEController<U, E>): Promise<void>;
|
|
34
|
+
pull?(controller: SSEController<U, E>): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
declare function sse<U, E extends string, T extends SSEEvent<U, E>>(source: SSESource<U, E> | AsyncGenerator<T, unknown, unknown> | (() => AsyncGenerator<T, unknown, unknown>)): KaitoSSEResponse<T>;
|
|
37
|
+
declare function sseFromAnyReadable<R, U, E extends string>(stream: ReadableStream<R>, transform: (chunk: R) => SSEEvent<U, E>): KaitoSSEResponse<SSEEvent<U, E>>;
|
|
38
|
+
|
|
39
|
+
export { KaitoSSEResponse, KaitoStreamResponse, SSEController, type SSEEvent, type SSESource, sse, sseEventToString, sseFromAnyReadable, stream };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
declare class KaitoStreamResponse<R> extends Response {
|
|
2
|
+
constructor(body: ReadableStream<R>);
|
|
3
|
+
[Symbol.asyncIterator](): AsyncGenerator<Uint8Array<ArrayBufferLike>, void, unknown>;
|
|
4
|
+
}
|
|
5
|
+
declare class KaitoSSEResponse<_ClientType> extends KaitoStreamResponse<string> {
|
|
6
|
+
}
|
|
7
|
+
declare function stream<R>(body: UnderlyingDefaultSource<R>): KaitoStreamResponse<R>;
|
|
8
|
+
type SSEEvent<T, E extends string> = ({
|
|
9
|
+
data: T;
|
|
10
|
+
event?: E | undefined;
|
|
11
|
+
} | {
|
|
12
|
+
data?: T | undefined;
|
|
13
|
+
event: E;
|
|
14
|
+
}) & {
|
|
15
|
+
retry?: number;
|
|
16
|
+
id?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Converts an SSE Event into a string, ready for sending to the client
|
|
20
|
+
* @param event The SSE Event
|
|
21
|
+
* @returns A stringified version
|
|
22
|
+
*/
|
|
23
|
+
declare function sseEventToString(event: SSEEvent<unknown, string>): string;
|
|
24
|
+
declare class SSEController<U, E extends string> implements Disposable {
|
|
25
|
+
private readonly controller;
|
|
26
|
+
constructor(controller: ReadableStreamDefaultController<string>);
|
|
27
|
+
enqueue(event: SSEEvent<U, E>): void;
|
|
28
|
+
close(): void;
|
|
29
|
+
[Symbol.dispose](): void;
|
|
30
|
+
}
|
|
31
|
+
interface SSESource<U, E extends string> {
|
|
32
|
+
cancel?: UnderlyingSourceCancelCallback;
|
|
33
|
+
start?(controller: SSEController<U, E>): Promise<void>;
|
|
34
|
+
pull?(controller: SSEController<U, E>): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
declare function sse<U, E extends string, T extends SSEEvent<U, E>>(source: SSESource<U, E> | AsyncGenerator<T, unknown, unknown> | (() => AsyncGenerator<T, unknown, unknown>)): KaitoSSEResponse<T>;
|
|
37
|
+
declare function sseFromAnyReadable<R, U, E extends string>(stream: ReadableStream<R>, transform: (chunk: R) => SSEEvent<U, E>): KaitoSSEResponse<SSEEvent<U, E>>;
|
|
38
|
+
|
|
39
|
+
export { KaitoSSEResponse, KaitoStreamResponse, SSEController, type SSEEvent, type SSESource, sse, sseEventToString, sseFromAnyReadable, stream };
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// src/stream/stream.ts
|
|
2
|
+
var KaitoStreamResponse = class extends Response {
|
|
3
|
+
constructor(body) {
|
|
4
|
+
super(body, {
|
|
5
|
+
headers: {
|
|
6
|
+
"Content-Type": "text/event-stream",
|
|
7
|
+
"Cache-Control": "no-cache",
|
|
8
|
+
Connection: "keep-alive"
|
|
9
|
+
}
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
async *[Symbol.asyncIterator]() {
|
|
13
|
+
for await (const chunk of this.body) {
|
|
14
|
+
yield chunk;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var KaitoSSEResponse = class extends KaitoStreamResponse {
|
|
19
|
+
};
|
|
20
|
+
function stream(body) {
|
|
21
|
+
return new KaitoStreamResponse(new ReadableStream(body));
|
|
22
|
+
}
|
|
23
|
+
function sseEventToString(event) {
|
|
24
|
+
let result = "";
|
|
25
|
+
if (event.event) {
|
|
26
|
+
result += `event:${event.event}
|
|
27
|
+
`;
|
|
28
|
+
}
|
|
29
|
+
if (event.id) {
|
|
30
|
+
result += `id:${event.id}
|
|
31
|
+
`;
|
|
32
|
+
}
|
|
33
|
+
if (event.retry) {
|
|
34
|
+
result += `retry:${event.retry}
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
if (event.data !== void 0) {
|
|
38
|
+
result += `data:${JSON.stringify(event.data)}`;
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
}
|
|
42
|
+
var SSEController = class {
|
|
43
|
+
controller;
|
|
44
|
+
constructor(controller) {
|
|
45
|
+
this.controller = controller;
|
|
46
|
+
}
|
|
47
|
+
enqueue(event) {
|
|
48
|
+
this.controller.enqueue(sseEventToString(event) + "\n\n");
|
|
49
|
+
}
|
|
50
|
+
close() {
|
|
51
|
+
this.controller.close();
|
|
52
|
+
}
|
|
53
|
+
[Symbol.dispose]() {
|
|
54
|
+
this.close();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
function sseFromSource(source) {
|
|
58
|
+
const start = source.start;
|
|
59
|
+
const pull = source.pull;
|
|
60
|
+
const cancel = source.cancel;
|
|
61
|
+
const readable = new ReadableStream({
|
|
62
|
+
...cancel ? { cancel } : {},
|
|
63
|
+
...start ? {
|
|
64
|
+
start: async (controller) => {
|
|
65
|
+
await start(new SSEController(controller));
|
|
66
|
+
}
|
|
67
|
+
} : {},
|
|
68
|
+
...pull ? {
|
|
69
|
+
pull: async (controller) => {
|
|
70
|
+
await pull(new SSEController(controller));
|
|
71
|
+
}
|
|
72
|
+
} : {}
|
|
73
|
+
});
|
|
74
|
+
return new KaitoSSEResponse(readable);
|
|
75
|
+
}
|
|
76
|
+
function sse(source) {
|
|
77
|
+
const evaluated = typeof source === "function" ? source() : source;
|
|
78
|
+
if ("next" in evaluated) {
|
|
79
|
+
const generator = evaluated;
|
|
80
|
+
return sseFromSource({
|
|
81
|
+
async start(controller) {
|
|
82
|
+
try {
|
|
83
|
+
for await (const event of generator) {
|
|
84
|
+
controller.enqueue(event);
|
|
85
|
+
}
|
|
86
|
+
} finally {
|
|
87
|
+
controller.close();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
return sseFromSource(evaluated);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function sseFromAnyReadable(stream2, transform) {
|
|
96
|
+
const transformer = new TransformStream({
|
|
97
|
+
transform: (chunk, controller) => {
|
|
98
|
+
controller.enqueue(transform(chunk));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return sse(stream2.pipeThrough(transformer));
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
KaitoSSEResponse,
|
|
105
|
+
KaitoStreamResponse,
|
|
106
|
+
SSEController,
|
|
107
|
+
sse,
|
|
108
|
+
sseEventToString,
|
|
109
|
+
sseFromAnyReadable,
|
|
110
|
+
stream
|
|
111
|
+
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaito-http/core",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"author": "Alistair Smith <hi@alistair.sh>",
|
|
6
6
|
"description": "Functional HTTP Framework for TypeScript",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsup"
|
|
8
|
+
"build": "tsup",
|
|
9
|
+
"attw": "attw --profile node16 --pack .",
|
|
10
|
+
"test": "node --test --import=tsx ./src/**/*.test.ts"
|
|
9
11
|
},
|
|
10
12
|
"exports": {
|
|
11
13
|
"./package.json": "./package.json",
|
|
12
14
|
".": {
|
|
13
|
-
"import":
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./src/index.ts",
|
|
17
|
+
"default": "./dist/index.js"
|
|
18
|
+
},
|
|
14
19
|
"require": "./dist/index.cjs"
|
|
20
|
+
},
|
|
21
|
+
"./stream": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./src/stream/stream.ts",
|
|
24
|
+
"default": "./dist/stream/stream.js"
|
|
25
|
+
},
|
|
26
|
+
"require": "./dist/stream/stream.cjs"
|
|
15
27
|
}
|
|
16
28
|
},
|
|
17
29
|
"homepage": "https://github.com/kaito-http/kaito",
|
|
@@ -23,6 +35,7 @@
|
|
|
23
35
|
],
|
|
24
36
|
"license": "MIT",
|
|
25
37
|
"devDependencies": {
|
|
38
|
+
"@arethetypeswrong/cli": "^0.17.2",
|
|
26
39
|
"@types/node": "^22.10.2",
|
|
27
40
|
"tsup": "^8.3.5",
|
|
28
41
|
"typescript": "^5.7.2"
|
|
@@ -30,7 +43,8 @@
|
|
|
30
43
|
"files": [
|
|
31
44
|
"package.json",
|
|
32
45
|
"README.md",
|
|
33
|
-
"dist"
|
|
46
|
+
"dist",
|
|
47
|
+
"src"
|
|
34
48
|
],
|
|
35
49
|
"bugs": {
|
|
36
50
|
"url": "https://github.com/kaito-http/kaito/issues"
|
package/src/error.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class WrappedError<T> extends Error {
|
|
2
|
+
public static maybe<T>(maybeError: T) {
|
|
3
|
+
if (maybeError instanceof Error) {
|
|
4
|
+
return maybeError;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
return WrappedError.from(maybeError);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public static from<T>(data: T) {
|
|
11
|
+
return new WrappedError(data);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private constructor(public readonly data: T) {
|
|
15
|
+
super('Something was thrown, but it was not an instance of Error, so a WrappedError was created.');
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class KaitoError extends Error {
|
|
20
|
+
constructor(
|
|
21
|
+
public readonly status: number,
|
|
22
|
+
message: string,
|
|
23
|
+
) {
|
|
24
|
+
super(message);
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/index.ts
ADDED
package/src/request.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export class KaitoRequest {
|
|
2
|
+
public readonly url: URL;
|
|
3
|
+
|
|
4
|
+
private readonly _request: Request;
|
|
5
|
+
|
|
6
|
+
public constructor(url: URL, request: Request) {
|
|
7
|
+
this._request = request;
|
|
8
|
+
this.url = url;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public get headers() {
|
|
12
|
+
return this._request.headers;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public get method() {
|
|
16
|
+
return this._request.method;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
public async arrayBuffer(): Promise<ArrayBuffer> {
|
|
20
|
+
return this._request.arrayBuffer();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async blob(): Promise<Blob> {
|
|
24
|
+
return this._request.blob();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public async formData(): Promise<FormData> {
|
|
28
|
+
return this._request.formData();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public async bytes(): Promise<Uint8Array> {
|
|
32
|
+
const buffer = await this.arrayBuffer();
|
|
33
|
+
return new Uint8Array(buffer);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public async json(): Promise<unknown> {
|
|
37
|
+
return this._request.json();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async text(): Promise<string> {
|
|
41
|
+
return this._request.text();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public get request() {
|
|
45
|
+
return this._request;
|
|
46
|
+
}
|
|
47
|
+
}
|
package/src/response.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type {APIResponse} from './util.ts';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* This class is similar to a `Response` object from the Web Fetch API, but
|
|
5
|
+
* this with no body stream, and is only used for setting status codes/headers.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const response = new KaitoResponse();
|
|
10
|
+
*
|
|
11
|
+
* response.status(200);
|
|
12
|
+
* response.headers.set('Content-Type', 'application/json');
|
|
13
|
+
*
|
|
14
|
+
* console.log(response.headers); // Headers {'content-type': 'application/json'}
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export class KaitoResponse {
|
|
18
|
+
private _headers: Headers | null;
|
|
19
|
+
private _status: number;
|
|
20
|
+
|
|
21
|
+
public constructor() {
|
|
22
|
+
this._headers = null;
|
|
23
|
+
this._status = 200;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public get headers() {
|
|
27
|
+
if (this._headers === null) {
|
|
28
|
+
this._headers = new Headers();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return this._headers;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets the status code of this KaitoResponse instance
|
|
36
|
+
* @returns The status code
|
|
37
|
+
*/
|
|
38
|
+
public status(): number;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Sets the status code of this KaitoResponse instance
|
|
42
|
+
* @param status The status code to set
|
|
43
|
+
* @returns This KaitoResponse instance
|
|
44
|
+
*/
|
|
45
|
+
public status(status: number): this;
|
|
46
|
+
|
|
47
|
+
public status(status?: number) {
|
|
48
|
+
if (status === undefined) {
|
|
49
|
+
return this._status;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this._status = status;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Turn this KaitoResponse instance into a Response instance
|
|
58
|
+
* @param body The Kaito JSON format to be sent as the response body
|
|
59
|
+
* @returns A Response instance, ready to be sent
|
|
60
|
+
*/
|
|
61
|
+
public toResponse<T>(body: APIResponse<T>): Response {
|
|
62
|
+
const init: ResponseInit = {
|
|
63
|
+
status: this._status,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
if (this._headers) {
|
|
67
|
+
init.headers = this._headers;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return Response.json(body, init);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/route.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import type {KaitoMethod} from './router/types.ts';
|
|
3
|
+
import type {ExtractRouteParams, InferParsable, Parsable} from './util.ts';
|
|
4
|
+
|
|
5
|
+
export type RouteArgument<Path extends string, Context, QueryOutput, BodyOutput> = {
|
|
6
|
+
ctx: Context;
|
|
7
|
+
body: BodyOutput;
|
|
8
|
+
query: QueryOutput;
|
|
9
|
+
params: ExtractRouteParams<Path>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type AnyQueryDefinition = Record<string, Parsable<any, string | undefined>>;
|
|
13
|
+
|
|
14
|
+
export type Through<From, To> = (context: From) => Promise<To>;
|
|
15
|
+
|
|
16
|
+
export type Route<
|
|
17
|
+
// Router context
|
|
18
|
+
ContextFrom,
|
|
19
|
+
ContextTo,
|
|
20
|
+
// Route information
|
|
21
|
+
Result,
|
|
22
|
+
Path extends string,
|
|
23
|
+
Method extends KaitoMethod,
|
|
24
|
+
// Schemas
|
|
25
|
+
Query extends AnyQueryDefinition,
|
|
26
|
+
Body extends Parsable,
|
|
27
|
+
> = {
|
|
28
|
+
through: Through<ContextFrom, ContextTo>;
|
|
29
|
+
body?: Body;
|
|
30
|
+
query?: Query;
|
|
31
|
+
path: Path;
|
|
32
|
+
method: Method;
|
|
33
|
+
run(
|
|
34
|
+
arg: RouteArgument<
|
|
35
|
+
Path,
|
|
36
|
+
ContextTo,
|
|
37
|
+
{
|
|
38
|
+
[Key in keyof Query]: InferParsable<Query[Key]>['output'];
|
|
39
|
+
},
|
|
40
|
+
InferParsable<Body>['output']
|
|
41
|
+
>,
|
|
42
|
+
): Promise<Result>;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type AnyRoute<ContextFrom = any, ContextTo = any> = Route<
|
|
46
|
+
ContextFrom,
|
|
47
|
+
ContextTo,
|
|
48
|
+
any,
|
|
49
|
+
any,
|
|
50
|
+
any,
|
|
51
|
+
AnyQueryDefinition,
|
|
52
|
+
any
|
|
53
|
+
>;
|