@kaito-http/core 4.0.0-beta.2 → 4.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.
@@ -24,33 +24,101 @@ __export(cors_exports, {
24
24
  experimental_createOriginMatcher: () => experimental_createOriginMatcher
25
25
  });
26
26
  module.exports = __toCommonJS(cors_exports);
27
- function experimental_createOriginMatcher(origins) {
27
+ function experimental_createOriginMatcher(originIterator) {
28
+ const origins = Array.from(originIterator);
28
29
  if (origins.length === 0) {
29
30
  return () => false;
30
31
  }
32
+ const escapedCharsRegex = /[.+?^${}()|[\]\\]/g;
31
33
  const source = origins.map((origin) => {
32
- if (origin.startsWith("*.")) {
33
- const escapedDomain = origin.slice(2).replace(/[.+?^${}()|[\]\\]/g, "\\$&");
34
- return `^(?:https?://)[^.]+\\.${escapedDomain}$`;
34
+ if (origin.includes("://*.")) {
35
+ const parts = origin.split("://");
36
+ if (parts.length !== 2) {
37
+ throw new Error(`Invalid origin pattern: ${origin}. Must include protocol (e.g., https://*.example.com)`);
38
+ }
39
+ const [protocol, rest] = parts;
40
+ const domain = rest.slice(2).replace(escapedCharsRegex, "\\$&");
41
+ const pattern = `^${protocol.replace(escapedCharsRegex, "\\$&")}:\\/\\/[^.]+\\.${domain}$`;
42
+ return pattern;
35
43
  } else {
36
- const escapedOrigin = origin.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
37
- return `^${escapedOrigin}$`;
44
+ const pattern = `^${origin.replace(escapedCharsRegex, "\\$&")}$`;
45
+ return pattern;
38
46
  }
39
47
  }).join("|");
40
48
  const regex = new RegExp(source);
41
49
  return (origin) => regex.test(origin);
42
50
  }
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
- }
51
+ function experimental_createCORSTransform(originsIterator) {
52
+ let allowedOrigins = new Set(originsIterator);
53
+ let matcher = experimental_createOriginMatcher(allowedOrigins);
54
+ const updateMatcher = () => {
55
+ matcher = experimental_createOriginMatcher(allowedOrigins);
56
+ };
57
+ return {
58
+ /**
59
+ * Handle OPTIONS requests in Kaito's `before()` hook
60
+ *
61
+ * @param request - The request object
62
+ * @returns A 204 response
63
+ */
64
+ before: (request) => {
65
+ if (request.method === "OPTIONS") {
66
+ return new Response(null, { status: 204 });
67
+ }
68
+ },
69
+ /**
70
+ * Apply CORS headers to the response if origin matches.
71
+ *
72
+ * @param request - The request object
73
+ * @param response - The response object
74
+ */
75
+ transform: (request, response) => {
76
+ const origin = request.headers.get("Origin");
77
+ if (origin && matcher(origin)) {
78
+ response.headers.set("Access-Control-Allow-Origin", origin);
79
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
80
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
81
+ response.headers.set("Access-Control-Max-Age", "86400");
82
+ response.headers.set("Access-Control-Allow-Credentials", "true");
83
+ }
84
+ },
85
+ /**
86
+ * Replace all allowed origins with a new set.
87
+ *
88
+ * @param newOrigins - The new set of allowed origins
89
+ */
90
+ setOrigins: (newOrigins) => {
91
+ allowedOrigins = new Set(newOrigins);
92
+ updateMatcher();
93
+ },
94
+ /**
95
+ * Add one or more origins to the allowed list.
96
+ *
97
+ * @param origins - The origins to add
98
+ */
99
+ addOrigins: (...origins) => {
100
+ for (const origin of origins) {
101
+ allowedOrigins.add(origin);
102
+ }
103
+ updateMatcher();
104
+ },
105
+ /**
106
+ * Remove one or more origins from the allowed list.
107
+ *
108
+ * @param origins - The origins to remove
109
+ */
110
+ removeOrigins: (...origins) => {
111
+ for (const origin of origins) {
112
+ allowedOrigins.delete(origin);
113
+ }
114
+ updateMatcher();
115
+ },
116
+ /**
117
+ * Clones the current set of allowed origins and returns it
118
+ *
119
+ * @returns A set of allowed origins
120
+ */
121
+ getOrigins: () => new Set(allowedOrigins)
54
122
  };
55
123
  }
56
124
  // Annotate the CommonJS export names for ESM import in node:
@@ -1,55 +1,151 @@
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 (e.g., '*.example.com').
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
- * @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.
7
+ * @param originIterator Array of origin patterns to match against.
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
- * 'https://example.com',
15
- * '*.trusted-domain.com' // Won't match https://evil-domain.com, only subdomains
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 match
21
- * console.log(matcher('https://example.com')); // true
22
- * console.log(matcher('http://example.com')); // false
43
+ * // Exact matches
44
+ * matcher('https://example.com'); // true
45
+ * matcher('http://example.com'); // true
23
46
  *
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
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
- declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
56
+ declare function experimental_createOriginMatcher(originIterator: Iterable<string>): (origin: string) => boolean;
32
57
  /**
33
- * Create a function to apply CORS headers with sane defaults for most apps.
58
+ * Create a CORS handler with sane defaults for most apps.
34
59
  *
35
60
  * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
36
61
  *
37
- * @param options Options object
38
- * @returns A function that will mutate the Response object by applying the CORS headers
62
+ * @param originsIterator Array of allowed origin patterns. Each pattern must include protocol (http:// or https://).
63
+ * Supports both exact matches and wildcard subdomain patterns. See {@link experimental_createOriginMatcher}
64
+ * for detailed pattern matching rules.
65
+ *
66
+ * @returns An object containing:
67
+ * - `before`: A handler for OPTIONS requests that returns a 204 response
68
+ * - `transform`: A function that applies CORS headers to the response if origin matches
69
+ * - `setOrigins`: A function to replace all allowed origins with a new set
70
+ * - `appendOrigin`: A function to add a new origin to the allowed list
71
+ * - `removeOrigin`: A function to remove an origin from the allowed list
72
+ * - `getOrigins`: A function that returns the current list of allowed origins
73
+ *
39
74
  * @example
40
75
  * ```ts
41
- * const cors = createCORSHandler({
42
- * origins: ['https://example.com', "*.allows-subdomains.com", "http://localhost:3000"],
43
- * });
76
+ * const corsHandler = experimental_createCORSTransform([
77
+ * // Exact matches
78
+ * 'https://example.com',
79
+ * 'http://localhost:3000',
80
+ *
81
+ * // Wildcard subdomain matches
82
+ * 'https://*.myapp.com', // matches https://dashboard.myapp.com
83
+ * 'http://*.myapp.com', // matches http://dashboard.myapp.com
44
84
  *
45
- * const handler = createKaitoHandler({
46
- * // ...
47
- * transform: async (request, response) => {
48
- * cors(request, response);
49
- * }
85
+ * // Match both subdomain and root domain
86
+ * 'https://*.staging.com', // matches https://app.staging.com
87
+ * 'https://staging.com' // matches https://staging.com
88
+ * ]);
89
+ *
90
+ * const router = create({
91
+ * // Handle preflight requests
92
+ * before: corsHandler.before,
93
+ *
94
+ * // Or expanded
95
+ * before: (request) => {
96
+ * const res = cors.before(request);
97
+ * if (res) return res;
98
+ * },
99
+ *
100
+ * // Apply CORS headers to all responses
101
+ * transform: corsHandler.transform,
50
102
  * });
103
+ *
104
+ * // Manage origins dynamically
105
+ * corsHandler.appendOrigin('https://newdomain.com');
106
+ * corsHandler.removeOrigin('http://localhost:3000');
107
+ * corsHandler.setOrigins(['https://completely-new-domain.com']);
51
108
  * ```
52
109
  */
53
- declare function experimental_createCORSTransform(origins: string[]): (request: Request, response: Response) => void;
110
+ declare function experimental_createCORSTransform(originsIterator: Iterable<string>): {
111
+ /**
112
+ * Handle OPTIONS requests in Kaito's `before()` hook
113
+ *
114
+ * @param request - The request object
115
+ * @returns A 204 response
116
+ */
117
+ before: (request: Request) => Response | undefined;
118
+ /**
119
+ * Apply CORS headers to the response if origin matches.
120
+ *
121
+ * @param request - The request object
122
+ * @param response - The response object
123
+ */
124
+ transform: (request: Request, response: Response) => void;
125
+ /**
126
+ * Replace all allowed origins with a new set.
127
+ *
128
+ * @param newOrigins - The new set of allowed origins
129
+ */
130
+ setOrigins: (newOrigins: Iterable<string>) => void;
131
+ /**
132
+ * Add one or more origins to the allowed list.
133
+ *
134
+ * @param origins - The origins to add
135
+ */
136
+ addOrigins: (...origins: string[]) => void;
137
+ /**
138
+ * Remove one or more origins from the allowed list.
139
+ *
140
+ * @param origins - The origins to remove
141
+ */
142
+ removeOrigins: (...origins: string[]) => void;
143
+ /**
144
+ * Clones the current set of allowed origins and returns it
145
+ *
146
+ * @returns A set of allowed origins
147
+ */
148
+ getOrigins: () => Set<string>;
149
+ };
54
150
 
55
151
  export { experimental_createCORSTransform, experimental_createOriginMatcher };
@@ -1,55 +1,151 @@
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 (e.g., '*.example.com').
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
- * @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.
7
+ * @param originIterator Array of origin patterns to match against.
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
- * 'https://example.com',
15
- * '*.trusted-domain.com' // Won't match https://evil-domain.com, only subdomains
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 match
21
- * console.log(matcher('https://example.com')); // true
22
- * console.log(matcher('http://example.com')); // false
43
+ * // Exact matches
44
+ * matcher('https://example.com'); // true
45
+ * matcher('http://example.com'); // true
23
46
  *
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
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
- declare function experimental_createOriginMatcher(origins: string[]): (origin: string) => boolean;
56
+ declare function experimental_createOriginMatcher(originIterator: Iterable<string>): (origin: string) => boolean;
32
57
  /**
33
- * Create a function to apply CORS headers with sane defaults for most apps.
58
+ * Create a CORS handler with sane defaults for most apps.
34
59
  *
35
60
  * **⚠️ This API is experimental and may change or even be removed in the future. ⚠️**
36
61
  *
37
- * @param options Options object
38
- * @returns A function that will mutate the Response object by applying the CORS headers
62
+ * @param originsIterator Array of allowed origin patterns. Each pattern must include protocol (http:// or https://).
63
+ * Supports both exact matches and wildcard subdomain patterns. See {@link experimental_createOriginMatcher}
64
+ * for detailed pattern matching rules.
65
+ *
66
+ * @returns An object containing:
67
+ * - `before`: A handler for OPTIONS requests that returns a 204 response
68
+ * - `transform`: A function that applies CORS headers to the response if origin matches
69
+ * - `setOrigins`: A function to replace all allowed origins with a new set
70
+ * - `appendOrigin`: A function to add a new origin to the allowed list
71
+ * - `removeOrigin`: A function to remove an origin from the allowed list
72
+ * - `getOrigins`: A function that returns the current list of allowed origins
73
+ *
39
74
  * @example
40
75
  * ```ts
41
- * const cors = createCORSHandler({
42
- * origins: ['https://example.com', "*.allows-subdomains.com", "http://localhost:3000"],
43
- * });
76
+ * const corsHandler = experimental_createCORSTransform([
77
+ * // Exact matches
78
+ * 'https://example.com',
79
+ * 'http://localhost:3000',
80
+ *
81
+ * // Wildcard subdomain matches
82
+ * 'https://*.myapp.com', // matches https://dashboard.myapp.com
83
+ * 'http://*.myapp.com', // matches http://dashboard.myapp.com
44
84
  *
45
- * const handler = createKaitoHandler({
46
- * // ...
47
- * transform: async (request, response) => {
48
- * cors(request, response);
49
- * }
85
+ * // Match both subdomain and root domain
86
+ * 'https://*.staging.com', // matches https://app.staging.com
87
+ * 'https://staging.com' // matches https://staging.com
88
+ * ]);
89
+ *
90
+ * const router = create({
91
+ * // Handle preflight requests
92
+ * before: corsHandler.before,
93
+ *
94
+ * // Or expanded
95
+ * before: (request) => {
96
+ * const res = cors.before(request);
97
+ * if (res) return res;
98
+ * },
99
+ *
100
+ * // Apply CORS headers to all responses
101
+ * transform: corsHandler.transform,
50
102
  * });
103
+ *
104
+ * // Manage origins dynamically
105
+ * corsHandler.appendOrigin('https://newdomain.com');
106
+ * corsHandler.removeOrigin('http://localhost:3000');
107
+ * corsHandler.setOrigins(['https://completely-new-domain.com']);
51
108
  * ```
52
109
  */
53
- declare function experimental_createCORSTransform(origins: string[]): (request: Request, response: Response) => void;
110
+ declare function experimental_createCORSTransform(originsIterator: Iterable<string>): {
111
+ /**
112
+ * Handle OPTIONS requests in Kaito's `before()` hook
113
+ *
114
+ * @param request - The request object
115
+ * @returns A 204 response
116
+ */
117
+ before: (request: Request) => Response | undefined;
118
+ /**
119
+ * Apply CORS headers to the response if origin matches.
120
+ *
121
+ * @param request - The request object
122
+ * @param response - The response object
123
+ */
124
+ transform: (request: Request, response: Response) => void;
125
+ /**
126
+ * Replace all allowed origins with a new set.
127
+ *
128
+ * @param newOrigins - The new set of allowed origins
129
+ */
130
+ setOrigins: (newOrigins: Iterable<string>) => void;
131
+ /**
132
+ * Add one or more origins to the allowed list.
133
+ *
134
+ * @param origins - The origins to add
135
+ */
136
+ addOrigins: (...origins: string[]) => void;
137
+ /**
138
+ * Remove one or more origins from the allowed list.
139
+ *
140
+ * @param origins - The origins to remove
141
+ */
142
+ removeOrigins: (...origins: string[]) => void;
143
+ /**
144
+ * Clones the current set of allowed origins and returns it
145
+ *
146
+ * @returns A set of allowed origins
147
+ */
148
+ getOrigins: () => Set<string>;
149
+ };
54
150
 
55
151
  export { experimental_createCORSTransform, experimental_createOriginMatcher };
package/dist/cors/cors.js CHANGED
@@ -1,31 +1,99 @@
1
1
  // src/cors/cors.ts
2
- function experimental_createOriginMatcher(origins) {
2
+ function experimental_createOriginMatcher(originIterator) {
3
+ const origins = Array.from(originIterator);
3
4
  if (origins.length === 0) {
4
5
  return () => false;
5
6
  }
7
+ const escapedCharsRegex = /[.+?^${}()|[\]\\]/g;
6
8
  const source = origins.map((origin) => {
7
- if (origin.startsWith("*.")) {
8
- const escapedDomain = origin.slice(2).replace(/[.+?^${}()|[\]\\]/g, "\\$&");
9
- return `^(?:https?://)[^.]+\\.${escapedDomain}$`;
9
+ if (origin.includes("://*.")) {
10
+ const parts = origin.split("://");
11
+ if (parts.length !== 2) {
12
+ throw new Error(`Invalid origin pattern: ${origin}. Must include protocol (e.g., https://*.example.com)`);
13
+ }
14
+ const [protocol, rest] = parts;
15
+ const domain = rest.slice(2).replace(escapedCharsRegex, "\\$&");
16
+ const pattern = `^${protocol.replace(escapedCharsRegex, "\\$&")}:\\/\\/[^.]+\\.${domain}$`;
17
+ return pattern;
10
18
  } else {
11
- const escapedOrigin = origin.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
12
- return `^${escapedOrigin}$`;
19
+ const pattern = `^${origin.replace(escapedCharsRegex, "\\$&")}$`;
20
+ return pattern;
13
21
  }
14
22
  }).join("|");
15
23
  const regex = new RegExp(source);
16
24
  return (origin) => regex.test(origin);
17
25
  }
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
- }
26
+ function experimental_createCORSTransform(originsIterator) {
27
+ let allowedOrigins = new Set(originsIterator);
28
+ let matcher = experimental_createOriginMatcher(allowedOrigins);
29
+ const updateMatcher = () => {
30
+ matcher = experimental_createOriginMatcher(allowedOrigins);
31
+ };
32
+ return {
33
+ /**
34
+ * Handle OPTIONS requests in Kaito's `before()` hook
35
+ *
36
+ * @param request - The request object
37
+ * @returns A 204 response
38
+ */
39
+ before: (request) => {
40
+ if (request.method === "OPTIONS") {
41
+ return new Response(null, { status: 204 });
42
+ }
43
+ },
44
+ /**
45
+ * Apply CORS headers to the response if origin matches.
46
+ *
47
+ * @param request - The request object
48
+ * @param response - The response object
49
+ */
50
+ transform: (request, response) => {
51
+ const origin = request.headers.get("Origin");
52
+ if (origin && matcher(origin)) {
53
+ response.headers.set("Access-Control-Allow-Origin", origin);
54
+ response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
55
+ response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
56
+ response.headers.set("Access-Control-Max-Age", "86400");
57
+ response.headers.set("Access-Control-Allow-Credentials", "true");
58
+ }
59
+ },
60
+ /**
61
+ * Replace all allowed origins with a new set.
62
+ *
63
+ * @param newOrigins - The new set of allowed origins
64
+ */
65
+ setOrigins: (newOrigins) => {
66
+ allowedOrigins = new Set(newOrigins);
67
+ updateMatcher();
68
+ },
69
+ /**
70
+ * Add one or more origins to the allowed list.
71
+ *
72
+ * @param origins - The origins to add
73
+ */
74
+ addOrigins: (...origins) => {
75
+ for (const origin of origins) {
76
+ allowedOrigins.add(origin);
77
+ }
78
+ updateMatcher();
79
+ },
80
+ /**
81
+ * Remove one or more origins from the allowed list.
82
+ *
83
+ * @param origins - The origins to remove
84
+ */
85
+ removeOrigins: (...origins) => {
86
+ for (const origin of origins) {
87
+ allowedOrigins.delete(origin);
88
+ }
89
+ updateMatcher();
90
+ },
91
+ /**
92
+ * Clones the current set of allowed origins and returns it
93
+ *
94
+ * @returns A set of allowed origins
95
+ */
96
+ getOrigins: () => new Set(allowedOrigins)
29
97
  };
30
98
  }
31
99
  export {