@adonix.org/cloud-spark 2.0.1 → 2.0.3
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/README.md +10 -13
- package/dist/cache.d.ts +58 -0
- package/dist/cache.js +2 -2
- package/dist/cache.js.map +1 -1
- package/dist/cors.d.ts +106 -0
- package/dist/cors.js +1 -1
- package/dist/cors.js.map +1 -1
- package/dist/index.d.ts +11 -504
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware-CfKw8AlN.d.ts +106 -0
- package/dist/sessions.d.ts +207 -0
- package/dist/websocket.d.ts +29 -0
- package/dist/websocket.js +1 -1
- package/dist/websocket.js.map +1 -1
- package/package.json +10 -10
package/dist/cors.d.ts
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { b as Middleware } from './middleware-CfKw8AlN.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* User-supplied options for configuring `CORS` behavior.
|
|
5
|
+
*
|
|
6
|
+
* This is a partial form of {@link CorsConfig}, meaning you only need
|
|
7
|
+
* to provide the options you want to override. Any missing values will
|
|
8
|
+
* fall back to the {@link defaultCorsConfig}.
|
|
9
|
+
*
|
|
10
|
+
* Example:
|
|
11
|
+
* ```ts
|
|
12
|
+
* const cors: CorsInit = {
|
|
13
|
+
* allowedOrigins: ["https://example.com"],
|
|
14
|
+
* allowCredentials: true,
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
type CorsInit = Partial<CorsConfig>;
|
|
19
|
+
/**
|
|
20
|
+
* Configuration options for Cross-Origin Resource Sharing `CORS`.
|
|
21
|
+
*
|
|
22
|
+
* Implementations of `CORS` middleware use this interface to determine
|
|
23
|
+
* how cross-origin requests are validated and which headers are sent
|
|
24
|
+
* in the response.
|
|
25
|
+
*
|
|
26
|
+
* @default
|
|
27
|
+
* ```ts
|
|
28
|
+
* {
|
|
29
|
+
* allowedOrigins: ["*"],
|
|
30
|
+
* allowedHeaders: ["Content-Type"],
|
|
31
|
+
* exposedHeaders: [],
|
|
32
|
+
* allowCredentials: false,
|
|
33
|
+
* maxAge: 300, // 5 minutes
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
interface CorsConfig {
|
|
38
|
+
/**
|
|
39
|
+
* Origins allowed for `CORS` requests.
|
|
40
|
+
*
|
|
41
|
+
* Use `["*"]` to allow all origins, or provide a list of specific origins.
|
|
42
|
+
* Example: `["https://example.com", "https://api.example.com"]`
|
|
43
|
+
*
|
|
44
|
+
* @default ["*"]
|
|
45
|
+
*/
|
|
46
|
+
allowedOrigins: string[];
|
|
47
|
+
/**
|
|
48
|
+
* HTTP headers allowed in `CORS` requests.
|
|
49
|
+
*
|
|
50
|
+
* Browsers always allow `CORS`-safelisted request headers* without preflight:
|
|
51
|
+
* - `Accept`
|
|
52
|
+
* - `Accept-Language`
|
|
53
|
+
* - `Content-Language`
|
|
54
|
+
* - `Content-Type` (but only if its value is `application/x-www-form-urlencoded`,
|
|
55
|
+
* `multipart/form-data`, or `text/plain`)
|
|
56
|
+
*
|
|
57
|
+
* Because `Content-Type` is only partially safelisted, it is included in the
|
|
58
|
+
* default allowed headers.
|
|
59
|
+
*
|
|
60
|
+
* Add custom headers here (e.g., `Authorization`) if your clients send them.
|
|
61
|
+
*
|
|
62
|
+
* @default ["Content-Type"]
|
|
63
|
+
*/
|
|
64
|
+
allowedHeaders: string[];
|
|
65
|
+
/**
|
|
66
|
+
* HTTP headers exposed to the client.
|
|
67
|
+
*
|
|
68
|
+
* By default, most headers are not accessible from client-side JavaScript.
|
|
69
|
+
* Use this option to explicitly allow certain response headers to be read.
|
|
70
|
+
*
|
|
71
|
+
* @default []
|
|
72
|
+
*/
|
|
73
|
+
exposedHeaders: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Whether the resource supports user credentials (cookies, HTTP authentication).
|
|
76
|
+
*
|
|
77
|
+
* If true, the Access-Control-Allow-Origin response header must not be "*".
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*/
|
|
81
|
+
allowCredentials: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Maximum age (in seconds) that the results of a preflight request
|
|
84
|
+
* can be cached by the client.
|
|
85
|
+
*
|
|
86
|
+
* Increase for production use to reduce preflights, or lower for development
|
|
87
|
+
* if you frequently adjust `CORS` rules.
|
|
88
|
+
*
|
|
89
|
+
* @default 300 (5 minutes)
|
|
90
|
+
*/
|
|
91
|
+
maxAge: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns a `CORS` middleware instance.
|
|
96
|
+
*
|
|
97
|
+
* This middleware automatically handles Cross-Origin Resource Sharing (CORS)
|
|
98
|
+
* for incoming requests, including preflight `OPTIONS` requests, and adds
|
|
99
|
+
* appropriate headers to responses.
|
|
100
|
+
*
|
|
101
|
+
* @param init - Optional configuration for `CORS` behavior. See {@link CorsConfig}.
|
|
102
|
+
* @returns A {@link Middleware} instance that can be used in your middleware chain.
|
|
103
|
+
*/
|
|
104
|
+
declare function cors(init?: CorsInit): Middleware;
|
|
105
|
+
|
|
106
|
+
export { cors };
|
package/dist/cors.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {StatusCodes}from'http-status-codes/build/es/status-codes';import
|
|
1
|
+
import {StatusCodes}from'http-status-codes/build/es/status-codes';import R from'cache-control-parser';import {getReasonPhrase}from'http-status-codes/build/es/utils-functions';function l(e){return Array.isArray(e)&&e.every(t=>typeof t=="string")}function S(e){return typeof e=="string"}function _(e){return typeof e=="number"&&!Number.isNaN(e)}function g(e){return typeof e=="boolean"}function I(e){if(e===void 0)return;if(typeof e!="object"||e===null)throw new TypeError("CorsInit must be an object.");let t=e;if(t.allowedOrigins!==void 0&&!l(t.allowedOrigins))throw new TypeError("CorsInit.allowedOrigins must be a string array.");if(t.allowedHeaders!==void 0&&!l(t.allowedHeaders))throw new TypeError("CorsInit.allowedHeaders must be a string array.");if(t.exposedHeaders!==void 0&&!l(t.exposedHeaders))throw new TypeError("CorsInit.exposedHeaders must be a string array.");if(t.allowCredentials!==void 0&&!g(t.allowCredentials))throw new TypeError("CorsInit.allowCredentials must be a boolean.");if(t.maxAge!==void 0&&!_(t.maxAge))throw new TypeError("CorsInit.maxAge must be a number.")}var u=(c=>(c.GET="GET",c.PUT="PUT",c.HEAD="HEAD",c.POST="POST",c.PATCH="PATCH",c.DELETE="DELETE",c.OPTIONS="OPTIONS",c))(u||{}),{GET:oe,PUT:ne,HEAD:ie,POST:ae,PATCH:ce,DELETE:de,OPTIONS:x}=u;var H={parse:R.parse,stringify:R.stringify,DISABLE:Object.freeze({"no-cache":true,"no-store":true,"must-revalidate":true,"max-age":0})};var L={Minute:60};var s={CACHE_CONTROL:"cache-control",CONTENT_DISPOSITION:"content-disposition",CONTENT_ENCODING:"content-encoding",CONTENT_LANGUAGE:"content-language",CONTENT_LENGTH:"content-length",CONTENT_RANGE:"content-range",CONTENT_TYPE:"content-type",CONTENT_MD5:"content-md5",ORIGIN:"origin",VARY:"vary",ACCESS_CONTROL_ALLOW_CREDENTIALS:"access-control-allow-credentials",ACCESS_CONTROL_ALLOW_HEADERS:"access-control-allow-headers",ACCESS_CONTROL_ALLOW_METHODS:"access-control-allow-methods",ACCESS_CONTROL_ALLOW_ORIGIN:"access-control-allow-origin",ACCESS_CONTROL_EXPOSE_HEADERS:"access-control-expose-headers",ACCESS_CONTROL_MAX_AGE:"access-control-max-age",UPGRADE:"upgrade"},w=[s.CONTENT_TYPE,s.CONTENT_LENGTH,s.CONTENT_RANGE,s.CONTENT_ENCODING,s.CONTENT_LANGUAGE,s.CONTENT_DISPOSITION,s.CONTENT_MD5],y=[s.CONTENT_LENGTH,s.CONTENT_RANGE];var p="*",D=[StatusCodes.SWITCHING_PROTOCOLS,StatusCodes.CONTINUE,StatusCodes.PROCESSING,StatusCodes.EARLY_HINTS,StatusCodes.MOVED_PERMANENTLY,StatusCodes.MOVED_TEMPORARILY,StatusCodes.SEE_OTHER,StatusCodes.TEMPORARY_REDIRECT,StatusCodes.PERMANENT_REDIRECT],b={allowedOrigins:[p],allowedHeaders:[s.CONTENT_TYPE],exposedHeaders:[],allowCredentials:false,maxAge:5*L.Minute};var V=new Set(Object.values(u));function j(e){return S(e)&&V.has(e)}function $(e){return Array.isArray(e)&&e.every(j)}function P(e){if(!$(e)){let t=Array.isArray(e)?JSON.stringify(e):String(e);throw new TypeError(`Invalid method array: ${t}`)}}var G="utf-8";function T(e,t){return e<t?-1:e>t?1:0}function a(e,t,r){let o=Array.isArray(r)?r:[r],i=Array.from(new Set(o.map(d=>d.trim()))).filter(d=>d.length).sort(T);if(!i.length){e.delete(t);return}e.set(t,i.join(", "));}function E(e,t,r){let o=Array.isArray(r)?r:[r];if(o.length===0)return;let d=q(e,t).concat(o.map(X=>X.trim()));a(e,t,d);}function q(e,t){let r=e.get(t)?.split(",").map(o=>o.trim()).filter(o=>o.length>0)??[];return Array.from(new Set(r)).sort(T)}function f(e,t){for(let r of t)e.delete(r);}function M(e,t){return !t||e.toLowerCase().includes("charset=")?e:`${e}; charset=${t.toLowerCase()}`}var m=class{headers=new Headers;status=StatusCodes.OK;statusText;webSocket;mediaType=M("text/plain",G);get responseInit(){return {headers:this.headers,status:this.status,statusText:this.statusText??getReasonPhrase(this.status),webSocket:this.webSocket,encodeBody:"automatic"}}setHeader(t,r){a(this.headers,t,r);}mergeHeader(t,r){E(this.headers,t,r);}addContentType(){this.headers.get(s.CONTENT_TYPE)||this.setHeader(s.CONTENT_TYPE,this.mediaType);}filterHeaders(){this.status===StatusCodes.NO_CONTENT?f(this.headers,y):this.status===StatusCodes.NOT_MODIFIED&&f(this.headers,w);}},N=class extends m{constructor(r){super();this.cache=r;}addCacheHeader(){this.cache&&this.setHeader(s.CACHE_CONTROL,H.stringify(this.cache));}},A=class extends N{constructor(r=null,o){super(o);this.body=r;}async response(){this.addCacheHeader();let r=[StatusCodes.NO_CONTENT,StatusCodes.NOT_MODIFIED].includes(this.status)?null:this.body;return r&&this.addContentType(),this.filterHeaders(),new Response(r,this.responseInit)}},C=class extends A{constructor(t,r){super(t.body,r),this.status=t.status,this.statusText=t.statusText,this.headers=new Headers(t.headers);}};function W(e,t,r){let o=new C(e),i=K(t.request);return Y(o.headers),v(o.headers,r),i&&(F(o.headers,r,i),U(o.headers,r,i),J(o.headers,t),Q(o.headers,r),Z(o.headers,r)),o.response()}function k(e,t,r){let o=new C(e),i=K(t.request);return Y(o.headers),v(o.headers,r),i&&(F(o.headers,r,i),U(o.headers,r,i),ee(o.headers,r)),o.response()}function v(e,t){h(t)||E(e,s.VARY,s.ORIGIN);}function F(e,t,r){if(h(t)){a(e,s.ACCESS_CONTROL_ALLOW_ORIGIN,p);return}t.allowedOrigins.includes(r)&&a(e,s.ACCESS_CONTROL_ALLOW_ORIGIN,r);}function U(e,t,r){t.allowCredentials&&(h(t)||t.allowedOrigins.includes(r)&&a(e,s.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true"));}function J(e,t){let r=t.getAllowedMethods();P(r),a(e,s.ACCESS_CONTROL_ALLOW_METHODS,r);}function Z(e,t){let r=Math.max(0,Math.floor(t.maxAge));a(e,s.ACCESS_CONTROL_MAX_AGE,String(r));}function Q(e,t){a(e,s.ACCESS_CONTROL_ALLOW_HEADERS,t.allowedHeaders);}function ee(e,t){a(e,s.ACCESS_CONTROL_EXPOSE_HEADERS,t.exposedHeaders);}function h(e){return e.allowedOrigins.includes(p)}function Y(e){e.delete(s.ACCESS_CONTROL_MAX_AGE),e.delete(s.ACCESS_CONTROL_ALLOW_ORIGIN),e.delete(s.ACCESS_CONTROL_ALLOW_HEADERS),e.delete(s.ACCESS_CONTROL_ALLOW_METHODS),e.delete(s.ACCESS_CONTROL_EXPOSE_HEADERS),e.delete(s.ACCESS_CONTROL_ALLOW_CREDENTIALS);}function B(e){let{status:t,headers:r}=e;return !!(D.includes(t)||r.has(s.UPGRADE))}function K(e){let t=e.headers.get(s.ORIGIN)?.trim();if(!t||t==="null")return null;try{return new URL(t).origin}catch{return null}}var O=class{config;constructor(t){this.config={...b,...t};}async handle(t,r){let o=await r();return t.request.method===x?W(o,t,this.config):B(o)?o:k(o,t,this.config)}};function Qe(e){return I(e),new O(e)}export{Qe as cors};//# sourceMappingURL=cors.js.map
|
|
2
2
|
//# sourceMappingURL=cors.js.map
|
package/dist/cors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/guards/basic.ts","../src/guards/cors.ts","../src/constants/methods.ts","../src/constants/cache.ts","../src/constants/time.ts","../src/constants/headers.ts","../src/middleware/cors/constants.ts","../src/guards/methods.ts","../src/constants/media.ts","../src/utils/compare.ts","../src/utils/headers.ts","../src/utils/media.ts","../src/responses.ts","../src/middleware/cors/utils.ts","../src/middleware/cors/handler.ts","../src/middleware/cors/cors.ts"],"names":["isStringArray","value","item","isString","isNumber","isBoolean","assertCorsInit","obj","Method","GET","PUT","HEAD","POST","PATCH","DELETE","OPTIONS","CacheControl","CacheLib","Time","HttpHeader","FORBIDDEN_304_HEADERS","FORBIDDEN_204_HEADERS","ALLOW_ALL_ORIGINS","SKIP_CORS_STATUSES","StatusCodes","defaultCorsConfig","METHOD_SET","isMethod","isMethodArray","assertMethods","desc","UTF8_CHARSET","lexCompare","a","b","setHeader","headers","key","raw","values","v","mergeHeader","merged","getHeaderValues","filterHeaders","keys","withCharset","mediaType","charset","BaseResponse","getReasonPhrase","CacheResponse","cache","WorkerResponse","body","CopyResponse","response","options","worker","cors","copy","origin","getOrigin","deleteCorsHeaders","setVaryOrigin","setAllowOrigin","setAllowCredentials","setAllowMethods","setAllowHeaders","setMaxAge","apply","setExposedHeaders","allowAllOrigins","allowed","maxAge","skipCors","status","request","CorsHandler","init","next"],"mappings":"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","file":"cors.js","sourcesContent":["/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the provided value is an array of strings.\n *\n * @param value - The value to check.\n * @returns True if `array` is an array where every item is a string.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * Checks if a value is a string.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a string, otherwise `false`.\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n\n/**\n * Checks if a value is a valid number (not NaN).\n *\n * This function returns `true` if the value is of type `number`\n * and is not `NaN`. It works as a type guard for TypeScript.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a number and not `NaN`, otherwise `false`.\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Checks if a value is a boolean.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a boolean (`true` or `false`), otherwise `false`.\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === \"boolean\";\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CorsInit } from \"../interfaces/cors\";\n\nimport { isBoolean, isNumber, isStringArray } from \"./basic\";\n\n/**\n * Throws if the given value is not a valid CorsInit.\n *\n * Checks only the fields that are present, since CorsInit is Partial<CorsConfig>.\n *\n * @param value - The value to check.\n * @throws TypeError If `value` is not a valid {@link CorsInit}.\n */\nexport function assertCorsInit(value: unknown): asserts value is CorsInit {\n if (value === undefined) return;\n\n if (typeof value !== \"object\" || value === null) {\n throw new TypeError(\"CorsInit must be an object.\");\n }\n\n const obj = value as Record<string, unknown>;\n\n if (obj[\"allowedOrigins\"] !== undefined && !isStringArray(obj[\"allowedOrigins\"])) {\n throw new TypeError(\"CorsInit.allowedOrigins must be a string array.\");\n }\n\n if (obj[\"allowedHeaders\"] !== undefined && !isStringArray(obj[\"allowedHeaders\"])) {\n throw new TypeError(\"CorsInit.allowedHeaders must be a string array.\");\n }\n\n if (obj[\"exposedHeaders\"] !== undefined && !isStringArray(obj[\"exposedHeaders\"])) {\n throw new TypeError(\"CorsInit.exposedHeaders must be a string array.\");\n }\n\n if (obj[\"allowCredentials\"] !== undefined && !isBoolean(obj[\"allowCredentials\"])) {\n throw new TypeError(\"CorsInit.allowCredentials must be a boolean.\");\n }\n\n if (obj[\"maxAge\"] !== undefined && !isNumber(obj[\"maxAge\"])) {\n throw new TypeError(\"CorsInit.maxAge must be a number.\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Standard HTTP request methods.\n */\nexport enum Method {\n GET = \"GET\",\n PUT = \"PUT\",\n HEAD = \"HEAD\",\n POST = \"POST\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n OPTIONS = \"OPTIONS\",\n}\n\n/**\n * Shorthand constants for each HTTP method.\n *\n * These are equivalent to the corresponding enum members in `Method`.\n * For example, `GET === Method.GET`.\n */\nexport const { GET, PUT, HEAD, POST, PATCH, DELETE, OPTIONS } = Method;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport CacheLib from \"cache-control-parser\";\n\n/**\n * @see {@link https://github.com/etienne-martin/cache-control-parser | cache-control-parser}\n */\nexport type CacheControl = CacheLib.CacheControl;\nexport const CacheControl = {\n parse: CacheLib.parse,\n stringify: CacheLib.stringify,\n\n /** A CacheControl directive that disables all caching. */\n DISABLE: Object.freeze({\n \"no-cache\": true,\n \"no-store\": true,\n \"must-revalidate\": true,\n \"max-age\": 0,\n }) satisfies CacheControl,\n};\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Time constants in seconds. Month is approximated as 30 days.\n */\nexport const Time = {\n Second: 1,\n Minute: 60,\n Hour: 3600, // 60 * 60\n Day: 86400, // 60 * 60 * 24\n Week: 604800, // 60 * 60 * 24 * 7\n Month: 2592000, // 60 * 60 * 24 * 30\n Year: 31536000, // 60 * 60 * 24 * 365\n} as const;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internally used headers.\n */\nexport const HttpHeader = {\n ACCEPT: \"accept\",\n ACCEPT_ENCODING: \"accept-encoding\",\n ACCEPT_LANGUAGE: \"accept-language\",\n ACCEPT_RANGES: \"accept-ranges\",\n ALLOW: \"allow\",\n AUTHORIZATION: \"authorization\",\n CACHE_CONTROL: \"cache-control\",\n CONNECTION: \"connection\",\n CONTENT_DISPOSITION: \"content-disposition\",\n CONTENT_ENCODING: \"content-encoding\",\n CONTENT_LANGUAGE: \"content-language\",\n CONTENT_LENGTH: \"content-length\",\n CONTENT_RANGE: \"content-range\",\n CONTENT_TYPE: \"content-type\",\n CONTENT_MD5: \"content-md5\",\n COOKIE: \"cookie\",\n ETAG: \"etag\",\n IF_MATCH: \"if-match\",\n IF_MODIFIED_SINCE: \"if-modified-since\",\n IF_NONE_MATCH: \"if-none-match\",\n IF_UNMODIFIED_SINCE: \"if-unmodified-since\",\n LAST_MODIFIED: \"last-modified\",\n ORIGIN: \"origin\",\n RANGE: \"range\",\n SET_COOKIE: \"set-cookie\",\n VARY: \"vary\",\n\n // Cors Headers\n ACCESS_CONTROL_ALLOW_CREDENTIALS: \"access-control-allow-credentials\",\n ACCESS_CONTROL_ALLOW_HEADERS: \"access-control-allow-headers\",\n ACCESS_CONTROL_ALLOW_METHODS: \"access-control-allow-methods\",\n ACCESS_CONTROL_ALLOW_ORIGIN: \"access-control-allow-origin\",\n ACCESS_CONTROL_EXPOSE_HEADERS: \"access-control-expose-headers\",\n ACCESS_CONTROL_MAX_AGE: \"access-control-max-age\",\n\n // Websocket Headers\n SEC_WEBSOCKET_VERSION: \"sec-websocket-version\",\n UPGRADE: \"upgrade\",\n\n // Internal Headers\n INTERNAL_VARIANT_SET: \"internal-variant-set\",\n} as const;\n\n/**\n * Headers that must not be sent in 304 Not Modified responses.\n * These are stripped to comply with the HTTP spec.\n */\nexport const FORBIDDEN_304_HEADERS = [\n HttpHeader.CONTENT_TYPE,\n HttpHeader.CONTENT_LENGTH,\n HttpHeader.CONTENT_RANGE,\n HttpHeader.CONTENT_ENCODING,\n HttpHeader.CONTENT_LANGUAGE,\n HttpHeader.CONTENT_DISPOSITION,\n HttpHeader.CONTENT_MD5,\n];\n\n/**\n * Headers that should not be sent in 204 No Content responses.\n * Stripping them is recommended but optional per spec.\n */\nexport const FORBIDDEN_204_HEADERS = [HttpHeader.CONTENT_LENGTH, HttpHeader.CONTENT_RANGE];\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { StatusCodes } from \"../../constants\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { Time } from \"../../constants/time\";\nimport { CorsConfig } from \"../../interfaces/cors\";\n\n/**\n * A wildcard value used in the CORS `Access-Control-Allow-Origin` header\n * to permit requests from **any** origin.\n */\nexport const ALLOW_ALL_ORIGINS = \"*\";\n\n/**\n * Status codes for which `CORS` should be skipped.\n *\n * Skips `CORS` for:\n * - 101 Switching Protocols (WebSocket upgrade)\n * - 100 Continue\n * - 3xx Redirects (`CORS` is applied to the final URL only)\n */\nexport const SKIP_CORS_STATUSES = [\n StatusCodes.SWITCHING_PROTOCOLS,\n StatusCodes.CONTINUE,\n StatusCodes.PROCESSING,\n StatusCodes.EARLY_HINTS,\n StatusCodes.MOVED_PERMANENTLY,\n StatusCodes.MOVED_TEMPORARILY,\n StatusCodes.SEE_OTHER,\n StatusCodes.TEMPORARY_REDIRECT,\n StatusCodes.PERMANENT_REDIRECT,\n];\n\n/**\n * Default configuration for `CORS` middleware.\n */\nexport const defaultCorsConfig: CorsConfig = {\n allowedOrigins: [ALLOW_ALL_ORIGINS],\n allowedHeaders: [HttpHeader.CONTENT_TYPE],\n exposedHeaders: [],\n allowCredentials: false,\n maxAge: 5 * Time.Minute,\n} as const;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Method } from \"../constants/methods\";\n\nimport { isString } from \"./basic\";\n\n/**\n * A set containing all supported HTTP methods.\n *\n * Useful for runtime checks like validating request methods.\n */\nconst METHOD_SET: Set<string> = new Set(Object.values(Method));\n\n/**\n * Type guard that checks if a string is a valid HTTP method.\n *\n * @param value - The string to test.\n * @returns True if `value` is a recognized HTTP method.\n */\nexport function isMethod(value: unknown): value is Method {\n return isString(value) && METHOD_SET.has(value);\n}\n\n/**\n * Checks if a value is an array of valid HTTP methods.\n *\n * Each element is verified using the `isMethod` type guard.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an array and every element is a valid `Method`, otherwise `false`.\n */\nexport function isMethodArray(value: unknown): value is Method[] {\n return Array.isArray(value) && value.every(isMethod);\n}\n\n/**\n * Asserts that a value is an array of valid HTTP methods.\n *\n * This function uses {@link isMethodArray} to validate the input. If the\n * value is not an array of `Method` elements, it throws a `TypeError`.\n * Otherwise, TypeScript will narrow the type of `value` to `Method[]`\n * within the calling scope.\n *\n * @param value - The value to check.\n * @throws TypeError If `value` is not a valid {@link Method} array.\n */\nexport function assertMethods(value: unknown): asserts value is Method[] {\n if (!isMethodArray(value)) {\n const desc = Array.isArray(value) ? JSON.stringify(value) : String(value);\n throw new TypeError(`Invalid method array: ${desc}`);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const UTF8_CHARSET = \"utf-8\";\n\n/**\n * Internal media types.\n */\nexport enum MediaType {\n PLAIN_TEXT = \"text/plain\",\n HTML = \"text/html\",\n JSON = \"application/json\",\n OCTET_STREAM = \"application/octet-stream\",\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Lexicographically compares two strings.\n *\n * This comparator can be used in `Array.prototype.sort()` to produce a\n * consistent, stable ordering of string arrays.\n *\n * @param a - The first string to compare.\n * @param b - The second string to compare.\n * @returns A number indicating the relative order of `a` and `b`.\n */\nexport function lexCompare(a: string, b: string): number {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lexCompare } from \"./compare\";\n\n/**\n * Sets a header on the given Headers object.\n *\n * - If `value` is an array, any duplicates and empty strings are removed.\n * - If the resulting value is empty, the header is deleted.\n * - Otherwise, values are joined with `\", \"` and set as the header value.\n *\n * @param headers - The Headers object to modify.\n * @param key - The header name to set.\n * @param value - The header value(s) to set. Can be a string or array of strings.\n */\nexport function setHeader(headers: Headers, key: string, value: string | string[]): void {\n const raw = Array.isArray(value) ? value : [value];\n const values = Array.from(new Set(raw.map((v) => v.trim())))\n .filter((v) => v.length)\n .sort(lexCompare);\n\n if (!values.length) {\n headers.delete(key);\n return;\n }\n\n headers.set(key, values.join(\", \"));\n}\n\n/**\n * Merges new value(s) into an existing header on the given Headers object.\n *\n * - Preserves any existing values and adds new ones.\n * - Removes duplicates and trims all values.\n * - If the header does not exist, it is created.\n * - If the resulting value array is empty, the header is deleted.\n *\n * @param headers - The Headers object to modify.\n * @param key - The header name to merge into.\n * @param value - The new header value(s) to add. Can be a string or array of strings.\n */\nexport function mergeHeader(headers: Headers, key: string, value: string | string[]): void {\n const values = Array.isArray(value) ? value : [value];\n if (values.length === 0) return;\n\n const existing = getHeaderValues(headers, key);\n const merged = existing.concat(values.map((v) => v.trim()));\n\n setHeader(headers, key, merged);\n}\n\n/**\n * Returns the values of an HTTP header as an array of strings.\n *\n * This helper:\n * - Retrieves the header value by `key`.\n * - Splits the value on commas.\n * - Trims surrounding whitespace from each entry.\n * - Filters out any empty tokens.\n * - Removes duplicate values (case-sensitive)\n *\n * If the header is not present, an empty array is returned.\n *\n */\nexport function getHeaderValues(headers: Headers, key: string): string[] {\n const values =\n headers\n .get(key)\n ?.split(\",\")\n .map((v) => v.trim())\n .filter((v) => v.length > 0) ?? [];\n return Array.from(new Set(values)).sort(lexCompare);\n}\n\n/**\n * Removes a list of header fields from a {@link Headers} object.\n *\n * @param headers - The {@link Headers} object to modify in place.\n * @param keys - An array of header field names to remove. Header names are\n * matched case-insensitively per the Fetch spec.\n */\nexport function filterHeaders(headers: Headers, keys: string[]): void {\n for (const key of keys) {\n headers.delete(key);\n }\n}\n\n/**\n * Extracts all header names from a `Headers` object, normalizes them,\n * and returns them in a stable, lexicographically sorted array.\n *\n * @param headers - The `Headers` object to extract keys from.\n * @returns A sorted array of lowercase header names.\n */\nexport function getHeaderKeys(headers: Headers): string[] {\n return [...headers.keys()].sort(lexCompare);\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Appends a charset parameter to a given media type string,\n * avoiding duplicates and ignoring empty charsets.\n *\n * @param {string} mediaType - The MIME type (e.g., \"text/html\").\n * @param {string} charset - The character set to append (e.g., \"utf-8\").\n * @returns {string} The media type with charset appended if provided.\n */\nexport function withCharset(mediaType: string, charset: string): string {\n if (!charset || mediaType.toLowerCase().includes(\"charset=\")) {\n return mediaType;\n }\n return `${mediaType}; charset=${charset.toLowerCase()}`;\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { StatusCodes } from \"http-status-codes/build/es/status-codes\";\nimport { getReasonPhrase } from \"http-status-codes/build/es/utils-functions\";\n\nimport { CacheControl } from \"./constants/cache\";\nimport { FORBIDDEN_204_HEADERS, FORBIDDEN_304_HEADERS, HttpHeader } from \"./constants/headers\";\nimport { MediaType, UTF8_CHARSET } from \"./constants/media\";\nimport { GET, HEAD } from \"./constants/methods\";\nimport { assertMethods } from \"./guards/methods\";\nimport { assertOctetStreamInit } from \"./guards/responses\";\nimport { Worker } from \"./interfaces\";\nimport { OctetStreamInit } from \"./interfaces/response\";\nimport { filterHeaders, mergeHeader, setHeader } from \"./utils/headers\";\nimport { withCharset } from \"./utils/media\";\n\n/**\n * Base class for building HTTP responses.\n * Manages headers, status, and media type.\n */\nabstract class BaseResponse {\n /** HTTP headers for the response. */\n public headers: Headers = new Headers();\n\n /** HTTP status code (default 200 OK). */\n public status: StatusCodes = StatusCodes.OK;\n\n /** Optional status text. Defaults to standard reason phrase. */\n public statusText?: string;\n\n /** Optional websocket property. */\n public webSocket?: WebSocket | null;\n\n /** Default media type of the response body. */\n public mediaType: string = withCharset(MediaType.PLAIN_TEXT, UTF8_CHARSET);\n\n /** Converts current state to ResponseInit for constructing a Response. */\n protected get responseInit(): ResponseInit {\n return {\n headers: this.headers,\n status: this.status,\n statusText: this.statusText ?? getReasonPhrase(this.status),\n webSocket: this.webSocket,\n encodeBody: \"automatic\",\n };\n }\n\n /** Sets a header, overwriting any existing value. */\n public setHeader(key: string, value: string | string[]): void {\n setHeader(this.headers, key, value);\n }\n\n /** Merges a header with existing values (does not overwrite). */\n public mergeHeader(key: string, value: string | string[]): void {\n mergeHeader(this.headers, key, value);\n }\n\n /** Adds a Content-Type header if not already existing (does not overwrite). */\n public addContentType() {\n if (!this.headers.get(HttpHeader.CONTENT_TYPE)) {\n this.setHeader(HttpHeader.CONTENT_TYPE, this.mediaType);\n }\n }\n\n /**\n * Removes headers that are disallowed or discouraged based on the current\n * status code.\n *\n * - **204 No Content:** strips headers that \"should not\" be sent\n * (`Content-Length`, `Content-Range`), per the HTTP spec.\n * - **304 Not Modified:** strips headers that \"must not\" be sent\n * (`Content-Type`, `Content-Length`, `Content-Range`, etc.), per the HTTP spec.\n *\n * This ensures that responses remain compliant with HTTP/1.1 standards while preserving\n * custom headers that are allowed.\n */\n public filterHeaders(): void {\n if (this.status === StatusCodes.NO_CONTENT) {\n filterHeaders(this.headers, FORBIDDEN_204_HEADERS);\n } else if (this.status === StatusCodes.NOT_MODIFIED) {\n filterHeaders(this.headers, FORBIDDEN_304_HEADERS);\n }\n }\n}\n\n/**\n * Base response class that adds caching headers.\n */\nabstract class CacheResponse extends BaseResponse {\n constructor(public cache?: CacheControl) {\n super();\n }\n\n /** Adds Cache-Control header if caching is configured. */\n protected addCacheHeader(): void {\n if (this.cache) {\n this.setHeader(HttpHeader.CACHE_CONTROL, CacheControl.stringify(this.cache));\n }\n }\n}\n\n/**\n * Core response. Combines caching, and content type headers.\n */\nexport abstract class WorkerResponse extends CacheResponse {\n constructor(\n private readonly body: BodyInit | null = null,\n cache?: CacheControl,\n ) {\n super(cache);\n }\n\n /** Builds the Response with body, headers, and status. */\n public async response(): Promise<Response> {\n this.addCacheHeader();\n\n const body = [StatusCodes.NO_CONTENT, StatusCodes.NOT_MODIFIED].includes(this.status)\n ? null\n : this.body;\n\n if (body) this.addContentType();\n\n this.filterHeaders();\n\n return new Response(body, this.responseInit);\n }\n}\n\n/**\n * Copies an existing response for mutation. Pass in a CacheControl\n * to be used for the response, overriding any existing `cache-control`\n * on the source response.\n */\nexport class CopyResponse extends WorkerResponse {\n constructor(response: Response, cache?: CacheControl) {\n super(response.body, cache);\n this.status = response.status;\n this.statusText = response.statusText;\n this.headers = new Headers(response.headers);\n }\n}\n\n/**\n * Copies the response, but with null body and status 304 Not Modified.\n */\nexport class NotModified extends WorkerResponse {\n constructor(response: Response) {\n super();\n this.status = StatusCodes.NOT_MODIFIED;\n this.headers = new Headers(response.headers);\n }\n}\n\n/**\n * Represents a successful response with customizable body, cache and status.\n */\nexport class SuccessResponse extends WorkerResponse {\n constructor(\n body: BodyInit | null = null,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n ) {\n super(body, cache);\n this.status = status;\n }\n}\n\n/**\n * JSON response. Automatically sets Content-Type to application/json.\n */\nexport class JsonResponse extends SuccessResponse {\n constructor(json: unknown = {}, cache?: CacheControl, status: StatusCodes = StatusCodes.OK) {\n super(JSON.stringify(json), cache, status);\n this.mediaType = withCharset(MediaType.JSON, UTF8_CHARSET);\n }\n}\n\n/**\n * HTML response. Automatically sets Content-Type to text/html.\n */\nexport class HtmlResponse extends SuccessResponse {\n constructor(\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n charset: string = UTF8_CHARSET,\n ) {\n super(body, cache, status);\n this.mediaType = withCharset(MediaType.HTML, charset);\n }\n}\n\n/**\n * Plain text response. Automatically sets Content-Type to text/plain.\n */\nexport class TextResponse extends SuccessResponse {\n constructor(\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n charset: string = UTF8_CHARSET,\n ) {\n super(body, cache, status);\n this.mediaType = withCharset(MediaType.PLAIN_TEXT, charset);\n }\n}\n\n/**\n * Represents an HTTP response for serving binary data as `application/octet-stream`.\n *\n * This class wraps a `ReadableStream` and sets all necessary headers for both\n * full and partial content responses, handling range requests in a hybrid way\n * to maximize browser and CDN caching.\n *\n * Key behaviors:\n * - `Content-Type` is set to `application/octet-stream`.\n * - `Accept-Ranges: bytes` is always included.\n * - `Content-Length` is always set to the validated length of the response body.\n * - If the request is a true partial range (offset > 0 or length < size), the response\n * will be `206 Partial Content` with the appropriate `Content-Range` header.\n * - If the requested range covers the entire file (even if a Range header is present),\n * the response will return `200 OK` to enable browser and edge caching.\n * - Zero-length streams (`size = 0`) are never treated as partial.\n * - Special case: a requested range of `0-0` on a non-empty file is normalized to 1 byte.\n */\nexport class OctetStream extends WorkerResponse {\n constructor(stream: ReadableStream, init: OctetStreamInit, cache?: CacheControl) {\n assertOctetStreamInit(init);\n\n super(stream, cache);\n this.mediaType = MediaType.OCTET_STREAM;\n\n const normalized = OctetStream.normalizeInit(init);\n const { size, offset, length } = normalized;\n\n if (OctetStream.isPartial(normalized)) {\n this.setHeader(\n HttpHeader.CONTENT_RANGE,\n `bytes ${offset}-${offset + length - 1}/${size}`,\n );\n this.status = StatusCodes.PARTIAL_CONTENT;\n }\n\n this.setHeader(HttpHeader.ACCEPT_RANGES, \"bytes\");\n this.setHeader(HttpHeader.CONTENT_LENGTH, `${length}`);\n }\n\n /**\n * Normalizes a partially-specified `OctetStreamInit` into a fully-specified object.\n *\n * Ensures that all required fields (`size`, `offset`, `length`) are defined:\n * - `offset` defaults to 0 if not provided.\n * - `length` defaults to `size - offset` if not provided.\n * - Special case: if `offset` and `length` are both 0 but `size > 0`, `length` is set to 1\n * to avoid zero-length partial streams.\n *\n * @param init - The initial `OctetStreamInit` object, possibly with missing `offset` or `length`.\n * @returns A fully-specified `OctetStreamInit` object with `size`, `offset`, and `length` guaranteed.\n */\n private static normalizeInit(init: OctetStreamInit): Required<OctetStreamInit> {\n const { size } = init;\n const offset = init.offset ?? 0;\n let length = init.length ?? size - offset;\n\n if (offset === 0 && length === 0 && size > 0) {\n length = 1;\n }\n\n return { size, offset, length };\n }\n\n /**\n * Determines whether the given `OctetStreamInit` represents a partial range.\n *\n * Partial ranges are defined as any range that does **not** cover the entire file:\n * - If `size === 0`, the stream is never partial.\n * - If `offset === 0` and `length === size`, the stream is treated as a full file (not partial),\n * even if a Range header is present. This enables browser and CDN caching.\n * - All other cases are considered partial, and will result in a `206 Partial Content` response.\n *\n * @param init - A fully-normalized `OctetStreamInit` object.\n * @returns `true` if the stream represents a partial range; `false` if it represents the full file.\n */\n private static isPartial(init: Required<OctetStreamInit>): boolean {\n if (init.size === 0) return false;\n return !(init.offset === 0 && init.length === init.size);\n }\n}\n\n/**\n * A streaming response for Cloudflare R2 objects.\n *\n * **Partial content support:** To enable HTTP 206 streaming, you must provide\n * request headers containing the `Range` header when calling the R2 bucket's `get()` method.\n *\n * Example:\n * ```ts\n * const stream = await this.env.R2_BUCKET.get(\"key\", { range: this.request.headers });\n * ```\n *\n * @param source - The R2 object to stream.\n * @param cache - Optional caching override.\n */\nexport class R2ObjectStream extends OctetStream {\n constructor(source: R2ObjectBody, cache?: CacheControl) {\n let useCache = cache;\n if (!useCache && source.httpMetadata?.cacheControl) {\n useCache = CacheControl.parse(source.httpMetadata.cacheControl);\n }\n\n super(source.body, R2ObjectStream.computeRange(source.size, source.range), useCache);\n\n this.setHeader(HttpHeader.ETAG, source.httpEtag);\n\n if (source.httpMetadata?.contentType) {\n this.mediaType = source.httpMetadata.contentType;\n }\n }\n\n /**\n * Computes an `OctetStreamInit` object from a given R2 range.\n *\n * This function normalizes a Cloudflare R2 `R2Range` into the shape expected\n * by `OctetStream`. It handles the following cases:\n *\n * - No range provided: returns `{ size }` (full content).\n * - `suffix` range: calculates the offset and length from the end of the file.\n * - Explicit `offset` and/or `length`: passed through as-is.\n *\n * @param size - The total size of the file/object.\n * @param range - Optional range to extract (from R2). Can be:\n * - `{ offset: number; length?: number }`\n * - `{ offset?: number; length: number }`\n * - `{ suffix: number }`\n * @returns An `OctetStreamInit` object suitable for `OctetStream`.\n */\n private static computeRange(size: number, range?: R2Range): OctetStreamInit {\n if (!range) return { size };\n\n if (\"suffix\" in range) {\n const offset = Math.max(0, size - range.suffix);\n const length = size - offset;\n return { size, offset, length };\n }\n\n return { size, ...range };\n }\n}\n\n/**\n * Response for WebSocket upgrade requests.\n * Automatically sets status to 101 and attaches the client socket.\n */\nexport class WebSocketUpgrade extends WorkerResponse {\n constructor(client: WebSocket) {\n super();\n this.status = StatusCodes.SWITCHING_PROTOCOLS;\n this.webSocket = client;\n }\n}\n\n/**\n * Response for `HEAD` requests. Copy headers and status from a `GET` response\n * without the body.\n */\nexport class Head extends WorkerResponse {\n constructor(get: Response) {\n super();\n this.status = get.status;\n this.statusText = get.statusText;\n this.headers = new Headers(get.headers);\n }\n}\n\n/**\n * Response for `OPTIONS` requests.\n */\nexport class Options extends WorkerResponse {\n constructor(worker: Worker) {\n const allowed = Array.from(new Set([GET, HEAD, ...worker.getAllowedMethods()]));\n assertMethods(allowed);\n\n super();\n this.status = StatusCodes.NO_CONTENT;\n this.setHeader(HttpHeader.ALLOW, allowed);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { HttpHeader } from \"../../constants/headers\";\nimport { assertMethods } from \"../../guards/methods\";\nimport { CorsConfig } from \"../../interfaces/cors\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { CopyResponse } from \"../../responses\";\nimport { mergeHeader, setHeader } from \"../../utils/headers\";\n\nimport { ALLOW_ALL_ORIGINS, SKIP_CORS_STATUSES } from \"./constants\";\n\n/**\n * Handles a `CORS` preflight `OPTIONS` request.\n *\n * This function **modifies the provided response** to include the appropriate\n * CORS headers based on the configuration and the request's origin.\n *\n * Steps:\n * 1. Clears any existing CORS headers from the response.\n * 2. Sets `Vary: Origin` if needed for caching purposes.\n * 3. If an `Origin` header is present in the request:\n * - Sets `Access-Control-Allow-Origin`\n * - Sets `Access-Control-Allow-Credentials`\n * - Sets `Access-Control-Allow-Methods`\n * - Sets `Access-Control-Allow-Headers`\n * - Sets `Access-Control-Max-Age`\n *\n * @param response - The original Response object to modify for the preflight.\n * @param worker - The Worker handling the request.\n * @param cors - The CORS configuration to apply.\n * @returns A Response object suitable for responding to the preflight request.\n */\nexport function options(\n response: Response,\n worker: Worker,\n cors: CorsConfig,\n): Promise<Response> {\n const copy = new CopyResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(copy.headers);\n setVaryOrigin(copy.headers, cors);\n\n if (origin) {\n setAllowOrigin(copy.headers, cors, origin);\n setAllowCredentials(copy.headers, cors, origin);\n setAllowMethods(copy.headers, worker);\n setAllowHeaders(copy.headers, cors);\n setMaxAge(copy.headers, cors);\n }\n\n return copy.response();\n}\n\n/**\n * Applies CORS headers to an existing response for non-preflight requests.\n *\n * This function **modifies the provided response** to include the appropriate\n * CORS headers based on the configuration and the request's origin.\n *\n * Steps:\n * 1. Clears any existing CORS headers from the response.\n * 2. Sets `Vary: Origin` if needed for caching purposes.\n * 3. If an `Origin` header is present in the request:\n * - Sets `Access-Control-Allow-Origin`\n * - Sets `Access-Control-Allow-Credentials`\n * - Sets `Access-Control-Expose-Headers`\n *\n * @param response - The original Response object to modify.\n * @param worker - The Worker handling the request.\n * @param cors - The CORS configuration to apply.\n * @returns A Response object with CORS headers applied, suitable for returning\n * to the client.\n */\nexport function apply(\n response: Response,\n worker: Worker,\n cors: CorsConfig,\n): Promise<Response> {\n const copy = new CopyResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(copy.headers);\n setVaryOrigin(copy.headers, cors);\n\n if (origin) {\n setAllowOrigin(copy.headers, cors, origin);\n setAllowCredentials(copy.headers, cors, origin);\n setExposedHeaders(copy.headers, cors);\n }\n\n return copy.response();\n}\n\n/**\n * Adds `Vary: Origin` when `CORS` is restricted to specific origins.\n * This ensures caches differentiate responses by request origin.\n *\n * Skipped when all origins are allowed.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setVaryOrigin(headers: Headers, cors: CorsConfig): void {\n if (!allowAllOrigins(cors)) {\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.ORIGIN);\n }\n}\n\n/**\n * Sets the Access-Control-Allow-Origin header based on the `CORS` config\n * and request origin.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n * @param origin - The request's origin, or null if not present.\n */\nexport function setAllowOrigin(headers: Headers, cors: CorsConfig, origin: string): void {\n if (allowAllOrigins(cors)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW_ALL_ORIGINS);\n return;\n }\n\n if (cors.allowedOrigins.includes(origin)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, origin);\n }\n}\n\n/**\n * Conditionally sets the `Access-Control-Allow-Credentials` header\n * for a `CORS` response.\n *\n * This header is only set if:\n * 1. `cors.allowCredentials` is true,\n * 2. The configuration does **not** allow any origin (`*`), and\n * 3. The provided `origin` is explicitly listed in `cors.allowedOrigins`.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration.\n * @param origin - The origin of the incoming request.\n */\nexport function setAllowCredentials(headers: Headers, cors: CorsConfig, origin: string): void {\n if (!cors.allowCredentials) return;\n if (allowAllOrigins(cors)) return;\n if (!cors.allowedOrigins.includes(origin)) return;\n\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_CREDENTIALS, \"true\");\n}\n\n/**\n * Sets the `Access-Control-Allow-Methods` header for a `CORS` response.\n *\n * @param headers - The Headers object to modify.\n * @param worker - The Worker instance used to retrieve allowed methods.\n */\nexport function setAllowMethods(headers: Headers, worker: Worker): void {\n const allowed = worker.getAllowedMethods();\n assertMethods(allowed);\n\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_METHODS, allowed);\n}\n\n/**\n * Sets the `Access-Control-Max-Age` header for a `CORS` response.\n *\n * This header indicates how long the results of a preflight request\n * can be cached by the client (in seconds).\n *\n * The value is **clamped to a non-negative integer** to comply with\n * the `CORS` specification:\n * - Decimal values are floored to the nearest integer.\n * - Negative values are treated as `0`.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration containing the `maxAge` value in seconds.\n */\nexport function setMaxAge(headers: Headers, cors: CorsConfig): void {\n const maxAge = Math.max(0, Math.floor(cors.maxAge));\n setHeader(headers, HttpHeader.ACCESS_CONTROL_MAX_AGE, String(maxAge));\n}\n\n/**\n * Sets the Access-Control-Allow-Headers header based on the `CORS` configuration.\n *\n * Only the headers explicitly listed in `cors.allowedHeaders` are sent.\n * If the array is empty, no Access-Control-Allow-Headers header is added.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setAllowHeaders(headers: Headers, cors: CorsConfig): void {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS, cors.allowedHeaders);\n}\n\n/**\n * Sets the Access-Control-Expose-Headers header for a response.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setExposedHeaders(headers: Headers, cors: CorsConfig): void {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_EXPOSE_HEADERS, cors.exposedHeaders);\n}\n\n/**\n * Returns true if the `CORS` config allows all origins ('*').\n *\n * @param cors - The `CORS` configuration.\n */\nexport function allowAllOrigins(cors: CorsConfig): boolean {\n return cors.allowedOrigins.includes(ALLOW_ALL_ORIGINS);\n}\n\n/**\n * Deletes any existing `CORS` headers from the provided headers object.\n *\n * @param headers - The headers object to modify.\n */\nexport function deleteCorsHeaders(headers: Headers): void {\n headers.delete(HttpHeader.ACCESS_CONTROL_MAX_AGE);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_METHODS);\n headers.delete(HttpHeader.ACCESS_CONTROL_EXPOSE_HEADERS);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_CREDENTIALS);\n}\n\n/**\n * Determines whether CORS headers should be skipped for a response.\n *\n * Certain responses do not require CORS headers, such as:\n * - Informational or protocol-level responses (1xx)\n * - Redirect responses (3xx)\n * - Responses initiating a protocol upgrade\n *\n * @param response - The Response object to inspect.\n * @returns `true` if CORS headers should be omitted, `false` otherwise.\n */\nexport function skipCors(response: Response): boolean {\n const { status, headers } = response;\n if (SKIP_CORS_STATUSES.includes(status)) return true;\n if (headers.has(HttpHeader.UPGRADE)) return true;\n\n return false;\n}\n\n/**\n * Extracts and normalizes the `Origin` header from a request.\n *\n * Returns the origin (scheme + host + port) as a string if present and valid.\n * Returns `null` if:\n * - The `Origin` header is missing\n * - The `Origin` header is `\"null\"` (opaque origin)\n * - The `Origin` header is malformed\n *\n * @param request - The incoming {@link Request} object.\n * @returns The normalized origin string, or `null` if not present or invalid.\n */\nexport function getOrigin(request: Request): string | null {\n const origin = request.headers.get(HttpHeader.ORIGIN)?.trim();\n if (!origin || origin === \"null\") return null;\n\n try {\n return new URL(origin).origin;\n } catch {\n return null;\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { OPTIONS } from \"../../constants/methods\";\nimport { CorsConfig, CorsInit } from \"../../interfaces/cors\";\nimport { Middleware } from \"../../interfaces/middleware\";\nimport { Worker } from \"../../interfaces/worker\";\n\nimport { defaultCorsConfig } from \"./constants\";\nimport { apply, options, skipCors } from \"./utils\";\n\n/**\n * CORS Middleware Implementation\n *\n * Handles Cross-Origin Resource Sharing (CORS) for incoming requests.\n *\n * Behavior:\n * - Invokes the downstream middleware for all requests first by calling `next()`.\n * - If the request is an `OPTIONS` request (preflight), transforms the downstream\n * response into a preflight CORS response.\n * - For other HTTP methods, applies CORS headers to the downstream response,\n * unless the response explicitly opts out via `skipCors`.\n *\n * This ensures that all responses comply with the configured CORS rules\n * while still allowing downstream middleware to run for every request.\n *\n * @see {@link cors} for full configuration options and defaults.\n */\nexport class CorsHandler implements Middleware {\n /** The resolved CORS configuration for this middleware instance. */\n private readonly config: CorsConfig;\n\n /**\n * Constructs a new `CorsHandler` instance.\n *\n * Merges the provided partial configuration with the default settings.\n *\n * @param init - Optional partial configuration to override defaults.\n * Any fields not provided will use `defaultCorsConfig`.\n */\n constructor(init?: CorsInit) {\n this.config = { ...defaultCorsConfig, ...init };\n }\n\n /**\n * Applies CORS handling to an incoming request.\n *\n * @param worker - The Worker instance containing the request and context.\n * @param next - Function invoking the next middleware in the chain.\n * @returns A Response object with CORS headers applied.\n * - For `OPTIONS` requests, returns a preflight response based on\n * the downstream response.\n * - For other methods, returns the downstream response with\n * CORS headers applied, unless `skipCors` prevents it.\n */\n public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n const response = await next();\n\n if (worker.request.method === OPTIONS) {\n return options(response, worker, this.config);\n }\n\n if (skipCors(response)) return response;\n\n return apply(response, worker, this.config);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assertCorsInit } from \"../../guards/cors\";\nimport { CorsInit } from \"../../interfaces/cors\";\nimport { Middleware } from \"../../interfaces/middleware\";\n\nimport { CorsHandler } from \"./handler\";\n\n/**\n * Returns a `CORS` middleware instance.\n *\n * This middleware automatically handles Cross-Origin Resource Sharing (CORS)\n * for incoming requests, including preflight `OPTIONS` requests, and adds\n * appropriate headers to responses.\n *\n * @param init - Optional configuration for `CORS` behavior. See {@link CorsConfig}.\n * @returns A {@link Middleware} instance that can be used in your middleware chain.\n */\nexport function cors(init?: CorsInit): Middleware {\n assertCorsInit(init);\n return new CorsHandler(init);\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/guards/basic.ts","../src/guards/cors.ts","../src/constants/methods.ts","../src/constants/cache.ts","../src/constants/time.ts","../src/constants/headers.ts","../src/middleware/cors/constants.ts","../src/guards/methods.ts","../src/constants/media.ts","../src/utils/compare.ts","../src/utils/headers.ts","../src/utils/media.ts","../src/responses.ts","../src/middleware/cors/utils.ts","../src/middleware/cors/handler.ts","../src/middleware/cors/cors.ts"],"names":["isStringArray","value","item","isString","isNumber","isBoolean","assertCorsInit","obj","Method","GET","PUT","HEAD","POST","PATCH","DELETE","OPTIONS","CacheControl","CacheLib","Time","HttpHeader","FORBIDDEN_304_HEADERS","FORBIDDEN_204_HEADERS","ALLOW_ALL_ORIGINS","SKIP_CORS_STATUSES","StatusCodes","defaultCorsConfig","METHOD_SET","isMethod","isMethodArray","assertMethods","desc","UTF8_CHARSET","lexCompare","a","b","setHeader","headers","key","raw","values","v","mergeHeader","merged","getHeaderValues","filterHeaders","keys","withCharset","mediaType","charset","BaseResponse","getReasonPhrase","CacheResponse","cache","WorkerResponse","body","CopyResponse","response","options","worker","cors","copy","origin","getOrigin","deleteCorsHeaders","setVaryOrigin","setAllowOrigin","setAllowCredentials","setAllowMethods","setAllowHeaders","setMaxAge","apply","setExposedHeaders","allowAllOrigins","allowed","maxAge","skipCors","status","request","CorsHandler","init","next"],"mappings":"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","file":"cors.js","sourcesContent":["/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Checks if the provided value is an array of strings.\n *\n * @param value - The value to check.\n * @returns True if `array` is an array where every item is a string.\n */\nexport function isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\n/**\n * Checks if a value is a string.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a string, otherwise `false`.\n */\nexport function isString(value: unknown): value is string {\n return typeof value === \"string\";\n}\n\n\n/**\n * Checks if a value is a valid number (not NaN).\n *\n * This function returns `true` if the value is of type `number`\n * and is not `NaN`. It works as a type guard for TypeScript.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a number and not `NaN`, otherwise `false`.\n */\nexport function isNumber(value: unknown): value is number {\n return typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Checks if a value is a boolean.\n *\n * @param value - The value to check.\n * @returns `true` if the value is a boolean (`true` or `false`), otherwise `false`.\n */\nexport function isBoolean(value: unknown): value is boolean {\n return typeof value === \"boolean\";\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CorsInit } from \"../middleware/cors/interfaces\";\n\nimport { isBoolean, isNumber, isStringArray } from \"./basic\";\n\n/**\n * Throws if the given value is not a valid CorsInit.\n *\n * Checks only the fields that are present, since CorsInit is Partial<CorsConfig>.\n *\n * @param value - The value to check.\n * @throws TypeError If `value` is not a valid {@link CorsInit}.\n */\nexport function assertCorsInit(value: unknown): asserts value is CorsInit {\n if (value === undefined) return;\n\n if (typeof value !== \"object\" || value === null) {\n throw new TypeError(\"CorsInit must be an object.\");\n }\n\n const obj = value as Record<string, unknown>;\n\n if (obj[\"allowedOrigins\"] !== undefined && !isStringArray(obj[\"allowedOrigins\"])) {\n throw new TypeError(\"CorsInit.allowedOrigins must be a string array.\");\n }\n\n if (obj[\"allowedHeaders\"] !== undefined && !isStringArray(obj[\"allowedHeaders\"])) {\n throw new TypeError(\"CorsInit.allowedHeaders must be a string array.\");\n }\n\n if (obj[\"exposedHeaders\"] !== undefined && !isStringArray(obj[\"exposedHeaders\"])) {\n throw new TypeError(\"CorsInit.exposedHeaders must be a string array.\");\n }\n\n if (obj[\"allowCredentials\"] !== undefined && !isBoolean(obj[\"allowCredentials\"])) {\n throw new TypeError(\"CorsInit.allowCredentials must be a boolean.\");\n }\n\n if (obj[\"maxAge\"] !== undefined && !isNumber(obj[\"maxAge\"])) {\n throw new TypeError(\"CorsInit.maxAge must be a number.\");\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Standard HTTP request methods.\n */\nexport enum Method {\n GET = \"GET\",\n PUT = \"PUT\",\n HEAD = \"HEAD\",\n POST = \"POST\",\n PATCH = \"PATCH\",\n DELETE = \"DELETE\",\n OPTIONS = \"OPTIONS\",\n}\n\n/**\n * Shorthand constants for each HTTP method.\n *\n * These are equivalent to the corresponding enum members in `Method`.\n * For example, `GET === Method.GET`.\n */\nexport const { GET, PUT, HEAD, POST, PATCH, DELETE, OPTIONS } = Method;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport CacheLib from \"cache-control-parser\";\n\n/**\n * @see {@link https://github.com/etienne-martin/cache-control-parser | cache-control-parser}\n */\nexport type CacheControl = CacheLib.CacheControl;\nexport const CacheControl = {\n parse: CacheLib.parse,\n stringify: CacheLib.stringify,\n\n /** A CacheControl directive that disables all caching. */\n DISABLE: Object.freeze({\n \"no-cache\": true,\n \"no-store\": true,\n \"must-revalidate\": true,\n \"max-age\": 0,\n }) satisfies CacheControl,\n};\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Time constants in seconds. Month is approximated as 30 days.\n */\nexport const Time = {\n Second: 1,\n Minute: 60,\n Hour: 3600, // 60 * 60\n Day: 86400, // 60 * 60 * 24\n Week: 604800, // 60 * 60 * 24 * 7\n Month: 2592000, // 60 * 60 * 24 * 30\n Year: 31536000, // 60 * 60 * 24 * 365\n} as const;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Internally used headers.\n */\nexport const HttpHeader = {\n ACCEPT: \"accept\",\n ACCEPT_ENCODING: \"accept-encoding\",\n ACCEPT_LANGUAGE: \"accept-language\",\n ACCEPT_RANGES: \"accept-ranges\",\n ALLOW: \"allow\",\n AUTHORIZATION: \"authorization\",\n CACHE_CONTROL: \"cache-control\",\n CONNECTION: \"connection\",\n CONTENT_DISPOSITION: \"content-disposition\",\n CONTENT_ENCODING: \"content-encoding\",\n CONTENT_LANGUAGE: \"content-language\",\n CONTENT_LENGTH: \"content-length\",\n CONTENT_RANGE: \"content-range\",\n CONTENT_TYPE: \"content-type\",\n CONTENT_MD5: \"content-md5\",\n COOKIE: \"cookie\",\n ETAG: \"etag\",\n IF_MATCH: \"if-match\",\n IF_MODIFIED_SINCE: \"if-modified-since\",\n IF_NONE_MATCH: \"if-none-match\",\n IF_UNMODIFIED_SINCE: \"if-unmodified-since\",\n LAST_MODIFIED: \"last-modified\",\n ORIGIN: \"origin\",\n RANGE: \"range\",\n SET_COOKIE: \"set-cookie\",\n VARY: \"vary\",\n\n // Cors Headers\n ACCESS_CONTROL_ALLOW_CREDENTIALS: \"access-control-allow-credentials\",\n ACCESS_CONTROL_ALLOW_HEADERS: \"access-control-allow-headers\",\n ACCESS_CONTROL_ALLOW_METHODS: \"access-control-allow-methods\",\n ACCESS_CONTROL_ALLOW_ORIGIN: \"access-control-allow-origin\",\n ACCESS_CONTROL_EXPOSE_HEADERS: \"access-control-expose-headers\",\n ACCESS_CONTROL_MAX_AGE: \"access-control-max-age\",\n\n // Websocket Headers\n SEC_WEBSOCKET_VERSION: \"sec-websocket-version\",\n UPGRADE: \"upgrade\",\n\n // Internal Headers\n INTERNAL_VARIANT_SET: \"internal-variant-set\",\n} as const;\n\n/**\n * Headers that must not be sent in 304 Not Modified responses.\n * These are stripped to comply with the HTTP spec.\n */\nexport const FORBIDDEN_304_HEADERS = [\n HttpHeader.CONTENT_TYPE,\n HttpHeader.CONTENT_LENGTH,\n HttpHeader.CONTENT_RANGE,\n HttpHeader.CONTENT_ENCODING,\n HttpHeader.CONTENT_LANGUAGE,\n HttpHeader.CONTENT_DISPOSITION,\n HttpHeader.CONTENT_MD5,\n];\n\n/**\n * Headers that should not be sent in 204 No Content responses.\n * Stripping them is recommended but optional per spec.\n */\nexport const FORBIDDEN_204_HEADERS = [HttpHeader.CONTENT_LENGTH, HttpHeader.CONTENT_RANGE];\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { StatusCodes } from \"../../constants\";\nimport { HttpHeader } from \"../../constants/headers\";\nimport { Time } from \"../../constants/time\";\n\nimport { CorsConfig } from \"./interfaces\";\n\n/**\n * A wildcard value used in the CORS `Access-Control-Allow-Origin` header\n * to permit requests from **any** origin.\n */\nexport const ALLOW_ALL_ORIGINS = \"*\";\n\n/**\n * Status codes for which `CORS` should be skipped.\n *\n * Skips `CORS` for:\n * - 101 Switching Protocols (WebSocket upgrade)\n * - 100 Continue\n * - 3xx Redirects (`CORS` is applied to the final URL only)\n */\nexport const SKIP_CORS_STATUSES = [\n StatusCodes.SWITCHING_PROTOCOLS,\n StatusCodes.CONTINUE,\n StatusCodes.PROCESSING,\n StatusCodes.EARLY_HINTS,\n StatusCodes.MOVED_PERMANENTLY,\n StatusCodes.MOVED_TEMPORARILY,\n StatusCodes.SEE_OTHER,\n StatusCodes.TEMPORARY_REDIRECT,\n StatusCodes.PERMANENT_REDIRECT,\n];\n\n/**\n * Default configuration for `CORS` middleware.\n */\nexport const defaultCorsConfig: CorsConfig = {\n allowedOrigins: [ALLOW_ALL_ORIGINS],\n allowedHeaders: [HttpHeader.CONTENT_TYPE],\n exposedHeaders: [],\n allowCredentials: false,\n maxAge: 5 * Time.Minute,\n} as const;\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Method } from \"../constants/methods\";\n\nimport { isString } from \"./basic\";\n\n/**\n * A set containing all supported HTTP methods.\n *\n * Useful for runtime checks like validating request methods.\n */\nconst METHOD_SET: Set<string> = new Set(Object.values(Method));\n\n/**\n * Type guard that checks if a string is a valid HTTP method.\n *\n * @param value - The string to test.\n * @returns True if `value` is a recognized HTTP method.\n */\nexport function isMethod(value: unknown): value is Method {\n return isString(value) && METHOD_SET.has(value);\n}\n\n/**\n * Checks if a value is an array of valid HTTP methods.\n *\n * Each element is verified using the `isMethod` type guard.\n *\n * @param value - The value to check.\n * @returns `true` if `value` is an array and every element is a valid `Method`, otherwise `false`.\n */\nexport function isMethodArray(value: unknown): value is Method[] {\n return Array.isArray(value) && value.every(isMethod);\n}\n\n/**\n * Asserts that a value is an array of valid HTTP methods.\n *\n * This function uses {@link isMethodArray} to validate the input. If the\n * value is not an array of `Method` elements, it throws a `TypeError`.\n * Otherwise, TypeScript will narrow the type of `value` to `Method[]`\n * within the calling scope.\n *\n * @param value - The value to check.\n * @throws TypeError If `value` is not a valid {@link Method} array.\n */\nexport function assertMethods(value: unknown): asserts value is Method[] {\n if (!isMethodArray(value)) {\n const desc = Array.isArray(value) ? JSON.stringify(value) : String(value);\n throw new TypeError(`Invalid method array: ${desc}`);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport const UTF8_CHARSET = \"utf-8\";\n\n/**\n * Internal media types.\n */\nexport enum MediaType {\n PLAIN_TEXT = \"text/plain\",\n HTML = \"text/html\",\n JSON = \"application/json\",\n OCTET_STREAM = \"application/octet-stream\",\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Lexicographically compares two strings.\n *\n * This comparator can be used in `Array.prototype.sort()` to produce a\n * consistent, stable ordering of string arrays.\n *\n * @param a - The first string to compare.\n * @param b - The second string to compare.\n * @returns A number indicating the relative order of `a` and `b`.\n */\nexport function lexCompare(a: string, b: string): number {\n if (a < b) return -1;\n if (a > b) return 1;\n return 0;\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { lexCompare } from \"./compare\";\n\n/**\n * Sets a header on the given Headers object.\n *\n * - If `value` is an array, any duplicates and empty strings are removed.\n * - If the resulting value is empty, the header is deleted.\n * - Otherwise, values are joined with `\", \"` and set as the header value.\n *\n * @param headers - The Headers object to modify.\n * @param key - The header name to set.\n * @param value - The header value(s) to set. Can be a string or array of strings.\n */\nexport function setHeader(headers: Headers, key: string, value: string | string[]): void {\n const raw = Array.isArray(value) ? value : [value];\n const values = Array.from(new Set(raw.map((v) => v.trim())))\n .filter((v) => v.length)\n .sort(lexCompare);\n\n if (!values.length) {\n headers.delete(key);\n return;\n }\n\n headers.set(key, values.join(\", \"));\n}\n\n/**\n * Merges new value(s) into an existing header on the given Headers object.\n *\n * - Preserves any existing values and adds new ones.\n * - Removes duplicates and trims all values.\n * - If the header does not exist, it is created.\n * - If the resulting value array is empty, the header is deleted.\n *\n * @param headers - The Headers object to modify.\n * @param key - The header name to merge into.\n * @param value - The new header value(s) to add. Can be a string or array of strings.\n */\nexport function mergeHeader(headers: Headers, key: string, value: string | string[]): void {\n const values = Array.isArray(value) ? value : [value];\n if (values.length === 0) return;\n\n const existing = getHeaderValues(headers, key);\n const merged = existing.concat(values.map((v) => v.trim()));\n\n setHeader(headers, key, merged);\n}\n\n/**\n * Returns the values of an HTTP header as an array of strings.\n *\n * This helper:\n * - Retrieves the header value by `key`.\n * - Splits the value on commas.\n * - Trims surrounding whitespace from each entry.\n * - Filters out any empty tokens.\n * - Removes duplicate values (case-sensitive)\n *\n * If the header is not present, an empty array is returned.\n *\n */\nexport function getHeaderValues(headers: Headers, key: string): string[] {\n const values =\n headers\n .get(key)\n ?.split(\",\")\n .map((v) => v.trim())\n .filter((v) => v.length > 0) ?? [];\n return Array.from(new Set(values)).sort(lexCompare);\n}\n\n/**\n * Removes a list of header fields from a {@link Headers} object.\n *\n * @param headers - The {@link Headers} object to modify in place.\n * @param keys - An array of header field names to remove. Header names are\n * matched case-insensitively per the Fetch spec.\n */\nexport function filterHeaders(headers: Headers, keys: string[]): void {\n for (const key of keys) {\n headers.delete(key);\n }\n}\n\n/**\n * Extracts all header names from a `Headers` object, normalizes them,\n * and returns them in a stable, lexicographically sorted array.\n *\n * @param headers - The `Headers` object to extract keys from.\n * @returns A sorted array of lowercase header names.\n */\nexport function getHeaderKeys(headers: Headers): string[] {\n return [...headers.keys()].sort(lexCompare);\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Appends a charset parameter to a given media type string,\n * avoiding duplicates and ignoring empty charsets.\n *\n * @param {string} mediaType - The MIME type (e.g., \"text/html\").\n * @param {string} charset - The character set to append (e.g., \"utf-8\").\n * @returns {string} The media type with charset appended if provided.\n */\nexport function withCharset(mediaType: string, charset: string): string {\n if (!charset || mediaType.toLowerCase().includes(\"charset=\")) {\n return mediaType;\n }\n return `${mediaType}; charset=${charset.toLowerCase()}`;\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { getReasonPhrase } from \"http-status-codes/build/es/utils-functions\";\n\nimport { StatusCodes } from \"./constants\";\nimport { CacheControl } from \"./constants/cache\";\nimport { FORBIDDEN_204_HEADERS, FORBIDDEN_304_HEADERS, HttpHeader } from \"./constants/headers\";\nimport { MediaType, UTF8_CHARSET } from \"./constants/media\";\nimport { GET, HEAD } from \"./constants/methods\";\nimport { assertMethods } from \"./guards/methods\";\nimport { assertOctetStreamInit } from \"./guards/responses\";\nimport { Worker } from \"./interfaces\";\nimport { OctetStreamInit } from \"./interfaces/response\";\nimport { filterHeaders, mergeHeader, setHeader } from \"./utils/headers\";\nimport { withCharset } from \"./utils/media\";\n\n/**\n * Base class for building HTTP responses.\n * Manages headers, status, and media type.\n */\nabstract class BaseResponse {\n /** HTTP headers for the response. */\n public headers: Headers = new Headers();\n\n /** HTTP status code (default 200 OK). */\n public status: StatusCodes = StatusCodes.OK;\n\n /** Optional status text. Defaults to standard reason phrase. */\n public statusText?: string;\n\n /** Optional websocket property. */\n public webSocket?: WebSocket | null;\n\n /** Default media type of the response body. */\n public mediaType: string = withCharset(MediaType.PLAIN_TEXT, UTF8_CHARSET);\n\n /** Converts current state to ResponseInit for constructing a Response. */\n protected get responseInit(): ResponseInit {\n return {\n headers: this.headers,\n status: this.status,\n statusText: this.statusText ?? getReasonPhrase(this.status),\n webSocket: this.webSocket,\n encodeBody: \"automatic\",\n };\n }\n\n /** Sets a header, overwriting any existing value. */\n public setHeader(key: string, value: string | string[]): void {\n setHeader(this.headers, key, value);\n }\n\n /** Merges a header with existing values (does not overwrite). */\n public mergeHeader(key: string, value: string | string[]): void {\n mergeHeader(this.headers, key, value);\n }\n\n /** Adds a Content-Type header if not already existing (does not overwrite). */\n public addContentType() {\n if (!this.headers.get(HttpHeader.CONTENT_TYPE)) {\n this.setHeader(HttpHeader.CONTENT_TYPE, this.mediaType);\n }\n }\n\n /**\n * Removes headers that are disallowed or discouraged based on the current\n * status code.\n *\n * - **204 No Content:** strips headers that \"should not\" be sent\n * (`Content-Length`, `Content-Range`), per the HTTP spec.\n * - **304 Not Modified:** strips headers that \"must not\" be sent\n * (`Content-Type`, `Content-Length`, `Content-Range`, etc.), per the HTTP spec.\n *\n * This ensures that responses remain compliant with HTTP/1.1 standards while preserving\n * custom headers that are allowed.\n */\n public filterHeaders(): void {\n if (this.status === StatusCodes.NO_CONTENT) {\n filterHeaders(this.headers, FORBIDDEN_204_HEADERS);\n } else if (this.status === StatusCodes.NOT_MODIFIED) {\n filterHeaders(this.headers, FORBIDDEN_304_HEADERS);\n }\n }\n}\n\n/**\n * Base response class that adds caching headers.\n */\nabstract class CacheResponse extends BaseResponse {\n constructor(public cache?: CacheControl) {\n super();\n }\n\n /** Adds Cache-Control header if caching is configured. */\n protected addCacheHeader(): void {\n if (this.cache) {\n this.setHeader(HttpHeader.CACHE_CONTROL, CacheControl.stringify(this.cache));\n }\n }\n}\n\n/**\n * Core response. Combines caching, and content type headers.\n */\nexport abstract class WorkerResponse extends CacheResponse {\n constructor(\n private readonly body: BodyInit | null = null,\n cache?: CacheControl,\n ) {\n super(cache);\n }\n\n /** Builds the Response with body, headers, and status. */\n public async response(): Promise<Response> {\n this.addCacheHeader();\n\n const body = [StatusCodes.NO_CONTENT, StatusCodes.NOT_MODIFIED].includes(this.status)\n ? null\n : this.body;\n\n if (body) this.addContentType();\n\n this.filterHeaders();\n\n return new Response(body, this.responseInit);\n }\n}\n\n/**\n * Copies an existing response for mutation. Pass in a CacheControl\n * to be used for the response, overriding any existing `cache-control`\n * on the source response.\n */\nexport class CopyResponse extends WorkerResponse {\n constructor(response: Response, cache?: CacheControl) {\n super(response.body, cache);\n this.status = response.status;\n this.statusText = response.statusText;\n this.headers = new Headers(response.headers);\n }\n}\n\n/**\n * Copies the response, but with null body and status 304 Not Modified.\n */\nexport class NotModified extends WorkerResponse {\n constructor(response: Response) {\n super();\n this.status = StatusCodes.NOT_MODIFIED;\n this.headers = new Headers(response.headers);\n }\n}\n\n/**\n * Represents a successful response with customizable body, cache and status.\n */\nexport class SuccessResponse extends WorkerResponse {\n constructor(\n body: BodyInit | null = null,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n ) {\n super(body, cache);\n this.status = status;\n }\n}\n\n/**\n * JSON response. Automatically sets Content-Type to application/json.\n */\nexport class JsonResponse extends SuccessResponse {\n constructor(json: unknown = {}, cache?: CacheControl, status: StatusCodes = StatusCodes.OK) {\n super(JSON.stringify(json), cache, status);\n this.mediaType = withCharset(MediaType.JSON, UTF8_CHARSET);\n }\n}\n\n/**\n * HTML response. Automatically sets Content-Type to text/html.\n */\nexport class HtmlResponse extends SuccessResponse {\n constructor(\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n charset: string = UTF8_CHARSET,\n ) {\n super(body, cache, status);\n this.mediaType = withCharset(MediaType.HTML, charset);\n }\n}\n\n/**\n * Plain text response. Automatically sets Content-Type to text/plain.\n */\nexport class TextResponse extends SuccessResponse {\n constructor(\n body: string,\n cache?: CacheControl,\n status: StatusCodes = StatusCodes.OK,\n charset: string = UTF8_CHARSET,\n ) {\n super(body, cache, status);\n this.mediaType = withCharset(MediaType.PLAIN_TEXT, charset);\n }\n}\n\n/**\n * Represents an HTTP response for serving binary data as `application/octet-stream`.\n *\n * This class wraps a `ReadableStream` and sets all necessary headers for both\n * full and partial content responses, handling range requests in a hybrid way\n * to maximize browser and CDN caching.\n *\n * Key behaviors:\n * - `Content-Type` is set to `application/octet-stream`.\n * - `Accept-Ranges: bytes` is always included.\n * - `Content-Length` is always set to the validated length of the response body.\n * - If the request is a true partial range (offset > 0 or length < size), the response\n * will be `206 Partial Content` with the appropriate `Content-Range` header.\n * - If the requested range covers the entire file (even if a Range header is present),\n * the response will return `200 OK` to enable browser and edge caching.\n * - Zero-length streams (`size = 0`) are never treated as partial.\n * - Special case: a requested range of `0-0` on a non-empty file is normalized to 1 byte.\n */\nexport class OctetStream extends WorkerResponse {\n constructor(stream: ReadableStream, init: OctetStreamInit, cache?: CacheControl) {\n assertOctetStreamInit(init);\n\n super(stream, cache);\n this.mediaType = MediaType.OCTET_STREAM;\n\n const normalized = OctetStream.normalizeInit(init);\n const { size, offset, length } = normalized;\n\n if (OctetStream.isPartial(normalized)) {\n this.setHeader(\n HttpHeader.CONTENT_RANGE,\n `bytes ${offset}-${offset + length - 1}/${size}`,\n );\n this.status = StatusCodes.PARTIAL_CONTENT;\n }\n\n this.setHeader(HttpHeader.ACCEPT_RANGES, \"bytes\");\n this.setHeader(HttpHeader.CONTENT_LENGTH, `${length}`);\n }\n\n /**\n * Normalizes a partially-specified `OctetStreamInit` into a fully-specified object.\n *\n * Ensures that all required fields (`size`, `offset`, `length`) are defined:\n * - `offset` defaults to 0 if not provided.\n * - `length` defaults to `size - offset` if not provided.\n * - Special case: if `offset` and `length` are both 0 but `size > 0`, `length` is set to 1\n * to avoid zero-length partial streams.\n *\n * @param init - The initial `OctetStreamInit` object, possibly with missing `offset` or `length`.\n * @returns A fully-specified `OctetStreamInit` object with `size`, `offset`, and `length` guaranteed.\n */\n private static normalizeInit(init: OctetStreamInit): Required<OctetStreamInit> {\n const { size } = init;\n const offset = init.offset ?? 0;\n let length = init.length ?? size - offset;\n\n if (offset === 0 && length === 0 && size > 0) {\n length = 1;\n }\n\n return { size, offset, length };\n }\n\n /**\n * Determines whether the given `OctetStreamInit` represents a partial range.\n *\n * Partial ranges are defined as any range that does **not** cover the entire file:\n * - If `size === 0`, the stream is never partial.\n * - If `offset === 0` and `length === size`, the stream is treated as a full file (not partial),\n * even if a Range header is present. This enables browser and CDN caching.\n * - All other cases are considered partial, and will result in a `206 Partial Content` response.\n *\n * @param init - A fully-normalized `OctetStreamInit` object.\n * @returns `true` if the stream represents a partial range; `false` if it represents the full file.\n */\n private static isPartial(init: Required<OctetStreamInit>): boolean {\n if (init.size === 0) return false;\n return !(init.offset === 0 && init.length === init.size);\n }\n}\n\n/**\n * A streaming response for Cloudflare R2 objects.\n *\n * **Partial content support:** To enable HTTP 206 streaming, you must provide\n * request headers containing the `Range` header when calling the R2 bucket's `get()` method.\n *\n * Example:\n * ```ts\n * const stream = await this.env.R2_BUCKET.get(\"key\", { range: this.request.headers });\n * ```\n *\n * @param source - The R2 object to stream.\n * @param cache - Optional caching override.\n */\nexport class R2ObjectStream extends OctetStream {\n constructor(source: R2ObjectBody, cache?: CacheControl) {\n let useCache = cache;\n if (!useCache && source.httpMetadata?.cacheControl) {\n useCache = CacheControl.parse(source.httpMetadata.cacheControl);\n }\n\n super(source.body, R2ObjectStream.computeRange(source.size, source.range), useCache);\n\n this.setHeader(HttpHeader.ETAG, source.httpEtag);\n\n if (source.httpMetadata?.contentType) {\n this.mediaType = source.httpMetadata.contentType;\n }\n }\n\n /**\n * Computes an `OctetStreamInit` object from a given R2 range.\n *\n * This function normalizes a Cloudflare R2 `R2Range` into the shape expected\n * by `OctetStream`. It handles the following cases:\n *\n * - No range provided: returns `{ size }` (full content).\n * - `suffix` range: calculates the offset and length from the end of the file.\n * - Explicit `offset` and/or `length`: passed through as-is.\n *\n * @param size - The total size of the file/object.\n * @param range - Optional range to extract (from R2). Can be:\n * - `{ offset: number; length?: number }`\n * - `{ offset?: number; length: number }`\n * - `{ suffix: number }`\n * @returns An `OctetStreamInit` object suitable for `OctetStream`.\n */\n private static computeRange(size: number, range?: R2Range): OctetStreamInit {\n if (!range) return { size };\n\n if (\"suffix\" in range) {\n const offset = Math.max(0, size - range.suffix);\n const length = size - offset;\n return { size, offset, length };\n }\n\n return { size, ...range };\n }\n}\n\n/**\n * Response for WebSocket upgrade requests.\n * Automatically sets status to 101 and attaches the client socket.\n */\nexport class WebSocketUpgrade extends WorkerResponse {\n constructor(client: WebSocket) {\n super();\n this.status = StatusCodes.SWITCHING_PROTOCOLS;\n this.webSocket = client;\n }\n}\n\n/**\n * Response for `HEAD` requests. Copy headers and status from a `GET` response\n * without the body.\n */\nexport class Head extends WorkerResponse {\n constructor(get: Response) {\n super();\n this.status = get.status;\n this.statusText = get.statusText;\n this.headers = new Headers(get.headers);\n }\n}\n\n/**\n * Response for `OPTIONS` requests.\n */\nexport class Options extends WorkerResponse {\n constructor(worker: Worker) {\n const allowed = Array.from(new Set([GET, HEAD, ...worker.getAllowedMethods()]));\n assertMethods(allowed);\n\n super();\n this.status = StatusCodes.NO_CONTENT;\n this.setHeader(HttpHeader.ALLOW, allowed);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { HttpHeader } from \"../../constants/headers\";\nimport { assertMethods } from \"../../guards/methods\";\nimport { Worker } from \"../../interfaces/worker\";\nimport { CopyResponse } from \"../../responses\";\nimport { mergeHeader, setHeader } from \"../../utils/headers\";\n\nimport { ALLOW_ALL_ORIGINS, SKIP_CORS_STATUSES } from \"./constants\";\nimport { CorsConfig } from \"./interfaces\";\n\n/**\n * Handles a `CORS` preflight `OPTIONS` request.\n *\n * This function **modifies the provided response** to include the appropriate\n * CORS headers based on the configuration and the request's origin.\n *\n * Steps:\n * 1. Clears any existing CORS headers from the response.\n * 2. Sets `Vary: Origin` if needed for caching purposes.\n * 3. If an `Origin` header is present in the request:\n * - Sets `Access-Control-Allow-Origin`\n * - Sets `Access-Control-Allow-Credentials`\n * - Sets `Access-Control-Allow-Methods`\n * - Sets `Access-Control-Allow-Headers`\n * - Sets `Access-Control-Max-Age`\n *\n * @param response - The original Response object to modify for the preflight.\n * @param worker - The Worker handling the request.\n * @param cors - The CORS configuration to apply.\n * @returns A Response object suitable for responding to the preflight request.\n */\nexport function options(response: Response, worker: Worker, cors: CorsConfig): Promise<Response> {\n const copy = new CopyResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(copy.headers);\n setVaryOrigin(copy.headers, cors);\n\n if (origin) {\n setAllowOrigin(copy.headers, cors, origin);\n setAllowCredentials(copy.headers, cors, origin);\n setAllowMethods(copy.headers, worker);\n setAllowHeaders(copy.headers, cors);\n setMaxAge(copy.headers, cors);\n }\n\n return copy.response();\n}\n\n/**\n * Applies CORS headers to an existing response for non-preflight requests.\n *\n * This function **modifies the provided response** to include the appropriate\n * CORS headers based on the configuration and the request's origin.\n *\n * Steps:\n * 1. Clears any existing CORS headers from the response.\n * 2. Sets `Vary: Origin` if needed for caching purposes.\n * 3. If an `Origin` header is present in the request:\n * - Sets `Access-Control-Allow-Origin`\n * - Sets `Access-Control-Allow-Credentials`\n * - Sets `Access-Control-Expose-Headers`\n *\n * @param response - The original Response object to modify.\n * @param worker - The Worker handling the request.\n * @param cors - The CORS configuration to apply.\n * @returns A Response object with CORS headers applied, suitable for returning\n * to the client.\n */\nexport function apply(response: Response, worker: Worker, cors: CorsConfig): Promise<Response> {\n const copy = new CopyResponse(response);\n const origin = getOrigin(worker.request);\n\n deleteCorsHeaders(copy.headers);\n setVaryOrigin(copy.headers, cors);\n\n if (origin) {\n setAllowOrigin(copy.headers, cors, origin);\n setAllowCredentials(copy.headers, cors, origin);\n setExposedHeaders(copy.headers, cors);\n }\n\n return copy.response();\n}\n\n/**\n * Adds `Vary: Origin` when `CORS` is restricted to specific origins.\n * This ensures caches differentiate responses by request origin.\n *\n * Skipped when all origins are allowed.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setVaryOrigin(headers: Headers, cors: CorsConfig): void {\n if (!allowAllOrigins(cors)) {\n mergeHeader(headers, HttpHeader.VARY, HttpHeader.ORIGIN);\n }\n}\n\n/**\n * Sets the Access-Control-Allow-Origin header based on the `CORS` config\n * and request origin.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n * @param origin - The request's origin, or null if not present.\n */\nexport function setAllowOrigin(headers: Headers, cors: CorsConfig, origin: string): void {\n if (allowAllOrigins(cors)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, ALLOW_ALL_ORIGINS);\n return;\n }\n\n if (cors.allowedOrigins.includes(origin)) {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN, origin);\n }\n}\n\n/**\n * Conditionally sets the `Access-Control-Allow-Credentials` header\n * for a `CORS` response.\n *\n * This header is only set if:\n * 1. `cors.allowCredentials` is true,\n * 2. The configuration does **not** allow any origin (`*`), and\n * 3. The provided `origin` is explicitly listed in `cors.allowedOrigins`.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration.\n * @param origin - The origin of the incoming request.\n */\nexport function setAllowCredentials(headers: Headers, cors: CorsConfig, origin: string): void {\n if (!cors.allowCredentials) return;\n if (allowAllOrigins(cors)) return;\n if (!cors.allowedOrigins.includes(origin)) return;\n\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_CREDENTIALS, \"true\");\n}\n\n/**\n * Sets the `Access-Control-Allow-Methods` header for a `CORS` response.\n *\n * @param headers - The Headers object to modify.\n * @param worker - The Worker instance used to retrieve allowed methods.\n */\nexport function setAllowMethods(headers: Headers, worker: Worker): void {\n const allowed = worker.getAllowedMethods();\n assertMethods(allowed);\n\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_METHODS, allowed);\n}\n\n/**\n * Sets the `Access-Control-Max-Age` header for a `CORS` response.\n *\n * This header indicates how long the results of a preflight request\n * can be cached by the client (in seconds).\n *\n * The value is **clamped to a non-negative integer** to comply with\n * the `CORS` specification:\n * - Decimal values are floored to the nearest integer.\n * - Negative values are treated as `0`.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration containing the `maxAge` value in seconds.\n */\nexport function setMaxAge(headers: Headers, cors: CorsConfig): void {\n const maxAge = Math.max(0, Math.floor(cors.maxAge));\n setHeader(headers, HttpHeader.ACCESS_CONTROL_MAX_AGE, String(maxAge));\n}\n\n/**\n * Sets the Access-Control-Allow-Headers header based on the `CORS` configuration.\n *\n * Only the headers explicitly listed in `cors.allowedHeaders` are sent.\n * If the array is empty, no Access-Control-Allow-Headers header is added.\n *\n * @param headers - The Headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setAllowHeaders(headers: Headers, cors: CorsConfig): void {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS, cors.allowedHeaders);\n}\n\n/**\n * Sets the Access-Control-Expose-Headers header for a response.\n *\n * @param headers - The headers object to modify.\n * @param cors - The `CORS` configuration.\n */\nexport function setExposedHeaders(headers: Headers, cors: CorsConfig): void {\n setHeader(headers, HttpHeader.ACCESS_CONTROL_EXPOSE_HEADERS, cors.exposedHeaders);\n}\n\n/**\n * Returns true if the `CORS` config allows all origins ('*').\n *\n * @param cors - The `CORS` configuration.\n */\nexport function allowAllOrigins(cors: CorsConfig): boolean {\n return cors.allowedOrigins.includes(ALLOW_ALL_ORIGINS);\n}\n\n/**\n * Deletes any existing `CORS` headers from the provided headers object.\n *\n * @param headers - The headers object to modify.\n */\nexport function deleteCorsHeaders(headers: Headers): void {\n headers.delete(HttpHeader.ACCESS_CONTROL_MAX_AGE);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_ORIGIN);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_HEADERS);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_METHODS);\n headers.delete(HttpHeader.ACCESS_CONTROL_EXPOSE_HEADERS);\n headers.delete(HttpHeader.ACCESS_CONTROL_ALLOW_CREDENTIALS);\n}\n\n/**\n * Determines whether CORS headers should be skipped for a response.\n *\n * Certain responses do not require CORS headers, such as:\n * - Informational or protocol-level responses (1xx)\n * - Redirect responses (3xx)\n * - Responses initiating a protocol upgrade\n *\n * @param response - The Response object to inspect.\n * @returns `true` if CORS headers should be omitted, `false` otherwise.\n */\nexport function skipCors(response: Response): boolean {\n const { status, headers } = response;\n if (SKIP_CORS_STATUSES.includes(status)) return true;\n if (headers.has(HttpHeader.UPGRADE)) return true;\n\n return false;\n}\n\n/**\n * Extracts and normalizes the `Origin` header from a request.\n *\n * Returns the origin (scheme + host + port) as a string if present and valid.\n * Returns `null` if:\n * - The `Origin` header is missing\n * - The `Origin` header is `\"null\"` (opaque origin)\n * - The `Origin` header is malformed\n *\n * @param request - The incoming {@link Request} object.\n * @returns The normalized origin string, or `null` if not present or invalid.\n */\nexport function getOrigin(request: Request): string | null {\n const origin = request.headers.get(HttpHeader.ORIGIN)?.trim();\n if (!origin || origin === \"null\") return null;\n\n try {\n return new URL(origin).origin;\n } catch {\n return null;\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { OPTIONS } from \"../../constants/methods\";\nimport { Middleware } from \"../../interfaces/middleware\";\nimport { Worker } from \"../../interfaces/worker\";\n\nimport { defaultCorsConfig } from \"./constants\";\nimport { CorsConfig, CorsInit } from \"./interfaces\";\nimport { apply, options, skipCors } from \"./utils\";\n\n/**\n * CORS Middleware Implementation\n *\n * Handles Cross-Origin Resource Sharing (CORS) for incoming requests.\n *\n * Behavior:\n * - Invokes the downstream middleware for all requests first by calling `next()`.\n * - If the request is an `OPTIONS` request (preflight), transforms the downstream\n * response into a preflight CORS response.\n * - For other HTTP methods, applies CORS headers to the downstream response,\n * unless the response explicitly opts out via `skipCors`.\n *\n * This ensures that all responses comply with the configured CORS rules\n * while still allowing downstream middleware to run for every request.\n *\n * @see {@link cors} for full configuration options and defaults.\n */\nexport class CorsHandler implements Middleware {\n /** The resolved CORS configuration for this middleware instance. */\n private readonly config: CorsConfig;\n\n /**\n * Constructs a new `CorsHandler` instance.\n *\n * Merges the provided partial configuration with the default settings.\n *\n * @param init - Optional partial configuration to override defaults.\n * Any fields not provided will use `defaultCorsConfig`.\n */\n constructor(init?: CorsInit) {\n this.config = { ...defaultCorsConfig, ...init };\n }\n\n /**\n * Applies CORS handling to an incoming request.\n *\n * @param worker - The Worker instance containing the request and context.\n * @param next - Function invoking the next middleware in the chain.\n * @returns A Response object with CORS headers applied.\n * - For `OPTIONS` requests, returns a preflight response based on\n * the downstream response.\n * - For other methods, returns the downstream response with\n * CORS headers applied, unless `skipCors` prevents it.\n */\n public async handle(worker: Worker, next: () => Promise<Response>): Promise<Response> {\n const response = await next();\n\n if (worker.request.method === OPTIONS) {\n return options(response, worker, this.config);\n }\n\n if (skipCors(response)) return response;\n\n return apply(response, worker, this.config);\n }\n}\n","/*\n * Copyright (C) 2025 Ty Busby\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { assertCorsInit } from \"../../guards/cors\";\nimport { Middleware } from \"../../interfaces/middleware\";\n\nimport { CorsHandler } from \"./handler\";\nimport { CorsInit } from \"./interfaces\";\n\n/**\n * Returns a `CORS` middleware instance.\n *\n * This middleware automatically handles Cross-Origin Resource Sharing (CORS)\n * for incoming requests, including preflight `OPTIONS` requests, and adds\n * appropriate headers to responses.\n *\n * @param init - Optional configuration for `CORS` behavior. See {@link CorsConfig}.\n * @returns A {@link Middleware} instance that can be used in your middleware chain.\n */\nexport function cors(init?: CorsInit): Middleware {\n assertCorsInit(init);\n return new CorsHandler(init);\n}\n"]}
|