@kaito-http/core 4.0.0-beta.6 → 4.0.0-beta.8
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/cors/cors.cjs +13 -6
- package/dist/cors/cors.d.cts +61 -20
- package/dist/cors/cors.d.ts +61 -20
- package/dist/cors/cors.js +13 -6
- package/dist/index.cjs +5 -2
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -2
- package/package.json +1 -1
package/dist/cors/cors.cjs
CHANGED
|
@@ -28,13 +28,20 @@ function experimental_createOriginMatcher(origins) {
|
|
|
28
28
|
if (origins.length === 0) {
|
|
29
29
|
return () => false;
|
|
30
30
|
}
|
|
31
|
+
const escapedCharsRegex = /[.+?^${}()|[\]\\]/g;
|
|
31
32
|
const source = origins.map((origin) => {
|
|
32
|
-
if (origin.
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
if (origin.includes("://*.")) {
|
|
34
|
+
const parts = origin.split("://");
|
|
35
|
+
if (parts.length !== 2) {
|
|
36
|
+
throw new Error(`Invalid origin pattern: ${origin}. Must include protocol (e.g., https://*.example.com)`);
|
|
37
|
+
}
|
|
38
|
+
const [protocol, rest] = parts;
|
|
39
|
+
const domain = rest.slice(2).replace(escapedCharsRegex, "\\$&");
|
|
40
|
+
const pattern = `^${protocol.replace(escapedCharsRegex, "\\$&")}:\\/\\/[^.]+\\.${domain}$`;
|
|
41
|
+
return pattern;
|
|
35
42
|
} else {
|
|
36
|
-
const
|
|
37
|
-
return
|
|
43
|
+
const pattern = `^${origin.replace(escapedCharsRegex, "\\$&")}$`;
|
|
44
|
+
return pattern;
|
|
38
45
|
}
|
|
39
46
|
}).join("|");
|
|
40
47
|
const regex = new RegExp(source);
|
|
@@ -46,7 +53,7 @@ function experimental_createCORSTransform(origins) {
|
|
|
46
53
|
const origin = request.headers.get("Origin");
|
|
47
54
|
if (origin && matcher(origin)) {
|
|
48
55
|
response.headers.set("Access-Control-Allow-Origin", origin);
|
|
49
|
-
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
56
|
+
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
50
57
|
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
51
58
|
response.headers.set("Access-Control-Max-Age", "86400");
|
|
52
59
|
response.headers.set("Access-Control-Allow-Credentials", "true");
|
package/dist/cors/cors.d.cts
CHANGED
|
@@ -1,31 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
3
|
+
* The matcher handles both exact matches and wildcard subdomain patterns.
|
|
4
4
|
*
|
|
5
5
|
* **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
|
|
6
6
|
*
|
|
7
7
|
* @param origins Array of origin patterns to match against.
|
|
8
|
-
*
|
|
8
|
+
* Each pattern MUST include the protocol (http:// or https://).
|
|
9
|
+
* Two types of patterns are supported:
|
|
10
|
+
* 1. Exact matches (e.g., 'https://example.com') - matches only the exact domain with exact protocol
|
|
11
|
+
* 2. Wildcard subdomain patterns (e.g., 'https://*.example.com') - matches ONLY subdomains with exact protocol
|
|
12
|
+
*
|
|
13
|
+
* Important matching rules:
|
|
14
|
+
* - Protocol is always matched exactly - if you need both HTTP and HTTPS, include both patterns
|
|
15
|
+
* - Wildcard patterns (e.g., 'https://*.example.com') will ONLY match subdomains, NOT the root domain
|
|
16
|
+
* - To match both subdomains AND the root domain, include both patterns:
|
|
17
|
+
* ['https://*.example.com', 'https://example.com']
|
|
18
|
+
*
|
|
9
19
|
* @returns A function that tests if an origin matches any of the patterns
|
|
10
20
|
*
|
|
11
21
|
* @example
|
|
12
22
|
* ```typescript
|
|
13
23
|
* const allowedOrigins = [
|
|
14
|
-
*
|
|
15
|
-
* '
|
|
24
|
+
* // Exact matches - protocol required
|
|
25
|
+
* 'https://example.com', // matches only https://example.com
|
|
26
|
+
* 'http://example.com', // matches only http://example.com
|
|
27
|
+
*
|
|
28
|
+
* // Wildcard subdomain matches - protocol required
|
|
29
|
+
* 'https://*.example.com', // matches https://app.example.com, https://api.example.com
|
|
30
|
+
* // does NOT match https://example.com
|
|
31
|
+
*
|
|
32
|
+
* // To match both HTTP and HTTPS, include both
|
|
33
|
+
* 'https://*.staging.com', // matches https://app.staging.com
|
|
34
|
+
* 'http://*.staging.com', // matches http://app.staging.com
|
|
35
|
+
*
|
|
36
|
+
* // To match both subdomains and root domain, include both
|
|
37
|
+
* 'https://*.production.com', // matches https://app.production.com
|
|
38
|
+
* 'https://production.com', // matches https://production.com
|
|
16
39
|
* ];
|
|
17
40
|
*
|
|
18
41
|
* const matcher = createOriginMatcher(allowedOrigins);
|
|
19
42
|
*
|
|
20
|
-
* // Exact
|
|
21
|
-
*
|
|
22
|
-
*
|
|
43
|
+
* // Exact matches
|
|
44
|
+
* matcher('https://example.com'); // true
|
|
45
|
+
* matcher('http://example.com'); // true
|
|
23
46
|
*
|
|
24
|
-
* //
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
47
|
+
* // Subdomain matches (protocol specific)
|
|
48
|
+
* matcher('https://app.example.com'); // true
|
|
49
|
+
* matcher('http://app.example.com'); // false - wrong protocol
|
|
50
|
+
*
|
|
51
|
+
* // Root domain with wildcard pattern
|
|
52
|
+
* matcher('https://example.com'); // false - wildcards don't match root
|
|
53
|
+
* matcher('https://production.com'); // true - matched by exact pattern
|
|
29
54
|
* ```
|
|
30
55
|
*/
|
|
31
56
|
declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
|
|
@@ -38,15 +63,31 @@ declare function experimental_createOriginMatcher(origins: string[]): (origin: s
|
|
|
38
63
|
* @returns A function that will mutate the Response object by applying the CORS headers
|
|
39
64
|
* @example
|
|
40
65
|
* ```ts
|
|
41
|
-
* const cors =
|
|
42
|
-
*
|
|
43
|
-
*
|
|
66
|
+
* const cors = experimental_createCORSTransform([
|
|
67
|
+
* // Exact matches
|
|
68
|
+
* 'https://example.com',
|
|
69
|
+
* 'http://localhost:3000',
|
|
70
|
+
*
|
|
71
|
+
* // Wildcard subdomain matches
|
|
72
|
+
* 'https://*.myapp.com', // matches https://dashboard.myapp.com
|
|
73
|
+
* 'http://*.myapp.com', // matches http://dashboard.myapp.com
|
|
74
|
+
*
|
|
75
|
+
* // Match both subdomain and root domain
|
|
76
|
+
* 'https://*.staging.com', // matches https://app.staging.com
|
|
77
|
+
* 'https://staging.com' // matches https://staging.com
|
|
78
|
+
* ]);
|
|
44
79
|
*
|
|
45
|
-
* const
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
80
|
+
* const router = create({
|
|
81
|
+
* before: async req => {
|
|
82
|
+
* if (req.method === 'OPTIONS') {
|
|
83
|
+
* // Return early to skip the router. This response still gets passed to `.transform()`
|
|
84
|
+
* // So our CORS headers will still be applied
|
|
85
|
+
* return new Response(null, {status: 204});
|
|
86
|
+
* }
|
|
87
|
+
* },
|
|
88
|
+
* transform: async (request, response) => {
|
|
89
|
+
* cors(request, response);
|
|
90
|
+
* }
|
|
50
91
|
* });
|
|
51
92
|
* ```
|
|
52
93
|
*/
|
package/dist/cors/cors.d.ts
CHANGED
|
@@ -1,31 +1,56 @@
|
|
|
1
1
|
/**
|
|
2
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
|
|
3
|
+
* The matcher handles both exact matches and wildcard subdomain patterns.
|
|
4
4
|
*
|
|
5
5
|
* **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
|
|
6
6
|
*
|
|
7
7
|
* @param origins Array of origin patterns to match against.
|
|
8
|
-
*
|
|
8
|
+
* Each pattern MUST include the protocol (http:// or https://).
|
|
9
|
+
* Two types of patterns are supported:
|
|
10
|
+
* 1. Exact matches (e.g., 'https://example.com') - matches only the exact domain with exact protocol
|
|
11
|
+
* 2. Wildcard subdomain patterns (e.g., 'https://*.example.com') - matches ONLY subdomains with exact protocol
|
|
12
|
+
*
|
|
13
|
+
* Important matching rules:
|
|
14
|
+
* - Protocol is always matched exactly - if you need both HTTP and HTTPS, include both patterns
|
|
15
|
+
* - Wildcard patterns (e.g., 'https://*.example.com') will ONLY match subdomains, NOT the root domain
|
|
16
|
+
* - To match both subdomains AND the root domain, include both patterns:
|
|
17
|
+
* ['https://*.example.com', 'https://example.com']
|
|
18
|
+
*
|
|
9
19
|
* @returns A function that tests if an origin matches any of the patterns
|
|
10
20
|
*
|
|
11
21
|
* @example
|
|
12
22
|
* ```typescript
|
|
13
23
|
* const allowedOrigins = [
|
|
14
|
-
*
|
|
15
|
-
* '
|
|
24
|
+
* // Exact matches - protocol required
|
|
25
|
+
* 'https://example.com', // matches only https://example.com
|
|
26
|
+
* 'http://example.com', // matches only http://example.com
|
|
27
|
+
*
|
|
28
|
+
* // Wildcard subdomain matches - protocol required
|
|
29
|
+
* 'https://*.example.com', // matches https://app.example.com, https://api.example.com
|
|
30
|
+
* // does NOT match https://example.com
|
|
31
|
+
*
|
|
32
|
+
* // To match both HTTP and HTTPS, include both
|
|
33
|
+
* 'https://*.staging.com', // matches https://app.staging.com
|
|
34
|
+
* 'http://*.staging.com', // matches http://app.staging.com
|
|
35
|
+
*
|
|
36
|
+
* // To match both subdomains and root domain, include both
|
|
37
|
+
* 'https://*.production.com', // matches https://app.production.com
|
|
38
|
+
* 'https://production.com', // matches https://production.com
|
|
16
39
|
* ];
|
|
17
40
|
*
|
|
18
41
|
* const matcher = createOriginMatcher(allowedOrigins);
|
|
19
42
|
*
|
|
20
|
-
* // Exact
|
|
21
|
-
*
|
|
22
|
-
*
|
|
43
|
+
* // Exact matches
|
|
44
|
+
* matcher('https://example.com'); // true
|
|
45
|
+
* matcher('http://example.com'); // true
|
|
23
46
|
*
|
|
24
|
-
* //
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
47
|
+
* // Subdomain matches (protocol specific)
|
|
48
|
+
* matcher('https://app.example.com'); // true
|
|
49
|
+
* matcher('http://app.example.com'); // false - wrong protocol
|
|
50
|
+
*
|
|
51
|
+
* // Root domain with wildcard pattern
|
|
52
|
+
* matcher('https://example.com'); // false - wildcards don't match root
|
|
53
|
+
* matcher('https://production.com'); // true - matched by exact pattern
|
|
29
54
|
* ```
|
|
30
55
|
*/
|
|
31
56
|
declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
|
|
@@ -38,15 +63,31 @@ declare function experimental_createOriginMatcher(origins: string[]): (origin: s
|
|
|
38
63
|
* @returns A function that will mutate the Response object by applying the CORS headers
|
|
39
64
|
* @example
|
|
40
65
|
* ```ts
|
|
41
|
-
* const cors =
|
|
42
|
-
*
|
|
43
|
-
*
|
|
66
|
+
* const cors = experimental_createCORSTransform([
|
|
67
|
+
* // Exact matches
|
|
68
|
+
* 'https://example.com',
|
|
69
|
+
* 'http://localhost:3000',
|
|
70
|
+
*
|
|
71
|
+
* // Wildcard subdomain matches
|
|
72
|
+
* 'https://*.myapp.com', // matches https://dashboard.myapp.com
|
|
73
|
+
* 'http://*.myapp.com', // matches http://dashboard.myapp.com
|
|
74
|
+
*
|
|
75
|
+
* // Match both subdomain and root domain
|
|
76
|
+
* 'https://*.staging.com', // matches https://app.staging.com
|
|
77
|
+
* 'https://staging.com' // matches https://staging.com
|
|
78
|
+
* ]);
|
|
44
79
|
*
|
|
45
|
-
* const
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
80
|
+
* const router = create({
|
|
81
|
+
* before: async req => {
|
|
82
|
+
* if (req.method === 'OPTIONS') {
|
|
83
|
+
* // Return early to skip the router. This response still gets passed to `.transform()`
|
|
84
|
+
* // So our CORS headers will still be applied
|
|
85
|
+
* return new Response(null, {status: 204});
|
|
86
|
+
* }
|
|
87
|
+
* },
|
|
88
|
+
* transform: async (request, response) => {
|
|
89
|
+
* cors(request, response);
|
|
90
|
+
* }
|
|
50
91
|
* });
|
|
51
92
|
* ```
|
|
52
93
|
*/
|
package/dist/cors/cors.js
CHANGED
|
@@ -3,13 +3,20 @@ function experimental_createOriginMatcher(origins) {
|
|
|
3
3
|
if (origins.length === 0) {
|
|
4
4
|
return () => false;
|
|
5
5
|
}
|
|
6
|
+
const escapedCharsRegex = /[.+?^${}()|[\]\\]/g;
|
|
6
7
|
const source = origins.map((origin) => {
|
|
7
|
-
if (origin.
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
if (origin.includes("://*.")) {
|
|
9
|
+
const parts = origin.split("://");
|
|
10
|
+
if (parts.length !== 2) {
|
|
11
|
+
throw new Error(`Invalid origin pattern: ${origin}. Must include protocol (e.g., https://*.example.com)`);
|
|
12
|
+
}
|
|
13
|
+
const [protocol, rest] = parts;
|
|
14
|
+
const domain = rest.slice(2).replace(escapedCharsRegex, "\\$&");
|
|
15
|
+
const pattern = `^${protocol.replace(escapedCharsRegex, "\\$&")}:\\/\\/[^.]+\\.${domain}$`;
|
|
16
|
+
return pattern;
|
|
10
17
|
} else {
|
|
11
|
-
const
|
|
12
|
-
return
|
|
18
|
+
const pattern = `^${origin.replace(escapedCharsRegex, "\\$&")}$`;
|
|
19
|
+
return pattern;
|
|
13
20
|
}
|
|
14
21
|
}).join("|");
|
|
15
22
|
const regex = new RegExp(source);
|
|
@@ -21,7 +28,7 @@ function experimental_createCORSTransform(origins) {
|
|
|
21
28
|
const origin = request.headers.get("Origin");
|
|
22
29
|
if (origin && matcher(origin)) {
|
|
23
30
|
response.headers.set("Access-Control-Allow-Origin", origin);
|
|
24
|
-
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
31
|
+
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
|
25
32
|
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
26
33
|
response.headers.set("Access-Control-Max-Age", "86400");
|
|
27
34
|
response.headers.set("Access-Control-Allow-Credentials", "true");
|
package/dist/index.cjs
CHANGED
|
@@ -178,7 +178,10 @@ var Router = class _Router {
|
|
|
178
178
|
merge = (pathPrefix, other) => {
|
|
179
179
|
const newRoutes = [...other.state.routes].map((route) => ({
|
|
180
180
|
...route,
|
|
181
|
-
|
|
181
|
+
// handle pathPrefix = / & route.path = / case causing //
|
|
182
|
+
// we intentionally are replacing on the joining path and not the pathPrefix, in case of
|
|
183
|
+
// /named -> merged to -> / causing /named/ not /named
|
|
184
|
+
path: `${pathPrefix}${route.path === "/" ? "" : route.path}`
|
|
182
185
|
}));
|
|
183
186
|
return new _Router({
|
|
184
187
|
...this.state,
|
|
@@ -366,7 +369,7 @@ var Router = class _Router {
|
|
|
366
369
|
const item = {
|
|
367
370
|
description: route.openapi?.description ?? "Successful response",
|
|
368
371
|
responses: {
|
|
369
|
-
|
|
372
|
+
200: {
|
|
370
373
|
description: route.openapi?.description ?? "Successful response",
|
|
371
374
|
content
|
|
372
375
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -148,7 +148,7 @@ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Reco
|
|
|
148
148
|
};
|
|
149
149
|
type AnyRoute = Route<any, any, any, any, any, any, any>;
|
|
150
150
|
|
|
151
|
-
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
|
|
151
|
+
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
|
|
152
152
|
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
153
153
|
type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute> = {
|
|
154
154
|
routes: Set<Routes>;
|
package/dist/index.d.ts
CHANGED
|
@@ -148,7 +148,7 @@ type Route<ContextTo, Result, Path extends string, AdditionalParams extends Reco
|
|
|
148
148
|
};
|
|
149
149
|
type AnyRoute = Route<any, any, any, any, any, any, any>;
|
|
150
150
|
|
|
151
|
-
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
|
|
151
|
+
type PrefixRoutesPathInner<R extends AnyRoute, Prefix extends `/${string}`> = R extends Route<infer ContextTo, infer Result, infer Path, infer AdditionalParams, infer Method, infer Query, infer BodyOutput> ? Route<ContextTo, Result, `${Prefix}${Path extends '/' ? '' : Path}`, AdditionalParams, Method, Query, BodyOutput> : never;
|
|
152
152
|
type PrefixRoutesPath<Prefix extends `/${string}`, R extends AnyRoute> = R extends R ? PrefixRoutesPathInner<R, Prefix> : never;
|
|
153
153
|
type RouterState<ContextFrom, ContextTo, RequiredParams extends Record<string, unknown>, Routes extends AnyRoute> = {
|
|
154
154
|
routes: Set<Routes>;
|
package/dist/index.js
CHANGED
|
@@ -148,7 +148,10 @@ var Router = class _Router {
|
|
|
148
148
|
merge = (pathPrefix, other) => {
|
|
149
149
|
const newRoutes = [...other.state.routes].map((route) => ({
|
|
150
150
|
...route,
|
|
151
|
-
|
|
151
|
+
// handle pathPrefix = / & route.path = / case causing //
|
|
152
|
+
// we intentionally are replacing on the joining path and not the pathPrefix, in case of
|
|
153
|
+
// /named -> merged to -> / causing /named/ not /named
|
|
154
|
+
path: `${pathPrefix}${route.path === "/" ? "" : route.path}`
|
|
152
155
|
}));
|
|
153
156
|
return new _Router({
|
|
154
157
|
...this.state,
|
|
@@ -336,7 +339,7 @@ var Router = class _Router {
|
|
|
336
339
|
const item = {
|
|
337
340
|
description: route.openapi?.description ?? "Successful response",
|
|
338
341
|
responses: {
|
|
339
|
-
|
|
342
|
+
200: {
|
|
340
343
|
description: route.openapi?.description ?? "Successful response",
|
|
341
344
|
content
|
|
342
345
|
}
|