@kaito-http/core 3.0.1 → 3.0.2

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.
@@ -0,0 +1,60 @@
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/cors/cors.ts
21
+ var cors_exports = {};
22
+ __export(cors_exports, {
23
+ experimental_createCORSTransform: () => experimental_createCORSTransform,
24
+ experimental_createOriginMatcher: () => experimental_createOriginMatcher
25
+ });
26
+ module.exports = __toCommonJS(cors_exports);
27
+ function experimental_createOriginMatcher(origins) {
28
+ if (origins.length === 0) {
29
+ return () => false;
30
+ }
31
+ const source = origins.map((origin) => {
32
+ if (origin.startsWith("*.")) {
33
+ const escapedDomain = origin.slice(2).replace(/[.+?^${}()|[\]\\]/g, "\\$&");
34
+ return `^(?:https?://)[^.]+\\.${escapedDomain}$`;
35
+ } else {
36
+ const escapedOrigin = origin.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
37
+ return `^${escapedOrigin}$`;
38
+ }
39
+ }).join("|");
40
+ const regex = new RegExp(source);
41
+ return (origin) => regex.test(origin);
42
+ }
43
+ function experimental_createCORSTransform(origins) {
44
+ const matcher = experimental_createOriginMatcher(origins);
45
+ return (request, response) => {
46
+ const origin = request.headers.get("Origin");
47
+ if (origin && matcher(origin)) {
48
+ response.headers.set("Access-Control-Allow-Origin", origin);
49
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
50
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
51
+ response.headers.set("Access-Control-Max-Age", "86400");
52
+ response.headers.set("Access-Control-Allow-Credentials", "true");
53
+ }
54
+ };
55
+ }
56
+ // Annotate the CommonJS export names for ESM import in node:
57
+ 0 && (module.exports = {
58
+ experimental_createCORSTransform,
59
+ experimental_createOriginMatcher
60
+ });
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Creates a function that matches origins against a predefined set of patterns, supporting wildcards.
3
+ * The matcher handles both exact matches and wildcard subdomain patterns (e.g., '*.example.com').
4
+ *
5
+ * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
6
+ *
7
+ * @param origins Array of origin patterns to match against.
8
+ * Patterns can be exact origins (e.g., 'https://example.com') or wildcard patterns (e.g., '*.example.com') that match subdomains.
9
+ * @returns A function that tests if an origin matches any of the patterns
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const allowedOrigins = [
14
+ * 'https://example.com',
15
+ * '*.trusted-domain.com' // Won't match https://evil-domain.com, only subdomains
16
+ * ];
17
+ *
18
+ * const matcher = createOriginMatcher(allowedOrigins);
19
+ *
20
+ * // Exact match
21
+ * console.log(matcher('https://example.com')); // true
22
+ * console.log(matcher('http://example.com')); // false
23
+ *
24
+ * // Wildcard subdomain matches
25
+ * console.log(matcher('https://app.trusted-domain.com')); // true
26
+ * console.log(matcher('https://staging.trusted-domain.com')); // true
27
+ * console.log(matcher('https://trusted-domain.com')); // false, because it's not a subdomain
28
+ * console.log(matcher('https://evil-domain.com')); // false
29
+ * ```
30
+ */
31
+ declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
32
+ /**
33
+ * Create a function to apply CORS headers with sane defaults for most apps.
34
+ *
35
+ * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
36
+ *
37
+ * @param options Options object
38
+ * @returns A function that will mutate the Response object by applying the CORS headers
39
+ * @example
40
+ * ```ts
41
+ * const cors = createCORSHandler({
42
+ * origins: ['https://example.com', "*.allows-subdomains.com", "http://localhost:3000"],
43
+ * });
44
+ *
45
+ * const handler = createKaitoHandler({
46
+ * // ...
47
+ * transform: async (request, response) => {
48
+ * cors(request, response);
49
+ * }
50
+ * });
51
+ * ```
52
+ */
53
+ declare function experimental_createCORSTransform(origins: string[]): (request: Request, response: Response) => void;
54
+
55
+ export { experimental_createCORSTransform, experimental_createOriginMatcher };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Creates a function that matches origins against a predefined set of patterns, supporting wildcards.
3
+ * The matcher handles both exact matches and wildcard subdomain patterns (e.g., '*.example.com').
4
+ *
5
+ * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
6
+ *
7
+ * @param origins Array of origin patterns to match against.
8
+ * Patterns can be exact origins (e.g., 'https://example.com') or wildcard patterns (e.g., '*.example.com') that match subdomains.
9
+ * @returns A function that tests if an origin matches any of the patterns
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const allowedOrigins = [
14
+ * 'https://example.com',
15
+ * '*.trusted-domain.com' // Won't match https://evil-domain.com, only subdomains
16
+ * ];
17
+ *
18
+ * const matcher = createOriginMatcher(allowedOrigins);
19
+ *
20
+ * // Exact match
21
+ * console.log(matcher('https://example.com')); // true
22
+ * console.log(matcher('http://example.com')); // false
23
+ *
24
+ * // Wildcard subdomain matches
25
+ * console.log(matcher('https://app.trusted-domain.com')); // true
26
+ * console.log(matcher('https://staging.trusted-domain.com')); // true
27
+ * console.log(matcher('https://trusted-domain.com')); // false, because it's not a subdomain
28
+ * console.log(matcher('https://evil-domain.com')); // false
29
+ * ```
30
+ */
31
+ declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
32
+ /**
33
+ * Create a function to apply CORS headers with sane defaults for most apps.
34
+ *
35
+ * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
36
+ *
37
+ * @param options Options object
38
+ * @returns A function that will mutate the Response object by applying the CORS headers
39
+ * @example
40
+ * ```ts
41
+ * const cors = createCORSHandler({
42
+ * origins: ['https://example.com', "*.allows-subdomains.com", "http://localhost:3000"],
43
+ * });
44
+ *
45
+ * const handler = createKaitoHandler({
46
+ * // ...
47
+ * transform: async (request, response) => {
48
+ * cors(request, response);
49
+ * }
50
+ * });
51
+ * ```
52
+ */
53
+ declare function experimental_createCORSTransform(origins: string[]): (request: Request, response: Response) => void;
54
+
55
+ export { experimental_createCORSTransform, experimental_createOriginMatcher };
@@ -0,0 +1,34 @@
1
+ // src/cors/cors.ts
2
+ function experimental_createOriginMatcher(origins) {
3
+ if (origins.length === 0) {
4
+ return () => false;
5
+ }
6
+ const source = origins.map((origin) => {
7
+ if (origin.startsWith("*.")) {
8
+ const escapedDomain = origin.slice(2).replace(/[.+?^${}()|[\]\\]/g, "\\$&");
9
+ return `^(?:https?://)[^.]+\\.${escapedDomain}$`;
10
+ } else {
11
+ const escapedOrigin = origin.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
12
+ return `^${escapedOrigin}$`;
13
+ }
14
+ }).join("|");
15
+ const regex = new RegExp(source);
16
+ return (origin) => regex.test(origin);
17
+ }
18
+ function experimental_createCORSTransform(origins) {
19
+ const matcher = experimental_createOriginMatcher(origins);
20
+ return (request, response) => {
21
+ const origin = request.headers.get("Origin");
22
+ if (origin && matcher(origin)) {
23
+ response.headers.set("Access-Control-Allow-Origin", origin);
24
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
25
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
26
+ response.headers.set("Access-Control-Max-Age", "86400");
27
+ response.headers.set("Access-Control-Allow-Credentials", "true");
28
+ }
29
+ };
30
+ }
31
+ export {
32
+ experimental_createCORSTransform,
33
+ experimental_createOriginMatcher
34
+ };
package/dist/index.d.cts CHANGED
@@ -154,7 +154,7 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
154
154
  method: Method;
155
155
  run(arg: RouteArgument<Path, ContextTo, {
156
156
  [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result>;
157
+ }, InferParsable<Body>['output']>): Promise<Result> | Result;
158
158
  };
159
159
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
160
160
 
@@ -175,13 +175,13 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
175
175
  readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
176
176
  freeze: (server: Omit<HandlerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
177
177
  private readonly method;
178
- 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>>;
179
- 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>>;
180
- 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>>;
181
- 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>>;
182
- 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>>;
183
- 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>>;
184
- 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>>;
178
+ 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"]>) => Result | 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>>;
179
+ 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"]>) => Result | 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>>;
180
+ 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"]>) => Result | 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>>;
181
+ 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"]>) => Result | 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>>;
182
+ 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"]>) => Result | 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>>;
183
+ 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"]>) => Result | 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>>;
184
+ 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"]>) => Result | 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>>;
185
185
  through: <NextContext>(through: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
186
186
  }
187
187
 
@@ -228,7 +228,7 @@ type HandlerConfig<ContextFrom> = {
228
228
  * }
229
229
  * ```
230
230
  */
231
- before?: (req: Request) => Promise<Response | void | undefined>;
231
+ before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
232
232
  /**
233
233
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
234
  *
@@ -248,7 +248,7 @@ type HandlerConfig<ContextFrom> = {
248
248
  * }
249
249
  * ```
250
250
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
251
+ transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
252
252
  };
253
253
  declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
254
254
 
package/dist/index.d.ts CHANGED
@@ -154,7 +154,7 @@ type Route<ContextFrom, ContextTo, Result, Path extends string, Method extends K
154
154
  method: Method;
155
155
  run(arg: RouteArgument<Path, ContextTo, {
156
156
  [Key in keyof Query]: InferParsable<Query[Key]>['output'];
157
- }, InferParsable<Body>['output']>): Promise<Result>;
157
+ }, InferParsable<Body>['output']>): Promise<Result> | Result;
158
158
  };
159
159
  type AnyRoute<ContextFrom = any, ContextTo = any> = Route<ContextFrom, ContextTo, any, any, any, AnyQueryDefinition, any>;
160
160
 
@@ -175,13 +175,13 @@ declare class Router<ContextFrom, ContextTo, R extends AnyRoute> {
175
175
  readonly merge: <PathPrefix extends `/${string}`, OtherRoutes extends AnyRoute>(pathPrefix: PathPrefix, other: Router<ContextFrom, unknown, OtherRoutes>) => Router<ContextFrom, ContextTo, Extract<R | PrefixRoutesPath<PathPrefix, OtherRoutes>, AnyRoute>>;
176
176
  freeze: (server: Omit<HandlerConfig<ContextFrom>, "router">) => (req: Request) => Promise<Response>;
177
177
  private readonly method;
178
- 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>>;
179
- 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>>;
180
- 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>>;
181
- 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>>;
182
- 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>>;
183
- 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>>;
184
- 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>>;
178
+ 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"]>) => Result | 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>>;
179
+ 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"]>) => Result | 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>>;
180
+ 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"]>) => Result | 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>>;
181
+ 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"]>) => Result | 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>>;
182
+ 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"]>) => Result | 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>>;
183
+ 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"]>) => Result | 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>>;
184
+ 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"]>) => Result | 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>>;
185
185
  through: <NextContext>(through: (context: ContextTo) => Promise<NextContext>) => Router<ContextFrom, NextContext, R>;
186
186
  }
187
187
 
@@ -228,7 +228,7 @@ type HandlerConfig<ContextFrom> = {
228
228
  * }
229
229
  * ```
230
230
  */
231
- before?: (req: Request) => Promise<Response | void | undefined>;
231
+ before?: (req: Request) => Promise<Response | void | undefined> | Response | void | undefined;
232
232
  /**
233
233
  * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
234
234
  *
@@ -248,7 +248,7 @@ type HandlerConfig<ContextFrom> = {
248
248
  * }
249
249
  * ```
250
250
  */
251
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
251
+ transform?: (req: Request, res: Response) => Promise<Response | void | undefined> | Response | void | undefined;
252
252
  };
253
253
  declare function createKaitoHandler<Context>(config: HandlerConfig<Context>): (request: Request) => Promise<Response>;
254
254
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaito-http/core",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "type": "module",
5
5
  "author": "Alistair Smith <hi@alistair.sh>",
6
6
  "description": "Functional HTTP Framework for TypeScript",
@@ -12,18 +12,16 @@
12
12
  "exports": {
13
13
  "./package.json": "./package.json",
14
14
  ".": {
15
- "import": {
16
- "types": "./src/index.ts",
17
- "default": "./dist/index.js"
18
- },
15
+ "import": "./dist/index.js",
19
16
  "require": "./dist/index.cjs"
20
17
  },
21
18
  "./stream": {
22
- "import": {
23
- "types": "./src/stream/stream.ts",
24
- "default": "./dist/stream/stream.js"
25
- },
19
+ "import": "./dist/stream/stream.js",
26
20
  "require": "./dist/stream/stream.cjs"
21
+ },
22
+ "./cors": {
23
+ "import": "./dist/cors/cors.js",
24
+ "require": "./dist/cors/cors.cjs"
27
25
  }
28
26
  },
29
27
  "homepage": "https://github.com/kaito-http/kaito",
@@ -43,8 +41,7 @@
43
41
  "files": [
44
42
  "package.json",
45
43
  "README.md",
46
- "dist",
47
- "src"
44
+ "dist"
48
45
  ],
49
46
  "bugs": {
50
47
  "url": "https://github.com/kaito-http/kaito/issues"
package/src/error.ts DELETED
@@ -1,26 +0,0 @@
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/handler.ts DELETED
@@ -1,96 +0,0 @@
1
- import type {KaitoError} from './error.ts';
2
- import type {KaitoRequest} from './request.ts';
3
- import type {Router} from './router/router.ts';
4
- import type {GetContext} from './util.ts';
5
-
6
- export type HandlerConfig<ContextFrom> = {
7
- /**
8
- * The root router to mount on this handler.
9
- */
10
- router: Router<ContextFrom, unknown, any>;
11
-
12
- /**
13
- * A function that is called to get the context for a request.
14
- *
15
- * This is useful for things like authentication, to pass in a database connection, etc.
16
- *
17
- * It's fine for this function to throw; if it does, the error is passed to the `onError` function.
18
- */
19
- getContext: GetContext<ContextFrom>;
20
-
21
- /**
22
- * A function that is called when an error occurs inside a route handler.
23
- *
24
- * The result of this function is used to determine the response status and message, and is
25
- * always sent to the client. You could include logic to check for production vs development
26
- * environments here, and this would also be a good place to include error tracking
27
- * like Sentry or Rollbar.
28
- *
29
- * @param arg - The error thrown, and the KaitoRequest instance
30
- * @returns A KaitoError or an object with a status and message
31
- */
32
- onError: (arg: {error: Error; req: KaitoRequest}) => Promise<KaitoError | {status: number; message: string}>;
33
-
34
- /**
35
- * A function that is called before every request. Most useful for bailing out early in the case of an OPTIONS request.
36
- *
37
- * @example
38
- * ```ts
39
- * before: async req => {
40
- * if (req.method === 'OPTIONS') {
41
- * return new Response(null, {status: 204});
42
- * }
43
- * }
44
- * ```
45
- */
46
- before?: (req: Request) => Promise<Response | void | undefined>;
47
-
48
- /**
49
- * Transforms the response before it is sent to the client. Very useful for settings headers like CORS.
50
- *
51
- * You can also return a new response in this function, or just mutate the current one.
52
- *
53
- * This function WILL receive the result of `.before()` if you return a response from it. This means
54
- * you only need to define headers in a single place.
55
- *
56
- * @example
57
- * ```ts
58
- * transform: async (req, res) => {
59
- * res.headers.set('Access-Control-Allow-Origin', 'http://localhost:3000');
60
- * res.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
61
- * res.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
62
- * res.headers.set('Access-Control-Max-Age', '86400');
63
- * res.headers.set('Access-Control-Allow-Credentials', 'true');
64
- * }
65
- * ```
66
- */
67
- transform?: (req: Request, res: Response) => Promise<Response | void | undefined>;
68
- };
69
-
70
- export function createKaitoHandler<Context>(config: HandlerConfig<Context>) {
71
- const handle = config.router.freeze(config);
72
-
73
- return async (request: Request): Promise<Response> => {
74
- if (config.before) {
75
- const result = await config.before(request);
76
-
77
- if (result instanceof Response) {
78
- if (config.transform) {
79
- const result2 = await config.transform(request, result);
80
- if (result2 instanceof Response) return result;
81
- }
82
-
83
- return result;
84
- }
85
- }
86
-
87
- const response = await handle(request);
88
-
89
- if (config.transform) {
90
- const result = await config.transform(request, response);
91
- if (result instanceof Response) return result;
92
- }
93
-
94
- return response;
95
- };
96
- }
package/src/head.ts DELETED
@@ -1,83 +0,0 @@
1
- import type {APIResponse} from './util.ts';
2
-
3
- /**
4
- * This class is merely a wrapper around a `Headers` object and a status code.
5
- * It's used while the router is executing a route to store any mutations to the status
6
- * code or headers that the developer may want to make.
7
- *
8
- * This exists because there's otherwise no way to indicate back to Kaito that
9
- * the developer wants to change the status code or headers.
10
- *
11
- * @example
12
- * ```ts
13
- * const response = new KaitoHead();
14
- *
15
- * response.status(200);
16
- * response.headers.set('Content-Type', 'application/json');
17
- *
18
- * console.log(response.headers); // Headers {'content-type': 'application/json'}
19
- * ```
20
- */
21
- export class KaitoHead {
22
- private _headers: Headers | null;
23
- private _status: number;
24
-
25
- public constructor() {
26
- this._headers = null;
27
- this._status = 200;
28
- }
29
-
30
- public get headers() {
31
- if (this._headers === null) {
32
- this._headers = new Headers();
33
- }
34
-
35
- return this._headers;
36
- }
37
-
38
- /**
39
- * Gets the status code of this KaitoHead instance
40
- * @returns The status code
41
- */
42
- public status(): number;
43
-
44
- /**
45
- * Sets the status code of this KaitoHead instance
46
- * @param status The status code to set
47
- * @returns This KaitoHead instance
48
- */
49
- public status(status: number): this;
50
-
51
- public status(status?: number) {
52
- if (status === undefined) {
53
- return this._status;
54
- }
55
-
56
- this._status = status;
57
- return this;
58
- }
59
-
60
- /**
61
- * Turn this KaitoHead instance into a Response instance
62
- * @param body The Kaito JSON format to be sent as the response body
63
- * @returns A Response instance, ready to be sent
64
- */
65
- public toResponse<T>(body: APIResponse<T>): Response {
66
- const init: ResponseInit = {
67
- status: this._status,
68
- };
69
-
70
- if (this._headers) {
71
- init.headers = this._headers;
72
- }
73
-
74
- return Response.json(body, init);
75
- }
76
-
77
- /**
78
- * Whether this KaitoHead instance has been touched/modified
79
- */
80
- public get touched() {
81
- return this._status !== 200 || this._headers !== null;
82
- }
83
- }
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export * from './error.ts';
2
- export * from './handler.ts';
3
- export * from './request.ts';
4
- export * from './route.ts';
5
- export * from './router/router.ts';
6
- export type {KaitoMethod} from './router/types.ts';
7
- export * from './util.ts';
package/src/request.ts DELETED
@@ -1,47 +0,0 @@
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
- }