11import type { FreshContext } from "../context.ts" ;
22import type { MiddlewareFn } from "./mod.ts" ;
33
4- export type CORSOptions = {
4+ export type CORSOptions < State > = {
55 origin :
66 | string
77 | string [ ]
8- | ( ( requestOrigin : string , ctx : FreshContext ) => string | undefined | null ) ;
8+ | ( (
9+ requestOrigin : string ,
10+ ctx : FreshContext < State > ,
11+ ) => string | undefined | null ) ;
912 allowMethods ?: string [ ] ;
1013 allowHeaders ?: string [ ] ;
1114 maxAge ?: number ;
@@ -51,36 +54,19 @@ export type CORSOptions = {
5154 * // ];
5255 * ```
5356 */
54- export function cors < T > ( options ?: CORSOptions ) : MiddlewareFn < T > {
55- const opts : CORSOptions = {
57+ export function cors < State > ( options ?: CORSOptions < State > ) : MiddlewareFn < State > {
58+ const opts : CORSOptions < State > = {
5659 origin : "*" ,
5760 allowMethods : [ "GET" , "HEAD" , "PUT" , "POST" , "DELETE" , "PATCH" ] ,
5861 allowHeaders : [ ] ,
5962 exposeHeaders : [ ] ,
6063 ...options ,
6164 } ;
6265
63- const findAllowOrigin = ( ( optsOrigin : CORSOptions [ "origin" ] ) => {
64- if ( typeof optsOrigin === "string" ) {
65- if ( optsOrigin === "*" ) {
66- return ( _requestOrigin : string , _ctx : FreshContext ) => optsOrigin ;
67- } else {
68- return ( requestOrigin : string , _ctx : FreshContext ) =>
69- optsOrigin === requestOrigin ? requestOrigin : null ;
70- }
71- } else if ( typeof optsOrigin === "function" ) {
72- return ( requestOrigin : string , ctx : FreshContext ) =>
73- optsOrigin ( requestOrigin , ctx ) ;
74- } else {
75- return ( requestOrigin : string , _ctx : FreshContext ) =>
76- optsOrigin . includes ( requestOrigin ) ? requestOrigin : null ;
77- }
78- } ) ( opts . origin ) ;
79-
8066 const addHeaderProperties = (
8167 headers : Headers ,
8268 allowOrigin : string | null | undefined ,
83- opts : CORSOptions ,
69+ opts : CORSOptions < State > ,
8470 ) => {
8571 if ( allowOrigin ) {
8672 headers . set ( "Access-Control-Allow-Origin" , allowOrigin ) ;
@@ -98,77 +84,82 @@ export function cors<T>(options?: CORSOptions): MiddlewareFn<T> {
9884 }
9985 } ;
10086
101- const OptionsResponse = (
102- ctx : FreshContext ,
103- allowOrigin : string | null | undefined ,
104- opts : CORSOptions ,
105- varyValues : Set < string > ,
106- ) => {
107- const headers = new Headers ( ) ;
87+ const optsOrigin = opts . origin ;
10888
109- addHeaderProperties (
110- headers ,
111- allowOrigin ,
112- opts ,
113- ) ;
89+ return async ( ctx ) => {
90+ const requestOrigin = ctx . req . headers . get ( "origin" ) || "" ;
11491
115- if ( opts . maxAge != null ) {
116- headers . set ( "Access-Control-Max-Age" , opts . maxAge . toString ( ) ) ;
92+ let allowOrigin : string | null = null ;
93+ if ( typeof optsOrigin === "string" ) {
94+ if ( optsOrigin === "*" ) {
95+ allowOrigin = optsOrigin ;
96+ } else {
97+ allowOrigin = optsOrigin === requestOrigin ? requestOrigin : null ;
98+ }
99+ } else if ( typeof optsOrigin === "function" ) {
100+ allowOrigin = optsOrigin ( requestOrigin , ctx ) ?? null ;
101+ } else {
102+ allowOrigin = optsOrigin . includes ( requestOrigin ) ? requestOrigin : null ;
117103 }
118104
119- if ( opts . allowMethods ?. length ) {
120- headers . set (
121- "Access-Control-Allow-Methods" ,
122- opts . allowMethods . join ( "," ) ,
123- ) ;
105+ const vary = new Set < string > ( ) ;
106+ // Add 'Origin' to Vary if a specific origin is allowed, not '*'
107+ if ( opts . origin !== "*" && allowOrigin && allowOrigin !== "*" ) {
108+ vary . add ( "Origin" ) ;
124109 }
125110
126- let effectiveAllowHeaders = opts . allowHeaders ;
127- if ( ! effectiveAllowHeaders ?. length ) {
128- const reqHeaders = ctx . req . headers . get (
129- "Access-Control-Request-Headers" ,
130- ) ;
131- if ( reqHeaders ) {
132- effectiveAllowHeaders = reqHeaders . split ( / \s * , \s * / ) ;
133- }
134- }
111+ if ( ctx . req . method === "OPTIONS" ) {
112+ const headers = new Headers ( ) ;
135113
136- if ( effectiveAllowHeaders ?. length ) {
137- headers . set (
138- "Access-Control-Allow-Headers" ,
139- effectiveAllowHeaders . join ( "," ) ,
114+ addHeaderProperties (
115+ headers ,
116+ allowOrigin ,
117+ opts ,
140118 ) ;
141- varyValues . add ( "Access-Control-Request-Headers" ) ;
142- }
143119
144- if ( varyValues . size > 0 ) {
145- headers . set ( "Vary" , Array . from ( varyValues ) . join ( ", " ) ) ;
146- } else {
147- headers . delete ( "Vary" ) ; // Ensure Vary is not set if no conditions met
148- }
120+ if ( opts . maxAge != null ) {
121+ headers . set ( "Access-Control-Max-Age" , opts . maxAge . toString ( ) ) ;
122+ }
149123
150- headers . delete ( "Content-Length" ) ;
151- headers . delete ( "Content-Type" ) ;
124+ if ( opts . allowMethods ?. length ) {
125+ headers . set (
126+ "Access-Control-Allow-Methods" ,
127+ opts . allowMethods . join ( "," ) ,
128+ ) ;
129+ }
152130
153- return new Response ( null , {
154- status : 204 ,
155- statusText : "No Content" ,
156- headers,
157- } ) ;
158- } ;
131+ let allowHeaders = opts . allowHeaders ;
132+ if ( ! allowHeaders ?. length ) {
133+ const reqHeaders = ctx . req . headers . get (
134+ "Access-Control-Request-Headers" ,
135+ ) ;
136+ if ( reqHeaders ) {
137+ allowHeaders = reqHeaders . split ( / \s * , \s * / ) ;
138+ }
139+ }
159140
160- return async ( ctx : FreshContext ) : Promise < Response > => {
161- const requestOrigin = ctx . req . headers . get ( "origin" ) || "" ;
162- const allowOrigin = findAllowOrigin ( requestOrigin , ctx ) ;
141+ if ( allowHeaders ?. length ) {
142+ headers . set (
143+ "Access-Control-Allow-Headers" ,
144+ allowHeaders . join ( "," ) ,
145+ ) ;
146+ vary . add ( "Access-Control-Request-Headers" ) ;
147+ }
163148
164- const varyValues = new Set < string > ( ) ;
165- // Add 'Origin' to Vary if a specific origin is allowed, not '*'
166- if ( opts . origin !== "*" && allowOrigin && allowOrigin !== "*" ) {
167- varyValues . add ( "Origin ") ;
168- }
149+ if ( vary . size > 0 ) {
150+ headers . set ( " Vary" , Array . from ( vary ) . join ( ", " ) ) ;
151+ } else {
152+ headers . delete ( "Vary ") ; // Ensure Vary is not set if no conditions met
153+ }
169154
170- if ( ctx . req . method === "OPTIONS" ) {
171- return OptionsResponse ( ctx , allowOrigin , opts , varyValues ) ;
155+ headers . delete ( "Content-Length" ) ;
156+ headers . delete ( "Content-Type" ) ;
157+
158+ return new Response ( null , {
159+ status : 204 ,
160+ statusText : "No Content" ,
161+ headers,
162+ } ) ;
172163 }
173164
174165 // For non-OPTIONS requests
@@ -181,13 +172,13 @@ export function cors<T>(options?: CORSOptions): MiddlewareFn<T> {
181172 ) ;
182173
183174 // Merge our calculated varyValues with any existing Vary from downstream response
184- if ( varyValues . size > 0 ) {
185- const existingVary = res . headers . get ( "Vary" ) ;
186- if ( existingVary ) {
187- existingVary . split ( / \s * , \s * / ) . forEach ( ( v ) => varyValues . add ( v ) ) ;
175+ if ( vary . size > 0 ) {
176+ const existing = res . headers . get ( "Vary" ) ;
177+ if ( existing ) {
178+ existing . split ( / \s * , \s * / ) . forEach ( ( v ) => vary . add ( v ) ) ;
188179 }
189180
190- res . headers . set ( "Vary" , Array . from ( varyValues ) . join ( ", " ) ) ;
181+ res . headers . set ( "Vary" , Array . from ( vary ) . join ( ", " ) ) ;
191182 }
192183
193184 return res ;
0 commit comments