@rayselfs/cf-rule-engine 1.7.0 → 1.8.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.
Files changed (43) hide show
  1. package/README.md +31 -3
  2. package/dist/adapters/cf-function.cjs +4 -4
  3. package/dist/adapters/cf-function.js +3 -3
  4. package/dist/adapters/lambda-edge.d.cts +1 -1
  5. package/dist/adapters/lambda-edge.d.ts +1 -1
  6. package/dist/adapters/viewer-request.cjs +3 -3
  7. package/dist/adapters/viewer-request.js +2 -2
  8. package/dist/behaviors/index.cjs +13 -13
  9. package/dist/behaviors/index.js +12 -12
  10. package/dist/behaviors/set-cors-headers.cjs +6 -2
  11. package/dist/behaviors/set-cors-headers.d.cts +53 -42
  12. package/dist/behaviors/set-cors-headers.d.ts +53 -42
  13. package/dist/behaviors/set-cors-headers.js +5 -1
  14. package/dist/{chunk-H2LO6MQG.js → chunk-CLDF6FM5.js} +22 -12
  15. package/dist/{chunk-PR5UQJCC.js → chunk-MTZ35AIR.js} +1 -1
  16. package/dist/{chunk-SOBTD7AD.js → chunk-PI7RKMGS.js} +21 -10
  17. package/dist/{chunk-63WIEBQB.cjs → chunk-PW4FKMJ2.cjs} +22 -12
  18. package/dist/{chunk-5ZIB3AJ7.cjs → chunk-SYPOHDLN.cjs} +2 -2
  19. package/dist/chunk-XWJEVLMZ.cjs +50 -0
  20. package/dist/criteria/header-equals.d.cts +2 -2
  21. package/dist/criteria/header-equals.d.ts +2 -2
  22. package/dist/criteria/index.cjs +12 -12
  23. package/dist/criteria/index.js +12 -12
  24. package/dist/helpers/index.cjs +9 -8
  25. package/dist/helpers/index.js +8 -7
  26. package/dist/helpers/preflight-request.cjs +3 -2
  27. package/dist/helpers/preflight-request.d.cts +7 -6
  28. package/dist/helpers/preflight-request.d.ts +7 -6
  29. package/dist/helpers/preflight-request.js +2 -1
  30. package/dist/helpers/whitelist.cjs +3 -3
  31. package/dist/helpers/whitelist.js +2 -2
  32. package/dist/index.cjs +7 -7
  33. package/dist/index.d.cts +1 -1
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +5 -5
  36. package/dist/{lambda-edge-9xiONGmR.d.cts → lambda-edge-D1NHYwvA.d.cts} +1 -1
  37. package/dist/{lambda-edge-BK3-bFx8.d.ts → lambda-edge-DnIvWFGe.d.ts} +1 -1
  38. package/package.json +1 -1
  39. package/dist/chunk-GK5JX7OM.cjs +0 -39
  40. package/dist/{chunk-SGN2N3WI.cjs → chunk-FK3B4SP5.cjs} +2 -2
  41. package/dist/{chunk-BJZPAQHW.js → chunk-I2GJR6LY.js} +3 -3
  42. package/dist/{chunk-ER2YEZZO.js → chunk-RL7ZETZR.js} +3 -3
  43. package/dist/{chunk-MO7HW25R.cjs → chunk-T5EXFHVA.cjs} +3 -3
package/README.md CHANGED
@@ -29,12 +29,12 @@ export default defineViewerRequest([
29
29
  **viewer-response** — security and CORS headers:
30
30
 
31
31
  ```typescript
32
- import { setSecurityHeaders, setCorsHeaders } from '@rayselfs/cf-rule-engine/behaviors/index'
32
+ import { setSecurityHeaders, setCorsHeaders, ORIGIN_WILDCARD } from '@rayselfs/cf-rule-engine/behaviors/index'
33
33
  import { defineViewerResponse } from '@rayselfs/cf-rule-engine/adapters/viewer-response'
34
34
 
35
35
  export default defineViewerResponse([
36
36
  setSecurityHeaders(),
37
- setCorsHeaders({ allowedOrigins: ['https://www.example.com'] }),
37
+ setCorsHeaders({ allowedOrigins: ORIGIN_WILDCARD }),
38
38
  ])
39
39
  ```
40
40
 
@@ -113,6 +113,33 @@ Use `chain()` when one behavior must see the request mutations (URI rewrite, hea
113
113
  | `imageOptimize(options)` | ✅ | ✅ |
114
114
  | `verifyToken(options)` | ❌ | ✅ |
115
115
 
116
+ ### setCorsHeaders — OriginPolicy
117
+
118
+ `allowedOrigins` accepts an `OriginPolicy` that controls how `Access-Control-Allow-Origin` is set:
119
+
120
+ | Value | Behavior |
121
+ |---|---|
122
+ | `ORIGIN_WILDCARD` (`'*'`) | Static `Access-Control-Allow-Origin: *` — for fully public APIs |
123
+ | `Origin[]` | Compare request `Origin` against the list; echo if matched, skip if not. Supports wildcard subdomains (`https://*.example.com`). |
124
+ | `ORIGIN_ECHO` (`'echo'`) | Echo any request `Origin` if present, skip if none — use with `allowCredentials: true` |
125
+
126
+ ```typescript
127
+ import { setCorsHeaders, ORIGIN_WILDCARD, ORIGIN_ECHO } from '@rayselfs/cf-rule-engine/behaviors/index'
128
+
129
+ // Public API
130
+ setCorsHeaders({ allowedOrigins: ORIGIN_WILDCARD })
131
+
132
+ // Restricted — echo only listed origins
133
+ setCorsHeaders({ allowedOrigins: ['https://*.viverse.com', 'https://sdk-api.viverse.com'] })
134
+
135
+ // Echo any origin (required when allowCredentials: true)
136
+ setCorsHeaders({ allowedOrigins: ORIGIN_ECHO, allowCredentials: true })
137
+ ```
138
+
139
+ `allowedMethods` accepts a `Methods[]` array (e.g. `['GET', 'POST', 'OPTIONS']`); items are joined with `, ` to form the header value.
140
+ `allowedHeaders` accepts a `string[]` array (e.g. `['Content-Type', 'Authorization']`); items are joined with `, `.
141
+ Both are optional — omit to exclude those headers from the response.
142
+
116
143
  ## Helpers (`@rayselfs/cf-rule-engine/helpers/index`)
117
144
 
118
145
  Helpers are pre-configured rule factories that combine multiple criteria and behaviors for common use cases.
@@ -134,9 +161,10 @@ Adds `x-cf-distribution: staging` to the response when the request carries `aws-
134
161
 
135
162
  ```typescript
136
163
  import { stagingIndicator } from '@rayselfs/cf-rule-engine/helpers/index'
164
+ import { setCorsHeaders, ORIGIN_WILDCARD } from '@rayselfs/cf-rule-engine/behaviors/index'
137
165
 
138
166
  defineViewerResponse([
139
- setCorsHeaders({ allowedOrigins: ['https://www.example.com'] }),
167
+ setCorsHeaders({ allowedOrigins: ORIGIN_WILDCARD }),
140
168
  stagingIndicator(),
141
169
  ])
142
170
  ```
@@ -1,14 +1,14 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true});require('../chunk-5ZIB3AJ7.cjs');
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});require('../chunk-SYPOHDLN.cjs');
2
2
 
3
3
 
4
- var _chunkSGN2N3WIcjs = require('../chunk-SGN2N3WI.cjs');
5
- require('../chunk-WKYMSRCD.cjs');
4
+ var _chunkFK3B4SP5cjs = require('../chunk-FK3B4SP5.cjs');
6
5
 
7
6
 
8
7
  var _chunkN6QAQALUcjs = require('../chunk-N6QAQALU.cjs');
9
8
  require('../chunk-6NFAPLQ7.cjs');
9
+ require('../chunk-WKYMSRCD.cjs');
10
10
  require('../chunk-75ZPJI57.cjs');
11
11
 
12
12
 
13
13
 
14
- exports.defineViewerRequest = _chunkSGN2N3WIcjs.defineViewerRequest; exports.defineViewerResponse = _chunkN6QAQALUcjs.defineViewerResponse;
14
+ exports.defineViewerRequest = _chunkFK3B4SP5cjs.defineViewerRequest; exports.defineViewerResponse = _chunkN6QAQALUcjs.defineViewerResponse;
@@ -1,12 +1,12 @@
1
- import "../chunk-PR5UQJCC.js";
1
+ import "../chunk-MTZ35AIR.js";
2
2
  import {
3
3
  defineViewerRequest
4
- } from "../chunk-BJZPAQHW.js";
5
- import "../chunk-Q4NP4C3B.js";
4
+ } from "../chunk-I2GJR6LY.js";
6
5
  import {
7
6
  defineViewerResponse
8
7
  } from "../chunk-TURH5IFN.js";
9
8
  import "../chunk-CQ7YZ3AR.js";
9
+ import "../chunk-Q4NP4C3B.js";
10
10
  import "../chunk-MLKGABMK.js";
11
11
  export {
12
12
  defineViewerRequest,
@@ -1,2 +1,2 @@
1
1
  import '../core/types.cjs';
2
- export { d as defineViewerRequest, a as defineViewerResponse } from '../lambda-edge-9xiONGmR.cjs';
2
+ export { d as defineViewerRequest, a as defineViewerResponse } from '../lambda-edge-D1NHYwvA.cjs';
@@ -1,2 +1,2 @@
1
1
  import '../core/types.js';
2
- export { d as defineViewerRequest, a as defineViewerResponse } from '../lambda-edge-BK3-bFx8.js';
2
+ export { d as defineViewerRequest, a as defineViewerResponse } from '../lambda-edge-DnIvWFGe.js';
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkSGN2N3WIcjs = require('../chunk-SGN2N3WI.cjs');
4
- require('../chunk-WKYMSRCD.cjs');
3
+ var _chunkFK3B4SP5cjs = require('../chunk-FK3B4SP5.cjs');
5
4
  require('../chunk-6NFAPLQ7.cjs');
5
+ require('../chunk-WKYMSRCD.cjs');
6
6
  require('../chunk-75ZPJI57.cjs');
7
7
 
8
8
 
9
- exports.defineViewerRequest = _chunkSGN2N3WIcjs.defineViewerRequest;
9
+ exports.defineViewerRequest = _chunkFK3B4SP5cjs.defineViewerRequest;
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  defineViewerRequest
3
- } from "../chunk-BJZPAQHW.js";
4
- import "../chunk-Q4NP4C3B.js";
3
+ } from "../chunk-I2GJR6LY.js";
5
4
  import "../chunk-CQ7YZ3AR.js";
5
+ import "../chunk-Q4NP4C3B.js";
6
6
  import "../chunk-MLKGABMK.js";
7
7
  export {
8
8
  defineViewerRequest
@@ -1,5 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
2
 
3
+ var _chunkXWJEVLMZcjs = require('../chunk-XWJEVLMZ.cjs');
4
+
5
+
6
+ var _chunkZXS23HXAcjs = require('../chunk-ZXS23HXA.cjs');
7
+
8
+
3
9
  var _chunkPPUHEL4Hcjs = require('../chunk-PPUHEL4H.cjs');
4
10
 
5
11
 
@@ -12,6 +18,12 @@ var _chunk3UXNXJ6Ncjs = require('../chunk-3UXNXJ6N.cjs');
12
18
  var _chunkMSES76XKcjs = require('../chunk-MSES76XK.cjs');
13
19
 
14
20
 
21
+ var _chunkJU5WX5RUcjs = require('../chunk-JU5WX5RU.cjs');
22
+
23
+
24
+ var _chunkLTLBEBKLcjs = require('../chunk-LTLBEBKL.cjs');
25
+
26
+
15
27
  var _chunkKXC6ES3Bcjs = require('../chunk-KXC6ES3B.cjs');
16
28
 
17
29
 
@@ -27,19 +39,7 @@ var _chunkMRPTC74Icjs = require('../chunk-MRPTC74I.cjs');
27
39
  var _chunkCV234DQTcjs = require('../chunk-CV234DQT.cjs');
28
40
 
29
41
 
30
- var _chunkGK5JX7OMcjs = require('../chunk-GK5JX7OM.cjs');
31
-
32
-
33
- var _chunkZXS23HXAcjs = require('../chunk-ZXS23HXA.cjs');
34
-
35
-
36
42
  var _chunkOSGZTNTScjs = require('../chunk-OSGZTNTS.cjs');
37
-
38
-
39
- var _chunkJU5WX5RUcjs = require('../chunk-JU5WX5RU.cjs');
40
-
41
-
42
- var _chunkLTLBEBKLcjs = require('../chunk-LTLBEBKL.cjs');
43
43
  require('../chunk-75ZPJI57.cjs');
44
44
 
45
45
  // src/behaviors/verify-token.ts
@@ -123,4 +123,4 @@ function verifyToken(options) {
123
123
 
124
124
 
125
125
 
126
- exports.constructResponse = _chunkOSGZTNTScjs.constructResponse; exports.copyHeader = _chunkJU5WX5RUcjs.copyHeader; exports.directoryIndex = _chunkLTLBEBKLcjs.directoryIndex; exports.imageOptimize = _chunkKXC6ES3Bcjs.imageOptimize; exports.redirect = _chunkWWSRNCUPcjs.redirect; exports.removeResponseHeaders = _chunkSGEBNQR2cjs.removeResponseHeaders; exports.rewriteUri = _chunkMRPTC74Icjs.rewriteUri; exports.setCacheControl = _chunkCV234DQTcjs.setCacheControl; exports.setCorsHeaders = _chunkGK5JX7OMcjs.setCorsHeaders; exports.setCsp = _chunkZXS23HXAcjs.setCsp; exports.setRequestHeader = _chunkPPUHEL4Hcjs.setRequestHeader; exports.setResponseHeader = _chunkB4WEJSEZcjs.setResponseHeader; exports.setSecurityHeaders = _chunk3UXNXJ6Ncjs.setSecurityHeaders; exports.stripQueryParams = _chunkMSES76XKcjs.stripQueryParams; exports.verifyToken = verifyToken;
126
+ exports.constructResponse = _chunkOSGZTNTScjs.constructResponse; exports.copyHeader = _chunkJU5WX5RUcjs.copyHeader; exports.directoryIndex = _chunkLTLBEBKLcjs.directoryIndex; exports.imageOptimize = _chunkKXC6ES3Bcjs.imageOptimize; exports.redirect = _chunkWWSRNCUPcjs.redirect; exports.removeResponseHeaders = _chunkSGEBNQR2cjs.removeResponseHeaders; exports.rewriteUri = _chunkMRPTC74Icjs.rewriteUri; exports.setCacheControl = _chunkCV234DQTcjs.setCacheControl; exports.setCorsHeaders = _chunkXWJEVLMZcjs.setCorsHeaders; exports.setCsp = _chunkZXS23HXAcjs.setCsp; exports.setRequestHeader = _chunkPPUHEL4Hcjs.setRequestHeader; exports.setResponseHeader = _chunkB4WEJSEZcjs.setResponseHeader; exports.setSecurityHeaders = _chunk3UXNXJ6Ncjs.setSecurityHeaders; exports.stripQueryParams = _chunkMSES76XKcjs.stripQueryParams; exports.verifyToken = verifyToken;
@@ -1,3 +1,9 @@
1
+ import {
2
+ setCorsHeaders
3
+ } from "../chunk-PI7RKMGS.js";
4
+ import {
5
+ setCsp
6
+ } from "../chunk-XUI4Y22M.js";
1
7
  import {
2
8
  setRequestHeader
3
9
  } from "../chunk-M5KUQBDW.js";
@@ -10,6 +16,12 @@ import {
10
16
  import {
11
17
  stripQueryParams
12
18
  } from "../chunk-XPQG5IML.js";
19
+ import {
20
+ copyHeader
21
+ } from "../chunk-BDNPQ7AU.js";
22
+ import {
23
+ directoryIndex
24
+ } from "../chunk-R7WXS7BI.js";
13
25
  import {
14
26
  imageOptimize
15
27
  } from "../chunk-LQRLWDQQ.js";
@@ -25,21 +37,9 @@ import {
25
37
  import {
26
38
  setCacheControl
27
39
  } from "../chunk-ZTMSH34E.js";
28
- import {
29
- setCorsHeaders
30
- } from "../chunk-SOBTD7AD.js";
31
- import {
32
- setCsp
33
- } from "../chunk-XUI4Y22M.js";
34
40
  import {
35
41
  constructResponse
36
42
  } from "../chunk-6DBZBV2M.js";
37
- import {
38
- copyHeader
39
- } from "../chunk-BDNPQ7AU.js";
40
- import {
41
- directoryIndex
42
- } from "../chunk-R7WXS7BI.js";
43
43
  import "../chunk-MLKGABMK.js";
44
44
 
45
45
  // src/behaviors/verify-token.ts
@@ -1,7 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
- var _chunkGK5JX7OMcjs = require('../chunk-GK5JX7OM.cjs');
3
+
4
+
5
+ var _chunkXWJEVLMZcjs = require('../chunk-XWJEVLMZ.cjs');
4
6
  require('../chunk-75ZPJI57.cjs');
5
7
 
6
8
 
7
- exports.setCorsHeaders = _chunkGK5JX7OMcjs.setCorsHeaders;
9
+
10
+
11
+ exports.ORIGIN_ECHO = _chunkXWJEVLMZcjs.ORIGIN_ECHO; exports.ORIGIN_WILDCARD = _chunkXWJEVLMZcjs.ORIGIN_WILDCARD; exports.setCorsHeaders = _chunkXWJEVLMZcjs.setCorsHeaders;
@@ -1,38 +1,52 @@
1
1
  import { ResponseBehaviorFn } from '../core/types.cjs';
2
2
 
3
+ declare const ORIGIN_WILDCARD: "*";
4
+ type OriginWildcard = typeof ORIGIN_WILDCARD;
5
+ declare const ORIGIN_ECHO: "echo";
6
+ type OriginEcho = typeof ORIGIN_ECHO;
3
7
  /**
4
- * CORS configuration options for the `setCorsHeaders` behavior.
8
+ * A valid HTTP origin must start with `https://` or `http://`.
9
+ * Supports wildcard subdomains (e.g. `https://*.viverse.com`).
10
+ */
11
+ type Origin = `https://${string}` | `http://${string}`;
12
+ /**
13
+ * Controls how `Access-Control-Allow-Origin` is set:
14
+ * - `ORIGIN_WILDCARD` (`'*'`) — static `*`, allows all origins without inspection
15
+ * - `Origin[]` — compare request `Origin` header against the list; echo if matched, skip if not
16
+ * - `ORIGIN_ECHO` (`'echo'`) — echo any request `Origin` if present, skip if none
17
+ */
18
+ type OriginPolicy = OriginWildcard | Origin[] | OriginEcho;
19
+ /**
20
+ * Standard HTTP methods allowed in `Access-Control-Allow-Methods`.
21
+ */
22
+ type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT';
23
+ /**
24
+ * CORS configuration options for `setCorsHeaders` and `preflightRequest`.
5
25
  */
6
26
  interface CorsOptions {
7
27
  /**
8
- * List of allowed origin patterns. Supports exact strings and wildcard `*` patterns
9
- * (e.g. `'https://*.example.com'`). Use `['*']` to allow all origins.
10
- * Default: `['*']`
11
- */
12
- allowedOrigins?: string[];
13
- /**
14
- * When `true`, reflects the incoming `Origin` request header as the
15
- * `Access-Control-Allow-Origin` response value, provided it matches one of
16
- * `allowedOrigins`. Required when `allowCredentials` is `true` (browsers reject
17
- * `Access-Control-Allow-Origin: *` with credentials).
18
- * Default: `false`
28
+ * Origin policy. See `OriginPolicy` for details.
19
29
  */
20
- allowOriginEcho?: boolean;
30
+ allowedOrigins: OriginPolicy;
21
31
  /**
22
- * Value for the `Access-Control-Allow-Methods` header.
23
- * Default: `'GET, POST, OPTIONS'`
32
+ * HTTP methods to include in `Access-Control-Allow-Methods`.
33
+ * Array items are joined with `, ` before being written to the header.
34
+ * Omit to exclude the header.
35
+ *
36
+ * @example `['GET', 'POST', 'OPTIONS']`
24
37
  */
25
- allowedMethods?: string;
38
+ allowedMethods?: Methods[];
26
39
  /**
27
- * Value for the `Access-Control-Allow-Headers` header.
28
- * Default: `'Content-Type, Cache-Control, Pragma, Range'`
40
+ * Header names to include in `Access-Control-Allow-Headers`.
41
+ * Array items are joined with `, ` before being written to the header.
42
+ * Omit to exclude the header.
43
+ *
44
+ * @example `['Content-Type', 'Authorization']`
29
45
  */
30
- allowedHeaders?: string;
46
+ allowedHeaders?: string[];
31
47
  /**
32
48
  * When `true`, sets `Access-Control-Allow-Credentials: true`.
33
- * Must be used together with `allowOriginEcho: true`; browsers reject
34
- * wildcard origins when credentials are present.
35
- * Default: `false`
49
+ * Use with `ORIGIN_ECHO` or `Origin[]` browsers reject `*` with credentials.
36
50
  */
37
51
  allowCredentials?: boolean;
38
52
  /**
@@ -42,35 +56,32 @@ interface CorsOptions {
42
56
  maxAge?: number;
43
57
  }
44
58
  /**
45
- * Sets CORS response headers with configurable origin matching, methods, headers,
46
- * credentials, and preflight cache duration.
59
+ * Sets CORS response headers with configurable origin policy.
47
60
  *
48
- * Akamai equivalent: typically implemented via `modifyOutgoingResponseHeader` rules
49
- * for each CORS header individually.
50
- *
51
- * @param options - CORS configuration. All fields are optional with safe defaults.
61
+ * @param options - CORS configuration. `allowedOrigins` is required.
52
62
  * @returns A `ResponseBehaviorFn` to use directly in `defineViewerResponse` or wrapped in a `ResponseRule`.
53
63
  *
54
64
  * @example
55
65
  * ```ts
56
- * import { setCorsHeaders } from '@rayselfs/cf-rule-engine/behaviors'
57
- * import { defineViewerResponse } from '@rayselfs/cf-rule-engine/adapters/cf-function'
66
+ * import { setCorsHeaders, ORIGIN_WILDCARD, ORIGIN_ECHO } from '@rayselfs/cf-rule-engine/behaviors/set-cors-headers'
67
+ * import { defineViewerResponse } from '@rayselfs/cf-rule-engine/adapters/viewer-response'
68
+ *
69
+ * // Public API — static Access-Control-Allow-Origin: *
70
+ * export default defineViewerResponse([
71
+ * setCorsHeaders({ allowedOrigins: ORIGIN_WILDCARD }),
72
+ * ])
58
73
  *
59
- * // Allow all origins (default)
60
- * export default defineViewerResponse([setCorsHeaders()])
74
+ * // Restricted echo only listed origins (supports wildcard subdomains)
75
+ * export default defineViewerResponse([
76
+ * setCorsHeaders({ allowedOrigins: ['https://*.viverse.com', 'https://sdk-api.viverse.com'] }),
77
+ * ])
61
78
  *
62
- * // Echo origin with credentials (e.g. for authenticated API endpoints)
79
+ * // Echo any origin (e.g. for credentialed requests)
63
80
  * export default defineViewerResponse([
64
- * setCorsHeaders({
65
- * allowedOrigins: ['https://www.example.com', 'https://*.example.com'],
66
- * allowOriginEcho: true,
67
- * allowCredentials: true,
68
- * allowedMethods: 'GET, POST, PUT, DELETE, OPTIONS',
69
- * maxAge: 86400,
70
- * }),
81
+ * setCorsHeaders({ allowedOrigins: ORIGIN_ECHO, allowCredentials: true }),
71
82
  * ])
72
83
  * ```
73
84
  */
74
- declare function setCorsHeaders(options?: CorsOptions): ResponseBehaviorFn;
85
+ declare function setCorsHeaders(options: CorsOptions): ResponseBehaviorFn;
75
86
 
76
- export { type CorsOptions, setCorsHeaders };
87
+ export { type CorsOptions, type Methods, ORIGIN_ECHO, ORIGIN_WILDCARD, type Origin, type OriginEcho, type OriginPolicy, type OriginWildcard, setCorsHeaders };
@@ -1,38 +1,52 @@
1
1
  import { ResponseBehaviorFn } from '../core/types.js';
2
2
 
3
+ declare const ORIGIN_WILDCARD: "*";
4
+ type OriginWildcard = typeof ORIGIN_WILDCARD;
5
+ declare const ORIGIN_ECHO: "echo";
6
+ type OriginEcho = typeof ORIGIN_ECHO;
3
7
  /**
4
- * CORS configuration options for the `setCorsHeaders` behavior.
8
+ * A valid HTTP origin must start with `https://` or `http://`.
9
+ * Supports wildcard subdomains (e.g. `https://*.viverse.com`).
10
+ */
11
+ type Origin = `https://${string}` | `http://${string}`;
12
+ /**
13
+ * Controls how `Access-Control-Allow-Origin` is set:
14
+ * - `ORIGIN_WILDCARD` (`'*'`) — static `*`, allows all origins without inspection
15
+ * - `Origin[]` — compare request `Origin` header against the list; echo if matched, skip if not
16
+ * - `ORIGIN_ECHO` (`'echo'`) — echo any request `Origin` if present, skip if none
17
+ */
18
+ type OriginPolicy = OriginWildcard | Origin[] | OriginEcho;
19
+ /**
20
+ * Standard HTTP methods allowed in `Access-Control-Allow-Methods`.
21
+ */
22
+ type Methods = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'TRACE' | 'CONNECT';
23
+ /**
24
+ * CORS configuration options for `setCorsHeaders` and `preflightRequest`.
5
25
  */
6
26
  interface CorsOptions {
7
27
  /**
8
- * List of allowed origin patterns. Supports exact strings and wildcard `*` patterns
9
- * (e.g. `'https://*.example.com'`). Use `['*']` to allow all origins.
10
- * Default: `['*']`
11
- */
12
- allowedOrigins?: string[];
13
- /**
14
- * When `true`, reflects the incoming `Origin` request header as the
15
- * `Access-Control-Allow-Origin` response value, provided it matches one of
16
- * `allowedOrigins`. Required when `allowCredentials` is `true` (browsers reject
17
- * `Access-Control-Allow-Origin: *` with credentials).
18
- * Default: `false`
28
+ * Origin policy. See `OriginPolicy` for details.
19
29
  */
20
- allowOriginEcho?: boolean;
30
+ allowedOrigins: OriginPolicy;
21
31
  /**
22
- * Value for the `Access-Control-Allow-Methods` header.
23
- * Default: `'GET, POST, OPTIONS'`
32
+ * HTTP methods to include in `Access-Control-Allow-Methods`.
33
+ * Array items are joined with `, ` before being written to the header.
34
+ * Omit to exclude the header.
35
+ *
36
+ * @example `['GET', 'POST', 'OPTIONS']`
24
37
  */
25
- allowedMethods?: string;
38
+ allowedMethods?: Methods[];
26
39
  /**
27
- * Value for the `Access-Control-Allow-Headers` header.
28
- * Default: `'Content-Type, Cache-Control, Pragma, Range'`
40
+ * Header names to include in `Access-Control-Allow-Headers`.
41
+ * Array items are joined with `, ` before being written to the header.
42
+ * Omit to exclude the header.
43
+ *
44
+ * @example `['Content-Type', 'Authorization']`
29
45
  */
30
- allowedHeaders?: string;
46
+ allowedHeaders?: string[];
31
47
  /**
32
48
  * When `true`, sets `Access-Control-Allow-Credentials: true`.
33
- * Must be used together with `allowOriginEcho: true`; browsers reject
34
- * wildcard origins when credentials are present.
35
- * Default: `false`
49
+ * Use with `ORIGIN_ECHO` or `Origin[]` browsers reject `*` with credentials.
36
50
  */
37
51
  allowCredentials?: boolean;
38
52
  /**
@@ -42,35 +56,32 @@ interface CorsOptions {
42
56
  maxAge?: number;
43
57
  }
44
58
  /**
45
- * Sets CORS response headers with configurable origin matching, methods, headers,
46
- * credentials, and preflight cache duration.
59
+ * Sets CORS response headers with configurable origin policy.
47
60
  *
48
- * Akamai equivalent: typically implemented via `modifyOutgoingResponseHeader` rules
49
- * for each CORS header individually.
50
- *
51
- * @param options - CORS configuration. All fields are optional with safe defaults.
61
+ * @param options - CORS configuration. `allowedOrigins` is required.
52
62
  * @returns A `ResponseBehaviorFn` to use directly in `defineViewerResponse` or wrapped in a `ResponseRule`.
53
63
  *
54
64
  * @example
55
65
  * ```ts
56
- * import { setCorsHeaders } from '@rayselfs/cf-rule-engine/behaviors'
57
- * import { defineViewerResponse } from '@rayselfs/cf-rule-engine/adapters/cf-function'
66
+ * import { setCorsHeaders, ORIGIN_WILDCARD, ORIGIN_ECHO } from '@rayselfs/cf-rule-engine/behaviors/set-cors-headers'
67
+ * import { defineViewerResponse } from '@rayselfs/cf-rule-engine/adapters/viewer-response'
68
+ *
69
+ * // Public API — static Access-Control-Allow-Origin: *
70
+ * export default defineViewerResponse([
71
+ * setCorsHeaders({ allowedOrigins: ORIGIN_WILDCARD }),
72
+ * ])
58
73
  *
59
- * // Allow all origins (default)
60
- * export default defineViewerResponse([setCorsHeaders()])
74
+ * // Restricted echo only listed origins (supports wildcard subdomains)
75
+ * export default defineViewerResponse([
76
+ * setCorsHeaders({ allowedOrigins: ['https://*.viverse.com', 'https://sdk-api.viverse.com'] }),
77
+ * ])
61
78
  *
62
- * // Echo origin with credentials (e.g. for authenticated API endpoints)
79
+ * // Echo any origin (e.g. for credentialed requests)
63
80
  * export default defineViewerResponse([
64
- * setCorsHeaders({
65
- * allowedOrigins: ['https://www.example.com', 'https://*.example.com'],
66
- * allowOriginEcho: true,
67
- * allowCredentials: true,
68
- * allowedMethods: 'GET, POST, PUT, DELETE, OPTIONS',
69
- * maxAge: 86400,
70
- * }),
81
+ * setCorsHeaders({ allowedOrigins: ORIGIN_ECHO, allowCredentials: true }),
71
82
  * ])
72
83
  * ```
73
84
  */
74
- declare function setCorsHeaders(options?: CorsOptions): ResponseBehaviorFn;
85
+ declare function setCorsHeaders(options: CorsOptions): ResponseBehaviorFn;
75
86
 
76
- export { type CorsOptions, setCorsHeaders };
87
+ export { type CorsOptions, type Methods, ORIGIN_ECHO, ORIGIN_WILDCARD, type Origin, type OriginEcho, type OriginPolicy, type OriginWildcard, setCorsHeaders };
@@ -1,7 +1,11 @@
1
1
  import {
2
+ ORIGIN_ECHO,
3
+ ORIGIN_WILDCARD,
2
4
  setCorsHeaders
3
- } from "../chunk-SOBTD7AD.js";
5
+ } from "../chunk-PI7RKMGS.js";
4
6
  import "../chunk-MLKGABMK.js";
5
7
  export {
8
+ ORIGIN_ECHO,
9
+ ORIGIN_WILDCARD,
6
10
  setCorsHeaders
7
11
  };
@@ -1,6 +1,10 @@
1
1
  import {
2
2
  methodIs
3
3
  } from "./chunk-PY3JMRDG.js";
4
+ import {
5
+ ORIGIN_ECHO,
6
+ ORIGIN_WILDCARD
7
+ } from "./chunk-PI7RKMGS.js";
4
8
 
5
9
  // src/helpers/preflight-request.ts
6
10
  function matchesOriginPattern(origin, pattern) {
@@ -10,27 +14,33 @@ function matchesOriginPattern(origin, pattern) {
10
14
  return new RegExp(`^${escaped}$`).test(origin);
11
15
  }
12
16
  function preflightRequest(options) {
13
- const allowedOrigins = options?.allowedOrigins ?? ["*"];
14
- const allowedMethods = options?.allowedMethods ?? "GET, POST, OPTIONS";
15
- const allowedHeaders = options?.allowedHeaders ?? "Content-Type, Cache-Control, Pragma, Range";
16
- const allowCredentials = options?.allowCredentials ?? false;
17
- const maxAge = options?.maxAge;
17
+ const { allowedOrigins } = options;
18
+ const allowedMethods = options.allowedMethods ?? ["GET", "POST", "OPTIONS"];
19
+ const allowedHeaders = options.allowedHeaders ?? ["Content-Type", "Cache-Control", "Pragma", "Range"];
20
+ const allowCredentials = options.allowCredentials ?? false;
21
+ const maxAge = options.maxAge;
18
22
  return {
19
23
  criteria: methodIs(["OPTIONS"]),
20
24
  behavior: (request) => {
21
25
  let allowOrigin;
22
- if (options?.allowOriginEcho) {
23
- const originHeader = request.headers["origin"]?.value;
24
- allowOrigin = originHeader && allowedOrigins.some((p) => matchesOriginPattern(originHeader, p)) ? originHeader : allowedOrigins[0] ?? "*";
26
+ if (allowedOrigins === ORIGIN_WILDCARD) {
27
+ allowOrigin = "*";
28
+ } else if (allowedOrigins === ORIGIN_ECHO) {
29
+ allowOrigin = request.headers["origin"]?.value;
25
30
  } else {
26
- allowOrigin = allowedOrigins.includes("*") ? "*" : allowedOrigins[0] ?? "*";
31
+ const originHeader = request.headers["origin"]?.value;
32
+ if (originHeader && allowedOrigins.some((p) => matchesOriginPattern(originHeader, p))) {
33
+ allowOrigin = originHeader;
34
+ }
27
35
  }
28
36
  const headers = {
29
37
  "cache-control": { value: "no-store" },
30
- "access-control-allow-origin": { value: allowOrigin },
31
- "access-control-allow-methods": { value: allowedMethods },
32
- "access-control-allow-headers": { value: allowedHeaders }
38
+ "access-control-allow-methods": { value: allowedMethods.join(", ") },
39
+ "access-control-allow-headers": { value: allowedHeaders.join(", ") }
33
40
  };
41
+ if (allowOrigin !== void 0) {
42
+ headers["access-control-allow-origin"] = { value: allowOrigin };
43
+ }
34
44
  if (allowCredentials) {
35
45
  headers["access-control-allow-credentials"] = { value: "true" };
36
46
  }
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  defineViewerRequest
3
- } from "./chunk-BJZPAQHW.js";
3
+ } from "./chunk-I2GJR6LY.js";
4
4
  import {
5
5
  defineViewerResponse
6
6
  } from "./chunk-TURH5IFN.js";
@@ -1,4 +1,6 @@
1
1
  // src/behaviors/set-cors-headers.ts
2
+ var ORIGIN_WILDCARD = "*";
3
+ var ORIGIN_ECHO = "echo";
2
4
  function matchesOriginPattern(origin, pattern) {
3
5
  if (pattern === "*") return true;
4
6
  if (!pattern.includes("*")) return origin === pattern;
@@ -6,26 +8,33 @@ function matchesOriginPattern(origin, pattern) {
6
8
  return new RegExp(`^${escaped}$`).test(origin);
7
9
  }
8
10
  function setCorsHeaders(options) {
9
- const allowedOrigins = options?.allowedOrigins ?? ["*"];
10
- const allowedMethods = options?.allowedMethods ?? "GET, POST, OPTIONS";
11
- const allowedHeaders = options?.allowedHeaders ?? "Content-Type, Cache-Control, Pragma, Range";
11
+ const { allowedOrigins } = options;
12
12
  return (request, response) => {
13
- let allowOrigin = allowedOrigins[0] ?? "*";
14
- if (options?.allowOriginEcho) {
13
+ let allowOrigin;
14
+ if (allowedOrigins === ORIGIN_WILDCARD) {
15
+ allowOrigin = "*";
16
+ } else if (allowedOrigins === ORIGIN_ECHO) {
17
+ allowOrigin = request.headers["origin"]?.value;
18
+ } else {
15
19
  const originHeader = request.headers["origin"]?.value;
16
20
  if (originHeader && allowedOrigins.some((p) => matchesOriginPattern(originHeader, p))) {
17
21
  allowOrigin = originHeader;
18
22
  }
19
23
  }
24
+ if (allowOrigin === void 0) return response;
20
25
  const corsHeaders = {
21
- "access-control-allow-origin": { value: allowOrigin },
22
- "access-control-allow-methods": { value: allowedMethods },
23
- "access-control-allow-headers": { value: allowedHeaders }
26
+ "access-control-allow-origin": { value: allowOrigin }
24
27
  };
25
- if (options?.allowCredentials) {
28
+ if (options.allowedMethods !== void 0) {
29
+ corsHeaders["access-control-allow-methods"] = { value: options.allowedMethods.join(", ") };
30
+ }
31
+ if (options.allowedHeaders !== void 0) {
32
+ corsHeaders["access-control-allow-headers"] = { value: options.allowedHeaders.join(", ") };
33
+ }
34
+ if (options.allowCredentials) {
26
35
  corsHeaders["access-control-allow-credentials"] = { value: "true" };
27
36
  }
28
- if (options?.maxAge !== void 0) {
37
+ if (options.maxAge !== void 0) {
29
38
  corsHeaders["access-control-max-age"] = { value: String(options.maxAge) };
30
39
  }
31
40
  return Object.assign({}, response, {
@@ -35,5 +44,7 @@ function setCorsHeaders(options) {
35
44
  }
36
45
 
37
46
  export {
47
+ ORIGIN_WILDCARD,
48
+ ORIGIN_ECHO,
38
49
  setCorsHeaders
39
50
  };