@tekir/cors 0.1.0 → 0.1.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.
- package/dist/cors.js +57 -7
- package/package.json +1 -1
- package/src/cors.ts +51 -6
package/dist/cors.js
CHANGED
|
@@ -80,15 +80,65 @@ export function cors(userConfig = {}) {
|
|
|
80
80
|
...(cfg.credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),
|
|
81
81
|
...(cfg.maxAge ? { 'Access-Control-Max-Age': String(cfg.maxAge) } : {}),
|
|
82
82
|
...(cfg.exposeHeaders?.length ? { 'Access-Control-Expose-Headers': cfg.exposeHeaders.join(', ') } : {}),
|
|
83
|
+
'Vary': 'Origin',
|
|
83
84
|
},
|
|
84
85
|
});
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
87
|
+
// Capture any throw from `next()` so the CORS merge below still runs
|
|
88
|
+
// even when a downstream middleware errors and an outer error handler
|
|
89
|
+
// turned it into a Response on `ctx.$result`. Without this guard the
|
|
90
|
+
// browser sees a "CORS error" on every error response, regardless of
|
|
91
|
+
// middleware order.
|
|
92
|
+
let nextError;
|
|
93
|
+
try {
|
|
94
|
+
await next();
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
nextError = err;
|
|
98
|
+
}
|
|
99
|
+
// Inject CORS headers into the actual response. Routes return a mix of
|
|
100
|
+
// Response objects (SSE streams, file downloads, custom payloads) and
|
|
101
|
+
// plain values that tekir wraps automatically. Coerce both shapes into
|
|
102
|
+
// a Response here so the headers always land on the wire.
|
|
103
|
+
const result = ctx.$result;
|
|
104
|
+
let response;
|
|
105
|
+
if (result instanceof Response) {
|
|
106
|
+
response = result;
|
|
107
|
+
}
|
|
108
|
+
else if (result == null) {
|
|
109
|
+
response = new Response(null, { status: 204 });
|
|
110
|
+
}
|
|
111
|
+
else if (typeof result === 'object') {
|
|
112
|
+
response = Response.json(result);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
response = new Response(String(result));
|
|
116
|
+
}
|
|
117
|
+
const merged = new Headers(response.headers);
|
|
118
|
+
merged.set('Access-Control-Allow-Origin', allowOrigin);
|
|
119
|
+
if (cfg.credentials)
|
|
120
|
+
merged.set('Access-Control-Allow-Credentials', 'true');
|
|
121
|
+
if (cfg.exposeHeaders?.length)
|
|
122
|
+
merged.set('Access-Control-Expose-Headers', cfg.exposeHeaders.join(', '));
|
|
123
|
+
// Vary: Origin keeps caches honest when the allow-origin is request-derived.
|
|
124
|
+
const existingVary = merged.get('Vary');
|
|
125
|
+
if (!existingVary) {
|
|
126
|
+
merged.set('Vary', 'Origin');
|
|
127
|
+
}
|
|
128
|
+
else if (!existingVary.split(',').map(s => s.trim().toLowerCase()).includes('origin')) {
|
|
129
|
+
merged.set('Vary', `${existingVary}, Origin`);
|
|
130
|
+
}
|
|
131
|
+
ctx.$result = new Response(response.body, {
|
|
132
|
+
status: response.status,
|
|
133
|
+
statusText: response.statusText,
|
|
134
|
+
headers: merged,
|
|
135
|
+
});
|
|
136
|
+
// Re-throw so the outer error handler can still see the original error
|
|
137
|
+
// for logging or transformation. By now `ctx.$result` carries the CORS
|
|
138
|
+
// headers, so even if the outer handler builds its own error response
|
|
139
|
+
// it can copy them off the existing one. If the outer handler swallows
|
|
140
|
+
// the throw, we already wrote a coerced response above.
|
|
141
|
+
if (nextError !== undefined)
|
|
142
|
+
throw nextError;
|
|
93
143
|
};
|
|
94
144
|
}
|
package/package.json
CHANGED
package/src/cors.ts
CHANGED
|
@@ -81,17 +81,62 @@ export function cors(userConfig: CorsConfig = {}) {
|
|
|
81
81
|
...(cfg.credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),
|
|
82
82
|
...(cfg.maxAge ? { 'Access-Control-Max-Age': String(cfg.maxAge) } : {}),
|
|
83
83
|
...(cfg.exposeHeaders?.length ? { 'Access-Control-Expose-Headers': cfg.exposeHeaders.join(', ') } : {}),
|
|
84
|
+
'Vary': 'Origin',
|
|
84
85
|
},
|
|
85
86
|
})
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
// Capture any throw from `next()` so the CORS merge below still runs
|
|
90
|
+
// even when a downstream middleware errors and an outer error handler
|
|
91
|
+
// turned it into a Response on `ctx.$result`. Without this guard the
|
|
92
|
+
// browser sees a "CORS error" on every error response, regardless of
|
|
93
|
+
// middleware order.
|
|
94
|
+
let nextError: unknown
|
|
95
|
+
try {
|
|
96
|
+
await next()
|
|
97
|
+
} catch (err) {
|
|
98
|
+
nextError = err
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
|
|
101
|
+
// Inject CORS headers into the actual response. Routes return a mix of
|
|
102
|
+
// Response objects (SSE streams, file downloads, custom payloads) and
|
|
103
|
+
// plain values that tekir wraps automatically. Coerce both shapes into
|
|
104
|
+
// a Response here so the headers always land on the wire.
|
|
105
|
+
const result = ctx.$result
|
|
106
|
+
let response: Response
|
|
107
|
+
if (result instanceof Response) {
|
|
108
|
+
response = result
|
|
109
|
+
} else if (result == null) {
|
|
110
|
+
response = new Response(null, { status: 204 })
|
|
111
|
+
} else if (typeof result === 'object') {
|
|
112
|
+
response = Response.json(result)
|
|
113
|
+
} else {
|
|
114
|
+
response = new Response(String(result))
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const merged = new Headers(response.headers)
|
|
118
|
+
merged.set('Access-Control-Allow-Origin', allowOrigin)
|
|
119
|
+
if (cfg.credentials) merged.set('Access-Control-Allow-Credentials', 'true')
|
|
120
|
+
if (cfg.exposeHeaders?.length) merged.set('Access-Control-Expose-Headers', cfg.exposeHeaders.join(', '))
|
|
121
|
+
// Vary: Origin keeps caches honest when the allow-origin is request-derived.
|
|
122
|
+
const existingVary = merged.get('Vary')
|
|
123
|
+
if (!existingVary) {
|
|
124
|
+
merged.set('Vary', 'Origin')
|
|
125
|
+
} else if (!existingVary.split(',').map(s => s.trim().toLowerCase()).includes('origin')) {
|
|
126
|
+
merged.set('Vary', `${existingVary}, Origin`)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
ctx.$result = new Response(response.body, {
|
|
130
|
+
status: response.status,
|
|
131
|
+
statusText: response.statusText,
|
|
132
|
+
headers: merged,
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
// Re-throw so the outer error handler can still see the original error
|
|
136
|
+
// for logging or transformation. By now `ctx.$result` carries the CORS
|
|
137
|
+
// headers, so even if the outer handler builds its own error response
|
|
138
|
+
// it can copy them off the existing one. If the outer handler swallows
|
|
139
|
+
// the throw, we already wrote a coerced response above.
|
|
140
|
+
if (nextError !== undefined) throw nextError
|
|
96
141
|
}
|
|
97
142
|
}
|