@tekir/cors 0.1.0 → 0.1.1

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 CHANGED
@@ -80,15 +80,47 @@ 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
- ctx.store = ctx.store || {};
87
- ctx.store.__corsHeaders = {
88
- 'Access-Control-Allow-Origin': allowOrigin,
89
- ...(cfg.credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),
90
- ...(cfg.exposeHeaders?.length ? { 'Access-Control-Expose-Headers': cfg.exposeHeaders.join(', ') } : {}),
91
- };
92
87
  await next();
88
+ // Inject CORS headers into the actual response. Routes return a mix of
89
+ // Response objects (SSE streams, file downloads, custom payloads) and
90
+ // plain values that tekir wraps automatically. Coerce both shapes into
91
+ // a Response here so the headers always land on the wire.
92
+ const result = ctx.$result;
93
+ let response;
94
+ if (result instanceof Response) {
95
+ response = result;
96
+ }
97
+ else if (result == null) {
98
+ response = new Response(null, { status: 204 });
99
+ }
100
+ else if (typeof result === 'object') {
101
+ response = Response.json(result);
102
+ }
103
+ else {
104
+ response = new Response(String(result));
105
+ }
106
+ const merged = new Headers(response.headers);
107
+ merged.set('Access-Control-Allow-Origin', allowOrigin);
108
+ if (cfg.credentials)
109
+ merged.set('Access-Control-Allow-Credentials', 'true');
110
+ if (cfg.exposeHeaders?.length)
111
+ merged.set('Access-Control-Expose-Headers', cfg.exposeHeaders.join(', '));
112
+ // Vary: Origin keeps caches honest when the allow-origin is request-derived.
113
+ const existingVary = merged.get('Vary');
114
+ if (!existingVary) {
115
+ merged.set('Vary', 'Origin');
116
+ }
117
+ else if (!existingVary.split(',').map(s => s.trim().toLowerCase()).includes('origin')) {
118
+ merged.set('Vary', `${existingVary}, Origin`);
119
+ }
120
+ ctx.$result = new Response(response.body, {
121
+ status: response.status,
122
+ statusText: response.statusText,
123
+ headers: merged,
124
+ });
93
125
  };
94
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekir/cors",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "CORS middleware for cross-origin requests",
5
5
  "author": "dev@tekir.io",
6
6
  "license": "MIT",
package/src/cors.ts CHANGED
@@ -81,17 +81,45 @@ 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
- ctx.store = ctx.store || {}
89
- ctx.store.__corsHeaders = {
90
- 'Access-Control-Allow-Origin': allowOrigin,
91
- ...(cfg.credentials ? { 'Access-Control-Allow-Credentials': 'true' } : {}),
92
- ...(cfg.exposeHeaders?.length ? { 'Access-Control-Expose-Headers': cfg.exposeHeaders.join(', ') } : {}),
89
+ await next()
90
+
91
+ // Inject CORS headers into the actual response. Routes return a mix of
92
+ // Response objects (SSE streams, file downloads, custom payloads) and
93
+ // plain values that tekir wraps automatically. Coerce both shapes into
94
+ // a Response here so the headers always land on the wire.
95
+ const result = ctx.$result
96
+ let response: Response
97
+ if (result instanceof Response) {
98
+ response = result
99
+ } else if (result == null) {
100
+ response = new Response(null, { status: 204 })
101
+ } else if (typeof result === 'object') {
102
+ response = Response.json(result)
103
+ } else {
104
+ response = new Response(String(result))
93
105
  }
94
106
 
95
- await next()
107
+ const merged = new Headers(response.headers)
108
+ merged.set('Access-Control-Allow-Origin', allowOrigin)
109
+ if (cfg.credentials) merged.set('Access-Control-Allow-Credentials', 'true')
110
+ if (cfg.exposeHeaders?.length) merged.set('Access-Control-Expose-Headers', cfg.exposeHeaders.join(', '))
111
+ // Vary: Origin keeps caches honest when the allow-origin is request-derived.
112
+ const existingVary = merged.get('Vary')
113
+ if (!existingVary) {
114
+ merged.set('Vary', 'Origin')
115
+ } else if (!existingVary.split(',').map(s => s.trim().toLowerCase()).includes('origin')) {
116
+ merged.set('Vary', `${existingVary}, Origin`)
117
+ }
118
+
119
+ ctx.$result = new Response(response.body, {
120
+ status: response.status,
121
+ statusText: response.statusText,
122
+ headers: merged,
123
+ })
96
124
  }
97
125
  }